From 7f4912ae2c3efce336546fc2ef3dd8bc12cf492c Mon Sep 17 00:00:00 2001 From: Mario Zorz Date: Thu, 10 Sep 2020 14:32:49 -0300 Subject: [PATCH 01/47] added WPANdroid bridge interface OnStoryCreatorRequestListener --- .../wordpress/android/ui/posts/EditPostActivity.java | 11 +++++++++++ .../android/editor/EditorFragmentAbstract.java | 1 + .../editor/gutenberg/GutenbergContainerFragment.java | 3 +++ .../editor/gutenberg/GutenbergEditorFragment.java | 6 ++++++ libs/gutenberg-mobile | 2 +- 5 files changed, 22 insertions(+), 1 deletion(-) 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 48b48466de6c..b2a0c76c4b74 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 @@ -110,6 +110,7 @@ import org.wordpress.android.ui.ActivityId; import org.wordpress.android.ui.ActivityLauncher; import org.wordpress.android.ui.LocaleAwareActivity; +import org.wordpress.android.ui.PagePostCreationSourcesDetail; import org.wordpress.android.ui.PrivateAtCookieRefreshProgressDialog; import org.wordpress.android.ui.PrivateAtCookieRefreshProgressDialog.PrivateAtCookieProgressDialogOnDismissListener; import org.wordpress.android.ui.RequestCodes; @@ -2952,6 +2953,16 @@ public void onTrackableEvent(TrackableEvent event, Map propertie mEditorTracker.trackEditorEvent(event, mEditorFragment.getEditorName(), properties); } + @Override public void onStoryComposerLoaderRequested(int postId) { + // TODO here trigger the StoryCreator in the listener, figure out which media ids the + // story block contains, etc. + + // ActivityLauncher.addNewStoryWithMediaIdsForResult + // TODO we'll create a new ActivityLauncher method that passes the actual block content for the Story, + // after having found it and deserialized from local repository + ActivityLauncher.addNewStoryForResult(this, getSite(), PagePostCreationSourcesDetail.NO_DETAIL); + } + // FluxC events @SuppressWarnings("unused") 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 84ddb0b4dc7a..fd8403e95d40 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 @@ -206,6 +206,7 @@ public interface EditorFragmentListener { void onGutenbergEditorSetStarterPageTemplatesTooltipShown(boolean tooltipShown); boolean onGutenbergEditorRequestStarterPageTemplatesTooltipShown(); String getErrorMessageFromMedia(int mediaId); + void onStoryComposerLoaderRequested(int postId); } /** diff --git a/libs/editor/WordPressEditor/src/main/java/org/wordpress/android/editor/gutenberg/GutenbergContainerFragment.java b/libs/editor/WordPressEditor/src/main/java/org/wordpress/android/editor/gutenberg/GutenbergContainerFragment.java index 97483ba500fd..33fdf0efa525 100644 --- a/libs/editor/WordPressEditor/src/main/java/org/wordpress/android/editor/gutenberg/GutenbergContainerFragment.java +++ b/libs/editor/WordPressEditor/src/main/java/org/wordpress/android/editor/gutenberg/GutenbergContainerFragment.java @@ -25,6 +25,7 @@ import org.wordpress.mobile.WPAndroidGlue.WPAndroidGlueCode.OnMediaEditorListener; import org.wordpress.mobile.WPAndroidGlue.WPAndroidGlueCode.OnMediaLibraryButtonListener; import org.wordpress.mobile.WPAndroidGlue.WPAndroidGlueCode.OnReattachQueryListener; +import org.wordpress.mobile.WPAndroidGlue.WPAndroidGlueCode.OnStoryCreatorRequestListener; import java.util.ArrayList; @@ -62,6 +63,7 @@ public void attachToContainer(ViewGroup viewGroup, OnMediaLibraryButtonListener onGutenbergDidRequestUnsupportedBlockFallbackListener, AddMentionUtil addMentionUtil, OnStarterPageTemplatesTooltipShownEventListener onSPTTooltipShownEventListener, + OnStoryCreatorRequestListener onStoryCreatorRequestListener, boolean isDarkMode) { mWPAndroidGlueCode.attachToContainer( viewGroup, @@ -77,6 +79,7 @@ public void attachToContainer(ViewGroup viewGroup, OnMediaLibraryButtonListener onGutenbergDidRequestUnsupportedBlockFallbackListener, addMentionUtil, onSPTTooltipShownEventListener, + onStoryCreatorRequestListener, isDarkMode); } diff --git a/libs/editor/WordPressEditor/src/main/java/org/wordpress/android/editor/gutenberg/GutenbergEditorFragment.java b/libs/editor/WordPressEditor/src/main/java/org/wordpress/android/editor/gutenberg/GutenbergEditorFragment.java index 9d75659dead9..10e38c6c87e9 100644 --- a/libs/editor/WordPressEditor/src/main/java/org/wordpress/android/editor/gutenberg/GutenbergEditorFragment.java +++ b/libs/editor/WordPressEditor/src/main/java/org/wordpress/android/editor/gutenberg/GutenbergEditorFragment.java @@ -62,6 +62,7 @@ import org.wordpress.mobile.WPAndroidGlue.WPAndroidGlueCode.OnStarterPageTemplatesTooltipShownEventListener; import org.wordpress.mobile.WPAndroidGlue.WPAndroidGlueCode.OnMediaLibraryButtonListener; import org.wordpress.mobile.WPAndroidGlue.WPAndroidGlueCode.OnReattachQueryListener; +import org.wordpress.mobile.WPAndroidGlue.WPAndroidGlueCode.OnStoryCreatorRequestListener; import java.util.ArrayList; import java.util.HashMap; @@ -337,6 +338,11 @@ public boolean onRequestStarterPageTemplatesTooltipShown() { return mEditorFragmentListener.onGutenbergEditorRequestStarterPageTemplatesTooltipShown(); } }, + new OnStoryCreatorRequestListener() { + @Override public void onRequestStoryCreatorLoad(int postId) { + mEditorFragmentListener.onStoryComposerLoaderRequested(postId); + } + }, GutenbergUtils.isDarkMode(getActivity())); // request dependency injection. Do this after setting min/max dimensions diff --git a/libs/gutenberg-mobile b/libs/gutenberg-mobile index e4825b2569e2..60088f818f04 160000 --- a/libs/gutenberg-mobile +++ b/libs/gutenberg-mobile @@ -1 +1 @@ -Subproject commit e4825b2569e25b962f2527596580814a6c05ee59 +Subproject commit 60088f818f04a6a3eefca661da5220e7e2d0dbe0 From 0735ef64e1e51b6fa8834d2ae94f27cbfb435a50 Mon Sep 17 00:00:00 2001 From: Mario Zorz Date: Fri, 11 Sep 2020 17:43:48 -0300 Subject: [PATCH 02/47] adapted the gutenberg bridge usage to receive Story block's mediaFiles and clientId from Gutenberg when block editing is requested --- .../android/ui/ActivityLauncher.java | 15 ++++++++++++++ .../wordpress/android/ui/RequestCodes.java | 1 + .../android/ui/posts/EditPostActivity.java | 13 +++++++++--- .../editor/EditorFragmentAbstract.java | 2 +- .../gutenberg/GutenbergContainerFragment.java | 8 ++++++-- .../gutenberg/GutenbergEditorFragment.java | 20 ++++++++++++++----- libs/gutenberg-mobile | 2 +- 7 files changed, 49 insertions(+), 12 deletions(-) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/ActivityLauncher.java b/WordPress/src/main/java/org/wordpress/android/ui/ActivityLauncher.java index aabd6ed392c1..c24181cf873d 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/ActivityLauncher.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/ActivityLauncher.java @@ -724,6 +724,21 @@ public static void addNewStoryWithMediaUrisForResult( activity.startActivityForResult(intent, RequestCodes.CREATE_STORY); } + public static void editStoryWithMediaIdsForResult( + Activity activity, + SiteModel site, + long[] mediaIds + ) { + if (site == null) { + return; + } + + Intent intent = new Intent(activity, StoryComposerActivity.class); + intent.putExtra(WordPress.SITE, site); + intent.putExtra(MediaBrowserActivity.RESULT_IDS, mediaIds); + activity.startActivityForResult(intent, RequestCodes.EDIT_STORY); + } + public static void editPostOrPageForResult(Activity activity, SiteModel site, PostModel post) { editPostOrPageForResult(new Intent(activity, EditPostActivity.class), activity, site, post.getId(), false); } diff --git a/WordPress/src/main/java/org/wordpress/android/ui/RequestCodes.java b/WordPress/src/main/java/org/wordpress/android/ui/RequestCodes.java index f41c0a7350a4..e7b9add340e8 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/RequestCodes.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/RequestCodes.java @@ -63,4 +63,5 @@ public class RequestCodes { // Story creator public static final int CREATE_STORY = 8000; + public static final int EDIT_STORY = 8001; } 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 b2a0c76c4b74..b40bfeb75692 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 @@ -2070,7 +2070,8 @@ public Fragment getItem(int position) { isSiteUsingWpComRestApi, WordPress.getUserAgent(), mTenorFeatureConfig.isEnabled(), - gutenbergPropsBuilder + gutenbergPropsBuilder, + RequestCodes.EDIT_STORY ); } else { // If gutenberg editor is not selected, default to Aztec. @@ -2953,14 +2954,20 @@ public void onTrackableEvent(TrackableEvent event, Map propertie mEditorTracker.trackEditorEvent(event, mEditorFragment.getEditorName(), properties); } - @Override public void onStoryComposerLoaderRequested(int postId) { + @Override public void onStoryComposerLoadRequested(ArrayList mediaFiles, String blockId) { // TODO here trigger the StoryCreator in the listener, figure out which media ids the // story block contains, etc. // ActivityLauncher.addNewStoryWithMediaIdsForResult // TODO we'll create a new ActivityLauncher method that passes the actual block content for the Story, // after having found it and deserialized from local repository - ActivityLauncher.addNewStoryForResult(this, getSite(), PagePostCreationSourcesDetail.NO_DETAIL); + + ArrayList tmpMediaIds = new ArrayList<>(); + for (Object mediaFile : mediaFiles) { + long mediaId = new Double(((HashMap)mediaFile).get("id").toString()).longValue(); + tmpMediaIds.add(mediaId); + } + ActivityLauncher.editStoryWithMediaIdsForResult(this, getSite(), ListUtils.toLongArray(tmpMediaIds)); } // FluxC events 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 fd8403e95d40..b023d94911ee 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 @@ -206,7 +206,7 @@ public interface EditorFragmentListener { void onGutenbergEditorSetStarterPageTemplatesTooltipShown(boolean tooltipShown); boolean onGutenbergEditorRequestStarterPageTemplatesTooltipShown(); String getErrorMessageFromMedia(int mediaId); - void onStoryComposerLoaderRequested(int postId); + void onStoryComposerLoadRequested(ArrayList mediaFiles, String blockId); } /** diff --git a/libs/editor/WordPressEditor/src/main/java/org/wordpress/android/editor/gutenberg/GutenbergContainerFragment.java b/libs/editor/WordPressEditor/src/main/java/org/wordpress/android/editor/gutenberg/GutenbergContainerFragment.java index 33fdf0efa525..03f7d66228d9 100644 --- a/libs/editor/WordPressEditor/src/main/java/org/wordpress/android/editor/gutenberg/GutenbergContainerFragment.java +++ b/libs/editor/WordPressEditor/src/main/java/org/wordpress/android/editor/gutenberg/GutenbergContainerFragment.java @@ -25,7 +25,7 @@ import org.wordpress.mobile.WPAndroidGlue.WPAndroidGlueCode.OnMediaEditorListener; import org.wordpress.mobile.WPAndroidGlue.WPAndroidGlueCode.OnMediaLibraryButtonListener; import org.wordpress.mobile.WPAndroidGlue.WPAndroidGlueCode.OnReattachQueryListener; -import org.wordpress.mobile.WPAndroidGlue.WPAndroidGlueCode.OnStoryCreatorRequestListener; +import org.wordpress.mobile.WPAndroidGlue.WPAndroidGlueCode.OnStoryCreatorLoadRequestListener; import java.util.ArrayList; @@ -63,7 +63,7 @@ public void attachToContainer(ViewGroup viewGroup, OnMediaLibraryButtonListener onGutenbergDidRequestUnsupportedBlockFallbackListener, AddMentionUtil addMentionUtil, OnStarterPageTemplatesTooltipShownEventListener onSPTTooltipShownEventListener, - OnStoryCreatorRequestListener onStoryCreatorRequestListener, + OnStoryCreatorLoadRequestListener onStoryCreatorRequestListener, boolean isDarkMode) { mWPAndroidGlueCode.attachToContainer( viewGroup, @@ -205,6 +205,10 @@ public void replaceUnsupportedBlock(String content, String blockId) { mWPAndroidGlueCode.replaceUnsupportedBlock(content, blockId); } + public void replaceStoryEditedBlock(String mediaFiles, String blockId) { + mWPAndroidGlueCode.replaceStoryEditedBlock(mediaFiles, blockId); + } + public void updateTheme(Bundle editorTheme) { mWPAndroidGlueCode.updateTheme(editorTheme); } diff --git a/libs/editor/WordPressEditor/src/main/java/org/wordpress/android/editor/gutenberg/GutenbergEditorFragment.java b/libs/editor/WordPressEditor/src/main/java/org/wordpress/android/editor/gutenberg/GutenbergEditorFragment.java index 10e38c6c87e9..1051bbc35ce5 100644 --- a/libs/editor/WordPressEditor/src/main/java/org/wordpress/android/editor/gutenberg/GutenbergEditorFragment.java +++ b/libs/editor/WordPressEditor/src/main/java/org/wordpress/android/editor/gutenberg/GutenbergEditorFragment.java @@ -62,7 +62,7 @@ import org.wordpress.mobile.WPAndroidGlue.WPAndroidGlueCode.OnStarterPageTemplatesTooltipShownEventListener; import org.wordpress.mobile.WPAndroidGlue.WPAndroidGlueCode.OnMediaLibraryButtonListener; import org.wordpress.mobile.WPAndroidGlue.WPAndroidGlueCode.OnReattachQueryListener; -import org.wordpress.mobile.WPAndroidGlue.WPAndroidGlueCode.OnStoryCreatorRequestListener; +import org.wordpress.mobile.WPAndroidGlue.WPAndroidGlueCode.OnStoryCreatorLoadRequestListener; import java.util.ArrayList; import java.util.HashMap; @@ -91,6 +91,7 @@ public class GutenbergEditorFragment extends EditorFragmentAbstract implements private static final String ARG_SITE_USER_AGENT = "param_user_agent"; private static final String ARG_TENOR_ENABLED = "param_tenor_enabled"; private static final String ARG_GUTENBERG_PROPS_BUILDER = "param_gutenberg_props_builder"; + private static final String ARG_STORY_EDITOR_REQUEST_CODE = "param_sory_editor_request_code"; private static final int CAPTURE_PHOTO_PERMISSION_REQUEST_CODE = 101; private static final int CAPTURE_VIDEO_PERMISSION_REQUEST_CODE = 102; @@ -108,6 +109,7 @@ public class GutenbergEditorFragment extends EditorFragmentAbstract implements private Runnable mInvalidateOptionsRunnable; private LiveTextWatcher mTextWatcher = new LiveTextWatcher(); + private int mStoryBlockEditRequestCode; // pointer (to the Gutenberg container fragment) that outlives this fragment's Android lifecycle. The retained // fragment can be alive and accessible even before it gets attached to an activity. @@ -135,7 +137,8 @@ public static GutenbergEditorFragment newInstance(String title, boolean isSiteUsingWpComRestApi, String userAgent, boolean tenorEnabled, - GutenbergPropsBuilder gutenbergPropsBuilder) { + GutenbergPropsBuilder gutenbergPropsBuilder, + int storyBlockEditRequestCode) { GutenbergEditorFragment fragment = new GutenbergEditorFragment(); Bundle args = new Bundle(); args.putString(ARG_PARAM_TITLE, title); @@ -151,6 +154,7 @@ public static GutenbergEditorFragment newInstance(String title, args.putString(ARG_SITE_USER_AGENT, userAgent); args.putBoolean(ARG_TENOR_ENABLED, tenorEnabled); args.putParcelable(ARG_GUTENBERG_PROPS_BUILDER, gutenbergPropsBuilder); + args.putInt(ARG_STORY_EDITOR_REQUEST_CODE, storyBlockEditRequestCode); fragment.setArguments(args); return fragment; } @@ -197,6 +201,7 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle sa if (getArguments() != null) { mIsNewPost = getArguments().getBoolean(ARG_IS_NEW_POST); + mStoryBlockEditRequestCode = getArguments().getInt(ARG_STORY_EDITOR_REQUEST_CODE); } ViewGroup gutenbergContainer = view.findViewById(R.id.gutenberg_container); @@ -338,9 +343,9 @@ public boolean onRequestStarterPageTemplatesTooltipShown() { return mEditorFragmentListener.onGutenbergEditorRequestStarterPageTemplatesTooltipShown(); } }, - new OnStoryCreatorRequestListener() { - @Override public void onRequestStoryCreatorLoad(int postId) { - mEditorFragmentListener.onStoryComposerLoaderRequested(postId); + new OnStoryCreatorLoadRequestListener() { + @Override public void onRequestStoryCreatorLoad(ArrayList mediaFiles, String blockId) { + mEditorFragmentListener.onStoryComposerLoadRequested(mediaFiles, blockId); } }, GutenbergUtils.isDarkMode(getActivity())); @@ -425,6 +430,11 @@ public void onActivityResult(int requestCode, int resultCode, @Nullable Intent d } else { trackWebViewClosed("dismiss"); } + } else if (requestCode == mStoryBlockEditRequestCode) { + // handle edited block content + String blockId = data.getStringExtra(WPGutenbergWebViewActivity.ARG_BLOCK_ID); + String content = data.getStringExtra(WPGutenbergWebViewActivity.ARG_BLOCK_CONTENT); + getGutenbergContainerFragment().replaceStoryEditedBlock(content, blockId); } } diff --git a/libs/gutenberg-mobile b/libs/gutenberg-mobile index 60088f818f04..4def44feb730 160000 --- a/libs/gutenberg-mobile +++ b/libs/gutenberg-mobile @@ -1 +1 @@ -Subproject commit 60088f818f04a6a3eefca661da5220e7e2d0dbe0 +Subproject commit 4def44feb7302a36eda62f6a459e51d08449750c From ae9a0c3cf617d2da1ded3c2daf93e924654f7738 Mon Sep 17 00:00:00 2001 From: Mario Zorz Date: Tue, 15 Sep 2020 08:25:51 -0300 Subject: [PATCH 03/47] introducing StoriesPrefs to save serialized Stories slides so these can be retrieved and edited later Gutenberg --- .../android/ui/posts/EditPostActivity.java | 16 +++- .../media/AddLocalMediaToPostUseCase.kt | 8 ++ .../posts/editor/media/EditorMediaListener.kt | 3 + .../stories/SaveStoryGutenbergBlockUseCase.kt | 25 +++++ .../ui/stories/StoryComposerActivity.kt | 5 + .../media/StoryMediaSaveUploadBridge.kt | 27 ++++++ .../android/ui/stories/prefs/StoriesPrefs.kt | 96 +++++++++++++++++++ libs/stories-android | 2 +- 8 files changed, 179 insertions(+), 3 deletions(-) create mode 100644 WordPress/src/main/java/org/wordpress/android/ui/stories/prefs/StoriesPrefs.kt 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 b40bfeb75692..0fc0c12b7135 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 @@ -110,7 +110,6 @@ import org.wordpress.android.ui.ActivityId; import org.wordpress.android.ui.ActivityLauncher; import org.wordpress.android.ui.LocaleAwareActivity; -import org.wordpress.android.ui.PagePostCreationSourcesDetail; import org.wordpress.android.ui.PrivateAtCookieRefreshProgressDialog; import org.wordpress.android.ui.PrivateAtCookieRefreshProgressDialog.PrivateAtCookieProgressDialogOnDismissListener; import org.wordpress.android.ui.RequestCodes; @@ -157,6 +156,7 @@ import org.wordpress.android.ui.prefs.AppPrefs; import org.wordpress.android.ui.reader.utils.ReaderUtilsWrapper; import org.wordpress.android.ui.stockmedia.StockMediaPickerActivity; +import org.wordpress.android.ui.stories.prefs.StoriesPrefs; import org.wordpress.android.ui.uploads.PostEvents; import org.wordpress.android.ui.uploads.UploadService; import org.wordpress.android.ui.uploads.UploadUtils; @@ -2963,10 +2963,17 @@ public void onTrackableEvent(TrackableEvent event, Map propertie // after having found it and deserialized from local repository ArrayList tmpMediaIds = new ArrayList<>(); + boolean allStorySlidesAreEditable = true; for (Object mediaFile : mediaFiles) { - long mediaId = new Double(((HashMap)mediaFile).get("id").toString()).longValue(); + long mediaId = new Double(((HashMap) mediaFile).get("id").toString()).longValue(); + if (allStorySlidesAreEditable + && !StoriesPrefs.isValidSlide(this, getImmutablePost().getLocalSiteId(), mediaId)) { + // flag this as soon as we find one media item not being really editable + allStorySlidesAreEditable = false; + } tmpMediaIds.add(mediaId); } + // TODO pass the allStorySlidesAreEditable boolean flag make sure to show the warning dialog ActivityLauncher.editStoryWithMediaIdsForResult(this, getSite(), ListUtils.toLongArray(tmpMediaIds)); } @@ -3196,6 +3203,11 @@ public void syncPostObjectWithUiAndSaveIt(@Nullable OnPostUpdatedFromUIListener WPMediaUtils.advertiseImageOptimization(this, listener::invoke); } + @Override + public void onMediaModelsCreatedFromOptimizedUris(@NotNull Map oldUriToMediaModels) { + // no op - we're not doing any special handling on MediaModels in EditPostActivity + } + @Override public Consumer getExceptionLogger() { return (Exception e) -> AppLog.e(T.EDITOR, e); diff --git a/WordPress/src/main/java/org/wordpress/android/ui/posts/editor/media/AddLocalMediaToPostUseCase.kt b/WordPress/src/main/java/org/wordpress/android/ui/posts/editor/media/AddLocalMediaToPostUseCase.kt index 06129dd43457..ccb97c3de05c 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/posts/editor/media/AddLocalMediaToPostUseCase.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/posts/editor/media/AddLocalMediaToPostUseCase.kt @@ -67,6 +67,14 @@ class AddLocalMediaToPostUseCase @Inject constructor( optimizeMediaResult.optimizedMediaUris ) + // here we pass a map of "old" (before optimisation) Uris to the new MediaModels which contain + // both the mediaModel ids and the optimized media URLs. + // this way, the listener will be able to process from other models potining to the old URLs + // and make any needed updates + editorMediaListener.onMediaModelsCreatedFromOptimizedUris( + uriList.zip(createMediaModelsResult.mediaModels).toMap() + ) + // Add media to editor and optionally initiate upload addToEditorAndOptionallyUpload(createMediaModelsResult.mediaModels, editorMediaListener, doUploadAfterAdding) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/posts/editor/media/EditorMediaListener.kt b/WordPress/src/main/java/org/wordpress/android/ui/posts/editor/media/EditorMediaListener.kt index bb8254f726d7..e9fadf48d011 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/posts/editor/media/EditorMediaListener.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/posts/editor/media/EditorMediaListener.kt @@ -1,5 +1,7 @@ package org.wordpress.android.ui.posts.editor.media +import android.net.Uri +import org.wordpress.android.fluxc.model.MediaModel import org.wordpress.android.fluxc.model.PostImmutableModel import org.wordpress.android.ui.posts.EditPostActivity.OnPostUpdatedFromUIListener import org.wordpress.android.util.helpers.MediaFile @@ -8,5 +10,6 @@ interface EditorMediaListener { fun appendMediaFiles(mediaFiles: Map) fun syncPostObjectWithUiAndSaveIt(listener: OnPostUpdatedFromUIListener? = null) fun advertiseImageOptimization(listener: () -> Unit) + fun onMediaModelsCreatedFromOptimizedUris(oldUriToMediaFiles: Map) fun getImmutablePost(): PostImmutableModel } diff --git a/WordPress/src/main/java/org/wordpress/android/ui/stories/SaveStoryGutenbergBlockUseCase.kt b/WordPress/src/main/java/org/wordpress/android/ui/stories/SaveStoryGutenbergBlockUseCase.kt index beb2107a6659..64d8976bb0ef 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/stories/SaveStoryGutenbergBlockUseCase.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/stories/SaveStoryGutenbergBlockUseCase.kt @@ -1,8 +1,10 @@ package org.wordpress.android.ui.stories import com.google.gson.Gson +import org.wordpress.android.WordPress import org.wordpress.android.fluxc.model.PostModel import org.wordpress.android.ui.posts.EditPostRepository +import org.wordpress.android.ui.stories.prefs.StoriesPrefs import org.wordpress.android.util.StringUtils import org.wordpress.android.util.helpers.MediaFile import javax.inject.Inject @@ -57,6 +59,29 @@ class SaveStoryGutenbergBlockUseCase @Inject constructor() { id = mediaFile.mediaId.toInt() link = mediaFile.fileURL url = mediaFile.fileURL + + // look for the slide saved with the local id key (mediaFile.id), and re-convert to mediaId. + val localIdKey = mediaFile.id.toLong() + val remoteIdKey = mediaFile.mediaId.toLong() + val localSiteId = post.localSiteId.toLong() + StoriesPrefs.getSlide( + WordPress.getContext(), + localSiteId, + localIdKey + )?.let { + StoriesPrefs.saveSlide( + WordPress.getContext(), + localSiteId, + remoteIdKey, // use the new mediaId as key + it + ) + // now delete the old entry + StoriesPrefs.deleteSlide( + WordPress.getContext(), + localSiteId, + localIdKey + ) + } } post.setContent(createGBStoryBlockStringFromJson(requireNotNull(storyBlockData))) } diff --git a/WordPress/src/main/java/org/wordpress/android/ui/stories/StoryComposerActivity.kt b/WordPress/src/main/java/org/wordpress/android/ui/stories/StoryComposerActivity.kt index 1feab1a932e4..c1881f931c31 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/stories/StoryComposerActivity.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/stories/StoryComposerActivity.kt @@ -26,6 +26,7 @@ import org.wordpress.android.WordPress import org.wordpress.android.analytics.AnalyticsTracker.Stat import org.wordpress.android.analytics.AnalyticsTracker.Stat.PREPUBLISHING_BOTTOM_SHEET_OPENED import org.wordpress.android.fluxc.model.LocalOrRemoteId.LocalId +import org.wordpress.android.fluxc.model.MediaModel import org.wordpress.android.fluxc.model.PostImmutableModel import org.wordpress.android.fluxc.model.SiteModel import org.wordpress.android.fluxc.store.PostStore @@ -343,6 +344,10 @@ class StoryComposerActivity : ComposeLoopFrameActivity(), WPMediaUtils.advertiseImageOptimization(this) { listener.invoke() } } + override fun onMediaModelsCreatedFromOptimizedUris(oldUriToMediaFiles: Map) { + // no op - we're not doing any special handling while composing, only when saving in the UploadBridge + } + private fun updateAddingMediaToStoryComposerProgressDialogState(uiState: ProgressDialogUiState) { addingMediaToEditorProgressDialog = progressDialogHelper .updateProgressDialogState(this, addingMediaToEditorProgressDialog, uiState, uiHelpers) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/stories/media/StoryMediaSaveUploadBridge.kt b/WordPress/src/main/java/org/wordpress/android/ui/stories/media/StoryMediaSaveUploadBridge.kt index 1b16d1c9dc9b..b3c65c86c1f7 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/stories/media/StoryMediaSaveUploadBridge.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/stories/media/StoryMediaSaveUploadBridge.kt @@ -16,6 +16,7 @@ import kotlinx.coroutines.launch import org.greenrobot.eventbus.Subscribe import org.greenrobot.eventbus.ThreadMode.MAIN import org.wordpress.android.WordPress +import org.wordpress.android.fluxc.model.MediaModel import org.wordpress.android.fluxc.model.PostImmutableModel import org.wordpress.android.fluxc.model.SiteModel import org.wordpress.android.modules.UI_THREAD @@ -28,6 +29,7 @@ import org.wordpress.android.ui.posts.editor.media.EditorMediaListener import org.wordpress.android.ui.stories.SaveStoryGutenbergBlockUseCase import org.wordpress.android.ui.stories.StoriesTrackerHelper import org.wordpress.android.ui.stories.StoryComposerActivity +import org.wordpress.android.ui.stories.prefs.StoriesPrefs import org.wordpress.android.ui.uploads.UploadServiceFacade import org.wordpress.android.util.EventBusWrapper import org.wordpress.android.util.NetworkUtilsWrapper @@ -170,4 +172,29 @@ class StoryMediaSaveUploadBridge @Inject constructor( override fun advertiseImageOptimization(listener: () -> Unit) { // no op } + + override fun onMediaModelsCreatedFromOptimizedUris(oldUriToMediaFiles: Map) { + // in order to support Story editing capabilities, we save a serialized version of the Story slides + // after their composedFrameFiles have been processed. + + // here we change the ids on the actual StoryFrameItems, and also update the flattened / composed image + // urls with the new URLs which may have been replaced after image optimization + for (story in StoryRepository.getImmutableStories()) { + // find the MediaModel for a given Uri from composedFrameFile + for (frame in story.frames) { + // if the old URI in frame.composedFrameFile exists as a key in the passed map, then update that + // value with the new (probably optimized) URL and also keep track of the new id. + val mediaModel = oldUriToMediaFiles.get(Uri.fromFile(frame.composedFrameFile)) + mediaModel?.let { + StoriesPrefs.saveSlide( + appContext, + mediaModel.localSiteId.toLong(), + mediaModel.id.toLong(), // use the local id to save the original, will be replaced later + // with mediaModel.mediaId after uploading to the remote site + frame + ) + } + } + } + } } diff --git a/WordPress/src/main/java/org/wordpress/android/ui/stories/prefs/StoriesPrefs.kt b/WordPress/src/main/java/org/wordpress/android/ui/stories/prefs/StoriesPrefs.kt new file mode 100644 index 000000000000..ae0c94f55187 --- /dev/null +++ b/WordPress/src/main/java/org/wordpress/android/ui/stories/prefs/StoriesPrefs.kt @@ -0,0 +1,96 @@ +package org.wordpress.android.ui.stories.prefs + +import android.content.Context +import android.net.Uri +import androidx.preference.PreferenceManager +import com.wordpress.stories.compose.story.StoryFrameItem +import com.wordpress.stories.compose.story.StoryFrameItem.BackgroundSource.FileBackgroundSource +import com.wordpress.stories.compose.story.StoryFrameItem.BackgroundSource.UriBackgroundSource +import com.wordpress.stories.compose.story.StorySerializerUtils + +object StoriesPrefs { + private const val KEY_PREFIX_STORIES_SLIDE_ID = "story_slide_id-" + + private fun buildSlideKey(siteId: Long, mediaId: Long): String { + return KEY_PREFIX_STORIES_SLIDE_ID + siteId.toString() + "-" + mediaId.toString() + } + + private fun checkSlideIdExists(context: Context, siteId: Long, mediaId: Long): Boolean { + val slideIdKey = buildSlideKey(siteId, mediaId) + return PreferenceManager.getDefaultSharedPreferences(context).contains(slideIdKey) + } + + private fun checkSlideOriginalBackgroundMediaExists(context: Context, siteId: Long, mediaId: Long): Boolean { + val storyFrameItem: StoryFrameItem? = getSlide(context, siteId, mediaId) + storyFrameItem?.let { frame -> + // now check the background media exists or is accessible on this device + frame.source.let { source -> + if (source is FileBackgroundSource) { + source.file?.let { + return it.exists() + } ?: return false + } else if (source is UriBackgroundSource) { + source.contentUri?.let { + return isUriAccessible(it, context) + } ?: return false + } + } + } + return false + } + + private fun isUriAccessible(uri: Uri, context: Context): Boolean { + if (uri.toString().startsWith("http")) { + // TODO: assume it'll be accessible - we'll figure out later + // potentially force external download using MediaUtils.downloadExternalMedia() here to ensure + return true + } + try { + val inputStream = context.contentResolver.openInputStream(uri) + if (inputStream != null) { + inputStream.close() + return true + } + } catch (e: java.lang.Exception) { + e.printStackTrace() + } + return false + } + + private fun saveSlide(context: Context, slideIdKey: String, storySlideJson: String) { + val editor = PreferenceManager.getDefaultSharedPreferences(context).edit() + editor.putString(slideIdKey, storySlideJson) + editor.apply() + } + + @JvmStatic + fun isValidSlide(context: Context, siteId: Long, mediaId: Long): Boolean { + return checkSlideIdExists(context, siteId, mediaId) && + checkSlideOriginalBackgroundMediaExists(context, siteId, mediaId) + } + + private fun getSlideJson(context: Context, siteId: Long, mediaId: Long): String? { + val slideIdKey = buildSlideKey(siteId, mediaId) + return PreferenceManager.getDefaultSharedPreferences(context).getString(slideIdKey, null) + } + + fun getSlide(context: Context, siteId: Long, mediaId: Long): StoryFrameItem? { + val jsonSlide = getSlideJson(context, siteId, mediaId) + jsonSlide?.let { + return StorySerializerUtils.deserializeStoryFrameItem(jsonSlide) + } ?: return null + } + + fun saveSlide(context: Context, siteId: Long, mediaId: Long, storyFrameItem: StoryFrameItem) { + val slideIdKey = buildSlideKey(siteId, mediaId) +// storyFrameItem.id = slideIdKey + saveSlide(context, slideIdKey, StorySerializerUtils.serializeStoryFrameItem(storyFrameItem)) + } + + fun deleteSlide(context: Context, siteId: Long, mediaId: Long) { + val slideIdKey = buildSlideKey(siteId, mediaId) + val editor = PreferenceManager.getDefaultSharedPreferences(context).edit() + editor.remove(slideIdKey) + editor.apply() + } +} diff --git a/libs/stories-android b/libs/stories-android index 03a40da7be5a..c9ffa96cb84a 160000 --- a/libs/stories-android +++ b/libs/stories-android @@ -1 +1 @@ -Subproject commit 03a40da7be5ab7da8fce7c4895227c193e04b9b9 +Subproject commit c9ffa96cb84ae77b43f32ca87d77d2909d78f201 From 3298f038c0e6e6e0cf330db48b223bed244a8909 Mon Sep 17 00:00:00 2001 From: Mario Zorz Date: Tue, 15 Sep 2020 15:56:22 -0300 Subject: [PATCH 04/47] added logic in onStoryComposerLoadRequested() to load a Story in the composer after tapping on a Story block for editing --- WordPress/src/main/AndroidManifest.xml | 1 + .../android/ui/ActivityLauncher.java | 26 ++++++++- .../android/ui/posts/EditPostActivity.java | 55 ++++++++++++++----- .../stories/SaveStoryGutenbergBlockUseCase.kt | 1 + .../ui/stories/StoryComposerActivity.kt | 11 +++- .../ui/stories/StoryComposerViewModel.kt | 8 ++- .../media/StoryMediaSaveUploadBridge.kt | 5 +- .../android/ui/stories/prefs/StoriesPrefs.kt | 8 ++- libs/stories-android | 2 +- 9 files changed, 93 insertions(+), 24 deletions(-) diff --git a/WordPress/src/main/AndroidManifest.xml b/WordPress/src/main/AndroidManifest.xml index 1e800e40ad02..c9eba1929991 100644 --- a/WordPress/src/main/AndroidManifest.xml +++ b/WordPress/src/main/AndroidManifest.xml @@ -227,6 +227,7 @@ android:name=".ui.stories.StoryComposerActivity" android:label="@string/app_name" android:screenOrientation="portrait" + android:launchMode="singleTop" android:theme="@style/WordPress.Stories.Immersive"> diff --git a/WordPress/src/main/java/org/wordpress/android/ui/ActivityLauncher.java b/WordPress/src/main/java/org/wordpress/android/ui/ActivityLauncher.java index 5e4a0ac6225f..725acb28a390 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/ActivityLauncher.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/ActivityLauncher.java @@ -13,6 +13,8 @@ import androidx.core.app.TaskStackBuilder; import androidx.fragment.app.Fragment; +import com.wordpress.stories.compose.ComposeLoopFrameActivity; + import org.wordpress.android.R; import org.wordpress.android.WordPress; import org.wordpress.android.analytics.AnalyticsTracker; @@ -93,6 +95,7 @@ import java.util.List; import java.util.Map; +import static com.wordpress.stories.util.BundleUtilsKt.KEY_STORY_INDEX; import static org.wordpress.android.analytics.AnalyticsTracker.ACTIVITY_LOG_ACTIVITY_ID_KEY; import static org.wordpress.android.analytics.AnalyticsTracker.Stat.POST_LIST_ACCESS_ERROR; import static org.wordpress.android.analytics.AnalyticsTracker.Stat.READER_ARTICLE_DETAIL_REBLOGGED; @@ -102,6 +105,7 @@ import static org.wordpress.android.login.LoginMode.WPCOM_LOGIN_ONLY; import static org.wordpress.android.ui.media.MediaBrowserActivity.ARG_BROWSER_TYPE; import static org.wordpress.android.ui.pages.PagesActivityKt.EXTRA_PAGE_REMOTE_ID_KEY; +import static org.wordpress.android.ui.stories.StoryComposerActivity.KEY_LAUNCHED_FROM_GUTENBERG; import static org.wordpress.android.viewmodel.activitylog.ActivityLogDetailViewModelKt.ACTIVITY_LOG_ID_KEY; public class ActivityLauncher { @@ -723,7 +727,8 @@ public static void addNewStoryWithMediaUrisForResult( public static void editStoryWithMediaIdsForResult( Activity activity, SiteModel site, - long[] mediaIds + long[] mediaIds, + boolean launchingFromGutenberg ) { if (site == null) { return; @@ -735,6 +740,25 @@ public static void editStoryWithMediaIdsForResult( activity.startActivityForResult(intent, RequestCodes.EDIT_STORY); } + public static void editStoryForResult( + Activity activity, + SiteModel site, + int storyIndex, + boolean allStorySlidesAreEditable, + boolean launchedFromGutenberg + ) { + if (site == null) { + return; + } + + // TODO pass the allStorySlidesAreEditable boolean flag make sure to show the warning dialog + Intent intent = new Intent(activity, StoryComposerActivity.class); + intent.putExtra(WordPress.SITE, site); + intent.putExtra(KEY_STORY_INDEX, storyIndex); + intent.putExtra(KEY_LAUNCHED_FROM_GUTENBERG, launchedFromGutenberg); + activity.startActivityForResult(intent, RequestCodes.EDIT_STORY); + } + public static void editPostOrPageForResult(Activity activity, SiteModel site, PostModel post) { editPostOrPageForResult(new Intent(activity, EditPostActivity.class), activity, site, post.getId(), 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 efbe227936a6..58aabbe17e88 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 @@ -40,6 +40,8 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder; import com.google.android.material.snackbar.Snackbar; +import com.wordpress.stories.compose.story.StoryFrameItem; +import com.wordpress.stories.compose.story.StoryRepository; import org.greenrobot.eventbus.EventBus; import org.greenrobot.eventbus.Subscribe; @@ -2999,26 +3001,53 @@ public void onTrackableEvent(TrackableEvent event, Map propertie } @Override public void onStoryComposerLoadRequested(ArrayList mediaFiles, String blockId) { - // TODO here trigger the StoryCreator in the listener, figure out which media ids the - // story block contains, etc. - - // ActivityLauncher.addNewStoryWithMediaIdsForResult - // TODO we'll create a new ActivityLauncher method that passes the actual block content for the Story, - // after having found it and deserialized from local repository - - ArrayList tmpMediaIds = new ArrayList<>(); + ArrayList tmpMediaIdsLong = new ArrayList<>(); + ArrayList tmpMediaIdsString = new ArrayList<>(); boolean allStorySlidesAreEditable = true; for (Object mediaFile : mediaFiles) { - long mediaId = new Double(((HashMap) mediaFile).get("id").toString()).longValue(); + long mediaIdLong = new Double(((HashMap) mediaFile).get("id").toString()).longValue(); + String mediaIdString = String.valueOf(mediaIdLong); // convert back after stripping the decimals in original Double if (allStorySlidesAreEditable - && !StoriesPrefs.isValidSlide(this, getImmutablePost().getLocalSiteId(), mediaId)) { + && !StoriesPrefs.isValidSlide(this, mSite.getSiteId(), mediaIdLong)) { // flag this as soon as we find one media item not being really editable allStorySlidesAreEditable = false; } - tmpMediaIds.add(mediaId); + tmpMediaIdsLong.add(mediaIdLong); + tmpMediaIdsString.add(mediaIdString); + } + + // now look for a Story in the StoryRepository that has all these frames and, if not found, let's + // just build the Story object ourselves to keep these files arrangement + int storyIndex = StoryRepository.findStoryContainingStoryFrameItemsByIds(tmpMediaIdsString); + if (storyIndex == StoryRepository.DEFAULT_NONE_SELECTED) { + // the StoryRepository didn' have it but we have editable serialized slides so, + // create a new Story from scratch with these deserialized StoryFrameItems + StoryRepository.loadStory(storyIndex); + storyIndex = StoryRepository.currentStoryIndex; + for (String mediaId : tmpMediaIdsString) { + StoryFrameItem storyFrameItem = StoriesPrefs.getSlide(this, mSite.getSiteId(), Long.parseLong(mediaId)); + if (storyFrameItem != null) { + StoryRepository.addStoryFrameItemToCurrentStory(storyFrameItem); + } else { + allStorySlidesAreEditable = false; + // create a new frame using the actual uploaded flattened media as a background + List mediaModelList = mMediaStore.getSiteMediaWithIds(mSite, tmpMediaIdsLong); + for (MediaModel mediaModel : mediaModelList) { + storyFrameItem = StoryFrameItem.Companion.getNewStoryFrameItemFromUri( + Uri.parse(mediaModel.getUrl()), + mediaModel.isVideo() + ); + storyFrameItem.setId(String.valueOf(mediaModel.getMediaId())); + StoryRepository.addStoryFrameItemToCurrentStory(storyFrameItem); + } + } + // Story instance re-created! Load it + ActivityLauncher.editStoryForResult(this, mSite, storyIndex, allStorySlidesAreEditable, true); + } + } else { + // Story found! Load it + ActivityLauncher.editStoryForResult(this, mSite, storyIndex, allStorySlidesAreEditable, true); } - // TODO pass the allStorySlidesAreEditable boolean flag make sure to show the warning dialog - ActivityLauncher.editStoryWithMediaIdsForResult(this, getSite(), ListUtils.toLongArray(tmpMediaIds)); } // FluxC events diff --git a/WordPress/src/main/java/org/wordpress/android/ui/stories/SaveStoryGutenbergBlockUseCase.kt b/WordPress/src/main/java/org/wordpress/android/ui/stories/SaveStoryGutenbergBlockUseCase.kt index 64d8976bb0ef..6ff3b070d04e 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/stories/SaveStoryGutenbergBlockUseCase.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/stories/SaveStoryGutenbergBlockUseCase.kt @@ -69,6 +69,7 @@ class SaveStoryGutenbergBlockUseCase @Inject constructor() { localSiteId, localIdKey )?.let { + it.id = mediaFile.mediaId // update the StoryFrameItem id to hold the same value as the remote mediaID StoriesPrefs.saveSlide( WordPress.getContext(), localSiteId, diff --git a/WordPress/src/main/java/org/wordpress/android/ui/stories/StoryComposerActivity.kt b/WordPress/src/main/java/org/wordpress/android/ui/stories/StoryComposerActivity.kt index a418eae79202..2201e797d251 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/stories/StoryComposerActivity.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/stories/StoryComposerActivity.kt @@ -1,5 +1,6 @@ package org.wordpress.android.ui.stories +import android.app.Activity import android.app.PendingIntent import android.app.ProgressDialog import android.content.Intent @@ -26,6 +27,7 @@ import org.wordpress.android.R.id import org.wordpress.android.WordPress import org.wordpress.android.analytics.AnalyticsTracker.Stat import org.wordpress.android.analytics.AnalyticsTracker.Stat.PREPUBLISHING_BOTTOM_SHEET_OPENED +import org.wordpress.android.editor.EditorImageMetaData import org.wordpress.android.fluxc.model.LocalOrRemoteId.LocalId import org.wordpress.android.fluxc.model.MediaModel import org.wordpress.android.fluxc.model.PostImmutableModel @@ -101,6 +103,7 @@ class StoryComposerActivity : ComposeLoopFrameActivity(), const val STATE_KEY_POST_LOCAL_ID = "state_key_post_model_local_id" const val STATE_KEY_EDITOR_SESSION_DATA = "stateKeyEditorSessionData" const val KEY_POST_LOCAL_ID = "key_post_model_local_id" + const val KEY_LAUNCHED_FROM_GUTENBERG = "key_launched_from_gutenberg" const val UNUSED_KEY = "unused_key" const val BASE_FRAME_MEDIA_ERROR_NOTIFICATION_ID: Int = 72300 } @@ -396,7 +399,13 @@ class StoryComposerActivity : ComposeLoopFrameActivity(), } override fun onStoryDiscarded() { - viewModel.onStoryDiscarded() + val launchedFromGutenberg = intent.getBooleanExtra(KEY_LAUNCHED_FROM_GUTENBERG, false) + viewModel.onStoryDiscarded(!launchedFromGutenberg) + + if (launchedFromGutenberg) { + setResult(Activity.RESULT_CANCELED) + finish() + } } private fun openPrepublishingBottomSheet() { diff --git a/WordPress/src/main/java/org/wordpress/android/ui/stories/StoryComposerViewModel.kt b/WordPress/src/main/java/org/wordpress/android/ui/stories/StoryComposerViewModel.kt index eefab234efb8..0e79012021b3 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/stories/StoryComposerViewModel.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/stories/StoryComposerViewModel.kt @@ -112,9 +112,11 @@ class StoryComposerViewModel @Inject constructor( outState.putSerializable(StoryComposerActivity.STATE_KEY_EDITOR_SESSION_DATA, postEditorAnalyticsSession) } - fun onStoryDiscarded() { - // delete empty post from database - dispatcher.dispatch(PostActionBuilder.newRemovePostAction(editPostRepository.getEditablePost())) + fun onStoryDiscarded(deleteDiscardedPost: Boolean) { + if (deleteDiscardedPost) { + // delete empty post from database + dispatcher.dispatch(PostActionBuilder.newRemovePostAction(editPostRepository.getEditablePost())) + } postEditorAnalyticsSession.setOutcome(CANCEL) } diff --git a/WordPress/src/main/java/org/wordpress/android/ui/stories/media/StoryMediaSaveUploadBridge.kt b/WordPress/src/main/java/org/wordpress/android/ui/stories/media/StoryMediaSaveUploadBridge.kt index b3c65c86c1f7..a1829d2daf96 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/stories/media/StoryMediaSaveUploadBridge.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/stories/media/StoryMediaSaveUploadBridge.kt @@ -186,10 +186,11 @@ class StoryMediaSaveUploadBridge @Inject constructor( // value with the new (probably optimized) URL and also keep track of the new id. val mediaModel = oldUriToMediaFiles.get(Uri.fromFile(frame.composedFrameFile)) mediaModel?.let { + frame.id = it.id.toString() StoriesPrefs.saveSlide( appContext, - mediaModel.localSiteId.toLong(), - mediaModel.id.toLong(), // use the local id to save the original, will be replaced later + it.localSiteId.toLong(), + it.id.toLong(), // use the local id to save the original, will be replaced later // with mediaModel.mediaId after uploading to the remote site frame ) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/stories/prefs/StoriesPrefs.kt b/WordPress/src/main/java/org/wordpress/android/ui/stories/prefs/StoriesPrefs.kt index ae0c94f55187..fbd94db60fb2 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/stories/prefs/StoriesPrefs.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/stories/prefs/StoriesPrefs.kt @@ -15,12 +15,14 @@ object StoriesPrefs { return KEY_PREFIX_STORIES_SLIDE_ID + siteId.toString() + "-" + mediaId.toString() } - private fun checkSlideIdExists(context: Context, siteId: Long, mediaId: Long): Boolean { + @JvmStatic + fun checkSlideIdExists(context: Context, siteId: Long, mediaId: Long): Boolean { val slideIdKey = buildSlideKey(siteId, mediaId) return PreferenceManager.getDefaultSharedPreferences(context).contains(slideIdKey) } - private fun checkSlideOriginalBackgroundMediaExists(context: Context, siteId: Long, mediaId: Long): Boolean { + @JvmStatic + fun checkSlideOriginalBackgroundMediaExists(context: Context, siteId: Long, mediaId: Long): Boolean { val storyFrameItem: StoryFrameItem? = getSlide(context, siteId, mediaId) storyFrameItem?.let { frame -> // now check the background media exists or is accessible on this device @@ -74,6 +76,7 @@ object StoriesPrefs { return PreferenceManager.getDefaultSharedPreferences(context).getString(slideIdKey, null) } + @JvmStatic fun getSlide(context: Context, siteId: Long, mediaId: Long): StoryFrameItem? { val jsonSlide = getSlideJson(context, siteId, mediaId) jsonSlide?.let { @@ -83,7 +86,6 @@ object StoriesPrefs { fun saveSlide(context: Context, siteId: Long, mediaId: Long, storyFrameItem: StoryFrameItem) { val slideIdKey = buildSlideKey(siteId, mediaId) -// storyFrameItem.id = slideIdKey saveSlide(context, slideIdKey, StorySerializerUtils.serializeStoryFrameItem(storyFrameItem)) } diff --git a/libs/stories-android b/libs/stories-android index ae06c31785a2..22b8d57c9ae5 160000 --- a/libs/stories-android +++ b/libs/stories-android @@ -1 +1 @@ -Subproject commit ae06c31785a2e124566a20c9c8639bc62552856f +Subproject commit 22b8d57c9ae5d48849f7f54f986dfa392e8c43a8 From d5ba9a3ad7b127700e2b2e81f5d72a4af3d08d76 Mon Sep 17 00:00:00 2001 From: Mario Zorz Date: Tue, 15 Sep 2020 15:58:55 -0300 Subject: [PATCH 05/47] removed unused import --- .../org/wordpress/android/ui/stories/StoryComposerActivity.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/stories/StoryComposerActivity.kt b/WordPress/src/main/java/org/wordpress/android/ui/stories/StoryComposerActivity.kt index 2201e797d251..4f827f055e92 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/stories/StoryComposerActivity.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/stories/StoryComposerActivity.kt @@ -27,7 +27,6 @@ import org.wordpress.android.R.id import org.wordpress.android.WordPress import org.wordpress.android.analytics.AnalyticsTracker.Stat import org.wordpress.android.analytics.AnalyticsTracker.Stat.PREPUBLISHING_BOTTOM_SHEET_OPENED -import org.wordpress.android.editor.EditorImageMetaData import org.wordpress.android.fluxc.model.LocalOrRemoteId.LocalId import org.wordpress.android.fluxc.model.MediaModel import org.wordpress.android.fluxc.model.PostImmutableModel From 556c7ccccd0bef6a529ae7a426679a131f7cf6fb Mon Sep 17 00:00:00 2001 From: Mario Zorz Date: Tue, 15 Sep 2020 23:46:02 -0300 Subject: [PATCH 06/47] fixed bug that had the Activity start as many times as slides where added --- WordPress/src/main/AndroidManifest.xml | 1 - .../org/wordpress/android/ui/posts/EditPostActivity.java | 7 ++----- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/WordPress/src/main/AndroidManifest.xml b/WordPress/src/main/AndroidManifest.xml index c9eba1929991..1e800e40ad02 100644 --- a/WordPress/src/main/AndroidManifest.xml +++ b/WordPress/src/main/AndroidManifest.xml @@ -227,7 +227,6 @@ android:name=".ui.stories.StoryComposerActivity" android:label="@string/app_name" android:screenOrientation="portrait" - android:launchMode="singleTop" android:theme="@style/WordPress.Stories.Immersive"> 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 58aabbe17e88..0c1917ffff87 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 @@ -3041,13 +3041,10 @@ public void onTrackableEvent(TrackableEvent event, Map propertie StoryRepository.addStoryFrameItemToCurrentStory(storyFrameItem); } } - // Story instance re-created! Load it - ActivityLauncher.editStoryForResult(this, mSite, storyIndex, allStorySlidesAreEditable, true); } - } else { - // Story found! Load it - ActivityLauncher.editStoryForResult(this, mSite, storyIndex, allStorySlidesAreEditable, true); } + // Story instance loaded or re-created! Load it + ActivityLauncher.editStoryForResult(this, mSite, storyIndex, allStorySlidesAreEditable, true); } // FluxC events From 667a8dbeee64a34e79ec43b76fbdc6c7400b4165 Mon Sep 17 00:00:00 2001 From: Mario Zorz Date: Wed, 16 Sep 2020 08:32:45 -0300 Subject: [PATCH 07/47] removed unused import, removed comment --- .../main/java/org/wordpress/android/ui/ActivityLauncher.java | 2 -- .../java/org/wordpress/android/ui/posts/EditPostActivity.java | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/ActivityLauncher.java b/WordPress/src/main/java/org/wordpress/android/ui/ActivityLauncher.java index 725acb28a390..df9c7833fae5 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/ActivityLauncher.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/ActivityLauncher.java @@ -13,8 +13,6 @@ import androidx.core.app.TaskStackBuilder; import androidx.fragment.app.Fragment; -import com.wordpress.stories.compose.ComposeLoopFrameActivity; - import org.wordpress.android.R; import org.wordpress.android.WordPress; import org.wordpress.android.analytics.AnalyticsTracker; 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 0c1917ffff87..a21b95426895 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 @@ -3006,7 +3006,7 @@ public void onTrackableEvent(TrackableEvent event, Map propertie boolean allStorySlidesAreEditable = true; for (Object mediaFile : mediaFiles) { long mediaIdLong = new Double(((HashMap) mediaFile).get("id").toString()).longValue(); - String mediaIdString = String.valueOf(mediaIdLong); // convert back after stripping the decimals in original Double + String mediaIdString = String.valueOf(mediaIdLong); if (allStorySlidesAreEditable && !StoriesPrefs.isValidSlide(this, mSite.getSiteId(), mediaIdLong)) { // flag this as soon as we find one media item not being really editable From 34f858e6cb6dcbe248cfc56400d1a7729f41ad5b Mon Sep 17 00:00:00 2001 From: Mario Zorz Date: Wed, 16 Sep 2020 09:00:55 -0300 Subject: [PATCH 08/47] fixed loading flattened slides for editing --- .../org/wordpress/android/ui/posts/EditPostActivity.java | 7 ++++--- libs/stories-android | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) 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 a21b95426895..c96cfe4dc250 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 @@ -3001,7 +3001,6 @@ public void onTrackableEvent(TrackableEvent event, Map propertie } @Override public void onStoryComposerLoadRequested(ArrayList mediaFiles, String blockId) { - ArrayList tmpMediaIdsLong = new ArrayList<>(); ArrayList tmpMediaIdsString = new ArrayList<>(); boolean allStorySlidesAreEditable = true; for (Object mediaFile : mediaFiles) { @@ -3012,7 +3011,6 @@ public void onTrackableEvent(TrackableEvent event, Map propertie // flag this as soon as we find one media item not being really editable allStorySlidesAreEditable = false; } - tmpMediaIdsLong.add(mediaIdLong); tmpMediaIdsString.add(mediaIdString); } @@ -3030,7 +3028,10 @@ public void onTrackableEvent(TrackableEvent event, Map propertie StoryRepository.addStoryFrameItemToCurrentStory(storyFrameItem); } else { allStorySlidesAreEditable = false; - // create a new frame using the actual uploaded flattened media as a background + + // for this missing frame we'll create a new frame using the actual uploaded flattened media + ArrayList tmpMediaIdsLong = new ArrayList<>(); + tmpMediaIdsLong.add(Long.parseLong(mediaId)); List mediaModelList = mMediaStore.getSiteMediaWithIds(mSite, tmpMediaIdsLong); for (MediaModel mediaModel : mediaModelList) { storyFrameItem = StoryFrameItem.Companion.getNewStoryFrameItemFromUri( diff --git a/libs/stories-android b/libs/stories-android index 22b8d57c9ae5..821a8cd96c89 160000 --- a/libs/stories-android +++ b/libs/stories-android @@ -1 +1 @@ -Subproject commit 22b8d57c9ae5d48849f7f54f986dfa392e8c43a8 +Subproject commit 821a8cd96c8991533476ce5b6b11c720009d808f From e27fea5c6213bf8669b6b1c50b4fc8b7d2340adb Mon Sep 17 00:00:00 2001 From: Mario Zorz Date: Wed, 16 Sep 2020 09:45:49 -0300 Subject: [PATCH 09/47] fixed and added new test for onStoryDiscarded --- .../ui/stories/StoryComposerViewModelTest.kt | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/WordPress/src/test/java/org/wordpress/android/ui/stories/StoryComposerViewModelTest.kt b/WordPress/src/test/java/org/wordpress/android/ui/stories/StoryComposerViewModelTest.kt index 33127f3c799a..e10f3519b2cc 100644 --- a/WordPress/src/test/java/org/wordpress/android/ui/stories/StoryComposerViewModelTest.kt +++ b/WordPress/src/test/java/org/wordpress/android/ui/stories/StoryComposerViewModelTest.kt @@ -192,15 +192,25 @@ class StoryComposerViewModelTest : BaseUnitTest() { } @Test - fun `If onStoryDiscarded is called then the post is removed with the dispatcher`() { + fun `If onStoryDiscarded is called then the post is removed with the dispatcher when deleteDiscardedPost true `() { // act viewModel.start(site, editPostRepository, LocalId(0), mock(), mock()) - viewModel.onStoryDiscarded() + viewModel.onStoryDiscarded(deleteDiscardedPost = true) // assert verify(dispatcher, times(1)).dispatch(any>()) } + @Test + fun `If onStoryDiscarded is called then the post is not removed when deleteDiscardedPost false `() { + // act + viewModel.start(site, editPostRepository, LocalId(0), mock(), mock()) + viewModel.onStoryDiscarded(deleteDiscardedPost = false) + + // assert + verify(dispatcher, times(0)).dispatch(any>()) + } + @Test fun `verify that triggering onStorySaveButtonPressed will trigger the associated openPrepublishingBottomSheet`() { // act From a7be9d3a5553e7ef1f7c34ac4381baf348b60aa3 Mon Sep 17 00:00:00 2001 From: Mario Zorz Date: Wed, 16 Sep 2020 10:22:48 -0300 Subject: [PATCH 10/47] updated stories lib hash --- libs/stories-android | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/stories-android b/libs/stories-android index 821a8cd96c89..add3c28aaf69 160000 --- a/libs/stories-android +++ b/libs/stories-android @@ -1 +1 @@ -Subproject commit 821a8cd96c8991533476ce5b6b11c720009d808f +Subproject commit add3c28aaf69ebb401a8036eeae019923be00adc From 2bc41c93b6dd67629e90cdfbb23366ffae564129 Mon Sep 17 00:00:00 2001 From: Mario Zorz Date: Wed, 16 Sep 2020 10:53:17 -0300 Subject: [PATCH 11/47] using site localid for slide retrieval from StoriesPrefs --- .../java/org/wordpress/android/ui/posts/EditPostActivity.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 c96cfe4dc250..74fc61e9dd2a 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 @@ -3007,7 +3007,7 @@ public void onTrackableEvent(TrackableEvent event, Map propertie long mediaIdLong = new Double(((HashMap) mediaFile).get("id").toString()).longValue(); String mediaIdString = String.valueOf(mediaIdLong); if (allStorySlidesAreEditable - && !StoriesPrefs.isValidSlide(this, mSite.getSiteId(), mediaIdLong)) { + && !StoriesPrefs.isValidSlide(this, mSite.getId(), mediaIdLong)) { // flag this as soon as we find one media item not being really editable allStorySlidesAreEditable = false; } @@ -3023,7 +3023,7 @@ public void onTrackableEvent(TrackableEvent event, Map propertie StoryRepository.loadStory(storyIndex); storyIndex = StoryRepository.currentStoryIndex; for (String mediaId : tmpMediaIdsString) { - StoryFrameItem storyFrameItem = StoriesPrefs.getSlide(this, mSite.getSiteId(), Long.parseLong(mediaId)); + StoryFrameItem storyFrameItem = StoriesPrefs.getSlide(this, mSite.getId(), Long.parseLong(mediaId)); if (storyFrameItem != null) { StoryRepository.addStoryFrameItemToCurrentStory(storyFrameItem); } else { From 9ad4a87d2039d2ee48b112697c1112a9e7f8fde9 Mon Sep 17 00:00:00 2001 From: Mario Zorz Date: Wed, 16 Sep 2020 11:10:21 -0300 Subject: [PATCH 12/47] updated stories lib commit hash --- libs/stories-android | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/stories-android b/libs/stories-android index add3c28aaf69..56deb4537e1d 160000 --- a/libs/stories-android +++ b/libs/stories-android @@ -1 +1 @@ -Subproject commit add3c28aaf69ebb401a8036eeae019923be00adc +Subproject commit 56deb4537e1dabc8551231fb5fc00b94e7df9131 From e755a7413e419ef461e5d8f0aa8e6057e2880b64 Mon Sep 17 00:00:00 2001 From: Mario Zorz Date: Wed, 16 Sep 2020 11:45:03 -0300 Subject: [PATCH 13/47] introducing LocalMediaId and RemoteMediaId data classes to enforce meaning, plus using a differentiated prefix when buildng keys to avoid the chance of local/remote overlapping ids --- .../android/ui/posts/EditPostActivity.java | 9 +++- .../stories/SaveStoryGutenbergBlockUseCase.kt | 14 ++--- .../media/StoryMediaSaveUploadBridge.kt | 6 ++- .../android/ui/stories/prefs/StoriesPrefs.kt | 52 ++++++++++++++----- 4 files changed, 59 insertions(+), 22 deletions(-) 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 74fc61e9dd2a..aa8a9b8068ed 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 @@ -161,6 +161,7 @@ import org.wordpress.android.ui.reader.utils.ReaderUtilsWrapper; import org.wordpress.android.ui.stockmedia.StockMediaPickerActivity; import org.wordpress.android.ui.stories.prefs.StoriesPrefs; +import org.wordpress.android.ui.stories.prefs.StoriesPrefs.RemoteMediaId; import org.wordpress.android.ui.uploads.PostEvents; import org.wordpress.android.ui.uploads.UploadService; import org.wordpress.android.ui.uploads.UploadUtils; @@ -3007,7 +3008,7 @@ public void onTrackableEvent(TrackableEvent event, Map propertie long mediaIdLong = new Double(((HashMap) mediaFile).get("id").toString()).longValue(); String mediaIdString = String.valueOf(mediaIdLong); if (allStorySlidesAreEditable - && !StoriesPrefs.isValidSlide(this, mSite.getId(), mediaIdLong)) { + && !StoriesPrefs.isValidSlide(this, mSite.getId(), new RemoteMediaId(mediaIdLong))) { // flag this as soon as we find one media item not being really editable allStorySlidesAreEditable = false; } @@ -3023,7 +3024,11 @@ public void onTrackableEvent(TrackableEvent event, Map propertie StoryRepository.loadStory(storyIndex); storyIndex = StoryRepository.currentStoryIndex; for (String mediaId : tmpMediaIdsString) { - StoryFrameItem storyFrameItem = StoriesPrefs.getSlide(this, mSite.getId(), Long.parseLong(mediaId)); + StoryFrameItem storyFrameItem = StoriesPrefs.getSlideWithRemoteId( + this, + mSite.getId(), + new RemoteMediaId(Long.parseLong(mediaId)) + ); if (storyFrameItem != null) { StoryRepository.addStoryFrameItemToCurrentStory(storyFrameItem); } else { diff --git a/WordPress/src/main/java/org/wordpress/android/ui/stories/SaveStoryGutenbergBlockUseCase.kt b/WordPress/src/main/java/org/wordpress/android/ui/stories/SaveStoryGutenbergBlockUseCase.kt index 6ff3b070d04e..e9c22e279c03 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/stories/SaveStoryGutenbergBlockUseCase.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/stories/SaveStoryGutenbergBlockUseCase.kt @@ -5,6 +5,8 @@ import org.wordpress.android.WordPress import org.wordpress.android.fluxc.model.PostModel import org.wordpress.android.ui.posts.EditPostRepository import org.wordpress.android.ui.stories.prefs.StoriesPrefs +import org.wordpress.android.ui.stories.prefs.StoriesPrefs.LocalMediaId +import org.wordpress.android.ui.stories.prefs.StoriesPrefs.RemoteMediaId import org.wordpress.android.util.StringUtils import org.wordpress.android.util.helpers.MediaFile import javax.inject.Inject @@ -64,23 +66,23 @@ class SaveStoryGutenbergBlockUseCase @Inject constructor() { val localIdKey = mediaFile.id.toLong() val remoteIdKey = mediaFile.mediaId.toLong() val localSiteId = post.localSiteId.toLong() - StoriesPrefs.getSlide( + StoriesPrefs.getSlideWithLocalId( WordPress.getContext(), localSiteId, - localIdKey + LocalMediaId(localIdKey) )?.let { it.id = mediaFile.mediaId // update the StoryFrameItem id to hold the same value as the remote mediaID - StoriesPrefs.saveSlide( + StoriesPrefs.saveSlideWithRemoteId( WordPress.getContext(), localSiteId, - remoteIdKey, // use the new mediaId as key + RemoteMediaId(remoteIdKey), // use the new mediaId as key it ) // now delete the old entry - StoriesPrefs.deleteSlide( + StoriesPrefs.deleteSlideWithLocalId( WordPress.getContext(), localSiteId, - localIdKey + LocalMediaId(localIdKey) ) } } diff --git a/WordPress/src/main/java/org/wordpress/android/ui/stories/media/StoryMediaSaveUploadBridge.kt b/WordPress/src/main/java/org/wordpress/android/ui/stories/media/StoryMediaSaveUploadBridge.kt index a1829d2daf96..d45c809556d7 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/stories/media/StoryMediaSaveUploadBridge.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/stories/media/StoryMediaSaveUploadBridge.kt @@ -30,6 +30,7 @@ import org.wordpress.android.ui.stories.SaveStoryGutenbergBlockUseCase import org.wordpress.android.ui.stories.StoriesTrackerHelper import org.wordpress.android.ui.stories.StoryComposerActivity import org.wordpress.android.ui.stories.prefs.StoriesPrefs +import org.wordpress.android.ui.stories.prefs.StoriesPrefs.LocalMediaId import org.wordpress.android.ui.uploads.UploadServiceFacade import org.wordpress.android.util.EventBusWrapper import org.wordpress.android.util.NetworkUtilsWrapper @@ -187,11 +188,12 @@ class StoryMediaSaveUploadBridge @Inject constructor( val mediaModel = oldUriToMediaFiles.get(Uri.fromFile(frame.composedFrameFile)) mediaModel?.let { frame.id = it.id.toString() - StoriesPrefs.saveSlide( + StoriesPrefs.saveSlideWithLocalId( appContext, it.localSiteId.toLong(), - it.id.toLong(), // use the local id to save the original, will be replaced later + // use the local id to save the original, will be replaced later // with mediaModel.mediaId after uploading to the remote site + LocalMediaId(it.id.toLong()), frame ) } diff --git a/WordPress/src/main/java/org/wordpress/android/ui/stories/prefs/StoriesPrefs.kt b/WordPress/src/main/java/org/wordpress/android/ui/stories/prefs/StoriesPrefs.kt index fbd94db60fb2..4dc3a8b850b8 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/stories/prefs/StoriesPrefs.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/stories/prefs/StoriesPrefs.kt @@ -10,20 +10,26 @@ import com.wordpress.stories.compose.story.StorySerializerUtils object StoriesPrefs { private const val KEY_PREFIX_STORIES_SLIDE_ID = "story_slide_id-" + private const val KEY_PREFIX_LOCAL_MEDIA_ID = "l-" + private const val KEY_PREFIX_REMOTE_MEDIA_ID = "r-" - private fun buildSlideKey(siteId: Long, mediaId: Long): String { - return KEY_PREFIX_STORIES_SLIDE_ID + siteId.toString() + "-" + mediaId.toString() + private fun buildSlideKey(siteId: Long, mediaId: RemoteMediaId): String { + return KEY_PREFIX_STORIES_SLIDE_ID + siteId.toString() + "-" + KEY_PREFIX_REMOTE_MEDIA_ID + mediaId.toString() + } + + private fun buildSlideKey(siteId: Long, mediaId: LocalMediaId): String { + return KEY_PREFIX_STORIES_SLIDE_ID + siteId.toString() + "-" + KEY_PREFIX_LOCAL_MEDIA_ID + mediaId.toString() } @JvmStatic - fun checkSlideIdExists(context: Context, siteId: Long, mediaId: Long): Boolean { + fun checkSlideIdExists(context: Context, siteId: Long, mediaId: RemoteMediaId): Boolean { val slideIdKey = buildSlideKey(siteId, mediaId) return PreferenceManager.getDefaultSharedPreferences(context).contains(slideIdKey) } @JvmStatic - fun checkSlideOriginalBackgroundMediaExists(context: Context, siteId: Long, mediaId: Long): Boolean { - val storyFrameItem: StoryFrameItem? = getSlide(context, siteId, mediaId) + fun checkSlideOriginalBackgroundMediaExists(context: Context, siteId: Long, mediaId: RemoteMediaId): Boolean { + val storyFrameItem: StoryFrameItem? = getSlideWithRemoteId(context, siteId, mediaId) storyFrameItem?.let { frame -> // now check the background media exists or is accessible on this device frame.source.let { source -> @@ -66,33 +72,55 @@ object StoriesPrefs { } @JvmStatic - fun isValidSlide(context: Context, siteId: Long, mediaId: Long): Boolean { + fun isValidSlide(context: Context, siteId: Long, mediaId: RemoteMediaId): Boolean { return checkSlideIdExists(context, siteId, mediaId) && checkSlideOriginalBackgroundMediaExists(context, siteId, mediaId) } - private fun getSlideJson(context: Context, siteId: Long, mediaId: Long): String? { - val slideIdKey = buildSlideKey(siteId, mediaId) + private fun getSlideJson(context: Context, slideIdKey: String): String? { return PreferenceManager.getDefaultSharedPreferences(context).getString(slideIdKey, null) } @JvmStatic - fun getSlide(context: Context, siteId: Long, mediaId: Long): StoryFrameItem? { - val jsonSlide = getSlideJson(context, siteId, mediaId) + fun getSlideWithRemoteId(context: Context, siteId: Long, mediaId: RemoteMediaId): StoryFrameItem? { + val jsonSlide = getSlideJson(context, buildSlideKey(siteId, mediaId)) + jsonSlide?.let { + return StorySerializerUtils.deserializeStoryFrameItem(jsonSlide) + } ?: return null + } + + @JvmStatic + fun getSlideWithLocalId(context: Context, siteId: Long, mediaId: LocalMediaId): StoryFrameItem? { + val jsonSlide = getSlideJson(context, buildSlideKey(siteId, mediaId)) jsonSlide?.let { return StorySerializerUtils.deserializeStoryFrameItem(jsonSlide) } ?: return null } - fun saveSlide(context: Context, siteId: Long, mediaId: Long, storyFrameItem: StoryFrameItem) { + fun saveSlideWithLocalId(context: Context, siteId: Long, mediaId: LocalMediaId, storyFrameItem: StoryFrameItem) { + val slideIdKey = buildSlideKey(siteId, mediaId) + saveSlide(context, slideIdKey, StorySerializerUtils.serializeStoryFrameItem(storyFrameItem)) + } + + fun saveSlideWithRemoteId(context: Context, siteId: Long, mediaId: RemoteMediaId, storyFrameItem: StoryFrameItem) { val slideIdKey = buildSlideKey(siteId, mediaId) saveSlide(context, slideIdKey, StorySerializerUtils.serializeStoryFrameItem(storyFrameItem)) } - fun deleteSlide(context: Context, siteId: Long, mediaId: Long) { + fun deleteSlideWithLocalId(context: Context, siteId: Long, mediaId: LocalMediaId) { + val slideIdKey = buildSlideKey(siteId, mediaId) + val editor = PreferenceManager.getDefaultSharedPreferences(context).edit() + editor.remove(slideIdKey) + editor.apply() + } + + fun deleteSlideWithRemoteId(context: Context, siteId: Long, mediaId: RemoteMediaId) { val slideIdKey = buildSlideKey(siteId, mediaId) val editor = PreferenceManager.getDefaultSharedPreferences(context).edit() editor.remove(slideIdKey) editor.apply() } + + data class RemoteMediaId(val mediaId: Long) + data class LocalMediaId(val id: Long) } From 9c032f6710f87d986cad2487490429f2a428680b Mon Sep 17 00:00:00 2001 From: Mario Zorz Date: Wed, 16 Sep 2020 13:05:24 -0300 Subject: [PATCH 14/47] using setUseTempCaptureFile on the stories library to keep captured media --- .../org/wordpress/android/ui/stories/StoryComposerActivity.kt | 1 + libs/stories-android | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/stories/StoryComposerActivity.kt b/WordPress/src/main/java/org/wordpress/android/ui/stories/StoryComposerActivity.kt index 4f827f055e92..ca0da66f8e22 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/stories/StoryComposerActivity.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/stories/StoryComposerActivity.kt @@ -119,6 +119,7 @@ class StoryComposerActivity : ComposeLoopFrameActivity(), setNotificationTrackerProvider((application as WordPress).getStoryNotificationTrackerProvider()) setPrepublishingEventProvider(this) setPermissionDialogProvider(this) + setUseTempCaptureFile(false) // we need to keep the captured files for later Story editing initViewModel(savedInstanceState) } diff --git a/libs/stories-android b/libs/stories-android index 56deb4537e1d..5b9460ac6a86 160000 --- a/libs/stories-android +++ b/libs/stories-android @@ -1 +1 @@ -Subproject commit 56deb4537e1dabc8551231fb5fc00b94e7df9131 +Subproject commit 5b9460ac6a86ab7c1f8f1f5b43a62d57d2aa994e From 0232b7c4111839daec1ac0b8ab20c333c6530f88 Mon Sep 17 00:00:00 2001 From: Mario Zorz Date: Wed, 16 Sep 2020 14:18:52 -0300 Subject: [PATCH 15/47] converting intent extra KEY_LAUNCHED_FROM_GUTENBERG to stories KEY_STORY_EDIT_MODE so we can show discard dialog messaging properly --- .../org/wordpress/android/ui/stories/StoryComposerActivity.kt | 3 +++ libs/stories-android | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/stories/StoryComposerActivity.kt b/WordPress/src/main/java/org/wordpress/android/ui/stories/StoryComposerActivity.kt index ca0da66f8e22..697367f752ad 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/stories/StoryComposerActivity.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/stories/StoryComposerActivity.kt @@ -21,6 +21,7 @@ import com.wordpress.stories.compose.SnackbarProvider import com.wordpress.stories.compose.StoryDiscardListener import com.wordpress.stories.compose.frame.StorySaveEvents.StorySaveResult import com.wordpress.stories.compose.story.StoryIndex +import com.wordpress.stories.util.KEY_STORY_EDIT_MODE import com.wordpress.stories.util.KEY_STORY_INDEX import com.wordpress.stories.util.KEY_STORY_SAVE_RESULT import org.wordpress.android.R.id @@ -108,6 +109,8 @@ class StoryComposerActivity : ComposeLoopFrameActivity(), } override fun onCreate(savedInstanceState: Bundle?) { + // convert our WPAndroid KEY_LAUNCHED_FROM_GUTENBERG flag into Stories general purpose EDIT_MODE flag + intent.putExtra(KEY_STORY_EDIT_MODE, intent.getBooleanExtra(KEY_LAUNCHED_FROM_GUTENBERG, false)) super.onCreate(savedInstanceState) (application as WordPress).component().inject(this) setSnackbarProvider(this) diff --git a/libs/stories-android b/libs/stories-android index 5b9460ac6a86..78b87d4cd5cc 160000 --- a/libs/stories-android +++ b/libs/stories-android @@ -1 +1 @@ -Subproject commit 5b9460ac6a86ab7c1f8f1f5b43a62d57d2aa994e +Subproject commit 78b87d4cd5cc1dab9a3eb22ab902b66143f4b08d From a35d6c236eec486265ea54187f10dd4fe62b377b Mon Sep 17 00:00:00 2001 From: Mario Zorz Date: Wed, 16 Sep 2020 15:31:07 -0300 Subject: [PATCH 16/47] implemented the GenericAnnouncementDialogProvider interface and showing the limited editing dialog when a Story slide to be eidted hasnt been found in StoriesPrefs --- .../android/ui/ActivityLauncher.java | 3 ++- .../ui/stories/StoryComposerActivity.kt | 24 ++++++++++++++++--- WordPress/src/main/res/values/strings.xml | 3 +++ libs/stories-android | 2 +- 4 files changed, 27 insertions(+), 5 deletions(-) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/ActivityLauncher.java b/WordPress/src/main/java/org/wordpress/android/ui/ActivityLauncher.java index df9c7833fae5..28d25cf97060 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/ActivityLauncher.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/ActivityLauncher.java @@ -103,6 +103,7 @@ import static org.wordpress.android.login.LoginMode.WPCOM_LOGIN_ONLY; import static org.wordpress.android.ui.media.MediaBrowserActivity.ARG_BROWSER_TYPE; import static org.wordpress.android.ui.pages.PagesActivityKt.EXTRA_PAGE_REMOTE_ID_KEY; +import static org.wordpress.android.ui.stories.StoryComposerActivity.KEY_ALL_UNFLATTENED_LOADED_SLIDES; import static org.wordpress.android.ui.stories.StoryComposerActivity.KEY_LAUNCHED_FROM_GUTENBERG; import static org.wordpress.android.viewmodel.activitylog.ActivityLogDetailViewModelKt.ACTIVITY_LOG_ID_KEY; @@ -749,11 +750,11 @@ public static void editStoryForResult( return; } - // TODO pass the allStorySlidesAreEditable boolean flag make sure to show the warning dialog Intent intent = new Intent(activity, StoryComposerActivity.class); intent.putExtra(WordPress.SITE, site); intent.putExtra(KEY_STORY_INDEX, storyIndex); intent.putExtra(KEY_LAUNCHED_FROM_GUTENBERG, launchedFromGutenberg); + intent.putExtra(KEY_ALL_UNFLATTENED_LOADED_SLIDES, allStorySlidesAreEditable); activity.startActivityForResult(intent, RequestCodes.EDIT_STORY); } diff --git a/WordPress/src/main/java/org/wordpress/android/ui/stories/StoryComposerActivity.kt b/WordPress/src/main/java/org/wordpress/android/ui/stories/StoryComposerActivity.kt index 697367f752ad..ff44aa5e536a 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/stories/StoryComposerActivity.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/stories/StoryComposerActivity.kt @@ -12,6 +12,8 @@ import androidx.lifecycle.ViewModelProviders import com.google.android.material.snackbar.Snackbar import com.wordpress.stories.compose.AuthenticationHeadersProvider import com.wordpress.stories.compose.ComposeLoopFrameActivity +import com.wordpress.stories.compose.FrameSaveErrorDialog +import com.wordpress.stories.compose.GenericAnnouncementDialogProvider import com.wordpress.stories.compose.MediaPickerProvider import com.wordpress.stories.compose.MetadataProvider import com.wordpress.stories.compose.NotificationIntentLoader @@ -24,7 +26,7 @@ import com.wordpress.stories.compose.story.StoryIndex import com.wordpress.stories.util.KEY_STORY_EDIT_MODE import com.wordpress.stories.util.KEY_STORY_INDEX import com.wordpress.stories.util.KEY_STORY_SAVE_RESULT -import org.wordpress.android.R.id +import org.wordpress.android.R import org.wordpress.android.WordPress import org.wordpress.android.analytics.AnalyticsTracker.Stat import org.wordpress.android.analytics.AnalyticsTracker.Stat.PREPUBLISHING_BOTTOM_SHEET_OPENED @@ -79,7 +81,8 @@ class StoryComposerActivity : ComposeLoopFrameActivity(), EditPostActivityHook, PrepublishingEventProvider, PrepublishingBottomSheetListener, - PermanentPermissionDenialDialogProvider { + PermanentPermissionDenialDialogProvider, + GenericAnnouncementDialogProvider { private var site: SiteModel? = null @Inject lateinit var storyEditorMedia: StoryEditorMedia @@ -100,10 +103,12 @@ class StoryComposerActivity : ComposeLoopFrameActivity(), override fun getEditPostRepository() = editPostRepository companion object { + protected const val FRAGMENT_ANNOUNCEMENT_DIALOG = "story_announcement_dialog" const val STATE_KEY_POST_LOCAL_ID = "state_key_post_model_local_id" const val STATE_KEY_EDITOR_SESSION_DATA = "stateKeyEditorSessionData" const val KEY_POST_LOCAL_ID = "key_post_model_local_id" const val KEY_LAUNCHED_FROM_GUTENBERG = "key_launched_from_gutenberg" + const val KEY_ALL_UNFLATTENED_LOADED_SLIDES = "key_all_unflattened_laoded_slides" const val UNUSED_KEY = "unused_key" const val BASE_FRAME_MEDIA_ERROR_NOTIFICATION_ID: Int = 72300 } @@ -122,6 +127,7 @@ class StoryComposerActivity : ComposeLoopFrameActivity(), setNotificationTrackerProvider((application as WordPress).getStoryNotificationTrackerProvider()) setPrepublishingEventProvider(this) setPermissionDialogProvider(this) + setGenericAnnouncementDialogProvider(this) setUseTempCaptureFile(false) // we need to keep the captured files for later Story editing initViewModel(savedInstanceState) @@ -324,7 +330,7 @@ class StoryComposerActivity : ComposeLoopFrameActivity(), if (messageHolder != null) { WPSnackbar .make( - findViewById(id.editor_activity), + findViewById(org.wordpress.android.R.id.editor_activity), uiHelpers.getTextOfUiString(this, messageHolder.message), Snackbar.LENGTH_SHORT ) @@ -434,4 +440,16 @@ class StoryComposerActivity : ComposeLoopFrameActivity(), override fun showPermissionPermanentlyDeniedDialog(permission: String) { WPPermissionUtils.showPermissionAlwaysDeniedDialog(this, permission) } + + override fun showGenericAnnouncementDialog() { + if (intent.getBooleanExtra(KEY_LAUNCHED_FROM_GUTENBERG, false)) { + if (!intent.getBooleanExtra(KEY_ALL_UNFLATTENED_LOADED_SLIDES, false)) { + // not all slides in this Story could be unflattened so, show the warning informative dialog + FrameSaveErrorDialog.newInstance( + title = getString(R.string.dialog_edit_story_limited_title), + message = getString(R.string.dialog_edit_story_limited_message) + ).show(supportFragmentManager, FRAGMENT_ANNOUNCEMENT_DIALOG) + } + } + } } diff --git a/WordPress/src/main/res/values/strings.xml b/WordPress/src/main/res/values/strings.xml index fb2454bca40c..4d066ad74c38 100644 --- a/WordPress/src/main/res/values/strings.xml +++ b/WordPress/src/main/res/values/strings.xml @@ -2871,6 +2871,9 @@ %s selected %s + + Limited Story Editing + This story was edited on a different device and the ability to edit certain objects may be limited. Capture Flip camera Flash diff --git a/libs/stories-android b/libs/stories-android index 78b87d4cd5cc..44deeb3a86c0 160000 --- a/libs/stories-android +++ b/libs/stories-android @@ -1 +1 @@ -Subproject commit 78b87d4cd5cc1dab9a3eb22ab902b66143f4b08d +Subproject commit 44deeb3a86c0b37220e6aa606753a7cdae775543 From 3f5da8d2394c5ce36fd929b661927d2666101caa Mon Sep 17 00:00:00 2001 From: Mario Zorz Date: Wed, 16 Sep 2020 18:23:12 -0300 Subject: [PATCH 17/47] updated stories lib hash --- libs/stories-android | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/stories-android b/libs/stories-android index 44deeb3a86c0..b9322a3f440b 160000 --- a/libs/stories-android +++ b/libs/stories-android @@ -1 +1 @@ -Subproject commit 44deeb3a86c0b37220e6aa606753a7cdae775543 +Subproject commit b9322a3f440bd33b6b5d919e67617c60dacea36b From 8fa059d29563eafbe53ea48c379fdd4232ab545b Mon Sep 17 00:00:00 2001 From: Mario Zorz Date: Fri, 18 Sep 2020 07:47:31 -0300 Subject: [PATCH 18/47] initializing request codes right away in onCreate before calling super() to make sure these can be properly evaluated on creation --- .../org/wordpress/android/ui/stories/StoryComposerActivity.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/stories/StoryComposerActivity.kt b/WordPress/src/main/java/org/wordpress/android/ui/stories/StoryComposerActivity.kt index ff44aa5e536a..90a0c67e01fe 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/stories/StoryComposerActivity.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/stories/StoryComposerActivity.kt @@ -116,10 +116,10 @@ class StoryComposerActivity : ComposeLoopFrameActivity(), override fun onCreate(savedInstanceState: Bundle?) { // convert our WPAndroid KEY_LAUNCHED_FROM_GUTENBERG flag into Stories general purpose EDIT_MODE flag intent.putExtra(KEY_STORY_EDIT_MODE, intent.getBooleanExtra(KEY_LAUNCHED_FROM_GUTENBERG, false)) + setMediaPickerProvider(this) super.onCreate(savedInstanceState) (application as WordPress).component().inject(this) setSnackbarProvider(this) - setMediaPickerProvider(this) setAuthenticationProvider(this) setNotificationExtrasLoader(this) setMetadataProvider(this) From df91b54a0b87a32c9c0ceb383d28b9979b9ebdef Mon Sep 17 00:00:00 2001 From: Mario Zorz Date: Fri, 18 Sep 2020 09:05:44 -0300 Subject: [PATCH 19/47] refactored code, introducing LoadStoryFromStoriesPrefsUseCase --- .../android/ui/posts/EditPostActivity.java | 60 ++++-------- .../ui/stories/StoryRepositoryWrapper.kt | 5 + .../android/ui/stories/prefs/StoriesPrefs.kt | 4 +- .../LoadStoryFromStoriesPrefsUseCase.kt | 91 +++++++++++++++++++ libs/stories-android | 2 +- 5 files changed, 119 insertions(+), 43 deletions(-) create mode 100644 WordPress/src/main/java/org/wordpress/android/ui/stories/usecase/LoadStoryFromStoriesPrefsUseCase.kt 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 aa8a9b8068ed..9987050575c7 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 @@ -160,8 +160,11 @@ import org.wordpress.android.ui.prefs.SiteSettingsInterface; import org.wordpress.android.ui.reader.utils.ReaderUtilsWrapper; import org.wordpress.android.ui.stockmedia.StockMediaPickerActivity; +import org.wordpress.android.ui.stories.StoryRepositoryWrapper; import org.wordpress.android.ui.stories.prefs.StoriesPrefs; import org.wordpress.android.ui.stories.prefs.StoriesPrefs.RemoteMediaId; +import org.wordpress.android.ui.stories.usecase.LoadStoryFromStoriesPrefsUseCase; +import org.wordpress.android.ui.stories.usecase.LoadStoryFromStoriesPrefsUseCase.ReCreateStoryResult; import org.wordpress.android.ui.uploads.PostEvents; import org.wordpress.android.ui.uploads.UploadService; import org.wordpress.android.ui.uploads.UploadUtils; @@ -3002,52 +3005,29 @@ public void onTrackableEvent(TrackableEvent event, Map propertie } @Override public void onStoryComposerLoadRequested(ArrayList mediaFiles, String blockId) { - ArrayList tmpMediaIdsString = new ArrayList<>(); - boolean allStorySlidesAreEditable = true; - for (Object mediaFile : mediaFiles) { - long mediaIdLong = new Double(((HashMap) mediaFile).get("id").toString()).longValue(); - String mediaIdString = String.valueOf(mediaIdLong); - if (allStorySlidesAreEditable - && !StoriesPrefs.isValidSlide(this, mSite.getId(), new RemoteMediaId(mediaIdLong))) { - // flag this as soon as we find one media item not being really editable - allStorySlidesAreEditable = false; - } - tmpMediaIdsString.add(mediaIdString); - } + LoadStoryFromStoriesPrefsUseCase loadStoryFromStoriesPrefsUseCase = new LoadStoryFromStoriesPrefsUseCase( + new StoryRepositoryWrapper(), + mSite, + mMediaStore, + this + ); + ArrayList mediaIds = + loadStoryFromStoriesPrefsUseCase.getMediaIdsFromStoryBlockBridgeMediaFiles(mediaFiles); + boolean allStorySlidesAreEditable = loadStoryFromStoriesPrefsUseCase.areAllStorySlidesEditable(mSite, mediaIds); // now look for a Story in the StoryRepository that has all these frames and, if not found, let's // just build the Story object ourselves to keep these files arrangement - int storyIndex = StoryRepository.findStoryContainingStoryFrameItemsByIds(tmpMediaIdsString); + int storyIndex = StoryRepository.findStoryContainingStoryFrameItemsByIds(mediaIds); if (storyIndex == StoryRepository.DEFAULT_NONE_SELECTED) { - // the StoryRepository didn' have it but we have editable serialized slides so, + // the StoryRepository didn't have it but we have editable serialized slides so, // create a new Story from scratch with these deserialized StoryFrameItems - StoryRepository.loadStory(storyIndex); - storyIndex = StoryRepository.currentStoryIndex; - for (String mediaId : tmpMediaIdsString) { - StoryFrameItem storyFrameItem = StoriesPrefs.getSlideWithRemoteId( - this, - mSite.getId(), - new RemoteMediaId(Long.parseLong(mediaId)) - ); - if (storyFrameItem != null) { - StoryRepository.addStoryFrameItemToCurrentStory(storyFrameItem); - } else { - allStorySlidesAreEditable = false; - - // for this missing frame we'll create a new frame using the actual uploaded flattened media - ArrayList tmpMediaIdsLong = new ArrayList<>(); - tmpMediaIdsLong.add(Long.parseLong(mediaId)); - List mediaModelList = mMediaStore.getSiteMediaWithIds(mSite, tmpMediaIdsLong); - for (MediaModel mediaModel : mediaModelList) { - storyFrameItem = StoryFrameItem.Companion.getNewStoryFrameItemFromUri( - Uri.parse(mediaModel.getUrl()), - mediaModel.isVideo() - ); - storyFrameItem.setId(String.valueOf(mediaModel.getMediaId())); - StoryRepository.addStoryFrameItemToCurrentStory(storyFrameItem); - } - } + ReCreateStoryResult result = + loadStoryFromStoriesPrefsUseCase.loadOrReCreateStoryFromStoriesPrefs(mediaIds); + if (allStorySlidesAreEditable) { + // double check and override if we found at least one couldn't be inflated + allStorySlidesAreEditable = result.getAllStorySlidesAreEditable(); } + storyIndex = result.getStoryIndex(); } // Story instance loaded or re-created! Load it ActivityLauncher.editStoryForResult(this, mSite, storyIndex, allStorySlidesAreEditable, true); diff --git a/WordPress/src/main/java/org/wordpress/android/ui/stories/StoryRepositoryWrapper.kt b/WordPress/src/main/java/org/wordpress/android/ui/stories/StoryRepositoryWrapper.kt index ee6deefcb3eb..7b89cea9bbda 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/stories/StoryRepositoryWrapper.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/stories/StoryRepositoryWrapper.kt @@ -1,5 +1,7 @@ package org.wordpress.android.ui.stories +import com.wordpress.stories.compose.story.StoryFrameItem +import com.wordpress.stories.compose.story.StoryIndex import com.wordpress.stories.compose.story.StoryRepository import javax.inject.Inject @@ -7,4 +9,7 @@ class StoryRepositoryWrapper @Inject constructor() { fun setCurrentStoryTitle(title: String) = StoryRepository.setCurrentStoryTitle(title) fun getCurrentStoryThumbnailUrl() = StoryRepository.getCurrentStoryThumbnailUrl() fun getCurrentStoryTitle() = StoryRepository.getCurrentStoryTitle() + fun getCurrentStoryIndex(): StoryIndex = StoryRepository.currentStoryIndex + fun loadStory(storyIndex: StoryIndex) = StoryRepository.loadStory(storyIndex) + fun addStoryFrameItemToCurrentStory(item: StoryFrameItem) = StoryRepository.addStoryFrameItemToCurrentStory(item) } diff --git a/WordPress/src/main/java/org/wordpress/android/ui/stories/prefs/StoriesPrefs.kt b/WordPress/src/main/java/org/wordpress/android/ui/stories/prefs/StoriesPrefs.kt index 4dc3a8b850b8..82b924756a21 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/stories/prefs/StoriesPrefs.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/stories/prefs/StoriesPrefs.kt @@ -14,11 +14,11 @@ object StoriesPrefs { private const val KEY_PREFIX_REMOTE_MEDIA_ID = "r-" private fun buildSlideKey(siteId: Long, mediaId: RemoteMediaId): String { - return KEY_PREFIX_STORIES_SLIDE_ID + siteId.toString() + "-" + KEY_PREFIX_REMOTE_MEDIA_ID + mediaId.toString() + return KEY_PREFIX_STORIES_SLIDE_ID + siteId.toString() + "-" + KEY_PREFIX_REMOTE_MEDIA_ID + mediaId.mediaId.toString() } private fun buildSlideKey(siteId: Long, mediaId: LocalMediaId): String { - return KEY_PREFIX_STORIES_SLIDE_ID + siteId.toString() + "-" + KEY_PREFIX_LOCAL_MEDIA_ID + mediaId.toString() + return KEY_PREFIX_STORIES_SLIDE_ID + siteId.toString() + "-" + KEY_PREFIX_LOCAL_MEDIA_ID + mediaId.id.toString() } @JvmStatic diff --git a/WordPress/src/main/java/org/wordpress/android/ui/stories/usecase/LoadStoryFromStoriesPrefsUseCase.kt b/WordPress/src/main/java/org/wordpress/android/ui/stories/usecase/LoadStoryFromStoriesPrefsUseCase.kt new file mode 100644 index 000000000000..def87ba9aa2b --- /dev/null +++ b/WordPress/src/main/java/org/wordpress/android/ui/stories/usecase/LoadStoryFromStoriesPrefsUseCase.kt @@ -0,0 +1,91 @@ +package org.wordpress.android.ui.stories.usecase + +import android.content.Context +import android.net.Uri +import com.wordpress.stories.compose.story.StoryFrameItem +import com.wordpress.stories.compose.story.StoryIndex +import com.wordpress.stories.compose.story.StoryRepository +import dagger.Reusable +import org.wordpress.android.fluxc.model.MediaModel +import org.wordpress.android.fluxc.model.SiteModel +import org.wordpress.android.fluxc.store.MediaStore +import org.wordpress.android.ui.stories.StoryRepositoryWrapper +import org.wordpress.android.ui.stories.prefs.StoriesPrefs.RemoteMediaId +import org.wordpress.android.ui.stories.prefs.StoriesPrefs.getSlideWithRemoteId +import org.wordpress.android.ui.stories.prefs.StoriesPrefs.isValidSlide +import java.util.ArrayList +import java.util.HashMap +import javax.inject.Inject + +@Reusable +class LoadStoryFromStoriesPrefsUseCase @Inject constructor( + private val storyRepositoryWrapper: StoryRepositoryWrapper, + private val site: SiteModel, + private val mediaStore: MediaStore, + private val context: Context +) { + fun getMediaIdsFromStoryBlockBridgeMediaFiles(mediaFiles: ArrayList): ArrayList { + val mediaIds = ArrayList() + for (mediaFile in mediaFiles) { + val mediaIdLong = (mediaFile as HashMap)["id"] + .toString() + .toDouble() // this conversion is needed to strip off decimals that can come from RN + .toLong() + val mediaIdString = mediaIdLong.toString() + mediaIds.add(mediaIdString) + } + return mediaIds + } + + fun areAllStorySlidesEditable(site: SiteModel, mediaIds: ArrayList): Boolean { + for (mediaId in mediaIds) { + if (!isValidSlide(context, site.getId().toLong(), RemoteMediaId(mediaId.toLong()))) { + return false + } + } + return true + } + + fun loadOrReCreateStoryFromStoriesPrefs(mediaIds: ArrayList): ReCreateStoryResult { + // the StoryRepository didn't have it but we have editable serialized slides so, + // create a new Story from scratch with these deserialized StoryFrameItems + var allStorySlidesAreEditable: Boolean = true + var storyIndex = StoryRepository.DEFAULT_NONE_SELECTED + storyRepositoryWrapper.loadStory(storyIndex) + storyIndex = storyRepositoryWrapper.getCurrentStoryIndex() + for (mediaId in mediaIds) { + var storyFrameItem = getSlideWithRemoteId( + context, + site.getId().toLong(), + RemoteMediaId(mediaId.toLong()) + ) + if (storyFrameItem != null) { + storyRepositoryWrapper.addStoryFrameItemToCurrentStory(storyFrameItem) + } else { + allStorySlidesAreEditable = false + + // for this missing frame we'll create a new frame using the actual uploaded flattened media + val tmpMediaIdsLong = ArrayList() + tmpMediaIdsLong.add(mediaId.toLong()) + val mediaModelList: List = mediaStore.getSiteMediaWithIds( + site, + tmpMediaIdsLong + ) + for (mediaModel in mediaModelList) { + storyFrameItem = StoryFrameItem.getNewStoryFrameItemFromUri( + Uri.parse(mediaModel.url), + mediaModel.isVideo + ) + storyFrameItem.id = mediaModel.mediaId.toString() + storyRepositoryWrapper.addStoryFrameItemToCurrentStory(storyFrameItem) + } + } + } + + return ReCreateStoryResult(storyIndex, allStorySlidesAreEditable) + } + + data class ReCreateStoryResult(val storyIndex: StoryIndex, val allStorySlidesAreEditable: Boolean) +} + + diff --git a/libs/stories-android b/libs/stories-android index b9322a3f440b..68fc2ec21ccc 160000 --- a/libs/stories-android +++ b/libs/stories-android @@ -1 +1 @@ -Subproject commit b9322a3f440bd33b6b5d919e67617c60dacea36b +Subproject commit 68fc2ec21ccce9af40f89bca3dcc57a914dd291e From 448dffd9005b1be3c91d1af38e711b62f409ad25 Mon Sep 17 00:00:00 2001 From: Mario Zorz Date: Fri, 18 Sep 2020 09:56:48 -0300 Subject: [PATCH 20/47] added path for story media not fetched locally --- .../android/ui/posts/EditPostActivity.java | 50 +++++++++++++++++-- .../LoadStoryFromStoriesPrefsUseCase.kt | 26 ++++++---- WordPress/src/main/res/values/strings.xml | 2 + 3 files changed, 66 insertions(+), 12 deletions(-) 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 9987050575c7..ac5871c852fc 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 @@ -40,7 +40,6 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder; import com.google.android.material.snackbar.Snackbar; -import com.wordpress.stories.compose.story.StoryFrameItem; import com.wordpress.stories.compose.story.StoryRepository; import org.greenrobot.eventbus.EventBus; @@ -75,6 +74,7 @@ import org.wordpress.android.fluxc.action.AccountAction; import org.wordpress.android.fluxc.generated.AccountActionBuilder; import org.wordpress.android.fluxc.generated.EditorThemeActionBuilder; +import org.wordpress.android.fluxc.generated.MediaActionBuilder; import org.wordpress.android.fluxc.generated.PostActionBuilder; import org.wordpress.android.fluxc.generated.SiteActionBuilder; import org.wordpress.android.fluxc.model.AccountModel; @@ -95,9 +95,11 @@ import org.wordpress.android.fluxc.store.EditorThemeStore.FetchEditorThemePayload; import org.wordpress.android.fluxc.store.EditorThemeStore.OnEditorThemeChanged; import org.wordpress.android.fluxc.store.MediaStore; +import org.wordpress.android.fluxc.store.MediaStore.FetchMediaListPayload; import org.wordpress.android.fluxc.store.MediaStore.MediaError; import org.wordpress.android.fluxc.store.MediaStore.MediaErrorType; import org.wordpress.android.fluxc.store.MediaStore.OnMediaChanged; +import org.wordpress.android.fluxc.store.MediaStore.OnMediaListFetched; import org.wordpress.android.fluxc.store.MediaStore.OnMediaUploaded; import org.wordpress.android.fluxc.store.PostStore; import org.wordpress.android.fluxc.store.PostStore.OnPostChanged; @@ -185,6 +187,7 @@ import org.wordpress.android.util.LocaleManager; import org.wordpress.android.util.LocaleManagerWrapper; import org.wordpress.android.util.MediaUtils; +import org.wordpress.android.util.NetworkUtils; import org.wordpress.android.util.PermissionUtils; import org.wordpress.android.util.ReblogUtils; import org.wordpress.android.util.ShortcutUtils; @@ -611,6 +614,13 @@ protected void onCreate(Bundle savedInstanceState) { } if (!mIsNewPost) { + // if we are opening an existing Post, and it contains a Story block, pre-fetch the media in case + // the user wants to edit the block (we'll need to download it first if the slides images weren't + // created on this device) + if (PostUtils.contentContainsWPStoryGutenbergBlocks(mEditPostRepository.getPost().getContent())) { + fetchMediaList(); + } + // if we are opening a Post for which an error notification exists, we need to remove it from the dashboard // to prevent the user from tapping RETRY on a Post that is being currently edited UploadService.cancelFinalNotification(this, mEditPostRepository.getPost()); @@ -3014,6 +3024,7 @@ public void onTrackableEvent(TrackableEvent event, Map propertie ArrayList mediaIds = loadStoryFromStoriesPrefsUseCase.getMediaIdsFromStoryBlockBridgeMediaFiles(mediaFiles); boolean allStorySlidesAreEditable = loadStoryFromStoriesPrefsUseCase.areAllStorySlidesEditable(mSite, mediaIds); + boolean failedLoadingOrReCreatingStory = false; // now look for a Story in the StoryRepository that has all these frames and, if not found, let's // just build the Story object ourselves to keep these files arrangement @@ -3023,14 +3034,29 @@ public void onTrackableEvent(TrackableEvent event, Map propertie // create a new Story from scratch with these deserialized StoryFrameItems ReCreateStoryResult result = loadStoryFromStoriesPrefsUseCase.loadOrReCreateStoryFromStoriesPrefs(mediaIds); + failedLoadingOrReCreatingStory = result.getNoSlidesLoaded(); if (allStorySlidesAreEditable) { // double check and override if we found at least one couldn't be inflated allStorySlidesAreEditable = result.getAllStorySlidesAreEditable(); } storyIndex = result.getStoryIndex(); } - // Story instance loaded or re-created! Load it - ActivityLauncher.editStoryForResult(this, mSite, storyIndex, allStorySlidesAreEditable, true); + + if (!failedLoadingOrReCreatingStory) { + // Story instance loaded or re-created! Load it + ActivityLauncher.editStoryForResult(this, mSite, storyIndex, allStorySlidesAreEditable, true); + } else { + // unfortunately we couldn't even load the remote media Ids indicated by the StoryBLock so we can't allow + // editing at this time :( + AlertDialog.Builder builder = new MaterialAlertDialogBuilder(this); + builder.setTitle(getString(R.string.dialog_edit_story_unavailable_title)); + builder.setMessage(getString(R.string.dialog_edit_story_unavailable_message)); + builder.setPositiveButton(R.string.yes, (dialog, id) -> { + dialog.dismiss(); + }); + AlertDialog dialog = builder.create(); + dialog.show(); + } } // FluxC events @@ -3065,6 +3091,13 @@ public void onMediaUploaded(OnMediaUploaded event) { // FluxC events + @SuppressWarnings("unused") + @Subscribe(threadMode = ThreadMode.MAIN) + public void onMediaListFetched(OnMediaListFetched event) { + // no op - we don't need to check anything just now, but declaring the method so it's + // clear we make a request to FetchMedia in this class. + } + @SuppressWarnings("unused") @Subscribe(threadMode = ThreadMode.MAIN) public void onPostChanged(OnPostChanged event) { @@ -3190,6 +3223,17 @@ private void refreshEditorTheme() { mDispatcher.dispatch(EditorThemeActionBuilder.newFetchEditorThemeAction(payload)); } + private void fetchMediaList() { + // do not refresh if there is no network + if (!NetworkUtils.isNetworkAvailable(this)) { + return; + } + FetchMediaListPayload payload = + new FetchMediaListPayload(mSite, MediaStore.DEFAULT_NUM_MEDIA_PER_FETCH, false); + mDispatcher.dispatch(MediaActionBuilder.newFetchMediaListAction(payload)); + } + + @SuppressWarnings("unused") @Subscribe(threadMode = ThreadMode.MAIN_ORDERED) public void onEditorThemeChanged(OnEditorThemeChanged event) { diff --git a/WordPress/src/main/java/org/wordpress/android/ui/stories/usecase/LoadStoryFromStoriesPrefsUseCase.kt b/WordPress/src/main/java/org/wordpress/android/ui/stories/usecase/LoadStoryFromStoriesPrefsUseCase.kt index def87ba9aa2b..07ad84298e8d 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/stories/usecase/LoadStoryFromStoriesPrefsUseCase.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/stories/usecase/LoadStoryFromStoriesPrefsUseCase.kt @@ -50,6 +50,7 @@ class LoadStoryFromStoriesPrefsUseCase @Inject constructor( // the StoryRepository didn't have it but we have editable serialized slides so, // create a new Story from scratch with these deserialized StoryFrameItems var allStorySlidesAreEditable: Boolean = true + var noSlidesLoaded = false var storyIndex = StoryRepository.DEFAULT_NONE_SELECTED storyRepositoryWrapper.loadStory(storyIndex) storyIndex = storyRepositoryWrapper.getCurrentStoryIndex() @@ -71,21 +72,28 @@ class LoadStoryFromStoriesPrefsUseCase @Inject constructor( site, tmpMediaIdsLong ) - for (mediaModel in mediaModelList) { - storyFrameItem = StoryFrameItem.getNewStoryFrameItemFromUri( - Uri.parse(mediaModel.url), - mediaModel.isVideo - ) - storyFrameItem.id = mediaModel.mediaId.toString() - storyRepositoryWrapper.addStoryFrameItemToCurrentStory(storyFrameItem) + if (mediaModelList.size == 0) { + noSlidesLoaded = true + } else { + for (mediaModel in mediaModelList) { + storyFrameItem = StoryFrameItem.getNewStoryFrameItemFromUri( + Uri.parse(mediaModel.url), + mediaModel.isVideo + ) + storyFrameItem.id = mediaModel.mediaId.toString() + storyRepositoryWrapper.addStoryFrameItemToCurrentStory(storyFrameItem) + } } } } - return ReCreateStoryResult(storyIndex, allStorySlidesAreEditable) + return ReCreateStoryResult(storyIndex, allStorySlidesAreEditable, noSlidesLoaded) } - data class ReCreateStoryResult(val storyIndex: StoryIndex, val allStorySlidesAreEditable: Boolean) + data class ReCreateStoryResult( + val storyIndex: StoryIndex, + val allStorySlidesAreEditable: Boolean, + val noSlidesLoaded: Boolean) } diff --git a/WordPress/src/main/res/values/strings.xml b/WordPress/src/main/res/values/strings.xml index 4d066ad74c38..a4dc6e748ade 100644 --- a/WordPress/src/main/res/values/strings.xml +++ b/WordPress/src/main/res/values/strings.xml @@ -2874,6 +2874,8 @@ Limited Story Editing This story was edited on a different device and the ability to edit certain objects may be limited. + Can\'t edit Story + This story was created on a different device and can\'t be edited at this moment. Capture Flip camera Flash From 190b1b7a6f4d71a441dfb8e0744cdf3dcfb4ed79 Mon Sep 17 00:00:00 2001 From: Mario Zorz Date: Fri, 18 Sep 2020 10:39:15 -0300 Subject: [PATCH 21/47] updated stories lib hash --- libs/stories-android | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/stories-android b/libs/stories-android index 68fc2ec21ccc..a44d8e2937d9 160000 --- a/libs/stories-android +++ b/libs/stories-android @@ -1 +1 @@ -Subproject commit 68fc2ec21ccce9af40f89bca3dcc57a914dd291e +Subproject commit a44d8e2937d97ac1a04670add18869c99304b6a0 From 4698adefd2cc5a7eb8a880fda0d3b13110972d56 Mon Sep 17 00:00:00 2001 From: Mario Zorz Date: Fri, 18 Sep 2020 10:51:14 -0300 Subject: [PATCH 22/47] removed unused imports --- .../java/org/wordpress/android/ui/posts/EditPostActivity.java | 2 -- 1 file changed, 2 deletions(-) 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 ac5871c852fc..e1ea792be876 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 @@ -163,8 +163,6 @@ import org.wordpress.android.ui.reader.utils.ReaderUtilsWrapper; import org.wordpress.android.ui.stockmedia.StockMediaPickerActivity; import org.wordpress.android.ui.stories.StoryRepositoryWrapper; -import org.wordpress.android.ui.stories.prefs.StoriesPrefs; -import org.wordpress.android.ui.stories.prefs.StoriesPrefs.RemoteMediaId; import org.wordpress.android.ui.stories.usecase.LoadStoryFromStoriesPrefsUseCase; import org.wordpress.android.ui.stories.usecase.LoadStoryFromStoriesPrefsUseCase.ReCreateStoryResult; import org.wordpress.android.ui.uploads.PostEvents; From 143eb2e0d1e14ebea4e5961374c909711b660177 Mon Sep 17 00:00:00 2001 From: Mario Zorz Date: Fri, 18 Sep 2020 10:55:44 -0300 Subject: [PATCH 23/47] updated dialog OK button label --- .../java/org/wordpress/android/ui/posts/EditPostActivity.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 e1ea792be876..ecda0a9db427 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 @@ -3049,7 +3049,7 @@ public void onTrackableEvent(TrackableEvent event, Map propertie AlertDialog.Builder builder = new MaterialAlertDialogBuilder(this); builder.setTitle(getString(R.string.dialog_edit_story_unavailable_title)); builder.setMessage(getString(R.string.dialog_edit_story_unavailable_message)); - builder.setPositiveButton(R.string.yes, (dialog, id) -> { + builder.setPositiveButton(R.string.dialog_button_ok, (dialog, id) -> { dialog.dismiss(); }); AlertDialog dialog = builder.create(); From 587960f7252af94373e4c687779b4be2aff1b973 Mon Sep 17 00:00:00 2001 From: Mario Zorz Date: Fri, 18 Sep 2020 11:15:30 -0300 Subject: [PATCH 24/47] fixed lint warnings --- .../org/wordpress/android/ui/stories/prefs/StoriesPrefs.kt | 6 ++++-- .../ui/stories/usecase/LoadStoryFromStoriesPrefsUseCase.kt | 5 ++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/stories/prefs/StoriesPrefs.kt b/WordPress/src/main/java/org/wordpress/android/ui/stories/prefs/StoriesPrefs.kt index 82b924756a21..b2c88bb789c2 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/stories/prefs/StoriesPrefs.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/stories/prefs/StoriesPrefs.kt @@ -14,11 +14,13 @@ object StoriesPrefs { private const val KEY_PREFIX_REMOTE_MEDIA_ID = "r-" private fun buildSlideKey(siteId: Long, mediaId: RemoteMediaId): String { - return KEY_PREFIX_STORIES_SLIDE_ID + siteId.toString() + "-" + KEY_PREFIX_REMOTE_MEDIA_ID + mediaId.mediaId.toString() + return KEY_PREFIX_STORIES_SLIDE_ID + siteId.toString() + "-" + + KEY_PREFIX_REMOTE_MEDIA_ID + mediaId.mediaId.toString() } private fun buildSlideKey(siteId: Long, mediaId: LocalMediaId): String { - return KEY_PREFIX_STORIES_SLIDE_ID + siteId.toString() + "-" + KEY_PREFIX_LOCAL_MEDIA_ID + mediaId.id.toString() + return KEY_PREFIX_STORIES_SLIDE_ID + siteId.toString() + "-" + + KEY_PREFIX_LOCAL_MEDIA_ID + mediaId.id.toString() } @JvmStatic diff --git a/WordPress/src/main/java/org/wordpress/android/ui/stories/usecase/LoadStoryFromStoriesPrefsUseCase.kt b/WordPress/src/main/java/org/wordpress/android/ui/stories/usecase/LoadStoryFromStoriesPrefsUseCase.kt index 07ad84298e8d..ca3fe97647a5 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/stories/usecase/LoadStoryFromStoriesPrefsUseCase.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/stories/usecase/LoadStoryFromStoriesPrefsUseCase.kt @@ -93,7 +93,6 @@ class LoadStoryFromStoriesPrefsUseCase @Inject constructor( data class ReCreateStoryResult( val storyIndex: StoryIndex, val allStorySlidesAreEditable: Boolean, - val noSlidesLoaded: Boolean) + val noSlidesLoaded: Boolean + ) } - - From 95903d9105055fd638fd792a99d27a5af7458fb9 Mon Sep 17 00:00:00 2001 From: Mario Zorz Date: Thu, 1 Oct 2020 16:32:39 -0300 Subject: [PATCH 25/47] updated commit hash for mobile gutenberg --- libs/gutenberg-mobile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/gutenberg-mobile b/libs/gutenberg-mobile index 00a0cc0b7e84..b2685445fa4f 160000 --- a/libs/gutenberg-mobile +++ b/libs/gutenberg-mobile @@ -1 +1 @@ -Subproject commit 00a0cc0b7e849521595d428d5161dea557aa29e9 +Subproject commit b2685445fa4fda8b03799a5a71606adb348bd12b From 8311a30867d6cbcf75a2e33d58f95c8c05b88333 Mon Sep 17 00:00:00 2001 From: Mario Zorz Date: Thu, 1 Oct 2020 17:24:56 -0300 Subject: [PATCH 26/47] updated gutenbeg-mobile commit hash --- libs/gutenberg-mobile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/gutenberg-mobile b/libs/gutenberg-mobile index b2685445fa4f..33f2addaab7f 160000 --- a/libs/gutenberg-mobile +++ b/libs/gutenberg-mobile @@ -1 +1 @@ -Subproject commit b2685445fa4fda8b03799a5a71606adb348bd12b +Subproject commit 33f2addaab7f5338f791d968680cb6f1e97b4ffa From 111378b765f816aa297e0917fc3434cb8f7f667b Mon Sep 17 00:00:00 2001 From: Mario Zorz Date: Thu, 1 Oct 2020 17:40:57 -0300 Subject: [PATCH 27/47] added missing method declaration --- .../org/wordpress/android/ui/stories/StoryComposerActivity.kt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/stories/StoryComposerActivity.kt b/WordPress/src/main/java/org/wordpress/android/ui/stories/StoryComposerActivity.kt index dfa611a75fbb..bcf70b56ff64 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/stories/StoryComposerActivity.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/stories/StoryComposerActivity.kt @@ -414,6 +414,10 @@ class StoryComposerActivity : ComposeLoopFrameActivity(), } } + override fun onFrameRemove(storyIndex: StoryIndex, storyFrameIndex: Int) { + // TODO will implement later + } + private fun openPrepublishingBottomSheet() { val fragment = supportFragmentManager.findFragmentByTag(PrepublishingBottomSheetFragment.TAG) if (fragment == null) { From 0f4d87c25bde9da952d91fa9a5902a189acc0ed2 Mon Sep 17 00:00:00 2001 From: Mario Zorz Date: Wed, 7 Oct 2020 09:15:05 -0300 Subject: [PATCH 28/47] refactored onStoryComposerLoadRequested logic into LoadStoryFromStoriesPrefsUseCase --- .../android/ui/posts/EditPostActivity.java | 38 ++++--------------- .../ui/stories/StoryRepositoryWrapper.kt | 4 +- .../android/ui/stories/prefs/StoriesPrefs.kt | 5 --- .../LoadStoryFromStoriesPrefsUseCase.kt | 27 ++++++++++++- libs/stories-android | 2 +- 5 files changed, 36 insertions(+), 40 deletions(-) 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 916f8aa71075..ea0096acf06c 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 @@ -41,7 +41,6 @@ import com.google.android.material.appbar.AppBarLayout; import com.google.android.material.dialog.MaterialAlertDialogBuilder; import com.google.android.material.snackbar.Snackbar; -import com.wordpress.stories.compose.story.StoryRepository; import org.greenrobot.eventbus.EventBus; import org.greenrobot.eventbus.Subscribe; @@ -163,7 +162,6 @@ import org.wordpress.android.ui.prefs.SiteSettingsInterface; import org.wordpress.android.ui.reader.utils.ReaderUtilsWrapper; import org.wordpress.android.ui.stockmedia.StockMediaPickerActivity; -import org.wordpress.android.ui.stories.StoryRepositoryWrapper; import org.wordpress.android.ui.stories.usecase.LoadStoryFromStoriesPrefsUseCase; import org.wordpress.android.ui.stories.usecase.LoadStoryFromStoriesPrefsUseCase.ReCreateStoryResult; import org.wordpress.android.ui.uploads.PostEvents; @@ -392,6 +390,7 @@ enum RestartEditorOptions { @Inject ModalLayoutPickerFeatureConfig mModalLayoutPickerFeatureConfig; @Inject CrashLogging mCrashLogging; @Inject MediaPickerLauncher mMediaPickerLauncher; + @Inject LoadStoryFromStoriesPrefsUseCase mLoadStoryFromStoriesPrefsUseCase; private StorePostViewModel mViewModel; @@ -3066,36 +3065,13 @@ public void onTrackableEvent(TrackableEvent event, Map propertie } @Override public void onStoryComposerLoadRequested(ArrayList mediaFiles, String blockId) { - LoadStoryFromStoriesPrefsUseCase loadStoryFromStoriesPrefsUseCase = new LoadStoryFromStoriesPrefsUseCase( - new StoryRepositoryWrapper(), - mSite, - mMediaStore, - this - ); - ArrayList mediaIds = - loadStoryFromStoriesPrefsUseCase.getMediaIdsFromStoryBlockBridgeMediaFiles(mediaFiles); - boolean allStorySlidesAreEditable = loadStoryFromStoriesPrefsUseCase.areAllStorySlidesEditable(mSite, mediaIds); - boolean failedLoadingOrReCreatingStory = false; - - // now look for a Story in the StoryRepository that has all these frames and, if not found, let's - // just build the Story object ourselves to keep these files arrangement - int storyIndex = StoryRepository.findStoryContainingStoryFrameItemsByIds(mediaIds); - if (storyIndex == StoryRepository.DEFAULT_NONE_SELECTED) { - // the StoryRepository didn't have it but we have editable serialized slides so, - // create a new Story from scratch with these deserialized StoryFrameItems - ReCreateStoryResult result = - loadStoryFromStoriesPrefsUseCase.loadOrReCreateStoryFromStoriesPrefs(mediaIds); - failedLoadingOrReCreatingStory = result.getNoSlidesLoaded(); - if (allStorySlidesAreEditable) { - // double check and override if we found at least one couldn't be inflated - allStorySlidesAreEditable = result.getAllStorySlidesAreEditable(); - } - storyIndex = result.getStoryIndex(); - } - - if (!failedLoadingOrReCreatingStory) { + ReCreateStoryResult result = mLoadStoryFromStoriesPrefsUseCase + .loadStoryFromMemoryOrRecreateFromPrefs(mSite, mediaFiles); + if (!result.getNoSlidesLoaded()) { // Story instance loaded or re-created! Load it - ActivityLauncher.editStoryForResult(this, mSite, storyIndex, allStorySlidesAreEditable, true); + ActivityLauncher.editStoryForResult( + this, mSite, result.getStoryIndex(), result.getAllStorySlidesAreEditable(), true + ); } else { // unfortunately we couldn't even load the remote media Ids indicated by the StoryBLock so we can't allow // editing at this time :( diff --git a/WordPress/src/main/java/org/wordpress/android/ui/stories/StoryRepositoryWrapper.kt b/WordPress/src/main/java/org/wordpress/android/ui/stories/StoryRepositoryWrapper.kt index 7b89cea9bbda..570db83441ed 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/stories/StoryRepositoryWrapper.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/stories/StoryRepositoryWrapper.kt @@ -12,4 +12,6 @@ class StoryRepositoryWrapper @Inject constructor() { fun getCurrentStoryIndex(): StoryIndex = StoryRepository.currentStoryIndex fun loadStory(storyIndex: StoryIndex) = StoryRepository.loadStory(storyIndex) fun addStoryFrameItemToCurrentStory(item: StoryFrameItem) = StoryRepository.addStoryFrameItemToCurrentStory(item) -} + fun findStoryContainingStoryFrameItemsByIds(ids: ArrayList) = + StoryRepository.findStoryContainingStoryFrameItemsByIds(ids) + } diff --git a/WordPress/src/main/java/org/wordpress/android/ui/stories/prefs/StoriesPrefs.kt b/WordPress/src/main/java/org/wordpress/android/ui/stories/prefs/StoriesPrefs.kt index b2c88bb789c2..58024377eb65 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/stories/prefs/StoriesPrefs.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/stories/prefs/StoriesPrefs.kt @@ -23,13 +23,11 @@ object StoriesPrefs { KEY_PREFIX_LOCAL_MEDIA_ID + mediaId.id.toString() } - @JvmStatic fun checkSlideIdExists(context: Context, siteId: Long, mediaId: RemoteMediaId): Boolean { val slideIdKey = buildSlideKey(siteId, mediaId) return PreferenceManager.getDefaultSharedPreferences(context).contains(slideIdKey) } - @JvmStatic fun checkSlideOriginalBackgroundMediaExists(context: Context, siteId: Long, mediaId: RemoteMediaId): Boolean { val storyFrameItem: StoryFrameItem? = getSlideWithRemoteId(context, siteId, mediaId) storyFrameItem?.let { frame -> @@ -73,7 +71,6 @@ object StoriesPrefs { editor.apply() } - @JvmStatic fun isValidSlide(context: Context, siteId: Long, mediaId: RemoteMediaId): Boolean { return checkSlideIdExists(context, siteId, mediaId) && checkSlideOriginalBackgroundMediaExists(context, siteId, mediaId) @@ -83,7 +80,6 @@ object StoriesPrefs { return PreferenceManager.getDefaultSharedPreferences(context).getString(slideIdKey, null) } - @JvmStatic fun getSlideWithRemoteId(context: Context, siteId: Long, mediaId: RemoteMediaId): StoryFrameItem? { val jsonSlide = getSlideJson(context, buildSlideKey(siteId, mediaId)) jsonSlide?.let { @@ -91,7 +87,6 @@ object StoriesPrefs { } ?: return null } - @JvmStatic fun getSlideWithLocalId(context: Context, siteId: Long, mediaId: LocalMediaId): StoryFrameItem? { val jsonSlide = getSlideJson(context, buildSlideKey(siteId, mediaId)) jsonSlide?.let { diff --git a/WordPress/src/main/java/org/wordpress/android/ui/stories/usecase/LoadStoryFromStoriesPrefsUseCase.kt b/WordPress/src/main/java/org/wordpress/android/ui/stories/usecase/LoadStoryFromStoriesPrefsUseCase.kt index ca3fe97647a5..978f52f750dd 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/stories/usecase/LoadStoryFromStoriesPrefsUseCase.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/stories/usecase/LoadStoryFromStoriesPrefsUseCase.kt @@ -20,7 +20,6 @@ import javax.inject.Inject @Reusable class LoadStoryFromStoriesPrefsUseCase @Inject constructor( private val storyRepositoryWrapper: StoryRepositoryWrapper, - private val site: SiteModel, private val mediaStore: MediaStore, private val context: Context ) { @@ -46,7 +45,7 @@ class LoadStoryFromStoriesPrefsUseCase @Inject constructor( return true } - fun loadOrReCreateStoryFromStoriesPrefs(mediaIds: ArrayList): ReCreateStoryResult { + private fun loadOrReCreateStoryFromStoriesPrefs(site: SiteModel, mediaIds: ArrayList): ReCreateStoryResult { // the StoryRepository didn't have it but we have editable serialized slides so, // create a new Story from scratch with these deserialized StoryFrameItems var allStorySlidesAreEditable: Boolean = true @@ -90,6 +89,30 @@ class LoadStoryFromStoriesPrefsUseCase @Inject constructor( return ReCreateStoryResult(storyIndex, allStorySlidesAreEditable, noSlidesLoaded) } + fun loadStoryFromMemoryOrRecreateFromPrefs(site: SiteModel, mediaFiles: ArrayList): ReCreateStoryResult { + val mediaIds = getMediaIdsFromStoryBlockBridgeMediaFiles( + mediaFiles + ) + var allStorySlidesAreEditable = areAllStorySlidesEditable( + site, + mediaIds + ) + + // now look for a Story in the StoryRepository that has all these frames and, if not found, let's + // just build the Story object ourselves to keep these files arrangement + var storyIndex = storyRepositoryWrapper.findStoryContainingStoryFrameItemsByIds(mediaIds) + if (storyIndex == StoryRepository.DEFAULT_NONE_SELECTED) { + // the StoryRepository didn't have it but we have editable serialized slides so, + // create a new Story from scratch with these deserialized StoryFrameItems + return loadOrReCreateStoryFromStoriesPrefs( + site, + mediaIds + ) + } else { + return ReCreateStoryResult(storyIndex, allStorySlidesAreEditable, false) + } + } + data class ReCreateStoryResult( val storyIndex: StoryIndex, val allStorySlidesAreEditable: Boolean, diff --git a/libs/stories-android b/libs/stories-android index 2082538ec174..7966a7c7364c 160000 --- a/libs/stories-android +++ b/libs/stories-android @@ -1 +1 @@ -Subproject commit 2082538ec174aadf312ecd0fb2c5272efa33381b +Subproject commit 7966a7c7364c347170ba6986c8259018d69f9009 From 834be35827697bea6e67e62bc0394bf34f8341b3 Mon Sep 17 00:00:00 2001 From: mzorz Date: Wed, 7 Oct 2020 09:24:55 -0300 Subject: [PATCH 29/47] Update WordPress/src/main/java/org/wordpress/android/ui/posts/EditPostActivity.java Co-authored-by: Joel Dean --- .../java/org/wordpress/android/ui/posts/EditPostActivity.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 ea0096acf06c..168f3ad232c9 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 @@ -3073,7 +3073,7 @@ public void onTrackableEvent(TrackableEvent event, Map propertie this, mSite, result.getStoryIndex(), result.getAllStorySlidesAreEditable(), true ); } else { - // unfortunately we couldn't even load the remote media Ids indicated by the StoryBLock so we can't allow + // unfortunately we couldn't even load the remote media Ids indicated by the StoryBlock so we can't allow // editing at this time :( AlertDialog.Builder builder = new MaterialAlertDialogBuilder(this); builder.setTitle(getString(R.string.dialog_edit_story_unavailable_title)); From 10a8a9e9f9b83f0e8b2e54c096a1d1218b61c78a Mon Sep 17 00:00:00 2001 From: mzorz Date: Wed, 7 Oct 2020 09:27:40 -0300 Subject: [PATCH 30/47] Update WordPress/src/main/java/org/wordpress/android/ui/posts/editor/media/AddLocalMediaToPostUseCase.kt Co-authored-by: Joel Dean --- .../android/ui/posts/editor/media/AddLocalMediaToPostUseCase.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/posts/editor/media/AddLocalMediaToPostUseCase.kt b/WordPress/src/main/java/org/wordpress/android/ui/posts/editor/media/AddLocalMediaToPostUseCase.kt index ccb97c3de05c..6deae0e3139f 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/posts/editor/media/AddLocalMediaToPostUseCase.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/posts/editor/media/AddLocalMediaToPostUseCase.kt @@ -69,7 +69,7 @@ class AddLocalMediaToPostUseCase @Inject constructor( // here we pass a map of "old" (before optimisation) Uris to the new MediaModels which contain // both the mediaModel ids and the optimized media URLs. - // this way, the listener will be able to process from other models potining to the old URLs + // this way, the listener will be able to process from other models pointing to the old URLs // and make any needed updates editorMediaListener.onMediaModelsCreatedFromOptimizedUris( uriList.zip(createMediaModelsResult.mediaModels).toMap() From f6d92d293ce64df43f5e5f08705c655203d9c74f Mon Sep 17 00:00:00 2001 From: Mario Zorz Date: Wed, 7 Oct 2020 10:16:42 -0300 Subject: [PATCH 31/47] made StoriesPrefs a @Singleton annotated injectable class --- .../stories/SaveStoryGutenbergBlockUseCase.kt | 14 ++--- .../media/StoryMediaSaveUploadBridge.kt | 4 +- .../android/ui/stories/prefs/StoriesPrefs.kt | 57 +++++++++++-------- .../LoadStoryFromStoriesPrefsUseCase.kt | 13 ++--- .../ui/uploads/MediaUploadReadyProcessor.java | 5 +- 5 files changed, 49 insertions(+), 44 deletions(-) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/stories/SaveStoryGutenbergBlockUseCase.kt b/WordPress/src/main/java/org/wordpress/android/ui/stories/SaveStoryGutenbergBlockUseCase.kt index e9c22e279c03..d8b43ff0a138 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/stories/SaveStoryGutenbergBlockUseCase.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/stories/SaveStoryGutenbergBlockUseCase.kt @@ -1,7 +1,6 @@ package org.wordpress.android.ui.stories import com.google.gson.Gson -import org.wordpress.android.WordPress import org.wordpress.android.fluxc.model.PostModel import org.wordpress.android.ui.posts.EditPostRepository import org.wordpress.android.ui.stories.prefs.StoriesPrefs @@ -11,7 +10,9 @@ import org.wordpress.android.util.StringUtils import org.wordpress.android.util.helpers.MediaFile import javax.inject.Inject -class SaveStoryGutenbergBlockUseCase @Inject constructor() { +class SaveStoryGutenbergBlockUseCase @Inject constructor( + private val storiesPrefs: StoriesPrefs +) { fun buildJetpackStoryBlockInPost( editPostRepository: EditPostRepository, mediaFiles: Map @@ -66,21 +67,18 @@ class SaveStoryGutenbergBlockUseCase @Inject constructor() { val localIdKey = mediaFile.id.toLong() val remoteIdKey = mediaFile.mediaId.toLong() val localSiteId = post.localSiteId.toLong() - StoriesPrefs.getSlideWithLocalId( - WordPress.getContext(), + storiesPrefs.getSlideWithLocalId( localSiteId, LocalMediaId(localIdKey) )?.let { it.id = mediaFile.mediaId // update the StoryFrameItem id to hold the same value as the remote mediaID - StoriesPrefs.saveSlideWithRemoteId( - WordPress.getContext(), + storiesPrefs.saveSlideWithRemoteId( localSiteId, RemoteMediaId(remoteIdKey), // use the new mediaId as key it ) // now delete the old entry - StoriesPrefs.deleteSlideWithLocalId( - WordPress.getContext(), + storiesPrefs.deleteSlideWithLocalId( localSiteId, LocalMediaId(localIdKey) ) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/stories/media/StoryMediaSaveUploadBridge.kt b/WordPress/src/main/java/org/wordpress/android/ui/stories/media/StoryMediaSaveUploadBridge.kt index d45c809556d7..7c29f1928d49 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/stories/media/StoryMediaSaveUploadBridge.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/stories/media/StoryMediaSaveUploadBridge.kt @@ -50,6 +50,7 @@ import kotlin.coroutines.CoroutineContext class StoryMediaSaveUploadBridge @Inject constructor( private val addLocalMediaToPostUseCase: AddLocalMediaToPostUseCase, private val savePostToDbUseCase: SavePostToDbUseCase, + private val storiesPrefs: StoriesPrefs, private val uploadService: UploadServiceFacade, private val networkUtils: NetworkUtilsWrapper, private val postUtils: PostUtilsWrapper, @@ -188,8 +189,7 @@ class StoryMediaSaveUploadBridge @Inject constructor( val mediaModel = oldUriToMediaFiles.get(Uri.fromFile(frame.composedFrameFile)) mediaModel?.let { frame.id = it.id.toString() - StoriesPrefs.saveSlideWithLocalId( - appContext, + storiesPrefs.saveSlideWithLocalId( it.localSiteId.toLong(), // use the local id to save the original, will be replaced later // with mediaModel.mediaId after uploading to the remote site diff --git a/WordPress/src/main/java/org/wordpress/android/ui/stories/prefs/StoriesPrefs.kt b/WordPress/src/main/java/org/wordpress/android/ui/stories/prefs/StoriesPrefs.kt index 58024377eb65..f4bae3b9bb6c 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/stories/prefs/StoriesPrefs.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/stories/prefs/StoriesPrefs.kt @@ -7,11 +7,18 @@ import com.wordpress.stories.compose.story.StoryFrameItem import com.wordpress.stories.compose.story.StoryFrameItem.BackgroundSource.FileBackgroundSource import com.wordpress.stories.compose.story.StoryFrameItem.BackgroundSource.UriBackgroundSource import com.wordpress.stories.compose.story.StorySerializerUtils - -object StoriesPrefs { - private const val KEY_PREFIX_STORIES_SLIDE_ID = "story_slide_id-" - private const val KEY_PREFIX_LOCAL_MEDIA_ID = "l-" - private const val KEY_PREFIX_REMOTE_MEDIA_ID = "r-" +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class StoriesPrefs @Inject constructor( + private val context: Context +) { + companion object { + private val KEY_PREFIX_STORIES_SLIDE_ID = "story_slide_id-" + private val KEY_PREFIX_LOCAL_MEDIA_ID = "l-" + private val KEY_PREFIX_REMOTE_MEDIA_ID = "r-" + } private fun buildSlideKey(siteId: Long, mediaId: RemoteMediaId): String { return KEY_PREFIX_STORIES_SLIDE_ID + siteId.toString() + "-" + @@ -23,13 +30,13 @@ object StoriesPrefs { KEY_PREFIX_LOCAL_MEDIA_ID + mediaId.id.toString() } - fun checkSlideIdExists(context: Context, siteId: Long, mediaId: RemoteMediaId): Boolean { + fun checkSlideIdExists(siteId: Long, mediaId: RemoteMediaId): Boolean { val slideIdKey = buildSlideKey(siteId, mediaId) return PreferenceManager.getDefaultSharedPreferences(context).contains(slideIdKey) } - fun checkSlideOriginalBackgroundMediaExists(context: Context, siteId: Long, mediaId: RemoteMediaId): Boolean { - val storyFrameItem: StoryFrameItem? = getSlideWithRemoteId(context, siteId, mediaId) + fun checkSlideOriginalBackgroundMediaExists(siteId: Long, mediaId: RemoteMediaId): Boolean { + val storyFrameItem: StoryFrameItem? = getSlideWithRemoteId(siteId, mediaId) storyFrameItem?.let { frame -> // now check the background media exists or is accessible on this device frame.source.let { source -> @@ -39,7 +46,7 @@ object StoriesPrefs { } ?: return false } else if (source is UriBackgroundSource) { source.contentUri?.let { - return isUriAccessible(it, context) + return isUriAccessible(it) } ?: return false } } @@ -47,7 +54,7 @@ object StoriesPrefs { return false } - private fun isUriAccessible(uri: Uri, context: Context): Boolean { + private fun isUriAccessible(uri: Uri): Boolean { if (uri.toString().startsWith("http")) { // TODO: assume it'll be accessible - we'll figure out later // potentially force external download using MediaUtils.downloadExternalMedia() here to ensure @@ -65,53 +72,53 @@ object StoriesPrefs { return false } - private fun saveSlide(context: Context, slideIdKey: String, storySlideJson: String) { + private fun saveSlide(slideIdKey: String, storySlideJson: String) { val editor = PreferenceManager.getDefaultSharedPreferences(context).edit() editor.putString(slideIdKey, storySlideJson) editor.apply() } - fun isValidSlide(context: Context, siteId: Long, mediaId: RemoteMediaId): Boolean { - return checkSlideIdExists(context, siteId, mediaId) && - checkSlideOriginalBackgroundMediaExists(context, siteId, mediaId) + fun isValidSlide(siteId: Long, mediaId: RemoteMediaId): Boolean { + return checkSlideIdExists(siteId, mediaId) && + checkSlideOriginalBackgroundMediaExists(siteId, mediaId) } - private fun getSlideJson(context: Context, slideIdKey: String): String? { + private fun getSlideJson(slideIdKey: String): String? { return PreferenceManager.getDefaultSharedPreferences(context).getString(slideIdKey, null) } - fun getSlideWithRemoteId(context: Context, siteId: Long, mediaId: RemoteMediaId): StoryFrameItem? { - val jsonSlide = getSlideJson(context, buildSlideKey(siteId, mediaId)) + fun getSlideWithRemoteId(siteId: Long, mediaId: RemoteMediaId): StoryFrameItem? { + val jsonSlide = getSlideJson(buildSlideKey(siteId, mediaId)) jsonSlide?.let { return StorySerializerUtils.deserializeStoryFrameItem(jsonSlide) } ?: return null } - fun getSlideWithLocalId(context: Context, siteId: Long, mediaId: LocalMediaId): StoryFrameItem? { - val jsonSlide = getSlideJson(context, buildSlideKey(siteId, mediaId)) + fun getSlideWithLocalId(siteId: Long, mediaId: LocalMediaId): StoryFrameItem? { + val jsonSlide = getSlideJson(buildSlideKey(siteId, mediaId)) jsonSlide?.let { return StorySerializerUtils.deserializeStoryFrameItem(jsonSlide) } ?: return null } - fun saveSlideWithLocalId(context: Context, siteId: Long, mediaId: LocalMediaId, storyFrameItem: StoryFrameItem) { + fun saveSlideWithLocalId(siteId: Long, mediaId: LocalMediaId, storyFrameItem: StoryFrameItem) { val slideIdKey = buildSlideKey(siteId, mediaId) - saveSlide(context, slideIdKey, StorySerializerUtils.serializeStoryFrameItem(storyFrameItem)) + saveSlide(slideIdKey, StorySerializerUtils.serializeStoryFrameItem(storyFrameItem)) } - fun saveSlideWithRemoteId(context: Context, siteId: Long, mediaId: RemoteMediaId, storyFrameItem: StoryFrameItem) { + fun saveSlideWithRemoteId(siteId: Long, mediaId: RemoteMediaId, storyFrameItem: StoryFrameItem) { val slideIdKey = buildSlideKey(siteId, mediaId) - saveSlide(context, slideIdKey, StorySerializerUtils.serializeStoryFrameItem(storyFrameItem)) + saveSlide(slideIdKey, StorySerializerUtils.serializeStoryFrameItem(storyFrameItem)) } - fun deleteSlideWithLocalId(context: Context, siteId: Long, mediaId: LocalMediaId) { + fun deleteSlideWithLocalId(siteId: Long, mediaId: LocalMediaId) { val slideIdKey = buildSlideKey(siteId, mediaId) val editor = PreferenceManager.getDefaultSharedPreferences(context).edit() editor.remove(slideIdKey) editor.apply() } - fun deleteSlideWithRemoteId(context: Context, siteId: Long, mediaId: RemoteMediaId) { + fun deleteSlideWithRemoteId(siteId: Long, mediaId: RemoteMediaId) { val slideIdKey = buildSlideKey(siteId, mediaId) val editor = PreferenceManager.getDefaultSharedPreferences(context).edit() editor.remove(slideIdKey) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/stories/usecase/LoadStoryFromStoriesPrefsUseCase.kt b/WordPress/src/main/java/org/wordpress/android/ui/stories/usecase/LoadStoryFromStoriesPrefsUseCase.kt index 978f52f750dd..088619301dfb 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/stories/usecase/LoadStoryFromStoriesPrefsUseCase.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/stories/usecase/LoadStoryFromStoriesPrefsUseCase.kt @@ -1,6 +1,5 @@ package org.wordpress.android.ui.stories.usecase -import android.content.Context import android.net.Uri import com.wordpress.stories.compose.story.StoryFrameItem import com.wordpress.stories.compose.story.StoryIndex @@ -10,9 +9,8 @@ import org.wordpress.android.fluxc.model.MediaModel import org.wordpress.android.fluxc.model.SiteModel import org.wordpress.android.fluxc.store.MediaStore import org.wordpress.android.ui.stories.StoryRepositoryWrapper +import org.wordpress.android.ui.stories.prefs.StoriesPrefs import org.wordpress.android.ui.stories.prefs.StoriesPrefs.RemoteMediaId -import org.wordpress.android.ui.stories.prefs.StoriesPrefs.getSlideWithRemoteId -import org.wordpress.android.ui.stories.prefs.StoriesPrefs.isValidSlide import java.util.ArrayList import java.util.HashMap import javax.inject.Inject @@ -20,8 +18,8 @@ import javax.inject.Inject @Reusable class LoadStoryFromStoriesPrefsUseCase @Inject constructor( private val storyRepositoryWrapper: StoryRepositoryWrapper, - private val mediaStore: MediaStore, - private val context: Context + private val storiesPrefs: StoriesPrefs, + private val mediaStore: MediaStore ) { fun getMediaIdsFromStoryBlockBridgeMediaFiles(mediaFiles: ArrayList): ArrayList { val mediaIds = ArrayList() @@ -38,7 +36,7 @@ class LoadStoryFromStoriesPrefsUseCase @Inject constructor( fun areAllStorySlidesEditable(site: SiteModel, mediaIds: ArrayList): Boolean { for (mediaId in mediaIds) { - if (!isValidSlide(context, site.getId().toLong(), RemoteMediaId(mediaId.toLong()))) { + if (!storiesPrefs.isValidSlide(site.getId().toLong(), RemoteMediaId(mediaId.toLong()))) { return false } } @@ -54,8 +52,7 @@ class LoadStoryFromStoriesPrefsUseCase @Inject constructor( storyRepositoryWrapper.loadStory(storyIndex) storyIndex = storyRepositoryWrapper.getCurrentStoryIndex() for (mediaId in mediaIds) { - var storyFrameItem = getSlideWithRemoteId( - context, + var storyFrameItem = storiesPrefs.getSlideWithRemoteId( site.getId().toLong(), RemoteMediaId(mediaId.toLong()) ) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/uploads/MediaUploadReadyProcessor.java b/WordPress/src/main/java/org/wordpress/android/ui/uploads/MediaUploadReadyProcessor.java index 8ff1bbdc09d1..1e1a6b419858 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/uploads/MediaUploadReadyProcessor.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/uploads/MediaUploadReadyProcessor.java @@ -9,6 +9,7 @@ import org.wordpress.android.ui.posts.PostUtils; import org.wordpress.android.ui.prefs.AppPrefs; import org.wordpress.android.ui.stories.SaveStoryGutenbergBlockUseCase; +import org.wordpress.android.ui.stories.prefs.StoriesPrefs; import org.wordpress.android.util.helpers.MediaFile; @@ -21,7 +22,9 @@ public PostModel replaceMediaFileWithUrlInPost(@Nullable PostModel post, String boolean showGutenbergEditor = AppPrefs.isGutenbergEditorEnabled(); if (PostUtils.contentContainsWPStoryGutenbergBlocks(post.getContent())) { - SaveStoryGutenbergBlockUseCase saveStoryGutenbergBlockUseCase = new SaveStoryGutenbergBlockUseCase(); + SaveStoryGutenbergBlockUseCase saveStoryGutenbergBlockUseCase = new SaveStoryGutenbergBlockUseCase( + new StoriesPrefs(WordPress.getContext()) + ); saveStoryGutenbergBlockUseCase .replaceLocalMediaIdsWithRemoteMediaIdsInPost(post, mediaFile); } else if (showGutenbergEditor && PostUtils.contentContainsGutenbergBlocks(post.getContent())) { From 34d4effeacc4f6b9356cb0c780bdbb8b2f73bd4e Mon Sep 17 00:00:00 2001 From: Mario Zorz Date: Wed, 7 Oct 2020 10:20:20 -0300 Subject: [PATCH 32/47] kotlinified expression --- .../org/wordpress/android/ui/stories/prefs/StoriesPrefs.kt | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/stories/prefs/StoriesPrefs.kt b/WordPress/src/main/java/org/wordpress/android/ui/stories/prefs/StoriesPrefs.kt index f4bae3b9bb6c..6f256a6f663b 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/stories/prefs/StoriesPrefs.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/stories/prefs/StoriesPrefs.kt @@ -61,9 +61,8 @@ class StoriesPrefs @Inject constructor( return true } try { - val inputStream = context.contentResolver.openInputStream(uri) - if (inputStream != null) { - inputStream.close() + context.contentResolver.openInputStream(uri)?.let { + it.close() return true } } catch (e: java.lang.Exception) { From a0faf8ac175f71a7812ee038d7ca6c43c7169659 Mon Sep 17 00:00:00 2001 From: mzorz Date: Wed, 7 Oct 2020 10:24:25 -0300 Subject: [PATCH 33/47] Update WordPress/src/main/java/org/wordpress/android/ui/stories/prefs/StoriesPrefs.kt Co-authored-by: Joel Dean --- .../org/wordpress/android/ui/stories/prefs/StoriesPrefs.kt | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/stories/prefs/StoriesPrefs.kt b/WordPress/src/main/java/org/wordpress/android/ui/stories/prefs/StoriesPrefs.kt index 6f256a6f663b..4b632a3a8bb6 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/stories/prefs/StoriesPrefs.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/stories/prefs/StoriesPrefs.kt @@ -119,9 +119,10 @@ class StoriesPrefs @Inject constructor( fun deleteSlideWithRemoteId(siteId: Long, mediaId: RemoteMediaId) { val slideIdKey = buildSlideKey(siteId, mediaId) - val editor = PreferenceManager.getDefaultSharedPreferences(context).edit() - editor.remove(slideIdKey) - editor.apply() + PreferenceManager.getDefaultSharedPreferences(context).edit().apply { + remove(slideIdKey) + apply() + } } data class RemoteMediaId(val mediaId: Long) From 34631b73f799058600f1395c8a08ef3531a25bcf Mon Sep 17 00:00:00 2001 From: mzorz Date: Wed, 7 Oct 2020 10:25:05 -0300 Subject: [PATCH 34/47] Update WordPress/src/main/java/org/wordpress/android/ui/stories/usecase/LoadStoryFromStoriesPrefsUseCase.kt Co-authored-by: Joel Dean --- .../ui/stories/usecase/LoadStoryFromStoriesPrefsUseCase.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/stories/usecase/LoadStoryFromStoriesPrefsUseCase.kt b/WordPress/src/main/java/org/wordpress/android/ui/stories/usecase/LoadStoryFromStoriesPrefsUseCase.kt index 088619301dfb..d711a273c5f4 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/stories/usecase/LoadStoryFromStoriesPrefsUseCase.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/stories/usecase/LoadStoryFromStoriesPrefsUseCase.kt @@ -21,7 +21,7 @@ class LoadStoryFromStoriesPrefsUseCase @Inject constructor( private val storiesPrefs: StoriesPrefs, private val mediaStore: MediaStore ) { - fun getMediaIdsFromStoryBlockBridgeMediaFiles(mediaFiles: ArrayList): ArrayList { + fun getMediaIdsFromStoryBlockBridgeMediaFiles(mediaFiles: ArrayList): ArrayList { val mediaIds = ArrayList() for (mediaFile in mediaFiles) { val mediaIdLong = (mediaFile as HashMap)["id"] From aba2177e498778e78ad10b1b4fcce518249c3771 Mon Sep 17 00:00:00 2001 From: Mario Zorz Date: Wed, 7 Oct 2020 10:28:40 -0300 Subject: [PATCH 35/47] fixed method signature --- .../ui/stories/usecase/LoadStoryFromStoriesPrefsUseCase.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/stories/usecase/LoadStoryFromStoriesPrefsUseCase.kt b/WordPress/src/main/java/org/wordpress/android/ui/stories/usecase/LoadStoryFromStoriesPrefsUseCase.kt index d711a273c5f4..e5bb1365ca4f 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/stories/usecase/LoadStoryFromStoriesPrefsUseCase.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/stories/usecase/LoadStoryFromStoriesPrefsUseCase.kt @@ -86,7 +86,7 @@ class LoadStoryFromStoriesPrefsUseCase @Inject constructor( return ReCreateStoryResult(storyIndex, allStorySlidesAreEditable, noSlidesLoaded) } - fun loadStoryFromMemoryOrRecreateFromPrefs(site: SiteModel, mediaFiles: ArrayList): ReCreateStoryResult { + fun loadStoryFromMemoryOrRecreateFromPrefs(site: SiteModel, mediaFiles: ArrayList): ReCreateStoryResult { val mediaIds = getMediaIdsFromStoryBlockBridgeMediaFiles( mediaFiles ) From df5b594e2667dc1d94efa845553ae1e88bb71748 Mon Sep 17 00:00:00 2001 From: mzorz Date: Wed, 7 Oct 2020 10:32:01 -0300 Subject: [PATCH 36/47] Update WordPress/src/main/java/org/wordpress/android/ui/stories/usecase/LoadStoryFromStoriesPrefsUseCase.kt Co-authored-by: Joel Dean --- .../ui/stories/usecase/LoadStoryFromStoriesPrefsUseCase.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/stories/usecase/LoadStoryFromStoriesPrefsUseCase.kt b/WordPress/src/main/java/org/wordpress/android/ui/stories/usecase/LoadStoryFromStoriesPrefsUseCase.kt index e5bb1365ca4f..6f477f556f58 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/stories/usecase/LoadStoryFromStoriesPrefsUseCase.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/stories/usecase/LoadStoryFromStoriesPrefsUseCase.kt @@ -68,7 +68,7 @@ class LoadStoryFromStoriesPrefsUseCase @Inject constructor( site, tmpMediaIdsLong ) - if (mediaModelList.size == 0) { + if (mediaModelList.isEmpty()) { noSlidesLoaded = true } else { for (mediaModel in mediaModelList) { From 99efe00984b0504395b4d1121b538c937c27379d Mon Sep 17 00:00:00 2001 From: Mario Zorz Date: Wed, 7 Oct 2020 10:33:15 -0300 Subject: [PATCH 37/47] using implicit getter --- .../ui/stories/usecase/LoadStoryFromStoriesPrefsUseCase.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/stories/usecase/LoadStoryFromStoriesPrefsUseCase.kt b/WordPress/src/main/java/org/wordpress/android/ui/stories/usecase/LoadStoryFromStoriesPrefsUseCase.kt index 6f477f556f58..14056fda4035 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/stories/usecase/LoadStoryFromStoriesPrefsUseCase.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/stories/usecase/LoadStoryFromStoriesPrefsUseCase.kt @@ -36,7 +36,7 @@ class LoadStoryFromStoriesPrefsUseCase @Inject constructor( fun areAllStorySlidesEditable(site: SiteModel, mediaIds: ArrayList): Boolean { for (mediaId in mediaIds) { - if (!storiesPrefs.isValidSlide(site.getId().toLong(), RemoteMediaId(mediaId.toLong()))) { + if (!storiesPrefs.isValidSlide(site.id.toLong(), RemoteMediaId(mediaId.toLong()))) { return false } } From ddcda8b0698df5350af06a51397d7469d0dee3d8 Mon Sep 17 00:00:00 2001 From: Mario Zorz Date: Wed, 7 Oct 2020 10:50:04 -0300 Subject: [PATCH 38/47] using FluxC's LocalOrRemoteId sealed class variants --- .../stories/SaveStoryGutenbergBlockUseCase.kt | 12 +++---- .../media/StoryMediaSaveUploadBridge.kt | 4 +-- .../android/ui/stories/prefs/StoriesPrefs.kt | 33 +++++++++---------- .../LoadStoryFromStoriesPrefsUseCase.kt | 6 ++-- 4 files changed, 27 insertions(+), 28 deletions(-) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/stories/SaveStoryGutenbergBlockUseCase.kt b/WordPress/src/main/java/org/wordpress/android/ui/stories/SaveStoryGutenbergBlockUseCase.kt index d8b43ff0a138..9b3fcbab5d8a 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/stories/SaveStoryGutenbergBlockUseCase.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/stories/SaveStoryGutenbergBlockUseCase.kt @@ -1,11 +1,11 @@ package org.wordpress.android.ui.stories import com.google.gson.Gson +import org.wordpress.android.fluxc.model.LocalOrRemoteId.LocalId +import org.wordpress.android.fluxc.model.LocalOrRemoteId.RemoteId import org.wordpress.android.fluxc.model.PostModel import org.wordpress.android.ui.posts.EditPostRepository import org.wordpress.android.ui.stories.prefs.StoriesPrefs -import org.wordpress.android.ui.stories.prefs.StoriesPrefs.LocalMediaId -import org.wordpress.android.ui.stories.prefs.StoriesPrefs.RemoteMediaId import org.wordpress.android.util.StringUtils import org.wordpress.android.util.helpers.MediaFile import javax.inject.Inject @@ -64,23 +64,23 @@ class SaveStoryGutenbergBlockUseCase @Inject constructor( url = mediaFile.fileURL // look for the slide saved with the local id key (mediaFile.id), and re-convert to mediaId. - val localIdKey = mediaFile.id.toLong() + val localIdKey = mediaFile.id.toInt() val remoteIdKey = mediaFile.mediaId.toLong() val localSiteId = post.localSiteId.toLong() storiesPrefs.getSlideWithLocalId( localSiteId, - LocalMediaId(localIdKey) + LocalId(localIdKey) )?.let { it.id = mediaFile.mediaId // update the StoryFrameItem id to hold the same value as the remote mediaID storiesPrefs.saveSlideWithRemoteId( localSiteId, - RemoteMediaId(remoteIdKey), // use the new mediaId as key + RemoteId(remoteIdKey), // use the new mediaId as key it ) // now delete the old entry storiesPrefs.deleteSlideWithLocalId( localSiteId, - LocalMediaId(localIdKey) + LocalId(localIdKey) ) } } diff --git a/WordPress/src/main/java/org/wordpress/android/ui/stories/media/StoryMediaSaveUploadBridge.kt b/WordPress/src/main/java/org/wordpress/android/ui/stories/media/StoryMediaSaveUploadBridge.kt index 7c29f1928d49..41d1d5938a5a 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/stories/media/StoryMediaSaveUploadBridge.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/stories/media/StoryMediaSaveUploadBridge.kt @@ -16,6 +16,7 @@ import kotlinx.coroutines.launch import org.greenrobot.eventbus.Subscribe import org.greenrobot.eventbus.ThreadMode.MAIN import org.wordpress.android.WordPress +import org.wordpress.android.fluxc.model.LocalOrRemoteId.LocalId import org.wordpress.android.fluxc.model.MediaModel import org.wordpress.android.fluxc.model.PostImmutableModel import org.wordpress.android.fluxc.model.SiteModel @@ -30,7 +31,6 @@ import org.wordpress.android.ui.stories.SaveStoryGutenbergBlockUseCase import org.wordpress.android.ui.stories.StoriesTrackerHelper import org.wordpress.android.ui.stories.StoryComposerActivity import org.wordpress.android.ui.stories.prefs.StoriesPrefs -import org.wordpress.android.ui.stories.prefs.StoriesPrefs.LocalMediaId import org.wordpress.android.ui.uploads.UploadServiceFacade import org.wordpress.android.util.EventBusWrapper import org.wordpress.android.util.NetworkUtilsWrapper @@ -193,7 +193,7 @@ class StoryMediaSaveUploadBridge @Inject constructor( it.localSiteId.toLong(), // use the local id to save the original, will be replaced later // with mediaModel.mediaId after uploading to the remote site - LocalMediaId(it.id.toLong()), + LocalId(it.id.toInt()), frame ) } diff --git a/WordPress/src/main/java/org/wordpress/android/ui/stories/prefs/StoriesPrefs.kt b/WordPress/src/main/java/org/wordpress/android/ui/stories/prefs/StoriesPrefs.kt index 4b632a3a8bb6..aed7d5e74f88 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/stories/prefs/StoriesPrefs.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/stories/prefs/StoriesPrefs.kt @@ -7,6 +7,8 @@ import com.wordpress.stories.compose.story.StoryFrameItem import com.wordpress.stories.compose.story.StoryFrameItem.BackgroundSource.FileBackgroundSource import com.wordpress.stories.compose.story.StoryFrameItem.BackgroundSource.UriBackgroundSource import com.wordpress.stories.compose.story.StorySerializerUtils +import org.wordpress.android.fluxc.model.LocalOrRemoteId.LocalId +import org.wordpress.android.fluxc.model.LocalOrRemoteId.RemoteId import javax.inject.Inject import javax.inject.Singleton @@ -20,22 +22,22 @@ class StoriesPrefs @Inject constructor( private val KEY_PREFIX_REMOTE_MEDIA_ID = "r-" } - private fun buildSlideKey(siteId: Long, mediaId: RemoteMediaId): String { + private fun buildSlideKey(siteId: Long, mediaId: RemoteId): String { return KEY_PREFIX_STORIES_SLIDE_ID + siteId.toString() + "-" + - KEY_PREFIX_REMOTE_MEDIA_ID + mediaId.mediaId.toString() + KEY_PREFIX_REMOTE_MEDIA_ID + mediaId.value.toString() } - private fun buildSlideKey(siteId: Long, mediaId: LocalMediaId): String { + private fun buildSlideKey(siteId: Long, mediaId: LocalId): String { return KEY_PREFIX_STORIES_SLIDE_ID + siteId.toString() + "-" + - KEY_PREFIX_LOCAL_MEDIA_ID + mediaId.id.toString() + KEY_PREFIX_LOCAL_MEDIA_ID + mediaId.value.toString() } - fun checkSlideIdExists(siteId: Long, mediaId: RemoteMediaId): Boolean { + fun checkSlideIdExists(siteId: Long, mediaId: RemoteId): Boolean { val slideIdKey = buildSlideKey(siteId, mediaId) return PreferenceManager.getDefaultSharedPreferences(context).contains(slideIdKey) } - fun checkSlideOriginalBackgroundMediaExists(siteId: Long, mediaId: RemoteMediaId): Boolean { + fun checkSlideOriginalBackgroundMediaExists(siteId: Long, mediaId: RemoteId): Boolean { val storyFrameItem: StoryFrameItem? = getSlideWithRemoteId(siteId, mediaId) storyFrameItem?.let { frame -> // now check the background media exists or is accessible on this device @@ -77,7 +79,7 @@ class StoriesPrefs @Inject constructor( editor.apply() } - fun isValidSlide(siteId: Long, mediaId: RemoteMediaId): Boolean { + fun isValidSlide(siteId: Long, mediaId: RemoteId): Boolean { return checkSlideIdExists(siteId, mediaId) && checkSlideOriginalBackgroundMediaExists(siteId, mediaId) } @@ -86,45 +88,42 @@ class StoriesPrefs @Inject constructor( return PreferenceManager.getDefaultSharedPreferences(context).getString(slideIdKey, null) } - fun getSlideWithRemoteId(siteId: Long, mediaId: RemoteMediaId): StoryFrameItem? { + fun getSlideWithRemoteId(siteId: Long, mediaId: RemoteId): StoryFrameItem? { val jsonSlide = getSlideJson(buildSlideKey(siteId, mediaId)) jsonSlide?.let { return StorySerializerUtils.deserializeStoryFrameItem(jsonSlide) } ?: return null } - fun getSlideWithLocalId(siteId: Long, mediaId: LocalMediaId): StoryFrameItem? { + fun getSlideWithLocalId(siteId: Long, mediaId: LocalId): StoryFrameItem? { val jsonSlide = getSlideJson(buildSlideKey(siteId, mediaId)) jsonSlide?.let { return StorySerializerUtils.deserializeStoryFrameItem(jsonSlide) } ?: return null } - fun saveSlideWithLocalId(siteId: Long, mediaId: LocalMediaId, storyFrameItem: StoryFrameItem) { + fun saveSlideWithLocalId(siteId: Long, mediaId: LocalId, storyFrameItem: StoryFrameItem) { val slideIdKey = buildSlideKey(siteId, mediaId) saveSlide(slideIdKey, StorySerializerUtils.serializeStoryFrameItem(storyFrameItem)) } - fun saveSlideWithRemoteId(siteId: Long, mediaId: RemoteMediaId, storyFrameItem: StoryFrameItem) { + fun saveSlideWithRemoteId(siteId: Long, mediaId: RemoteId, storyFrameItem: StoryFrameItem) { val slideIdKey = buildSlideKey(siteId, mediaId) saveSlide(slideIdKey, StorySerializerUtils.serializeStoryFrameItem(storyFrameItem)) } - fun deleteSlideWithLocalId(siteId: Long, mediaId: LocalMediaId) { + fun deleteSlideWithLocalId(siteId: Long, mediaId: LocalId) { val slideIdKey = buildSlideKey(siteId, mediaId) val editor = PreferenceManager.getDefaultSharedPreferences(context).edit() editor.remove(slideIdKey) editor.apply() } - fun deleteSlideWithRemoteId(siteId: Long, mediaId: RemoteMediaId) { + fun deleteSlideWithRemoteId(siteId: Long, mediaId: RemoteId) { val slideIdKey = buildSlideKey(siteId, mediaId) - PreferenceManager.getDefaultSharedPreferences(context).edit().apply { + PreferenceManager.getDefaultSharedPreferences(context).edit().apply { remove(slideIdKey) apply() } } - - data class RemoteMediaId(val mediaId: Long) - data class LocalMediaId(val id: Long) } diff --git a/WordPress/src/main/java/org/wordpress/android/ui/stories/usecase/LoadStoryFromStoriesPrefsUseCase.kt b/WordPress/src/main/java/org/wordpress/android/ui/stories/usecase/LoadStoryFromStoriesPrefsUseCase.kt index 14056fda4035..e7fb88edf057 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/stories/usecase/LoadStoryFromStoriesPrefsUseCase.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/stories/usecase/LoadStoryFromStoriesPrefsUseCase.kt @@ -5,12 +5,12 @@ import com.wordpress.stories.compose.story.StoryFrameItem import com.wordpress.stories.compose.story.StoryIndex import com.wordpress.stories.compose.story.StoryRepository import dagger.Reusable +import org.wordpress.android.fluxc.model.LocalOrRemoteId.RemoteId import org.wordpress.android.fluxc.model.MediaModel import org.wordpress.android.fluxc.model.SiteModel import org.wordpress.android.fluxc.store.MediaStore import org.wordpress.android.ui.stories.StoryRepositoryWrapper import org.wordpress.android.ui.stories.prefs.StoriesPrefs -import org.wordpress.android.ui.stories.prefs.StoriesPrefs.RemoteMediaId import java.util.ArrayList import java.util.HashMap import javax.inject.Inject @@ -36,7 +36,7 @@ class LoadStoryFromStoriesPrefsUseCase @Inject constructor( fun areAllStorySlidesEditable(site: SiteModel, mediaIds: ArrayList): Boolean { for (mediaId in mediaIds) { - if (!storiesPrefs.isValidSlide(site.id.toLong(), RemoteMediaId(mediaId.toLong()))) { + if (!storiesPrefs.isValidSlide(site.id.toLong(), RemoteId(mediaId.toLong()))) { return false } } @@ -54,7 +54,7 @@ class LoadStoryFromStoriesPrefsUseCase @Inject constructor( for (mediaId in mediaIds) { var storyFrameItem = storiesPrefs.getSlideWithRemoteId( site.getId().toLong(), - RemoteMediaId(mediaId.toLong()) + RemoteId(mediaId.toLong()) ) if (storyFrameItem != null) { storyRepositoryWrapper.addStoryFrameItemToCurrentStory(storyFrameItem) From 2c54019f4edb6e1d905ec05f9a31c7a796ea334c Mon Sep 17 00:00:00 2001 From: Mario Zorz Date: Thu, 8 Oct 2020 15:48:53 -0300 Subject: [PATCH 39/47] made MediaUploadReadyProcessor injectable, and injecting SaveStoryGutenbergBlockUseCase --- .../wordpress/android/modules/AppComponent.java | 3 +++ .../ui/uploads/MediaUploadReadyProcessor.java | 16 ++++++++++------ 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/WordPress/src/main/java/org/wordpress/android/modules/AppComponent.java b/WordPress/src/main/java/org/wordpress/android/modules/AppComponent.java index de63dfa201aa..1b4ed181b6fe 100644 --- a/WordPress/src/main/java/org/wordpress/android/modules/AppComponent.java +++ b/WordPress/src/main/java/org/wordpress/android/modules/AppComponent.java @@ -182,6 +182,7 @@ import org.wordpress.android.ui.themes.ThemeBrowserActivity; import org.wordpress.android.ui.themes.ThemeBrowserFragment; import org.wordpress.android.ui.uploads.MediaUploadHandler; +import org.wordpress.android.ui.uploads.MediaUploadReadyProcessor; import org.wordpress.android.ui.uploads.PostUploadHandler; import org.wordpress.android.ui.uploads.UploadService; import org.wordpress.android.ui.whatsnew.FeatureAnnouncementDialogFragment; @@ -584,6 +585,8 @@ public interface AppComponent extends AndroidInjector { void inject(MediaPickerFragment object); + void inject(MediaUploadReadyProcessor object); + // Allows us to inject the application without having to instantiate any modules, and provides the Application // in the app graph @Component.Builder diff --git a/WordPress/src/main/java/org/wordpress/android/ui/uploads/MediaUploadReadyProcessor.java b/WordPress/src/main/java/org/wordpress/android/ui/uploads/MediaUploadReadyProcessor.java index 1e1a6b419858..7eb3180df08d 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/uploads/MediaUploadReadyProcessor.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/uploads/MediaUploadReadyProcessor.java @@ -9,11 +9,18 @@ import org.wordpress.android.ui.posts.PostUtils; import org.wordpress.android.ui.prefs.AppPrefs; import org.wordpress.android.ui.stories.SaveStoryGutenbergBlockUseCase; -import org.wordpress.android.ui.stories.prefs.StoriesPrefs; import org.wordpress.android.util.helpers.MediaFile; +import javax.inject.Inject; + public class MediaUploadReadyProcessor implements MediaUploadReadyListener { + @Inject SaveStoryGutenbergBlockUseCase mSaveStoryGutenbergBlockUseCase; + + @Inject public MediaUploadReadyProcessor() { + ((WordPress) WordPress.getContext().getApplicationContext()).component().inject(this); + } + @Override public PostModel replaceMediaFileWithUrlInPost(@Nullable PostModel post, String localMediaId, MediaFile mediaFile, String siteUrl) { @@ -22,11 +29,8 @@ public PostModel replaceMediaFileWithUrlInPost(@Nullable PostModel post, String boolean showGutenbergEditor = AppPrefs.isGutenbergEditorEnabled(); if (PostUtils.contentContainsWPStoryGutenbergBlocks(post.getContent())) { - SaveStoryGutenbergBlockUseCase saveStoryGutenbergBlockUseCase = new SaveStoryGutenbergBlockUseCase( - new StoriesPrefs(WordPress.getContext()) - ); - saveStoryGutenbergBlockUseCase - .replaceLocalMediaIdsWithRemoteMediaIdsInPost(post, mediaFile); + mSaveStoryGutenbergBlockUseCase + .replaceLocalMediaIdsWithRemoteMediaIdsInPost(post, mediaFile); } else if (showGutenbergEditor && PostUtils.contentContainsGutenbergBlocks(post.getContent())) { post.setContent( PostUtils.replaceMediaFileWithUrlInGutenbergPost(post.getContent(), localMediaId, mediaFile, From cadf22b4df7d65b78aa6c3d37ef339dbca0aea22 Mon Sep 17 00:00:00 2001 From: Mario Zorz Date: Thu, 8 Oct 2020 15:53:42 -0300 Subject: [PATCH 40/47] modified comment --- .../ui/stories/usecase/LoadStoryFromStoriesPrefsUseCase.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/stories/usecase/LoadStoryFromStoriesPrefsUseCase.kt b/WordPress/src/main/java/org/wordpress/android/ui/stories/usecase/LoadStoryFromStoriesPrefsUseCase.kt index e7fb88edf057..9d717a4bef5d 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/stories/usecase/LoadStoryFromStoriesPrefsUseCase.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/stories/usecase/LoadStoryFromStoriesPrefsUseCase.kt @@ -96,7 +96,7 @@ class LoadStoryFromStoriesPrefsUseCase @Inject constructor( ) // now look for a Story in the StoryRepository that has all these frames and, if not found, let's - // just build the Story object ourselves to keep these files arrangement + // just build the Story object ourselves to match the order in which the media files were passed. var storyIndex = storyRepositoryWrapper.findStoryContainingStoryFrameItemsByIds(mediaIds) if (storyIndex == StoryRepository.DEFAULT_NONE_SELECTED) { // the StoryRepository didn't have it but we have editable serialized slides so, From 62fec15aded73be7233ca1b13fdc3cb06122e7e9 Mon Sep 17 00:00:00 2001 From: Mario Zorz Date: Thu, 8 Oct 2020 16:20:33 -0300 Subject: [PATCH 41/47] adding specific error dialogs in case we dont find the backing media for a given story slide so to make it editable --- .../android/ui/posts/EditPostActivity.java | 38 +++++++++++++++---- WordPress/src/main/res/values/strings.xml | 4 +- 2 files changed, 33 insertions(+), 9 deletions(-) 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 168f3ad232c9..65f156da84ca 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 @@ -398,6 +398,8 @@ enum RestartEditorOptions { private SiteSettingsInterface mSiteSettings; private boolean mIsJetpackSsoEnabled; + private boolean mNetworkErrorOnLastMediaFetchAttempt = false; + public static boolean checkToRestart(@NonNull Intent data) { return data.hasExtra(EditPostActivity.EXTRA_RESTART_EDITOR) && RestartEditorOptions.valueOf(data.getStringExtra(EditPostActivity.EXTRA_RESTART_EDITOR)) @@ -3075,14 +3077,30 @@ public void onTrackableEvent(TrackableEvent event, Map propertie } else { // unfortunately we couldn't even load the remote media Ids indicated by the StoryBlock so we can't allow // editing at this time :( - AlertDialog.Builder builder = new MaterialAlertDialogBuilder(this); - builder.setTitle(getString(R.string.dialog_edit_story_unavailable_title)); - builder.setMessage(getString(R.string.dialog_edit_story_unavailable_message)); - builder.setPositiveButton(R.string.dialog_button_ok, (dialog, id) -> { - dialog.dismiss(); - }); - AlertDialog dialog = builder.create(); - dialog.show(); + if (mNetworkErrorOnLastMediaFetchAttempt) { + // there was an error fetching media when we were loading the editor, + // we *may* still have a possibility, tell the user they may try refreshing the media again + AlertDialog.Builder builder = new MaterialAlertDialogBuilder(this); + builder.setTitle(getString(R.string.dialog_edit_story_unavailable_title)); + builder.setMessage(getString(R.string.dialog_edit_story_unavailable_message)); + builder.setPositiveButton(R.string.dialog_button_ok, (dialog, id) -> { + // try another fetchMedia request + fetchMediaList(); + dialog.dismiss(); + }); + AlertDialog dialog = builder.create(); + dialog.show(); + } else { + // unrecoverable error, nothing we can do, inform the user :(. + AlertDialog.Builder builder = new MaterialAlertDialogBuilder(this); + builder.setTitle(getString(R.string.dialog_edit_story_unrecoverable_title)); + builder.setMessage(getString(R.string.dialog_edit_story_unrecoverable_message)); + builder.setPositiveButton(R.string.dialog_button_ok, (dialog, id) -> { + dialog.dismiss(); + }); + AlertDialog dialog = builder.create(); + dialog.show(); + } } } @@ -3123,6 +3141,9 @@ public void onMediaUploaded(OnMediaUploaded event) { public void onMediaListFetched(OnMediaListFetched event) { // no op - we don't need to check anything just now, but declaring the method so it's // clear we make a request to FetchMedia in this class. + if (event != null) { + mNetworkErrorOnLastMediaFetchAttempt = event.isError(); + } } @SuppressWarnings("unused") @@ -3253,6 +3274,7 @@ private void refreshEditorTheme() { private void fetchMediaList() { // do not refresh if there is no network if (!NetworkUtils.isNetworkAvailable(this)) { + mNetworkErrorOnLastMediaFetchAttempt = true; return; } FetchMediaListPayload payload = diff --git a/WordPress/src/main/res/values/strings.xml b/WordPress/src/main/res/values/strings.xml index aa8a9915c1cd..5a4ed6dda55a 100644 --- a/WordPress/src/main/res/values/strings.xml +++ b/WordPress/src/main/res/values/strings.xml @@ -2886,7 +2886,9 @@ Limited Story Editing This story was edited on a different device and the ability to edit certain objects may be limited. Can\'t edit Story - This story was created on a different device and can\'t be edited at this moment. + Unable to load media for this story. Check your internet connection and try again in a moment. + Can\'t edit Story + We couldn\'t find the media for your story on this site. Capture Flip camera Flash From 437c44b9500328b15976834a97639f293f0d1c91 Mon Sep 17 00:00:00 2001 From: mzorz Date: Thu, 8 Oct 2020 22:47:56 -0300 Subject: [PATCH 42/47] Update WordPress/src/main/res/values/strings.xml Co-authored-by: Alex --- WordPress/src/main/res/values/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WordPress/src/main/res/values/strings.xml b/WordPress/src/main/res/values/strings.xml index 5a4ed6dda55a..017e32f35f7e 100644 --- a/WordPress/src/main/res/values/strings.xml +++ b/WordPress/src/main/res/values/strings.xml @@ -2888,7 +2888,7 @@ Can\'t edit Story Unable to load media for this story. Check your internet connection and try again in a moment. Can\'t edit Story - We couldn\'t find the media for your story on this site. + We couldn\'t find the media for this story on the site. Capture Flip camera Flash From 13e5bfd2ca54ff0635dabe11769dc847dddb8f7f Mon Sep 17 00:00:00 2001 From: Mario Zorz Date: Fri, 9 Oct 2020 10:25:02 -0300 Subject: [PATCH 43/47] removed comment --- .../java/org/wordpress/android/ui/posts/EditPostActivity.java | 2 -- 1 file changed, 2 deletions(-) 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 65f156da84ca..71e5b1e78b1d 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 @@ -3139,8 +3139,6 @@ public void onMediaUploaded(OnMediaUploaded event) { @SuppressWarnings("unused") @Subscribe(threadMode = ThreadMode.MAIN) public void onMediaListFetched(OnMediaListFetched event) { - // no op - we don't need to check anything just now, but declaring the method so it's - // clear we make a request to FetchMedia in this class. if (event != null) { mNetworkErrorOnLastMediaFetchAttempt = event.isError(); } From f74f63bcca376a0fe6d5754577d83f4ccc578427 Mon Sep 17 00:00:00 2001 From: Mario Zorz Date: Fri, 9 Oct 2020 13:23:27 -0300 Subject: [PATCH 44/47] udpated gutenberg-mobile and submodules --- libs/gutenberg-mobile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/gutenberg-mobile b/libs/gutenberg-mobile index 33f2addaab7f..0f8916f5a4dc 160000 --- a/libs/gutenberg-mobile +++ b/libs/gutenberg-mobile @@ -1 +1 @@ -Subproject commit 33f2addaab7f5338f791d968680cb6f1e97b4ffa +Subproject commit 0f8916f5a4dccd263cfbecfb88e1a6df61cf09f2 From 0d193db0a15985d69e5b477889fadbcaa598bc17 Mon Sep 17 00:00:00 2001 From: Mario Zorz Date: Fri, 9 Oct 2020 15:41:15 -0300 Subject: [PATCH 45/47] updated gutenberg-mobile hash --- libs/gutenberg-mobile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/gutenberg-mobile b/libs/gutenberg-mobile index 0f8916f5a4dc..7c7430cf0261 160000 --- a/libs/gutenberg-mobile +++ b/libs/gutenberg-mobile @@ -1 +1 @@ -Subproject commit 0f8916f5a4dccd263cfbecfb88e1a6df61cf09f2 +Subproject commit 7c7430cf026148db0bfea6392cf938f27c43e237 From 1f91b0f1991857004aacd9ebbe368384035a6fe4 Mon Sep 17 00:00:00 2001 From: Mario Zorz Date: Fri, 9 Oct 2020 15:56:39 -0300 Subject: [PATCH 46/47] updated gutenberg-mobile hash --- libs/gutenberg-mobile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/gutenberg-mobile b/libs/gutenberg-mobile index 7c7430cf0261..6360f4c43299 160000 --- a/libs/gutenberg-mobile +++ b/libs/gutenberg-mobile @@ -1 +1 @@ -Subproject commit 7c7430cf026148db0bfea6392cf938f27c43e237 +Subproject commit 6360f4c43299fcac464a775196a8e7627d47fa81 From 78ed9dba6033b99b73899dc32e60e4046d5cb8d4 Mon Sep 17 00:00:00 2001 From: Mario Zorz Date: Sat, 10 Oct 2020 11:50:05 -0300 Subject: [PATCH 47/47] updated gutenberg mobile commit hash to fix initial html data setup --- libs/gutenberg-mobile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/gutenberg-mobile b/libs/gutenberg-mobile index 6360f4c43299..aa1b58b4d8ca 160000 --- a/libs/gutenberg-mobile +++ b/libs/gutenberg-mobile @@ -1 +1 @@ -Subproject commit 6360f4c43299fcac464a775196a8e7627d47fa81 +Subproject commit aa1b58b4d8cae446cf8a84542709b0fde1a40fc2