From e85dfda01853d51a37b2dbee53026fdfa1696a77 Mon Sep 17 00:00:00 2001 From: Fabian-K <fabian.kajzar@sap.com> Date: Thu, 2 Jul 2020 15:46:36 +0200 Subject: [PATCH] Background Priority Service (#800) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [INTERNAL] Translation delivery: commit by LX Lab Change-Id: Ic0905f66e03544c549ee6bf0edde3707c528215c * Translation branch update (#127) * Implement submission done fragment (#20) * Added submission done fragment (currently dangling in nav graph) * fixed leftover javadoc * Moved QR code scan and registration fragment to submission package (#21) * Dependency Updates and Gradle Fixes (#34) * Bump up Gradle to 4.0.0 / 6.1.1 * Bump up NDK * Bump Up SQLite * adapt circle config * Fix #23: Typo in onboarding screen (#31) Co-authored-by: Jakob Möller <jakob.moeller@sap.com> * Submission UI Additions (#25) * Moved camera permission check to dispatcher fragment * Added illustrations for test result screen * Updated result fragment to match new design * Added constraints to status card * Integrated submission result positive other warning fragment in ui flow * Added submission done fragment to nav graph Signed-off-by: Kolya Opahle <k.opahle@sap.com> * Added ability to dialog helper to create dialog without negative button * Switched from AlertDialog.Builder to DIalogHelper * Enable diagnosis key upload to backend (#35) * Moved camera permission check to dispatcher fragment * Added illustrations for test result screen * Updated result fragment to match new design * Added constraints to status card * Integrated submission result positive other warning fragment in ui flow * add TAN fetching to the key submission transaction * remove unneeded function * Fix log message for fetching TAN * Added submission done fragment to nav graph Signed-off-by: Kolya Opahle <k.opahle@sap.com> * Added ability to dialog helper to create dialog without negative button * Switched from AlertDialog.Builder to DIalogHelper * use actual TAN (authCode) for key submission set the TAN in the corresponding header field fixes #8 * request permission to retrieve diagnosis keys * permission needs to be requested again after dialog has been presented to the users * trigger transaction to retrieve TAN and upload keys Co-authored-by: Kolya Opahle <k.opahle@sap.com> * Removed ExposureNotificationIntentService, the functionality is handled by NotificationHelper (#40) * Fixing tele tan registration (#45) * Fixed teleTAN based device registration Signed-off-by: Kolya Opahle <k.opahle@sap.com> * allowed excluded tan chars for testing Signed-off-by: Kolya Opahle <k.opahle@sap.com> * Revert "allowed excluded tan chars for testing" This reverts commit 253ede4ea9c8ba2b5e6dd324680eed0f8f7e74c1. * Release/0.5.5 (#48) * Release/0.5.5 * Hotfix for resetting Gradle Version (internal pipeline confirmation) (#52) * Gradle Reset to 0.5.4 (#53) * Release/0.5.5 (#48) * Hotfix for resetting Gradle Version (internal pipeline confirmation) (#52) * Minify and Shrink Disabled (#56) * Release/0.5.5 * Gradle Reset to 0.5.4 (#53) * Release/0.5.5 (#48) * Hotfix for resetting Gradle Version (internal pipeline confirmation) (#52) * Disable minify / shrink * Dont obfuscate * 0.5.6 * Update known issues and fixing typo in readme (#44) * added strings wip to known issues * updated readme Co-authored-by: Muschko <marc.muschko@sap.com> * Updated strings, introduced more details on app-information screens (#88) * updated strings for risk card and risk card details; plural strings are todo when finalized * updated more strings on main / details * added comments * added strings for information about, information technical hotline; changed technical hotline layout * removed hotline test icon * fixed build issues * Cleanup pull_request_template.md (#54) * Update pull_request_template.md * Removed Link to Contribution Guidelines (added by Github on the right hand side, anyhow) * Removed Link to issue #41 from title example (this has been linked quite a lot...) * 0.5.6 * Fix typo Co-authored-by: Jakob Möller <jakob.moeller@sap.com> Co-authored-by: harambasicluka <64483219+harambasicluka@users.noreply.github.com> Co-authored-by: marcmuschko <marc.muschko@sap.com> * Add Support for lower case characters in the teleTAN screen (#66) * added support for lower case input in teleTan fragment Signed-off-by: Kolya Opahle <k.opahle@sap.com> * Removed notice of case insensitivity from temporary strings.xml Signed-off-by: Kolya Opahle <k.opahle@sap.com> Co-authored-by: harambasicluka <64483219+harambasicluka@users.noreply.github.com> * Correct typos in architecture overview (#67) Co-authored-by: marcmuschko <marc.muschko@sap.com> * Bluetooth & connection card (#97) * introduced settings navigation helper * added connection and bluetooth stati to main and tracing settings * moved strings * removed unnecessary backgroundTint Co-authored-by: marcmuschko <marc.muschko@sap.com> * Fix typo in class documentation (#71) * 0.5.6 * fix typo Co-authored-by: Jakob Möller <jakob.moeller@sap.com> Co-authored-by: marcmuschko <marc.muschko@sap.com> * spelling fixes (#87) * 0.5.6 * spelling fixes Co-authored-by: Jakob Möller <jakob.moeller@sap.com> Co-authored-by: klemens <ka7@github.com> Co-authored-by: marcmuschko <marc.muschko@sap.com> * Moves creation of notification channel to app startup (#32) Co-authored-by: Jakob Möller <jakob.moeller@sap.com> * Tracing start/stop fix due to missing handling inside tracing (#99) Co-authored-by: Jakob Möller <jakob.moeller@sap.com> * Switch the DB Password to use the KeyStore MasterKey (#98) Signed-off-by: d067928 <jakob.moeller@sap.com> * Shared prefs ktx shorthand (#38) * closes #33 * Add Commit Flag to ensure synchronous consistent Updates to Disk Signed-off-by: d067928 <jakob.moeller@sap.com> * Add persistance of timestamp and whether user is allowed to submit keys (#74) * Store timestamp of receiving registration token * also reset this timestamp when deleting the registration token * use correct timestamp for display of test time * replace tracing activation time by test result reception time * rename TAN request to match context * set isAllowedToSubmitDiagnosisKeys properly Co-authored-by: Jakob Möller <jakob.moeller@sap.com> * set "enabled" for include_settings_switch_row in notifications settings, fixes wrong binding and removed clickable rows (#101) Co-authored-by: marcmuschko <marc.muschko@sap.com> * fix #82 (#94) * fix #82 Co-authored-by: Jakob Möller <jakob.moeller@sap.com> * Add ShareHelper (#85) * Add ShareHelper * Use ShareHelper in MainShareFragment * Replace the current exposure status with a positive result card when test result is positive (#79) * Made submission status card style more cross device friendly Signed-off-by: Kolya Opahle <k.opahle@sap.com> * Added the positive result submission status card Signed-off-by: Kolya Opahle <k.opahle@sap.com> * lint fix * Updated main fragment documentation * Added icon for sharing risk status Signed-off-by: Kolya Opahle <k.opahle@sap.com> * Updated positive result home screen card text and icons Signed-off-by: Kolya Opahle <k.opahle@sap.com> * Added loading indicator to test result fragment (#100) Co-authored-by: marcmuschko <marc.muschko@sap.com> Co-authored-by: harambasicluka <64483219+harambasicluka@users.noreply.github.com> * Adjusted onboarding flow, adjusted contribution file, small fixes in settings (#106) * changed permission dialog logic for onboarding tracing fragment and onboarding notification fragment * adjusted contributing file * added settings notification handling to display value in settings overview * fixed app crash during bluetooth settings navigation * corrected wrong value from view model in tracing settings text * Submission Contact Fragment & StepView Custom View (#102) * - submission-contact fragment added - StepEntry custom view added to unify various step entry views - SimpleStepEntry as a simple implementation of a StepEntry with title and text added * - apply naming conventions for ids - use CallHelper to trigger a call - access views using kotlinx.android.synthetic Co-authored-by: harambasicluka <64483219+harambasicluka@users.noreply.github.com> * Notifications when risk value has changed between low and high. (#76) * Notifications when risk value has changed between low and high. * Notifications when risk value has changed between low and high. Fixed wildcard imports. * Fixed formatting, ktlint issue. * app foreground/background checking. Notifications are not sent when app is in background * update comments * initialize foreground flag as false(valid for background threads before ui starts) * comments * included UNDERTERMINED as a low, for initialisation * fixed icon * fixed tests Co-authored-by: Jakob Möller <jakob.moeller@sap.com> * added onboarding strings (#109) * disable colored output for ktlint (#105) Co-authored-by: harambasicluka <64483219+harambasicluka@users.noreply.github.com> Co-authored-by: Jakob Möller <jakob.moeller@sap.com> * Limit number of keys to be uploaded to server (#107) * limit the number of keys to be uploaded to 14 * fix spaces Co-authored-by: Jakob Möller <jakob.moeller@sap.com> * Fix white space, informal language and spelling of Docker in Readme (#78) * 0.5.6 * Fix spelling gonna and docker * Add missing "to" Co-authored-by: Jakob Möller <jakob.moeller@sap.com> Co-authored-by: marcmuschko <marc.muschko@sap.com> * Update build.gradle (#111) * Build.Gradle Conflict Resolving (0.8.0) (#112) * Minify and proguard fix (#113) Commented out -dontobfuscate * Circle progress bar (#96) * circle progress bar impl * ktlint format * added new ui * added circle to contact tracing without binding * added binding and final icon * added circle progress to risk card * added comments * changed default value for disableText * adapted views order to designs * removed testing value Co-authored-by: Kirill <kirill.sergeev@sap.com> * TracingStatusHelper unit test (#114) * TracingStatusHelper unit test * removed duplicate * Prevent screenshot of the app for all activities (#108) * Prevent screenshot of the app for all activities * fix ktlint * code smell fix Co-authored-by: harambasicluka <64483219+harambasicluka@users.noreply.github.com> * Feature: Bluetooth callback (#115) * bluetooth callback added * bound bluetooth callback to the ui Co-authored-by: Kirill <kirill.sergeev@sap.com> * Added main overview page to main menu (#120) * added overview fragment baseline * main overview draft version completed with build fixes * checked some final strings again * Fixed screen orientation to portrait (#121) * added restriction to portrait and reversed portrait orientation only * suppress SourceLockedOrientationActivity lint error * changed local config that a high match can be generated via QR code, integrated it with the UI (#125) * Feature: Translation preparation (#126) * renamed old strings and reordered * app information strings translation tags * updated translation tags for long texts * added translation comments to strings * fixed naming error * removed empty comment Co-authored-by: Muschko <marc.muschko@sap.com> Co-authored-by: Kolya Opahle <k.opahle@sap.com> Co-authored-by: Jakob Möller <jakob.moeller@sap.com> Co-authored-by: Michael Keppler <bananeweizen@gmx.de> Co-authored-by: Thomas Klingbeil <64434904+tklingbeil@users.noreply.github.com> Co-authored-by: Hee Tatt Ooi <64406309+HeeTattSap@users.noreply.github.com> Co-authored-by: Muschko <marc.muschko@sap.com> Co-authored-by: Thomas Kowark <thomas.kowark@sap.com> Co-authored-by: Robert Scheck <robert-scheck@users.noreply.github.com> Co-authored-by: Volkmar Vogel <volkmar@vogel.app> Co-authored-by: ka7 <ka7@la-evento.com> Co-authored-by: klemens <ka7@github.com> Co-authored-by: Tim Brüggenthies <tim.brueggenthies@outlook.de> Co-authored-by: mseele <mseele@gmail.com> Co-authored-by: Janik Steegmüller <janik.steegmueller@gmail.com> Co-authored-by: Fabian-K <fabian.kajzar@sap.com> Co-authored-by: Hee Tatt Ooi <hee.tatt.ooi@sap.com> Co-authored-by: Matthias Küch <mail@matthias-kuech.de> Co-authored-by: oemerb <66002424+oemerb@users.noreply.github.com> Co-authored-by: AlexanderAlferov <64849422+AlexanderAlferov@users.noreply.github.com> Co-authored-by: Kirill <kirill.sergeev@sap.com> Co-authored-by: Philipp Woessner <64482866+pwoessner@users.noreply.github.com> * [INTERNAL] Translation delivery: commit by LX Lab Change-Id: Ic0905f66e03544c549ee6bf0edde3707c528215c * Remove values-2Q Signed-off-by: d067928 <jakob.moeller@sap.com> * [INTERNAL] Translation delivery: commit by LX Lab Change-Id: Idf52dece137fdc6e314ed752a8ef6b79b08683b0 * deleted 2Q * [INTERNAL] Translation delivery: commit by LX Lab Change-Id: I63afce1d7307c912b034728d8b78d437974f818d * excluded translation from lint for dev * fixed quickBuild errors * [INTERNAL] Translation delivery: commit by LX Lab Change-Id: I0b2e12f977b0a4bf37716d85e527d8d05168120f * fixed local issues * [INTERNAL] Translation delivery: commit by LX Lab Change-Id: Ied4c086215e1392cb4ac061c9e82760f4757d5b2 * fixes * resolved merge conflicts * [INTERNAL] Translation delivery: commit by LX Lab Change-Id: I0dbb47c30c1f19b7533e953e349a3145694db366 * [INTERNAL] Translation delivery: commit by LX Lab Change-Id: If73a3358a2939902b8df4e491fb7b3beb626f8fc * [INTERNAL] Translation delivery: commit by LX Lab Change-Id: I101e87820c63ecb7c40b044ac0bc2e05331b352b * changed default value for strings * added https to english faq links * fixed error during merge * [INTERNAL] Translation delivery: commit by LX Lab Change-Id: I8b27c238c3387741363b0dc353711994217801b6 * [INTERNAL] Translation delivery: commit by LX Lab Change-Id: I9bc5d20688a11bfd5fd4a481b3c36143d4caba64 * [INTERNAL] Translation delivery: commit by LX Lab Change-Id: Ib3f20898335ded0101af92c452c766c329946611 * [INTERNAL] Translation delivery: commit by LX Lab Change-Id: I9b5bbd9b5b5d44029f694b7afca8f7251b48e86a * added privacy, technical and terms html file with english content * include Turkish translation (#792) I added Turkish to resConfigs for properly including the new translation into the build. * replaced NOTR tag with XTXT to make "main_about_link" translatable * [INTERNAL] Translation delivery: commit by LX Lab Change-Id: I01f4b04ec61a334e0b25815b03e2953e5031e0d9 * [INTERNAL] Translation delivery: commit by LX Lab Change-Id: I71fcec61630c5d99e009eeb1341fd37bdd0c55e9 * string placeholders required for background priority added (#797) * additional strings for explanatory card added (#798) * add turkish privacy & terms * [INTERNAL] Translation delivery: commit by LX Lab Change-Id: I3708d2af77b48642eee7a6562cffd935327ed875 * Implementation of "Background Priority Service (1592)" - new permission REQUEST_IGNORE_BATTERY_OPTIMIZATIONS added - settings entry to enable background priority * replaced turkish faq link with the old one * extract PowerManagementHelper.kt from Repository * update style for static code analysis... * ktlint format Co-authored-by: service-tip-git <tmsatsls@gmail.com> Co-authored-by: harambasicluka <64483219+harambasicluka@users.noreply.github.com> Co-authored-by: Kolya Opahle <k.opahle@sap.com> Co-authored-by: Jakob Möller <jakob.moeller@sap.com> Co-authored-by: Michael Keppler <bananeweizen@gmx.de> Co-authored-by: Thomas Klingbeil <64434904+tklingbeil@users.noreply.github.com> Co-authored-by: Hee Tatt Ooi <64406309+HeeTattSap@users.noreply.github.com> Co-authored-by: Muschko <marc.muschko@sap.com> Co-authored-by: Thomas Kowark <thomas.kowark@sap.com> Co-authored-by: Robert Scheck <robert-scheck@users.noreply.github.com> Co-authored-by: Volkmar Vogel <volkmar@vogel.app> Co-authored-by: ka7 <ka7@la-evento.com> Co-authored-by: klemens <ka7@github.com> Co-authored-by: Tim Brüggenthies <tim.brueggenthies@outlook.de> Co-authored-by: mseele <mseele@gmail.com> Co-authored-by: Janik Steegmüller <janik.steegmueller@gmail.com> Co-authored-by: Hee Tatt Ooi <hee.tatt.ooi@sap.com> Co-authored-by: Matthias Küch <mail@matthias-kuech.de> Co-authored-by: oemerb <66002424+oemerb@users.noreply.github.com> Co-authored-by: AlexanderAlferov <64849422+AlexanderAlferov@users.noreply.github.com> Co-authored-by: Kirill <kirill.sergeev@sap.com> Co-authored-by: Philipp Woessner <64482866+pwoessner@users.noreply.github.com> Co-authored-by: Luka Harambasic <luka.harambasic@sap.com> Co-authored-by: Karina Jung <66269900+kaluju@users.noreply.github.com> Co-authored-by: service-tip-git <tmsatsls+github.com_service-tip-git@sap.com> Co-authored-by: duchampdev <duchampdev@outlook.com> --- .../res/navigation/nav_graph.xml | 9 ++ Corona-Warn-App/src/main/AndroidManifest.xml | 2 + .../storage/SettingsRepository.kt | 10 ++ .../SettingsBackgroundPriorityFragment.kt | 84 ++++++++++++++ .../ui/settings/SettingsFragment.kt | 7 ++ .../ui/viewmodel/SettingsViewModel.kt | 7 ++ .../util/ExternalActionHelper.kt | 29 +++++ .../util/PowerManagementHelper.kt | 14 +++ .../util/formatter/FormatterSettingsHelper.kt | 23 ++++ ..._settings_background_priority_disabled.xml | 12 ++ ...c_settings_background_priority_enabled.xml | 12 ++ .../src/main/res/layout/fragment_settings.xml | 18 ++- .../fragment_settings_background_priority.xml | 106 ++++++++++++++++++ .../src/main/res/navigation/nav_graph.xml | 9 ++ .../util/ExternalActionHelperTest.kt | 15 +++ .../formatter/FormatterSettingsHelperTest.kt | 50 +++++++++ 16 files changed, 406 insertions(+), 1 deletion(-) create mode 100644 Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/settings/SettingsBackgroundPriorityFragment.kt create mode 100644 Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/PowerManagementHelper.kt create mode 100644 Corona-Warn-App/src/main/res/drawable/ic_settings_background_priority_disabled.xml create mode 100644 Corona-Warn-App/src/main/res/drawable/ic_settings_background_priority_enabled.xml create mode 100644 Corona-Warn-App/src/main/res/layout/fragment_settings_background_priority.xml diff --git a/Corona-Warn-App/src/deviceForTesters/res/navigation/nav_graph.xml b/Corona-Warn-App/src/deviceForTesters/res/navigation/nav_graph.xml index 178330347..58d6e0bd7 100644 --- a/Corona-Warn-App/src/deviceForTesters/res/navigation/nav_graph.xml +++ b/Corona-Warn-App/src/deviceForTesters/res/navigation/nav_graph.xml @@ -76,6 +76,9 @@ <action android:id="@+id/action_settingsFragment_to_settingsNotificationFragment" app:destination="@id/settingsNotificationFragment" /> + <action + android:id="@+id/action_settingsFragment_to_settingsBackgroundPriorityFragment" + app:destination="@id/settingsBackgroundPriorityFragment" /> </fragment> <fragment @@ -90,6 +93,12 @@ android:label="SettingsNotificationFragment" tools:layout="@layout/fragment_settings_notifications" /> + <fragment + android:id="@+id/settingsBackgroundPriorityFragment" + android:name="de.rki.coronawarnapp.ui.settings.SettingsBackgroundPriorityFragment" + android:label="SettingsBackgroundPriorityFragment" + tools:layout="@layout/fragment_settings_background_priority" /> + <fragment android:id="@+id/settingsResetFragment" android:name="de.rki.coronawarnapp.ui.settings.SettingsResetFragment" diff --git a/Corona-Warn-App/src/main/AndroidManifest.xml b/Corona-Warn-App/src/main/AndroidManifest.xml index 7a6b385e4..c0208b74d 100644 --- a/Corona-Warn-App/src/main/AndroidManifest.xml +++ b/Corona-Warn-App/src/main/AndroidManifest.xml @@ -18,6 +18,8 @@ android:name="android.permission.INTERNET" android:required="true" /> + <uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" /> + <application android:name="de.rki.coronawarnapp.CoronaWarnApplication" android:allowBackup="false" diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/storage/SettingsRepository.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/storage/SettingsRepository.kt index 77b7402d3..5cf13c062 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/storage/SettingsRepository.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/storage/SettingsRepository.kt @@ -4,6 +4,7 @@ import android.content.Context import androidx.core.app.NotificationManagerCompat import androidx.lifecycle.MutableLiveData import de.rki.coronawarnapp.util.ConnectivityHelper +import de.rki.coronawarnapp.util.PowerManagementHelper /** * The Settings Repository maps all setting states from different sources to MutableLiveData. @@ -25,6 +26,7 @@ object SettingsRepository { val isConnectionEnabled = MutableLiveData(true) val isBluetoothEnabled = MutableLiveData(true) val isBackgroundJobEnabled = MutableLiveData(true) + val isBackgroundPriorityEnabled = MutableLiveData(false) val manualKeyRetrievalTime = MutableLiveData<Long>() /** @@ -115,4 +117,12 @@ object SettingsRepository { fun updateManualKeyRetrievalTime(value: Long) { manualKeyRetrievalTime.postValue(value) } + + /** + * Refresh the current background priority state. + */ + fun refreshBackgroundPriorityEnabled(context: Context) { + isBackgroundPriorityEnabled.value = + PowerManagementHelper.isIgnoringBatteryOptimizations(context) + } } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/settings/SettingsBackgroundPriorityFragment.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/settings/SettingsBackgroundPriorityFragment.kt new file mode 100644 index 000000000..b5d465e9f --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/settings/SettingsBackgroundPriorityFragment.kt @@ -0,0 +1,84 @@ +package de.rki.coronawarnapp.ui.settings + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.view.accessibility.AccessibilityEvent +import androidx.fragment.app.Fragment +import androidx.fragment.app.activityViewModels +import de.rki.coronawarnapp.databinding.FragmentSettingsBackgroundPriorityBinding +import de.rki.coronawarnapp.ui.main.MainActivity +import de.rki.coronawarnapp.ui.viewmodel.SettingsViewModel +import de.rki.coronawarnapp.ui.viewmodel.TracingViewModel +import de.rki.coronawarnapp.util.ExternalActionHelper + +/** + * This is the setting background priority page. Here the user sees the background priority setting status. + * If background priority is disabled it can be activated. + * + * @see TracingViewModel + * @see SettingsViewModel + */ +class SettingsBackgroundPriorityFragment : Fragment() { + companion object { + private val TAG: String? = SettingsBackgroundPriorityFragment::class.simpleName + } + + private val settingsViewModel: SettingsViewModel by activityViewModels() + private var _binding: FragmentSettingsBackgroundPriorityBinding? = null + private val binding: FragmentSettingsBackgroundPriorityBinding get() = _binding!! + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + _binding = FragmentSettingsBackgroundPriorityBinding.inflate(inflater) + binding.settingsViewModel = settingsViewModel + binding.lifecycleOwner = this + return binding.root + } + + override fun onDestroyView() { + super.onDestroyView() + _binding = null + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + setButtonOnClickListener() + } + + override fun onResume() { + super.onResume() + binding.settingsBackgroundPriorityContainer.sendAccessibilityEvent(AccessibilityEvent.TYPE_ANNOUNCEMENT) + // refresh required data + settingsViewModel.refreshBackgroundPriorityEnabled(requireContext()) + } + + private fun setButtonOnClickListener() { + val switch = binding.settingsSwitchRowBackgroundPriority.settingsSwitchRowSwitch + val switchRow = binding.settingsSwitchRowBackgroundPriority.settingsSwitchRow + + // enable background priority + setOf(switch, switchRow).forEach { + it.setOnClickListener { + val isPriorityEnabled = settingsViewModel.isBackgroundPriorityEnabled.value == true + + if (!isPriorityEnabled) + ExternalActionHelper.disableBatteryOptimizations(requireContext()) + } + } + + // explanatory card + binding.settingsTracingStatusConnection.tracingStatusCardButton.setOnClickListener { + ExternalActionHelper.toBatteryOptimizationSettings(requireContext()) + } + + // back navigation + binding.settingsBackgroundPriorityHeader.headerButtonBack.buttonIcon.setOnClickListener { + (activity as MainActivity).goBack() + } + } +} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/settings/SettingsFragment.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/settings/SettingsFragment.kt index 3c7e49013..258db57c5 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/settings/SettingsFragment.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/settings/SettingsFragment.kt @@ -60,6 +60,7 @@ class SettingsFragment : Fragment() { settingsViewModel.refreshNotificationsEnabled(requireContext()) settingsViewModel.refreshNotificationsRiskEnabled() settingsViewModel.refreshNotificationsTestEnabled() + settingsViewModel.refreshBackgroundPriorityEnabled(requireContext()) binding.settingsContainer.sendAccessibilityEvent(AccessibilityEvent.TYPE_ANNOUNCEMENT) } @@ -67,6 +68,7 @@ class SettingsFragment : Fragment() { private fun setButtonOnClickListener() { val tracingRow = binding.settingsTracing.settingsRow val notificationRow = binding.settingsNotifications.settingsRow + val backgroundPriorityRow = binding.settingsBackgroundPriority.settingsRow val resetRow = binding.settingsReset val goBack = binding.settingsHeader.headerButtonBack.buttonIcon resetRow.setOnClickListener { @@ -84,6 +86,11 @@ class SettingsFragment : Fragment() { SettingsFragmentDirections.actionSettingsFragmentToSettingsNotificationFragment() ) } + backgroundPriorityRow.setOnClickListener { + findNavController().doNavigate( + SettingsFragmentDirections.actionSettingsFragmentToSettingsBackgroundPriorityFragment() + ) + } goBack.setOnClickListener { (activity as MainActivity).goBack() } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/viewmodel/SettingsViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/viewmodel/SettingsViewModel.kt index 179afc711..c3e32a21d 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/viewmodel/SettingsViewModel.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/viewmodel/SettingsViewModel.kt @@ -25,6 +25,9 @@ class SettingsViewModel : ViewModel() { // Will impact UI if background activity is not permitted, persistent storing is not necessary val isBackgroundJobEnabled: LiveData<Boolean> = SettingsRepository.isBackgroundJobEnabled + val isBackgroundPriorityEnabled: LiveData<Boolean> = + SettingsRepository.isBackgroundPriorityEnabled + /** * Is manual key retrieval enabled * Used for "Update" button on the Risk Card and in the Risk Details @@ -114,4 +117,8 @@ class SettingsViewModel : ViewModel() { fun updateManualKeyRetrievalEnabled(value: Boolean) { SettingsRepository.updateManualKeyRetrievalEnabled(value) } + + fun refreshBackgroundPriorityEnabled(context: Context) { + SettingsRepository.refreshBackgroundPriorityEnabled(context) + } } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/ExternalActionHelper.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/ExternalActionHelper.kt index e200dadb0..7afb041b8 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/ExternalActionHelper.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/ExternalActionHelper.kt @@ -155,4 +155,33 @@ object ExternalActionHelper { ) } } + + fun disableBatteryOptimizations(context: Context) { + try { + val intent = Intent( + Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS, + Uri.parse("package:" + context.packageName) + ) + context.startActivity(intent) + } catch (exception: Exception) { + // catch generic exception on settings navigation + // most likely due to device / rom specific intent issue + ExternalActionException(exception).report( + ExceptionCategory.UI + ) + } + } + + fun toBatteryOptimizationSettings(context: Context) { + try { + val intent = Intent(Settings.ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS) + context.startActivity(intent) + } catch (exception: Exception) { + // catch generic exception on settings navigation + // most likely due to device / rom specific intent issue + ExternalActionException(exception).report( + ExceptionCategory.UI + ) + } + } } diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/PowerManagementHelper.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/PowerManagementHelper.kt new file mode 100644 index 000000000..a7c142914 --- /dev/null +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/PowerManagementHelper.kt @@ -0,0 +1,14 @@ +package de.rki.coronawarnapp.util + +import android.content.Context +import android.os.PowerManager + +object PowerManagementHelper { + /** + * Checks if app is excluded from battery optimizations + */ + fun isIgnoringBatteryOptimizations(context: Context): Boolean { + val powerManager = context.getSystemService(Context.POWER_SERVICE) as PowerManager + return powerManager.isIgnoringBatteryOptimizations(context.packageName) + } +} diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/formatter/FormatterSettingsHelper.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/formatter/FormatterSettingsHelper.kt index 038e11a18..d1decae43 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/formatter/FormatterSettingsHelper.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/formatter/FormatterSettingsHelper.kt @@ -267,6 +267,29 @@ fun formatSettingsTracingIcon( } } +/** + * Formats the settings icon for background priority + */ +fun formatSettingsBackgroundPriorityIcon( + enabled: Boolean +): Drawable? = formatDrawable( + enabled, + R.drawable.ic_settings_background_priority_enabled, + R.drawable.ic_settings_background_priority_disabled +) + +/** + * Formats the settings icon color for background priority + */ +fun formatSettingsBackgroundPriorityIconColor( + enabled: Boolean +): Int = + formatColor( + enabled, + R.color.colorAccentTintIcon, + R.color.colorTextSemanticRed + ) + /** * Formats the tracing switch status based on the tracing status * diff --git a/Corona-Warn-App/src/main/res/drawable/ic_settings_background_priority_disabled.xml b/Corona-Warn-App/src/main/res/drawable/ic_settings_background_priority_disabled.xml new file mode 100644 index 000000000..6107eb6fd --- /dev/null +++ b/Corona-Warn-App/src/main/res/drawable/ic_settings_background_priority_disabled.xml @@ -0,0 +1,12 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="40dp" + android:height="40dp" + android:viewportWidth="40" + android:viewportHeight="40"> + <path + android:pathData="M17.44,25.7C18.59,26.51 19.99,27 21.5,27C22.9375,27 24.2729,26.5681 25.3838,25.8269L26.8184,27.2609C25.3284,28.3542 23.4895,29 21.5,29C19.44,29 17.54,28.3 16.02,27.14L16.02,27.14ZM13.943,11.398L14.0095,11.4567L29.3036,26.7509C29.5792,27.0265 29.5792,27.4735 29.3036,27.7491C29.0491,28.0036 28.6487,28.0232 28.3718,27.8079L28.3053,27.7491L13.0112,12.455C12.7355,12.1794 12.7355,11.7324 13.0112,11.4567C13.2657,11.2023 13.666,11.1827 13.943,11.398ZM21.5,11C26.47,11 30.5,15.03 30.5,20C30.5,21.9619 29.872,23.7773 28.8062,25.2561L27.3697,23.8189C28.0847,22.7211 28.5,21.4097 28.5,20C28.5,16.13 25.37,13 21.5,13C20.0903,13 18.7789,13.4153 17.6811,14.1303L16.2439,12.6938C17.7227,11.628 19.5381,11 21.5,11ZM14.2391,14.6816L15.6731,16.1162C14.9319,17.2271 14.5,18.5625 14.5,20L14.5,20L17.5,20L13.5,24L9.5,20L12.5,20C12.5,18.0105 13.1458,16.1716 14.2391,14.6816Z" + android:strokeWidth="1" + android:fillColor="#C00F2D" + android:fillType="nonZero" + android:strokeColor="#00000000"/> +</vector> diff --git a/Corona-Warn-App/src/main/res/drawable/ic_settings_background_priority_enabled.xml b/Corona-Warn-App/src/main/res/drawable/ic_settings_background_priority_enabled.xml new file mode 100644 index 000000000..ef4ebe1ae --- /dev/null +++ b/Corona-Warn-App/src/main/res/drawable/ic_settings_background_priority_enabled.xml @@ -0,0 +1,12 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="40dp" + android:height="40dp" + android:viewportWidth="40" + android:viewportHeight="40"> + <path + android:pathData="M23.5,20C23.5,18.9 22.6,18 21.5,18C20.4,18 19.5,18.9 19.5,20C19.5,21.1 20.4,22 21.5,22C22.6,22 23.5,21.1 23.5,20ZM21.5,11C16.53,11 12.5,15.03 12.5,20L9.5,20L13.5,24L17.5,20L14.5,20C14.5,16.13 17.63,13 21.5,13C25.37,13 28.5,16.13 28.5,20C28.5,23.87 25.37,27 21.5,27C19.99,27 18.59,26.51 17.44,25.7L16.02,27.14C17.54,28.3 19.44,29 21.5,29C26.47,29 30.5,24.97 30.5,20C30.5,15.03 26.47,11 21.5,11Z" + android:strokeWidth="1" + android:fillColor="#007FAD" + android:fillType="nonZero" + android:strokeColor="#00000000"/> +</vector> diff --git a/Corona-Warn-App/src/main/res/layout/fragment_settings.xml b/Corona-Warn-App/src/main/res/layout/fragment_settings.xml index 49b89205e..3051209a2 100644 --- a/Corona-Warn-App/src/main/res/layout/fragment_settings.xml +++ b/Corona-Warn-App/src/main/res/layout/fragment_settings.xml @@ -80,6 +80,22 @@ app:subtitle="@{@string/settings_notifications_title}" app:tracingViewModel="@{tracingViewModel}" /> + <include + android:id="@+id/settings_background_priority" + layout="@layout/include_setting_row" + android:layout_width="@dimen/match_constraint" + android:layout_height="wrap_content" + app:body="@{@string/settings_background_priority_body_description}" + app:color="@{FormatterSettingsHelper.formatSettingsBackgroundPriorityIconColor(settingsViewModel.isBackgroundPriorityEnabled())}" + app:icon="@{FormatterSettingsHelper.formatSettingsBackgroundPriorityIcon(settingsViewModel.isBackgroundPriorityEnabled())}" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@+id/settings_notifications" + app:showDivider="@{true}" + app:statusText="@{FormatterSettingsHelper.formatStatus(settingsViewModel.isBackgroundPriorityEnabled())}" + app:subtitle="@{@string/settings_background_priority_title}" + app:tracingViewModel="@{tracingViewModel}" /> + <androidx.constraintlayout.widget.ConstraintLayout android:id="@+id/settings_reset" style="@style/row" @@ -87,7 +103,7 @@ android:layout_height="wrap_content" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toBottomOf="@+id/settings_notifications"> + app:layout_constraintTop_toBottomOf="@+id/settings_background_priority"> <androidx.constraintlayout.widget.ConstraintLayout android:layout_width="0dp" diff --git a/Corona-Warn-App/src/main/res/layout/fragment_settings_background_priority.xml b/Corona-Warn-App/src/main/res/layout/fragment_settings_background_priority.xml new file mode 100644 index 000000000..fba3d512d --- /dev/null +++ b/Corona-Warn-App/src/main/res/layout/fragment_settings_background_priority.xml @@ -0,0 +1,106 @@ +<?xml version="1.0" encoding="utf-8"?> +<layout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto"> + + <data> + + <import type="de.rki.coronawarnapp.util.formatter.FormatterHelper" /> + + <import type="de.rki.coronawarnapp.util.formatter.FormatterSettingsHelper" /> + + <variable + name="settingsViewModel" + type="de.rki.coronawarnapp.ui.viewmodel.SettingsViewModel" /> + </data> + + <androidx.constraintlayout.widget.ConstraintLayout + android:id="@+id/settings_background_priority_container" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:contentDescription="@string/settings_background_priority_title" + android:focusable="true"> + + <include + android:id="@+id/settings_background_priority_header" + layout="@layout/include_header" + android:layout_width="@dimen/match_constraint" + android:layout_height="wrap_content" + app:icon="@{@drawable/ic_back}" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" + app:title="@{@string/settings_background_priority_title}" /> + + <ScrollView + android:layout_width="@dimen/match_constraint" + android:layout_height="@dimen/match_constraint" + android:fillViewport="true" + app:layout_constraintBottom_toBottomOf="@+id/guideline_bottom" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@+id/settings_background_priority_header"> + + <androidx.constraintlayout.widget.ConstraintLayout + android:layout_width="match_parent" + android:layout_height="wrap_content"> + + <include + android:id="@+id/settings_background_priority_header_details" + layout="@layout/include_information_details" + android:layout_width="@dimen/match_constraint" + android:layout_height="wrap_content" + app:body="@{@string/settings_background_priority_body}" + app:headline="@{@string/settings_background_priority_headline}" + app:illustration="@{@drawable/ic_settings_illustration_reset}" + app:illustrationDescription="@{@string/settings_background_priority_illustration_description}" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" /> + + <include + android:id="@+id/settings_switch_row_background_priority" + layout="@layout/include_settings_switch_row" + android:layout_width="@dimen/match_constraint" + android:layout_height="wrap_content" + android:layout_marginTop="@dimen/spacing_small" + app:enabled="@{!settingsViewModel.isBackgroundPriorityEnabled()}" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@+id/settings_background_priority_header_details" + app:showDivider="@{true}" + app:status="@{settingsViewModel.isBackgroundPriorityEnabled()}" + app:statusText="@{FormatterSettingsHelper.formatStatus(settingsViewModel.isBackgroundPriorityEnabled())}" + app:subtitle="@{@string/settings_background_priority_title}" /> + + <include + android:id="@+id/settings_tracing_status_connection" + layout="@layout/include_tracing_status_card" + android:layout_width="@dimen/match_constraint" + android:layout_height="wrap_content" + android:layout_marginTop="@dimen/spacing_small" + android:visibility="@{FormatterHelper.formatVisibility(settingsViewModel.isBackgroundPriorityEnabled())}" + app:body="@{@string/settings_background_priority_card_body}" + app:buttonText="@{@string/settings_background_priority_card_button}" + app:headline="@{@string/settings_background_priority_card_headline}" + app:layout_constraintEnd_toStartOf="@+id/guideline_card_end" + app:layout_constraintStart_toStartOf="@+id/guideline_card_start" + app:layout_constraintTop_toBottomOf="@id/settings_switch_row_background_priority" /> + + <include layout="@layout/merge_guidelines_side" /> + + <include layout="@layout/merge_guidelines_card" /> + + </androidx.constraintlayout.widget.ConstraintLayout> + + </ScrollView> + + <androidx.constraintlayout.widget.Guideline + android:id="@+id/guideline_bottom" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:orientation="horizontal" + app:layout_constraintGuide_end="@dimen/guideline_bottom" /> + + </androidx.constraintlayout.widget.ConstraintLayout> + +</layout> \ No newline at end of file diff --git a/Corona-Warn-App/src/main/res/navigation/nav_graph.xml b/Corona-Warn-App/src/main/res/navigation/nav_graph.xml index da14ba42c..13ffd4246 100644 --- a/Corona-Warn-App/src/main/res/navigation/nav_graph.xml +++ b/Corona-Warn-App/src/main/res/navigation/nav_graph.xml @@ -73,6 +73,9 @@ <action android:id="@+id/action_settingsFragment_to_settingsNotificationFragment" app:destination="@id/settingsNotificationFragment" /> + <action + android:id="@+id/action_settingsFragment_to_settingsBackgroundPriorityFragment" + app:destination="@id/settingsBackgroundPriorityFragment" /> </fragment> <fragment @@ -87,6 +90,12 @@ android:label="SettingsNotificationFragment" tools:layout="@layout/fragment_settings_notifications" /> + <fragment + android:id="@+id/settingsBackgroundPriorityFragment" + android:name="de.rki.coronawarnapp.ui.settings.SettingsBackgroundPriorityFragment" + android:label="SettingsBackgroundPriorityFragment" + tools:layout="@layout/fragment_settings_background_priority" /> + <fragment android:id="@+id/settingsResetFragment" android:name="de.rki.coronawarnapp.ui.settings.SettingsResetFragment" diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/util/ExternalActionHelperTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/util/ExternalActionHelperTest.kt index 0ca6efe8c..2ed543620 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/util/ExternalActionHelperTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/util/ExternalActionHelperTest.kt @@ -79,6 +79,21 @@ class ExternalActionHelperTest { verify(exactly = 1) { fragment.startActivity(any()) } } + @Test + fun disableBatteryOptimizations() { + every { context.packageName } returns "package_name" + every { context.startActivity(any()) } just Runs + ExternalActionHelper.disableBatteryOptimizations(context) + verify(exactly = 1) { context.startActivity(any()) } + } + + @Test + fun toBatteryOptimizationSettings() { + every { context.startActivity(any()) } just Runs + ExternalActionHelper.toBatteryOptimizationSettings(context) + verify(exactly = 1) { context.startActivity(any()) } + } + @After fun cleanUp() { unmockkAll() diff --git a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/util/formatter/FormatterSettingsHelperTest.kt b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/util/formatter/FormatterSettingsHelperTest.kt index e52f804ed..cb6f079b1 100644 --- a/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/util/formatter/FormatterSettingsHelperTest.kt +++ b/Corona-Warn-App/src/test/java/de/rki/coronawarnapp/util/formatter/FormatterSettingsHelperTest.kt @@ -6,6 +6,7 @@ import de.rki.coronawarnapp.CoronaWarnApplication import de.rki.coronawarnapp.R import io.mockk.MockKAnnotations import io.mockk.every +import io.mockk.mockk import io.mockk.impl.annotations.MockK import io.mockk.mockkObject import io.mockk.unmockkAll @@ -855,6 +856,55 @@ class FormatterSettingsHelperTest { formatNotificationImageBase(bNotifications = false) } + @Test + fun formatSettingsBackgroundPriorityIconColor() { + formatSettingsBackgroundPriorityIconColorBase(true, R.color.colorAccentTintIcon) + formatSettingsBackgroundPriorityIconColorBase(false, R.color.colorTextSemanticRed) + } + + private fun formatSettingsBackgroundPriorityIconColorBase( + enabled: Boolean, + expectedColor: Int + ) { + every { context.getColor(R.color.colorAccentTintIcon) } returns R.color.colorAccentTintIcon + every { context.getColor(R.color.colorTextSemanticRed) } returns R.color.colorTextSemanticRed + + val result = + formatSettingsBackgroundPriorityIconColor(enabled) + assertThat( + result, `is`(context.getColor(expectedColor)) + ) + } + + @Test + fun formatSettingsBackgroundPriorityIcon() { + formatSettingsBackgroundPriorityIconBase( + true, + R.drawable.ic_settings_background_priority_enabled + ) + formatSettingsBackgroundPriorityIconBase( + false, + R.drawable.ic_settings_background_priority_disabled + ) + } + + private fun formatSettingsBackgroundPriorityIconBase( + enabled: Boolean, + expectedDrawable: Int + ) { + val drawableA = mockk<Drawable>() + val drawableB = mockk<Drawable>() + + every { context.getDrawable(R.drawable.ic_settings_background_priority_enabled) } returns drawableA + every { context.getDrawable(R.drawable.ic_settings_background_priority_disabled) } returns drawableB + + val result = + formatSettingsBackgroundPriorityIcon(enabled) + assertThat( + result, `is`(context.getDrawable(expectedDrawable)) + ) + } + @After fun cleanUp() { unmockkAll() -- GitLab