From a7d7de04514e4f4bca16c77d09c74cc0d9c67366 Mon Sep 17 00:00:00 2001 From: Carter Jernigan Date: Thu, 9 Feb 2023 06:47:04 -0500 Subject: [PATCH] [#745] Set min API to 27 * Add Android min SDK API level check test --------- Co-authored-by: Honza --- app/build.gradle.kts | 2 +- .../{TargetApiTest.kt => AndroidApiTest.kt} | 15 ++++- ...ecant.android-build-conventions.gradle.kts | 12 ++-- ...secant.emulator-wtf-conventions.gradle.kts | 19 +----- build.gradle.kts | 2 +- gradle.properties | 6 +- .../EncryptedPreferenceProviderTest.kt | 3 - .../zcash/spackle/AndroidApiVersion.kt | 6 -- .../zcash/spackle/StrictModeCompat.kt | 66 ++++++------------- .../zcash/test/UiTestPrerequisites.kt | 5 -- .../zcash/ui/design/compat/FontCompat.kt | 31 --------- .../co/electriccoin/zcash/ui/MainActivity.kt | 12 +--- .../screen/support/model/EnvironmentInfo.kt | 20 ++---- 13 files changed, 50 insertions(+), 149 deletions(-) rename app/src/androidTest/java/co/electriccoin/zcash/app/{TargetApiTest.kt => AndroidApiTest.kt} (57%) delete mode 100644 ui-design-lib/src/main/java/co/electriccoin/zcash/ui/design/compat/FontCompat.kt diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 9dddec4c..f683c00f 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -304,7 +304,7 @@ fladle { val FIREBASE_TEST_LAB_MAX_SDK = 33 val minSdkVersion = run { - val buildMinSdk = project.properties["ANDROID_APP_MIN_SDK_VERSION"].toString().toInt() + val buildMinSdk = project.properties["ANDROID_MIN_SDK_VERSION"].toString().toInt() buildMinSdk.coerceAtLeast(FIREBASE_TEST_LAB_MIN_SDK).toString() } val targetSdkVersion = run { diff --git a/app/src/androidTest/java/co/electriccoin/zcash/app/TargetApiTest.kt b/app/src/androidTest/java/co/electriccoin/zcash/app/AndroidApiTest.kt similarity index 57% rename from app/src/androidTest/java/co/electriccoin/zcash/app/TargetApiTest.kt rename to app/src/androidTest/java/co/electriccoin/zcash/app/AndroidApiTest.kt index 647eff5d..4e674862 100644 --- a/app/src/androidTest/java/co/electriccoin/zcash/app/TargetApiTest.kt +++ b/app/src/androidTest/java/co/electriccoin/zcash/app/AndroidApiTest.kt @@ -6,9 +6,10 @@ import androidx.test.core.app.ApplicationProvider import androidx.test.filters.SmallTest import org.hamcrest.MatcherAssert.assertThat import org.hamcrest.Matchers.lessThanOrEqualTo +import org.junit.Assert.assertEquals import org.junit.Test -class TargetApiTest { +class AndroidApiTest { @Test @SmallTest @@ -21,4 +22,16 @@ class TargetApiTest { lessThanOrEqualTo(Build.VERSION_CODES.TIRAMISU) ) } + + @Test + @SmallTest + fun checkMinApi() { + // This test case prevents accidental release of the app with a different API level than we + // have currently set in gradle.properties. It could impact the app's functionality. Don't + // change this unless you're absolutely sure we're ready to set a new API level. + assertEquals( + ApplicationProvider.getApplicationContext().applicationInfo.minSdkVersion, + Build.VERSION_CODES.O_MR1 + ) + } } diff --git a/build-conventions-secant/src/main/kotlin/secant.android-build-conventions.gradle.kts b/build-conventions-secant/src/main/kotlin/secant.android-build-conventions.gradle.kts index 0a207454..1a2ac4ac 100644 --- a/build-conventions-secant/src/main/kotlin/secant.android-build-conventions.gradle.kts +++ b/build-conventions-secant/src/main/kotlin/secant.android-build-conventions.gradle.kts @@ -7,7 +7,7 @@ pluginManager.withPlugin("com.android.application") { configureBaseExtension(isLibrary = false) defaultConfig { - minSdk = project.property("ANDROID_APP_MIN_SDK_VERSION").toString().toInt() + minSdk = project.property("ANDROID_MIN_SDK_VERSION").toString().toInt() targetSdk = project.property("ANDROID_TARGET_SDK_VERSION").toString().toInt() // en_XA and ar_XB are pseudolocales for debugging. @@ -31,7 +31,7 @@ pluginManager.withPlugin("com.android.library") { configureBaseExtension(isLibrary = true) defaultConfig { - minSdk = project.property("ANDROID_LIB_MIN_SDK_VERSION").toString().toInt() + minSdk = project.property("ANDROID_MIN_SDK_VERSION").toString().toInt() // This is deprecated but we don't have a replacement for the instrumentation APKs yet targetSdk = project.property("ANDROID_TARGET_SDK_VERSION").toString().toInt() @@ -60,7 +60,7 @@ pluginManager.withPlugin("com.android.test") { configureBaseExtension(isLibrary = true) defaultConfig { - minSdk = project.property("ANDROID_LIB_MIN_SDK_VERSION").toString().toInt() + minSdk = project.property("ANDROID_MIN_SDK_VERSION").toString().toInt() // This is deprecated but we don't have a replacement for the instrumentation APKs yet targetSdk = project.property("ANDROID_TARGET_SDK_VERSION").toString().toInt() @@ -130,11 +130,7 @@ fun com.android.build.gradle.BaseExtension.configureBaseExtension(isLibrary: Boo val MANAGED_DEVICES_MIN_SDK = 27 val testDeviceMinSdkVersion = run { - val buildMinSdk = if (isLibrary) { - project.properties["ANDROID_LIB_MIN_SDK_VERSION"].toString().toInt() - } else { - project.properties["ANDROID_APP_MIN_SDK_VERSION"].toString().toInt() - } + val buildMinSdk = project.properties["ANDROID_MIN_SDK_VERSION"].toString().toInt() buildMinSdk.coerceAtLeast(MANAGED_DEVICES_MIN_SDK) } val testDeviceMaxSdkVersion = project.properties["ANDROID_TARGET_SDK_VERSION"].toString().toInt() diff --git a/build-conventions-secant/src/main/kotlin/secant.emulator-wtf-conventions.gradle.kts b/build-conventions-secant/src/main/kotlin/secant.emulator-wtf-conventions.gradle.kts index 29e9a889..af8693c5 100644 --- a/build-conventions-secant/src/main/kotlin/secant.emulator-wtf-conventions.gradle.kts +++ b/build-conventions-secant/src/main/kotlin/secant.emulator-wtf-conventions.gradle.kts @@ -1,6 +1,3 @@ -import com.android.build.gradle.internal.cxx.configure.buildTypeOf -import com.android.build.gradle.internal.dsl.decorator.androidPluginDslDecorator - // Emulator WTF has min and max values that might differ from our project's // These are determined by `ew-cli --models` @@ -24,18 +21,8 @@ pluginManager.withPlugin("wtf.emulator.gradle") { environmentVariables.set(mapOf("useTestStorageService" to "false")) } - val buildMinSdk = if (pluginManager.hasPlugin("com.android.application")) { - project.properties["ANDROID_APP_MIN_SDK_VERSION"].toString().toInt() - } else if (pluginManager.hasPlugin("com.android.test")) { - project.properties["ANDROID_APP_MIN_SDK_VERSION"].toString().toInt() - } else if (pluginManager.hasPlugin("com.android.library")) { - project.properties["ANDROID_LIB_MIN_SDK_VERSION"].toString().toInt() - } else { - throw IllegalArgumentException("Unsupported plugin type. Make sure that the plugin type you've added is" + - " supported by ${this.javaClass.name}.") - } - - val moduleMinSdkVersion = run { + val minSdkVersion = run { + val buildMinSdk = project.properties["ANDROID_MIN_SDK_VERSION"].toString().toInt() buildMinSdk.coerceAtLeast(EMULATOR_WTF_MIN_SDK).toString() } val targetSdkVersion = run { @@ -45,7 +32,7 @@ pluginManager.withPlugin("wtf.emulator.gradle") { devices.set( listOf( - mapOf("model" to "Pixel2", "version" to moduleMinSdkVersion), + mapOf("model" to "Pixel2", "version" to minSdkVersion), mapOf("model" to "Pixel2", "version" to targetSdkVersion) ) ) diff --git a/build.gradle.kts b/build.gradle.kts index 2cd45a5f..107d781a 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -151,7 +151,7 @@ fladle { val minSdkVersion = run { // Fladle will use the app APK as the additional APK, so we have to // use the app's minSdkVersion here. - val buildMinSdk = project.properties["ANDROID_APP_MIN_SDK_VERSION"].toString().toInt() + val buildMinSdk = project.properties["ANDROID_MIN_SDK_VERSION"].toString().toInt() buildMinSdk.coerceAtLeast(FIREBASE_TEST_LAB_MIN_SDK).toString() } val targetSdkVersion = run { diff --git a/gradle.properties b/gradle.properties index bf27ec36..8a8b4f63 100644 --- a/gradle.properties +++ b/gradle.properties @@ -92,11 +92,7 @@ SDK_INCLUDED_BUILD_PATH= BIP_39_INCLUDED_BUILD_PATH= # Versions -# A lower version on the libraries helps to ensure some degree of backwards compatiblity, for the project -# as a whole. But a higher version on the app ensures that we aren't directly supporting users -# with old devices. -ANDROID_LIB_MIN_SDK_VERSION=24 -ANDROID_APP_MIN_SDK_VERSION=27 +ANDROID_MIN_SDK_VERSION=27 ANDROID_TARGET_SDK_VERSION=33 ANDROID_COMPILE_SDK_VERSION=33 diff --git a/preference-impl-android-lib/src/androidTest/java/co/electriccoin/zcash/preference/EncryptedPreferenceProviderTest.kt b/preference-impl-android-lib/src/androidTest/java/co/electriccoin/zcash/preference/EncryptedPreferenceProviderTest.kt index c8efa6a1..070ff3e2 100644 --- a/preference-impl-android-lib/src/androidTest/java/co/electriccoin/zcash/preference/EncryptedPreferenceProviderTest.kt +++ b/preference-impl-android-lib/src/androidTest/java/co/electriccoin/zcash/preference/EncryptedPreferenceProviderTest.kt @@ -1,9 +1,7 @@ package co.electriccoin.zcash.preference import android.content.Context -import android.os.Build import androidx.test.core.app.ApplicationProvider -import androidx.test.filters.SdkSuppress import androidx.test.filters.SmallTest import co.electriccoin.zcash.preference.test.fixture.StringDefaultPreferenceFixture import kotlinx.coroutines.runBlocking @@ -68,7 +66,6 @@ class EncryptedPreferenceProviderTest { // e.g. the directory path and the fact the preferences are stored as XML @Test @SmallTest - @SdkSuppress(minSdkVersion = Build.VERSION_CODES.N) fun verify_no_plaintext() = runBlocking { val expectedValue = StringDefaultPreferenceFixture.DEFAULT_VALUE + "extra" diff --git a/spackle-android-lib/src/main/kotlin/co/electriccoin/zcash/spackle/AndroidApiVersion.kt b/spackle-android-lib/src/main/kotlin/co/electriccoin/zcash/spackle/AndroidApiVersion.kt index 3d15a9f4..6bb6a11c 100644 --- a/spackle-android-lib/src/main/kotlin/co/electriccoin/zcash/spackle/AndroidApiVersion.kt +++ b/spackle-android-lib/src/main/kotlin/co/electriccoin/zcash/spackle/AndroidApiVersion.kt @@ -15,12 +15,6 @@ object AndroidApiVersion { return Build.VERSION.SDK_INT >= sdk } - @ChecksSdkIntAtLeast(api = Build.VERSION_CODES.N) - val isAtLeastN = isAtLeast(Build.VERSION_CODES.N) - - @ChecksSdkIntAtLeast(api = Build.VERSION_CODES.O) - val isAtLeastO = isAtLeast(Build.VERSION_CODES.O) - @ChecksSdkIntAtLeast(api = Build.VERSION_CODES.P) val isAtLeastP = isAtLeast(Build.VERSION_CODES.P) diff --git a/spackle-android-lib/src/main/kotlin/co/electriccoin/zcash/spackle/StrictModeCompat.kt b/spackle-android-lib/src/main/kotlin/co/electriccoin/zcash/spackle/StrictModeCompat.kt index 28d3905e..efe383cc 100644 --- a/spackle-android-lib/src/main/kotlin/co/electriccoin/zcash/spackle/StrictModeCompat.kt +++ b/spackle-android-lib/src/main/kotlin/co/electriccoin/zcash/spackle/StrictModeCompat.kt @@ -1,25 +1,12 @@ package co.electriccoin.zcash.spackle import android.annotation.SuppressLint -import android.os.Build -import android.os.Handler -import android.os.Looper import android.os.StrictMode object StrictModeCompat { fun enableStrictMode(isCrashOnViolation: Boolean) { configureStrictMode(isCrashOnViolation) - - // Workaround for Android bug - // https://issuetracker.google.com/issues/36951662 - // Not needed if target O_MR1 and running on O_MR1 - // Don't really need to check target, because of Google Play enforcement on targetSdkVersion for app updates - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O_MR1) { - Handler(Looper.getMainLooper()).postAtFrontOfQueue { - configureStrictMode(isCrashOnViolation) - } - } } @SuppressLint("NewApi") @@ -38,39 +25,26 @@ object StrictModeCompat { ) // Don't enable missing network tags, because those are noisy. - if (AndroidApiVersion.isAtLeastO) { - StrictMode.setVmPolicy( - StrictMode.VmPolicy.Builder().apply { - detectActivityLeaks() - detectCleartextNetwork() - detectContentUriWithoutPermission() - detectFileUriExposure() - detectLeakedClosableObjects() - detectLeakedRegistrationObjects() - detectLeakedSqlLiteObjects() - if (AndroidApiVersion.isAtLeastP) { - // Disable because this is mostly flagging Android X and Play Services - // builder.detectNonSdkApiUsage(); - } + StrictMode.setVmPolicy( + StrictMode.VmPolicy.Builder().apply { + detectActivityLeaks() + detectCleartextNetwork() + detectContentUriWithoutPermission() + detectFileUriExposure() + detectLeakedClosableObjects() + detectLeakedRegistrationObjects() + detectLeakedSqlLiteObjects() + if (AndroidApiVersion.isAtLeastP) { + // Disable because this is mostly flagging Android X and Play Services + // builder.detectNonSdkApiUsage(); + } - if (isCrashOnViolation) { - penaltyDeath() - } else { - penaltyLog() - } - }.build() - ) - } else { - StrictMode.setVmPolicy( - StrictMode.VmPolicy.Builder().apply { - detectAll() - if (isCrashOnViolation) { - penaltyDeath() - } else { - penaltyLog() - } - }.build() - ) - } + if (isCrashOnViolation) { + penaltyDeath() + } else { + penaltyLog() + } + }.build() + ) } } diff --git a/test-lib/src/main/kotlin/co/electriccoin/zcash/test/UiTestPrerequisites.kt b/test-lib/src/main/kotlin/co/electriccoin/zcash/test/UiTestPrerequisites.kt index 2d1bd73a..c16056b0 100644 --- a/test-lib/src/main/kotlin/co/electriccoin/zcash/test/UiTestPrerequisites.kt +++ b/test-lib/src/main/kotlin/co/electriccoin/zcash/test/UiTestPrerequisites.kt @@ -1,9 +1,7 @@ package co.electriccoin.zcash.test -import android.annotation.TargetApi import android.app.KeyguardManager import android.content.Context -import android.os.Build import android.os.PowerManager import androidx.test.core.app.ApplicationProvider import org.junit.Before @@ -15,21 +13,18 @@ import org.junit.Before // Originally hoped to put this into ZcashUiTestRunner, although it causes reporting of test results to fail open class UiTestPrerequisites { @Before - @TargetApi(Build.VERSION_CODES.KITKAT_WATCH) fun verifyPrerequisites() { assertScreenIsOn() assertKeyguardIsUnlocked() } companion object { - @TargetApi(Build.VERSION_CODES.KITKAT_WATCH) fun assertScreenIsOn() { if (!isScreenOn()) { throw AssertionError("Screen must be on for Android UI tests to run") // $NON-NLS } } - @TargetApi(Build.VERSION_CODES.KITKAT_WATCH) private fun isScreenOn(): Boolean { val powerService = ApplicationProvider.getApplicationContext() .getSystemService(Context.POWER_SERVICE) as PowerManager diff --git a/ui-design-lib/src/main/java/co/electriccoin/zcash/ui/design/compat/FontCompat.kt b/ui-design-lib/src/main/java/co/electriccoin/zcash/ui/design/compat/FontCompat.kt deleted file mode 100644 index 8dfdfca2..00000000 --- a/ui-design-lib/src/main/java/co/electriccoin/zcash/ui/design/compat/FontCompat.kt +++ /dev/null @@ -1,31 +0,0 @@ -package co.electriccoin.zcash.ui.design.compat - -import android.content.Context -import androidx.annotation.FontRes -import androidx.core.content.res.ResourcesCompat -import co.electriccoin.zcash.spackle.AndroidApiVersion -import co.electriccoin.zcash.ui.design.R -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.withContext - -object FontCompat { - fun isFontPrefetchNeeded() = !AndroidApiVersion.isAtLeastO - - suspend fun prefetchFontsLegacy(context: Context) { - prefetchFontLegacy(context, R.font.zboto) - } - - /** - * Pre-fetches fonts on Android N (API 25) and below. - */ - /* - * ResourcesCompat is used implicitly by Compose on older Android versions. - * The backwards compatibility library performs disk IO and then - * caches the results. This moves that IO off the main thread, to prevent ANRs and - * jank during app startup. - */ - private suspend fun prefetchFontLegacy(context: Context, @FontRes fontRes: Int) = - withContext(Dispatchers.IO) { - ResourcesCompat.getFont(context, fontRes) - } -} diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/MainActivity.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/MainActivity.kt index 261cfeac..5e19a36b 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/MainActivity.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/MainActivity.kt @@ -13,10 +13,8 @@ import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen import androidx.lifecycle.compose.collectAsStateWithLifecycle -import androidx.lifecycle.lifecycleScope import androidx.navigation.NavHostController import co.electriccoin.zcash.ui.common.BindCompLocalProvider -import co.electriccoin.zcash.ui.design.compat.FontCompat import co.electriccoin.zcash.ui.design.component.ConfigurationOverride import co.electriccoin.zcash.ui.design.component.GradientSurface import co.electriccoin.zcash.ui.design.component.Override @@ -28,7 +26,6 @@ import co.electriccoin.zcash.ui.screen.onboarding.WrapOnboarding import co.electriccoin.zcash.ui.screen.warning.WrapNotEnoughSpace import co.electriccoin.zcash.ui.screen.warning.viewmodel.StorageCheckViewModel import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.launch import kotlin.time.Duration import kotlin.time.Duration.Companion.milliseconds import kotlin.time.Duration.Companion.seconds @@ -49,14 +46,7 @@ class MainActivity : ComponentActivity() { setupSplashScreen() - if (FontCompat.isFontPrefetchNeeded()) { - lifecycleScope.launch { - FontCompat.prefetchFontsLegacy(applicationContext) - setupUiContent() - } - } else { - setupUiContent() - } + setupUiContent() } private fun setupSplashScreen() { diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/support/model/EnvironmentInfo.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/support/model/EnvironmentInfo.kt index 4a662698..3c5f1d9f 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/support/model/EnvironmentInfo.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/support/model/EnvironmentInfo.kt @@ -1,10 +1,8 @@ package co.electriccoin.zcash.ui.screen.support.model -import android.annotation.SuppressLint import android.content.Context import cash.z.ecc.sdk.ext.ui.model.MonetarySeparators import co.electriccoin.zcash.global.StorageChecker -import co.electriccoin.zcash.spackle.AndroidApiVersion import java.util.Locale class EnvironmentInfo(val locale: Locale, val monetarySeparators: MonetarySeparators, val usableStorageMegabytes: Int) { @@ -20,20 +18,12 @@ class EnvironmentInfo(val locale: Locale, val monetarySeparators: MonetarySepara suspend fun new(context: Context): EnvironmentInfo { val usableStorage = StorageChecker.checkAvailableStorageMegabytes() - return EnvironmentInfo(currentLocale(context), MonetarySeparators.current(), usableStorage) + return EnvironmentInfo( + context.resources.configuration.locales[0], + MonetarySeparators.current(), + usableStorage + ) } - - private fun currentLocale(context: Context) = if (AndroidApiVersion.isAtLeastN) { - currentLocaleNPlus(context) - } else { - currentLocaleLegacy(context) - } - - @SuppressLint("NewApi") - private fun currentLocaleNPlus(context: Context) = context.resources.configuration.locales[0] - - @Suppress("Deprecation") - private fun currentLocaleLegacy(context: Context) = context.resources.configuration.locale } }