Merge "Telephony.Carriers.BEARER is deprecated." into main
diff --git a/Android.bp b/Android.bp
index 8c50ed5..20f8634 100644
--- a/Android.bp
+++ b/Android.bp
@@ -103,7 +103,6 @@
"securebox",
"settings-logtags",
"statslog-settings",
- "zxing-core-1.7",
"android.hardware.dumpstate-V1.0-java",
"android.hardware.dumpstate-V1.1-java",
"android.hardware.dumpstate-V1-java",
diff --git a/res/xml/app_data_usage.xml b/res/xml/app_data_usage.xml
index aacc1f6..d5d646c 100644
--- a/res/xml/app_data_usage.xml
+++ b/res/xml/app_data_usage.xml
@@ -76,6 +76,7 @@
<PreferenceCategory
android:key="app_list"
- android:title="@string/data_usage_other_apps" />
+ android:title="@string/data_usage_other_apps"
+ settings:controller="com.android.settings.datausage.AppDataUsageListController" />
</PreferenceScreen>
diff --git a/src/com/android/settings/datausage/AppDataUsage.java b/src/com/android/settings/datausage/AppDataUsage.java
index 33307a3..e0dbcbf 100644
--- a/src/com/android/settings/datausage/AppDataUsage.java
+++ b/src/com/android/settings/datausage/AppDataUsage.java
@@ -16,6 +16,8 @@
import static android.net.NetworkPolicyManager.POLICY_REJECT_METERED_BACKGROUND;
+import static com.android.settings.datausage.lib.AppDataUsageRepository.getAppUid;
+
import android.app.Activity;
import android.app.settings.SettingsEnums;
import android.content.Context;
@@ -32,6 +34,7 @@
import android.util.IconDrawableFactory;
import android.util.Log;
import android.util.Range;
+import android.util.SparseBooleanArray;
import android.view.View;
import android.widget.AdapterView;
@@ -42,7 +45,6 @@
import androidx.loader.content.Loader;
import androidx.preference.Preference;
import androidx.preference.Preference.OnPreferenceChangeListener;
-import androidx.preference.PreferenceCategory;
import androidx.recyclerview.widget.DefaultItemAnimator;
import androidx.recyclerview.widget.RecyclerView;
@@ -78,12 +80,10 @@
private static final String KEY_BACKGROUND_USAGE = "background_usage";
private static final String KEY_APP_SETTINGS = "app_settings";
private static final String KEY_RESTRICT_BACKGROUND = "restrict_background";
- private static final String KEY_APP_LIST = "app_list";
private static final String KEY_CYCLE = "cycle";
private static final String KEY_UNRESTRICTED_DATA = "unrestricted_data_saver";
private static final int LOADER_APP_USAGE_DATA = 2;
- private static final int LOADER_APP_PREF = 3;
private PackageManager mPackageManager;
private final ArraySet<String> mPackages = new ArraySet<>();
@@ -92,7 +92,6 @@
private Preference mBackgroundUsage;
private Preference mAppSettings;
private RestrictedSwitchPreference mRestrictBackground;
- private PreferenceCategory mAppList;
private Drawable mIcon;
@VisibleForTesting
@@ -170,6 +169,7 @@
final UidDetailProvider uidDetailProvider = getUidDetailProvider();
+ final var appDataUsageListController = use(AppDataUsageListController.class);
if (mAppItem.key > 0) {
if ((!isSimHardwareVisible(mContext)) || !UserHandle.isApp(mAppItem.key)) {
final UidDetail uidDetail = uidDetailProvider.getUidDetail(mAppItem.key, true);
@@ -212,14 +212,8 @@
removePreference(KEY_APP_SETTINGS);
mAppSettings = null;
}
+ appDataUsageListController.init(mAppItem.uids);
- if (mPackages.size() > 1) {
- mAppList = findPreference(KEY_APP_LIST);
- LoaderManager.getInstance(this).restartLoader(LOADER_APP_PREF, Bundle.EMPTY,
- mAppPrefCallbacks);
- } else {
- removePreference(KEY_APP_LIST);
- }
} else {
final Context context = getActivity();
final UidDetail uidDetail = uidDetailProvider.getUidDetail(mAppItem.key, true);
@@ -230,7 +224,7 @@
removePreference(KEY_UNRESTRICTED_DATA);
removePreference(KEY_APP_SETTINGS);
removePreference(KEY_RESTRICT_BACKGROUND);
- removePreference(KEY_APP_LIST);
+ appDataUsageListController.init(new SparseBooleanArray());
}
addEntityHeader();
@@ -360,11 +354,7 @@
}
private void addUid(int uid) {
- if (Process.isSdkSandboxUid(uid)) {
- // For a sandbox process, get the associated app UID
- uid = Process.getAppUidForSdkSandboxUid(uid);
- }
- String[] packages = mPackageManager.getPackagesForUid(uid);
+ String[] packages = mPackageManager.getPackagesForUid(getAppUid(uid));
if (packages != null) {
Collections.addAll(mPackages, packages);
}
@@ -501,29 +491,6 @@
}
};
- private final LoaderManager.LoaderCallbacks<ArraySet<Preference>> mAppPrefCallbacks =
- new LoaderManager.LoaderCallbacks<>() {
- @Override
- @NonNull
- public Loader<ArraySet<Preference>> onCreateLoader(int i, Bundle bundle) {
- return new AppPrefLoader(getPrefContext(), mPackages, getPackageManager());
- }
-
- @Override
- public void onLoadFinished(@NonNull Loader<ArraySet<Preference>> loader,
- ArraySet<Preference> preferences) {
- if (preferences != null && mAppList != null) {
- for (Preference preference : preferences) {
- mAppList.addPreference(preference);
- }
- }
- }
-
- @Override
- public void onLoaderReset(@NonNull Loader<ArraySet<Preference>> loader) {
- }
- };
-
@Override
public void onDataSaverChanged(boolean isDataSaving) {
diff --git a/src/com/android/settings/datausage/AppDataUsageListController.kt b/src/com/android/settings/datausage/AppDataUsageListController.kt
new file mode 100644
index 0000000..ec944f4
--- /dev/null
+++ b/src/com/android/settings/datausage/AppDataUsageListController.kt
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2023 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.settings.datausage
+
+import android.content.Context
+import android.util.SparseBooleanArray
+import androidx.annotation.OpenForTesting
+import androidx.core.util.keyIterator
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.LifecycleOwner
+import androidx.lifecycle.lifecycleScope
+import androidx.lifecycle.repeatOnLifecycle
+import androidx.preference.PreferenceGroup
+import androidx.preference.PreferenceScreen
+import com.android.settings.core.BasePreferenceController
+import com.android.settings.datausage.lib.AppDataUsageRepository.Companion.getAppUid
+import com.android.settings.datausage.lib.AppPreferenceRepository
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
+
+@OpenForTesting
+open class AppDataUsageListController @JvmOverloads constructor(
+ context: Context,
+ preferenceKey: String,
+ private val repository: AppPreferenceRepository = AppPreferenceRepository(context),
+) : BasePreferenceController(context, preferenceKey) {
+
+ private lateinit var uids: List<Int>
+ private lateinit var preference: PreferenceGroup
+
+ fun init(uids: SparseBooleanArray) {
+ this.uids = uids.keyIterator().asSequence().map { getAppUid(it) }.distinct().toList()
+ }
+
+ override fun getAvailabilityStatus() = AVAILABLE
+
+ override fun displayPreference(screen: PreferenceScreen) {
+ super.displayPreference(screen)
+ preference = screen.findPreference(preferenceKey)!!
+ }
+
+ override fun onViewCreated(viewLifecycleOwner: LifecycleOwner) {
+ viewLifecycleOwner.lifecycleScope.launch {
+ viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
+ updateList()
+ }
+ }
+ }
+
+ private suspend fun updateList() {
+ if (uids.size <= 1) {
+ preference.isVisible = false
+ return
+ }
+ preference.isVisible = true
+ val appPreferences = withContext(Dispatchers.Default) {
+ repository.loadAppPreferences(uids)
+ }
+ preference.removeAll()
+ for (appPreference in appPreferences) {
+ preference.addPreference(appPreference)
+ }
+ }
+}
diff --git a/src/com/android/settings/datausage/AppPrefLoader.java b/src/com/android/settings/datausage/AppPrefLoader.java
deleted file mode 100644
index 1e0a554..0000000
--- a/src/com/android/settings/datausage/AppPrefLoader.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright (C) 2017 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.settings.datausage;
-
-import android.content.Context;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.util.ArraySet;
-
-import androidx.preference.Preference;
-
-import com.android.settingslib.utils.AsyncLoaderCompat;
-
-public class AppPrefLoader extends AsyncLoaderCompat<ArraySet<Preference>> {
- private ArraySet<String> mPackages;
- private PackageManager mPackageManager;
- private Context mPrefContext;
-
- public AppPrefLoader(Context prefContext, ArraySet<String> pkgs, PackageManager pm) {
- super(prefContext);
- mPackages = pkgs;
- mPackageManager = pm;
- mPrefContext = prefContext;
- }
-
- @Override
- public ArraySet<Preference> loadInBackground() {
- ArraySet<Preference> results = new ArraySet<>();
- for (int i = 1, size = mPackages.size(); i < size; i++) {
- try {
- ApplicationInfo info = mPackageManager.getApplicationInfo(mPackages.valueAt(i), 0);
- Preference preference = new Preference(mPrefContext);
- preference.setIcon(info.loadIcon(mPackageManager));
- preference.setTitle(info.loadLabel(mPackageManager));
- preference.setSelectable(false);
- results.add(preference);
- } catch (PackageManager.NameNotFoundException e) {
- }
- }
- return results;
- }
-
- @Override
- protected void onDiscardResult(ArraySet<Preference> result) {
- }
-}
diff --git a/src/com/android/settings/datausage/lib/AppDataUsageRepository.kt b/src/com/android/settings/datausage/lib/AppDataUsageRepository.kt
index 074a555..ccd3e60 100644
--- a/src/com/android/settings/datausage/lib/AppDataUsageRepository.kt
+++ b/src/com/android/settings/datausage/lib/AppDataUsageRepository.kt
@@ -126,12 +126,7 @@
items = items,
)
}
- // Map SDK sandbox back to its corresponding app
- collapseKey = if (Process.isSdkSandboxUid(uid)) {
- Process.getAppUidForSdkSandboxUid(uid)
- } else {
- uid
- }
+ collapseKey = getAppUid(uid)
category = AppItem.CATEGORY_APP
} else {
// If it is a removed user add it to the removed users' key
@@ -200,6 +195,15 @@
val bytes: Long,
)
+ @JvmStatic
+ fun getAppUid(uid: Int): Int {
+ if (Process.isSdkSandboxUid(uid)) {
+ // For a sandbox process, get the associated app UID
+ return Process.getAppUidForSdkSandboxUid(uid)
+ }
+ return uid
+ }
+
private fun convertToBuckets(stats: NetworkStats): List<Bucket> {
val buckets = mutableListOf<Bucket>()
stats.use {
diff --git a/src/com/android/settings/datausage/lib/AppPreferenceRepository.kt b/src/com/android/settings/datausage/lib/AppPreferenceRepository.kt
new file mode 100644
index 0000000..a71bc8f
--- /dev/null
+++ b/src/com/android/settings/datausage/lib/AppPreferenceRepository.kt
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2023 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.settings.datausage.lib
+
+import android.content.Context
+import android.content.pm.PackageManager
+import android.os.UserHandle
+import androidx.preference.Preference
+import com.android.settingslib.Utils
+
+class AppPreferenceRepository(private val context: Context) {
+ private val packageManager = context.packageManager
+
+ fun loadAppPreferences(uids: List<Int>): List<Preference> = uids.flatMap { uid ->
+ val userId = UserHandle.getUserId(uid)
+ getPackagesForUid(uid).mapNotNull { packageName ->
+ getPreference(packageName, userId)
+ }
+ }
+
+ private fun getPackagesForUid(uid: Int): Array<String> =
+ packageManager.getPackagesForUid(uid) ?: emptyArray()
+
+ private fun getPreference(packageName: String, userId: Int): Preference? = try {
+ val app = packageManager.getApplicationInfoAsUser(packageName, 0, userId)
+ Preference(context).apply {
+ icon = Utils.getBadgedIcon(context, app)
+ title = app.loadLabel(packageManager)
+ isSelectable = false
+ }
+ } catch (e: PackageManager.NameNotFoundException) {
+ null
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/datausage/AppDataUsageTest.java b/tests/robotests/src/com/android/settings/datausage/AppDataUsageTest.java
index 6a1c0a6..e4b91c6 100644
--- a/tests/robotests/src/com/android/settings/datausage/AppDataUsageTest.java
+++ b/tests/robotests/src/com/android/settings/datausage/AppDataUsageTest.java
@@ -60,6 +60,7 @@
import com.android.settingslib.AppItem;
import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
import com.android.settingslib.RestrictedSwitchPreference;
+import com.android.settingslib.core.AbstractPreferenceController;
import com.android.settingslib.net.NetworkCycleDataForUid;
import com.android.settingslib.net.NetworkCycleDataForUidLoader;
import com.android.settingslib.net.UidDetail;
@@ -109,10 +110,7 @@
@Test
@Config(shadows = ShadowFragment.class)
public void onCreate_appUid_shouldGetAppLabelFromAppInfo() throws NameNotFoundException {
- mFragment = spy(new AppDataUsage() {
- @Override
- public boolean isSimHardwareVisible(Context context) { return true; }
- });
+ mFragment = spy(new TestFragment());
final FragmentActivity activity = spy(Robolectric.setupActivity(FragmentActivity.class));
doReturn(mPackageManager).when(activity).getPackageManager();
doReturn(activity).when(mFragment).getActivity();
@@ -142,10 +140,7 @@
@Test
@Config(shadows = ShadowFragment.class)
public void onCreate_notAppUid_shouldGetAppLabelFromUidDetailProvider() {
- mFragment = spy(new AppDataUsage() {
- @Override
- public boolean isSimHardwareVisible(Context context) { return true; }
- });
+ mFragment = spy(new TestFragment());
ReflectionHelpers.setField(mFragment, "mDashboardFeatureProvider",
FakeFeatureFactory.setupForTest().dashboardFeatureProvider);
doReturn(Robolectric.setupActivity(FragmentActivity.class)).when(mFragment).getActivity();
@@ -172,10 +167,7 @@
@Test
public void bindAppHeader_allWorkApps_shouldNotShowAppInfoLink() {
- mFragment = spy(new AppDataUsage() {
- @Override
- public boolean isSimHardwareVisible(Context context) { return true; }
- });
+ mFragment = spy(new TestFragment());
when(mFragment.getPreferenceManager())
.thenReturn(mock(PreferenceManager.class, RETURNS_DEEP_STUBS));
@@ -192,10 +184,7 @@
throws PackageManager.NameNotFoundException {
final int fakeUserId = 100;
- mFragment = spy(new AppDataUsage() {
- @Override
- public boolean isSimHardwareVisible(Context context) { return true; }
- });
+ mFragment = spy(new TestFragment());
final ArraySet<String> packages = new ArraySet<>();
packages.add("pkg");
final AppItem appItem = new AppItem(123456789);
@@ -221,10 +210,7 @@
@Test
public void changePreference_backgroundData_shouldUpdateUI() {
- mFragment = spy(new AppDataUsage() {
- @Override
- public boolean isSimHardwareVisible(Context context) { return true; }
- });
+ mFragment = spy(new TestFragment());
final AppItem appItem = new AppItem(123456789);
final RestrictedSwitchPreference pref = mock(RestrictedSwitchPreference.class);
final DataSaverBackend dataSaverBackend = mock(DataSaverBackend.class);
@@ -241,10 +227,7 @@
@Test
public void updatePrefs_restrictedByAdmin_shouldDisablePreference() {
- mFragment = spy(new AppDataUsage() {
- @Override
- public boolean isSimHardwareVisible(Context context) { return true; }
- });
+ mFragment = spy(new TestFragment());
final int testUid = 123123;
final AppItem appItem = new AppItem(testUid);
final RestrictedSwitchPreference restrictBackgroundPref
@@ -272,10 +255,7 @@
@Test
public void bindData_noAppUsageData_shouldHideCycleSpinner() {
- mFragment = spy(new AppDataUsage() {
- @Override
- public boolean isSimHardwareVisible(Context context) { return true; }
- });
+ mFragment = spy(new TestFragment());
final SpinnerPreference cycle = mock(SpinnerPreference.class);
ReflectionHelpers.setField(mFragment, "mCycle", cycle);
final Preference preference = mock(Preference.class);
@@ -291,10 +271,7 @@
@Test
public void bindData_hasAppUsageData_shouldShowCycleSpinnerAndUpdateUsageSummary() {
- mFragment = spy(new AppDataUsage() {
- @Override
- public boolean isSimHardwareVisible(Context context) { return true; }
- });
+ mFragment = spy(new TestFragment());
final Context context = RuntimeEnvironment.application;
ReflectionHelpers.setField(mFragment, "mContext", context);
final long backgroundBytes = 1234L;
@@ -323,10 +300,7 @@
@Test
public void onCreateLoader_categoryApp_shouldQueryDataUsageUsingAppKey() {
- mFragment = new AppDataUsage() {
- @Override
- public boolean isSimHardwareVisible(Context context) { return true; }
- };
+ mFragment = new TestFragment();
final Context context = RuntimeEnvironment.application;
final int testUid = 123123;
final AppItem appItem = new AppItem(testUid);
@@ -349,10 +323,7 @@
@Test
public void onCreateLoader_categoryUser_shouldQueryDataUsageUsingAssociatedUids() {
- mFragment = new AppDataUsage() {
- @Override
- public boolean isSimHardwareVisible(Context context) { return true; }
- };
+ mFragment = new TestFragment();
final Context context = RuntimeEnvironment.application;
final int testUserId = 11;
final AppItem appItem = new AppItem(testUserId);
@@ -389,10 +360,7 @@
appItem.category = AppItem.CATEGORY_APP;
appItem.addUid(uid);
- mFragment = new AppDataUsage() {
- @Override
- public boolean isSimHardwareVisible(Context context) { return true; }
- };
+ mFragment = new TestFragment();
ReflectionHelpers.setField(mFragment, "mContext", RuntimeEnvironment.application);
ReflectionHelpers.setField(mFragment, "mCycles", testCycles);
ReflectionHelpers.setField(mFragment, "mAppItem", appItem);
@@ -425,10 +393,7 @@
builder.setStartTime(tenDaysAgo).setEndTime(now).setTotalUsage(1234L);
data.add(builder.build());
- mFragment = new AppDataUsage() {
- @Override
- public boolean isSimHardwareVisible(Context context) { return true; }
- };
+ mFragment = new TestFragment();
ReflectionHelpers.setField(mFragment, "mContext", RuntimeEnvironment.application);
ReflectionHelpers.setField(mFragment, "mCycleAdapter", mock(CycleAdapter.class));
ReflectionHelpers.setField(mFragment, "mSelectedCycle", tenDaysAgo);
@@ -455,10 +420,7 @@
ShadowDataUsageUtils.HAS_SIM = false;
ShadowSubscriptionManager.setDefaultDataSubscriptionId(
SubscriptionManager.INVALID_SUBSCRIPTION_ID);
- mFragment = spy(new AppDataUsage() {
- @Override
- public boolean isSimHardwareVisible(Context context) { return true; }
- });
+ mFragment = spy(new TestFragment());
doReturn(Robolectric.setupActivity(FragmentActivity.class)).when(mFragment).getActivity();
doReturn(RuntimeEnvironment.application).when(mFragment).getContext();
final UidDetailProvider uidDetailProvider = mock(UidDetailProvider.class);
@@ -478,4 +440,16 @@
assertTrue(mFragment.mTemplate.getSubscriberIds().isEmpty());
assertTrue(mFragment.mTemplate.getWifiNetworkKeys().isEmpty());
}
+
+ private static class TestFragment extends AppDataUsage {
+ @Override
+ protected <T extends AbstractPreferenceController> T use(Class<T> clazz) {
+ return mock(clazz);
+ }
+
+ @Override
+ public boolean isSimHardwareVisible(Context context) {
+ return true;
+ }
+ }
}
diff --git a/tests/spa_unit/src/com/android/settings/datausage/AppDataUsageListControllerTest.kt b/tests/spa_unit/src/com/android/settings/datausage/AppDataUsageListControllerTest.kt
new file mode 100644
index 0000000..6727232
--- /dev/null
+++ b/tests/spa_unit/src/com/android/settings/datausage/AppDataUsageListControllerTest.kt
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2023 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.settings.datausage
+
+import android.content.Context
+import android.util.SparseBooleanArray
+import androidx.lifecycle.testing.TestLifecycleOwner
+import androidx.preference.Preference
+import androidx.preference.PreferenceCategory
+import androidx.preference.PreferenceManager
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.settings.datausage.lib.AppPreferenceRepository
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.runBlocking
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.any
+import org.mockito.kotlin.doAnswer
+import org.mockito.kotlin.mock
+
+@RunWith(AndroidJUnit4::class)
+class AppDataUsageListControllerTest {
+ private val context: Context = ApplicationProvider.getApplicationContext()
+
+ private val repository = mock<AppPreferenceRepository> {
+ on { loadAppPreferences(any()) } doAnswer {
+ val uids = it.arguments[0] as List<*>
+ uids.map { Preference(context) }
+ }
+ }
+
+ private val controller = AppDataUsageListController(
+ context = context,
+ preferenceKey = KEY,
+ repository = repository,
+ )
+
+ private val preference = PreferenceCategory(context).apply { key = KEY }
+
+ private val preferenceScreen = PreferenceManager(context).createPreferenceScreen(context)
+
+ @Before
+ fun setUp() {
+ preferenceScreen.addPreference(preference)
+ }
+
+ @Test
+ fun onViewCreated_singleUid_hidePreference(): Unit = runBlocking {
+ controller.init(SparseBooleanArray().apply {
+ put(UID_0, true)
+ })
+ controller.displayPreference(preferenceScreen)
+
+ controller.onViewCreated(TestLifecycleOwner())
+ delay(100)
+
+ assertThat(preference.isVisible).isFalse()
+ }
+
+ @Test
+ fun onViewCreated_twoUid_showPreference(): Unit = runBlocking {
+ controller.init(SparseBooleanArray().apply {
+ put(UID_0, true)
+ put(UID_1, true)
+ })
+ controller.displayPreference(preferenceScreen)
+
+ controller.onViewCreated(TestLifecycleOwner())
+ delay(100)
+
+ assertThat(preference.isVisible).isTrue()
+ assertThat(preference.preferenceCount).isEqualTo(2)
+ }
+
+ private companion object {
+ const val KEY = "test_key"
+ const val UID_0 = 10000
+ const val UID_1 = 10001
+ }
+}
diff --git a/tests/spa_unit/src/com/android/settings/datausage/lib/AppPreferenceRepositoryTest.kt b/tests/spa_unit/src/com/android/settings/datausage/lib/AppPreferenceRepositoryTest.kt
new file mode 100644
index 0000000..c7371ee
--- /dev/null
+++ b/tests/spa_unit/src/com/android/settings/datausage/lib/AppPreferenceRepositoryTest.kt
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2023 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.settings.datausage.lib
+
+import android.content.Context
+import android.content.pm.ApplicationInfo
+import android.content.pm.PackageManager
+import android.graphics.drawable.Drawable
+import android.os.UserHandle
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.any
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.doThrow
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.spy
+import org.mockito.kotlin.stub
+
+@RunWith(AndroidJUnit4::class)
+class AppPreferenceRepositoryTest {
+ private val packageManager = mock<PackageManager> {
+ on { getPackagesForUid(UID) } doReturn arrayOf(PACKAGE_NAME)
+ }
+
+ private val context: Context = spy(ApplicationProvider.getApplicationContext()) {
+ on { packageManager } doReturn packageManager
+ }
+
+ private val repository = AppPreferenceRepository(context)
+
+ @Test
+ fun loadAppPreferences_packageNotFound_returnEmpty() {
+ packageManager.stub {
+ on {
+ getApplicationInfoAsUser(PACKAGE_NAME, 0, UserHandle.getUserId(UID))
+ } doThrow PackageManager.NameNotFoundException()
+ }
+
+ val preferences = repository.loadAppPreferences(listOf(UID))
+
+ assertThat(preferences).isEmpty()
+ }
+
+ @Test
+ fun loadAppPreferences_packageFound_returnPreference() {
+ val app = mock<ApplicationInfo> {
+ on { loadUnbadgedIcon(any()) } doReturn UNBADGED_ICON
+ on { loadLabel(any()) } doReturn LABEL
+ }
+ packageManager.stub {
+ on {
+ getApplicationInfoAsUser(PACKAGE_NAME, 0, UserHandle.getUserId(UID))
+ } doReturn app
+ }
+
+ val preferences = repository.loadAppPreferences(listOf(UID))
+
+ assertThat(preferences).hasSize(1)
+ preferences[0].apply {
+ assertThat(title).isEqualTo(LABEL)
+ assertThat(icon).isNotNull()
+ assertThat(isSelectable).isFalse()
+ }
+ }
+
+ private companion object {
+ const val UID = 10000
+ const val PACKAGE_NAME = "package.name"
+ const val LABEL = "Label"
+ val UNBADGED_ICON = mock<Drawable>()
+ }
+}
diff --git a/tests/unit/src/com/android/settings/datausage/AppPrefLoaderTest.java b/tests/unit/src/com/android/settings/datausage/AppPrefLoaderTest.java
deleted file mode 100644
index 902906c..0000000
--- a/tests/unit/src/com/android/settings/datausage/AppPrefLoaderTest.java
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * Copyright (C) 2020 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.settings.datausage;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.graphics.drawable.Drawable;
-import android.util.ArraySet;
-
-import androidx.preference.Preference;
-import androidx.test.core.app.ApplicationProvider;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-@RunWith(AndroidJUnit4.class)
-public class AppPrefLoaderTest {
-
- @Mock
- private PackageManager mPackageManager;
-
- private AppPrefLoader mLoader;
-
- @Before
- public void setUp() throws Exception {
- MockitoAnnotations.initMocks(this);
- final ArraySet<String> pkgs = new ArraySet<>(2);
- pkgs.add("pkg0");
- pkgs.add("pkg1");
- mLoader = new AppPrefLoader(
- ApplicationProvider.getApplicationContext(), pkgs, mPackageManager);
- }
-
- @Test
- public void loadInBackground_packageNotFound_shouldReturnEmptySet()
- throws NameNotFoundException {
- when(mPackageManager.getApplicationInfo(anyString(), anyInt()))
- .thenThrow(new NameNotFoundException());
-
- assertThat(mLoader.loadInBackground()).isEmpty();
- }
-
- @Test
- public void loadInBackground_shouldReturnPreference() throws NameNotFoundException {
- ApplicationInfo info = mock(ApplicationInfo.class);
- when(mPackageManager.getApplicationInfo(anyString(), anyInt())).thenReturn(info);
- final Drawable drawable = mock(Drawable.class);
- final String label = "Label1";
- when(info.loadIcon(mPackageManager)).thenReturn(drawable);
- when(info.loadLabel(mPackageManager)).thenReturn(label);
-
- Preference preference = mLoader.loadInBackground().valueAt(0);
- assertThat(preference.getTitle()).isEqualTo(label);
- assertThat(preference.getIcon()).isEqualTo(drawable);
- assertThat(preference.isSelectable()).isFalse();
- }
-}