Skip to content

Commit

Permalink
Rewrite more deprecated calls
Browse files Browse the repository at this point in the history
Fix lifecycle related calls
  • Loading branch information
wec43 committed Jul 19, 2024
1 parent ef12900 commit 99b72cb
Show file tree
Hide file tree
Showing 10 changed files with 87 additions and 75 deletions.
2 changes: 1 addition & 1 deletion app-android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ dependencies {
}
implementation 'androidx.sqlite:sqlite-ktx:2.4.0'

implementation "androidx.room:room-runtime:$room_version"
implementation "androidx.room:room-ktx:$room_version"
ksp "androidx.room:room-compiler:$room_version"

if (file("../libs/android-database-sqlcipher-4.5.0.aar").exists()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ import android.content.res.Configuration.UI_MODE_NIGHT_MASK
import android.content.res.Configuration.UI_MODE_NIGHT_YES
import android.graphics.drawable.ColorDrawable
import android.util.Log
import android.view.View
import androidx.annotation.ColorInt
import androidx.core.view.WindowInsetsControllerCompat
import de.tutao.tutanota.ipc.ThemeFacade
import org.json.JSONException
import org.json.JSONObject
Expand Down Expand Up @@ -105,15 +105,16 @@ class AndroidThemeFacade(
// It is not an accident that navBg and headerBg seem to be swapped, the original color scheme was reused in
// this way.

val decorView = activity.window.decorView
val navBg = getColor(theme, "header_bg")

@ColorInt val navColor = parseColor(navBg)
val isNavBarLight = navBg.isLightHexColor()
var visibilityFlags = 0
activity.window.navigationBarColor = navColor

val windowInsetController = WindowInsetsControllerCompat(activity.window, activity.window.decorView)

if (isNavBarLight) {
visibilityFlags = visibilityFlags or View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR
windowInsetController.isAppearanceLightNavigationBars = true
}

val headerBg = getColor(theme, "navigation_bg")
Expand All @@ -128,10 +129,8 @@ class AndroidThemeFacade(
// we change lightStatusBar flag accordingly.
activity.window.statusBarColor = statusBarColor
if (isStatusBarLight) {
visibilityFlags = visibilityFlags or View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
windowInsetController.isAppearanceLightStatusBars = true
}

decorView.systemUiVisibility = visibilityFlags
}

private fun getColor(theme: Map<String, String>, key: String): String =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,7 @@ abstract class LifecycleJobService : JobService(), LifecycleOwner {
lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_DESTROY)
super.onDestroy()
}

override val lifecycle: Lifecycle
get() = lifecycleRegistry
}
56 changes: 25 additions & 31 deletions app-android/app/src/main/java/de/tutao/tutanota/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@ import androidx.core.view.ViewCompat.setSystemGestureExclusionRects
import androidx.core.view.doOnLayout
import androidx.fragment.app.FragmentActivity
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.flowWithLifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import de.tutao.tutanota.alarms.AlarmNotificationsManager
import de.tutao.tutanota.alarms.SystemAlarmFacade
import de.tutao.tutanota.credentials.CredentialsEncryptionFactory
Expand Down Expand Up @@ -90,7 +90,6 @@ interface WebauthnHandler {
}



class MainActivity : FragmentActivity() {
lateinit var webView: WebView
private set
Expand Down Expand Up @@ -119,8 +118,8 @@ class MainActivity : FragmentActivity() {

val db = AppDatabase.getDatabase(this, false)
sseStorage = SseStorage(
db,
createAndroidKeyStoreFacade()
db,
createAndroidKeyStoreFacade()
)
val localNotificationsFacade = LocalNotificationsFacade(this, sseStorage)
val fileFacade =
Expand Down Expand Up @@ -299,22 +298,20 @@ class MainActivity : FragmentActivity() {
queryParameters["noAutoLogin"] = "true"
}

webView.post { // use webView.post to switch to main thread again to be able to observe sseStorage
sseStorage.observeUsers().observe(this@MainActivity) { userInfos ->
if (userInfos!!.isEmpty()) {
Log.d(TAG, "invalidateAlarms")
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.CREATED) {
commonNativeFacade.invalidateAlarms()
}

// Start observing SSE users in the background.
// If there are no users we need to tell web part to invalidate alarms.
launch {
sseStorage.observeUsers()
.flowWithLifecycle(lifecycle, Lifecycle.State.STARTED)
.collect { userInfos ->
if (userInfos.isEmpty()) {
Log.d(TAG, "invalidateAlarms")
commonNativeFacade.invalidateAlarms()
}
}
}
}

// FIXME
mobileFacade.visibilityChange(true)

startWebApp(queryParameters)
}

Expand Down Expand Up @@ -388,6 +385,9 @@ class MainActivity : FragmentActivity() {
override fun onStart() {
super.onStart()
Log.d(TAG, "onStart")
lifecycleScope.launch {
mobileFacade.visibilityChange(true)
}
}

override fun onResume() {
Expand Down Expand Up @@ -432,7 +432,6 @@ class MainActivity : FragmentActivity() {
}

private fun handleIntent(intent: Intent) = lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.CREATED) {
// When we redirect to the app from outside, for example after doing payment verification,
// we don't want to do any kind of intent handling
val data = intent.data
Expand All @@ -442,7 +441,7 @@ class MainActivity : FragmentActivity() {
}

if (data != null && data.toString().startsWith("tutanota://")) {
return@repeatOnLifecycle
return@launch
}

if (intent.action != null && !intent.getBooleanExtra(ALREADY_HANDLED_INTENT, false)) {
Expand All @@ -461,7 +460,6 @@ class MainActivity : FragmentActivity() {
}
}
}
}
}
}

Expand Down Expand Up @@ -653,11 +651,9 @@ class MainActivity : FragmentActivity() {
mailToUrlString
)
} catch (e: RemoteExecutionException) {
val name = if (e.message != null) {
val element = Json.parseToJsonElement(e.message!!)
val name = e.message?.let { message ->
val element = Json.parseToJsonElement(message)
element.jsonObject["name"]?.jsonPrimitive?.content
} else {
null
}
Log.d(TAG, "failed to create a mail editor because of a ${name ?: "unknown error"}")
}
Expand Down Expand Up @@ -729,15 +725,13 @@ class MainActivity : FragmentActivity() {
private fun onBackPressedCallback() {
if (commonSystemFacade.initialized) {
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.CREATED) {
val result = mobileFacade.handleBackPress()
try {
if (!result) {
goBack()
}
} catch (e: JSONException) {
Log.e(TAG, "error parsing response", e)
val result = mobileFacade.handleBackPress()
try {
if (!result) {
goBack()
}
} catch (e: JSONException) {
Log.e(TAG, "error parsing response", e)
}
}
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ class IdTuple(
) {

@OptIn(ExperimentalSerializationApi::class)
@Serializer(forClass = IdTuple::class)
companion object IdTupleSerializer : KSerializer<IdTuple> {
override val descriptor: SerialDescriptor = listSerialDescriptor<String>()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,15 @@ package de.tutao.tutanota.data

import androidx.lifecycle.LiveData
import androidx.room.*
import kotlinx.coroutines.flow.Flow

@Dao
abstract class UserInfoDao {
@get:Query("SELECT * FROM User")
abstract val users: List<User>

@Query("SELECT * FROM User")
abstract fun observeUsers(): LiveData<List<User>>
abstract fun observeUsers(): Flow<List<User>>

@Query("SELECT * FROM PushIdentifierKey WHERE pushIdentifierId = :pushIdentifier")
abstract fun getPushIdentifierKey(pushIdentifier: String): PushIdentifierKey?
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -160,12 +160,6 @@ class LocalNotificationsFacade(private val context: Context, private val sseStor
Manifest.permission.POST_NOTIFICATIONS
) != PackageManager.PERMISSION_GRANTED
) {
// FIXME: Do we want to ask permission here?
// here to request the missing permissions, and then overriding
// public void onRequestPermissionsResult(int requestCode, String[] permissions,
// int[] grantResults)
// to handle the case where the user grants the permission. See the documentation
// for ActivityCompat#requestPermissions for more details.
return
}
notificationManager.notify(mailNotificationId("downloads"), notification)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,37 @@ import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.net.ConnectivityManager
import android.net.ConnectivityManager.NetworkCallback
import android.net.Network
import android.util.Log
import androidx.core.content.getSystemService
import androidx.lifecycle.DefaultLifecycleObserver
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import kotlinx.coroutines.launch

class NetworkObserver internal constructor(private val context: Context, lifecycleOwner: LifecycleOwner) :
BroadcastReceiver(), DefaultLifecycleObserver {
private val connectivityManager: ConnectivityManager =
context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
BroadcastReceiver(), DefaultLifecycleObserver {
private val connectivityManager: ConnectivityManager = context.getSystemService<ConnectivityManager>()!!
private var networkConnectivityListener: NetworkConnectivityListener? = null
private val networkObserverCallback = object : NetworkCallback() {
override fun onAvailable(network: Network) {
networkConnectivityListener?.onNetworkConnectivityChange(true)
}

override fun onLost(network: Network) {
networkConnectivityListener?.onNetworkConnectivityChange(false)
}
}

init {
context.registerReceiver(this, IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION))
lifecycleOwner.lifecycle.addObserver(this)
lifecycleOwner.lifecycleScope.launch {
lifecycleOwner.lifecycle.repeatOnLifecycle(Lifecycle.State.CREATED) {
connectivityManager.registerDefaultNetworkCallback(networkObserverCallback)
}
}
}

override fun onReceive(context: Context, intent: Intent) {
Expand All @@ -33,8 +51,7 @@ class NetworkObserver internal constructor(private val context: Context, lifecyc
}

fun hasNetworkConnection(): Boolean {
val networkInfo = connectivityManager.activeNetworkInfo
return networkInfo != null && networkInfo.isConnectedOrConnecting
return connectivityManager.activeNetwork != null
}

fun setNetworkConnectivityListener(networkConnectivityListener: NetworkConnectivityListener) {
Expand All @@ -46,7 +63,7 @@ class NetworkObserver internal constructor(private val context: Context, lifecyc
}

override fun onDestroy(owner: LifecycleOwner) {
context.unregisterReceiver(this)
connectivityManager.unregisterNetworkCallback(this.networkObserverCallback)
}

companion object {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import de.tutao.tutanota.ipc.NativeCredentialsFacade
import de.tutao.tutanota.push.SseClient.SseListener
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import okhttp3.OkHttpClient
import java.util.concurrent.TimeUnit

Expand Down Expand Up @@ -58,7 +59,7 @@ private enum class State {
*
* SSE has its own event loop, we are just listening for events here and mediating between it and SSE storage.
*/
class PushNotificationService(override val lifecycle: Lifecycle) : LifecycleJobService() {
class PushNotificationService : LifecycleJobService() {
@Volatile
private var jobParameters: JobParameters? = null
private lateinit var localNotificationsFacade: LocalNotificationsFacade
Expand Down Expand Up @@ -104,25 +105,27 @@ class PushNotificationService(override val lifecycle: Lifecycle) : LifecycleJobS
),
NetworkUtils.defaultClient
)
sseStorage.observeUsers().observeForever { userInfos ->
Log.d(TAG, "sse storage updated " + userInfos.size)
// Closing the connection sends RST packets over network and it triggers StrictMode
// violations so we dispatch it to another thread.
lifecycleScope.launch(Dispatchers.IO) {
val userIds = userInfos.mapTo(HashSet()) { it.userId }

if (userIds.isEmpty()) {
sseClient.stopConnection()
removeForegroundNotification()
finishJobIfNeeded()
} else {
sseClient.restartConnectionIfNeeded(
SseInfo(
sseStorage.getPushIdentifier()!!,
userIds,
sseStorage.getSseOrigin()!!
lifecycleScope.launch {
sseStorage.observeUsers().collect { userInfos ->
Log.d(TAG, "sse storage updated " + userInfos.size)
// Closing the connection sends RST packets over network and it triggers StrictMode
// violations so we dispatch it to another thread.
withContext(Dispatchers.IO) {
val userIds = userInfos.mapTo(HashSet()) { it.userId }

if (userIds.isEmpty()) {
sseClient.stopConnection()
removeForegroundNotification()
finishJobIfNeeded()
} else {
sseClient.restartConnectionIfNeeded(
SseInfo(
sseStorage.getPushIdentifier()!!,
userIds,
sseStorage.getSseOrigin()!!
)
)
)
}
}
}
}
Expand All @@ -131,6 +134,7 @@ class PushNotificationService(override val lifecycle: Lifecycle) : LifecycleJobS
}


@Suppress("DEPRECATION")
private fun removeForegroundNotification() {
Log.d(TAG, "removeForegroundNotification")
if (atLeastTiramisu()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import de.tutao.tutanota.data.AppDatabase
import de.tutao.tutanota.data.PushIdentifierKey
import de.tutao.tutanota.data.User
import de.tutao.tutanota.ipc.ExtendedNotificationMode
import kotlinx.coroutines.flow.Flow
import java.security.KeyStoreException
import java.security.UnrecoverableEntryException
import java.util.Date
Expand Down Expand Up @@ -54,7 +55,7 @@ class SseStorage(
return keyStoreFacade.decryptKey(userInfo.deviceEncPushIdentifierKey!!)
}

fun observeUsers(): LiveData<List<User>> {
fun observeUsers(): Flow<List<User>> {
return db.userInfoDao().observeUsers()
}

Expand Down

0 comments on commit 99b72cb

Please sign in to comment.