diff --git a/res/color/dream_card_color_state_list.xml b/res/color/dream_card_color_state_list.xml
index eae3fe46a6d..082408d1583 100644
--- a/res/color/dream_card_color_state_list.xml
+++ b/res/color/dream_card_color_state_list.xml
@@ -17,6 +17,6 @@
-
-
+
+
\ No newline at end of file
diff --git a/res/color/dream_card_icon_color_state_list.xml b/res/color/dream_card_icon_color_state_list.xml
index a91ae3d3be3..ed34ae39357 100644
--- a/res/color/dream_card_icon_color_state_list.xml
+++ b/res/color/dream_card_icon_color_state_list.xml
@@ -17,6 +17,6 @@
-
-
+
+
\ No newline at end of file
diff --git a/res/color/dream_card_summary_color_state_list.xml b/res/color/dream_card_summary_color_state_list.xml
new file mode 100644
index 00000000000..a1845f44d95
--- /dev/null
+++ b/res/color/dream_card_summary_color_state_list.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/res/color/dream_card_text_color_state_list.xml b/res/color/dream_card_text_color_state_list.xml
index bd1f16578ea..b39bbed75c9 100644
--- a/res/color/dream_card_text_color_state_list.xml
+++ b/res/color/dream_card_text_color_state_list.xml
@@ -17,6 +17,6 @@
-
-
+
+
\ No newline at end of file
diff --git a/res/drawable/dream_default_preview_icon.xml b/res/drawable/dream_default_preview_icon.xml
index 7d247bb2957..8989929fdd7 100644
--- a/res/drawable/dream_default_preview_icon.xml
+++ b/res/drawable/dream_default_preview_icon.xml
@@ -15,10 +15,11 @@
-->
-
\ No newline at end of file
diff --git a/res/drawable/dream_preview_rounded_bg.xml b/res/drawable/dream_preview_rounded_bg.xml
index 2aae26b2ad2..7cae599b6c8 100644
--- a/res/drawable/dream_preview_rounded_bg.xml
+++ b/res/drawable/dream_preview_rounded_bg.xml
@@ -17,6 +17,6 @@
-
+
\ No newline at end of file
diff --git a/res/layout/dream_preference_layout.xml b/res/layout/dream_preference_layout.xml
index f7281c1a573..aff8ad3133e 100644
--- a/res/layout/dream_preference_layout.xml
+++ b/res/layout/dream_preference_layout.xml
@@ -93,7 +93,7 @@
android:maxLines="2"
android:ellipsize="end"
android:textSize="@dimen/dream_item_summary_text_size"
- android:textColor="@color/dream_card_text_color_state_list"
+ android:textColor="@color/dream_card_summary_color_state_list"
app:layout_constraintTop_toBottomOf="@+id/title_text"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
diff --git a/res/layout/dream_preview_button.xml b/res/layout/dream_preview_button.xml
index 04d272aecdd..feeb9dd36ea 100644
--- a/res/layout/dream_preview_button.xml
+++ b/res/layout/dream_preview_button.xml
@@ -17,17 +17,16 @@
diff --git a/src/com/android/settings/SettingsActivityUtil.kt b/src/com/android/settings/SettingsActivityUtil.kt
index cac341fff02..65d26defbf8 100644
--- a/src/com/android/settings/SettingsActivityUtil.kt
+++ b/src/com/android/settings/SettingsActivityUtil.kt
@@ -35,6 +35,7 @@ import com.android.settings.spa.app.specialaccess.DisplayOverOtherAppsAppListPro
import com.android.settings.spa.app.specialaccess.InstallUnknownAppsListProvider
import com.android.settings.spa.app.specialaccess.MediaManagementAppsAppListProvider
import com.android.settings.spa.app.specialaccess.ModifySystemSettingsAppListProvider
+import com.android.settings.spa.app.specialaccess.NfcTagAppsSettingsProvider
import com.android.settings.spa.app.specialaccess.PictureInPictureListProvider
import com.android.settings.spa.app.specialaccess.WifiControlAppListProvider
import com.android.settings.wifi.ChangeWifiStateDetails
@@ -62,6 +63,8 @@ object SettingsActivityUtil {
MediaManagementAppsAppListProvider.getAppInfoRoutePrefix(),
ChangeWifiStateDetails::class.qualifiedName to
WifiControlAppListProvider.getAppInfoRoutePrefix(),
+ NfcTagAppsSettingsProvider::class.qualifiedName to
+ NfcTagAppsSettingsProvider.getAppInfoRoutePrefix(),
)
@JvmStatic
diff --git a/src/com/android/settings/applications/credentials/CombinedProviderInfo.java b/src/com/android/settings/applications/credentials/CombinedProviderInfo.java
new file mode 100644
index 00000000000..ce985975c90
--- /dev/null
+++ b/src/com/android/settings/applications/credentials/CombinedProviderInfo.java
@@ -0,0 +1,262 @@
+/*
+ * 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.applications.credentials;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.ServiceInfo;
+import android.credentials.CredentialProviderInfo;
+import android.graphics.drawable.Drawable;
+import android.service.autofill.AutofillServiceInfo;
+import android.text.TextUtils;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Holds combined autofill and credential manager data grouped by package name. Contains backing
+ * logic for each row in settings.
+ */
+public final class CombinedProviderInfo {
+ private final List mCredentialProviderInfos;
+ private final @Nullable AutofillServiceInfo mAutofillServiceInfo;
+ private final boolean mIsDefaultAutofillProvider;
+ private final boolean mIsDefaultCredmanProvider;
+
+ /** Constructs an information instance from both autofill and credential provider. */
+ public CombinedProviderInfo(
+ @Nullable List cpis,
+ @Nullable AutofillServiceInfo asi,
+ boolean isDefaultAutofillProvider,
+ boolean isDefaultCredmanProvider) {
+ mCredentialProviderInfos = new ArrayList<>(cpis);
+ mAutofillServiceInfo = asi;
+ mIsDefaultAutofillProvider = isDefaultAutofillProvider;
+ mIsDefaultCredmanProvider = isDefaultCredmanProvider;
+ }
+
+ /** Returns the credential provider info. */
+ @Nullable
+ public List getCredentialProviderInfos() {
+ return mCredentialProviderInfos;
+ }
+
+ /** Returns the autofill provider info. */
+ @Nullable
+ public AutofillServiceInfo getAutofillServiceInfo() {
+ return mAutofillServiceInfo;
+ }
+
+ /** Returns the application info. */
+ public @Nullable ApplicationInfo getApplicationInfo() {
+ if (!mCredentialProviderInfos.isEmpty()) {
+ return mCredentialProviderInfos.get(0).getServiceInfo().applicationInfo;
+ }
+ return mAutofillServiceInfo.getServiceInfo().applicationInfo;
+ }
+
+ /** Returns the app icon. */
+ @Nullable
+ public Drawable getAppIcon(@NonNull Context context) {
+ Drawable icon = null;
+ ServiceInfo brandingService = getBrandingService();
+ if (brandingService != null) {
+ icon = brandingService.loadIcon(context.getPackageManager());
+ }
+
+ // If the branding service gave us a icon then use that.
+ if (icon != null) {
+ return icon;
+ }
+
+ // Otherwise fallback to the app label and then the package name.
+ return getApplicationInfo().loadIcon(context.getPackageManager());
+ }
+
+ /** Returns the app name. */
+ @Nullable
+ public CharSequence getAppName(@NonNull Context context) {
+ CharSequence name = "";
+ ServiceInfo brandingService = getBrandingService();
+ if (brandingService != null) {
+ name = brandingService.loadLabel(context.getPackageManager());
+ }
+
+ // If the branding service gave us a name then use that.
+ if (!TextUtils.isEmpty(name)) {
+ return name;
+ }
+
+ // Otherwise fallback to the app label and then the package name.
+ name = getApplicationInfo().loadLabel(context.getPackageManager());
+ if (TextUtils.isEmpty(name)) {
+ name = getApplicationInfo().packageName;
+ }
+ return name;
+ }
+
+ /** Gets the service to use for branding (name, icons). */
+ public @Nullable ServiceInfo getBrandingService() {
+ // If the app has an autofill service then use that.
+ if (mAutofillServiceInfo != null) {
+ return mAutofillServiceInfo.getServiceInfo();
+ }
+
+ // If there are no credman providers then stop here.
+ if (mCredentialProviderInfos.isEmpty()) {
+ return null;
+ }
+
+ // Build a list of credential providers and sort them by component names
+ // alphabetically to ensure we are deterministic when picking the provider.
+ Map flattenedNamesToServices = new HashMap<>();
+ List flattenedNames = new ArrayList<>();
+ for (CredentialProviderInfo cpi : mCredentialProviderInfos) {
+ final String flattenedName = cpi.getComponentName().flattenToString();
+ flattenedNamesToServices.put(flattenedName, cpi.getServiceInfo());
+ flattenedNames.add(flattenedName);
+ }
+
+ Collections.sort(flattenedNames);
+ return flattenedNamesToServices.get(flattenedNames.get(0));
+ }
+
+ /** Returns whether the provider is the default autofill provider. */
+ public boolean isDefaultAutofillProvider() {
+ return mIsDefaultAutofillProvider;
+ }
+
+ /** Returns whether the provider is the default credman provider. */
+ public boolean isDefaultCredmanProvider() {
+ return mIsDefaultCredmanProvider;
+ }
+
+ /** Returns the settings subtitle. */
+ @Nullable
+ public String getSettingsSubtitle() {
+ List subtitles = new ArrayList<>();
+ for (CredentialProviderInfo cpi : mCredentialProviderInfos) {
+ // Convert from a CharSequence.
+ String subtitle = String.valueOf(cpi.getSettingsSubtitle());
+ if (subtitle != null && !TextUtils.isEmpty(subtitle) && !subtitle.equals("null")) {
+ subtitles.add(subtitle);
+ }
+ }
+
+ if (subtitles.size() == 0) {
+ return "";
+ }
+
+ return String.join(", ", subtitles);
+ }
+
+ /** Returns the autofill component name string. */
+ @Nullable
+ public String getAutofillServiceString() {
+ if (mAutofillServiceInfo != null) {
+ return mAutofillServiceInfo.getServiceInfo().getComponentName().flattenToString();
+ }
+ return null;
+ }
+
+ /** Returns the provider that gets the top spot. */
+ public static @Nullable CombinedProviderInfo getTopProvider(
+ List providers) {
+ // If there is an autofill provider then it should be the
+ // top app provider.
+ for (CombinedProviderInfo cpi : providers) {
+ if (cpi.isDefaultAutofillProvider()) {
+ return cpi;
+ }
+ }
+
+ // TODO(280454916): Add logic here.
+ return null;
+ }
+
+ public static List buildMergedList(
+ List asiList,
+ List cpiList,
+ @Nullable String defaultAutofillProvider) {
+ ComponentName defaultAutofillProviderComponent =
+ (defaultAutofillProvider == null)
+ ? null
+ : ComponentName.unflattenFromString(defaultAutofillProvider);
+
+ // Index the autofill providers by package name.
+ Set packageNames = new HashSet<>();
+ Map> autofillServices = new HashMap<>();
+ for (AutofillServiceInfo asi : asiList) {
+ final String packageName = asi.getServiceInfo().packageName;
+ if (!autofillServices.containsKey(packageName)) {
+ autofillServices.put(packageName, new ArrayList<>());
+ }
+
+ autofillServices.get(packageName).add(asi);
+ packageNames.add(packageName);
+ }
+
+ // Index the credman providers by package name.
+ Map> credmanServices = new HashMap<>();
+ for (CredentialProviderInfo cpi : cpiList) {
+ String packageName = cpi.getServiceInfo().packageName;
+ if (!credmanServices.containsKey(packageName)) {
+ credmanServices.put(packageName, new ArrayList<>());
+ }
+
+ credmanServices.get(packageName).add(cpi);
+ packageNames.add(packageName);
+ }
+
+ // Now go through and build the joint datasets.
+ List cmpi = new ArrayList<>();
+ for (String packageName : packageNames) {
+ List asi = autofillServices.get(packageName);
+ List cpi = credmanServices.get(packageName);
+
+ // If there are multiple autofill services then pick the first one.
+ AutofillServiceInfo selectedAsi = asi.isEmpty() ? null : asi.get(0);
+
+ // Check if we are the default autofill provider.
+ boolean isDefaultAutofillProvider = false;
+ if (defaultAutofillProviderComponent != null
+ && defaultAutofillProviderComponent.getPackageName().equals(packageName)) {
+ isDefaultAutofillProvider = true;
+ }
+
+ // Check if we have any enabled cred man services.
+ boolean isDefaultCredmanProvider = false;
+ if (!cpi.isEmpty()) {
+ isDefaultCredmanProvider = cpi.get(0).isEnabled();
+ }
+
+ cmpi.add(
+ new CombinedProviderInfo(
+ cpi, selectedAsi, isDefaultAutofillProvider, isDefaultCredmanProvider));
+ }
+
+ return cmpi;
+ }
+}
diff --git a/src/com/android/settings/applications/manageapplications/ManageApplicationsUtil.kt b/src/com/android/settings/applications/manageapplications/ManageApplicationsUtil.kt
index 78a4a6bfe54..6574f6926fc 100644
--- a/src/com/android/settings/applications/manageapplications/ManageApplicationsUtil.kt
+++ b/src/com/android/settings/applications/manageapplications/ManageApplicationsUtil.kt
@@ -63,6 +63,7 @@ import com.android.settings.spa.app.specialaccess.DisplayOverOtherAppsAppListPro
import com.android.settings.spa.app.specialaccess.InstallUnknownAppsListProvider
import com.android.settings.spa.app.specialaccess.MediaManagementAppsAppListProvider
import com.android.settings.spa.app.specialaccess.ModifySystemSettingsAppListProvider
+import com.android.settings.spa.app.specialaccess.NfcTagAppsSettingsProvider
import com.android.settings.spa.app.specialaccess.WifiControlAppListProvider
import com.android.settings.spa.notification.AppListNotificationsPageProvider
import com.android.settings.spa.system.AppLanguagesPageProvider
@@ -112,6 +113,7 @@ object ManageApplicationsUtil {
LIST_TYPE_NOTIFICATION -> AppListNotificationsPageProvider.name
LIST_TYPE_APPS_LOCALE -> AppLanguagesPageProvider.name
LIST_TYPE_MAIN -> AllAppListPageProvider.name
+ LIST_TYPE_NFC_TAG_APPS -> NfcTagAppsSettingsProvider.getAppListRoute()
else -> null
}
}
diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintSettings.java b/src/com/android/settings/biometrics/fingerprint/FingerprintSettings.java
index a5e5f579c02..e0f402bdc02 100644
--- a/src/com/android/settings/biometrics/fingerprint/FingerprintSettings.java
+++ b/src/com/android/settings/biometrics/fingerprint/FingerprintSettings.java
@@ -283,7 +283,9 @@ public class FingerprintSettings extends SubSettings {
case MSG_REFRESH_FINGERPRINT_TEMPLATES:
removeFingerprintPreference(msg.arg1);
updateAddPreference();
- updateFingerprintUnlockCategoryVisibility();
+ if (isSfps()) {
+ updateFingerprintUnlockCategoryVisibility();
+ }
updatePreferences();
break;
case MSG_FINGER_AUTH_SUCCESS:
@@ -494,9 +496,13 @@ public class FingerprintSettings extends SubSettings {
}
private boolean isSfps() {
- for (FingerprintSensorPropertiesInternal prop : mSensorProperties) {
- if (prop.isAnySidefpsType()) {
- return true;
+ mFingerprintManager = Utils.getFingerprintManagerOrNull(getActivity());
+ if (mFingerprintManager != null) {
+ mSensorProperties = mFingerprintManager.getSensorPropertiesInternal();
+ for (FingerprintSensorPropertiesInternal prop : mSensorProperties) {
+ if (prop.isAnySidefpsType()) {
+ return true;
+ }
}
}
return false;
@@ -838,18 +844,20 @@ public class FingerprintSettings extends SubSettings {
private List buildPreferenceControllers(Context context) {
final List controllers = new ArrayList<>();
- mFingerprintUnlockCategoryPreferenceController =
+ if (isSfps()) {
+ mFingerprintUnlockCategoryPreferenceController =
new FingerprintUnlockCategoryController(
- context,
- KEY_FINGERPRINT_UNLOCK_CATEGORY
+ context,
+ KEY_FINGERPRINT_UNLOCK_CATEGORY
);
- mRequireScreenOnToAuthPreferenceController =
- new FingerprintSettingsRequireScreenOnToAuthPreferenceController(
- context,
- KEY_REQUIRE_SCREEN_ON_TO_AUTH
- );
- controllers.add(mFingerprintUnlockCategoryPreferenceController);
- controllers.add(mRequireScreenOnToAuthPreferenceController);
+ mRequireScreenOnToAuthPreferenceController =
+ new FingerprintSettingsRequireScreenOnToAuthPreferenceController(
+ context,
+ KEY_REQUIRE_SCREEN_ON_TO_AUTH
+ );
+ controllers.add(mFingerprintUnlockCategoryPreferenceController);
+ controllers.add(mRequireScreenOnToAuthPreferenceController);
+ }
return controllers;
}
diff --git a/src/com/android/settings/datausage/DataSaverSummary.java b/src/com/android/settings/datausage/DataSaverSummary.java
deleted file mode 100644
index 67644a6c992..00000000000
--- a/src/com/android/settings/datausage/DataSaverSummary.java
+++ /dev/null
@@ -1,234 +0,0 @@
-/*
- * Copyright (C) 2016 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.app.Application;
-import android.app.settings.SettingsEnums;
-import android.content.Context;
-import android.icu.text.MessageFormat;
-import android.os.Bundle;
-import android.telephony.SubscriptionManager;
-import android.widget.Switch;
-
-import androidx.preference.Preference;
-
-import com.android.settings.R;
-import com.android.settings.SettingsActivity;
-import com.android.settings.SettingsPreferenceFragment;
-import com.android.settings.applications.AppStateBaseBridge.Callback;
-import com.android.settings.datausage.DataSaverBackend.Listener;
-import com.android.settings.search.BaseSearchIndexProvider;
-import com.android.settings.widget.SettingsMainSwitchBar;
-import com.android.settingslib.applications.ApplicationsState;
-import com.android.settingslib.applications.ApplicationsState.AppEntry;
-import com.android.settingslib.applications.ApplicationsState.Callbacks;
-import com.android.settingslib.applications.ApplicationsState.Session;
-import com.android.settingslib.search.SearchIndexable;
-import com.android.settingslib.widget.OnMainSwitchChangeListener;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Locale;
-import java.util.Map;
-
-@SearchIndexable
-public class DataSaverSummary extends SettingsPreferenceFragment
- implements OnMainSwitchChangeListener, Listener, Callback, Callbacks {
-
- private static final String KEY_UNRESTRICTED_ACCESS = "unrestricted_access";
-
- private SettingsMainSwitchBar mSwitchBar;
- private DataSaverBackend mDataSaverBackend;
- private Preference mUnrestrictedAccess;
- private ApplicationsState mApplicationsState;
- private AppStateDataUsageBridge mDataUsageBridge;
- private Session mSession;
-
- // Flag used to avoid infinite loop due if user switch it on/off too quicky.
- private boolean mSwitching;
-
- private Runnable mLoadAppRunnable = () -> {
- mApplicationsState = ApplicationsState.getInstance(
- (Application) getContext().getApplicationContext());
- mDataUsageBridge = new AppStateDataUsageBridge(mApplicationsState, this, mDataSaverBackend);
- mSession = mApplicationsState.newSession(this, getSettingsLifecycle());
- mDataUsageBridge.resume(true /* forceLoadAllApps */);
- };
-
- @Override
- public void onCreate(Bundle icicle) {
- super.onCreate(icicle);
-
- if (!isDataSaverVisible(getContext())) {
- finishFragment();
- return;
- }
-
- addPreferencesFromResource(R.xml.data_saver);
- mUnrestrictedAccess = findPreference(KEY_UNRESTRICTED_ACCESS);
- mDataSaverBackend = new DataSaverBackend(getContext());
- }
-
- @Override
- public void onActivityCreated(Bundle savedInstanceState) {
- super.onActivityCreated(savedInstanceState);
- mSwitchBar = ((SettingsActivity) getActivity()).getSwitchBar();
- mSwitchBar.setTitle(getContext().getString(R.string.data_saver_switch_title));
- mSwitchBar.show();
- mSwitchBar.addOnSwitchChangeListener(this);
- }
-
- @Override
- public void onResume() {
- super.onResume();
- mDataSaverBackend.refreshAllowlist();
- mDataSaverBackend.refreshDenylist();
- mDataSaverBackend.addListener(this);
- if (mDataUsageBridge != null) {
- mDataUsageBridge.resume(true /* forceLoadAllApps */);
- } else {
- getView().post(mLoadAppRunnable);
- }
- }
-
- @Override
- public void onPause() {
- super.onPause();
- mDataSaverBackend.remListener(this);
- if (mDataUsageBridge != null) {
- mDataUsageBridge.pause();
- }
- }
-
- @Override
- public void onSwitchChanged(Switch switchView, boolean isChecked) {
- synchronized (this) {
- if (mSwitching) {
- return;
- }
- mSwitching = true;
- mDataSaverBackend.setDataSaverEnabled(isChecked);
- }
- }
-
- @Override
- public int getMetricsCategory() {
- return SettingsEnums.DATA_SAVER_SUMMARY;
- }
-
- @Override
- public int getHelpResource() {
- return R.string.help_url_data_saver;
- }
-
- @Override
- public void onDataSaverChanged(boolean isDataSaving) {
- synchronized (this) {
- mSwitchBar.setChecked(isDataSaving);
- mSwitching = false;
- }
- }
-
- @Override
- public void onAllowlistStatusChanged(int uid, boolean isAllowlisted) {
- }
-
- @Override
- public void onDenylistStatusChanged(int uid, boolean isDenylisted) {
- }
-
- @Override
- public void onExtraInfoUpdated() {
- updateUnrestrictedAccessSummary();
- }
-
- @Override
- public void onRunningStateChanged(boolean running) {
-
- }
-
- @Override
- public void onPackageListChanged() {
-
- }
-
- @Override
- public void onRebuildComplete(ArrayList apps) {
-
- }
-
- @Override
- public void onPackageIconChanged() {
-
- }
-
- @Override
- public void onPackageSizeChanged(String packageName) {
-
- }
-
- @Override
- public void onAllSizesComputed() {
- updateUnrestrictedAccessSummary();
- }
-
- @Override
- public void onLauncherInfoChanged() {
- updateUnrestrictedAccessSummary();
- }
-
- @Override
- public void onLoadEntriesCompleted() {
-
- }
-
- private void updateUnrestrictedAccessSummary() {
- if (!isAdded() || isFinishingOrDestroyed() || mSession == null) return;
-
- int count = 0;
- for (AppEntry entry : mSession.getAllApps()) {
- if (!ApplicationsState.FILTER_DOWNLOADED_AND_LAUNCHER.filterApp(entry)) {
- continue;
- }
- if (entry.extraInfo != null && ((AppStateDataUsageBridge.DataUsageState)
- entry.extraInfo).isDataSaverAllowlisted) {
- count++;
- }
- }
- MessageFormat msgFormat = new MessageFormat(
- getResources().getString(R.string.data_saver_unrestricted_summary),
- Locale.getDefault());
- Map arguments = new HashMap<>();
- arguments.put("count", count);
- mUnrestrictedAccess.setSummary(msgFormat.format(arguments));
- }
-
- public static boolean isDataSaverVisible(Context context) {
- return context.getResources()
- .getBoolean(R.bool.config_show_data_saver);
- }
-
- public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
- new BaseSearchIndexProvider(R.xml.data_saver) {
-
- @Override
- protected boolean isPageSearchEnabled(Context context) {
- return isDataSaverVisible(context)
- && DataUsageUtils.hasMobileData(context)
- && DataUsageUtils.getDefaultSubscriptionId(context)
- != SubscriptionManager.INVALID_SUBSCRIPTION_ID;
- }
- };
-}
diff --git a/src/com/android/settings/datausage/DataSaverSummary.kt b/src/com/android/settings/datausage/DataSaverSummary.kt
new file mode 100644
index 00000000000..1d9cbb73a66
--- /dev/null
+++ b/src/com/android/settings/datausage/DataSaverSummary.kt
@@ -0,0 +1,176 @@
+/*
+ * 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.app.Application
+import android.app.settings.SettingsEnums
+import android.content.Context
+import android.os.Bundle
+import android.telephony.SubscriptionManager
+import android.widget.Switch
+import androidx.lifecycle.lifecycleScope
+import androidx.preference.Preference
+import com.android.settings.R
+import com.android.settings.SettingsActivity
+import com.android.settings.SettingsPreferenceFragment
+import com.android.settings.applications.AppStateBaseBridge
+import com.android.settings.datausage.AppStateDataUsageBridge.DataUsageState
+import com.android.settings.search.BaseSearchIndexProvider
+import com.android.settings.widget.SettingsMainSwitchBar
+import com.android.settingslib.applications.ApplicationsState
+import com.android.settingslib.search.SearchIndexable
+import com.android.settingslib.spa.framework.util.formatString
+import kotlinx.coroutines.launch
+
+@SearchIndexable
+class DataSaverSummary : SettingsPreferenceFragment() {
+ private lateinit var switchBar: SettingsMainSwitchBar
+ private lateinit var dataSaverBackend: DataSaverBackend
+ private lateinit var unrestrictedAccess: Preference
+ private var dataUsageBridge: AppStateDataUsageBridge? = null
+ private var session: ApplicationsState.Session? = null
+
+ // Flag used to avoid infinite loop due if user switch it on/off too quick.
+ private var switching = false
+
+ override fun onCreate(bundle: Bundle?) {
+ super.onCreate(bundle)
+
+ if (!requireContext().isDataSaverVisible()) {
+ finishFragment()
+ return
+ }
+
+ addPreferencesFromResource(R.xml.data_saver)
+ unrestrictedAccess = findPreference(KEY_UNRESTRICTED_ACCESS)!!
+ dataSaverBackend = DataSaverBackend(requireContext())
+ }
+
+ override fun onActivityCreated(savedInstanceState: Bundle?) {
+ super.onActivityCreated(savedInstanceState)
+ switchBar = (activity as SettingsActivity).switchBar.apply {
+ setTitle(getString(R.string.data_saver_switch_title))
+ show()
+ addOnSwitchChangeListener { _: Switch, isChecked: Boolean ->
+ onSwitchChanged(isChecked)
+ }
+ }
+ }
+
+ override fun onResume() {
+ super.onResume()
+ dataSaverBackend.refreshAllowlist()
+ dataSaverBackend.refreshDenylist()
+ dataSaverBackend.addListener(dataSaverBackendListener)
+ dataUsageBridge?.resume(/* forceLoadAllApps= */ true)
+ ?: viewLifecycleOwner.lifecycleScope.launch {
+ val applicationsState = ApplicationsState.getInstance(
+ requireContext().applicationContext as Application
+ )
+ dataUsageBridge = AppStateDataUsageBridge(
+ applicationsState, dataUsageBridgeCallbacks, dataSaverBackend
+ )
+ session =
+ applicationsState.newSession(applicationsStateCallbacks, settingsLifecycle)
+ dataUsageBridge?.resume(/* forceLoadAllApps= */ true)
+ }
+ }
+
+ override fun onPause() {
+ super.onPause()
+ dataSaverBackend.remListener(dataSaverBackendListener)
+ dataUsageBridge?.pause()
+ }
+
+ private fun onSwitchChanged(isChecked: Boolean) {
+ synchronized(this) {
+ if (!switching) {
+ switching = true
+ dataSaverBackend.isDataSaverEnabled = isChecked
+ }
+ }
+ }
+
+ override fun getMetricsCategory() = SettingsEnums.DATA_SAVER_SUMMARY
+
+ override fun getHelpResource() = R.string.help_url_data_saver
+
+ private val dataSaverBackendListener = object : DataSaverBackend.Listener {
+ override fun onDataSaverChanged(isDataSaving: Boolean) {
+ synchronized(this) {
+ switchBar.isChecked = isDataSaving
+ switching = false
+ }
+ }
+
+ override fun onAllowlistStatusChanged(uid: Int, isAllowlisted: Boolean) {}
+
+ override fun onDenylistStatusChanged(uid: Int, isDenylisted: Boolean) {}
+ }
+
+ private val dataUsageBridgeCallbacks = AppStateBaseBridge.Callback {
+ updateUnrestrictedAccessSummary()
+ }
+
+ private val applicationsStateCallbacks = object : ApplicationsState.Callbacks {
+ override fun onRunningStateChanged(running: Boolean) {}
+
+ override fun onPackageListChanged() {}
+
+ override fun onRebuildComplete(apps: ArrayList?) {}
+
+ override fun onPackageIconChanged() {}
+
+ override fun onPackageSizeChanged(packageName: String?) {}
+
+ override fun onAllSizesComputed() {
+ updateUnrestrictedAccessSummary()
+ }
+
+ override fun onLauncherInfoChanged() {
+ updateUnrestrictedAccessSummary()
+ }
+
+ override fun onLoadEntriesCompleted() {}
+ }
+
+ private fun updateUnrestrictedAccessSummary() {
+ if (!isAdded || isFinishingOrDestroyed) return
+ val allApps = session?.allApps ?: return
+ val count = allApps.count {
+ ApplicationsState.FILTER_DOWNLOADED_AND_LAUNCHER.filterApp(it) &&
+ (it.extraInfo as? DataUsageState)?.isDataSaverAllowlisted == true
+ }
+ unrestrictedAccess.summary =
+ resources.formatString(R.string.data_saver_unrestricted_summary, "count" to count)
+ }
+
+ companion object {
+ private const val KEY_UNRESTRICTED_ACCESS = "unrestricted_access"
+
+ private fun Context.isDataSaverVisible(): Boolean =
+ resources.getBoolean(R.bool.config_show_data_saver)
+
+ @JvmField
+ val SEARCH_INDEX_DATA_PROVIDER = object : BaseSearchIndexProvider(R.xml.data_saver) {
+ override fun isPageSearchEnabled(context: Context): Boolean =
+ context.isDataSaverVisible() &&
+ DataUsageUtils.hasMobileData(context) &&
+ (DataUsageUtils.getDefaultSubscriptionId(context) !=
+ SubscriptionManager.INVALID_SUBSCRIPTION_ID)
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/com/android/settings/display/AmbientDisplayAlwaysOnPreferenceController.java b/src/com/android/settings/display/AmbientDisplayAlwaysOnPreferenceController.java
index 5d95ddbfeb8..245803493e2 100644
--- a/src/com/android/settings/display/AmbientDisplayAlwaysOnPreferenceController.java
+++ b/src/com/android/settings/display/AmbientDisplayAlwaysOnPreferenceController.java
@@ -22,6 +22,7 @@ import android.os.PowerManager;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.provider.Settings;
+import android.text.TextUtils;
import androidx.preference.Preference;
@@ -63,7 +64,7 @@ public class AmbientDisplayAlwaysOnPreferenceController extends TogglePreference
@Override
public boolean isPublicSlice() {
- return true;
+ return TextUtils.equals(getPreferenceKey(), "ambient_display_always_on");
}
@Override
diff --git a/src/com/android/settings/fuelgauge/TopLevelBatteryPreferenceController.java b/src/com/android/settings/fuelgauge/TopLevelBatteryPreferenceController.java
index 5bec7bd4aae..d686594275b 100644
--- a/src/com/android/settings/fuelgauge/TopLevelBatteryPreferenceController.java
+++ b/src/com/android/settings/fuelgauge/TopLevelBatteryPreferenceController.java
@@ -30,6 +30,7 @@ import com.android.settings.overlay.FeatureFactory;
import com.android.settingslib.core.lifecycle.LifecycleObserver;
import com.android.settingslib.core.lifecycle.events.OnStart;
import com.android.settingslib.core.lifecycle.events.OnStop;
+import com.android.settingslib.Utils;
import com.android.settingslib.utils.ThreadUtils;
public class TopLevelBatteryPreferenceController extends BasePreferenceController implements
@@ -37,11 +38,13 @@ public class TopLevelBatteryPreferenceController extends BasePreferenceControlle
private static final String TAG = "TopLvBatteryPrefControl";
- @VisibleForTesting
- protected boolean mIsBatteryPresent = true;
@VisibleForTesting
Preference mPreference;
+ @VisibleForTesting
+ protected boolean mIsBatteryPresent = true;
+
private final BatteryBroadcastReceiver mBatteryBroadcastReceiver;
+
private BatteryInfo mBatteryInfo;
private BatteryStatusFeatureProvider mBatteryStatusFeatureProvider;
private String mBatteryStatusLabel;
@@ -55,8 +58,11 @@ public class TopLevelBatteryPreferenceController extends BasePreferenceControlle
mIsBatteryPresent = false;
}
BatteryInfo.getBatteryInfo(mContext, info -> {
+ Log.d(TAG, "getBatteryInfo: " + info);
mBatteryInfo = info;
updateState(mPreference);
+ // Update the preference summary text to the latest state.
+ setSummaryAsync(info);
}, true /* shortString */);
});
@@ -104,18 +110,19 @@ public class TopLevelBatteryPreferenceController extends BasePreferenceControlle
if (info == null || context == null) {
return null;
}
-
- Log.d(TAG, "getDashboardLabel: batteryStatusUpdate=" + batteryStatusUpdate);
+ Log.d(TAG, "getDashboardLabel: " + mBatteryStatusLabel + " batteryStatusUpdate="
+ + batteryStatusUpdate);
if (batteryStatusUpdate) {
setSummaryAsync(info);
}
-
- return (mBatteryStatusLabel == null) ? generateLabel(info) : mBatteryStatusLabel;
+ return mBatteryStatusLabel == null ? generateLabel(info) : mBatteryStatusLabel;
}
private void setSummaryAsync(BatteryInfo info) {
ThreadUtils.postOnBackgroundThread(() -> {
+ // Return false if built-in status should be used, will use updateBatteryStatus()
+ // method to inject the customized battery status label.
final boolean triggerBatteryStatusUpdate =
mBatteryStatusFeatureProvider.triggerBatteryStatusUpdate(this, info);
ThreadUtils.postOnMainThread(() -> {
@@ -123,12 +130,15 @@ public class TopLevelBatteryPreferenceController extends BasePreferenceControlle
mBatteryStatusLabel = null; // will generateLabel()
}
mPreference.setSummary(
- (mBatteryStatusLabel == null) ? generateLabel(info) : mBatteryStatusLabel);
+ mBatteryStatusLabel == null ? generateLabel(info) : mBatteryStatusLabel);
});
});
}
private CharSequence generateLabel(BatteryInfo info) {
+ if (Utils.containsIncompatibleChargers(mContext, TAG)) {
+ return mContext.getString(R.string.battery_info_status_not_charging);
+ }
if (!info.discharging && info.chargeLabel != null) {
return info.chargeLabel;
} else if (info.remainingLabel == null) {
@@ -146,13 +156,13 @@ public class TopLevelBatteryPreferenceController extends BasePreferenceControlle
@Override
public void updateBatteryStatus(String label, BatteryInfo info) {
mBatteryStatusLabel = label; // Null if adaptive charging is not active
-
- if (mPreference != null) {
- // Do not triggerBatteryStatusUpdate(), otherwise there will be an infinite loop
- final CharSequence summary = getSummary(false /* batteryStatusUpdate */);
- if (summary != null) {
- mPreference.setSummary(summary);
- }
+ if (mPreference == null) {
+ return;
+ }
+ // Do not triggerBatteryStatusUpdate() here to cause infinite loop
+ final CharSequence summary = getSummary(false /* batteryStatusUpdate */);
+ if (summary != null) {
+ mPreference.setSummary(summary);
}
}
@@ -170,4 +180,4 @@ public class TopLevelBatteryPreferenceController extends BasePreferenceControlle
String pkgName = lastPkgIndex > 0 ? classPath.substring(0, lastPkgIndex) : "";
return new ComponentName(pkgName, split[classNameIndex]);
}
-}
\ No newline at end of file
+}
diff --git a/src/com/android/settings/spa/SettingsSpaEnvironment.kt b/src/com/android/settings/spa/SettingsSpaEnvironment.kt
index 455fe9ff4b9..caf5b1532ce 100644
--- a/src/com/android/settings/spa/SettingsSpaEnvironment.kt
+++ b/src/com/android/settings/spa/SettingsSpaEnvironment.kt
@@ -29,6 +29,7 @@ import com.android.settings.spa.app.specialaccess.DisplayOverOtherAppsAppListPro
import com.android.settings.spa.app.specialaccess.InstallUnknownAppsListProvider
import com.android.settings.spa.app.specialaccess.MediaManagementAppsAppListProvider
import com.android.settings.spa.app.specialaccess.ModifySystemSettingsAppListProvider
+import com.android.settings.spa.app.specialaccess.NfcTagAppsSettingsProvider
import com.android.settings.spa.app.specialaccess.PictureInPictureListProvider
import com.android.settings.spa.app.specialaccess.SpecialAppAccessPageProvider
import com.android.settings.spa.app.specialaccess.WifiControlAppListProvider
@@ -61,6 +62,7 @@ open class SettingsSpaEnvironment(context: Context) : SpaEnvironment(context) {
InstallUnknownAppsListProvider,
AlarmsAndRemindersAppListProvider,
WifiControlAppListProvider,
+ NfcTagAppsSettingsProvider,
)
}
diff --git a/src/com/android/settings/spa/app/specialaccess/NfcTagAppsSettings.kt b/src/com/android/settings/spa/app/specialaccess/NfcTagAppsSettings.kt
new file mode 100644
index 00000000000..3dede42a34d
--- /dev/null
+++ b/src/com/android/settings/spa/app/specialaccess/NfcTagAppsSettings.kt
@@ -0,0 +1,127 @@
+/*
+ * 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.spa.app.specialaccess
+
+import android.content.Context
+import android.content.pm.ApplicationInfo
+import android.content.pm.PackageManager.GET_ACTIVITIES
+import android.content.pm.PackageManager.PackageInfoFlags
+import android.nfc.NfcAdapter
+import android.util.Log
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.livedata.observeAsState
+import com.android.settings.R
+import com.android.settingslib.spaprivileged.model.app.AppRecord
+import com.android.settingslib.spaprivileged.model.app.userId
+import com.android.settingslib.spaprivileged.template.app.TogglePermissionAppListModel
+import com.android.settingslib.spaprivileged.template.app.TogglePermissionAppListProvider
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.map
+
+object NfcTagAppsSettingsProvider : TogglePermissionAppListProvider {
+ override val permissionType = "NfcTagAppsSettings"
+ override fun createModel(context: Context) = NfcTagAppsSettingsListModel(context)
+}
+
+data class NfcTagAppsSettingsRecord(
+ override val app: ApplicationInfo,
+ val controller: NfcTagAppsSettingsController,
+ val isSupported: Boolean,
+) : AppRecord
+
+class NfcTagAppsSettingsListModel(private val context: Context) :
+ TogglePermissionAppListModel {
+ override val pageTitleResId = R.string.change_nfc_tag_apps_title
+ override val switchTitleResId = R.string.change_nfc_tag_apps_detail_switch
+ override val footerResId = R.string.change_nfc_tag_apps_detail_summary
+
+ private val packageManager = context.packageManager
+
+ override fun transform(
+ userIdFlow: Flow,
+ appListFlow: Flow>
+ ): Flow> =
+ userIdFlow.combine(appListFlow) { userId, appList ->
+ // The appListFlow always refreshed on resume, need to update nfcTagAppsSettingsPackages
+ // here to handle status change.
+ val nfcTagAppsSettingsPackages = getNfcTagAppsSettingsPackages(userId)
+ appList.map { app ->
+ createNfcTagAppsSettingsRecord(
+ app = app,
+ isAllowed = nfcTagAppsSettingsPackages[app.packageName],
+ )
+ }
+ }
+
+ private fun getNfcTagAppsSettingsPackages(userId: Int): Map {
+ NfcAdapter.getDefaultAdapter(context)?.let { nfcAdapter ->
+ if (nfcAdapter.isTagIntentAppPreferenceSupported) {
+ return nfcAdapter.getTagIntentAppPreferenceForUser(userId)
+ }
+ }
+ return emptyMap()
+ }
+
+ override fun transformItem(app: ApplicationInfo) =
+ createNfcTagAppsSettingsRecord(
+ app = app,
+ isAllowed = getNfcTagAppsSettingsPackages(app.userId)[app.packageName],
+ )
+
+ private fun createNfcTagAppsSettingsRecord(
+ app: ApplicationInfo,
+ isAllowed: Boolean?,
+ ) =
+ NfcTagAppsSettingsRecord(
+ app = app,
+ isSupported = isAllowed != null,
+ controller = NfcTagAppsSettingsController(isAllowed == true),
+ )
+
+ override fun filter(
+ userIdFlow: Flow,
+ recordListFlow: Flow>
+ ) = recordListFlow.map { recordList -> recordList.filter { it.isSupported } }
+
+ @Composable
+ override fun isAllowed(record: NfcTagAppsSettingsRecord) =
+ record.controller.isAllowed.observeAsState()
+
+ override fun isChangeable(record: NfcTagAppsSettingsRecord) = true
+
+ override fun setAllowed(record: NfcTagAppsSettingsRecord, newAllowed: Boolean) {
+ NfcAdapter.getDefaultAdapter(context)?.let {
+ if (
+ it.setTagIntentAppPreferenceForUser(
+ record.app.userId,
+ record.app.packageName,
+ newAllowed
+ ) == NfcAdapter.TAG_INTENT_APP_PREF_RESULT_SUCCESS
+ ) {
+ record.controller.setAllowed(newAllowed)
+ } else {
+ Log.e(TAG, "Error updating TagIntentAppPreference")
+ }
+ }
+ }
+
+ private companion object {
+ const val TAG = "NfcTagAppsSettingsListModel"
+ val GET_ACTIVITIES_FLAGS = PackageInfoFlags.of(GET_ACTIVITIES.toLong())
+ }
+}
diff --git a/src/com/android/settings/spa/app/specialaccess/NfcTagAppsSettingsController.kt b/src/com/android/settings/spa/app/specialaccess/NfcTagAppsSettingsController.kt
new file mode 100644
index 00000000000..6e1b7b36f70
--- /dev/null
+++ b/src/com/android/settings/spa/app/specialaccess/NfcTagAppsSettingsController.kt
@@ -0,0 +1,30 @@
+/*
+ * 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.spa.app.specialaccess
+
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.MutableLiveData
+
+class NfcTagAppsSettingsController(initialStatus: Boolean) {
+ val isAllowed: LiveData
+ get() = _allowed
+
+ fun setAllowed(newAllowed: Boolean) {
+ _allowed.postValue(newAllowed)
+ }
+ private val _allowed = MutableLiveData(initialStatus)
+}
diff --git a/tests/robotests/src/com/android/settings/accessibility/SystemControlsFragmentTest.java b/tests/robotests/src/com/android/settings/accessibility/SystemControlsFragmentTest.java
index 1d8fb32a0c4..506882b45fb 100644
--- a/tests/robotests/src/com/android/settings/accessibility/SystemControlsFragmentTest.java
+++ b/tests/robotests/src/com/android/settings/accessibility/SystemControlsFragmentTest.java
@@ -25,16 +25,19 @@ import androidx.test.core.app.ApplicationProvider;
import com.android.settings.R;
import com.android.settings.testutils.XmlTestUtils;
+import com.android.settings.testutils.shadow.ShadowKeyCharacterMap;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.Config;
import java.util.List;
/** Tests for {@link SystemControlsFragment}. */
@RunWith(RobolectricTestRunner.class)
+@Config(shadows = {ShadowKeyCharacterMap.class})
public class SystemControlsFragmentTest {
private final Context mContext = ApplicationProvider.getApplicationContext();
diff --git a/tests/robotests/src/com/android/settings/applications/ClonedAppsPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/applications/ClonedAppsPreferenceControllerTest.java
index 828d88d994c..56117d1952c 100644
--- a/tests/robotests/src/com/android/settings/applications/ClonedAppsPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/applications/ClonedAppsPreferenceControllerTest.java
@@ -23,12 +23,15 @@ import static com.android.settings.core.BasePreferenceController.AVAILABLE;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
import android.content.Context;
+import android.content.res.Resources;
import android.provider.DeviceConfig;
import androidx.test.core.app.ApplicationProvider;
+import com.android.settings.R;
import com.android.settings.Utils;
import com.android.settings.testutils.shadow.ShadowDeviceConfig;
@@ -45,10 +48,15 @@ public class ClonedAppsPreferenceControllerTest {
private ClonedAppsPreferenceController mController;
private static final String KEY = "key";
private Context mContext;
+ private Resources mResources;
@Before
public void setUp() {
mContext = spy(ApplicationProvider.getApplicationContext());
+
+ mResources = spy(mContext.getResources());
+ when(mContext.getResources()).thenReturn(mResources);
+
mController = new ClonedAppsPreferenceController(mContext, KEY);
}
@@ -56,6 +64,7 @@ public class ClonedAppsPreferenceControllerTest {
public void getAvailabilityStatus_featureNotEnabled_shouldNotReturnAvailable() {
DeviceConfig.setProperty(NAMESPACE_APP_CLONING, Utils.PROPERTY_CLONED_APPS_ENABLED,
"false", true /* makeDefault */);
+ when(mResources.getBoolean(R.bool.config_cloned_apps_page_enabled)).thenReturn(false);
assertThat(mController.getAvailabilityStatus()).isNotEqualTo(AVAILABLE);
}
@@ -64,7 +73,26 @@ public class ClonedAppsPreferenceControllerTest {
public void getAvailabilityStatus_featureEnabled_shouldReturnAvailable() {
DeviceConfig.setProperty(NAMESPACE_APP_CLONING, Utils.PROPERTY_CLONED_APPS_ENABLED,
"true", true /* makeDefault */);
+ when(mResources.getBoolean(R.bool.config_cloned_apps_page_enabled)).thenReturn(true);
assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
}
+
+ @Test
+ public void getAvailabilityStatus_deviceConfigFalseAndConfigEnabled_shouldNotReturnAvailable() {
+ DeviceConfig.setProperty(NAMESPACE_APP_CLONING, Utils.PROPERTY_CLONED_APPS_ENABLED,
+ "false", true /* makeDefault */);
+ when(mResources.getBoolean(R.bool.config_cloned_apps_page_enabled)).thenReturn(true);
+
+ assertThat(mController.getAvailabilityStatus()).isNotEqualTo(AVAILABLE);
+ }
+
+ @Test
+ public void getAvailabilityStatus_deviceConfigTrueAndConfigDisabled_shouldNotReturnAvailable() {
+ DeviceConfig.setProperty(NAMESPACE_APP_CLONING, Utils.PROPERTY_CLONED_APPS_ENABLED,
+ "true", true /* makeDefault */);
+ when(mResources.getBoolean(R.bool.config_cloned_apps_page_enabled)).thenReturn(false);
+
+ assertThat(mController.getAvailabilityStatus()).isNotEqualTo(AVAILABLE);
+ }
}
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/TopLevelBatteryPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/TopLevelBatteryPreferenceControllerTest.java
index 85e29429c9e..5f825ae2cef 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/TopLevelBatteryPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/TopLevelBatteryPreferenceControllerTest.java
@@ -28,6 +28,9 @@ import static org.mockito.Mockito.when;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.hardware.usb.UsbManager;
+import android.hardware.usb.UsbPort;
+import android.hardware.usb.UsbPortStatus;
import androidx.preference.Preference;
import androidx.test.core.app.ApplicationProvider;
@@ -38,21 +41,33 @@ import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
+import java.util.ArrayList;
+import java.util.List;
+
@RunWith(RobolectricTestRunner.class)
public class TopLevelBatteryPreferenceControllerTest {
private Context mContext;
private TopLevelBatteryPreferenceController mController;
private BatterySettingsFeatureProvider mBatterySettingsFeatureProvider;
+ @Mock
+ private UsbPort mUsbPort;
+ @Mock
+ private UsbManager mUsbManager;
+ @Mock
+ private UsbPortStatus mUsbPortStatus;
+
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = spy(ApplicationProvider.getApplicationContext());
mController = new TopLevelBatteryPreferenceController(mContext, "test_key");
+ when(mContext.getSystemService(UsbManager.class)).thenReturn(mUsbManager);
}
@Test
@@ -88,27 +103,61 @@ public class TopLevelBatteryPreferenceControllerTest {
}
@Test
- public void getDashboardLabel_returnsCorrectLabel() {
+ public void getDashboardLabel_returnsBatterPercentString() {
mController.mPreference = new Preference(mContext);
BatteryInfo info = new BatteryInfo();
info.batteryPercentString = "3%";
+
assertThat(mController.getDashboardLabel(mContext, info, true))
.isEqualTo(info.batteryPercentString);
+ }
+ @Test
+ public void getDashboardLabel_returnsRemainingLabel() {
+ mController.mPreference = new Preference(mContext);
+ BatteryInfo info = new BatteryInfo();
+ info.batteryPercentString = "3%";
info.remainingLabel = "Phone will shut down soon";
+
assertThat(mController.getDashboardLabel(mContext, info, true))
.isEqualTo("3% - Phone will shut down soon");
+ }
+ @Test
+ public void getDashboardLabel_returnsChargeLabel() {
+ mController.mPreference = new Preference(mContext);
+ BatteryInfo info = new BatteryInfo();
info.discharging = false;
info.chargeLabel = "5% - charging";
- assertThat(mController.getDashboardLabel(mContext, info, true)).isEqualTo("5% - charging");
+
+ assertThat(mController.getDashboardLabel(mContext, info, true))
+ .isEqualTo(info.chargeLabel);
+ }
+
+ @Test
+ public void getDashboardLabel_incompatibleCharger_returnsCorrectLabel() {
+ setupIncompatibleEvent();
+ mController.mPreference = new Preference(mContext);
+ BatteryInfo info = new BatteryInfo();
+
+ assertThat(mController.getDashboardLabel(mContext, info, true))
+ .isEqualTo(mContext.getString(R.string.battery_info_status_not_charging));
}
@Test
public void getSummary_batteryNotPresent_shouldShowWarningMessage() {
mController.mIsBatteryPresent = false;
-
assertThat(mController.getSummary())
.isEqualTo(mContext.getString(R.string.battery_missing_message));
}
+
+ private void setupIncompatibleEvent() {
+ final List usbPorts = new ArrayList<>();
+ usbPorts.add(mUsbPort);
+ when(mUsbManager.getPorts()).thenReturn(usbPorts);
+ when(mUsbPort.getStatus()).thenReturn(mUsbPortStatus);
+ when(mUsbPort.supportsComplianceWarnings()).thenReturn(true);
+ when(mUsbPortStatus.isConnected()).thenReturn(true);
+ when(mUsbPortStatus.getComplianceWarnings()).thenReturn(new int[]{1});
+ }
}
diff --git a/tests/robotests/src/com/android/settings/network/PrivateDnsPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/network/PrivateDnsPreferenceControllerTest.java
index 057b6cbf0b9..b2f0ad55bc7 100644
--- a/tests/robotests/src/com/android/settings/network/PrivateDnsPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/network/PrivateDnsPreferenceControllerTest.java
@@ -169,6 +169,7 @@ public class PrivateDnsPreferenceControllerTest {
@Test
public void getAvailibilityStatus_availableByDefault() {
+ doReturn(true).when(mUserManager).isAdminUser();
assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
}