Merge "DO NOT MERGE: Downbranch merge conflict [Output Switcher] Behavior improvement" into tm-dev
This commit is contained in:
@@ -46,8 +46,6 @@ import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
@@ -56,7 +54,6 @@ import java.util.concurrent.CopyOnWriteArrayList;
|
||||
*/
|
||||
@RequiresApi(Build.VERSION_CODES.R)
|
||||
public class LocalMediaManager implements BluetoothCallback {
|
||||
private static final Comparator<MediaDevice> COMPARATOR = Comparator.naturalOrder();
|
||||
private static final String TAG = "LocalMediaManager";
|
||||
private static final int MAX_DISCONNECTED_DEVICE_NUM = 5;
|
||||
|
||||
@@ -65,13 +62,15 @@ public class LocalMediaManager implements BluetoothCallback {
|
||||
MediaDeviceState.STATE_CONNECTING,
|
||||
MediaDeviceState.STATE_DISCONNECTED,
|
||||
MediaDeviceState.STATE_CONNECTING_FAILED,
|
||||
MediaDeviceState.STATE_SELECTED})
|
||||
MediaDeviceState.STATE_SELECTED,
|
||||
MediaDeviceState.STATE_GROUPING})
|
||||
public @interface MediaDeviceState {
|
||||
int STATE_CONNECTED = 0;
|
||||
int STATE_CONNECTING = 1;
|
||||
int STATE_DISCONNECTED = 2;
|
||||
int STATE_CONNECTING_FAILED = 3;
|
||||
int STATE_SELECTED = 4;
|
||||
int STATE_GROUPING = 5;
|
||||
}
|
||||
|
||||
private final Collection<DeviceCallback> mCallbacks = new CopyOnWriteArrayList<>();
|
||||
@@ -322,6 +321,7 @@ public class LocalMediaManager implements BluetoothCallback {
|
||||
* @return If add device successful return {@code true}, otherwise return {@code false}
|
||||
*/
|
||||
public boolean addDeviceToPlayMedia(MediaDevice device) {
|
||||
device.setState(MediaDeviceState.STATE_GROUPING);
|
||||
return mInfoMediaManager.addDeviceToPlayMedia(device);
|
||||
}
|
||||
|
||||
@@ -332,6 +332,7 @@ public class LocalMediaManager implements BluetoothCallback {
|
||||
* @return If device stop successful return {@code true}, otherwise return {@code false}
|
||||
*/
|
||||
public boolean removeDeviceFromPlayMedia(MediaDevice device) {
|
||||
device.setState(MediaDeviceState.STATE_GROUPING);
|
||||
return mInfoMediaManager.removeDeviceFromPlayMedia(device);
|
||||
}
|
||||
|
||||
@@ -524,7 +525,6 @@ public class LocalMediaManager implements BluetoothCallback {
|
||||
@Override
|
||||
public void onDeviceListAdded(List<MediaDevice> devices) {
|
||||
synchronized (mMediaDevicesLock) {
|
||||
Collections.sort(devices, COMPARATOR);
|
||||
mMediaDevices.clear();
|
||||
mMediaDevices.addAll(devices);
|
||||
// Add disconnected bluetooth devices only when phone output device is available.
|
||||
|
||||
@@ -39,6 +39,7 @@
|
||||
android:visibility="gone"
|
||||
android:paddingStart="0dp"
|
||||
android:paddingEnd="0dp"
|
||||
android:background="@null"
|
||||
android:progressDrawable="@drawable/media_output_dialog_seekbar_background"
|
||||
android:thumb="@null"
|
||||
android:layout_width="match_parent"
|
||||
|
||||
@@ -166,6 +166,14 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter {
|
||||
true /* showSubtitle */, true /* showStatus */);
|
||||
mSubTitleText.setText(R.string.media_output_dialog_connect_failed);
|
||||
mContainerLayout.setOnClickListener(v -> onItemClick(v, device));
|
||||
} else if (device.getState() == MediaDeviceState.STATE_GROUPING) {
|
||||
mProgressBar.getIndeterminateDrawable().setColorFilter(
|
||||
new PorterDuffColorFilter(
|
||||
mController.getColorItemContent(),
|
||||
PorterDuff.Mode.SRC_IN));
|
||||
setSingleLineLayout(getItemTitle(device), true /* bFocused */,
|
||||
false /* showSeekBar*/,
|
||||
true /* showProgressBar */, false /* showStatus */);
|
||||
} else if (mController.getSelectedMediaDevice().size() > 1
|
||||
&& isDeviceIncluded(mController.getSelectedMediaDevice(), device)) {
|
||||
mTitleText.setTextColor(mController.getColorItemContent());
|
||||
|
||||
@@ -184,6 +184,19 @@ public abstract class MediaOutputBaseDialog extends SystemUIDialog implements
|
||||
}
|
||||
};
|
||||
|
||||
private class LayoutManagerWrapper extends LinearLayoutManager {
|
||||
LayoutManagerWrapper(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLayoutCompleted(RecyclerView.State state) {
|
||||
super.onLayoutCompleted(state);
|
||||
mMediaOutputController.setRefreshing(false);
|
||||
mMediaOutputController.refreshDataSetIfNeeded();
|
||||
}
|
||||
}
|
||||
|
||||
public MediaOutputBaseDialog(Context context, BroadcastSender broadcastSender,
|
||||
MediaOutputController mediaOutputController) {
|
||||
super(context, R.style.Theme_SystemUI_Dialog_Media);
|
||||
@@ -192,7 +205,7 @@ public abstract class MediaOutputBaseDialog extends SystemUIDialog implements
|
||||
mContext = getContext();
|
||||
mBroadcastSender = broadcastSender;
|
||||
mMediaOutputController = mediaOutputController;
|
||||
mLayoutManager = new LinearLayoutManager(mContext);
|
||||
mLayoutManager = new LayoutManagerWrapper(mContext);
|
||||
mListMaxHeight = context.getResources().getDimensionPixelSize(
|
||||
R.dimen.media_output_dialog_list_max_height);
|
||||
mExecutor = Executors.newSingleThreadExecutor();
|
||||
@@ -274,6 +287,10 @@ public abstract class MediaOutputBaseDialog extends SystemUIDialog implements
|
||||
}
|
||||
|
||||
void refresh(boolean deviceSetChanged) {
|
||||
if (mMediaOutputController.isRefreshing()) {
|
||||
return;
|
||||
}
|
||||
mMediaOutputController.setRefreshing(true);
|
||||
// Update header icon
|
||||
final int iconRes = getHeaderIconRes();
|
||||
final IconCompat iconCompat = getHeaderIcon();
|
||||
|
||||
@@ -81,6 +81,8 @@ import com.android.systemui.statusbar.phone.SystemUIDialog;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
@@ -108,11 +110,15 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback,
|
||||
private final DialogLaunchAnimator mDialogLaunchAnimator;
|
||||
private final List<MediaDevice> mGroupMediaDevices = new CopyOnWriteArrayList<>();
|
||||
private final CommonNotifCollection mNotifCollection;
|
||||
private final Object mMediaDevicesLock = new Object();
|
||||
@VisibleForTesting
|
||||
final List<MediaDevice> mMediaDevices = new CopyOnWriteArrayList<>();
|
||||
final List<MediaDevice> mCachedMediaDevices = new CopyOnWriteArrayList<>();
|
||||
private final NearbyMediaDevicesManager mNearbyMediaDevicesManager;
|
||||
private final Map<String, Integer> mNearbyDeviceInfoMap = new ConcurrentHashMap<>();
|
||||
|
||||
private boolean mIsRefreshing = false;
|
||||
private boolean mNeedRefresh = false;
|
||||
private MediaController mMediaController;
|
||||
@VisibleForTesting
|
||||
Callback mCallback;
|
||||
@@ -172,7 +178,10 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback,
|
||||
}
|
||||
|
||||
void start(@NonNull Callback cb) {
|
||||
mMediaDevices.clear();
|
||||
synchronized (mMediaDevicesLock) {
|
||||
mCachedMediaDevices.clear();
|
||||
mMediaDevices.clear();
|
||||
}
|
||||
mNearbyDeviceInfoMap.clear();
|
||||
if (mNearbyMediaDevicesManager != null) {
|
||||
mNearbyMediaDevicesManager.registerNearbyDevicesCallback(this);
|
||||
@@ -211,6 +220,14 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback,
|
||||
return routerParams != null && !routerParams.isMediaTransferReceiverEnabled();
|
||||
}
|
||||
|
||||
void setRefreshing(boolean refreshing) {
|
||||
mIsRefreshing = refreshing;
|
||||
}
|
||||
|
||||
boolean isRefreshing() {
|
||||
return mIsRefreshing;
|
||||
}
|
||||
|
||||
void stop() {
|
||||
if (mMediaController != null) {
|
||||
mMediaController.unregisterCallback(mCb);
|
||||
@@ -219,7 +236,10 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback,
|
||||
mLocalMediaManager.unregisterCallback(this);
|
||||
mLocalMediaManager.stopScan();
|
||||
}
|
||||
mMediaDevices.clear();
|
||||
synchronized (mMediaDevicesLock) {
|
||||
mCachedMediaDevices.clear();
|
||||
mMediaDevices.clear();
|
||||
}
|
||||
if (mNearbyMediaDevicesManager != null) {
|
||||
mNearbyMediaDevicesManager.unregisterNearbyDevicesCallback(this);
|
||||
}
|
||||
@@ -228,15 +248,23 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback,
|
||||
|
||||
@Override
|
||||
public void onDeviceListUpdate(List<MediaDevice> devices) {
|
||||
buildMediaDevices(devices);
|
||||
mCallback.onDeviceListChanged();
|
||||
if (mMediaDevices.isEmpty() || !mIsRefreshing) {
|
||||
buildMediaDevices(devices);
|
||||
mCallback.onDeviceListChanged();
|
||||
} else {
|
||||
synchronized (mMediaDevicesLock) {
|
||||
mNeedRefresh = true;
|
||||
mCachedMediaDevices.clear();
|
||||
mCachedMediaDevices.addAll(devices);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSelectedDeviceStateChanged(MediaDevice device,
|
||||
@LocalMediaManager.MediaDeviceState int state) {
|
||||
mCallback.onRouteChanged();
|
||||
mMetricLogger.logOutputSuccess(device.toString(), mMediaDevices);
|
||||
mMetricLogger.logOutputSuccess(device.toString(), new ArrayList<>(mMediaDevices));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -247,7 +275,7 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback,
|
||||
@Override
|
||||
public void onRequestFailed(int reason) {
|
||||
mCallback.onRouteChanged();
|
||||
mMetricLogger.logOutputFailure(mMediaDevices, reason);
|
||||
mMetricLogger.logOutputFailure(new ArrayList<>(mMediaDevices), reason);
|
||||
}
|
||||
|
||||
Drawable getAppSourceIcon() {
|
||||
@@ -399,6 +427,14 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback,
|
||||
}
|
||||
}
|
||||
|
||||
void refreshDataSetIfNeeded() {
|
||||
if (mNeedRefresh) {
|
||||
buildMediaDevices(mCachedMediaDevices);
|
||||
mCallback.onDeviceListChanged();
|
||||
mNeedRefresh = false;
|
||||
}
|
||||
}
|
||||
|
||||
public int getColorConnectedItemBackground() {
|
||||
return mColorConnectedItemBackground;
|
||||
}
|
||||
@@ -432,50 +468,55 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback,
|
||||
}
|
||||
|
||||
private void buildMediaDevices(List<MediaDevice> devices) {
|
||||
// For the first time building list, to make sure the top device is the connected device.
|
||||
if (mMediaDevices.isEmpty()) {
|
||||
final MediaDevice connectedMediaDevice = getCurrentConnectedMediaDevice();
|
||||
if (connectedMediaDevice == null) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "No connected media device.");
|
||||
synchronized (mMediaDevicesLock) {
|
||||
attachRangeInfo(devices);
|
||||
Collections.sort(devices, Comparator.naturalOrder());
|
||||
// For the first time building list, to make sure the top device is the connected
|
||||
// device.
|
||||
if (mMediaDevices.isEmpty()) {
|
||||
final MediaDevice connectedMediaDevice = getCurrentConnectedMediaDevice();
|
||||
if (connectedMediaDevice == null) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "No connected media device.");
|
||||
}
|
||||
mMediaDevices.addAll(devices);
|
||||
return;
|
||||
}
|
||||
for (MediaDevice device : devices) {
|
||||
if (TextUtils.equals(device.getId(), connectedMediaDevice.getId())) {
|
||||
mMediaDevices.add(0, device);
|
||||
} else {
|
||||
mMediaDevices.add(device);
|
||||
}
|
||||
}
|
||||
mMediaDevices.addAll(devices);
|
||||
return;
|
||||
}
|
||||
for (MediaDevice device : devices) {
|
||||
if (TextUtils.equals(device.getId(), connectedMediaDevice.getId())) {
|
||||
mMediaDevices.add(0, device);
|
||||
} else {
|
||||
mMediaDevices.add(device);
|
||||
// To keep the same list order
|
||||
final List<MediaDevice> targetMediaDevices = new ArrayList<>();
|
||||
for (MediaDevice originalDevice : mMediaDevices) {
|
||||
for (MediaDevice newDevice : devices) {
|
||||
if (TextUtils.equals(originalDevice.getId(), newDevice.getId())) {
|
||||
targetMediaDevices.add(newDevice);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
// To keep the same list order
|
||||
final Collection<MediaDevice> targetMediaDevices = new ArrayList<>();
|
||||
for (MediaDevice originalDevice : mMediaDevices) {
|
||||
for (MediaDevice newDevice : devices) {
|
||||
if (TextUtils.equals(originalDevice.getId(), newDevice.getId())) {
|
||||
targetMediaDevices.add(newDevice);
|
||||
break;
|
||||
}
|
||||
if (targetMediaDevices.size() != devices.size()) {
|
||||
devices.removeAll(targetMediaDevices);
|
||||
targetMediaDevices.addAll(devices);
|
||||
}
|
||||
mMediaDevices.clear();
|
||||
mMediaDevices.addAll(targetMediaDevices);
|
||||
}
|
||||
if (targetMediaDevices.size() != devices.size()) {
|
||||
devices.removeAll(targetMediaDevices);
|
||||
targetMediaDevices.addAll(devices);
|
||||
}
|
||||
mMediaDevices.clear();
|
||||
mMediaDevices.addAll(targetMediaDevices);
|
||||
attachRangeInfo();
|
||||
}
|
||||
|
||||
private void attachRangeInfo() {
|
||||
for (MediaDevice mediaDevice : mMediaDevices) {
|
||||
private void attachRangeInfo(List<MediaDevice> devices) {
|
||||
for (MediaDevice mediaDevice : devices) {
|
||||
if (mNearbyDeviceInfoMap.containsKey(mediaDevice.getId())) {
|
||||
mediaDevice.setRangeZone(mNearbyDeviceInfoMap.get(mediaDevice.getId()));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
List<MediaDevice> getGroupMediaDevices() {
|
||||
@@ -609,26 +650,30 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback,
|
||||
}
|
||||
|
||||
boolean isTransferring() {
|
||||
for (MediaDevice device : mMediaDevices) {
|
||||
if (device.getState() == LocalMediaManager.MediaDeviceState.STATE_CONNECTING) {
|
||||
return true;
|
||||
synchronized (mMediaDevicesLock) {
|
||||
for (MediaDevice device : mMediaDevices) {
|
||||
if (device.getState() == LocalMediaManager.MediaDeviceState.STATE_CONNECTING) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean isZeroMode() {
|
||||
if (mMediaDevices.size() == 1) {
|
||||
final MediaDevice device = mMediaDevices.iterator().next();
|
||||
// Add "pair new" only when local output device exists
|
||||
final int type = device.getDeviceType();
|
||||
if (type == MediaDevice.MediaDeviceType.TYPE_PHONE_DEVICE
|
||||
|| type == MediaDevice.MediaDeviceType.TYPE_3POINT5_MM_AUDIO_DEVICE
|
||||
|| type == MediaDevice.MediaDeviceType.TYPE_USB_C_AUDIO_DEVICE) {
|
||||
return true;
|
||||
synchronized (mMediaDevicesLock) {
|
||||
if (mMediaDevices.size() == 1) {
|
||||
final MediaDevice device = mMediaDevices.iterator().next();
|
||||
// Add "pair new" only when local output device exists
|
||||
final int type = device.getDeviceType();
|
||||
if (type == MediaDevice.MediaDeviceType.TYPE_PHONE_DEVICE
|
||||
|| type == MediaDevice.MediaDeviceType.TYPE_3POINT5_MM_AUDIO_DEVICE
|
||||
|| type == MediaDevice.MediaDeviceType.TYPE_USB_C_AUDIO_DEVICE) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void launchBluetoothPairing(View view) {
|
||||
|
||||
Reference in New Issue
Block a user