1 /* 2 * Copyright (C) 2022 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.settingslib.media; 18 19 import static android.media.AudioDeviceInfo.AudioDeviceType; 20 import static android.media.AudioDeviceInfo.TYPE_BUILTIN_SPEAKER; 21 import static android.media.AudioDeviceInfo.TYPE_DOCK; 22 import static android.media.AudioDeviceInfo.TYPE_HDMI; 23 import static android.media.AudioDeviceInfo.TYPE_HDMI_ARC; 24 import static android.media.AudioDeviceInfo.TYPE_HDMI_EARC; 25 import static android.media.AudioDeviceInfo.TYPE_USB_ACCESSORY; 26 import static android.media.AudioDeviceInfo.TYPE_USB_DEVICE; 27 import static android.media.AudioDeviceInfo.TYPE_USB_HEADSET; 28 import static android.media.AudioDeviceInfo.TYPE_WIRED_HEADPHONES; 29 import static android.media.AudioDeviceInfo.TYPE_WIRED_HEADSET; 30 31 import android.annotation.DrawableRes; 32 import android.annotation.SuppressLint; 33 import android.content.Context; 34 import android.content.pm.PackageManager; 35 import android.graphics.drawable.Drawable; 36 import android.media.AudioDeviceInfo; 37 import android.media.AudioManager; 38 import android.media.MediaRoute2Info; 39 import android.os.SystemProperties; 40 import android.util.SparseIntArray; 41 42 import androidx.annotation.NonNull; 43 44 import com.android.internal.annotations.VisibleForTesting; 45 import com.android.settingslib.R; 46 import com.android.settingslib.media.flags.Flags; 47 48 import java.util.Arrays; 49 import java.util.Objects; 50 51 /** A util class to get the appropriate icon for different device types. */ 52 public class DeviceIconUtil { 53 54 private static final SparseIntArray AUDIO_DEVICE_TO_MEDIA_ROUTE_TYPE = new SparseIntArray(); 55 56 private final boolean mIsTv; 57 private final boolean mIsTablet; 58 private final Context mContext; DeviceIconUtil(@onNull Context context)59 public DeviceIconUtil(@NonNull Context context) { 60 mContext = Objects.requireNonNull(context); 61 mIsTv = 62 mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK) 63 && Flags.enableTvMediaOutputDialog(); 64 mIsTablet = 65 Arrays.asList(SystemProperties.get("ro.build.characteristics").split(",")) 66 .contains("tablet"); 67 } 68 69 @VisibleForTesting DeviceIconUtil(boolean isTv)70 /* package */ DeviceIconUtil(boolean isTv) { 71 mContext = null; 72 mIsTv = isTv; 73 mIsTablet = false; 74 } 75 76 /** Returns a drawable for an icon representing the given audioDeviceType. */ getIconFromAudioDeviceType(@udioDeviceType int audioDeviceType)77 public Drawable getIconFromAudioDeviceType(@AudioDeviceType int audioDeviceType) { 78 return mContext.getDrawable(getIconResIdFromAudioDeviceType(audioDeviceType)); 79 } 80 81 /** Returns a drawable res ID for an icon representing the given audioDeviceType. */ 82 @DrawableRes getIconResIdFromAudioDeviceType(@udioDeviceType int audioDeviceType)83 public int getIconResIdFromAudioDeviceType(@AudioDeviceType int audioDeviceType) { 84 int mediaRouteType = 85 AUDIO_DEVICE_TO_MEDIA_ROUTE_TYPE.get(audioDeviceType, /* defaultValue */ -1); 86 return getIconResIdFromMediaRouteType(mediaRouteType); 87 } 88 89 /** Returns a drawable res ID for an icon representing the given mediaRouteType. */ 90 @DrawableRes getIconResIdFromMediaRouteType(@ediaRoute2Info.Type int type)91 public int getIconResIdFromMediaRouteType(@MediaRoute2Info.Type int type) { 92 return mIsTv 93 ? getIconResourceIdForTv(type) 94 : getIconResourceIdForPhoneOrTablet(type, mIsTablet); 95 } 96 97 @SuppressLint("SwitchIntDef") 98 @DrawableRes getIconResourceIdForPhoneOrTablet( @ediaRoute2Info.Type int type, boolean isTablet)99 private static int getIconResourceIdForPhoneOrTablet( 100 @MediaRoute2Info.Type int type, boolean isTablet) { 101 int defaultResId = isTablet ? R.drawable.ic_media_tablet : R.drawable.ic_smartphone; 102 103 return switch (type) { 104 case MediaRoute2Info.TYPE_USB_DEVICE, 105 MediaRoute2Info.TYPE_USB_HEADSET, 106 MediaRoute2Info.TYPE_USB_ACCESSORY, 107 MediaRoute2Info.TYPE_WIRED_HEADSET, 108 MediaRoute2Info.TYPE_WIRED_HEADPHONES -> 109 R.drawable.ic_headphone; 110 case MediaRoute2Info.TYPE_DOCK -> R.drawable.ic_dock_device; 111 case MediaRoute2Info.TYPE_HDMI, 112 MediaRoute2Info.TYPE_HDMI_ARC, 113 MediaRoute2Info.TYPE_HDMI_EARC -> 114 R.drawable.ic_external_display; 115 default -> defaultResId; // Includes TYPE_BUILTIN_SPEAKER. 116 }; 117 } 118 119 @SuppressLint("SwitchIntDef") 120 @DrawableRes getIconResourceIdForTv(@ediaRoute2Info.Type int type)121 private int getIconResourceIdForTv(@MediaRoute2Info.Type int type) { 122 return switch (type) { 123 case MediaRoute2Info.TYPE_USB_DEVICE, MediaRoute2Info.TYPE_USB_HEADSET -> 124 R.drawable.ic_headphone; 125 case MediaRoute2Info.TYPE_USB_ACCESSORY -> R.drawable.ic_usb; 126 case MediaRoute2Info.TYPE_DOCK -> R.drawable.ic_dock_device; 127 case MediaRoute2Info.TYPE_BUILTIN_SPEAKER -> 128 isPanelTv() ? R.drawable.ic_tv : R.drawable.ic_tv_box_internal_speaker; 129 case MediaRoute2Info.TYPE_HDMI -> R.drawable.ic_tv; 130 case MediaRoute2Info.TYPE_HDMI_ARC, MediaRoute2Info.TYPE_HDMI_EARC -> 131 R.drawable.ic_hdmi; 132 case MediaRoute2Info.TYPE_WIRED_HEADSET, MediaRoute2Info.TYPE_WIRED_HEADPHONES -> 133 R.drawable.ic_wired_device; 134 default -> R.drawable.ic_media_speaker_device; 135 }; 136 } 137 138 private boolean isPanelTv() { 139 if (mContext == null) { 140 // This should only happen during testing. 141 return true; 142 } 143 AudioManager audioManager = mContext.getSystemService(AudioManager.class); 144 AudioDeviceInfo[] devices = audioManager.getDevices( 145 AudioManager.GET_DEVICES_OUTPUTS); 146 // If we have an HDMI output (not ARC/eARC) we can assume it's a dongle / set top box. 147 for (AudioDeviceInfo device : devices) { 148 if (device.getType() == TYPE_HDMI) { 149 return false; 150 } 151 } 152 return true; 153 } 154 155 static { 156 AUDIO_DEVICE_TO_MEDIA_ROUTE_TYPE.put(TYPE_USB_DEVICE, MediaRoute2Info.TYPE_USB_DEVICE); 157 AUDIO_DEVICE_TO_MEDIA_ROUTE_TYPE.put(TYPE_USB_HEADSET, MediaRoute2Info.TYPE_USB_HEADSET); 158 AUDIO_DEVICE_TO_MEDIA_ROUTE_TYPE.put( 159 TYPE_USB_ACCESSORY, MediaRoute2Info.TYPE_USB_ACCESSORY); 160 AUDIO_DEVICE_TO_MEDIA_ROUTE_TYPE.put(TYPE_DOCK, MediaRoute2Info.TYPE_DOCK); 161 AUDIO_DEVICE_TO_MEDIA_ROUTE_TYPE.put(TYPE_HDMI, MediaRoute2Info.TYPE_HDMI); 162 AUDIO_DEVICE_TO_MEDIA_ROUTE_TYPE.put(TYPE_HDMI_ARC, MediaRoute2Info.TYPE_HDMI_ARC); 163 AUDIO_DEVICE_TO_MEDIA_ROUTE_TYPE.put(TYPE_HDMI_EARC, MediaRoute2Info.TYPE_HDMI_EARC); 164 AUDIO_DEVICE_TO_MEDIA_ROUTE_TYPE.put( 165 TYPE_WIRED_HEADSET, MediaRoute2Info.TYPE_WIRED_HEADSET); 166 AUDIO_DEVICE_TO_MEDIA_ROUTE_TYPE.put( 167 TYPE_WIRED_HEADPHONES, MediaRoute2Info.TYPE_WIRED_HEADPHONES); 168 AUDIO_DEVICE_TO_MEDIA_ROUTE_TYPE.put( 169 TYPE_BUILTIN_SPEAKER, MediaRoute2Info.TYPE_BUILTIN_SPEAKER); 170 } 171 } 172