1 /* 2 * Copyright (C) 2018 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.bluetooth.avrcp; 18 19 import android.content.Context; 20 import android.content.pm.PackageManager; 21 import android.media.MediaDescription; 22 import android.media.MediaMetadata; 23 import android.media.browse.MediaBrowser.MediaItem; 24 import android.media.session.MediaSession; 25 import android.os.Bundle; 26 import android.util.Log; 27 28 import java.util.ArrayList; 29 import java.util.List; 30 31 class Util { 32 public static String TAG = "AvrcpUtil"; 33 public static boolean DEBUG = false; 34 35 private static final String GPM_KEY = "com.google.android.music.mediasession.music_metadata"; 36 37 // TODO (apanicke): Remove this prefix later, for now it makes debugging easier. 38 public static final String NOW_PLAYING_PREFIX = "NowPlayingId"; 39 empty_data()40 public static final Metadata empty_data() { 41 Metadata ret = new Metadata(); 42 ret.mediaId = "Not Provided"; 43 ret.title = "Not Provided"; 44 ret.artist = ""; 45 ret.album = ""; 46 ret.genre = ""; 47 ret.trackNum = "1"; 48 ret.numTracks = "1"; 49 ret.duration = "0"; 50 return ret; 51 } 52 bundleToMetadata(Bundle bundle)53 public static Metadata bundleToMetadata(Bundle bundle) { 54 if (bundle == null) return empty_data(); 55 56 Metadata temp = new Metadata(); 57 temp.title = bundle.getString(MediaMetadata.METADATA_KEY_TITLE, "Not Provided"); 58 temp.artist = bundle.getString(MediaMetadata.METADATA_KEY_ARTIST, ""); 59 temp.album = bundle.getString(MediaMetadata.METADATA_KEY_ALBUM, ""); 60 temp.trackNum = "" + bundle.getLong(MediaMetadata.METADATA_KEY_TRACK_NUMBER, 1); 61 temp.numTracks = "" + bundle.getLong(MediaMetadata.METADATA_KEY_NUM_TRACKS, 1); 62 temp.genre = bundle.getString(MediaMetadata.METADATA_KEY_GENRE, ""); 63 temp.duration = "" + bundle.getLong(MediaMetadata.METADATA_KEY_DURATION, 0); 64 return temp; 65 } 66 descriptionToBundle(MediaDescription desc)67 public static Bundle descriptionToBundle(MediaDescription desc) { 68 Bundle ret = new Bundle(); 69 if (desc == null) return ret; 70 71 if (desc.getTitle() != null) { 72 ret.putString(MediaMetadata.METADATA_KEY_TITLE, desc.getTitle().toString()); 73 } 74 75 if (desc.getSubtitle() != null) { 76 ret.putString(MediaMetadata.METADATA_KEY_ARTIST, desc.getSubtitle().toString()); 77 } 78 79 if (desc.getDescription() != null) { 80 ret.putString(MediaMetadata.METADATA_KEY_ALBUM, desc.getDescription().toString()); 81 } 82 83 // If the bundle has title or artist use those over the description title or subtitle. 84 if (desc.getExtras() != null) ret.putAll(desc.getExtras()); 85 86 if (ret.containsKey(GPM_KEY)) { 87 if (DEBUG) Log.d(TAG, "MediaDescription contains GPM data"); 88 ret.putAll(mediaMetadataToBundle((MediaMetadata) ret.get(GPM_KEY))); 89 } 90 91 return ret; 92 } 93 mediaMetadataToBundle(MediaMetadata data)94 public static Bundle mediaMetadataToBundle(MediaMetadata data) { 95 Bundle bundle = new Bundle(); 96 if (data == null) return bundle; 97 98 if (data.containsKey(MediaMetadata.METADATA_KEY_TITLE)) { 99 bundle.putString(MediaMetadata.METADATA_KEY_TITLE, 100 data.getString(MediaMetadata.METADATA_KEY_TITLE)); 101 } 102 103 if (data.containsKey(MediaMetadata.METADATA_KEY_ARTIST)) { 104 bundle.putString(MediaMetadata.METADATA_KEY_ARTIST, 105 data.getString(MediaMetadata.METADATA_KEY_ARTIST)); 106 } 107 108 if (data.containsKey(MediaMetadata.METADATA_KEY_ALBUM)) { 109 bundle.putString(MediaMetadata.METADATA_KEY_ALBUM, 110 data.getString(MediaMetadata.METADATA_KEY_ALBUM)); 111 } 112 113 if (data.containsKey(MediaMetadata.METADATA_KEY_TRACK_NUMBER)) { 114 bundle.putLong(MediaMetadata.METADATA_KEY_TRACK_NUMBER, 115 data.getLong(MediaMetadata.METADATA_KEY_TRACK_NUMBER)); 116 } 117 118 if (data.containsKey(MediaMetadata.METADATA_KEY_NUM_TRACKS)) { 119 bundle.putLong(MediaMetadata.METADATA_KEY_NUM_TRACKS, 120 data.getLong(MediaMetadata.METADATA_KEY_NUM_TRACKS)); 121 } 122 123 if (data.containsKey(MediaMetadata.METADATA_KEY_GENRE)) { 124 bundle.putString(MediaMetadata.METADATA_KEY_GENRE, 125 data.getString(MediaMetadata.METADATA_KEY_GENRE)); 126 } 127 128 if (data.containsKey(MediaMetadata.METADATA_KEY_DURATION)) { 129 bundle.putLong(MediaMetadata.METADATA_KEY_DURATION, 130 data.getLong(MediaMetadata.METADATA_KEY_DURATION)); 131 } 132 133 return bundle; 134 } 135 toMetadata(MediaSession.QueueItem item)136 public static Metadata toMetadata(MediaSession.QueueItem item) { 137 if (item == null) { 138 return empty_data(); 139 } 140 141 Bundle bundle = descriptionToBundle(item.getDescription()); 142 143 if (DEBUG) { 144 for (String key : bundle.keySet()) { 145 Log.d(TAG, "toMetadata: QueueItem: ContainsKey: " + key); 146 } 147 } 148 149 Metadata ret = bundleToMetadata(bundle); 150 151 // For Queue Items, the Media Id will always be just its Queue ID 152 // We don't need to use its actual ID since we don't promise UIDS being valid 153 // between a file system and it's now playing list. 154 ret.mediaId = NOW_PLAYING_PREFIX + item.getQueueId(); 155 156 return ret; 157 } 158 toMetadata(MediaMetadata data)159 public static Metadata toMetadata(MediaMetadata data) { 160 if (data == null) { 161 return empty_data(); 162 } 163 164 MediaDescription desc = data.getDescription(); 165 166 Bundle dataBundle = mediaMetadataToBundle(data); 167 Bundle bundle = descriptionToBundle(data.getDescription()); 168 169 // Prioritize the media metadata over the media description 170 bundle.putAll(dataBundle); 171 172 if (DEBUG) { 173 for (String key : bundle.keySet()) { 174 Log.d(TAG, "toMetadata: MediaMetadata: ContainsKey: " + key); 175 } 176 } 177 178 Metadata ret = bundleToMetadata(bundle); 179 180 // This will always be currsong. The AVRCP service will overwrite the mediaId if it needs to 181 // TODO (apanicke): Remove when the service is ready, right now it makes debugging much more 182 // convenient 183 ret.mediaId = "currsong"; 184 185 return ret; 186 } 187 toMetadata(MediaItem item)188 public static Metadata toMetadata(MediaItem item) { 189 if (item == null) { 190 return empty_data(); 191 } 192 193 Bundle bundle = descriptionToBundle(item.getDescription()); 194 195 if (DEBUG) { 196 for (String key : bundle.keySet()) { 197 Log.d(TAG, "toMetadata: MediaItem: ContainsKey: " + key); 198 } 199 } 200 201 Metadata ret = bundleToMetadata(bundle); 202 ret.mediaId = item.getMediaId(); 203 204 return ret; 205 } 206 toMetadataList(List<MediaSession.QueueItem> items)207 public static List<Metadata> toMetadataList(List<MediaSession.QueueItem> items) { 208 ArrayList<Metadata> list = new ArrayList<Metadata>(); 209 210 if (items == null) return list; 211 212 for (int i = 0; i < items.size(); i++) { 213 Metadata data = toMetadata(items.get(i)); 214 data.trackNum = "" + (i + 1); 215 data.numTracks = "" + items.size(); 216 list.add(data); 217 } 218 219 return list; 220 } 221 222 // Helper method to close a list of ListItems so that if the callee wants 223 // to mutate the list they can do it without affecting any internally cached info cloneList(List<ListItem> list)224 public static List<ListItem> cloneList(List<ListItem> list) { 225 List<ListItem> clone = new ArrayList<ListItem>(list.size()); 226 for (ListItem item : list) clone.add(item.clone()); 227 return clone; 228 } 229 getDisplayName(Context context, String packageName)230 public static String getDisplayName(Context context, String packageName) { 231 try { 232 PackageManager manager = context.getPackageManager(); 233 return manager.getApplicationLabel(manager.getApplicationInfo(packageName, 0)) 234 .toString(); 235 } catch (Exception e) { 236 Log.w(TAG, "Name Not Found using package name: " + packageName); 237 return packageName; 238 } 239 } 240 } 241