• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2021 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.providers.media.photopicker.ui;
18 
19 import android.content.Context;
20 import android.graphics.ImageDecoder;
21 import android.graphics.drawable.Drawable;
22 import android.net.Uri;
23 import android.provider.CloudMediaProviderContract;
24 import android.provider.MediaStore;
25 import android.util.Log;
26 import android.widget.ImageView;
27 
28 import androidx.annotation.NonNull;
29 
30 import com.android.providers.media.photopicker.data.model.Category;
31 import com.android.providers.media.photopicker.data.model.Item;
32 
33 import com.bumptech.glide.Glide;
34 import com.bumptech.glide.load.Option;
35 import com.bumptech.glide.request.RequestOptions;
36 import com.bumptech.glide.signature.ObjectKey;
37 
38 /**
39  * A class to assist with loading and managing the Images (i.e. thumbnails and preview) associated
40  * with item.
41  */
42 public class ImageLoader {
43 
44     public static final Option<Boolean> THUMBNAIL_REQUEST =
45             Option.memory(CloudMediaProviderContract.EXTRA_MEDIASTORE_THUMB, false);
46     private static final String TAG = "ImageLoader";
47     private final Context mContext;
48 
ImageLoader(Context context)49     public ImageLoader(Context context) {
50         mContext = context;
51     }
52 
53     /**
54      * Load the thumbnail of the {@code category} and set it on the {@code imageView}
55      *
56      * @param category  the album
57      * @param imageView the imageView shows the thumbnail
58      */
loadAlbumThumbnail(@onNull Category category, @NonNull ImageView imageView)59     public void loadAlbumThumbnail(@NonNull Category category, @NonNull ImageView imageView) {
60         // Always show all thumbnails as bitmap images instead of drawables
61         // This is to ensure that we do not animate any thumbnail (for eg GIF)
62         // TODO(b/194285082): Use drawable instead of bitmap, as it saves memory.
63         Glide.with(mContext)
64                 .asBitmap()
65                 .load(category.getCoverUri())
66                 .apply(RequestOptions.option(THUMBNAIL_REQUEST, true))
67                 .into(imageView);
68     }
69 
70     /**
71      * Load the thumbnail of the photo item {@code item} and set it on the {@code imageView}
72      *
73      * @param item      the photo item
74      * @param imageView the imageView shows the thumbnail
75      */
loadPhotoThumbnail(@onNull Item item, @NonNull ImageView imageView)76     public void loadPhotoThumbnail(@NonNull Item item, @NonNull ImageView imageView) {
77         Uri uri = item.getContentUri();
78         // Always show all thumbnails as bitmap images instead of drawables
79         // This is to ensure that we do not animate any thumbnail (for eg GIF)
80         // TODO(b/194285082): Use drawable instead of bitmap, as it saves memory.
81         Glide.with(mContext)
82                 .asBitmap()
83                 .load(uri)
84                 .signature(getGlideSignature(item, /* prefix */ ""))
85                 .apply(RequestOptions.option(THUMBNAIL_REQUEST, true))
86                 .into(imageView);
87     }
88 
89     /**
90      * Load the image of the photo item {@code item} and set it on the {@code imageView}
91      *
92      * @param item      the photo item
93      * @param imageView the imageView shows the image
94      */
loadImagePreview(@onNull Item item, @NonNull ImageView imageView)95     public void loadImagePreview(@NonNull Item item, @NonNull ImageView imageView)  {
96         if (item.isGif()) {
97             Glide.with(mContext)
98                     .asGif()
99                     .load(item.getContentUri())
100                     .signature(getGlideSignature(item, /* prefix */ ""))
101                     .into(imageView);
102             return;
103         }
104 
105         if (item.isAnimatedWebp()) {
106             loadAnimatedWebpPreview(item, imageView);
107             return;
108         }
109 
110         // Preview as bitmap image for all other image types
111         Glide.with(mContext)
112                 .asBitmap()
113                 .load(item.getContentUri())
114                 .signature(getGlideSignature(item, /* prefix */ ""))
115                 .into(imageView);
116     }
117 
loadAnimatedWebpPreview(@onNull Item item, @NonNull ImageView imageView)118     private void loadAnimatedWebpPreview(@NonNull Item item, @NonNull ImageView imageView) {
119         final Uri uri = item.getContentUri();
120         final ImageDecoder.Source source = ImageDecoder.createSource(mContext.getContentResolver(),
121                 uri);
122         Drawable drawable = null;
123         try {
124             drawable = ImageDecoder.decodeDrawable(source);
125         } catch (Exception e) {
126             Log.d(TAG, "Failed to decode drawable for uri: " + uri, e);
127         }
128 
129         // If we failed to decode drawable for a source using ImageDecoder, then try using uri
130         // directly. Glide will show static image for an animated webp. That is okay as we tried our
131         // best to load animated webp but couldn't, and we anyway show the GIF badge in preview.
132         Glide.with(mContext)
133                 .load(drawable == null ? uri : drawable)
134                 .signature(getGlideSignature(item, /* prefix */ ""))
135                 .into(imageView);
136     }
137 
138     /**
139      * Loads the image from first frame of the given video item
140      */
loadImageFromVideoForPreview(@onNull Item item, @NonNull ImageView imageView)141     public void loadImageFromVideoForPreview(@NonNull Item item, @NonNull ImageView imageView) {
142         Glide.with(mContext)
143                 .asBitmap()
144                 .load(item.getContentUri())
145                 .apply(new RequestOptions().frame(1000))
146                 .signature(getGlideSignature(item, "Preview"))
147                 .into(imageView);
148     }
149 
getGlideSignature(Item item, String prefix)150     private ObjectKey getGlideSignature(Item item, String prefix) {
151         // TODO(b/224725723): Remove media store version from key once MP ids are stable.
152         return new ObjectKey(
153                 MediaStore.getVersion(mContext) + prefix + item.getContentUri().toString() +
154                         item.getGenerationModified());
155     }
156 }
157