1 /*
2  * Copyright (C) 2015 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.messaging.ui.mediapicker;
18 
19 import android.Manifest;
20 import android.app.Activity;
21 import android.content.Intent;
22 import android.content.pm.PackageManager;
23 import android.database.Cursor;
24 import android.database.MatrixCursor;
25 import android.database.MergeCursor;
26 import androidx.appcompat.app.ActionBar;
27 import android.view.LayoutInflater;
28 import android.view.Menu;
29 import android.view.MenuInflater;
30 import android.view.MenuItem;
31 import android.view.View;
32 import android.view.ViewGroup;
33 
34 import com.android.messaging.Factory;
35 import com.android.messaging.R;
36 import com.android.messaging.datamodel.data.GalleryGridItemData;
37 import com.android.messaging.datamodel.data.MediaPickerData;
38 import com.android.messaging.datamodel.data.MessagePartData;
39 import com.android.messaging.datamodel.data.MediaPickerData.MediaPickerDataListener;
40 import com.android.messaging.datamodel.data.PendingAttachmentData;
41 import com.android.messaging.ui.UIIntents;
42 import com.android.messaging.ui.mediapicker.DocumentImagePicker.SelectionListener;
43 import com.android.messaging.util.Assert;
44 import com.android.messaging.util.OsUtil;
45 
46 /**
47  * Chooser which allows the user to select one or more existing images or videos or audios.
48  */
49 class GalleryMediaChooser extends MediaChooser implements
50         GalleryGridView.GalleryGridViewListener, MediaPickerDataListener {
51     private final GalleryGridAdapter mAdapter;
52     private GalleryGridView mGalleryGridView;
53     private View mMissingPermissionView;
54 
55     /** Handles picking a media from the document picker. */
56     private DocumentImagePicker mDocumentImagePicker;
57 
GalleryMediaChooser(final MediaPicker mediaPicker)58     GalleryMediaChooser(final MediaPicker mediaPicker) {
59         super(mediaPicker);
60         mAdapter = new GalleryGridAdapter(Factory.get().getApplicationContext(), null);
61         mDocumentImagePicker = new DocumentImagePicker(mMediaPicker,
62                 new SelectionListener() {
63                     @Override
64                     public void onDocumentSelected(final PendingAttachmentData data) {
65                         if (mBindingRef.isBound()) {
66                             mMediaPicker.dispatchPendingItemAdded(data);
67                         }
68                     }
69                 });
70     }
71 
72     @Override
getSupportedMediaTypes()73     public int getSupportedMediaTypes() {
74         return (MediaPicker.MEDIA_TYPE_IMAGE
75                 | MediaPicker.MEDIA_TYPE_VIDEO
76                 | MediaPicker.MEDIA_TYPE_AUDIO);
77     }
78 
79     @Override
destroyView()80     public View destroyView() {
81         mGalleryGridView.setAdapter(null);
82         mAdapter.setHostInterface(null);
83         // The loader is started only if startMediaPickerDataLoader() is called
84         if (OsUtil.hasStoragePermission()) {
85             mBindingRef.getData().destroyLoader(MediaPickerData.GALLERY_MEDIA_LOADER);
86         }
87         return super.destroyView();
88     }
89 
90     @Override
getIconResource()91     public int getIconResource() {
92         return R.drawable.ic_image_light;
93     }
94 
95     @Override
getIconDescriptionResource()96     public int getIconDescriptionResource() {
97         return R.string.mediapicker_galleryChooserDescription;
98     }
99 
100     @Override
canSwipeDown()101     public boolean canSwipeDown() {
102         return mGalleryGridView.canSwipeDown();
103     }
104 
105     @Override
onItemSelected(final MessagePartData item)106     public void onItemSelected(final MessagePartData item) {
107         mMediaPicker.dispatchItemsSelected(item, !mGalleryGridView.isMultiSelectEnabled());
108     }
109 
110     @Override
onItemUnselected(final MessagePartData item)111     public void onItemUnselected(final MessagePartData item) {
112         mMediaPicker.dispatchItemUnselected(item);
113     }
114 
115     @Override
onConfirmSelection()116     public void onConfirmSelection() {
117         // The user may only confirm if multiselect is enabled.
118         Assert.isTrue(mGalleryGridView.isMultiSelectEnabled());
119         mMediaPicker.dispatchConfirmItemSelection();
120     }
121 
122     @Override
onUpdate()123     public void onUpdate() {
124         mMediaPicker.invalidateOptionsMenu();
125     }
126 
127     @Override
onCreateOptionsMenu(final MenuInflater inflater, final Menu menu)128     public void onCreateOptionsMenu(final MenuInflater inflater, final Menu menu) {
129         if (mView != null) {
130             mGalleryGridView.onCreateOptionsMenu(inflater, menu);
131         }
132     }
133 
134     @Override
onOptionsItemSelected(final MenuItem item)135     public boolean onOptionsItemSelected(final MenuItem item) {
136         return (mView != null) ? mGalleryGridView.onOptionsItemSelected(item) : false;
137     }
138 
139     @Override
createView(final ViewGroup container)140     protected View createView(final ViewGroup container) {
141         final LayoutInflater inflater = getLayoutInflater();
142         final View view = inflater.inflate(
143                 R.layout.mediapicker_gallery_chooser,
144                 container /* root */,
145                 false /* attachToRoot */);
146 
147         mGalleryGridView = (GalleryGridView) view.findViewById(R.id.gallery_grid_view);
148         mAdapter.setHostInterface(mGalleryGridView);
149         mGalleryGridView.setAdapter(mAdapter);
150         mGalleryGridView.setHostInterface(this);
151         mGalleryGridView.setDraftMessageDataModel(mMediaPicker.getDraftMessageDataModel());
152         if (OsUtil.hasStoragePermission()) {
153             startMediaPickerDataLoader();
154         }
155 
156         mMissingPermissionView = view.findViewById(R.id.missing_permission_view);
157         updateForPermissionState(OsUtil.hasStoragePermission());
158         return view;
159     }
160 
161     @Override
getActionBarTitleResId()162     int getActionBarTitleResId() {
163         return R.string.mediapicker_gallery_title;
164     }
165 
166     @Override
onDocumentPickerItemClicked()167     public void onDocumentPickerItemClicked() {
168         // Launch an external picker to pick item from document picker as attachment.
169         mDocumentImagePicker.launchPicker();
170     }
171 
172     @Override
updateActionBar(final ActionBar actionBar)173     void updateActionBar(final ActionBar actionBar) {
174         super.updateActionBar(actionBar);
175         if (mGalleryGridView == null) {
176             return;
177         }
178         final int selectionCount = mGalleryGridView.getSelectionCount();
179         if (selectionCount > 0 && mGalleryGridView.isMultiSelectEnabled()) {
180             actionBar.setTitle(getContext().getResources().getString(
181                     R.string.mediapicker_gallery_title_selection,
182                     selectionCount));
183         }
184     }
185 
186     @Override
onMediaPickerDataUpdated(final MediaPickerData mediaPickerData, final Object data, final int loaderId)187     public void onMediaPickerDataUpdated(final MediaPickerData mediaPickerData, final Object data,
188             final int loaderId) {
189         mBindingRef.ensureBound(mediaPickerData);
190         Assert.equals(MediaPickerData.GALLERY_MEDIA_LOADER, loaderId);
191         Cursor rawCursor = null;
192         if (data instanceof Cursor) {
193             rawCursor = (Cursor) data;
194         }
195         // Before delivering the cursor, wrap around the local gallery cursor
196         // with an extra item for document picker integration in the front.
197         final MatrixCursor specialItemsCursor =
198                 new MatrixCursor(GalleryGridItemData.SPECIAL_ITEM_COLUMNS);
199         specialItemsCursor.addRow(new Object[] { GalleryGridItemData.ID_DOCUMENT_PICKER_ITEM });
200         final MergeCursor cursor =
201                 new MergeCursor(new Cursor[] { specialItemsCursor, rawCursor });
202         mAdapter.swapCursor(cursor);
203     }
204 
205     @Override
onResume()206     public void onResume() {
207         if (OsUtil.hasStoragePermission()) {
208             // Work around a bug in MediaStore where cursors querying the Files provider don't get
209             // updated for changes to Images.Media or Video.Media.
210             startMediaPickerDataLoader();
211         }
212     }
213 
214     @Override
setSelected(final boolean selected)215     protected void setSelected(final boolean selected) {
216         super.setSelected(selected);
217         if (selected && !OsUtil.hasStoragePermission()) {
218             mMediaPicker.requestPermissions(
219                     new String[] { Manifest.permission.READ_EXTERNAL_STORAGE },
220                     MediaPicker.GALLERY_PERMISSION_REQUEST_CODE);
221         }
222     }
223 
startMediaPickerDataLoader()224     private void startMediaPickerDataLoader() {
225         mBindingRef
226                 .getData()
227                 .startLoader(MediaPickerData.GALLERY_MEDIA_LOADER, mBindingRef, null, this);
228     }
229 
230     @Override
onRequestPermissionsResult( final int requestCode, final String permissions[], final int[] grantResults)231     protected void onRequestPermissionsResult(
232             final int requestCode, final String permissions[], final int[] grantResults) {
233         if (requestCode == MediaPicker.GALLERY_PERMISSION_REQUEST_CODE) {
234             final boolean permissionGranted = grantResults[0] == PackageManager.PERMISSION_GRANTED;
235             if (permissionGranted) {
236                 startMediaPickerDataLoader();
237             }
238             updateForPermissionState(permissionGranted);
239         }
240     }
241 
updateForPermissionState(final boolean granted)242     private void updateForPermissionState(final boolean granted) {
243         // onRequestPermissionsResult can sometimes get called before createView().
244         if (mGalleryGridView == null) {
245             return;
246         }
247 
248         mGalleryGridView.setVisibility(granted ? View.VISIBLE : View.GONE);
249         mMissingPermissionView.setVisibility(granted ? View.GONE : View.VISIBLE);
250     }
251 
252     @Override
onActivityResult( final int requestCode, final int resultCode, final Intent data)253     protected void onActivityResult(
254             final int requestCode, final int resultCode, final Intent data) {
255         if (requestCode == UIIntents.REQUEST_PICK_MEDIA_FROM_DOCUMENT_PICKER
256                 && resultCode == Activity.RESULT_OK) {
257             mDocumentImagePicker.onActivityResult(requestCode, resultCode, data);
258         }
259     }
260 }
261