Snap for 7357013 from c19ad05b02 to sc-release

Change-Id: I9407c736eddf35cc0415c615a1d6a756ccad524f
This commit is contained in:
android-build-team Robot
2021-05-13 01:09:41 +00:00
87 changed files with 1375 additions and 822 deletions

25
res/drawable/ic_info.xml Normal file
View File

@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2021 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.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="?android:attr/colorAccent"
android:pathData="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-6h2v6zm0-8h-2V7h2v2z"/>
</vector>

View File

@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2021 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">
<item android:state_enabled="true" android:drawable="@drawable/ic_info"/>
<item android:state_enabled="false" android:drawable="@drawable/ic_info_outline_24"/>
</selector>

View File

@@ -24,6 +24,7 @@
<TextView
android:text="@string/condition_airplane_title"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textColor="?android:attr/textColorSecondary"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"

View File

@@ -28,7 +28,7 @@
android:layout_height="wrap_content"
android:layout_marginVertical="16dp"
android:textAppearance="?android:attr/textAppearanceSmall"
settings:textColor="?android:attr/textColorSecondary"
android:textColor="?android:attr/textColorSecondary"
android:text="@string/battery_usage_chart_graph_hint" />
<com.android.settings.fuelgauge.BatteryChartView
@@ -37,6 +37,7 @@
android:layout_height="165dp"
android:layout_marginBottom="16dp"
android:visibility="invisible"
android:contentDescription="@string/battery_usage_chart"
android:textAppearance="?android:attr/textAppearanceSmall"
settings:textColor="?android:attr/textColorSecondary" />
<TextView

View File

@@ -155,6 +155,55 @@
</LinearLayout>
<!-- Keep in mind -->
<com.google.android.setupdesign.view.RichTextView
style="@style/SudDescription.Glif"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAlignment="viewStart"
android:paddingTop="12dp"
android:text="@string/security_settings_fingerprint_enroll_introduction_footer_title_2" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:paddingTop="12dp">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="@null"
android:src="@drawable/ic_info_outline_24dp"/>
<Space
android:layout_width="24dp"
android:layout_height="wrap_content"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/security_settings_fingerprint_v2_enroll_introduction_footer_message_5" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:paddingTop="12dp">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="@null"
android:src="@drawable/ic_link_24dp"/>
<Space
android:layout_width="24dp"
android:layout_height="wrap_content"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/security_settings_fingerprint_v2_enroll_introduction_message_learn_more" />
</LinearLayout>
</LinearLayout>
</com.google.android.setupdesign.GlifLayout>

View File

@@ -49,6 +49,7 @@
android:padding="@*android:dimen/preference_fragment_padding_side"
android:layout_gravity="center"
android:gravity="center_vertical"
android:textAppearance="?android:attr/textAppearanceMedium"
android:visibility="gone" />
<LinearLayout

View File

@@ -16,7 +16,7 @@
<ImageView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/settings_button"
android:id="@+id/info_button"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
@@ -24,5 +24,4 @@
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
android:background="?android:attr/selectableItemBackground"
android:scaleType="center"
android:src="@drawable/ic_info_outline_24"
android:contentDescription="@string/settings_button" />
android:src="@drawable/ic_info_selector" />

View File

@@ -1512,12 +1512,19 @@
<item>@string/enhanced_4g_lte_mode_summary_4g_calling</item>
</string-array>
<!-- An allowlist which packages won't show summary in battery usage screen. [CHAR LIMIT=NONE] -->
<!-- An allowlist which packages won't show summary in battery usage screen.
[CHAR LIMIT=NONE] -->
<string-array name="allowlist_hide_summary_in_battery_usage" translatable="false">
<!-- Google -->
<item>"com.google.android.googlequicksearchbox"</item>
</string-array>
<!-- An allowlist which packages won't show entry in battery usage screen.
[CHAR LIMIT=NONE] -->
<string-array name="allowlist_hide_entry_in_battery_usage" translatable="false">
<item>"com.google.android.gms.persistent"</item>
</string-array>
<!-- Array of titles palette list for accessibility. -->
<string-array name="setting_palette_data" translatable="false" >
<item>@string/color_red</item>

View File

@@ -943,6 +943,8 @@
<string name="security_settings_fingerprint_v2_enroll_introduction_footer_message_3">The phone will also use images from your interactions with Fingerprint Unlock to update your fingerprint model. Images used to create your fingerprint model are never stored, but the fingerprint model is stored securely on your phone and never leaves the phone. All processing occurs securely on your phone.</string>
<!-- Introduction description message shown in fingerprint enrollment introduction screen in setup wizard. [CHAR LIMIT=NONE]-->
<string name="security_settings_fingerprint_v2_enroll_introduction_footer_message_4">You can delete your fingerprint model, or turn off Fingerprint Unlock at any time in Settings. Fingerprint models are stored on the phone until you delete them.</string>
<!-- Introduction description message shown in fingerprint enrollment introduction screen in setup wizard. [CHAR LIMIT=NONE] -->
<string name="security_settings_fingerprint_v2_enroll_introduction_footer_message_5">Your phone can be unlocked when you don\u2019t intend to, like if someone holds it up to your finger.</string>
<!-- Introduction detail message shown in fingerprint enrollment introduction to learn more about fingerprint [CHAR LIMIT=NONE]-->
<string name="security_settings_fingerprint_v2_enroll_introduction_message_learn_more"></string>
@@ -5249,6 +5251,8 @@
<string name="accessibility_shortcut_edit_dialog_summary_software_gesture_talkback">Swipe up from the bottom of the screen with 3 fingers.\n\nTo switch between features, swipe up with 3 fingers and hold.</string>
<!-- Summary for software shortcut in accessibility edit shortcut dialog when user had enabled the accessibility floating button mode (Floating over other apps). [CHAR LIMIT=NONE] -->
<string name="accessibility_shortcut_edit_dialog_summary_software_floating"><annotation id="link">Customize accessibility button</annotation></string>
<!-- Summary for footer to show help link. [CHAR LIMIT=NONE] -->
<string name="footer_learn_more"><annotation id="link">Learn more</annotation></string>
<!-- Title for hardware shortcut in accessibility edit shortcut dialog. [CHAR LIMIT=NONE] -->
<string name="accessibility_shortcut_edit_dialog_title_hardware">Hold volume keys</string>
<!-- Part of list to compose user's accessibility shortcut list. [CHAR LIMIT=NONE] -->
@@ -5998,7 +6002,7 @@
<!-- Summary for the early hedas up tip [CHAR LIMIT=NONE] -->
<string name="battery_tip_early_heads_up_summary">Battery may run out earlier than usual</string>
<!-- Title when early heads up is solved [CHAR LIMIT=NONE] -->
<string name="battery_tip_early_heads_up_done_title">Battery Saver is on</string>
<string name="battery_tip_early_heads_up_done_title">Battery Saver on</string>
<!-- Summary when early heads up is solved [CHAR LIMIT=NONE] -->
<string name="battery_tip_early_heads_up_done_summary">Some features may be limited</string>
<!-- Title for the battery high usage tip [CHAR LIMIT=NONE] -->
@@ -6324,9 +6328,9 @@
<string name ="battery_detail_manage_title">Manage battery usage</string>
<!-- Description for battery total and background usage time for an app, i.e. 1 hr 15 min total • 39 min background for past 24 hr. Note: ^1 and ^2 should be used in all translations [CHAR LIMIT=120] -->
<string name="battery_total_and_bg_usage"><xliff:g id="time" example="1 hr 15 min">^1</xliff:g> total • <xliff:g id="time" example="39 min">^2</xliff:g> background for past 24 hr</string>
<string name="battery_total_and_bg_usage"><xliff:g id="time" example="1 hr 15 min">^1</xliff:g> total • <xliff:g id="time" example="39 min">^2</xliff:g> background\nfor past 24 hr</string>
<!-- Description for battery total and background usage time in a time period for an app, i.e. 1 hr 15 min total • 39 min background for 12 am-2 am. Note: ^1, ^2 and ^3 should be used in all translations [CHAR LIMIT=120] -->
<string name="battery_total_and_bg_usage_with_period"><xliff:g id="time" example="1 hr 15 min">^1</xliff:g> total • <xliff:g id="time" example="39 min">^2</xliff:g> background for <xliff:g id="time_period" example="12 am-2 am">^3</xliff:g></string>
<string name="battery_total_and_bg_usage_with_period"><xliff:g id="time" example="1 hr 15 min">^1</xliff:g> total • <xliff:g id="time" example="39 min">^2</xliff:g> background\nfor <xliff:g id="time_period" example="12 am-2 am">^3</xliff:g></string>
<!-- Description for battery total usage time is less than a minute for an app [CHAR LIMIT=120] -->
<string name="battery_total_usage_less_minute">Total less than a minute for past 24 hr</string>
@@ -6349,9 +6353,9 @@
<string name="battery_bg_usage_with_period"><xliff:g id="time" example="1 hr 15 min">^1</xliff:g> background for <xliff:g id="time_period" example="12 am-2 am">^2</xliff:g></string>
<!-- Description for battery total usage with background usage time less than a minute for an app, i.e. 1 hr 15 min total • background less than a minute for past 24 hr. Note: ^1 should be used in all translations [CHAR LIMIT=120] -->
<string name="battery_total_usage_and_bg_less_minute_usage"><xliff:g id="time" example="1 hr 15 min">^1</xliff:g> total • background less than a minute for past 24 hr</string>
<string name="battery_total_usage_and_bg_less_minute_usage"><xliff:g id="time" example="1 hr 15 min">^1</xliff:g> total • background less than a minute\nfor past 24 hr</string>
<!-- Description for battery total usage with background usage time less than a minute in a time period for an app, i.e. 1 hr 15 min total • background less than a minute for 12 am-2 am. Note: ^1 and ^2 should be used in all translations [CHAR LIMIT=120] -->
<string name="battery_total_usage_and_bg_less_minute_usage_with_period"><xliff:g id="time" example="1 hr 15 min">^1</xliff:g> total • background less than a minute for <xliff:g id="time_period" example="12 am-2 am">^2</xliff:g></string>
<string name="battery_total_usage_and_bg_less_minute_usage_with_period"><xliff:g id="time" example="1 hr 15 min">^1</xliff:g> total • background less than a minute\nfor <xliff:g id="time_period" example="12 am-2 am">^2</xliff:g></string>
<!-- Description for no any battery usage for past 24 hr [CHAR LIMIT=120] -->
<string name="battery_not_usage">No usage for past 24 hr</string>
@@ -6469,6 +6473,8 @@
<string name="battery_usage_for_background_time">Background: <xliff:g id="time">%s</xliff:g></string>
<!-- [CHAR_LIMIT=NONE] Battery usage main screen footer contentt -->
<string name="battery_usage_screen_footer">Battery usage data is approximate and doesn\'t measure usage when phone is charging</string>
<!-- [CHAR_LIMIT=NONE] Accessibility content description for battery chart view. -->
<string name="battery_usage_chart">Battery usage chart</string>
<!-- Process Stats strings -->
<skip />
@@ -11358,6 +11364,9 @@
<!-- [CHAR LIMIT=60] Summary string on dark theme explaining why the toggle is disabled while the setting is still on-->
<string name="dark_ui_mode_disabled_summary_dark_theme_on">Temporarily disabled due to Battery Saver</string>
<!-- [CHAR LIMIT=60] Summary string for screen attention explaining why the toggle is disabled by battery saver-->
<string name="ambient_camera_summary_battery_saver_on">Temporarily disabled due to Battery Saver</string>
<!-- [CHAR LIMIT=60] Summary string on dark theme explaining why the toggle is disabled while the setting is off-->
<string name="dark_ui_mode_disabled_summary_dark_theme_off">Temporarily turned on due to Battery Saver</string>

View File

@@ -216,16 +216,17 @@
<style name="vpn_label">
<item name="android:layout_width">match_parent</item>
<item name="android:layout_height">wrap_content</item>
<item name="android:textAppearance">@android:style/TextAppearance.DeviceDefault.Small</item>
<item name="android:textAppearance">?android:attr/textAppearanceSmall</item>
</style>
<style name="vpn_value">
<item name="android:layout_width">match_parent</item>
<item name="android:layout_height">wrap_content</item>
<item name="android:textAppearance">@android:style/TextAppearance.DeviceDefault.Medium</item>
<item name="android:textAppearance">?android:attr/textAppearanceMedium</item>
<item name="android:singleLine">true</item>
<item name="android:textAlignment">viewStart</item>
<item name="android:minHeight">@dimen/min_tap_target_size</item>
<item name="android:textColorHint">?android:attr/textColorSecondary</item>
</style>
<style name="vpn_warning">

View File

@@ -32,6 +32,11 @@
android:summary="@string/auto_rotate_camera_lock_summary"
settings:controller="com.android.settings.display.SmartAutoRotateCameraStateController" />
<com.android.settingslib.widget.BannerMessagePreference
android:key="camera_battery_saver_state"
android:title="@string/ambient_camera_summary_battery_saver_on"
settings:controller="com.android.settings.display.SmartAutoRotateBatterySaverController" />
<SwitchPreference
android:key="face_based_rotate"
android:title="@string/auto_rotate_switch_face_based"

View File

@@ -21,12 +21,6 @@
android:persistent="false"
android:title="@string/accessibility_captioning_title">
<com.android.settings.widget.SettingsMainSwitchPreference
android:key="captioning_preference_switch"
android:persistent="false"
android:summary="@string/accessibility_caption_primary_switch_summary"
android:title="@string/accessibility_caption_primary_switch_title" />
<com.android.settingslib.widget.LayoutPreference
android:key="captions_preview"
android:layout="@layout/accessibility_captions_preview"
@@ -35,6 +29,12 @@
android:title="@string/summary_placeholder"
settings:searchable="false" />
<com.android.settings.widget.SettingsMainSwitchPreference
android:key="captioning_preference_switch"
android:persistent="false"
android:summary="@string/accessibility_caption_primary_switch_summary"
android:title="@string/accessibility_caption_primary_switch_title" />
<Preference
android:fragment="com.android.settings.accessibility.CaptionAppearanceFragment"
android:key="captioning_caption_appearance"

View File

@@ -22,15 +22,13 @@
<com.android.settingslib.widget.LayoutPreference
android:key="header_view"
android:layout="@layout/settings_entity_header"
android:selectable="false"
settings:allowDividerBelow="true"/>
android:selectable="false"/>
<com.android.settingslib.widget.ActionButtonsPreference
android:key="action_buttons"/>
<PreferenceCategory
android:title="@string/battery_detail_manage_title"
settings:allowDividerAbove="true">
android:title="@string/battery_detail_manage_title">
<com.android.settingslib.widget.RadioButtonPreference
android:key="unrestricted_pref"

View File

@@ -23,16 +23,14 @@
android:key="header_view"
android:layout="@layout/settings_entity_header"
android:selectable="false"
android:order="-10000"
settings:allowDividerBelow="true"/>
android:order="-10000"/>
<com.android.settingslib.widget.ActionButtonsPreference
android:key="action_buttons"
android:order="-9999"/>
<PreferenceCategory
android:title="@string/battery_detail_manage_title"
settings:allowDividerAbove="true">
android:title="@string/battery_detail_manage_title">
<com.android.settingslib.RestrictedPreference
android:key="background_activity"

View File

@@ -469,6 +469,19 @@ public final class Utils extends com.android.settingslib.Utils {
return UserHandle.USER_NULL;
}
/** Returns user ID of current user, throws IllegalStateException if it's not available. */
public static int getCurrentUserId(UserManager userManager, boolean isWorkProfile)
throws IllegalStateException {
if (isWorkProfile) {
final UserHandle managedUserHandle = getManagedProfile(userManager);
if (managedUserHandle == null) {
throw new IllegalStateException("Work profile user ID is not available.");
}
return managedUserHandle.getIdentifier();
}
return UserHandle.myUserId();
}
/**
* Returns the target user for a Settings activity.
* <p>

View File

@@ -34,4 +34,9 @@ public class AccessibilityControlTimeoutFooterPreferenceController extends
protected String getLabelName() {
return mContext.getString(R.string.accessibility_setting_item_control_timeout_title);
}
@Override
protected int getHelpResource() {
return R.string.help_url_timeout;
}
}

View File

@@ -16,6 +16,7 @@
package com.android.settings.accessibility;
import static com.android.internal.accessibility.AccessibilityShortcutController.ACCESSIBILITY_BUTTON_COMPONENT_NAME;
import static com.android.internal.accessibility.AccessibilityShortcutController.MAGNIFICATION_COMPONENT_NAME;
import android.accessibilityservice.AccessibilityServiceInfo;
@@ -101,6 +102,12 @@ public class AccessibilityDetailsSettingsFragment extends InstrumentedFragment {
arguments, getContext());
return new LaunchFragmentArguments(destination, arguments);
}
if (ACCESSIBILITY_BUTTON_COMPONENT_NAME.equals(componentName)) {
final String destination = AccessibilityButtonFragment.class.getName();
return new LaunchFragmentArguments(destination, /* arguments= */ null);
}
return null;
}

View File

@@ -70,45 +70,32 @@ public class AccessibilityEditDialogUtils {
@Retention(RetentionPolicy.SOURCE)
@IntDef({
DialogType.EDIT_SHORTCUT_GENERIC,
DialogType.EDIT_SHORTCUT_GENERIC_SUW,
DialogType.EDIT_SHORTCUT_MAGNIFICATION,
DialogType.EDIT_SHORTCUT_MAGNIFICATION_SUW,
DialogType.EDIT_MAGNIFICATION_SWITCH_SHORTCUT,
})
private @interface DialogType {
public @interface DialogType {
int EDIT_SHORTCUT_GENERIC = 0;
int EDIT_SHORTCUT_MAGNIFICATION = 1;
int EDIT_MAGNIFICATION_SWITCH_SHORTCUT = 2;
int EDIT_SHORTCUT_GENERIC_SUW = 1;
int EDIT_SHORTCUT_MAGNIFICATION = 2;
int EDIT_SHORTCUT_MAGNIFICATION_SUW = 3;
int EDIT_MAGNIFICATION_SWITCH_SHORTCUT = 4;
}
/**
* Method to show the edit shortcut dialog.
*
* @param context A valid context
* @param dialogType The type of edit shortcut dialog
* @param dialogTitle The title of edit shortcut dialog
* @param listener The listener to determine the action of edit shortcut dialog
* @return A edit shortcut dialog for showing
*/
public static AlertDialog showEditShortcutDialog(Context context, CharSequence dialogTitle,
DialogInterface.OnClickListener listener) {
final AlertDialog alertDialog = createDialog(context, DialogType.EDIT_SHORTCUT_GENERIC,
dialogTitle, listener);
alertDialog.show();
setScrollIndicators(alertDialog);
return alertDialog;
}
/**
* Method to show the edit shortcut dialog in Magnification.
*
* @param context A valid context
* @param dialogTitle The title of edit shortcut dialog
* @param listener The listener to determine the action of edit shortcut dialog
* @return A edit shortcut dialog for showing in Magnification
*/
public static AlertDialog showMagnificationEditShortcutDialog(Context context,
public static AlertDialog showEditShortcutDialog(Context context, int dialogType,
CharSequence dialogTitle, DialogInterface.OnClickListener listener) {
final AlertDialog alertDialog = createDialog(context,
DialogType.EDIT_SHORTCUT_MAGNIFICATION, dialogTitle, listener);
final AlertDialog alertDialog = createDialog(context, dialogType, dialogTitle, listener);
alertDialog.show();
setScrollIndicators(alertDialog);
return alertDialog;
@@ -241,6 +228,12 @@ public class AccessibilityEditDialogUtils {
initSoftwareShortcut(context, contentView);
initHardwareShortcut(context, contentView);
break;
case DialogType.EDIT_SHORTCUT_GENERIC_SUW:
contentView = inflater.inflate(
R.layout.accessibility_edit_shortcut, null);
initSoftwareShortcutForSUW(context, contentView);
initHardwareShortcut(context, contentView);
break;
case DialogType.EDIT_SHORTCUT_MAGNIFICATION:
contentView = inflater.inflate(
R.layout.accessibility_edit_shortcut_magnification, null);
@@ -249,6 +242,14 @@ public class AccessibilityEditDialogUtils {
initMagnifyShortcut(context, contentView);
initAdvancedWidget(contentView);
break;
case DialogType.EDIT_SHORTCUT_MAGNIFICATION_SUW:
contentView = inflater.inflate(
R.layout.accessibility_edit_shortcut_magnification, null);
initSoftwareShortcutForSUW(context, contentView);
initHardwareShortcut(context, contentView);
initMagnifyShortcut(context, contentView);
initAdvancedWidget(contentView);
break;
case DialogType.EDIT_MAGNIFICATION_SWITCH_SHORTCUT:
contentView = inflater.inflate(
R.layout.accessibility_edit_magnification_shortcut, null);
@@ -278,6 +279,18 @@ public class AccessibilityEditDialogUtils {
image.setImageResource(imageResId);
}
private static void initSoftwareShortcutForSUW(Context context, View view) {
final View dialogView = view.findViewById(R.id.software_shortcut);
final CharSequence title = context.getText(
R.string.accessibility_shortcut_edit_dialog_title_software);
final TextView summary = dialogView.findViewById(R.id.summary);
final int lineHeight = summary.getLineHeight();
setupShortcutWidget(dialogView, title,
retrieveSoftwareShortcutSummaryForSUW(context, lineHeight),
retrieveSoftwareShortcutImageResId(context));
}
private static void initSoftwareShortcut(Context context, View view) {
final View dialogView = view.findViewById(R.id.software_shortcut);
final CharSequence title = context.getText(
@@ -285,7 +298,8 @@ public class AccessibilityEditDialogUtils {
final TextView summary = dialogView.findViewById(R.id.summary);
final int lineHeight = summary.getLineHeight();
setupShortcutWidget(dialogView, title, retrieveSummary(context, lineHeight),
setupShortcutWidget(dialogView, title,
retrieveSoftwareShortcutSummary(context, lineHeight),
retrieveSoftwareShortcutImageResId(context));
}
@@ -320,7 +334,16 @@ public class AccessibilityEditDialogUtils {
});
}
private static CharSequence retrieveSummary(Context context, int lineHeight) {
private static CharSequence retrieveSoftwareShortcutSummaryForSUW(Context context,
int lineHeight) {
final SpannableStringBuilder sb = new SpannableStringBuilder();
if (!AccessibilityUtil.isFloatingMenuEnabled(context)) {
sb.append(getSummaryStringWithIcon(context, lineHeight));
}
return sb;
}
private static CharSequence retrieveSoftwareShortcutSummary(Context context, int lineHeight) {
final SpannableStringBuilder sb = new SpannableStringBuilder();
if (!AccessibilityUtil.isFloatingMenuEnabled(context)) {
sb.append(getSummaryStringWithIcon(context, lineHeight));

View File

@@ -17,6 +17,8 @@
package com.android.settings.accessibility;
import android.content.Context;
import android.content.Intent;
import android.text.SpannableStringBuilder;
import android.text.TextUtils;
import android.text.method.LinkMovementMethod;
import android.util.AttributeSet;
@@ -25,7 +27,9 @@ import android.widget.TextView;
import androidx.preference.PreferenceViewHolder;
import com.android.settingslib.R;
import com.android.settings.R;
import com.android.settings.utils.AnnotationSpan;
import com.android.settingslib.HelpUtils;
import com.android.settingslib.widget.FooterPreference;
/**
@@ -106,4 +110,23 @@ public final class AccessibilityFooterPreference extends FooterPreference {
public boolean isLinkEnabled() {
return mLinkEnabled;
}
/**
* Appends {@link AnnotationSpan} with learn more link apart from the other text.
*
* @param helpLinkRes The Help Uri Resource key
*/
public void appendHelpLink(int helpLinkRes) {
final SpannableStringBuilder sb = new SpannableStringBuilder();
sb.append(getTitle()).append("\n\n").append(getLearnMoreLink(getContext(), helpLinkRes));
setTitle(sb);
}
private CharSequence getLearnMoreLink(Context context, int helpLinkRes) {
final Intent helpIntent = HelpUtils.getHelpIntent(
context, context.getString(helpLinkRes), context.getClass().getName());
final AnnotationSpan.LinkInfo linkInfo = new AnnotationSpan.LinkInfo(
context, AnnotationSpan.LinkInfo.DEFAULT_ANNOTATION, helpIntent);
return AnnotationSpan.linkify(context.getText(R.string.footer_learn_more), linkInfo);
}
}

View File

@@ -46,6 +46,14 @@ public abstract class AccessibilityFooterPreferenceController extends BasePrefer
updateFooterPreferences(footerPreference);
}
/**
* Override this if showing a help item in the footer bar, by returning the resource id.
*
* @return the resource id for the help url
*/
protected int getHelpResource() {
return 0;
}
/** Returns the accessibility feature name. */
protected abstract String getLabelName();
@@ -54,5 +62,10 @@ public abstract class AccessibilityFooterPreferenceController extends BasePrefer
final String iconContentDescription = mContext.getString(
R.string.accessibility_introduction_title, getLabelName());
footerPreference.setIconContentDescription(iconContentDescription);
if (getHelpResource() != 0) {
footerPreference.appendHelpLink(getHelpResource());
footerPreference.setLinkEnabled(true);
}
}
}

View File

@@ -33,4 +33,9 @@ public class CaptionFooterPreferenceController extends AccessibilityFooterPrefer
protected String getLabelName() {
return mContext.getString(R.string.accessibility_captioning_title);
}
@Override
protected int getHelpResource() {
return R.string.help_url_caption;
}
}

View File

@@ -34,4 +34,9 @@ public class ToggleAutoclickFooterPreferenceController extends
protected String getLabelName() {
return mContext.getString(R.string.accessibility_autoclick_preference_title);
}
@Override
protected int getHelpResource() {
return R.string.help_url_autoclick;
}
}

View File

@@ -103,8 +103,8 @@ public final class ToggleDaltonizerPreferenceFragment extends ToggleFeaturePrefe
/** Customizes the order by preference key. */
protected List<String> getPreferenceOrderList() {
final List<String> lists = new ArrayList<>();
lists.add(KEY_USE_SERVICE_PREFERENCE);
lists.add(KEY_PREVIEW);
lists.add(KEY_USE_SERVICE_PREFERENCE);
lists.add(KEY_CATEGORY_MODE);
lists.add(KEY_GENERAL_CATEGORY);
lists.add(KEY_HTML_DESCRIPTION_PREFERENCE);

View File

@@ -50,12 +50,15 @@ import androidx.preference.PreferenceScreen;
import com.android.settings.R;
import com.android.settings.SettingsActivity;
import com.android.settings.SettingsPreferenceFragment;
import com.android.settings.accessibility.AccessibilityEditDialogUtils.DialogType;
import com.android.settings.accessibility.AccessibilityUtil.UserShortcutType;
import com.android.settings.widget.SettingsMainSwitchBar;
import com.android.settings.widget.SettingsMainSwitchPreference;
import com.android.settingslib.accessibility.AccessibilityUtils;
import com.android.settingslib.widget.OnMainSwitchChangeListener;
import com.google.android.setupcompat.util.WizardManagerHelper;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
@@ -221,8 +224,11 @@ public abstract class ToggleFeaturePreferenceFragment extends SettingsPreference
case DialogEnums.EDIT_SHORTCUT:
final CharSequence dialogTitle = getPrefContext().getString(
R.string.accessibility_shortcut_title, mPackageName);
final int dialogType = WizardManagerHelper.isAnySetupWizard(getIntent())
? DialogType.EDIT_SHORTCUT_GENERIC_SUW : DialogType.EDIT_SHORTCUT_GENERIC;
dialog = AccessibilityEditDialogUtils.showEditShortcutDialog(
getPrefContext(), dialogTitle, this::callOnAlertDialogCheckboxClicked);
getPrefContext(), dialogType, dialogTitle,
this::callOnAlertDialogCheckboxClicked);
setupEditShortcutDialog(dialog);
return dialog;
case DialogEnums.LAUNCH_ACCESSIBILITY_TUTORIAL:
@@ -303,6 +309,11 @@ public abstract class ToggleFeaturePreferenceFragment extends SettingsPreference
return SettingsEnums.ACCESSIBILITY_SERVICE;
}
@Override
public int getHelpResource() {
return 0;
}
@Override
public void onDestroyView() {
super.onDestroyView();
@@ -379,8 +390,8 @@ public abstract class ToggleFeaturePreferenceFragment extends SettingsPreference
/** Customizes the order by preference key. */
protected List<String> getPreferenceOrderList() {
final List<String> lists = new ArrayList<>();
lists.add(KEY_USE_SERVICE_PREFERENCE);
lists.add(KEY_ANIMATED_IMAGE);
lists.add(KEY_USE_SERVICE_PREFERENCE);
lists.add(KEY_GENERAL_CATEGORY);
lists.add(KEY_HTML_DESCRIPTION_PREFERENCE);
return lists;
@@ -508,7 +519,13 @@ public abstract class ToggleFeaturePreferenceFragment extends SettingsPreference
new AccessibilityFooterPreference(screen.getContext());
htmlFooterPreference.setKey(KEY_HTML_DESCRIPTION_PREFERENCE);
htmlFooterPreference.setSummary(htmlDescription);
htmlFooterPreference.setLinkEnabled(false);
// Only framework tools support help link
if (getHelpResource() != 0) {
htmlFooterPreference.appendHelpLink(getHelpResource());
htmlFooterPreference.setLinkEnabled(true);
} else {
htmlFooterPreference.setLinkEnabled(false);
}
htmlFooterPreference.setIconContentDescription(iconContentDescription);
screen.addPreference(htmlFooterPreference);
}
@@ -542,6 +559,10 @@ public abstract class ToggleFeaturePreferenceFragment extends SettingsPreference
new AccessibilityFooterPreference(screen.getContext());
footerPreference.setSummary(summary);
footerPreference.setIconContentDescription(iconContentDescription);
if (getHelpResource() != 0) {
footerPreference.appendHelpLink(getHelpResource());
footerPreference.setLinkEnabled(true);
}
screen.addPreference(footerPreference);
}

View File

@@ -44,8 +44,11 @@ import androidx.preference.PreferenceCategory;
import com.android.internal.annotations.VisibleForTesting;
import com.android.settings.DialogCreatable;
import com.android.settings.R;
import com.android.settings.accessibility.AccessibilityEditDialogUtils.DialogType;
import com.android.settings.accessibility.AccessibilityUtil.UserShortcutType;
import com.google.android.setupcompat.util.WizardManagerHelper;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
@@ -127,9 +130,11 @@ public class ToggleScreenMagnificationPreferenceFragment extends
case DialogEnums.MAGNIFICATION_EDIT_SHORTCUT:
final CharSequence dialogTitle = getPrefContext().getString(
R.string.accessibility_shortcut_title, mPackageName);
dialog = AccessibilityEditDialogUtils.showMagnificationEditShortcutDialog(
getPrefContext(), dialogTitle,
this::callOnAlertDialogCheckboxClicked);
final int dialogType = WizardManagerHelper.isAnySetupWizard(getIntent())
? DialogType.EDIT_SHORTCUT_MAGNIFICATION_SUW
: DialogType.EDIT_SHORTCUT_MAGNIFICATION;
dialog = AccessibilityEditDialogUtils.showEditShortcutDialog(getPrefContext(),
dialogType, dialogTitle, this::callOnAlertDialogCheckboxClicked);
setupMagnificationEditShortcutDialog(dialog);
return dialog;
default:
@@ -139,7 +144,6 @@ public class ToggleScreenMagnificationPreferenceFragment extends
@Override
protected void initSettingsPreference() {
// If the device doesn't support magnification area, it should hide the settings preference.
if (!getContext().getResources().getBoolean(
com.android.internal.R.bool.config_magnification_area)) {
@@ -296,6 +300,11 @@ public class ToggleScreenMagnificationPreferenceFragment extends
getShortcutTypeSummary(getPrefContext()));
}
@Override
public int getHelpResource() {
return R.string.help_url_magnification;
}
@Override
public int getMetricsCategory() {
// TODO: Distinguish between magnification modes

View File

@@ -16,26 +16,35 @@
package com.android.settings.applications;
import static android.app.usage.UsageStatsManager.INTERVAL_MONTHLY;
import static android.provider.DeviceConfig.NAMESPACE_APP_HIBERNATION;
import static com.android.settings.Utils.PROPERTY_APP_HIBERNATION_ENABLED;
import android.app.usage.UsageStats;
import android.app.usage.UsageStatsManager;
import android.apphibernation.AppHibernationManager;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.provider.DeviceConfig;
import android.util.ArrayMap;
import com.android.settings.R;
import com.android.settings.core.BasePreferenceController;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
/**
* A preference controller handling the logic for updating summary of hibernated apps.
*/
public final class HibernatedAppsPreferenceController extends BasePreferenceController {
private static final String TAG = "HibernatedAppsPrefController";
private static final String PROPERTY_HIBERNATION_UNUSED_THRESHOLD_MILLIS =
"auto_revoke_unused_threshold_millis2";
private static final long DEFAULT_UNUSED_THRESHOLD_MS = TimeUnit.DAYS.toMillis(90);
public HibernatedAppsPreferenceController(Context context, String preferenceKey) {
super(context, preferenceKey);
@@ -55,17 +64,36 @@ public final class HibernatedAppsPreferenceController extends BasePreferenceCont
}
private int getNumHibernated() {
// TODO(b/187465752): Find a way to export this logic from PermissionController module
final PackageManager pm = mContext.getPackageManager();
final AppHibernationManager ahm = mContext.getSystemService(AppHibernationManager.class);
final List<String> hibernatedPackages = ahm.getHibernatingPackagesForUser();
int numHibernated = hibernatedPackages.size();
// Also need to count packages that are auto revoked but not hibernated.
final UsageStatsManager usm = mContext.getSystemService(UsageStatsManager.class);
final long now = System.currentTimeMillis();
final long unusedThreshold = DeviceConfig.getLong(DeviceConfig.NAMESPACE_PERMISSIONS,
PROPERTY_HIBERNATION_UNUSED_THRESHOLD_MILLIS, DEFAULT_UNUSED_THRESHOLD_MS);
final List<UsageStats> usageStatsList = usm.queryUsageStats(INTERVAL_MONTHLY,
now - unusedThreshold, now);
final Map<String, UsageStats> recentlyUsedPackages = new ArrayMap<>();
for (UsageStats us : usageStatsList) {
recentlyUsedPackages.put(us.mPackageName, us);
}
final List<PackageInfo> packages = pm.getInstalledPackages(
PackageManager.MATCH_DISABLED_COMPONENTS | PackageManager.GET_PERMISSIONS);
for (PackageInfo pi : packages) {
final String packageName = pi.packageName;
if (!hibernatedPackages.contains(packageName) && pi.requestedPermissions != null) {
final UsageStats usageStats = recentlyUsedPackages.get(packageName);
// Only count packages that have not been used recently as auto-revoked permissions may
// stay revoked even after use if the user has not regranted them.
final boolean usedRecently = (usageStats != null
&& (now - usageStats.getLastTimeAnyComponentUsed() < unusedThreshold
|| now - usageStats.getLastTimeVisible() < unusedThreshold));
if (!hibernatedPackages.contains(packageName)
&& pi.requestedPermissions != null
&& !usedRecently) {
for (String perm : pi.requestedPermissions) {
if ((pm.getPermissionFlags(perm, packageName, mContext.getUser())
& PackageManager.FLAG_PERMISSION_AUTO_REVOKED) != 0) {

View File

@@ -126,7 +126,7 @@ public abstract class BiometricEnrollIntroduction extends BiometricEnrollBase
public abstract void onClick(LinkSpan span);
protected interface GenerateChallengeCallback {
void onChallengeGenerated(int sensorId, long challenge);
void onChallengeGenerated(int sensorId, int userId, long challenge);
}
@Override
@@ -261,7 +261,7 @@ public abstract class BiometricEnrollIntroduction extends BiometricEnrollBase
updatePasswordQuality();
overridePendingTransition(R.anim.sud_slide_next_in, R.anim.sud_slide_next_out);
getNextButton().setEnabled(false);
getChallenge(((sensorId, challenge) -> {
getChallenge(((sensorId, userId, challenge) -> {
mSensorId = sensorId;
mChallenge = challenge;
mToken = BiometricUtils.requestGatekeeperHat(this, data, mUserId, challenge);
@@ -277,7 +277,7 @@ public abstract class BiometricEnrollIntroduction extends BiometricEnrollBase
if (resultCode == RESULT_OK && data != null) {
overridePendingTransition(R.anim.sud_slide_next_in, R.anim.sud_slide_next_out);
getNextButton().setEnabled(false);
getChallenge(((sensorId, challenge) -> {
getChallenge(((sensorId, userId, challenge) -> {
mSensorId = sensorId;
mChallenge = challenge;
mToken = BiometricUtils.requestGatekeeperHat(this, data, mUserId, challenge);

View File

@@ -67,7 +67,7 @@ public class MultiBiometricEnrollHelper {
private void launchFaceEnroll() {
final FaceManager faceManager = mActivity.getSystemService(FaceManager.class);
faceManager.generateChallenge((sensorId, challenge) -> {
faceManager.generateChallenge(mUserId, (sensorId, userId, challenge) -> {
final byte[] hardwareAuthToken = BiometricUtils.requestGatekeeperHat(mActivity,
mGkPwHandle, mUserId, challenge);
final Intent faceIntent = BiometricUtils.getFaceIntroIntent(mActivity,
@@ -96,7 +96,7 @@ public class MultiBiometricEnrollHelper {
private void launchFingerprintEnroll() {
final FingerprintManager fingerprintManager = mActivity
.getSystemService(FingerprintManager.class);
fingerprintManager.generateChallenge(mUserId, ((sensorId, challenge) -> {
fingerprintManager.generateChallenge(mUserId, ((sensorId, userId, challenge) -> {
final byte[] hardwareAuthToken = BiometricUtils.requestGatekeeperHat(mActivity,
mGkPwHandle, mUserId, challenge);
final Intent intent = BiometricUtils.getFingerprintIntroIntent(mActivity,

View File

@@ -115,7 +115,7 @@ public abstract class BiometricsSettingsBase extends DashboardFragment {
// since FingerprintSettings and FaceSettings revoke the challenge when finishing.
if (getFacePreferenceKey().equals(key)) {
mDoNotFinishActivity = true;
mFaceManager.generateChallenge((sensorId, challenge) -> {
mFaceManager.generateChallenge(mUserId, (sensorId, userId, challenge) -> {
final byte[] token = BiometricUtils.requestGatekeeperHat(getActivity(), mGkPwHandle,
mUserId, challenge);
final Bundle extras = preference.getExtras();
@@ -125,7 +125,7 @@ public abstract class BiometricsSettingsBase extends DashboardFragment {
});
} else if (getFingerprintPreferenceKey().equals(key)) {
mDoNotFinishActivity = true;
mFingerprintManager.generateChallenge(mUserId, (sensorId, challenge) -> {
mFingerprintManager.generateChallenge(mUserId, (sensorId, userId, challenge) -> {
final byte[] token = BiometricUtils.requestGatekeeperHat(getActivity(), mGkPwHandle,
mUserId, challenge);
final Bundle extras = preference.getExtras();

View File

@@ -110,7 +110,7 @@ public class FaceEnrollIntroduction extends BiometricEnrollIntroduction {
mFooterBarMixin.getPrimaryButton().setEnabled(false);
// We either block on generateChallenge, or need to gray out the "next" button until
// the challenge is ready. Let's just do this for now.
mFaceManager.generateChallenge((sensorId, challenge) -> {
mFaceManager.generateChallenge(mUserId, (sensorId, userId, challenge) -> {
mToken = BiometricUtils.requestGatekeeperHat(this, getIntent(), mUserId, challenge);
mSensorId = sensorId;
mChallenge = challenge;
@@ -196,10 +196,10 @@ public class FaceEnrollIntroduction extends BiometricEnrollIntroduction {
protected void getChallenge(GenerateChallengeCallback callback) {
mFaceManager = Utils.getFaceManagerOrNull(this);
if (mFaceManager == null) {
callback.onChallengeGenerated(0, 0L);
callback.onChallengeGenerated(0, 0, 0L);
return;
}
mFaceManager.generateChallenge(callback::onChallengeGenerated);
mFaceManager.generateChallenge(mUserId, callback::onChallengeGenerated);
}
@Override

View File

@@ -249,7 +249,7 @@ public class FaceSettings extends DashboardFragment {
if (requestCode == CONFIRM_REQUEST) {
if (resultCode == RESULT_FINISHED || resultCode == RESULT_OK) {
// The pin/pattern/password was set.
mFaceManager.generateChallenge((sensorId, challenge) -> {
mFaceManager.generateChallenge(mUserId, (sensorId, userId, challenge) -> {
mToken = BiometricUtils.requestGatekeeperHat(getPrefContext(), data, mUserId,
challenge);
mSensorId = sensorId;

View File

@@ -81,7 +81,7 @@ public class FingerprintEnrollFindSensor extends BiometricEnrollBase implements
// adb shell am start -a android.app.action.SET_NEW_PASSWORD
if (mToken == null && BiometricUtils.containsGatekeeperPasswordHandle(getIntent())) {
final FingerprintManager fpm = getSystemService(FingerprintManager.class);
fpm.generateChallenge(mUserId, (sensorId, challenge) -> {
fpm.generateChallenge(mUserId, (sensorId, userId, challenge) -> {
mChallenge = challenge;
mSensorId = sensorId;
mToken = BiometricUtils.requestGatekeeperHat(this, getIntent(), mUserId, challenge);

View File

@@ -157,7 +157,7 @@ public class FingerprintEnrollIntroduction extends BiometricEnrollIntroduction {
protected void getChallenge(GenerateChallengeCallback callback) {
mFingerprintManager = Utils.getFingerprintManagerOrNull(this);
if (mFingerprintManager == null) {
callback.onChallengeGenerated(0, 0L);
callback.onChallengeGenerated(0, 0, 0L);
return;
}
mFingerprintManager.generateChallenge(mUserId, callback::onChallengeGenerated);

View File

@@ -601,12 +601,15 @@ public class FingerprintSettings extends SubSettings {
mLaunchedConfirm = false;
if (resultCode == RESULT_FINISHED || resultCode == RESULT_OK) {
if (data != null && BiometricUtils.containsGatekeeperPasswordHandle(data)) {
mFingerprintManager.generateChallenge(mUserId, (sensorId, challenge) -> {
mToken = BiometricUtils.requestGatekeeperHat(getActivity(), data,
mUserId, challenge);
mChallenge = challenge;
BiometricUtils.removeGatekeeperPasswordHandle(getActivity(), data);
updateAddPreference();
mFingerprintManager.generateChallenge(mUserId,
(sensorId, userId, challenge) -> {
mToken = BiometricUtils.requestGatekeeperHat(getActivity(),
data,
mUserId, challenge);
mChallenge = challenge;
BiometricUtils.removeGatekeeperPasswordHandle(getActivity(),
data);
updateAddPreference();
});
} else {
Log.d(TAG, "Data null or GK PW missing");

View File

@@ -16,6 +16,8 @@
package com.android.settings.bluetooth;
import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
import android.os.Bundle;
import androidx.fragment.app.FragmentActivity;
@@ -31,6 +33,7 @@ public final class DevicePickerActivity extends FragmentActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getWindow().addSystemFlags(SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
setContentView(R.layout.bluetooth_device_picker);
}
}

View File

@@ -20,6 +20,7 @@ import android.util.ArrayMap;
import com.android.settings.accounts.AccountDashboardFragment;
import com.android.settings.applications.manageapplications.ManageApplications;
import com.android.settings.deviceinfo.StorageDashboardFragment;
import com.android.settings.location.LocationServices;
import com.android.settings.location.RecentLocationAccessSeeAllFragment;
@@ -46,5 +47,7 @@ public class ProfileFragmentBridge {
ProfileSelectRecentLocationAccessFragment.class.getName());
FRAGMENT_MAP.put(LocationServices.class.getName(),
ProfileSelectLocationServicesFragment.class.getName());
FRAGMENT_MAP.put(StorageDashboardFragment.class.getName(),
ProfileSelectStorageFragment.class.getName());
}
}

View File

@@ -0,0 +1,47 @@
/*
* Copyright (C) 2021 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.dashboard.profileselector;
import android.os.Bundle;
import androidx.fragment.app.Fragment;
import com.android.settings.deviceinfo.StorageDashboardFragment;
/**
* Storage Settings page for personal/managed profile.
*/
public class ProfileSelectStorageFragment extends ProfileSelectFragment {
@Override
public Fragment[] getFragments() {
final Bundle workBundle = new Bundle();
workBundle.putInt(EXTRA_PROFILE, ProfileType.WORK);
final Fragment workFragment = new StorageDashboardFragment();
workFragment.setArguments(workBundle);
final Bundle personalBundle = new Bundle();
personalBundle.putInt(EXTRA_PROFILE, ProfileType.PERSONAL);
final Fragment personalFragment = new StorageDashboardFragment();
personalFragment.setArguments(personalBundle);
return new Fragment[] {
personalFragment,
workFragment
};
}
}

View File

@@ -85,6 +85,7 @@ public class StorageDashboardFragment extends DashboardFragment
private static final int VOLUME_SIZE_JOB_ID = 2;
private StorageManager mStorageManager;
private UserManager mUserManager;
private final List<StorageEntry> mStorageEntries = new ArrayList<>();
private StorageEntry mSelectedStorageEntry;
private PrivateStorageInfo mStorageInfo;
@@ -96,7 +97,8 @@ public class StorageDashboardFragment extends DashboardFragment
private StorageSelectionPreferenceController mStorageSelectionController;
private StorageUsageProgressBarPreferenceController mStorageUsageProgressBarController;
private List<AbstractPreferenceController> mSecondaryUsers;
private boolean mPersonalOnly;
private boolean mIsWorkProfile;
private int mUserId;
private Preference mFreeUpSpacePreference;
private final StorageEventListener mStorageEventListener = new StorageEventListener() {
@@ -270,8 +272,6 @@ public class StorageDashboardFragment extends DashboardFragment
final Activity activity = getActivity();
mStorageManager = activity.getSystemService(StorageManager.class);
mPersonalOnly = getArguments().getInt(ProfileSelectFragment.EXTRA_PROFILE)
== ProfileSelectFragment.ProfileType.PERSONAL;
if (icicle == null) {
final VolumeInfo specifiedVolumeInfo =
@@ -288,18 +288,19 @@ public class StorageDashboardFragment extends DashboardFragment
}
private void initializePreference() {
if (mPersonalOnly) {
final Preference summary = getPreferenceScreen().findPreference(SUMMARY_PREF_KEY);
if (summary != null) {
summary.setVisible(false);
}
}
mFreeUpSpacePreference = getPreferenceScreen().findPreference(FREE_UP_SPACE_PREF_KEY);
mFreeUpSpacePreference.setOnPreferenceClickListener(this);
}
@Override
public void onAttach(Context context) {
// These member variables are initialized befoer super.onAttach for
// createPreferenceControllers to work correctly.
mUserManager = context.getSystemService(UserManager.class);
mIsWorkProfile = getArguments().getInt(ProfileSelectFragment.EXTRA_PROFILE)
== ProfileSelectFragment.ProfileType.WORK;
mUserId = Utils.getCurrentUserId(mUserManager, mIsWorkProfile);
super.onAttach(context);
use(AutomaticStorageManagementSwitchPreferenceController.class).setFragmentManager(
getFragmentManager());
@@ -396,7 +397,7 @@ public class StorageDashboardFragment extends DashboardFragment
}
if (mAppsResult != null) {
mPreferenceController.onLoadFinished(mAppsResult, UserHandle.myUserId());
mPreferenceController.onLoadFinished(mAppsResult, mUserId);
updateSecondaryUserControllers(mSecondaryUsers, mAppsResult);
stopLoading = true;
}
@@ -427,14 +428,13 @@ public class StorageDashboardFragment extends DashboardFragment
@Override
protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
final List<AbstractPreferenceController> controllers = new ArrayList<>();
StorageManager sm = context.getSystemService(StorageManager.class);
mPreferenceController = new StorageItemPreferenceController(context, this,
null /* volume */, new StorageManagerVolumeProvider(sm));
null /* volume */, new StorageManagerVolumeProvider(sm), mIsWorkProfile);
controllers.add(mPreferenceController);
final UserManager userManager = context.getSystemService(UserManager.class);
mSecondaryUsers = SecondaryUserController.getSecondaryUserControllers(context, userManager);
mSecondaryUsers = SecondaryUserController.getSecondaryUserControllers(context,
mUserManager, mIsWorkProfile /* isWorkProfileOnly */);
controllers.addAll(mSecondaryUsers);
return controllers;
@@ -480,9 +480,10 @@ public class StorageDashboardFragment extends DashboardFragment
final UserManager userManager = context.getSystemService(UserManager.class);
final List<AbstractPreferenceController> controllers = new ArrayList<>();
controllers.add(new StorageItemPreferenceController(context, null /* host */,
null /* volume */, new StorageManagerVolumeProvider(sm)));
null /* volume */, new StorageManagerVolumeProvider(sm),
false /* isWorkProfile */));
controllers.addAll(SecondaryUserController.getSecondaryUserControllers(
context, userManager));
context, userManager, false /* isWorkProfileOnly */));
return controllers;
}
@@ -492,7 +493,7 @@ public class StorageDashboardFragment extends DashboardFragment
public Loader<SparseArray<StorageAsyncLoader.AppsStorageResult>> onCreateLoader(int id,
Bundle args) {
final Context context = getContext();
return new StorageAsyncLoader(context, context.getSystemService(UserManager.class),
return new StorageAsyncLoader(context, mUserManager,
mSelectedStorageEntry.getFsUuid(),
new StorageStatsSource(context),
context.getPackageManager());
@@ -519,7 +520,7 @@ public class StorageDashboardFragment extends DashboardFragment
metricsFeatureProvider.logClickedPreference(preference, getMetricsCategory());
metricsFeatureProvider.action(context, SettingsEnums.STORAGE_FREE_UP_SPACE_NOW);
final Intent intent = new Intent(StorageManager.ACTION_MANAGE_STORAGE);
context.startActivity(intent);
context.startActivityAsUser(intent, new UserHandle(mUserId));
return true;
}
return false;
@@ -574,16 +575,14 @@ public class StorageDashboardFragment extends DashboardFragment
}
private void initializeCacheProvider() {
mCachedStorageValuesHelper =
new CachedStorageValuesHelper(getContext(), UserHandle.myUserId());
mCachedStorageValuesHelper = new CachedStorageValuesHelper(getContext(), mUserId);
initializeCachedValues();
onReceivedSizes();
}
private void maybeCacheFreshValues() {
if (mStorageInfo != null && mAppsResult != null) {
mCachedStorageValuesHelper.cacheResult(
mStorageInfo, mAppsResult.get(UserHandle.myUserId()));
mCachedStorageValuesHelper.cacheResult(mStorageInfo, mAppsResult.get(mUserId));
}
}

View File

@@ -1,138 +0,0 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.deviceinfo;
import android.app.settings.SettingsEnums;
import android.content.Context;
import android.os.Bundle;
import android.os.UserHandle;
import android.os.UserManager;
import android.os.storage.StorageManager;
import android.os.storage.VolumeInfo;
import android.util.SparseArray;
import androidx.annotation.VisibleForTesting;
import androidx.loader.app.LoaderManager;
import androidx.loader.content.Loader;
import com.android.settings.R;
import com.android.settings.Utils;
import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.deviceinfo.storage.StorageAsyncLoader;
import com.android.settings.deviceinfo.storage.StorageAsyncLoader.AppsStorageResult;
import com.android.settings.deviceinfo.storage.StorageItemPreferenceController;
import com.android.settingslib.applications.StorageStatsSource;
import com.android.settingslib.core.AbstractPreferenceController;
import com.android.settingslib.deviceinfo.StorageManagerVolumeProvider;
import java.util.ArrayList;
import java.util.List;
/**
* StorageProfileFragment is a fragment which shows the storage results for a profile of the
* primary user.
*/
public class StorageProfileFragment extends DashboardFragment
implements LoaderManager.LoaderCallbacks<SparseArray<AppsStorageResult>> {
private static final String TAG = "StorageProfileFragment";
public static final String USER_ID_EXTRA = "userId";
private static final int APPS_JOB_ID = 0;
private VolumeInfo mVolume;
private int mUserId;
private StorageItemPreferenceController mPreferenceController;
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
final Bundle args = getArguments();
// Initialize the storage sizes that we can quickly calc.
final Context context = getActivity();
final StorageManager sm = context.getSystemService(StorageManager.class);
mVolume = Utils.maybeInitializeVolume(sm, args);
if (mVolume == null) {
getActivity().finish();
return;
}
mPreferenceController.setVolume(mVolume);
mUserId = args.getInt(USER_ID_EXTRA, UserHandle.myUserId());
mPreferenceController.setUserId(UserHandle.of(mUserId));
}
@Override
public void onResume() {
super.onResume();
getLoaderManager().initLoader(APPS_JOB_ID, Bundle.EMPTY, this);
}
@Override
public int getMetricsCategory() {
return SettingsEnums.SETTINGS_STORAGE_PROFILE;
}
@Override
protected String getLogTag() {
return TAG;
}
@Override
protected int getPreferenceScreenResId() {
return R.xml.storage_profile_fragment;
}
@Override
protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
final List<AbstractPreferenceController> controllers = new ArrayList<>();
final StorageManager sm = context.getSystemService(StorageManager.class);
mPreferenceController =
new StorageItemPreferenceController(
context,
this,
mVolume,
new StorageManagerVolumeProvider(sm),
/* isWorkProfile */ true);
controllers.add(mPreferenceController);
return controllers;
}
@Override
public Loader<SparseArray<AppsStorageResult>> onCreateLoader(int id, Bundle args) {
final Context context = getContext();
return new StorageAsyncLoader(context,
context.getSystemService(UserManager.class),
mVolume.fsUuid,
new StorageStatsSource(context),
context.getPackageManager());
}
@Override
public void onLoadFinished(Loader<SparseArray<AppsStorageResult>> loader,
SparseArray<AppsStorageResult> result) {
mPreferenceController.onLoadFinished(result, mUserId);
}
@Override
public void onLoaderReset(Loader<SparseArray<AppsStorageResult>> loader) {
}
@VisibleForTesting
void setPreferenceController(StorageItemPreferenceController controller) {
mPreferenceController = controller;
}
}

View File

@@ -63,9 +63,11 @@ public class SecondaryUserController extends AbstractPreferenceController implem
*
* @param context Context for initializing the preference controllers.
* @param userManager UserManagerWrapper for figuring out which controllers to add.
* @param isWorkProfileOnly only shows secondary users of work profile.
* (e.g., it should be true in work profile tab)
*/
public static List<AbstractPreferenceController> getSecondaryUserControllers(
Context context, UserManager userManager) {
Context context, UserManager userManager, boolean isWorkProfileOnly) {
List<AbstractPreferenceController> controllers = new ArrayList<>();
UserInfo primaryUser = userManager.getPrimaryUser();
@@ -77,7 +79,11 @@ public class SecondaryUserController extends AbstractPreferenceController implem
continue;
}
if (info == null || Utils.isProfileOf(primaryUser, info)) {
if (Utils.isProfileOf(primaryUser, info)) {
continue;
}
if (isWorkProfileOnly && !info.isManagedProfile()) {
continue;
}

View File

@@ -82,7 +82,7 @@ public class StorageAsyncLoader
}
});
for (int i = 0, userCount = infos.size(); i < userCount; i++) {
UserInfo info = infos.get(i);
final UserInfo info = infos.get(i);
result.put(info.id, getStorageResultForUser(info.id));
}
return result;
@@ -109,7 +109,7 @@ public class StorageAsyncLoader
final long dataSize = stats.getDataBytes();
final long cacheQuota = mStatsManager.getCacheQuotaBytes(mUuid, app.uid);
final long cacheBytes = stats.getCacheBytes();
long blamedSize = dataSize;
long blamedSize = dataSize + stats.getCodeBytes();
// Technically, we could overages as freeable on the storage settings screen.
// If the app is using more cache than its quota, we would accidentally subtract the
// overage from the system size (because it shows up as unused) during our attribution.
@@ -118,12 +118,11 @@ public class StorageAsyncLoader
blamedSize = blamedSize - cacheBytes + cacheQuota;
}
// This isn't quite right because it slams the first user by user id with the whole code
// size, but this ensures that we count all apps seen once.
boolean isAddCodeBytesForFirstUserId = false;
if (!mSeenPackages.contains(app.packageName)) {
isAddCodeBytesForFirstUserId = true;
blamedSize += stats.getCodeBytes();
// Code bytes may share between different profiles. To know all the duplicate code size
// and we can get a reasonable system size in StorageItemPreferenceController.
if (mSeenPackages.contains(app.packageName)) {
result.duplicateCodeSize += stats.getCodeBytes();
} else {
mSeenPackages.add(app.packageName);
}
@@ -135,9 +134,7 @@ public class StorageAsyncLoader
// TODO(b/170918505): Should revamp audio size calculation with the data
// from media provider.
result.musicAppsSize += blamedSize;
if (isAddCodeBytesForFirstUserId) {
result.musicAppsSize -= stats.getCodeBytes();
}
result.musicAppsSize -= stats.getCodeBytes();
result.otherAppsSize += blamedSize;
break;
@@ -145,9 +142,7 @@ public class StorageAsyncLoader
// TODO(b/170918505): Should revamp video size calculation with the data
// from media provider.
result.videoAppsSize += blamedSize;
if (isAddCodeBytesForFirstUserId) {
result.videoAppsSize -= stats.getCodeBytes();
}
result.videoAppsSize -= stats.getCodeBytes();
result.otherAppsSize += blamedSize;
break;
@@ -155,9 +150,7 @@ public class StorageAsyncLoader
// TODO(b/170918505): Should revamp image size calculation with the data
// from media provider.
result.photosAppsSize += blamedSize;
if (isAddCodeBytesForFirstUserId) {
result.photosAppsSize -= stats.getCodeBytes();
}
result.photosAppsSize -= stats.getCodeBytes();
result.otherAppsSize += blamedSize;
break;
@@ -194,6 +187,7 @@ public class StorageAsyncLoader
public long videoAppsSize;
public long otherAppsSize;
public long cacheSize;
public long duplicateCodeSize;
public StorageStatsSource.ExternalStorageStats externalStats;
}

View File

@@ -101,6 +101,7 @@ public class StorageItemPreferenceController extends AbstractPreferenceControlle
private static final int LAST_STORAGE_CATEGORY_PREFERENCE_ORDER = 200;
private PackageManager mPackageManager;
private UserManager mUserManager;
private final Fragment mFragment;
private final MetricsFeatureProvider mMetricsFeatureProvider;
private final StorageVolumeProvider mSvp;
@@ -134,15 +135,17 @@ public class StorageItemPreferenceController extends AbstractPreferenceControlle
private static final String AUTHORITY_MEDIA = "com.android.providers.media.documents";
public StorageItemPreferenceController(
Context context, Fragment hostFragment, VolumeInfo volume, StorageVolumeProvider svp) {
public StorageItemPreferenceController(Context context, Fragment hostFragment,
VolumeInfo volume, StorageVolumeProvider svp, boolean isWorkProfile) {
super(context);
mPackageManager = context.getPackageManager();
mUserManager = context.getSystemService(UserManager.class);
mFragment = hostFragment;
mVolume = volume;
mSvp = svp;
mIsWorkProfile = isWorkProfile;
mMetricsFeatureProvider = FeatureFactory.getFactory(context).getMetricsFeatureProvider();
mUserId = UserHandle.myUserId();
mUserId = getCurrentUserId();
mImagesUri = Uri.parse(context.getResources()
.getString(R.string.config_images_storage_category_uri));
@@ -154,14 +157,9 @@ public class StorageItemPreferenceController extends AbstractPreferenceControlle
.getString(R.string.config_documents_and_other_storage_category_uri));
}
public StorageItemPreferenceController(
Context context,
Fragment hostFragment,
VolumeInfo volume,
StorageVolumeProvider svp,
boolean isWorkProfile) {
this(context, hostFragment, volume, svp);
mIsWorkProfile = isWorkProfile;
@VisibleForTesting
int getCurrentUserId() {
return Utils.getCurrentUserId(mUserManager, mIsWorkProfile);
}
@Override
@@ -311,6 +309,9 @@ public class StorageItemPreferenceController extends AbstractPreferenceControlle
* Sets the user id for which this preference controller is handling.
*/
public void setUserId(UserHandle userHandle) {
if (mIsWorkProfile && !mUserManager.isManagedProfile(userHandle.getIdentifier())) {
throw new IllegalArgumentException("Only accept work profile userHandle");
}
mUserId = userHandle.getIdentifier();
tintPreference(mPublicStoragePreference);
@@ -359,21 +360,21 @@ public class StorageItemPreferenceController extends AbstractPreferenceControlle
public void onLoadFinished(SparseArray<StorageAsyncLoader.AppsStorageResult> result,
int userId) {
final StorageAsyncLoader.AppsStorageResult data = result.get(userId);
final StorageAsyncLoader.AppsStorageResult profileData = result.get(
Utils.getManagedProfileId(mContext.getSystemService(UserManager.class), userId));
mImagesPreference.setStorageSize(getImagesSize(data, profileData), mTotalSize);
mVideosPreference.setStorageSize(getVideosSize(data, profileData), mTotalSize);
mAudiosPreference.setStorageSize(getAudiosSize(data, profileData), mTotalSize);
mAppsPreference.setStorageSize(getAppsSize(data, profileData), mTotalSize);
mGamesPreference.setStorageSize(getGamesSize(data, profileData), mTotalSize);
mDocumentsAndOtherPreference.setStorageSize(getDocumentsAndOtherSize(data, profileData),
mImagesPreference.setStorageSize(getImagesSize(data), mTotalSize);
mVideosPreference.setStorageSize(getVideosSize(data), mTotalSize);
mAudiosPreference.setStorageSize(getAudiosSize(data), mTotalSize);
mAppsPreference.setStorageSize(getAppsSize(data), mTotalSize);
mGamesPreference.setStorageSize(getGamesSize(data), mTotalSize);
mDocumentsAndOtherPreference.setStorageSize(getDocumentsAndOtherSize(data),
mTotalSize);
mTrashPreference.setStorageSize(getTrashSize(data, profileData), mTotalSize);
mTrashPreference.setStorageSize(getTrashSize(data), mTotalSize);
if (mSystemPreference != null) {
// Everything else that hasn't already been attributed is tracked as
// belonging to system.
// TODO(b/170918505): Should revamp system size calculation with the data
// from media provider.
long attributedSize = 0;
for (int i = 0; i < result.size(); i++) {
final StorageAsyncLoader.AppsStorageResult otherData = result.valueAt(i);
@@ -385,6 +386,7 @@ public class StorageItemPreferenceController extends AbstractPreferenceControlle
+ otherData.otherAppsSize;
attributedSize += otherData.externalStats.totalBytes
- otherData.externalStats.appBytes;
attributedSize -= otherData.duplicateCodeSize;
}
final long systemSize = Math.max(TrafficStats.GB_IN_BYTES, mUsedBytes - attributedSize);
@@ -404,47 +406,28 @@ public class StorageItemPreferenceController extends AbstractPreferenceControlle
private void launchPublicStorageIntent() {
final Intent intent = mVolume.buildBrowseIntent();
if (intent != null) {
mContext.startActivity(intent);
if (intent == null) {
return;
}
mContext.startActivityAsUser(intent, new UserHandle(mUserId));
}
private void launchActivityWithUri(Uri dataUri) {
final Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(dataUri);
mContext.startActivity(intent);
mContext.startActivityAsUser(intent, new UserHandle(mUserId));
}
private long getImagesSize(StorageAsyncLoader.AppsStorageResult data,
StorageAsyncLoader.AppsStorageResult profileData) {
if (profileData != null) {
return data.photosAppsSize + data.externalStats.imageBytes
+ data.externalStats.videoBytes
+ profileData.photosAppsSize + profileData.externalStats.imageBytes
+ profileData.externalStats.videoBytes;
} else {
return data.photosAppsSize + data.externalStats.imageBytes
+ data.externalStats.videoBytes;
}
private long getImagesSize(StorageAsyncLoader.AppsStorageResult data) {
return data.photosAppsSize + data.externalStats.imageBytes + data.externalStats.videoBytes;
}
private long getVideosSize(StorageAsyncLoader.AppsStorageResult data,
StorageAsyncLoader.AppsStorageResult profileData) {
if (profileData != null) {
return data.videoAppsSize + profileData.videoAppsSize;
} else {
return data.videoAppsSize;
}
private long getVideosSize(StorageAsyncLoader.AppsStorageResult data) {
return data.videoAppsSize;
}
private long getAudiosSize(StorageAsyncLoader.AppsStorageResult data,
StorageAsyncLoader.AppsStorageResult profileData) {
if (profileData != null) {
return data.musicAppsSize + data.externalStats.audioBytes
+ profileData.musicAppsSize + profileData.externalStats.audioBytes;
} else {
return data.musicAppsSize + data.externalStats.audioBytes;
}
private long getAudiosSize(StorageAsyncLoader.AppsStorageResult data) {
return data.musicAppsSize + data.externalStats.audioBytes;
}
private void launchAppsIntent() {
@@ -463,13 +446,8 @@ public class StorageItemPreferenceController extends AbstractPreferenceControlle
Utils.launchIntent(mFragment, intent);
}
private long getAppsSize(StorageAsyncLoader.AppsStorageResult data,
StorageAsyncLoader.AppsStorageResult profileData) {
if (profileData != null) {
return data.otherAppsSize + profileData.otherAppsSize;
} else {
return data.otherAppsSize;
}
private long getAppsSize(StorageAsyncLoader.AppsStorageResult data) {
return data.otherAppsSize;
}
private void launchGamesIntent() {
@@ -486,13 +464,8 @@ public class StorageItemPreferenceController extends AbstractPreferenceControlle
Utils.launchIntent(mFragment, intent);
}
private long getGamesSize(StorageAsyncLoader.AppsStorageResult data,
StorageAsyncLoader.AppsStorageResult profileData) {
if (profileData != null) {
return data.gamesSize + profileData.gamesSize;
} else {
return data.gamesSize;
}
private long getGamesSize(StorageAsyncLoader.AppsStorageResult data) {
return data.gamesSize;
}
private Bundle getWorkAnnotatedBundle(int additionalCapacity) {
@@ -502,26 +475,12 @@ public class StorageItemPreferenceController extends AbstractPreferenceControlle
return args;
}
private long getDocumentsAndOtherSize(StorageAsyncLoader.AppsStorageResult data,
StorageAsyncLoader.AppsStorageResult profileData) {
if (profileData != null) {
return data.externalStats.totalBytes
- data.externalStats.audioBytes
- data.externalStats.videoBytes
- data.externalStats.imageBytes
- data.externalStats.appBytes
+ profileData.externalStats.totalBytes
- profileData.externalStats.audioBytes
- profileData.externalStats.videoBytes
- profileData.externalStats.imageBytes
- profileData.externalStats.appBytes;
} else {
return data.externalStats.totalBytes
- data.externalStats.audioBytes
- data.externalStats.videoBytes
- data.externalStats.imageBytes
- data.externalStats.appBytes;
}
private long getDocumentsAndOtherSize(StorageAsyncLoader.AppsStorageResult data) {
return data.externalStats.totalBytes
- data.externalStats.audioBytes
- data.externalStats.videoBytes
- data.externalStats.imageBytes
- data.externalStats.appBytes;
}
private void launchTrashIntent() {
@@ -530,12 +489,11 @@ public class StorageItemPreferenceController extends AbstractPreferenceControlle
if (intent.resolveActivity(mPackageManager) == null) {
EmptyTrashFragment.show(mFragment);
} else {
mContext.startActivity(intent);
mContext.startActivityAsUser(intent, new UserHandle(mUserId));
}
}
private long getTrashSize(StorageAsyncLoader.AppsStorageResult data,
StorageAsyncLoader.AppsStorageResult profileData) {
private long getTrashSize(StorageAsyncLoader.AppsStorageResult data) {
// TODO(170918505): Implement it.
return 0L;
}

View File

@@ -73,6 +73,10 @@ public class StorageSelectionPreferenceController extends BasePreferenceControll
Collections.sort(mStorageEntries);
mStorageEntries.addAll(storageEntries);
mStorageAdapter.addAll(storageEntries);
if (mSpinnerPreference != null) {
mSpinnerPreference.setClickable(mStorageAdapter.getCount() > 1);
}
}
/** set selected storage in the spinner. */
@@ -93,6 +97,7 @@ public class StorageSelectionPreferenceController extends BasePreferenceControll
mSpinnerPreference = screen.findPreference(getPreferenceKey());
mSpinnerPreference.setAdapter(mStorageAdapter);
mSpinnerPreference.setOnItemSelectedListener(this);
mSpinnerPreference.setClickable(mStorageAdapter.getCount() > 1);
}
@Override
@@ -100,7 +105,8 @@ public class StorageSelectionPreferenceController extends BasePreferenceControll
if (mOnItemSelectedListener == null) {
return;
}
mOnItemSelectedListener.onItemSelected(mStorageAdapter.getItem(position));
mOnItemSelectedListener.onItemSelected(
(StorageEntry) mSpinnerPreference.getSelectedItem());
}
@Override

View File

@@ -1,138 +0,0 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.deviceinfo.storage;
import android.app.settings.SettingsEnums;
import android.content.Context;
import android.content.pm.UserInfo;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.storage.VolumeInfo;
import android.util.SparseArray;
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
import com.android.internal.util.Preconditions;
import com.android.settings.Utils;
import com.android.settings.core.PreferenceControllerMixin;
import com.android.settings.core.SubSettingLauncher;
import com.android.settings.deviceinfo.StorageItemPreference;
import com.android.settings.deviceinfo.StorageProfileFragment;
import com.android.settingslib.core.AbstractPreferenceController;
/**
* Defines a {@link AbstractPreferenceController} which handles a single profile of the primary
* user.
*/
public class UserProfileController extends AbstractPreferenceController implements
PreferenceControllerMixin, StorageAsyncLoader.ResultHandler,
UserIconLoader.UserIconHandler {
private static final String PREFERENCE_KEY_BASE = "pref_profile_";
private StorageItemPreference mStoragePreference;
private UserInfo mUser;
private long mTotalSizeBytes;
private final int mPreferenceOrder;
public UserProfileController(Context context, UserInfo info, int preferenceOrder) {
super(context);
mUser = Preconditions.checkNotNull(info);
mPreferenceOrder = preferenceOrder;
}
@Override
public boolean isAvailable() {
return true;
}
@Override
public String getPreferenceKey() {
return PREFERENCE_KEY_BASE + mUser.id;
}
@Override
public void displayPreference(PreferenceScreen screen) {
mStoragePreference = new StorageItemPreference(screen.getContext());
mStoragePreference.setOrder(mPreferenceOrder);
mStoragePreference.setKey(PREFERENCE_KEY_BASE + mUser.id);
mStoragePreference.setTitle(mUser.name);
screen.addPreference(mStoragePreference);
}
@Override
public boolean handlePreferenceTreeClick(Preference preference) {
if (preference != null && mStoragePreference == preference) {
final Bundle args = new Bundle();
args.putInt(StorageProfileFragment.USER_ID_EXTRA, mUser.id);
args.putString(VolumeInfo.EXTRA_VOLUME_ID, VolumeInfo.ID_PRIVATE_INTERNAL);
new SubSettingLauncher(mContext)
.setDestination(StorageProfileFragment.class.getName())
.setArguments(args)
.setTitleText(mUser.name)
.setSourceMetricsCategory(SettingsEnums.DEVICEINFO_STORAGE)
.launch();
return true;
}
return false;
}
@Override
public void handleResult(SparseArray<StorageAsyncLoader.AppsStorageResult> stats) {
Preconditions.checkNotNull(stats);
int userId = mUser.id;
StorageAsyncLoader.AppsStorageResult result = stats.get(userId);
if (result != null) {
setSize(result.externalStats.totalBytes
+ result.otherAppsSize
+ result.videoAppsSize
+ result.musicAppsSize
+ result.gamesSize,
mTotalSizeBytes);
}
}
/**
* Sets the size for the preference using a byte count.
*/
public void setSize(long size, long totalSize) {
if (mStoragePreference != null) {
mStoragePreference.setStorageSize(size, totalSize);
}
}
public void setTotalSize(long totalSize) {
mTotalSizeBytes = totalSize;
}
@Override
public void handleUserIcons(SparseArray<Drawable> fetchedIcons) {
Drawable userIcon = fetchedIcons.get(mUser.id);
if (userIcon != null) {
mStoragePreference.setIcon(applyTint(mContext, userIcon));
}
}
private static Drawable applyTint(Context context, Drawable icon) {
icon = icon.mutate();
icon.setTintList(Utils.getColorAttr(context, android.R.attr.colorControlNormal));
return icon;
}
}

View File

@@ -0,0 +1,71 @@
/*
* Copyright (C) 2021 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.display;
import android.content.Context;
import android.os.PowerManager;
import androidx.annotation.VisibleForTesting;
import androidx.preference.PreferenceScreen;
import com.android.settings.R;
import com.android.settingslib.widget.BannerMessagePreference;
/**
* The controller of Screen attention's battery saver warning preference.
* The preference appears when Screen Attention feature is disabled by battery saver mode.
*/
public class AdaptiveSleepBatterySaverPreferenceController {
@VisibleForTesting
final BannerMessagePreference mPreference;
private final PowerManager mPowerManager;
public AdaptiveSleepBatterySaverPreferenceController(Context context) {
mPreference = new BannerMessagePreference(context);
mPreference.setTitle(R.string.ambient_camera_summary_battery_saver_on);
mPreference.setPositiveButtonText(R.string.disable_text);
mPowerManager = context.getSystemService(PowerManager.class);
mPreference.setPositiveButtonOnClickListener(p -> {
mPowerManager.setPowerSaveModeEnabled(false);
});
}
/**
* Adds the controlled preference to the provided preference screen.
*/
public void addToScreen(PreferenceScreen screen) {
screen.addPreference(mPreference);
updateVisibility();
}
/**
* Need this because all controller tests use RoboElectric. No easy way to mock this service,
* so we mock the call we need
*/
@VisibleForTesting
boolean isPowerSaveMode() {
return mPowerManager.isPowerSaveMode();
}
/**
* Refreshes the visibility of the preference.
*/
public void updateVisibility() {
mPreference.setVisible(isPowerSaveMode());
}
}

View File

@@ -28,6 +28,7 @@ import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.hardware.SensorPrivacyManager;
import android.os.PowerManager;
import android.os.UserManager;
import android.provider.Settings;
import android.service.attention.AttentionService;
@@ -53,6 +54,7 @@ public class AdaptiveSleepPreferenceController {
private final PackageManager mPackageManager;
private final Context mContext;
private final MetricsFeatureProvider mMetricsFeatureProvider;
private final PowerManager mPowerManager;
@VisibleForTesting
RestrictedSwitchPreference mPreference;
@@ -62,6 +64,20 @@ public class AdaptiveSleepPreferenceController {
mRestrictionUtils = restrictionUtils;
mMetricsFeatureProvider = FeatureFactory.getFactory(context).getMetricsFeatureProvider();
mPrivacyManager = SensorPrivacyManager.getInstance(context);
mPowerManager = context.getSystemService(PowerManager.class);
mPreference = new RestrictedSwitchPreference(context);
mPreference.setTitle(R.string.adaptive_sleep_title);
mPreference.setSummary(R.string.adaptive_sleep_description);
mPreference.setChecked(isChecked());
mPreference.setKey(PREFERENCE_KEY);
mPreference.setOnPreferenceClickListener(preference -> {
final boolean isChecked = ((RestrictedSwitchPreference) preference).isChecked();
mMetricsFeatureProvider.action(context, SettingsEnums.ACTION_SCREEN_ATTENTION_CHANGED,
isChecked);
Settings.Secure.putInt(context.getContentResolver(),
Settings.Secure.ADAPTIVE_SLEEP, isChecked ? 1 : DEFAULT_VALUE);
return true;
});
mPackageManager = context.getPackageManager();
}
@@ -87,7 +103,8 @@ public class AdaptiveSleepPreferenceController {
if (enforcedAdmin != null) {
mPreference.setDisabledByAdmin(enforcedAdmin);
} else {
mPreference.setEnabled(hasSufficientPermission(mPackageManager) && !isCameraLocked());
mPreference.setEnabled(hasSufficientPermission(mPackageManager) && !isCameraLocked()
&& !isPowerSaveMode());
}
}
@@ -114,7 +131,7 @@ public class AdaptiveSleepPreferenceController {
@VisibleForTesting
boolean isChecked() {
return hasSufficientPermission(mContext.getPackageManager()) && !isCameraLocked()
&& Settings.Secure.getInt(mContext.getContentResolver(),
&& !isPowerSaveMode() && Settings.Secure.getInt(mContext.getContentResolver(),
Settings.Secure.ADAPTIVE_SLEEP, DEFAULT_VALUE)
!= DEFAULT_VALUE;
}
@@ -128,6 +145,11 @@ public class AdaptiveSleepPreferenceController {
return mPrivacyManager.isSensorPrivacyEnabled(CAMERA);
}
@VisibleForTesting
boolean isPowerSaveMode() {
return mPowerManager.isPowerSaveMode();
}
public static int isControllerAvailable(Context context) {
return context.getResources().getBoolean(
com.android.internal.R.bool.config_adaptive_sleep_available)

View File

@@ -21,10 +21,14 @@ import static android.provider.Settings.System.SCREEN_OFF_TIMEOUT;
import android.app.admin.DevicePolicyManager;
import android.app.settings.SettingsEnums;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.Resources;
import android.graphics.drawable.Drawable;
import android.hardware.SensorPrivacyManager;
import android.os.PowerManager;
import android.os.UserHandle;
import android.provider.Settings;
import android.text.SpannableString;
@@ -73,6 +77,12 @@ public class ScreenTimeoutSettings extends RadioButtonPickerFragment implements
private FooterPreference mPrivacyPreference;
private final MetricsFeatureProvider mMetricsFeatureProvider;
private SensorPrivacyManager mPrivacyManager;
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
mAdaptiveSleepBatterySaverPreferenceController.updateVisibility();
}
};
@VisibleForTesting
Context mContext;
@@ -92,6 +102,9 @@ public class ScreenTimeoutSettings extends RadioButtonPickerFragment implements
@VisibleForTesting
AdaptiveSleepPreferenceController mAdaptiveSleepController;
@VisibleForTesting
AdaptiveSleepBatterySaverPreferenceController mAdaptiveSleepBatterySaverPreferenceController;
public ScreenTimeoutSettings() {
super();
mMetricsFeatureProvider = FeatureFactory.getFactory(getContext())
@@ -109,6 +122,13 @@ public class ScreenTimeoutSettings extends RadioButtonPickerFragment implements
context);
mAdaptiveSleepCameraStatePreferenceController =
new AdaptiveSleepCameraStatePreferenceController(context);
mAdaptiveSleepBatterySaverPreferenceController =
new AdaptiveSleepBatterySaverPreferenceController(context);
mPrivacyPreference = new FooterPreference(context);
mPrivacyPreference.setIcon(R.drawable.ic_privacy_shield_24dp);
mPrivacyPreference.setTitle(R.string.adaptive_sleep_privacy);
mPrivacyPreference.setSelectable(false);
mPrivacyPreference.setLayoutResource(R.layout.preference_footer);
mPrivacyManager = SensorPrivacyManager.getInstance(context);
mPrivacyManager.addSensorPrivacyListener(CAMERA,
(sensor, enabled) -> mAdaptiveSleepController.updatePreference());
@@ -136,7 +156,16 @@ public class ScreenTimeoutSettings extends RadioButtonPickerFragment implements
super.onStart();
mAdaptiveSleepPermissionController.updateVisibility();
mAdaptiveSleepCameraStatePreferenceController.updateVisibility();
mAdaptiveSleepBatterySaverPreferenceController.updateVisibility();
mAdaptiveSleepController.updatePreference();
mContext.registerReceiver(mReceiver,
new IntentFilter(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED));
}
@Override
public void onStop() {
super.onStop();
mContext.unregisterReceiver(mReceiver);
}
@Override
@@ -176,6 +205,7 @@ public class ScreenTimeoutSettings extends RadioButtonPickerFragment implements
if (isScreenAttentionAvailable(getContext())) {
mAdaptiveSleepPermissionController.addToScreen(screen);
mAdaptiveSleepCameraStatePreferenceController.addToScreen(screen);
mAdaptiveSleepBatterySaverPreferenceController.addToScreen(screen);
mAdaptiveSleepController.addToScreen(screen);
screen.addPreference(mPrivacyPreference);
}

View File

@@ -0,0 +1,96 @@
/*
* Copyright (C) 2021 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.display;
import static com.android.settings.display.SmartAutoRotateController.isRotationResolverServiceAvailable;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.PowerManager;
import androidx.annotation.VisibleForTesting;
import androidx.lifecycle.LifecycleObserver;
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
import com.android.settings.R;
import com.android.settings.core.BasePreferenceController;
import com.android.settingslib.core.lifecycle.events.OnStart;
import com.android.settingslib.core.lifecycle.events.OnStop;
import com.android.settingslib.widget.BannerMessagePreference;
/**
* The controller of camera based rotate battery saver warning preference. The preference appears
* when battery saver mode is enabled.
*/
public class SmartAutoRotateBatterySaverController extends BasePreferenceController implements
LifecycleObserver, OnStart, OnStop {
private Preference mPreference;
private final PowerManager mPowerManager;
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (mPreference == null) {
return;
}
mPreference.setVisible(isPowerSaveMode());
updateState(mPreference);
}
};
public SmartAutoRotateBatterySaverController(Context context, String key) {
super(context, key);
mPowerManager = context.getSystemService(PowerManager.class);
}
@VisibleForTesting
boolean isPowerSaveMode() {
return mPowerManager.isPowerSaveMode();
}
@Override
public void displayPreference(PreferenceScreen screen) {
super.displayPreference(screen);
mPreference = screen.findPreference(getPreferenceKey());
((BannerMessagePreference) mPreference)
.setPositiveButtonText(R.string.disable_text)
.setPositiveButtonOnClickListener(v -> {
mPowerManager.setPowerSaveModeEnabled(false);
});
}
@Override
public void onStart() {
mContext.registerReceiver(mReceiver,
new IntentFilter(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED));
}
@Override
public void onStop() {
mContext.unregisterReceiver(mReceiver);
}
@Override
@AvailabilityStatus
public int getAvailabilityStatus() {
return isRotationResolverServiceAvailable(mContext)
&& isPowerSaveMode() ? AVAILABLE_UNSEARCHABLE : UNSUPPORTED_ON_DEVICE;
}
}

View File

@@ -20,15 +20,19 @@ import static android.provider.Settings.Secure.CAMERA_AUTOROTATE;
import android.Manifest;
import android.app.settings.SettingsEnums;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.hardware.SensorPrivacyManager;
import android.os.PowerManager;
import android.provider.Settings;
import android.service.rotationresolver.RotationResolverService;
import android.text.TextUtils;
import androidx.lifecycle.LifecycleObserver;
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
@@ -37,15 +41,24 @@ import com.android.internal.view.RotationPolicy;
import com.android.settings.core.TogglePreferenceController;
import com.android.settings.overlay.FeatureFactory;
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
import com.android.settingslib.core.lifecycle.events.OnStart;
import com.android.settingslib.core.lifecycle.events.OnStop;
/**
* SmartAutoRotateController controls whether auto rotation is enabled
*/
public class SmartAutoRotateController extends TogglePreferenceController implements
Preference.OnPreferenceChangeListener {
Preference.OnPreferenceChangeListener, LifecycleObserver, OnStart, OnStop {
private final MetricsFeatureProvider mMetricsFeatureProvider;
private final SensorPrivacyManager mPrivacyManager;
private final PowerManager mPowerManager;
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
updateState(mPreference);
}
};
private Preference mPreference;
public SmartAutoRotateController(Context context, String preferenceKey) {
@@ -54,6 +67,7 @@ public class SmartAutoRotateController extends TogglePreferenceController implem
mPrivacyManager = SensorPrivacyManager.getInstance(context);
mPrivacyManager
.addSensorPrivacyListener(CAMERA, (sensor, enabled) -> updateState(mPreference));
mPowerManager = context.getSystemService(PowerManager.class);
}
@Override
@@ -62,7 +76,7 @@ public class SmartAutoRotateController extends TogglePreferenceController implem
return UNSUPPORTED_ON_DEVICE;
}
return !RotationPolicy.isRotationLocked(mContext) && hasSufficientPermission(mContext)
&& !isCameraLocked() ? AVAILABLE : DISABLED_DEPENDENT_SETTING;
&& !isCameraLocked() && !isPowerSaveMode() ? AVAILABLE : DISABLED_DEPENDENT_SETTING;
}
@Override
@@ -82,11 +96,27 @@ public class SmartAutoRotateController extends TogglePreferenceController implem
return mPrivacyManager.isSensorPrivacyEnabled(SensorPrivacyManager.Sensors.CAMERA);
}
@VisibleForTesting
boolean isPowerSaveMode() {
return mPowerManager.isPowerSaveMode();
}
@Override
public void onStart() {
mContext.registerReceiver(mReceiver,
new IntentFilter(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED));
}
@Override
public void onStop() {
mContext.unregisterReceiver(mReceiver);
}
@Override
public boolean isChecked() {
return hasSufficientPermission(mContext) && !isCameraLocked() && Settings.Secure.getInt(
mContext.getContentResolver(),
CAMERA_AUTOROTATE, 0) == 1;
return hasSufficientPermission(mContext) && !isCameraLocked() && !isPowerSaveMode()
&& Settings.Secure.getInt(mContext.getContentResolver(),
CAMERA_AUTOROTATE, 0) == 1;
}
@Override

View File

@@ -21,6 +21,7 @@ import static com.android.settings.display.SmartAutoRotateController.isRotationR
import android.app.settings.SettingsEnums;
import android.hardware.SensorPrivacyManager;
import android.os.Bundle;
import android.os.PowerManager;
import android.text.Html;
import android.view.LayoutInflater;
import android.view.View;
@@ -50,6 +51,7 @@ public class SmartAutoRotatePreferenceFragment extends DashboardFragment {
private RotationPolicy.RotationPolicyListener mRotationPolicyListener;
private SensorPrivacyManager mPrivacyManager;
private AutoRotateSwitchBarController mSwitchBarController;
private PowerManager mPowerManager;
private static final String FACE_SWITCH_PREFERENCE_ID = "face_based_rotate";
@Override
@@ -69,6 +71,7 @@ public class SmartAutoRotatePreferenceFragment extends DashboardFragment {
mSwitchBarController = new AutoRotateSwitchBarController(activity, switchBar,
getSettingsLifecycle());
mPrivacyManager = SensorPrivacyManager.getInstance(activity);
mPowerManager = getSystemService(PowerManager.class);
final Preference footerPreference = findPreference(FooterPreference.KEY_FOOTER);
if (footerPreference != null) {
footerPreference.setTitle(Html.fromHtml(getString(R.string.smart_rotate_text_headline),
@@ -89,9 +92,10 @@ public class SmartAutoRotatePreferenceFragment extends DashboardFragment {
final boolean isLocked = RotationPolicy.isRotationLocked(getContext());
final boolean isCameraLocked = mPrivacyManager.isSensorPrivacyEnabled(
SensorPrivacyManager.Sensors.CAMERA);
final boolean isBatterySaver = mPowerManager.isPowerSaveMode();
final Preference preference = findPreference(FACE_SWITCH_PREFERENCE_ID);
if (preference != null && hasSufficientPermission(getContext())) {
preference.setEnabled(!isLocked && !isCameraLocked);
preference.setEnabled(!isLocked && !isCameraLocked && !isBatterySaver);
}
}
};

View File

@@ -17,7 +17,9 @@
package com.android.settings.fuelgauge;
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.text.TextUtils;
@@ -36,8 +38,10 @@ import com.android.settings.core.PreferenceControllerMixin;
import com.android.settingslib.core.AbstractPreferenceController;
import com.android.settingslib.core.lifecycle.Lifecycle;
import com.android.settingslib.core.lifecycle.LifecycleObserver;
import com.android.settingslib.core.lifecycle.events.OnCreate;
import com.android.settingslib.core.lifecycle.events.OnDestroy;
import com.android.settingslib.core.lifecycle.events.OnPause;
import com.android.settingslib.core.lifecycle.events.OnResume;
import com.android.settingslib.core.lifecycle.events.OnSaveInstanceState;
import com.android.settingslib.utils.StringUtil;
import java.time.Clock;
@@ -50,8 +54,9 @@ import java.util.Map;
/** Controls the update for chart graph and the list items. */
public class BatteryChartPreferenceController extends AbstractPreferenceController
implements PreferenceControllerMixin, LifecycleObserver, OnPause, OnDestroy,
BatteryChartView.OnSelectListener, ExpandDividerPreference.OnExpandListener {
implements PreferenceControllerMixin, LifecycleObserver, OnCreate, OnDestroy,
OnSaveInstanceState, BatteryChartView.OnSelectListener, OnResume,
ExpandDividerPreference.OnExpandListener {
private static final String TAG = "BatteryChartPreferenceController";
/** Desired battery history size for timestamp slots. */
public static final int DESIRED_HISTORY_SIZE = 25;
@@ -60,6 +65,12 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
private static final long VALID_USAGE_TIME_DURATION = DateUtils.HOUR_IN_MILLIS * 2;
private static final long VALID_DIFF_DURATION = DateUtils.MINUTE_IN_MILLIS * 3;
// Keys for bundle instance to restore configurations.
private static final String KEY_EXPAND_SYSTEM_INFO = "expand_system_info";
private static final String KEY_CURRENT_TIME_SLOT = "current_time_slot";
private static int sUiMode = Configuration.UI_MODE_NIGHT_UNDEFINED;
@VisibleForTesting
Map<Integer, List<BatteryDiffEntry>> mBatteryIndexedMap;
@@ -69,6 +80,7 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
@VisibleForTesting BatteryChartView mBatteryChartView;
@VisibleForTesting ExpandDividerPreference mExpandDividerPreference;
@VisibleForTesting boolean mIsExpanded = false;
@VisibleForTesting int[] mBatteryHistoryLevels;
@VisibleForTesting long[] mBatteryHistoryKeys;
@VisibleForTesting int mTrapezoidIndex = BatteryChartView.SELECTED_INDEX_INVALID;
@@ -78,8 +90,7 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
private final InstrumentedPreferenceFragment mFragment;
private final Handler mHandler = new Handler(Looper.getMainLooper());
private final CharSequence[] mNotAllowShowSummaryPackages;
private boolean mIsExpanded = false;
private final CharSequence[] mNotAllowShowEntryPackages;
// Preference cache to avoid create new instance each time.
@VisibleForTesting
@@ -97,13 +108,47 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
mPreferenceKey = preferenceKey;
mNotAllowShowSummaryPackages = context.getResources()
.getTextArray(R.array.allowlist_hide_summary_in_battery_usage);
mNotAllowShowEntryPackages = context.getResources()
.getTextArray(R.array.allowlist_hide_entry_in_battery_usage);
if (lifecycle != null) {
lifecycle.addObserver(this);
}
}
@Override
public void onPause() {
public void onCreate(Bundle savedInstanceState) {
if (savedInstanceState == null) {
return;
}
mTrapezoidIndex =
savedInstanceState.getInt(KEY_CURRENT_TIME_SLOT, mTrapezoidIndex);
mIsExpanded =
savedInstanceState.getBoolean(KEY_EXPAND_SYSTEM_INFO, mIsExpanded);
Log.d(TAG, String.format("onCreate() slotIndex=%d isExpanded=%b",
mTrapezoidIndex, mIsExpanded));
}
@Override
public void onResume() {
final int currentUiMode =
mContext.getResources().getConfiguration().uiMode
& Configuration.UI_MODE_NIGHT_MASK;
if (sUiMode != currentUiMode) {
sUiMode = currentUiMode;
BatteryDiffEntry.clearCache();
Log.d(TAG, "clear icon and label cache since uiMode is changed");
}
}
@Override
public void onSaveInstanceState(Bundle savedInstance) {
if (savedInstance == null) {
return;
}
savedInstance.putInt(KEY_CURRENT_TIME_SLOT, mTrapezoidIndex);
savedInstance.putBoolean(KEY_EXPAND_SYSTEM_INFO, mIsExpanded);
Log.d(TAG, String.format("onSaveInstanceState() slotIndex=%d isExpanded=%b",
mTrapezoidIndex, mIsExpanded));
}
@Override
@@ -154,8 +199,8 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
isValidPackage = mBatteryUtils.getPackageUid(packageName)
!= BatteryUtils.UID_NULL;
}
Log.d(TAG, String.format("handleClick() label=%s key=%s isValid:%b %s",
diffEntry.getAppLabel(), histEntry.getKey(), isValidPackage, packageName));
Log.d(TAG, String.format("handleClick() label=%s key=%s isValid:%b\n%s",
diffEntry.getAppLabel(), histEntry.getKey(), isValidPackage, histEntry));
if (isValidPackage) {
AdvancedPowerUsageDetail.startBatteryDetailPage(
mActivity, mFragment, diffEntry, powerPref.getPercent(),
@@ -234,7 +279,9 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
}
void setBatteryChartView(final BatteryChartView batteryChartView) {
mHandler.post(() -> setBatteryChartViewInner(batteryChartView));
if (mBatteryChartView != batteryChartView) {
mHandler.post(() -> setBatteryChartViewInner(batteryChartView));
}
}
private void setBatteryChartViewInner(final BatteryChartView batteryChartView) {
@@ -250,6 +297,7 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
: mTrapezoidIndex;
if (mBatteryChartView != null) {
mBatteryChartView.setLevels(mBatteryHistoryLevels);
mBatteryChartView.setSelectedIndex(refreshIndex);
setTimestampLabel();
}
refreshUi(refreshIndex, /*isForce=*/ true);
@@ -286,6 +334,11 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
final List<BatteryDiffEntry> appEntries = new ArrayList<>();
mSystemEntries.clear();
entries.forEach(entry -> {
final String packageName = entry.getPackageName();
if (!isValidToShowEntry(packageName)) {
Log.w(TAG, "ignore showing item:" + packageName);
return;
}
if (entry.isSystemEntry()) {
mSystemEntries.add(entry);
} else {
@@ -310,6 +363,7 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
if (mExpandDividerPreference == null) {
mExpandDividerPreference = new ExpandDividerPreference(mPrefContext);
mExpandDividerPreference.setOnExpandListener(this);
mExpandDividerPreference.setIsExpanded(mIsExpanded);
}
mExpandDividerPreference.setOrder(
mAppListPrefGroup.getPreferenceCount());
@@ -480,15 +534,14 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
return mPrefContext.getString(resourceId, timeSequence);
}
private boolean isValidToShowSummary(String packageName) {
if (mNotAllowShowSummaryPackages != null) {
for (CharSequence notAllowPackageName : mNotAllowShowSummaryPackages) {
if (TextUtils.equals(packageName, notAllowPackageName)) {
return false;
}
}
}
return true;
@VisibleForTesting
boolean isValidToShowSummary(String packageName) {
return !contains(packageName, mNotAllowShowSummaryPackages);
}
@VisibleForTesting
boolean isValidToShowEntry(String packageName) {
return !contains(packageName, mNotAllowShowEntryPackages);
}
@VisibleForTesting
@@ -522,6 +575,17 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
return builder.toString();
}
private static boolean contains(String target, CharSequence[] packageNames) {
if (target != null && packageNames != null) {
for (CharSequence packageName : packageNames) {
if (TextUtils.equals(target, packageName)) {
return true;
}
}
}
return false;
}
@VisibleForTesting
static boolean validateUsageTime(BatteryDiffEntry entry) {
final long foregroundUsageTimeInMs = entry.mForegroundUsageTimeInMs;

View File

@@ -111,8 +111,9 @@ public class BatteryHeaderPreferenceController extends BasePreferenceController
private CharSequence generateLabel(BatteryInfo info) {
if (BatteryUtils.isBatteryDefenderOn(info)) {
return null;
} else if (info.remainingLabel == null) {
// Present status independently if no remaining time
} else if (info.remainingLabel == null
|| info.batteryStatus == BatteryManager.BATTERY_STATUS_NOT_CHARGING) {
// Present status only if no remaining time or status anomalous
return info.statusLabel;
} else if (info.statusLabel != null && !info.discharging) {
// Charging state

View File

@@ -46,6 +46,7 @@ public class BatteryInfo {
public CharSequence chargeLabel;
public CharSequence remainingLabel;
public int batteryLevel;
public int batteryStatus;
public boolean discharging = true;
public boolean isOverheated;
public long remainingTimeUs = 0;
@@ -238,6 +239,8 @@ public class BatteryInfo {
== BatteryManager.BATTERY_HEALTH_OVERHEAT;
info.statusLabel = Utils.getBatteryStatus(context, batteryBroadcast);
info.batteryStatus = batteryBroadcast.getIntExtra(
BatteryManager.EXTRA_STATUS, BatteryManager.BATTERY_STATUS_UNKNOWN);
if (!info.mCharging) {
updateBatteryInfoDischarging(context, shortString, estimate, info);
} else {

View File

@@ -400,7 +400,7 @@ public class BatteryUtils {
// couldn't get estimate from cache or provider, use fallback
if (estimate == null) {
estimate = new Estimate(
PowerUtil.convertUsToMs(batteryUsageStats.getBatteryTimeRemainingMs()),
batteryUsageStats.getBatteryTimeRemainingMs(),
false /* isBasedOnUsage */,
EstimateKt.AVERAGE_TIME_TO_DISCHARGE_UNKNOWN);
}

View File

@@ -370,7 +370,6 @@ public class NetworkProviderSettings extends RestrictedSettingsFragment
}
}
};
registerForContextMenu(getListView());
setHasOptionsMenu(true);
if (savedInstanceState != null) {

View File

@@ -43,6 +43,7 @@ import com.android.settings.network.telephony.NetworkProviderWorker;
import com.android.settings.slices.CustomSliceable;
import com.android.settings.slices.SliceBackgroundWorker;
import com.android.settings.slices.SliceBuilderUtils;
import com.android.settings.wifi.WifiUtils;
import com.android.settings.wifi.slice.WifiSlice;
import com.android.settings.wifi.slice.WifiSliceItem;
import com.android.wifitrackerlib.WifiEntry;
@@ -255,7 +256,8 @@ public class ProviderModelSlice extends WifiSlice {
final @ColorInt int tint = Utils.getColorAttrDefaultColor(mContext,
android.R.attr.colorControlNormal);
final Drawable drawable = mContext.getDrawable(
Utils.getWifiIconResource(wifiSliceItem.getLevel()));
WifiUtils.getInternetIconResource(
wifiSliceItem.getLevel(), wifiSliceItem.shouldShowXLevelIcon()));
drawable.setTint(tint);
return Utils.createIconWithDrawable(drawable);
}

View File

@@ -29,7 +29,6 @@ import android.os.UserManager;
import android.service.notification.NotifyingApp;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.IconDrawableFactory;
import android.util.Slog;
@@ -56,8 +55,6 @@ import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* This controller displays a list of recently used apps and a "See all" button. If there is
@@ -149,9 +146,12 @@ public class RecentNotifyingAppsPreferenceController extends AbstractPreferenceC
@VisibleForTesting
void refreshUi(Context prefContext) {
((PrimarySwitchPreference) mCategory.findPreference(KEY_PLACEHOLDER + 1)).setChecked(true);
((PrimarySwitchPreference) mCategory.findPreference(KEY_PLACEHOLDER + 2)).setChecked(true);
((PrimarySwitchPreference) mCategory.findPreference(KEY_PLACEHOLDER + 3)).setChecked(true);
for (int i = 1; i <= SHOW_RECENT_APP_COUNT; i++) {
PrimarySwitchPreference app = mCategory.findPreference(KEY_PLACEHOLDER + i);
if (app != null) {
app.setChecked(true);
}
}
ThreadUtils.postOnBackgroundThread(() -> {
reloadData();
final List<NotifyingApp> recentApps = getDisplayableRecentAppList();

View File

@@ -24,8 +24,11 @@ import android.content.pm.ApplicationInfo;
import android.content.pm.PackageItemInfo;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.os.UserHandle;
import android.os.UserManager;
import android.util.ArraySet;
import android.view.View;
import android.util.Log;
import androidx.preference.PreferenceScreen;
@@ -92,6 +95,11 @@ public class ZenAccessSettings extends EmptyTextSettings implements
}
private void reloadList() {
if (mContext.getSystemService(UserManager.class)
.isManagedProfile(UserHandle.myUserId())) {
Log.w(TAG, "DND access cannot be enabled in a work profile");
return;
}
final PreferenceScreen screen = getPreferenceScreen();
screen.removeAll();
final ArrayList<ApplicationInfo> apps = new ArrayList<>();

View File

@@ -322,11 +322,13 @@ public class InternetConnectivityPanel implements PanelContent, LifecycleObserve
final List<ScanResult> wifiList = mWifiManager.getScanResults();
if (wifiList != null && wifiList.size() != 0) {
if (mIsProgressBarVisible) {
// When the Wi-Fi scan result callback is received
// Sub-Title: Searching for networks...
mSubtitle = SUBTITLE_TEXT_SEARCHING_FOR_NETWORKS;
}
return;
}
if (mIsProgressBarVisible) {
// When the Wi-Fi scan result callback is received
// Sub-Title: Searching for networks...
mSubtitle = SUBTITLE_TEXT_SEARCHING_FOR_NETWORKS;
return;
}

View File

@@ -26,6 +26,7 @@ import android.text.TextUtils;
import androidx.preference.Preference;
import com.android.settings.R;
import com.android.settings.SettingsPreferenceFragment;
import com.android.settings.Utils;
import com.android.settings.core.SubSettingLauncher;
import com.android.settings.password.ChooseLockGeneric;
@@ -36,7 +37,7 @@ public class ChangeProfileScreenLockPreferenceController extends
private static final String KEY_UNLOCK_SET_OR_CHANGE_PROFILE = "unlock_set_or_change_profile";
public ChangeProfileScreenLockPreferenceController(Context context,
SecuritySettings host) {
SettingsPreferenceFragment host) {
super(context, host);
}

View File

@@ -29,9 +29,9 @@ import com.android.settings.core.BasePreferenceController;
public class EncryptionStatusPreferenceController extends BasePreferenceController {
static final String PREF_KEY_ENCRYPTION_DETAIL_PAGE =
public static final String PREF_KEY_ENCRYPTION_DETAIL_PAGE =
"encryption_and_credentials_encryption_status";
static final String PREF_KEY_ENCRYPTION_SECURITY_PAGE = "encryption_and_credential";
public static final String PREF_KEY_ENCRYPTION_SECURITY_PAGE = "encryption_and_credential";
private final UserManager mUserManager;

View File

@@ -33,6 +33,7 @@ import androidx.preference.PreferenceScreen;
import com.android.internal.widget.LockPatternUtils;
import com.android.internal.widget.LockscreenCredential;
import com.android.settings.R;
import com.android.settings.SettingsPreferenceFragment;
import com.android.settings.Utils;
import com.android.settings.core.PreferenceControllerMixin;
import com.android.settings.core.SubSettingLauncher;
@@ -67,7 +68,7 @@ public class LockUnificationPreferenceController extends AbstractPreferenceContr
private final DevicePolicyManager mDpm;
private final LockPatternUtils mLockPatternUtils;
private final int mProfileUserId;
private final SecuritySettings mHost;
private final SettingsPreferenceFragment mHost;
private RestrictedSwitchPreference mUnifyProfile;
@@ -82,7 +83,7 @@ public class LockUnificationPreferenceController extends AbstractPreferenceContr
mUnifyProfile = screen.findPreference(KEY_UNIFICATION);
}
public LockUnificationPreferenceController(Context context, SecuritySettings host) {
public LockUnificationPreferenceController(Context context, SettingsPreferenceFragment host) {
super(context);
mHost = host;
mUm = context.getSystemService(UserManager.class);
@@ -175,7 +176,10 @@ public class LockUnificationPreferenceController extends AbstractPreferenceContr
.launch();
}
void startUnification() {
/**
* Unify primary and profile locks.
*/
public void startUnification() {
// Confirm profile lock
final String title = mContext.getString(
R.string.unlock_set_unlock_launch_picker_title_profile);

View File

@@ -31,11 +31,11 @@ import androidx.preference.PreferenceScreen;
import com.android.internal.widget.LockPatternUtils;
import com.android.settings.R;
import com.android.settings.SettingsPreferenceFragment;
import com.android.settings.core.PreferenceControllerMixin;
import com.android.settings.overlay.FeatureFactory;
import com.android.settings.password.ChooseLockSettingsHelper;
import com.android.settings.security.SecurityFeatureProvider;
import com.android.settings.security.SecuritySettings;
import com.android.settingslib.RestrictedPreference;
import com.android.settingslib.core.AbstractPreferenceController;
import com.android.settingslib.core.lifecycle.Lifecycle;
@@ -61,7 +61,7 @@ public class TrustAgentListPreferenceController extends AbstractPreferenceContro
private final LockPatternUtils mLockPatternUtils;
private final TrustAgentManager mTrustAgentManager;
private final SecuritySettings mHost;
private final SettingsPreferenceFragment mHost;
private Intent mTrustAgentClickIntent;
private PreferenceCategory mSecurityCategory;
@@ -69,7 +69,7 @@ public class TrustAgentListPreferenceController extends AbstractPreferenceContro
@VisibleForTesting
final List<String> mTrustAgentsKeyList;
public TrustAgentListPreferenceController(Context context, SecuritySettings host,
public TrustAgentListPreferenceController(Context context, SettingsPreferenceFragment host,
Lifecycle lifecycle) {
super(context);
final SecurityFeatureProvider provider = FeatureFactory.getFactory(context)

View File

@@ -829,13 +829,14 @@ public class UserSettings extends SettingsPreferenceFragment
/**
* Erase the current user (guest) and switch to another user.
*/
private void exitGuest() {
@VisibleForTesting
void exitGuest() {
// Just to be safe
mMetricsFeatureProvider.action(getActivity(),
SettingsEnums.ACTION_USER_GUEST_EXIT_CONFIRMED);
if (!isCurrentUserGuest()) {
return;
}
mMetricsFeatureProvider.action(getActivity(),
SettingsEnums.ACTION_USER_GUEST_EXIT_CONFIRMED);
removeThisUser();
}

View File

@@ -35,7 +35,8 @@ import com.android.wifitrackerlib.WifiEntry;
import java.nio.charset.StandardCharsets;
public class WifiUtils {
/** A utility class for Wi-Fi functions. */
public class WifiUtils extends com.android.settingslib.wifi.WifiUtils {
private static final int SSID_ASCII_MIN_LENGTH = 1;
private static final int SSID_ASCII_MAX_LENGTH = 32;

View File

@@ -49,6 +49,7 @@ import com.android.settings.slices.SliceBackgroundWorker;
import com.android.settings.slices.SliceBuilderUtils;
import com.android.settings.wifi.WifiDialogActivity;
import com.android.settings.wifi.WifiSettings;
import com.android.settings.wifi.WifiUtils;
import com.android.settings.wifi.details2.WifiNetworkDetailsFragment2;
import com.android.wifitrackerlib.WifiEntry;
@@ -176,7 +177,8 @@ public class WifiSlice implements CustomSliceable {
}
final Drawable drawable = mContext.getDrawable(
com.android.settingslib.Utils.getWifiIconResource(wifiSliceItem.getLevel()));
WifiUtils.getInternetIconResource(wifiSliceItem.getLevel(),
wifiSliceItem.shouldShowXLevelIcon()));
drawable.setTint(tint);
return Utils.createIconWithDrawable(drawable);
}

View File

@@ -34,6 +34,7 @@ public class WifiSliceItem {
private final int mSecurity;
private final int mConnectedState;
private final int mLevel;
private final boolean mShouldShowXLevelIcon;
private final boolean mShouldEditBeforeConnect;
private final String mSummary;
@@ -53,6 +54,7 @@ public class WifiSliceItem {
mSecurity = wifiEntry.getSecurity();
mConnectedState = wifiEntry.getConnectedState();
mLevel = wifiEntry.getLevel();
mShouldShowXLevelIcon = wifiEntry.shouldShowXLevelIcon();
mShouldEditBeforeConnect = wifiEntry.shouldEditBeforeConnect();
mSummary = wifiEntry.getSummary(false /* concise */);
}
@@ -73,6 +75,9 @@ public class WifiSliceItem {
if (getLevel() != otherItem.getLevel()) {
return false;
}
if (shouldShowXLevelIcon() != otherItem.shouldShowXLevelIcon()) {
return false;
}
if (!TextUtils.equals(getSummary(), otherItem.getSummary())) {
return false;
}
@@ -99,6 +104,13 @@ public class WifiSliceItem {
return mLevel;
}
/**
* Returns whether the level icon for this network should show an X or not.
*/
public boolean shouldShowXLevelIcon() {
return mShouldShowXLevelIcon;
}
/**
* In Wi-Fi picker, when users click a saved network, it will connect to the Wi-Fi network.
* However, for some special cases, Wi-Fi picker should show Wi-Fi editor UI for users to edit

View File

@@ -36,9 +36,7 @@ com.android.settings.datausage.DataUsageList
com.android.settings.datausage.DataUsageSummary
com.android.settings.datetime.timezone.TimeZoneSettings
com.android.settings.development.compat.PlatformCompatDashboard
com.android.settings.deviceinfo.PrivateVolumeSettings
com.android.settings.deviceinfo.PublicVolumeSettings
com.android.settings.deviceinfo.StorageProfileFragment
com.android.settings.deviceinfo.legal.ModuleLicensesDashboard
com.android.settings.enterprise.ApplicationListFragment$AdminGrantedPermissionCamera
com.android.settings.enterprise.ApplicationListFragment$AdminGrantedPermissionLocation

View File

@@ -16,6 +16,7 @@
package com.android.settings.accessibility;
import static com.android.internal.accessibility.AccessibilityShortcutController.ACCESSIBILITY_BUTTON_COMPONENT_NAME;
import static com.android.internal.accessibility.AccessibilityShortcutController.MAGNIFICATION_COMPONENT_NAME;
import static com.google.common.truth.Truth.assertThat;
@@ -85,7 +86,18 @@ public class AccessibilityDetailsSettingsFragmentTest {
}
@Test
public void onCreate_hasValidExtraComponentName_launchExpectedFragmentAndFinish() {
public void onCreate_afterSuccessfullyLaunch_shouldBeFinished() {
final Intent intent = new Intent();
intent.putExtra(Intent.EXTRA_COMPONENT_NAME, COMPONENT_NAME);
doReturn(intent).when(mActivity).getIntent();
mFragment.onCreate(Bundle.EMPTY);
verify(mActivity).finish();
}
@Test
public void onCreate_hasValidExtraComponentName_launchExpectedFragment() {
final Intent intent = new Intent();
intent.putExtra(Intent.EXTRA_COMPONENT_NAME, COMPONENT_NAME);
doReturn(intent).when(mActivity).getIntent();
@@ -94,11 +106,10 @@ public class AccessibilityDetailsSettingsFragmentTest {
assertStartActivityWithExpectedFragment(mActivity,
ToggleAccessibilityServicePreferenceFragment.class.getName());
verify(mActivity).finish();
}
@Test
public void onCreate_hasInvalidExtraComponentName_launchAccessibilitySettingsAndFinish() {
public void onCreate_hasInvalidExtraComponentName_launchAccessibilitySettings() {
final Intent intent = new Intent();
intent.putExtra(Intent.EXTRA_COMPONENT_NAME, PACKAGE_NAME + "/.service");
doReturn(intent).when(mActivity).getIntent();
@@ -106,22 +117,20 @@ public class AccessibilityDetailsSettingsFragmentTest {
mFragment.onCreate(Bundle.EMPTY);
assertStartActivityWithExpectedFragment(mActivity, AccessibilitySettings.class.getName());
verify(mActivity).finish();
}
@Test
public void onCreate_hasNoExtraComponentName_launchAccessibilitySettingsAndFinish() {
public void onCreate_hasNoExtraComponentName_launchAccessibilitySettings() {
final Intent intent = new Intent();
doReturn(intent).when(mActivity).getIntent();
mFragment.onCreate(Bundle.EMPTY);
assertStartActivityWithExpectedFragment(mActivity, AccessibilitySettings.class.getName());
verify(mActivity).finish();
}
@Test
public void onCreate_extraComponentNameIsDisallowed_launchAccessibilitySettingsAndFinish() {
public void onCreate_extraComponentNameIsDisallowed_launchAccessibilitySettings() {
final Intent intent = new Intent();
intent.putExtra(Intent.EXTRA_COMPONENT_NAME, COMPONENT_NAME);
doReturn(intent).when(mActivity).getIntent();
@@ -130,11 +139,10 @@ public class AccessibilityDetailsSettingsFragmentTest {
mFragment.onCreate(Bundle.EMPTY);
assertStartActivityWithExpectedFragment(mActivity, AccessibilitySettings.class.getName());
verify(mActivity).finish();
}
@Test
public void onCreate_magnificationComponentName_launchMagnificationFragmentAndFinish() {
public void onCreate_magnificationComponentName_launchMagnificationFragment() {
final Intent intent = new Intent();
intent.putExtra(Intent.EXTRA_COMPONENT_NAME,
MAGNIFICATION_COMPONENT_NAME.flattenToString());
@@ -145,7 +153,19 @@ public class AccessibilityDetailsSettingsFragmentTest {
assertStartActivityWithExpectedFragment(mActivity,
ToggleScreenMagnificationPreferenceFragment.class.getName());
verify(mActivity).finish();
}
@Test
public void onCreate_accessibilityButton_launchAccessibilityButtonFragment() {
final Intent intent = new Intent();
intent.putExtra(Intent.EXTRA_COMPONENT_NAME,
ACCESSIBILITY_BUTTON_COMPONENT_NAME.flattenToString());
doReturn(intent).when(mActivity).getIntent();
mFragment.onCreate(Bundle.EMPTY);
assertStartActivityWithExpectedFragment(mActivity,
AccessibilityButtonFragment.class.getName());
}
private AccessibilityServiceInfo getMockAccessibilityServiceInfo() {

View File

@@ -25,8 +25,9 @@ import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.preference.PreferenceViewHolder;
import androidx.test.core.app.ApplicationProvider;
import com.android.settingslib.R;
import com.android.settings.R;
import org.junit.Before;
import org.junit.Test;
@@ -41,6 +42,7 @@ public final class AccessibilityFooterPreferenceTest {
private static final String DEFAULT_SUMMARY = "default summary";
private static final String DEFAULT_DESCRIPTION = "default description";
private Context mContext = ApplicationProvider.getApplicationContext();
private AccessibilityFooterPreference mAccessibilityFooterPreference;
private PreferenceViewHolder mPreferenceViewHolder;
@@ -83,4 +85,14 @@ public final class AccessibilityFooterPreferenceTest {
assertThat(infoFrame.getContentDescription()).isEqualTo(DEFAULT_DESCRIPTION);
assertThat(infoFrame.isFocusable()).isEqualTo(false);
}
@Test
public void appendHelpLink_timeoutHelpUri_updateSummary() {
mAccessibilityFooterPreference.setSummary(DEFAULT_SUMMARY);
mAccessibilityFooterPreference.appendHelpLink(R.string.help_url_timeout);
final String title = mAccessibilityFooterPreference.getTitle().toString();
assertThat(title.contains(mContext.getString(R.string.footer_learn_more))).isTrue();
}
}

View File

@@ -43,6 +43,7 @@ import androidx.preference.PreferenceScreen;
import androidx.test.core.app.ApplicationProvider;
import com.android.settings.R;
import com.android.settings.accessibility.AccessibilityEditDialogUtils.DialogType;
import com.android.settings.accessibility.AccessibilityUtil.UserShortcutType;
import com.android.settings.testutils.shadow.ShadowFragment;
@@ -145,7 +146,8 @@ public class ToggleFeaturePreferenceFragmentTest {
public void setupEditShortcutDialog_shortcutPreferenceOff_checkboxIsEmptyValue() {
mContext.setTheme(R.style.Theme_AppCompat);
final AlertDialog dialog = AccessibilityEditDialogUtils.showEditShortcutDialog(
mContext, PLACEHOLDER_DIALOG_TITLE, this::callEmptyOnClicked);
mContext, DialogType.EDIT_SHORTCUT_GENERIC, PLACEHOLDER_DIALOG_TITLE,
this::callEmptyOnClicked);
final ShortcutPreference shortcutPreference = new ShortcutPreference(mContext, /* attrs= */
null);
mFragment.mComponentName = PLACEHOLDER_COMPONENT_NAME;
@@ -162,7 +164,8 @@ public class ToggleFeaturePreferenceFragmentTest {
public void setupEditShortcutDialog_shortcutPreferenceOn_checkboxIsSavedValue() {
mContext.setTheme(R.style.Theme_AppCompat);
final AlertDialog dialog = AccessibilityEditDialogUtils.showEditShortcutDialog(
mContext, PLACEHOLDER_DIALOG_TITLE, this::callEmptyOnClicked);
mContext, DialogType.EDIT_SHORTCUT_GENERIC, PLACEHOLDER_DIALOG_TITLE,
this::callEmptyOnClicked);
final ShortcutPreference shortcutPreference = new ShortcutPreference(mContext, /* attrs= */
null);
final PreferredShortcut hardwareShortcut = new PreferredShortcut(
@@ -183,7 +186,8 @@ public class ToggleFeaturePreferenceFragmentTest {
public void restoreValueFromSavedInstanceState_assignToVariable() {
mContext.setTheme(R.style.Theme_AppCompat);
final AlertDialog dialog = AccessibilityEditDialogUtils.showEditShortcutDialog(
mContext, PLACEHOLDER_DIALOG_TITLE, this::callEmptyOnClicked);
mContext, DialogType.EDIT_SHORTCUT_GENERIC, PLACEHOLDER_DIALOG_TITLE,
this::callEmptyOnClicked);
final Bundle savedInstanceState = new Bundle();
final ShortcutPreference shortcutPreference = new ShortcutPreference(mContext, /* attrs= */
null);

View File

@@ -53,6 +53,7 @@ import androidx.test.core.app.ApplicationProvider;
import com.android.settings.DialogCreatable;
import com.android.settings.R;
import com.android.settings.accessibility.AccessibilityEditDialogUtils.DialogType;
import com.android.settings.testutils.shadow.ShadowFragment;
import com.android.settings.testutils.shadow.ShadowSettingsPreferenceFragment;
import com.android.settingslib.core.lifecycle.Lifecycle;
@@ -208,8 +209,9 @@ public class ToggleScreenMagnificationPreferenceFragmentTest {
@Test
public void setupMagnificationEditShortcutDialog_shortcutPreferenceOff_checkboxIsEmptyValue() {
mContext.setTheme(R.style.Theme_AppCompat);
final AlertDialog dialog = AccessibilityEditDialogUtils.showMagnificationEditShortcutDialog(
mContext, PLACEHOLDER_DIALOG_TITLE, this::callEmptyOnClicked);
final AlertDialog dialog = AccessibilityEditDialogUtils.showEditShortcutDialog(
mContext, DialogType.EDIT_SHORTCUT_MAGNIFICATION, PLACEHOLDER_DIALOG_TITLE,
this::callEmptyOnClicked);
final ShortcutPreference shortcutPreference = new ShortcutPreference(mContext, /* attrs= */
null);
mFragment.mShortcutPreference = shortcutPreference;
@@ -224,8 +226,9 @@ public class ToggleScreenMagnificationPreferenceFragmentTest {
@Test
public void setupMagnificationEditShortcutDialog_shortcutPreferenceOn_checkboxIsSavedValue() {
mContext.setTheme(R.style.Theme_AppCompat);
final AlertDialog dialog = AccessibilityEditDialogUtils.showMagnificationEditShortcutDialog(
mContext, PLACEHOLDER_DIALOG_TITLE, this::callEmptyOnClicked);
final AlertDialog dialog = AccessibilityEditDialogUtils.showEditShortcutDialog(
mContext, DialogType.EDIT_SHORTCUT_MAGNIFICATION, PLACEHOLDER_DIALOG_TITLE,
this::callEmptyOnClicked);
final ShortcutPreference shortcutPreference = new ShortcutPreference(mContext, /* attrs= */
null);
final PreferredShortcut tripletapShortcut = new PreferredShortcut(
@@ -244,8 +247,9 @@ public class ToggleScreenMagnificationPreferenceFragmentTest {
@Config(shadows = ShadowFragment.class)
public void restoreValueFromSavedInstanceState_assignToVariable() {
mContext.setTheme(R.style.Theme_AppCompat);
final AlertDialog dialog = AccessibilityEditDialogUtils.showMagnificationEditShortcutDialog(
mContext, PLACEHOLDER_DIALOG_TITLE, this::callEmptyOnClicked);
final AlertDialog dialog = AccessibilityEditDialogUtils.showEditShortcutDialog(
mContext, DialogType.EDIT_SHORTCUT_MAGNIFICATION, PLACEHOLDER_DIALOG_TITLE,
this::callEmptyOnClicked);
final Bundle savedInstanceState = new Bundle();
mFragment.mShortcutPreference = new ShortcutPreference(mContext, /* attrs= */ null);

View File

@@ -1,72 +0,0 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License
*/
package com.android.settings.deviceinfo;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import android.util.SparseArray;
import com.android.settings.deviceinfo.storage.StorageAsyncLoader;
import com.android.settings.deviceinfo.storage.StorageItemPreferenceController;
import com.android.settingslib.applications.StorageStatsSource;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
@RunWith(RobolectricTestRunner.class)
public class StorageProfileFragmentTest {
@Captor
private ArgumentCaptor<SparseArray<StorageAsyncLoader.AppsStorageResult>> mCaptor;
@Test
public void verifyAppSizesAreNotZeroedOut() {
StorageItemPreferenceController controller = mock(StorageItemPreferenceController.class);
StorageProfileFragment fragment = new StorageProfileFragment();
StorageAsyncLoader.AppsStorageResult result = new StorageAsyncLoader.AppsStorageResult();
result.musicAppsSize = 100;
result.otherAppsSize = 200;
result.gamesSize = 300;
result.videoAppsSize = 400;
result.externalStats = new StorageStatsSource.ExternalStorageStats(6, 1, 2, 3, 0);
SparseArray<StorageAsyncLoader.AppsStorageResult> resultsArray = new SparseArray<>();
resultsArray.put(0, result);
fragment.setPreferenceController(controller);
fragment.onLoadFinished(null, resultsArray);
MockitoAnnotations.initMocks(this);
verify(controller).onLoadFinished(mCaptor.capture(), anyInt());
StorageAsyncLoader.AppsStorageResult extractedResult = mCaptor.getValue().get(0);
assertThat(extractedResult.musicAppsSize).isEqualTo(100);
assertThat(extractedResult.videoAppsSize).isEqualTo(400);
assertThat(extractedResult.otherAppsSize).isEqualTo(200);
assertThat(extractedResult.gamesSize).isEqualTo(300);
assertThat(extractedResult.externalStats.audioBytes).isEqualTo(1);
assertThat(extractedResult.externalStats.videoBytes).isEqualTo(2);
assertThat(extractedResult.externalStats.imageBytes).isEqualTo(3);
assertThat(extractedResult.externalStats.totalBytes).isEqualTo(6);
}
}

View File

@@ -22,6 +22,7 @@ import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -112,7 +113,8 @@ public class SecondaryUserControllerTest {
when(mUserManager.getPrimaryUser()).thenReturn(mPrimaryUser);
when(mUserManager.getUsers()).thenReturn(userInfos);
final List<AbstractPreferenceController> controllers =
SecondaryUserController.getSecondaryUserControllers(mContext, mUserManager);
SecondaryUserController.getSecondaryUserControllers(mContext, mUserManager,
false /* isWorkProfileOnly */);
assertThat(controllers).hasSize(1);
// We should have the NoSecondaryUserController.
@@ -122,15 +124,17 @@ public class SecondaryUserControllerTest {
@Test
public void getSecondaryUserControllers_notWorkProfile_addSecondaryUserController() {
final ArrayList<UserInfo> userInfos = new ArrayList<>();
final UserInfo secondaryUser = new UserInfo();
final UserInfo secondaryUser = spy(new UserInfo());
secondaryUser.id = 10;
secondaryUser.profileGroupId = 101010; // this just has to be something not 0
when(secondaryUser.isManagedProfile()).thenReturn(false);
userInfos.add(mPrimaryUser);
userInfos.add(secondaryUser);
when(mUserManager.getPrimaryUser()).thenReturn(mPrimaryUser);
when(mUserManager.getUsers()).thenReturn(userInfos);
final List<AbstractPreferenceController> controllers =
SecondaryUserController.getSecondaryUserControllers(mContext, mUserManager);
SecondaryUserController.getSecondaryUserControllers(mContext, mUserManager,
false /* isWorkProfileOnly */);
assertThat(controllers).hasSize(1);
assertThat(controllers.get(0) instanceof SecondaryUserController).isTrue();
@@ -139,20 +143,41 @@ public class SecondaryUserControllerTest {
@Test
public void getSecondaryUserControllers_workProfile_addNoSecondaryUserController() {
final ArrayList<UserInfo> userInfos = new ArrayList<>();
final UserInfo secondaryUser = new UserInfo();
final UserInfo secondaryUser = spy(new UserInfo());
secondaryUser.id = 10;
secondaryUser.profileGroupId = 101010; // this just has to be something not 0
when(secondaryUser.isManagedProfile()).thenReturn(true);
userInfos.add(mPrimaryUser);
userInfos.add(secondaryUser);
when(mUserManager.getPrimaryUser()).thenReturn(mPrimaryUser);
when(mUserManager.getUsers()).thenReturn(userInfos);
final List<AbstractPreferenceController> controllers =
SecondaryUserController.getSecondaryUserControllers(mContext, mUserManager);
SecondaryUserController.getSecondaryUserControllers(mContext, mUserManager,
false /* isWorkProfileOnly */);
assertThat(controllers).hasSize(1);
assertThat(controllers.get(0) instanceof SecondaryUserController).isTrue();
}
@Test
public void getSecondaryUserControllers_notWorkProfileWorkProfileOnly_addNoSecondController() {
final ArrayList<UserInfo> userInfos = new ArrayList<>();
final UserInfo secondaryUser = spy(new UserInfo());
secondaryUser.id = 10;
secondaryUser.profileGroupId = 101010; // this just has to be something not 0
when(secondaryUser.isManagedProfile()).thenReturn(false);
userInfos.add(mPrimaryUser);
userInfos.add(secondaryUser);
when(mUserManager.getPrimaryUser()).thenReturn(mPrimaryUser);
when(mUserManager.getUsers()).thenReturn(userInfos);
final List<AbstractPreferenceController> controllers =
SecondaryUserController.getSecondaryUserControllers(mContext, mUserManager,
true /* isWorkProfileOnly */);
assertThat(controllers).hasSize(1);
assertThat(controllers.get(0) instanceof SecondaryUserController).isFalse();
}
@Test
public void profilesOfPrimaryUserAreIgnored() {
final ArrayList<UserInfo> userInfos = new ArrayList<>();
@@ -164,7 +189,8 @@ public class SecondaryUserControllerTest {
when(mUserManager.getUsers()).thenReturn(userInfos);
final List<AbstractPreferenceController> controllers =
SecondaryUserController.getSecondaryUserControllers(mContext, mUserManager);
SecondaryUserController.getSecondaryUserControllers(mContext, mUserManager,
false /* isWorkProfileOnly */);
assertThat(controllers).hasSize(1);
assertThat(controllers.get(0) instanceof SecondaryUserController).isFalse();
@@ -206,7 +232,8 @@ public class SecondaryUserControllerTest {
when(mUserManager.getPrimaryUser()).thenReturn(mPrimaryUser);
when(mUserManager.getUsers()).thenReturn(userInfos);
final List<AbstractPreferenceController> controllers =
SecondaryUserController.getSecondaryUserControllers(mContext, mUserManager);
SecondaryUserController.getSecondaryUserControllers(mContext, mUserManager,
false /* isWorkProfileOnly */);
assertThat(controllers).hasSize(1);
// We should have the NoSecondaryUserController.

View File

@@ -94,7 +94,8 @@ public class StorageItemPreferenceControllerTest {
mVolume = spy(new VolumeInfo("id", 0, null, "id"));
// Note: null is passed as the Lifecycle because we are handling it outside of the normal
// Settings fragment lifecycle for test purposes.
mController = new StorageItemPreferenceController(mContext, mFragment, mVolume, mSvp);
mController = new StorageItemPreferenceController(mContext, mFragment, mVolume, mSvp,
false /* isWorkProfile */);
mPreference = new StorageItemPreference(mContext);
// Inflate the preference and the widget.
@@ -165,7 +166,7 @@ public class StorageItemPreferenceControllerTest {
mController.handlePreferenceTreeClick(mPreference);
final ArgumentCaptor<Intent> argumentCaptor = ArgumentCaptor.forClass(Intent.class);
verify(mContext).startActivity(argumentCaptor.capture());
verify(mContext).startActivityAsUser(argumentCaptor.capture(), nullable(UserHandle.class));
final Intent intent = argumentCaptor.getValue();
assertThat(intent.getAction()).isEqualTo(fakeBrowseAction);
@@ -176,11 +177,12 @@ public class StorageItemPreferenceControllerTest {
mPreference.setKey(StorageItemPreferenceController.IMAGES_KEY);
final Context mockContext = getMockContext();
mController = new StorageItemPreferenceController(mockContext, mFragment, mVolume,
mSvp);
mSvp, false /* isWorkProfile */);
mController.handlePreferenceTreeClick(mPreference);
final ArgumentCaptor<Intent> argumentCaptor = ArgumentCaptor.forClass(Intent.class);
verify(mockContext).startActivity(argumentCaptor.capture());
verify(mockContext).startActivityAsUser(argumentCaptor.capture(),
nullable(UserHandle.class));
final Intent intent = argumentCaptor.getValue();
assertThat(intent.getAction()).isEqualTo(Intent.ACTION_VIEW);
@@ -192,11 +194,12 @@ public class StorageItemPreferenceControllerTest {
mPreference.setKey(StorageItemPreferenceController.AUDIOS_KEY);
final Context mockContext = getMockContext();
mController = new StorageItemPreferenceController(mockContext, mFragment, mVolume,
mSvp);
mSvp, false /* isWorkProfile */);
mController.handlePreferenceTreeClick(mPreference);
final ArgumentCaptor<Intent> argumentCaptor = ArgumentCaptor.forClass(Intent.class);
verify(mockContext).startActivity(argumentCaptor.capture());
verify(mockContext).startActivityAsUser(argumentCaptor.capture(),
nullable(UserHandle.class));
final Intent intent = argumentCaptor.getValue();
assertThat(intent.getAction()).isEqualTo(Intent.ACTION_VIEW);
@@ -240,7 +243,8 @@ public class StorageItemPreferenceControllerTest {
@Test
public void launchAppsIntent_forWork_settingsIntent() {
mController = new StorageItemPreferenceController(mContext, mFragment, mVolume, mSvp, true);
mController = new FakeStorageItemPreferenceController(mContext, mFragment, mVolume, mSvp,
true /* isWorkProfile */);
mPreference.setKey(StorageItemPreferenceController.APPS_KEY);
mController.handlePreferenceTreeClick(mPreference);
@@ -270,11 +274,12 @@ public class StorageItemPreferenceControllerTest {
mPreference.setKey(StorageItemPreferenceController.DOCUMENTS_AND_OTHER_KEY);
final Context mockContext = getMockContext();
mController = new StorageItemPreferenceController(mockContext, mFragment, mVolume,
mSvp);
mSvp, false /* isWorkProfile */);
mController.handlePreferenceTreeClick(mPreference);
final ArgumentCaptor<Intent> argumentCaptor = ArgumentCaptor.forClass(Intent.class);
verify(mockContext).startActivity(argumentCaptor.capture());
verify(mockContext).startActivityAsUser(argumentCaptor.capture(),
nullable(UserHandle.class));
Intent intent = argumentCaptor.getValue();
assertThat(intent.getAction()).isEqualTo(Intent.ACTION_VIEW);
@@ -304,11 +309,12 @@ public class StorageItemPreferenceControllerTest {
mPreference.setKey(StorageItemPreferenceController.VIDEOS_KEY);
final Context mockContext = getMockContext();
mController = new StorageItemPreferenceController(mockContext, mFragment, mVolume,
mSvp);
mSvp, false /* isWorkProfile */);
mController.handlePreferenceTreeClick(mPreference);
final ArgumentCaptor<Intent> argumentCaptor = ArgumentCaptor.forClass(Intent.class);
verify(mockContext).startActivity(argumentCaptor.capture());
verify(mockContext).startActivityAsUser(argumentCaptor.capture(),
nullable(UserHandle.class));
Intent intent = argumentCaptor.getValue();
assertThat(intent.getAction()).isEqualTo(Intent.ACTION_VIEW);
@@ -468,4 +474,20 @@ public class StorageItemPreferenceControllerTest {
when(resources.getString(anyInt())).thenReturn("");
return context;
}
private static class FakeStorageItemPreferenceController
extends StorageItemPreferenceController {
private static final int CURRENT_USER_ID = 10;
FakeStorageItemPreferenceController(Context context, Fragment hostFragment,
VolumeInfo volume, StorageVolumeProvider svp, boolean isWorkProfile) {
super(context, hostFragment, volume, svp, isWorkProfile);
}
@Override
int getCurrentUserId() {
return CURRENT_USER_ID;
}
}
}

View File

@@ -1,138 +0,0 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.deviceinfo.storage;
import static com.android.settings.utils.FileSizeFormatter.MEGABYTE_IN_BYTES;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.pm.UserInfo;
import android.graphics.drawable.Drawable;
import android.util.SparseArray;
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
import com.android.settings.SettingsActivity;
import com.android.settings.SubSettings;
import com.android.settings.deviceinfo.StorageProfileFragment;
import com.android.settingslib.applications.StorageStatsSource;
import com.android.settingslib.drawable.UserIconDrawable;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.Robolectric;
import org.robolectric.RobolectricTestRunner;
@RunWith(RobolectricTestRunner.class)
public class UserProfileControllerTest {
private static final String TEST_NAME = "Fred";
@Mock
private PreferenceScreen mScreen;
private Context mContext;
private UserProfileController mController;
private UserInfo mPrimaryProfile;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
mContext = spy(Robolectric.setupActivity(Activity.class));
mPrimaryProfile = new UserInfo();
mController = new UserProfileController(mContext, mPrimaryProfile, 0);
when(mScreen.getContext()).thenReturn(mContext);
mPrimaryProfile.name = TEST_NAME;
mPrimaryProfile.id = 10;
mController.displayPreference(mScreen);
}
@Test
public void controllerAddsPrimaryProfilePreference() {
final ArgumentCaptor<Preference> argumentCaptor = ArgumentCaptor.forClass(Preference.class);
verify(mScreen).addPreference(argumentCaptor.capture());
final Preference preference = argumentCaptor.getValue();
assertThat(preference.getTitle()).isEqualTo(TEST_NAME);
assertThat(preference.getKey()).isEqualTo("pref_profile_10");
}
@Test
public void tappingProfilePreferenceSendsToStorageProfileFragment() {
final ArgumentCaptor<Preference> argumentCaptor = ArgumentCaptor.forClass(Preference.class);
verify(mScreen).addPreference(argumentCaptor.capture());
final Preference preference = argumentCaptor.getValue();
assertThat(mController.handlePreferenceTreeClick(preference)).isTrue();
final ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
verify(mContext).startActivity(intentCaptor.capture());
final Intent intent = intentCaptor.getValue();
assertThat(intent.getComponent().getClassName()).isEqualTo(SubSettings.class.getName());
assertThat(intent.getStringExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT))
.isEqualTo(StorageProfileFragment.class.getName());
}
@Test
public void acceptingResultUpdatesPreferenceSize() {
final SparseArray<StorageAsyncLoader.AppsStorageResult> result = new SparseArray<>();
final StorageAsyncLoader.AppsStorageResult userResult =
new StorageAsyncLoader.AppsStorageResult();
userResult.externalStats =
new StorageStatsSource.ExternalStorageStats(
99 * MEGABYTE_IN_BYTES,
33 * MEGABYTE_IN_BYTES,
33 * MEGABYTE_IN_BYTES,
33 * MEGABYTE_IN_BYTES, 0);
result.put(10, userResult);
mController.handleResult(result);
final ArgumentCaptor<Preference> argumentCaptor = ArgumentCaptor.forClass(Preference.class);
verify(mScreen).addPreference(argumentCaptor.capture());
final Preference preference = argumentCaptor.getValue();
assertThat(preference.getSummary()).isEqualTo("0.10 GB");
}
@Test
public void iconCallbackChangesPreferenceIcon() {
final SparseArray<Drawable> icons = new SparseArray<>();
final UserIconDrawable drawable = mock(UserIconDrawable.class);
when(drawable.mutate()).thenReturn(drawable);
icons.put(mPrimaryProfile.id, drawable);
mController.handleUserIcons(icons);
final ArgumentCaptor<Preference> argumentCaptor = ArgumentCaptor.forClass(Preference.class);
verify(mScreen).addPreference(argumentCaptor.capture());
final Preference preference = argumentCaptor.getValue();
assertThat(preference.getIcon()).isEqualTo(drawable);
}
}

View File

@@ -0,0 +1,79 @@
/*
* Copyright (C) 2021 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.display;
import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
import android.content.Context;
import android.content.pm.PackageManager;
import androidx.preference.PreferenceScreen;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
@RunWith(RobolectricTestRunner.class)
public class AdaptiveSleepBatterySaverPreferenceControllerTest {
private Context mContext;
private AdaptiveSleepBatterySaverPreferenceController mController;
@Mock
private PackageManager mPackageManager;
@Mock
private PreferenceScreen mScreen;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = spy(getApplicationContext());
doReturn(mPackageManager).when(mContext).getPackageManager();
when(mPackageManager.getAttentionServicePackageName()).thenReturn("some.package");
when(mPackageManager.checkPermission(any(), any())).thenReturn(
PackageManager.PERMISSION_GRANTED);
mController = spy(new AdaptiveSleepBatterySaverPreferenceController(mContext));
when(mController.isPowerSaveMode()).thenReturn(false);
}
@Test
public void addToScreen_normalCase_hidePreference() {
mController.addToScreen(mScreen);
assertThat(mController.mPreference.isVisible()).isFalse();
}
@Test
public void addToScreen_powerSaveEnabled_showPreference() {
when(mController.isPowerSaveMode()).thenReturn(true);
mController.addToScreen(mScreen);
assertThat(mController.mPreference.isVisible()).isTrue();
}
}

View File

@@ -83,9 +83,10 @@ public class AdaptiveSleepPreferenceControllerTest {
when(mRestrictionUtils.checkIfRestrictionEnforced(any(),
eq(UserManager.DISALLOW_CONFIG_SCREEN_TIMEOUT))).thenReturn(null);
mController = new AdaptiveSleepPreferenceController(mContext, mRestrictionUtils);
mController = spy(new AdaptiveSleepPreferenceController(mContext, mRestrictionUtils));
mController.initializePreference();
when(mController.isCameraLocked()).thenReturn(false);
when(mController.isPowerSaveMode()).thenReturn(false);
}
@Test
@@ -174,4 +175,14 @@ public class AdaptiveSleepPreferenceControllerTest {
assertThat(mController.mPreference.isEnabled()).isFalse();
}
@Test
public void addToScreen_powerSaveEnabled_disablePreference() {
when(mController.isPowerSaveMode()).thenReturn(true);
mController.addToScreen(mScreen);
assertThat(mController.mPreference.isEnabled()).isFalse();
}
}

View File

@@ -0,0 +1,80 @@
/*
* Copyright (C) 2021 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.display;
import static com.android.settings.core.BasePreferenceController.AVAILABLE_UNSEARCHABLE;
import static com.android.settings.core.BasePreferenceController.UNSUPPORTED_ON_DEVICE;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.when;
import android.content.ContentResolver;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import com.android.settings.testutils.ResolveInfoBuilder;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
@RunWith(RobolectricTestRunner.class)
public class SmartAutoRotateBatterySaverControllerTest {
private static final String PACKAGE_NAME = "package_name";
private SmartAutoRotateBatterySaverController mController;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
final Context context = Mockito.spy(RuntimeEnvironment.application);
final ContentResolver contentResolver = RuntimeEnvironment.application.getContentResolver();
when(context.getContentResolver()).thenReturn(contentResolver);
final PackageManager packageManager = Mockito.mock(PackageManager.class);
when(context.getPackageManager()).thenReturn(packageManager);
doReturn(PACKAGE_NAME).when(packageManager).getRotationResolverPackageName();
mController = Mockito.spy(
new SmartAutoRotateBatterySaverController(context, "smart_auto_rotate"));
when(mController.isPowerSaveMode()).thenReturn(false);
final ResolveInfo resolveInfo = new ResolveInfoBuilder(PACKAGE_NAME).build();
resolveInfo.serviceInfo = new ServiceInfo();
when(packageManager.resolveService(any(), anyInt())).thenReturn(resolveInfo);
}
@Test
public void getAvailabilityStatus_returnUnsupportedOnDevice() {
assertThat(mController.getAvailabilityStatus()).isEqualTo(UNSUPPORTED_ON_DEVICE);
}
@Test
public void getAvailabilityStatus_powerSaveModeEnabled_returnAvailableUnSearchAble() {
when(mController.isPowerSaveMode()).thenReturn(true);
assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE_UNSEARCHABLE);
}
}

View File

@@ -74,8 +74,9 @@ public class SmartAutoRotateControllerTest {
doReturn(PACKAGE_NAME).when(mPackageManager).getRotationResolverPackageName();
doReturn(PackageManager.PERMISSION_GRANTED).when(mPackageManager).checkPermission(
Manifest.permission.CAMERA, PACKAGE_NAME);
mController = new SmartAutoRotateController(context, "test_key");
mController = Mockito.spy(new SmartAutoRotateController(context, "test_key"));
when(mController.isCameraLocked()).thenReturn(false);
when(mController.isPowerSaveMode()).thenReturn(false);
doReturn(mController.getPreferenceKey()).when(mPreference).getKey();
final ResolveInfo resolveInfo = new ResolveInfoBuilder(PACKAGE_NAME).build();
@@ -115,6 +116,12 @@ public class SmartAutoRotateControllerTest {
assertThat(mController.getAvailabilityStatus()).isEqualTo(DISABLED_DEPENDENT_SETTING);
}
@Test
public void getAvailabilityStatus_powerSaveEnabled_returnDisableDependentSetting() {
when(mController.isPowerSaveMode()).thenReturn(true);
assertThat(mController.getAvailabilityStatus()).isEqualTo(DISABLED_DEPENDENT_SETTING);
}
private void enableAutoRotation() {
Settings.System.putIntForUser(mContentResolver,
Settings.System.ACCELEROMETER_ROTATION, 1, UserHandle.USER_CURRENT);

View File

@@ -29,7 +29,10 @@ import static org.mockito.Mockito.when;
import android.content.Context;
import android.content.ContentValues;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.text.format.DateUtils;
import androidx.preference.Preference;
@@ -75,6 +78,8 @@ public final class BatteryChartPreferenceControllerTest {
@Mock private PowerGaugePreference mPowerGaugePreference;
@Mock private ExpandDividerPreference mExpandDividerPreference;
@Mock private BatteryUtils mBatteryUtils;
@Mock private Configuration mConfiguration;
@Mock private Resources mResources;
private Context mContext;
private BatteryDiffEntry mBatteryDiffEntry;
@@ -103,6 +108,33 @@ public final class BatteryChartPreferenceControllerTest {
createBatteryHistoryMap());
}
@Test
public void testOnResume_uiModeIsChanged_clearBatteryDiffEntryCache() {
doReturn(mResources).when(mContext).getResources();
doReturn(mConfiguration).when(mResources).getConfiguration();
mConfiguration.uiMode = Configuration.UI_MODE_NIGHT_UNDEFINED;
// Ensures the testing environment is correct.
assertThat(BatteryDiffEntry.sResourceCache).hasSize(1);
mBatteryChartPreferenceController.onResume();
// Changes the uiMode in the configuration.
mConfiguration.uiMode = Configuration.UI_MODE_NIGHT_YES;
mBatteryChartPreferenceController.onResume();
assertThat(BatteryDiffEntry.sResourceCache).isEmpty();
}
@Test
public void testOnResume_uiModeIsNotChanged_notClearBatteryDiffEntryCache() {
doReturn(mResources).when(mContext).getResources();
doReturn(mConfiguration).when(mResources).getConfiguration();
mConfiguration.uiMode = Configuration.UI_MODE_NIGHT_UNDEFINED;
// Ensures the testing environment is correct.
assertThat(BatteryDiffEntry.sResourceCache).hasSize(1);
mBatteryChartPreferenceController.onResume();
assertThat(BatteryDiffEntry.sResourceCache).isNotEmpty();
}
@Test
public void testOnDestroy_activityIsChanging_clearBatteryEntryCache() {
doReturn(true).when(mSettingsActivity).isChangingConfigurations();
@@ -559,6 +591,51 @@ public final class BatteryChartPreferenceControllerTest {
.setTimestamps(any());
}
@Test
public void testOnSaveInstanceState_restoreSelectedIndexAndExpandState() {
final int expectedIndex = 1;
final boolean isExpanded = true;
final Bundle bundle = new Bundle();
mBatteryChartPreferenceController.mTrapezoidIndex = expectedIndex;
mBatteryChartPreferenceController.mIsExpanded = isExpanded;
mBatteryChartPreferenceController.onSaveInstanceState(bundle);
// Replaces the original controller with other values.
mBatteryChartPreferenceController.mTrapezoidIndex = -1;
mBatteryChartPreferenceController.mIsExpanded = false;
mBatteryChartPreferenceController.onCreate(bundle);
mBatteryChartPreferenceController.setBatteryHistoryMap(
createBatteryHistoryMap());
assertThat(mBatteryChartPreferenceController.mTrapezoidIndex)
.isEqualTo(expectedIndex);
assertThat(mBatteryChartPreferenceController.mIsExpanded).isTrue();
}
@Test
public void testIsValidToShowSummary_returnExpectedResult() {
assertThat(mBatteryChartPreferenceController
.isValidToShowSummary("com.google.android.apps.scone"))
.isTrue();
// Verifies the item which is defined in the array list.
assertThat(mBatteryChartPreferenceController
.isValidToShowSummary("com.google.android.googlequicksearchbox"))
.isFalse();
}
@Test
public void testIsValidToShowEntry_returnExpectedResult() {
assertThat(mBatteryChartPreferenceController
.isValidToShowEntry("com.google.android.apps.scone"))
.isTrue();
// Verifies the items which are defined in the array list.
assertThat(mBatteryChartPreferenceController
.isValidToShowEntry("com.google.android.gms.persistent"))
.isFalse();
}
private static Map<Long, Map<String, BatteryHistEntry>> createBatteryHistoryMap() {
final Map<Long, Map<String, BatteryHistEntry>> batteryHistoryMap = new HashMap<>();
for (int index = 0; index < DESIRED_HISTORY_SIZE; index++) {

View File

@@ -167,6 +167,17 @@ public class BatteryHeaderPreferenceControllerTest {
verify(mBatteryUsageProgressBarPref).setBottomSummary(BATTERY_STATUS);
}
@Test
public void updatePreference_statusAnomalous_showStatusLabel() {
mBatteryInfo.remainingLabel = TIME_LEFT;
mBatteryInfo.statusLabel = BATTERY_STATUS;
mBatteryInfo.batteryStatus = BatteryManager.BATTERY_STATUS_NOT_CHARGING;
mController.updateHeaderPreference(mBatteryInfo);
verify(mBatteryUsageProgressBarPref).setBottomSummary(BATTERY_STATUS);
}
@Test
public void updatePreference_charging_showFullText() {
setChargingState(/* isDischarging */ false, /* updatedByStatusFeature */ false);
@@ -184,7 +195,7 @@ public class BatteryHeaderPreferenceControllerTest {
mController.updateHeaderPreference(mBatteryInfo);
final String expectedResult = "Battery Saver is on • " + TIME_LEFT;
final String expectedResult = "Battery Saver on • " + TIME_LEFT;
verify(mBatteryUsageProgressBarPref).setBottomSummary(expectedResult);
}

View File

@@ -220,6 +220,42 @@ public class RecentNotifyingAppsPreferenceControllerTest {
verify(mSeeAllPref).setIcon(R.drawable.ic_chevron_right_24dp);
}
@Test
public void display_noCrashIfLessThan3() throws Exception {
List<Event> events = new ArrayList<>();
Event app = new Event();
app.mEventType = Event.NOTIFICATION_INTERRUPTION;
app.mPackage = "a";
app.mTimeStamp = System.currentTimeMillis();
events.add(app);
ApplicationsState.AppEntry app1Entry = mock(ApplicationsState.AppEntry.class);
app1Entry.info = mApplicationInfo;
app1Entry.label = "app 1";
when(mAppState.getEntry(app.getPackageName(), UserHandle.myUserId()))
.thenReturn(app1Entry);
when(mPackageManager.resolveActivity(any(Intent.class), anyInt())).thenReturn(
new ResolveInfo());
UsageEvents usageEvents = getUsageEvents(
new String[] {app.getPackageName()},
events);
when(mIUsageStatsManager.queryEventsForUser(anyLong(), anyLong(), anyInt(), anyString()))
.thenReturn(usageEvents);
mAppEntry.info = mApplicationInfo;
mController.displayPreference(mScreen);
verify(mCategory).setTitle(R.string.recent_notifications);
// Only add app1 & app2. app3 skipped because it's invalid app.
assertThat(mApp1.getTitle()).isEqualTo(app1Entry.label);
verify(mCategory).removePreferenceRecursively("app2");
mController.refreshUi(mContext);
}
@Test
public void display_showRecentsWithInstantApp() throws Exception {
List<Event> events = new ArrayList<>();
@@ -267,35 +303,12 @@ public class RecentNotifyingAppsPreferenceControllerTest {
mController.displayPreference(mScreen);
assertThat(mApp1.getTitle()).isEqualTo(appEntry.label);
assertThat(mApp1.getSummary()).isEqualTo("Just now");
assertThat(mApp2.getTitle()).isEqualTo(app1Entry.label);
verify(mCategory).removePreferenceRecursively(mApp3.getKey());
}
@Test
public void display_showRecents_formatSummary() throws Exception {
List<Event> events = new ArrayList<>();
Event app = new Event();
app.mEventType = Event.NOTIFICATION_INTERRUPTION;
app.mPackage = "pkg.class";
app.mTimeStamp = System.currentTimeMillis();
events.add(app);
UsageEvents usageEvents = getUsageEvents(new String[] {"pkg.class"}, events);
when(mIUsageStatsManager.queryEventsForUser(anyLong(), anyLong(), anyInt(), anyString()))
.thenReturn(usageEvents);
when(mAppState.getEntry(app.getPackageName(), UserHandle.myUserId()))
.thenReturn(mAppEntry);
when(mPackageManager.resolveActivity(any(Intent.class), anyInt())).thenReturn(
new ResolveInfo());
mAppEntry.info = mApplicationInfo;
mController.displayPreference(mScreen);
assertThat(mApp1.getSummary()).isEqualTo("Just now");
}
@Test
public void reloadData() throws Exception {
when(mUserManager.getProfileIdsWithDisabled(0)).thenReturn(new int[] {0, 10});

View File

@@ -25,6 +25,7 @@ import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
@@ -35,6 +36,7 @@ import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.robolectric.Shadows.shadowOf;
import android.app.admin.DevicePolicyManager;
import android.app.settings.SettingsEnums;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -56,6 +58,7 @@ import com.android.settings.testutils.shadow.ShadowDevicePolicyManager;
import com.android.settings.testutils.shadow.ShadowUserManager;
import com.android.settingslib.RestrictedLockUtils;
import com.android.settingslib.RestrictedPreference;
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
import org.junit.After;
import org.junit.Before;
@@ -105,6 +108,8 @@ public class UserDetailsSettingsTest {
private FragmentActivity mActivity;
private Context mContext;
private UserCapabilities mUserCapabilities;
@Mock
private MetricsFeatureProvider mMetricsFeatureProvider;
private UserDetailsSettings mFragment;
private Bundle mArguments;
private UserInfo mUserInfo;
@@ -128,6 +133,7 @@ public class UserDetailsSettingsTest {
ReflectionHelpers.setField(mFragment, "mUserManager", userManager);
ReflectionHelpers.setField(mFragment, "mUserCaps", mUserCapabilities);
ReflectionHelpers.setField(mFragment, "mMetricsFeatureProvider", mMetricsFeatureProvider);
doReturn(mActivity).when(mFragment).getActivity();
doReturn(mActivity).when(mFragment).getContext();
@@ -472,6 +478,23 @@ public class UserDetailsSettingsTest {
mFragment.onPreferenceClick(mSwitchUserPref);
verify(mFragment).switchUser();
verify(mMetricsFeatureProvider, never()).action(any(),
eq(SettingsEnums.ACTION_SWITCH_TO_GUEST));
}
@Test
public void onPreferenceClick_switchToGuestClicked_canSwitch_shouldSwitch() {
setupSelectedGuest();
mUserManager.setSwitchabilityStatus(SWITCHABILITY_STATUS_OK);
mFragment.mSwitchUserPref = mSwitchUserPref;
mFragment.mRemoveUserPref = mRemoveUserPref;
mFragment.mAppAndContentAccessPref = mAppAndContentAccessPref;
mFragment.mUserInfo = mUserInfo;
mFragment.onPreferenceClick(mSwitchUserPref);
verify(mFragment).switchUser();
verify(mMetricsFeatureProvider).action(any(), eq(SettingsEnums.ACTION_SWITCH_TO_GUEST));
}
@Test

View File

@@ -176,6 +176,22 @@ public class UserSettingsTest {
assertThat(UserSettings.assignDefaultPhoto(null, ACTIVE_USER_ID)).isFalse();
}
@Test
public void testExitGuest_ShouldLogAction() {
mUserCapabilities.mIsGuest = true;
mFragment.exitGuest();
verify(mMetricsFeatureProvider).action(any(),
eq(SettingsEnums.ACTION_USER_GUEST_EXIT_CONFIRMED));
}
@Test
public void testExitGuestWhenNotGuest_ShouldNotLogAction() {
mUserCapabilities.mIsGuest = false;
mFragment.exitGuest();
verify(mMetricsFeatureProvider, never()).action(any(),
eq(SettingsEnums.ACTION_USER_GUEST_EXIT_CONFIRMED));
}
@Test
public void withDisallowRemoveUser_ShouldDisableRemoveUser() {
// TODO(b/115781615): Tidy robolectric tests

View File

@@ -32,10 +32,8 @@ android_test {
"platform-test-annotations",
"truth-prebuilt",
"ub-uiautomator",
"SettingsLibSettingsSpinner",
"SettingsLibUsageProgressBarPreference",
"SettingsLibTwoTargetPreference",
"SettingsLibMainSwitchPreference",
// Don't add SettingsLib libraries here - you can use them directly as they are in the
// instrumented Settings app.
],
// Include all test java files.

View File

@@ -360,7 +360,6 @@ public class InternetConnectivityPanelTest {
assertThat(mPanel.isProgressBarVisible()).isTrue();
verify(mPanelContentCallback).onProgressBarVisibleChanged();
verify(mPanelContentCallback).onHeaderChanged();
verify(mMainThreadHandler).postDelayed(any() /* mHideProgressBarRunnable */, anyLong());
}