Support video attachments through GalleryMediaChooser am: 6c77158718

Change-Id: I152f496a6438e2dc907f5137a8bc8c747996c4df
This commit is contained in:
Automerger Merge Worker
2020-02-19 22:24:48 +00:00
20 changed files with 131 additions and 99 deletions
+10
View File
@@ -43,4 +43,14 @@
android:paddingTop="4dp" android:paddingTop="4dp"
android:visibility="gone" android:visibility="gone"
android:contentDescription="@string/gallery_checkbox_content_description" /> android:contentDescription="@string/gallery_checkbox_content_description" />
<!-- Play button image for video contents only -->
<ImageView
android:id="@+id/video_thumbnail_play_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:background="@color/background_item_transparent"
android:src="@drawable/ic_video_play_light"
android:visibility="gone" />
</com.android.messaging.ui.mediapicker.GalleryGridItemView> </com.android.messaging.ui.mediapicker.GalleryGridItemView>
+1 -1
View File
@@ -53,7 +53,7 @@
<string name="mediapicker_cameraChooserDescription">Capture pictures or video</string> <string name="mediapicker_cameraChooserDescription">Capture pictures or video</string>
<string name="mediapicker_galleryChooserDescription">Choose images from this device</string> <string name="mediapicker_galleryChooserDescription">Choose images from this device</string>
<string name="mediapicker_audioChooserDescription">Record audio</string> <string name="mediapicker_audioChooserDescription">Record audio</string>
<string name="mediapicker_gallery_title">Choose photo</string> <string name="mediapicker_gallery_title">Choose media</string>
<string name="mediapicker_gallery_item_selected_content_description">The media is selected.</string> <string name="mediapicker_gallery_item_selected_content_description">The media is selected.</string>
<string name="mediapicker_gallery_item_unselected_content_description">The media is unselected.</string> <string name="mediapicker_gallery_item_unselected_content_description">The media is unselected.</string>
<string name="mediapicker_gallery_title_selection"><xliff:g id="count">%d</xliff:g> selected</string> <string name="mediapicker_gallery_title_selection"><xliff:g id="count">%d</xliff:g> selected</string>
@@ -20,7 +20,7 @@ import android.content.Context;
import android.net.Uri; import android.net.Uri;
import android.provider.MediaStore.Files; import android.provider.MediaStore.Files;
import android.provider.MediaStore.Files.FileColumns; import android.provider.MediaStore.Files.FileColumns;
import android.provider.MediaStore.Images.Media; import android.provider.MediaStore.MediaColumns;
import com.android.messaging.datamodel.data.GalleryGridItemData; import com.android.messaging.datamodel.data.GalleryGridItemData;
import com.android.messaging.datamodel.data.MessagePartData; import com.android.messaging.datamodel.data.MessagePartData;
@@ -32,18 +32,18 @@ import com.google.common.base.Joiner;
public class GalleryBoundCursorLoader extends BoundCursorLoader { public class GalleryBoundCursorLoader extends BoundCursorLoader {
public static final String MEDIA_SCANNER_VOLUME_EXTERNAL = "external"; public static final String MEDIA_SCANNER_VOLUME_EXTERNAL = "external";
private static final Uri STORAGE_URI = Files.getContentUri(MEDIA_SCANNER_VOLUME_EXTERNAL); private static final Uri STORAGE_URI = Files.getContentUri(MEDIA_SCANNER_VOLUME_EXTERNAL);
private static final String SORT_ORDER = Media.DATE_MODIFIED + " DESC"; private static final String SORT_ORDER = MediaColumns.DATE_MODIFIED + " DESC";
private static final String IMAGE_SELECTION = createSelection( private static final String SELECTION = createSelection(
MessagePartData.ACCEPTABLE_IMAGE_TYPES, MessagePartData.ACCEPTABLE_GALLERY_MEDIA_TYPES,
new Integer[] { FileColumns.MEDIA_TYPE_IMAGE }); new Integer[] {FileColumns.MEDIA_TYPE_IMAGE, FileColumns.MEDIA_TYPE_VIDEO});
public GalleryBoundCursorLoader(final String bindingId, final Context context) { public GalleryBoundCursorLoader(final String bindingId, final Context context) {
super(bindingId, context, STORAGE_URI, GalleryGridItemData.IMAGE_PROJECTION, super(bindingId, context, STORAGE_URI, GalleryGridItemData.MEDIA_PROJECTION, SELECTION,
IMAGE_SELECTION, null, SORT_ORDER); null, SORT_ORDER);
} }
private static String createSelection(final String[] mimeTypes, Integer[] mediaTypes) { private static String createSelection(final String[] mimeTypes, Integer[] mediaTypes) {
return Media.MIME_TYPE + " IN ('" + Joiner.on("','").join(mimeTypes) + "') AND " return MediaColumns.MIME_TYPE + " IN ('" + Joiner.on("','").join(mimeTypes) + "') AND "
+ FileColumns.MEDIA_TYPE + " IN (" + Joiner.on(',').join(mediaTypes) + ")"; + FileColumns.MEDIA_TYPE + " IN (" + Joiner.on(',').join(mediaTypes) + ")";
} }
} }
@@ -20,25 +20,27 @@ import android.database.Cursor;
import android.graphics.Rect; import android.graphics.Rect;
import android.net.Uri; import android.net.Uri;
import android.provider.BaseColumns; import android.provider.BaseColumns;
import android.provider.MediaStore.Images.Media; import android.provider.MediaStore.MediaColumns;
import android.text.TextUtils; import android.text.TextUtils;
import com.android.messaging.datamodel.media.FileImageRequestDescriptor; import com.android.messaging.datamodel.media.FileImageRequestDescriptor;
import com.android.messaging.datamodel.media.ImageRequest; import com.android.messaging.datamodel.media.ImageRequest;
import com.android.messaging.datamodel.media.UriImageRequestDescriptor; import com.android.messaging.datamodel.media.UriImageRequestDescriptor;
import com.android.messaging.datamodel.media.VideoThumbnailRequestDescriptor;
import com.android.messaging.util.Assert; import com.android.messaging.util.Assert;
import com.android.messaging.util.ContentType;
/** /**
* Provides data for GalleryGridItemView * Provides data for GalleryGridItemView
*/ */
public class GalleryGridItemData { public class GalleryGridItemData {
public static final String[] IMAGE_PROJECTION = new String[] { public static final String[] MEDIA_PROJECTION = new String[] {
Media._ID, MediaColumns._ID,
Media.DATA, MediaColumns.DATA,
Media.WIDTH, MediaColumns.WIDTH,
Media.HEIGHT, MediaColumns.HEIGHT,
Media.MIME_TYPE, MediaColumns.MIME_TYPE,
Media.DATE_MODIFIED}; MediaColumns.DATE_MODIFIED};
public static final String[] SPECIAL_ITEM_COLUMNS = new String[] { public static final String[] SPECIAL_ITEM_COLUMNS = new String[] {
BaseColumns._ID BaseColumns._ID
@@ -46,14 +48,14 @@ public class GalleryGridItemData {
private static final int INDEX_ID = 0; private static final int INDEX_ID = 0;
// For local image gallery. // For local media gallery.
private static final int INDEX_DATA_PATH = 1; private static final int INDEX_DATA_PATH = 1;
private static final int INDEX_WIDTH = 2; private static final int INDEX_WIDTH = 2;
private static final int INDEX_HEIGHT = 3; private static final int INDEX_HEIGHT = 3;
private static final int INDEX_MIME_TYPE = 4; private static final int INDEX_MIME_TYPE = 4;
private static final int INDEX_DATE_MODIFIED = 5; private static final int INDEX_DATE_MODIFIED = 5;
/** A special item's id for picking images from document picker */ /** A special item's id for picking a media from document picker */
public static final String ID_DOCUMENT_PICKER_ITEM = "-1"; public static final String ID_DOCUMENT_PICKER_ITEM = "-1";
private UriImageRequestDescriptor mImageData; private UriImageRequestDescriptor mImageData;
@@ -85,15 +87,24 @@ public class GalleryGridItemData {
mContentType = cursor.getString(INDEX_MIME_TYPE); mContentType = cursor.getString(INDEX_MIME_TYPE);
final String dateModified = cursor.getString(INDEX_DATE_MODIFIED); final String dateModified = cursor.getString(INDEX_DATE_MODIFIED);
mDateSeconds = !TextUtils.isEmpty(dateModified) ? Long.parseLong(dateModified) : -1; mDateSeconds = !TextUtils.isEmpty(dateModified) ? Long.parseLong(dateModified) : -1;
mImageData = new FileImageRequestDescriptor( if (ContentType.isVideoType(mContentType)) {
cursor.getString(INDEX_DATA_PATH), mImageData = new VideoThumbnailRequestDescriptor(
desiredWidth, cursor.getLong(INDEX_ID),
desiredHeight, desiredWidth,
sourceWidth, desiredHeight,
sourceHeight, sourceWidth,
true /* canUseThumbnail */, sourceHeight);
true /* allowCompression */, } else {
true /* isStatic */); mImageData = new FileImageRequestDescriptor(
cursor.getString(INDEX_DATA_PATH),
desiredWidth,
desiredHeight,
sourceWidth,
sourceHeight,
true /* canUseThumbnail */,
true /* allowCompression */,
true /* isStatic */);
}
} }
} }
@@ -51,7 +51,7 @@ public class MediaPickerData extends BindableData {
mGalleryLoaderCallbacks = new GalleryLoaderCallbacks(); mGalleryLoaderCallbacks = new GalleryLoaderCallbacks();
} }
public static final int GALLERY_IMAGE_LOADER = 1; public static final int GALLERY_MEDIA_LOADER = 1;
/** /**
* A trampoline class so that we can inherit from LoaderManager.LoaderCallbacks multiple times. * A trampoline class so that we can inherit from LoaderManager.LoaderCallbacks multiple times.
@@ -63,7 +63,7 @@ public class MediaPickerData extends BindableData {
// Check if data still bound to the requesting ui element // Check if data still bound to the requesting ui element
if (isBound(bindingId)) { if (isBound(bindingId)) {
switch (id) { switch (id) {
case GALLERY_IMAGE_LOADER: case GALLERY_MEDIA_LOADER:
return new GalleryBoundCursorLoader(bindingId, mContext); return new GalleryBoundCursorLoader(bindingId, mContext);
default: default:
@@ -84,9 +84,9 @@ public class MediaPickerData extends BindableData {
final BoundCursorLoader cursorLoader = (BoundCursorLoader) loader; final BoundCursorLoader cursorLoader = (BoundCursorLoader) loader;
if (isBound(cursorLoader.getBindingId())) { if (isBound(cursorLoader.getBindingId())) {
switch (loader.getId()) { switch (loader.getId()) {
case GALLERY_IMAGE_LOADER: case GALLERY_MEDIA_LOADER:
mListener.onMediaPickerDataUpdated(MediaPickerData.this, data, mListener.onMediaPickerDataUpdated(MediaPickerData.this, data,
GALLERY_IMAGE_LOADER); GALLERY_MEDIA_LOADER);
break; break;
default: default:
@@ -106,9 +106,9 @@ public class MediaPickerData extends BindableData {
final BoundCursorLoader cursorLoader = (BoundCursorLoader) loader; final BoundCursorLoader cursorLoader = (BoundCursorLoader) loader;
if (isBound(cursorLoader.getBindingId())) { if (isBound(cursorLoader.getBindingId())) {
switch (loader.getId()) { switch (loader.getId()) {
case GALLERY_IMAGE_LOADER: case GALLERY_MEDIA_LOADER:
mListener.onMediaPickerDataUpdated(MediaPickerData.this, null, mListener.onMediaPickerDataUpdated(MediaPickerData.this, null,
GALLERY_IMAGE_LOADER); GALLERY_MEDIA_LOADER);
break; break;
default: default:
@@ -129,7 +129,7 @@ public class MediaPickerData extends BindableData {
args = new Bundle(); args = new Bundle();
} }
args.putString(BINDING_ID, binding.getBindingId()); args.putString(BINDING_ID, binding.getBindingId());
if (loaderId == GALLERY_IMAGE_LOADER) { if (loaderId == GALLERY_MEDIA_LOADER) {
mLoaderManager.initLoader(loaderId, args, mGalleryLoaderCallbacks).forceLoad(); mLoaderManager.initLoader(loaderId, args, mGalleryLoaderCallbacks).forceLoad();
} else { } else {
Assert.fail("Unsupported loader id for media picker!"); Assert.fail("Unsupported loader id for media picker!");
@@ -149,7 +149,7 @@ public class MediaPickerData extends BindableData {
protected void unregisterListeners() { protected void unregisterListeners() {
// This could be null if we bind but the caller doesn't init the BindableData // This could be null if we bind but the caller doesn't init the BindableData
if (mLoaderManager != null) { if (mLoaderManager != null) {
mLoaderManager.destroyLoader(GALLERY_IMAGE_LOADER); mLoaderManager.destroyLoader(GALLERY_MEDIA_LOADER);
mLoaderManager = null; mLoaderManager = null;
} }
} }
@@ -52,9 +52,17 @@ import java.util.concurrent.TimeUnit;
*/ */
public class MessagePartData implements Parcelable { public class MessagePartData implements Parcelable {
public static final int UNSPECIFIED_SIZE = MessagingContentProvider.UNSPECIFIED_SIZE; public static final int UNSPECIFIED_SIZE = MessagingContentProvider.UNSPECIFIED_SIZE;
public static final String[] ACCEPTABLE_IMAGE_TYPES =
new String[] { ContentType.IMAGE_JPEG, ContentType.IMAGE_JPG, ContentType.IMAGE_PNG, public static final String[] ACCEPTABLE_GALLERY_MEDIA_TYPES =
ContentType.IMAGE_GIF }; new String[] {
// Acceptable image types
ContentType.IMAGE_JPEG, ContentType.IMAGE_JPG, ContentType.IMAGE_PNG,
ContentType.IMAGE_GIF, ContentType.IMAGE_WBMP, ContentType.IMAGE_X_MS_BMP,
// Acceptable video types
ContentType.VIDEO_3GP, ContentType.VIDEO_3GPP, ContentType.VIDEO_3G2,
ContentType.VIDEO_H263, ContentType.VIDEO_M4V, ContentType.VIDEO_MP4,
ContentType.VIDEO_MPEG, ContentType.VIDEO_MPEG4, ContentType.VIDEO_WEBM
};
private static final String[] sProjection = { private static final String[] sProjection = {
PartColumns._ID, PartColumns._ID,
@@ -87,9 +87,4 @@ public class UriImageRequestDescriptor extends ImageRequestDescriptor {
return new NetworkUriImageRequest<UriImageRequestDescriptor>(context, this); return new NetworkUriImageRequest<UriImageRequestDescriptor>(context, this);
} }
} }
/** ID of the resource in MediaStore or null if this resource didn't come from MediaStore */
public Long getMediaStoreId() {
return null;
}
} }
@@ -16,15 +16,11 @@
package com.android.messaging.datamodel.media; package com.android.messaging.datamodel.media;
import android.content.ContentResolver;
import android.content.Context; import android.content.Context;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.provider.MediaStore.Video.Thumbnails;
import com.android.messaging.Factory;
import com.android.messaging.util.MediaMetadataRetrieverWrapper; import com.android.messaging.util.MediaMetadataRetrieverWrapper;
import com.android.messaging.util.MediaUtil; import com.android.messaging.util.MediaUtil;
import com.android.messaging.util.OsUtil;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.io.IOException; import java.io.IOException;
@@ -57,19 +53,15 @@ public class VideoThumbnailRequest extends ImageRequest<UriImageRequestDescripto
@Override @Override
protected Bitmap getBitmapForResource() throws IOException { protected Bitmap getBitmapForResource() throws IOException {
final Long mediaId = mDescriptor.getMediaStoreId();
Bitmap bitmap = null; Bitmap bitmap = null;
if (mediaId != null) { // Get a thumbnail through MediaMetadataRetriever to get a representative frame at any time
final ContentResolver cr = Factory.get().getApplicationContext().getContentResolver(); // position instead.
bitmap = Thumbnails.getThumbnail(cr, mediaId, Thumbnails.MICRO_KIND, null); final MediaMetadataRetrieverWrapper retriever = new MediaMetadataRetrieverWrapper();
} else { try {
final MediaMetadataRetrieverWrapper retriever = new MediaMetadataRetrieverWrapper(); retriever.setDataSource(mDescriptor.uri);
try { bitmap = retriever.getFrameAtTime();
retriever.setDataSource(mDescriptor.uri); } finally {
bitmap = retriever.getFrameAtTime(); retriever.release();
} finally {
retriever.release();
}
} }
if (bitmap != null) { if (bitmap != null) {
mDescriptor.updateSourceDimensions(bitmap.getWidth(), bitmap.getHeight()); mDescriptor.updateSourceDimensions(bitmap.getWidth(), bitmap.getHeight());
@@ -21,24 +21,17 @@ import com.android.messaging.util.ImageUtils;
import com.android.messaging.util.UriUtil; import com.android.messaging.util.UriUtil;
public class VideoThumbnailRequestDescriptor extends UriImageRequestDescriptor { public class VideoThumbnailRequestDescriptor extends UriImageRequestDescriptor {
protected final long mMediaId; public VideoThumbnailRequestDescriptor(final long id, int desiredWidth, int desiredHeight,
public VideoThumbnailRequestDescriptor(final long id, String path, int desiredWidth, int sourceWidth, int sourceHeight) {
int desiredHeight, int sourceWidth, int sourceHeight) { super(UriUtil.getContentUriForMediaStoreId(id), desiredWidth, desiredHeight, sourceWidth,
super(UriUtil.getUriForResourceFile(path), desiredWidth, desiredHeight, sourceWidth,
sourceHeight, false /* canCompress */, false /* isStatic */, sourceHeight, false /* canCompress */, false /* isStatic */,
false /* cropToCircle */, false /* cropToCircle */,
ImageUtils.DEFAULT_CIRCLE_BACKGROUND_COLOR /* circleBackgroundColor */, ImageUtils.DEFAULT_CIRCLE_BACKGROUND_COLOR /* circleBackgroundColor */,
ImageUtils.DEFAULT_CIRCLE_STROKE_COLOR /* circleStrokeColor */); ImageUtils.DEFAULT_CIRCLE_STROKE_COLOR /* circleStrokeColor */);
mMediaId = id;
} }
@Override @Override
public MediaRequest<ImageResource> buildSyncMediaRequest(Context context) { public MediaRequest<ImageResource> buildSyncMediaRequest(Context context) {
return new VideoThumbnailRequest(context, this); return new VideoThumbnailRequest(context, this);
} }
@Override
public Long getMediaStoreId() {
return mMediaId;
}
} }
+4 -3
View File
@@ -44,8 +44,8 @@ public abstract class UIIntents {
// Sending draft data (from share intent / message forwarding) to the ConversationActivity. // Sending draft data (from share intent / message forwarding) to the ConversationActivity.
public static final String UI_INTENT_EXTRA_DRAFT_DATA = "draft_data"; public static final String UI_INTENT_EXTRA_DRAFT_DATA = "draft_data";
// The request code for picking image from the Document picker. // The request code for picking a media from the Document picker.
public static final int REQUEST_PICK_IMAGE_FROM_DOCUMENT_PICKER = 1400; public static final int REQUEST_PICK_MEDIA_FROM_DOCUMENT_PICKER = 1400;
// Indicates what type of notification this applies to (See BugleNotifications: // Indicates what type of notification this applies to (See BugleNotifications:
// UPDATE_NONE, UPDATE_MESSAGES, UPDATE_ERRORS, UPDATE_ALL) // UPDATE_NONE, UPDATE_MESSAGES, UPDATE_ERRORS, UPDATE_ALL)
@@ -166,7 +166,8 @@ public abstract class UIIntents {
public abstract void launchAddContactActivity(final Context context, final String destination); public abstract void launchAddContactActivity(final Context context, final String destination);
/** /**
* Launch an activity to show the document picker to pick an image. * Launch an activity to show the document picker to pick an image/video.
*
* @param fragment the requesting fragment * @param fragment the requesting fragment
*/ */
public abstract void launchDocumentImagePicker(final Fragment fragment); public abstract void launchDocumentImagePicker(final Fragment fragment);
@@ -236,11 +236,11 @@ public class UIIntentsImpl extends UIIntents {
@Override @Override
public void launchDocumentImagePicker(final Fragment fragment) { public void launchDocumentImagePicker(final Fragment fragment) {
final Intent intent = new Intent(Intent.ACTION_GET_CONTENT); final Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.putExtra(Intent.EXTRA_MIME_TYPES, MessagePartData.ACCEPTABLE_IMAGE_TYPES); intent.putExtra(Intent.EXTRA_MIME_TYPES, MessagePartData.ACCEPTABLE_GALLERY_MEDIA_TYPES);
intent.addCategory(Intent.CATEGORY_OPENABLE); intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType(ContentType.IMAGE_UNSPECIFIED); intent.setType(ContentType.ANY_TYPE);
fragment.startActivityForResult(intent, REQUEST_PICK_IMAGE_FROM_DOCUMENT_PICKER); fragment.startActivityForResult(intent, REQUEST_PICK_MEDIA_FROM_DOCUMENT_PICKER);
} }
@Override @Override
@@ -30,8 +30,8 @@ import com.android.messaging.util.ImageUtils;
import com.android.messaging.util.SafeAsyncTask; import com.android.messaging.util.SafeAsyncTask;
/** /**
* Wraps around the functionalities to allow the user to pick images from the document * Wraps around the functionalities to allow the user to pick an image/video from the document
* picker. Instances of this class must be tied to a Fragment which is able to delegate activity * picker. Instances of this class must be tied to a Fragment which is able to delegate activity
* result callbacks. * result callbacks.
*/ */
public class DocumentImagePicker { public class DocumentImagePicker {
@@ -79,8 +79,8 @@ public class DocumentImagePicker {
* Must be called from the fragment/activity's onActivityResult(). * Must be called from the fragment/activity's onActivityResult().
*/ */
public void onActivityResult(final int requestCode, final int resultCode, final Intent data) { public void onActivityResult(final int requestCode, final int resultCode, final Intent data) {
if (requestCode == UIIntents.REQUEST_PICK_IMAGE_FROM_DOCUMENT_PICKER && if (requestCode == UIIntents.REQUEST_PICK_MEDIA_FROM_DOCUMENT_PICKER
resultCode == Activity.RESULT_OK) { && resultCode == Activity.RESULT_OK) {
// Sometimes called after media item has been picked from the document picker. // Sometimes called after media item has been picked from the document picker.
String url = data.getStringExtra(EXTRA_PHOTO_URL); String url = data.getStringExtra(EXTRA_PHOTO_URL);
if (url == null) { if (url == null) {
@@ -24,13 +24,14 @@ import android.view.TouchDelegate;
import android.view.View; import android.view.View;
import android.widget.CheckBox; import android.widget.CheckBox;
import android.widget.FrameLayout; import android.widget.FrameLayout;
import android.widget.ImageView.ScaleType; import android.widget.ImageView;
import com.android.messaging.R; import com.android.messaging.R;
import com.android.messaging.datamodel.DataModel; import com.android.messaging.datamodel.DataModel;
import com.android.messaging.datamodel.data.GalleryGridItemData; import com.android.messaging.datamodel.data.GalleryGridItemData;
import com.android.messaging.ui.AsyncImageView; import com.android.messaging.ui.AsyncImageView;
import com.android.messaging.ui.ConversationDrawables; import com.android.messaging.ui.ConversationDrawables;
import com.android.messaging.util.ContentType;
import com.google.common.annotations.VisibleForTesting; import com.google.common.annotations.VisibleForTesting;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@@ -135,17 +136,24 @@ public class GalleryGridItemView extends FrameLayout {
} }
private void updateImageView() { private void updateImageView() {
ImageView playButton = (ImageView) findViewById(R.id.video_thumbnail_play_button);
if (mData.isDocumentPickerItem()) { if (mData.isDocumentPickerItem()) {
mImageView.setScaleType(ScaleType.CENTER); mImageView.setScaleType(ImageView.ScaleType.CENTER);
setBackgroundColor(ConversationDrawables.get().getConversationThemeColor()); setBackgroundColor(ConversationDrawables.get().getConversationThemeColor());
mImageView.setImageResourceId(null); mImageView.setImageResourceId(null);
mImageView.setImageResource(R.drawable.ic_photo_library_light); mImageView.setImageResource(R.drawable.ic_photo_library_light);
playButton.setVisibility(GONE);
mImageView.setContentDescription(getResources().getString( mImageView.setContentDescription(getResources().getString(
R.string.pick_image_from_document_library_content_description)); R.string.pick_image_from_document_library_content_description));
} else { } else {
mImageView.setScaleType(ScaleType.CENTER_CROP); mImageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
setBackgroundColor(getResources().getColor(R.color.gallery_image_default_background)); setBackgroundColor(getResources().getColor(R.color.gallery_image_default_background));
mImageView.setImageResourceId(mData.getImageRequestDescriptor()); mImageView.setImageResourceId(mData.getImageRequestDescriptor());
if (ContentType.isVideoType(mData.getContentType())) {
playButton.setVisibility(VISIBLE);
} else {
playButton.setVisibility(GONE);
}
final long dateSeconds = mData.getDateSeconds(); final long dateSeconds = mData.getDateSeconds();
final boolean isValidDate = (dateSeconds > 0); final boolean isValidDate = (dateSeconds > 0);
final int templateId = isValidDate ? final int templateId = isValidDate ?
@@ -43,16 +43,16 @@ import java.util.Iterator;
import java.util.Map; import java.util.Map;
/** /**
* Shows a list of galley images from external storage in a GridView with multi-select * Shows a list of galley mediae from external storage in a GridView with multi-select capabilities,
* capabilities, and with the option to intent out to a standalone image picker. * and with the option to intent out to a standalone media picker.
*/ */
public class GalleryGridView extends MediaPickerGridView implements public class GalleryGridView extends MediaPickerGridView implements
GalleryGridItemView.HostInterface, GalleryGridItemView.HostInterface,
PersistentInstanceState, PersistentInstanceState,
DraftMessageDataListener { DraftMessageDataListener {
/** /**
* Implemented by the owner of this GalleryGridView instance to communicate on image * Implemented by the owner of this GalleryGridView instance to communicate on media picking and
* picking and multi-image selection events. * multi-media selection events.
*/ */
public interface GalleryGridViewListener { public interface GalleryGridViewListener {
void onDocumentPickerItemClicked(); void onDocumentPickerItemClicked();
@@ -127,7 +127,7 @@ public class GalleryGridView extends MediaPickerGridView implements
final MessagePartData item = mSelectedImages.remove(data.getImageUri()); final MessagePartData item = mSelectedImages.remove(data.getImageUri());
mListener.onItemUnselected(item); mListener.onItemUnselected(item);
if (mSelectedImages.size() == 0) { if (mSelectedImages.size() == 0) {
// No image is selected any more, turn off multi-select mode. // No media is selected any more, turn off multi-select mode.
setMultiSelectEnabled(false); setMultiSelectEnabled(false);
} }
} else { } else {
@@ -63,7 +63,7 @@ class GalleryMediaChooser extends MediaChooser implements
mAdapter.setHostInterface(null); mAdapter.setHostInterface(null);
// The loader is started only if startMediaPickerDataLoader() is called // The loader is started only if startMediaPickerDataLoader() is called
if (OsUtil.hasStoragePermission()) { if (OsUtil.hasStoragePermission()) {
mBindingRef.getData().destroyLoader(MediaPickerData.GALLERY_IMAGE_LOADER); mBindingRef.getData().destroyLoader(MediaPickerData.GALLERY_MEDIA_LOADER);
} }
return super.destroyView(); return super.destroyView();
} }
@@ -121,7 +121,7 @@ class GalleryMediaChooser extends MediaChooser implements
protected View createView(final ViewGroup container) { protected View createView(final ViewGroup container) {
final LayoutInflater inflater = getLayoutInflater(); final LayoutInflater inflater = getLayoutInflater();
final View view = inflater.inflate( final View view = inflater.inflate(
R.layout.mediapicker_image_chooser, R.layout.mediapicker_gallery_chooser,
container /* root */, container /* root */,
false /* attachToRoot */); false /* attachToRoot */);
@@ -167,7 +167,7 @@ class GalleryMediaChooser extends MediaChooser implements
public void onMediaPickerDataUpdated(final MediaPickerData mediaPickerData, final Object data, public void onMediaPickerDataUpdated(final MediaPickerData mediaPickerData, final Object data,
final int loaderId) { final int loaderId) {
mBindingRef.ensureBound(mediaPickerData); mBindingRef.ensureBound(mediaPickerData);
Assert.equals(MediaPickerData.GALLERY_IMAGE_LOADER, loaderId); Assert.equals(MediaPickerData.GALLERY_MEDIA_LOADER, loaderId);
Cursor rawCursor = null; Cursor rawCursor = null;
if (data instanceof Cursor) { if (data instanceof Cursor) {
rawCursor = (Cursor) data; rawCursor = (Cursor) data;
@@ -202,8 +202,9 @@ class GalleryMediaChooser extends MediaChooser implements
} }
private void startMediaPickerDataLoader() { private void startMediaPickerDataLoader() {
mBindingRef.getData().startLoader(MediaPickerData.GALLERY_IMAGE_LOADER, mBindingRef, null, mBindingRef
this); .getData()
.startLoader(MediaPickerData.GALLERY_MEDIA_LOADER, mBindingRef, null, this);
} }
@Override @Override
@@ -159,7 +159,7 @@ public class MediaPicker extends Fragment implements DraftMessageSubscriptionDat
@VisibleForTesting @VisibleForTesting
final Binding<MediaPickerData> mBinding = BindingBase.createBinding(this); final Binding<MediaPickerData> mBinding = BindingBase.createBinding(this);
/** Handles picking image from the document picker */ /** Handles picking a media from the document picker. */
private DocumentImagePicker mDocumentImagePicker; private DocumentImagePicker mDocumentImagePicker;
/** Provides subscription-related data to access per-subscription configurations. */ /** Provides subscription-related data to access per-subscription configurations. */
@@ -26,6 +26,7 @@ import androidx.annotation.NonNull;
import android.text.TextUtils; import android.text.TextUtils;
import com.android.messaging.Factory; import com.android.messaging.Factory;
import com.android.messaging.datamodel.GalleryBoundCursorLoader;
import com.android.messaging.datamodel.MediaScratchFileProvider; import com.android.messaging.datamodel.MediaScratchFileProvider;
import com.android.messaging.util.Assert.DoesNotRunOnMainThread; import com.android.messaging.util.Assert.DoesNotRunOnMainThread;
import com.google.common.io.ByteStreams; import com.google.common.io.ByteStreams;
@@ -129,6 +130,18 @@ public class UriUtil {
TextUtils.equals(MEDIA_STORE_URI_KLP, uriAuthority)); TextUtils.equals(MEDIA_STORE_URI_KLP, uriAuthority));
} }
/**
* Gets the content:// style URI for the given MediaStore row Id in the files table on the
* external volume.
*
* @param id the MediaStore row Id to get the URI for
* @return the URI to the files table on the external storage.
*/
public static Uri getContentUriForMediaStoreId(final long id) {
return MediaStore.Files.getContentUri(
GalleryBoundCursorLoader.MEDIA_SCANNER_VOLUME_EXTERNAL, id);
}
/** /**
* Gets the size in bytes for the content uri. Currently we only support content in the * Gets the size in bytes for the content uri. Currently we only support content in the
* scratch space. * scratch space.
@@ -279,7 +279,7 @@ public class TestDataFactory {
new Object[] { Long.valueOf(1), "/sdcard/image2", 200, 200, "image/png" }, new Object[] { Long.valueOf(1), "/sdcard/image2", 200, 200, "image/png" },
new Object[] { Long.valueOf(2), "/sdcard/image3", 300, 300, "image/jpeg" }, new Object[] { Long.valueOf(2), "/sdcard/image3", 300, 300, "image/jpeg" },
}; };
return new FakeCursor(GalleryGridItemData.IMAGE_PROJECTION, sGalleryCursorColumns, return new FakeCursor(GalleryGridItemData.MEDIA_PROJECTION, sGalleryCursorColumns,
cursorData); cursorData);
} }
@@ -97,7 +97,7 @@ public class MediaPickerTest extends FragmentTestCase<MediaPicker> {
public void testDefaultTabs() { public void testDefaultTabs() {
Mockito.when(mMockMediaPickerData.getSelectedChooserIndex()).thenReturn(0); Mockito.when(mMockMediaPickerData.getSelectedChooserIndex()).thenReturn(0);
initFragment(MediaPicker.MEDIA_TYPE_ALL, new Integer[] { initFragment(MediaPicker.MEDIA_TYPE_ALL, new Integer[] {
MediaPickerData.GALLERY_IMAGE_LOADER }, MediaPickerData.GALLERY_MEDIA_LOADER },
false); false);
final MediaPicker mediaPicker = getFragment(); final MediaPicker mediaPicker = getFragment();
final View view = mediaPicker.getView(); final View view = mediaPicker.getView();
@@ -114,7 +114,7 @@ public class MediaPickerTest extends FragmentTestCase<MediaPicker> {
public void testFilterTabsBeforeAttach() { public void testFilterTabsBeforeAttach() {
Mockito.when(mMockMediaPickerData.getSelectedChooserIndex()).thenReturn(0); Mockito.when(mMockMediaPickerData.getSelectedChooserIndex()).thenReturn(0);
initFragment(MediaPicker.MEDIA_TYPE_IMAGE, new Integer[] { initFragment(MediaPicker.MEDIA_TYPE_IMAGE, new Integer[] {
MediaPickerData.GALLERY_IMAGE_LOADER }, MediaPickerData.GALLERY_MEDIA_LOADER },
true); true);
final MediaPicker mediaPicker = getFragment(); final MediaPicker mediaPicker = getFragment();
final View view = mediaPicker.getView(); final View view = mediaPicker.getView();