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.audio_util; 18 19 import android.content.Context; 20 import android.media.MediaDescription; 21 import android.media.MediaMetadata; 22 import android.media.browse.MediaBrowser.MediaItem; 23 import android.media.session.MediaSession; 24 import android.os.Bundle; 25 26 import java.util.Objects; 27 28 public class Metadata implements Cloneable { 29 public String mediaId; 30 public String title; 31 public String artist; 32 public String album; 33 public String trackNum; 34 public String numTracks; 35 public String genre; 36 public String duration; 37 public Image image; 38 39 @Override clone()40 public Metadata clone() { 41 Metadata data = new Metadata(); 42 data.mediaId = mediaId; 43 data.title = title; 44 data.artist = artist; 45 data.album = album; 46 data.trackNum = trackNum; 47 data.numTracks = numTracks; 48 data.genre = genre; 49 data.duration = duration; 50 data.image = image; 51 return data; 52 } 53 54 @Override equals(Object o)55 public boolean equals(Object o) { 56 if (o == null) return false; 57 if (!(o instanceof Metadata)) return false; 58 59 final Metadata m = (Metadata) o; 60 if (!Objects.equals(title, m.title)) return false; 61 if (!Objects.equals(artist, m.artist)) return false; 62 if (!Objects.equals(album, m.album)) return false; 63 if (!Objects.equals(trackNum, m.trackNum)) return false; 64 if (!Objects.equals(numTracks, m.numTracks)) return false; 65 if (!Objects.equals(image, m.image)) return false; 66 return true; 67 } 68 69 @Override toString()70 public String toString() { 71 return "{ mediaId=\"" + mediaId + "\" title=\"" + title + "\" artist=\"" + artist 72 + "\" album=\"" + album + "\" duration=" + duration 73 + " trackPosition=" + trackNum + "/" + numTracks + " image=" + image + " }"; 74 } 75 76 /** 77 * A Builder object to populate a Metadata from various different Media Framework objects 78 */ 79 public static class Builder { 80 private Metadata mMetadata = new Metadata(); 81 private Context mContext = null; 82 83 /** 84 * Set the Media ID fot the Metadata Object 85 */ setMediaId(String id)86 public Builder setMediaId(String id) { 87 mMetadata.mediaId = id; 88 return this; 89 } 90 91 /** 92 * Set the context this builder should use when resolving images 93 */ useContext(Context context)94 public Builder useContext(Context context) { 95 mContext = context; 96 return this; 97 } 98 99 /** 100 * Extract the fields from a MediaMetadata object into a Metadata, if they exist 101 */ fromMediaMetadata(MediaMetadata data)102 public Builder fromMediaMetadata(MediaMetadata data) { 103 if (data == null) return this; 104 105 // First, use the basic description available with the MediaMetadata 106 fromMediaDescription(data.getDescription()); 107 108 // Then, replace with better data if available on the MediaMetadata 109 if (data.containsKey(MediaMetadata.METADATA_KEY_MEDIA_ID)) { 110 mMetadata.mediaId = data.getString(MediaMetadata.METADATA_KEY_MEDIA_ID); 111 } 112 if (data.containsKey(MediaMetadata.METADATA_KEY_TITLE)) { 113 mMetadata.title = data.getString(MediaMetadata.METADATA_KEY_TITLE); 114 } 115 if (data.containsKey(MediaMetadata.METADATA_KEY_ARTIST)) { 116 mMetadata.artist = data.getString(MediaMetadata.METADATA_KEY_ARTIST); 117 } 118 if (data.containsKey(MediaMetadata.METADATA_KEY_ALBUM)) { 119 mMetadata.album = data.getString(MediaMetadata.METADATA_KEY_ALBUM); 120 } 121 if (data.containsKey(MediaMetadata.METADATA_KEY_TRACK_NUMBER)) { 122 mMetadata.trackNum = "" + data.getLong(MediaMetadata.METADATA_KEY_TRACK_NUMBER); 123 } 124 if (data.containsKey(MediaMetadata.METADATA_KEY_NUM_TRACKS)) { 125 mMetadata.numTracks = "" + data.getLong(MediaMetadata.METADATA_KEY_NUM_TRACKS); 126 } 127 if (data.containsKey(MediaMetadata.METADATA_KEY_GENRE)) { 128 mMetadata.genre = data.getString(MediaMetadata.METADATA_KEY_GENRE); 129 } 130 if (data.containsKey(MediaMetadata.METADATA_KEY_DURATION)) { 131 mMetadata.duration = "" + data.getLong(MediaMetadata.METADATA_KEY_DURATION); 132 } 133 if ((mContext != null && Util.areUriImagesSupported(mContext) 134 && (data.containsKey(MediaMetadata.METADATA_KEY_ART_URI) 135 || data.containsKey(MediaMetadata.METADATA_KEY_ALBUM_ART_URI) 136 || data.containsKey(MediaMetadata.METADATA_KEY_DISPLAY_ICON_URI))) 137 || data.containsKey(MediaMetadata.METADATA_KEY_ART) 138 || data.containsKey(MediaMetadata.METADATA_KEY_ALBUM_ART) 139 || data.containsKey(MediaMetadata.METADATA_KEY_DISPLAY_ICON)) { 140 mMetadata.image = new Image(mContext, data); 141 } 142 return this; 143 } 144 145 /** 146 * Extract the fields from a MediaItem object into a Metadata, if they exist 147 */ fromMediaItem(MediaItem item)148 public Builder fromMediaItem(MediaItem item) { 149 if (item == null) return this; 150 return fromMediaDescription(item.getDescription()).setMediaId(item.getMediaId()); 151 } 152 153 /** 154 * Extract the fields from a MediaDescription object into a Metadata, if they exist 155 */ fromMediaDescription(MediaDescription desc)156 public Builder fromMediaDescription(MediaDescription desc) { 157 if (desc == null) return this; 158 159 // Default the following mapping if they exist 160 if (desc.getTitle() != null) mMetadata.title = desc.getTitle().toString(); 161 if (desc.getSubtitle() != null) mMetadata.artist = desc.getSubtitle().toString(); 162 if (desc.getDescription() != null) mMetadata.album = desc.getDescription().toString(); 163 164 // Check for artwork 165 if (desc.getIconBitmap() != null) { 166 mMetadata.image = new Image(mContext, desc.getIconBitmap()); 167 } else if (mContext != null && Util.areUriImagesSupported(mContext) 168 && desc.getIconUri() != null) { 169 mMetadata.image = new Image(mContext, desc.getIconUri()); 170 } 171 172 // Then, check the extras in the description for even better data 173 return fromBundle(desc.getExtras()).setMediaId(desc.getMediaId()); 174 } 175 176 /** 177 * Extract the fields from a MediaSession.QueueItem object into a Metadata, if they exist 178 */ fromQueueItem(MediaSession.QueueItem item)179 public Builder fromQueueItem(MediaSession.QueueItem item) { 180 if (item == null) return this; 181 return fromMediaDescription(item.getDescription()); 182 } 183 184 /** 185 * Extract the fields from a Bundle of MediaMetadata constants into a Metadata, if they 186 * exist 187 */ fromBundle(Bundle bundle)188 public Builder fromBundle(Bundle bundle) { 189 if (bundle == null) return this; 190 if (bundle.containsKey(MediaMetadata.METADATA_KEY_MEDIA_ID)) { 191 mMetadata.mediaId = bundle.getString(MediaMetadata.METADATA_KEY_MEDIA_ID); 192 } 193 if (bundle.containsKey(MediaMetadata.METADATA_KEY_TITLE)) { 194 mMetadata.title = bundle.getString(MediaMetadata.METADATA_KEY_TITLE); 195 } 196 if (bundle.containsKey(MediaMetadata.METADATA_KEY_ARTIST)) { 197 mMetadata.artist = bundle.getString(MediaMetadata.METADATA_KEY_ARTIST); 198 } 199 if (bundle.containsKey(MediaMetadata.METADATA_KEY_ALBUM)) { 200 mMetadata.album = bundle.getString(MediaMetadata.METADATA_KEY_ALBUM); 201 } 202 if (bundle.containsKey(MediaMetadata.METADATA_KEY_TRACK_NUMBER)) { 203 mMetadata.trackNum = "" + bundle.getLong(MediaMetadata.METADATA_KEY_TRACK_NUMBER); 204 } 205 if (bundle.containsKey(MediaMetadata.METADATA_KEY_NUM_TRACKS)) { 206 mMetadata.numTracks = "" + bundle.getLong(MediaMetadata.METADATA_KEY_NUM_TRACKS); 207 } 208 if (bundle.containsKey(MediaMetadata.METADATA_KEY_GENRE)) { 209 mMetadata.genre = bundle.getString(MediaMetadata.METADATA_KEY_GENRE); 210 } 211 if (bundle.containsKey(MediaMetadata.METADATA_KEY_DURATION)) { 212 mMetadata.duration = "" + bundle.getLong(MediaMetadata.METADATA_KEY_DURATION); 213 } 214 if ((mContext != null && Util.areUriImagesSupported(mContext) 215 && (bundle.containsKey(MediaMetadata.METADATA_KEY_ART_URI) 216 || bundle.containsKey(MediaMetadata.METADATA_KEY_ALBUM_ART_URI) 217 || bundle.containsKey(MediaMetadata.METADATA_KEY_DISPLAY_ICON_URI))) 218 || bundle.containsKey(MediaMetadata.METADATA_KEY_ART) 219 || bundle.containsKey(MediaMetadata.METADATA_KEY_ALBUM_ART) 220 || bundle.containsKey(MediaMetadata.METADATA_KEY_DISPLAY_ICON)) { 221 mMetadata.image = new Image(mContext, bundle); 222 } 223 return this; 224 } 225 226 /** 227 * Elect to use default values in the Metadata in place of any missing values 228 */ useDefaults()229 public Builder useDefaults() { 230 if (mMetadata.mediaId == null) mMetadata.mediaId = "Not Provided"; 231 if (mMetadata.title == null) mMetadata.title = "Not Provided"; 232 if (mMetadata.artist == null) mMetadata.artist = ""; 233 if (mMetadata.album == null) mMetadata.album = ""; 234 if (mMetadata.trackNum == null) mMetadata.trackNum = "1"; 235 if (mMetadata.numTracks == null) mMetadata.numTracks = "1"; 236 if (mMetadata.genre == null) mMetadata.genre = ""; 237 if (mMetadata.duration == null) mMetadata.duration = "0"; 238 // The default value chosen for an image is null. Update here if we pick something else 239 return this; 240 } 241 242 /** 243 * Get the final Metadata objects you're building 244 */ build()245 public Metadata build() { 246 return mMetadata.clone(); 247 } 248 } 249 } 250