Snap for 10054401 from 09b2df4303 to udc-qpr1-release
Change-Id: Ib87bdcd8dd0ef0387dc7a1a7140642db60966d86
This commit is contained in:
@@ -17,6 +17,6 @@
|
|||||||
|
|
||||||
<selector xmlns:android="http://schemas.android.com/apk/res/android"
|
<selector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
|
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
|
||||||
<item android:state_selected="true" android:color="?androidprv:attr/colorAccentPrimary"/>
|
<item android:state_selected="true" android:color="?androidprv:attr/materialColorPrimaryContainer"/>
|
||||||
<item android:color="?androidprv:attr/materialColorSurfaceContainer"/>
|
<item android:color="?androidprv:attr/materialColorSurfaceBright"/>
|
||||||
</selector>
|
</selector>
|
||||||
@@ -17,6 +17,6 @@
|
|||||||
|
|
||||||
<selector xmlns:android="http://schemas.android.com/apk/res/android"
|
<selector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
|
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
|
||||||
<item android:state_selected="true" android:color="?androidprv:attr/materialColorOnPrimary"/>
|
<item android:state_selected="true" android:color="?androidprv:attr/materialColorOnPrimaryContainer"/>
|
||||||
<item android:color="?androidprv:attr/materialColorPrimaryContainer"/>
|
<item android:color="?androidprv:attr/materialColorPrimary"/>
|
||||||
</selector>
|
</selector>
|
||||||
22
res/color/dream_card_summary_color_state_list.xml
Normal file
22
res/color/dream_card_summary_color_state_list.xml
Normal 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>
|
||||||
@@ -17,6 +17,6 @@
|
|||||||
|
|
||||||
<selector xmlns:android="http://schemas.android.com/apk/res/android"
|
<selector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
|
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
|
||||||
<item android:state_selected="true" android:color="?androidprv:attr/materialColorOnPrimary"/>
|
<item android:state_selected="true" android:color="?androidprv:attr/materialColorOnPrimaryContainer"/>
|
||||||
<item android:color="?android:attr/textColorPrimary"/>
|
<item android:color="?androidprv:attr/materialColorOnSurface"/>
|
||||||
</selector>
|
</selector>
|
||||||
@@ -15,10 +15,11 @@
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
|
||||||
android:width="24dp"
|
android:width="24dp"
|
||||||
android:height="24dp"
|
android:height="24dp"
|
||||||
android:viewportWidth="24"
|
android:viewportWidth="24"
|
||||||
android:viewportHeight="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"/>
|
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>
|
</vector>
|
||||||
@@ -17,6 +17,6 @@
|
|||||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
|
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
|
||||||
android:shape="rectangle">
|
android:shape="rectangle">
|
||||||
<solid android:color="?androidprv:attr/materialColorSurfaceBright" />
|
<solid android:color="?androidprv:attr/materialColorSurfaceContainer" />
|
||||||
<corners android:radius="@dimen/dream_item_corner_radius"/>
|
<corners android:radius="@dimen/dream_item_corner_radius"/>
|
||||||
</shape>
|
</shape>
|
||||||
@@ -93,7 +93,7 @@
|
|||||||
android:maxLines="2"
|
android:maxLines="2"
|
||||||
android:ellipsize="end"
|
android:ellipsize="end"
|
||||||
android:textSize="@dimen/dream_item_summary_text_size"
|
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_constraintTop_toBottomOf="@+id/title_text"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
|||||||
@@ -17,17 +17,16 @@
|
|||||||
|
|
||||||
<com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
|
<com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
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"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
android:id="@+id/dream_preview_button"
|
android:id="@+id/dream_preview_button"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="@string/dream_preview_button_title"
|
android:text="@string/dream_preview_button_title"
|
||||||
android:textAllCaps="false"
|
android:textAllCaps="false"
|
||||||
android:textColor="?android:attr/textColorPrimaryInverse"
|
android:textColor="@color/settingslib_btn_colored_text_material"
|
||||||
android:theme="@style/Theme.CollapsingToolbar.Settings"
|
android:theme="@style/Theme.CollapsingToolbar.Settings"
|
||||||
android:layout_marginBottom="16dp"
|
android:layout_marginBottom="16dp"
|
||||||
android:layout_gravity="bottom|center"
|
android:layout_gravity="bottom|center"
|
||||||
app:backgroundTint="?androidprv:attr/materialColorPrimaryContainer"
|
app:backgroundTint="@color/settingslib_btn_colored_background_material"
|
||||||
app:iconTint="?android:attr/textColorPrimaryInverse"
|
app:iconTint="@color/settingslib_btn_colored_text_material"
|
||||||
app:icon="@drawable/dream_preview_icon"/>
|
app:icon="@drawable/dream_preview_icon"/>
|
||||||
|
|||||||
@@ -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.InstallUnknownAppsListProvider
|
||||||
import com.android.settings.spa.app.specialaccess.MediaManagementAppsAppListProvider
|
import com.android.settings.spa.app.specialaccess.MediaManagementAppsAppListProvider
|
||||||
import com.android.settings.spa.app.specialaccess.ModifySystemSettingsAppListProvider
|
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.PictureInPictureListProvider
|
||||||
import com.android.settings.spa.app.specialaccess.WifiControlAppListProvider
|
import com.android.settings.spa.app.specialaccess.WifiControlAppListProvider
|
||||||
import com.android.settings.wifi.ChangeWifiStateDetails
|
import com.android.settings.wifi.ChangeWifiStateDetails
|
||||||
@@ -62,6 +63,8 @@ object SettingsActivityUtil {
|
|||||||
MediaManagementAppsAppListProvider.getAppInfoRoutePrefix(),
|
MediaManagementAppsAppListProvider.getAppInfoRoutePrefix(),
|
||||||
ChangeWifiStateDetails::class.qualifiedName to
|
ChangeWifiStateDetails::class.qualifiedName to
|
||||||
WifiControlAppListProvider.getAppInfoRoutePrefix(),
|
WifiControlAppListProvider.getAppInfoRoutePrefix(),
|
||||||
|
NfcTagAppsSettingsProvider::class.qualifiedName to
|
||||||
|
NfcTagAppsSettingsProvider.getAppInfoRoutePrefix(),
|
||||||
)
|
)
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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.InstallUnknownAppsListProvider
|
||||||
import com.android.settings.spa.app.specialaccess.MediaManagementAppsAppListProvider
|
import com.android.settings.spa.app.specialaccess.MediaManagementAppsAppListProvider
|
||||||
import com.android.settings.spa.app.specialaccess.ModifySystemSettingsAppListProvider
|
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.app.specialaccess.WifiControlAppListProvider
|
||||||
import com.android.settings.spa.notification.AppListNotificationsPageProvider
|
import com.android.settings.spa.notification.AppListNotificationsPageProvider
|
||||||
import com.android.settings.spa.system.AppLanguagesPageProvider
|
import com.android.settings.spa.system.AppLanguagesPageProvider
|
||||||
@@ -112,6 +113,7 @@ object ManageApplicationsUtil {
|
|||||||
LIST_TYPE_NOTIFICATION -> AppListNotificationsPageProvider.name
|
LIST_TYPE_NOTIFICATION -> AppListNotificationsPageProvider.name
|
||||||
LIST_TYPE_APPS_LOCALE -> AppLanguagesPageProvider.name
|
LIST_TYPE_APPS_LOCALE -> AppLanguagesPageProvider.name
|
||||||
LIST_TYPE_MAIN -> AllAppListPageProvider.name
|
LIST_TYPE_MAIN -> AllAppListPageProvider.name
|
||||||
|
LIST_TYPE_NFC_TAG_APPS -> NfcTagAppsSettingsProvider.getAppListRoute()
|
||||||
else -> null
|
else -> null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -283,7 +283,9 @@ public class FingerprintSettings extends SubSettings {
|
|||||||
case MSG_REFRESH_FINGERPRINT_TEMPLATES:
|
case MSG_REFRESH_FINGERPRINT_TEMPLATES:
|
||||||
removeFingerprintPreference(msg.arg1);
|
removeFingerprintPreference(msg.arg1);
|
||||||
updateAddPreference();
|
updateAddPreference();
|
||||||
|
if (isSfps()) {
|
||||||
updateFingerprintUnlockCategoryVisibility();
|
updateFingerprintUnlockCategoryVisibility();
|
||||||
|
}
|
||||||
updatePreferences();
|
updatePreferences();
|
||||||
break;
|
break;
|
||||||
case MSG_FINGER_AUTH_SUCCESS:
|
case MSG_FINGER_AUTH_SUCCESS:
|
||||||
@@ -494,11 +496,15 @@ public class FingerprintSettings extends SubSettings {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private boolean isSfps() {
|
private boolean isSfps() {
|
||||||
|
mFingerprintManager = Utils.getFingerprintManagerOrNull(getActivity());
|
||||||
|
if (mFingerprintManager != null) {
|
||||||
|
mSensorProperties = mFingerprintManager.getSensorPropertiesInternal();
|
||||||
for (FingerprintSensorPropertiesInternal prop : mSensorProperties) {
|
for (FingerprintSensorPropertiesInternal prop : mSensorProperties) {
|
||||||
if (prop.isAnySidefpsType()) {
|
if (prop.isAnySidefpsType()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -838,6 +844,7 @@ public class FingerprintSettings extends SubSettings {
|
|||||||
|
|
||||||
private List<AbstractPreferenceController> buildPreferenceControllers(Context context) {
|
private List<AbstractPreferenceController> buildPreferenceControllers(Context context) {
|
||||||
final List<AbstractPreferenceController> controllers = new ArrayList<>();
|
final List<AbstractPreferenceController> controllers = new ArrayList<>();
|
||||||
|
if (isSfps()) {
|
||||||
mFingerprintUnlockCategoryPreferenceController =
|
mFingerprintUnlockCategoryPreferenceController =
|
||||||
new FingerprintUnlockCategoryController(
|
new FingerprintUnlockCategoryController(
|
||||||
context,
|
context,
|
||||||
@@ -850,6 +857,7 @@ public class FingerprintSettings extends SubSettings {
|
|||||||
);
|
);
|
||||||
controllers.add(mFingerprintUnlockCategoryPreferenceController);
|
controllers.add(mFingerprintUnlockCategoryPreferenceController);
|
||||||
controllers.add(mRequireScreenOnToAuthPreferenceController);
|
controllers.add(mRequireScreenOnToAuthPreferenceController);
|
||||||
|
}
|
||||||
return controllers;
|
return controllers;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
176
src/com/android/settings/datausage/DataSaverSummary.kt
Normal file
176
src/com/android/settings/datausage/DataSaverSummary.kt
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -22,6 +22,7 @@ import android.os.PowerManager;
|
|||||||
import android.os.SystemProperties;
|
import android.os.SystemProperties;
|
||||||
import android.os.UserHandle;
|
import android.os.UserHandle;
|
||||||
import android.provider.Settings;
|
import android.provider.Settings;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
|
||||||
import androidx.preference.Preference;
|
import androidx.preference.Preference;
|
||||||
|
|
||||||
@@ -63,7 +64,7 @@ public class AmbientDisplayAlwaysOnPreferenceController extends TogglePreference
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isPublicSlice() {
|
public boolean isPublicSlice() {
|
||||||
return true;
|
return TextUtils.equals(getPreferenceKey(), "ambient_display_always_on");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ import com.android.settings.overlay.FeatureFactory;
|
|||||||
import com.android.settingslib.core.lifecycle.LifecycleObserver;
|
import com.android.settingslib.core.lifecycle.LifecycleObserver;
|
||||||
import com.android.settingslib.core.lifecycle.events.OnStart;
|
import com.android.settingslib.core.lifecycle.events.OnStart;
|
||||||
import com.android.settingslib.core.lifecycle.events.OnStop;
|
import com.android.settingslib.core.lifecycle.events.OnStop;
|
||||||
|
import com.android.settingslib.Utils;
|
||||||
import com.android.settingslib.utils.ThreadUtils;
|
import com.android.settingslib.utils.ThreadUtils;
|
||||||
|
|
||||||
public class TopLevelBatteryPreferenceController extends BasePreferenceController implements
|
public class TopLevelBatteryPreferenceController extends BasePreferenceController implements
|
||||||
@@ -37,11 +38,13 @@ public class TopLevelBatteryPreferenceController extends BasePreferenceControlle
|
|||||||
|
|
||||||
private static final String TAG = "TopLvBatteryPrefControl";
|
private static final String TAG = "TopLvBatteryPrefControl";
|
||||||
|
|
||||||
@VisibleForTesting
|
|
||||||
protected boolean mIsBatteryPresent = true;
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
Preference mPreference;
|
Preference mPreference;
|
||||||
|
@VisibleForTesting
|
||||||
|
protected boolean mIsBatteryPresent = true;
|
||||||
|
|
||||||
private final BatteryBroadcastReceiver mBatteryBroadcastReceiver;
|
private final BatteryBroadcastReceiver mBatteryBroadcastReceiver;
|
||||||
|
|
||||||
private BatteryInfo mBatteryInfo;
|
private BatteryInfo mBatteryInfo;
|
||||||
private BatteryStatusFeatureProvider mBatteryStatusFeatureProvider;
|
private BatteryStatusFeatureProvider mBatteryStatusFeatureProvider;
|
||||||
private String mBatteryStatusLabel;
|
private String mBatteryStatusLabel;
|
||||||
@@ -55,8 +58,11 @@ public class TopLevelBatteryPreferenceController extends BasePreferenceControlle
|
|||||||
mIsBatteryPresent = false;
|
mIsBatteryPresent = false;
|
||||||
}
|
}
|
||||||
BatteryInfo.getBatteryInfo(mContext, info -> {
|
BatteryInfo.getBatteryInfo(mContext, info -> {
|
||||||
|
Log.d(TAG, "getBatteryInfo: " + info);
|
||||||
mBatteryInfo = info;
|
mBatteryInfo = info;
|
||||||
updateState(mPreference);
|
updateState(mPreference);
|
||||||
|
// Update the preference summary text to the latest state.
|
||||||
|
setSummaryAsync(info);
|
||||||
}, true /* shortString */);
|
}, true /* shortString */);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -104,18 +110,19 @@ public class TopLevelBatteryPreferenceController extends BasePreferenceControlle
|
|||||||
if (info == null || context == null) {
|
if (info == null || context == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
Log.d(TAG, "getDashboardLabel: " + mBatteryStatusLabel + " batteryStatusUpdate="
|
||||||
Log.d(TAG, "getDashboardLabel: batteryStatusUpdate=" + batteryStatusUpdate);
|
+ batteryStatusUpdate);
|
||||||
|
|
||||||
if (batteryStatusUpdate) {
|
if (batteryStatusUpdate) {
|
||||||
setSummaryAsync(info);
|
setSummaryAsync(info);
|
||||||
}
|
}
|
||||||
|
return mBatteryStatusLabel == null ? generateLabel(info) : mBatteryStatusLabel;
|
||||||
return (mBatteryStatusLabel == null) ? generateLabel(info) : mBatteryStatusLabel;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setSummaryAsync(BatteryInfo info) {
|
private void setSummaryAsync(BatteryInfo info) {
|
||||||
ThreadUtils.postOnBackgroundThread(() -> {
|
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 =
|
final boolean triggerBatteryStatusUpdate =
|
||||||
mBatteryStatusFeatureProvider.triggerBatteryStatusUpdate(this, info);
|
mBatteryStatusFeatureProvider.triggerBatteryStatusUpdate(this, info);
|
||||||
ThreadUtils.postOnMainThread(() -> {
|
ThreadUtils.postOnMainThread(() -> {
|
||||||
@@ -123,12 +130,15 @@ public class TopLevelBatteryPreferenceController extends BasePreferenceControlle
|
|||||||
mBatteryStatusLabel = null; // will generateLabel()
|
mBatteryStatusLabel = null; // will generateLabel()
|
||||||
}
|
}
|
||||||
mPreference.setSummary(
|
mPreference.setSummary(
|
||||||
(mBatteryStatusLabel == null) ? generateLabel(info) : mBatteryStatusLabel);
|
mBatteryStatusLabel == null ? generateLabel(info) : mBatteryStatusLabel);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private CharSequence generateLabel(BatteryInfo info) {
|
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) {
|
if (!info.discharging && info.chargeLabel != null) {
|
||||||
return info.chargeLabel;
|
return info.chargeLabel;
|
||||||
} else if (info.remainingLabel == null) {
|
} else if (info.remainingLabel == null) {
|
||||||
@@ -146,15 +156,15 @@ public class TopLevelBatteryPreferenceController extends BasePreferenceControlle
|
|||||||
@Override
|
@Override
|
||||||
public void updateBatteryStatus(String label, BatteryInfo info) {
|
public void updateBatteryStatus(String label, BatteryInfo info) {
|
||||||
mBatteryStatusLabel = label; // Null if adaptive charging is not active
|
mBatteryStatusLabel = label; // Null if adaptive charging is not active
|
||||||
|
if (mPreference == null) {
|
||||||
if (mPreference != null) {
|
return;
|
||||||
// Do not triggerBatteryStatusUpdate(), otherwise there will be an infinite loop
|
}
|
||||||
|
// Do not triggerBatteryStatusUpdate() here to cause infinite loop
|
||||||
final CharSequence summary = getSummary(false /* batteryStatusUpdate */);
|
final CharSequence summary = getSummary(false /* batteryStatusUpdate */);
|
||||||
if (summary != null) {
|
if (summary != null) {
|
||||||
mPreference.setSummary(summary);
|
mPreference.setSummary(summary);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
protected static ComponentName convertClassPathToComponentName(String classPath) {
|
protected static ComponentName convertClassPathToComponentName(String classPath) {
|
||||||
|
|||||||
@@ -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.InstallUnknownAppsListProvider
|
||||||
import com.android.settings.spa.app.specialaccess.MediaManagementAppsAppListProvider
|
import com.android.settings.spa.app.specialaccess.MediaManagementAppsAppListProvider
|
||||||
import com.android.settings.spa.app.specialaccess.ModifySystemSettingsAppListProvider
|
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.PictureInPictureListProvider
|
||||||
import com.android.settings.spa.app.specialaccess.SpecialAppAccessPageProvider
|
import com.android.settings.spa.app.specialaccess.SpecialAppAccessPageProvider
|
||||||
import com.android.settings.spa.app.specialaccess.WifiControlAppListProvider
|
import com.android.settings.spa.app.specialaccess.WifiControlAppListProvider
|
||||||
@@ -61,6 +62,7 @@ open class SettingsSpaEnvironment(context: Context) : SpaEnvironment(context) {
|
|||||||
InstallUnknownAppsListProvider,
|
InstallUnknownAppsListProvider,
|
||||||
AlarmsAndRemindersAppListProvider,
|
AlarmsAndRemindersAppListProvider,
|
||||||
WifiControlAppListProvider,
|
WifiControlAppListProvider,
|
||||||
|
NfcTagAppsSettingsProvider,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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())
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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)
|
||||||
|
}
|
||||||
@@ -25,16 +25,19 @@ import androidx.test.core.app.ApplicationProvider;
|
|||||||
|
|
||||||
import com.android.settings.R;
|
import com.android.settings.R;
|
||||||
import com.android.settings.testutils.XmlTestUtils;
|
import com.android.settings.testutils.XmlTestUtils;
|
||||||
|
import com.android.settings.testutils.shadow.ShadowKeyCharacterMap;
|
||||||
|
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
import org.robolectric.RobolectricTestRunner;
|
import org.robolectric.RobolectricTestRunner;
|
||||||
|
import org.robolectric.annotation.Config;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/** Tests for {@link SystemControlsFragment}. */
|
/** Tests for {@link SystemControlsFragment}. */
|
||||||
@RunWith(RobolectricTestRunner.class)
|
@RunWith(RobolectricTestRunner.class)
|
||||||
|
@Config(shadows = {ShadowKeyCharacterMap.class})
|
||||||
public class SystemControlsFragmentTest {
|
public class SystemControlsFragmentTest {
|
||||||
|
|
||||||
private final Context mContext = ApplicationProvider.getApplicationContext();
|
private final Context mContext = ApplicationProvider.getApplicationContext();
|
||||||
|
|||||||
@@ -23,12 +23,15 @@ import static com.android.settings.core.BasePreferenceController.AVAILABLE;
|
|||||||
import static com.google.common.truth.Truth.assertThat;
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
|
||||||
import static org.mockito.Mockito.spy;
|
import static org.mockito.Mockito.spy;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.content.res.Resources;
|
||||||
import android.provider.DeviceConfig;
|
import android.provider.DeviceConfig;
|
||||||
|
|
||||||
import androidx.test.core.app.ApplicationProvider;
|
import androidx.test.core.app.ApplicationProvider;
|
||||||
|
|
||||||
|
import com.android.settings.R;
|
||||||
import com.android.settings.Utils;
|
import com.android.settings.Utils;
|
||||||
import com.android.settings.testutils.shadow.ShadowDeviceConfig;
|
import com.android.settings.testutils.shadow.ShadowDeviceConfig;
|
||||||
|
|
||||||
@@ -45,10 +48,15 @@ public class ClonedAppsPreferenceControllerTest {
|
|||||||
private ClonedAppsPreferenceController mController;
|
private ClonedAppsPreferenceController mController;
|
||||||
private static final String KEY = "key";
|
private static final String KEY = "key";
|
||||||
private Context mContext;
|
private Context mContext;
|
||||||
|
private Resources mResources;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setUp() {
|
public void setUp() {
|
||||||
mContext = spy(ApplicationProvider.getApplicationContext());
|
mContext = spy(ApplicationProvider.getApplicationContext());
|
||||||
|
|
||||||
|
mResources = spy(mContext.getResources());
|
||||||
|
when(mContext.getResources()).thenReturn(mResources);
|
||||||
|
|
||||||
mController = new ClonedAppsPreferenceController(mContext, KEY);
|
mController = new ClonedAppsPreferenceController(mContext, KEY);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -56,6 +64,7 @@ public class ClonedAppsPreferenceControllerTest {
|
|||||||
public void getAvailabilityStatus_featureNotEnabled_shouldNotReturnAvailable() {
|
public void getAvailabilityStatus_featureNotEnabled_shouldNotReturnAvailable() {
|
||||||
DeviceConfig.setProperty(NAMESPACE_APP_CLONING, Utils.PROPERTY_CLONED_APPS_ENABLED,
|
DeviceConfig.setProperty(NAMESPACE_APP_CLONING, Utils.PROPERTY_CLONED_APPS_ENABLED,
|
||||||
"false", true /* makeDefault */);
|
"false", true /* makeDefault */);
|
||||||
|
when(mResources.getBoolean(R.bool.config_cloned_apps_page_enabled)).thenReturn(false);
|
||||||
|
|
||||||
assertThat(mController.getAvailabilityStatus()).isNotEqualTo(AVAILABLE);
|
assertThat(mController.getAvailabilityStatus()).isNotEqualTo(AVAILABLE);
|
||||||
}
|
}
|
||||||
@@ -64,7 +73,26 @@ public class ClonedAppsPreferenceControllerTest {
|
|||||||
public void getAvailabilityStatus_featureEnabled_shouldReturnAvailable() {
|
public void getAvailabilityStatus_featureEnabled_shouldReturnAvailable() {
|
||||||
DeviceConfig.setProperty(NAMESPACE_APP_CLONING, Utils.PROPERTY_CLONED_APPS_ENABLED,
|
DeviceConfig.setProperty(NAMESPACE_APP_CLONING, Utils.PROPERTY_CLONED_APPS_ENABLED,
|
||||||
"true", true /* makeDefault */);
|
"true", true /* makeDefault */);
|
||||||
|
when(mResources.getBoolean(R.bool.config_cloned_apps_page_enabled)).thenReturn(true);
|
||||||
|
|
||||||
assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,6 +28,9 @@ import static org.mockito.Mockito.when;
|
|||||||
import android.content.ComponentName;
|
import android.content.ComponentName;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
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.preference.Preference;
|
||||||
import androidx.test.core.app.ApplicationProvider;
|
import androidx.test.core.app.ApplicationProvider;
|
||||||
@@ -38,21 +41,33 @@ import org.junit.Before;
|
|||||||
import org.junit.Ignore;
|
import org.junit.Ignore;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
|
import org.mockito.Mock;
|
||||||
import org.mockito.MockitoAnnotations;
|
import org.mockito.MockitoAnnotations;
|
||||||
import org.robolectric.RobolectricTestRunner;
|
import org.robolectric.RobolectricTestRunner;
|
||||||
import org.robolectric.annotation.Config;
|
import org.robolectric.annotation.Config;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
@RunWith(RobolectricTestRunner.class)
|
@RunWith(RobolectricTestRunner.class)
|
||||||
public class TopLevelBatteryPreferenceControllerTest {
|
public class TopLevelBatteryPreferenceControllerTest {
|
||||||
private Context mContext;
|
private Context mContext;
|
||||||
private TopLevelBatteryPreferenceController mController;
|
private TopLevelBatteryPreferenceController mController;
|
||||||
private BatterySettingsFeatureProvider mBatterySettingsFeatureProvider;
|
private BatterySettingsFeatureProvider mBatterySettingsFeatureProvider;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private UsbPort mUsbPort;
|
||||||
|
@Mock
|
||||||
|
private UsbManager mUsbManager;
|
||||||
|
@Mock
|
||||||
|
private UsbPortStatus mUsbPortStatus;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setUp() {
|
public void setUp() {
|
||||||
MockitoAnnotations.initMocks(this);
|
MockitoAnnotations.initMocks(this);
|
||||||
mContext = spy(ApplicationProvider.getApplicationContext());
|
mContext = spy(ApplicationProvider.getApplicationContext());
|
||||||
mController = new TopLevelBatteryPreferenceController(mContext, "test_key");
|
mController = new TopLevelBatteryPreferenceController(mContext, "test_key");
|
||||||
|
when(mContext.getSystemService(UsbManager.class)).thenReturn(mUsbManager);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -88,27 +103,61 @@ public class TopLevelBatteryPreferenceControllerTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void getDashboardLabel_returnsCorrectLabel() {
|
public void getDashboardLabel_returnsBatterPercentString() {
|
||||||
mController.mPreference = new Preference(mContext);
|
mController.mPreference = new Preference(mContext);
|
||||||
BatteryInfo info = new BatteryInfo();
|
BatteryInfo info = new BatteryInfo();
|
||||||
info.batteryPercentString = "3%";
|
info.batteryPercentString = "3%";
|
||||||
|
|
||||||
assertThat(mController.getDashboardLabel(mContext, info, true))
|
assertThat(mController.getDashboardLabel(mContext, info, true))
|
||||||
.isEqualTo(info.batteryPercentString);
|
.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";
|
info.remainingLabel = "Phone will shut down soon";
|
||||||
|
|
||||||
assertThat(mController.getDashboardLabel(mContext, info, true))
|
assertThat(mController.getDashboardLabel(mContext, info, true))
|
||||||
.isEqualTo("3% - Phone will shut down soon");
|
.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.discharging = false;
|
||||||
info.chargeLabel = "5% - charging";
|
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
|
@Test
|
||||||
public void getSummary_batteryNotPresent_shouldShowWarningMessage() {
|
public void getSummary_batteryNotPresent_shouldShowWarningMessage() {
|
||||||
mController.mIsBatteryPresent = false;
|
mController.mIsBatteryPresent = false;
|
||||||
|
|
||||||
assertThat(mController.getSummary())
|
assertThat(mController.getSummary())
|
||||||
.isEqualTo(mContext.getString(R.string.battery_missing_message));
|
.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});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -169,6 +169,7 @@ public class PrivateDnsPreferenceControllerTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void getAvailibilityStatus_availableByDefault() {
|
public void getAvailibilityStatus_availableByDefault() {
|
||||||
|
doReturn(true).when(mUserManager).isAdminUser();
|
||||||
assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
|
assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user