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 178330347137a446bbba017e78eefdf165213ac0..58d6e0bd796160ee52a7719df0972f20daabb9e5 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 7a6b385e49000e19bc45e9c47cc5dd3678db7778..c0208b74d0c28e425600d0f5634c0729e41994f8 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 77b7402d34c144d93f7143f17c00c34995d76ae3..5cf13c0629f878b0f3f2c31d4b485d3597640799 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 0000000000000000000000000000000000000000..b5d465e9fbd0954d9ac49bb405d88c1ccf4239a7 --- /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 3c7e49013be8a55229817a5a96824722b5a3a233..258db57c53ac5fe4b0c60817f9f36ad815783419 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 179afc711a97dff1104e4bad712b146fd4d4adc5..c3e32a21d2382ab22e58c905758041298d1f4d49 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 e200dadb0909c7054ad0986a34cbbea311043a5a..7afb041b8b5d4471a2476f1e7bd3e7064e22b0ee 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 0000000000000000000000000000000000000000..a7c14291470c03b346044d284241a384ffb06099 --- /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 038e11a1885fe6bf242476af25d20ba0bc70755e..d1decae43d919e5eaf237faa76e648ba6d3fd9c6 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 0000000000000000000000000000000000000000..6107eb6fd917c32438faefd8ecd979964ffff2fd --- /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 0000000000000000000000000000000000000000..ef4ebe1ae2ded1a896273f9732e66007147e5bbb --- /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 49b89205e701f9fb68e34edc552096efbd3235e2..3051209a2f00d76b70441277c2f70e77a15d9745 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 0000000000000000000000000000000000000000..fba3d512d98f27b2defeae0c9440e8e38ea3c86e --- /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 da14ba42c2aa5a9cafc575e3a144300ffb8a76d2..13ffd42465f2d8521330216dffac087d8159dbf8 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 0ca6efe8ca0da64c57dfd852a2253cd8427774ee..2ed5436204ef831e009df21951b2f55ed0a1b42a 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 e52f804ed15db51eea73672946f5e89df94bdfbf..cb6f079b160efae458838538044eae46f909ccd9 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()