Snap for 11740859 from 1946b4a669 to 24Q3-release

Change-Id: I4368effe19f203c25fbfeb9f4b3989434e622a07
This commit is contained in:
Android Build Coastguard Worker
2024-04-20 01:21:26 +00:00
51 changed files with 656 additions and 455 deletions

View File

@@ -112,6 +112,7 @@ android_library {
"androidx.test.rules",
"telephony_flags_core_java_lib",
"setupdesign-lottie-loading-layout",
"device_policy_aconfig_flags_lib",
],
plugins: ["androidx.room_room-compiler-plugin"],

View File

@@ -971,39 +971,6 @@
android:value="@string/menu_key_apps"/>
</activity>
<activity-alias
android:name="BackupTasksActivity"
android:knownActivityEmbeddingCerts="@array/config_known_host_certs"
android:exported="true"
android:targetActivity=".spa.SpaBridgeActivity"
android:label="@string/run_backup_tasks_title">
<intent-filter android:priority="1">
<action android:name="android.settings.REQUEST_RUN_BACKUP_JOBS" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
<meta-data android:name="com.android.settings.spa.DESTINATION"
android:value="TogglePermissionAppList/BackupTasksApps"/>
<meta-data android:name="com.android.settings.HIGHLIGHT_MENU_KEY"
android:value="@string/menu_key_apps"/>
</activity-alias>
<activity-alias
android:name="AppBackupTasksActivity"
android:knownActivityEmbeddingCerts="@array/config_known_host_certs"
android:exported="true"
android:targetActivity=".spa.SpaAppBridgeActivity"
android:label="@string/run_backup_tasks_title">
<intent-filter android:priority="1">
<action android:name="android.settings.REQUEST_RUN_BACKUP_JOBS" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="package" />
</intent-filter>
<meta-data android:name="com.android.settings.spa.DESTINATION"
android:value="TogglePermissionAppInfoPage/BackupTasksApps"/>
<meta-data android:name="com.android.settings.HIGHLIGHT_MENU_KEY"
android:value="@string/menu_key_apps"/>
</activity-alias>
<activity
android:name="Settings$DateTimeSettingsActivity"
android:label="@string/date_and_time"

View File

@@ -13,4 +13,14 @@ flag {
namespace: "pixel_cross_device_control"
description: "Gates whether to enable checker for bluetooth profile toggle visibility"
bug: "321178209"
}
flag {
name: "hide_le_audio_toggle_for_le_audio_only_device"
namespace: "pixel_cross_device_control"
description: "Gates whether to hide LeAudio toggle for LeAudio-only device"
bug: "333827147"
metadata {
purpose: PURPOSE_BUGFIX
}
}

View File

@@ -35,3 +35,10 @@ flag {
description: "Feature flag to enable injection into PreferenceCategory."
bug: "333547416"
}
flag {
name: "slices_retirement"
namespace: "android_settings"
description: "Feature flag to remove relevant slices dependencies."
bug: "297367302"
}

View File

@@ -3469,7 +3469,7 @@
<!-- Title for the Delete private space settings controller [CHAR LIMIT=40] -->
<string name="reset_private_space_delete_title">Delete private space</string>
<!-- Delete private space dialog mentioning that private space and the apps in it along with its data will be permanently deleted [CHAR LIMIT=NONE] -->
<string name="reset_private_space_delete_dialog">If you have a private space on your device, it will be permanently deleted. All apps in your space and their data will be deleted.</string>
<string name="reset_private_space_delete_dialog">If you have a private space on your device, it will be permanently deleted and the Google Account added to your space will be removed.\n\nAll apps in your space and their data will also be permanently deleted.</string>
<!-- Main Clear -->
<!-- Button title to factory data reset the entire device [CHAR LIMIT=NONE] -->
@@ -6080,6 +6080,8 @@
<string name="battery_usage_chart_label_now">now</string>
<!-- [CHAR_LIMIT=NONE] A hyphen for two timestamps. For example, "6 AM - 8 AM", which means "from 6 AM to 8 AM". Please notice the spaces around the hyphen -->
<string name="battery_usage_timestamps_hyphen"><xliff:g id="from_timestamp">%1$s</xliff:g> - <xliff:g id="to_timestamp">%2$s</xliff:g></string>
<!-- [CHAR_LIMIT=NONE] Accessibility content description for two timestamps. For example, Battery usage for "6 AM to 8 PM" -->
<string name="battery_usage_timestamps_content_description"><xliff:g id="from_timestamp">%1$s</xliff:g> to <xliff:g id="to_timestamp">%2$s</xliff:g></string>
<!-- [CHAR_LIMIT=NONE] The first slot is a week day (e.g. "Monday"); the second slot is a hourly time span (e.g. "6 AM - 8 AM"). -->
<string name="battery_usage_day_and_hour"><xliff:g id="day">%1$s</xliff:g> <xliff:g id="hour">%2$s</xliff:g></string>
<!-- [CHAR_LIMIT=NONE] Accessibility content description for each slot in battery chart view. -->
@@ -6106,6 +6108,8 @@
<string name="battery_usage_spinner_view_by_systems">View by systems</string>
<!-- [CHAR_LIMIT=NONE] Less than some percentage, e.g. < 1% -->
<string name="battery_usage_less_than_percent">&lt; <xliff:g id="percentage">%1$s</xliff:g></string>
<!-- [CHAR_LIMIT=NONE] Accessibility content description for less than some percentage, e.g. less than 1% -->
<string name="battery_usage_less_than_percent_content_description">less than <xliff:g id="percentage">%1$s</xliff:g></string>
<!-- Process Stats strings -->
<skip />
<!-- Description of battery information footer text. [CHAR LIMIT=NONE] -->
@@ -10426,21 +10430,6 @@
<!-- Keywords for settings screen for controlling apps that can run long background tasks [CHAR LIMIT=NONE] -->
<string name="keywords_long_background_tasks">long jobs, data transfer, background tasks</string>
<!-- Title for the settings screen for controlling apps that hold the run backup jobs permission [CHAR LIMIT=60] -->
<string name="run_backup_tasks_title">Perform backup tasks in background</string>
<!-- Label for the switch to toggle the run backup jobs permission [CHAR LIMIT=100] -->
<string name="run_backup_tasks_switch_title">Allow app to run backup-related background tasks</string>
<!-- Description that appears below the run_backup_tasks switch [CHAR LIMIT=NONE] -->
<string name="run_backup_tasks_footer_title">
Indicates that this app has a major use-case where it needs to backup or sync content.
Granting this permission allows the app to run in the background for a slightly longer time
in order to complete the backup-related work.
\n\nIf this permission is denied, the system will not give any special exemption to this
app to complete backup-related work in the background.
</string>
<!-- Keywords for settings screen for controlling apps that hold the run backup tasks permission [CHAR LIMIT=NONE] -->
<string name="keywords_run_backup_tasks">backup tasks, backup jobs</string>
<!-- Reset rate-limiting in the system service ShortcutManager. "ShortcutManager" is the name of a system service and not translatable.
If the word "rate-limit" is hard to translate, use "Reset ShortcutManager API call limit" as the source text, which means
the same thing in this context.
@@ -13190,4 +13179,7 @@
<!--Text for acquire msg on UDFPS devices -->
<string name="fingerprint_acquired_imager_dirty_udfps">Clean your screen near the sensor and try again</string>
<!-- Provider Model: summary of converted in SIM category. [CHAR LIMIT=50] -->
<string name="sim_category_converted_sim">Converted to eSIM. Remove and discard.</string>
</resources>

View File

@@ -19,16 +19,6 @@
xmlns:settings="http://schemas.android.com/apk/res-auto"
android:title="@string/special_access">
<Preference
android:key="run_backup_tasks"
android:title="@string/run_backup_tasks_title"
android:order="-2000"
settings:isPreferenceVisible="false"
settings:searchable="false"
settings:keywords="@string/keywords_run_backup_tasks"
settings:controller="com.android.settings.spa.app.specialaccess.BackupTasksAppsPreferenceController">
</Preference>
<Preference
android:key="manage_external_storage"
android:title="@string/manage_external_storage_title"

View File

@@ -31,7 +31,6 @@ import com.android.settings.spa.SpaActivity.Companion.startSpaActivity
import com.android.settings.spa.SpaAppBridgeActivity.Companion.getDestinationForApp
import com.android.settings.spa.app.specialaccess.AlarmsAndRemindersAppListProvider
import com.android.settings.spa.app.specialaccess.AllFilesAccessAppListProvider
import com.android.settings.spa.app.specialaccess.BackupTasksAppsListProvider
import com.android.settings.spa.app.specialaccess.DisplayOverOtherAppsAppListProvider
import com.android.settings.spa.app.specialaccess.InstallUnknownAppsListProvider
import com.android.settings.spa.app.specialaccess.MediaManagementAppsAppListProvider
@@ -66,8 +65,6 @@ object SettingsActivityUtil {
WifiControlAppListProvider.getAppInfoRoutePrefix(),
NfcTagAppsSettingsProvider::class.qualifiedName to
NfcTagAppsSettingsProvider.getAppInfoRoutePrefix(),
BackupTasksAppsListProvider::class.qualifiedName to
BackupTasksAppsListProvider.getAppInfoRoutePrefix(),
)
@JvmStatic

View File

@@ -115,6 +115,7 @@ public class CredentialManagerPreferenceController extends BasePreferenceControl
private @Nullable Delegate mDelegate = null;
private @Nullable String mFlagOverrideForTest = null;
private @Nullable PreferenceScreen mPreferenceScreen = null;
private @Nullable PreferenceGroup mPreferenceGroup = null;
private Optional<Boolean> mSimulateHiddenForTests = Optional.empty();
private boolean mIsWorkProfile = false;
@@ -161,12 +162,6 @@ public class CredentialManagerPreferenceController extends BasePreferenceControl
return UNSUPPORTED_ON_DEVICE;
}
// If there is no top provider or any providers in the list then
// we should hide this pref.
if (isHiddenDueToNoProviderSet()) {
return CONDITIONALLY_UNAVAILABLE;
}
if (!hasNonPrimaryServices()) {
return CONDITIONALLY_UNAVAILABLE;
}
@@ -355,24 +350,11 @@ public class CredentialManagerPreferenceController extends BasePreferenceControl
}
// Get the list of new providers and components.
List<CredentialProviderInfo> newProviders =
setAvailableServices(
mCredentialManager.getCredentialProviderServices(
getUser(),
CredentialManager.PROVIDER_FILTER_USER_PROVIDERS_INCLUDING_HIDDEN);
Set<ComponentName> newComponents = buildComponentNameSet(newProviders, false);
Set<ComponentName> newPrimaryComponents = buildComponentNameSet(newProviders, true);
// Get the list of old components
Set<ComponentName> oldComponents = buildComponentNameSet(mServices, false);
Set<ComponentName> oldPrimaryComponents = buildComponentNameSet(mServices, true);
// If the sets are equal then don't update the UI.
if (oldComponents.equals(newComponents)
&& oldPrimaryComponents.equals(newPrimaryComponents)) {
return;
}
setAvailableServices(newProviders, null);
CredentialManager.PROVIDER_FILTER_USER_PROVIDERS_INCLUDING_HIDDEN),
null);
if (mPreferenceScreen != null) {
displayPreference(mPreferenceScreen);
@@ -396,11 +378,7 @@ public class CredentialManagerPreferenceController extends BasePreferenceControl
}
@VisibleForTesting
public boolean isHiddenDueToNoProviderSet() {
return isHiddenDueToNoProviderSet(getProviders());
}
private boolean isHiddenDueToNoProviderSet(
public boolean isHiddenDueToNoProviderSet(
Pair<List<CombinedProviderInfo>, CombinedProviderInfo> providerPair) {
if (mSimulateHiddenForTests.isPresent()) {
return mSimulateHiddenForTests.get();
@@ -444,17 +422,67 @@ public class CredentialManagerPreferenceController extends BasePreferenceControl
@Override
public void displayPreference(PreferenceScreen screen) {
super.displayPreference(screen);
final String prefKey = getPreferenceKey();
if (TextUtils.isEmpty(prefKey)) {
Log.w(TAG, "Skipping displayPreference because key is empty");
return;
}
// Since the UI is being cleared, clear any refs.
// Store this reference for later.
if (mPreferenceScreen == null) {
mPreferenceScreen = screen;
mPreferenceGroup = screen.findPreference(prefKey);
}
final Pair<List<CombinedProviderInfo>, CombinedProviderInfo> providerPair = getProviders();
maybeUpdateListOfPrefs(providerPair);
maybeUpdatePreferenceVisibility(providerPair);
}
private void maybeUpdateListOfPrefs(
Pair<List<CombinedProviderInfo>, CombinedProviderInfo> providerPair) {
if (mPreferenceScreen == null || mPreferenceGroup == null) {
return;
}
// Build the new list of prefs.
Map<String, CombiPreference> newPrefs =
buildPreferenceList(mPreferenceScreen.getContext(), providerPair);
// Determine if we need to update the prefs.
Set<String> existingPrefPackageNames = mPrefs.keySet();
if (existingPrefPackageNames.equals(newPrefs.keySet())) {
return;
}
// Since the UI is being cleared, clear any refs and prefs.
mPrefs.clear();
mPreferenceGroup.removeAll();
mPreferenceScreen = screen;
PreferenceGroup group = screen.findPreference(getPreferenceKey());
group.removeAll();
// Populate the preference list with new data.
mPrefs.putAll(newPrefs);
for (CombiPreference pref : newPrefs.values()) {
mPreferenceGroup.addPreference(pref);
}
}
Context context = screen.getContext();
mPrefs.putAll(buildPreferenceList(context, group));
private void maybeUpdatePreferenceVisibility(
Pair<List<CombinedProviderInfo>, CombinedProviderInfo> providerPair) {
if (mPreferenceScreen == null || mPreferenceGroup == null) {
return;
}
final boolean isAvailable =
(getAvailabilityStatus() == AVAILABLE) && !isHiddenDueToNoProviderSet(providerPair);
if (isAvailable) {
mPreferenceScreen.addPreference(mPreferenceGroup);
mPreferenceGroup.setVisible(true);
} else {
mPreferenceScreen.removePreference(mPreferenceGroup);
mPreferenceGroup.setVisible(false);
}
}
/**
@@ -511,9 +539,9 @@ public class CredentialManagerPreferenceController extends BasePreferenceControl
/** Aggregates the list of services and builds a list of UI prefs to show. */
@VisibleForTesting
public @NonNull Map<String, CombiPreference> buildPreferenceList(
@NonNull Context context, @NonNull PreferenceGroup group) {
// Get the providers and extract the values.
Pair<List<CombinedProviderInfo>, CombinedProviderInfo> providerPair = getProviders();
@NonNull Context context,
@NonNull Pair<List<CombinedProviderInfo>, CombinedProviderInfo> providerPair) {
// Extract the values.
CombinedProviderInfo topProvider = providerPair.second;
List<CombinedProviderInfo> providers = providerPair.first;
@@ -554,7 +582,6 @@ public class CredentialManagerPreferenceController extends BasePreferenceControl
combinedInfo.getSettingsActivity(),
combinedInfo.getDeviceAdminRestrictions(context, getUser()));
output.put(packageName, pref);
group.addPreference(pref);
}
// Set the visibility if we have services.
@@ -1023,11 +1050,12 @@ public class CredentialManagerPreferenceController extends BasePreferenceControl
public void onClick(View buttonView) {
// Forward the event.
if (mSwitch != null && mOnClickListener != null) {
if (!mOnClickListener.onCheckChanged(CombiPreference.this, mSwitch.isChecked())) {
// The update was not successful since there were too
// many enabled providers to manually reset any state.
mChecked = false;
mSwitch.setChecked(false);
if (!mOnClickListener.onCheckChanged(
CombiPreference.this, mSwitch.isChecked())) {
// The update was not successful since there were too
// many enabled providers to manually reset any state.
mChecked = false;
mSwitch.setChecked(false);
}
}
}
@@ -1083,8 +1111,10 @@ public class CredentialManagerPreferenceController extends BasePreferenceControl
if (mSwitch != null && !TextUtils.isEmpty(appName)) {
mSwitch.setContentDescription(
getContext().getString(
R.string.credman_on_off_switch_content_description, appName));
getContext()
.getString(
R.string.credman_on_off_switch_content_description,
appName));
}
}

View File

@@ -43,6 +43,7 @@ import com.android.settingslib.bluetooth.A2dpProfile;
import com.android.settingslib.bluetooth.BluetoothUtils;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.settingslib.bluetooth.HeadsetProfile;
import com.android.settingslib.bluetooth.HearingAidProfile;
import com.android.settingslib.bluetooth.LeAudioProfile;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.settingslib.bluetooth.LocalBluetoothProfile;
@@ -512,6 +513,19 @@ public class BluetoothDetailsProfilesController extends BluetoothDetailsControll
refresh();
}
private boolean isLeAudioOnlyDevice() {
if (mCachedDevice.getProfiles().stream()
.noneMatch(profile -> profile instanceof LeAudioProfile)) {
return false;
}
return mCachedDevice.getProfiles().stream()
.noneMatch(
profile ->
profile instanceof HearingAidProfile
|| profile instanceof A2dpProfile
|| profile instanceof HeadsetProfile);
}
private void updateLeAudioConfig() {
mIsLeContactSharingEnabled = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_SETTINGS_UI,
SettingsUIDeviceConfig.BT_LE_AUDIO_CONTACT_SHARING_ENABLED, true);
@@ -520,6 +534,13 @@ public class BluetoothDetailsProfilesController extends BluetoothDetailsControll
boolean isLeEnabledByDefault =
SystemProperties.getBoolean(LE_AUDIO_CONNECTION_BY_DEFAULT_PROPERTY, true);
mIsLeAudioToggleEnabled = isLeAudioToggleVisible || isLeEnabledByDefault;
if (Flags.hideLeAudioToggleForLeAudioOnlyDevice() && isLeAudioOnlyDevice()) {
mIsLeAudioToggleEnabled = false;
Log.d(
TAG,
"Hide LeAudio toggle for LeAudio-only Device: "
+ mCachedDevice.getDevice().getAnonymizedAddress());
}
Log.d(TAG, "BT_LE_AUDIO_CONTACT_SHARING_ENABLED:" + mIsLeContactSharingEnabled
+ ", LE_AUDIO_TOGGLE_VISIBLE_PROPERTY:" + isLeAudioToggleVisible
+ ", LE_AUDIO_CONNECTION_BY_DEFAULT_PROPERTY:" + isLeEnabledByDefault);

View File

@@ -39,6 +39,9 @@ public interface PowerUsageFeatureProvider {
/** Check whether the battery tips card is enabled in the battery usage page */
boolean isBatteryTipsEnabled();
/** Check whether to log the optimization mode of app entry in period job */
boolean isAppOptimizationModeLogged();
/**
* Returns a threshold (in milliseconds) for the minimal screen on time in battery usage list
*/

View File

@@ -83,6 +83,11 @@ public class PowerUsageFeatureProviderImpl implements PowerUsageFeatureProvider
return false;
}
@Override
public boolean isAppOptimizationModeLogged() {
return false;
}
@Override
public double getBatteryUsageListScreenOnTimeThresholdInMs() {
return 0;

View File

@@ -404,7 +404,7 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
}
}
String getSlotInformation() {
String getSlotInformation(boolean isAccessibilityText) {
if (mDailyViewModel == null || mHourlyViewModels == null) {
// No data
return null;
@@ -413,13 +413,20 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
return null;
}
final String selectedDayText = mDailyViewModel.getFullText(mDailyChartIndex);
final String selectedDayText =
isAccessibilityText
? mDailyViewModel.getContentDescription(mDailyChartIndex)
: mDailyViewModel.getFullText(mDailyChartIndex);
if (mHourlyChartIndex == BatteryChartViewModel.SELECTED_INDEX_ALL) {
return selectedDayText;
}
final String selectedHourText =
mHourlyViewModels.get(mDailyChartIndex).getFullText(mHourlyChartIndex);
isAccessibilityText
? mHourlyViewModels
.get(mDailyChartIndex)
.getContentDescription(mHourlyChartIndex)
: mHourlyViewModels.get(mDailyChartIndex).getFullText(mHourlyChartIndex);
if (isBatteryLevelDataInOneDay()) {
return selectedHourText;
}
@@ -444,7 +451,7 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
}
private String getAccessibilityAnnounceMessage() {
final String slotInformation = getSlotInformation();
final String slotInformation = getSlotInformation(/* isAccessibilityText= */ true);
final String slotInformationMessage =
slotInformation == null
? mPrefContext.getString(
@@ -600,6 +607,11 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
private abstract class BaseLabelTextGenerator
implements BatteryChartViewModel.LabelTextGenerator {
@Override
public String generateContentDescription(List<Long> timestamps, int index) {
return generateFullText(timestamps, index);
}
@Override
public String generateSlotBatteryLevelText(List<Integer> levels, int index) {
final int fromBatteryLevelIndex =
@@ -673,6 +685,16 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
generateText(timestamps, index + 1));
}
@Override
public String generateContentDescription(List<Long> timestamps, int index) {
return index == timestamps.size() - 1
? generateText(timestamps, index)
: mContext.getString(
R.string.battery_usage_timestamps_content_description,
generateText(timestamps, index),
generateText(timestamps, index + 1));
}
HourlyChartLabelTextGenerator updateSpecialCaseContext(
@NonNull final BatteryLevelData batteryLevelData) {
BatteryLevelData.PeriodBatteryLevelData firstDayLevelData =

View File

@@ -784,7 +784,7 @@ public class BatteryChartView extends AppCompatImageView implements View.OnClick
}
final AccessibilityNodeInfo childInfo =
new AccessibilityNodeInfo(BatteryChartView.this, index);
final String slotTimeInfo = mViewModel.getFullText(index);
final String slotTimeInfo = mViewModel.getContentDescription(index);
final String batteryLevelInfo = mViewModel.getSlotBatteryLevelText(index);
onInitializeAccessibilityNodeInfo(childInfo);
childInfo.setClickable(isValidToDraw(mViewModel, index));

View File

@@ -43,9 +43,12 @@ class BatteryChartViewModel {
/** Generates the label text. The text may be abbreviated to save space. */
String generateText(List<Long> timestamps, int index);
/** Generates the full text for accessibility. */
/** Generates the full text for slot information. */
String generateFullText(List<Long> timestamps, int index);
/** Generates the full text for accessibility. */
String generateContentDescription(List<Long> timestamps, int index);
/** Generates the battery level text of a slot for accessibility.*/
String generateSlotBatteryLevelText(List<Integer> levels, int index);
}
@@ -56,6 +59,7 @@ class BatteryChartViewModel {
private final LabelTextGenerator mLabelTextGenerator;
private final String[] mTexts;
private final String[] mFullTexts;
private final String[] mContentDescription;
private final String[] mBatteryLevelTexts;
private int mSelectedIndex = SELECTED_INDEX_ALL;
@@ -79,6 +83,7 @@ class BatteryChartViewModel {
mLabelTextGenerator = labelTextGenerator;
mTexts = new String[size()];
mFullTexts = new String[size()];
mContentDescription = new String[size()];
// Last one for SELECTED_INDEX_ALL
mBatteryLevelTexts = new String[size() + 1];
}
@@ -105,6 +110,14 @@ class BatteryChartViewModel {
return mFullTexts[index];
}
public String getContentDescription(int index) {
if (mContentDescription[index] == null) {
mContentDescription[index] =
mLabelTextGenerator.generateContentDescription(mTimestamps, index);
}
return mContentDescription[index];
}
public String getSlotBatteryLevelText(int index) {
final int textIndex = index != SELECTED_INDEX_ALL ? index : size();
if (mBatteryLevelTexts[textIndex] == null) {

View File

@@ -0,0 +1,54 @@
/*
* Copyright (C) 2024 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.fuelgauge.batteryusage;
import android.content.Context;
import android.util.ArrayMap;
import androidx.annotation.VisibleForTesting;
import com.android.settings.fuelgauge.BatteryOptimizeUtils;
import com.android.settingslib.fuelgauge.PowerAllowlistBackend;
import java.util.Map;
/** A cache to log battery optimization mode of an app */
final class BatteryOptimizationModeCache {
private static final String TAG = "BatteryOptimizationModeCache";
@VisibleForTesting final Map<Integer, BatteryOptimizationMode> mBatteryOptimizeModeCacheMap;
private final Context mContext;
BatteryOptimizationModeCache(final Context context) {
mContext = context;
mBatteryOptimizeModeCacheMap = new ArrayMap<>();
PowerAllowlistBackend.getInstance(mContext).refreshList();
}
BatteryOptimizationMode getBatteryOptimizeMode(final int uid, final String packageName) {
if (!mBatteryOptimizeModeCacheMap.containsKey(uid)) {
final BatteryOptimizeUtils batteryOptimizeUtils =
new BatteryOptimizeUtils(mContext, uid, packageName);
mBatteryOptimizeModeCacheMap.put(
uid,
BatteryOptimizationMode.forNumber(
batteryOptimizeUtils.getAppOptimizationMode(/* refreshList= */ false)));
}
return mBatteryOptimizeModeCacheMap.get(uid);
}
}

View File

@@ -88,6 +88,7 @@ public class BatteryUsageBreakdownController extends BasePreferenceController
@VisibleForTesting FooterPreference mFooterPreference;
@VisibleForTesting BatteryDiffData mBatteryDiffData;
@VisibleForTesting String mPercentLessThanThresholdText;
@VisibleForTesting String mPercentLessThanThresholdContentDescription;
@VisibleForTesting boolean mIsHighlightSlot;
@VisibleForTesting int mAnomalyKeyNumber;
@VisibleForTesting String mAnomalyEntryKey;
@@ -202,10 +203,14 @@ public class BatteryUsageBreakdownController extends BasePreferenceController
mSpinnerPreference = screen.findPreference(SPINNER_PREFERENCE_KEY);
mAppListPreferenceGroup = screen.findPreference(APP_LIST_PREFERENCE_KEY);
mFooterPreference = screen.findPreference(FOOTER_PREFERENCE_KEY);
final String formatPercentage =
Utils.formatPercentage(BatteryDiffData.SMALL_PERCENTAGE_THRESHOLD, false);
mPercentLessThanThresholdText =
mPrefContext.getString(R.string.battery_usage_less_than_percent, formatPercentage);
mPercentLessThanThresholdContentDescription =
mPrefContext.getString(
R.string.battery_usage_less_than_percent,
Utils.formatPercentage(BatteryDiffData.SMALL_PERCENTAGE_THRESHOLD, false));
R.string.battery_usage_less_than_percent_content_description,
formatPercentage);
mAppListPreferenceGroup.setOrderingAsAdded(false);
mSpinnerPreference.initializeSpinner(
@@ -394,12 +399,15 @@ public class BatteryUsageBreakdownController extends BasePreferenceController
@VisibleForTesting
void setPreferencePercentage(PowerGaugePreference preference, BatteryDiffEntry entry) {
preference.setPercentage(
entry.getPercentage() < BatteryDiffData.SMALL_PERCENTAGE_THRESHOLD
? mPercentLessThanThresholdText
: Utils.formatPercentage(
entry.getPercentage() + entry.getAdjustPercentageOffset(),
/* round= */ true));
if (entry.getPercentage() < BatteryDiffData.SMALL_PERCENTAGE_THRESHOLD) {
preference.setPercentage(mPercentLessThanThresholdText);
preference.setPercentageContentDescription(mPercentLessThanThresholdContentDescription);
} else {
preference.setPercentage(
Utils.formatPercentage(
entry.getPercentage() + entry.getAdjustPercentageOffset(),
/* round= */ true));
}
}
@VisibleForTesting

View File

@@ -27,6 +27,7 @@ import android.util.Log;
import androidx.annotation.VisibleForTesting;
import com.android.settings.fuelgauge.BatteryUsageHistoricalLogEntry.Action;
import com.android.settings.fuelgauge.PowerUsageFeatureProvider;
import com.android.settings.fuelgauge.batteryusage.bugreport.BatteryUsageLogUtils;
import com.android.settings.overlay.FeatureFactory;
@@ -124,9 +125,15 @@ public final class BatteryUsageDataLoader {
userIdsSeries,
/* isFromPeriodJob= */ true,
batteryDiffDataMap -> {
final PowerUsageFeatureProvider featureProvider =
FeatureFactory.getFeatureFactory()
.getPowerUsageFeatureProvider();
DatabaseUtils.sendBatteryUsageSlotData(
context,
ConvertUtils.convertToBatteryUsageSlotList(batteryDiffDataMap));
ConvertUtils.convertToBatteryUsageSlotList(
context,
batteryDiffDataMap,
featureProvider.isAppOptimizationModeLogged()));
if (batteryDiffDataMap.values().stream()
.anyMatch(
data ->
@@ -135,12 +142,10 @@ public final class BatteryUsageDataLoader {
.isEmpty()
|| !data.getAppDiffEntryList()
.isEmpty()))) {
FeatureFactory.getFeatureFactory()
.getPowerUsageFeatureProvider()
.detectPowerAnomaly(
context,
/* displayDrain= */ 0,
DetectRequestSourceType.TYPE_DATA_LOADER);
featureProvider.detectPowerAnomaly(
context,
/* displayDrain= */ 0,
DetectRequestSourceType.TYPE_DATA_LOADER);
}
});
if (batteryLevelData == null) {

View File

@@ -345,10 +345,15 @@ public final class ConvertUtils {
/** Converts from {@link Map<Long, BatteryDiffData>} to {@link List<BatteryUsageSlot>} */
public static List<BatteryUsageSlot> convertToBatteryUsageSlotList(
final Map<Long, BatteryDiffData> batteryDiffDataMap) {
final Context context,
final Map<Long, BatteryDiffData> batteryDiffDataMap,
final boolean isAppOptimizationModeLogged) {
List<BatteryUsageSlot> batteryUsageSlotList = new ArrayList<>();
final BatteryOptimizationModeCache optimizationModeCache =
isAppOptimizationModeLogged ? new BatteryOptimizationModeCache(context) : null;
for (BatteryDiffData batteryDiffData : batteryDiffDataMap.values()) {
batteryUsageSlotList.add(convertToBatteryUsageSlot(batteryDiffData));
batteryUsageSlotList.add(
convertToBatteryUsageSlot(batteryDiffData, optimizationModeCache));
}
return batteryUsageSlotList;
}
@@ -479,9 +484,10 @@ public final class ConvertUtils {
}
}
@VisibleForTesting
static BatteryUsageDiff convertToBatteryUsageDiff(BatteryDiffEntry batteryDiffEntry) {
static BatteryUsageDiff convertToBatteryUsageDiff(
final BatteryDiffEntry batteryDiffEntry,
final @Nullable BatteryOptimizationModeCache optimizationModeCache) {
BatteryUsageDiff.Builder builder =
BatteryUsageDiff.newBuilder()
.setUid(batteryDiffEntry.mUid)
@@ -511,11 +517,18 @@ public final class ConvertUtils {
if (batteryDiffEntry.mLegacyLabel != null) {
builder.setLabel(batteryDiffEntry.mLegacyLabel);
}
// Log the battery optimization mode of AppEntry while converting to batteryUsageSlot.
if (optimizationModeCache != null && !batteryDiffEntry.isSystemEntry()) {
builder.setAppOptimizationMode(
optimizationModeCache.getBatteryOptimizeMode(
(int) batteryDiffEntry.mUid, batteryDiffEntry.getPackageName()));
}
return builder.build();
}
private static BatteryUsageSlot convertToBatteryUsageSlot(
final BatteryDiffData batteryDiffData) {
final BatteryDiffData batteryDiffData,
final @Nullable BatteryOptimizationModeCache optimizationModeCache) {
if (batteryDiffData == null) {
return BatteryUsageSlot.getDefaultInstance();
}
@@ -527,10 +540,11 @@ public final class ConvertUtils {
.setEndBatteryLevel(batteryDiffData.getEndBatteryLevel())
.setScreenOnTime(batteryDiffData.getScreenOnTime());
for (BatteryDiffEntry batteryDiffEntry : batteryDiffData.getAppDiffEntryList()) {
builder.addAppUsage(convertToBatteryUsageDiff(batteryDiffEntry));
builder.addAppUsage(convertToBatteryUsageDiff(batteryDiffEntry, optimizationModeCache));
}
for (BatteryDiffEntry batteryDiffEntry : batteryDiffData.getSystemDiffEntryList()) {
builder.addSystemUsage(convertToBatteryUsageDiff(batteryDiffEntry));
builder.addSystemUsage(
convertToBatteryUsageDiff(batteryDiffEntry, /* optimizationModeCache= */ null));
}
return builder.build();
}

View File

@@ -18,6 +18,7 @@ package com.android.settings.fuelgauge.batteryusage;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
@@ -47,6 +48,7 @@ public class PowerGaugePreference extends AppPreference {
private BatteryDiffEntry mBatteryDiffEntry;
private CharSequence mContentDescription;
private CharSequence mProgress;
private CharSequence mProgressContentDescription;
private boolean mShowAnomalyIcon;
public PowerGaugePreference(
@@ -87,6 +89,13 @@ public class PowerGaugePreference extends AppPreference {
/** Sets the percentage to show. */
public void setPercentage(CharSequence percentage) {
mProgress = percentage;
mProgressContentDescription = percentage;
notifyChanged();
}
/** Sets the content description of the percentage. */
public void setPercentageContentDescription(CharSequence contentDescription) {
mProgressContentDescription = contentDescription;
notifyChanged();
}
@@ -133,6 +142,9 @@ public class PowerGaugePreference extends AppPreference {
final TextView subtitle = (TextView) view.findViewById(R.id.widget_summary);
subtitle.setText(mProgress);
if (!TextUtils.isEmpty(mProgressContentDescription)) {
subtitle.setContentDescription(mProgressContentDescription);
}
if (mShowAnomalyIcon) {
subtitle.setCompoundDrawablesRelativeWithIntrinsicBounds(
R.drawable.ic_warning_24dp, 0, 0, 0);

View File

@@ -239,7 +239,9 @@ public class PowerUsageAdvanced extends PowerUsageBase {
}
final int dailyIndex = mBatteryChartPreferenceController.getDailyChartIndex();
final int hourlyIndex = mBatteryChartPreferenceController.getHourlyChartIndex();
final String slotInformation = mBatteryChartPreferenceController.getSlotInformation();
final String slotInformation =
mBatteryChartPreferenceController.getSlotInformation(
/* isAccessibilityText= */ false);
final BatteryDiffData slotUsageData = mBatteryUsageMap.get(dailyIndex).get(hourlyIndex);
mScreenOnTimeController.handleSceenOnTimeUpdated(
slotUsageData != null ? slotUsageData.getScreenOnTime() : 0L, slotInformation);

View File

@@ -14,6 +14,13 @@ message BatteryUsageSlot {
repeated BatteryUsageDiff system_usage = 7;
}
enum BatteryOptimizationMode {
MODE_UNKNOWN = 0;
MODE_RESTRICTED = 1;
MODE_UNRESTRICTED = 2;
MODE_OPTIMIZED = 3;
}
message BatteryUsageDiff {
optional int64 uid = 1;
optional int64 user_id = 2;
@@ -32,4 +39,5 @@ message BatteryUsageDiff {
optional int64 background_usage_time = 15;
optional int64 screen_on_time = 16;
optional int64 foreground_service_usage_time = 17;
optional BatteryOptimizationMode app_optimization_mode = 18;
}

View File

@@ -44,6 +44,7 @@ public class NetworkProviderSimListController extends BasePreferenceController i
DefaultLifecycleObserver, MobileNetworkRepository.MobileNetworkCallback,
DefaultSubscriptionReceiver.DefaultSubscriptionListener {
private static final String TAG = "NetworkProviderSimListController";
private final SubscriptionManager mSubscriptionManager;
@Nullable
private PreferenceCategory mPreferenceCategory;
@@ -104,16 +105,21 @@ public class NetworkProviderSimListController extends BasePreferenceController i
final Drawable drawable = mContext.getDrawable(
info.isEmbedded ? R.drawable.ic_sim_card_download : R.drawable.ic_sim_card);
pref.setIcon(drawable);
pref.setOnPreferenceClickListener(clickedPref -> {
if (!info.isEmbedded && !isActiveSubscriptionId
&& !SubscriptionUtil.showToggleForPhysicalSim(mSubscriptionManager)) {
SubscriptionUtil.startToggleSubscriptionDialogActivity(mContext, subId,
true);
} else {
MobileNetworkUtils.launchMobileNetworkSettings(mContext, info);
}
return true;
});
if (SubscriptionUtil.isConvertedPsimSubscription(mContext, subId)) {
// If the subscription has been converted, disable the profile menu.
pref.setEnabled(false);
} else {
pref.setOnPreferenceClickListener(clickedPref -> {
if (!info.isEmbedded && !isActiveSubscriptionId
&& !SubscriptionUtil.showToggleForPhysicalSim(mSubscriptionManager)) {
SubscriptionUtil.startToggleSubscriptionDialogActivity(mContext, subId,
true);
} else {
MobileNetworkUtils.launchMobileNetworkSettings(mContext, info);
}
return true;
});
}
mPreferences.put(subId, pref);
}
for (RestrictedPreference pref : existingPreferences.values()) {
@@ -122,6 +128,10 @@ public class NetworkProviderSimListController extends BasePreferenceController i
}
public CharSequence getSummary(SubscriptionInfoEntity subInfo, CharSequence displayName) {
if (!subInfo.isEmbedded
&& SubscriptionUtil.isConvertedPsimSubscription(mContext, subInfo.getSubId())) {
return mContext.getString(R.string.sim_category_converted_sim);
}
if (subInfo.isActiveSubscriptionId) {
CharSequence config = SubscriptionUtil.getDefaultSimConfig(mContext,
subInfo.getSubId());

View File

@@ -549,6 +549,7 @@ class SimOnboardingActivity : SpaBaseDialogActivity() {
val intent = Intent(context, SimOnboardingActivity::class.java).apply {
putExtra(SUB_ID, subId)
}
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
context.startActivity(intent)
}

View File

@@ -18,6 +18,7 @@ package com.android.settings.network;
import static android.telephony.SubscriptionManager.INVALID_SIM_SLOT_INDEX;
import static android.telephony.SubscriptionManager.PROFILE_CLASS_PROVISIONING;
import static android.telephony.SubscriptionManager.TRANSFER_STATUS_CONVERTED;
import static android.telephony.UiccSlotInfo.CARD_STATE_INFO_PRESENT;
import static com.android.internal.util.CollectionUtils.emptyIfNull;
@@ -905,4 +906,27 @@ public class SubscriptionUtil {
context.getSystemService(ConnectivityManager.class);
return connectivityManager.getNetworkCapabilities(connectivityManager.getActiveNetwork());
}
/**
* Checks if the subscription with the given subId is converted pSIM.
*
* @param context {@code Context}
* @param subId The subscription ID.
*/
static boolean isConvertedPsimSubscription(@NonNull Context context, int subId) {
SubscriptionManager subscriptionManager = context.getSystemService(
SubscriptionManager.class);
List<SubscriptionInfo> allSubInofs = subscriptionManager.getAllSubscriptionInfoList();
for (SubscriptionInfo subInfo : allSubInofs) {
if (subInfo != null) {
if (com.android.internal.telephony.flags.Flags.supportPsimToEsimConversion()
&& subInfo.getSubscriptionId() == subId
&& !subInfo.isEmbedded()
&& subInfo.getTransferStatus() == TRANSFER_STATUS_CONVERTED) {
return true;
}
}
}
return false;
}
}

View File

@@ -34,6 +34,7 @@ import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import android.telephony.euicc.EuiccManager;
import android.text.TextUtils;
import android.util.Log;
import androidx.annotation.VisibleForTesting;
import androidx.lifecycle.LifecycleObserver;
@@ -42,22 +43,20 @@ import androidx.lifecycle.OnLifecycleEvent;
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
import com.android.internal.telephony.flags.Flags;
import com.android.internal.telephony.util.TelephonyUtils;
import com.android.settings.R;
import com.android.settings.network.MobileNetworkRepository;
import com.android.settings.network.SubscriptionUtil;
import com.android.settingslib.core.lifecycle.Lifecycle;
import com.android.settingslib.mobile.dataservice.SubscriptionInfoEntity;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class ConvertToEsimPreferenceController extends TelephonyBasePreferenceController implements
LifecycleObserver, MobileNetworkRepository.MobileNetworkCallback {
private static final String TAG = "ConvertToEsimPreference";
private Preference mPreference;
private LifecycleOwner mLifecycleOwner;
private MobileNetworkRepository mMobileNetworkRepository;
@@ -113,14 +112,27 @@ public class ConvertToEsimPreferenceController extends TelephonyBasePreferenceCo
* To avoid showing users dialogs that can cause confusion,
* add conditions to allow conversion in the absence of active eSIM.
*/
if (!mContext.getResources().getBoolean(R.bool.config_psim_conversion_menu_enabled)
|| !isPsimConversionSupport(subId)) {
if (!Flags.supportPsimToEsimConversion()) {
Log.d(TAG, "supportPsimToEsimConversion flag is not enabled");
return CONDITIONALLY_UNAVAILABLE;
}
SubscriptionManager subscriptionManager = mContext.getSystemService(
SubscriptionManager.class);
SubscriptionInfo subInfo = subscriptionManager.getActiveSubscriptionInfo(subId);
if (subInfo == null) {
return CONDITIONALLY_UNAVAILABLE;
}
EuiccManager euiccManager = (EuiccManager)
mContext.getSystemService(Context.EUICC_SERVICE);
if (!euiccManager.isPsimConversionSupported(subInfo.getCarrierId())) {
Log.i(TAG, "subId is not matched with pSIM conversion"
+ " supported carriers:" + subInfo.getCarrierId());
return CONDITIONALLY_UNAVAILABLE;
}
if (findConversionSupportComponent()) {
return mSubscriptionInfoEntity != null && mSubscriptionInfoEntity.isActiveSubscriptionId
&& !mSubscriptionInfoEntity.isEmbedded && isActiveSubscription(subId)
&& !hasActiveEsimProfiles()
? AVAILABLE
: CONDITIONALLY_UNAVAILABLE;
}
@@ -174,24 +186,6 @@ public class ConvertToEsimPreferenceController extends TelephonyBasePreferenceCo
return true;
}
private boolean hasActiveEsimProfiles() {
SubscriptionManager subscriptionManager = mContext.getSystemService(
SubscriptionManager.class);
List<SubscriptionInfo> subscriptionInfoList =
SubscriptionUtil.getActiveSubscriptions(subscriptionManager);
if (subscriptionInfoList == null || subscriptionInfoList.isEmpty()) {
return false;
}
int activatedEsimCount = (int) subscriptionInfoList
.stream()
.filter(SubscriptionInfo::isEmbedded)
.count();
if (activatedEsimCount > 0) {
return true;
}
return false;
}
private boolean findConversionSupportComponent() {
Intent intent = new Intent(EuiccService.ACTION_CONVERT_TO_EMBEDDED_SUBSCRIPTION);
PackageManager packageManager = mContext.getPackageManager();
@@ -242,16 +236,4 @@ public class ConvertToEsimPreferenceController extends TelephonyBasePreferenceCo
}
return true;
}
private boolean isPsimConversionSupport(int subId) {
SubscriptionManager subscriptionManager = mContext.getSystemService(
SubscriptionManager.class);
SubscriptionInfo subInfo = subscriptionManager.getActiveSubscriptionInfo(subId);
if (subInfo == null) {
return false;
}
final int[] supportedCarriers = mContext.getResources().getIntArray(
R.array.config_psim_conversion_menu_enabled_carrier);
return Arrays.stream(supportedCarriers).anyMatch(id -> id == subInfo.getCarrierId());
}
}

View File

@@ -167,6 +167,7 @@ public class NetworkSelectSettings extends DashboardFragment {
@Keep
@VisibleForTesting
@Nullable
protected SatelliteManager getSatelliteManager(Context context) {
return context.getSystemService(SatelliteManager.class);
}
@@ -359,7 +360,13 @@ public class NetworkSelectSettings extends DashboardFragment {
if (!Flags.carrierEnabledSatelliteFlag()) {
return new ArrayList<>();
}
return mSatelliteManager.getSatellitePlmnsForCarrier(mSubId);
if (mSatelliteManager != null) {
return mSatelliteManager.getSatellitePlmnsForCarrier(mSubId);
} else {
Log.e(TAG, "mSatelliteManager is null, return empty list");
return new ArrayList<>();
}
}
private void handleCarrierConfigChanged(int subId) {

View File

@@ -76,6 +76,7 @@ public class ToggleSubscriptionDialogActivity extends SubscriptionActionDialogAc
Intent intent = new Intent(context, ToggleSubscriptionDialogActivity.class);
intent.putExtra(ARG_SUB_ID, subId);
intent.putExtra(ARG_enable, enable);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
return intent;
}

View File

@@ -141,7 +141,7 @@ class ZenModesBackend {
void deactivateMode(ZenMode mode) {
if (mode.isManualDnd()) {
// TODO: b/326061620 - This shouldn't snooze any rules that are active.
// When calling with fromUser=true this will not snooze other modes.
mNotificationManager.setZenMode(Settings.Global.ZEN_MODE_OFF, null, TAG,
/* fromUser= */ true);
} else {

View File

@@ -27,6 +27,7 @@ import android.app.KeyguardManager;
import android.app.RemoteLockscreenValidationSession;
import android.app.admin.DevicePolicyManager;
import android.app.admin.ManagedSubscriptionsPolicy;
import android.app.admin.flags.Flags;
import android.content.ComponentName;
import android.content.Context;
import android.content.DialogInterface;
@@ -374,7 +375,14 @@ public abstract class ConfirmDeviceCredentialBaseFragment extends InstrumentedFr
private int getUserTypeForWipe() {
final UserInfo userToBeWiped = mUserManager.getUserInfo(
mDevicePolicyManager.getProfileWithMinimumFailedPasswordsForWipe(mEffectiveUserId));
if (userToBeWiped == null || userToBeWiped.isPrimary()) {
UserHandle primaryUser = UserHandle.SYSTEM;
if (Flags.headlessSingleUserFixes()) {
UserHandle mainUser = mUserManager.getMainUser();
if (mainUser != null ) {
primaryUser = mainUser;
}
}
if (userToBeWiped == null || userToBeWiped.getUserHandle().equals(primaryUser)) {
return USER_TYPE_PRIMARY;
} else if (userToBeWiped.isManagedProfile()) {
return USER_TYPE_MANAGED_PROFILE;

View File

@@ -293,7 +293,6 @@ public class PrivateSpaceMaintainer {
*/
public synchronized void unlockPrivateSpace(IntentSender intentSender) {
if (mUserHandle != null) {
Log.d(TAG, "Calling requestQuietModeEnabled to disableQuietMode");
mUserManager.requestQuietModeEnabled(false, mUserHandle, intentSender);
}
}
@@ -374,7 +373,6 @@ public class PrivateSpaceMaintainer {
*/
private final class ProfileAvailabilityBroadcastReceiver extends BroadcastReceiver {
void register() {
Log.d(TAG, "Registering the receiver");
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_PROFILE_UNAVAILABLE);
mContext.registerReceiver(/* receiver= */ this, filter, Context.RECEIVER_NOT_EXPORTED);

View File

@@ -125,7 +125,7 @@ public class ResetOptionsDeletePrivateSpaceController extends BasePreferenceCont
public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
Context context = getContext();
return new AlertDialog.Builder(context)
.setTitle(R.string.reset_private_space_delete_title)
.setTitle(R.string.private_space_delete_header)
.setMessage(R.string.reset_private_space_delete_dialog)
.setPositiveButton(
R.string.private_space_delete_button_label,

View File

@@ -240,9 +240,9 @@ public class ScreenPinningSettings extends SettingsPreferenceFragment
mUseScreenLock.setChecked(isScreenLockUsed());
mUseScreenLock.setTitle(getCurrentSecurityTitle(mLockPatternUtils));
} else {
mFooterPreference.setSummary(getAppPinningContent());
mUseScreenLock.setEnabled(false);
}
mFooterPreference.setSummary(getAppPinningContent());
}
private boolean isGuestModeSupported() {

View File

@@ -33,7 +33,6 @@ import static android.telephony.TelephonyManager.EXTRA_SUBSCRIPTION_ID;
import static android.telephony.data.ApnSetting.TYPE_MMS;
import static com.android.settings.Utils.SETTINGS_PACKAGE_NAME;
import static com.android.settings.sim.SimDialogActivity.PICK_DISMISS;
import android.app.Notification;
import android.app.NotificationChannel;

View File

@@ -371,9 +371,7 @@ public class SimSlotChangeHandler {
return;
}
Log.d(TAG, "Start ToggleSubscriptionDialogActivity with " + subId + " under DSDS+Mep.");
Intent intent = ToggleSubscriptionDialogActivity.getIntent(mContext, subId, true);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mContext.startActivityAsUser(intent, UserHandle.SYSTEM);
SubscriptionUtil.startToggleSubscriptionDialogActivity(mContext, subId, true);
}
private boolean isMultipleEnabledProfilesSupported() {

View File

@@ -29,7 +29,6 @@ import com.android.settings.spa.app.backgroundinstall.BackgroundInstalledAppsPag
import com.android.settings.spa.app.battery.BatteryOptimizationModeAppListPageProvider
import com.android.settings.spa.app.specialaccess.AlarmsAndRemindersAppListProvider
import com.android.settings.spa.app.specialaccess.AllFilesAccessAppListProvider
import com.android.settings.spa.app.specialaccess.BackupTasksAppsListProvider
import com.android.settings.spa.app.specialaccess.DisplayOverOtherAppsAppListProvider
import com.android.settings.spa.app.specialaccess.InstallUnknownAppsListProvider
import com.android.settings.spa.app.specialaccess.LongBackgroundTasksAppListProvider
@@ -79,7 +78,6 @@ open class SettingsSpaEnvironment(context: Context) : SpaEnvironment(context) {
NfcTagAppsSettingsProvider,
LongBackgroundTasksAppListProvider,
TurnScreenOnAppsAppListProvider,
BackupTasksAppsListProvider,
)
}

View File

@@ -38,7 +38,6 @@ import com.android.settings.flags.Flags
import com.android.settings.spa.SpaActivity.Companion.startSpaActivity
import com.android.settings.spa.app.appcompat.UserAspectRatioAppPreference
import com.android.settings.spa.app.specialaccess.AlarmsAndRemindersAppListProvider
import com.android.settings.spa.app.specialaccess.BackupTasksAppsListProvider
import com.android.settings.spa.app.specialaccess.DisplayOverOtherAppsAppListProvider
import com.android.settings.spa.app.specialaccess.InstallUnknownAppsListProvider
import com.android.settings.spa.app.specialaccess.ModifySystemSettingsAppListProvider
@@ -166,9 +165,6 @@ private fun AppInfoSettings(packageInfoPresenter: PackageInfoPresenter) {
InstallUnknownAppsListProvider.InfoPageEntryItem(app)
InteractAcrossProfilesDetailsPreference(app)
AlarmsAndRemindersAppListProvider.InfoPageEntryItem(app)
if (Flags.enablePerformBackupTasksInSettings()) {
BackupTasksAppsListProvider.InfoPageEntryItem(app)
}
}
Category(title = stringResource(R.string.app_install_details_group_title)) {

View File

@@ -1,54 +0,0 @@
/*
* Copyright (C) 2024 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.Manifest
import android.app.AppOpsManager
import android.app.settings.SettingsEnums
import android.content.Context
import com.android.settings.R
import com.android.settings.overlay.FeatureFactory.Companion.featureFactory
import com.android.settingslib.spaprivileged.template.app.AppOpPermissionListModel
import com.android.settingslib.spaprivileged.template.app.AppOpPermissionRecord
import com.android.settingslib.spaprivileged.template.app.TogglePermissionAppListProvider
object BackupTasksAppsListProvider : TogglePermissionAppListProvider {
override val permissionType = "BackupTasksApps"
override fun createModel(context: Context) = BackupTasksAppsListModel(context)
}
class BackupTasksAppsListModel(context: Context) : AppOpPermissionListModel(context) {
override val pageTitleResId = R.string.run_backup_tasks_title
override val switchTitleResId = R.string.run_backup_tasks_switch_title
override val footerResId = R.string.run_backup_tasks_footer_title
override val appOp = AppOpsManager.OP_RUN_BACKUP_JOBS
override val permission = Manifest.permission.RUN_BACKUP_JOBS
override val setModeByUid = true
override fun setAllowed(record: AppOpPermissionRecord, newAllowed: Boolean) {
super.setAllowed(record, newAllowed)
logPermissionChange(newAllowed)
}
private fun logPermissionChange(newAllowed: Boolean) {
featureFactory.metricsFeatureProvider.action(
context,
SettingsEnums.ACTION_RUN_BACKUP_TASKS_TOGGLE,
if (newAllowed) 1 else 0
)
}
}

View File

@@ -1,35 +0,0 @@
/*
* Copyright (C) 2024 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 androidx.preference.Preference
import com.android.settings.core.BasePreferenceController
import com.android.settings.spa.SpaActivity.Companion.startSpaActivity
class BackupTasksAppsPreferenceController(context: Context, preferenceKey: String) :
BasePreferenceController(context, preferenceKey) {
override fun getAvailabilityStatus() = CONDITIONALLY_UNAVAILABLE
override fun handlePreferenceTreeClick(preference: Preference): Boolean {
if (preference.key == mPreferenceKey) {
mContext.startSpaActivity(BackupTasksAppsListProvider.getAppListRoute())
return true
}
return false
}
}

View File

@@ -70,7 +70,6 @@ object SpecialAppAccessPageProvider : SettingsPageProvider {
WifiControlAppListProvider,
LongBackgroundTasksAppListProvider,
TurnScreenOnAppsAppListProvider,
BackupTasksAppsListProvider,
)
.map { it.buildAppListInjectEntry().setLink(fromPage = owner).build() }
}

View File

@@ -85,13 +85,16 @@ private fun LabelSimPreference(
onboardingService: SimOnboardingService,
subInfo: SubscriptionInfo,
) {
val originalSimCarrierName = subInfo.displayName.toString()
var titleSimName by remember {
mutableStateOf(onboardingService.getSubscriptionInfoDisplayName(subInfo))
}
val phoneNumber = phoneNumber(subInfo)
val alertDialogPresenter = rememberAlertDialogPresenter(
confirmButton = AlertDialogButton(stringResource(R.string.mobile_network_sim_name_rename)) {
onboardingService.addItemForRenaming(subInfo, titleSimName)
onboardingService.addItemForRenaming(
subInfo, if (titleSimName.isEmpty()) originalSimCarrierName else titleSimName
)
},
dismissButton = AlertDialogButton(stringResource(R.string.cancel)) {
titleSimName = onboardingService.getSubscriptionInfoDisplayName(subInfo)
@@ -105,6 +108,7 @@ private fun LabelSimPreference(
SettingsOutlinedTextField(
value = titleSimName,
label = stringResource(R.string.sim_onboarding_label_sim_dialog_label),
placeholder = {Text(text = originalSimCarrierName)},
modifier = Modifier.fillMaxWidth()
) {
titleSimName = it

View File

@@ -558,4 +558,49 @@ public class BluetoothDetailsProfilesControllerTest extends BluetoothDetailsCont
List<SwitchPreferenceCompat> switches = getProfileSwitches(false);
assertThat(switches.get(0).isVisible()).isTrue();
}
@Test
public void classicAudioDeviceWithLeAudio_showLeAudioToggle() {
mSetFlagsRule.enableFlags(Flags.FLAG_HIDE_LE_AUDIO_TOGGLE_FOR_LE_AUDIO_ONLY_DEVICE);
setupDevice(makeDefaultDeviceConfig());
LeAudioProfile leAudioProfile = mock(LeAudioProfile.class);
when(leAudioProfile.getNameResource(mDevice))
.thenReturn(com.android.settingslib.R.string.bluetooth_profile_le_audio);
when(leAudioProfile.isProfileReady()).thenReturn(true);
when(leAudioProfile.toString()).thenReturn("LE_AUDIO");
when(mProfileManager.getLeAudioProfile()).thenReturn(leAudioProfile);
mConnectableProfiles.add(leAudioProfile);
when(mCachedDevice.getProfiles())
.thenAnswer(
invocation ->
ImmutableList.of(
leAudioProfile, addMockA2dpProfile(false, false, false)));
showScreen(mController);
List<SwitchPreferenceCompat> switches = getProfileSwitches(false);
assertThat(switches.get(0).isVisible()).isTrue();
}
@Test
public void leAudioOnlyDevice_hideLeAudioToggle() {
mSetFlagsRule.enableFlags(Flags.FLAG_HIDE_LE_AUDIO_TOGGLE_FOR_LE_AUDIO_ONLY_DEVICE);
setupDevice(makeDefaultDeviceConfig());
LeAudioProfile leAudioProfile = mock(LeAudioProfile.class);
when(leAudioProfile.getNameResource(mDevice))
.thenReturn(com.android.settingslib.R.string.bluetooth_profile_le_audio);
when(leAudioProfile.isProfileReady()).thenReturn(true);
when(leAudioProfile.toString()).thenReturn("LE_AUDIO");
when(mProfileManager.getLeAudioProfile()).thenReturn(leAudioProfile);
mConnectableProfiles.add(leAudioProfile);
when(mCachedDevice.getProfiles())
.thenAnswer(invocation -> ImmutableList.of(leAudioProfile));
showScreen(mController);
List<SwitchPreferenceCompat> switches = getProfileSwitches(false);
assertThat(switches.get(0).isVisible()).isFalse();
}
}

View File

@@ -72,6 +72,11 @@ public class PowerUsageFeatureProviderImplTest {
assertThat(mPowerFeatureProvider.isBatteryTipsEnabled()).isFalse();
}
@Test
public void testIsAppOptimizationModeLogged_returnFalse() {
assertThat(mPowerFeatureProvider.isAppOptimizationModeLogged()).isFalse();
}
@Test
public void testGetBatteryUsageListConsumePowerThreshold_return0() {
assertThat(mPowerFeatureProvider.getBatteryUsageListConsumePowerThreshold()).isEqualTo(0.0);

View File

@@ -383,7 +383,7 @@ public final class BatteryChartPreferenceControllerTest {
mBatteryChartPreferenceController.mDailyChartIndex = SELECTED_INDEX_ALL;
mBatteryChartPreferenceController.mHourlyChartIndex = SELECTED_INDEX_ALL;
assertThat(mBatteryChartPreferenceController.getSlotInformation()).isEqualTo(null);
assertThat(mBatteryChartPreferenceController.getSlotInformation(false)).isEqualTo(null);
assertThat(mBatteryChartPreferenceController.getBatteryLevelPercentageInfo())
.isEqualTo("Battery level percentage from 100% to 66%");
}
@@ -394,7 +394,7 @@ public final class BatteryChartPreferenceControllerTest {
mBatteryChartPreferenceController.mDailyChartIndex = 0;
mBatteryChartPreferenceController.mHourlyChartIndex = SELECTED_INDEX_ALL;
assertThat(mBatteryChartPreferenceController.getSlotInformation()).isEqualTo(null);
assertThat(mBatteryChartPreferenceController.getSlotInformation(false)).isEqualTo(null);
assertThat(mBatteryChartPreferenceController.getBatteryLevelPercentageInfo())
.isEqualTo("Battery level percentage from 100% to 66%");
}
@@ -405,7 +405,7 @@ public final class BatteryChartPreferenceControllerTest {
mBatteryChartPreferenceController.mDailyChartIndex = 1;
mBatteryChartPreferenceController.mHourlyChartIndex = SELECTED_INDEX_ALL;
assertThat(mBatteryChartPreferenceController.getSlotInformation()).isEqualTo("Sunday");
assertThat(mBatteryChartPreferenceController.getSlotInformation(false)).isEqualTo("Sunday");
assertThat(mBatteryChartPreferenceController.getBatteryLevelPercentageInfo())
.isEqualTo("Battery level percentage from 83% to 59%");
}
@@ -416,8 +416,10 @@ public final class BatteryChartPreferenceControllerTest {
mBatteryChartPreferenceController.mDailyChartIndex = 0;
mBatteryChartPreferenceController.mHourlyChartIndex = 2;
assertThat(mBatteryChartPreferenceController.getSlotInformation())
assertThat(mBatteryChartPreferenceController.getSlotInformation(false))
.isEqualTo("10 AM - 12 PM");
assertThat(mBatteryChartPreferenceController.getSlotInformation(true))
.isEqualTo("10 AM to 12 PM");
assertThat(mBatteryChartPreferenceController.getBatteryLevelPercentageInfo())
.isEqualTo("Battery level percentage from 97% to 95%");
}
@@ -428,8 +430,10 @@ public final class BatteryChartPreferenceControllerTest {
mBatteryChartPreferenceController.mDailyChartIndex = 1;
mBatteryChartPreferenceController.mHourlyChartIndex = 8;
assertThat(mBatteryChartPreferenceController.getSlotInformation())
assertThat(mBatteryChartPreferenceController.getSlotInformation(false))
.isEqualTo("Sunday 4 PM - 6 PM");
assertThat(mBatteryChartPreferenceController.getSlotInformation(true))
.isEqualTo("Sunday 4 PM to 6 PM");
assertThat(mBatteryChartPreferenceController.getBatteryLevelPercentageInfo())
.isEqualTo("Battery level percentage from 67% to 65%");
}
@@ -440,8 +444,10 @@ public final class BatteryChartPreferenceControllerTest {
mBatteryChartPreferenceController.mDailyChartIndex = 0;
mBatteryChartPreferenceController.mHourlyChartIndex = 0;
assertThat(mBatteryChartPreferenceController.getSlotInformation())
assertThat(mBatteryChartPreferenceController.getSlotInformation(false))
.isEqualTo("7:01 AM - 8 AM");
assertThat(mBatteryChartPreferenceController.getSlotInformation(true))
.isEqualTo("7:01 AM to 8 AM");
assertThat(mBatteryChartPreferenceController.getBatteryLevelPercentageInfo())
.isEqualTo("Battery level percentage from 100% to 99%");
}
@@ -452,7 +458,10 @@ public final class BatteryChartPreferenceControllerTest {
mBatteryChartPreferenceController.mDailyChartIndex = 0;
mBatteryChartPreferenceController.mHourlyChartIndex = 3;
assertThat(mBatteryChartPreferenceController.getSlotInformation()).isEqualTo("12 PM - now");
assertThat(mBatteryChartPreferenceController.getSlotInformation(false))
.isEqualTo("12 PM - now");
assertThat(mBatteryChartPreferenceController.getSlotInformation(true))
.isEqualTo("12 PM to now");
assertThat(mBatteryChartPreferenceController.getBatteryLevelPercentageInfo())
.isEqualTo("Battery level percentage from 95% to 66%");
}
@@ -463,8 +472,10 @@ public final class BatteryChartPreferenceControllerTest {
mBatteryChartPreferenceController.mDailyChartIndex = 0;
mBatteryChartPreferenceController.mHourlyChartIndex = 0;
assertThat(mBatteryChartPreferenceController.getSlotInformation())
assertThat(mBatteryChartPreferenceController.getSlotInformation(false))
.isEqualTo("7:01 AM - now");
assertThat(mBatteryChartPreferenceController.getSlotInformation(true))
.isEqualTo("7:01 AM to now");
assertThat(mBatteryChartPreferenceController.getBatteryLevelPercentageInfo())
.isEqualTo("Battery level percentage from 100% to 66%");
}

View File

@@ -271,7 +271,7 @@ public final class BatteryUsageBreakdownControllerTest {
@Test
public void setPreferencePercent_lessThanThreshold_expectedFormat() {
final PowerGaugePreference pref = new PowerGaugePreference(mContext);
final PowerGaugePreference pref = spy(new PowerGaugePreference(mContext));
final BatteryDiffEntry batteryDiffEntry =
createBatteryDiffEntry(
/* isSystem= */ true,
@@ -282,15 +282,18 @@ public final class BatteryUsageBreakdownControllerTest {
batteryDiffEntry.mConsumePower = 0.8;
batteryDiffEntry.setTotalConsumePower(100);
mBatteryUsageBreakdownController.mPercentLessThanThresholdText = "< 1%";
mBatteryUsageBreakdownController.mPercentLessThanThresholdContentDescription =
"test content description";
mBatteryUsageBreakdownController.setPreferencePercentage(pref, batteryDiffEntry);
assertThat(pref.getPercentage()).isEqualTo("< 1%");
verify(pref).setPercentageContentDescription("test content description");
}
@Test
public void setPreferencePercent_greaterThanThreshold_expectedFormat() {
final PowerGaugePreference pref = new PowerGaugePreference(mContext);
final PowerGaugePreference pref = spy(new PowerGaugePreference(mContext));
final BatteryDiffEntry batteryDiffEntry =
createBatteryDiffEntry(
/* isSystem= */ true,
@@ -301,10 +304,13 @@ public final class BatteryUsageBreakdownControllerTest {
batteryDiffEntry.mConsumePower = 16;
batteryDiffEntry.setTotalConsumePower(100);
mBatteryUsageBreakdownController.mPercentLessThanThresholdText = "< 1%";
mBatteryUsageBreakdownController.mPercentLessThanThresholdContentDescription =
"test content description";
mBatteryUsageBreakdownController.setPreferencePercentage(pref, batteryDiffEntry);
assertThat(pref.getPercentage()).isEqualTo("16%");
verify(pref, never()).setPercentageContentDescription(any());
}
@Test

View File

@@ -382,9 +382,13 @@ public final class ConvertUtilsTest {
/* foregroundServiceUsageConsumePower= */ 1.3,
/* backgroundUsageConsumePower= */ 1.4,
/* cachedUsageConsumePower= */ 1.5);
BatteryOptimizationModeCache optimizationModeCache =
new BatteryOptimizationModeCache(mContext);
optimizationModeCache.mBatteryOptimizeModeCacheMap.put(
(int) batteryDiffEntry.mUid, BatteryOptimizationMode.MODE_OPTIMIZED);
final BatteryUsageDiff batteryUsageDiff =
ConvertUtils.convertToBatteryUsageDiff(batteryDiffEntry);
ConvertUtils.convertToBatteryUsageDiff(batteryDiffEntry, optimizationModeCache);
assertThat(batteryUsageDiff.getUid()).isEqualTo(101L);
assertThat(batteryUsageDiff.getUserId()).isEqualTo(1001L);
@@ -402,6 +406,8 @@ public final class ConvertUtilsTest {
assertThat(batteryUsageDiff.getBackgroundUsageTime()).isEqualTo(5678L);
assertThat(batteryUsageDiff.getScreenOnTime()).isEqualTo(123L);
assertThat(batteryUsageDiff.getKey()).isEqualTo("key");
assertThat(batteryUsageDiff.getAppOptimizationMode())
.isEqualTo(BatteryOptimizationMode.MODE_OPTIMIZED);
assertThat(batteryUsageDiff.hasPackageName()).isFalse();
assertThat(batteryUsageDiff.hasLabel()).isFalse();
}
@@ -591,7 +597,7 @@ public final class ConvertUtilsTest {
Map.of(11L, batteryDiffData1, 21L, batteryDiffData2, 31L, batteryDiffData3);
final List<BatteryUsageSlot> batteryUsageSlotList =
ConvertUtils.convertToBatteryUsageSlotList(batteryDiffDataMap);
ConvertUtils.convertToBatteryUsageSlotList(mContext, batteryDiffDataMap, false);
assertThat(batteryUsageSlotList).hasSize(3);
assertThat(batteryUsageSlotList.stream().map((s) -> s.getScreenOnTime()).sorted().toList())

View File

@@ -101,4 +101,13 @@ public class PowerGaugePreferenceTest {
assertThat(mPreferenceViewHolder.findViewById(android.R.id.title).getContentDescription())
.isEqualTo(CONTENT_DESCRIPTION);
}
@Test
public void testOnBindViewHolder_bindPercentageContentDescription() {
mPowerGaugePreference.setPercentageContentDescription(CONTENT_DESCRIPTION);
mPowerGaugePreference.onBindViewHolder(mPreferenceViewHolder);
assertThat(mPreferenceViewHolder.findViewById(R.id.widget_summary).getContentDescription())
.isEqualTo(CONTENT_DESCRIPTION);
}
}

View File

@@ -68,7 +68,7 @@ public final class ScreenOnTimeControllerTest {
@Test
public void handleSceenOnTimeUpdated_nullScreenOnTime_hideAllPreference() {
mScreenOnTimeController.handleSceenOnTimeUpdated(
/* screenOnTime= */ null, "Friday 12:00-now");
/* screenOnTime= */ null, "Friday 12:00 to now");
verify(mRootPreference).setVisible(false);
verify(mScreenOnTimeTextPreference).setVisible(false);
@@ -84,9 +84,9 @@ public final class ScreenOnTimeControllerTest {
@Test
public void showCategoryTitle_notNull_slotTimestamp() {
mScreenOnTimeController.showCategoryTitle("Friday 12:00-now");
mScreenOnTimeController.showCategoryTitle("Friday 12:00 to now");
verify(mRootPreference).setTitle("Screen time for Friday 12:00-now");
verify(mRootPreference).setTitle("Screen time for Friday 12:00 to now");
verify(mRootPreference).setVisible(true);
}

View File

@@ -18,34 +18,48 @@ package com.android.settings.security;
import static com.google.common.truth.Truth.assertThat;
import static org.robolectric.Shadows.shadowOf;
import android.app.admin.DevicePolicyManager;
import android.content.Context;
import android.content.Intent;
import android.icu.text.MessageFormat;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
import androidx.fragment.app.Fragment;
import androidx.test.core.app.ApplicationProvider;
import com.android.settings.R;
import com.android.settings.SettingsActivity;
import com.android.settings.testutils.shadow.ShadowLockPatternUtils;
import com.android.settingslib.search.SearchIndexableRaw;
import com.android.settingslib.widget.FooterPreference;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.android.controller.ActivityController;
import org.robolectric.annotation.Config;
import org.robolectric.shadows.ShadowUserManager;
import java.util.List;
import java.util.function.Consumer;
@RunWith(RobolectricTestRunner.class)
@Config(shadows = ShadowLockPatternUtils.class)
public class ScreenPinningSettingsTest {
private static final String KEY_FOOTER = "screen_pinning_settings_screen_footer";
private Context mContext;
private UserManager mUserManager;
@Before
public void setUp() {
mContext = ApplicationProvider.getApplicationContext();
mUserManager = mContext.getSystemService(UserManager.class);
}
@After
@@ -110,4 +124,97 @@ public class ScreenPinningSettingsTest {
assertThat(indexRaws.get(0).title).isEqualTo(
mContext.getString(R.string.screen_pinning_unlock_none));
}
@Test
public void onCreate_lockToAppEnabled_guestModeSupported_verifyFooterText() {
setupLockToAppState(/* enabled= */ true);
setupGuestModeState(/* supported= */ true);
launchFragmentAndRunTest(fragment -> {
FooterPreference footer = fragment.findPreference(KEY_FOOTER);
assertThat(footer.getSummary())
.isEqualTo(getExpectedFooterText(/* guestModeSupported= */ true));
});
}
@Test
public void onCreate_lockToAppEnabled_guestModeNotSupported_verifyFooterText() {
setupLockToAppState(/* enabled= */ true);
setupGuestModeState(/* supported= */ false);
launchFragmentAndRunTest(fragment -> {
FooterPreference footer = fragment.findPreference(KEY_FOOTER);
assertThat(footer.getSummary())
.isEqualTo(getExpectedFooterText(/* guestModeSupported= */ false));
});
}
@Test
public void onCreate_lockToAppDisabled_guestModeSupported_verifyFooterText() {
setupLockToAppState(/* enabled= */ false);
setupGuestModeState(/* supported= */ true);
launchFragmentAndRunTest(fragment -> {
FooterPreference footer = fragment.findPreference(KEY_FOOTER);
assertThat(footer.getSummary())
.isEqualTo(getExpectedFooterText(/* guestModeSupported= */ true));
});
}
@Test
public void onCreate_lockToAppDisabled_guestModeNotSupported_verifyFooterText() {
setupLockToAppState(/* enabled= */ false);
setupGuestModeState(/* supported= */ false);
launchFragmentAndRunTest(fragment -> {
FooterPreference footer = fragment.findPreference(KEY_FOOTER);
assertThat(footer.getSummary())
.isEqualTo(getExpectedFooterText(/* guestModeSupported= */ false));
});
}
private CharSequence getExpectedFooterText(boolean guestModeSupported) {
final int stringResource = guestModeSupported
? R.string.screen_pinning_guest_user_description
: R.string.screen_pinning_description;
return MessageFormat.format(mContext.getString(stringResource), 1, 2, 3);
}
private void setupLockToAppState(boolean enabled) {
Settings.System.putInt(mContext.getContentResolver(), Settings.System.LOCK_TO_APP_ENABLED,
enabled ? 1 : 0);
}
private void setupGuestModeState(boolean supported) {
ShadowUserManager shadowUserManager = shadowOf(mUserManager);
shadowUserManager.setSupportsMultipleUsers(supported);
shadowUserManager.setUserRestriction(
UserHandle.of(UserHandle.myUserId()), UserManager.DISALLOW_USER_SWITCH, !supported);
}
private void launchFragmentAndRunTest(Consumer<ScreenPinningSettings> test) {
Intent intent = new Intent();
intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT,
SecurityAdvancedSettings.class.getName());
// ScreenPinningSettings is tightly coupled with the SettingsActivity
// In order to successfully launch the ScreenPinningSettings, have to use an indirect route
// to launch the SecurityAdvancedSetting first, then replace it with ScreenPinningSettings.
try (ActivityController<SettingsActivity> controller =
ActivityController.of(new SettingsActivity(), intent)) {
controller.create().start().resume();
controller.get().getSupportFragmentManager().beginTransaction().replace(
R.id.main_content, ScreenPinningSettings.class, null).commitNow();
Fragment fragment = controller.get().getSupportFragmentManager()
.findFragmentById(R.id.main_content);
assertThat(fragment).isNotNull();
assertThat(fragment).isInstanceOf(ScreenPinningSettings.class);
test.accept((ScreenPinningSettings) fragment);
}
}
}

View File

@@ -1,82 +0,0 @@
/*
* Copyright (C) 2024 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.platform.test.annotations.RequiresFlagsDisabled
import android.platform.test.annotations.RequiresFlagsEnabled
import android.platform.test.flag.junit.CheckFlagsRule
import android.platform.test.flag.junit.DeviceFlagsValueProvider
import androidx.preference.Preference
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.settings.flags.Flags
import com.google.common.truth.Truth.assertThat
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.kotlin.any
import org.mockito.kotlin.doNothing
import org.mockito.kotlin.spy
import org.mockito.kotlin.whenever
@RunWith(AndroidJUnit4::class)
class BackupTasksAppsPreferenceControllerTest {
@get:Rule
val checkFlagsRule: CheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule()
private val context: Context = spy(ApplicationProvider.getApplicationContext()) {
doNothing().whenever(mock).startActivity(any())
}
private val matchedPreference = Preference(context).apply { key = preferenceKey }
private val misMatchedPreference = Preference(context).apply { key = testPreferenceKey }
private val controller = BackupTasksAppsPreferenceController(context, preferenceKey)
@Test
@RequiresFlagsEnabled(Flags.FLAG_ENABLE_PERFORM_BACKUP_TASKS_IN_SETTINGS)
fun getAvailabilityStatus_enableBackupTasksApps_returnAvailable() {
// Feature is currently disabled so it should return false regardless of flag status.
assertThat(controller.isAvailable).isFalse()
}
@Test
@RequiresFlagsDisabled(Flags.FLAG_ENABLE_PERFORM_BACKUP_TASKS_IN_SETTINGS)
fun getAvailableStatus_disableBackupTasksApps_returnConditionallyUnavailable() {
assertThat(controller.isAvailable).isFalse()
}
@Test
fun handlePreferenceTreeClick_keyMatched_returnTrue() {
assertThat(controller.handlePreferenceTreeClick(matchedPreference)).isTrue()
}
@Test
fun handlePreferenceTreeClick_keyMisMatched_returnFalse() {
assertThat(controller.handlePreferenceTreeClick(misMatchedPreference)).isFalse()
}
companion object {
private const val preferenceKey: String = "backup_tasks_apps"
private const val testPreferenceKey: String = "test_key"
}
}

View File

@@ -1,44 +0,0 @@
/*
* Copyright (C) 2024 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.Manifest
import android.app.AppOpsManager
import android.content.Context
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.google.common.truth.Truth.assertThat
import com.android.settings.R
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class BackupTasksAppsTest {
private val context: Context = ApplicationProvider.getApplicationContext()
private val listModel = BackupTasksAppsListModel(context)
@Test
fun modelResourceIdAndProperties() {
assertThat(listModel.pageTitleResId).isEqualTo(R.string.run_backup_tasks_title)
assertThat(listModel.switchTitleResId).isEqualTo(R.string.run_backup_tasks_switch_title)
assertThat(listModel.footerResId).isEqualTo(R.string.run_backup_tasks_footer_title)
assertThat(listModel.appOp).isEqualTo(AppOpsManager.OP_RUN_BACKUP_JOBS)
assertThat(listModel.permission).isEqualTo(Manifest.permission.RUN_BACKUP_JOBS)
assertThat(listModel.setModeByUid).isTrue()
}
}

View File

@@ -17,7 +17,6 @@
package com.android.settings.applications.credentials;
import static com.android.settings.core.BasePreferenceController.AVAILABLE;
import static com.android.settings.core.BasePreferenceController.CONDITIONALLY_UNAVAILABLE;
import static com.android.settings.core.BasePreferenceController.UNSUPPORTED_ON_DEVICE;
import static com.google.common.truth.Truth.assertThat;
@@ -36,7 +35,9 @@ import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Looper;
import android.provider.Settings;
import android.util.Pair;
import androidx.annotation.Nullable;
import androidx.preference.Preference;
import androidx.preference.PreferenceCategory;
import androidx.preference.PreferenceManager;
@@ -124,19 +125,37 @@ public class CredentialManagerPreferenceControllerTest {
controller.setSimulateConnectedForTests(true);
assertThat(controller.isConnected()).isTrue();
controller.setSimulateHiddenForTests(Optional.of(false));
assertThat(controller.isHiddenDueToNoProviderSet()).isFalse();
assertThat(controller.isHiddenDueToNoProviderSet(createPair())).isFalse();
assertThat(controller.getAvailabilityStatus()).isEqualTo(AVAILABLE);
}
@Test
public void getAvailabilityStatus_isHidden_returnsConditionallyUnavailable() {
public void isHiddenDueToNoProviderSet_hiddenDueToEmptyPair() {
CredentialManagerPreferenceController controller =
createControllerWithServices(Lists.newArrayList(createCredentialProviderInfo()));
controller.setSimulateConnectedForTests(true);
assertThat(controller.isConnected()).isTrue();
controller.setSimulateHiddenForTests(Optional.of(true));
assertThat(controller.isHiddenDueToNoProviderSet()).isTrue();
assertThat(controller.getAvailabilityStatus()).isEqualTo(CONDITIONALLY_UNAVAILABLE);
assertThat(controller.isHiddenDueToNoProviderSet(createPair())).isTrue();
}
@Test
public void isHiddenDueToNoProviderSet_hiddenDueToNoPrimaryProvider() {
CredentialManagerPreferenceController controller =
createControllerWithServices(Lists.newArrayList(createCredentialProviderInfo()));
Pair<List<CombinedProviderInfo>, CombinedProviderInfo> testPair =
new Pair<>(Lists.newArrayList(createCombinedProviderInfo()), null);
assertThat(controller.isHiddenDueToNoProviderSet(testPair)).isTrue();
}
@Test
public void isHiddenDueToNoProviderSet_validDataSoNotHidden() {
CredentialManagerPreferenceController controller =
createControllerWithServices(Lists.newArrayList(createCredentialProviderInfo()));
Pair<List<CombinedProviderInfo>, CombinedProviderInfo> testPair =
new Pair<>(
Lists.newArrayList(createCombinedProviderInfo()),
createCombinedProviderInfo());
assertThat(controller.isHiddenDueToNoProviderSet(testPair)).isFalse();
}
@Test
@@ -146,7 +165,7 @@ public class CredentialManagerPreferenceControllerTest {
controller.setSimulateConnectedForTests(true);
controller.setSimulateHiddenForTests(Optional.of(false));
assertThat(controller.isHiddenDueToNoProviderSet()).isFalse();
assertThat(controller.isHiddenDueToNoProviderSet(createPair())).isFalse();
assertThat(controller.isConnected()).isTrue();
assertThat(controller.getAvailabilityStatus()).isEqualTo(AVAILABLE);
@@ -170,7 +189,7 @@ public class CredentialManagerPreferenceControllerTest {
controller.setSimulateConnectedForTests(true);
assertThat(controller.isConnected()).isTrue();
controller.setSimulateHiddenForTests(Optional.of(false));
assertThat(controller.isHiddenDueToNoProviderSet()).isFalse();
assertThat(controller.isHiddenDueToNoProviderSet(createPair())).isFalse();
assertThat(controller.getAvailabilityStatus()).isEqualTo(AVAILABLE);
// Test the data is correct.
@@ -214,7 +233,7 @@ public class CredentialManagerPreferenceControllerTest {
controller.setSimulateConnectedForTests(true);
assertThat(controller.isConnected()).isTrue();
controller.setSimulateHiddenForTests(Optional.of(false));
assertThat(controller.isHiddenDueToNoProviderSet()).isFalse();
assertThat(controller.isHiddenDueToNoProviderSet(createPair())).isFalse();
assertThat(controller.getAvailabilityStatus()).isEqualTo(AVAILABLE);
// Ensure that we stay under 5 providers (one is reserved for primary).
@@ -283,7 +302,7 @@ public class CredentialManagerPreferenceControllerTest {
controller.setSimulateConnectedForTests(true);
assertThat(controller.isConnected()).isTrue();
controller.setSimulateHiddenForTests(Optional.of(false));
assertThat(controller.isHiddenDueToNoProviderSet()).isFalse();
assertThat(controller.isHiddenDueToNoProviderSet(createPair())).isFalse();
assertThat(controller.getAvailabilityStatus()).isEqualTo(AVAILABLE);
// Test the data is correct.
@@ -336,17 +355,26 @@ public class CredentialManagerPreferenceControllerTest {
createControllerWithServices(
Lists.newArrayList(serviceA1, serviceB1, serviceC1, serviceC2, serviceC3));
controller.setSimulateConnectedForTests(true);
controller.setSimulateHiddenForTests(Optional.of(false));
assertThat(controller.isHiddenDueToNoProviderSet()).isFalse();
assertThat(controller.isConnected()).isTrue();
assertThat(controller.getAvailabilityStatus()).isEqualTo(AVAILABLE);
controller.displayPreference(mScreen);
assertThat(mCredentialsPreferenceCategory.getPreferenceCount()).isEqualTo(3);
CombinedProviderInfo combinedProviderA =
new CombinedProviderInfo(Lists.newArrayList(serviceA1), null, false, false);
CombinedProviderInfo combinedProviderB =
new CombinedProviderInfo(Lists.newArrayList(serviceB1), null, false, false);
CombinedProviderInfo combinedProviderC =
new CombinedProviderInfo(
Lists.newArrayList(serviceC1, serviceC2, serviceC3), null, false, false);
Pair<List<CombinedProviderInfo>, CombinedProviderInfo> providerPair =
createPair(
Lists.newArrayList(combinedProviderA, combinedProviderB, combinedProviderC),
createCombinedProviderInfo());
assertThat(controller.isHiddenDueToNoProviderSet(providerPair)).isFalse();
Map<String, CredentialManagerPreferenceController.CombiPreference> prefs =
controller.buildPreferenceList(mContext, mCredentialsPreferenceCategory);
controller.buildPreferenceList(mContext, providerPair);
assertThat(prefs.keySet())
.containsExactly(TEST_PACKAGE_NAME_A, TEST_PACKAGE_NAME_B, TEST_PACKAGE_NAME_C);
assertThat(prefs.size()).isEqualTo(3);
@@ -531,8 +559,7 @@ public class CredentialManagerPreferenceControllerTest {
@Test
public void hasNonPrimaryServices_allServicesArePrimary() {
CredentialManagerPreferenceController controller =
createControllerWithServices(
Lists.newArrayList(createCredentialProviderPrimary()));
createControllerWithServices(Lists.newArrayList(createCredentialProviderPrimary()));
assertThat(controller.hasNonPrimaryServices()).isFalse();
}
@@ -540,8 +567,8 @@ public class CredentialManagerPreferenceControllerTest {
public void hasNonPrimaryServices_mixtureOfServices() {
CredentialManagerPreferenceController controller =
createControllerWithServices(
Lists.newArrayList(createCredentialProviderInfo(),
createCredentialProviderPrimary()));
Lists.newArrayList(
createCredentialProviderInfo(), createCredentialProviderPrimary()));
assertThat(controller.hasNonPrimaryServices()).isTrue();
}
@@ -599,11 +626,25 @@ public class CredentialManagerPreferenceControllerTest {
private CredentialProviderInfo createCredentialProviderPrimary() {
return createCredentialProviderInfoBuilder(
"com.android.primary", "CredManProvider", "Service Label", "App Name")
"com.android.primary", "CredManProvider", "Service Label", "App Name")
.setPrimary(true)
.build();
}
private Pair<List<CombinedProviderInfo>, CombinedProviderInfo> createPair() {
return createPair(Lists.newArrayList(), null);
}
private Pair<List<CombinedProviderInfo>, CombinedProviderInfo> createPair(
List<CombinedProviderInfo> providers, @Nullable CombinedProviderInfo primaryProvider) {
return new Pair<>(providers, primaryProvider);
}
private CombinedProviderInfo createCombinedProviderInfo() {
return new CombinedProviderInfo(
Lists.newArrayList(createCredentialProviderInfo()), null, false, false);
}
private CredentialProviderInfo createCredentialProviderInfoWithSubtitle(
String packageName, String className, CharSequence label, CharSequence subtitle) {
ServiceInfo si = new ServiceInfo();