From 36e77c9200e9b63ee65308025e4e2eadd82192ea Mon Sep 17 00:00:00 2001 From: Mohamed Metwalli <mohamed.metwalli@sap.com> Date: Tue, 1 Jun 2021 17:31:11 +0200 Subject: [PATCH] Target Android 11 (DEV) (#3310) * Target Android 11 * Update QrCodeFullScreenFragment.kt * Consistency on different versions * Move to IntentBuilder constructor * Run screenshots * Fix notification test * Update ScreenShotter.kt * Delete ScreenshotUnderTest.kt * Update ScreenShotter.kt * Docs * Revert Co-authored-by: harambasicluka <64483219+harambasicluka@users.noreply.github.com> Co-authored-by: BMItter <Berndus@gmx.de> --- Corona-Warn-App/build.gradle | 6 +- .../java/testhelpers/ScreenShotter.kt | 14 +++- .../java/testhelpers/ScreenshotUnderTest.kt | 15 ---- .../SettingsCrashReportDetailsFragment.kt | 3 +- .../ui/DataDonationTestFragment.kt | 20 +++--- .../ui/TestRiskLevelCalculationFragment.kt | 3 +- .../debuglog/ui/DebugLogFragment.kt | 2 +- .../overview/ContactDiaryOverviewFragment.kt | 3 +- .../common/PresenceTracingNotifications.kt | 15 ++-- .../fullscreen/QrCodeFullScreenFragment.kt | 71 +++++-------------- .../coronawarnapp/util/files/FileSharing.kt | 9 ++- .../common/TraceLocationNotificationsTest.kt | 7 +- 12 files changed, 62 insertions(+), 106 deletions(-) delete mode 100644 Corona-Warn-App/src/androidTest/java/testhelpers/ScreenshotUnderTest.kt diff --git a/Corona-Warn-App/build.gradle b/Corona-Warn-App/build.gradle index f1ce04d22..948ff9a9f 100644 --- a/Corona-Warn-App/build.gradle +++ b/Corona-Warn-App/build.gradle @@ -22,12 +22,12 @@ android { println("Current VERSION_PATCH: ${VERSION_PATCH}") println("Current VERSION_BUILD: ${VERSION_BUILD}") - compileSdkVersion 29 + compileSdkVersion 30 buildToolsVersion "30.0.3" defaultConfig { applicationId 'de.rki.coronawarnapp' minSdkVersion 23 - targetSdkVersion 29 + targetSdkVersion 30 ndkVersion "21.2.6472646" @@ -314,7 +314,7 @@ dependencies { // ANDROID STANDARD def nav_version = "2.3.3" implementation 'androidx.appcompat:appcompat:1.2.0' - implementation 'androidx.core:core-ktx:1.3.2' + implementation 'androidx.core:core-ktx:1.5.0' implementation 'com.google.android.material:material:1.3.0' implementation 'androidx.constraintlayout:constraintlayout:2.0.4' implementation "androidx.navigation:navigation-fragment-ktx:$nav_version" diff --git a/Corona-Warn-App/src/androidTest/java/testhelpers/ScreenShotter.kt b/Corona-Warn-App/src/androidTest/java/testhelpers/ScreenShotter.kt index 3898fdd65..b965fced3 100644 --- a/Corona-Warn-App/src/androidTest/java/testhelpers/ScreenShotter.kt +++ b/Corona-Warn-App/src/androidTest/java/testhelpers/ScreenShotter.kt @@ -2,6 +2,7 @@ package testhelpers import android.app.Activity import android.graphics.Bitmap +import android.os.Build import android.os.Bundle import android.util.Log import androidx.annotation.StyleRes @@ -9,6 +10,7 @@ import androidx.fragment.app.Fragment import androidx.fragment.app.FragmentFactory import androidx.test.espresso.ViewAction import androidx.test.platform.app.InstrumentationRegistry +import de.rki.coronawarnapp.BuildConfig import de.rki.coronawarnapp.R import java.io.BufferedOutputStream import java.io.File @@ -47,14 +49,15 @@ inline fun <reified F : Fragment> captureScreenshot( * Saves screenshots on the device's sdcard */ object SDCard { - private const val ROOT_DIRECTORY = "/sdcard" + private const val SDCARD_DIRECTORY = "/sdcard" + private const val DATA_DIRECTORY = "/data/data/${BuildConfig.APPLICATION_ID}" private const val SCREENSHOTS_DIRECTORY = "screenshots" private const val SCREENSHOT_FORMAT = ".png" private const val IMAGE_QUALITY = 100 fun screenshotCaptured(screenshotName: String, screenshot: Bitmap) { try { - val directory = File(ROOT_DIRECTORY, SCREENSHOTS_DIRECTORY) + val directory = File(rootDir, SCREENSHOTS_DIRECTORY) if (!directory.exists()) { directory.mkdirs() } @@ -72,4 +75,11 @@ object SDCard { throw RuntimeException("Unable to capture screenshot.", e) } } + + private val rootDir: String by lazy { + // Screenshots are saved in local directory on API 30+ due to scoped storage changes. + // Developer can explore screenshots taken locally using "Device File Explorer" in Android studio. + // Firebase TL runs screenshots on API 29 and pulls them from sdcard. + if (Build.VERSION.SDK_INT < 30) SDCARD_DIRECTORY else DATA_DIRECTORY + } } diff --git a/Corona-Warn-App/src/androidTest/java/testhelpers/ScreenshotUnderTest.kt b/Corona-Warn-App/src/androidTest/java/testhelpers/ScreenshotUnderTest.kt deleted file mode 100644 index 7d1de56b8..000000000 --- a/Corona-Warn-App/src/androidTest/java/testhelpers/ScreenshotUnderTest.kt +++ /dev/null @@ -1,15 +0,0 @@ -package testhelpers - -/** - * Similar to [Screenshot]. it is helpful during development and testing process to filter - * the test currently being implemented. - * In fastlane folder. replace `Screenshot` with `ScreenshotUnderTest` in Screengrabfile - * - * Note: this is only for testing purposes and should NOT be used in final tests - */ -@kotlin.annotation.Retention(AnnotationRetention.RUNTIME) -@Target( - AnnotationTarget.FUNCTION, - AnnotationTarget.CLASS -) -annotation class ScreenshotUnderTest diff --git a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/crash.ui/SettingsCrashReportDetailsFragment.kt b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/crash.ui/SettingsCrashReportDetailsFragment.kt index 4c7eb4270..9f111bf48 100644 --- a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/crash.ui/SettingsCrashReportDetailsFragment.kt +++ b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/crash.ui/SettingsCrashReportDetailsFragment.kt @@ -37,8 +37,7 @@ class SettingsCrashReportDetailsFragment : private fun shareCrashReport() { activity?.let { activity -> - val shareIntent = ShareCompat.IntentBuilder - .from(activity) + val shareIntent = ShareCompat.IntentBuilder(activity) .setType("text/plain") .setText(fragmentSettingsCrashReportDetailsBinding.textViewCrashReportDetails.text) .createChooserIntent() diff --git a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/datadonation/ui/DataDonationTestFragment.kt b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/datadonation/ui/DataDonationTestFragment.kt index ee563f419..58e5c94ed 100644 --- a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/datadonation/ui/DataDonationTestFragment.kt +++ b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/datadonation/ui/DataDonationTestFragment.kt @@ -55,20 +55,20 @@ class DataDonationTestFragment : Fragment(R.layout.fragment_test_datadonation), } vm.copyJWSEvent.observe2(this) { jws -> - val intent = ShareCompat.IntentBuilder.from(requireActivity()).apply { - setType("text/plain") - setSubject("JWS") - setText(jws) - }.createChooserIntent() + val intent = ShareCompat.IntentBuilder(requireActivity()) + .setType("text/plain") + .setSubject("JWS") + .setText(jws) + .createChooserIntent() startActivity(intent) } vm.copyAnalyticsEvent.observe2(this) { analytics -> - val intent = ShareCompat.IntentBuilder.from(requireActivity()).apply { - setType("text/plain") - setSubject("Analytics") - setText(analytics) - }.createChooserIntent() + val intent = ShareCompat.IntentBuilder(requireActivity()) + .setType("text/plain") + .setSubject("Analytics") + .setText(analytics) + .createChooserIntent() startActivity(intent) } diff --git a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/risklevel/ui/TestRiskLevelCalculationFragment.kt b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/risklevel/ui/TestRiskLevelCalculationFragment.kt index d8e1a5ba4..0a30d78f8 100644 --- a/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/risklevel/ui/TestRiskLevelCalculationFragment.kt +++ b/Corona-Warn-App/src/deviceForTesters/java/de/rki/coronawarnapp/test/risklevel/ui/TestRiskLevelCalculationFragment.kt @@ -106,8 +106,7 @@ class TestRiskLevelCalculationFragment : Timber.d("Opening Share-Intent for Exposure Windows") val shareFileUri = FileProvider.getUriForFile(requireContext(), requireContext().packageName + ".fileProvider", file) - val shareIntent = ShareCompat.IntentBuilder - .from(requireActivity()) + val shareIntent = ShareCompat.IntentBuilder(requireActivity()) .setStream(shareFileUri) .setType("text/plain") .intent diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/bugreporting/debuglog/ui/DebugLogFragment.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/bugreporting/debuglog/ui/DebugLogFragment.kt index 0f2eb8372..5acf8408a 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/bugreporting/debuglog/ui/DebugLogFragment.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/bugreporting/debuglog/ui/DebugLogFragment.kt @@ -174,7 +174,7 @@ class DebugLogFragment : Fragment(R.layout.bugreporting_debuglog_fragment), Auto MaterialAlertDialogBuilder(requireContext()).apply { setTitle(R.string.errors_generic_headline_short) setMessage(R.string.debugging_debuglog_start_low_storage_error) - setPositiveButton(android.R.string.yes) { _, _ -> /* dismiss */ } + setPositiveButton(android.R.string.ok) { _, _ -> /* dismiss */ } setNeutralButton(R.string.menu_settings) { _, _ -> try { startActivity(Intent(Settings.ACTION_INTERNAL_STORAGE_SETTINGS)) diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/overview/ContactDiaryOverviewFragment.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/overview/ContactDiaryOverviewFragment.kt index 5b26115e8..af6a5c380 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/overview/ContactDiaryOverviewFragment.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/contactdiary/ui/overview/ContactDiaryOverviewFragment.kt @@ -86,8 +86,7 @@ class ContactDiaryOverviewFragment : Fragment(R.layout.contact_diary_overview_fr private fun exportLocationsAndPersons(exportString: String) { Timber.d("exportLocationsAndPersons(exportString=$exportString)") activity?.let { activity -> - val shareIntent = ShareCompat.IntentBuilder - .from(activity) + val shareIntent = ShareCompat.IntentBuilder(activity) .setType("text/plain") .setSubject(getString(R.string.contact_diary_export_subject)) .setText(exportString) diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/presencetracing/common/PresenceTracingNotifications.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/presencetracing/common/PresenceTracingNotifications.kt index 65da46f62..90833b66e 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/presencetracing/common/PresenceTracingNotifications.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/presencetracing/common/PresenceTracingNotifications.kt @@ -2,10 +2,9 @@ package de.rki.coronawarnapp.presencetracing.common import android.annotation.TargetApi import android.app.Notification -import android.app.NotificationChannel -import android.app.NotificationManager import android.content.Context import android.os.Build +import androidx.core.app.NotificationChannelCompat import androidx.core.app.NotificationCompat import androidx.core.app.NotificationManagerCompat import androidx.navigation.NavDeepLinkBuilder @@ -37,13 +36,13 @@ class PresenceTracingNotifications @Inject constructor( fun setupChannel() { Timber.d("setupChannel()") - val channel = NotificationChannel( + val channel = NotificationChannelCompat.Builder( channelId, - context.getString(R.string.tracelocation_notification_channel_title), - NotificationManager.IMPORTANCE_DEFAULT - ).apply { - description = context.getString(R.string.tracelocation_notification_channel_description) - } + NotificationManagerCompat.IMPORTANCE_DEFAULT + ) + .setName(context.getString(R.string.tracelocation_notification_channel_title)) + .setDescription(context.getString(R.string.tracelocation_notification_channel_description)) + .build() notificationManagerCompat.createNotificationChannel(channel) } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/qrcode/fullscreen/QrCodeFullScreenFragment.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/qrcode/fullscreen/QrCodeFullScreenFragment.kt index ec0a46a97..349a66229 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/qrcode/fullscreen/QrCodeFullScreenFragment.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/qrcode/fullscreen/QrCodeFullScreenFragment.kt @@ -1,19 +1,12 @@ package de.rki.coronawarnapp.ui.qrcode.fullscreen import android.graphics.Color -import android.os.Build import android.os.Bundle import android.view.View -import android.view.View.SYSTEM_UI_FLAG_FULLSCREEN -import android.view.View.SYSTEM_UI_FLAG_HIDE_NAVIGATION -import android.view.View.SYSTEM_UI_FLAG_IMMERSIVE -import android.view.View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN -import android.view.View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION -import android.view.View.SYSTEM_UI_FLAG_LAYOUT_STABLE -import android.view.View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR -import android.view.View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR -import android.view.View.SYSTEM_UI_FLAG_VISIBLE import android.view.animation.AccelerateInterpolator +import androidx.core.view.WindowInsetsCompat.Type +import androidx.core.view.WindowInsetsControllerCompat +import androidx.core.view.WindowInsetsControllerCompat.BEHAVIOR_SHOW_BARS_BY_SWIPE import androidx.fragment.app.Fragment import androidx.navigation.fragment.navArgs import com.google.android.material.transition.MaterialContainerTransform @@ -29,10 +22,9 @@ import javax.inject.Inject class QrCodeFullScreenFragment : Fragment(R.layout.fragment_qr_code_full_screen), AutoInject { @Inject lateinit var viewModelFactory: CWAViewModelFactoryProvider.Factory - - private val binding: FragmentQrCodeFullScreenBinding by viewBinding() + private val binding by viewBinding<FragmentQrCodeFullScreenBinding>() private val args by navArgs<QrCodeFullScreenFragmentArgs>() - private val viewModel: QrCodeFullScreenViewModel by cwaViewModelsAssisted( + private val viewModel by cwaViewModelsAssisted<QrCodeFullScreenViewModel>( factoryProducer = { viewModelFactory }, constructorCall = { factory, _ -> factory as QrCodeFullScreenViewModel.Factory @@ -43,6 +35,8 @@ class QrCodeFullScreenFragment : Fragment(R.layout.fragment_qr_code_full_screen) } ) + private val insetsController by lazy { insetsController() } + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val containerTransform = MaterialContainerTransform() @@ -72,53 +66,24 @@ class QrCodeFullScreenFragment : Fragment(R.layout.fragment_qr_code_full_screen) override fun onStop() { super.onStop() - clearSystemUiFlags() - } - - private fun clearSystemUiFlags() { - decorView.systemUiVisibility = withLightUiFlags(SYSTEM_UI_FLAG_VISIBLE) - binding.toolbar.animate().translationY(0.0f) + exitImmersiveMode() } private fun exitImmersiveMode() { - binding.toolbar.animate().translationY(0.0f) - showSystemUI() + binding.toolbar.animate().alpha(1.0f) + insetsController.show(Type.systemBars()) } private fun enterImmersiveMode() { - hideSystemUI() - binding.toolbar.apply { - animate().translationY(-height.toFloat()) - } - } - - private fun hideSystemUI() { - decorView.systemUiVisibility = withLightUiFlags( - SYSTEM_UI_FLAG_IMMERSIVE - or SYSTEM_UI_FLAG_LAYOUT_STABLE - or SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION - or SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN - or SYSTEM_UI_FLAG_HIDE_NAVIGATION - or SYSTEM_UI_FLAG_FULLSCREEN - ) + insetsController.hide(Type.systemBars()) + binding.toolbar.animate().alpha(0.0f) } - private fun showSystemUI() { - decorView.systemUiVisibility = withLightUiFlags( - SYSTEM_UI_FLAG_LAYOUT_STABLE - or SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION - or SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN - ) - } - - private val decorView: View get() = requireActivity().window.decorView - - private fun withLightUiFlags(flags: Int): Int { - var uiFlags = flags - if (resources.getBoolean(R.bool.lightSystemUI)) { - uiFlags = uiFlags or SYSTEM_UI_FLAG_LIGHT_STATUS_BAR - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) uiFlags = uiFlags or SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR - } - return uiFlags + private fun insetsController(): WindowInsetsControllerCompat { + val window = requireActivity().window + return WindowInsetsControllerCompat(window, window.decorView) + .apply { + systemBarsBehavior = BEHAVIOR_SHOW_BARS_BY_SWIPE + } } } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/files/FileSharing.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/files/FileSharing.kt index 632ee2a10..efbf9cd1e 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/files/FileSharing.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/files/FileSharing.kt @@ -30,11 +30,10 @@ class FileSharing @Inject constructor( createChooserIntent: Boolean = false ): FileIntentProvider = object : FileIntentProvider { override fun intent(activity: Activity): Intent { - val builder = ShareCompat.IntentBuilder.from(activity).apply { - setType(path.determineMimeType()) - setStream(getFileUri(path)) - setSubject(title) - } + val builder = ShareCompat.IntentBuilder(activity) + .setType(path.determineMimeType()) + .setStream(getFileUri(path)) + .setSubject(title) val intent = if (createChooserIntent) { builder.createChooserIntent() diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/presencetracing/common/TraceLocationNotificationsTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/presencetracing/common/TraceLocationNotificationsTest.kt index d74619fb9..e4787c4dc 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/presencetracing/common/TraceLocationNotificationsTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/presencetracing/common/TraceLocationNotificationsTest.kt @@ -1,6 +1,7 @@ package de.rki.coronawarnapp.presencetracing.common import android.content.Context +import androidx.core.app.NotificationChannelCompat import androidx.core.app.NotificationManagerCompat import de.rki.coronawarnapp.util.BuildVersionWrap import io.mockk.MockKAnnotations @@ -33,7 +34,7 @@ class TraceLocationNotificationsTest : BaseTest() { every { BuildVersionWrap.SDK_INT } returns 42 notificationManager.apply { - every { createNotificationChannel(any()) } just Runs + every { createNotificationChannel(any<NotificationChannelCompat>()) } just Runs every { notify(any(), any()) } just Runs } } @@ -49,7 +50,7 @@ class TraceLocationNotificationsTest : BaseTest() { instance.sendNotification(1, mockk()) verify { - notificationManager.createNotificationChannel(any()) + notificationManager.createNotificationChannel(any<NotificationChannelCompat>()) notificationManager.notify(1, any()) } } @@ -60,7 +61,7 @@ class TraceLocationNotificationsTest : BaseTest() { val instance = createInstance() instance.sendNotification(1, mockk()) - verify(exactly = 0) { notificationManager.createNotificationChannel(any()) } + verify(exactly = 0) { notificationManager.createNotificationChannel(any<NotificationChannelCompat>()) } verify { notificationManager.notify(1, any()) } } } -- GitLab