diff --git a/res/layout/confirm_convert_fbe.xml b/res/layout/confirm_convert_fbe.xml
deleted file mode 100644
index 537c3680ba6..00000000000
--- a/res/layout/confirm_convert_fbe.xml
+++ /dev/null
@@ -1,46 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/res/layout/convert_fbe.xml b/res/layout/convert_fbe.xml
deleted file mode 100644
index d1e0ceae20a..00000000000
--- a/res/layout/convert_fbe.xml
+++ /dev/null
@@ -1,46 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/res/layout/le_audio_bt_entity_header.xml b/res/layout/le_audio_bt_entity_header.xml
new file mode 100644
index 00000000000..6e2a1e85b07
--- /dev/null
+++ b/res/layout/le_audio_bt_entity_header.xml
@@ -0,0 +1,137 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/res/raw/udfps_edge_hint_lottie.json b/res/raw/udfps_left_edge_hint_lottie.json
similarity index 100%
rename from res/raw/udfps_edge_hint_lottie.json
rename to res/raw/udfps_left_edge_hint_lottie.json
diff --git a/res/raw/udfps_right_edge_hint_lottie.json b/res/raw/udfps_right_edge_hint_lottie.json
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index c44a1cbfa46..8a59da2844b 100755
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -399,6 +399,13 @@
27.5dp
-4dp
+
+ 5dp
+ 10dp
+ 6dp
+ 20dp
+ 1.5dp
+
8dp
8dp
diff --git a/res/values/strings.xml b/res/values/strings.xml
index cea5481ced2..72e3cf826e5 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -98,10 +98,10 @@
Make larger
- Use Auto-rotate
+ Use auto-rotate
- Face Detection uses the front-facing camera to improve Auto-rotate accuracy. Images are never stored or sent to Google.
+ Face Detection uses the front-facing camera to improve auto-rotate accuracy. Images are never stored or sent to Google.
Sample text
@@ -2909,7 +2909,11 @@
On - Face-based
- Enable Face Detection
+ Face Detection
+
+ Automatically adjust the screen orientation when you move your phone between portrait and landscape
+
+ Learn more about auto-rotate
Screen resolution
@@ -5405,7 +5409,7 @@
To get started\n1. Go to accessibility settings\n2. Select a feature and tap the shortcut\n3. Choose whether you want to use a button or gesture to access the feature
- To get started\n1. Go to accessibility settings\n2. Select a feature and tap the shortcut\n3. Choose the button to use to access the feature
+ To get started\n1. Go to accessibility settings\n2. Select a feature and tap the shortcut\n3. Choose the button to access the feature
Use button or gesture
@@ -8296,7 +8300,7 @@
dont don\u2019t disturb, interrupt, interruption, break
RAM
nearby, location, history, reporting, GPS
- account, add an account, work profile, add account
+ account, add an account, work profile, add account, remove, delete
restriction, restrict, restricted
text correction, correct, sound, vibrate, auto, language, gesture, suggest, suggestion, theme, offensive, word, type, emoji, international
reset, preferences, default
@@ -9870,7 +9874,7 @@
Who can interrupt
- Even if messaging or calling apps can\u0027t notify you, people you choose here can still reach you through those apps
+ Even if messaging or calling apps can\u0027t notify you, people you choose here can still reach you through those apps
Calls
@@ -11614,11 +11618,6 @@
See more
-
- Really wipe user data and convert to file encryption?
-
- Wipe and convert
-
+
+
+
diff --git a/res/xml/auto_rotate_settings.xml b/res/xml/auto_rotate_settings.xml
index 0c120d68fff..adfb3773cf8 100644
--- a/res/xml/auto_rotate_settings.xml
+++ b/res/xml/auto_rotate_settings.xml
@@ -17,9 +17,19 @@
+
+
+
+
+ android:order="100"
+ android:key="auto_rotate_footer_preference"
+ android:title="@string/smart_rotate_text_headline"
+ android:selectable="true"
+ settings:searchable="false"/>
diff --git a/res/xml/bluetooth_device_details_fragment.xml b/res/xml/bluetooth_device_details_fragment.xml
index ef5a9909cf9..f6c0af662aa 100644
--- a/res/xml/bluetooth_device_details_fragment.xml
+++ b/res/xml/bluetooth_device_details_fragment.xml
@@ -34,6 +34,14 @@
settings:searchable="false"
settings:controller="com.android.settings.bluetooth.AdvancedBluetoothDetailsHeaderController"/>
+
+
diff --git a/res/xml/development_settings.xml b/res/xml/development_settings.xml
index 75395c0235e..98674196d93 100644
--- a/res/xml/development_settings.xml
+++ b/res/xml/development_settings.xml
@@ -92,12 +92,6 @@
android:summary="@string/runningservices_settings_summary"
android:fragment="com.android.settings.applications.RunningServices" />
-
-
{
- if (!runKeyguardConfirmation(KEYGUARD_REQUEST)) {
- convert();
- }
- });
-
- return rootView;
- }
-
- @Override
- public void onActivityResult(int requestCode, int resultCode, Intent data) {
- super.onActivityResult(requestCode, resultCode, data);
-
- if (requestCode != KEYGUARD_REQUEST) {
- return;
- }
-
- // If the user entered a valid keyguard credential, start the conversion
- // process
- if (resultCode == Activity.RESULT_OK) {
- convert();
- }
- }
-
- private void convert() {
- new SubSettingLauncher(getContext())
- .setDestination(ConfirmConvertToFbe.class.getName())
- .setTitleRes(R.string.convert_to_file_encryption)
- .setSourceMetricsCategory(getMetricsCategory())
- .launch();
- }
-
- @Override
- public int getMetricsCategory() {
- return SettingsEnums.CONVERT_FBE;
- }
-}
diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrolling.java b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrolling.java
index d42e8f1c3aa..70140c42c8c 100644
--- a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrolling.java
+++ b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrolling.java
@@ -81,9 +81,11 @@ public class FingerprintEnrollEnrolling extends BiometricsEnrollEnrolling {
private static final int STAGE_CENTER = 0;
private static final int STAGE_GUIDED = 1;
private static final int STAGE_FINGERTIP = 2;
- private static final int STAGE_EDGES = 3;
+ private static final int STAGE_LEFT_EDGE = 3;
+ private static final int STAGE_RIGHT_EDGE = 4;
- @IntDef({STAGE_UNKNOWN, STAGE_CENTER, STAGE_GUIDED, STAGE_FINGERTIP, STAGE_EDGES})
+ @IntDef({STAGE_UNKNOWN, STAGE_CENTER, STAGE_GUIDED, STAGE_FINGERTIP, STAGE_LEFT_EDGE,
+ STAGE_RIGHT_EDGE})
@Retention(RetentionPolicy.SOURCE)
private @interface EnrollStage {}
@@ -132,7 +134,8 @@ public class FingerprintEnrollEnrolling extends BiometricsEnrollEnrolling {
private boolean mIsAccessibilityEnabled;
private LottieAnimationView mIllustrationLottie;
private boolean mHaveShownUdfpsTipLottie;
- private boolean mHaveShownUdfpsSideLottie;
+ private boolean mHaveShownUdfpsLeftEdgeLottie;
+ private boolean mHaveShownUdfpsRightEdgeLottie;
private boolean mShouldShowLottie;
private OrientationEventListener mOrientationEventListener;
@@ -372,12 +375,31 @@ public class FingerprintEnrollEnrolling extends BiometricsEnrollEnrolling {
}
break;
- case STAGE_EDGES:
+ case STAGE_LEFT_EDGE:
setHeaderText(R.string.security_settings_udfps_enroll_edge_title);
- if (!mHaveShownUdfpsSideLottie && mIllustrationLottie != null) {
- mHaveShownUdfpsSideLottie = true;
+ if (!mHaveShownUdfpsLeftEdgeLottie && mIllustrationLottie != null) {
+ mHaveShownUdfpsLeftEdgeLottie = true;
setDescriptionText("");
- mIllustrationLottie.setAnimation(R.raw.udfps_edge_hint_lottie);
+ mIllustrationLottie.setAnimation(R.raw.udfps_left_edge_hint_lottie);
+ mIllustrationLottie.setVisibility(View.VISIBLE);
+ mIllustrationLottie.playAnimation();
+ mIllustrationLottie.setContentDescription(
+ getString(R.string.security_settings_udfps_side_fingerprint_help));
+ } else if (mIllustrationLottie == null) {
+ if (isStageHalfCompleted()) {
+ setDescriptionText(
+ R.string.security_settings_fingerprint_enroll_repeat_message);
+ } else {
+ setDescriptionText(R.string.security_settings_udfps_enroll_edge_message);
+ }
+ }
+ break;
+ case STAGE_RIGHT_EDGE:
+ setHeaderText(R.string.security_settings_udfps_enroll_edge_title);
+ if (!mHaveShownUdfpsRightEdgeLottie && mIllustrationLottie != null) {
+ mHaveShownUdfpsRightEdgeLottie = true;
+ setDescriptionText("");
+ mIllustrationLottie.setAnimation(R.raw.udfps_right_edge_hint_lottie);
mIllustrationLottie.setVisibility(View.VISIBLE);
mIllustrationLottie.playAnimation();
mIllustrationLottie.setContentDescription(
@@ -422,8 +444,10 @@ public class FingerprintEnrollEnrolling extends BiometricsEnrollEnrolling {
return STAGE_GUIDED;
} else if (progressSteps < getStageThresholdSteps(2)) {
return STAGE_FINGERTIP;
+ } else if (progressSteps < getStageThresholdSteps(3)) {
+ return STAGE_LEFT_EDGE;
} else {
- return STAGE_EDGES;
+ return STAGE_RIGHT_EDGE;
}
}
diff --git a/src/com/android/settings/bluetooth/BluetoothDetailsHeaderController.java b/src/com/android/settings/bluetooth/BluetoothDetailsHeaderController.java
index 9f5e78e0396..9c7aa58cf7c 100644
--- a/src/com/android/settings/bluetooth/BluetoothDetailsHeaderController.java
+++ b/src/com/android/settings/bluetooth/BluetoothDetailsHeaderController.java
@@ -16,6 +16,7 @@
package com.android.settings.bluetooth;
+import android.bluetooth.BluetoothProfile;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.text.TextUtils;
@@ -53,7 +54,10 @@ public class BluetoothDetailsHeaderController extends BluetoothDetailsController
@Override
public boolean isAvailable() {
- return !Utils.isAdvancedDetailsHeader(mCachedDevice.getDevice());
+ boolean hasLeAudio = mCachedDevice.getConnectableProfiles()
+ .stream()
+ .anyMatch(profile -> profile.getProfileId() == BluetoothProfile.LE_AUDIO);
+ return !Utils.isAdvancedDetailsHeader(mCachedDevice.getDevice()) && !hasLeAudio;
}
@Override
diff --git a/src/com/android/settings/bluetooth/BluetoothDetailsProfilesController.java b/src/com/android/settings/bluetooth/BluetoothDetailsProfilesController.java
index aacf41fbbc9..b57ea928d99 100644
--- a/src/com/android/settings/bluetooth/BluetoothDetailsProfilesController.java
+++ b/src/com/android/settings/bluetooth/BluetoothDetailsProfilesController.java
@@ -16,10 +16,12 @@
package com.android.settings.bluetooth;
+import android.bluetooth.BluetoothCsipSetCoordinator;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothProfile;
import android.content.Context;
import android.text.TextUtils;
+import android.util.Log;
import androidx.annotation.VisibleForTesting;
import androidx.preference.Preference;
@@ -31,6 +33,7 @@ import androidx.preference.SwitchPreference;
import com.android.settings.R;
import com.android.settingslib.bluetooth.A2dpProfile;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
+import com.android.settingslib.bluetooth.LeAudioProfile;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.settingslib.bluetooth.LocalBluetoothProfile;
import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
@@ -39,7 +42,10 @@ import com.android.settingslib.bluetooth.PanProfile;
import com.android.settingslib.bluetooth.PbapServerProfile;
import com.android.settingslib.core.lifecycle.Lifecycle;
+import java.util.ArrayList;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
/**
* This class adds switches for toggling the individual profiles that a Bluetooth device
@@ -48,8 +54,11 @@ import java.util.List;
public class BluetoothDetailsProfilesController extends BluetoothDetailsController
implements Preference.OnPreferenceClickListener,
LocalBluetoothProfileManager.ServiceListener {
+ private static final String TAG = "BtDetailsProfilesCtrl";
+
private static final String KEY_PROFILES_GROUP = "bluetooth_profiles";
private static final String KEY_BOTTOM_PREFERENCE = "bottom_preference";
+ private static final String HEADSET_CLIENT = "HEADSET_CLIENT";
private static final int ORDINAL = 99;
@VisibleForTesting
@@ -58,6 +67,9 @@ public class BluetoothDetailsProfilesController extends BluetoothDetailsControll
private LocalBluetoothManager mManager;
private LocalBluetoothProfileManager mProfileManager;
private CachedBluetoothDevice mCachedDevice;
+ private List mAllOfCachedDevices;
+ private Map> mProfileDeviceMap =
+ new HashMap>();
@VisibleForTesting
PreferenceCategory mProfilesContainer;
@@ -68,6 +80,7 @@ public class BluetoothDetailsProfilesController extends BluetoothDetailsControll
mManager = manager;
mProfileManager = mManager.getProfileManager();
mCachedDevice = device;
+ mAllOfCachedDevices = getAllOfCachedBluetoothDevices();
lifecycle.addObserver(this);
}
@@ -100,11 +113,66 @@ public class BluetoothDetailsProfilesController extends BluetoothDetailsControll
/**
* Refreshes the state for an existing SwitchPreference for a profile.
+ * If the LeAudio profile is enabled on the LeAudio devices, then the SwitchPreferences of
+ * A2dp profile and Hfp profile are graied out.
*/
private void refreshProfilePreference(SwitchPreference profilePref,
LocalBluetoothProfile profile) {
BluetoothDevice device = mCachedDevice.getDevice();
- profilePref.setEnabled(!mCachedDevice.isBusy());
+ boolean isLeAudioEnabled = false;
+ if (profile instanceof A2dpProfile || HEADSET_CLIENT.equals(profile.toString())) {
+ LocalBluetoothProfile leAudio = mProfileManager.getLeAudioProfile();
+ if (leAudio != null) {
+ List leAudioDeviceList = mProfileDeviceMap.get(
+ leAudio.toString());
+ if (leAudioDeviceList != null
+ && leAudioDeviceList.stream()
+ .anyMatch(item -> leAudio.isEnabled(item.getDevice()))) {
+ isLeAudioEnabled = true;
+ }
+ }
+ if (isLeAudioEnabled) {
+ // If the LeAudio profile is enabled on the LeAudio devices, then the
+ // SwitchPreferences of A2dp profile and Hfp profile are graied out.
+ profilePref.setEnabled(false);
+ } else {
+ List deviceList = mProfileDeviceMap.get(
+ profile.toString());
+ boolean isBusy = deviceList != null
+ && deviceList.stream().anyMatch(item -> item.isBusy());
+ profilePref.setEnabled(!isBusy);
+ }
+ } else if (profile instanceof LeAudioProfile) {
+ List leAudioDeviceList = mProfileDeviceMap.get(
+ profile.toString());
+ boolean isLeAudioProfileEnable =
+ leAudioDeviceList != null && leAudioDeviceList.stream().anyMatch(
+ item -> profile.isEnabled(item.getDevice()));
+ boolean isBusy = leAudioDeviceList != null
+ && leAudioDeviceList.stream().anyMatch(item -> item.isBusy());
+ if (isLeAudioProfileEnable && !isBusy) {
+ LocalBluetoothProfile a2dp = mProfileManager.getA2dpProfile();
+ LocalBluetoothProfile hfp = mProfileManager.getHfpClientProfile();
+ // If the LeAudio profile is enabled on the LeAudio devices, then the
+ // SwitchPreferences of A2dp profile and Hfp profile are graied out.
+ if (a2dp != null) {
+ SwitchPreference pref = mProfilesContainer.findPreference(a2dp.toString());
+ if (pref != null) {
+ pref.setEnabled(false);
+ }
+ }
+ if (hfp != null) {
+ SwitchPreference pref = mProfilesContainer.findPreference(hfp.toString());
+ if (pref != null) {
+ pref.setEnabled(false);
+ }
+ }
+ }
+ profilePref.setEnabled(!isBusy);
+ } else {
+ profilePref.setEnabled(!mCachedDevice.isBusy());
+ }
+
if (profile instanceof MapProfile) {
profilePref.setChecked(device.getMessageAccessPermission()
== BluetoothDevice.ACCESS_ALLOWED);
@@ -127,7 +195,7 @@ public class BluetoothDetailsProfilesController extends BluetoothDetailsControll
highQualityPref.setVisible(true);
highQualityPref.setTitle(a2dp.getHighQualityAudioOptionLabel(device));
highQualityPref.setChecked(a2dp.isHighQualityAudioEnabled(device));
- highQualityPref.setEnabled(!mCachedDevice.isBusy());
+ highQualityPref.setEnabled(!mCachedDevice.isBusy() && !isLeAudioEnabled);
} else {
highQualityPref.setVisible(false);
}
@@ -148,6 +216,12 @@ public class BluetoothDetailsProfilesController extends BluetoothDetailsControll
if (profile instanceof MapProfile) {
bluetoothDevice.setMessageAccessPermission(BluetoothDevice.ACCESS_ALLOWED);
}
+
+ if (profile instanceof LeAudioProfile) {
+ enableLeAudioProfile(profile);
+ return;
+ }
+
profile.setEnabled(bluetoothDevice, true);
}
@@ -155,8 +229,14 @@ public class BluetoothDetailsProfilesController extends BluetoothDetailsControll
* Helper method to disable a profile for a device
*/
private void disableProfile(LocalBluetoothProfile profile) {
+ if (profile instanceof LeAudioProfile) {
+ disableLeAudioProfile(profile);
+ return;
+ }
+
final BluetoothDevice bluetoothDevice = mCachedDevice.getDevice();
profile.setEnabled(bluetoothDevice, false);
+
if (profile instanceof MapProfile) {
bluetoothDevice.setMessageAccessPermission(BluetoothDevice.ACCESS_REJECTED);
} else if (profile instanceof PbapServerProfile) {
@@ -190,14 +270,33 @@ public class BluetoothDetailsProfilesController extends BluetoothDetailsControll
return true;
}
-
/**
* Helper to get the list of connectable and special profiles.
*/
private List getProfiles() {
- List result = mCachedDevice.getConnectableProfiles();
- final BluetoothDevice device = mCachedDevice.getDevice();
+ List result = new ArrayList();
+ mProfileDeviceMap.clear();
+ if (mAllOfCachedDevices == null || mAllOfCachedDevices.isEmpty()) {
+ return result;
+ }
+ for (CachedBluetoothDevice cachedItem : mAllOfCachedDevices) {
+ List tmpResult = cachedItem.getConnectableProfiles();
+ for (LocalBluetoothProfile profile : tmpResult) {
+ if (mProfileDeviceMap.containsKey(profile.toString())) {
+ mProfileDeviceMap.get(profile.toString()).add(cachedItem);
+ Log.d(TAG, "getProfiles: " + profile.toString() + " add device "
+ + cachedItem.getDevice().getAnonymizedAddress());
+ } else {
+ List tmpCachedDeviceList =
+ new ArrayList();
+ tmpCachedDeviceList.add(cachedItem);
+ mProfileDeviceMap.put(profile.toString(), tmpCachedDeviceList);
+ result.add(profile);
+ }
+ }
+ }
+ final BluetoothDevice device = mCachedDevice.getDevice();
final int pbapPermission = device.getPhonebookAccessPermission();
// Only provide PBAP cabability if the client device has requested PBAP.
if (pbapPermission != BluetoothDevice.ACCESS_UNKNOWN) {
@@ -210,10 +309,93 @@ public class BluetoothDetailsProfilesController extends BluetoothDetailsControll
if (mapPermission != BluetoothDevice.ACCESS_UNKNOWN) {
result.add(mapProfile);
}
-
+ Log.d(TAG, "getProfiles:result:" + result);
return result;
}
+ private List getAllOfCachedBluetoothDevices() {
+ List cachedBluetoothDevices = new ArrayList<>();
+ if (mCachedDevice == null) {
+ return cachedBluetoothDevices;
+ }
+ cachedBluetoothDevices.add(mCachedDevice);
+ if (mCachedDevice.getGroupId() != BluetoothCsipSetCoordinator.GROUP_ID_INVALID) {
+ for (CachedBluetoothDevice member : mCachedDevice.getMemberDevice()) {
+ cachedBluetoothDevices.add(member);
+ }
+ }
+ return cachedBluetoothDevices;
+ }
+
+ /**
+ * When user disable the Le Audio profile, the system needs to do two things.
+ * 1) Disable the Le Audio profile for each of the Le Audio devices.
+ * 2) Enable the A2dp profile and Hfp profile for the associated device. The system can't
+ * enable the A2dp profile and Hfp profile if the Le Audio profile is enabled.
+ *
+ * @param profile the LeAudio profile
+ */
+ private void disableLeAudioProfile(LocalBluetoothProfile profile) {
+ if (profile == null || mProfileDeviceMap.get(profile.toString()) == null) {
+ Log.e(TAG, "There is no the LE profile or no device in mProfileDeviceMap. Do nothing.");
+ return;
+ }
+ for (CachedBluetoothDevice leAudioDevice : mProfileDeviceMap.get(profile.toString())) {
+ profile.setEnabled(leAudioDevice.getDevice(), false);
+ }
+
+ LocalBluetoothProfile a2dp = mProfileManager.getA2dpProfile();
+ LocalBluetoothProfile hfp = mProfileManager.getHfpClientProfile();
+ if (a2dp != null && mProfileDeviceMap.get(a2dp.toString()) != null) {
+ for (CachedBluetoothDevice a2dpDevice : mProfileDeviceMap.get(a2dp.toString())) {
+ if (!a2dp.isEnabled(a2dpDevice.getDevice())) {
+ a2dp.setEnabled(a2dpDevice.getDevice(), true);
+ }
+ }
+ }
+ if (hfp != null && mProfileDeviceMap.get(hfp.toString()) != null) {
+ for (CachedBluetoothDevice hfpDevice : mProfileDeviceMap.get(hfp.toString())) {
+ if (!hfp.isEnabled(hfpDevice.getDevice())) {
+ hfp.setEnabled(hfpDevice.getDevice(), true);
+ }
+ }
+ }
+ }
+
+ /**
+ * When user enable the Le Audio profile, the system needs to do two things.
+ * 1) Disable the A2dp profile and Hfp profile for the associated device. The system can't
+ * enable the Le Audio if the A2dp profile and Hfp profile are enabled.
+ * 2) Enable the Le Audio profile for each of the Le Audio devices.
+ *
+ * @param profile the LeAudio profile
+ */
+ private void enableLeAudioProfile(LocalBluetoothProfile profile) {
+ if (profile == null || mProfileDeviceMap.get(profile.toString()) == null) {
+ Log.e(TAG, "There is no the LE profile or no device in mProfileDeviceMap. Do nothing.");
+ return;
+ }
+ LocalBluetoothProfile a2dp = mProfileManager.getA2dpProfile();
+ LocalBluetoothProfile hfp = mProfileManager.getHfpClientProfile();
+ if (a2dp != null && mProfileDeviceMap.get(a2dp.toString()) != null) {
+ for (CachedBluetoothDevice a2dpDevice : mProfileDeviceMap.get(a2dp.toString())) {
+ if (a2dp.isEnabled(a2dpDevice.getDevice())) {
+ a2dp.setEnabled(a2dpDevice.getDevice(), false);
+ }
+ }
+ }
+ if (hfp != null && mProfileDeviceMap.get(hfp.toString()) != null) {
+ for (CachedBluetoothDevice hfpDevice : mProfileDeviceMap.get(hfp.toString())) {
+ if (hfp.isEnabled(hfpDevice.getDevice())) {
+ hfp.setEnabled(hfpDevice.getDevice(), false);
+ }
+ }
+ }
+ for (CachedBluetoothDevice leAudioDevice : mProfileDeviceMap.get(profile.toString())) {
+ profile.setEnabled(leAudioDevice.getDevice(), true);
+ }
+ }
+
/**
* This is a helper method to be called after adding a Preference for a profile. If that
* profile happened to be A2dp and the device supports high quality audio, it will add a
@@ -243,16 +425,33 @@ public class BluetoothDetailsProfilesController extends BluetoothDetailsControll
@Override
public void onPause() {
- super.onPause();
+ for (CachedBluetoothDevice item : mAllOfCachedDevices) {
+ item.unregisterCallback(this);
+ }
mProfileManager.removeServiceListener(this);
}
@Override
public void onResume() {
- super.onResume();
+ for (CachedBluetoothDevice item : mAllOfCachedDevices) {
+ item.registerCallback(this);
+ }
mProfileManager.addServiceListener(this);
}
+ @Override
+ public void onDeviceAttributesChanged() {
+ for (CachedBluetoothDevice item : mAllOfCachedDevices) {
+ item.unregisterCallback(this);
+ }
+ mAllOfCachedDevices = getAllOfCachedBluetoothDevices();
+ for (CachedBluetoothDevice item : mAllOfCachedDevices) {
+ item.registerCallback(this);
+ }
+
+ super.onDeviceAttributesChanged();
+ }
+
@Override
public void onServiceConnected() {
refresh();
diff --git a/src/com/android/settings/bluetooth/BluetoothDeviceDetailsFragment.java b/src/com/android/settings/bluetooth/BluetoothDeviceDetailsFragment.java
index 653248263bd..6d443ee4044 100644
--- a/src/com/android/settings/bluetooth/BluetoothDeviceDetailsFragment.java
+++ b/src/com/android/settings/bluetooth/BluetoothDeviceDetailsFragment.java
@@ -117,6 +117,7 @@ public class BluetoothDeviceDetailsFragment extends RestrictedDashboardFragment
return;
}
use(AdvancedBluetoothDetailsHeaderController.class).init(mCachedDevice);
+ use(LeAudioBluetoothDetailsHeaderController.class).init(mCachedDevice, mManager);
final BluetoothFeatureProvider featureProvider = FeatureFactory.getFactory(
context).getBluetoothFeatureProvider(context);
diff --git a/src/com/android/settings/bluetooth/LeAudioBluetoothDetailsHeaderController.java b/src/com/android/settings/bluetooth/LeAudioBluetoothDetailsHeaderController.java
new file mode 100644
index 00000000000..06cee852442
--- /dev/null
+++ b/src/com/android/settings/bluetooth/LeAudioBluetoothDetailsHeaderController.java
@@ -0,0 +1,323 @@
+/*
+ * Copyright (C) 2022 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.bluetooth;
+
+import android.bluetooth.BluetoothCsipSetCoordinator;
+import android.bluetooth.BluetoothLeAudio;
+import android.bluetooth.BluetoothProfile;
+import android.content.Context;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffColorFilter;
+import android.graphics.drawable.Drawable;
+import android.os.Handler;
+import android.os.Looper;
+import android.util.Log;
+import android.util.Pair;
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import androidx.annotation.VisibleForTesting;
+import androidx.preference.PreferenceScreen;
+
+import com.android.settings.R;
+import com.android.settings.core.BasePreferenceController;
+import com.android.settings.fuelgauge.BatteryMeterView;
+import com.android.settingslib.bluetooth.BluetoothUtils;
+import com.android.settingslib.bluetooth.CachedBluetoothDevice;
+import com.android.settingslib.bluetooth.LeAudioProfile;
+import com.android.settingslib.bluetooth.LocalBluetoothManager;
+import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
+import com.android.settingslib.core.lifecycle.LifecycleObserver;
+import com.android.settingslib.core.lifecycle.events.OnDestroy;
+import com.android.settingslib.core.lifecycle.events.OnStart;
+import com.android.settingslib.core.lifecycle.events.OnStop;
+import com.android.settingslib.widget.LayoutPreference;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This class adds a header with device name and status (connected/disconnected, etc.).
+ */
+public class LeAudioBluetoothDetailsHeaderController extends BasePreferenceController implements
+ LifecycleObserver, OnStart, OnStop, OnDestroy, CachedBluetoothDevice.Callback {
+ private static final String TAG = "LeAudioBtHeaderCtrl";
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+ @VisibleForTesting
+ static final int LEFT_DEVICE_ID =
+ BluetoothLeAudio.AUDIO_LOCATION_FRONT_LEFT
+ | BluetoothLeAudio.AUDIO_LOCATION_BACK_LEFT
+ | BluetoothLeAudio.AUDIO_LOCATION_FRONT_LEFT_OF_CENTER
+ | BluetoothLeAudio.AUDIO_LOCATION_SIDE_LEFT
+ | BluetoothLeAudio.AUDIO_LOCATION_TOP_FRONT_LEFT
+ | BluetoothLeAudio.AUDIO_LOCATION_TOP_BACK_LEFT
+ | BluetoothLeAudio.AUDIO_LOCATION_TOP_SIDE_LEFT
+ | BluetoothLeAudio.AUDIO_LOCATION_BOTTOM_FRONT_LEFT
+ | BluetoothLeAudio.AUDIO_LOCATION_FRONT_LEFT_WIDE
+ | BluetoothLeAudio.AUDIO_LOCATION_LEFT_SURROUND;
+
+ @VisibleForTesting
+ static final int RIGHT_DEVICE_ID =
+ BluetoothLeAudio.AUDIO_LOCATION_FRONT_RIGHT
+ | BluetoothLeAudio.AUDIO_LOCATION_BACK_RIGHT
+ | BluetoothLeAudio.AUDIO_LOCATION_FRONT_RIGHT_OF_CENTER
+ | BluetoothLeAudio.AUDIO_LOCATION_SIDE_RIGHT
+ | BluetoothLeAudio.AUDIO_LOCATION_TOP_FRONT_RIGHT
+ | BluetoothLeAudio.AUDIO_LOCATION_TOP_BACK_RIGHT
+ | BluetoothLeAudio.AUDIO_LOCATION_TOP_SIDE_RIGHT
+ | BluetoothLeAudio.AUDIO_LOCATION_BOTTOM_FRONT_RIGHT
+ | BluetoothLeAudio.AUDIO_LOCATION_FRONT_RIGHT_WIDE
+ | BluetoothLeAudio.AUDIO_LOCATION_RIGHT_SURROUND;
+
+ @VisibleForTesting
+ static final int INVALID_RESOURCE_ID = -1;
+
+ @VisibleForTesting
+ LayoutPreference mLayoutPreference;
+ private CachedBluetoothDevice mCachedDevice;
+ @VisibleForTesting
+ Handler mHandler = new Handler(Looper.getMainLooper());
+ @VisibleForTesting
+ boolean mIsRegisterCallback = false;
+
+ private LocalBluetoothProfileManager mProfileManager;
+
+ public LeAudioBluetoothDetailsHeaderController(Context context, String prefKey) {
+ super(context, prefKey);
+ }
+
+ @Override
+ public int getAvailabilityStatus() {
+ if (mCachedDevice == null || mProfileManager == null) {
+ return CONDITIONALLY_UNAVAILABLE;
+ }
+ boolean hasLeAudio = mCachedDevice.getConnectableProfiles()
+ .stream()
+ .anyMatch(profile -> profile.getProfileId() == BluetoothProfile.LE_AUDIO);
+
+ return !Utils.isAdvancedDetailsHeader(mCachedDevice.getDevice()) && hasLeAudio
+ ? AVAILABLE : CONDITIONALLY_UNAVAILABLE;
+ }
+
+ @Override
+ public void displayPreference(PreferenceScreen screen) {
+ super.displayPreference(screen);
+ mLayoutPreference = screen.findPreference(getPreferenceKey());
+ mLayoutPreference.setVisible(isAvailable());
+ }
+
+ @Override
+ public void onStart() {
+ if (!isAvailable()) {
+ return;
+ }
+ mIsRegisterCallback = true;
+ mCachedDevice.registerCallback(this);
+ refresh();
+ }
+
+ @Override
+ public void onStop() {
+ if (!mIsRegisterCallback) {
+ return;
+ }
+ mCachedDevice.unregisterCallback(this);
+ mIsRegisterCallback = false;
+ }
+
+ @Override
+ public void onDestroy() {
+ }
+
+ public void init(CachedBluetoothDevice cachedBluetoothDevice,
+ LocalBluetoothManager bluetoothManager) {
+ mCachedDevice = cachedBluetoothDevice;
+ mProfileManager = bluetoothManager.getProfileManager();
+ }
+
+ @VisibleForTesting
+ void refresh() {
+ if (mLayoutPreference == null || mCachedDevice == null) {
+ return;
+ }
+ final ImageView imageView = mLayoutPreference.findViewById(R.id.entity_header_icon);
+ if (imageView != null) {
+ final Pair pair =
+ BluetoothUtils.getBtRainbowDrawableWithDescription(mContext, mCachedDevice);
+ imageView.setImageDrawable(pair.first);
+ imageView.setContentDescription(pair.second);
+ }
+
+ final TextView title = mLayoutPreference.findViewById(R.id.entity_header_title);
+ if (title != null) {
+ title.setText(mCachedDevice.getName());
+ }
+ final TextView summary = mLayoutPreference.findViewById(R.id.entity_header_summary);
+ if (summary != null) {
+ summary.setText(mCachedDevice.getConnectionSummary(true /* shortSummary */));
+ }
+
+ if (!mCachedDevice.isConnected() || mCachedDevice.isBusy()) {
+ hideAllOfBatteryLayouts();
+ return;
+ }
+
+ updateBatteryLayout();
+ }
+
+ @VisibleForTesting
+ Drawable createBtBatteryIcon(Context context, int level) {
+ final BatteryMeterView.BatteryMeterDrawable drawable =
+ new BatteryMeterView.BatteryMeterDrawable(context,
+ context.getColor(R.color.meter_background_color),
+ context.getResources().getDimensionPixelSize(
+ R.dimen.advanced_bluetooth_battery_meter_width),
+ context.getResources().getDimensionPixelSize(
+ R.dimen.advanced_bluetooth_battery_meter_height));
+ drawable.setBatteryLevel(level);
+ drawable.setColorFilter(new PorterDuffColorFilter(
+ com.android.settings.Utils.getColorAttrDefaultColor(context,
+ android.R.attr.colorControlNormal),
+ PorterDuff.Mode.SRC));
+ return drawable;
+ }
+
+ private int getBatteryTitleResource(int deviceId) {
+ if (deviceId == LEFT_DEVICE_ID) {
+ return R.id.bt_battery_left_title;
+ }
+ if (deviceId == RIGHT_DEVICE_ID) {
+ return R.id.bt_battery_right_title;
+ }
+ Log.d(TAG, "No resource id. The deviceId is " + deviceId);
+ return INVALID_RESOURCE_ID;
+ }
+
+ private int getBatterySummaryResource(int deviceId) {
+ if (deviceId == LEFT_DEVICE_ID) {
+ return R.id.bt_battery_left_summary;
+ }
+ if (deviceId == RIGHT_DEVICE_ID) {
+ return R.id.bt_battery_right_summary;
+ }
+ Log.d(TAG, "No resource id. The deviceId is " + deviceId);
+ return INVALID_RESOURCE_ID;
+ }
+
+ private void hideAllOfBatteryLayouts() {
+ // hide the case
+ updateBatteryLayout(R.id.bt_battery_case_title, R.id.bt_battery_case_summary,
+ BluetoothUtils.META_INT_ERROR);
+ // hide the left
+ updateBatteryLayout(R.id.bt_battery_left_title, R.id.bt_battery_left_summary,
+ BluetoothUtils.META_INT_ERROR);
+ // hide the right
+ updateBatteryLayout(R.id.bt_battery_right_title, R.id.bt_battery_right_summary,
+ BluetoothUtils.META_INT_ERROR);
+ }
+
+ private List getAllOfLeAudioDevices() {
+ if (mCachedDevice == null) {
+ return null;
+ }
+ List leAudioDevices = new ArrayList<>();
+ leAudioDevices.add(mCachedDevice);
+ if (mCachedDevice.getGroupId() != BluetoothCsipSetCoordinator.GROUP_ID_INVALID) {
+ for (CachedBluetoothDevice member : mCachedDevice.getMemberDevice()) {
+ leAudioDevices.add(member);
+ }
+ }
+ return leAudioDevices;
+ }
+
+ private void updateBatteryLayout() {
+ // Init the battery layouts.
+ hideAllOfBatteryLayouts();
+ final List leAudioDevices = getAllOfLeAudioDevices();
+ LeAudioProfile leAudioProfile = mProfileManager.getLeAudioProfile();
+ if (leAudioDevices == null || leAudioDevices.isEmpty()) {
+ Log.e(TAG, "There is no LeAudioProfile.");
+ return;
+ }
+
+ if (!leAudioProfile.isEnabled(mCachedDevice.getDevice())) {
+ Log.d(TAG, "Show the legacy battery style if the LeAudio is not enabled.");
+ final TextView summary = mLayoutPreference.findViewById(R.id.entity_header_summary);
+ if (summary != null) {
+ summary.setText(mCachedDevice.getConnectionSummary());
+ }
+ return;
+ }
+
+ for (CachedBluetoothDevice cachedDevice : leAudioDevices) {
+ int deviceId = leAudioProfile.getAudioLocation(cachedDevice.getDevice());
+ Log.d(TAG, "LeAudioDevices:" + cachedDevice.getDevice().getAnonymizedAddress()
+ + ", deviceId:" + deviceId);
+
+ if (deviceId == BluetoothLeAudio.AUDIO_LOCATION_INVALID) {
+ Log.d(TAG, "The device does not support the AUDIO_LOCATION.");
+ return;
+ }
+ boolean isLeft = (deviceId & LEFT_DEVICE_ID) != 0;
+ boolean isRight = (deviceId & LEFT_DEVICE_ID) != 0;
+ boolean isLeftRight = isLeft && isRight;
+ // The LE device updates the BatteryLayout
+ if (isLeftRight) {
+ Log.d(TAG, "The device id is left+right. Do nothing.");
+ } else if (isLeft) {
+ updateBatteryLayout(getBatteryTitleResource(LEFT_DEVICE_ID),
+ getBatterySummaryResource(LEFT_DEVICE_ID), cachedDevice.getBatteryLevel());
+ } else if (isRight) {
+ updateBatteryLayout(getBatteryTitleResource(RIGHT_DEVICE_ID),
+ getBatterySummaryResource(RIGHT_DEVICE_ID), cachedDevice.getBatteryLevel());
+ } else {
+ Log.d(TAG, "The device id is other Audio Location. Do nothing.");
+ }
+ }
+ }
+
+ private void updateBatteryLayout(int titleResId, int summaryResId, int batteryLevel) {
+ final TextView batteryTitleView = mLayoutPreference.findViewById(titleResId);
+ final TextView batterySummaryView = mLayoutPreference.findViewById(summaryResId);
+ if (batteryTitleView == null || batterySummaryView == null) {
+ Log.e(TAG, "updateBatteryLayout: No TextView");
+ return;
+ }
+ if (batteryLevel != BluetoothUtils.META_INT_ERROR) {
+ batteryTitleView.setVisibility(View.VISIBLE);
+ batterySummaryView.setVisibility(View.VISIBLE);
+ batterySummaryView.setText(
+ com.android.settings.Utils.formatPercentage(batteryLevel));
+ batterySummaryView.setCompoundDrawablesRelativeWithIntrinsicBounds(
+ createBtBatteryIcon(mContext, batteryLevel), /* top */ null,
+ /* end */ null, /* bottom */ null);
+ } else {
+ Log.d(TAG, "updateBatteryLayout: Hide it if it doesn't have battery information.");
+ batteryTitleView.setVisibility(View.GONE);
+ batterySummaryView.setVisibility(View.GONE);
+ }
+ }
+
+ @Override
+ public void onDeviceAttributesChanged() {
+ if (mCachedDevice != null) {
+ refresh();
+ }
+ }
+}
diff --git a/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java b/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java
index 636404eb89c..085a372c0ea 100644
--- a/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java
+++ b/src/com/android/settings/development/DevelopmentSettingsDashboardFragment.java
@@ -505,7 +505,6 @@ public class DevelopmentSettingsDashboardFragment extends RestrictedDashboardFra
controllers.add(new HdcpCheckingPreferenceController(context));
controllers.add(new BluetoothSnoopLogPreferenceController(context));
controllers.add(new OemUnlockPreferenceController(context, activity, fragment));
- controllers.add(new FileEncryptionPreferenceController(context));
controllers.add(new PictureColorModePreferenceController(context, lifecycle));
controllers.add(new WebViewAppPreferenceController(context));
controllers.add(new CoolColorTemperaturePreferenceController(context));
diff --git a/src/com/android/settings/development/FileEncryptionPreferenceController.java b/src/com/android/settings/development/FileEncryptionPreferenceController.java
deleted file mode 100644
index 82a58bad8c2..00000000000
--- a/src/com/android/settings/development/FileEncryptionPreferenceController.java
+++ /dev/null
@@ -1,85 +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.development;
-
-import android.content.Context;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.os.storage.IStorageManager;
-import android.text.TextUtils;
-import android.sysprop.CryptoProperties;
-import androidx.annotation.VisibleForTesting;
-import androidx.preference.Preference;
-
-import com.android.settings.R;
-import com.android.settings.core.PreferenceControllerMixin;
-import com.android.settingslib.development.DeveloperOptionsPreferenceController;
-
-public class FileEncryptionPreferenceController extends DeveloperOptionsPreferenceController
- implements PreferenceControllerMixin {
-
- private static final String KEY_CONVERT_FBE = "convert_to_file_encryption";
- private static final String KEY_STORAGE_MANAGER = "mount";
-
- private final IStorageManager mStorageManager;
-
- public FileEncryptionPreferenceController(Context context) {
- super(context);
-
- mStorageManager = getStorageManager();
- }
-
- @Override
- public boolean isAvailable() {
- if (mStorageManager == null) {
- return false;
- }
-
- try {
- return mStorageManager.isConvertibleToFBE();
- } catch (RemoteException e) {
- return false;
- }
- }
-
- @Override
- public String getPreferenceKey() {
- return KEY_CONVERT_FBE;
- }
-
- @Override
- public void updateState(Preference preference) {
- if (CryptoProperties.type().orElse(CryptoProperties.type_values.NONE) !=
- CryptoProperties.type_values.FILE) {
- return;
- }
-
- mPreference.setEnabled(false);
- mPreference.setSummary(
- mContext.getResources().getString(R.string.convert_to_file_encryption_done));
- }
-
- private IStorageManager getStorageManager() {
- try {
- return IStorageManager.Stub.asInterface(
- ServiceManager.getService(KEY_STORAGE_MANAGER));
- } catch (VerifyError e) {
- // Used for tests since Robolectric cannot initialize this class.
- return null;
- }
- }
-}
\ No newline at end of file
diff --git a/src/com/android/settings/deviceinfo/StorageItemPreference.java b/src/com/android/settings/deviceinfo/StorageItemPreference.java
index d3549d45061..91102a031eb 100644
--- a/src/com/android/settings/deviceinfo/StorageItemPreference.java
+++ b/src/com/android/settings/deviceinfo/StorageItemPreference.java
@@ -16,6 +16,8 @@
package com.android.settings.deviceinfo;
+import android.animation.TypeEvaluator;
+import android.animation.ValueAnimator;
import android.content.Context;
import android.util.AttributeSet;
import android.widget.ProgressBar;
@@ -30,6 +32,7 @@ public class StorageItemPreference extends Preference {
public int userHandle;
private static final int UNINITIALIZED = -1;
+ private static final int ANIMATE_DURATION_IN_MILLIS = 1000;
private ProgressBar mProgressBar;
private static final int PROGRESS_MAX = 100;
@@ -46,15 +49,33 @@ public class StorageItemPreference extends Preference {
}
public void setStorageSize(long size, long total) {
- mStorageSize = size;
- setSummary(StorageUtils.getStorageSizeLabel(getContext(), size));
+ setStorageSize(size, total, false /* animate */);
+ }
- if (total == 0) {
- mProgressPercent = 0;
+ /**
+ * Set the storage size info with/without animation
+ */
+ public void setStorageSize(long size, long total, boolean animate) {
+ if (animate) {
+ TypeEvaluator longEvaluator =
+ (fraction, startValue, endValue) -> {
+ // Directly returns end value if fraction is 1.0 and the end value is 0.
+ if (fraction >= 1.0f && endValue == 0) {
+ return endValue;
+ }
+ return startValue + (long) (fraction * (endValue - startValue));
+ };
+ ValueAnimator valueAnimator = ValueAnimator.ofObject(longEvaluator, mStorageSize, size);
+ valueAnimator.setDuration(ANIMATE_DURATION_IN_MILLIS);
+ valueAnimator.addUpdateListener(
+ animation -> {
+ updateProgressBarAndSizeInfo((long) animation.getAnimatedValue(), total);
+ });
+ valueAnimator.start();
} else {
- mProgressPercent = (int)(size * PROGRESS_MAX / total);
+ updateProgressBarAndSizeInfo(size, total);
}
- updateProgressBar();
+ mStorageSize = size;
}
public long getStorageSize() {
@@ -62,11 +83,18 @@ public class StorageItemPreference extends Preference {
}
protected void updateProgressBar() {
- if (mProgressBar == null || mProgressPercent == UNINITIALIZED)
+ if (mProgressBar == null || mProgressPercent == UNINITIALIZED) {
return;
+ }
mProgressBar.setMax(PROGRESS_MAX);
- mProgressBar.setProgress(mProgressPercent, true /* animate */);
+ mProgressBar.setProgress(mProgressPercent);
+ }
+
+ private void updateProgressBarAndSizeInfo(long size, long total) {
+ setSummary(StorageUtils.getStorageSizeLabel(getContext(), size));
+ mProgressPercent = total == 0 ? 0 : (int) (size * PROGRESS_MAX / total);
+ updateProgressBar();
}
@Override
diff --git a/src/com/android/settings/deviceinfo/storage/StorageItemPreferenceController.java b/src/com/android/settings/deviceinfo/storage/StorageItemPreferenceController.java
index 9813439b235..7e27414ec2d 100644
--- a/src/com/android/settings/deviceinfo/storage/StorageItemPreferenceController.java
+++ b/src/com/android/settings/deviceinfo/storage/StorageItemPreferenceController.java
@@ -378,18 +378,22 @@ public class StorageItemPreferenceController extends AbstractPreferenceControlle
*/
public void onLoadFinished(@Nullable SparseArray result,
int userId) {
+ // Enable animation when the storage size info is from StorageAsyncLoader whereas disable
+ // animation when the cached storage size info is used instead.
+ boolean animate = result != null && mIsPreferenceOrderedBySize;
// Calculate the size info for each category
StorageCacheHelper.StorageCache storageCache = getSizeInfo(result, userId);
// Set size info to each preference
- mImagesPreference.setStorageSize(storageCache.imagesSize, mTotalSize);
- mVideosPreference.setStorageSize(storageCache.videosSize, mTotalSize);
- mAudioPreference.setStorageSize(storageCache.audioSize, mTotalSize);
- mAppsPreference.setStorageSize(storageCache.allAppsExceptGamesSize, mTotalSize);
- mGamesPreference.setStorageSize(storageCache.gamesSize, mTotalSize);
- mDocumentsAndOtherPreference.setStorageSize(storageCache.documentsAndOtherSize, mTotalSize);
- mTrashPreference.setStorageSize(storageCache.trashSize, mTotalSize);
+ mImagesPreference.setStorageSize(storageCache.imagesSize, mTotalSize, animate);
+ mVideosPreference.setStorageSize(storageCache.videosSize, mTotalSize, animate);
+ mAudioPreference.setStorageSize(storageCache.audioSize, mTotalSize, animate);
+ mAppsPreference.setStorageSize(storageCache.allAppsExceptGamesSize, mTotalSize, animate);
+ mGamesPreference.setStorageSize(storageCache.gamesSize, mTotalSize, animate);
+ mDocumentsAndOtherPreference.setStorageSize(storageCache.documentsAndOtherSize, mTotalSize,
+ animate);
+ mTrashPreference.setStorageSize(storageCache.trashSize, mTotalSize, animate);
if (mSystemPreference != null) {
- mSystemPreference.setStorageSize(storageCache.systemSize, mTotalSize);
+ mSystemPreference.setStorageSize(storageCache.systemSize, mTotalSize, animate);
}
// Cache the size info
if (result != null) {
@@ -519,7 +523,7 @@ public class StorageItemPreferenceController extends AbstractPreferenceControlle
if (mTrashPreference == null) {
return;
}
- mTrashPreference.setStorageSize(0, mTotalSize);
+ mTrashPreference.setStorageSize(0, mTotalSize, true /* animate */);
updatePrivateStorageCategoryPreferencesOrder();
}
diff --git a/src/com/android/settings/display/AutoRotateSwitchBarController.java b/src/com/android/settings/display/AutoRotateSwitchBarController.java
index 48dedfd1fa3..d76104aef7f 100644
--- a/src/com/android/settings/display/AutoRotateSwitchBarController.java
+++ b/src/com/android/settings/display/AutoRotateSwitchBarController.java
@@ -18,86 +18,78 @@ package com.android.settings.display;
import android.app.settings.SettingsEnums;
import android.content.Context;
-import android.widget.Switch;
import com.android.internal.view.RotationPolicy;
+import com.android.settings.R;
import com.android.settings.overlay.FeatureFactory;
-import com.android.settings.widget.SettingsMainSwitchBar;
+import com.android.settings.widget.SettingsMainSwitchPreferenceController;
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
-import com.android.settingslib.core.lifecycle.Lifecycle;
import com.android.settingslib.core.lifecycle.LifecycleObserver;
import com.android.settingslib.core.lifecycle.events.OnStart;
import com.android.settingslib.core.lifecycle.events.OnStop;
-import com.android.settingslib.widget.OnMainSwitchChangeListener;
/**
- * The switch controller for auto-rotate.
+ * The main switch controller for auto-rotate.
*/
-public class AutoRotateSwitchBarController implements OnMainSwitchChangeListener,
+public class AutoRotateSwitchBarController extends SettingsMainSwitchPreferenceController implements
LifecycleObserver, OnStart, OnStop {
- private final SettingsMainSwitchBar mSwitchBar;
- private final Context mContext;
- private boolean mValidListener;
private final MetricsFeatureProvider mMetricsFeatureProvider;
+ private RotationPolicy.RotationPolicyListener mRotationPolicyListener;
- public AutoRotateSwitchBarController(Context context, SettingsMainSwitchBar switchBar,
- Lifecycle lifecycle) {
- mSwitchBar = switchBar;
- mContext = context;
+ public AutoRotateSwitchBarController(Context context, String key) {
+ super(context, key);
mMetricsFeatureProvider = FeatureFactory.getFactory(context).getMetricsFeatureProvider();
- if (lifecycle != null) {
- lifecycle.addObserver(this);
- }
+ }
+
+ @Override
+ public int getAvailabilityStatus() {
+ return RotationPolicy.isRotationLockToggleVisible(mContext)
+ && !DeviceStateAutoRotationHelper.isDeviceStateRotationEnabled(mContext)
+ ? AVAILABLE : UNSUPPORTED_ON_DEVICE;
}
@Override
public void onStart() {
- if (!mValidListener) {
- mSwitchBar.addOnSwitchChangeListener(this);
- mValidListener = true;
+ if (mRotationPolicyListener == null) {
+ mRotationPolicyListener = new RotationPolicy.RotationPolicyListener() {
+ @Override
+ public void onChange() {
+ if (mSwitchPreference != null) {
+ updateState(mSwitchPreference);
+ }
+ }
+ };
}
- onChange();
+ RotationPolicy.registerRotationPolicyListener(mContext,
+ mRotationPolicyListener);
}
@Override
public void onStop() {
- if (mValidListener) {
- mSwitchBar.removeOnSwitchChangeListener(this);
- mValidListener = false;
+ if (mRotationPolicyListener != null) {
+ RotationPolicy.unregisterRotationPolicyListener(mContext, mRotationPolicyListener);
}
}
- /**
- * Listens to the state change of the rotation primary switch.
- */
@Override
- public void onSwitchChanged(Switch switchView, boolean isChecked) {
- setRotationLock(isChecked);
+ public boolean isChecked() {
+ return !RotationPolicy.isRotationLocked(mContext);
}
-
- protected void onChange() {
- final boolean isEnabled = !RotationPolicy.isRotationLocked(mContext);
- if (isEnabled != mSwitchBar.isChecked()) {
- // set listener to null so that that code below doesn't trigger onCheckedChanged()
- if (mValidListener) {
- mSwitchBar.removeOnSwitchChangeListener(this);
- }
- mSwitchBar.setChecked(isEnabled);
- if (mValidListener) {
- mSwitchBar.addOnSwitchChangeListener(this);
- }
- }
- }
-
- private boolean setRotationLock(boolean isChecked) {
+ @Override
+ public boolean setChecked(boolean isChecked) {
final boolean isLocked = !isChecked;
mMetricsFeatureProvider.action(mContext, SettingsEnums.ACTION_ROTATE_ROTATE_MASTER_TOGGLE,
- isChecked);
+ isLocked);
RotationPolicy.setRotationLock(mContext, isLocked);
return true;
}
+ @Override
+ public int getSliceHighlightMenuRes() {
+ return R.string.menu_key_display;
+ }
+
}
diff --git a/src/com/android/settings/display/SmartAutoRotatePreferenceFragment.java b/src/com/android/settings/display/SmartAutoRotatePreferenceFragment.java
index 9fda03c4d5f..8a6120c3e89 100644
--- a/src/com/android/settings/display/SmartAutoRotatePreferenceFragment.java
+++ b/src/com/android/settings/display/SmartAutoRotatePreferenceFragment.java
@@ -20,7 +20,7 @@ import static com.android.settings.display.SmartAutoRotateController.isRotationR
import android.app.settings.SettingsEnums;
import android.content.Context;
import android.os.Bundle;
-import android.text.Html;
+import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -28,12 +28,11 @@ import android.view.ViewGroup;
import androidx.annotation.VisibleForTesting;
import androidx.preference.Preference;
-import com.android.internal.view.RotationPolicy;
import com.android.settings.R;
import com.android.settings.SettingsActivity;
import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.search.BaseSearchIndexProvider;
-import com.android.settings.widget.SettingsMainSwitchBar;
+import com.android.settingslib.HelpUtils;
import com.android.settingslib.core.AbstractPreferenceController;
import com.android.settingslib.search.Indexable;
import com.android.settingslib.search.SearchIndexable;
@@ -51,9 +50,11 @@ public class SmartAutoRotatePreferenceFragment extends DashboardFragment {
private static final String TAG = "SmartAutoRotatePreferenceFragment";
- private RotationPolicy.RotationPolicyListener mRotationPolicyListener;
- private AutoRotateSwitchBarController mSwitchBarController;
- @VisibleForTesting static final String AUTO_ROTATE_SWITCH_PREFERENCE_ID = "auto_rotate_switch";
+ @VisibleForTesting
+ static final String AUTO_ROTATE_MAIN_SWITCH_PREFERENCE_KEY = "auto_rotate_main_switch";
+ @VisibleForTesting
+ static final String AUTO_ROTATE_SWITCH_PREFERENCE_KEY = "auto_rotate_switch";
+ private static final String KEY_FOOTER_PREFERENCE = "auto_rotate_footer_preference";
@Override
protected int getPreferenceScreenResId() {
@@ -70,6 +71,13 @@ public class SmartAutoRotatePreferenceFragment extends DashboardFragment {
);
}
+ @Override
+ public void onStart() {
+ super.onStart();
+ setupFooter();
+ }
+
+
@Override
protected List createPreferenceControllers(Context context) {
return DeviceStateAutoRotationHelper.createPreferenceControllers(context);
@@ -81,10 +89,8 @@ public class SmartAutoRotatePreferenceFragment extends DashboardFragment {
final View view = super.onCreateView(inflater, container, savedInstanceState);
final SettingsActivity activity = (SettingsActivity) getActivity();
createHeader(activity);
- final Preference footerPreference = findPreference(FooterPreference.KEY_FOOTER);
+ final Preference footerPreference = findPreference(KEY_FOOTER_PREFERENCE);
if (footerPreference != null) {
- footerPreference.setTitle(Html.fromHtml(getString(R.string.smart_rotate_text_headline),
- Html.FROM_HTML_MODE_COMPACT));
footerPreference.setVisible(isRotationResolverServiceAvailable(activity));
}
return view;
@@ -95,39 +101,9 @@ public class SmartAutoRotatePreferenceFragment extends DashboardFragment {
boolean deviceStateRotationEnabled =
DeviceStateAutoRotationHelper.isDeviceStateRotationEnabled(activity);
if (isRotationResolverServiceAvailable(activity) && !deviceStateRotationEnabled) {
- final SettingsMainSwitchBar switchBar = activity.getSwitchBar();
- switchBar.setTitle(
- getContext().getString(R.string.auto_rotate_settings_primary_switch_title));
- switchBar.show();
- mSwitchBarController = new AutoRotateSwitchBarController(activity, switchBar,
- getSettingsLifecycle());
- findPreference(AUTO_ROTATE_SWITCH_PREFERENCE_ID).setVisible(false);
- }
- }
-
- @Override
- public void onResume() {
- super.onResume();
- if (mRotationPolicyListener == null) {
- mRotationPolicyListener = new RotationPolicy.RotationPolicyListener() {
- @Override
- public void onChange() {
- if (mSwitchBarController != null) {
- mSwitchBarController.onChange();
- }
- }
- };
- }
- RotationPolicy.registerRotationPolicyListener(getPrefContext(),
- mRotationPolicyListener);
- }
-
- @Override
- public void onPause() {
- super.onPause();
- if (mRotationPolicyListener != null) {
- RotationPolicy.unregisterRotationPolicyListener(getPrefContext(),
- mRotationPolicyListener);
+ findPreference(AUTO_ROTATE_SWITCH_PREFERENCE_KEY).setVisible(false);
+ } else {
+ findPreference(AUTO_ROTATE_MAIN_SWITCH_PREFERENCE_KEY).setVisible(false);
}
}
@@ -141,6 +117,35 @@ public class SmartAutoRotatePreferenceFragment extends DashboardFragment {
return TAG;
}
+ @Override
+ public int getHelpResource() {
+ return R.string.help_url_auto_rotate_settings;
+ }
+
+ // Updates the footer for this page.
+ @VisibleForTesting
+ void setupFooter() {
+ final String mHelpUri = getString(getHelpResource());
+ if (!TextUtils.isEmpty(mHelpUri)) {
+ addHelpLink();
+ }
+ }
+
+ // Changes the text to include a learn more link if the link is defined.
+ @VisibleForTesting
+ void addHelpLink() {
+ final FooterPreference pref = findPreference(KEY_FOOTER_PREFERENCE);
+ if (pref != null) {
+ pref.setSelectable(false);
+ pref.setLearnMoreAction(v -> {
+ startActivityForResult(HelpUtils.getHelpIntent(getContext(),
+ getString(getHelpResource()),
+ /*backupContext=*/ ""), /*requestCode=*/ 0);
+ });
+ pref.setLearnMoreContentDescription(getString(R.string.auto_rotate_link_a11y));
+ }
+ }
+
public static final Indexable.SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
new BaseSearchIndexProvider(R.xml.auto_rotate_settings) {
diff --git a/src/com/android/settings/homepage/SettingsHomepageActivity.java b/src/com/android/settings/homepage/SettingsHomepageActivity.java
index 73c473a27ff..3ed305eff23 100644
--- a/src/com/android/settings/homepage/SettingsHomepageActivity.java
+++ b/src/com/android/settings/homepage/SettingsHomepageActivity.java
@@ -182,6 +182,8 @@ public class SettingsHomepageActivity extends FragmentActivity implements
showSuggestionFragment(scrollNeeded);
if (FeatureFlagUtils.isEnabled(this, FeatureFlags.CONTEXTUAL_HOME)) {
showFragment(() -> new ContextualCardsFragment(), R.id.contextual_cards_content);
+ ((FrameLayout) findViewById(R.id.main_content))
+ .getLayoutTransition().enableTransitionType(LayoutTransition.CHANGING);
}
}
mMainFragment = showFragment(() -> {
@@ -191,9 +193,6 @@ public class SettingsHomepageActivity extends FragmentActivity implements
return fragment;
}, R.id.main_content);
- ((FrameLayout) findViewById(R.id.main_content))
- .getLayoutTransition().enableTransitionType(LayoutTransition.CHANGING);
-
// Launch the intent from deep link for large screen devices.
launchDeepLinkIntentToRight();
}
diff --git a/src/com/android/settings/homepage/TopLevelHighlightMixin.java b/src/com/android/settings/homepage/TopLevelHighlightMixin.java
index ebfe7f204a6..c473e87eeca 100644
--- a/src/com/android/settings/homepage/TopLevelHighlightMixin.java
+++ b/src/com/android/settings/homepage/TopLevelHighlightMixin.java
@@ -23,7 +23,6 @@ import android.os.Parcelable;
import android.text.TextUtils;
import android.util.Log;
-import androidx.fragment.app.FragmentActivity;
import androidx.preference.PreferenceScreen;
import androidx.recyclerview.widget.RecyclerView;
@@ -43,14 +42,17 @@ public class TopLevelHighlightMixin implements Parcelable, DialogInterface.OnSho
private String mHiddenKey;
private DialogInterface mDialog;
private HighlightableTopLevelPreferenceAdapter mTopLevelAdapter;
+ private boolean mActivityEmbedded;
- public TopLevelHighlightMixin() {
+ public TopLevelHighlightMixin(boolean activityEmbedded) {
+ mActivityEmbedded = activityEmbedded;
}
public TopLevelHighlightMixin(Parcel source) {
mCurrentKey = source.readString();
mPreviousKey = source.readString();
mHiddenKey = source.readString();
+ mActivityEmbedded = source.readBoolean();
}
@Override
@@ -58,6 +60,7 @@ public class TopLevelHighlightMixin implements Parcelable, DialogInterface.OnSho
dest.writeString(mCurrentKey);
dest.writeString(mPreviousKey);
dest.writeString(mHiddenKey);
+ dest.writeBoolean(mActivityEmbedded);
}
@Override
@@ -96,8 +99,16 @@ public class TopLevelHighlightMixin implements Parcelable, DialogInterface.OnSho
}
}
+ void setActivityEmbedded(boolean activityEmbedded) {
+ mActivityEmbedded = activityEmbedded;
+ }
+
+ boolean isActivityEmbedded() {
+ return mActivityEmbedded;
+ }
+
RecyclerView.Adapter onCreateAdapter(TopLevelSettings topLevelSettings,
- PreferenceScreen preferenceScreen) {
+ PreferenceScreen preferenceScreen, boolean scrollNeeded) {
if (TextUtils.isEmpty(mCurrentKey)) {
mCurrentKey = getHighlightPrefKeyFromArguments(topLevelSettings.getArguments());
}
@@ -105,7 +116,7 @@ public class TopLevelHighlightMixin implements Parcelable, DialogInterface.OnSho
Log.d(TAG, "onCreateAdapter, pref key: " + mCurrentKey);
mTopLevelAdapter = new HighlightableTopLevelPreferenceAdapter(
(SettingsHomepageActivity) topLevelSettings.getActivity(), preferenceScreen,
- topLevelSettings.getListView(), mCurrentKey);
+ topLevelSettings.getListView(), mCurrentKey, scrollNeeded);
return mTopLevelAdapter;
}
@@ -129,7 +140,7 @@ public class TopLevelHighlightMixin implements Parcelable, DialogInterface.OnSho
}
}
- void highlightPreferenceIfNeeded(FragmentActivity activity) {
+ void highlightPreferenceIfNeeded() {
if (mTopLevelAdapter != null) {
mTopLevelAdapter.requestHighlight();
}
diff --git a/src/com/android/settings/homepage/TopLevelSettings.java b/src/com/android/settings/homepage/TopLevelSettings.java
index 7d578347e7e..334462b0b46 100644
--- a/src/com/android/settings/homepage/TopLevelSettings.java
+++ b/src/com/android/settings/homepage/TopLevelSettings.java
@@ -58,6 +58,7 @@ public class TopLevelSettings extends DashboardFragment implements
private boolean mIsEmbeddingActivityEnabled;
private TopLevelHighlightMixin mHighlightMixin;
+ private boolean mScrollNeeded = true;
private boolean mFirstStarted = true;
public TopLevelSettings() {
@@ -133,11 +134,14 @@ public class TopLevelSettings extends DashboardFragment implements
return;
}
+ boolean activityEmbedded = SplitController.getInstance().isActivityEmbedded(getActivity());
if (icicle != null) {
mHighlightMixin = icicle.getParcelable(SAVED_HIGHLIGHT_MIXIN);
+ mScrollNeeded = !mHighlightMixin.isActivityEmbedded() && activityEmbedded;
+ mHighlightMixin.setActivityEmbedded(activityEmbedded);
}
if (mHighlightMixin == null) {
- mHighlightMixin = new TopLevelHighlightMixin();
+ mHighlightMixin = new TopLevelHighlightMixin(activityEmbedded);
}
}
@@ -201,7 +205,7 @@ public class TopLevelSettings extends DashboardFragment implements
@Override
public void highlightPreferenceIfNeeded() {
if (mHighlightMixin != null) {
- mHighlightMixin.highlightPreferenceIfNeeded(getActivity());
+ mHighlightMixin.highlightPreferenceIfNeeded();
}
}
@@ -243,7 +247,7 @@ public class TopLevelSettings extends DashboardFragment implements
if (!mIsEmbeddingActivityEnabled || !(getActivity() instanceof SettingsHomepageActivity)) {
return super.onCreateAdapter(preferenceScreen);
}
- return mHighlightMixin.onCreateAdapter(this, preferenceScreen);
+ return mHighlightMixin.onCreateAdapter(this, preferenceScreen, mScrollNeeded);
}
@Override
diff --git a/src/com/android/settings/network/telephony/EnabledNetworkModePreferenceController.java b/src/com/android/settings/network/telephony/EnabledNetworkModePreferenceController.java
index 8e3f21a3a9d..6246b9178fe 100644
--- a/src/com/android/settings/network/telephony/EnabledNetworkModePreferenceController.java
+++ b/src/com/android/settings/network/telephony/EnabledNetworkModePreferenceController.java
@@ -251,6 +251,10 @@ public class EnabledNetworkModePreferenceController extends
}
void setPreferenceEntries() {
+ mTelephonyManager = mTelephonyManager.createForSubscriptionId(mSubId);
+ final PersistableBundle carrierConfig = mCarrierConfigCache.getConfigForSubId(mSubId);
+ final boolean display2gOptions = carrierConfig
+ .getBoolean(CarrierConfigManager.KEY_PREFER_2G_BOOL);
clearAllEntries();
String[] entryValues;
int[] entryValuesInt;
@@ -266,7 +270,9 @@ public class EnabledNetworkModePreferenceController extends
add5gEntry(addNrToLteNetworkType(entryValuesInt[0]));
addLteEntry(entryValuesInt[0]);
add3gEntry(entryValuesInt[1]);
- add1xEntry(entryValuesInt[2]);
+ if (display2gOptions) {
+ add1xEntry(entryValuesInt[2]);
+ }
addGlobalEntry(entryValuesInt[3]);
break;
case ENABLED_NETWORKS_CDMA_NO_LTE_CHOICES:
@@ -278,7 +284,9 @@ public class EnabledNetworkModePreferenceController extends
"ENABLED_NETWORKS_CDMA_NO_LTE_CHOICES index error.");
}
add3gEntry(entryValuesInt[0]);
- add1xEntry(entryValuesInt[1]);
+ if (display2gOptions) {
+ add1xEntry(entryValuesInt[1]);
+ }
break;
case ENABLED_NETWORKS_CDMA_ONLY_LTE_CHOICES:
entryValues = getResourcesForSubId().getStringArray(
@@ -302,7 +310,9 @@ public class EnabledNetworkModePreferenceController extends
add5gEntry(addNrToLteNetworkType(entryValuesInt[0]));
addLteEntry(entryValuesInt[0]);
add3gEntry(entryValuesInt[1]);
- add2gEntry(entryValuesInt[2]);
+ if (display2gOptions) {
+ add2gEntry(entryValuesInt[2]);
+ }
break;
case ENABLED_NETWORKS_EXCEPT_GSM_LTE_CHOICES:
entryValues = getResourcesForSubId().getStringArray(
@@ -347,7 +357,9 @@ public class EnabledNetworkModePreferenceController extends
"ENABLED_NETWORKS_EXCEPT_LTE_CHOICES index error.");
}
add3gEntry(entryValuesInt[0]);
- add2gEntry(entryValuesInt[1]);
+ if (carrierConfig.getBoolean(CarrierConfigManager.KEY_PREFER_2G_BOOL)) {
+ add2gEntry(entryValuesInt[1]);
+ }
break;
case ENABLED_NETWORKS_4G_CHOICES:
entryValues = getResourcesForSubId().getStringArray(
@@ -361,7 +373,9 @@ public class EnabledNetworkModePreferenceController extends
entryValuesInt[0]));
add4gEntry(entryValuesInt[0]);
add3gEntry(entryValuesInt[1]);
- add2gEntry(entryValuesInt[2]);
+ if (display2gOptions) {
+ add2gEntry(entryValuesInt[2]);
+ }
break;
case ENABLED_NETWORKS_CHOICES:
entryValues = getResourcesForSubId().getStringArray(
@@ -373,7 +387,9 @@ public class EnabledNetworkModePreferenceController extends
add5gEntry(addNrToLteNetworkType(entryValuesInt[0]));
addLteEntry(entryValuesInt[0]);
add3gEntry(entryValuesInt[1]);
- add2gEntry(entryValuesInt[2]);
+ if (display2gOptions) {
+ add2gEntry(entryValuesInt[2]);
+ }
break;
case PREFERRED_NETWORK_MODE_CHOICES_WORLD_MODE:
entryValues = getResourcesForSubId().getStringArray(
diff --git a/src/com/android/settings/safetycenter/SafetySourceBroadcastReceiver.java b/src/com/android/settings/safetycenter/SafetySourceBroadcastReceiver.java
index 38458d92571..dec06b02e79 100644
--- a/src/com/android/settings/safetycenter/SafetySourceBroadcastReceiver.java
+++ b/src/com/android/settings/safetycenter/SafetySourceBroadcastReceiver.java
@@ -26,6 +26,7 @@ import android.app.settings.SettingsEnums;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
+import android.safetycenter.SafetyCenterManager;
import android.safetycenter.SafetyEvent;
import com.android.settings.security.ScreenLockPreferenceDetailsUtils;
@@ -37,8 +38,6 @@ import java.util.List;
/** Broadcast receiver for handling requests from Safety Center for fresh data. */
public class SafetySourceBroadcastReceiver extends BroadcastReceiver {
- private static final SafetyEvent EVENT_REFRESH_REQUESTED =
- new SafetyEvent.Builder(SAFETY_EVENT_TYPE_REFRESH_REQUESTED).build();
private static final SafetyEvent EVENT_DEVICE_REBOOTED =
new SafetyEvent.Builder(SAFETY_EVENT_TYPE_DEVICE_REBOOTED).build();
@@ -52,10 +51,15 @@ public class SafetySourceBroadcastReceiver extends BroadcastReceiver {
String[] sourceIdsExtra =
intent.getStringArrayExtra(EXTRA_REFRESH_SAFETY_SOURCE_IDS);
if (sourceIdsExtra != null && sourceIdsExtra.length > 0) {
+ final String refreshBroadcastId = intent.getStringExtra(
+ SafetyCenterManager.EXTRA_REFRESH_SAFETY_SOURCES_BROADCAST_ID);
+ final SafetyEvent safetyEvent = new SafetyEvent.Builder(
+ SAFETY_EVENT_TYPE_REFRESH_REQUESTED)
+ .setRefreshBroadcastId(refreshBroadcastId).build();
refreshSafetySources(
context,
ImmutableList.copyOf(sourceIdsExtra),
- EVENT_REFRESH_REQUESTED);
+ safetyEvent);
}
return;
}
diff --git a/src/com/android/settings/widget/HighlightableTopLevelPreferenceAdapter.java b/src/com/android/settings/widget/HighlightableTopLevelPreferenceAdapter.java
index ddd57f3a76d..911aabb13b8 100644
--- a/src/com/android/settings/widget/HighlightableTopLevelPreferenceAdapter.java
+++ b/src/com/android/settings/widget/HighlightableTopLevelPreferenceAdapter.java
@@ -67,10 +67,12 @@ public class HighlightableTopLevelPreferenceAdapter extends PreferenceGroupAdapt
private SparseArray mViewHolders;
public HighlightableTopLevelPreferenceAdapter(SettingsHomepageActivity homepageActivity,
- PreferenceGroup preferenceGroup, RecyclerView recyclerView, String key) {
+ PreferenceGroup preferenceGroup, RecyclerView recyclerView, String key,
+ boolean scrollNeeded) {
super(preferenceGroup);
mRecyclerView = recyclerView;
mHighlightKey = key;
+ mScrolled = !scrollNeeded;
mViewHolders = new SparseArray<>();
mContext = preferenceGroup.getContext();
mHomepageActivity = homepageActivity;
diff --git a/src/com/android/settings/wifi/dpp/WifiDppQrCodeScannerFragment.java b/src/com/android/settings/wifi/dpp/WifiDppQrCodeScannerFragment.java
index d8bdf32dbf4..528fbc3bac9 100644
--- a/src/com/android/settings/wifi/dpp/WifiDppQrCodeScannerFragment.java
+++ b/src/com/android/settings/wifi/dpp/WifiDppQrCodeScannerFragment.java
@@ -168,10 +168,18 @@ public class WifiDppQrCodeScannerFragment extends WifiDppQrCodeBaseFragment impl
break;
case MESSAGE_SCAN_ZXING_WIFI_FORMAT_SUCCESS:
+ final Context context = getContext();
+ if (context == null) {
+ // Context may be null if the message is received after the Activity has
+ // been destroyed
+ Log.d(TAG, "Scan success but context is null");
+ return;
+ }
+
// We may get 2 WifiConfiguration if the QR code has no password in it,
// one for open network and one for enhanced open network.
final WifiManager wifiManager =
- getContext().getSystemService(WifiManager.class);
+ context.getSystemService(WifiManager.class);
final WifiNetworkConfig qrCodeWifiNetworkConfig =
(WifiNetworkConfig)msg.obj;
final List qrCodeWifiConfigurations =
diff --git a/src/com/android/settings/wifi/qrcode/QrCamera.java b/src/com/android/settings/wifi/qrcode/QrCamera.java
index 3865eb1f116..d07c54335ad 100644
--- a/src/com/android/settings/wifi/qrcode/QrCamera.java
+++ b/src/com/android/settings/wifi/qrcode/QrCamera.java
@@ -124,6 +124,7 @@ public class QrCamera extends Handler {
}
if (mCamera != null) {
mCamera.stopPreview();
+ releaseCamera();
}
}
diff --git a/tests/robotests/assets/exempt_not_implementing_index_provider b/tests/robotests/assets/exempt_not_implementing_index_provider
index d4a1c2e268d..6a1a1ffbbc5 100644
--- a/tests/robotests/assets/exempt_not_implementing_index_provider
+++ b/tests/robotests/assets/exempt_not_implementing_index_provider
@@ -15,7 +15,6 @@ com.android.settings.applications.appinfo.ExternalSourcesDetails
com.android.settings.applications.appinfo.WriteSettingsDetails
com.android.settings.applications.AppLaunchSettings
com.android.settings.applications.AppStorageSettings
-com.android.settings.applications.ConfirmConvertToFbe
com.android.settings.applications.ProcessStatsDetail
com.android.settings.applications.ProcessStatsSummary
com.android.settings.applications.ProcessStatsUi
diff --git a/tests/robotests/src/com/android/settings/accessibility/AccessibilityFooterPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/accessibility/AccessibilityFooterPreferenceControllerTest.java
index cc8520b9745..cce240dfe61 100644
--- a/tests/robotests/src/com/android/settings/accessibility/AccessibilityFooterPreferenceControllerTest.java
+++ b/tests/robotests/src/com/android/settings/accessibility/AccessibilityFooterPreferenceControllerTest.java
@@ -121,4 +121,16 @@ public class AccessibilityFooterPreferenceControllerTest {
assertThat(learnMoreView.getVisibility()).isEqualTo(View.GONE);
assertThat(mPreference.isLinkEnabled()).isFalse();
}
+
+ @Test
+ public void onBindViewHolder_setHelpResource_expectSummaryViewIsNonFocusable() {
+ mController.setupHelpLink(R.string.help_url_timeout, TEST_CONTENT_DESCRIPTION);
+ mController.displayPreference(mScreen);
+
+ mPreference.onBindViewHolder(mPreferenceViewHolder);
+
+ final TextView summaryView = (TextView) mPreferenceViewHolder
+ .findViewById(android.R.id.title);
+ assertThat(summaryView.isFocusable()).isFalse();
+ }
}
diff --git a/tests/robotests/src/com/android/settings/accessibility/AccessibilityFooterPreferenceTest.java b/tests/robotests/src/com/android/settings/accessibility/AccessibilityFooterPreferenceTest.java
index 14c56e84726..0f83bc3a254 100644
--- a/tests/robotests/src/com/android/settings/accessibility/AccessibilityFooterPreferenceTest.java
+++ b/tests/robotests/src/com/android/settings/accessibility/AccessibilityFooterPreferenceTest.java
@@ -71,4 +71,15 @@ public final class AccessibilityFooterPreferenceTest {
android.R.id.title);
assertThat(summaryView.getMovementMethod()).isInstanceOf(MovementMethod.class);
}
+
+ @Test
+ public void onBindViewHolder_setLinkEnabled_expectSummaryViewIsNonFocusable() {
+ mAccessibilityFooterPreference.setLinkEnabled(true);
+
+ mAccessibilityFooterPreference.onBindViewHolder(mPreferenceViewHolder);
+
+ final TextView summaryView = (TextView) mPreferenceViewHolder.findViewById(
+ android.R.id.title);
+ assertThat(summaryView.isFocusable()).isFalse();
+ }
}
diff --git a/tests/robotests/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragmentTest.java b/tests/robotests/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragmentTest.java
index 04018a6b6d7..114f96985cf 100644
--- a/tests/robotests/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragmentTest.java
+++ b/tests/robotests/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragmentTest.java
@@ -322,7 +322,7 @@ public class ToggleFeaturePreferenceFragmentTest {
(AccessibilityFooterPreference) mFragment.getPreferenceScreen().getPreference(
mFragment.getPreferenceScreen().getPreferenceCount() - 1);
assertThat(accessibilityFooterPreference.getSummary()).isEqualTo(DEFAULT_SUMMARY);
- assertThat(accessibilityFooterPreference.isSelectable()).isEqualTo(true);
+ assertThat(accessibilityFooterPreference.isSelectable()).isEqualTo(false);
assertThat(accessibilityFooterPreference.getOrder()).isEqualTo(Integer.MAX_VALUE - 1);
}
diff --git a/tests/robotests/src/com/android/settings/development/FileEncryptionPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/development/FileEncryptionPreferenceControllerTest.java
deleted file mode 100644
index 0784a61b197..00000000000
--- a/tests/robotests/src/com/android/settings/development/FileEncryptionPreferenceControllerTest.java
+++ /dev/null
@@ -1,116 +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.development;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.content.Context;
-import android.os.RemoteException;
-import android.os.storage.IStorageManager;
-import android.sysprop.CryptoProperties;
-import androidx.preference.Preference;
-import androidx.preference.PreferenceScreen;
-
-import com.android.settings.R;
-
-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.RuntimeEnvironment;
-import org.robolectric.util.ReflectionHelpers;
-
-@RunWith(RobolectricTestRunner.class)
-public class FileEncryptionPreferenceControllerTest {
-
- @Mock
- private Preference mPreference;
- @Mock
- private PreferenceScreen mPreferenceScreen;
- @Mock
- private IStorageManager mStorageManager;
-
- private Context mContext;
- private FileEncryptionPreferenceController mController;
-
- @Before
- public void setup() {
- MockitoAnnotations.initMocks(this);
- mContext = RuntimeEnvironment.application;
- mController = new FileEncryptionPreferenceController(mContext);
- when(mPreferenceScreen.findPreference(mController.getPreferenceKey()))
- .thenReturn(mPreference);
- }
-
- @Test
- public void isAvailable_storageManagerNull_shouldBeFalse() {
- ReflectionHelpers.setField(mController, "mStorageManager", null);
-
- assertThat(mController.isAvailable()).isFalse();
- }
-
- @Test
- public void isAvailable_notConvertibleToFBE_shouldBeFalse() throws RemoteException {
- ReflectionHelpers.setField(mController, "mStorageManager", mStorageManager);
- when(mStorageManager.isConvertibleToFBE()).thenReturn(false);
-
- assertThat(mController.isAvailable()).isFalse();
- }
-
- @Test
- public void isAvailable_convertibleToFBE_shouldBeTrue() throws RemoteException {
- ReflectionHelpers.setField(mController, "mStorageManager", mStorageManager);
- when(mStorageManager.isConvertibleToFBE()).thenReturn(true);
-
- assertThat(mController.isAvailable()).isTrue();
- }
-
- @Test
- public void updateState_settingIsNotFile_shouldDoNothing() throws RemoteException {
- ReflectionHelpers.setField(mController, "mStorageManager", mStorageManager);
- when(mStorageManager.isConvertibleToFBE()).thenReturn(true);
- mController.displayPreference(mPreferenceScreen);
- CryptoProperties.type(CryptoProperties.type_values.NONE);
-
- mController.updateState(mPreference);
-
- verify(mPreference, never()).setEnabled(anyBoolean());
- verify(mPreference, never()).setSummary(anyString());
- }
-
- @Test
- public void updateState_settingIsFile_shouldSetSummaryAndDisablePreference()
- throws RemoteException {
- ReflectionHelpers.setField(mController, "mStorageManager", mStorageManager);
- when(mStorageManager.isConvertibleToFBE()).thenReturn(true);
- mController.displayPreference(mPreferenceScreen);
- CryptoProperties.type(CryptoProperties.type_values.FILE);
-
- mController.updateState(mPreference);
-
- verify(mPreference).setEnabled(false);
- verify(mPreference).setSummary(mContext.getString(R.string.convert_to_file_encryption_done));
- }
-}
diff --git a/tests/robotests/src/com/android/settings/display/SmartAutoRotatePreferenceFragmentTest.java b/tests/robotests/src/com/android/settings/display/SmartAutoRotatePreferenceFragmentTest.java
index 942fed6f619..e5374196681 100644
--- a/tests/robotests/src/com/android/settings/display/SmartAutoRotatePreferenceFragmentTest.java
+++ b/tests/robotests/src/com/android/settings/display/SmartAutoRotatePreferenceFragmentTest.java
@@ -16,7 +16,8 @@
package com.android.settings.display;
-import static com.android.settings.display.SmartAutoRotatePreferenceFragment.AUTO_ROTATE_SWITCH_PREFERENCE_ID;
+import static com.android.settings.display.SmartAutoRotatePreferenceFragment.AUTO_ROTATE_MAIN_SWITCH_PREFERENCE_KEY;
+import static com.android.settings.display.SmartAutoRotatePreferenceFragment.AUTO_ROTATE_SWITCH_PREFERENCE_KEY;
import static com.google.common.truth.Truth.assertThat;
@@ -45,7 +46,6 @@ import com.android.settings.SettingsActivity;
import com.android.settings.testutils.ResolveInfoBuilder;
import com.android.settings.testutils.shadow.ShadowDeviceStateRotationLockSettingsManager;
import com.android.settings.testutils.shadow.ShadowRotationPolicy;
-import com.android.settings.widget.SettingsMainSwitchBar;
import com.android.settingslib.core.AbstractPreferenceController;
import com.android.settingslib.devicestate.DeviceStateRotationLockSettingsManager;
@@ -71,8 +71,6 @@ public class SmartAutoRotatePreferenceFragmentTest {
private SmartAutoRotatePreferenceFragment mFragment;
- private SettingsMainSwitchBar mSwitchBar;
-
@Mock
private PackageManager mPackageManager;
@@ -87,6 +85,9 @@ public class SmartAutoRotatePreferenceFragmentTest {
private Resources mResources;
private Context mContext;
+ @Mock
+ private Preference mRotateMainSwitchPreference;
+
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
@@ -112,21 +113,21 @@ public class SmartAutoRotatePreferenceFragmentTest {
when(mFragment.getContext()).thenReturn(mContext);
doReturn(mView).when(mFragment).getView();
- when(mFragment.findPreference(AUTO_ROTATE_SWITCH_PREFERENCE_ID)).thenReturn(
+ when(mFragment.findPreference(AUTO_ROTATE_SWITCH_PREFERENCE_KEY)).thenReturn(
mRotateSwitchPreference);
- mSwitchBar = spy(new SettingsMainSwitchBar(mContext));
- when(mActivity.getSwitchBar()).thenReturn(mSwitchBar);
- doReturn(mSwitchBar).when(mView).findViewById(R.id.switch_bar);
- ShadowDeviceStateRotationLockSettingsManager.setDeviceStateRotationLockEnabled(false);
+
+ when(mFragment.findPreference(AUTO_ROTATE_MAIN_SWITCH_PREFERENCE_KEY)).thenReturn(
+ mRotateMainSwitchPreference);
}
@Test
public void createHeader_faceDetectionSupported_switchBarIsEnabled() {
+ ShadowDeviceStateRotationLockSettingsManager.setDeviceStateRotationLockEnabled(false);
mFragment.createHeader(mActivity);
- verify(mSwitchBar, times(1)).show();
+ verify(mRotateMainSwitchPreference, never()).setVisible(false);
verify(mRotateSwitchPreference, times(1)).setVisible(false);
}
@@ -137,7 +138,7 @@ public class SmartAutoRotatePreferenceFragmentTest {
mFragment.createHeader(mActivity);
- verify(mSwitchBar, never()).show();
+ verify(mRotateMainSwitchPreference, times(1)).setVisible(false);
verify(mRotateSwitchPreference, never()).setVisible(false);
}
@@ -147,7 +148,7 @@ public class SmartAutoRotatePreferenceFragmentTest {
mFragment.createHeader(mActivity);
- verify(mSwitchBar, never()).show();
+ verify(mRotateMainSwitchPreference, times(1)).setVisible(false);
verify(mRotateSwitchPreference, never()).setVisible(false);
}
@@ -176,6 +177,19 @@ public class SmartAutoRotatePreferenceFragmentTest {
DeviceStateAutoRotateSettingController.class);
}
+ @Test
+ public void setupFooter_linkAddedWhenAppropriate() {
+ doReturn("").when(mFragment).getText(anyInt());
+ doReturn("").when(mFragment).getString(anyInt());
+ mFragment.setupFooter();
+ verify(mFragment, never()).addHelpLink();
+
+ doReturn("testString").when(mFragment).getText(anyInt());
+ doReturn("testString").when(mFragment).getString(anyInt());
+ mFragment.setupFooter();
+ verify(mFragment, times(1)).addHelpLink();
+ }
+
private void enableDeviceStateSettableRotationStates(String[] settableStates,
String[] settableStatesDescriptions) {
when(mResources.getStringArray(
diff --git a/tests/unit/src/com/android/settings/accessibility/HighTextContrastPreferenceControllerTest.java b/tests/unit/src/com/android/settings/accessibility/HighTextContrastPreferenceControllerTest.java
index 1ada051f073..9c4df36cc2f 100644
--- a/tests/unit/src/com/android/settings/accessibility/HighTextContrastPreferenceControllerTest.java
+++ b/tests/unit/src/com/android/settings/accessibility/HighTextContrastPreferenceControllerTest.java
@@ -19,8 +19,11 @@ package com.android.settings.accessibility;
import static com.google.common.truth.Truth.assertThat;
import android.content.Context;
+import android.os.Looper;
import android.provider.Settings;
+import androidx.preference.PreferenceManager;
+import androidx.preference.PreferenceScreen;
import androidx.preference.SwitchPreference;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -37,6 +40,7 @@ import org.junit.runner.RunWith;
@RunWith(AndroidJUnit4.class)
public class HighTextContrastPreferenceControllerTest {
+ private static final String PREF_KEY = "text_contrast";
private static final int ON = 1;
private static final int OFF = 0;
private static final int UNKNOWN = -1;
@@ -44,12 +48,20 @@ public class HighTextContrastPreferenceControllerTest {
private Context mContext;
private SwitchPreference mPreference;
private HighTextContrastPreferenceController mController;
+ private PreferenceScreen mScreen;
@Before
public void setUp() {
mContext = ApplicationProvider.getApplicationContext();
+ if (Looper.myLooper() == null) {
+ Looper.prepare();
+ }
+ final PreferenceManager preferenceManager = new PreferenceManager(mContext);
+ mScreen = preferenceManager.createPreferenceScreen(mContext);
mPreference = new SwitchPreference(mContext);
- mController = new HighTextContrastPreferenceController(mContext, "text_contrast");
+ mPreference.setKey(PREF_KEY);
+ mScreen.addPreference(mPreference);
+ mController = new HighTextContrastPreferenceController(mContext, PREF_KEY);
}
@Test
@@ -99,10 +111,13 @@ public class HighTextContrastPreferenceControllerTest {
@Test
public void resetState_shouldDisableTextContrast() {
+ mController.displayPreference(mScreen);
mController.setChecked(true);
+ mPreference.setChecked(true);
mController.resetState();
+ assertThat(mPreference.isChecked()).isFalse();
assertThat(Settings.Secure.getInt(mContext.getContentResolver(),
Settings.Secure.ACCESSIBILITY_HIGH_TEXT_CONTRAST_ENABLED, UNKNOWN)).isEqualTo(OFF);
}
diff --git a/tests/unit/src/com/android/settings/safetycenter/SafetySourceBroadcastReceiverTest.java b/tests/unit/src/com/android/settings/safetycenter/SafetySourceBroadcastReceiverTest.java
index 8a4214ced2e..8004d600e9e 100644
--- a/tests/unit/src/com/android/settings/safetycenter/SafetySourceBroadcastReceiverTest.java
+++ b/tests/unit/src/com/android/settings/safetycenter/SafetySourceBroadcastReceiverTest.java
@@ -17,6 +17,7 @@
package com.android.settings.safetycenter;
import static android.safetycenter.SafetyCenterManager.ACTION_REFRESH_SAFETY_SOURCES;
+import static android.safetycenter.SafetyCenterManager.EXTRA_REFRESH_SAFETY_SOURCES_BROADCAST_ID;
import static android.safetycenter.SafetyCenterManager.EXTRA_REFRESH_SAFETY_SOURCE_IDS;
import static android.safetycenter.SafetyEvent.SAFETY_EVENT_TYPE_DEVICE_REBOOTED;
import static android.safetycenter.SafetyEvent.SAFETY_EVENT_TYPE_REFRESH_REQUESTED;
@@ -147,6 +148,29 @@ public class SafetySourceBroadcastReceiverTest {
new SafetyEvent.Builder(SAFETY_EVENT_TYPE_REFRESH_REQUESTED).build());
}
+ @Test
+ public void onReceive_onRefreshWithBroadcastId_setsRefreshEventWithBroadcastId() {
+ final String refreshBroadcastId = "REFRESH_BROADCAST_ID";
+ when(mSafetyCenterManagerWrapper.isEnabled(mApplicationContext)).thenReturn(true);
+ Intent intent =
+ new Intent()
+ .setAction(ACTION_REFRESH_SAFETY_SOURCES)
+ .putExtra(
+ EXTRA_REFRESH_SAFETY_SOURCE_IDS,
+ new String[]{ LockScreenSafetySource.SAFETY_SOURCE_ID })
+ .putExtra(EXTRA_REFRESH_SAFETY_SOURCES_BROADCAST_ID, refreshBroadcastId);
+
+ new SafetySourceBroadcastReceiver().onReceive(mApplicationContext, intent);
+ ArgumentCaptor captor = ArgumentCaptor.forClass(SafetyEvent.class);
+ verify(mSafetyCenterManagerWrapper, times(1))
+ .setSafetySourceData(any(), any(), any(), captor.capture());
+
+ assertThat(captor.getValue().getRefreshBroadcastId()).isEqualTo(refreshBroadcastId);
+ assertThat(captor.getValue()).isEqualTo(
+ new SafetyEvent.Builder(SAFETY_EVENT_TYPE_REFRESH_REQUESTED)
+ .setRefreshBroadcastId(refreshBroadcastId).build());
+ }
+
@Test
public void onReceive_onRefresh_withLockscreenSourceId_setsLockscreenData() {
when(mSafetyCenterManagerWrapper.isEnabled(mApplicationContext)).thenReturn(true);