From 672fb21bdb6ef0744a584177d2b64acc51d3992e Mon Sep 17 00:00:00 2001 From: machiav3lli Date: Thu, 9 Nov 2023 01:31:10 +0100 Subject: [PATCH 1/5] Add: ObservedEffect --- .../backup/ui/compose/extensions.kt | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/app/src/main/java/com/machiav3lli/backup/ui/compose/extensions.kt b/app/src/main/java/com/machiav3lli/backup/ui/compose/extensions.kt index bed99bace1..9a00c08653 100644 --- a/app/src/main/java/com/machiav3lli/backup/ui/compose/extensions.kt +++ b/app/src/main/java/com/machiav3lli/backup/ui/compose/extensions.kt @@ -5,15 +5,20 @@ import androidx.compose.foundation.lazy.LazyListState import androidx.compose.foundation.text.selection.SelectionContainer import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.composed import androidx.compose.ui.draw.clip import androidx.compose.ui.layout.layout +import androidx.compose.ui.platform.LocalLifecycleOwner import androidx.compose.ui.unit.dp +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.repeatOnLifecycle import com.machiav3lli.backup.traceFlows import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted @@ -60,6 +65,26 @@ fun SelectionContainerX(modifier: Modifier = Modifier, content: @Composable () - //content() } +@Composable +fun ObservedEffect(flow: Flow, onChange: (T?) -> Unit) { + val lcOwner = LocalLifecycleOwner.current + LaunchedEffect(flow, lcOwner.lifecycle) { + lcOwner.repeatOnLifecycle(Lifecycle.State.STARTED) { + flow.collect(onChange) + } + } +} + +@Composable +fun ObservedEffect(onChange: () -> Unit) { + val lcOwner = LocalLifecycleOwner.current + LaunchedEffect(lcOwner.lifecycle) { + lcOwner.repeatOnLifecycle(Lifecycle.State.STARTED) { + onChange() + } + } +} + class MutableComposableSharedFlow( var initial: T, From 51897ca33df54c4f01c842147e64acd0cb669524 Mon Sep 17 00:00:00 2001 From: machiav3lli Date: Thu, 9 Nov 2023 02:18:21 +0100 Subject: [PATCH 2/5] Update: Separate permissions from prefs related utils --- .../backup/utils/PermissionUtils.kt | 315 ++++++++++++++++++ .../com/machiav3lli/backup/utils/PrefUtils.kt | 289 ---------------- 2 files changed, 315 insertions(+), 289 deletions(-) create mode 100644 app/src/main/java/com/machiav3lli/backup/utils/PermissionUtils.kt diff --git a/app/src/main/java/com/machiav3lli/backup/utils/PermissionUtils.kt b/app/src/main/java/com/machiav3lli/backup/utils/PermissionUtils.kt new file mode 100644 index 0000000000..64c87deb41 --- /dev/null +++ b/app/src/main/java/com/machiav3lli/backup/utils/PermissionUtils.kt @@ -0,0 +1,315 @@ +package com.machiav3lli.backup.utils + +import android.Manifest +import android.app.Activity +import android.app.AppOpsManager +import android.content.ActivityNotFoundException +import android.content.Context +import android.content.DialogInterface +import android.content.Intent +import android.content.pm.PackageManager +import android.net.Uri +import android.os.Build +import android.os.Environment +import android.os.PowerManager +import android.os.Process +import android.provider.Settings +import androidx.activity.result.ActivityResultLauncher +import androidx.appcompat.app.AppCompatActivity +import androidx.core.app.ActivityCompat +import com.machiav3lli.backup.BACKUP_DIRECTORY_INTENT +import com.machiav3lli.backup.OABX +import com.machiav3lli.backup.R +import com.machiav3lli.backup.handler.ShellHandler +import com.machiav3lli.backup.preferences.persist_ignoreBatteryOptimization +import com.topjohnwu.superuser.Shell + +// Getters + +fun Activity.checkRootAccess(showDialogOnError: Boolean = false): Boolean { + val isRooted = Shell.getShell().isRoot + if (!isRooted) { + if (showDialogOnError) + showFatalUiWarning(getString(R.string.noSu)) + return false + } + try { + ShellHandler.runAsRoot("id") + } catch (e: ShellHandler.ShellCommandFailedException) { + showFatalUiWarning(getString(R.string.noSu)) + return false + } + return true +} + +val Context.allPermissionsGranted: Boolean + get() { + val powerManager = getSystemService(AppCompatActivity.POWER_SERVICE) as PowerManager + return hasStoragePermissions && + isStorageDirSetAndOk && + checkSMSMMSPermission && + checkCallLogsPermission && + checkContactsPermission && + checkUsageStatsPermission && + postNotificationsPermission && + checkBatteryOptimization(powerManager) + } + +val Context.hasStoragePermissions: Boolean + get() = when { + OABX.minSDK(Build.VERSION_CODES.R) -> + Environment.isExternalStorageManager() + + else -> + checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE) == + PackageManager.PERMISSION_GRANTED + } + +val Context.isStorageDirSetAndOk: Boolean + get() { + return try { + val storageDirPath = backupDirConfigured + if (storageDirPath.isEmpty()) { + return false + } + //val storageDir = StorageFile.fromUri(this, Uri.parse(storageDirPath)) + //storageDir.exists() + getBackupRoot().exists() //TODO kind of similar, but throws an exception "root not accessible" in some cases + } catch (e: Throwable) { + false + } + } + +val Context.checkSMSMMSPermission: Boolean + get() { + if (!packageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) { + return true + } + if (!specialBackupsEnabled) { + return true + } + val appOps = (getSystemService(Context.APP_OPS_SERVICE) as AppOpsManager) + val mode = when { + OABX.minSDK(Build.VERSION_CODES.Q) -> + appOps.unsafeCheckOpNoThrow( + AppOpsManager.OPSTR_READ_SMS, + Process.myUid(), + packageName + ) + // Done this way because on (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) + // it always says that the permission is granted even though it is not + else -> AppOpsManager.MODE_DEFAULT + } + return if (mode == AppOpsManager.MODE_DEFAULT) { + (checkCallingOrSelfPermission(Manifest.permission.READ_SMS) == + PackageManager.PERMISSION_GRANTED && + checkCallingOrSelfPermission(Manifest.permission.SEND_SMS) == + PackageManager.PERMISSION_GRANTED && + checkCallingOrSelfPermission(Manifest.permission.RECEIVE_SMS) == + PackageManager.PERMISSION_GRANTED && + checkCallingOrSelfPermission(Manifest.permission.RECEIVE_MMS) == + PackageManager.PERMISSION_GRANTED && + checkCallingOrSelfPermission(Manifest.permission.RECEIVE_WAP_PUSH) == + PackageManager.PERMISSION_GRANTED) + } else { + mode == AppOpsManager.MODE_ALLOWED + } + } + + +val Context.checkCallLogsPermission: Boolean + get() { + if (!packageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) { + return true + } + if (!specialBackupsEnabled) { + return true + } + val appOps = (getSystemService(Context.APP_OPS_SERVICE) as AppOpsManager) + val mode = when { + OABX.minSDK(Build.VERSION_CODES.Q) -> + appOps.unsafeCheckOpNoThrow( + AppOpsManager.OPSTR_READ_CALL_LOG, + Process.myUid(), + packageName + ) + // Done this way because on (Build.VERSION.SDK_INT <= Build.VERSION_CODES.Q) + // it always says that the permission is granted even though it is not + else -> AppOpsManager.MODE_DEFAULT + } + return if (mode == AppOpsManager.MODE_DEFAULT) { + (checkCallingOrSelfPermission(Manifest.permission.READ_CALL_LOG) == + PackageManager.PERMISSION_GRANTED && + checkCallingOrSelfPermission(Manifest.permission.WRITE_CALL_LOG) == + PackageManager.PERMISSION_GRANTED) + } else { + mode == AppOpsManager.MODE_ALLOWED + } + } + + +val Context.checkContactsPermission: Boolean + get() { + if (!packageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) { + return true + } + if (!specialBackupsEnabled) { + return true + } + val appOps = (getSystemService(Context.APP_OPS_SERVICE) as AppOpsManager) + val mode = when { + OABX.minSDK(Build.VERSION_CODES.Q) -> + appOps.unsafeCheckOpNoThrow( + AppOpsManager.OPSTR_READ_CONTACTS, + Process.myUid(), + packageName + ) + // Done this way because on (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) + // it always says that the permission is granted even though it is not + else -> AppOpsManager.MODE_DEFAULT + } + return if (mode == AppOpsManager.MODE_DEFAULT) { + checkCallingOrSelfPermission(Manifest.permission.READ_CONTACTS) == + PackageManager.PERMISSION_GRANTED + } else { + mode == AppOpsManager.MODE_ALLOWED + } + } + +val Context.checkUsageStatsPermission: Boolean + get() { + val appOps = (getSystemService(Context.APP_OPS_SERVICE) as AppOpsManager) + val mode = when { + OABX.minSDK(Build.VERSION_CODES.Q) -> + appOps.unsafeCheckOpNoThrow( + AppOpsManager.OPSTR_GET_USAGE_STATS, + Process.myUid(), + packageName + ) + + else -> + appOps.checkOpNoThrow( //TODO 'checkOpNoThrow(String, Int, String): Int' is deprecated. Deprecated in Java. @machiav3lli not replaceable without increasing minSDK as the two functions have different minSDK + AppOpsManager.OPSTR_GET_USAGE_STATS, + Process.myUid(), + packageName + ) + } + return if (mode == AppOpsManager.MODE_DEFAULT) { + checkCallingOrSelfPermission(Manifest.permission.PACKAGE_USAGE_STATS) == + PackageManager.PERMISSION_GRANTED + } else { + mode == AppOpsManager.MODE_ALLOWED + } + } + +val Context.postNotificationsPermission: Boolean + get() = if (OABX.minSDK(Build.VERSION_CODES.TIRAMISU)) { + checkCallingOrSelfPermission( + Manifest.permission.POST_NOTIFICATIONS + ) == PackageManager.PERMISSION_GRANTED + } else true + +fun Context.checkBatteryOptimization(powerManager: PowerManager) + : Boolean = persist_ignoreBatteryOptimization.value + || powerManager.isIgnoringBatteryOptimizations(packageName) + +// Actions + +fun Activity.requireStorageLocation(activityResultLauncher: ActivityResultLauncher) { + val intent = BACKUP_DIRECTORY_INTENT + try { + activityResultLauncher.launch(intent) + } catch (e: ActivityNotFoundException) { + showWarning( + getString(R.string.no_file_manager_title), + getString(R.string.no_file_manager_message) + ) { _: DialogInterface?, _: Int -> + finishAffinity() + } + } +} + + +fun Activity.getStoragePermission() { + when { + OABX.minSDK(Build.VERSION_CODES.R) -> { + val intent = Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION) + intent.data = Uri.parse("package:$packageName") + startActivity(intent) + } + + else -> { + requireWriteStoragePermission() + requireReadStoragePermission() + } + } +} + +private fun Activity.requireReadStoragePermission() { + if (checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE) != + PackageManager.PERMISSION_GRANTED + ) + ActivityCompat.requestPermissions( + this, + arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE), READ_PERMISSION + ) +} + +private fun Activity.requireWriteStoragePermission() { + if (checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) != + PackageManager.PERMISSION_GRANTED + ) + ActivityCompat.requestPermissions( + this, + arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE), WRITE_PERMISSION + ) +} + +fun Activity.requireSMSMMSPermission() { + val smsmmsPermissionList = arrayOf( + Manifest.permission.READ_SMS, + Manifest.permission.SEND_SMS, + Manifest.permission.RECEIVE_SMS, + Manifest.permission.RECEIVE_MMS, + Manifest.permission.RECEIVE_WAP_PUSH + ) + if ( + checkSelfPermission(Manifest.permission.READ_SMS) != + PackageManager.PERMISSION_GRANTED || + checkSelfPermission(Manifest.permission.SEND_SMS) != + PackageManager.PERMISSION_GRANTED || + checkSelfPermission(Manifest.permission.RECEIVE_SMS) != + PackageManager.PERMISSION_GRANTED || + checkSelfPermission(Manifest.permission.RECEIVE_MMS) != + PackageManager.PERMISSION_GRANTED || + checkSelfPermission(Manifest.permission.RECEIVE_WAP_PUSH) != + PackageManager.PERMISSION_GRANTED + ) + ActivityCompat.requestPermissions(this, smsmmsPermissionList, SMS_PERMISSION) +} + +fun Activity.requireCallLogsPermission() { + val callLogPermissionList = arrayOf( + Manifest.permission.READ_CALL_LOG, + Manifest.permission.WRITE_CALL_LOG + ) + if ( + checkSelfPermission(Manifest.permission.READ_CALL_LOG) != + PackageManager.PERMISSION_GRANTED || + checkSelfPermission(Manifest.permission.WRITE_CALL_LOG) != + PackageManager.PERMISSION_GRANTED + ) + ActivityCompat.requestPermissions(this, callLogPermissionList, CALLLOGS_PERMISSION) +} + +fun Activity.requireContactsPermission() { + if ( + checkSelfPermission(Manifest.permission.READ_CONTACTS) != + PackageManager.PERMISSION_GRANTED + ) + ActivityCompat.requestPermissions( + this, + arrayOf(Manifest.permission.READ_CONTACTS), + CONTACTS_PERMISSION + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/machiav3lli/backup/utils/PrefUtils.kt b/app/src/main/java/com/machiav3lli/backup/utils/PrefUtils.kt index 5a5db36c4b..d0d524f183 100644 --- a/app/src/main/java/com/machiav3lli/backup/utils/PrefUtils.kt +++ b/app/src/main/java/com/machiav3lli/backup/utils/PrefUtils.kt @@ -17,38 +17,21 @@ */ package com.machiav3lli.backup.utils -import android.Manifest -import android.app.Activity -import android.app.AppOpsManager import android.app.KeyguardManager -import android.content.ActivityNotFoundException import android.content.Context -import android.content.DialogInterface -import android.content.Intent import android.content.SharedPreferences -import android.content.pm.PackageManager import android.net.Uri -import android.os.Build -import android.os.Environment -import android.os.PowerManager -import android.os.Process import android.provider.DocumentsContract -import android.provider.Settings -import androidx.activity.result.ActivityResultLauncher import androidx.biometric.BiometricManager -import androidx.core.app.ActivityCompat import androidx.preference.PreferenceManager import androidx.security.crypto.EncryptedSharedPreferences import androidx.security.crypto.MasterKey -import com.machiav3lli.backup.BACKUP_DIRECTORY_INTENT import com.machiav3lli.backup.BuildConfig import com.machiav3lli.backup.OABX import com.machiav3lli.backup.PREFS_LANGUAGES_SYSTEM import com.machiav3lli.backup.PREFS_SHARED_PRIVATE import com.machiav3lli.backup.R -import com.machiav3lli.backup.handler.ShellHandler import com.machiav3lli.backup.items.SortFilterModel -import com.machiav3lli.backup.preferences.persist_ignoreBatteryOptimization import com.machiav3lli.backup.preferences.persist_salt import com.machiav3lli.backup.preferences.persist_sortFilter import com.machiav3lli.backup.preferences.persist_specialFilters @@ -75,7 +58,6 @@ import com.machiav3lli.backup.preferences.pref_restoreExternalData import com.machiav3lli.backup.preferences.pref_restoreMediaData import com.machiav3lli.backup.preferences.pref_restoreObbData import com.machiav3lli.backup.utils.FileUtils.invalidateBackupLocation -import com.topjohnwu.superuser.Shell import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch @@ -159,283 +141,12 @@ fun setBackupDir(value: Uri): String { return fullUri.toString() } -val Context.isStorageDirSetAndOk: Boolean - get() { - return try { - val storageDirPath = backupDirConfigured - if (storageDirPath.isEmpty()) { - return false - } - //val storageDir = StorageFile.fromUri(this, Uri.parse(storageDirPath)) - //storageDir.exists() - getBackupRoot().exists() //TODO kind of similar, but throws an exception "root not accessible" in some cases - } catch (e: Throwable) { - false - } - } - -fun Activity.requireStorageLocation(activityResultLauncher: ActivityResultLauncher) { - val intent = BACKUP_DIRECTORY_INTENT - try { - activityResultLauncher.launch(intent) - } catch (e: ActivityNotFoundException) { - showWarning( - getString(R.string.no_file_manager_title), - getString(R.string.no_file_manager_message) - ) { _: DialogInterface?, _: Int -> - finishAffinity() - } - } -} - -val Context.hasStoragePermissions: Boolean - get() = when { - OABX.minSDK(Build.VERSION_CODES.R) -> - Environment.isExternalStorageManager() - - else -> - checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE) == - PackageManager.PERMISSION_GRANTED - } - -fun Activity.getStoragePermission() { - when { - OABX.minSDK(Build.VERSION_CODES.R) -> { - val intent = Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION) - intent.data = Uri.parse("package:$packageName") - startActivity(intent) - } - - else -> { - requireWriteStoragePermission() - requireReadStoragePermission() - } - } -} - -fun Activity.checkRootAccess(showDialogOnError: Boolean = false): Boolean { - val isRooted = Shell.getShell().isRoot - if (!isRooted) { - if (showDialogOnError) - showFatalUiWarning(getString(R.string.noSu)) - return false - } - try { - ShellHandler.runAsRoot("id") - } catch (e: ShellHandler.ShellCommandFailedException) { - showFatalUiWarning(getString(R.string.noSu)) - return false - } - return true -} - -private fun Activity.requireReadStoragePermission() { - if (checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE) != - PackageManager.PERMISSION_GRANTED - ) - ActivityCompat.requestPermissions( - this, - arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE), READ_PERMISSION - ) -} - -private fun Activity.requireWriteStoragePermission() { - if (checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) != - PackageManager.PERMISSION_GRANTED - ) - ActivityCompat.requestPermissions( - this, - arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE), WRITE_PERMISSION - ) -} - val Context.canAccessExternalStorage: Boolean get() { val externalStorage = FileUtils.getExternalStorageDirectory(this) return externalStorage?.let { it.canRead() && it.canWrite() } ?: false } -fun Activity.requireSMSMMSPermission() { - val smsmmsPermissionList = arrayOf( - Manifest.permission.READ_SMS, - Manifest.permission.SEND_SMS, - Manifest.permission.RECEIVE_SMS, - Manifest.permission.RECEIVE_MMS, - Manifest.permission.RECEIVE_WAP_PUSH - ) - if ( - checkSelfPermission(Manifest.permission.READ_SMS) != - PackageManager.PERMISSION_GRANTED || - checkSelfPermission(Manifest.permission.SEND_SMS) != - PackageManager.PERMISSION_GRANTED || - checkSelfPermission(Manifest.permission.RECEIVE_SMS) != - PackageManager.PERMISSION_GRANTED || - checkSelfPermission(Manifest.permission.RECEIVE_MMS) != - PackageManager.PERMISSION_GRANTED || - checkSelfPermission(Manifest.permission.RECEIVE_WAP_PUSH) != - PackageManager.PERMISSION_GRANTED - ) - ActivityCompat.requestPermissions(this, smsmmsPermissionList, SMS_PERMISSION) -} - -val Context.checkSMSMMSPermission: Boolean - get() { - if (!packageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) { - return true - } - if (!specialBackupsEnabled) { - return true - } - val appOps = (getSystemService(Context.APP_OPS_SERVICE) as AppOpsManager) - val mode = when { - OABX.minSDK(Build.VERSION_CODES.Q) -> - appOps.unsafeCheckOpNoThrow( - AppOpsManager.OPSTR_READ_SMS, - Process.myUid(), - packageName - ) - // Done this way because on (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) - // it always says that the permission is granted even though it is not - else -> AppOpsManager.MODE_DEFAULT - } - return if (mode == AppOpsManager.MODE_DEFAULT) { - (checkCallingOrSelfPermission(Manifest.permission.READ_SMS) == - PackageManager.PERMISSION_GRANTED && - checkCallingOrSelfPermission(Manifest.permission.SEND_SMS) == - PackageManager.PERMISSION_GRANTED && - checkCallingOrSelfPermission(Manifest.permission.RECEIVE_SMS) == - PackageManager.PERMISSION_GRANTED && - checkCallingOrSelfPermission(Manifest.permission.RECEIVE_MMS) == - PackageManager.PERMISSION_GRANTED && - checkCallingOrSelfPermission(Manifest.permission.RECEIVE_WAP_PUSH) == - PackageManager.PERMISSION_GRANTED) - } else { - mode == AppOpsManager.MODE_ALLOWED - } - } - -fun Activity.requireCallLogsPermission() { - val callLogPermissionList = arrayOf( - Manifest.permission.READ_CALL_LOG, - Manifest.permission.WRITE_CALL_LOG - ) - if ( - checkSelfPermission(Manifest.permission.READ_CALL_LOG) != - PackageManager.PERMISSION_GRANTED || - checkSelfPermission(Manifest.permission.WRITE_CALL_LOG) != - PackageManager.PERMISSION_GRANTED - ) - ActivityCompat.requestPermissions(this, callLogPermissionList, CALLLOGS_PERMISSION) -} - -val Context.checkCallLogsPermission: Boolean - get() { - if (!packageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) { - return true - } - if (!specialBackupsEnabled) { - return true - } - val appOps = (getSystemService(Context.APP_OPS_SERVICE) as AppOpsManager) - val mode = when { - OABX.minSDK(Build.VERSION_CODES.Q) -> - appOps.unsafeCheckOpNoThrow( - AppOpsManager.OPSTR_READ_CALL_LOG, - Process.myUid(), - packageName - ) - // Done this way because on (Build.VERSION.SDK_INT <= Build.VERSION_CODES.Q) - // it always says that the permission is granted even though it is not - else -> AppOpsManager.MODE_DEFAULT - } - return if (mode == AppOpsManager.MODE_DEFAULT) { - (checkCallingOrSelfPermission(Manifest.permission.READ_CALL_LOG) == - PackageManager.PERMISSION_GRANTED && - checkCallingOrSelfPermission(Manifest.permission.WRITE_CALL_LOG) == - PackageManager.PERMISSION_GRANTED) - } else { - mode == AppOpsManager.MODE_ALLOWED - } - } - -fun Activity.requireContactsPermission() { - if ( - checkSelfPermission(Manifest.permission.READ_CONTACTS) != - PackageManager.PERMISSION_GRANTED - ) - ActivityCompat.requestPermissions( - this, - arrayOf(Manifest.permission.READ_CONTACTS), - CONTACTS_PERMISSION - ) -} - -val Context.checkContactsPermission: Boolean - get() { - if (!packageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) { - return true - } - if (!specialBackupsEnabled) { - return true - } - val appOps = (getSystemService(Context.APP_OPS_SERVICE) as AppOpsManager) - val mode = when { - OABX.minSDK(Build.VERSION_CODES.Q) -> - appOps.unsafeCheckOpNoThrow( - AppOpsManager.OPSTR_READ_CONTACTS, - Process.myUid(), - packageName - ) - // Done this way because on (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) - // it always says that the permission is granted even though it is not - else -> AppOpsManager.MODE_DEFAULT - } - return if (mode == AppOpsManager.MODE_DEFAULT) { - checkCallingOrSelfPermission(Manifest.permission.READ_CONTACTS) == - PackageManager.PERMISSION_GRANTED - } else { - mode == AppOpsManager.MODE_ALLOWED - } - } - -val Context.postNotificationsPermission: Boolean - get() = if (OABX.minSDK(Build.VERSION_CODES.TIRAMISU)) { - checkCallingOrSelfPermission( - Manifest.permission.POST_NOTIFICATIONS - ) == PackageManager.PERMISSION_GRANTED - } else true - -val Context.checkUsageStatsPermission: Boolean - get() { - val appOps = (getSystemService(Context.APP_OPS_SERVICE) as AppOpsManager) - val mode = when { - OABX.minSDK(Build.VERSION_CODES.Q) -> - appOps.unsafeCheckOpNoThrow( - AppOpsManager.OPSTR_GET_USAGE_STATS, - Process.myUid(), - packageName - ) - - else -> - appOps.checkOpNoThrow( //TODO 'checkOpNoThrow(String, Int, String): Int' is deprecated. Deprecated in Java. @machiav3lli not replaceable without increasing minSDK as the two functions have different minSDK - AppOpsManager.OPSTR_GET_USAGE_STATS, - Process.myUid(), - packageName - ) - } - return if (mode == AppOpsManager.MODE_DEFAULT) { - checkCallingOrSelfPermission(Manifest.permission.PACKAGE_USAGE_STATS) == - PackageManager.PERMISSION_GRANTED - } else { - mode == AppOpsManager.MODE_ALLOWED - } - } - -fun Context.checkBatteryOptimization(powerManager: PowerManager) - : Boolean = persist_ignoreBatteryOptimization.value - || powerManager.isIgnoringBatteryOptimizations(packageName) - - val isBackupDeviceProtectedData: Boolean get() = pref_backupDeviceProtectedData.value From c932aa52f700988ae7c09ed2e4eadcf7a91bbfa6 Mon Sep 17 00:00:00 2001 From: machiav3lli Date: Thu, 9 Nov 2023 02:24:45 +0100 Subject: [PATCH 3/5] Add: ClearBackStack function --- .../main/java/com/machiav3lli/backup/ui/navigation/Host.kt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/app/src/main/java/com/machiav3lli/backup/ui/navigation/Host.kt b/app/src/main/java/com/machiav3lli/backup/ui/navigation/Host.kt index 13762b044d..5a36ee5659 100644 --- a/app/src/main/java/com/machiav3lli/backup/ui/navigation/Host.kt +++ b/app/src/main/java/com/machiav3lli/backup/ui/navigation/Host.kt @@ -111,3 +111,9 @@ fun NavGraphBuilder.fadeComposable( composable(it) } } + +fun NavHostController.clearBackStack() { + while (this.currentBackStack.value.isNotEmpty()) { + popBackStack() + } +} From 440e5536ceaf0205ad4454850c7db0580d82ce73 Mon Sep 17 00:00:00 2001 From: machiav3lli Date: Thu, 9 Nov 2023 02:35:19 +0100 Subject: [PATCH 4/5] Update: Make Lock page safe on clicking back --- app/src/main/java/com/machiav3lli/backup/pages/SplashPage.kt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/src/main/java/com/machiav3lli/backup/pages/SplashPage.kt b/app/src/main/java/com/machiav3lli/backup/pages/SplashPage.kt index 6e9f6b35d1..a791daecf6 100644 --- a/app/src/main/java/com/machiav3lli/backup/pages/SplashPage.kt +++ b/app/src/main/java/com/machiav3lli/backup/pages/SplashPage.kt @@ -2,6 +2,7 @@ package com.machiav3lli.backup.pages import android.annotation.SuppressLint import android.app.Activity +import androidx.activity.compose.BackHandler import androidx.compose.foundation.Image import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box @@ -148,6 +149,9 @@ fun LockPage(launchMain: () -> Unit) { } } ) { + BackHandler { + OABX.main?.finishAffinity() + } Box(modifier = Modifier.fillMaxSize()) {} } } From 40d7e333367eeef93224871e53d0617480bffcb7 Mon Sep 17 00:00:00 2001 From: machiav3lli Date: Thu, 9 Nov 2023 02:41:34 +0100 Subject: [PATCH 5/5] Fix: Navigation bar item's ripple --- app/src/main/java/com/machiav3lli/backup/ui/navigation/Pager.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/main/java/com/machiav3lli/backup/ui/navigation/Pager.kt b/app/src/main/java/com/machiav3lli/backup/ui/navigation/Pager.kt index ce6c3d61db..5918272607 100644 --- a/app/src/main/java/com/machiav3lli/backup/ui/navigation/Pager.kt +++ b/app/src/main/java/com/machiav3lli/backup/ui/navigation/Pager.kt @@ -29,6 +29,7 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.res.stringResource @@ -166,6 +167,7 @@ fun RowScope.NavBarItem( Row( modifier = modifier .padding(vertical = 8.dp) + .clip(MaterialTheme.shapes.extraLarge) .clickable { onClick() } .background( background,