Merge "Add a content description for the illustration if it is an animation" into main
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 24839ff..6071b06 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -6611,8 +6611,12 @@
<string name="battery_usage_timestamps_content_description"><xliff:g id="from_timestamp">%1$s</xliff:g> to <xliff:g id="to_timestamp">%2$s</xliff:g></string>
<!-- [CHAR_LIMIT=NONE] The first slot is a week day (e.g. "Monday"); the second slot is a hourly time span (e.g. "6 AM - 8 AM"). -->
<string name="battery_usage_day_and_hour"><xliff:g id="day">%1$s</xliff:g> <xliff:g id="hour">%2$s</xliff:g></string>
- <!-- [CHAR_LIMIT=NONE] Accessibility content description for each slot in battery chart view. -->
- <string name="battery_usage_time_info_and_battery_level"><xliff:g id="time_info" example="Battery usage for Monday 6 AM - 8 AM">%1$s</xliff:g> <xliff:g id="battery_level" example="Battery level percentage from 83% to 59%">%2$s</xliff:g></string>
+ <!-- [CHAR_LIMIT=NONE] Accessibility content description for each slot in battery chart view. Please reuse the words in tc/6732629268310936155 -->
+ <string name="battery_usage_status_time_info_and_battery_level"><xliff:g id="selected_status" example="Selected">%1$s</xliff:g>, <xliff:g id="time_info" example="Battery usage for Monday 6 AM - 8 AM">%2$s</xliff:g> <xliff:g id="battery_level" example="Battery level percentage from 83% to 59%">%3$s</xliff:g></string>
+ <!-- [CHAR_LIMIT=NONE] Accessibility content description for the battery usage slot is selected. -->
+ <string name="battery_chart_slot_status_selected">Selected</string>
+ <!-- [CHAR_LIMIT=NONE] Accessibility content description for the battery usage slot is not selected -->
+ <string name="battery_chart_slot_status_unselected">Unselected</string>
<!-- [CHAR_LIMIT=NONE] Accessibility content description for battery chart view. -->
<string name="battery_usage_chart">Battery usage chart</string>
<!-- [CHAR_LIMIT=NONE] Accessibility content description for daily battery chart view. -->
diff --git a/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamMediaService.java b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamMediaService.java
index a1bb84c..ec8d7bc 100644
--- a/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamMediaService.java
+++ b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamMediaService.java
@@ -34,7 +34,10 @@
import android.media.session.MediaSession;
import android.media.session.PlaybackState;
import android.os.Bundle;
+import android.os.Handler;
+import android.os.HandlerThread;
import android.os.IBinder;
+import android.os.Process;
import android.util.Log;
import android.view.KeyEvent;
@@ -51,24 +54,21 @@
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant;
+import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant.LocalBluetoothLeBroadcastSourceState;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.settingslib.bluetooth.VolumeControlProfile;
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
import com.android.settingslib.utils.ThreadUtils;
-import java.util.Collections;
-import java.util.List;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.concurrent.atomic.AtomicInteger;
+import java.util.HashMap;
+import java.util.Map;
public class AudioStreamMediaService extends Service {
static final String BROADCAST_ID = "audio_stream_media_service_broadcast_id";
static final String BROADCAST_TITLE = "audio_stream_media_service_broadcast_title";
static final String DEVICES = "audio_stream_media_service_devices";
private static final String TAG = "AudioStreamMediaService";
- private static final int NOTIFICATION_ID = 1;
+ private static final int NOTIFICATION_ID = R.string.audio_streams_title;
private static final int BROADCAST_LISTENING_NOW_TEXT = R.string.audio_streams_listening_now;
private static final int BROADCAST_STREAM_PAUSED_TEXT = R.string.audio_streams_present_now;
@VisibleForTesting static final String LEAVE_BROADCAST_ACTION = "leave_broadcast_action";
@@ -113,17 +113,16 @@
private final MetricsFeatureProvider mMetricsFeatureProvider =
FeatureFactory.getFeatureFactory().getMetricsFeatureProvider();
- private final ExecutorService mExecutor = Executors.newSingleThreadExecutor();
- private final AtomicBoolean mIsMuted = new AtomicBoolean(false);
- private final AtomicBoolean mIsHysteresis = new AtomicBoolean(false);
+ private final HandlerThread mHandlerThread = new HandlerThread(TAG,
+ Process.THREAD_PRIORITY_BACKGROUND);
+ private boolean mIsMuted = false;
// Set 25 as default as the volume range from `VolumeControlProfile` is from 0 to 255.
// If the initial volume from `onDeviceVolumeChanged` is larger than zero (not muted), we will
// override this value. Otherwise, we raise the volume to 25 when the play button is clicked.
- private final AtomicInteger mLatestPositiveVolume = new AtomicInteger(25);
- private final Object mLocalSessionLock = new Object();
+ private int mLatestPositiveVolume = 25;
private boolean mHysteresisModeFixAvailable;
private int mBroadcastId;
- @Nullable private List<BluetoothDevice> mDevices;
+ @Nullable private Map<BluetoothDevice, LocalBluetoothLeBroadcastSourceState> mStateByDevice;
@Nullable private LocalBluetoothManager mLocalBtManager;
@Nullable private AudioStreamsHelper mAudioStreamsHelper;
@Nullable private LocalBluetoothLeBroadcastAssistant mLeBroadcastAssistant;
@@ -154,7 +153,6 @@
Log.w(TAG, "onCreate() : mLeBroadcastAssistant is null!");
return;
}
- mHysteresisModeFixAvailable = BluetoothUtils.isAudioSharingHysteresisModeFixAvailable(this);
mNotificationManager = getSystemService(NotificationManager.class);
if (mNotificationManager == null) {
@@ -162,7 +160,8 @@
return;
}
- mExecutor.execute(
+ mHandlerThread.start();
+ getHandler().post(
() -> {
if (mLocalBtManager == null
|| mLeBroadcastAssistant == null
@@ -184,45 +183,49 @@
mVolumeControl = mLocalBtManager.getProfileManager().getVolumeControlProfile();
if (mVolumeControl != null) {
mVolumeControlCallback = new VolumeControlCallback();
- mVolumeControl.registerCallback(mExecutor, mVolumeControlCallback);
+ mVolumeControl.registerCallback(getHandler()::post, mVolumeControlCallback);
}
mBroadcastAssistantCallback = new AssistantCallback();
mLeBroadcastAssistant.registerServiceCallBack(
- mExecutor, mBroadcastAssistantCallback);
+ getHandler()::post, mBroadcastAssistantCallback);
+
+ mHysteresisModeFixAvailable =
+ BluetoothUtils.isAudioSharingHysteresisModeFixAvailable(this);
});
}
+ @VisibleForTesting
+ Handler getHandler() {
+ return mHandlerThread.getThreadHandler();
+ }
+
@Override
public void onDestroy() {
Log.d(TAG, "onDestroy()");
- super.onDestroy();
- if (BluetoothUtils.isAudioSharingUIAvailable(this)) {
- if (mDevices != null) {
- mDevices.clear();
- mDevices = null;
- }
- synchronized (mLocalSessionLock) {
- if (mLocalSession != null) {
- mLocalSession.release();
- mLocalSession = null;
- }
- }
- mExecutor.execute(
- () -> {
- if (mLocalBtManager != null) {
- mLocalBtManager.getEventManager().unregisterCallback(
- mBluetoothCallback);
- }
- if (mLeBroadcastAssistant != null && mBroadcastAssistantCallback != null) {
- mLeBroadcastAssistant.unregisterServiceCallBack(
- mBroadcastAssistantCallback);
- }
- if (mVolumeControl != null && mVolumeControlCallback != null) {
- mVolumeControl.unregisterCallback(mVolumeControlCallback);
- }
- });
- }
+ getHandler().post(
+ () -> {
+ if (mStateByDevice != null) {
+ mStateByDevice.clear();
+ mStateByDevice = null;
+ }
+ if (mLocalSession != null) {
+ mLocalSession.release();
+ mLocalSession = null;
+ }
+ if (mLocalBtManager != null) {
+ mLocalBtManager.getEventManager().unregisterCallback(
+ mBluetoothCallback);
+ }
+ if (mLeBroadcastAssistant != null && mBroadcastAssistantCallback != null) {
+ mLeBroadcastAssistant.unregisterServiceCallBack(
+ mBroadcastAssistantCallback);
+ }
+ if (mVolumeControl != null && mVolumeControlCallback != null) {
+ mVolumeControl.unregisterCallback(mVolumeControlCallback);
+ }
+ });
+ mHandlerThread.quitSafely();
}
@Override
@@ -233,53 +236,59 @@
stopSelf();
return START_NOT_STICKY;
}
- mBroadcastId = intent.getIntExtra(BROADCAST_ID, -1);
- if (mBroadcastId == -1) {
- Log.w(TAG, "Invalid broadcast ID. Service will not start.");
- stopSelf();
- return START_NOT_STICKY;
- }
- var extra = intent.getParcelableArrayListExtra(DEVICES, BluetoothDevice.class);
- if (extra == null || extra.isEmpty()) {
- Log.w(TAG, "No device. Service will not start.");
- stopSelf();
- return START_NOT_STICKY;
- }
- mDevices = Collections.synchronizedList(extra);
- MediaSession.Token token =
- getOrCreateLocalMediaSession(intent.getStringExtra(BROADCAST_TITLE));
- startForeground(NOTIFICATION_ID, buildNotification(token));
+ getHandler().post(() -> {
+ mBroadcastId = intent.getIntExtra(BROADCAST_ID, -1);
+ if (mBroadcastId == -1) {
+ Log.w(TAG, "Invalid broadcast ID. Service will not start.");
+ stopSelf();
+ return;
+ }
+ var devices = intent.getParcelableArrayListExtra(DEVICES, BluetoothDevice.class);
+ if (devices == null || devices.isEmpty()) {
+ Log.w(TAG, "No device. Service will not start.");
+ stopSelf();
+ } else {
+ mStateByDevice = new HashMap<>();
+ devices.forEach(d -> mStateByDevice.put(d, STREAMING));
+ MediaSession.Token token =
+ getOrCreateLocalMediaSession(intent.getStringExtra(BROADCAST_TITLE));
+ startForeground(NOTIFICATION_ID, buildNotification(token));
+ }
+ });
return START_NOT_STICKY;
}
private MediaSession.Token getOrCreateLocalMediaSession(String title) {
- synchronized (mLocalSessionLock) {
- if (mLocalSession != null) {
- return mLocalSession.getSessionToken();
- }
- mLocalSession = new MediaSession(this, TAG);
- mLocalSession.setMetadata(
- new MediaMetadata.Builder()
- .putString(MediaMetadata.METADATA_KEY_TITLE, title)
- .putLong(MediaMetadata.METADATA_KEY_DURATION, STATIC_PLAYBACK_DURATION)
- .build());
- mLocalSession.setActive(true);
- mLocalSession.setPlaybackState(getPlaybackState());
- mMediaSessionCallback = new MediaSessionCallback();
- mLocalSession.setCallback(mMediaSessionCallback);
+ if (mLocalSession != null) {
return mLocalSession.getSessionToken();
}
+ mLocalSession = new MediaSession(this, TAG);
+ mLocalSession.setMetadata(
+ new MediaMetadata.Builder()
+ .putString(MediaMetadata.METADATA_KEY_TITLE, title)
+ .putLong(MediaMetadata.METADATA_KEY_DURATION, STATIC_PLAYBACK_DURATION)
+ .build());
+ mLocalSession.setActive(true);
+ mLocalSession.setPlaybackState(getPlaybackState());
+ mMediaSessionCallback = new MediaSessionCallback();
+ mLocalSession.setCallback(mMediaSessionCallback, getHandler());
+ return mLocalSession.getSessionToken();
}
private PlaybackState getPlaybackState() {
- if (mIsHysteresis.get()) {
+ if (isAllDeviceHysteresis()) {
return mPlayStateHysteresisBuilder.build();
}
- return mIsMuted.get() ? mPlayStatePausingBuilder.build() : mPlayStatePlayingBuilder.build();
+ return mIsMuted ? mPlayStatePausingBuilder.build() : mPlayStatePlayingBuilder.build();
+ }
+
+ private boolean isAllDeviceHysteresis() {
+ return mHysteresisModeFixAvailable && mStateByDevice != null
+ && mStateByDevice.values().stream().allMatch(v -> v == PAUSED);
}
private String getDeviceName() {
- if (mDevices == null || mDevices.isEmpty() || mLocalBtManager == null) {
+ if (mStateByDevice == null || mStateByDevice.isEmpty() || mLocalBtManager == null) {
return DEFAULT_DEVICE_NAME;
}
@@ -288,7 +297,8 @@
return DEFAULT_DEVICE_NAME;
}
- CachedBluetoothDevice device = manager.findDevice(mDevices.get(0));
+ CachedBluetoothDevice device = manager.findDevice(
+ mStateByDevice.keySet().iterator().next());
return device != null ? device.getName() : DEFAULT_DEVICE_NAME;
}
@@ -304,7 +314,7 @@
.setSmallIcon(com.android.settingslib.R.drawable.ic_bt_le_audio_sharing)
.setStyle(mediaStyle)
.setContentText(getString(
- mIsHysteresis.get() ? BROADCAST_STREAM_PAUSED_TEXT :
+ isAllDeviceHysteresis() ? BROADCAST_STREAM_PAUSED_TEXT :
BROADCAST_LISTENING_NOW_TEXT))
.setSilent(true);
return notificationBuilder.build();
@@ -333,7 +343,8 @@
public void onReceiveStateChanged(
BluetoothDevice sink, int sourceId, BluetoothLeBroadcastReceiveState state) {
super.onReceiveStateChanged(sink, sourceId, state);
- if (!mHysteresisModeFixAvailable || mDevices == null || !mDevices.contains(sink)) {
+ if (!mHysteresisModeFixAvailable || mStateByDevice == null
+ || !mStateByDevice.containsKey(sink)) {
return;
}
var sourceState = LocalBluetoothLeBroadcastAssistant.getLocalSourceState(state);
@@ -343,12 +354,10 @@
if (!streaming && !paused) {
return;
}
- // Atomically update mIsHysteresis if its current value is not the current paused state
- if (mIsHysteresis.compareAndSet(!paused, paused)) {
- synchronized (mLocalSessionLock) {
- if (mLocalSession == null) {
- return;
- }
+ boolean shouldUpdate = mStateByDevice.get(sink) != sourceState;
+ if (shouldUpdate) {
+ mStateByDevice.put(sink, sourceState);
+ if (mLocalSession != null) {
mLocalSession.setPlaybackState(getPlaybackState());
if (mNotificationManager != null) {
mNotificationManager.notify(
@@ -356,7 +365,7 @@
buildNotification(mLocalSession.getSessionToken())
);
}
- Log.d(TAG, "updating hysteresis mode to : " + paused);
+ Log.d(TAG, "updating source state to : " + sourceState);
}
}
}
@@ -374,24 +383,22 @@
@Override
public void onDeviceVolumeChanged(
@NonNull BluetoothDevice device, @IntRange(from = -255, to = 255) int volume) {
- if (mDevices == null || mDevices.isEmpty()) {
+ if (mStateByDevice == null || mStateByDevice.isEmpty()) {
Log.w(TAG, "active device or device has source is null!");
return;
}
Log.d(
TAG,
"onDeviceVolumeChanged() bluetoothDevice : " + device + " volume: " + volume);
- if (mDevices.contains(device)) {
+ if (mStateByDevice.containsKey(device)) {
if (volume == 0) {
- mIsMuted.set(true);
+ mIsMuted = true;
} else {
- mIsMuted.set(false);
- mLatestPositiveVolume.set(volume);
+ mIsMuted = false;
+ mLatestPositiveVolume = volume;
}
- synchronized (mLocalSessionLock) {
- if (mLocalSession != null) {
- mLocalSession.setPlaybackState(getPlaybackState());
- }
+ if (mLocalSession != null) {
+ mLocalSession.setPlaybackState(getPlaybackState());
}
}
}
@@ -400,10 +407,12 @@
private class BtCallback implements BluetoothCallback {
@Override
public void onBluetoothStateChanged(int bluetoothState) {
- if (BluetoothAdapter.STATE_OFF == bluetoothState) {
- Log.d(TAG, "onBluetoothStateChanged() : stopSelf");
- stopSelf();
- }
+ getHandler().post(() -> {
+ if (BluetoothAdapter.STATE_OFF == bluetoothState) {
+ Log.d(TAG, "onBluetoothStateChanged() : stopSelf");
+ stopSelf();
+ }
+ });
}
@Override
@@ -411,24 +420,17 @@
@NonNull CachedBluetoothDevice cachedDevice,
@ConnectionState int state,
int bluetoothProfile) {
- if (state == BluetoothAdapter.STATE_DISCONNECTED
- && bluetoothProfile == BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT
- && mDevices != null) {
- mDevices.remove(cachedDevice.getDevice());
- cachedDevice
- .getMemberDevice()
- .forEach(
- m -> {
- // Check nullability to pass NullAway check
- if (mDevices != null) {
- mDevices.remove(m.getDevice());
- }
- });
- }
- if (mDevices == null || mDevices.isEmpty()) {
- Log.d(TAG, "onProfileConnectionStateChanged() : stopSelf");
- stopSelf();
- }
+ getHandler().post(() -> {
+ if (state == BluetoothAdapter.STATE_DISCONNECTED
+ && bluetoothProfile == BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT
+ && mStateByDevice != null) {
+ mStateByDevice.remove(cachedDevice.getDevice());
+ }
+ if (mStateByDevice == null || mStateByDevice.isEmpty()) {
+ Log.d(TAG, "onProfileConnectionStateChanged() : stopSelf");
+ stopSelf();
+ }
+ });
}
}
@@ -454,10 +456,8 @@
@Override
public void onSeekTo(long pos) {
Log.d(TAG, "onSeekTo: " + pos);
- synchronized (mLocalSessionLock) {
- if (mLocalSession != null) {
- mLocalSession.setPlaybackState(getPlaybackState());
- }
+ if (mLocalSession != null) {
+ mLocalSession.setPlaybackState(getPlaybackState());
}
}
@@ -484,28 +484,26 @@
}
private void handleOnPlay() {
- if (mDevices == null || mDevices.isEmpty()) {
+ if (mStateByDevice == null || mStateByDevice.isEmpty()) {
Log.w(TAG, "active device or device has source is null!");
return;
}
- Log.d(
- TAG,
- "onPlay() setting volume for device : "
- + mDevices.getFirst()
- + " volume: "
- + mLatestPositiveVolume.get());
- setDeviceVolume(mDevices.getFirst(), mLatestPositiveVolume.get());
+ mStateByDevice.keySet().forEach(device -> {
+ Log.d(TAG, "onPlay() setting volume for device : " + device + " volume: "
+ + mLatestPositiveVolume);
+ setDeviceVolume(device, mLatestPositiveVolume);
+ });
}
private void handleOnPause() {
- if (mDevices == null || mDevices.isEmpty()) {
+ if (mStateByDevice == null || mStateByDevice.isEmpty()) {
Log.w(TAG, "active device or device has source is null!");
return;
}
- Log.d(
- TAG,
- "onPause() setting volume for device : " + mDevices.getFirst() + " volume: " + 0);
- setDeviceVolume(mDevices.getFirst(), /* volume= */ 0);
+ mStateByDevice.keySet().forEach(device -> {
+ Log.d(TAG, "onPause() setting volume for device : " + device + " volume: " + 0);
+ setDeviceVolume(device, /* volume= */ 0);
+ });
}
private void setDeviceVolume(BluetoothDevice device, int volume) {
@@ -514,7 +512,7 @@
ThreadUtils.postOnBackgroundThread(
() -> {
if (mVolumeControl != null) {
- mVolumeControl.setDeviceVolume(device, volume, true);
+ mVolumeControl.setDeviceVolume(device, volume, false);
mMetricsFeatureProvider.action(
getApplicationContext(), event, volume == 0 ? 1 : 0);
}
diff --git a/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceController.java b/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceController.java
index a248bdf..2681067 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceController.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceController.java
@@ -16,6 +16,9 @@
package com.android.settings.fuelgauge.batteryusage;
+import static com.android.settings.fuelgauge.batteryusage.BatteryChartViewModel.SELECTED_INDEX_ALL;
+import static com.android.settings.fuelgauge.batteryusage.BatteryChartViewModel.SELECTED_INDEX_INVALID;
+
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.app.settings.SettingsEnums;
@@ -82,10 +85,10 @@
@VisibleForTesting TextView mChartSummaryTextView;
@VisibleForTesting BatteryChartView mDailyChartView;
@VisibleForTesting BatteryChartView mHourlyChartView;
- @VisibleForTesting int mDailyChartIndex = BatteryChartViewModel.SELECTED_INDEX_ALL;
- @VisibleForTesting int mHourlyChartIndex = BatteryChartViewModel.SELECTED_INDEX_ALL;
- @VisibleForTesting int mDailyHighlightSlotIndex = BatteryChartViewModel.SELECTED_INDEX_INVALID;
- @VisibleForTesting int mHourlyHighlightSlotIndex = BatteryChartViewModel.SELECTED_INDEX_INVALID;
+ @VisibleForTesting int mDailyChartIndex = SELECTED_INDEX_ALL;
+ @VisibleForTesting int mHourlyChartIndex = SELECTED_INDEX_ALL;
+ @VisibleForTesting int mDailyHighlightSlotIndex = SELECTED_INDEX_INVALID;
+ @VisibleForTesting int mHourlyHighlightSlotIndex = SELECTED_INDEX_INVALID;
private boolean mIs24HourFormat;
private View mBatteryChartViewGroup;
@@ -198,8 +201,8 @@
getTotalHours(batteryLevelData));
if (batteryLevelData == null) {
- mDailyChartIndex = BatteryChartViewModel.SELECTED_INDEX_ALL;
- mHourlyChartIndex = BatteryChartViewModel.SELECTED_INDEX_ALL;
+ mDailyChartIndex = SELECTED_INDEX_ALL;
+ mHourlyChartIndex = SELECTED_INDEX_ALL;
mDailyViewModel = null;
mHourlyViewModels = null;
refreshUi();
@@ -226,9 +229,9 @@
}
boolean isHighlightSlotFocused() {
- return (mDailyHighlightSlotIndex != BatteryChartViewModel.SELECTED_INDEX_INVALID
+ return (mDailyHighlightSlotIndex != SELECTED_INDEX_INVALID
&& mDailyHighlightSlotIndex == mDailyChartIndex
- && mHourlyHighlightSlotIndex != BatteryChartViewModel.SELECTED_INDEX_INVALID
+ && mHourlyHighlightSlotIndex != SELECTED_INDEX_INVALID
&& mHourlyHighlightSlotIndex == mHourlyChartIndex);
}
@@ -242,8 +245,8 @@
}
void selectHighlightSlotIndex() {
- if (mDailyHighlightSlotIndex == BatteryChartViewModel.SELECTED_INDEX_INVALID
- || mHourlyHighlightSlotIndex == BatteryChartViewModel.SELECTED_INDEX_INVALID) {
+ if (mDailyHighlightSlotIndex == SELECTED_INDEX_INVALID
+ || mHourlyHighlightSlotIndex == SELECTED_INDEX_INVALID) {
return;
}
if (mDailyHighlightSlotIndex == mDailyChartIndex
@@ -258,8 +261,11 @@
"onDailyChartSelect:%d, onHourlyChartSelect:%d",
mDailyChartIndex, mHourlyChartIndex));
refreshUi();
+ // The highlight slot must be selected.
mHandler.post(
- () -> mDailyChartView.setAccessibilityPaneTitle(getAccessibilityAnnounceMessage()));
+ () ->
+ mDailyChartView.setAccessibilityPaneTitle(
+ getAccessibilityAnnounceMessage(/* isSlotSelected= */ true)));
if (mOnSelectedIndexUpdatedListener != null) {
mOnSelectedIndexUpdatedListener.onSelectedIndexUpdated();
}
@@ -295,15 +301,16 @@
}
Log.d(TAG, "onDailyChartSelect:" + trapezoidIndex);
mDailyChartIndex = trapezoidIndex;
- mHourlyChartIndex = BatteryChartViewModel.SELECTED_INDEX_ALL;
+ mHourlyChartIndex = SELECTED_INDEX_ALL;
refreshUi();
mHandler.post(
() ->
mDailyChartView.setAccessibilityPaneTitle(
- getAccessibilityAnnounceMessage()));
+ getAccessibilityAnnounceMessage(
+ mDailyChartIndex != SELECTED_INDEX_ALL)));
mMetricsFeatureProvider.action(
mPrefContext,
- trapezoidIndex == BatteryChartViewModel.SELECTED_INDEX_ALL
+ trapezoidIndex == SELECTED_INDEX_ALL
? SettingsEnums.ACTION_BATTERY_USAGE_DAILY_SHOW_ALL
: SettingsEnums.ACTION_BATTERY_USAGE_DAILY_TIME_SLOT,
mDailyChartIndex);
@@ -314,7 +321,7 @@
mHourlyChartView = hourlyChartView;
mHourlyChartView.setOnSelectListener(
trapezoidIndex -> {
- if (mDailyChartIndex == BatteryChartViewModel.SELECTED_INDEX_ALL) {
+ if (mDailyChartIndex == SELECTED_INDEX_ALL) {
// This will happen when a daily slot and an hour slot are clicked together.
return;
}
@@ -327,10 +334,11 @@
mHandler.post(
() ->
mHourlyChartView.setAccessibilityPaneTitle(
- getAccessibilityAnnounceMessage()));
+ getAccessibilityAnnounceMessage(
+ mHourlyChartIndex != SELECTED_INDEX_ALL)));
mMetricsFeatureProvider.action(
mPrefContext,
- trapezoidIndex == BatteryChartViewModel.SELECTED_INDEX_ALL
+ trapezoidIndex == SELECTED_INDEX_ALL
? SettingsEnums.ACTION_BATTERY_USAGE_SHOW_ALL
: SettingsEnums.ACTION_BATTERY_USAGE_TIME_SLOT,
mHourlyChartIndex);
@@ -378,27 +386,27 @@
} else {
mDailyChartView.setVisibility(View.VISIBLE);
if (mDailyChartIndex >= mDailyViewModel.size()) {
- mDailyChartIndex = BatteryChartViewModel.SELECTED_INDEX_ALL;
+ mDailyChartIndex = SELECTED_INDEX_ALL;
}
mDailyViewModel.setSelectedIndex(mDailyChartIndex);
mDailyViewModel.setHighlightSlotIndex(mDailyHighlightSlotIndex);
mDailyChartView.setViewModel(mDailyViewModel);
}
- if (mDailyChartIndex == BatteryChartViewModel.SELECTED_INDEX_ALL) {
+ if (mDailyChartIndex == SELECTED_INDEX_ALL) {
// Multiple days are selected, hide the hourly chart view.
animateBatteryHourlyChartView(/* visible= */ false);
} else {
animateBatteryHourlyChartView(/* visible= */ true);
final BatteryChartViewModel hourlyViewModel = mHourlyViewModels.get(mDailyChartIndex);
if (mHourlyChartIndex >= hourlyViewModel.size()) {
- mHourlyChartIndex = BatteryChartViewModel.SELECTED_INDEX_ALL;
+ mHourlyChartIndex = SELECTED_INDEX_ALL;
}
hourlyViewModel.setSelectedIndex(mHourlyChartIndex);
hourlyViewModel.setHighlightSlotIndex(
(mDailyChartIndex == mDailyHighlightSlotIndex)
? mHourlyHighlightSlotIndex
- : BatteryChartViewModel.SELECTED_INDEX_INVALID);
+ : SELECTED_INDEX_INVALID);
mHourlyChartView.setViewModel(hourlyViewModel);
}
}
@@ -416,7 +424,7 @@
isAccessibilityText
? mDailyViewModel.getContentDescription(mDailyChartIndex)
: mDailyViewModel.getFullText(mDailyChartIndex);
- if (mHourlyChartIndex == BatteryChartViewModel.SELECTED_INDEX_ALL) {
+ if (mHourlyChartIndex == SELECTED_INDEX_ALL) {
return selectedDayText;
}
@@ -441,15 +449,19 @@
return "";
}
- if (mDailyChartIndex == BatteryChartViewModel.SELECTED_INDEX_ALL
- || mHourlyChartIndex == BatteryChartViewModel.SELECTED_INDEX_ALL) {
+ if (mDailyChartIndex == SELECTED_INDEX_ALL || mHourlyChartIndex == SELECTED_INDEX_ALL) {
return mDailyViewModel.getSlotBatteryLevelText(mDailyChartIndex);
}
return mHourlyViewModels.get(mDailyChartIndex).getSlotBatteryLevelText(mHourlyChartIndex);
}
- private String getAccessibilityAnnounceMessage() {
+ private String getAccessibilityAnnounceMessage(final boolean isSlotSelected) {
+ final String selectedInformation =
+ mPrefContext.getString(
+ isSlotSelected
+ ? R.string.battery_chart_slot_status_selected
+ : R.string.battery_chart_slot_status_unselected);
final String slotInformation = getSlotInformation(/* isAccessibilityText= */ true);
final String slotInformationMessage =
slotInformation == null
@@ -460,7 +472,8 @@
final String batteryLevelPercentageMessage = getBatteryLevelPercentageInfo();
return mPrefContext.getString(
- R.string.battery_usage_time_info_and_battery_level,
+ R.string.battery_usage_status_time_info_and_battery_level,
+ selectedInformation,
slotInformationMessage,
batteryLevelPercentageMessage);
}
@@ -533,9 +546,8 @@
}
private boolean isAllSelected() {
- return (isBatteryLevelDataInOneDay()
- || mDailyChartIndex == BatteryChartViewModel.SELECTED_INDEX_ALL)
- && mHourlyChartIndex == BatteryChartViewModel.SELECTED_INDEX_ALL;
+ return (isBatteryLevelDataInOneDay() || mDailyChartIndex == SELECTED_INDEX_ALL)
+ && mHourlyChartIndex == SELECTED_INDEX_ALL;
}
@VisibleForTesting
@@ -571,9 +583,7 @@
return null;
}
BatteryDiffData allBatteryDiffData =
- batteryUsageData
- .get(BatteryChartViewModel.SELECTED_INDEX_ALL)
- .get(BatteryChartViewModel.SELECTED_INDEX_ALL);
+ batteryUsageData.get(SELECTED_INDEX_ALL).get(SELECTED_INDEX_ALL);
return allBatteryDiffData == null ? null : allBatteryDiffData.getAppDiffEntryList();
}
@@ -613,12 +623,9 @@
@Override
public String generateSlotBatteryLevelText(List<Integer> levels, int index) {
- final int fromBatteryLevelIndex =
- index == BatteryChartViewModel.SELECTED_INDEX_ALL ? 0 : index;
+ final int fromBatteryLevelIndex = index == SELECTED_INDEX_ALL ? 0 : index;
final int toBatteryLevelIndex =
- index == BatteryChartViewModel.SELECTED_INDEX_ALL
- ? levels.size() - 1
- : index + 1;
+ index == SELECTED_INDEX_ALL ? levels.size() - 1 : index + 1;
return mPrefContext.getString(
R.string.battery_level_percentage,
generateBatteryLevelText(levels.get(fromBatteryLevelIndex)),
@@ -687,9 +694,9 @@
return index == timestamps.size() - 1
? generateText(timestamps, index)
: mContext.getString(
- R.string.battery_usage_timestamps_content_description,
- generateText(timestamps, index),
- generateText(timestamps, index + 1));
+ R.string.battery_usage_timestamps_content_description,
+ generateText(timestamps, index),
+ generateText(timestamps, index + 1));
}
HourlyChartLabelTextGenerator updateSpecialCaseContext(
diff --git a/src/com/android/settings/fuelgauge/batteryusage/BatteryChartView.java b/src/com/android/settings/fuelgauge/batteryusage/BatteryChartView.java
index eafccdb..393d751 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/BatteryChartView.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/BatteryChartView.java
@@ -17,6 +17,8 @@
import static com.android.settings.Utils.formatPercentage;
import static com.android.settings.fuelgauge.batteryusage.BatteryChartViewModel.AxisLabelPosition.BETWEEN_TRAPEZOIDS;
+import static com.android.settings.fuelgauge.batteryusage.BatteryChartViewModel.SELECTED_INDEX_ALL;
+import static com.android.settings.fuelgauge.batteryusage.BatteryChartViewModel.SELECTED_INDEX_INVALID;
import static com.android.settingslib.fuelgauge.BatteryStatus.BATTERY_LEVEL_UNKNOWN;
import static java.lang.Math.abs;
@@ -81,7 +83,7 @@
getContext().getResources().getConfiguration().getLayoutDirection();
private BatteryChartViewModel mViewModel;
- private int mHoveredIndex = BatteryChartViewModel.SELECTED_INDEX_INVALID;
+ private int mHoveredIndex = SELECTED_INDEX_INVALID;
private int mDividerWidth;
private int mDividerHeight;
private float mTrapezoidVOffset;
@@ -245,9 +247,9 @@
// sent here.
return true;
case MotionEvent.ACTION_HOVER_EXIT:
- if (mHoveredIndex != BatteryChartViewModel.SELECTED_INDEX_INVALID) {
+ if (mHoveredIndex != SELECTED_INDEX_INVALID) {
sendAccessibilityEventForHover(AccessibilityEvent.TYPE_VIEW_HOVER_EXIT);
- mHoveredIndex = BatteryChartViewModel.SELECTED_INDEX_INVALID; // reset
+ mHoveredIndex = SELECTED_INDEX_INVALID; // reset
invalidate();
}
// Ignore the super.onHoverEvent() because the hovered trapezoid has already been
@@ -262,7 +264,7 @@
public void onHoverChanged(boolean hovered) {
super.onHoverChanged(hovered);
if (!hovered) {
- mHoveredIndex = BatteryChartViewModel.SELECTED_INDEX_INVALID; // reset
+ mHoveredIndex = SELECTED_INDEX_INVALID; // reset
invalidate();
}
}
@@ -295,9 +297,7 @@
if (mOnSelectListener != null) {
// Selects all if users click the same trapezoid item two times.
mOnSelectListener.onSelect(
- index == mViewModel.selectedIndex()
- ? BatteryChartViewModel.SELECTED_INDEX_ALL
- : index);
+ index == mViewModel.selectedIndex() ? SELECTED_INDEX_ALL : index);
}
view.performHapticFeedback(HapticFeedbackConstants.CONTEXT_CLICK);
}
@@ -332,8 +332,8 @@
setBackgroundColor(Color.TRANSPARENT);
mTrapezoidSolidColor = Utils.getColorAccentDefaultColor(context);
mTrapezoidColor = Utils.getDisabled(context, mTrapezoidSolidColor);
- mTrapezoidHoverColor = context.getColor(
- com.android.internal.R.color.materialColorSecondaryContainer);
+ mTrapezoidHoverColor =
+ context.getColor(com.android.internal.R.color.materialColorSecondaryContainer);
// Initializes the divider line paint.
final Resources resources = getContext().getResources();
mDividerWidth = resources.getDimensionPixelSize(R.dimen.chartview_divider_width);
@@ -623,8 +623,7 @@
// Configures the trapezoid paint color.
final int trapezoidColor =
(mViewModel.selectedIndex() == index
- || mViewModel.selectedIndex()
- == BatteryChartViewModel.SELECTED_INDEX_ALL)
+ || mViewModel.selectedIndex() == SELECTED_INDEX_ALL)
? mTrapezoidSolidColor
: mTrapezoidColor;
final boolean isHoverState =
@@ -659,9 +658,7 @@
}
private boolean isHighlightSlotValid() {
- return mViewModel != null
- && mViewModel.getHighlightSlotIndex()
- != BatteryChartViewModel.SELECTED_INDEX_INVALID;
+ return mViewModel != null && mViewModel.getHighlightSlotIndex() != SELECTED_INDEX_INVALID;
}
private void drawTransomLine(Canvas canvas) {
@@ -715,7 +712,7 @@
// Searches the corresponding trapezoid index from x location.
private int getTrapezoidIndex(float x) {
if (mTrapezoidSlots == null) {
- return BatteryChartViewModel.SELECTED_INDEX_INVALID;
+ return SELECTED_INDEX_INVALID;
}
for (int index = 0; index < mTrapezoidSlots.length; index++) {
final TrapezoidSlot slot = mTrapezoidSlots[index];
@@ -723,7 +720,7 @@
return index;
}
}
- return BatteryChartViewModel.SELECTED_INDEX_INVALID;
+ return SELECTED_INDEX_INVALID;
}
private void initializeAxisLabelsBounds() {
@@ -796,7 +793,11 @@
childInfo.setText(slotTimeInfo);
childInfo.setContentDescription(
mContext.getString(
- R.string.battery_usage_time_info_and_battery_level,
+ R.string.battery_usage_status_time_info_and_battery_level,
+ mContext.getString(
+ mViewModel.selectedIndex() == virtualViewId
+ ? R.string.battery_chart_slot_status_selected
+ : R.string.battery_chart_slot_status_unselected),
slotTimeInfo,
batteryLevelInfo));
childInfo.setAccessibilityFocused(virtualViewId == mAccessibilityFocusNodeViewId);
diff --git a/tests/robotests/src/com/android/settings/bluetooth/BluetoothDevicePreferenceTest.java b/tests/robotests/src/com/android/settings/bluetooth/BluetoothDevicePreferenceTest.java
index 6a72c7d..d318e06 100644
--- a/tests/robotests/src/com/android/settings/bluetooth/BluetoothDevicePreferenceTest.java
+++ b/tests/robotests/src/com/android/settings/bluetooth/BluetoothDevicePreferenceTest.java
@@ -34,6 +34,7 @@
import android.graphics.drawable.Drawable;
import android.os.Looper;
import android.os.UserManager;
+import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
import android.platform.test.flag.junit.SetFlagsRule;
import android.util.Pair;
@@ -175,6 +176,7 @@
}
@Test
+ @DisableFlags(Flags.FLAG_ENABLE_TEMPORARY_BOND_DEVICES_UI)
public void onClicked_deviceNotBonded_shouldLogBluetoothPairEvent() {
when(mCachedBluetoothDevice.isConnected()).thenReturn(false);
when(mCachedBluetoothDevice.getBondState()).thenReturn(BluetoothDevice.BOND_NONE);
@@ -192,6 +194,7 @@
}
@Test
+ @DisableFlags(Flags.FLAG_ENABLE_TEMPORARY_BOND_DEVICES_UI)
public void onClicked_deviceNotBonded_shouldLogBluetoothPairEventAndPairWithoutNameEvent() {
when(mCachedBluetoothDevice.isConnected()).thenReturn(false);
when(mCachedBluetoothDevice.getBondState()).thenReturn(BluetoothDevice.BOND_NONE);
diff --git a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamMediaServiceTest.java b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamMediaServiceTest.java
index a0e971b..c82c978 100644
--- a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamMediaServiceTest.java
+++ b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamMediaServiceTest.java
@@ -28,7 +28,6 @@
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
@@ -52,7 +51,9 @@
import android.media.session.ISessionController;
import android.media.session.MediaSessionManager;
import android.os.Bundle;
+import android.os.Handler;
import android.os.IBinder;
+import android.os.Looper;
import android.os.RemoteException;
import android.platform.test.flag.junit.SetFlagsRule;
import android.util.DisplayMetrics;
@@ -81,14 +82,12 @@
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
import org.robolectric.RobolectricTestRunner;
-import org.robolectric.android.util.concurrent.InlineExecutorService;
import org.robolectric.annotation.Config;
import org.robolectric.shadow.api.Shadow;
import org.robolectric.util.ReflectionHelpers;
import java.util.ArrayList;
import java.util.List;
-import java.util.Set;
@RunWith(RobolectricTestRunner.class)
@Config(
@@ -122,6 +121,7 @@
@Mock private PackageManager mPackageManager;
@Mock private DisplayMetrics mDisplayMetrics;
@Mock private Context mContext;
+ @Mock private Handler mHandler;
private FakeFeatureFactory mFeatureFactory;
private AudioStreamMediaService mAudioStreamMediaService;
@@ -145,11 +145,18 @@
when(mCachedBluetoothDevice.getName()).thenReturn(DEVICE_NAME);
when(mLocalBluetoothProfileManager.getVolumeControlProfile())
.thenReturn(mVolumeControlProfile);
-
- mAudioStreamMediaService = spy(new AudioStreamMediaService());
+ when(mHandler.post(any(Runnable.class))).thenAnswer(invocation -> {
+ ((Runnable) invocation.getArgument(0)).run();
+ return null;
+ });
+ when(mHandler.getLooper()).thenReturn(Looper.getMainLooper());
+ mAudioStreamMediaService = spy(new AudioStreamMediaService() {
+ @Override
+ Handler getHandler() {
+ return mHandler;
+ }
+ });
ReflectionHelpers.setField(mAudioStreamMediaService, "mBase", mContext);
- ReflectionHelpers.setField(
- mAudioStreamMediaService, "mExecutor", new InlineExecutorService());
when(mAudioStreamMediaService.getSystemService(anyString()))
.thenReturn(mMediaSessionManager);
when(mMediaSessionManager.createSession(any(), anyString(), any())).thenReturn(mISession);
@@ -392,31 +399,6 @@
}
@Test
- public void bluetoothCallback_onMemberDeviceDisconnect_stopSelf() {
- mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
- when(mCachedBluetoothDevice.getDevice()).thenReturn(mock(BluetoothDevice.class));
- CachedBluetoothDevice member = mock(CachedBluetoothDevice.class);
- when(mCachedBluetoothDevice.getMemberDevice()).thenReturn(Set.of(member));
- when(member.getDevice()).thenReturn(mDevice);
- var devices = new ArrayList<BluetoothDevice>();
- devices.add(mDevice);
-
- Intent intent = new Intent();
- intent.putExtra(BROADCAST_ID, 1);
- intent.putParcelableArrayListExtra(DEVICES, devices);
-
- mAudioStreamMediaService.onCreate();
- assertThat(mAudioStreamMediaService.mBluetoothCallback).isNotNull();
- mAudioStreamMediaService.onStartCommand(intent, /* flags= */ 0, /* startId= */ 0);
- mAudioStreamMediaService.mBluetoothCallback.onProfileConnectionStateChanged(
- mCachedBluetoothDevice,
- BluetoothAdapter.STATE_DISCONNECTED,
- BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT);
-
- verify(mAudioStreamMediaService).stopSelf();
- }
-
- @Test
public void mediaSessionCallback_onPause_setVolume() {
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);