From 82f23b7800b1ebd07d80fbadfbc9ee251e5460c6 Mon Sep 17 00:00:00 2001 From: Torsten Grote Date: Tue, 23 Aug 2022 09:50:55 -0300 Subject: [PATCH] Set minSdk to 32 and remove code for old SDKs We can't go to 33 yet, because roboelectric doesn't support that --- app/build.gradle | 2 +- .../restore/install/InstallProgressAdapter.kt | 7 +--- .../stevesoltys/seedvault/ui/AppViewHolder.kt | 11 ++--- .../recoverycode/RecoveryCodeInputFragment.kt | 9 ++-- .../seedvault/metadata/MetadataManagerTest.kt | 2 +- .../seedvault/plugins/saf/DocumentFileTest.kt | 2 +- .../restore/install/DeviceInfoTest.kt | 6 +-- build.gradle | 2 +- .../grobox/storagebackuptester/LogFragment.kt | 42 +++++-------------- .../settings/InfoFragment.kt | 9 ++-- storage/lib/src/main/AndroidManifest.xml | 7 ---- .../backup/storage/restore/FileRestore.kt | 14 +------ .../backup/storage/scanner/MediaScanner.kt | 21 +++------- .../backup/storage/ui/Notifications.kt | 3 +- 14 files changed, 36 insertions(+), 101 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index d976a9fd..17e2abd9 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" diff --git a/app/src/main/java/com/stevesoltys/seedvault/restore/install/InstallProgressAdapter.kt b/app/src/main/java/com/stevesoltys/seedvault/restore/install/InstallProgressAdapter.kt index ff08bad4..b3cc5cab 100644 --- a/app/src/main/java/com/stevesoltys/seedvault/restore/install/InstallProgressAdapter.kt +++ b/app/src/main/java/com/stevesoltys/seedvault/restore/install/InstallProgressAdapter.kt @@ -1,6 +1,5 @@ package com.stevesoltys.seedvault.restore.install -import android.os.Build.VERSION.SDK_INT import android.view.LayoutInflater import android.view.View import android.view.View.GONE @@ -81,10 +80,8 @@ internal class InstallProgressAdapter( IN_PROGRESS -> { 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/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/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/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/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/build.gradle b/build.gradle index f35c1cc2..6c90483f 100644 --- a/build.gradle +++ b/build.gradle @@ -22,7 +22,7 @@ buildscript { ext { buildToolsVersion = '33.0.0' compileSdkVersion = 33 - minSdkVersion = 29 + minSdkVersion = 32 targetSdkVersion = 33 } diff --git a/storage/demo/src/main/java/de/grobox/storagebackuptester/LogFragment.kt b/storage/demo/src/main/java/de/grobox/storagebackuptester/LogFragment.kt index fda4194b..6864347e 100644 --- a/storage/demo/src/main/java/de/grobox/storagebackuptester/LogFragment.kt +++ b/storage/demo/src/main/java/de/grobox/storagebackuptester/LogFragment.kt @@ -1,11 +1,7 @@ package de.grobox.storagebackuptester -import android.Manifest.permission.ACCESS_MEDIA_LOCATION -import android.Manifest.permission.WRITE_EXTERNAL_STORAGE import android.content.Intent -import android.content.pm.PackageManager.PERMISSION_GRANTED import android.net.Uri -import android.os.Build import android.os.Bundle import android.os.Environment import android.provider.Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION @@ -21,7 +17,6 @@ import android.widget.Button import android.widget.ProgressBar import android.widget.Toast import android.widget.Toast.LENGTH_SHORT -import androidx.activity.result.contract.ActivityResultContracts.RequestMultiplePermissions import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels import androidx.recyclerview.widget.RecyclerView @@ -43,13 +38,6 @@ open class LogFragment : Fragment() { private lateinit var button: Button private val adapter = LogAdapter() - private val permissionRequest = - registerForActivityResult(RequestMultiplePermissions()) { grantedMap -> - 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/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/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/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() }