diff --git a/.idea/runConfigurations/Instrumentation_tests__app.xml b/.idea/runConfigurations/Instrumentation_tests__app.xml
index b7b22097..4124cb1d 100644
--- a/.idea/runConfigurations/Instrumentation_tests__app.xml
+++ b/.idea/runConfigurations/Instrumentation_tests__app.xml
@@ -1,6 +1,6 @@
-
+
@@ -8,10 +8,12 @@
+
+
+
-
-
+
@@ -40,7 +42,7 @@
-
+
@@ -48,4 +50,4 @@
-
+
\ No newline at end of file
diff --git a/Android.bp b/Android.bp
index ba382399..04c3cc12 100644
--- a/Android.bp
+++ b/Android.bp
@@ -26,12 +26,16 @@ android_app {
static_libs: [
"kotlin-stdlib-jdk8",
"androidx.core_core-ktx",
+ "androidx.fragment_fragment-ktx",
+ "androidx.activity_activity-ktx",
"androidx.preference_preference",
"androidx.documentfile_documentfile",
"androidx.lifecycle_lifecycle-viewmodel-ktx",
"androidx.lifecycle_lifecycle-livedata-ktx",
"androidx-constraintlayout_constraintlayout",
"com.google.android.material_material",
+ "kotlinx-coroutines-android",
+ "kotlinx-coroutines-core",
// storage backup lib
"seedvault-lib-storage",
// koin
@@ -48,8 +52,9 @@ android_app {
privileged: true,
required: [
"LocalContactsBackup",
- "privapp_whitelist_com.stevesoltys.backup",
- "com.stevesoltys.backup_whitelist"
+ "com.stevesoltys.backup_allowlist",
+ "com.stevesoltys.backup_default-permissions",
+ "com.stevesoltys.backup_privapp_allowlist"
],
optimize: {
enabled: false,
@@ -57,17 +62,25 @@ android_app {
}
prebuilt_etc {
- name: "privapp_whitelist_com.stevesoltys.backup",
+ name: "com.stevesoltys.backup_allowlist",
+ system_ext_specific: true,
+ sub_dir: "sysconfig",
+ src: "allowlist_com.stevesoltys.seedvault.xml",
+ filename_from_src: true,
+}
+
+prebuilt_etc {
+ name: "com.stevesoltys.backup_default-permissions",
+ system_ext_specific: true,
+ sub_dir: "default-permissions",
+ src: "default-permissions_com.stevesoltys.seedvault.xml",
+ filename_from_src: true,
+}
+
+prebuilt_etc {
+ name: "com.stevesoltys.backup_privapp_allowlist",
system_ext_specific: true,
sub_dir: "permissions",
src: "permissions_com.stevesoltys.seedvault.xml",
filename_from_src: true,
}
-
-prebuilt_etc {
- name: "com.stevesoltys.backup_whitelist",
- system_ext_specific: true,
- sub_dir: "sysconfig",
- src: "whitelist_com.stevesoltys.seedvault.xml",
- filename_from_src: true,
-}
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 58b41811..c08347fa 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,9 @@
+## [13-3.1] - 2022-09-01
+* Initial release for Android 13
+* Don't attempt to restore app that is used as a backup location (e.g. Nextcloud),
+ because can cause restore to abort early
+* Upgrade several libraries
+
## [12-3.0] - 2021-10-13
* Initial release for Android 12
* Use the same (faster and more secure) crypto that storage backups use,
diff --git a/README.md b/README.md
index 41ccd2e9..1f53e0d9 100644
--- a/README.md
+++ b/README.md
@@ -19,10 +19,19 @@ If you are having an issue/question, please look at our [FAQ](../../wiki/FAQ).
## Requirements
-- Android 12
+SeedVault is developed alongwith AOSP releases
+
+We update it every time Google releases a new Android version, make any changes required for basic functionality, and any improvements possible through API changes in the OS.
+
+This means that for ROMs using SeedVault it's recommended to use the same branch as your android version
+
+- This current branch `android13` is meant for usage with Android 13
+- This is indicated by the version name starting with `13`, and the version code starting with `33` - the Android 13 API version
For older versions of Android, check out [the branches](https://github.com/seedvault-app/seedvault/branches).
+Trying to use an older branch on a newer version may lead to issues and is not something we can support.
+
## Getting Started
- Check out [the wiki](https://github.com/seedvault-app/seedvault/wiki) for information on building the application with
AOSP.
@@ -44,6 +53,7 @@ It uses the same internal APIs as `adb backup` which is deprecated and thus need
* `android.permission.MANAGE_DOCUMENTS` to retrieve the available storage roots (optional) for better UX.
* `android.permission.USE_BIOMETRIC` to authenticate saving a new recovery code
* `android.permission.INTERACT_ACROSS_USERS_FULL` to use storage roots in other users (optional).
+* `android.permission.POST_NOTIFICATIONS` to inform users about backup status and errors.
## Contributing
Bug reports and pull requests are welcome on GitHub at https://github.com/seedvault-app/seedvault.
diff --git a/whitelist_com.stevesoltys.seedvault.xml b/allowlist_com.stevesoltys.seedvault.xml
similarity index 100%
rename from whitelist_com.stevesoltys.seedvault.xml
rename to allowlist_com.stevesoltys.seedvault.xml
diff --git a/app/build.gradle b/app/build.gradle
index d4134c83..675b1dd0 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -18,7 +18,7 @@ android {
buildToolsVersion rootProject.ext.buildToolsVersion
defaultConfig {
- minSdkVersion 29 // leave at 29 for robolectric tests
+ minSdkVersion 32 // leave at 32 for robolectric tests
targetSdkVersion rootProject.ext.targetSdkVersion
versionNameSuffix "-$gitDescribe"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
@@ -38,12 +38,12 @@ android {
abortOnError true
}
compileOptions {
- targetCompatibility 1.8
- sourceCompatibility 1.8
+ sourceCompatibility = JavaVersion.VERSION_11
+ targetCompatibility = JavaVersion.VERSION_11
}
kotlinOptions {
- jvmTarget = JavaVersion.VERSION_1_8.toString()
- languageVersion = "1.4"
+ jvmTarget = JavaVersion.VERSION_11.toString()
+ languageVersion = "1.6"
}
testOptions {
unitTests.all {
@@ -122,13 +122,13 @@ dependencies {
* You can copy these libraries from ~/.gradle/caches/modules-2/files-2.1
*/
// later versions than 2.1.1 require newer kotlin version
-// implementation "io.insert-koin:koin-core-jvm:3.1.2"
-// implementation "io.insert-koin:koin-android:3.1.2"
+// implementation "io.insert-koin:koin-core-jvm:3.2.0"
+// implementation "io.insert-koin:koin-android:3.2.0"
implementation fileTree(include: ['*.jar'], dir: "${rootProject.rootDir}/libs/koin-android")
implementation fileTree(include: ['*.aar'], dir: "${rootProject.rootDir}/libs/koin-android")
-// implementation "cash.z.ecc.android:kotlin-bip39:1.0.2"
- implementation fileTree(include: ['kotlin-bip39-1.0.2.jar'], dir: "${rootProject.rootDir}/libs")
+// implementation "cash.z.ecc.android:kotlin-bip39:1.0.4"
+ implementation fileTree(include: ['kotlin-bip39-jvm-1.0.4.jar'], dir: "${rootProject.rootDir}/libs")
/**
* Test Dependencies (do not concern the AOSP build)
@@ -138,10 +138,11 @@ dependencies {
// anything less than 'implementation' fails tests run with gradlew
testImplementation rootProject.ext.aosp_libs
testImplementation 'androidx.test.ext:junit:1.1.3'
- testImplementation('org.robolectric:robolectric:4.3.1') { // 4.4 has issue with non-idle Looper
+ testImplementation('org.robolectric:robolectric:4.8.1') {
// https://github.com/robolectric/robolectric/issues/5245
exclude group: "com.google.auto.service", module: "auto-service"
}
+ testImplementation 'org.hamcrest:hamcrest:2.2'
testImplementation "org.junit.jupiter:junit-jupiter-api:$junit5_version"
testImplementation "org.junit.jupiter:junit-jupiter-params:$junit5_version"
testImplementation "io.mockk:mockk:$mockk_version"
@@ -159,9 +160,6 @@ apply from: "${rootProject.rootDir}/gradle/ktlint.gradle"
gradle.projectsEvaluated {
tasks.withType(JavaCompile) {
- if (JavaVersion.current() >= JavaVersion.VERSION_1_9) {
- options.compilerArgs.addAll(['--release', '8'])
- }
options.compilerArgs.add('-Xbootclasspath/p:app/libs/android.jar:app/libs/libcore.jar')
}
}
diff --git a/app/libs/android.jar b/app/libs/android.jar
index 48002e33..5e9d53d4 100644
Binary files a/app/libs/android.jar and b/app/libs/android.jar differ
diff --git a/app/libs/libcore.jar b/app/libs/libcore.jar
index 94d798d2..2c0049b7 100644
Binary files a/app/libs/libcore.jar and b/app/libs/libcore.jar differ
diff --git a/app/src/debug/AndroidManifest.xml b/app/src/debug/AndroidManifest.xml
index 09836263..0888d2f4 100644
--- a/app/src/debug/AndroidManifest.xml
+++ b/app/src/debug/AndroidManifest.xml
@@ -7,13 +7,11 @@
+ tools:remove="android:permission" />
+ tools:remove="android:permission" />
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 8fcb13f7..12b9e1d9 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -2,8 +2,8 @@
+ android:versionCode="33000301"
+ android:versionName="13-3.1">
-
+
+
+
+
@@ -50,6 +54,16 @@
+
+
+
+
+
+
-
-
-
{
appStatus.visibility = INVISIBLE
progressBar.visibility = VISIBLE
- if (SDK_INT >= 30) {
- progressBar.stateDescription =
- context.getString(R.string.restore_app_status_installing)
- }
+ progressBar.stateDescription =
+ context.getString(R.string.restore_app_status_installing)
}
SUCCEEDED -> {
appStatus.setImageResource(R.drawable.ic_check_green)
diff --git a/app/src/main/java/com/stevesoltys/seedvault/settings/AppListRetriever.kt b/app/src/main/java/com/stevesoltys/seedvault/settings/AppListRetriever.kt
index cae5c319..29cb923c 100644
--- a/app/src/main/java/com/stevesoltys/seedvault/settings/AppListRetriever.kt
+++ b/app/src/main/java/com/stevesoltys/seedvault/settings/AppListRetriever.kt
@@ -106,7 +106,7 @@ internal class AppListRetriever(
time = time,
status = status
)
- }.sortedBy { it.name.toLowerCase(locale) }
+ }.sortedBy { it.name.lowercase(locale) }
}
private fun getNotAllowedApps(): List {
@@ -120,7 +120,7 @@ internal class AppListRetriever(
time = 0,
status = FAILED_NOT_ALLOWED
)
- }.sortedBy { it.name.toLowerCase(locale) }
+ }.sortedBy { it.name.lowercase(locale) }
}
private fun getIcon(packageName: String): Drawable = when (packageName) {
diff --git a/app/src/main/java/com/stevesoltys/seedvault/settings/ExpertSettingsFragment.kt b/app/src/main/java/com/stevesoltys/seedvault/settings/ExpertSettingsFragment.kt
index 05164d78..f4a2effd 100644
--- a/app/src/main/java/com/stevesoltys/seedvault/settings/ExpertSettingsFragment.kt
+++ b/app/src/main/java/com/stevesoltys/seedvault/settings/ExpertSettingsFragment.kt
@@ -1,15 +1,35 @@
package com.stevesoltys.seedvault.settings
import android.os.Bundle
+import androidx.activity.result.contract.ActivityResultContracts.CreateDocument
+import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat
import com.stevesoltys.seedvault.R
import com.stevesoltys.seedvault.permitDiskReads
+import com.stevesoltys.seedvault.transport.backup.PackageService
+import org.koin.android.ext.android.inject
+import org.koin.androidx.viewmodel.ext.android.sharedViewModel
class ExpertSettingsFragment : PreferenceFragmentCompat() {
+
+ private val viewModel: SettingsViewModel by sharedViewModel()
+ private val packageService: PackageService by inject()
+ // TODO set mimeType when upgrading androidx lib
+ private val createFileLauncher = registerForActivityResult(CreateDocument()) { uri ->
+ viewModel.onLogcatUriReceived(uri)
+ }
+
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
permitDiskReads {
setPreferencesFromResource(R.xml.settings_expert, rootKey)
}
+ findPreference("logcat")?.setOnPreferenceClickListener {
+ val versionName = packageService.getVersionName(requireContext().packageName) ?: "ver"
+ val timestamp = System.currentTimeMillis()
+ val name = "seedvault-$versionName-$timestamp.txt"
+ createFileLauncher.launch(name)
+ true
+ }
}
override fun onStart() {
diff --git a/app/src/main/java/com/stevesoltys/seedvault/settings/SettingsFragment.kt b/app/src/main/java/com/stevesoltys/seedvault/settings/SettingsFragment.kt
index ff26c3a8..227e84d4 100644
--- a/app/src/main/java/com/stevesoltys/seedvault/settings/SettingsFragment.kt
+++ b/app/src/main/java/com/stevesoltys/seedvault/settings/SettingsFragment.kt
@@ -105,7 +105,7 @@ class SettingsFragment : PreferenceFragmentCompat() {
dialog.dismiss()
}
.setNegativeButton(R.string.settings_backup_apk_dialog_disable) { dialog, _ ->
- apkBackup.isChecked = enable
+ apkBackup.isChecked = false
dialog.dismiss()
}
.show()
@@ -130,14 +130,14 @@ class SettingsFragment : PreferenceFragmentCompat() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
- viewModel.lastBackupTime.observe(viewLifecycleOwner, { time ->
+ viewModel.lastBackupTime.observe(viewLifecycleOwner) { time ->
setAppBackupStatusSummary(time)
- })
+ }
val backupFiles: Preference = findPreference("backup_files")!!
- viewModel.filesSummary.observe(viewLifecycleOwner, { summary ->
+ viewModel.filesSummary.observe(viewLifecycleOwner) { summary ->
backupFiles.summary = summary
- })
+ }
}
override fun onStart() {
@@ -160,10 +160,10 @@ class SettingsFragment : PreferenceFragmentCompat() {
if (resources.getBoolean(R.bool.show_restore_in_settings)) {
menuRestore?.isVisible = true
}
- viewModel.backupPossible.observe(viewLifecycleOwner, { possible ->
+ viewModel.backupPossible.observe(viewLifecycleOwner) { possible ->
menuBackupNow?.isEnabled = possible
menuRestore?.isEnabled = possible
- })
+ }
}
override fun onOptionsItemSelected(item: MenuItem): Boolean = when (item.itemId) {
diff --git a/app/src/main/java/com/stevesoltys/seedvault/settings/SettingsViewModel.kt b/app/src/main/java/com/stevesoltys/seedvault/settings/SettingsViewModel.kt
index 8da4f9fc..3c190651 100644
--- a/app/src/main/java/com/stevesoltys/seedvault/settings/SettingsViewModel.kt
+++ b/app/src/main/java/com/stevesoltys/seedvault/settings/SettingsViewModel.kt
@@ -11,6 +11,7 @@ import android.net.Network
import android.net.NetworkCapabilities
import android.net.NetworkRequest
import android.net.Uri
+import android.os.Process.myUid
import android.provider.Settings
import android.util.Log
import android.widget.Toast
@@ -35,8 +36,11 @@ import com.stevesoltys.seedvault.ui.RequireProvisioningViewModel
import com.stevesoltys.seedvault.ui.notification.BackupNotificationManager
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
import org.calyxos.backup.storage.api.StorageBackup
import org.calyxos.backup.storage.backup.BackupJobService
+import java.io.IOException
+import java.lang.Runtime.getRuntime
import java.util.concurrent.TimeUnit.HOURS
private const val TAG = "SettingsViewModel"
@@ -193,9 +197,9 @@ internal class SettingsViewModel(
@UiThread
fun loadFilesSummary() = viewModelScope.launch {
val uriSummary = storageBackup.getUriSummaryString()
- _filesSummary.value = if (uriSummary.isEmpty()) {
+ _filesSummary.value = uriSummary.ifEmpty {
app.getString(R.string.settings_backup_files_summary)
- } else uriSummary
+ }
}
/**
@@ -233,4 +237,28 @@ internal class SettingsViewModel(
BackupJobService.cancelJob(app)
}
+ fun onLogcatUriReceived(uri: Uri?) = viewModelScope.launch(Dispatchers.IO) {
+ if (uri == null) {
+ onLogcatError()
+ return@launch
+ }
+ // 1000 is system uid, needed to get backup logs from the OS code.
+ val command = "logcat -d --uid=1000,${myUid()} *:V"
+ try {
+ app.contentResolver.openOutputStream(uri, "wt")?.use { outputStream ->
+ getRuntime().exec(command).inputStream.use { inputStream ->
+ inputStream.copyTo(outputStream)
+ }
+ } ?: throw IOException("OutputStream was null")
+ } catch (e: Exception) {
+ Log.e(TAG, "Error saving logcat ", e)
+ onLogcatError()
+ }
+ }
+
+ private suspend fun onLogcatError() = withContext(Dispatchers.Main) {
+ val str = app.getString(R.string.settings_expert_logcat_error)
+ Toast.makeText(app, str, LENGTH_LONG).show()
+ }
+
}
diff --git a/app/src/main/java/com/stevesoltys/seedvault/transport/backup/ApkBackup.kt b/app/src/main/java/com/stevesoltys/seedvault/transport/backup/ApkBackup.kt
index 60c9c9aa..02208eb0 100644
--- a/app/src/main/java/com/stevesoltys/seedvault/transport/backup/ApkBackup.kt
+++ b/app/src/main/java/com/stevesoltys/seedvault/transport/backup/ApkBackup.kt
@@ -154,7 +154,8 @@ internal class ApkBackup(
streamGetter: suspend (name: String) -> OutputStream,
): List {
check(packageInfo.splitNames != null)
- val splitSourceDirs = packageInfo.applicationInfo.splitSourceDirs
+ // attention: though not documented, splitSourceDirs can be null
+ val splitSourceDirs = packageInfo.applicationInfo.splitSourceDirs ?: emptyArray()
check(packageInfo.splitNames.size == splitSourceDirs.size) {
"Size Mismatch! ${packageInfo.splitNames.size} != ${splitSourceDirs.size} " +
"splitNames is ${packageInfo.splitNames.toList()}, " +
diff --git a/app/src/main/java/com/stevesoltys/seedvault/transport/backup/BackupCoordinator.kt b/app/src/main/java/com/stevesoltys/seedvault/transport/backup/BackupCoordinator.kt
index ac779830..5d791fc4 100644
--- a/app/src/main/java/com/stevesoltys/seedvault/transport/backup/BackupCoordinator.kt
+++ b/app/src/main/java/com/stevesoltys/seedvault/transport/backup/BackupCoordinator.kt
@@ -381,7 +381,13 @@ internal class BackupCoordinator(
}
// hook in here to back up APKs of apps that are otherwise not allowed for backup
if (isPmBackup && settingsManager.canDoBackupNow()) {
- backUpApksOfNotBackedUpPackages()
+ try {
+ backUpApksOfNotBackedUpPackages()
+ } catch (e: Exception) {
+ Log.e(TAG, "Error backing up APKs of opt-out apps: ", e)
+ // We are re-throwing this, because we want to know about problems here
+ throw e
+ }
}
}
result
diff --git a/app/src/main/java/com/stevesoltys/seedvault/transport/backup/KVBackup.kt b/app/src/main/java/com/stevesoltys/seedvault/transport/backup/KVBackup.kt
index dd89cad8..060f5431 100644
--- a/app/src/main/java/com/stevesoltys/seedvault/transport/backup/KVBackup.kt
+++ b/app/src/main/java/com/stevesoltys/seedvault/transport/backup/KVBackup.kt
@@ -209,6 +209,7 @@ internal class KVBackup(
else state.db.close()
TRANSPORT_OK
} catch (e: IOException) {
+ Log.e(TAG, "Error uploading DB", e)
TRANSPORT_ERROR
} finally {
this.state = null
diff --git a/app/src/main/java/com/stevesoltys/seedvault/ui/AppViewHolder.kt b/app/src/main/java/com/stevesoltys/seedvault/ui/AppViewHolder.kt
index e17800d7..37686fc4 100644
--- a/app/src/main/java/com/stevesoltys/seedvault/ui/AppViewHolder.kt
+++ b/app/src/main/java/com/stevesoltys/seedvault/ui/AppViewHolder.kt
@@ -2,7 +2,6 @@ package com.stevesoltys.seedvault.ui
import android.content.Context
import android.content.pm.PackageManager
-import android.os.Build.VERSION.SDK_INT
import android.view.View
import android.view.View.GONE
import android.view.View.INVISIBLE
@@ -40,12 +39,10 @@ internal abstract class AppViewHolder(protected val v: View) : RecyclerView.View
appInfo.visibility = GONE
appStatus.visibility = INVISIBLE
progressBar.visibility = VISIBLE
- if (SDK_INT >= 30) {
- progressBar.stateDescription = context.getString(
- if (isRestore) R.string.restore_restoring
- else R.string.backup_app_in_progress
- )
- }
+ progressBar.stateDescription = context.getString(
+ if (isRestore) R.string.restore_restoring
+ else R.string.backup_app_in_progress
+ )
} else {
appStatus.visibility = VISIBLE
progressBar.visibility = INVISIBLE
diff --git a/app/src/main/java/com/stevesoltys/seedvault/ui/notification/NotificationBackupObserver.kt b/app/src/main/java/com/stevesoltys/seedvault/ui/notification/NotificationBackupObserver.kt
index 6eb60079..d35971fc 100644
--- a/app/src/main/java/com/stevesoltys/seedvault/ui/notification/NotificationBackupObserver.kt
+++ b/app/src/main/java/com/stevesoltys/seedvault/ui/notification/NotificationBackupObserver.kt
@@ -43,7 +43,7 @@ internal class NotificationBackupObserver(
* @param currentBackupPackage The name of the package that now being backed up.
* @param backupProgress Current progress of backup for the package.
*/
- override fun onUpdate(currentBackupPackage: String, backupProgress: BackupProgress) {
+ override fun onUpdate(currentBackupPackage: String?, backupProgress: BackupProgress) {
showProgressNotification(currentBackupPackage)
}
@@ -57,7 +57,7 @@ internal class NotificationBackupObserver(
* that was initialized
* @param status Zero on success; a nonzero error code if the backup operation failed.
*/
- override fun onResult(target: String, status: Int) {
+ override fun onResult(target: String?, status: Int) {
if (isLoggable(TAG, INFO)) {
Log.i(TAG, "Completed. Target: $target, status: $status")
}
@@ -81,8 +81,8 @@ internal class NotificationBackupObserver(
nm.onBackupFinished(success, numBackedUp)
}
- private fun showProgressNotification(packageName: String) {
- if (currentPackage == packageName) return
+ private fun showProgressNotification(packageName: String?) {
+ if (packageName == null || currentPackage == packageName) return
if (isLoggable(TAG, INFO)) {
"Showing progress notification for $currentPackage $numPackages/$expectedPackages".let {
diff --git a/app/src/main/java/com/stevesoltys/seedvault/ui/recoverycode/RecoveryCodeActivity.kt b/app/src/main/java/com/stevesoltys/seedvault/ui/recoverycode/RecoveryCodeActivity.kt
index a6b3d3ed..5923a145 100644
--- a/app/src/main/java/com/stevesoltys/seedvault/ui/recoverycode/RecoveryCodeActivity.kt
+++ b/app/src/main/java/com/stevesoltys/seedvault/ui/recoverycode/RecoveryCodeActivity.kt
@@ -4,6 +4,7 @@ import android.os.Bundle
import android.view.MenuItem
import android.view.WindowManager.LayoutParams.FLAG_SECURE
import com.stevesoltys.seedvault.R
+import com.stevesoltys.seedvault.isDebugBuild
import com.stevesoltys.seedvault.ui.BackupActivity
import com.stevesoltys.seedvault.ui.INTENT_EXTRA_IS_RESTORE
import org.koin.androidx.viewmodel.ext.android.viewModel
@@ -15,7 +16,7 @@ class RecoveryCodeActivity : BackupActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
- window.addFlags(FLAG_SECURE)
+ if (!isDebugBuild()) window.addFlags(FLAG_SECURE)
setContentView(R.layout.activity_recovery_code)
diff --git a/app/src/main/java/com/stevesoltys/seedvault/ui/recoverycode/RecoveryCodeInputFragment.kt b/app/src/main/java/com/stevesoltys/seedvault/ui/recoverycode/RecoveryCodeInputFragment.kt
index f0bc993a..a0d507a6 100644
--- a/app/src/main/java/com/stevesoltys/seedvault/ui/recoverycode/RecoveryCodeInputFragment.kt
+++ b/app/src/main/java/com/stevesoltys/seedvault/ui/recoverycode/RecoveryCodeInputFragment.kt
@@ -6,7 +6,6 @@ import android.content.Intent
import android.hardware.biometrics.BiometricManager.Authenticators.BIOMETRIC_STRONG
import android.hardware.biometrics.BiometricManager.Authenticators.DEVICE_CREDENTIAL
import android.hardware.biometrics.BiometricPrompt
-import android.os.Build.VERSION.SDK_INT
import android.os.Bundle
import android.os.CancellationSignal
import android.view.LayoutInflater
@@ -22,7 +21,6 @@ import android.widget.TextView
import android.widget.Toast
import android.widget.Toast.LENGTH_LONG
import androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult
-import androidx.annotation.RequiresApi
import androidx.appcompat.app.AlertDialog
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.content.ContextCompat.getMainExecutor
@@ -122,9 +120,9 @@ class RecoveryCodeInputFragment : Fragment() {
newCodeButton.visibility = if (forStoringNewCode) GONE else VISIBLE
newCodeButton.setOnClickListener { generateNewCode() }
- viewModel.existingCodeChecked.observeEvent(viewLifecycleOwner, { verified ->
+ viewModel.existingCodeChecked.observeEvent(viewLifecycleOwner) { verified ->
onExistingCodeChecked(verified)
- })
+ }
if (forStoringNewCode && isDebugBuild() && !viewModel.isRestore) debugPreFill()
}
@@ -147,7 +145,7 @@ class RecoveryCodeInputFragment : Fragment() {
}
if (forStoringNewCode) {
val keyguardManager = requireContext().getSystemService(KeyguardManager::class.java)
- if (SDK_INT >= 30 && keyguardManager.isDeviceSecure) {
+ if (keyguardManager.isDeviceSecure) {
// if we have a lock-screen secret, we can ask for it before storing the code
storeNewCodeAfterAuth(input)
} else {
@@ -159,7 +157,6 @@ class RecoveryCodeInputFragment : Fragment() {
}
}
- @RequiresApi(30)
private fun storeNewCodeAfterAuth(input: List) {
val context = requireContext()
val biometricPrompt = BiometricPrompt.Builder(context)
diff --git a/app/src/main/res/drawable/ic_bug_report.xml b/app/src/main/res/drawable/ic_bug_report.xml
new file mode 100644
index 00000000..dd702e24
--- /dev/null
+++ b/app/src/main/res/drawable/ic_bug_report.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index c3ef5057..66e1800f 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -47,9 +47,11 @@
Unlimited app quota
Do not impose a limitation on the size of app backups.\n\nWarning: This can fill up your storage location quickly. Not needed for most apps.
Save app log
+
+ Could not save app log
Sending this to the developers can help diagnose bugs.\n\nAlways review it to ensure it has no personally identifiable info, and delete it afterwards!
Could not save app log
-
+
Choose where to store backups
Where to find your backups?
diff --git a/app/src/main/res/xml/settings_expert.xml b/app/src/main/res/xml/settings_expert.xml
index a257d898..11e8497c 100644
--- a/app/src/main/res/xml/settings_expert.xml
+++ b/app/src/main/res/xml/settings_expert.xml
@@ -5,4 +5,9 @@
android:key="unlimited_quota"
android:summary="@string/settings_expert_quota_summary"
android:title="@string/settings_expert_quota_title" />
-
\ No newline at end of file
+
+
diff --git a/app/src/test/java/com/stevesoltys/seedvault/metadata/MetadataManagerTest.kt b/app/src/test/java/com/stevesoltys/seedvault/metadata/MetadataManagerTest.kt
index 0e120ff2..9683ad06 100644
--- a/app/src/test/java/com/stevesoltys/seedvault/metadata/MetadataManagerTest.kt
+++ b/app/src/test/java/com/stevesoltys/seedvault/metadata/MetadataManagerTest.kt
@@ -41,7 +41,7 @@ import kotlin.random.Random
@Suppress("DEPRECATION")
@RunWith(AndroidJUnit4::class)
@Config(
- sdk = [29], // robolectric does not support 30, yet
+ sdk = [32], // robolectric does not support 33, yet
application = TestApp::class
)
class MetadataManagerTest {
diff --git a/app/src/test/java/com/stevesoltys/seedvault/plugins/saf/DocumentFileTest.kt b/app/src/test/java/com/stevesoltys/seedvault/plugins/saf/DocumentFileTest.kt
index b7346b2c..bc3443c6 100644
--- a/app/src/test/java/com/stevesoltys/seedvault/plugins/saf/DocumentFileTest.kt
+++ b/app/src/test/java/com/stevesoltys/seedvault/plugins/saf/DocumentFileTest.kt
@@ -19,7 +19,7 @@ import org.robolectric.annotation.Config
@RunWith(AndroidJUnit4::class)
@Config(
- sdk = [29], // robolectric does not support 30, yet
+ sdk = [32], // robolectric does not support 33, yet
application = TestApp::class
)
internal class DocumentFileTest {
diff --git a/app/src/test/java/com/stevesoltys/seedvault/restore/install/ApkBackupRestoreTest.kt b/app/src/test/java/com/stevesoltys/seedvault/restore/install/ApkBackupRestoreTest.kt
index 97012e3d..f8805c78 100644
--- a/app/src/test/java/com/stevesoltys/seedvault/restore/install/ApkBackupRestoreTest.kt
+++ b/app/src/test/java/com/stevesoltys/seedvault/restore/install/ApkBackupRestoreTest.kt
@@ -133,14 +133,8 @@ internal class ApkBackupRestoreTest : TransportTest() {
every { strictContext.cacheDir } returns tmpFile
every { crypto.getNameForApk(salt, packageName, "") } returns name
coEvery { storagePlugin.getInputStream(token, name) } returns inputStream
- every { pm.getPackageArchiveInfo(capture(apkPath), any()) } returns packageInfo
- every {
- @Suppress("UNRESOLVED_REFERENCE")
- pm.loadItemIcon(
- packageInfo.applicationInfo,
- packageInfo.applicationInfo
- )
- } returns icon
+ every { pm.getPackageArchiveInfo(capture(apkPath), any()) } returns packageInfo
+ every { applicationInfo.loadIcon(pm) } returns icon
every { pm.getApplicationLabel(packageInfo.applicationInfo) } returns appName
every {
splitCompatChecker.isCompatible(metadata.deviceName, listOf(splitName))
diff --git a/app/src/test/java/com/stevesoltys/seedvault/restore/install/ApkRestoreTest.kt b/app/src/test/java/com/stevesoltys/seedvault/restore/install/ApkRestoreTest.kt
index ad730402..6eb1e969 100644
--- a/app/src/test/java/com/stevesoltys/seedvault/restore/install/ApkRestoreTest.kt
+++ b/app/src/test/java/com/stevesoltys/seedvault/restore/install/ApkRestoreTest.kt
@@ -1,7 +1,6 @@
package com.stevesoltys.seedvault.restore.install
import android.content.Context
-import android.content.pm.ApplicationInfo
import android.content.pm.ApplicationInfo.FLAG_INSTALLED
import android.content.pm.ApplicationInfo.FLAG_SYSTEM
import android.content.pm.ApplicationInfo.FLAG_UPDATED_SYSTEM_APP
@@ -112,7 +111,7 @@ internal class ApkRestoreTest : TransportTest() {
every { strictContext.cacheDir } returns File(tmpDir.toString())
every { crypto.getNameForApk(salt, packageName, "") } returns name
coEvery { storagePlugin.getInputStream(token, name) } returns apkInputStream
- every { pm.getPackageArchiveInfo(any(), any()) } returns packageInfo
+ every { pm.getPackageArchiveInfo(any(), any()) } returns packageInfo
every { storagePlugin.providerPackageName } returns storageProviderPackageName
apkRestore.restore(backup).collectIndexed { i, value ->
@@ -176,14 +175,8 @@ internal class ApkRestoreTest : TransportTest() {
coEvery {
legacyStoragePlugin.getApkInputStream(token, packageName, "")
} returns apkInputStream
- every { pm.getPackageArchiveInfo(any(), any()) } returns packageInfo
- every {
- @Suppress("UNRESOLVED_REFERENCE")
- pm.loadItemIcon(
- packageInfo.applicationInfo,
- packageInfo.applicationInfo
- )
- } returns icon
+ every { pm.getPackageArchiveInfo(any(), any()) } returns packageInfo
+ every { applicationInfo.loadIcon(pm) } returns icon
every { pm.getApplicationLabel(packageInfo.applicationInfo) } returns appName
coEvery {
apkInstaller.install(match { it.size == 1 }, packageName, installerName, any())
@@ -200,13 +193,11 @@ internal class ApkRestoreTest : TransportTest() {
runBlocking {
val packageMetadata = this@ApkRestoreTest.packageMetadata.copy(system = true)
packageMetadataMap[packageName] = packageMetadata
- packageInfo.applicationInfo = mockk()
val installedPackageInfo: PackageInfo = mockk()
val willFail = Random.nextBoolean()
val isSystemApp = Random.nextBoolean()
cacheBaseApkAndGetInfo(tmpDir)
- every { packageInfo.applicationInfo.loadIcon(pm) } returns icon
every { storagePlugin.providerPackageName } returns storageProviderPackageName
if (willFail) {
@@ -214,7 +205,7 @@ internal class ApkRestoreTest : TransportTest() {
pm.getPackageInfo(packageName, 0)
} throws PackageManager.NameNotFoundException()
} else {
- installedPackageInfo.applicationInfo = ApplicationInfo().apply {
+ installedPackageInfo.applicationInfo = mockk {
flags =
if (!isSystemApp) FLAG_INSTALLED else FLAG_SYSTEM or FLAG_UPDATED_SYSTEM_APP
}
@@ -421,14 +412,8 @@ internal class ApkRestoreTest : TransportTest() {
every { strictContext.cacheDir } returns File(tmpDir.toString())
every { crypto.getNameForApk(salt, packageName, "") } returns name
coEvery { storagePlugin.getInputStream(token, name) } returns apkInputStream
- every { pm.getPackageArchiveInfo(any(), any()) } returns packageInfo
- every {
- @Suppress("UNRESOLVED_REFERENCE")
- pm.loadItemIcon(
- packageInfo.applicationInfo,
- packageInfo.applicationInfo
- )
- } returns icon
+ every { pm.getPackageArchiveInfo(any(), any()) } returns packageInfo
+ every { applicationInfo.loadIcon(pm) } returns icon
every { pm.getApplicationLabel(packageInfo.applicationInfo) } returns appName
}
diff --git a/app/src/test/java/com/stevesoltys/seedvault/restore/install/DeviceInfoTest.kt b/app/src/test/java/com/stevesoltys/seedvault/restore/install/DeviceInfoTest.kt
index 414f5c1d..8d93b053 100644
--- a/app/src/test/java/com/stevesoltys/seedvault/restore/install/DeviceInfoTest.kt
+++ b/app/src/test/java/com/stevesoltys/seedvault/restore/install/DeviceInfoTest.kt
@@ -22,7 +22,7 @@ import kotlin.random.Random
@RunWith(AndroidJUnit4::class)
@Config(
- sdk = [29], // robolectric does not support 30, yet
+ sdk = [32], // robolectric does not support 33, yet
application = TestApp::class
)
internal class DeviceInfoTest {
@@ -62,12 +62,10 @@ internal class DeviceInfoTest {
assertFalse(deviceInfo.isSupportedLanguage("de"))
// test areUnknownSplitsAllowed
- val deviceName = "unknown robolectric"
+ assertTrue(deviceInfo.areUnknownSplitsAllowed("robolectric robolectric"))
if (onlyOnSameDevice) {
- assertTrue(deviceInfo.areUnknownSplitsAllowed(deviceName))
assertFalse(deviceInfo.areUnknownSplitsAllowed("foo bar"))
} else {
- assertTrue(deviceInfo.areUnknownSplitsAllowed(deviceName))
assertTrue(deviceInfo.areUnknownSplitsAllowed("foo bar"))
}
}
diff --git a/app/src/test/java/com/stevesoltys/seedvault/transport/TransportTest.kt b/app/src/test/java/com/stevesoltys/seedvault/transport/TransportTest.kt
index 2f4e0cd2..35e81b28 100644
--- a/app/src/test/java/com/stevesoltys/seedvault/transport/TransportTest.kt
+++ b/app/src/test/java/com/stevesoltys/seedvault/transport/TransportTest.kt
@@ -37,12 +37,13 @@ internal abstract class TransportTest {
protected val sigInfo: SigningInfo = mockk()
protected val token = Random.nextLong()
+ protected val applicationInfo = mockk {
+ flags = FLAG_ALLOW_BACKUP or FLAG_INSTALLED
+ }
protected val packageInfo = PackageInfo().apply {
packageName = "org.example"
longVersionCode = Random.nextLong()
- applicationInfo = ApplicationInfo().apply {
- flags = FLAG_ALLOW_BACKUP or FLAG_INSTALLED
- }
+ applicationInfo = this@TransportTest.applicationInfo
signingInfo = sigInfo
}
protected val pmPackageInfo = PackageInfo().apply {
diff --git a/app/src/test/java/com/stevesoltys/seedvault/transport/backup/BackupCoordinatorTest.kt b/app/src/test/java/com/stevesoltys/seedvault/transport/backup/BackupCoordinatorTest.kt
index 9a58f3fb..d5facae7 100644
--- a/app/src/test/java/com/stevesoltys/seedvault/transport/backup/BackupCoordinatorTest.kt
+++ b/app/src/test/java/com/stevesoltys/seedvault/transport/backup/BackupCoordinatorTest.kt
@@ -5,7 +5,6 @@ import android.app.backup.BackupTransport.TRANSPORT_NOT_INITIALIZED
import android.app.backup.BackupTransport.TRANSPORT_OK
import android.app.backup.BackupTransport.TRANSPORT_PACKAGE_REJECTED
import android.app.backup.BackupTransport.TRANSPORT_QUOTA_EXCEEDED
-import android.content.pm.ApplicationInfo
import android.content.pm.ApplicationInfo.FLAG_STOPPED
import android.content.pm.PackageInfo
import android.net.Uri
@@ -399,7 +398,9 @@ internal class BackupCoordinatorTest : BackupTest() {
PackageInfo().apply {
packageName = "org.example.2"
// the second package does not get backed up, because it is stopped
- applicationInfo = ApplicationInfo().apply { flags = FLAG_STOPPED }
+ applicationInfo = mockk {
+ flags = FLAG_STOPPED
+ }
}
)
val packageMetadata: PackageMetadata = mockk()
diff --git a/build.gradle b/build.gradle
index bb25f307..6c90483f 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,11 +1,11 @@
buildscript {
- // 1.3.21 Android 10
// 1.3.61 Android 11
// 1.4.30 Android 12
+ // 1.6.10 Android 13
// Check:
- // https://android.googlesource.com/platform/external/kotlinc/+/refs/tags/android-12.0.0_r2/build.txt
- ext.aosp_kotlin_version = '1.4.31' // 1.4.30 breaks Kotlin plugin in Android Studio
- ext.kotlin_version = '1.4.31'
+ // https://android.googlesource.com/platform/external/kotlinc/+/refs/tags/android-13.0.0_r3/build.txt
+ ext.aosp_kotlin_version = '1.6.10' // 1.6.10-release-923 in AOSP
+ ext.kotlin_version = '1.6.10'
repositories {
mavenCentral()
@@ -15,15 +15,15 @@ buildscript {
//noinspection DifferentKotlinGradleVersion
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath "com.google.protobuf:protobuf-gradle-plugin:0.8.17"
- classpath 'com.android.tools.build:gradle:7.0.2'
+ classpath 'com.android.tools.build:gradle:7.2.2'
}
}
ext {
- buildToolsVersion = '31.0.0'
- compileSdkVersion = 31
- minSdkVersion = 29
- targetSdkVersion = 31
+ buildToolsVersion = '33.0.0'
+ compileSdkVersion = 33
+ minSdkVersion = 32
+ targetSdkVersion = 33
}
apply from: 'gradle/dependencies.gradle'
diff --git a/contactsbackup/build.gradle b/contactsbackup/build.gradle
index c7999696..71121216 100644
--- a/contactsbackup/build.gradle
+++ b/contactsbackup/build.gradle
@@ -15,12 +15,12 @@ android {
}
compileOptions {
- sourceCompatibility = 1.8
- targetCompatibility = 1.8
+ sourceCompatibility = JavaVersion.VERSION_11
+ targetCompatibility = JavaVersion.VERSION_11
}
kotlinOptions {
- jvmTarget = '1.8'
+ jvmTarget = JavaVersion.VERSION_11.toString()
}
testOptions {
diff --git a/contactsbackup/libs/com.android.vcard.jar b/contactsbackup/libs/com.android.vcard.jar
index f677ef61..8060d849 100644
Binary files a/contactsbackup/libs/com.android.vcard.jar and b/contactsbackup/libs/com.android.vcard.jar differ
diff --git a/contactsbackup/src/main/AndroidManifest.xml b/contactsbackup/src/main/AndroidManifest.xml
index a45b985d..6ce63f37 100644
--- a/contactsbackup/src/main/AndroidManifest.xml
+++ b/contactsbackup/src/main/AndroidManifest.xml
@@ -2,8 +2,8 @@
+ android:versionCode="33000301"
+ android:versionName="13-3.1">
+
+
+
diff --git a/gradle/dependencies.gradle b/gradle/dependencies.gradle
index 607af4e0..29a1f0aa 100644
--- a/gradle/dependencies.gradle
+++ b/gradle/dependencies.gradle
@@ -1,11 +1,13 @@
ext {
- // https://android.googlesource.com/platform/prebuilts/sdk/+/refs/tags/android-12.0.0_r2/current/androidx/Android.bp#2943
- ext.room_version = "2.3.0-beta02"
- // https://android.googlesource.com/platform/external/protobuf/+/refs/tags/android-12.0.0_r2/java/pom.xml#7
+ // https://android.googlesource.com/platform/prebuilts/sdk/+/refs/tags/android-13.0.0_r3/current/androidx/Android.bp#3901
+ ext.room_version = "2.4.0-alpha05"
+ // https://android.googlesource.com/platform/external/protobuf/+/refs/tags/android-13.0.0_r3/java/pom.xml#7
ext.protobuf_version = "3.9.1"
+
+ // test dependencies below - these do not care about AOSP and can be freely updated
junit4_version = "4.13.2"
- junit5_version = "5.5.2" // careful, upgrading this can change a Cipher's IV size in tests!?
- mockk_version = "1.12.0"
+ junit5_version = "5.7.2" // careful, upgrading this can change a Cipher's IV size in tests!?
+ mockk_version = "1.12.3"
espresso_version = "3.4.0"
}
@@ -37,63 +39,63 @@ ext.kotlin_libs = [
],
coroutines: [
dependencies.create('org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm') {
- // https://android.googlesource.com/platform/prebuilts/tools/+/refs/tags/android-12.0.0_r2/common/m2/Android.bp#273
- version { strictly '1.4.2' }
+ // https://android.googlesource.com/platform/external/kotlinx.coroutines/+/refs/tags/android-13.0.0_r3/CHANGES.md
+ version { strictly '1.5.2' }
},
dependencies.create('org.jetbrains.kotlinx:kotlinx-coroutines-android') {
- // https://android.googlesource.com/platform/prebuilts/tools/+/refs/tags/android-12.0.0_r2/common/m2/Android.bp#288
- version { strictly '1.3.0' }
+ // https://android.googlesource.com/platform/external/kotlinx.coroutines/+/refs/tags/android-13.0.0_r3/CHANGES.md
+ version { strictly '1.5.2' }
},
],
]
ext.std_libs = [
androidx_core: [
- // https://android.googlesource.com/platform/prebuilts/sdk/+/refs/tags/android-12.0.0_r2/current/androidx/Android.bp#867
+ // https://android.googlesource.com/platform/prebuilts/sdk/+/refs/tags/android-13.0.0_r3/current/androidx/Android.bp#1761
dependencies.create('androidx.core:core') {
- version { strictly '1.6.0' } // should be 1.6.0-beta03, but that is not even released, yet
+ version { strictly '1.9.0-alpha03' }
},
- // https://android.googlesource.com/platform/prebuilts/sdk/+/refs/tags/android-12.0.0_r2/current/androidx/Android.bp#833
+ // https://android.googlesource.com/platform/prebuilts/sdk/+/refs/tags/android-13.0.0_r3/current/androidx/Android.bp#1727
dependencies.create('androidx.core:core-ktx') {
- version { strictly '1.5.0-beta02' }
+ version { strictly '1.9.0-alpha03' }
},
],
- // https://android.googlesource.com/platform/prebuilts/sdk/+/refs/tags/android-12.0.0_r2/current/androidx/Android.bp#1189
+ // https://android.googlesource.com/platform/prebuilts/sdk/+/refs/tags/android-13.0.0_r3/current/androidx/Android.bp#2159
androidx_fragment: dependencies.create('androidx.fragment:fragment-ktx') {
- version { strictly '1.4.0-alpha01' }
+ version { strictly '1.4.0-alpha09' }
},
- // https://android.googlesource.com/platform/prebuilts/sdk/+/refs/tags/android-12.0.0_r2/current/androidx/Android.bp#20
+ // https://android.googlesource.com/platform/prebuilts/sdk/+/refs/tags/android-13.0.0_r3/current/androidx/Android.bp#57
androidx_activity: dependencies.create('androidx.activity:activity-ktx') {
- version { strictly '1.3.0-alpha03' }
+ version { strictly '1.4.0-alpha02' }
},
- // https://android.googlesource.com/platform/prebuilts/sdk/+/refs/tags/android-12.0.0_r2/current/androidx/Android.bp#2695
+ // https://android.googlesource.com/platform/prebuilts/sdk/+/refs/tags/android-13.0.0_r3/current/androidx/Android.bp#3597
androidx_preference: dependencies.create('androidx.preference:preference') {
- version { strictly '1.1.1' } // should be 1.2.0-alpha01, but that is not even released, yet
+ version { strictly '1.2.0-alpha01' }
},
- // https://android.googlesource.com/platform/prebuilts/sdk/+/refs/tags/android-12.0.0_r2/current/androidx/Android.bp#1820
+ // https://android.googlesource.com/platform/prebuilts/sdk/+/refs/tags/android-13.0.0_r3/current/androidx/Android.bp#2754
androidx_lifecycle_viewmodel_ktx: dependencies.create('androidx.lifecycle:lifecycle-viewmodel-ktx') {
- version { strictly '2.4.0-alpha01' }
+ version { strictly '2.4.0-alpha03' } // 2.4.0-alpha04 in AOSP but was never released
},
- // https://android.googlesource.com/platform/prebuilts/sdk/+/refs/tags/android-12.0.0_r2/current/androidx/Android.bp#1618
+ // https://android.googlesource.com/platform/prebuilts/sdk/+/refs/tags/android-13.0.0_r3/current/androidx/Android.bp#2550
androidx_lifecycle_livedata_ktx: dependencies.create('androidx.lifecycle:lifecycle-livedata-ktx') {
- version { strictly '2.4.0-alpha01' }
+ version { strictly '2.4.0-alpha03' } // 2.4.0-alpha04 in AOSP but was never released
},
- // https://android.googlesource.com/platform/prebuilts/sdk/+/refs/tags/android-12.0.0_r2/current/extras/constraint-layout-x/Android.bp#39
+ // https://android.googlesource.com/platform/prebuilts/sdk/+/refs/tags/android-13.0.0_r3/current/extras/constraint-layout-x/Android.bp#39
androidx_constraintlayout: dependencies.create('androidx.constraintlayout:constraintlayout') {
version { strictly '2.0.0-beta7' }
},
- // https://android.googlesource.com/platform/prebuilts/sdk/+/refs/tags/android-12.0.0_r2/current/androidx/Android.bp#969
+ // https://android.googlesource.com/platform/prebuilts/sdk/+/refs/tags/android-13.0.0_r3/current/androidx/Android.bp#1865
androidx_documentfile: dependencies.create('androidx.documentfile:documentfile') {
- version { strictly '1.1.0-alpha01' }
+ version { strictly '1.1.0-alpha01' } // 1.1.0-alpha02 in AOSP but not released yet
},
- // https://android.googlesource.com/platform/prebuilts/sdk/+/refs/tags/android-12.0.0_r2/current/extras/material-design-x/Android.bp#6
+ // https://android.googlesource.com/platform/prebuilts/sdk/+/refs/tags/android-13.0.0_r3/current/extras/material-design-x/Android.bp#6
com_google_android_material: dependencies.create('com.google.android.material:material') {
- version { strictly '1.4.0' }
+ version { strictly '1.6.0-alpha03' } // 1.6.0-alpha0301 in AOSP
},
]
ext.lint_libs = [
- exceptions: 'com.github.thirdegg:lint-rules:0.0.6-beta'
+ exceptions: 'com.github.thirdegg:lint-rules:0.1.0'
]
ext.storage_libs = [
@@ -103,7 +105,9 @@ ext.storage_libs = [
com_google_protobuf_javalite: dependencies.create('com.google.protobuf:protobuf-javalite') {
version { strictly "$protobuf_version" }
},
+ // https://github.com/google/tink/releases
com_google_crypto_tink_android: dependencies.create('com.google.crypto.tink:tink-android') {
- version { strictly '1.6.1' }
+ // careful with upgrading tink, so old backups continue to be decryptable
+ version { strictly '1.7.0' }
},
]
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 20d1c07e..6b46f328 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,7 +1,7 @@
-#Tue Aug 04 14:40:48 BRT 2020
+#Fri Aug 19 10:56:09 IST 2022
distributionBase=GRADLE_USER_HOME
+distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip
distributionPath=wrapper/dists
-zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-all.zip
-distributionSha256Sum=a8da5b02437a60819cad23e10fc7e9cf32bcb57029d9cb277e26eeff76ce014b
+zipStoreBase=GRADLE_USER_HOME
+distributionSha256Sum=b586e04868a22fd817c8971330fec37e298f3242eb85c374181b12d637f80302
diff --git a/libs/Android.bp b/libs/Android.bp
index 2fa04ce0..15a9fcf2 100644
--- a/libs/Android.bp
+++ b/libs/Android.bp
@@ -1,5 +1,5 @@
java_import {
name: "seedvault-lib-kotlin-bip39",
- jars: ["kotlin-bip39-1.0.2.jar"],
+ jars: ["kotlin-bip39-jvm-1.0.4.jar"],
sdk_version: "current",
}
diff --git a/libs/koin-android/Android.bp b/libs/koin-android/Android.bp
index 1df86bba..c3369d9c 100644
--- a/libs/koin-android/Android.bp
+++ b/libs/koin-android/Android.bp
@@ -1,11 +1,11 @@
android_library_import {
name: "seedvault-lib-koin-android",
- aars: ["koin-android-3.1.2.aar"],
+ aars: ["koin-android-3.2.0.aar"],
sdk_version: "current",
}
java_import {
name: "seedvault-lib-koin-core-jvm",
- jars: ["koin-core-jvm-3.1.2.jar"],
+ jars: ["koin-core-jvm-3.2.0.jar"],
sdk_version: "current",
}
diff --git a/libs/koin-android/koin-android-3.1.2.aar b/libs/koin-android/koin-android-3.1.2.aar
deleted file mode 100644
index be74b755..00000000
Binary files a/libs/koin-android/koin-android-3.1.2.aar and /dev/null differ
diff --git a/libs/koin-android/koin-android-3.2.0.aar b/libs/koin-android/koin-android-3.2.0.aar
new file mode 100644
index 00000000..3078638b
Binary files /dev/null and b/libs/koin-android/koin-android-3.2.0.aar differ
diff --git a/libs/koin-android/koin-core-jvm-3.1.2.jar b/libs/koin-android/koin-core-jvm-3.1.2.jar
deleted file mode 100644
index 0efd1737..00000000
Binary files a/libs/koin-android/koin-core-jvm-3.1.2.jar and /dev/null differ
diff --git a/libs/koin-android/koin-core-jvm-3.2.0.jar b/libs/koin-android/koin-core-jvm-3.2.0.jar
new file mode 100644
index 00000000..912fc35f
Binary files /dev/null and b/libs/koin-android/koin-core-jvm-3.2.0.jar differ
diff --git a/libs/kotlin-bip39-1.0.2.jar b/libs/kotlin-bip39-1.0.2.jar
deleted file mode 100644
index c05e1e05..00000000
Binary files a/libs/kotlin-bip39-1.0.2.jar and /dev/null differ
diff --git a/libs/kotlin-bip39-jvm-1.0.4.jar b/libs/kotlin-bip39-jvm-1.0.4.jar
new file mode 100644
index 00000000..55ff7745
Binary files /dev/null and b/libs/kotlin-bip39-jvm-1.0.4.jar differ
diff --git a/storage/demo/build.gradle b/storage/demo/build.gradle
index 6950ebc8..c99f2060 100644
--- a/storage/demo/build.gradle
+++ b/storage/demo/build.gradle
@@ -27,11 +27,11 @@ android {
}
}
compileOptions {
- sourceCompatibility JavaVersion.VERSION_1_8
- targetCompatibility JavaVersion.VERSION_1_8
+ sourceCompatibility = JavaVersion.VERSION_11
+ targetCompatibility = JavaVersion.VERSION_11
}
kotlinOptions {
- jvmTarget = '1.8'
+ jvmTarget = JavaVersion.VERSION_11.toString()
freeCompilerArgs += "-Xopt-in=kotlin.RequiresOptIn"
}
lintOptions {
diff --git a/storage/demo/src/main/AndroidManifest.xml b/storage/demo/src/main/AndroidManifest.xml
index 43a2b25c..15b03a47 100644
--- a/storage/demo/src/main/AndroidManifest.xml
+++ b/storage/demo/src/main/AndroidManifest.xml
@@ -2,6 +2,8 @@
+
+
- if (grantedMap[WRITE_EXTERNAL_STORAGE] == true) {
- Toast.makeText(requireContext(), "Please try again now!", LENGTH_SHORT).show()
- }
- }
-
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
@@ -62,19 +50,19 @@ open class LogFragment : Fragment() {
progressBar = v.findViewById(R.id.progressBar)
horizontalProgressBar = v.findViewById(R.id.horizontalProgressBar)
button = v.findViewById(R.id.button)
- viewModel.backupLog.observe(viewLifecycleOwner, { progress ->
+ viewModel.backupLog.observe(viewLifecycleOwner) { progress ->
progress.text?.let { adapter.addItem(it) }
horizontalProgressBar.max = progress.total
horizontalProgressBar.setProgress(progress.current, true)
list.postDelayed({
list.scrollToPosition(adapter.itemCount - 1)
}, 50)
- })
- viewModel.backupButtonEnabled.observe(viewLifecycleOwner, { enabled ->
+ }
+ viewModel.backupButtonEnabled.observe(viewLifecycleOwner) { enabled ->
button.isEnabled = enabled
progressBar.visibility = if (enabled) INVISIBLE else VISIBLE
if (!enabled) adapter.clear()
- })
+ }
button.setOnClickListener {
if (!checkPermission()) return@setOnClickListener
viewModel.simulateBackup()
@@ -120,23 +108,13 @@ open class LogFragment : Fragment() {
}
private fun checkPermission(): Boolean {
- return if (Build.VERSION.SDK_INT >= 30) {
- if (Environment.isExternalStorageManager()) return true
- Toast.makeText(requireContext(), "Permission needed", LENGTH_SHORT).show()
- val i = Intent(ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION).apply {
- data = Uri.parse("package:${requireContext().packageName}")
- }
- startActivity(i)
- false
- } else {
- if (requireContext().checkSelfPermission(WRITE_EXTERNAL_STORAGE) == PERMISSION_GRANTED) {
- true
- } else {
- Toast.makeText(requireContext(), "No storage permission", LENGTH_SHORT).show()
- permissionRequest.launch(arrayOf(WRITE_EXTERNAL_STORAGE, ACCESS_MEDIA_LOCATION))
- false
- }
+ if (Environment.isExternalStorageManager()) return true
+ Toast.makeText(requireContext(), "Permission needed", LENGTH_SHORT).show()
+ val i = Intent(ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION).apply {
+ data = Uri.parse("package:${requireContext().packageName}")
}
+ startActivity(i)
+ return false
}
}
diff --git a/storage/demo/src/main/java/de/grobox/storagebackuptester/settings/InfoFragment.kt b/storage/demo/src/main/java/de/grobox/storagebackuptester/settings/InfoFragment.kt
index 272c1d0b..2d090f46 100644
--- a/storage/demo/src/main/java/de/grobox/storagebackuptester/settings/InfoFragment.kt
+++ b/storage/demo/src/main/java/de/grobox/storagebackuptester/settings/InfoFragment.kt
@@ -1,6 +1,5 @@
package de.grobox.storagebackuptester.settings
-import android.os.Build.VERSION.SDK_INT
import android.os.Bundle
import android.provider.DocumentsContract
import android.provider.MediaStore
@@ -39,16 +38,14 @@ class InfoFragment : MediaScanFragment() {
} catch (e: IllegalArgumentException) {
e.toString()
}
- val gen = if (SDK_INT >= 30) try {
+ val gen = try {
MediaStore.getGeneration(context, volumeName)
} catch (e: IllegalArgumentException) {
e.toString()
- } else null
+ }
sb.appendLine(" $volumeName")
sb.appendLine(" version: $version")
- if (gen != null) {
- sb.appendLine(" generation: $gen")
- }
+ sb.appendLine(" generation: $gen")
}
sb.appendLine()
sb.appendLine("Media files smaller than 100 KB: ${mediaFilesSmallerThan(100 * 1024)}")
diff --git a/storage/lib/Android.bp b/storage/lib/Android.bp
index 88113b1d..4d25e3d2 100644
--- a/storage/lib/Android.bp
+++ b/storage/lib/Android.bp
@@ -17,11 +17,15 @@ android_library {
"seedvault-lib-tink-android",
"libprotobuf-java-lite",
"androidx.core_core-ktx",
+ "androidx.fragment_fragment-ktx",
+ "androidx.activity_activity-ktx",
"androidx.documentfile_documentfile",
"androidx.lifecycle_lifecycle-viewmodel-ktx",
"androidx.room_room-runtime",
"androidx-constraintlayout_constraintlayout",
"com.google.android.material_material",
+ "kotlinx-coroutines-android",
+ "kotlinx-coroutines-core",
],
plugins: [
"androidx.room_room-compiler-plugin",
@@ -37,6 +41,6 @@ android_library {
java_import {
name: "seedvault-lib-tink-android",
- jars: ["libs/tink-android-1.6.1.jar"],
+ jars: ["libs/tink-android-1.7.0.jar"],
sdk_version: "current",
}
diff --git a/storage/lib/build.gradle b/storage/lib/build.gradle
index e9544b3d..d820a205 100644
--- a/storage/lib/build.gradle
+++ b/storage/lib/build.gradle
@@ -4,7 +4,7 @@ plugins {
id 'kotlin-android'
id 'kotlin-kapt'
id "org.jlleitschuh.gradle.ktlint" version "10.2.0"
- id 'org.jetbrains.dokka' version '1.4.30'
+ id 'org.jetbrains.dokka' version "$kotlin_version"
}
android {
@@ -28,12 +28,12 @@ android {
}
}
compileOptions {
- sourceCompatibility JavaVersion.VERSION_1_8
- targetCompatibility JavaVersion.VERSION_1_8
+ sourceCompatibility = JavaVersion.VERSION_11
+ targetCompatibility = JavaVersion.VERSION_11
}
kotlinOptions {
- jvmTarget = '1.8'
- languageVersion = "1.4"
+ jvmTarget = JavaVersion.VERSION_11.toString()
+ languageVersion = "1.6"
freeCompilerArgs += '-Xopt-in=kotlin.RequiresOptIn'
freeCompilerArgs += '-Xexplicit-api=strict'
}
diff --git a/storage/lib/libs/tink-android-1.6.1.jar b/storage/lib/libs/tink-android-1.6.1.jar
deleted file mode 100644
index 4929a0c7..00000000
Binary files a/storage/lib/libs/tink-android-1.6.1.jar and /dev/null differ
diff --git a/storage/lib/libs/tink-android-1.7.0.jar b/storage/lib/libs/tink-android-1.7.0.jar
new file mode 100644
index 00000000..5bb1c7d8
Binary files /dev/null and b/storage/lib/libs/tink-android-1.7.0.jar differ
diff --git a/storage/lib/src/main/AndroidManifest.xml b/storage/lib/src/main/AndroidManifest.xml
index 08d696be..593723ef 100644
--- a/storage/lib/src/main/AndroidManifest.xml
+++ b/storage/lib/src/main/AndroidManifest.xml
@@ -3,13 +3,6 @@
xmlns:tools="http://schemas.android.com/tools"
package="org.calyxos.backup.storage">
-
-
-
diff --git a/storage/lib/src/main/java/org/calyxos/backup/storage/backup/Backup.kt b/storage/lib/src/main/java/org/calyxos/backup/storage/backup/Backup.kt
index 48c74c2e..14dd470f 100644
--- a/storage/lib/src/main/java/org/calyxos/backup/storage/backup/Backup.kt
+++ b/storage/lib/src/main/java/org/calyxos/backup/storage/backup/Backup.kt
@@ -111,11 +111,12 @@ internal class Backup(
}
Log.e(TAG, "Changed files backup took $duration")
} finally {
- backupObserver?.onBackupComplete(duration?.toLongMilliseconds())
+ backupObserver?.onBackupComplete(duration?.inWholeMilliseconds)
}
}
@Throws(IOException::class, GeneralSecurityException::class)
+ @OptIn(ExperimentalTime::class)
private suspend fun backupFiles(
filesResult: FileScannerResult,
availableChunkIds: HashSet,
diff --git a/storage/lib/src/main/java/org/calyxos/backup/storage/backup/ChunksCacheRepopulater.kt b/storage/lib/src/main/java/org/calyxos/backup/storage/backup/ChunksCacheRepopulater.kt
index 764e994f..0b38d48e 100644
--- a/storage/lib/src/main/java/org/calyxos/backup/storage/backup/ChunksCacheRepopulater.kt
+++ b/storage/lib/src/main/java/org/calyxos/backup/storage/backup/ChunksCacheRepopulater.kt
@@ -8,7 +8,7 @@ import org.calyxos.backup.storage.measure
import org.calyxos.backup.storage.plugin.SnapshotRetriever
import java.io.IOException
import java.security.GeneralSecurityException
-import java.util.concurrent.TimeUnit.MILLISECONDS
+import kotlin.time.DurationUnit.MILLISECONDS
import kotlin.time.ExperimentalTime
import kotlin.time.toDuration
diff --git a/storage/lib/src/main/java/org/calyxos/backup/storage/plugin/saf/SafStoragePlugin.kt b/storage/lib/src/main/java/org/calyxos/backup/storage/plugin/saf/SafStoragePlugin.kt
index 6c143315..090ad385 100644
--- a/storage/lib/src/main/java/org/calyxos/backup/storage/plugin/saf/SafStoragePlugin.kt
+++ b/storage/lib/src/main/java/org/calyxos/backup/storage/plugin/saf/SafStoragePlugin.kt
@@ -18,6 +18,7 @@ import org.calyxos.backup.storage.plugin.saf.DocumentFileExt.listFilesBlocking
import java.io.IOException
import java.io.InputStream
import java.io.OutputStream
+import kotlin.time.ExperimentalTime
private val folderRegex = Regex("^[a-f0-9]{16}\\.sv$")
private val chunkFolderRegex = Regex("[a-f0-9]{2}")
@@ -89,6 +90,7 @@ public abstract class SafStoragePlugin(
* Chunk folders will get cached in the given [chunkFolders] for faster access.
*/
@Throws(IOException::class)
+ @OptIn(ExperimentalTime::class)
private suspend fun populateChunkFolders(
folder: DocumentFile,
chunkFolders: HashMap,
@@ -126,6 +128,7 @@ public abstract class SafStoragePlugin(
}
@Throws(IOException::class)
+ @OptIn(ExperimentalTime::class)
private fun createMissingChunkFolders(
root: DocumentFile,
chunkFolders: HashMap,
diff --git a/storage/lib/src/main/java/org/calyxos/backup/storage/prune/Pruner.kt b/storage/lib/src/main/java/org/calyxos/backup/storage/prune/Pruner.kt
index 17350158..3e503e86 100644
--- a/storage/lib/src/main/java/org/calyxos/backup/storage/prune/Pruner.kt
+++ b/storage/lib/src/main/java/org/calyxos/backup/storage/prune/Pruner.kt
@@ -47,7 +47,7 @@ internal class Pruner(
}
}
Log.i(TAG, "Pruning took $duration")
- backupObserver?.onPruneComplete(duration.toLongMilliseconds())
+ backupObserver?.onPruneComplete(duration.inWholeMilliseconds)
}
@Throws(IOException::class, GeneralSecurityException::class)
diff --git a/storage/lib/src/main/java/org/calyxos/backup/storage/restore/FileRestore.kt b/storage/lib/src/main/java/org/calyxos/backup/storage/restore/FileRestore.kt
index 01decb79..e25caf55 100644
--- a/storage/lib/src/main/java/org/calyxos/backup/storage/restore/FileRestore.kt
+++ b/storage/lib/src/main/java/org/calyxos/backup/storage/restore/FileRestore.kt
@@ -4,7 +4,6 @@ import android.content.ContentValues
import android.content.Context
import android.media.MediaScannerConnection
import android.net.Uri
-import android.os.Build.VERSION.SDK_INT
import android.os.Environment.getExternalStorageDirectory
import android.provider.MediaStore.MediaColumns
import android.util.Log
@@ -39,13 +38,7 @@ internal class FileRestore(
val finalTag: String
when {
file.mediaFile != null -> {
- bytes = if (SDK_INT < 30) {
- // MediaProvider on API 29 doesn't let us write files into any folders freely,
- // so don't attempt to restore via MediaStore API
- restoreFile(file, streamWriter)
- } else {
- restoreFile(file.mediaFile, streamWriter)
- }
+ bytes = restoreFile(file.mediaFile, streamWriter)
finalTag = "M$tag"
}
file.docFile != null -> {
@@ -112,10 +105,7 @@ internal class FileRestore(
// changing owner requires backup permission
put(MediaColumns.OWNER_PACKAGE_NAME, mediaFile.ownerPackageName)
put(MediaColumns.IS_PENDING, 1)
- if (SDK_INT >= 30) {
- val isFavorite = if (mediaFile.isFavorite) 1 else 0
- put(MediaColumns.IS_FAVORITE, isFavorite)
- }
+ put(MediaColumns.IS_FAVORITE, if (mediaFile.isFavorite) 1 else 0)
}
val contentUri = MediaType.fromBackupMediaType(mediaFile.type).contentUri
val uri = contentResolver.insert(contentUri, contentValues)!!
diff --git a/storage/lib/src/main/java/org/calyxos/backup/storage/restore/Restore.kt b/storage/lib/src/main/java/org/calyxos/backup/storage/restore/Restore.kt
index 297c1d5c..b338ac6d 100644
--- a/storage/lib/src/main/java/org/calyxos/backup/storage/restore/Restore.kt
+++ b/storage/lib/src/main/java/org/calyxos/backup/storage/restore/Restore.kt
@@ -52,6 +52,7 @@ internal class Restore(
MultiChunkRestore(context, storagePlugin, fileRestore, streamCrypto, streamKey)
}
+ @OptIn(ExperimentalTime::class)
fun getBackupSnapshots(): Flow = flow {
val numSnapshots: Int
val time = measure {
@@ -138,7 +139,7 @@ internal class Restore(
Log.e(TAG, "Restoring ${split.multiChunkFiles.size} multi chunks took $multiChunkDuration.")
val totalDuration = smallFilesDuration + singleChunkDuration + multiChunkDuration
- observer?.onRestoreComplete(totalDuration.toLongMilliseconds())
+ observer?.onRestoreComplete(totalDuration.inWholeMilliseconds)
Log.e(TAG, "Restored $restoredFiles/$filesTotal files.")
}
diff --git a/storage/lib/src/main/java/org/calyxos/backup/storage/scanner/FileScanner.kt b/storage/lib/src/main/java/org/calyxos/backup/storage/scanner/FileScanner.kt
index bbce61c4..02d9cbdb 100644
--- a/storage/lib/src/main/java/org/calyxos/backup/storage/scanner/FileScanner.kt
+++ b/storage/lib/src/main/java/org/calyxos/backup/storage/scanner/FileScanner.kt
@@ -11,6 +11,7 @@ import org.calyxos.backup.storage.content.DocFile
import org.calyxos.backup.storage.content.MediaFile
import org.calyxos.backup.storage.db.UriStore
import org.calyxos.backup.storage.measure
+import kotlin.time.ExperimentalTime
internal class FileScannerResult(
val smallFiles: List,
@@ -30,6 +31,7 @@ internal class FileScanner(
private const val FILES_LARGE = "large"
}
+ @OptIn(ExperimentalTime::class)
fun getFiles(): FileScannerResult {
// scan both APIs
val mediaFiles = ArrayList()
diff --git a/storage/lib/src/main/java/org/calyxos/backup/storage/scanner/MediaScanner.kt b/storage/lib/src/main/java/org/calyxos/backup/storage/scanner/MediaScanner.kt
index 673175e4..725e0a9d 100644
--- a/storage/lib/src/main/java/org/calyxos/backup/storage/scanner/MediaScanner.kt
+++ b/storage/lib/src/main/java/org/calyxos/backup/storage/scanner/MediaScanner.kt
@@ -5,13 +5,11 @@ import android.content.ContentUris
import android.content.Context
import android.database.Cursor
import android.net.Uri
-import android.os.Build.VERSION.SDK_INT
import android.os.Bundle
import android.os.Environment
import android.provider.MediaStore
import android.provider.MediaStore.MediaColumns.IS_DOWNLOAD
import android.util.Log
-import androidx.annotation.RequiresApi
import androidx.core.database.getIntOrNull
import androidx.core.database.getLongOrNull
import androidx.core.database.getStringOrNull
@@ -23,7 +21,7 @@ import java.io.File
public class MediaScanner(context: Context) {
private companion object {
- private val PROJECTION_29 = arrayOf(
+ private val PROJECTION = arrayOf(
MediaStore.MediaColumns._ID,
MediaStore.MediaColumns.RELATIVE_PATH,
MediaStore.MediaColumns.DISPLAY_NAME,
@@ -31,10 +29,6 @@ public class MediaScanner(context: Context) {
MediaStore.MediaColumns.SIZE,
MediaStore.MediaColumns.OWNER_PACKAGE_NAME,
MediaStore.MediaColumns.VOLUME_NAME,
- )
-
- @RequiresApi(30)
- private val PROJECTION_30 = arrayOf(
MediaStore.MediaColumns.IS_FAVORITE,
MediaStore.MediaColumns.GENERATION_MODIFIED,
)
@@ -59,7 +53,7 @@ public class MediaScanner(context: Context) {
internal fun scanMediaUri(uri: Uri, extraQuery: String? = null): List {
val extras = Bundle().apply {
val query = StringBuilder()
- if (SDK_INT >= 30 && uri != MediaType.Downloads.contentUri) {
+ if (uri != MediaType.Downloads.contentUri) {
query.append("$IS_DOWNLOAD=0")
}
extraQuery?.let {
@@ -68,8 +62,7 @@ public class MediaScanner(context: Context) {
}
if (query.isNotEmpty()) putString(QUERY_ARG_SQL_SELECTION, query.toString())
}
- val projection = if (SDK_INT >= 30) PROJECTION_29 + PROJECTION_30 else PROJECTION_29
- val cursor = contentResolver.query(uri, projection, extras, null)
+ val cursor = contentResolver.query(uri, PROJECTION, extras, null)
return ArrayList(cursor?.count ?: 0).apply {
cursor?.use { c ->
while (c.moveToNext()) add(createMediaFile(c, uri))
@@ -94,13 +87,9 @@ public class MediaScanner(context: Context) {
dir = cursor.getString(PROJECTION_PATH),
fileName = cursor.getString(PROJECTION_NAME),
dateModified = cursor.getLongOrNull(PROJECTION_DATE_MODIFIED),
- generationModified = if (SDK_INT >= 30) cursor.getLongOrNull(
- PROJECTION_GENERATION_MODIFIED
- ) else null,
+ generationModified = cursor.getLongOrNull(PROJECTION_GENERATION_MODIFIED),
size = cursor.getLong(PROJECTION_SIZE),
- isFavorite = if (SDK_INT >= 30) {
- cursor.getIntOrNull(PROJECTION_IS_FAVORITE) == 1
- } else false,
+ isFavorite = cursor.getIntOrNull(PROJECTION_IS_FAVORITE) == 1,
ownerPackageName = cursor.getStringOrNull(PROJECTION_OWNER_PACKAGE_NAME),
volume = cursor.getString(PROJECTION_VOLUME_NAME)
)
diff --git a/storage/lib/src/main/java/org/calyxos/backup/storage/ui/Notifications.kt b/storage/lib/src/main/java/org/calyxos/backup/storage/ui/Notifications.kt
index 7f979eb5..b1828bec 100644
--- a/storage/lib/src/main/java/org/calyxos/backup/storage/ui/Notifications.kt
+++ b/storage/lib/src/main/java/org/calyxos/backup/storage/ui/Notifications.kt
@@ -7,7 +7,6 @@ import android.app.NotificationManager
import android.app.NotificationManager.IMPORTANCE_LOW
import android.app.PendingIntent
import android.content.Context
-import android.os.Build.VERSION.SDK_INT
import androidx.annotation.DrawableRes
import androidx.annotation.StringRes
import androidx.core.app.NotificationCompat
@@ -145,7 +144,7 @@ internal class Notifications(private val context: Context) {
setShowWhen(false)
setWhen(System.currentTimeMillis())
setProgress(expected, transferred, expected == 0)
- if (SDK_INT >= 31) setForegroundServiceBehavior(FOREGROUND_SERVICE_IMMEDIATE)
+ setForegroundServiceBehavior(FOREGROUND_SERVICE_IMMEDIATE)
}.build()
}