1 /*
2  * Copyright (C) 2020 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 package com.android.bluetooth.audio_util;
17 
18 import android.content.Context;
19 import android.graphics.Bitmap;
20 import android.graphics.BitmapFactory;
21 import android.media.MediaDescription;
22 import android.media.MediaMetadata;
23 import android.net.Uri;
24 import android.os.Bundle;
25 import android.util.Log;
26 
27 import java.io.IOException;
28 import java.io.InputStream;
29 
30 /**
31  * An object to represent an image from the Media Framework
32  *
33  * This object abstracts away the method used to get the bitmap and provides a way for us to
34  * determine image equality in an application/folder/item agnostic way.
35  */
36 public class Image {
37     private static final String TAG = "Image";
38     private static final boolean DEBUG = false;
39 
40     public static int SOURCE_NONE = 0;
41     public static int SOURCE_URI = 1;
42     public static int SOURCE_BITMAP = 2;
43 
44     private final Context mContext;
45 
46     private int mSource = SOURCE_NONE;
47     private Bitmap mImage = null;
48 
49     // For use with other applications so they can conveniently assign the handle their storage
50     // solution has picked for this image and pass this object on directly.
51     private String mImageHandle = null;
52 
53     /**
54      * Create an Image object from a MediaMetadata object
55      */
Image(Context context, MediaMetadata metadata)56     public Image(Context context, MediaMetadata metadata) {
57         mContext = context;
58 
59         String uri_art = metadata.getString(MediaMetadata.METADATA_KEY_ART_URI);
60         String uri_album_art = metadata.getString(MediaMetadata.METADATA_KEY_ALBUM_ART_URI);
61         String uri_icon = metadata.getString(MediaMetadata.METADATA_KEY_DISPLAY_ICON_URI);
62         Bitmap bmp_art = metadata.getBitmap(MediaMetadata.METADATA_KEY_ART);
63         Bitmap bmp_album_art = metadata.getBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART);
64         Bitmap bmp_icon = metadata.getBitmap(MediaMetadata.METADATA_KEY_DISPLAY_ICON);
65 
66         if (bmp_art != null) {
67             setImage(bmp_art);
68         } else if (bmp_album_art != null) {
69             setImage(bmp_album_art);
70         } else if (bmp_icon != null) {
71             setImage(bmp_icon);
72         } else if (uri_art != null) {
73             setImage(uri_art);
74         } else if (uri_album_art != null) {
75             setImage(uri_album_art);
76         } else if (uri_icon != null) {
77             setImage(uri_icon);
78         }
79     }
80 
81     /**
82      * Create an image out of a bundle of MediaMetadata keyed values
83      */
Image(Context context, Bundle bundle)84     public Image(Context context, Bundle bundle) {
85         mContext = context;
86 
87         String uri_art = bundle.getString(MediaMetadata.METADATA_KEY_ART_URI);
88         String uri_album_art = bundle.getString(MediaMetadata.METADATA_KEY_ALBUM_ART_URI);
89         String uri_icon = bundle.getString(MediaMetadata.METADATA_KEY_DISPLAY_ICON_URI);
90         Bitmap bmp_art = bundle.getParcelable(MediaMetadata.METADATA_KEY_ART);
91         Bitmap bmp_album_art = bundle.getParcelable(MediaMetadata.METADATA_KEY_ALBUM_ART);
92         Bitmap bmp_icon = bundle.getParcelable(MediaMetadata.METADATA_KEY_DISPLAY_ICON);
93 
94         if (bmp_art != null) {
95             setImage(bmp_art);
96         } else if (bmp_album_art != null) {
97             setImage(bmp_album_art);
98         } else if (bmp_icon != null) {
99             setImage(bmp_icon);
100         } else if (uri_art != null) {
101             setImage(uri_art);
102         } else if (uri_album_art != null) {
103             setImage(uri_album_art);
104         } else if (uri_icon != null) {
105             setImage(uri_icon);
106         }
107     }
108 
109     /**
110      * Create an Image object from a MediaDescription object
111      */
Image(Context context, MediaDescription desc)112     public Image(Context context, MediaDescription desc) {
113         mContext = context;
114         Uri uri = desc.getIconUri();
115         Bitmap bitmap = desc.getIconBitmap();
116         if (bitmap != null) {
117             setImage(bitmap);
118         } else if (uri != null) {
119             setImage(uri);
120         }
121     }
122 
123     /**
124      * Create an Image object from a raw image uri
125      */
Image(Context context, Uri uri)126     public Image(Context context, Uri uri) {
127         mContext = context;
128         setImage(uri);
129     }
130 
131     /**
132      * Create an Image object from a raw image
133      */
Image(Context context, Bitmap image)134     public Image(Context context, Bitmap image) {
135         mContext = context;
136         setImage(image);
137     }
138 
139     /**
140      * Set the image by resolving a URI to a bitmap
141      */
setImage(String uriString)142     private void setImage(String uriString) {
143         if (uriString == null) return;
144         Uri uri = Uri.parse(uriString);
145         setImage(uri);
146     }
147 
148     /**
149      * Set the image by resolving a URI to a bitmap
150      */
setImage(Uri uri)151     private void setImage(Uri uri) {
152         if (uri == null) return;
153         Bitmap image = getImageFromUri(uri);
154         if (image == null) return;
155         setImage(image);
156         setSource(SOURCE_URI);
157     }
158 
159     /**
160      * Set the image directly from a bitmap
161      */
setImage(Bitmap image)162     private void setImage(Bitmap image) {
163         if (image == null) return;
164         mImage = image;
165         setSource(SOURCE_BITMAP);
166     }
167 
168     /**
169      * Get the bitmap associated with this Image
170      */
getImage()171     public Bitmap getImage() {
172         return mImage;
173     }
174 
175     /**
176      * Get an image Bitmap from a Uri
177      *
178      * Used to convert Uris into the images they represent
179      *
180      * @param uri A Uri pointing to an image
181      * @return A Bitmap object representing the image at the given Uri
182      */
getImageFromUri(Uri uri)183     private Bitmap getImageFromUri(Uri uri) {
184         if (uri == null) return null;
185         Bitmap art = null;
186         InputStream input = null;
187         try {
188             if (mContext == null) return null;
189             input = mContext.getContentResolver().openInputStream(uri);
190             art = BitmapFactory.decodeStream(input);
191         } catch (Exception e) {
192             Log.w("Failed to fetch image at uri=" + uri, e);
193             art = null;
194         }
195         try {
196             if (input != null) {
197                 input.close();
198             }
199         } catch (IOException e) {
200             Log.e(TAG, "Failed to close image file stream, exception=" + e);
201         }
202         return art;
203     }
204 
205     /**
206      * Get the source of the image, if known
207      *
208      * Images currently come from either raw bitmaps or a URI that points to a ContentProvider.
209      * This allows us to set where it came from, largely used for debug purposes.
210      */
getSource()211     public int getSource() {
212         return mSource;
213     }
214 
215     /**
216      * Set the source of the image.
217      *
218      * Images currently come from either raw bitmaps or a URI that points to a ContentProvider.
219      * This allows us to set where it came from, largely used for debug purposes.
220      */
setSource(int source)221     private void setSource(int source) {
222         mSource = source;
223     }
224 
225     /**
226      * Assign a handle value from your storage solution to this image
227      */
setImageHandle(String handle)228     public void setImageHandle(String handle) {
229         mImageHandle = handle;
230     }
231 
232     /**
233      * Get the handle value associated with this image from your storage situation
234      */
getImageHandle()235     public String getImageHandle() {
236         return mImageHandle;
237     }
238 
239     /**
240      * Determine if two image objects are the same.
241      */
242     @Override
equals(Object o)243     public boolean equals(Object o) {
244         if (o == null) return false;
245         if (!(o instanceof Image)) return false;
246         final Image image = (Image) o;
247         final Bitmap bmp = image.getImage();
248         if (bmp == null) return (mImage == null);
249         return bmp.sameAs(mImage);
250     }
251 
252     /**
253      * Get a string representation of the image and its metadata
254      */
255     @Override
toString()256     public String toString() {
257         return "<Image source=" + mSource + ">";
258     }
259 }
260