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