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 50 GalleryMediaChooser(final MediaPicker mediaPicker) { 51 super(mediaPicker); 52 mAdapter = new GalleryGridAdapter(Factory.get().getApplicationContext(), null); 53 } 54 55 @Override 56 public int getSupportedMediaTypes() { 57 return MediaPicker.MEDIA_TYPE_IMAGE | MediaPicker.MEDIA_TYPE_VIDEO; 58 } 59 60 @Override 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 72 public int getIconResource() { 73 return R.drawable.ic_image_light; 74 } 75 76 @Override 77 public int getIconDescriptionResource() { 78 return R.string.mediapicker_galleryChooserDescription; 79 } 80 81 @Override 82 public boolean canSwipeDown() { 83 return mGalleryGridView.canSwipeDown(); 84 } 85 86 @Override 87 public void onItemSelected(final MessagePartData item) { 88 mMediaPicker.dispatchItemsSelected(item, !mGalleryGridView.isMultiSelectEnabled()); 89 } 90 91 @Override 92 public void onItemUnselected(final MessagePartData item) { 93 mMediaPicker.dispatchItemUnselected(item); 94 } 95 96 @Override 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 104 public void onUpdate() { 105 mMediaPicker.invalidateOptionsMenu(); 106 } 107 108 @Override 109 public void onCreateOptionsMenu(final MenuInflater inflater, final Menu menu) { 110 if (mView != null) { 111 mGalleryGridView.onCreateOptionsMenu(inflater, menu); 112 } 113 } 114 115 @Override 116 public boolean onOptionsItemSelected(final MenuItem item) { 117 return (mView != null) ? mGalleryGridView.onOptionsItemSelected(item) : false; 118 } 119 120 @Override 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 143 int getActionBarTitleResId() { 144 return R.string.mediapicker_gallery_title; 145 } 146 147 @Override 148 public void onDocumentPickerItemClicked() { 149 mMediaPicker.launchDocumentPicker(); 150 } 151 152 @Override 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 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 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 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 204 private void startMediaPickerDataLoader() { 205 mBindingRef.getData().startLoader(MediaPickerData.GALLERY_IMAGE_LOADER, mBindingRef, null, 206 this); 207 } 208 209 @Override 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 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