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 the READ_EXTERNAL_STORAGE permission.
70             listener.onAssetRetrieved(null);
71         }
72 
73         new AsyncTask<Void, Void, Asset>() {
74             @Override
75             protected Asset doInBackground(Void... params) {
76                 String[] projection = new String[]{
77                         MediaStore.Images.ImageColumns._ID,
78                         MediaStore.Images.ImageColumns.DATE_TAKEN,
79                 };
80                 String sortOrder = MediaStore.Images.ImageColumns.DATE_TAKEN + " DESC LIMIT 1";
81                 Cursor cursor = context.getContentResolver().query(
82                         MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
83                         projection,
84                         null /* selection */,
85                         null /* selectionArgs */,
86                         sortOrder);
87 
88                 Asset asset = null;
89                 if (cursor != null) {
90                     if (cursor.moveToNext()) {
91                         asset = new ContentUriAsset(context, Uri.parse(
92                                 MediaStore.Images.Media.EXTERNAL_CONTENT_URI + "/" + cursor.getString(0)));
93                     }
94                     cursor.close();
95                 }
96 
97                 return asset;
98             }
99 
100             @Override
101             protected void onPostExecute(Asset thumbnail) {
102                 listener.onAssetRetrieved(thumbnail);
103             }
104         }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
105     }
106 
107     /**
108      * Returns whether READ_EXTERNAL_STORAGE has been granted for the application.
109      */
isReadExternalStoragePermissionGranted(Context context)110     private static boolean isReadExternalStoragePermissionGranted(Context context) {
111         return context.getPackageManager().checkPermission(permission.READ_EXTERNAL_STORAGE,
112                 context.getPackageName()) == PackageManager.PERMISSION_GRANTED;
113     }
114 
115     @Override
onClick(View view)116     public void onClick(View view) {
117         mMyPhotosStarter.requestCustomPhotoPicker(this);
118     }
119 
120     /**
121      * Draws the overlay icon or last-taken photo as thumbnail for the ViewHolder depending on whether
122      * storage permission has been granted to the app.
123      */
bind()124   /* package */ void bind() {
125         if (isReadExternalStoragePermissionGranted(mActivity)) {
126             mOverlayIconView.setVisibility(View.GONE);
127             drawThumbnail();
128         } else {
129             mOverlayIconView.setVisibility(View.VISIBLE);
130             mOverlayIconView.setImageDrawable(mActivity.getDrawable(
131                     R.drawable.myphotos_empty_tile_illustration));
132         }
133     }
134 
135     @Override
onPermissionsGranted()136     public void onPermissionsGranted() {
137         bind();
138     }
139 
140     @Override
onPermissionsDenied(boolean dontAskAgain)141     public void onPermissionsDenied(boolean dontAskAgain) {
142         // No-op
143     }
144 
drawThumbnail()145     private void drawThumbnail() {
146         fetchThumbnail(mActivity, new AssetListener() {
147             @Override
148             public void onAssetRetrieved(@Nullable Asset thumbnail) {
149                 if (thumbnail == null) {
150                     return;
151                 }
152 
153                 thumbnail.loadDrawable(mActivity, mThumbnailView,
154                         ResourceUtils.getColorAttr(mActivity, android.R.attr.colorSecondary));
155             }
156         });
157     }
158 
159     private interface AssetListener {
160         /**
161          * Called when the requested Asset is retrieved.
162          */
onAssetRetrieved(@ullable Asset asset)163         void onAssetRetrieved(@Nullable Asset asset);
164     }
165 }
166