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