diff --git a/WordPress/src/main/java/org/wordpress/android/ui/photopicker/PhotoPickerActivity.java b/WordPress/src/main/java/org/wordpress/android/ui/photopicker/PhotoPickerActivity.java index 6bf70089a2f4..96757dc962e1 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/photopicker/PhotoPickerActivity.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/photopicker/PhotoPickerActivity.java @@ -288,7 +288,7 @@ public void onPhotoPickerMediaChosen(@NonNull List uriList) { } @Override - public void onPhotoPickerIconClicked(@NonNull PhotoPickerFragment.PhotoPickerIcon icon) { + public void onPhotoPickerIconClicked(@NonNull PhotoPickerFragment.PhotoPickerIcon icon, boolean multiple) { switch (icon) { case ANDROID_CAPTURE_PHOTO: launchCamera(); diff --git a/WordPress/src/main/java/org/wordpress/android/ui/photopicker/PhotoPickerFragment.java b/WordPress/src/main/java/org/wordpress/android/ui/photopicker/PhotoPickerFragment.java index 6fd4e4b174b3..5d02eb41131d 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/photopicker/PhotoPickerFragment.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/photopicker/PhotoPickerFragment.java @@ -68,7 +68,7 @@ public enum PhotoPickerIcon { public interface PhotoPickerListener { void onPhotoPickerMediaChosen(@NonNull List uriList); - void onPhotoPickerIconClicked(@NonNull PhotoPickerIcon icon); + void onPhotoPickerIconClicked(@NonNull PhotoPickerIcon icon, boolean allowMultipleSelection); } private EmptyViewRecyclerView mRecycler; @@ -252,7 +252,7 @@ public void doIconClicked(@NonNull PhotoPickerIcon icon) { } if (mListener != null) { - mListener.onPhotoPickerIconClicked(icon); + mListener.onPhotoPickerIconClicked(icon, false); } } diff --git a/WordPress/src/main/java/org/wordpress/android/ui/posts/EditPostActivity.java b/WordPress/src/main/java/org/wordpress/android/ui/posts/EditPostActivity.java index c369a2dce30b..32bb24c5140a 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/posts/EditPostActivity.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/posts/EditPostActivity.java @@ -19,6 +19,7 @@ import android.text.Editable; import android.text.Spanned; import android.text.TextUtils; +import android.util.ArrayMap; import android.view.ContextThemeWrapper; import android.view.DragEvent; import android.view.Menu; @@ -257,6 +258,7 @@ enum RestartEditorOptions { private boolean mShowAztecEditor; private boolean mShowGutenbergEditor; private boolean mMediaInsertedOnCreation; + private boolean mAllowMultipleSelection; private List mPendingVideoPressInfoRequests; private List mAztecBackspaceDeletedOrGbBlockDeletedMediaItemIds = new ArrayList<>(); @@ -1204,8 +1206,9 @@ public void onMediaToolbarButtonClicked(MediaToolbarAction action) { * picker, or WP media picker */ @Override - public void onPhotoPickerIconClicked(@NonNull PhotoPickerIcon icon) { + public void onPhotoPickerIconClicked(@NonNull PhotoPickerIcon icon, boolean allowMultipleSelection) { hidePhotoPicker(); + mAllowMultipleSelection = allowMultipleSelection; switch (icon) { case ANDROID_CAPTURE_PHOTO: launchCamera(); @@ -1787,13 +1790,11 @@ private void onUploadProgress(MediaModel media, float progress) { } private void launchPictureLibrary() { - // don't allow multiple selection for Gutenberg, as we're on a single image block for now - WPMediaUtils.launchPictureLibrary(this, !mShowGutenbergEditor); + WPMediaUtils.launchPictureLibrary(this, mAllowMultipleSelection); } private void launchVideoLibrary() { - // don't allow multiple selection for Gutenberg, as we're on a single image block for now - WPMediaUtils.launchVideoLibrary(this, !mShowGutenbergEditor); + WPMediaUtils.launchVideoLibrary(this, mAllowMultipleSelection); } private void launchVideoCamera() { @@ -2522,6 +2523,24 @@ private boolean addExistingMediaToEditor(@NonNull AddExistingdMediaSource source return true; } + private void addExistingMediaToEditor(@NonNull AddExistingdMediaSource source, List mediaIdList) { + ArrayMap mediaMap = new ArrayMap<>(); + for (Long mediaId : mediaIdList) { + MediaModel media = mMediaStore.getSiteMediaWithId(mSite, mediaId); + if (media == null) { + AppLog.w(T.MEDIA, "Cannot add null media to post"); + } else { + trackAddMediaEvent(source, media); + + MediaFile mediaFile = FluxCUtils.mediaFileFromMediaModel(media); + String urlToUse = TextUtils.isEmpty(media.getUrl()) ? media.getFilePath() : media.getUrl(); + + mediaMap.put(urlToUse, mediaFile); + } + } + mEditorFragment.appendMediaFiles(mediaMap); + } + private class LoadPostContentTask extends AsyncTask { @Override protected Spanned doInBackground(String... params) { @@ -2826,7 +2845,7 @@ private boolean addMedia(Uri mediaUri, boolean isNew) { private void addMediaList(@NonNull List uriList, boolean isNew) { // fetch any shared media first - must be done on the main thread List fetchedUriList = fetchMediaList(uriList); - mAddMediaListThread = new AddMediaListThread(fetchedUriList, isNew); + mAddMediaListThread = new AddMediaListThread(fetchedUriList, isNew, mAllowMultipleSelection); mAddMediaListThread.start(); } @@ -2849,6 +2868,9 @@ private class AddMediaListThread extends Thread { private final boolean mIsNew; private ProgressDialog mProgressDialog; private boolean mDidAnyFail; + private int mFinishedUploads = 0; + private boolean mAllowMultipleSelection = false; + private Map mediaMap = new ArrayMap<>(); AddMediaListThread(@NonNull List uriList, boolean isNew) { this.mUriList.addAll(uriList); @@ -2856,6 +2878,13 @@ private class AddMediaListThread extends Thread { showOverlay(false); } + AddMediaListThread(@NonNull List uriList, boolean isNew, boolean allowMultipleSelection) { + this.mUriList.addAll(uriList); + this.mIsNew = isNew; + this.mAllowMultipleSelection = allowMultipleSelection; + showOverlay(false); + } + private void showProgressDialog(final boolean show) { runOnUiThread(new Runnable() { @Override @@ -2955,20 +2984,45 @@ private boolean processMedia(Uri mediaUri) { } private void postProcessMedia(final Uri mediaUri, final String path, final boolean isVideo) { - runOnUiThread(new Runnable() { - @Override - public void run() { - addMediaVisualEditor(mediaUri, path); + if (mAllowMultipleSelection) { + MediaFile mediaFile = getMediaFile(mediaUri); + if (mediaFile != null) { + mediaMap.put(path, mediaFile); } - }); + mFinishedUploads++; + if (mUriList.size() == mFinishedUploads) { + runOnUiThread(new Runnable() { + @Override + public void run() { + mEditorFragment.appendMediaFiles(mediaMap); + } + }); + } + } else { + runOnUiThread(new Runnable() { + @Override + public void run() { + addMediaVisualEditor(mediaUri, path); + } + }); + } } } private void addMediaVisualEditor(Uri uri, String path) { + MediaFile mediaFile = getMediaFile(uri); + if (mediaFile != null) { + mEditorFragment.appendMediaFile(mediaFile, path, mImageLoader); + } + } + + private MediaFile getMediaFile(Uri uri) { MediaModel media = queueFileForUpload(uri, getContentResolver().getType(uri)); MediaFile mediaFile = FluxCUtils.mediaFileFromMediaModel(media); if (media != null) { - mEditorFragment.appendMediaFile(mediaFile, path, mImageLoader); + return mediaFile; + } else { + return null; } } @@ -2996,10 +3050,12 @@ private void addMediaLegacyEditor(Uri mediaUri, boolean isVideo) { private void addMediaItemGroupOrSingleItem(Intent data) { ClipData clipData = data.getClipData(); if (clipData != null) { + ArrayList uriList = new ArrayList<>(); for (int i = 0; i < clipData.getItemCount(); i++) { ClipData.Item item = clipData.getItemAt(i); - addMedia(item.getUri(), false); + uriList.add(item.getUri()); } + addMediaList(uriList, false); } else { addMedia(data.getData(), false); } @@ -3229,8 +3285,14 @@ private void handleMediaPickerResult(Intent data) { if (ids.size() > 1 && allAreImages && !mShowGutenbergEditor) { showInsertMediaDialog(ids); } else { - for (Long id : ids) { - addExistingMediaToEditor(AddExistingdMediaSource.WP_MEDIA_LIBRARY, id); + // if mAllowMultipleSelection and gutenberg editor, pass all ids to addExistingMediaToEditor at once + if (mShowGutenbergEditor && mAllowMultipleSelection) { + addExistingMediaToEditor(AddExistingdMediaSource.WP_MEDIA_LIBRARY, ids); + mAllowMultipleSelection = false; + } else { + for (Long id : ids) { + addExistingMediaToEditor(AddExistingdMediaSource.WP_MEDIA_LIBRARY, id); + } } savePostAsync(null); } @@ -3462,33 +3524,35 @@ public void onAddMediaClicked() { } @Override - public void onAddMediaImageClicked() { + public void onAddMediaImageClicked(boolean allowMultipleSelection) { + mAllowMultipleSelection = allowMultipleSelection; ActivityLauncher.viewMediaPickerForResult(this, mSite, MediaBrowserType.GUTENBERG_IMAGE_PICKER); } @Override - public void onAddMediaVideoClicked() { + public void onAddMediaVideoClicked(boolean allowMultipleSelection) { + mAllowMultipleSelection = allowMultipleSelection; ActivityLauncher.viewMediaPickerForResult(this, mSite, MediaBrowserType.GUTENBERG_VIDEO_PICKER); } @Override - public void onAddPhotoClicked() { - onPhotoPickerIconClicked(PhotoPickerIcon.ANDROID_CHOOSE_PHOTO); + public void onAddPhotoClicked(boolean allowMultipleSelection) { + onPhotoPickerIconClicked(PhotoPickerIcon.ANDROID_CHOOSE_PHOTO, allowMultipleSelection); } @Override public void onCapturePhotoClicked() { - onPhotoPickerIconClicked(PhotoPickerIcon.ANDROID_CAPTURE_PHOTO); + onPhotoPickerIconClicked(PhotoPickerIcon.ANDROID_CAPTURE_PHOTO, false); } @Override - public void onAddVideoClicked() { - onPhotoPickerIconClicked(PhotoPickerIcon.ANDROID_CHOOSE_VIDEO); + public void onAddVideoClicked(boolean allowMultipleSelectio) { + onPhotoPickerIconClicked(PhotoPickerIcon.ANDROID_CHOOSE_VIDEO, allowMultipleSelectio); } @Override public void onCaptureVideoClicked() { - onPhotoPickerIconClicked(PhotoPickerIcon.ANDROID_CAPTURE_VIDEO); + onPhotoPickerIconClicked(PhotoPickerIcon.ANDROID_CAPTURE_VIDEO, false); } @Override diff --git a/libs/editor/WordPressEditor/src/main/java/org/wordpress/android/editor/AztecEditorFragment.java b/libs/editor/WordPressEditor/src/main/java/org/wordpress/android/editor/AztecEditorFragment.java index d6756c71343f..1f361683606f 100644 --- a/libs/editor/WordPressEditor/src/main/java/org/wordpress/android/editor/AztecEditorFragment.java +++ b/libs/editor/WordPressEditor/src/main/java/org/wordpress/android/editor/AztecEditorFragment.java @@ -105,6 +105,7 @@ import java.util.HashMap; import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Set; import java.util.UUID; @@ -1098,6 +1099,8 @@ private void replaceDrawable(Drawable newDrawable) { } } + @Override public void appendMediaFiles(Map mediaList) { } + private Drawable getLoadingMediaErrorPlaceholder(String msg) { if (TextUtils.isEmpty(msg)) { ToastUtils.showToast(getActivity(), R.string.error_media_load); diff --git a/libs/editor/WordPressEditor/src/main/java/org/wordpress/android/editor/EditorFragmentAbstract.java b/libs/editor/WordPressEditor/src/main/java/org/wordpress/android/editor/EditorFragmentAbstract.java index c59730421e11..ff0248a1786a 100644 --- a/libs/editor/WordPressEditor/src/main/java/org/wordpress/android/editor/EditorFragmentAbstract.java +++ b/libs/editor/WordPressEditor/src/main/java/org/wordpress/android/editor/EditorFragmentAbstract.java @@ -16,6 +16,7 @@ import java.util.ArrayList; import java.util.HashMap; +import java.util.Map; import java.util.Set; public abstract class EditorFragmentAbstract extends Fragment { @@ -28,6 +29,7 @@ public class EditorFragmentNotAddedException extends Exception { public abstract CharSequence getContent(CharSequence originalContent) throws EditorFragmentNotAddedException; public abstract LiveData getTitleOrContentChanged(); public abstract void appendMediaFile(MediaFile mediaFile, String imageUrl, ImageLoader imageLoader); + public abstract void appendMediaFiles(Map mediaList); public abstract void appendGallery(MediaGallery mediaGallery); public abstract void setUrlForVideoPressId(String videoPressId, String url, String posterUrl); public abstract boolean isUploadingMedia(); @@ -171,11 +173,11 @@ public interface EditorFragmentListener { void onEditorFragmentInitialized(); void onEditorFragmentContentReady(ArrayList unsupportedBlocks); void onAddMediaClicked(); - void onAddMediaImageClicked(); - void onAddMediaVideoClicked(); - void onAddPhotoClicked(); + void onAddMediaImageClicked(boolean allowMultipleSelection); + void onAddMediaVideoClicked(boolean allowMultipleSelection); + void onAddPhotoClicked(boolean allowMultipleSelection); void onCapturePhotoClicked(); - void onAddVideoClicked(); + void onAddVideoClicked(boolean allowMultipleSelection); void onCaptureVideoClicked(); boolean onMediaRetryClicked(String mediaId); void onMediaRetryAllClicked(Set mediaIdSet); diff --git a/libs/editor/WordPressEditor/src/main/java/org/wordpress/android/editor/GutenbergContainerFragment.java b/libs/editor/WordPressEditor/src/main/java/org/wordpress/android/editor/GutenbergContainerFragment.java index 976f677851ed..575e9b24c4bd 100644 --- a/libs/editor/WordPressEditor/src/main/java/org/wordpress/android/editor/GutenbergContainerFragment.java +++ b/libs/editor/WordPressEditor/src/main/java/org/wordpress/android/editor/GutenbergContainerFragment.java @@ -5,6 +5,7 @@ import androidx.fragment.app.Fragment; +import org.wordpress.mobile.WPAndroidGlue.Media; import org.wordpress.mobile.WPAndroidGlue.WPAndroidGlueCode; import org.wordpress.mobile.WPAndroidGlue.WPAndroidGlueCode.OnAuthHeaderRequestedListener; import org.wordpress.mobile.WPAndroidGlue.WPAndroidGlueCode.OnEditorAutosaveListener; @@ -13,6 +14,8 @@ import org.wordpress.mobile.WPAndroidGlue.WPAndroidGlueCode.OnMediaLibraryButtonListener; import org.wordpress.mobile.WPAndroidGlue.WPAndroidGlueCode.OnReattachQueryListener; +import java.util.ArrayList; + public class GutenbergContainerFragment extends Fragment { public static final String TAG = "gutenberg_container_fragment_tag"; @@ -125,6 +128,10 @@ public void appendMediaFile(int mediaId, final String mediaUrl, final boolean is mWPAndroidGlueCode.appendMediaFile(mediaId, mediaUrl, isVideo); } + public void appendMediaFiles(ArrayList mediaList) { + mWPAndroidGlueCode.appendMediaFiles(mediaList); + } + public void showDevOptionsDialog() { mWPAndroidGlueCode.showDevOptionsDialog(); } @@ -133,6 +140,10 @@ public void appendUploadMediaFile(final int mediaId, final String mediaUri, fina mWPAndroidGlueCode.appendUploadMediaFile(mediaId, mediaUri, isVideo); } + public void appendUploadMediaFiles(ArrayList mediaList) { + mWPAndroidGlueCode.appendUploadMediaFiles(mediaList); + } + public void mediaFileUploadProgress(final int mediaId, final float progress) { mWPAndroidGlueCode.mediaFileUploadProgress(mediaId, progress); } diff --git a/libs/editor/WordPressEditor/src/main/java/org/wordpress/android/editor/GutenbergEditorFragment.java b/libs/editor/WordPressEditor/src/main/java/org/wordpress/android/editor/GutenbergEditorFragment.java index a677b4d33754..c260adde74aa 100644 --- a/libs/editor/WordPressEditor/src/main/java/org/wordpress/android/editor/GutenbergEditorFragment.java +++ b/libs/editor/WordPressEditor/src/main/java/org/wordpress/android/editor/GutenbergEditorFragment.java @@ -39,6 +39,7 @@ import org.wordpress.android.util.helpers.MediaFile; import org.wordpress.android.util.helpers.MediaGallery; import org.wordpress.aztec.IHistoryListener; +import org.wordpress.mobile.WPAndroidGlue.Media; import org.wordpress.mobile.WPAndroidGlue.WPAndroidGlueCode.OnAuthHeaderRequestedListener; import org.wordpress.mobile.WPAndroidGlue.WPAndroidGlueCode.OnEditorAutosaveListener; import org.wordpress.mobile.WPAndroidGlue.WPAndroidGlueCode.OnEditorMountListener; @@ -51,6 +52,7 @@ import java.util.Arrays; import java.util.HashSet; import java.util.Locale; +import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; @@ -219,25 +221,25 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle sa ViewGroup gutenbergContainer = view.findViewById(R.id.gutenberg_container); getGutenbergContainerFragment().attachToContainer(gutenbergContainer, new OnMediaLibraryButtonListener() { - @Override public void onMediaLibraryImageButtonClicked() { + @Override public void onMediaLibraryImageButtonClicked(boolean allowMultipleSelection) { mEditorFragmentListener.onTrackableEvent(TrackableEvent.MEDIA_BUTTON_TAPPED); - mEditorFragmentListener.onAddMediaImageClicked(); + mEditorFragmentListener.onAddMediaImageClicked(allowMultipleSelection); } @Override - public void onMediaLibraryVideoButtonClicked() { + public void onMediaLibraryVideoButtonClicked(boolean allowMultipleSelection) { mEditorFragmentListener.onTrackableEvent(TrackableEvent.MEDIA_BUTTON_TAPPED); - mEditorFragmentListener.onAddMediaVideoClicked(); + mEditorFragmentListener.onAddMediaVideoClicked(allowMultipleSelection); } @Override - public void onUploadPhotoButtonClicked() { - mEditorFragmentListener.onAddPhotoClicked(); + public void onUploadPhotoButtonClicked(boolean allowMultipleSelection) { + mEditorFragmentListener.onAddPhotoClicked(allowMultipleSelection); } @Override - public void onUploadVideoButtonClicked() { - mEditorFragmentListener.onAddVideoClicked(); + public void onUploadVideoButtonClicked(boolean allowMultipleSelection) { + mEditorFragmentListener.onAddVideoClicked(allowMultipleSelection); } @Override @@ -686,6 +688,53 @@ public void appendMediaFile(final MediaFile mediaFile, final String mediaUrl, Im } } + @Override + public void appendMediaFiles(Map mediaList) { + if (getActivity() == null) { + // appendMediaFile may be called from a background thread (example: EditPostActivity.java#L2165) and + // Activity may have already be gone. + // Ticket: https://github.com/wordpress-mobile/WordPress-Android/issues/7386 + AppLog.d(T.MEDIA, "appendMediaFiles() called but Activity is null!"); + return; + } + + ArrayList rnMediaList = new ArrayList<>(); + + // Get media URL of first of media first to check if it is network or local one. + String mediaUrl = ""; + Object[] mediaUrls = mediaList.keySet().toArray(); + if (mediaUrls != null && mediaUrls.length > 0) { + mediaUrl = (String) mediaUrls[0]; + } + + if (URLUtil.isNetworkUrl(mediaUrl)) { + for (Map.Entry mediaEntry : mediaList.entrySet()) { + rnMediaList.add( + new Media( + Integer.valueOf(mediaEntry.getValue().getMediaId()), + mediaEntry.getKey(), + mediaEntry.getValue().getMimeType() + ) + ); + } + getGutenbergContainerFragment().appendMediaFiles(rnMediaList); + } else { + for (Map.Entry mediaEntry : mediaList.entrySet()) { + rnMediaList.add( + new Media( + mediaEntry.getValue().getId(), + "file://" + mediaEntry.getKey(), + mediaEntry.getValue().getMimeType() + ) + ); + } + getGutenbergContainerFragment().appendUploadMediaFiles(rnMediaList); + for (Media media : rnMediaList) { + mUploadingMediaProgressMax.put(String.valueOf(media.getId()), 0f); + } + } + } + @Override public void appendGallery(MediaGallery mediaGallery) { } diff --git a/libs/gutenberg-mobile b/libs/gutenberg-mobile index ad9edc1d69d9..b8e8fe1f6b43 160000 --- a/libs/gutenberg-mobile +++ b/libs/gutenberg-mobile @@ -1 +1 @@ -Subproject commit ad9edc1d69d9e422a9044bd6a72b5dfed30916e4 +Subproject commit b8e8fe1f6b438b5cfcc9a9bf86d23616969eeff5