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.MediaRoute2Info;
37 import android.os.SystemProperties;
38 import android.util.SparseIntArray;
39 
40 import androidx.annotation.NonNull;
41 
42 import com.android.internal.annotations.VisibleForTesting;
43 import com.android.settingslib.R;
44 import com.android.settingslib.media.flags.Flags;
45 
46 import java.util.Arrays;
47 import java.util.Objects;
48 
49 /** A util class to get the appropriate icon for different device types. */
50 public class DeviceIconUtil {
51 
52     private static final SparseIntArray AUDIO_DEVICE_TO_MEDIA_ROUTE_TYPE = new SparseIntArray();
53 
54     private final boolean mIsTv;
55     private final boolean mIsTablet;
56     private final Context mContext;
DeviceIconUtil(@onNull Context context)57     public DeviceIconUtil(@NonNull Context context) {
58         mContext = Objects.requireNonNull(context);
59         mIsTv =
60                 mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK)
61                         && Flags.enableTvMediaOutputDialog();
62         mIsTablet =
63                 Arrays.asList(SystemProperties.get("ro.build.characteristics").split(","))
64                         .contains("tablet");
65     }
66 
67     @VisibleForTesting
DeviceIconUtil(boolean isTv)68     /* package */ DeviceIconUtil(boolean isTv) {
69         mContext = null;
70         mIsTv = isTv;
71         mIsTablet = false;
72     }
73 
74     /** Returns a drawable for an icon representing the given audioDeviceType. */
getIconFromAudioDeviceType(@udioDeviceType int audioDeviceType)75     public Drawable getIconFromAudioDeviceType(@AudioDeviceType int audioDeviceType) {
76         return mContext.getDrawable(getIconResIdFromAudioDeviceType(audioDeviceType));
77     }
78 
79     /** Returns a drawable res ID for an icon representing the given audioDeviceType. */
80     @DrawableRes
getIconResIdFromAudioDeviceType(@udioDeviceType int audioDeviceType)81     public int getIconResIdFromAudioDeviceType(@AudioDeviceType int audioDeviceType) {
82         int mediaRouteType =
83                 AUDIO_DEVICE_TO_MEDIA_ROUTE_TYPE.get(audioDeviceType, /* defaultValue */ -1);
84         return getIconResIdFromMediaRouteType(mediaRouteType);
85     }
86 
87     /** Returns a drawable res ID for an icon representing the given mediaRouteType. */
88     @DrawableRes
getIconResIdFromMediaRouteType(@ediaRoute2Info.Type int type)89     public int getIconResIdFromMediaRouteType(@MediaRoute2Info.Type int type) {
90         return mIsTv
91                 ? getIconResourceIdForTv(type)
92                 : getIconResourceIdForPhoneOrTablet(type, mIsTablet);
93     }
94 
95     @SuppressLint("SwitchIntDef")
96     @DrawableRes
getIconResourceIdForPhoneOrTablet( @ediaRoute2Info.Type int type, boolean isTablet)97     private static int getIconResourceIdForPhoneOrTablet(
98             @MediaRoute2Info.Type int type, boolean isTablet) {
99         int defaultResId = isTablet ? R.drawable.ic_media_tablet : R.drawable.ic_smartphone;
100 
101         return switch (type) {
102             case MediaRoute2Info.TYPE_USB_DEVICE,
103                             MediaRoute2Info.TYPE_USB_HEADSET,
104                             MediaRoute2Info.TYPE_USB_ACCESSORY,
105                             MediaRoute2Info.TYPE_WIRED_HEADSET,
106                             MediaRoute2Info.TYPE_WIRED_HEADPHONES ->
107                     R.drawable.ic_headphone;
108             case MediaRoute2Info.TYPE_DOCK -> R.drawable.ic_dock_device;
109             case MediaRoute2Info.TYPE_HDMI,
110                             MediaRoute2Info.TYPE_HDMI_ARC,
111                             MediaRoute2Info.TYPE_HDMI_EARC ->
112                     R.drawable.ic_external_display;
113             default -> defaultResId; // Includes TYPE_BUILTIN_SPEAKER.
114         };
115     }
116 
117     @SuppressLint("SwitchIntDef")
118     @DrawableRes
getIconResourceIdForTv(@ediaRoute2Info.Type int type)119     private static int getIconResourceIdForTv(@MediaRoute2Info.Type int type) {
120         return switch (type) {
121             case MediaRoute2Info.TYPE_USB_DEVICE, MediaRoute2Info.TYPE_USB_HEADSET ->
122                     R.drawable.ic_headphone;
123             case MediaRoute2Info.TYPE_USB_ACCESSORY -> R.drawable.ic_usb;
124             case MediaRoute2Info.TYPE_DOCK -> R.drawable.ic_dock_device;
125             case MediaRoute2Info.TYPE_HDMI, MediaRoute2Info.TYPE_BUILTIN_SPEAKER ->
126                     R.drawable.ic_tv;
127             case MediaRoute2Info.TYPE_HDMI_ARC, MediaRoute2Info.TYPE_HDMI_EARC ->
128                     R.drawable.ic_hdmi;
129             case MediaRoute2Info.TYPE_WIRED_HEADSET, MediaRoute2Info.TYPE_WIRED_HEADPHONES ->
130                     R.drawable.ic_wired_device;
131             default -> R.drawable.ic_media_speaker_device;
132         };
133     }
134 
135     static {
136         AUDIO_DEVICE_TO_MEDIA_ROUTE_TYPE.put(TYPE_USB_DEVICE, MediaRoute2Info.TYPE_USB_DEVICE);
137         AUDIO_DEVICE_TO_MEDIA_ROUTE_TYPE.put(TYPE_USB_HEADSET, MediaRoute2Info.TYPE_USB_HEADSET);
138         AUDIO_DEVICE_TO_MEDIA_ROUTE_TYPE.put(
139                 TYPE_USB_ACCESSORY, MediaRoute2Info.TYPE_USB_ACCESSORY);
140         AUDIO_DEVICE_TO_MEDIA_ROUTE_TYPE.put(TYPE_DOCK, MediaRoute2Info.TYPE_DOCK);
141         AUDIO_DEVICE_TO_MEDIA_ROUTE_TYPE.put(TYPE_HDMI, MediaRoute2Info.TYPE_HDMI);
142         AUDIO_DEVICE_TO_MEDIA_ROUTE_TYPE.put(TYPE_HDMI_ARC, MediaRoute2Info.TYPE_HDMI_ARC);
143         AUDIO_DEVICE_TO_MEDIA_ROUTE_TYPE.put(TYPE_HDMI_EARC, MediaRoute2Info.TYPE_HDMI_EARC);
144         AUDIO_DEVICE_TO_MEDIA_ROUTE_TYPE.put(
145                 TYPE_WIRED_HEADSET, MediaRoute2Info.TYPE_WIRED_HEADSET);
146         AUDIO_DEVICE_TO_MEDIA_ROUTE_TYPE.put(
147                 TYPE_WIRED_HEADPHONES, MediaRoute2Info.TYPE_WIRED_HEADPHONES);
148         AUDIO_DEVICE_TO_MEDIA_ROUTE_TYPE.put(
149                 TYPE_BUILTIN_SPEAKER, MediaRoute2Info.TYPE_BUILTIN_SPEAKER);
150     }
151 }
152