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

Commit 1400c7a3 authored by Caitlin Cassidy's avatar Caitlin Cassidy
Browse files

[Media SASS] Choose the correct device icon based on the device

attributes.

Bug: 206614671
Test: verify `adb shell cmd statusbar media-mute-await 9
MuteAwaitDevice start` shows the headphone icon
Test: verify `adb shell cmd statusbar media-mute-await 2
MuteAwaitDevice start` shows the smartphone icon
Test: atest DeviceIconUtilTest, PhoneMediaDeviceTest
Test: atest MediaMuteAwaitConnectionManagerTest

Change-Id: I7122828bd65a730b94d20d553509886fd166dcb2
parent bc8c6790
Loading
Loading
Loading
Loading
+126 −0
Original line number Diff line number Diff line
/*
 * 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.settingslib.media;

import android.annotation.DrawableRes;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.media.AudioDeviceInfo;
import android.media.MediaRoute2Info;

import com.android.settingslib.R;

import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/** A util class to get the appropriate icon for different device types. */
public class DeviceIconUtil {
    // A map from a @AudioDeviceInfo.AudioDeviceType to full device information.
    private final Map<Integer, Device> mAudioDeviceTypeToIconMap = new HashMap<>();
    // A map from a @MediaRoute2Info.Type to full device information.
    private final Map<Integer, Device> mMediaRouteTypeToIconMap = new HashMap<>();
    // A default icon to use if the type is not present in the map.
    @DrawableRes private static final int DEFAULT_ICON = R.drawable.ic_smartphone;

    public DeviceIconUtil() {
        List<Device> deviceList = Arrays.asList(
                new Device(
                    AudioDeviceInfo.TYPE_USB_DEVICE,
                    MediaRoute2Info.TYPE_USB_DEVICE,
                    R.drawable.ic_headphone),
                new Device(
                    AudioDeviceInfo.TYPE_USB_HEADSET,
                    MediaRoute2Info.TYPE_USB_HEADSET,
                    R.drawable.ic_headphone),
                new Device(
                    AudioDeviceInfo.TYPE_USB_ACCESSORY,
                    MediaRoute2Info.TYPE_USB_ACCESSORY,
                    R.drawable.ic_headphone),
                new Device(
                    AudioDeviceInfo.TYPE_DOCK,
                    MediaRoute2Info.TYPE_DOCK,
                    R.drawable.ic_headphone),
                new Device(
                    AudioDeviceInfo.TYPE_HDMI,
                    MediaRoute2Info.TYPE_HDMI,
                    R.drawable.ic_headphone),
                new Device(
                    AudioDeviceInfo.TYPE_WIRED_HEADSET,
                    MediaRoute2Info.TYPE_WIRED_HEADSET,
                    R.drawable.ic_headphone),
                new Device(
                    AudioDeviceInfo.TYPE_WIRED_HEADPHONES,
                    MediaRoute2Info.TYPE_WIRED_HEADPHONES,
                    R.drawable.ic_headphone),
                new Device(
                    AudioDeviceInfo.TYPE_BUILTIN_SPEAKER,
                    MediaRoute2Info.TYPE_BUILTIN_SPEAKER,
                    R.drawable.ic_smartphone));
        for (int i = 0; i < deviceList.size(); i++) {
            Device device = deviceList.get(i);
            mAudioDeviceTypeToIconMap.put(device.mAudioDeviceType, device);
            mMediaRouteTypeToIconMap.put(device.mMediaRouteType, device);
        }
    }

    /** Returns a drawable for an icon representing the given audioDeviceType. */
    public Drawable getIconFromAudioDeviceType(
            @AudioDeviceInfo.AudioDeviceType int audioDeviceType, Context context) {
        return context.getDrawable(getIconResIdFromAudioDeviceType(audioDeviceType));
    }

    /** Returns a drawable res ID for an icon representing the given audioDeviceType. */
    @DrawableRes
    public int getIconResIdFromAudioDeviceType(
            @AudioDeviceInfo.AudioDeviceType int audioDeviceType) {
        if (mAudioDeviceTypeToIconMap.containsKey(audioDeviceType)) {
            return mAudioDeviceTypeToIconMap.get(audioDeviceType).mIconDrawableRes;
        }
        return DEFAULT_ICON;
    }

    /** Returns a drawable res ID for an icon representing the given mediaRouteType. */
    @DrawableRes
    public int getIconResIdFromMediaRouteType(
            @MediaRoute2Info.Type int mediaRouteType) {
        if (mMediaRouteTypeToIconMap.containsKey(mediaRouteType)) {
            return mMediaRouteTypeToIconMap.get(mediaRouteType).mIconDrawableRes;
        }
        return DEFAULT_ICON;
    }

    private static class Device {
        @AudioDeviceInfo.AudioDeviceType
        private final int mAudioDeviceType;

        @MediaRoute2Info.Type
        private final int mMediaRouteType;

        @DrawableRes
        private final int mIconDrawableRes;

        Device(@AudioDeviceInfo.AudioDeviceType int audioDeviceType,
                @MediaRoute2Info.Type int mediaRouteType,
                @DrawableRes int iconDrawableRes) {
            mAudioDeviceType = audioDeviceType;
            mMediaRouteType = mediaRouteType;
            mIconDrawableRes = iconDrawableRes;
        }
    }
}
+4 −18
Original line number Diff line number Diff line
@@ -47,10 +47,12 @@ public class PhoneMediaDevice extends MediaDevice {

    private String mSummary = "";

    private final DeviceIconUtil mDeviceIconUtil;

    PhoneMediaDevice(Context context, MediaRouter2Manager routerManager, MediaRoute2Info info,
            String packageName) {
        super(context, routerManager, info, packageName);

        mDeviceIconUtil = new DeviceIconUtil();
        initDeviceRecord();
    }

@@ -94,23 +96,7 @@ public class PhoneMediaDevice extends MediaDevice {

    @VisibleForTesting
    int getDrawableResId() {
        int resId;
        switch (mRouteInfo.getType()) {
            case TYPE_USB_DEVICE:
            case TYPE_USB_HEADSET:
            case TYPE_USB_ACCESSORY:
            case TYPE_DOCK:
            case TYPE_HDMI:
            case TYPE_WIRED_HEADSET:
            case TYPE_WIRED_HEADPHONES:
                resId = R.drawable.ic_headphone;
                break;
            case TYPE_BUILTIN_SPEAKER:
            default:
                resId = R.drawable.ic_smartphone;
                break;
        }
        return resId;
        return mDeviceIconUtil.getIconResIdFromMediaRouteType(mRouteInfo.getType());
    }

    @Override
+150 −0
Original line number Diff line number Diff line
/*
 * Copyright 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.settingslib.media;

import static com.google.common.truth.Truth.assertThat;

import android.media.AudioDeviceInfo;
import android.media.MediaRoute2Info;

import com.android.settingslib.R;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;

@RunWith(RobolectricTestRunner.class)
public class DeviceIconUtilTest {
    private final DeviceIconUtil mDeviceIconUtil = new DeviceIconUtil();

    @Test
    public void getIconResIdFromMediaRouteType_usbDevice_isHeadphone() {
        assertThat(mDeviceIconUtil.getIconResIdFromMediaRouteType(MediaRoute2Info.TYPE_USB_DEVICE))
            .isEqualTo(R.drawable.ic_headphone);
    }

    @Test
    public void getIconResIdFromMediaRouteType_usbHeadset_isHeadphone() {
        assertThat(mDeviceIconUtil.getIconResIdFromMediaRouteType(MediaRoute2Info.TYPE_USB_HEADSET))
            .isEqualTo(R.drawable.ic_headphone);
    }

    @Test
    public void getIconResIdFromMediaRouteType_usbAccessory_isHeadphone() {
        assertThat(
            mDeviceIconUtil.getIconResIdFromMediaRouteType(MediaRoute2Info.TYPE_USB_ACCESSORY))
            .isEqualTo(R.drawable.ic_headphone);
    }

    @Test
    public void getIconResIdFromMediaRouteType_dock_isHeadphone() {
        assertThat(mDeviceIconUtil.getIconResIdFromMediaRouteType(MediaRoute2Info.TYPE_DOCK))
            .isEqualTo(R.drawable.ic_headphone);
    }

    @Test
    public void getIconResIdFromMediaRouteType_hdmi_isHeadphone() {
        assertThat(mDeviceIconUtil.getIconResIdFromMediaRouteType(MediaRoute2Info.TYPE_HDMI))
            .isEqualTo(R.drawable.ic_headphone);
    }

    @Test
    public void getIconResIdFromMediaRouteType_wiredHeadset_isHeadphone() {
        assertThat(
            mDeviceIconUtil.getIconResIdFromMediaRouteType(MediaRoute2Info.TYPE_WIRED_HEADSET))
            .isEqualTo(R.drawable.ic_headphone);
    }

    @Test
    public void getIconResIdFromMediaRouteType_wiredHeadphones_isHeadphone() {
        assertThat(
            mDeviceIconUtil.getIconResIdFromMediaRouteType(MediaRoute2Info.TYPE_WIRED_HEADPHONES))
            .isEqualTo(R.drawable.ic_headphone);
    }

    @Test
    public void getIconResIdFromMediaRouteType_builtinSpeaker_isSmartphone() {
        assertThat(
            mDeviceIconUtil.getIconResIdFromMediaRouteType(MediaRoute2Info.TYPE_BUILTIN_SPEAKER))
            .isEqualTo(R.drawable.ic_smartphone);
    }

    @Test
    public void getIconResIdFromMediaRouteType_unsupportedType_isSmartphone() {
        assertThat(mDeviceIconUtil.getIconResIdFromMediaRouteType(MediaRoute2Info.TYPE_UNKNOWN))
            .isEqualTo(R.drawable.ic_smartphone);
    }

    @Test
    public void getIconResIdFromAudioDeviceType_usbDevice_isHeadphone() {
        assertThat(mDeviceIconUtil.getIconResIdFromAudioDeviceType(AudioDeviceInfo.TYPE_USB_DEVICE))
            .isEqualTo(R.drawable.ic_headphone);
    }

    @Test
    public void getIconResIdFromAudioDeviceType_usbHeadset_isHeadphone() {
        assertThat(
            mDeviceIconUtil.getIconResIdFromAudioDeviceType(AudioDeviceInfo.TYPE_USB_HEADSET))
            .isEqualTo(R.drawable.ic_headphone);
    }

    @Test
    public void getIconResIdFromAudioDeviceType_usbAccessory_isHeadphone() {
        assertThat(
            mDeviceIconUtil.getIconResIdFromAudioDeviceType(AudioDeviceInfo.TYPE_USB_ACCESSORY))
            .isEqualTo(R.drawable.ic_headphone);
    }

    @Test
    public void getIconResIdFromAudioDeviceType_dock_isHeadphone() {
        assertThat(mDeviceIconUtil.getIconResIdFromAudioDeviceType(AudioDeviceInfo.TYPE_DOCK))
            .isEqualTo(R.drawable.ic_headphone);
    }

    @Test
    public void getIconResIdFromAudioDeviceType_hdmi_isHeadphone() {
        assertThat(mDeviceIconUtil.getIconResIdFromAudioDeviceType(AudioDeviceInfo.TYPE_HDMI))
            .isEqualTo(R.drawable.ic_headphone);
    }

    @Test
    public void getIconResIdFromAudioDeviceType_wiredHeadset_isHeadphone() {
        assertThat(
            mDeviceIconUtil.getIconResIdFromAudioDeviceType(AudioDeviceInfo.TYPE_WIRED_HEADSET))
            .isEqualTo(R.drawable.ic_headphone);
    }

    @Test
    public void getIconResIdFromAudioDeviceType_wiredHeadphones_isHeadphone() {
        assertThat(
            mDeviceIconUtil.getIconResIdFromAudioDeviceType(AudioDeviceInfo.TYPE_WIRED_HEADPHONES))
            .isEqualTo(R.drawable.ic_headphone);
    }

    @Test
    public void getIconResIdFromAudioDeviceType_builtinSpeaker_isSmartphone() {
        assertThat(
            mDeviceIconUtil.getIconResIdFromAudioDeviceType(AudioDeviceInfo.TYPE_BUILTIN_SPEAKER))
            .isEqualTo(R.drawable.ic_smartphone);
    }

    @Test
    public void getIconResIdFromAudioDeviceType_unsupportedType_isSmartphone() {
        assertThat(mDeviceIconUtil.getIconResIdFromAudioDeviceType(AudioDeviceInfo.TYPE_UNKNOWN))
            .isEqualTo(R.drawable.ic_smartphone);
    }
}
+5 −6
Original line number Diff line number Diff line
@@ -19,7 +19,6 @@ package com.android.systemui.media.muteawait
import android.content.Context
import android.media.AudioAttributes.USAGE_MEDIA
import android.media.AudioDeviceAttributes
import android.media.AudioDeviceInfo
import android.media.AudioManager
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.statusbar.commandline.Command
@@ -42,13 +41,13 @@ class MediaMuteAwaitConnectionCli @Inject constructor(
        override fun execute(pw: PrintWriter, args: List<String>) {
            val device = AudioDeviceAttributes(
                AudioDeviceAttributes.ROLE_OUTPUT,
                AudioDeviceInfo.TYPE_USB_HEADSET,
                /* type= */ Integer.parseInt(args[0]),
                ADDRESS,
                /* name= */ args[0],
                /* name= */ args[1],
                listOf(),
                listOf(),
            )
            val startOrCancel = args[1]
            val startOrCancel = args[2]

            val audioManager: AudioManager =
                context.getSystemService(Context.AUDIO_SERVICE) as AudioManager
@@ -58,12 +57,12 @@ class MediaMuteAwaitConnectionCli @Inject constructor(
                            intArrayOf(USAGE_MEDIA), device, TIMEOUT, TIMEOUT_UNITS
                    )
                CANCEL -> audioManager.cancelMuteAwaitConnection(device)
                else -> pw.println("Must specify $START or $CANCEL")
                else -> pw.println("Must specify `$START` or `$CANCEL`; was $startOrCancel")
            }
        }
        override fun help(pw: PrintWriter) {
            pw.println("Usage: adb shell cmd statusbar $MEDIA_MUTE_AWAIT_COMMAND " +
                    "[name] [$START|$CANCEL]")
                    "[type] [name] [$START|$CANCEL]")
        }
    }
}
+9 −7
Original line number Diff line number Diff line
@@ -21,8 +21,8 @@ import android.graphics.drawable.Drawable
import android.media.AudioAttributes.USAGE_MEDIA
import android.media.AudioDeviceAttributes
import android.media.AudioManager
import com.android.settingslib.media.DeviceIconUtil
import com.android.settingslib.media.LocalMediaManager
import com.android.systemui.R
import com.android.systemui.dagger.qualifiers.Main
import java.util.concurrent.Executor

@@ -39,7 +39,8 @@ import java.util.concurrent.Executor
class MediaMuteAwaitConnectionManager constructor(
    @Main mainExecutor: Executor,
    localMediaManager: LocalMediaManager,
    private val context: Context
    private val context: Context,
    private val deviceIconUtil: DeviceIconUtil
) {
    var currentMutedDevice: AudioDeviceAttributes? = null

@@ -51,7 +52,7 @@ class MediaMuteAwaitConnectionManager constructor(
                // There should only be one device that's mutedUntilConnection at a time, so we can
                // safely override any previous value.
                currentMutedDevice = device
                localMediaManager.dispatchAboutToConnectDeviceChanged(device.name, getIcon())
                localMediaManager.dispatchAboutToConnectDeviceChanged(device.name, device.getIcon())
            }
        }

@@ -75,12 +76,13 @@ class MediaMuteAwaitConnectionManager constructor(
        val currentDevice = audioManager.mutingExpectedDevice
        if (currentDevice != null) {
            currentMutedDevice = currentDevice
            localMediaManager.dispatchAboutToConnectDeviceChanged(currentDevice.name, getIcon())
            localMediaManager.dispatchAboutToConnectDeviceChanged(
                currentDevice.name, currentDevice.getIcon()
            )
        }
    }

    private fun getIcon(): Drawable {
        // TODO(b/206614671): Choose the icon based on device type.
        return context.getDrawable(R.drawable.ic_headphone)!!
    private fun AudioDeviceAttributes.getIcon(): Drawable {
        return deviceIconUtil.getIconFromAudioDeviceType(this.type, context)
    }
}
Loading