Merge "Update the illustration of Double-tap to check phone page."
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 290f84e..0279eec 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -4349,6 +4349,7 @@
android:exported="true">
<intent-filter>
<action android:name="android.safetycenter.action.REFRESH_SAFETY_SOURCES"/>
+ <action android:name="android.intent.action.BOOT_COMPLETED"/>
</intent-filter>
</receiver>
diff --git a/res/layout/apps_filter_spinner.xml b/res/layout/apps_filter_spinner.xml
index 1de5705..fcdcb5e 100644
--- a/res/layout/apps_filter_spinner.xml
+++ b/res/layout/apps_filter_spinner.xml
@@ -21,13 +21,11 @@
android:layout_height="wrap_content"
android:background="@android:color/transparent">
- <com.android.settingslib.widget.settingsspinner.SettingsSpinner
+ <Spinner
android:id="@+id/filter_spinner"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:layout_centerHorizontal="true"
- android:layout_marginTop="16dp"
- android:layout_marginBottom="8dp"
android:theme="@style/Widget.PopupWindow.Settings"/>
<ImageView
diff --git a/res/layout/data_usage_cycles.xml b/res/layout/data_usage_cycles.xml
index 05c38e0..c957f9c 100644
--- a/res/layout/data_usage_cycles.xml
+++ b/res/layout/data_usage_cycles.xml
@@ -20,7 +20,7 @@
android:layout_height="wrap_content"
android:minHeight="?android:attr/listPreferredItemHeight">
- <com.android.settingslib.widget.settingsspinner.SettingsSpinner
+ <Spinner
android:id="@+id/cycles_spinner"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
diff --git a/res/layout/manage_apps_filter_spinner.xml b/res/layout/manage_apps_filter_spinner.xml
index 8283bb8..a2d0e12 100644
--- a/res/layout/manage_apps_filter_spinner.xml
+++ b/res/layout/manage_apps_filter_spinner.xml
@@ -21,12 +21,10 @@
android:layout_height="wrap_content"
android:background="@android:color/transparent">
- <com.android.settingslib.widget.settingsspinner.SettingsSpinner
+ <Spinner
android:id="@+id/filter_spinner"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:layout_marginStart="24dp"
- android:layout_marginTop="16dp"
- android:layout_marginBottom="8dp"
android:theme="@style/Widget.PopupWindow.Settings"/>
</FrameLayout>
diff --git a/res/layout/tare_dropdown_page.xml b/res/layout/tare_dropdown_page.xml
index 79931e8..674b189 100644
--- a/res/layout/tare_dropdown_page.xml
+++ b/res/layout/tare_dropdown_page.xml
@@ -6,15 +6,14 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".development.tare.DropdownActivity">
- <com.android.settingslib.widget.settingsspinner.SettingsSpinner
+ <Spinner
android:id="@+id/spinner"
- android:layout_width="match_parent"
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
- android:theme="@style/Widget.PopupWindow.Settings"
- android:padding="10dp" />
+ android:theme="@style/Widget.PopupWindow.Settings" />
<FrameLayout
android:id="@+id/frame_layout"
android:layout_width="match_parent"
diff --git a/res/layout/tare_homepage.xml b/res/layout/tare_homepage.xml
index 2c184d2..ddc9333 100644
--- a/res/layout/tare_homepage.xml
+++ b/res/layout/tare_homepage.xml
@@ -60,5 +60,6 @@
android:layout_height="wrap_content"
android:layout_margin="20dp"
android:onClick="revertSettings"
- android:text="@string/tare_revert" />
+ android:text="@string/tare_revert"
+ style="@style/ActionPrimaryButton" />
</LinearLayout>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 9d868cb..ff14d1d 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -13939,4 +13939,13 @@
<!-- Text to explain an activity is a temporary placeholder [CHAR LIMIT=none] -->
<string name="placeholder_activity" translatable="false">*This is a temporary placeholder fallback activity.</string>
+
+ <!-- The title of the spatial audio [CHAR LIMIT=none] -->
+ <string name="bluetooth_details_spatial_audio_title">Spatial audio</string>
+ <!-- The summary of the spatial audio [CHAR LIMIT=none] -->
+ <string name="bluetooth_details_spatial_audio_summary">Immersive audio seems like it\u0027s coming from all around you. Only works with some media.</string>
+ <!-- The title of the head tracking [CHAR LIMIT=none] -->
+ <string name="bluetooth_details_head_tracking_title">Make audio more realistic</string>
+ <!-- The summary of the head tracking [CHAR LIMIT=none] -->
+ <string name="bluetooth_details_head_tracking_summary">Shift positioning of audio so it sounds more natural.</string>
</resources>
diff --git a/res/xml/bluetooth_device_details_fragment.xml b/res/xml/bluetooth_device_details_fragment.xml
index 9df1955..b21d5c9 100644
--- a/res/xml/bluetooth_device_details_fragment.xml
+++ b/res/xml/bluetooth_device_details_fragment.xml
@@ -53,6 +53,9 @@
android:key="device_companion_apps"/>
<PreferenceCategory
+ android:key="spatial_audio_group"/>
+
+ <PreferenceCategory
android:key="bluetooth_profiles"/>
<com.android.settingslib.widget.FooterPreference
diff --git a/src/com/android/settings/applications/ProcessStatsBase.java b/src/com/android/settings/applications/ProcessStatsBase.java
index 67fc4c1..ce1453f 100644
--- a/src/com/android/settings/applications/ProcessStatsBase.java
+++ b/src/com/android/settings/applications/ProcessStatsBase.java
@@ -30,7 +30,7 @@
import com.android.settings.applications.ProcStatsData.MemInfo;
import com.android.settings.core.SubSettingLauncher;
import com.android.settingslib.core.instrumentation.Instrumentable;
-import com.android.settingslib.widget.settingsspinner.SettingsSpinnerAdapter;
+import com.android.settingslib.widget.SettingsSpinnerAdapter;
public abstract class ProcessStatsBase extends SettingsPreferenceFragment
implements OnItemSelectedListener {
diff --git a/src/com/android/settings/applications/manageapplications/ManageApplications.java b/src/com/android/settings/applications/manageapplications/ManageApplications.java
index 0c12706..a6ce6fb 100644
--- a/src/com/android/settings/applications/manageapplications/ManageApplications.java
+++ b/src/com/android/settings/applications/manageapplications/ManageApplications.java
@@ -135,7 +135,7 @@
import com.android.settingslib.applications.ApplicationsState.VolumeFilter;
import com.android.settingslib.fuelgauge.PowerAllowlistBackend;
import com.android.settingslib.utils.ThreadUtils;
-import com.android.settingslib.widget.settingsspinner.SettingsSpinnerAdapter;
+import com.android.settingslib.widget.SettingsSpinnerAdapter;
import com.google.android.material.appbar.AppBarLayout;
diff --git a/src/com/android/settings/bluetooth/BluetoothDetailsSpatialAudioController.java b/src/com/android/settings/bluetooth/BluetoothDetailsSpatialAudioController.java
new file mode 100644
index 0000000..89d923d
--- /dev/null
+++ b/src/com/android/settings/bluetooth/BluetoothDetailsSpatialAudioController.java
@@ -0,0 +1,155 @@
+/*
+ * 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.content.Context;
+import android.media.AudioDeviceAttributes;
+import android.media.AudioDeviceInfo;
+import android.media.AudioManager;
+import android.media.Spatializer;
+import android.text.TextUtils;
+import android.util.Log;
+
+import androidx.annotation.VisibleForTesting;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceCategory;
+import androidx.preference.PreferenceFragmentCompat;
+import androidx.preference.PreferenceScreen;
+import androidx.preference.SwitchPreference;
+
+import com.android.settings.R;
+import com.android.settingslib.bluetooth.CachedBluetoothDevice;
+import com.android.settingslib.core.lifecycle.Lifecycle;
+
+/**
+ * The controller of the Spatial audio setting in the bluetooth detail settings.
+ */
+public class BluetoothDetailsSpatialAudioController extends BluetoothDetailsController
+ implements Preference.OnPreferenceClickListener {
+
+ private static final String TAG = "BluetoothSpatialAudioController";
+ private static final String KEY_SPATIAL_AUDIO_GROUP = "spatial_audio_group";
+ private static final String KEY_SPATIAL_AUDIO = "spatial_audio";
+ private static final String KEY_HEAD_TRACKING = "head_tracking";
+
+ private final Spatializer mSpatializer;
+
+ @VisibleForTesting
+ PreferenceCategory mProfilesContainer;
+ @VisibleForTesting
+ AudioDeviceAttributes mAudioDevice;
+
+ public BluetoothDetailsSpatialAudioController(
+ Context context,
+ PreferenceFragmentCompat fragment,
+ CachedBluetoothDevice device,
+ Lifecycle lifecycle) {
+ super(context, fragment, device, lifecycle);
+ AudioManager audioManager = context.getSystemService(AudioManager.class);
+ mSpatializer = audioManager.getSpatializer();
+ mAudioDevice = new AudioDeviceAttributes(
+ AudioDeviceAttributes.ROLE_OUTPUT,
+ AudioDeviceInfo.TYPE_BLUETOOTH_A2DP,
+ mCachedDevice.getAddress());
+
+ }
+
+ @Override
+ public boolean isAvailable() {
+ return mSpatializer.isAvailableForDevice(mAudioDevice) ? true : false;
+ }
+
+ @Override
+ public boolean onPreferenceClick(Preference preference) {
+ SwitchPreference switchPreference = (SwitchPreference) preference;
+ String key = switchPreference.getKey();
+ if (TextUtils.equals(key, KEY_SPATIAL_AUDIO)) {
+ if (switchPreference.isChecked()) {
+ mSpatializer.addCompatibleAudioDevice(mAudioDevice);
+ } else {
+ mSpatializer.removeCompatibleAudioDevice(mAudioDevice);
+ }
+ refresh();
+ return true;
+ } else if (TextUtils.equals(key, KEY_HEAD_TRACKING)) {
+ mSpatializer.setHeadTrackerEnabled(switchPreference.isChecked(), mAudioDevice);
+ return true;
+ } else {
+ Log.w(TAG, "invalid key name.");
+ return false;
+ }
+ }
+
+ @Override
+ public String getPreferenceKey() {
+ return KEY_SPATIAL_AUDIO_GROUP;
+ }
+
+ @Override
+ protected void init(PreferenceScreen screen) {
+ mProfilesContainer = screen.findPreference(getPreferenceKey());
+ mProfilesContainer.setLayoutResource(R.layout.preference_bluetooth_profile_category);
+ refresh();
+ }
+
+ @Override
+ protected void refresh() {
+ SwitchPreference spatialAudioPref = mProfilesContainer.findPreference(KEY_SPATIAL_AUDIO);
+ if (spatialAudioPref == null) {
+ spatialAudioPref = createSpatialAudioPreference(mProfilesContainer.getContext());
+ mProfilesContainer.addPreference(spatialAudioPref);
+ }
+
+ boolean isSpatialAudioOn = mSpatializer.getCompatibleAudioDevices().contains(mAudioDevice);
+ Log.d(TAG, "refresh() isSpatialAudioOn : " + isSpatialAudioOn);
+ spatialAudioPref.setChecked(isSpatialAudioOn);
+
+ SwitchPreference headTrackingPref = mProfilesContainer.findPreference(KEY_HEAD_TRACKING);
+ if (headTrackingPref == null) {
+ headTrackingPref = createHeadTrackingPreference(mProfilesContainer.getContext());
+ mProfilesContainer.addPreference(headTrackingPref);
+ }
+
+ boolean isHeadTrackingAvailable =
+ isSpatialAudioOn && mSpatializer.hasHeadTracker(mAudioDevice);
+ Log.d(TAG, "refresh() has head tracker : " + mSpatializer.hasHeadTracker(mAudioDevice));
+ headTrackingPref.setVisible(isHeadTrackingAvailable);
+ if (isHeadTrackingAvailable) {
+ headTrackingPref.setChecked(mSpatializer.isHeadTrackerEnabled(mAudioDevice));
+ }
+ }
+
+ @VisibleForTesting
+ SwitchPreference createSpatialAudioPreference(Context context) {
+ SwitchPreference pref = new SwitchPreference(context);
+ pref.setKey(KEY_SPATIAL_AUDIO);
+ pref.setTitle(context.getString(R.string.bluetooth_details_spatial_audio_title));
+ pref.setSummary(context.getString(R.string.bluetooth_details_spatial_audio_summary));
+ pref.setOnPreferenceClickListener(this);
+ return pref;
+ }
+
+ @VisibleForTesting
+ SwitchPreference createHeadTrackingPreference(Context context) {
+ SwitchPreference pref = new SwitchPreference(context);
+ pref.setKey(KEY_HEAD_TRACKING);
+ pref.setTitle(context.getString(R.string.bluetooth_details_head_tracking_title));
+ pref.setSummary(context.getString(R.string.bluetooth_details_head_tracking_summary));
+ pref.setOnPreferenceClickListener(this);
+ return pref;
+ }
+}
diff --git a/src/com/android/settings/bluetooth/BluetoothDeviceDetailsFragment.java b/src/com/android/settings/bluetooth/BluetoothDeviceDetailsFragment.java
index 4980ba3..6532482 100644
--- a/src/com/android/settings/bluetooth/BluetoothDeviceDetailsFragment.java
+++ b/src/com/android/settings/bluetooth/BluetoothDeviceDetailsFragment.java
@@ -187,6 +187,8 @@
lifecycle));
controllers.add(new BluetoothDetailsCompanionAppsController(context, this,
mCachedDevice, lifecycle));
+ controllers.add(new BluetoothDetailsSpatialAudioController(context, this, mCachedDevice,
+ lifecycle));
controllers.add(new BluetoothDetailsProfilesController(context, this, mManager,
mCachedDevice, lifecycle));
controllers.add(new BluetoothDetailsMacAddressController(context, this, mCachedDevice,
diff --git a/src/com/android/settings/datausage/CycleAdapter.java b/src/com/android/settings/datausage/CycleAdapter.java
index 1292d00..2cabd8d 100644
--- a/src/com/android/settings/datausage/CycleAdapter.java
+++ b/src/com/android/settings/datausage/CycleAdapter.java
@@ -27,7 +27,7 @@
import com.android.settings.Utils;
import com.android.settingslib.net.ChartData;
import com.android.settingslib.net.NetworkCycleData;
-import com.android.settingslib.widget.settingsspinner.SettingsSpinnerAdapter;
+import com.android.settingslib.widget.SettingsSpinnerAdapter;
import java.time.ZonedDateTime;
import java.util.Iterator;
diff --git a/src/com/android/settings/datausage/SpinnerPreference.java b/src/com/android/settings/datausage/SpinnerPreference.java
index 67298a1..867930b 100644
--- a/src/com/android/settings/datausage/SpinnerPreference.java
+++ b/src/com/android/settings/datausage/SpinnerPreference.java
@@ -18,12 +18,12 @@
import android.util.AttributeSet;
import android.view.View;
import android.widget.AdapterView;
+import android.widget.Spinner;
import androidx.preference.Preference;
import androidx.preference.PreferenceViewHolder;
import com.android.settings.R;
-import com.android.settingslib.widget.settingsspinner.SettingsSpinner;
public class SpinnerPreference extends Preference implements CycleAdapter.SpinnerInterface {
@@ -63,7 +63,7 @@
@Override
public void onBindViewHolder(PreferenceViewHolder holder) {
super.onBindViewHolder(holder);
- SettingsSpinner spinner = (SettingsSpinner) holder.findViewById(R.id.cycles_spinner);
+ Spinner spinner = (Spinner) holder.findViewById(R.id.cycles_spinner);
spinner.setAdapter(mAdapter);
spinner.setSelection(mPosition);
spinner.setOnItemSelectedListener(mOnSelectedListener);
diff --git a/src/com/android/settings/development/tare/DropdownActivity.java b/src/com/android/settings/development/tare/DropdownActivity.java
index c1a11fa..55f1fec 100644
--- a/src/com/android/settings/development/tare/DropdownActivity.java
+++ b/src/com/android/settings/development/tare/DropdownActivity.java
@@ -26,6 +26,7 @@
import android.widget.Spinner;
import com.android.settings.R;
+import com.android.settingslib.widget.SettingsSpinnerAdapter;
/**
* Dropdown activity to allow for the user to easily switch between the different TARE
@@ -58,9 +59,8 @@
String[] policies = getResources().getStringArray(R.array.tare_policies);
- ArrayAdapter<String> arrayAdapter = new ArrayAdapter<>(DropdownActivity.this,
- android.R.layout.simple_list_item_1, policies);
- arrayAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+ ArrayAdapter<String> arrayAdapter = new SettingsSpinnerAdapter<String>(this);
+ arrayAdapter.addAll(policies);
mSpinner.setAdapter(arrayAdapter);
mSpinner.setSelection(policy);
diff --git a/src/com/android/settings/deviceinfo/storage/StorageSelectionPreferenceController.java b/src/com/android/settings/deviceinfo/storage/StorageSelectionPreferenceController.java
index 5d5a2a5..d972575 100644
--- a/src/com/android/settings/deviceinfo/storage/StorageSelectionPreferenceController.java
+++ b/src/com/android/settings/deviceinfo/storage/StorageSelectionPreferenceController.java
@@ -26,8 +26,8 @@
import androidx.preference.PreferenceScreen;
import com.android.settings.core.BasePreferenceController;
+import com.android.settingslib.widget.SettingsSpinnerAdapter;
import com.android.settingslib.widget.SettingsSpinnerPreference;
-import com.android.settingslib.widget.settingsspinner.SettingsSpinnerAdapter;
import java.util.ArrayList;
import java.util.Collections;
diff --git a/src/com/android/settings/fuelgauge/BatteryBroadcastReceiver.java b/src/com/android/settings/fuelgauge/BatteryBroadcastReceiver.java
index 71e65bf..436cde8 100644
--- a/src/com/android/settings/fuelgauge/BatteryBroadcastReceiver.java
+++ b/src/com/android/settings/fuelgauge/BatteryBroadcastReceiver.java
@@ -112,7 +112,8 @@
if (intent != null && mBatteryListener != null) {
if (Intent.ACTION_BATTERY_CHANGED.equals(intent.getAction())) {
final String batteryLevel = Utils.getBatteryPercentage(intent);
- final String batteryStatus = Utils.getBatteryStatus(mContext, intent);
+ final String batteryStatus =
+ Utils.getBatteryStatus(mContext, intent, /* compactStatus= */ false);
final int batteryHealth = intent.getIntExtra(
BatteryManager.EXTRA_HEALTH, BatteryManager.BATTERY_HEALTH_UNKNOWN);
if (!Utils.isBatteryPresent(intent)) {
diff --git a/src/com/android/settings/fuelgauge/BatteryInfo.java b/src/com/android/settings/fuelgauge/BatteryInfo.java
index 4e90710..98f19fe 100644
--- a/src/com/android/settings/fuelgauge/BatteryInfo.java
+++ b/src/com/android/settings/fuelgauge/BatteryInfo.java
@@ -37,7 +37,6 @@
import com.android.settings.overlay.FeatureFactory;
import com.android.settings.widget.UsageView;
import com.android.settingslib.R;
-import com.android.settingslib.fuelgauge.BatteryStatus;
import com.android.settingslib.fuelgauge.Estimate;
import com.android.settingslib.fuelgauge.EstimateKt;
import com.android.settingslib.utils.PowerUtil;
@@ -244,6 +243,8 @@
@NonNull BatteryUsageStats batteryUsageStats, Estimate estimate,
long elapsedRealtimeUs, boolean shortString) {
final long startTime = System.currentTimeMillis();
+ final boolean isCompactStatus = context.getResources().getBoolean(
+ com.android.settings.R.bool.config_use_compact_battery_status);
BatteryInfo info = new BatteryInfo();
info.mBatteryUsageStats = batteryUsageStats;
info.batteryLevel = Utils.getBatteryLevel(batteryBroadcast);
@@ -254,21 +255,21 @@
BatteryManager.EXTRA_HEALTH, BatteryManager.BATTERY_HEALTH_UNKNOWN)
== BatteryManager.BATTERY_HEALTH_OVERHEAT;
- info.statusLabel = getBatteryStatus(context, batteryBroadcast);
+ info.statusLabel = Utils.getBatteryStatus(context, batteryBroadcast, isCompactStatus);
info.batteryStatus = batteryBroadcast.getIntExtra(
BatteryManager.EXTRA_STATUS, BatteryManager.BATTERY_STATUS_UNKNOWN);
if (!info.mCharging) {
updateBatteryInfoDischarging(context, shortString, estimate, info);
} else {
updateBatteryInfoCharging(context, batteryBroadcast, batteryUsageStats,
- info);
+ info, isCompactStatus);
}
BatteryUtils.logRuntime(LOG_TAG, "time for getBatteryInfo", startTime);
return info;
}
private static void updateBatteryInfoCharging(Context context, Intent batteryBroadcast,
- BatteryUsageStats stats, BatteryInfo info) {
+ BatteryUsageStats stats, BatteryInfo info, boolean compactStatus) {
final Resources resources = context.getResources();
final long chargeTimeMs = stats.getChargeTimeRemainingMs();
final int status = batteryBroadcast.getIntExtra(BatteryManager.EXTRA_STATUS,
@@ -292,7 +293,8 @@
R.string.power_remaining_charging_duration_only, timeString);
info.chargeLabel = context.getString(resId, info.batteryPercentString, timeString);
} else {
- final String chargeStatusLabel = getBatteryStatus(context, batteryBroadcast);
+ final String chargeStatusLabel =
+ Utils.getBatteryStatus(context, batteryBroadcast, compactStatus);
info.remainingLabel = null;
info.chargeLabel = info.batteryLevel == 100 ? info.batteryPercentString :
resources.getString(R.string.power_charging, info.batteryPercentString,
@@ -326,35 +328,6 @@
}
}
- private static String getBatteryStatus(Context context, Intent batteryChangedIntent) {
- final Resources res = context.getResources();
- final boolean isShortStatus =
- res.getBoolean(com.android.settings.R.bool.config_use_compact_battery_status);
-
- if (!isShortStatus) {
- return Utils.getBatteryStatus(context, batteryChangedIntent);
- }
-
- final int status = batteryChangedIntent.getIntExtra(BatteryManager.EXTRA_STATUS,
- BatteryManager.BATTERY_STATUS_UNKNOWN);
- final BatteryStatus batteryStatus = new BatteryStatus(batteryChangedIntent);
- String statusString = res.getString(R.string.battery_info_status_unknown);
-
- if (batteryStatus.isCharged()) {
- statusString = res.getString(R.string.battery_info_status_full);
- } else {
- if (status == BatteryManager.BATTERY_STATUS_CHARGING) {
- statusString = res.getString(R.string.battery_info_status_charging);
- } else if (status == BatteryManager.BATTERY_STATUS_DISCHARGING) {
- statusString = res.getString(R.string.battery_info_status_discharging);
- } else if (status == BatteryManager.BATTERY_STATUS_NOT_CHARGING) {
- statusString = res.getString(R.string.battery_info_status_not_charging);
- }
- }
-
- return statusString;
- }
-
public interface BatteryDataParser {
void onParsingStarted(long startTime, long endTime);
diff --git a/src/com/android/settings/safetycenter/SafetySourceBroadcastReceiver.java b/src/com/android/settings/safetycenter/SafetySourceBroadcastReceiver.java
index 0fd0c0d..bdc52ad 100644
--- a/src/com/android/settings/safetycenter/SafetySourceBroadcastReceiver.java
+++ b/src/com/android/settings/safetycenter/SafetySourceBroadcastReceiver.java
@@ -16,6 +16,8 @@
package com.android.settings.safetycenter;
+import static android.content.Intent.ACTION_BOOT_COMPLETED;
+import static android.safetycenter.SafetyCenterManager.ACTION_REFRESH_SAFETY_SOURCES;
import static android.safetycenter.SafetyCenterManager.EXTRA_REFRESH_SAFETY_SOURCE_IDS;
import android.content.BroadcastReceiver;
@@ -24,6 +26,8 @@
import com.google.common.collect.ImmutableList;
+import java.util.List;
+
/** Broadcast receiver for handling requests from Safety Center for fresh data. */
public class SafetySourceBroadcastReceiver extends BroadcastReceiver {
@@ -33,17 +37,33 @@
return;
}
- String[] sourceIdsExtra = intent.getStringArrayExtra(EXTRA_REFRESH_SAFETY_SOURCE_IDS);
- if (sourceIdsExtra != null && sourceIdsExtra.length > 0) {
- ImmutableList<String> sourceIds = ImmutableList.copyOf(sourceIdsExtra);
-
- if (sourceIds.contains(LockScreenSafetySource.SAFETY_SOURCE_ID)) {
- LockScreenSafetySource.sendSafetyData(context);
+ if (ACTION_REFRESH_SAFETY_SOURCES.equals(intent.getAction())) {
+ String[] sourceIdsExtra =
+ intent.getStringArrayExtra(EXTRA_REFRESH_SAFETY_SOURCE_IDS);
+ if (sourceIdsExtra != null && sourceIdsExtra.length > 0) {
+ refreshSafetySources(context, ImmutableList.copyOf(sourceIdsExtra));
}
-
- if (sourceIds.contains(BiometricsSafetySource.SAFETY_SOURCE_ID)) {
- BiometricsSafetySource.sendSafetyData(context);
- }
+ return;
}
+
+
+ if (ACTION_BOOT_COMPLETED.equals(intent.getAction())) {
+ refreshAllSafetySources(context);
+ }
+ }
+
+ private static void refreshSafetySources(Context context, List<String> sourceIds) {
+ if (sourceIds.contains(LockScreenSafetySource.SAFETY_SOURCE_ID)) {
+ LockScreenSafetySource.sendSafetyData(context);
+ }
+
+ if (sourceIds.contains(BiometricsSafetySource.SAFETY_SOURCE_ID)) {
+ BiometricsSafetySource.sendSafetyData(context);
+ }
+ }
+
+ private static void refreshAllSafetySources(Context context) {
+ LockScreenSafetySource.sendSafetyData(context);
+ BiometricsSafetySource.sendSafetyData(context);
}
}
diff --git a/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsSpatialAudioControllerTest.java b/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsSpatialAudioControllerTest.java
new file mode 100644
index 0000000..ef81247
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/bluetooth/BluetoothDetailsSpatialAudioControllerTest.java
@@ -0,0 +1,206 @@
+/*
+ * 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 static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.media.AudioDeviceAttributes;
+import android.media.AudioManager;
+import android.media.Spatializer;
+
+import androidx.preference.PreferenceCategory;
+import androidx.preference.SwitchPreference;
+
+import com.android.settingslib.core.lifecycle.Lifecycle;
+
+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 java.util.ArrayList;
+import java.util.List;
+
+@RunWith(RobolectricTestRunner.class)
+public class BluetoothDetailsSpatialAudioControllerTest extends BluetoothDetailsControllerTestBase {
+
+ private static final String MAC_ADDRESS = "04:52:C7:0B:D8:3C";
+ private static final String KEY_SPATIAL_AUDIO = "spatial_audio";
+ private static final String KEY_HEAD_TRACKING = "head_tracking";
+
+ @Mock
+ private AudioManager mAudioManager;
+ @Mock
+ private Spatializer mSpatializer;
+ @Mock
+ private Lifecycle mSpatialAudioLifecycle;
+ @Mock
+ private PreferenceCategory mProfilesContainer;
+
+ private BluetoothDetailsSpatialAudioController mController;
+ private SwitchPreference mSpatialAudioPref;
+ private SwitchPreference mHeadTrackingPref;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ mContext = spy(RuntimeEnvironment.application);
+ when(mContext.getSystemService(AudioManager.class)).thenReturn(mAudioManager);
+ when(mAudioManager.getSpatializer()).thenReturn(mSpatializer);
+ when(mCachedDevice.getAddress()).thenReturn(MAC_ADDRESS);
+
+ mController = new BluetoothDetailsSpatialAudioController(mContext, mFragment,
+ mCachedDevice, mSpatialAudioLifecycle);
+ mController.mProfilesContainer = mProfilesContainer;
+
+ mSpatialAudioPref = mController.createSpatialAudioPreference(mContext);
+ mHeadTrackingPref = mController.createHeadTrackingPreference(mContext);
+
+ when(mProfilesContainer.findPreference(KEY_SPATIAL_AUDIO)).thenReturn(mSpatialAudioPref);
+ when(mProfilesContainer.findPreference(KEY_HEAD_TRACKING)).thenReturn(mHeadTrackingPref);
+ }
+
+ @Test
+ public void isAvailable_spatialAudioIsAvailable_returnsTrue() {
+ when(mSpatializer.isAvailableForDevice(mController.mAudioDevice)).thenReturn(true);
+ assertThat(mController.isAvailable()).isTrue();
+ }
+
+ @Test
+ public void isAvailable_spatialAudioIsNotAvailable_returnsFalse() {
+ when(mSpatializer.isAvailableForDevice(mController.mAudioDevice)).thenReturn(false);
+ assertThat(mController.isAvailable()).isFalse();
+ }
+
+ @Test
+ public void refresh_spatialAudioIsTurnedOn_checksSpatialAudioPreference() {
+ List<AudioDeviceAttributes> compatibleAudioDevices = new ArrayList<>();
+ compatibleAudioDevices.add(mController.mAudioDevice);
+ when(mSpatializer.getCompatibleAudioDevices()).thenReturn(compatibleAudioDevices);
+
+ mController.refresh();
+
+ assertThat(mSpatialAudioPref.isChecked()).isTrue();
+ }
+
+ @Test
+ public void refresh_spatialAudioIsTurnedOff_unchecksSpatialAudioPreference() {
+ List<AudioDeviceAttributes> compatibleAudioDevices = new ArrayList<>();
+ when(mSpatializer.getCompatibleAudioDevices()).thenReturn(compatibleAudioDevices);
+
+ mController.refresh();
+
+ assertThat(mSpatialAudioPref.isChecked()).isFalse();
+ }
+
+ @Test
+ public void refresh_spatialAudioOnAndHeadTrackingIsAvailable_showsHeadTrackingPreference() {
+ List<AudioDeviceAttributes> compatibleAudioDevices = new ArrayList<>();
+ compatibleAudioDevices.add(mController.mAudioDevice);
+ when(mSpatializer.getCompatibleAudioDevices()).thenReturn(compatibleAudioDevices);
+ when(mSpatializer.hasHeadTracker(mController.mAudioDevice)).thenReturn(true);
+
+ mController.refresh();
+
+ assertThat(mHeadTrackingPref.isVisible()).isTrue();
+ }
+
+ @Test
+ public void
+ refresh_spatialAudioOnAndHeadTrackingIsNotAvailable_hidesHeadTrackingPreference() {
+ List<AudioDeviceAttributes> compatibleAudioDevices = new ArrayList<>();
+ compatibleAudioDevices.add(mController.mAudioDevice);
+ when(mSpatializer.getCompatibleAudioDevices()).thenReturn(compatibleAudioDevices);
+ when(mSpatializer.hasHeadTracker(mController.mAudioDevice)).thenReturn(false);
+
+ mController.refresh();
+
+ assertThat(mHeadTrackingPref.isVisible()).isFalse();
+ }
+
+ @Test
+ public void refresh_spatialAudioOff_hidesHeadTrackingPreference() {
+ List<AudioDeviceAttributes> compatibleAudioDevices = new ArrayList<>();
+ when(mSpatializer.getCompatibleAudioDevices()).thenReturn(compatibleAudioDevices);
+
+ mController.refresh();
+
+ assertThat(mHeadTrackingPref.isVisible()).isFalse();
+ }
+
+ @Test
+ public void refresh_headTrackingIsTurnedOn_checksHeadTrackingPreference() {
+ List<AudioDeviceAttributes> compatibleAudioDevices = new ArrayList<>();
+ compatibleAudioDevices.add(mController.mAudioDevice);
+ when(mSpatializer.getCompatibleAudioDevices()).thenReturn(compatibleAudioDevices);
+ when(mSpatializer.hasHeadTracker(mController.mAudioDevice)).thenReturn(true);
+ when(mSpatializer.isHeadTrackerEnabled(mController.mAudioDevice)).thenReturn(true);
+
+ mController.refresh();
+
+ assertThat(mHeadTrackingPref.isChecked()).isTrue();
+ }
+
+ @Test
+ public void refresh_headTrackingIsTurnedOff_unchecksHeadTrackingPreference() {
+ List<AudioDeviceAttributes> compatibleAudioDevices = new ArrayList<>();
+ compatibleAudioDevices.add(mController.mAudioDevice);
+ when(mSpatializer.getCompatibleAudioDevices()).thenReturn(compatibleAudioDevices);
+ when(mSpatializer.hasHeadTracker(mController.mAudioDevice)).thenReturn(true);
+ when(mSpatializer.isHeadTrackerEnabled(mController.mAudioDevice)).thenReturn(false);
+
+ mController.refresh();
+
+ assertThat(mHeadTrackingPref.isChecked()).isFalse();
+ }
+
+ @Test
+ public void turnedOnSpatialAudio_invokesAddCompatibleAudioDevice() {
+ mSpatialAudioPref.setChecked(true);
+ mController.onPreferenceClick(mSpatialAudioPref);
+ verify(mSpatializer).addCompatibleAudioDevice(mController.mAudioDevice);
+ }
+
+ @Test
+ public void turnedOffSpatialAudio_invokesRemoveCompatibleAudioDevice() {
+ mSpatialAudioPref.setChecked(false);
+ mController.onPreferenceClick(mSpatialAudioPref);
+ verify(mSpatializer).removeCompatibleAudioDevice(mController.mAudioDevice);
+ }
+
+ @Test
+ public void turnedOnHeadTracking_invokesSetHeadTrackerEnabled_setsTrue() {
+ mHeadTrackingPref.setChecked(true);
+ mController.onPreferenceClick(mHeadTrackingPref);
+ verify(mSpatializer).setHeadTrackerEnabled(true, mController.mAudioDevice);
+ }
+
+ @Test
+ public void turnedOffHeadTracking_invokesSetHeadTrackerEnabled_setsFalse() {
+ mHeadTrackingPref.setChecked(false);
+ mController.onPreferenceClick(mHeadTrackingPref);
+ verify(mSpatializer).setHeadTrackerEnabled(false, mController.mAudioDevice);
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/fuelgauge/BatteryBroadcastReceiverTest.java b/tests/robotests/src/com/android/settings/fuelgauge/BatteryBroadcastReceiverTest.java
index 5f08698..d446930 100644
--- a/tests/robotests/src/com/android/settings/fuelgauge/BatteryBroadcastReceiverTest.java
+++ b/tests/robotests/src/com/android/settings/fuelgauge/BatteryBroadcastReceiverTest.java
@@ -85,8 +85,8 @@
assertThat(mBatteryBroadcastReceiver.mBatteryLevel)
.isEqualTo(Utils.getBatteryPercentage(mChargingIntent));
- assertThat(mBatteryBroadcastReceiver.mBatteryStatus)
- .isEqualTo(Utils.getBatteryStatus(mContext, mChargingIntent));
+ assertThat(mBatteryBroadcastReceiver.mBatteryStatus).isEqualTo(
+ Utils.getBatteryStatus(mContext, mChargingIntent, /* compactStatus= */ false));
verify(mBatteryListener).onBatteryChanged(BatteryUpdateType.BATTERY_LEVEL);
}
@@ -134,7 +134,7 @@
public void testOnReceive_batteryDataNotChanged_listenerNotInvoked() {
final String batteryLevel = Utils.getBatteryPercentage(mChargingIntent);
final String batteryStatus =
- Utils.getBatteryStatus(mContext, mChargingIntent);
+ Utils.getBatteryStatus(mContext, mChargingIntent, /* compactStatus= */ false);
mBatteryBroadcastReceiver.mBatteryLevel = batteryLevel;
mBatteryBroadcastReceiver.mBatteryStatus = batteryStatus;
@@ -159,8 +159,8 @@
assertThat(mBatteryBroadcastReceiver.mBatteryLevel)
.isEqualTo(Utils.getBatteryPercentage(mChargingIntent));
- assertThat(mBatteryBroadcastReceiver.mBatteryStatus)
- .isEqualTo(Utils.getBatteryStatus(mContext, mChargingIntent));
+ assertThat(mBatteryBroadcastReceiver.mBatteryStatus).isEqualTo(
+ Utils.getBatteryStatus(mContext, mChargingIntent, /* compactStatus= */ false));
assertThat(mBatteryBroadcastReceiver.mBatteryHealth)
.isEqualTo(BatteryManager.BATTERY_HEALTH_UNKNOWN);
// 2 times because register will force update the battery
diff --git a/tests/unit/src/com/android/settings/safetycenter/SafetySourceBroadcastReceiverTest.java b/tests/unit/src/com/android/settings/safetycenter/SafetySourceBroadcastReceiverTest.java
index 8806e50..6c9addd 100644
--- a/tests/unit/src/com/android/settings/safetycenter/SafetySourceBroadcastReceiverTest.java
+++ b/tests/unit/src/com/android/settings/safetycenter/SafetySourceBroadcastReceiverTest.java
@@ -16,6 +16,7 @@
package com.android.settings.safetycenter;
+import static android.safetycenter.SafetyCenterManager.ACTION_REFRESH_SAFETY_SOURCES;
import static android.safetycenter.SafetyCenterManager.EXTRA_REFRESH_SAFETY_SOURCE_IDS;
import static com.google.common.truth.Truth.assertThat;
@@ -67,12 +68,24 @@
}
@Test
+ public void sendSafetyData_whenSafetyCenterIsEnabled_withNoIntentAction_sendsNoData() {
+ when(mSafetyCenterStatusHolder.isEnabled(mApplicationContext)).thenReturn(true);
+ Intent intent = new Intent().putExtra(EXTRA_REFRESH_SAFETY_SOURCE_IDS, new String[]{});
+
+ new SafetySourceBroadcastReceiver().onReceive(mApplicationContext, intent);
+
+ verify(mSafetyCenterManagerWrapper, never()).sendSafetyCenterUpdate(any(), any());
+ }
+
+ @Test
public void sendSafetyData_whenSafetyCenterIsDisabled_sendsNoData() {
when(mSafetyCenterStatusHolder.isEnabled(mApplicationContext)).thenReturn(false);
Intent intent =
- new Intent().putExtra(
- EXTRA_REFRESH_SAFETY_SOURCE_IDS,
- new String[]{ LockScreenSafetySource.SAFETY_SOURCE_ID });
+ new Intent()
+ .setAction(ACTION_REFRESH_SAFETY_SOURCES)
+ .putExtra(
+ EXTRA_REFRESH_SAFETY_SOURCE_IDS,
+ new String[]{ LockScreenSafetySource.SAFETY_SOURCE_ID });
new SafetySourceBroadcastReceiver().onReceive(mApplicationContext, intent);
@@ -82,7 +95,7 @@
@Test
public void sendSafetyData_whenSafetyCenterIsEnabled_withNullSourceIds_sendsNoData() {
when(mSafetyCenterStatusHolder.isEnabled(mApplicationContext)).thenReturn(true);
- Intent intent = new Intent();
+ Intent intent = new Intent().setAction(ACTION_REFRESH_SAFETY_SOURCES);
new SafetySourceBroadcastReceiver().onReceive(mApplicationContext, intent);
@@ -92,7 +105,10 @@
@Test
public void sendSafetyData_whenSafetyCenterIsEnabled_withNoSourceIds_sendsNoData() {
when(mSafetyCenterStatusHolder.isEnabled(mApplicationContext)).thenReturn(true);
- Intent intent = new Intent().putExtra(EXTRA_REFRESH_SAFETY_SOURCE_IDS, new String[]{});
+ Intent intent =
+ new Intent()
+ .setAction(ACTION_REFRESH_SAFETY_SOURCES)
+ .putExtra(EXTRA_REFRESH_SAFETY_SOURCE_IDS, new String[]{});
new SafetySourceBroadcastReceiver().onReceive(mApplicationContext, intent);
@@ -103,9 +119,11 @@
public void sendSafetyData_withLockscreenSourceId_sendsLockscreenData() {
when(mSafetyCenterStatusHolder.isEnabled(mApplicationContext)).thenReturn(true);
Intent intent =
- new Intent().putExtra(
- EXTRA_REFRESH_SAFETY_SOURCE_IDS,
- new String[]{ LockScreenSafetySource.SAFETY_SOURCE_ID });
+ new Intent()
+ .setAction(ACTION_REFRESH_SAFETY_SOURCES)
+ .putExtra(
+ EXTRA_REFRESH_SAFETY_SOURCE_IDS,
+ new String[]{ LockScreenSafetySource.SAFETY_SOURCE_ID });
new SafetySourceBroadcastReceiver().onReceive(mApplicationContext, intent);
ArgumentCaptor<SafetySourceData> captor = ArgumentCaptor.forClass(SafetySourceData.class);
@@ -120,13 +138,31 @@
public void sendSafetyData_withBiometricsSourceId_sendsBiometricData() {
when(mSafetyCenterStatusHolder.isEnabled(mApplicationContext)).thenReturn(true);
Intent intent =
- new Intent().putExtra(
- EXTRA_REFRESH_SAFETY_SOURCE_IDS,
- new String[]{ BiometricsSafetySource.SAFETY_SOURCE_ID });
+ new Intent()
+ .setAction(ACTION_REFRESH_SAFETY_SOURCES)
+ .putExtra(
+ EXTRA_REFRESH_SAFETY_SOURCE_IDS,
+ new String[]{ BiometricsSafetySource.SAFETY_SOURCE_ID });
new SafetySourceBroadcastReceiver().onReceive(mApplicationContext, intent);
// TODO(b/215517420): Update this test when BiometricSafetySource is implemented.
verify(mSafetyCenterManagerWrapper, never()).sendSafetyCenterUpdate(any(), any());
}
+
+ @Test
+ public void sendSafetyData_onBootCompleted_sendsBiometricAndLockscreenData() {
+ when(mSafetyCenterStatusHolder.isEnabled(mApplicationContext)).thenReturn(true);
+ Intent intent = new Intent().setAction(Intent.ACTION_BOOT_COMPLETED);
+
+ // TODO(b/215517420): Update this test when BiometricSafetySource is implemented to test
+ // that biometrics data is also sent.
+ new SafetySourceBroadcastReceiver().onReceive(mApplicationContext, intent);
+ ArgumentCaptor<SafetySourceData> captor = ArgumentCaptor.forClass(SafetySourceData.class);
+ verify(mSafetyCenterManagerWrapper, times(1))
+ .sendSafetyCenterUpdate(any(), captor.capture());
+ SafetySourceData safetySourceData = captor.getValue();
+
+ assertThat(safetySourceData.getId()).isEqualTo(LockScreenSafetySource.SAFETY_SOURCE_ID);
+ }
}