Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 51b5fb54 authored by Derek Jedral's avatar Derek Jedral Committed by Android (Google) Code Review
Browse files

Merge "Add methods for suggested devices called from UMO" into main

parents e35b0e57 985485b5
Loading
Loading
Loading
Loading
+8 −2
Original line number Diff line number Diff line
@@ -54,12 +54,18 @@ public class ComplexMediaDevice extends MediaDevice {

    @Override
    public Drawable getIcon() {
        return mContext.getDrawable(R.drawable.ic_media_avr_device);
        return getIcon(mContext);
    }

    @Override
    public Drawable getIconWithoutBackground() {
        return mContext.getDrawable(R.drawable.ic_media_avr_device);
        return getIcon(mContext);
    }

    /** Gets the drawable associated with the complex media device. */
    @NonNull
    public static Drawable getIcon(Context context) {
        return context.getDrawable(R.drawable.ic_media_avr_device);
    }

    @Override
+7 −1
Original line number Diff line number Diff line
@@ -33,6 +33,7 @@ import android.graphics.drawable.Drawable;
import android.media.MediaRoute2Info;
import android.media.RouteListingPreference;

import androidx.annotation.DrawableRes;
import androidx.annotation.VisibleForTesting;

import com.android.settingslib.R;
@@ -75,9 +76,14 @@ public class InfoMediaDevice extends MediaDevice {

    @VisibleForTesting
    @SuppressWarnings("NewApi")
    @DrawableRes
    int getDrawableResIdByType() {
        return getDrawableResIdByType(mRouteInfo.getType());
    }

    static int getDrawableResIdByType(@MediaRoute2Info.Type int type) {
        int resId;
        switch (mRouteInfo.getType()) {
        switch (type) {
            case TYPE_GROUP:
                resId = R.drawable.ic_media_group_device;
                break;
+111 −44
Original line number Diff line number Diff line
@@ -52,6 +52,7 @@ import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.content.ComponentName;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.media.MediaRoute2Info;
import android.media.RouteListingPreference;
import android.media.RoutingSessionInfo;
@@ -69,6 +70,7 @@ import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;

import com.android.internal.annotations.VisibleForTesting;
import com.android.settingslib.R;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.settingslib.media.flags.Flags;
@@ -161,6 +163,12 @@ public abstract class InfoMediaManager {
            return mConnectionState;
        }

        /** Gets the drawable associated with the suggested device type. */
        @NonNull
        public Drawable getIcon(Context context) {
            return getDrawableForSuggestion(context, this);
        }

        @Override
        public boolean equals(Object obj) {
            if (this == obj) {
@@ -681,6 +689,11 @@ public abstract class InfoMediaManager {
        return getActiveRoutingSession().getName();
    }

    @Nullable
    public SuggestedDeviceState getSuggestedDevice() {
        return mSuggestedDeviceState;
    }

    @TargetApi(Build.VERSION_CODES.R)
    boolean shouldEnableVolumeSeekBar(RoutingSessionInfo sessionInfo) {
        return sessionInfo.isSystemSession() // System sessions are not remote
@@ -717,6 +730,11 @@ public abstract class InfoMediaManager {
                }
            }
            if (newSuggestedDeviceState == null) {
                if (topSuggestion
                        .getRouteId()
                        .equals(previousState.getSuggestedDeviceInfo().getRouteId())) {
                    return;
                }
                newSuggestedDeviceState = new SuggestedDeviceState(topSuggestion);
            }
        }
@@ -862,6 +880,59 @@ public abstract class InfoMediaManager {
    void addMediaDevice(@NonNull MediaRoute2Info route, @NonNull RoutingSessionInfo activeSession) {
        final int deviceType = route.getType();
        MediaDevice mediaDevice = null;
        if (isInfoMediaDevice(deviceType)) {
            mediaDevice =
                    new InfoMediaDevice(mContext, route, mPreferenceItemMap.get(route.getId()));

        } else if (isPhoneMediaDevice(deviceType)) {
            mediaDevice =
                    new PhoneMediaDevice(
                            mContext, route, mPreferenceItemMap.getOrDefault(route.getId(), null));

        } else if (isBluetoothMediaDevice(deviceType)) {
            if (route.getAddress() == null) {
                Log.e(TAG, "Ignoring bluetooth route with no set address: " + route);
            } else {
                final BluetoothDevice device =
                        BluetoothAdapter.getDefaultAdapter().getRemoteDevice(route.getAddress());
                final CachedBluetoothDevice cachedDevice =
                        mBluetoothManager.getCachedDeviceManager().findDevice(device);
                if (cachedDevice != null) {
                    mediaDevice =
                            new BluetoothMediaDevice(
                                    mContext,
                                    cachedDevice,
                                    route,
                                    mPreferenceItemMap.getOrDefault(route.getId(), null));
                }
            }
        } else if (isComplexMediaDevice(deviceType)) {
            mediaDevice =
                    new ComplexMediaDevice(mContext, route, mPreferenceItemMap.get(route.getId()));

        } else {
            Log.w(TAG, "addMediaDevice() unknown device type : " + deviceType);
        }
        if (mediaDevice != null) {
            if (activeSession.getSelectedRoutes().contains(route.getId())) {
                setDeviceState(mediaDevice, STATE_SELECTED);
            }
            mMediaDevices.add(mediaDevice);
        }
    }

    /** Updates the state of the device and updates liteners of the updated device state. */
    public void setDeviceState(MediaDevice device, @LocalMediaManager.MediaDeviceState int state) {
        if (device.getState() == state) {
            return;
        }
        device.setState(state);
        if (device.isSuggestedDevice()) {
            updateDeviceSuggestion();
        }
    }

    private static boolean isInfoMediaDevice(int deviceType) {
        switch (deviceType) {
            case TYPE_UNKNOWN:
            case TYPE_REMOTE_TV:
@@ -874,12 +945,14 @@ public abstract class InfoMediaManager {
            case TYPE_REMOTE_CAR:
            case TYPE_REMOTE_SMARTWATCH:
            case TYPE_REMOTE_SMARTPHONE:
                mediaDevice =
                        new InfoMediaDevice(
                                mContext,
                                route,
                                mPreferenceItemMap.get(route.getId()));
                break;
                return true;
            default:
                return false;
        }
    }

    private static boolean isPhoneMediaDevice(int deviceType) {
        switch (deviceType) {
            case TYPE_BUILTIN_SPEAKER:
            case TYPE_USB_DEVICE:
            case TYPE_USB_HEADSET:
@@ -893,51 +966,45 @@ public abstract class InfoMediaManager {
            case TYPE_AUX_LINE:
            case TYPE_WIRED_HEADSET:
            case TYPE_WIRED_HEADPHONES:
                mediaDevice =
                        new PhoneMediaDevice(
                                mContext,
                                route,
                                mPreferenceItemMap.getOrDefault(route.getId(), null));
                break;
                return true;
            default:
                return false;
        }
    }

    private static boolean isBluetoothMediaDevice(int deviceType) {
        switch (deviceType) {
            case TYPE_HEARING_AID:
            case TYPE_BLUETOOTH_A2DP:
            case TYPE_BLE_HEADSET:
                if (route.getAddress() == null) {
                    Log.e(TAG, "Ignoring bluetooth route with no set address: " + route);
                    break;
                return true;
            default:
                return false;
        }
                final BluetoothDevice device =
                        BluetoothAdapter.getDefaultAdapter()
                                .getRemoteDevice(route.getAddress());
                final CachedBluetoothDevice cachedDevice =
                        mBluetoothManager.getCachedDeviceManager().findDevice(device);
                if (cachedDevice != null) {
                    mediaDevice =
                            new BluetoothMediaDevice(
                                    mContext,
                                    cachedDevice,
                                    route,
                                    mPreferenceItemMap.getOrDefault(route.getId(), null));
    }
                break;
            case TYPE_REMOTE_AUDIO_VIDEO_RECEIVER:
                mediaDevice =
                        new ComplexMediaDevice(
                                mContext,
                                route,
                                mPreferenceItemMap.get(route.getId()));
                break;
            default:
                Log.w(TAG, "addMediaDevice() unknown device type : " + deviceType);
                break;

    private static boolean isComplexMediaDevice(int deviceType) {
        return deviceType == TYPE_REMOTE_AUDIO_VIDEO_RECEIVER;
    }

        if (mediaDevice != null) {
            if (activeSession.getSelectedRoutes().contains(route.getId())) {
                mediaDevice.setState(STATE_SELECTED);
    private static Drawable getDrawableForSuggestion(
            Context context, SuggestedDeviceState suggestion) {
        if (suggestion.getConnectionState()
                == LocalMediaManager.MediaDeviceState.STATE_CONNECTING_FAILED) {
            return context.getDrawable(android.R.drawable.ic_info);
        }
            mMediaDevices.add(mediaDevice);
        int deviceType = suggestion.getSuggestedDeviceInfo().getType();
        if (isInfoMediaDevice(deviceType)) {
            return context.getDrawable(InfoMediaDevice.getDrawableResIdByType(deviceType));
        }
        if (isPhoneMediaDevice(deviceType)) {
            return context.getDrawable(
                    new DeviceIconUtil(context).getIconResIdFromMediaRouteType(deviceType));
        }
        if (isBluetoothMediaDevice(deviceType)) {
            return ComplexMediaDevice.getIcon(context);
        }
        return context.getDrawable(R.drawable.ic_media_speaker_device);
    }

    @RequiresApi(34)
+8 −2
Original line number Diff line number Diff line
@@ -62,6 +62,8 @@ public final class InputRouteManager {

    private final AudioManager mAudioManager;

    private final InfoMediaManager mInfoMediaManager;

    @VisibleForTesting final List<MediaDevice> mInputMediaDevices = new CopyOnWriteArrayList<>();

    private @AudioDeviceType int mSelectedInputDeviceType;
@@ -107,9 +109,13 @@ public final class InputRouteManager {
                }
            };

    public InputRouteManager(@NonNull Context context, @NonNull AudioManager audioManager) {
    public InputRouteManager(
            @NonNull Context context,
            @NonNull AudioManager audioManager,
            @NonNull InfoMediaManager infoMediaManager) {
        mContext = context;
        mAudioManager = audioManager;
        mInfoMediaManager = infoMediaManager;
        Handler handler = new Handler(context.getMainLooper());

        mAudioManager.registerAudioDeviceCallback(mAudioDeviceCallback, handler);
@@ -210,7 +216,7 @@ public final class InputRouteManager {
                            getProductNameFromAudioDeviceInfo(info));
            if (mediaDevice != null) {
                if (info.getType() == mSelectedInputDeviceType) {
                    mediaDevice.setState(STATE_SELECTED);
                    mInfoMediaManager.setDeviceState(mediaDevice, STATE_SELECTED);
                }
                mInputMediaDevices.add(mediaDevice);
            }
+112 −8
Original line number Diff line number Diff line
@@ -27,6 +27,7 @@ import android.media.AudioDeviceAttributes;
import android.media.AudioManager;
import android.media.RoutingSessionInfo;
import android.os.Build;
import android.os.Handler;
import android.text.TextUtils;
import android.util.Log;

@@ -35,6 +36,7 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;

import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.settingslib.bluetooth.A2dpProfile;
import com.android.settingslib.bluetooth.BluetoothCallback;
@@ -44,12 +46,14 @@ import com.android.settingslib.bluetooth.HearingAidProfile;
import com.android.settingslib.bluetooth.LeAudioProfile;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.settingslib.bluetooth.LocalBluetoothProfile;
import com.android.settingslib.media.InfoMediaManager.SuggestedDeviceState;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.CopyOnWriteArrayList;

/**
@@ -98,6 +102,13 @@ public class LocalMediaManager implements BluetoothCallback {
    @VisibleForTesting
    DeviceAttributeChangeCallback mDeviceAttributeChangeCallback =
            new DeviceAttributeChangeCallback();

    @GuardedBy("mMediaDevicesLock")
    @Nullable
    ConnectingSuggestedDeviceState mConnectingSuggestedDeviceState;

    @VisibleForTesting Handler mConnectSuggestedDeviceHandler;

    @VisibleForTesting
    BluetoothAdapter mBluetoothAdapter;

@@ -140,6 +151,7 @@ public class LocalMediaManager implements BluetoothCallback {
                LocalBluetoothManager.getInstance(context, /* onInitCallback= */ null);
        mAudioManager = context.getSystemService(AudioManager.class);
        mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
        mConnectSuggestedDeviceHandler = new Handler(context.getMainLooper());
        if (mLocalBluetoothManager == null) {
            Log.e(TAG, "Bluetooth is not supported on this device");
            return;
@@ -169,6 +181,7 @@ public class LocalMediaManager implements BluetoothCallback {
        mPackageName = packageName;
        mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
        mAudioManager = context.getSystemService(AudioManager.class);
        mConnectSuggestedDeviceHandler = new Handler(context.getMainLooper());
    }

    /**
@@ -187,7 +200,7 @@ public class LocalMediaManager implements BluetoothCallback {
                    ((BluetoothMediaDevice) device).getCachedDevice();
            if (!cachedDevice.isConnected() && !cachedDevice.isBusy()) {
                mOnTransferBluetoothDevice = connectDevice;
                device.setState(MediaDeviceState.STATE_CONNECTING);
                mInfoMediaManager.setDeviceState(device, MediaDeviceState.STATE_CONNECTING);
                cachedDevice.connect();
                return true;
            }
@@ -198,11 +211,53 @@ public class LocalMediaManager implements BluetoothCallback {
            return false;
        }

        device.setState(MediaDeviceState.STATE_CONNECTING);
        mInfoMediaManager.setDeviceState(device, MediaDeviceState.STATE_CONNECTING);
        mInfoMediaManager.connectToDevice(device);
        return true;
    }

    /**
     * Connects to a suggested device. If the device is not already scanned, a scan will be started
     * to attempt to discover the device.
     *
     * @param suggestion the suggested device to connect to.
     */
    public void connectSuggestedDevice(SuggestedDeviceState suggestion) {
        synchronized (mMediaDevicesLock) {
            if (suggestion == null || mConnectingSuggestedDeviceState != null) {
                return;
            }
            SuggestedDeviceState currentSuggestion = mInfoMediaManager.getSuggestedDevice();
            if (!Objects.equals(suggestion, currentSuggestion)) {
                return;
            }
            for (MediaDevice device : mMediaDevices) {
                if (suggestion.getSuggestedDeviceInfo().getRouteId().equals(device.getId())) {
                    connectDevice(device);
                    return;
                }
            }
            mConnectingSuggestedDeviceState =
                    new ConnectingSuggestedDeviceState(
                            currentSuggestion, mConnectSuggestedDeviceHandler);
            mConnectingSuggestedDeviceState.tryConnect();
        }
    }

    private boolean connectToDeviceIfConnectionPending(MediaDevice device) {
        synchronized (mMediaDevicesLock) {
            if (mConnectingSuggestedDeviceState != null
                    && mConnectingSuggestedDeviceState
                            .mSuggestedDeviceState
                            .getSuggestedDeviceInfo()
                            .getRouteId()
                            .equals(device.getId())) {
                return connectDevice(device);
            }
            return false;
        }
    }

    void dispatchSelectedDeviceStateChanged(MediaDevice device, @MediaDeviceState int state) {
        for (DeviceCallback callback : getCallbacks()) {
            callback.onSelectedDeviceStateChanged(device, state);
@@ -327,7 +382,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);
        mInfoMediaManager.setDeviceState(device, MediaDeviceState.STATE_GROUPING);
        return mInfoMediaManager.addDeviceToPlayMedia(device);
    }

@@ -338,7 +393,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);
        mInfoMediaManager.setDeviceState(device, MediaDeviceState.STATE_GROUPING);
        return mInfoMediaManager.removeDeviceFromPlayMedia(device);
    }

@@ -556,7 +611,8 @@ public class LocalMediaManager implements BluetoothCallback {
            dispatchDeviceListUpdate();
            if (mOnTransferBluetoothDevice != null && mOnTransferBluetoothDevice.isConnected()) {
                connectDevice(mOnTransferBluetoothDevice);
                mOnTransferBluetoothDevice.setState(MediaDeviceState.STATE_CONNECTED);
                mInfoMediaManager.setDeviceState(
                        mOnTransferBluetoothDevice, MediaDeviceState.STATE_CONNECTED);
                dispatchSelectedDeviceStateChanged(mOnTransferBluetoothDevice,
                        MediaDeviceState.STATE_CONNECTED);
                mOnTransferBluetoothDevice = null;
@@ -671,7 +727,7 @@ public class LocalMediaManager implements BluetoothCallback {

            mCurrentConnectedDevice = connectDevice;
            if (connectDevice != null) {
                connectDevice.setState(MediaDeviceState.STATE_CONNECTED);
                mInfoMediaManager.setDeviceState(connectDevice, MediaDeviceState.STATE_CONNECTED);

                dispatchSelectedDeviceStateChanged(mCurrentConnectedDevice,
                        MediaDeviceState.STATE_CONNECTED);
@@ -683,7 +739,8 @@ public class LocalMediaManager implements BluetoothCallback {
            synchronized (mMediaDevicesLock) {
                for (MediaDevice device : mMediaDevices) {
                    if (device.getState() == MediaDeviceState.STATE_CONNECTING) {
                        device.setState(MediaDeviceState.STATE_CONNECTING_FAILED);
                        mInfoMediaManager.setDeviceState(
                                device, MediaDeviceState.STATE_CONNECTING_FAILED);
                    }
                }
            }
@@ -782,11 +839,58 @@ public class LocalMediaManager implements BluetoothCallback {
                    .isBusy()
                    && !mOnTransferBluetoothDevice.isConnected()) {
                // Failed to connect
                mOnTransferBluetoothDevice.setState(MediaDeviceState.STATE_CONNECTING_FAILED);
                mInfoMediaManager.setDeviceState(
                        mOnTransferBluetoothDevice, MediaDeviceState.STATE_CONNECTING_FAILED);
                mOnTransferBluetoothDevice = null;
                dispatchOnRequestFailed(REASON_UNKNOWN_ERROR);
            }
            dispatchDeviceAttributesChanged();
        }
    }

    private class ConnectingSuggestedDeviceState {
        private static final int SCAN_DURATION_MS = 10000;

        @NonNull final SuggestedDeviceState mSuggestedDeviceState;
        @NonNull final Handler mConnectSuggestedDeviceHandler;
        @NonNull final DeviceCallback mDeviceCallback;
        @NonNull final Runnable mConnectionAttemptFinishedRunnable;

        ConnectingSuggestedDeviceState(SuggestedDeviceState suggestedDeviceState, Handler handler) {
            mSuggestedDeviceState = suggestedDeviceState;
            mConnectSuggestedDeviceHandler = handler;
            mDeviceCallback =
                    new DeviceCallback() {
                        @Override
                        public void onDeviceListUpdate(List<MediaDevice> mediaDevices) {
                            for (MediaDevice mediaDevice : mediaDevices) {
                                if (connectToDeviceIfConnectionPending(mediaDevice)) {
                                    mConnectSuggestedDeviceHandler.removeCallbacks(
                                            mConnectionAttemptFinishedRunnable);
                                    mConnectionAttemptFinishedRunnable.run();
                                    break;
                                }
                            }
                        }
                    };
            mConnectionAttemptFinishedRunnable =
                    new Runnable() {
                        @Override
                        public void run() {
                            synchronized (mMediaDevicesLock) {
                                mConnectingSuggestedDeviceState = null;
                            }
                            unregisterCallback(mDeviceCallback);
                            stopScan();
                        }
                    };
        }

        void tryConnect() {
            registerCallback(mDeviceCallback);
            startScan();
            mConnectSuggestedDeviceHandler.postDelayed(
                    mConnectionAttemptFinishedRunnable, SCAN_DURATION_MS);
        }
    }
}
Loading