Extract abstract SafetyCenterViewModel and factory.
This is prep for adding a fake or mock viewmodel to use in unit tests
for the SafetyCenterDashboardFragment.
Bug: 229854704
Test: Deployed to phone locally
Change-Id: Ib710ec0c28843e504da1a805abb90dd6c7f0e128
diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterActivity.java b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterActivity.java
index 3058671..ae7c0cd 100644
--- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterActivity.java
+++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterActivity.java
@@ -28,9 +28,7 @@
import com.android.permissioncontroller.R;
import com.android.settingslib.collapsingtoolbar.CollapsingToolbarBaseActivity;
-/**
- * Entry-point activity for SafetyCenter.
- */
+/** Entry-point activity for SafetyCenter. */
@Keep
public final class SafetyCenterActivity extends CollapsingToolbarBaseActivity {
@@ -45,10 +43,12 @@
if (maybeRedirectIfDisabled()) return;
setTitle(getString(R.string.safety_center_dashboard_page_title));
- getSupportFragmentManager()
- .beginTransaction()
- .replace(R.id.content_frame, new SafetyCenterDashboardFragment())
- .commitNow();
+ if (savedInstanceState == null) {
+ getSupportFragmentManager()
+ .beginTransaction()
+ .add(R.id.content_frame, new SafetyCenterDashboardFragment())
+ .commitNow();
+ }
}
@Override
diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterDashboardFragment.java b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterDashboardFragment.java
index a9e1775..fe1c418 100644
--- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterDashboardFragment.java
+++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/SafetyCenterDashboardFragment.java
@@ -33,12 +33,14 @@
import android.widget.Toast;
import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
import androidx.lifecycle.ViewModelProvider;
import androidx.preference.PreferenceCategory;
import androidx.preference.PreferenceFragmentCompat;
import androidx.preference.PreferenceGroup;
import com.android.permissioncontroller.R;
+import com.android.permissioncontroller.safetycenter.ui.model.LiveSafetyCenterViewModelFactory;
import com.android.permissioncontroller.safetycenter.ui.model.SafetyCenterViewModel;
import java.util.List;
@@ -53,17 +55,42 @@
private static final String ENTRIES_GROUP_KEY = "entries_group";
private static final String STATIC_ENTRIES_GROUP_KEY = "static_entries_group";
+ @Nullable
+ private final ViewModelProvider.Factory mSafetyCenterViewModelFactoryOverride;
+
private SafetyStatusPreference mSafetyStatusPreference;
private PreferenceGroup mIssuesGroup;
private PreferenceGroup mEntriesGroup;
private PreferenceGroup mStaticEntriesGroup;
private SafetyCenterViewModel mViewModel;
+ public SafetyCenterDashboardFragment() {
+ this(null);
+ }
+
+ /**
+ * Allows providing an override view model factory for testing this fragment. The view model
+ * factory will not be retained between recreations of the fragment.
+ */
+ @VisibleForTesting
+ public SafetyCenterDashboardFragment(
+ @Nullable ViewModelProvider.Factory safetyCenterViewModelFactoryOverride) {
+ mSafetyCenterViewModelFactoryOverride = safetyCenterViewModelFactoryOverride;
+ }
+
+ private ViewModelProvider.Factory getSafetyCenterViewModelFactory() {
+ return mSafetyCenterViewModelFactoryOverride != null
+ ? mSafetyCenterViewModelFactoryOverride
+ : new LiveSafetyCenterViewModelFactory(requireActivity().getApplication());
+ }
+
@Override
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
setPreferencesFromResource(R.xml.safety_center_dashboard, rootKey);
- mViewModel = new ViewModelProvider(requireActivity()).get(SafetyCenterViewModel.class);
+ mViewModel =
+ new ViewModelProvider(requireActivity(), getSafetyCenterViewModelFactory())
+ .get(SafetyCenterViewModel.class);
mSafetyStatusPreference =
requireNonNull(getPreferenceScreen().findPreference(SAFETY_STATUS_KEY));
diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/model/LiveSafetyCenterViewModel.kt b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/model/LiveSafetyCenterViewModel.kt
new file mode 100644
index 0000000..9d1b0a7
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/model/LiveSafetyCenterViewModel.kt
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.permissioncontroller.safetycenter.ui.model
+
+import android.app.Application
+import android.safetycenter.SafetyCenterData
+import android.safetycenter.SafetyCenterErrorDetails
+import android.safetycenter.SafetyCenterIssue
+import android.safetycenter.SafetyCenterManager
+import androidx.core.content.ContextCompat.getMainExecutor
+import androidx.lifecycle.DefaultLifecycleObserver
+import androidx.lifecycle.LifecycleOwner
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.ViewModelProvider
+
+/* A SafetyCenterViewModel that talks to the real backing service for Safety Center. */
+class LiveSafetyCenterViewModel(app: Application) : SafetyCenterViewModel(app) {
+
+ override val safetyCenterLiveData: LiveData<SafetyCenterData>
+ get() = _safetyCenterLiveData
+ override val errorLiveData: LiveData<SafetyCenterErrorDetails>
+ get() = _errorLiveData
+
+ private val _safetyCenterLiveData = SafetyCenterLiveData()
+ private val _errorLiveData = MutableLiveData<SafetyCenterErrorDetails>()
+
+ private val safetyCenterManager = app.getSystemService(SafetyCenterManager::class.java)!!
+
+ override fun dismissIssue(issue: SafetyCenterIssue) {
+ safetyCenterManager.dismissSafetyCenterIssue(issue.id)
+ }
+
+ override fun executeIssueAction(issue: SafetyCenterIssue, action: SafetyCenterIssue.Action) {
+ safetyCenterManager.executeSafetyCenterIssueAction(issue.id, action.id)
+ }
+
+ override fun rescan() {
+ safetyCenterManager.refreshSafetySources(
+ SafetyCenterManager.REFRESH_REASON_RESCAN_BUTTON_CLICK)
+ }
+
+ override fun clearError() {
+ _errorLiveData.value = null
+ }
+
+ override fun refresh() {
+ safetyCenterManager.refreshSafetySources(SafetyCenterManager.REFRESH_REASON_PAGE_OPEN)
+ }
+
+ inner class SafetyCenterLiveData :
+ MutableLiveData<SafetyCenterData>(), SafetyCenterManager.OnSafetyCenterDataChangedListener {
+
+ override fun onActive() {
+ safetyCenterManager.addOnSafetyCenterDataChangedListener(
+ getMainExecutor(app.applicationContext), this)
+ super.onActive()
+ }
+
+ override fun onInactive() {
+ safetyCenterManager.removeOnSafetyCenterDataChangedListener(this)
+ super.onInactive()
+ }
+
+ override fun onSafetyCenterDataChanged(data: SafetyCenterData) {
+ value = data
+ }
+
+ override fun onError(errorDetails: SafetyCenterErrorDetails) {
+ _errorLiveData.value = errorDetails
+ }
+ }
+
+ inner class AutoRefreshManager : DefaultLifecycleObserver {
+ // TODO(b/222323674): We may need to do this in onResume to cover certain edge cases.
+ // i.e. FMD changed from quick settings while SC is open
+ override fun onStart(owner: LifecycleOwner) {
+ refresh()
+ }
+ }
+}
+
+class LiveSafetyCenterViewModelFactory(private val app: Application) : ViewModelProvider.Factory {
+ override fun <T : ViewModel> create(modelClass: Class<T>): T {
+ @Suppress("UNCHECKED_CAST") return LiveSafetyCenterViewModel(app) as T
+ }
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/model/SafetyCenterViewModel.kt b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/model/SafetyCenterViewModel.kt
index ad6e1dd..ebd83f0 100644
--- a/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/model/SafetyCenterViewModel.kt
+++ b/PermissionController/src/com/android/permissioncontroller/safetycenter/ui/model/SafetyCenterViewModel.kt
@@ -20,64 +20,26 @@
import android.safetycenter.SafetyCenterData
import android.safetycenter.SafetyCenterErrorDetails
import android.safetycenter.SafetyCenterIssue
-import android.safetycenter.SafetyCenterManager
-import androidx.core.content.ContextCompat.getMainExecutor
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.DefaultLifecycleObserver
import androidx.lifecycle.LifecycleOwner
-import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.LiveData
-class SafetyCenterViewModel(val app: Application) : AndroidViewModel(app) {
+abstract class SafetyCenterViewModel(protected val app: Application) : AndroidViewModel(app) {
- val safetyCenterLiveData = SafetyCenterLiveData()
- val errorLiveData = MutableLiveData<SafetyCenterErrorDetails>()
+ abstract val safetyCenterLiveData: LiveData<SafetyCenterData>
+ abstract val errorLiveData: LiveData<SafetyCenterErrorDetails>
val autoRefreshManager = AutoRefreshManager()
- private val safetyCenterManager = app.getSystemService(SafetyCenterManager::class.java)!!
+ abstract fun dismissIssue(issue: SafetyCenterIssue)
- fun dismissIssue(issue: SafetyCenterIssue) {
- safetyCenterManager.dismissSafetyCenterIssue(issue.id)
- }
+ abstract fun executeIssueAction(issue: SafetyCenterIssue, action: SafetyCenterIssue.Action)
- fun executeIssueAction(issue: SafetyCenterIssue, action: SafetyCenterIssue.Action) {
- safetyCenterManager.executeSafetyCenterIssueAction(issue.id, action.id)
- }
+ abstract fun rescan()
- fun rescan() {
- safetyCenterManager.refreshSafetySources(
- SafetyCenterManager.REFRESH_REASON_RESCAN_BUTTON_CLICK)
- }
+ abstract fun clearError()
- fun clearError() {
- errorLiveData.value = null
- }
-
- private fun refresh() {
- safetyCenterManager.refreshSafetySources(SafetyCenterManager.REFRESH_REASON_PAGE_OPEN)
- }
-
- inner class SafetyCenterLiveData :
- MutableLiveData<SafetyCenterData>(), SafetyCenterManager.OnSafetyCenterDataChangedListener {
-
- override fun onActive() {
- safetyCenterManager.addOnSafetyCenterDataChangedListener(
- getMainExecutor(app.applicationContext), this)
- super.onActive()
- }
-
- override fun onInactive() {
- safetyCenterManager.removeOnSafetyCenterDataChangedListener(this)
- super.onInactive()
- }
-
- override fun onSafetyCenterDataChanged(data: SafetyCenterData) {
- value = data
- }
-
- override fun onError(errorDetails: SafetyCenterErrorDetails) {
- errorLiveData.value = errorDetails
- }
- }
+ protected abstract fun refresh()
inner class AutoRefreshManager : DefaultLifecycleObserver {
// TODO(b/222323674): We may need to do this in onResume to cover certain edge cases.