From 05de9819e9bd27a1bf610e4de1b053da368a7924 Mon Sep 17 00:00:00 2001 From: Tsung-Mao Fang Date: Thu, 5 May 2022 18:10:45 +0800 Subject: [PATCH] Disable setting items in App details page Prior to this cl, we allow user to tap on those setting items which belong to another user profile app. However, we already observed some functional broken cases now. Such as, device can't get the storage data/screen time/mobile data battery, notification for those apps from another user profile. Therefore, we decide to grey out those setting items, and we don't allow user to tap on unsupported setting items. Test: Download apk in different user profile, and see setting items is disabled/enabled correctly. and run robo test. Fix: 230303570 Change-Id: I1bb6b1d8b52f6a00088b2f0e4279b896d568f8a6 --- .../AppBatteryPreferenceController.java | 9 +++- .../AppDataUsagePreferenceController.java | 2 + .../appinfo/AppInfoDashboardFragment.java | 1 + .../AppInfoPreferenceControllerBase.java | 6 ++- .../AppNotificationPreferenceController.java | 10 +++- .../AppStoragePreferenceController.java | 22 +++++--- .../TimeSpentInAppPreferenceController.java | 13 +++++ .../AppBatteryPreferenceControllerTest.java | 22 ++++++++ .../AppDataUsagePreferenceControllerTest.java | 51 ++++++++++++++++-- .../AppStoragePreferenceControllerTest.java | 52 ++++++++++++++++--- ...imeSpentInAppPreferenceControllerTest.java | 41 +++++++++++++++ 11 files changed, 210 insertions(+), 19 deletions(-) diff --git a/src/com/android/settings/applications/appinfo/AppBatteryPreferenceController.java b/src/com/android/settings/applications/appinfo/AppBatteryPreferenceController.java index 3cb22e07b61..d69c2c880cc 100644 --- a/src/com/android/settings/applications/appinfo/AppBatteryPreferenceController.java +++ b/src/com/android/settings/applications/appinfo/AppBatteryPreferenceController.java @@ -45,6 +45,7 @@ import com.android.settings.fuelgauge.BatteryUsageStatsLoader; import com.android.settings.fuelgauge.BatteryUtils; import com.android.settings.fuelgauge.PowerUsageFeatureProvider; import com.android.settings.overlay.FeatureFactory; +import com.android.settingslib.applications.AppUtils; import com.android.settingslib.core.lifecycle.Lifecycle; import com.android.settingslib.core.lifecycle.LifecycleObserver; import com.android.settingslib.core.lifecycle.events.OnPause; @@ -71,9 +72,10 @@ public class AppBatteryPreferenceController extends BasePreferenceController BatteryDiffEntry mBatteryDiffEntry; @VisibleForTesting boolean mIsChartGraphEnabled; + @VisibleForTesting + final AppInfoDashboardFragment mParent; private Preference mPreference; - private final AppInfoDashboardFragment mParent; private String mBatteryPercent; private final String mPackageName; private final int mUid; @@ -107,6 +109,11 @@ public class AppBatteryPreferenceController extends BasePreferenceController super.displayPreference(screen); mPreference = screen.findPreference(getPreferenceKey()); mPreference.setEnabled(false); + if (!AppUtils.isAppInstalled(mParent.getAppEntry())) { + mPreference.setSummary(""); + return; + } + loadBatteryDiffEntries(); } diff --git a/src/com/android/settings/applications/appinfo/AppDataUsagePreferenceController.java b/src/com/android/settings/applications/appinfo/AppDataUsagePreferenceController.java index 6a254cbd8c2..4848be7763f 100644 --- a/src/com/android/settings/applications/appinfo/AppDataUsagePreferenceController.java +++ b/src/com/android/settings/applications/appinfo/AppDataUsagePreferenceController.java @@ -35,6 +35,7 @@ import com.android.settings.Utils; import com.android.settings.datausage.AppDataUsage; import com.android.settings.datausage.DataUsageUtils; import com.android.settingslib.AppItem; +import com.android.settingslib.applications.AppUtils; import com.android.settingslib.core.lifecycle.LifecycleObserver; import com.android.settingslib.core.lifecycle.events.OnPause; import com.android.settingslib.core.lifecycle.events.OnResume; @@ -61,6 +62,7 @@ public class AppDataUsagePreferenceController extends AppInfoPreferenceControlle @Override public void displayPreference(PreferenceScreen screen) { super.displayPreference(screen); + mPreference.setEnabled(AppUtils.isAppInstalled(mAppEntry)); } @Override diff --git a/src/com/android/settings/applications/appinfo/AppInfoDashboardFragment.java b/src/com/android/settings/applications/appinfo/AppInfoDashboardFragment.java index 4cf591125a3..18cb4b3b80b 100755 --- a/src/com/android/settings/applications/appinfo/AppInfoDashboardFragment.java +++ b/src/com/android/settings/applications/appinfo/AppInfoDashboardFragment.java @@ -154,6 +154,7 @@ public class AppInfoDashboardFragment extends DashboardFragment final TimeSpentInAppPreferenceController timeSpentInAppPreferenceController = use( TimeSpentInAppPreferenceController.class); timeSpentInAppPreferenceController.setPackageName(packageName); + timeSpentInAppPreferenceController.setParentFragment(this); timeSpentInAppPreferenceController.initLifeCycleOwner(this); use(AppDataUsagePreferenceController.class).setParentFragment(this); diff --git a/src/com/android/settings/applications/appinfo/AppInfoPreferenceControllerBase.java b/src/com/android/settings/applications/appinfo/AppInfoPreferenceControllerBase.java index c495cbc50c8..718ddb8490b 100644 --- a/src/com/android/settings/applications/appinfo/AppInfoPreferenceControllerBase.java +++ b/src/com/android/settings/applications/appinfo/AppInfoPreferenceControllerBase.java @@ -25,6 +25,7 @@ import androidx.preference.PreferenceScreen; import com.android.settings.SettingsPreferenceFragment; import com.android.settings.core.BasePreferenceController; +import com.android.settingslib.applications.ApplicationsState; /* * Abstract base controller for the app detail preferences that refresh the state when the app state @@ -35,6 +36,7 @@ public abstract class AppInfoPreferenceControllerBase extends BasePreferenceCont protected AppInfoDashboardFragment mParent; protected Preference mPreference; + protected ApplicationsState.AppEntry mAppEntry; private final Class mDetailFragmentClass; @@ -72,10 +74,12 @@ public abstract class AppInfoPreferenceControllerBase extends BasePreferenceCont public void setParentFragment(AppInfoDashboardFragment parent) { mParent = parent; parent.addToCallbackList(this); + mAppEntry = mParent.getAppEntry(); } /** * Gets the fragment class to be launched when the preference is clicked. + * * @return the fragment to launch */ protected Class getDetailFragmentClass() { @@ -84,10 +88,10 @@ public abstract class AppInfoPreferenceControllerBase extends BasePreferenceCont /** * Gets any extras that should be passed to the fragment class when the preference is clicked. + * * @return a bundle of extras to include in the launch intent */ protected Bundle getArguments() { return null; } - } diff --git a/src/com/android/settings/applications/appinfo/AppNotificationPreferenceController.java b/src/com/android/settings/applications/appinfo/AppNotificationPreferenceController.java index bc87b44a69c..a32205bdfdb 100644 --- a/src/com/android/settings/applications/appinfo/AppNotificationPreferenceController.java +++ b/src/com/android/settings/applications/appinfo/AppNotificationPreferenceController.java @@ -22,11 +22,13 @@ import android.content.Context; import android.os.Bundle; import androidx.preference.Preference; +import androidx.preference.PreferenceScreen; import com.android.settings.R; import com.android.settings.SettingsPreferenceFragment; -import com.android.settings.notification.app.AppNotificationSettings; import com.android.settings.notification.NotificationBackend; +import com.android.settings.notification.app.AppNotificationSettings; +import com.android.settingslib.applications.AppUtils; import com.android.settingslib.applications.ApplicationsState; public class AppNotificationPreferenceController extends AppInfoPreferenceControllerBase { @@ -49,6 +51,12 @@ public class AppNotificationPreferenceController extends AppInfoPreferenceContro } } + @Override + public void displayPreference(PreferenceScreen screen) { + super.displayPreference(screen); + mPreference.setEnabled(AppUtils.isAppInstalled(mAppEntry)); + } + @Override public void updateState(Preference preference) { preference.setSummary(getNotificationSummary(mParent.getAppEntry(), mContext, mBackend)); diff --git a/src/com/android/settings/applications/appinfo/AppStoragePreferenceController.java b/src/com/android/settings/applications/appinfo/AppStoragePreferenceController.java index 4e6bf4e1428..490fa0e87b2 100644 --- a/src/com/android/settings/applications/appinfo/AppStoragePreferenceController.java +++ b/src/com/android/settings/applications/appinfo/AppStoragePreferenceController.java @@ -26,12 +26,13 @@ import androidx.annotation.VisibleForTesting; import androidx.loader.app.LoaderManager; import androidx.loader.content.Loader; import androidx.preference.Preference; +import androidx.preference.PreferenceScreen; import com.android.settings.R; import com.android.settings.SettingsPreferenceFragment; import com.android.settings.applications.AppStorageSettings; import com.android.settings.applications.FetchPackageStorageAsyncLoader; -import com.android.settingslib.applications.ApplicationsState; +import com.android.settingslib.applications.AppUtils; import com.android.settingslib.applications.StorageStatsSource; import com.android.settingslib.core.lifecycle.LifecycleObserver; import com.android.settingslib.core.lifecycle.events.OnPause; @@ -47,14 +48,22 @@ public class AppStoragePreferenceController extends AppInfoPreferenceControllerB super(context, key); } + @Override + public void displayPreference(PreferenceScreen screen) { + super.displayPreference(screen); + mPreference.setEnabled(AppUtils.isAppInstalled(mAppEntry)); + } + @Override public void updateState(Preference preference) { - final ApplicationsState.AppEntry entry = mParent.getAppEntry(); - if (entry != null && entry.info != null) { - final boolean isExternal = - (entry.info.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0; - preference.setSummary(getStorageSummary(mLastResult, isExternal)); + if (!AppUtils.isAppInstalled(mAppEntry)) { + preference.setSummary(""); + return; } + + final boolean isExternal = + (mAppEntry.info.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0; + preference.setSummary(getStorageSummary(mLastResult, isExternal)); } @Override @@ -102,5 +111,4 @@ public class AppStoragePreferenceController extends AppInfoPreferenceControllerB @Override public void onLoaderReset(Loader loader) { } - } diff --git a/src/com/android/settings/applications/appinfo/TimeSpentInAppPreferenceController.java b/src/com/android/settings/applications/appinfo/TimeSpentInAppPreferenceController.java index 27d90269e3f..3682af27f2f 100644 --- a/src/com/android/settings/applications/appinfo/TimeSpentInAppPreferenceController.java +++ b/src/com/android/settings/applications/appinfo/TimeSpentInAppPreferenceController.java @@ -31,6 +31,8 @@ import androidx.preference.PreferenceScreen; import com.android.settings.applications.ApplicationFeatureProvider; import com.android.settings.core.LiveDataController; import com.android.settings.overlay.FeatureFactory; +import com.android.settingslib.applications.AppUtils; +import com.android.settingslib.applications.ApplicationsState; import java.util.List; @@ -45,6 +47,8 @@ public class TimeSpentInAppPreferenceController extends LiveDataController { private final ApplicationFeatureProvider mAppFeatureProvider; private Intent mIntent; private String mPackageName; + protected AppInfoDashboardFragment mParent; + protected ApplicationsState.AppEntry mAppEntry; public TimeSpentInAppPreferenceController(Context context, String preferenceKey) { super(context, preferenceKey); @@ -59,6 +63,14 @@ public class TimeSpentInAppPreferenceController extends LiveDataController { .putExtra(Intent.EXTRA_PACKAGE_NAME, mPackageName); } + /** + * Set a parent fragment for this controller. + */ + public void setParentFragment(AppInfoDashboardFragment parent) { + mParent = parent; + mAppEntry = mParent.getAppEntry(); + } + @Override public int getAvailabilityStatus() { if (TextUtils.isEmpty(mPackageName)) { @@ -84,6 +96,7 @@ public class TimeSpentInAppPreferenceController extends LiveDataController { if (pref != null) { pref.setIntent(mIntent); } + pref.setEnabled(AppUtils.isAppInstalled(mAppEntry)); } @Override diff --git a/tests/robotests/src/com/android/settings/applications/appinfo/AppBatteryPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/applications/appinfo/AppBatteryPreferenceControllerTest.java index bcca0f20b6f..03b3a479895 100644 --- a/tests/robotests/src/com/android/settings/applications/appinfo/AppBatteryPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/applications/appinfo/AppBatteryPreferenceControllerTest.java @@ -28,6 +28,7 @@ import static org.mockito.Mockito.when; import android.app.AppOpsManager; import android.content.Context; +import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.os.BatteryUsageStats; @@ -41,6 +42,7 @@ import androidx.preference.PreferenceScreen; import com.android.settings.SettingsActivity; import com.android.settings.fuelgauge.BatteryDiffEntry; import com.android.settings.fuelgauge.BatteryUtils; +import com.android.settingslib.applications.ApplicationsState; import org.junit.Before; import org.junit.Test; @@ -176,6 +178,26 @@ public class AppBatteryPreferenceControllerTest { assertThat(mBatteryPreference.getSummary()).isEqualTo("60% use for past 24 hours"); } + @Test + public void displayPreference_noEntry_preferenceShouldSetEmptySummary() { + mController.mParent.setAppEntry(null); + + mController.displayPreference(mScreen); + + assertThat(mBatteryPreference.getSummary()).isEqualTo(""); + } + + @Test + public void displayPreference_appIsNotInstalled_preferenceShouldSetEmptySummary() { + final ApplicationsState.AppEntry appEntry = mock(ApplicationsState.AppEntry.class); + appEntry.info = new ApplicationInfo(); + mController.mParent.setAppEntry(appEntry); + + mController.displayPreference(mScreen); + + assertThat(mBatteryPreference.getSummary()).isEqualTo(""); + } + @Test public void isBatteryStatsAvailable_hasBatteryStatsHelperAndSipper_returnTrue() { mController.mBatteryUsageStats = mBatteryUsageStats; diff --git a/tests/robotests/src/com/android/settings/applications/appinfo/AppDataUsagePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/applications/appinfo/AppDataUsagePreferenceControllerTest.java index 472bf221c57..aa7d6bc9492 100644 --- a/tests/robotests/src/com/android/settings/applications/appinfo/AppDataUsagePreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/applications/appinfo/AppDataUsagePreferenceControllerTest.java @@ -34,6 +34,7 @@ import android.os.Bundle; import androidx.loader.app.LoaderManager; import androidx.preference.Preference; +import androidx.preference.PreferenceScreen; import com.android.settings.core.BasePreferenceController; import com.android.settings.datausage.AppDataUsage; @@ -54,6 +55,8 @@ public class AppDataUsagePreferenceControllerTest { private LoaderManager mLoaderManager; @Mock private AppInfoDashboardFragment mFragment; + @Mock + private PreferenceScreen mScreen; private Context mContext; private AppDataUsagePreferenceController mController; @@ -71,7 +74,7 @@ public class AppDataUsagePreferenceControllerTest { doReturn(true).when(mController).isBandwidthControlEnabled(); assertThat(mController.getAvailabilityStatus()) - .isEqualTo(BasePreferenceController.AVAILABLE); + .isEqualTo(BasePreferenceController.AVAILABLE); } @Test @@ -79,13 +82,14 @@ public class AppDataUsagePreferenceControllerTest { doReturn(false).when(mController).isBandwidthControlEnabled(); assertThat(mController.getAvailabilityStatus()) - .isEqualTo(BasePreferenceController.CONDITIONALLY_UNAVAILABLE); + .isEqualTo(BasePreferenceController.CONDITIONALLY_UNAVAILABLE); } @Test public void onResume_notAvailable_shouldNotRestartDataLoader() { doReturn(mLoaderManager).when(mFragment).getLoaderManager(); - doReturn(BasePreferenceController.CONDITIONALLY_UNAVAILABLE).when(mController).getAvailabilityStatus(); + doReturn(BasePreferenceController.CONDITIONALLY_UNAVAILABLE).when( + mController).getAvailabilityStatus(); mController.onResume(); @@ -130,4 +134,45 @@ public class AppDataUsagePreferenceControllerTest { verify(preference).setSummary(any()); } + + @Test + public void displayPreference_noEntry_preferenceShouldNotEnable() { + mController.mAppEntry = null; + Preference preference = new Preference(mContext); + when(mScreen.findPreference(any())).thenReturn(preference); + doReturn(true).when(mController).isBandwidthControlEnabled(); + + mController.displayPreference(mScreen); + + assertThat(preference.isEnabled()).isFalse(); + } + + @Test + public void displayPreference_appIsInstalled_preferenceShouldEnable() { + final AppEntry appEntry = mock(AppEntry.class); + appEntry.info = new ApplicationInfo(); + appEntry.info.flags = ApplicationInfo.FLAG_INSTALLED; + mController.mAppEntry = appEntry; + Preference preference = new Preference(mContext); + when(mScreen.findPreference(any())).thenReturn(preference); + doReturn(true).when(mController).isBandwidthControlEnabled(); + + mController.displayPreference(mScreen); + + assertThat(preference.isEnabled()).isTrue(); + } + + @Test + public void displayPreference_appIsNotInstalled_preferenceShouldDisable() { + final AppEntry appEntry = mock(AppEntry.class); + appEntry.info = new ApplicationInfo(); + mController.mAppEntry = appEntry; + Preference preference = new Preference(mContext); + when(mScreen.findPreference(any())).thenReturn(preference); + doReturn(true).when(mController).isBandwidthControlEnabled(); + + mController.displayPreference(mScreen); + + assertThat(preference.isEnabled()).isFalse(); + } } diff --git a/tests/robotests/src/com/android/settings/applications/appinfo/AppStoragePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/applications/appinfo/AppStoragePreferenceControllerTest.java index 065544b2f68..27e5db7a2ed 100644 --- a/tests/robotests/src/com/android/settings/applications/appinfo/AppStoragePreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/applications/appinfo/AppStoragePreferenceControllerTest.java @@ -21,7 +21,6 @@ import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -32,6 +31,7 @@ import android.os.Bundle; import androidx.loader.app.LoaderManager; import androidx.preference.Preference; +import androidx.preference.PreferenceScreen; import com.android.settings.applications.AppStorageSettings; import com.android.settingslib.applications.ApplicationsState.AppEntry; @@ -52,6 +52,8 @@ public class AppStoragePreferenceControllerTest { private LoaderManager mLoaderManager; @Mock private AppInfoDashboardFragment mFragment; + @Mock + private PreferenceScreen mScreen; private Context mContext; private AppStoragePreferenceController mController; @@ -89,10 +91,48 @@ public class AppStoragePreferenceControllerTest { } @Test - public void updateState_shouldUpdatePreferenceSummary() { + public void displayPreference_noEntry_preferenceShouldNotEnable() { + mController.mAppEntry = null; + Preference preference = new Preference(mContext); + when(mScreen.findPreference(any())).thenReturn(preference); + + mController.displayPreference(mScreen); + + assertThat(preference.isEnabled()).isFalse(); + } + + @Test + public void displayPreference_appIsInstalled_preferenceShouldEnable() { final AppEntry appEntry = mock(AppEntry.class); appEntry.info = new ApplicationInfo(); - when(mFragment.getAppEntry()).thenReturn(appEntry); + appEntry.info.flags = ApplicationInfo.FLAG_INSTALLED; + mController.mAppEntry = appEntry; + Preference preference = new Preference(mContext); + when(mScreen.findPreference(any())).thenReturn(preference); + + mController.displayPreference(mScreen); + + assertThat(preference.isEnabled()).isTrue(); + } + + @Test + public void displayPreference_appIsNotInstalled_preferenceShouldDisable() { + final AppEntry appEntry = mock(AppEntry.class); + appEntry.info = new ApplicationInfo(); + mController.mAppEntry = appEntry; + Preference preference = new Preference(mContext); + when(mScreen.findPreference(any())).thenReturn(preference); + + mController.displayPreference(mScreen); + + assertThat(preference.isEnabled()).isFalse(); + } + + @Test + public void updateState_hasAppEntry_shouldUpdatePreferenceSummary() { + final AppEntry appEntry = mock(AppEntry.class); + appEntry.info = new ApplicationInfo(); + mController.mAppEntry = appEntry; Preference preference = mock(Preference.class); mController.updateState(preference); @@ -102,12 +142,12 @@ public class AppStoragePreferenceControllerTest { @Test public void updateState_entryIsNull_shouldNotUpdatePreferenceSummary() { - when(mFragment.getAppEntry()).thenReturn(null); - Preference preference = mock(Preference.class); + mController.mAppEntry = null; + Preference preference = new Preference(mContext); mController.updateState(preference); - verify(preference, never()).setSummary(any()); + assertThat(preference.getSummary()).isEqualTo(""); } @Test diff --git a/tests/robotests/src/com/android/settings/applications/appinfo/TimeSpentInAppPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/applications/appinfo/TimeSpentInAppPreferenceControllerTest.java index 775025616ba..0f3e7469ba3 100644 --- a/tests/robotests/src/com/android/settings/applications/appinfo/TimeSpentInAppPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/applications/appinfo/TimeSpentInAppPreferenceControllerTest.java @@ -20,7 +20,9 @@ import static android.content.Intent.EXTRA_PACKAGE_NAME; import static com.google.common.truth.Truth.assertThat; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.nullable; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -35,6 +37,7 @@ import androidx.preference.PreferenceScreen; import com.android.settings.core.BasePreferenceController; import com.android.settings.testutils.FakeFeatureFactory; +import com.android.settingslib.applications.ApplicationsState; import org.junit.Before; import org.junit.Test; @@ -125,4 +128,42 @@ public class TimeSpentInAppPreferenceControllerTest { verify(mFeatureFactory.applicationFeatureProvider).getTimeSpentInApp( nullable(String.class)); } + + @Test + public void displayPreference_noEntry_preferenceShouldNotEnable() { + mController.mAppEntry = null; + Preference preference = new Preference(mContext); + when(mScreen.findPreference(any())).thenReturn(preference); + + mController.displayPreference(mScreen); + + assertThat(preference.isEnabled()).isFalse(); + } + + @Test + public void displayPreference_appIsInstalled_preferenceShouldEnable() { + final ApplicationsState.AppEntry appEntry = mock(ApplicationsState.AppEntry.class); + appEntry.info = new ApplicationInfo(); + appEntry.info.flags = ApplicationInfo.FLAG_INSTALLED; + mController.mAppEntry = appEntry; + Preference preference = new Preference(mContext); + when(mScreen.findPreference(any())).thenReturn(preference); + + mController.displayPreference(mScreen); + + assertThat(preference.isEnabled()).isTrue(); + } + + @Test + public void displayPreference_appIsNotInstalled_preferenceShouldDisable() { + final ApplicationsState.AppEntry appEntry = mock(ApplicationsState.AppEntry.class); + appEntry.info = new ApplicationInfo(); + mController.mAppEntry = appEntry; + Preference preference = new Preference(mContext); + when(mScreen.findPreference(any())).thenReturn(preference); + + mController.displayPreference(mScreen); + + assertThat(preference.isEnabled()).isFalse(); + } }