1 /*
2  * Copyright (C) 2017 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.wallpaper.picker.individual;
17 
18 import android.Manifest.permission;
19 import android.app.Activity;
20 import android.content.Context;
21 import android.content.pm.PackageManager;
22 import android.database.Cursor;
23 import android.net.Uri;
24 import android.os.AsyncTask;
25 import android.provider.MediaStore;
26 import android.view.View;
27 import android.widget.ImageView;
28 
29 import androidx.annotation.Nullable;
30 import androidx.recyclerview.widget.RecyclerView.ViewHolder;
31 
32 import com.android.wallpaper.R;
33 import com.android.wallpaper.asset.Asset;
34 import com.android.wallpaper.asset.ContentUriAsset;
35 import com.android.wallpaper.picker.MyPhotosStarter;
36 import com.android.wallpaper.util.ResourceUtils;
37 
38 /**
39  * ViewHolder for a "my photos" tile presented in an individual category grid.
40  */
41 public class MyPhotosViewHolder extends ViewHolder implements View.OnClickListener,
42         MyPhotosStarter.PermissionChangedListener {
43 
44     private final Activity mActivity;
45     private final MyPhotosStarter mMyPhotosStarter;
46     private final ImageView mThumbnailView;
47     private final ImageView mOverlayIconView;
48 
MyPhotosViewHolder(Activity activity, MyPhotosStarter myPhotosStarter, int tileHeightPx, View itemView)49     /* package */ MyPhotosViewHolder(Activity activity, MyPhotosStarter myPhotosStarter,
50             int tileHeightPx, View itemView) {
51         super(itemView);
52 
53         mActivity = activity;
54         mMyPhotosStarter = myPhotosStarter;
55         itemView.getLayoutParams().height = tileHeightPx;
56 
57         itemView.findViewById(R.id.tile).setOnClickListener(this);
58 
59         mThumbnailView = itemView.findViewById(R.id.thumbnail);
60         mOverlayIconView = itemView.findViewById(R.id.overlay_icon);
61     }
62 
63     /**
64      * Fetches a thumbnail asset to represent "my photos" (as the most recently taken photo from the
65      * user's custom photo collection(s)) and calls back to the main thread with the asset.
66      */
fetchThumbnail(final Context context, final AssetListener listener)67     private static void fetchThumbnail(final Context context, final AssetListener listener) {
68         if (!isReadExternalStoragePermissionGranted(context)) {
69             // MediaStore.Images.Media.EXTERNAL_CONTENT_URI requires
70             // the READ_MEDIA_IMAGES permission.
71             listener.onAssetRetrieved(null);
72         }
73 
74         new AsyncTask<Void, Void, Asset>() {
75             @Override
76             protected Asset doInBackground(Void... params) {
77                 String[] projection = new String[]{
78                         MediaStore.Images.ImageColumns._ID,
79                         MediaStore.Images.ImageColumns.DATE_TAKEN,
80                 };
81                 String sortOrder = MediaStore.Images.ImageColumns.DATE_TAKEN + " DESC LIMIT 1";
82                 Cursor cursor = context.getContentResolver().query(
83                         MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
84                         projection,
85                         null /* selection */,
86                         null /* selectionArgs */,
87                         sortOrder);
88 
89                 Asset asset = null;
90                 if (cursor != null) {
91                     if (cursor.moveToNext()) {
92                         asset = new ContentUriAsset(context, Uri.parse(
93                                 MediaStore.Images.Media.EXTERNAL_CONTENT_URI + "/" + cursor.getString(0)));
94                     }
95                     cursor.close();
96                 }
97 
98                 return asset;
99             }
100 
101             @Override
102             protected void onPostExecute(Asset thumbnail) {
103                 listener.onAssetRetrieved(thumbnail);
104             }
105         }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
106     }
107 
108     /**
109      * Returns whether READ_MEDIA_IMAGES has been granted for the application.
110      */
isReadExternalStoragePermissionGranted(Context context)111     private static boolean isReadExternalStoragePermissionGranted(Context context) {
112         return context.getPackageManager().checkPermission(permission.READ_MEDIA_IMAGES,
113                 context.getPackageName()) == PackageManager.PERMISSION_GRANTED;
114     }
115 
116     @Override
onClick(View view)117     public void onClick(View view) {
118         mMyPhotosStarter.requestCustomPhotoPicker(this);
119     }
120 
121     /**
122      * Draws the overlay icon or last-taken photo as thumbnail for the ViewHolder depending on whether
123      * storage permission has been granted to the app.
124      */
bind()125   /* package */ void bind() {
126         if (isReadExternalStoragePermissionGranted(mActivity)) {
127             mOverlayIconView.setVisibility(View.GONE);
128             drawThumbnail();
129         } else {
130             mOverlayIconView.setVisibility(View.VISIBLE);
131             mOverlayIconView.setImageDrawable(mActivity.getDrawable(
132                     R.drawable.myphotos_empty_tile_illustration));
133         }
134     }
135 
136     @Override
onPermissionsGranted()137     public void onPermissionsGranted() {
138         bind();
139     }
140 
141     @Override
onPermissionsDenied(boolean dontAskAgain)142     public void onPermissionsDenied(boolean dontAskAgain) {
143         // No-op
144     }
145 
drawThumbnail()146     private void drawThumbnail() {
147         fetchThumbnail(mActivity, new AssetListener() {
148             @Override
149             public void onAssetRetrieved(@Nullable Asset thumbnail) {
150                 if (thumbnail == null) {
151                     return;
152                 }
153 
154                 thumbnail.loadDrawable(mActivity, mThumbnailView,
155                         ResourceUtils.getColorAttr(mActivity, android.R.attr.colorSecondary));
156             }
157         });
158     }
159 
160     private interface AssetListener {
161         /**
162          * Called when the requested Asset is retrieved.
163          */
onAssetRetrieved(@ullable Asset asset)164         void onAssetRetrieved(@Nullable Asset asset);
165     }
166 }
167