Snap for 10054401 from 09b2df4303 to udc-qpr1-release

Change-Id: Ib87bdcd8dd0ef0387dc7a1a7140642db60966d86
This commit is contained in:
Android Build Coastguard Worker
2023-05-03 01:31:43 +00:00
23 changed files with 770 additions and 280 deletions

View File

@@ -17,6 +17,6 @@
<selector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
<item android:state_selected="true" android:color="?androidprv:attr/colorAccentPrimary"/>
<item android:color="?androidprv:attr/materialColorSurfaceContainer"/>
<item android:state_selected="true" android:color="?androidprv:attr/materialColorPrimaryContainer"/>
<item android:color="?androidprv:attr/materialColorSurfaceBright"/>
</selector>

View File

@@ -17,6 +17,6 @@
<selector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
<item android:state_selected="true" android:color="?androidprv:attr/materialColorOnPrimary"/>
<item android:color="?androidprv:attr/materialColorPrimaryContainer"/>
<item android:state_selected="true" android:color="?androidprv:attr/materialColorOnPrimaryContainer"/>
<item android:color="?androidprv:attr/materialColorPrimary"/>
</selector>

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ 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.
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
<item android:state_selected="true" android:color="?androidprv:attr/materialColorOnPrimaryContainer"/>
<item android:color="?androidprv:attr/materialColorOnSurfaceVariant"/>
</selector>

View File

@@ -17,6 +17,6 @@
<selector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
<item android:state_selected="true" android:color="?androidprv:attr/materialColorOnPrimary"/>
<item android:color="?android:attr/textColorPrimary"/>
<item android:state_selected="true" android:color="?androidprv:attr/materialColorOnPrimaryContainer"/>
<item android:color="?androidprv:attr/materialColorOnSurface"/>
</selector>

View File

@@ -15,10 +15,11 @@
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path android:fillColor="?android:attr/textColorSecondary"
<path android:fillColor="?androidprv:attr/materialColorOnSurfaceVariant"
android:pathData="M3,19Q2.175,19 1.588,18.413Q1,17.825 1,17V7Q1,6.175 1.588,5.588Q2.175,5 3,5H13Q13.825,5 14.413,5.588Q15,6.175 15,7V17Q15,17.825 14.413,18.413Q13.825,19 13,19ZM3,17H13Q13,17 13,17Q13,17 13,17V7Q13,7 13,7Q13,7 13,7H3Q3,7 3,7Q3,7 3,7V17Q3,17 3,17Q3,17 3,17ZM17,19V5H19V19ZM21,19V5H23V19ZM4,15H12L9.4,11.5L7.5,14L6.1,12.15ZM3,7Q3,7 3,7Q3,7 3,7V17Q3,17 3,17Q3,17 3,17Q3,17 3,17Q3,17 3,17V7Q3,7 3,7Q3,7 3,7Z"/>
</vector>

View File

@@ -17,6 +17,6 @@
<shape xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
android:shape="rectangle">
<solid android:color="?androidprv:attr/materialColorSurfaceBright" />
<solid android:color="?androidprv:attr/materialColorSurfaceContainer" />
<corners android:radius="@dimen/dream_item_corner_radius"/>
</shape>

View File

@@ -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"

View File

@@ -17,17 +17,16 @@
<com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/dream_preview_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/dream_preview_button_title"
android:textAllCaps="false"
android:textColor="?android:attr/textColorPrimaryInverse"
android:textColor="@color/settingslib_btn_colored_text_material"
android:theme="@style/Theme.CollapsingToolbar.Settings"
android:layout_marginBottom="16dp"
android:layout_gravity="bottom|center"
app:backgroundTint="?androidprv:attr/materialColorPrimaryContainer"
app:iconTint="?android:attr/textColorPrimaryInverse"
app:backgroundTint="@color/settingslib_btn_colored_background_material"
app:iconTint="@color/settingslib_btn_colored_text_material"
app:icon="@drawable/dream_preview_icon"/>

View File

@@ -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

View File

@@ -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<CredentialProviderInfo> 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<CredentialProviderInfo> 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<CredentialProviderInfo> 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<String, ServiceInfo> flattenedNamesToServices = new HashMap<>();
List<String> 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<String> 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<CombinedProviderInfo> 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<CombinedProviderInfo> buildMergedList(
List<AutofillServiceInfo> asiList,
List<CredentialProviderInfo> cpiList,
@Nullable String defaultAutofillProvider) {
ComponentName defaultAutofillProviderComponent =
(defaultAutofillProvider == null)
? null
: ComponentName.unflattenFromString(defaultAutofillProvider);
// Index the autofill providers by package name.
Set<String> packageNames = new HashSet<>();
Map<String, List<AutofillServiceInfo>> 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<String, List<CredentialProviderInfo>> 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<CombinedProviderInfo> cmpi = new ArrayList<>();
for (String packageName : packageNames) {
List<AutofillServiceInfo> asi = autofillServices.get(packageName);
List<CredentialProviderInfo> 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;
}
}

View File

@@ -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
}
}

View File

@@ -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<AbstractPreferenceController> buildPreferenceControllers(Context context) {
final List<AbstractPreferenceController> 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;
}

View File

@@ -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<AppEntry> 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<String, Object> 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;
}
};
}

View File

@@ -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<ApplicationsState.AppEntry>?) {}
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)
}
}
}

View File

@@ -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

View File

@@ -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]);
}
}
}

View File

@@ -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,
)
}

View File

@@ -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<NfcTagAppsSettingsRecord> {
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<Int>,
appListFlow: Flow<List<ApplicationInfo>>
): Flow<List<NfcTagAppsSettingsRecord>> =
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<String, Boolean> {
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<Int>,
recordListFlow: Flow<List<NfcTagAppsSettingsRecord>>
) = 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())
}
}

View File

@@ -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<Boolean>
get() = _allowed
fun setAllowed(newAllowed: Boolean) {
_allowed.postValue(newAllowed)
}
private val _allowed = MutableLiveData<Boolean>(initialStatus)
}

View File

@@ -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();

View File

@@ -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);
}
}

View File

@@ -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<UsbPort> 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});
}
}

View File

@@ -169,6 +169,7 @@ public class PrivateDnsPreferenceControllerTest {
@Test
public void getAvailibilityStatus_availableByDefault() {
doReturn(true).when(mUserManager).isAdminUser();
assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
}