Snap for 7249665 from 844347bd27 to sc-release

Change-Id: I932badb295b901553c090c78fe86bec7e57de7d2
This commit is contained in:
android-build-team Robot
2021-04-01 01:08:15 +00:00
30 changed files with 1276 additions and 177 deletions

View File

@@ -4045,4 +4045,100 @@
column="36"/>
</issue>
<issue
id="HardCodedColor"
severity="Error"
message="Avoid using hardcoded color"
category="Correctness"
priority="4"
summary="Using hardcoded color"
explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.&#xA;This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
errorLine1=" &lt;item android:offset=&quot;0&quot; android:color=&quot;#33263238&quot;/>"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/drawable/ic_files_go_round.xml"
line="46"
column="34"/>
</issue>
<issue
id="HardCodedColor"
severity="Error"
message="Avoid using hardcoded color"
category="Correctness"
priority="4"
summary="Using hardcoded color"
explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.&#xA;This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
errorLine1=" &lt;item android:offset=&quot;1&quot; android:color=&quot;#00263238&quot;/>"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/drawable/ic_files_go_round.xml"
line="47"
column="34"/>
</issue>
<issue
id="HardCodedColor"
severity="Error"
message="Avoid using hardcoded color"
category="Correctness"
priority="4"
summary="Using hardcoded color"
explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.&#xA;This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
errorLine1=" &lt;item android:offset=&quot;0&quot; android:color=&quot;#281A237E&quot;/>"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/drawable/ic_files_go_round.xml"
line="71"
column="34"/>
</issue>
<issue
id="HardCodedColor"
severity="Error"
message="Avoid using hardcoded color"
category="Correctness"
priority="4"
summary="Using hardcoded color"
explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.&#xA;This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
errorLine1=" &lt;item android:offset=&quot;1&quot; android:color=&quot;#051A237E&quot;/>"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/drawable/ic_files_go_round.xml"
line="72"
column="34"/>
</issue>
<issue
id="HardCodedColor"
severity="Error"
message="Avoid using hardcoded color"
category="Correctness"
priority="4"
summary="Using hardcoded color"
explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.&#xA;This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
errorLine1=" &lt;item android:offset=&quot;0.0029046&quot; android:color=&quot;#19FFFFFF&quot;/>"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/drawable/ic_files_go_round.xml"
line="116"
column="42"/>
</issue>
<issue
id="HardCodedColor"
severity="Error"
message="Avoid using hardcoded color"
category="Correctness"
priority="4"
summary="Using hardcoded color"
explanation="Hardcoded color values are bad because theme changes cannot be uniformly applied.Instead use the theme specific colors such as `?android:attr/textColorPrimary` in attributes.&#xA;This ensures that a theme change from a light to a dark theme can be uniformlyapplied across the app."
errorLine1=" &lt;item android:offset=&quot;1&quot; android:color=&quot;#00FFFFFF&quot;/>"
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
<location
file="res/drawable/ic_files_go_round.xml"
line="117"
column="34"/>
</issue>
</issues>

View File

@@ -684,10 +684,21 @@
</plurals>
<!-- Location settings, loading the number of apps which have location permission [CHAR LIMIT=30] -->
<string name="location_settings_loading_app_permission_stats">Loading\u2026</string>
<!-- Location settings footer warning text [CHAR LIMIT=NONE] -->
<string name="location_settings_footer">
<!-- Location settings footer warning text when location is on [CHAR LIMIT=NONE] -->
<string name="location_settings_footer_location_on">
Location may use sources like GPS, Wi\u2011Fi, mobile networks, and sensors to help estimate
your device\u2019s location.
your device\u2019s location. Apps with the nearby devices permission can determine the
relative position of connected devices.
&lt;a href="<xliff:g example="http://www.google.com" id="url">
https://support.google.com/android/answer/3467281</xliff:g>">Learn more&lt;/a>.
</string>
<!-- Location settings footer warning text when location is off [CHAR LIMIT=NONE] -->
<string name="location_settings_footer_location_off">
Location access is off for apps and services. Your device location may still be sent to
emergency responders when you call or text an emergency number.
&lt;br>&lt;br>Apps with the nearby devices permission can determine the relative position of
connected devices.
</string>
<!-- Main Settings screen setting option title for the item to take you to the accounts screen [CHAR LIMIT=22] -->
@@ -4090,7 +4101,7 @@
<string name="managed_profile_location_switch_title">Location for work profile</string>
<!-- [CHAR LIMIT=30] Location settings screen. It's a link that directs the user to a page that
shows the location permission setting for each installed app -->
<string name="location_app_level_permissions">Manage location permissions</string>
<string name="location_app_level_permissions">App location permissions</string>
<!-- Summary for app permission on Location settings page when location is off [CHAR LIMIT=NONE] -->
<string name="location_app_permission_summary_location_off">Location is off</string>
<!--
@@ -4138,6 +4149,8 @@
<string name="location_scanning_bluetooth_always_scanning_title">Bluetooth scanning</string>
<!-- Description text for Bluetooth always scanning -->
<string name="location_scanning_bluetooth_always_scanning_description">Allow apps and services to scan for nearby devices at any time, even when Bluetooth is off. This can be used, for example, to improve location-based features and services.</string>
<!-- [CHAR LIMIT=60] Location Services preference title -->
<string name="location_services_preference_title">Manage location services</string>
<!-- [CHAR LIMIT=60] Location Services screen, screen title -->
<string name="location_services_screen_title">Location Services</string>
<!-- [CHAR LIMIT=50] Location settings screen, sub category for location services for managed profile -->
@@ -6562,6 +6575,11 @@
<string name="certificate_management_app_description">Certificates installed by this app identify you to the apps and URLs below</string>
<!-- Label for button to remove the credential management app [CHAR LIMIT=30] -->
<string name="remove_credential_management_app">Remove</string>
<!-- List item found in the credential management app's authentication policy [CHAR LIMIT=NONE] -->
<plurals name="number_of_urls">
<item quantity="one"><xliff:g id="number">%d</xliff:g> URL</item>
<item quantity="other"><xliff:g id="number">%d</xliff:g> URLs</item>
</plurals>
<!-- Sound settings screen, setting check box label -->
<string name="emergency_tone_title">Emergency dialing signal</string>
@@ -8449,7 +8467,7 @@
<string name="work_use_personal_sounds_title">Use personal profile sounds</string>
<!-- Work Sound: Summary for the switch that enables syncing of personal ringtones to work profile. [CHAR LIMIT=160] -->
<string name="work_use_personal_sounds_summary">Sounds are the same for work and personal profiles</string>
<string name="work_use_personal_sounds_summary">Use the same sounds as your personal profile</string>
<!-- Work Sounds: Title for the option defining the work phone ringtone. [CHAR LIMIT=60] -->
<string name="work_ringtone_title">Work phone ringtone</string>
@@ -8464,13 +8482,13 @@
<string name="work_sound_same_as_personal">Same as personal profile</string>
<!-- Work Sound: Title for dialog shown when enabling sync with personal sounds. [CHAR LIMIT=60] -->
<string name="work_sync_dialog_title">Replace sounds?</string>
<string name="work_sync_dialog_title">Use personal profile sounds?</string>
<!-- Work Sound: Confirm action text for dialog shown when overriding work notification sounds with personal sounds. [CHAR LIMIT=30] -->
<string name="work_sync_dialog_yes">Replace</string>
<string name="work_sync_dialog_yes">Confirm</string>
<!-- Work Sound: Message for dialog shown when using the same sounds for work events as for personal events (notifications / ringtones / alarms). [CHAR LIMIT=none] -->
<string name="work_sync_dialog_message">Your personal profile sounds will be used for your work profile</string>
<string name="work_sync_dialog_message">Your work profile will use the same sounds as your personal profile</string>
<!-- Sound installation: Title for the dialog to confirm that a new sound will be installed to the Ringtones, Notifications, or Alarms folder. [CHAR LIMIT=50] -->
<string name="ringtones_install_custom_sound_title">Add custom sound?</string>

View File

@@ -64,13 +64,14 @@
<Preference
android:fragment="com.android.settings.location.LocationServices"
android:key="location_services"
android:title="@string/location_services_screen_title"
android:title="@string/location_services_preference_title"
settings:controller="com.android.settings.location.LocationServicesPreferenceController"/>
</PreferenceCategory>
<com.android.settingslib.widget.FooterPreference
android:title="@string/location_settings_footer"
android:title="@string/location_settings_footer_location_on"
android:key="location_footer"
android:selectable="false"/>
android:selectable="false"
settings:controller="com.android.settings.location.LocationSettingsFooterPreferenceController"/>
</PreferenceScreen>

View File

@@ -54,7 +54,8 @@
settings:controller="com.android.settings.location.LocationServicesPreferenceController"/>
<com.android.settingslib.widget.FooterPreference
android:title="@string/location_settings_footer"
android:title="@string/location_settings_footer_location_on"
android:key="location_footer"
settings:controller="com.android.settings.location.LocationSettingsFooterPreferenceController"
android:selectable="false"/>
</PreferenceScreen>

View File

@@ -62,7 +62,8 @@
settings:controller="com.android.settings.location.LocationServicesForWorkPreferenceController"/>
<com.android.settingslib.widget.FooterPreference
android:title="@string/location_settings_footer"
android:title="@string/location_settings_footer_location_on"
android:key="location_footer"
settings:controller="com.android.settings.location.LocationSettingsFooterPreferenceController"
android:selectable="false"/>
</PreferenceScreen>

View File

@@ -212,43 +212,10 @@
android:summary="%s"
android:order="-10"/>
<!-- TODO(b/174964721): make this category its own entry -->
<com.android.settings.widget.WorkOnlyCategory
android:key="sound_work_settings_section"
<Preference
android:key="sound_work_settings"
android:title="@string/sound_work_settings"
android:order="100">
<!-- Use the same sounds of the work profile -->
<SwitchPreference
android:key="work_use_personal_sounds"
android:title="@string/work_use_personal_sounds_title"
android:summary="@string/work_use_personal_sounds_summary"
android:disableDependentsState="true"/>
<!-- Work phone ringtone -->
<com.android.settings.DefaultRingtonePreference
android:key="work_ringtone"
android:title="@string/work_ringtone_title"
android:dialogTitle="@string/work_alarm_ringtone_title"
android:ringtoneType="ringtone"
android:dependency="work_use_personal_sounds"/>
<!-- Default work notification ringtone -->
<com.android.settings.DefaultRingtonePreference
android:key="work_notification_ringtone"
android:title="@string/work_notification_ringtone_title"
android:dialogTitle="@string/work_alarm_ringtone_title"
android:ringtoneType="notification"
android:dependency="work_use_personal_sounds"/>
<!-- Default work alarm ringtone -->
<com.android.settings.DefaultRingtonePreference
android:key="work_alarm_ringtone"
android:title="@string/work_alarm_ringtone_title"
android:dialogTitle="@string/work_alarm_ringtone_title"
android:persistent="false"
android:ringtoneType="alarm"
android:dependency="work_use_personal_sounds"/>
</com.android.settings.widget.WorkOnlyCategory>
android:fragment="com.android.settings.notification.SoundWorkSettings"
android:order="100"
settings:controller="com.android.settings.notification.WorkSoundsPreferenceController"/>
</PreferenceScreen>

View File

@@ -0,0 +1,53 @@
<?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.
-->
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android"
android:title="@string/sound_work_settings">
<!-- Use the same sounds of the work profile -->
<SwitchPreference
android:key="work_use_personal_sounds"
android:title="@string/work_use_personal_sounds_title"
android:summary="@string/work_use_personal_sounds_summary"
android:disableDependentsState="true"/>
<!-- Work phone ringtone -->
<com.android.settings.DefaultRingtonePreference
android:key="work_ringtone"
android:title="@string/work_ringtone_title"
android:dialogTitle="@string/work_alarm_ringtone_title"
android:ringtoneType="ringtone"
android:dependency="work_use_personal_sounds"/>
<!-- Default work notification ringtone -->
<com.android.settings.DefaultRingtonePreference
android:key="work_notification_ringtone"
android:title="@string/work_notification_ringtone_title"
android:dialogTitle="@string/work_alarm_ringtone_title"
android:ringtoneType="notification"
android:dependency="work_use_personal_sounds"/>
<!-- Default work alarm ringtone -->
<com.android.settings.DefaultRingtonePreference
android:key="work_alarm_ringtone"
android:title="@string/work_alarm_ringtone_title"
android:dialogTitle="@string/work_alarm_ringtone_title"
android:persistent="false"
android:ringtoneType="alarm"
android:dependency="work_use_personal_sounds"/>
</PreferenceScreen>

View File

@@ -46,7 +46,6 @@ import androidx.appcompat.app.AlertDialog;
import com.android.settings.R;
import com.android.settings.biometrics.BiometricEnrollSidecar;
import com.android.settings.biometrics.BiometricErrorDialog;
import com.android.settings.biometrics.BiometricUtils;
import com.android.settings.biometrics.BiometricsEnrollEnrolling;
import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
@@ -111,32 +110,6 @@ public class FingerprintEnrollEnrolling extends BiometricsEnrollEnrolling {
private boolean mRestoring;
private Vibrator mVibrator;
public static class FingerprintErrorDialog extends BiometricErrorDialog {
static FingerprintErrorDialog newInstance(CharSequence msg, int msgId) {
FingerprintErrorDialog dialog = new FingerprintErrorDialog();
Bundle args = new Bundle();
args.putCharSequence(KEY_ERROR_MSG, msg);
args.putInt(KEY_ERROR_ID, msgId);
dialog.setArguments(args);
return dialog;
}
@Override
public int getMetricsCategory() {
return SettingsEnums.DIALOG_FINGERPINT_ERROR;
}
@Override
public int getTitleResId() {
return R.string.security_settings_fingerprint_enroll_error_dialog_title;
}
@Override
public int getOkButtonTextResId() {
return R.string.security_settings_fingerprint_enroll_dialog_ok;
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -339,19 +312,7 @@ public class FingerprintEnrollEnrolling extends BiometricsEnrollEnrolling {
@Override
public void onEnrollmentError(int errMsgId, CharSequence errString) {
int msgId;
switch (errMsgId) {
case FingerprintManager.FINGERPRINT_ERROR_TIMEOUT:
// This message happens when the underlying crypto layer decides to revoke the
// enrollment auth token.
msgId = R.string.security_settings_fingerprint_enroll_error_timeout_dialog_message;
break;
default:
// There's nothing specific to tell the user about. Ask them to try again.
msgId = R.string.security_settings_fingerprint_enroll_error_generic_dialog_message;
break;
}
showErrorDialog(getText(msgId), errMsgId);
FingerprintErrorDialog.showErrorDialog(this, errMsgId);
stopIconAnimation();
if (!mCanAssumeUdfps) {
mErrorText.removeCallbacks(mTouchAgainRunnable);
@@ -398,11 +359,6 @@ public class FingerprintEnrollEnrolling extends BiometricsEnrollEnrolling {
return PROGRESS_BAR_MAX * progress / (steps + 1);
}
private void showErrorDialog(CharSequence msg, int msgId) {
BiometricErrorDialog dlg = FingerprintErrorDialog.newInstance(msg, msgId);
dlg.show(getSupportFragmentManager(), FingerprintErrorDialog.class.getName());
}
private void showIconTouchDialog() {
mIconTouchCount = 0;
new IconTouchDialog().show(getSupportFragmentManager(), null /* tag */);

View File

@@ -28,7 +28,7 @@ import androidx.annotation.Nullable;
import com.android.settings.R;
import com.android.settings.Utils;
import com.android.settings.biometrics.BiometricEnrollBase;
import com.android.settings.biometrics.BiometricEnrollSidecar.Listener;
import com.android.settings.biometrics.BiometricEnrollSidecar;
import com.android.settings.biometrics.BiometricUtils;
import com.android.settings.password.ChooseLockSettingsHelper;
@@ -40,7 +40,8 @@ import java.util.List;
/**
* Activity explaining the fingerprint sensor location for fingerprint enrollment.
*/
public class FingerprintEnrollFindSensor extends BiometricEnrollBase {
public class FingerprintEnrollFindSensor extends BiometricEnrollBase implements
BiometricEnrollSidecar.Listener {
@Nullable
private FingerprintFindSensorAnimation mAnimation;
@@ -139,25 +140,27 @@ public class FingerprintEnrollFindSensor extends BiometricEnrollBase {
.add(mSidecar, FingerprintEnrollEnrolling.TAG_SIDECAR)
.commitAllowingStateLoss();
}
mSidecar.setListener(new Listener() {
@Override
public void onEnrollmentProgressChange(int steps, int remaining) {
mNextClicked = true;
proceedToEnrolling(true /* cancelEnrollment */);
}
mSidecar.setListener(this);
}
@Override
public void onEnrollmentHelp(int helpMsgId, CharSequence helpString) {
}
@Override
public void onEnrollmentProgressChange(int steps, int remaining) {
mNextClicked = true;
proceedToEnrolling(true /* cancelEnrollment */);
}
@Override
public void onEnrollmentError(int errMsgId, CharSequence errString) {
if (mNextClicked && errMsgId == FingerprintManager.FINGERPRINT_ERROR_CANCELED) {
mNextClicked = false;
proceedToEnrolling(false /* cancelEnrollment */);
}
}
});
@Override
public void onEnrollmentHelp(int helpMsgId, CharSequence helpString) {
}
@Override
public void onEnrollmentError(int errMsgId, CharSequence errString) {
if (mNextClicked && errMsgId == FingerprintManager.FINGERPRINT_ERROR_CANCELED) {
mNextClicked = false;
proceedToEnrolling(false /* cancelEnrollment */);
} else {
FingerprintErrorDialog.showErrorDialog(this, errMsgId);
}
}
@Override

View File

@@ -0,0 +1,72 @@
/*
* Copyright 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.biometrics.fingerprint;
import android.app.settings.SettingsEnums;
import android.hardware.fingerprint.FingerprintManager;
import android.os.Bundle;
import com.android.settings.R;
import com.android.settings.biometrics.BiometricEnrollBase;
import com.android.settings.biometrics.BiometricErrorDialog;
/**
* Fingerprint error dialog, will be shown when an error occurs during fingerprint enrollment.
*/
public class FingerprintErrorDialog extends BiometricErrorDialog {
public static void showErrorDialog(BiometricEnrollBase host, int errMsgId) {
final CharSequence errMsg = host.getText(getErrorMessage(errMsgId));
final FingerprintErrorDialog dialog = newInstance(errMsg, errMsgId);
dialog.show(host.getSupportFragmentManager(), FingerprintErrorDialog.class.getName());
}
private static int getErrorMessage(int errMsgId) {
switch (errMsgId) {
case FingerprintManager.FINGERPRINT_ERROR_TIMEOUT:
// This message happens when the underlying crypto layer decides to revoke the
// enrollment auth token.
return R.string.security_settings_fingerprint_enroll_error_timeout_dialog_message;
default:
// There's nothing specific to tell the user about. Ask them to try again.
return R.string.security_settings_fingerprint_enroll_error_generic_dialog_message;
}
}
private static FingerprintErrorDialog newInstance(CharSequence msg, int msgId) {
FingerprintErrorDialog dialog = new FingerprintErrorDialog();
Bundle args = new Bundle();
args.putCharSequence(KEY_ERROR_MSG, msg);
args.putInt(KEY_ERROR_ID, msgId);
dialog.setArguments(args);
return dialog;
}
@Override
public int getTitleResId() {
return R.string.security_settings_fingerprint_enroll_error_dialog_title;
}
@Override
public int getOkButtonTextResId() {
return R.string.security_settings_fingerprint_enroll_dialog_ok;
}
@Override
public int getMetricsCategory() {
return SettingsEnums.DIALOG_FINGERPINT_ERROR;
}
}

View File

@@ -236,14 +236,6 @@ public class StorageEntry implements Comparable<StorageEntry>, Parcelable {
return mVolumeInfo == null ? false : mVolumeInfo.getType() == VolumeInfo.TYPE_PUBLIC;
}
/**
* Stub volume is a volume that is maintained by external party such as the ChromeOS processes
* in ARC++.
*/
public boolean isStub() {
return mVolumeInfo == null ? false : mVolumeInfo.getType() == VolumeInfo.TYPE_STUB;
}
/** Returns description. */
public String getDescription() {
if (isVolumeInfo()) {

View File

@@ -230,6 +230,8 @@ public class StorageItemPreferenceController extends AbstractPreferenceControlle
}
private boolean isValidPublicVolume() {
// Stub volume is a volume that is maintained by external party such as the ChromeOS
// processes in ARC++.
return mVolume != null
&& (mVolume.getType() == VolumeInfo.TYPE_PUBLIC
|| mVolume.getType() == VolumeInfo.TYPE_STUB)

View File

@@ -21,12 +21,10 @@ import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.graphics.ColorFilter;
import android.icu.text.NumberFormat;
import android.os.BatteryManager;
import android.os.PowerManager;
import android.text.TextUtils;
import android.widget.ImageView;
import androidx.annotation.VisibleForTesting;
import androidx.preference.PreferenceFragmentCompat;
@@ -62,7 +60,6 @@ public class BatteryHeaderPreferenceController extends BasePreferenceController
private Activity mActivity;
private PreferenceFragmentCompat mHost;
private Lifecycle mLifecycle;
private ColorFilter mAccentColorFilter;
private final PowerManager mPowerManager;
public BatteryHeaderPreferenceController(Context context, String key) {
@@ -88,9 +85,6 @@ public class BatteryHeaderPreferenceController extends BasePreferenceController
public void displayPreference(PreferenceScreen screen) {
super.displayPreference(screen);
mBatteryUsageProgressBarPref = screen.findPreference(getPreferenceKey());
mAccentColorFilter = com.android.settings.Utils.getAlphaInvariantColorFilterForColor(
com.android.settings.Utils.getColorAttrDefaultColor(
mContext, android.R.attr.colorAccent));
if (com.android.settings.Utils.isBatteryPresent(mContext)) {
quickUpdateHeaderPreference();
@@ -124,17 +118,15 @@ public class BatteryHeaderPreferenceController extends BasePreferenceController
public void updateHeaderPreference(BatteryInfo info) {
mBatteryUsageProgressBarPref.setUsageSummary(
formatBatteryPercentageText(info.batteryLevel));
mBatteryUsageProgressBarPref.setTotalSummary(generateLabel(info));
mBatteryUsageProgressBarPref.setBottomSummary(generateLabel(info));
mBatteryUsageProgressBarPref.setPercent(info.batteryLevel, BATTERY_MAX_LEVEL);
mBatteryUsageProgressBarPref.setCustomContent(
getBatteryIcon(!info.discharging, info.batteryLevel));
}
/**
* Callback which receives text for the summary line.
*/
public void updateBatteryStatus(String label, BatteryInfo info) {
mBatteryUsageProgressBarPref.setTotalSummary(label != null ? label : generateLabel(info));
mBatteryUsageProgressBarPref.setBottomSummary(label != null ? label : generateLabel(info));
}
public void quickUpdateHeaderPreference() {
@@ -146,28 +138,10 @@ public class BatteryHeaderPreferenceController extends BasePreferenceController
mBatteryUsageProgressBarPref.setUsageSummary(formatBatteryPercentageText(batteryLevel));
mBatteryUsageProgressBarPref.setPercent(batteryLevel, BATTERY_MAX_LEVEL);
mBatteryUsageProgressBarPref.setCustomContent(getBatteryIcon(!discharging, batteryLevel));
}
private CharSequence formatBatteryPercentageText(int batteryLevel) {
return TextUtils.expandTemplate(mContext.getText(R.string.battery_header_title_alternate),
NumberFormat.getIntegerInstance().format(batteryLevel));
}
//TODO(b/179237746): Update the battery icon after receiving final asset
private ImageView getBatteryIcon(boolean isCharging, int batteryLevel) {
ImageView batteryIcon = new ImageView(mContext);
if (batteryLevel <= (mContext.getResources().getInteger(
com.android.internal.R.integer.config_lowBatteryWarningLevel))) {
batteryIcon.setImageResource(R.drawable.ic_battery_low);
} else if (isCharging) {
batteryIcon.setColorFilter(mAccentColorFilter);
batteryIcon.setImageResource(R.drawable.ic_battery_charging_full);
} else {
batteryIcon = null;
}
return batteryIcon;
}
}

View File

@@ -50,8 +50,7 @@ public class LocationPersonalSettings extends DashboardFragment {
super.onAttach(context);
use(AppLocationPermissionPreferenceController.class).init(this);
// STOPSHIP(b/180533061): resolve the personal/work location services issue before we can
// ship.
use(LocationSettingsFooterPreferenceController.class).init(this);
use(RecentLocationAccessSeeAllButtonPreferenceController.class).init(this);
final int profileType = getArguments().getInt(ProfileSelectFragment.EXTRA_PROFILE);

View File

@@ -85,6 +85,7 @@ public class LocationSettings extends DashboardFragment {
use(RecentLocationAccessPreferenceController.class).init(this);
use(RecentLocationAccessSeeAllButtonPreferenceController.class).init(this);
use(LocationForWorkPreferenceController.class).init(this);
use(LocationSettingsFooterPreferenceController.class).init(this);
}
@Override

View File

@@ -0,0 +1,50 @@
/*
* 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.location;
import android.content.Context;
import android.text.Html;
import androidx.preference.PreferenceScreen;
import com.android.settings.R;
import com.android.settingslib.widget.FooterPreference;
/**
* Preference controller for Location Settings footer.
*/
public class LocationSettingsFooterPreferenceController extends LocationBasePreferenceController {
FooterPreference mFooterPreference;
public LocationSettingsFooterPreferenceController(Context context, String key) {
super(context, key);
}
@Override
public void displayPreference(PreferenceScreen screen) {
super.displayPreference(screen);
mFooterPreference = screen.findPreference(getPreferenceKey());
}
@Override
public void onLocationModeChanged(int mode, boolean restricted) {
boolean enabled = mLocationEnabler.isEnabled(mode);
mFooterPreference.setTitle(Html.fromHtml(mContext.getString(
enabled ? R.string.location_settings_footer_location_on
: R.string.location_settings_footer_location_off)));
}
}

View File

@@ -52,6 +52,7 @@ public class LocationWorkProfileSettings extends DashboardFragment {
use(AppLocationPermissionPreferenceController.class).init(this);
use(LocationForWorkPreferenceController.class).init(this);
use(RecentLocationAccessSeeAllButtonPreferenceController.class).init(this);
use(LocationSettingsFooterPreferenceController.class).init(this);
final int profileType = getArguments().getInt(ProfileSelectFragment.EXTRA_PROFILE);
final RecentLocationAccessPreferenceController controller = use(

View File

@@ -25,6 +25,7 @@ import android.os.Looper;
import android.os.Message;
import android.os.UserHandle;
import android.preference.SeekBarVolumizer;
import android.provider.SearchIndexableResource;
import android.text.TextUtils;
import android.util.FeatureFlagUtils;
@@ -250,8 +251,11 @@ public class SoundSettings extends DashboardFragment implements OnActivityResult
controllers.add(new AlarmRingtonePreferenceController(context));
controllers.add(new NotificationRingtonePreferenceController(context));
// === Work Sound Settings ===
controllers.add(new WorkSoundPreferenceController(context, fragment, lifecycle));
if (!FeatureFlagUtils.isEnabled(context, FeatureFlags.SILKY_HOME)) {
// TODO(b/174964721): This should be removed when the flag is deprecated.
// === Work Sound Settings ===
controllers.add(new WorkSoundPreferenceController(context, fragment, lifecycle));
}
// === Other Sound Settings ===
final DialPadTonePreferenceController dialPadTonePreferenceController =
@@ -308,15 +312,27 @@ public class SoundSettings extends DashboardFragment implements OnActivityResult
return buildPreferenceControllers(context, null /* fragment */,
null /* lifecycle */);
}
@Override
public List<SearchIndexableResource> getXmlResourcesToIndex(
Context context, boolean enabled) {
final SearchIndexableResource sir = new SearchIndexableResource(context);
sir.xmlResId = FeatureFlagUtils.isEnabled(context, FeatureFlags.SILKY_HOME)
? R.xml.sound_settings_v2 : R.xml.sound_settings;
return Arrays.asList(sir);
}
};
// === Work Sound Settings ===
void enableWorkSync() {
final WorkSoundPreferenceController workSoundController =
use(WorkSoundPreferenceController.class);
if (workSoundController != null) {
workSoundController.enableWorkSync();
// TODO(b/174964721): This should be refined when the flag is deprecated.
if (!FeatureFlagUtils.isEnabled(getContext(), FeatureFlags.SILKY_HOME)) {
final WorkSoundPreferenceController workSoundController =
use(WorkSoundPreferenceController.class);
if (workSoundController != null) {
workSoundController.enableWorkSync();
}
}
}

View File

@@ -0,0 +1,153 @@
/*
* 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.notification;
import android.app.settings.SettingsEnums;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.os.UserHandle;
import android.os.UserManager;
import android.text.TextUtils;
import android.util.FeatureFlagUtils;
import androidx.preference.Preference;
import com.android.settings.R;
import com.android.settings.RingtonePreference;
import com.android.settings.core.FeatureFlags;
import com.android.settings.core.OnActivityResultListener;
import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settingslib.core.AbstractPreferenceController;
import com.android.settingslib.core.lifecycle.Lifecycle;
import com.android.settingslib.search.SearchIndexable;
import java.util.ArrayList;
import java.util.List;
/** Sounds settings for work profile. */
@SearchIndexable
public class SoundWorkSettings extends DashboardFragment implements OnActivityResultListener {
private static final String TAG = "SoundWorkSettings";
private static final int REQUEST_CODE = 200;
private static final String SELECTED_PREFERENCE_KEY = "selected_preference";
private RingtonePreference mRequestPreference;
@Override
public int getMetricsCategory() {
return SettingsEnums.WORK_PROFILE_SOUNDS;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState != null) {
String selectedPreference = savedInstanceState.getString(
SELECTED_PREFERENCE_KEY, /* defaultValue= */ null);
if (!TextUtils.isEmpty(selectedPreference)) {
mRequestPreference = findPreference(selectedPreference);
}
}
}
@Override
public boolean onPreferenceTreeClick(Preference preference) {
if (preference instanceof RingtonePreference) {
writePreferenceClickMetric(preference);
mRequestPreference = (RingtonePreference) preference;
mRequestPreference.onPrepareRingtonePickerIntent(mRequestPreference.getIntent());
getActivity().startActivityForResultAsUser(
mRequestPreference.getIntent(),
REQUEST_CODE,
/* options= */ null,
UserHandle.of(mRequestPreference.getUserId()));
return true;
}
return super.onPreferenceTreeClick(preference);
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (mRequestPreference != null) {
mRequestPreference.onActivityResult(requestCode, resultCode, data);
mRequestPreference = null;
}
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
if (mRequestPreference != null) {
outState.putString(SELECTED_PREFERENCE_KEY, mRequestPreference.getKey());
}
}
@Override
protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
return buildPreferenceControllers(context, /* fragment= */ this, getSettingsLifecycle());
}
@Override
protected String getLogTag() {
return TAG;
}
@Override
protected int getPreferenceScreenResId() {
return R.xml.sound_work_settings;
}
private static List<AbstractPreferenceController> buildPreferenceControllers(Context context,
SoundWorkSettings fragment, Lifecycle lifecycle) {
final List<AbstractPreferenceController> controllers = new ArrayList<>();
controllers.add(new SoundWorkSettingsController(context, fragment, lifecycle));
return controllers;
}
static final boolean isSupportWorkProfileSound(Context context) {
// TODO(b/174964721): Feature flag should be removed when silky home launched.
final boolean isSilkyEnabled = FeatureFlagUtils.isEnabled(context,
FeatureFlags.SILKY_HOME);
final AudioHelper audioHelper = new AudioHelper(context);
final boolean hasWorkProfile = audioHelper.getManagedProfileId(
UserManager.get(context)) != UserHandle.USER_NULL;
final boolean shouldShowRingtoneSettings = !audioHelper.isSingleVolume();
return isSilkyEnabled && hasWorkProfile && shouldShowRingtoneSettings;
}
void enableWorkSync() {
final SoundWorkSettingsController soundWorkSettingsController =
use(SoundWorkSettingsController.class);
if (soundWorkSettingsController != null) {
soundWorkSettingsController.enableWorkSync();
}
}
public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
new BaseSearchIndexProvider(R.xml.sound_work_settings) {
@Override
protected boolean isPageSearchEnabled(Context context) {
return isSupportWorkProfileSound(context);
}
};
}

View File

@@ -0,0 +1,358 @@
/*
* 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.notification;
import android.annotation.UserIdInt;
import android.app.Dialog;
import android.app.settings.SettingsEnums;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.media.Ringtone;
import android.media.RingtoneManager;
import android.net.Uri;
import android.os.Bundle;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
import androidx.annotation.VisibleForTesting;
import androidx.appcompat.app.AlertDialog;
import androidx.fragment.app.FragmentManager;
import androidx.preference.Preference;
import androidx.preference.PreferenceGroup;
import androidx.preference.PreferenceScreen;
import androidx.preference.TwoStatePreference;
import com.android.settings.DefaultRingtonePreference;
import com.android.settings.R;
import com.android.settings.Utils;
import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
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.OnPause;
import com.android.settingslib.core.lifecycle.events.OnResume;
/** Controller that manages the Sounds settings relevant preferences for work profile. */
public class SoundWorkSettingsController extends AbstractPreferenceController
implements Preference.OnPreferenceChangeListener, LifecycleObserver, OnResume, OnPause {
private static final String TAG = "SoundWorkSettingsController";
private static final String KEY_WORK_USE_PERSONAL_SOUNDS = "work_use_personal_sounds";
private static final String KEY_WORK_PHONE_RINGTONE = "work_ringtone";
private static final String KEY_WORK_NOTIFICATION_RINGTONE = "work_notification_ringtone";
private static final String KEY_WORK_ALARM_RINGTONE = "work_alarm_ringtone";
private final boolean mVoiceCapable;
private final UserManager mUserManager;
private final SoundWorkSettings mParent;
private final AudioHelper mHelper;
private TwoStatePreference mWorkUsePersonalSounds;
private Preference mWorkPhoneRingtonePreference;
private Preference mWorkNotificationRingtonePreference;
private Preference mWorkAlarmRingtonePreference;
private PreferenceScreen mScreen;
@UserIdInt
private int mManagedProfileId;
private final BroadcastReceiver mManagedProfileReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
final int userId = ((UserHandle) intent.getExtra(Intent.EXTRA_USER)).getIdentifier();
switch (intent.getAction()) {
case Intent.ACTION_MANAGED_PROFILE_ADDED: {
onManagedProfileAdded(userId);
return;
}
case Intent.ACTION_MANAGED_PROFILE_REMOVED: {
onManagedProfileRemoved(userId);
return;
}
}
}
};
public SoundWorkSettingsController(Context context, SoundWorkSettings parent,
Lifecycle lifecycle) {
this(context, parent, lifecycle, new AudioHelper(context));
}
@VisibleForTesting
SoundWorkSettingsController(Context context, SoundWorkSettings parent, Lifecycle lifecycle,
AudioHelper helper) {
super(context);
mUserManager = UserManager.get(context);
mVoiceCapable = Utils.isVoiceCapable(mContext);
mParent = parent;
mHelper = helper;
if (lifecycle != null) {
lifecycle.addObserver(this);
}
}
@Override
public void displayPreference(PreferenceScreen screen) {
super.displayPreference(screen);
mScreen = screen;
}
@Override
public void onResume() {
IntentFilter managedProfileFilter = new IntentFilter();
managedProfileFilter.addAction(Intent.ACTION_MANAGED_PROFILE_ADDED);
managedProfileFilter.addAction(Intent.ACTION_MANAGED_PROFILE_REMOVED);
mContext.registerReceiver(mManagedProfileReceiver, managedProfileFilter);
mManagedProfileId = mHelper.getManagedProfileId(mUserManager);
updateWorkPreferences();
}
@Override
public void onPause() {
mContext.unregisterReceiver(mManagedProfileReceiver);
}
@Override
public boolean isAvailable() {
return mHelper.getManagedProfileId(mUserManager) != UserHandle.USER_NULL
&& shouldShowRingtoneSettings();
}
@Override
public boolean handlePreferenceTreeClick(Preference preference) {
return false;
}
@Override
public String getPreferenceKey() {
return null;
}
/**
* Updates the summary of work preferences
*
* This controller listens to changes on the work ringtone preferences, identified by keys
* "work_ringtone", "work_notification_ringtone" and "work_alarm_ringtone".
*/
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
int ringtoneType;
if (KEY_WORK_PHONE_RINGTONE.equals(preference.getKey())) {
ringtoneType = RingtoneManager.TYPE_RINGTONE;
} else if (KEY_WORK_NOTIFICATION_RINGTONE.equals(preference.getKey())) {
ringtoneType = RingtoneManager.TYPE_NOTIFICATION;
} else if (KEY_WORK_ALARM_RINGTONE.equals(preference.getKey())) {
ringtoneType = RingtoneManager.TYPE_ALARM;
} else {
return true;
}
preference.setSummary(updateRingtoneName(getManagedProfileContext(), ringtoneType));
return true;
}
private boolean shouldShowRingtoneSettings() {
return !mHelper.isSingleVolume();
}
private CharSequence updateRingtoneName(Context context, int type) {
if (context == null || !mHelper.isUserUnlocked(mUserManager, context.getUserId())) {
return mContext.getString(R.string.managed_profile_not_available_label);
}
Uri ringtoneUri = RingtoneManager.getActualDefaultRingtoneUri(context, type);
return Ringtone.getTitle(context, ringtoneUri, false /* followSettingsUri */,
/* allowRemote= */ true);
}
private Context getManagedProfileContext() {
if (mManagedProfileId == UserHandle.USER_NULL) {
return null;
}
return mHelper.createPackageContextAsUser(mManagedProfileId);
}
private DefaultRingtonePreference initWorkPreference(PreferenceGroup root, String key) {
final DefaultRingtonePreference pref = root.findPreference(key);
pref.setOnPreferenceChangeListener(this);
// Required so that RingtonePickerActivity lists the work profile ringtones
pref.setUserId(mManagedProfileId);
return pref;
}
private void updateWorkPreferences() {
if (!isAvailable()) {
return;
}
if (mWorkUsePersonalSounds == null) {
mWorkUsePersonalSounds = mScreen.findPreference(KEY_WORK_USE_PERSONAL_SOUNDS);
mWorkUsePersonalSounds.setOnPreferenceChangeListener((Preference p, Object value) -> {
if ((boolean) value) {
SoundWorkSettingsController.UnifyWorkDialogFragment.show(mParent);
return false;
} else {
disableWorkSync();
return true;
}
});
}
if (mWorkPhoneRingtonePreference == null) {
mWorkPhoneRingtonePreference = initWorkPreference(mScreen,
KEY_WORK_PHONE_RINGTONE);
}
if (mWorkNotificationRingtonePreference == null) {
mWorkNotificationRingtonePreference = initWorkPreference(mScreen,
KEY_WORK_NOTIFICATION_RINGTONE);
}
if (mWorkAlarmRingtonePreference == null) {
mWorkAlarmRingtonePreference = initWorkPreference(mScreen,
KEY_WORK_ALARM_RINGTONE);
}
if (!mVoiceCapable) {
mWorkPhoneRingtonePreference.setVisible(false);
mWorkPhoneRingtonePreference = null;
}
final Context managedProfileContext = getManagedProfileContext();
if (Settings.Secure.getIntForUser(managedProfileContext.getContentResolver(),
Settings.Secure.SYNC_PARENT_SOUNDS, /* def= */ 0, mManagedProfileId) == 1) {
enableWorkSyncSettings();
} else {
disableWorkSyncSettings();
}
}
void enableWorkSync() {
RingtoneManager.enableSyncFromParent(getManagedProfileContext());
enableWorkSyncSettings();
}
private void enableWorkSyncSettings() {
mWorkUsePersonalSounds.setChecked(true);
if (mWorkPhoneRingtonePreference != null) {
mWorkPhoneRingtonePreference.setSummary(R.string.work_sound_same_as_personal);
}
mWorkNotificationRingtonePreference.setSummary(R.string.work_sound_same_as_personal);
mWorkAlarmRingtonePreference.setSummary(R.string.work_sound_same_as_personal);
}
private void disableWorkSync() {
RingtoneManager.disableSyncFromParent(getManagedProfileContext());
disableWorkSyncSettings();
}
private void disableWorkSyncSettings() {
if (mWorkPhoneRingtonePreference != null) {
mWorkPhoneRingtonePreference.setEnabled(true);
}
mWorkNotificationRingtonePreference.setEnabled(true);
mWorkAlarmRingtonePreference.setEnabled(true);
updateWorkRingtoneSummaries();
}
private void updateWorkRingtoneSummaries() {
Context managedProfileContext = getManagedProfileContext();
if (mWorkPhoneRingtonePreference != null) {
mWorkPhoneRingtonePreference.setSummary(
updateRingtoneName(managedProfileContext, RingtoneManager.TYPE_RINGTONE));
}
mWorkNotificationRingtonePreference.setSummary(
updateRingtoneName(managedProfileContext, RingtoneManager.TYPE_NOTIFICATION));
mWorkAlarmRingtonePreference.setSummary(
updateRingtoneName(managedProfileContext, RingtoneManager.TYPE_ALARM));
}
/**
* Update work preferences if work profile added.
* @param profileId the profile identifier.
*/
public void onManagedProfileAdded(@UserIdInt int profileId) {
if (mManagedProfileId == UserHandle.USER_NULL) {
mManagedProfileId = profileId;
updateWorkPreferences();
}
}
/**
* Update work preferences if work profile removed.
* @param profileId the profile identifier.
*/
public void onManagedProfileRemoved(@UserIdInt int profileId) {
if (mManagedProfileId == profileId) {
mManagedProfileId = mHelper.getManagedProfileId(mUserManager);
updateWorkPreferences();
}
}
/**
* Dialog to confirm with the user if it's ok to use the personal profile sounds as the work
* profile sounds.
*/
public static class UnifyWorkDialogFragment extends InstrumentedDialogFragment
implements DialogInterface.OnClickListener {
private static final String TAG = "UnifyWorkDialogFragment";
private static final int REQUEST_CODE = 200;
/**
* Show dialog that allows to use the personal profile sounds as the work profile sounds.
* @param parent SoundWorkSettings fragment.
*/
public static void show(SoundWorkSettings parent) {
FragmentManager fm = parent.getFragmentManager();
if (fm.findFragmentByTag(TAG) == null) {
UnifyWorkDialogFragment fragment = new UnifyWorkDialogFragment();
fragment.setTargetFragment(parent, REQUEST_CODE);
fragment.show(fm, TAG);
}
}
@Override
public int getMetricsCategory() {
return SettingsEnums.DIALOG_UNIFY_SOUND_SETTINGS;
}
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
return new AlertDialog.Builder(getActivity())
.setTitle(R.string.work_sync_dialog_title)
.setMessage(R.string.work_sync_dialog_message)
.setPositiveButton(R.string.work_sync_dialog_yes,
SoundWorkSettingsController.UnifyWorkDialogFragment.this)
.setNegativeButton(android.R.string.no, /* listener= */ null)
.create();
}
@Override
public void onClick(DialogInterface dialog, int which) {
SoundWorkSettings soundWorkSettings = (SoundWorkSettings) getTargetFragment();
if (soundWorkSettings.isAdded()) {
soundWorkSettings.enableWorkSync();
}
}
}
}

View File

@@ -54,6 +54,9 @@ import com.android.settingslib.core.lifecycle.events.OnResume;
import java.util.List;
/**
* TODO(b/183670633): Remove this file when silky flag deprecated.
*/
public class WorkSoundPreferenceController extends AbstractPreferenceController
implements PreferenceControllerMixin, OnPreferenceChangeListener, LifecycleObserver,
OnResume, OnPause {

View File

@@ -0,0 +1,35 @@
/*
* 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.notification;
import android.content.Context;
import com.android.settings.core.BasePreferenceController;
/** This controller manages the work profile sounds preference. */
public class WorkSoundsPreferenceController extends BasePreferenceController {
public WorkSoundsPreferenceController(Context context, String preferenceKey) {
super(context, preferenceKey);
}
@Override
public int getAvailabilityStatus() {
return SoundWorkSettings.isSupportWorkProfileSound(mContext) ? AVAILABLE
: DISABLED_FOR_USER;
}
}

View File

@@ -190,8 +190,8 @@ public class CredentialManagementAppAdapter extends RecyclerView.Adapter<Recycle
}
private String getNumberOfUrlsText(Map<Uri, String> urisToAliases) {
String url = urisToAliases.size() > 1 ? " URLs" : " URL";
return urisToAliases.size() + url;
return mContext.getResources().getQuantityString(R.plurals.number_of_urls,
urisToAliases.size(), urisToAliases.size());
}
}

View File

@@ -139,7 +139,7 @@ public class BatteryHeaderPreferenceControllerTest {
mController.updateHeaderPreference(mBatteryInfo);
verify(mBatteryUsageProgressBarPref).setTotalSummary(mBatteryInfo.remainingLabel);
verify(mBatteryUsageProgressBarPref).setBottomSummary(mBatteryInfo.remainingLabel);
}
@Test
@@ -151,7 +151,7 @@ public class BatteryHeaderPreferenceControllerTest {
mController.updateHeaderPreference(mBatteryInfo);
verify(mBatteryUsageProgressBarPref).setUsageSummary(formatBatteryPercentageText());
verify(mBatteryUsageProgressBarPref).setTotalSummary(mBatteryInfo.remainingLabel);
verify(mBatteryUsageProgressBarPref).setBottomSummary(mBatteryInfo.remainingLabel);
verify(mBatteryUsageProgressBarPref).setPercent(BATTERY_LEVEL, BATTERY_MAX_LEVEL);
}
@@ -162,7 +162,7 @@ public class BatteryHeaderPreferenceControllerTest {
mController.updateHeaderPreference(mBatteryInfo);
verify(mBatteryUsageProgressBarPref).setTotalSummary(BATTERY_STATUS);
verify(mBatteryUsageProgressBarPref).setBottomSummary(BATTERY_STATUS);
}
@Test
@@ -171,7 +171,7 @@ public class BatteryHeaderPreferenceControllerTest {
mController.updateHeaderPreference(mBatteryInfo);
verify(mBatteryUsageProgressBarPref).setTotalSummary(null);
verify(mBatteryUsageProgressBarPref).setBottomSummary(null);
}
@Test

View File

@@ -153,7 +153,7 @@ public class PowerMenuPreferenceControllerTest {
mShadowPackageManager.setSystemFeature(CONTROLS_FEATURE, false);
when(mResources.getBoolean(
com.android.internal.R.bool.config_longPressOnPowerForAssistantSettingAvailable))
.thenReturn(true);
.thenReturn(false);
assertThat(mController.getAvailabilityStatus()).isEqualTo(
BasePreferenceController.CONDITIONALLY_UNAVAILABLE);

View File

@@ -0,0 +1,189 @@
/*
* 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.notification;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.Context;
import android.os.UserHandle;
import android.os.UserManager;
import android.telephony.TelephonyManager;
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
import androidx.preference.TwoStatePreference;
import com.android.settings.DefaultRingtonePreference;
import com.android.settings.R;
import com.android.settings.RingtonePreference;
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 SoundWorkSettingsControllerTest {
private static final String KEY_WORK_USE_PERSONAL_SOUNDS = "work_use_personal_sounds";
private static final String KEY_WORK_PHONE_RINGTONE = "work_ringtone";
private static final String KEY_WORK_NOTIFICATION_RINGTONE = "work_notification_ringtone";
private static final String KEY_WORK_ALARM_RINGTONE = "work_alarm_ringtone";
@Mock
private Context mContext;
@Mock
private PreferenceScreen mScreen;
@Mock
private TelephonyManager mTelephonyManager;
@Mock
private AudioHelper mAudioHelper;
@Mock
private SoundWorkSettings mFragment;
private SoundWorkSettingsController mController;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
when(mContext.getSystemService(Context.TELEPHONY_SERVICE)).thenReturn(mTelephonyManager);
when(mTelephonyManager.isVoiceCapable()).thenReturn(true);
when(mFragment.getPreferenceScreen()).thenReturn(mScreen);
when(mScreen.findPreference(KEY_WORK_USE_PERSONAL_SOUNDS))
.thenReturn(mock(TwoStatePreference.class));
when(mScreen.findPreference(KEY_WORK_PHONE_RINGTONE))
.thenReturn(mock(DefaultRingtonePreference.class));
when(mScreen.findPreference(KEY_WORK_NOTIFICATION_RINGTONE))
.thenReturn(mock(DefaultRingtonePreference.class));
when(mScreen.findPreference(KEY_WORK_ALARM_RINGTONE))
.thenReturn(mock(DefaultRingtonePreference.class));
mController = new SoundWorkSettingsController(mContext, mFragment, null, mAudioHelper);
}
@Test
public void isAvailable_managedProfileAndNotSingleVolume_shouldReturnTrue() {
when(mAudioHelper.getManagedProfileId(nullable(UserManager.class)))
.thenReturn(UserHandle.myUserId());
when(mAudioHelper.isSingleVolume()).thenReturn(false);
assertThat(mController.isAvailable()).isTrue();
}
@Test
public void isAvailable_noManagedProfile_shouldReturnFalse() {
when(mAudioHelper.getManagedProfileId(nullable(UserManager.class)))
.thenReturn(UserHandle.USER_NULL);
when(mAudioHelper.isSingleVolume()).thenReturn(false);
assertThat(mController.isAvailable()).isFalse();
}
@Test
public void isAvailable_singleVolume_shouldReturnFalse() {
when(mAudioHelper.getManagedProfileId(nullable(UserManager.class)))
.thenReturn(UserHandle.myUserId());
when(mAudioHelper.isSingleVolume()).thenReturn(true);
assertThat(mController.isAvailable()).isFalse();
}
@Test
public void onPreferenceChange_shouldUpdateSummary() {
final Preference preference = mock(Preference.class);
when(preference.getKey()).thenReturn(KEY_WORK_PHONE_RINGTONE);
mController.onPreferenceChange(preference, "hello");
verify(preference).setSummary(nullable(String.class));
}
@Test
public void onResume_noVoiceCapability_shouldHidePhoneRingtone() {
when(mTelephonyManager.isVoiceCapable()).thenReturn(false);
mController = new SoundWorkSettingsController(mContext, mFragment, null, mAudioHelper);
when(mAudioHelper.getManagedProfileId(nullable(UserManager.class)))
.thenReturn(UserHandle.myUserId());
when(mAudioHelper.isUserUnlocked(nullable(UserManager.class), anyInt())).thenReturn(true);
when(mAudioHelper.isSingleVolume()).thenReturn(false);
when(mAudioHelper.createPackageContextAsUser(anyInt())).thenReturn(mContext);
// Precondition: work profile is available.
assertThat(mController.isAvailable()).isTrue();
mController.displayPreference(mScreen);
mController.onResume();
verify((Preference) mScreen.findPreference(KEY_WORK_PHONE_RINGTONE)).setVisible(false);
}
@Test
public void onResume_availableButLocked_shouldRedactPreferences() {
final String notAvailable = "(not available)";
when(mContext.getString(R.string.managed_profile_not_available_label))
.thenReturn(notAvailable);
// Given a device with a managed profile:
when(mAudioHelper.isSingleVolume()).thenReturn(false);
when(mAudioHelper.createPackageContextAsUser(anyInt())).thenReturn(mContext);
when(mAudioHelper.getManagedProfileId(nullable(UserManager.class)))
.thenReturn(UserHandle.myUserId());
when(mAudioHelper.isUserUnlocked(nullable(UserManager.class), anyInt())).thenReturn(false);
// When resumed:
mController.displayPreference(mScreen);
mController.onResume();
// Sound preferences should explain that the profile isn't available yet.
verify((Preference) mScreen.findPreference(KEY_WORK_PHONE_RINGTONE))
.setSummary(eq(notAvailable));
verify((Preference) mScreen.findPreference(KEY_WORK_NOTIFICATION_RINGTONE))
.setSummary(eq(notAvailable));
verify((Preference) mScreen.findPreference(KEY_WORK_ALARM_RINGTONE))
.setSummary(eq(notAvailable));
}
@Test
public void onResume_shouldSetUserIdToPreference() {
final int managedProfileUserId = 10;
when(mAudioHelper.getManagedProfileId(nullable(UserManager.class)))
.thenReturn(managedProfileUserId);
when(mAudioHelper.isUserUnlocked(nullable(UserManager.class), anyInt())).thenReturn(true);
when(mAudioHelper.isSingleVolume()).thenReturn(false);
when(mAudioHelper.createPackageContextAsUser(anyInt())).thenReturn(mContext);
mController.displayPreference(mScreen);
mController.onResume();
verify((RingtonePreference) mScreen.findPreference(KEY_WORK_PHONE_RINGTONE))
.setUserId(managedProfileUserId);
verify((RingtonePreference) mScreen.findPreference(KEY_WORK_NOTIFICATION_RINGTONE))
.setUserId(managedProfileUserId);
verify((RingtonePreference) mScreen.findPreference(KEY_WORK_ALARM_RINGTONE))
.setUserId(managedProfileUserId);
}
}

View File

@@ -0,0 +1,76 @@
/*
* 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.notification;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
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.os.UserHandle;
import androidx.fragment.app.FragmentActivity;
import androidx.preference.Preference;
import com.android.settings.DefaultRingtonePreference;
import com.android.settings.testutils.FakeFeatureFactory;
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
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;
import org.robolectric.util.ReflectionHelpers;
@RunWith(RobolectricTestRunner.class)
public class SoundWorkSettingsTest {
@Mock
private FragmentActivity mActivity;
@Mock
private MetricsFeatureProvider mMetricsFeatureProvider;
private FakeFeatureFactory mFeatureFactory;
private SoundWorkSettings mFragment;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mFragment = spy(new SoundWorkSettings());
when(mFragment.getActivity()).thenReturn(mActivity);
mFeatureFactory = FakeFeatureFactory.setupForTest();
mMetricsFeatureProvider = mFeatureFactory.getMetricsFeatureProvider();
ReflectionHelpers.setField(mFragment, "mMetricsFeatureProvider", mMetricsFeatureProvider);
}
@Test
public void onPreferenceTreeClick_isRingtonePreference_shouldStartActivity() {
final DefaultRingtonePreference ringtonePreference = mock(DefaultRingtonePreference.class);
when(mMetricsFeatureProvider.logClickedPreference(any(Preference.class),
anyInt())).thenReturn(true);
mFragment.onPreferenceTreeClick(ringtonePreference);
verify(mActivity).startActivityForResultAsUser(any(), anyInt(), any(),
any(UserHandle.class));
}
}

View File

@@ -0,0 +1,73 @@
/*
* 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.notification;
import static com.android.settings.core.BasePreferenceController.AVAILABLE;
import static com.android.settings.core.BasePreferenceController.DISABLED_FOR_USER;
import static com.google.common.truth.Truth.assertThat;
import android.content.Context;
import android.os.UserHandle;
import android.util.FeatureFlagUtils;
import com.android.settings.core.FeatureFlags;
import com.android.settings.testutils.shadow.ShadowAudioHelper;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
@Config(shadows = ShadowAudioHelper.class)
@RunWith(RobolectricTestRunner.class)
public class WorkSoundsPreferenceControllerTest {
private Context mContext;
private WorkSoundsPreferenceController mController;
@Before
public void setUp() {
mContext = RuntimeEnvironment.application;
mController = new WorkSoundsPreferenceController(mContext, "test_key");
}
@After
public void tearDown() {
ShadowAudioHelper.reset();
}
@Test
public void getAvailabilityStatus_supportWorkProfileSound_shouldReturnAvailable() {
FeatureFlagUtils.setEnabled(mContext, FeatureFlags.SILKY_HOME, true);
ShadowAudioHelper.setIsSingleVolume(false);
ShadowAudioHelper.setManagedProfileId(UserHandle.USER_CURRENT);
assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
}
@Test
public void getAvailabilityStatus_notSupportWorkProfileSound_shouldReturnDisabled() {
ShadowAudioHelper.setIsSingleVolume(true);
ShadowAudioHelper.setManagedProfileId(UserHandle.USER_NULL);
assertThat(mController.getAvailabilityStatus()).isEqualTo(DISABLED_FOR_USER);
}
}

View File

@@ -23,17 +23,35 @@ import com.android.settings.notification.AudioHelper;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import org.robolectric.annotation.Resetter;
@Implements(AudioHelper.class)
public class ShadowAudioHelper {
private static boolean sIsSingleVolume = true;
private static int sManagedProfileId = UserHandle.USER_CURRENT;
@Resetter
public static void reset() {
sIsSingleVolume = true;
sManagedProfileId = UserHandle.USER_CURRENT;
}
public static void setIsSingleVolume(boolean isSingleVolume) {
sIsSingleVolume = isSingleVolume;
}
public static void setManagedProfileId(int managedProfileId) {
sManagedProfileId = managedProfileId;
}
@Implementation
protected boolean isSingleVolume() {
return true;
return sIsSingleVolume;
}
@Implementation
protected int getManagedProfileId(UserManager um) {
return UserHandle.USER_CURRENT;
return sManagedProfileId;
}
}

View File

@@ -217,15 +217,6 @@ public class StorageEntryTest {
assertThat(publicStorage.isPublic()).isTrue();
}
@Test
public void isStub_stubVolume_shouldReturnTrue() {
final VolumeInfo stubVolumeInfo = mock(VolumeInfo.class);
final StorageEntry stubStorage = new StorageEntry(mContext, stubVolumeInfo);
when(stubVolumeInfo.getType()).thenReturn(VolumeInfo.TYPE_STUB);
assertThat(stubStorage.isStub()).isTrue();
}
@Test
public void isPrivate_nonVolumeInfo_shouldReturnFalse() {
final DiskInfo diskInfo = mock(DiskInfo.class);