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