diff --git a/WordPress/build.gradle b/WordPress/build.gradle index 4d2939073e66..6402167fd714 100644 --- a/WordPress/build.gradle +++ b/WordPress/build.gradle @@ -180,8 +180,8 @@ android { buildConfigField "boolean", "ENABLE_SITE_MONITORING", "false" buildConfigField "boolean", "SYNC_PUBLISHING", "false" buildConfigField "boolean", "ENABLE_IN_APP_UPDATES", "false" - buildConfigField "boolean", "ENABLE_NEW_GUTENBERG", "false" - buildConfigField "boolean", "ENABLE_NEW_GUTENBERG_THEME_STYLES", "false" + buildConfigField "boolean", "ENABLE_GUTENBERG_KIT", "false" + buildConfigField "boolean", "ENABLE_GUTENBERG_KIT_THEME_STYLES", "false" manifestPlaceholders = [magicLinkScheme:"wordpress"] } diff --git a/WordPress/src/main/java/org/wordpress/android/ui/posts/EditPostActivity.kt b/WordPress/src/main/java/org/wordpress/android/ui/posts/EditPostActivity.kt index ec2539d9f6b6..1f7e03f7c192 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/posts/EditPostActivity.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/posts/EditPostActivity.kt @@ -72,6 +72,7 @@ import org.wordpress.android.editor.EditorThemeUpdateListener import org.wordpress.android.editor.ExceptionLogger import org.wordpress.android.editor.gutenberg.DialogVisibility import org.wordpress.android.editor.gutenberg.GutenbergEditorFragment +import org.wordpress.android.editor.gutenberg.GutenbergKitEditorFragment import org.wordpress.android.editor.gutenberg.GutenbergNetworkConnectionListener import org.wordpress.android.editor.gutenberg.GutenbergPropsBuilder import org.wordpress.android.editor.gutenberg.GutenbergWebViewAuthorizationData @@ -234,8 +235,8 @@ import org.wordpress.android.util.analytics.AnalyticsUtils import org.wordpress.android.util.analytics.AnalyticsUtils.BlockEditorEnabledSource import org.wordpress.android.util.config.ContactSupportFeatureConfig import org.wordpress.android.util.config.PostConflictResolutionFeatureConfig -import org.wordpress.android.util.config.NewGutenbergFeatureConfig -import org.wordpress.android.util.config.NewGutenbergThemeStylesFeatureConfig +import org.wordpress.android.util.config.GutenbergKitFeatureConfig +import org.wordpress.android.util.config.GutenbergKitThemeStylesFeatureConfig import org.wordpress.android.util.extensions.setLiftOnScrollTargetViewIdAndRequestLayout import org.wordpress.android.util.helpers.MediaFile import org.wordpress.android.util.helpers.MediaGallery @@ -314,7 +315,7 @@ class EditPostActivity : LocaleAwareActivity(), EditorFragmentActivity, EditorIm private var isXPostsCapable: Boolean? = null private var onGetSuggestionResult: Consumer? = null private var isVoiceContentSet = false - private var isNewGutenbergEditor = false + private var isGutenbergKitEditor = false // For opening the context menu after permissions have been granted private var menuView: View? = null @@ -409,8 +410,8 @@ class EditPostActivity : LocaleAwareActivity(), EditorFragmentActivity, EditorIm @Inject lateinit var postConflictResolutionFeatureConfig: PostConflictResolutionFeatureConfig - @Inject lateinit var newGutenbergFeatureConfig: NewGutenbergFeatureConfig - @Inject lateinit var newGutenbergThemeStylesConfig: NewGutenbergThemeStylesFeatureConfig + @Inject lateinit var gutenbergKitFeatureConfig: GutenbergKitFeatureConfig + @Inject lateinit var gutenbergKitThemeStylesConfig: GutenbergKitThemeStylesFeatureConfig @Inject lateinit var storePostViewModel: StorePostViewModel @Inject lateinit var storageUtilsViewModel: StorageUtilsViewModel @@ -516,7 +517,7 @@ class EditPostActivity : LocaleAwareActivity(), EditorFragmentActivity, EditorIm } onBackPressedDispatcher.addCallback(this, callback) dispatcher.register(this) - isNewGutenbergEditor = newGutenbergFeatureConfig.isEnabled() + isGutenbergKitEditor = gutenbergKitFeatureConfig.isEnabled() createEditShareMessageActivityResultLauncher() @@ -725,7 +726,7 @@ class EditPostActivity : LocaleAwareActivity(), EditorFragmentActivity, EditorIm } isNewPost = state.getBoolean(EditPostActivityConstants.STATE_KEY_IS_NEW_POST, false) - isNewGutenbergEditor = state.getBoolean(EditPostActivityConstants.STATE_KEY_IS_NEW_GUTENBERG, false) + isGutenbergKitEditor = state.getBoolean(EditPostActivityConstants.STATE_KEY_IS_GUTENBERG_KIT, false) isVoiceContentSet = state.getBoolean(EditPostActivityConstants.STATE_KEY_IS_VOICE_CONTENT_SET, false) updatePostLoadingAndDialogState( fromInt( @@ -782,7 +783,7 @@ class EditPostActivity : LocaleAwareActivity(), EditorFragmentActivity, EditorIm } private fun setupEditor() { - if (isNewGutenbergEditor) { + if (isGutenbergKitEditor) { GutenbergWebViewPool.getPreloadedWebView(getContext()) } // Check whether to show the visual editor @@ -1000,7 +1001,7 @@ class EditPostActivity : LocaleAwareActivity(), EditorFragmentActivity, EditorIm editorMedia.toastMessage.observe(this) { event: Event -> event.getContentIfNotHandled()?.show(this) } - if (!isNewGutenbergEditor) { + if (!isGutenbergKitEditor) { storePostViewModel.onSavePostTriggered.observe(this) { unitEvent: Event -> unitEvent.applyIfNotHandled { updateAndSavePostAsync() @@ -1201,7 +1202,7 @@ class EditPostActivity : LocaleAwareActivity(), EditorFragmentActivity, EditorIm outState.putInt(EditPostActivityConstants.STATE_KEY_POST_LOADING_STATE, postLoadingState.value) outState.putBoolean(EditPostActivityConstants.STATE_KEY_IS_NEW_POST, isNewPost) outState.putBoolean(EditPostActivityConstants.STATE_KEY_IS_VOICE_CONTENT_SET, isVoiceContentSet) - outState.putBoolean(EditPostActivityConstants.STATE_KEY_IS_NEW_GUTENBERG, isNewGutenbergEditor) + outState.putBoolean(EditPostActivityConstants.STATE_KEY_IS_GUTENBERG_KIT, isGutenbergKitEditor) outState.putBoolean( EditPostActivityConstants.STATE_KEY_IS_PHOTO_PICKER_VISIBLE, editorPhotoPicker?.isPhotoPickerShowing() ?: false @@ -1411,11 +1412,11 @@ class EditPostActivity : LocaleAwareActivity(), EditorFragmentActivity, EditorIm val helpMenuItem = menu.findItem(R.id.menu_editor_help) if (undoItem != null) { undoItem.setEnabled(menuHasUndo) - undoItem.setVisible(!htmlModeMenuStateOn && !isNewGutenbergEditor) + undoItem.setVisible(!htmlModeMenuStateOn && !isGutenbergKitEditor) } if (redoItem != null) { redoItem.setEnabled(menuHasRedo) - redoItem.setVisible(!htmlModeMenuStateOn && !isNewGutenbergEditor) + redoItem.setVisible(!htmlModeMenuStateOn && !isGutenbergKitEditor) } if (secondaryAction != null && editPostRepository.hasPost()) { secondaryAction.setVisible(showMenuItems && this.secondaryAction.isVisible) @@ -1425,7 +1426,7 @@ class EditPostActivity : LocaleAwareActivity(), EditorFragmentActivity, EditorIm if (viewHtmlModeMenuItem != null) { viewHtmlModeMenuItem.setVisible( (((editorFragment is AztecEditorFragment) - || (editorFragment is GutenbergEditorFragment))) && !isNewGutenbergEditor && showMenuItems + || (editorFragment is GutenbergEditorFragment))) && showMenuItems ) viewHtmlModeMenuItem.setTitle( if (htmlModeMenuStateOn) R.string.menu_visual_mode else R.string.menu_html_mode) @@ -1462,7 +1463,7 @@ class EditPostActivity : LocaleAwareActivity(), EditorFragmentActivity, EditorIm } val contentInfo = menu.findItem(R.id.menu_content_info) (editorFragment as? GutenbergEditorFragment)?.let { gutenbergEditorFragment -> - if (isNewGutenbergEditor) { + if (isGutenbergKitEditor) { contentInfo.isVisible = false } else { contentInfo.setOnMenuItemClickListener { _: MenuItem? -> @@ -1487,7 +1488,7 @@ class EditPostActivity : LocaleAwareActivity(), EditorFragmentActivity, EditorIm val showHelpAndSupport = jetpackFeatureRemovalPhaseHelper.shouldShowHelpAndSupportOnEditor() val helpMenuTitle = if (showHelpAndSupport) R.string.help_and_support else R.string.help helpMenuItem.setTitle(helpMenuTitle) - if (editorFragment is GutenbergEditorFragment && showMenuItems && !isNewGutenbergEditor) { + if (editorFragment is GutenbergEditorFragment && showMenuItems) { helpMenuItem.setVisible(true) } else { helpMenuItem.setVisible(false) @@ -1620,6 +1621,8 @@ class EditPostActivity : LocaleAwareActivity(), EditorFragmentActivity, EditorIm (editorFragment as AztecEditorFragment).onToolbarHtmlButtonClicked() } else if (editorFragment is GutenbergEditorFragment) { (editorFragment as GutenbergEditorFragment).onToggleHtmlMode() + } else if (editorFragment is GutenbergKitEditorFragment) { + (editorFragment as GutenbergKitEditorFragment).onToggleHtmlMode() } } else if (itemId == R.id.menu_switch_to_gutenberg) { // The following boolean check should be always redundant but was added to manage @@ -1644,10 +1647,16 @@ class EditPostActivity : LocaleAwareActivity(), EditorFragmentActivity, EditorIm if (editorFragment is GutenbergEditorFragment) { (editorFragment as GutenbergEditorFragment).onUndoPressed() } + if (editorFragment is GutenbergKitEditorFragment) { + (editorFragment as GutenbergKitEditorFragment).onUndoPressed() + } } else if (itemId == R.id.menu_redo_action) { if (editorFragment is GutenbergEditorFragment) { (editorFragment as GutenbergEditorFragment).onRedoPressed() } + if (editorFragment is GutenbergKitEditorFragment) { + (editorFragment as GutenbergKitEditorFragment).onRedoPressed() + } } } return false @@ -1799,13 +1808,14 @@ class EditPostActivity : LocaleAwareActivity(), EditorFragmentActivity, EditorIm private fun trackPostSessionEditorModeSwitch() { val isGutenberg: Boolean = editorFragment is GutenbergEditorFragment + val isGutenbergKit: Boolean = editorFragment is GutenbergKitEditorFragment postEditorAnalyticsSession?.switchEditor( - if (htmlModeMenuStateOn) PostEditorAnalyticsSession.Editor.HTML - else - ( - if (isGutenberg) PostEditorAnalyticsSession.Editor.GUTENBERG - else PostEditorAnalyticsSession.Editor.CLASSIC - ) + when { + htmlModeMenuStateOn -> PostEditorAnalyticsSession.Editor.HTML + isGutenberg -> PostEditorAnalyticsSession.Editor.GUTENBERG + isGutenbergKit -> PostEditorAnalyticsSession.Editor.GUTENBERG_KIT + else -> PostEditorAnalyticsSession.Editor.CLASSIC + } ) } @@ -2161,7 +2171,7 @@ class EditPostActivity : LocaleAwareActivity(), EditorFragmentActivity, EditorIm i.putExtra(EditPostActivityConstants.EXTRA_RESTART_EDITOR, restartEditorOption.name) i.putExtra(EditPostActivityConstants.STATE_KEY_EDITOR_SESSION_DATA, postEditorAnalyticsSession) i.putExtra(EditPostActivityConstants.EXTRA_IS_NEW_POST, isNewPost) - i.putExtra(EditPostActivityConstants.STATE_KEY_IS_NEW_GUTENBERG, isNewGutenbergEditor) + i.putExtra(EditPostActivityConstants.STATE_KEY_IS_GUTENBERG_KIT, isGutenbergKitEditor) setResult(RESULT_OK, i) } @@ -2391,7 +2401,9 @@ class EditPostActivity : LocaleAwareActivity(), EditorFragmentActivity, EditorIm override fun getItem(position: Int): Fragment { return when (position) { PAGE_CONTENT -> { - if (showGutenbergEditor) { + if (isGutenbergKitEditor && showGutenbergEditor) { + createGutenbergKitEditorFragment() + } else if (showGutenbergEditor) { createGutenbergEditorFragment() } else { // If gutenberg editor is not selected, default to Aztec. @@ -2405,7 +2417,7 @@ class EditPostActivity : LocaleAwareActivity(), EditorFragmentActivity, EditorIm } } - private fun createGutenbergEditorFragment(): GutenbergEditorFragment { + private fun createGutenbergKitEditorFragment(): GutenbergKitEditorFragment { // Enable gutenberg on the site & show the informative popup upon opening // the GB editor the first time when the remote setting value is still null setGutenbergEnabledIfNeeded() @@ -2414,7 +2426,6 @@ class EditPostActivity : LocaleAwareActivity(), EditorFragmentActivity, EditorIm } val isWpCom = site.isWPCom || siteModel.isPrivateWPComAtomic || siteModel.isWPComAtomic - val gutenbergPropsBuilder = gutenbergPropsBuilder val gutenbergWebViewAuthorizationData = GutenbergWebViewAuthorizationData( siteModel.url, isWpCom, @@ -2445,27 +2456,59 @@ class EditPostActivity : LocaleAwareActivity(), EditorFragmentActivity, EditorIm "siteApiRoot" to siteApiRoot, "authHeader" to authHeader, "siteApiNamespace" to siteApiNamespace, - "themeStyles" to newGutenbergThemeStylesConfig.isEnabled() + "themeStyles" to gutenbergKitThemeStylesConfig.isEnabled() ) - return GutenbergEditorFragment.newInstance( + return GutenbergKitEditorFragment.newInstance( getContext(), isNewPost, gutenbergWebViewAuthorizationData, - gutenbergPropsBuilder, jetpackFeatureRemovalPhaseHelper.shouldShowJetpackPoweredEditorFeatures(), - isNewGutenbergEditor, settings ) } + private fun createGutenbergEditorFragment(): GutenbergEditorFragment { + // Enable gutenberg on the site & show the informative popup upon opening + // the GB editor the first time when the remote setting value is still null + setGutenbergEnabledIfNeeded() + xPostsCapabilityChecker.retrieveCapability(siteModel) { isXpostsCapable -> + onXpostsSettingsCapability(isXpostsCapable) + } + + val isWpCom = site.isWPCom || siteModel.isPrivateWPComAtomic || siteModel.isWPComAtomic + val gutenbergPropsBuilder = gutenbergPropsBuilder + val gutenbergWebViewAuthorizationData = GutenbergWebViewAuthorizationData( + siteModel.url, + isWpCom, + accountStore.account.userId, + accountStore.account.userName, + accountStore.accessToken, + siteModel.selfHostedSiteId, + siteModel.username, + siteModel.password, + siteModel.isUsingWpComRestApi, + siteModel.webEditor, + userAgent.toString(), + isJetpackSsoEnabled + ) + + return GutenbergEditorFragment.newInstance( + getContext(), + isNewPost, + gutenbergWebViewAuthorizationData, + gutenbergPropsBuilder, + jetpackFeatureRemovalPhaseHelper.shouldShowJetpackPoweredEditorFeatures() + ) + } + override fun instantiateItem(container: ViewGroup, position: Int): Any { val fragment: Fragment = super.instantiateItem(container, position) as Fragment when (position) { PAGE_CONTENT -> { editorFragment = fragment as EditorFragmentAbstract editorFragment?.setImageLoader(imageLoader) - if (isNewGutenbergEditor) { + if (isGutenbergKitEditor) { editorFragment?.onEditorContentChanged(object : GutenbergView.ContentChangeListener { override fun onContentChanged(title: String, content: String) { storePostViewModel.savePostWithDelay() @@ -3592,7 +3635,7 @@ class EditPostActivity : LocaleAwareActivity(), EditorFragmentActivity, EditorIm } private fun updateVoiceContentIfNeeded() { - if (isNewGutenbergEditor) { + if (isGutenbergKitEditor) { return } // Check if voice content exists and this is a new post for a Gutenberg editor fragment diff --git a/WordPress/src/main/java/org/wordpress/android/ui/posts/EditPostActivityConstants.kt b/WordPress/src/main/java/org/wordpress/android/ui/posts/EditPostActivityConstants.kt index efd64596609a..8cb22e17928b 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/posts/EditPostActivityConstants.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/posts/EditPostActivityConstants.kt @@ -42,5 +42,5 @@ object EditPostActivityConstants{ const val STATE_KEY_UNDO = "stateKeyUndo" const val STATE_KEY_REDO = "stateKeyRedo" const val STATE_KEY_IS_VOICE_CONTENT_SET = "stateKeyIsVoiceContentSet" - const val STATE_KEY_IS_NEW_GUTENBERG = "stateKeyIsNewGutenberg" + const val STATE_KEY_IS_GUTENBERG_KIT = "stateKeyIsGutenbergKit" } diff --git a/WordPress/src/main/java/org/wordpress/android/ui/posts/PostEditorAnalyticsSession.java b/WordPress/src/main/java/org/wordpress/android/ui/posts/PostEditorAnalyticsSession.java index a3f28808157b..7d71b82d3a17 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/posts/PostEditorAnalyticsSession.java +++ b/WordPress/src/main/java/org/wordpress/android/ui/posts/PostEditorAnalyticsSession.java @@ -52,6 +52,7 @@ public class PostEditorAnalyticsSession implements Serializable { public enum Editor { GUTENBERG, + GUTENBERG_KIT, CLASSIC, HTML, WP_STORIES_CREATOR diff --git a/WordPress/src/main/java/org/wordpress/android/util/config/GutenbergKitFeatureConfig.kt b/WordPress/src/main/java/org/wordpress/android/util/config/GutenbergKitFeatureConfig.kt new file mode 100644 index 000000000000..f0475c1a9235 --- /dev/null +++ b/WordPress/src/main/java/org/wordpress/android/util/config/GutenbergKitFeatureConfig.kt @@ -0,0 +1,16 @@ +package org.wordpress.android.util.config + +import org.wordpress.android.BuildConfig +import org.wordpress.android.annotation.Feature +import javax.inject.Inject + +private const val GUTENBERG_KIT_FEATURE_REMOTE_FIELD = "experimental_block_editor" + +@Feature(GUTENBERG_KIT_FEATURE_REMOTE_FIELD, false) +class GutenbergKitFeatureConfig @Inject constructor( + appConfig: AppConfig +) : FeatureConfig( + appConfig, + BuildConfig.ENABLE_GUTENBERG_KIT, + GUTENBERG_KIT_FEATURE_REMOTE_FIELD +) diff --git a/WordPress/src/main/java/org/wordpress/android/util/config/GutenbergKitThemeStylesFeatureConfig.kt b/WordPress/src/main/java/org/wordpress/android/util/config/GutenbergKitThemeStylesFeatureConfig.kt new file mode 100644 index 000000000000..45904fd1b8cb --- /dev/null +++ b/WordPress/src/main/java/org/wordpress/android/util/config/GutenbergKitThemeStylesFeatureConfig.kt @@ -0,0 +1,16 @@ +package org.wordpress.android.util.config + +import org.wordpress.android.BuildConfig +import org.wordpress.android.annotation.Feature +import javax.inject.Inject + +private const val GUTENBERG_KIT_THEME_STYLES_FEATURE_REMOTE_FIELD = "experimental_block_editor_theme_styles" + +@Feature(GUTENBERG_KIT_THEME_STYLES_FEATURE_REMOTE_FIELD, false) +class GutenbergKitThemeStylesFeatureConfig @Inject constructor( + appConfig: AppConfig +) : FeatureConfig( + appConfig, + BuildConfig.ENABLE_GUTENBERG_KIT_THEME_STYLES, + GUTENBERG_KIT_THEME_STYLES_FEATURE_REMOTE_FIELD +) diff --git a/WordPress/src/main/java/org/wordpress/android/util/config/NewGutenbergFeatureConfig.kt b/WordPress/src/main/java/org/wordpress/android/util/config/NewGutenbergFeatureConfig.kt deleted file mode 100644 index 57c65030f957..000000000000 --- a/WordPress/src/main/java/org/wordpress/android/util/config/NewGutenbergFeatureConfig.kt +++ /dev/null @@ -1,16 +0,0 @@ -package org.wordpress.android.util.config - -import org.wordpress.android.BuildConfig -import org.wordpress.android.annotation.Feature -import javax.inject.Inject - -private const val NEW_GUTENBERG_FEATURE_REMOTE_FIELD = "experimental_block_editor" - -@Feature(NEW_GUTENBERG_FEATURE_REMOTE_FIELD, false) -class NewGutenbergFeatureConfig @Inject constructor( - appConfig: AppConfig -) : FeatureConfig( - appConfig, - BuildConfig.ENABLE_NEW_GUTENBERG, - NEW_GUTENBERG_FEATURE_REMOTE_FIELD -) diff --git a/WordPress/src/main/java/org/wordpress/android/util/config/NewGutenbergThemeStylesFeatureConfig.kt b/WordPress/src/main/java/org/wordpress/android/util/config/NewGutenbergThemeStylesFeatureConfig.kt deleted file mode 100644 index ce07f89586cb..000000000000 --- a/WordPress/src/main/java/org/wordpress/android/util/config/NewGutenbergThemeStylesFeatureConfig.kt +++ /dev/null @@ -1,16 +0,0 @@ -package org.wordpress.android.util.config - -import org.wordpress.android.BuildConfig -import org.wordpress.android.annotation.Feature -import javax.inject.Inject - -private const val NEW_GUTENBERG_THEME_STYLES_FEATURE_REMOTE_FIELD = "experimental_block_editor_theme_styles" - -@Feature(NEW_GUTENBERG_THEME_STYLES_FEATURE_REMOTE_FIELD, false) -class NewGutenbergThemeStylesFeatureConfig @Inject constructor( - appConfig: AppConfig -) : FeatureConfig( - appConfig, - BuildConfig.ENABLE_NEW_GUTENBERG_THEME_STYLES, - NEW_GUTENBERG_THEME_STYLES_FEATURE_REMOTE_FIELD -) diff --git a/libs/editor/src/main/java/org/wordpress/android/editor/gutenberg/GutenbergEditorFragment.java b/libs/editor/src/main/java/org/wordpress/android/editor/gutenberg/GutenbergEditorFragment.java index 7c3041a96d01..aaeacc51ca47 100644 --- a/libs/editor/src/main/java/org/wordpress/android/editor/gutenberg/GutenbergEditorFragment.java +++ b/libs/editor/src/main/java/org/wordpress/android/editor/gutenberg/GutenbergEditorFragment.java @@ -2,11 +2,9 @@ import android.app.Activity; import android.app.ProgressDialog; -import android.content.ClipData; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; -import android.net.Uri; import android.os.Bundle; import android.os.Handler; import android.os.Looper; @@ -20,7 +18,6 @@ import android.view.ViewGroup; import android.view.inputmethod.InputMethodManager; import android.webkit.URLUtil; -import android.webkit.ValueCallback; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -68,11 +65,8 @@ import org.wordpress.android.util.helpers.MediaFile; import org.wordpress.android.util.helpers.MediaGallery; import org.wordpress.aztec.IHistoryListener; -import org.wordpress.gutenberg.GutenbergView; -import org.wordpress.gutenberg.GutenbergView.TitleAndContentCallback; import org.wordpress.gutenberg.GutenbergView.ContentChangeListener; import org.wordpress.gutenberg.GutenbergView.OpenMediaLibraryListener; -import org.wordpress.gutenberg.GutenbergWebViewPool; import org.wordpress.mobile.ReactNativeGutenbergBridge.GutenbergBridgeJS2Parent.LogExceptionCallback; import org.wordpress.mobile.ReactNativeGutenbergBridge.GutenbergEmbedWebViewActivity; import org.wordpress.mobile.WPAndroidGlue.GutenbergJsException; @@ -98,7 +92,6 @@ import org.wordpress.mobile.WPAndroidGlue.WPAndroidGlueCode.OnReattachMediaUploadQueryListener; import org.wordpress.mobile.WPAndroidGlue.WPAndroidGlueCode.OnSetFeaturedImageListener; -import java.io.Serializable; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; @@ -107,11 +100,9 @@ import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.CountDownLatch; import java.util.stream.Collectors; import static org.wordpress.mobile.WPAndroidGlue.Media.createRNMediaUsingMimeType; -import static org.wordpress.gutenberg.Media.createMediaUsingMimeType; public class GutenbergEditorFragment extends EditorFragmentAbstract implements EditorMediaUploadListener, @@ -120,7 +111,6 @@ public class GutenbergEditorFragment extends EditorFragmentAbstract implements GutenbergDialogPositiveClickInterface, GutenbergDialogNegativeClickInterface, GutenbergNetworkConnectionListener { - @Nullable private GutenbergView mGutenbergView; private static final String GUTENBERG_EDITOR_NAME = "gutenberg"; private static final String KEY_HTML_MODE_ENABLED = "KEY_HTML_MODE_ENABLED"; private static final String KEY_EDITOR_DID_MOUNT = "KEY_EDITOR_DID_MOUNT"; @@ -131,8 +121,6 @@ public class GutenbergEditorFragment extends EditorFragmentAbstract implements public static final String ARG_FAILED_MEDIAS = "arg_failed_medias"; public static final String ARG_FEATURED_IMAGE_ID = "featured_image_id"; public static final String ARG_JETPACK_FEATURES_ENABLED = "jetpack_features_enabled"; - public static final String ARG_IS_NEW_GUTENBERG_ENABLED = "new_gutenberg"; - public static final String ARG_NEW_GUTENBERG_SETTINGS = "new_gutenberg_settings"; private static final int CAPTURE_PHOTO_PERMISSION_REQUEST_CODE = 101; private static final int CAPTURE_VIDEO_PERMISSION_REQUEST_CODE = 102; @@ -158,8 +146,6 @@ public class GutenbergEditorFragment extends EditorFragmentAbstract implements private Runnable mInvalidateOptionsRunnable; private LiveTextWatcher mTextWatcher = new LiveTextWatcher(); - @Nullable private ContentChangeListener mContentChangeListener = null; - @Nullable private OpenMediaLibraryListener mOpenMediaLibraryListener = null; // 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. @@ -172,7 +158,6 @@ public class GutenbergEditorFragment extends EditorFragmentAbstract implements private boolean mIsNewPost; private boolean mIsJetpackSsoEnabled; - private static boolean mIsNewGutenbergEnabled; private boolean mEditorDidMount; private GutenbergPropsBuilder mCurrentGutenbergPropsBuilder; @@ -183,25 +168,18 @@ public class GutenbergEditorFragment extends EditorFragmentAbstract implements private String mUpdatedStoryBlockContent = null; private ProgressDialog mSavingContentProgressDialog; - @Nullable private static Map mSettings; public static GutenbergEditorFragment newInstance(Context context, boolean isNewPost, GutenbergWebViewAuthorizationData webViewAuthorizationData, GutenbergPropsBuilder gutenbergPropsBuilder, - boolean jetpackFeaturesEnabled, - boolean newGutenbergEnabled, - @Nullable Map settings) { + boolean jetpackFeaturesEnabled) { GutenbergEditorFragment fragment = new GutenbergEditorFragment(); Bundle args = new Bundle(); args.putBoolean(ARG_IS_NEW_POST, isNewPost); args.putBoolean(ARG_JETPACK_FEATURES_ENABLED, jetpackFeaturesEnabled); - args.putBoolean(ARG_IS_NEW_GUTENBERG_ENABLED, newGutenbergEnabled); - args.putSerializable(ARG_NEW_GUTENBERG_SETTINGS, (Serializable) settings); fragment.setArguments(args); SavedInstanceDatabase db = SavedInstanceDatabase.Companion.getDatabase(context); - mIsNewGutenbergEnabled = newGutenbergEnabled; - mSettings = settings; if (db != null) { db.addParcel(ARG_GUTENBERG_WEB_VIEW_AUTH_DATA, webViewAuthorizationData); db.addParcel(ARG_GUTENBERG_PROPS_BUILDER, gutenbergPropsBuilder); @@ -210,10 +188,6 @@ public static GutenbergEditorFragment newInstance(Context context, } private GutenbergContainerFragment getGutenbergContainerFragment() { - if (mIsNewGutenbergEnabled) { - return mRetainedGutenbergContainerFragment; - } - if (mRetainedGutenbergContainerFragment == null) { mRetainedGutenbergContainerFragment = (GutenbergContainerFragment) getChildFragmentManager() .findFragmentByTag(GutenbergContainerFragment.TAG); @@ -230,10 +204,6 @@ private GutenbergContainerFragment getGutenbergContainerFragment() { public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); - if (getArguments() != null) { - mIsNewGutenbergEnabled = getArguments().getBoolean(ARG_IS_NEW_GUTENBERG_ENABLED); - } - if (getGutenbergContainerFragment() == null) { GutenbergPropsBuilder gutenbergPropsBuilder = null; SavedInstanceDatabase db = SavedInstanceDatabase.Companion.getDatabase(getContext()); @@ -242,18 +212,16 @@ public void onCreate(@Nullable Bundle savedInstanceState) { } mCurrentGutenbergPropsBuilder = gutenbergPropsBuilder; - if (!mIsNewGutenbergEnabled) { - FragmentManager fragmentManager = getChildFragmentManager(); - FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction(); - GutenbergContainerFragment fragment = - GutenbergContainerFragment.newInstance(requireContext(), gutenbergPropsBuilder); - fragment.setRetainInstance(true); - fragmentTransaction.add(fragment, GutenbergContainerFragment.TAG); - fragmentTransaction.commitNow(); - } + FragmentManager fragmentManager = getChildFragmentManager(); + FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction(); + GutenbergContainerFragment fragment = + GutenbergContainerFragment.newInstance(requireContext(), gutenbergPropsBuilder); + fragment.setRetainInstance(true); + fragmentTransaction.add(fragment, GutenbergContainerFragment.TAG); + fragmentTransaction.commitNow(); } - if (mUpdateCapabilitiesOnCreate && !mIsNewGutenbergEnabled) { + if (mUpdateCapabilitiesOnCreate) { getGutenbergContainerFragment().updateCapabilities(mCurrentGutenbergPropsBuilder); } @@ -267,7 +235,6 @@ public void onCreate(@Nullable Bundle savedInstanceState) { ARG_STORY_BLOCK_EXTERNALLY_EDITED_ORIGINAL_HASH); mFailedMediaIds = (HashSet) savedInstanceState.getSerializable(ARG_FAILED_MEDIAS); mFeaturedImageId = savedInstanceState.getLong(ARG_FEATURED_IMAGE_ID); - mIsNewGutenbergEnabled = savedInstanceState.getBoolean(ARG_IS_NEW_GUTENBERG_ENABLED); } } @@ -276,43 +243,8 @@ public void onCreate(@Nullable Bundle savedInstanceState) { public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { if (getArguments() != null) { mIsNewPost = getArguments().getBoolean(ARG_IS_NEW_POST); - mIsNewGutenbergEnabled = getArguments().getBoolean(ARG_IS_NEW_GUTENBERG_ENABLED); - mSettings = (Map) getArguments().getSerializable(ARG_NEW_GUTENBERG_SETTINGS); } - if (mIsNewGutenbergEnabled) { - mGutenbergView = GutenbergWebViewPool.getPreloadedWebView(requireContext()); - mGutenbergView.setLayoutParams(new ViewGroup.LayoutParams( - ViewGroup.LayoutParams.MATCH_PARENT, - ViewGroup.LayoutParams.MATCH_PARENT - )); - mGutenbergView.setOnFileChooserRequestedListener((intent, requestCode) -> { - startActivityForResult(intent, requestCode); - return null; - }); - mGutenbergView.setContentChangeListener(mContentChangeListener); - mGutenbergView.setOpenMediaLibraryListener(mOpenMediaLibraryListener); - mGutenbergView.setEditorDidBecomeAvailable(view -> { - mEditorFragmentListener.onEditorFragmentContentReady(new ArrayList(), false); - }); - - Integer postId = (Integer) mSettings.get("postId"); - if (postId != null && postId == 0) { - postId = -1; - } - mGutenbergView.start( - (String) mSettings.get("siteApiRoot"), - (String) mSettings.get("siteApiNamespace"), - (String) mSettings.get("authHeader"), - (Boolean) mSettings.get("themeStyles"), - postId, - (String) mSettings.get("postType"), - (String) mSettings.get("postTitle"), - (String) mSettings.get("postContent") - ); - - return mGutenbergView; - } View view = inflater.inflate(R.layout.fragment_gutenberg_editor, container, false); initializeSavingProgressDialog(); @@ -745,33 +677,6 @@ private void openGutenbergEmbedWebViewActivity(String html, String title) { public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { super.onActivityResult(requestCode, resultCode, data); - if (mIsNewGutenbergEnabled) { - if (requestCode == mGutenbergView.getPickImageRequestCode()) { - ValueCallback filePathCallback = mGutenbergView.getFilePathCallback(); - - if (filePathCallback != null) { - if (resultCode == Activity.RESULT_OK && data != null) { - if (data.getClipData() != null) { - ClipData clipData = data.getClipData(); - Uri[] uris = new Uri[clipData.getItemCount()]; - for (int i = 0; i < clipData.getItemCount(); i++) { - uris[i] = clipData.getItemAt(i).getUri(); - } - filePathCallback.onReceiveValue(uris); - } else if (data.getData() != null) { - Uri uri = data.getData(); - filePathCallback.onReceiveValue(new Uri[]{uri}); - } else { - filePathCallback.onReceiveValue(null); - } - } else { - filePathCallback.onReceiveValue(null); - } - mGutenbergView.resetFilePathCallback(); - } - } - } - if (requestCode == UNSUPPORTED_BLOCK_REQUEST_CODE) { if (resultCode == Activity.RESULT_OK) { String blockId = data.getStringExtra(WPGutenbergWebViewActivity.ARG_BLOCK_ID); @@ -875,10 +780,6 @@ public void onRequestPermissionsResult(int requestCode, @NonNull String[] permis } private void setEditorProgressBarVisibility(boolean shown) { - if (mIsNewGutenbergEnabled) { - return; - } - if (isAdded() && getView() != null) { getView().findViewById(R.id.editor_progress).setVisibility(shown ? View.VISIBLE : View.GONE); } @@ -1184,9 +1085,6 @@ public void setTitle(CharSequence title) { title = ""; } - if (mIsNewGutenbergEnabled) { - return; - } getGutenbergContainerFragment().setTitle(title.toString()); } @@ -1196,11 +1094,6 @@ public void setContent(CharSequence text) { text = ""; } - if (mIsNewGutenbergEnabled) { - mGutenbergView.setContent((String) text); - return; - } - String postContent = removeVisualEditorProgressTag(text.toString()); getGutenbergContainerFragment().setContent(postContent); } @@ -1256,9 +1149,6 @@ private void toggleHtmlMode() { } public void sendToJSPostSaveEvent() { - if (mIsNewGutenbergEnabled) { - return; - } getGutenbergContainerFragment().sendToJSPostSaveEvent(); } @@ -1282,27 +1172,6 @@ private String removeVisualEditorProgressTag(String originalText) { @Override public Pair getTitleAndContent(CharSequence originalContent) throws EditorFragmentNotAddedException { - if (mIsNewGutenbergEnabled) { - final Pair[] result = new Pair[1]; - final CountDownLatch latch = new CountDownLatch(1); - - mGutenbergView.getTitleAndContent(new TitleAndContentCallback() { - @Override - public void onResult(@Nullable String title, @NonNull String content) { - result[0] = new Pair<>(title, content); - latch.countDown(); - } - }, true); - - try { - latch.await(); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - return new Pair<>("", ""); - } - - return result[0] != null ? result[0] : new Pair<>("", ""); - } if (!isAdded()) { throw new EditorFragmentNotAddedException(); } @@ -1388,11 +1257,9 @@ public void onContentInfoReceived(HashMap contentInfo) { } public void onEditorContentChanged(@NonNull ContentChangeListener listener) { - mContentChangeListener = listener; } public void onOpenMediaLibrary(@NonNull OpenMediaLibraryListener listener) { - mOpenMediaLibraryListener = listener; } @Override @@ -1424,63 +1291,34 @@ public void appendMediaFiles(Map mediaList) { boolean isNetworkUrl = URLUtil.isNetworkUrl(mediaUrl); - if (mIsNewGutenbergEnabled) { - // Disable upload handling until supported--e.g., media shared to the app - if (mGutenbergView == null || !isNetworkUrl) { - return; - } + ArrayList processedMediaList = new ArrayList<>(); - ArrayList processedMediaList = new ArrayList<>(); - - for (Map.Entry mediaEntry : mediaList.entrySet()) { - int mediaId = Integer.parseInt(mediaEntry.getValue().getMediaId()); - String url = mediaEntry.getKey(); - MediaFile mediaFile = mediaEntry.getValue(); - Bundle metadata = new Bundle(); - String videoPressGuid = mediaFile.getVideoPressGuid(); - if (videoPressGuid != null) { - metadata.putString("videopressGUID", videoPressGuid); - } - processedMediaList.add(createMediaUsingMimeType(mediaId, - url, - mediaFile.getMimeType(), - mediaFile.getCaption(), - mediaFile.getTitle(), - mediaFile.getAlt())); - } - - String mediaString = new Gson().toJson(processedMediaList); - mGutenbergView.setMediaUploadAttachment(mediaString); - } else { - ArrayList processedMediaList = new ArrayList<>(); - - if (!isNetworkUrl) { - for (Media media : processedMediaList) { - mUploadingMediaProgressMax.put(String.valueOf(media.getId()), 0f); - } + if (!isNetworkUrl) { + for (Media media : processedMediaList) { + mUploadingMediaProgressMax.put(String.valueOf(media.getId()), 0f); } + } - for (Map.Entry mediaEntry : mediaList.entrySet()) { - int mediaId = isNetworkUrl ? Integer.valueOf(mediaEntry.getValue().getMediaId()) - : mediaEntry.getValue().getId(); - String url = isNetworkUrl ? mediaEntry.getKey() : "file://" + mediaEntry.getKey(); - MediaFile mediaFile = mediaEntry.getValue(); - WritableNativeMap metadata = new WritableNativeMap(); - String videoPressGuid = mediaFile.getVideoPressGuid(); - if (videoPressGuid != null) { - metadata.putString("videopressGUID", videoPressGuid); - } - processedMediaList.add(createRNMediaUsingMimeType(mediaId, - url, - mediaFile.getMimeType(), - mediaFile.getCaption(), - mediaFile.getTitle(), - mediaFile.getAlt(), - metadata)); + for (Map.Entry mediaEntry : mediaList.entrySet()) { + int mediaId = isNetworkUrl ? Integer.valueOf(mediaEntry.getValue().getMediaId()) + : mediaEntry.getValue().getId(); + String url = isNetworkUrl ? mediaEntry.getKey() : "file://" + mediaEntry.getKey(); + MediaFile mediaFile = mediaEntry.getValue(); + WritableNativeMap metadata = new WritableNativeMap(); + String videoPressGuid = mediaFile.getVideoPressGuid(); + if (videoPressGuid != null) { + metadata.putString("videopressGUID", videoPressGuid); } - - getGutenbergContainerFragment().appendMediaFiles(processedMediaList); + processedMediaList.add(createRNMediaUsingMimeType(mediaId, + url, + mediaFile.getMimeType(), + mediaFile.getCaption(), + mediaFile.getTitle(), + mediaFile.getAlt(), + metadata)); } + + getGutenbergContainerFragment().appendMediaFiles(processedMediaList); } @Override @@ -1540,10 +1378,6 @@ private boolean hideSavingProgressDialog() { @Override public void onDestroy() { - if (mIsNewGutenbergEnabled && mGutenbergView != null) { - GutenbergWebViewPool.recycleWebView(mGutenbergView); - mContentChangeListener = null; - } hideSavingProgressDialog(); super.onDestroy(); } @@ -1617,17 +1451,11 @@ public void onGalleryMediaUploadSucceeded(final long galleryId, long remoteMedia @Override public void onEditorThemeUpdated(Bundle editorTheme) { - if (mIsNewGutenbergEnabled) { - return; - } getGutenbergContainerFragment().updateTheme(editorTheme); } @Override public void showNotice(String message) { - if (mIsNewGutenbergEnabled) { - return; - } getGutenbergContainerFragment().showNotice(message); } @@ -1664,9 +1492,6 @@ public void onGutenbergDialogNegativeClicked(@NonNull String instanceTag) { @Override public void onConnectionStatusChange(boolean isConnected) { - if (mIsNewGutenbergEnabled) { - return; - } getGutenbergContainerFragment().onConnectionStatusChange(isConnected); if (isConnected && hasFailedMediaUploads()) { mEditorFragmentListener.onMediaRetryAll(mFailedMediaIds); diff --git a/libs/editor/src/main/java/org/wordpress/android/editor/gutenberg/GutenbergKitEditorFragment.java b/libs/editor/src/main/java/org/wordpress/android/editor/gutenberg/GutenbergKitEditorFragment.java new file mode 100644 index 000000000000..dbe53032e50b --- /dev/null +++ b/libs/editor/src/main/java/org/wordpress/android/editor/gutenberg/GutenbergKitEditorFragment.java @@ -0,0 +1,545 @@ +package org.wordpress.android.editor.gutenberg; + +import android.app.Activity; +import android.content.ClipData; +import android.content.Context; +import android.content.Intent; +import android.net.Uri; +import android.os.Bundle; +import android.text.Editable; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.view.View; +import android.view.ViewGroup; +import android.webkit.URLUtil; +import android.webkit.ValueCallback; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.core.util.Pair; +import androidx.lifecycle.LiveData; + +import com.android.volley.toolbox.ImageLoader; +import com.google.gson.Gson; + +import org.wordpress.android.editor.BuildConfig; +import org.wordpress.android.editor.EditorEditMediaListener; +import org.wordpress.android.editor.EditorFragmentAbstract; +import org.wordpress.android.editor.EditorImagePreviewListener; +import org.wordpress.android.editor.EditorMediaUploadListener; +import org.wordpress.android.editor.EditorThemeUpdateListener; +import org.wordpress.android.editor.LiveTextWatcher; +import org.wordpress.android.editor.R; +import org.wordpress.android.editor.gutenberg.GutenbergDialogFragment.GutenbergDialogNegativeClickInterface; +import org.wordpress.android.editor.gutenberg.GutenbergDialogFragment.GutenbergDialogPositiveClickInterface; +import org.wordpress.android.editor.savedinstance.SavedInstanceDatabase; +import org.wordpress.android.util.AppLog; +import org.wordpress.android.util.AppLog.T; +import org.wordpress.android.util.PermissionUtils; +import org.wordpress.android.util.ProfilingUtils; +import org.wordpress.android.util.helpers.MediaFile; +import org.wordpress.android.util.helpers.MediaGallery; +import org.wordpress.aztec.IHistoryListener; +import org.wordpress.gutenberg.GutenbergView; +import org.wordpress.gutenberg.GutenbergView.ContentChangeListener; +import org.wordpress.gutenberg.GutenbergView.OpenMediaLibraryListener; +import org.wordpress.gutenberg.GutenbergView.TitleAndContentCallback; +import org.wordpress.gutenberg.GutenbergWebViewPool; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Map; +import java.util.concurrent.CountDownLatch; + +import static org.wordpress.gutenberg.Media.createMediaUsingMimeType; + +public class GutenbergKitEditorFragment extends EditorFragmentAbstract implements + EditorMediaUploadListener, + IHistoryListener, + EditorThemeUpdateListener, + GutenbergDialogPositiveClickInterface, + GutenbergDialogNegativeClickInterface, + GutenbergNetworkConnectionListener { + @Nullable private GutenbergView mGutenbergView; + private static final String GUTENBERG_EDITOR_NAME = "gutenberg"; + private static final String KEY_HTML_MODE_ENABLED = "KEY_HTML_MODE_ENABLED"; + private static final String KEY_EDITOR_DID_MOUNT = "KEY_EDITOR_DID_MOUNT"; + private static final String ARG_IS_NEW_POST = "param_is_new_post"; + private static final String ARG_GUTENBERG_WEB_VIEW_AUTH_DATA = "param_gutenberg_web_view_auth_data"; + public static final String ARG_FEATURED_IMAGE_ID = "featured_image_id"; + public static final String ARG_JETPACK_FEATURES_ENABLED = "jetpack_features_enabled"; + public static final String ARG_GUTENBERG_KIT_SETTINGS = "gutenberg_kit_settings"; + + private static final int CAPTURE_PHOTO_PERMISSION_REQUEST_CODE = 101; + private static final int CAPTURE_VIDEO_PERMISSION_REQUEST_CODE = 102; + + private boolean mHtmlModeEnabled; + + private final LiveTextWatcher mTextWatcher = new LiveTextWatcher(); + @Nullable private ContentChangeListener mContentChangeListener = null; + @Nullable private OpenMediaLibraryListener mOpenMediaLibraryListener = null; + + private boolean mEditorDidMount; + + @Nullable private static Map mSettings; + + public static GutenbergKitEditorFragment newInstance(Context context, + boolean isNewPost, + GutenbergWebViewAuthorizationData webViewAuthorizationData, + boolean jetpackFeaturesEnabled, + @Nullable Map settings) { + GutenbergKitEditorFragment fragment = new GutenbergKitEditorFragment(); + Bundle args = new Bundle(); + args.putBoolean(ARG_IS_NEW_POST, isNewPost); + args.putBoolean(ARG_JETPACK_FEATURES_ENABLED, jetpackFeaturesEnabled); + args.putSerializable(ARG_GUTENBERG_KIT_SETTINGS, (Serializable) settings); + fragment.setArguments(args); + SavedInstanceDatabase db = SavedInstanceDatabase.Companion.getDatabase(context); + mSettings = settings; + if (db != null) { + db.addParcel(ARG_GUTENBERG_WEB_VIEW_AUTH_DATA, webViewAuthorizationData); + } + return fragment; + } + + @Override + public void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + ProfilingUtils.start("Visual Editor Startup"); + ProfilingUtils.split("EditorFragment.onCreate"); + + if (savedInstanceState != null) { + mHtmlModeEnabled = savedInstanceState.getBoolean(KEY_HTML_MODE_ENABLED); + mEditorDidMount = savedInstanceState.getBoolean(KEY_EDITOR_DID_MOUNT); + mFeaturedImageId = savedInstanceState.getLong(ARG_FEATURED_IMAGE_ID); + } + } + + @SuppressWarnings("MethodLength") + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + if (getArguments() != null) { + mSettings = (Map) getArguments().getSerializable(ARG_GUTENBERG_KIT_SETTINGS); + } + + mGutenbergView = GutenbergWebViewPool.getPreloadedWebView(requireContext()); + mGutenbergView.setLayoutParams(new ViewGroup.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT + )); + mGutenbergView.setOnFileChooserRequestedListener((intent, requestCode) -> { + startActivityForResult(intent, requestCode); + return null; + }); + mGutenbergView.setContentChangeListener(mContentChangeListener); + mGutenbergView.setOpenMediaLibraryListener(mOpenMediaLibraryListener); + mGutenbergView.setEditorDidBecomeAvailable(view -> { + mEditorFragmentListener.onEditorFragmentContentReady(new ArrayList<>(), false); + }); + + Integer postId = (Integer) mSettings.get("postId"); + if (postId != null && postId == 0) { + postId = -1; + } + mGutenbergView.start( + (String) mSettings.get("siteApiRoot"), + (String) mSettings.get("siteApiNamespace"), + (String) mSettings.get("authHeader"), + (Boolean) mSettings.get("themeStyles"), + postId, + (String) mSettings.get("postType"), + (String) mSettings.get("postTitle"), + (String) mSettings.get("postContent") + ); + + return mGutenbergView; + } + + @Override + public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { + super.onActivityResult(requestCode, resultCode, data); + + if (requestCode == mGutenbergView.getPickImageRequestCode()) { + ValueCallback filePathCallback = mGutenbergView.getFilePathCallback(); + + if (filePathCallback != null) { + if (resultCode == Activity.RESULT_OK && data != null) { + if (data.getClipData() != null) { + ClipData clipData = data.getClipData(); + Uri[] uris = new Uri[clipData.getItemCount()]; + for (int i = 0; i < clipData.getItemCount(); i++) { + uris[i] = clipData.getItemAt(i).getUri(); + } + filePathCallback.onReceiveValue(uris); + } else if (data.getData() != null) { + Uri uri = data.getData(); + filePathCallback.onReceiveValue(new Uri[]{uri}); + } else { + filePathCallback.onReceiveValue(null); + } + } else { + filePathCallback.onReceiveValue(null); + } + mGutenbergView.resetFilePathCallback(); + } + } + } + + @Override public void onResume() { + super.onResume(); + } + + @Override + public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, + @NonNull int[] grantResults) { + if (PermissionUtils.checkCameraAndStoragePermissions(this.getActivity())) { + if (requestCode == CAPTURE_PHOTO_PERMISSION_REQUEST_CODE) { + mEditorFragmentListener.onCapturePhotoClicked(); + } else if (requestCode == CAPTURE_VIDEO_PERMISSION_REQUEST_CODE) { + mEditorFragmentListener.onCaptureVideoClicked(); + } + } + } + + @Override + public void onAttach(Activity activity) { + super.onAttach(activity); + + try { + mEditorDragAndDropListener = (EditorDragAndDropListener) activity; + } catch (ClassCastException e) { + throw new ClassCastException(activity + " must implement EditorDragAndDropListener"); + } + + try { + mEditorImagePreviewListener = (EditorImagePreviewListener) activity; + } catch (ClassCastException e) { + throw new ClassCastException(activity + " must implement EditorImagePreviewListener"); + } + + try { + mEditorEditMediaListener = (EditorEditMediaListener) activity; + } catch (ClassCastException e) { + throw new ClassCastException(activity + " must implement EditorEditMediaListener"); + } + } + + @Override + public void onSaveInstanceState(Bundle outState) { + outState.putBoolean(KEY_HTML_MODE_ENABLED, mHtmlModeEnabled); + outState.putBoolean(KEY_EDITOR_DID_MOUNT, mEditorDidMount); + outState.putLong(ARG_FEATURED_IMAGE_ID, mFeaturedImageId); + } + + @Override + public void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflater) { + inflater.inflate(R.menu.menu_gutenberg, menu); + } + + @Override + public void onPrepareOptionsMenu(@NonNull Menu menu) { + MenuItem debugMenuItem = menu.findItem(R.id.debugmenu); + debugMenuItem.setVisible(BuildConfig.DEBUG); + + super.onPrepareOptionsMenu(menu); + } + + @Override + public boolean onOptionsItemSelected(@NonNull MenuItem item) { + return false; + } + + @Override + public void onRedoEnabled() { + // Currently unsupported + } + + @Override + public void onUndoEnabled() { + // Currently unsupported + } + + @Override + public void onUndo() { + // Analytics tracking is not available in GB mobile + } + + @Override + public void onRedo() { + // Analytics tracking is not available in GB mobile + } + + @Override + public void setTitle(CharSequence title) { + // Unused, no-op retained for the shared interface with Gutenberg + } + + @Override + public void setContent(CharSequence text) { + if (text == null) { + text = ""; + } + + mGutenbergView.setContent((String) text); + } + + @Override + public void updateContent(@Nullable CharSequence text) { + // Unused, no-op retained for the shared interface with Gutenberg + } + + public void onToggleHtmlMode() { + if (!isAdded()) { + return; + } + + toggleHtmlMode(); + } + + private void toggleHtmlMode() { + mHtmlModeEnabled = !mHtmlModeEnabled; + mEditorFragmentListener.onTrackableEvent(TrackableEvent.HTML_BUTTON_TAPPED); + mEditorFragmentListener.onHtmlModeToggledInToolbar(); + } + + @Override + public Pair getTitleAndContent(CharSequence originalContent) throws + EditorFragmentNotAddedException { + final Pair[] result = new Pair[1]; + final CountDownLatch latch = new CountDownLatch(1); + + mGutenbergView.getTitleAndContent(new TitleAndContentCallback() { + @Override + public void onResult(@Nullable String title, @NonNull String content) { + result[0] = new Pair<>(title, content); + latch.countDown(); + } + }, true); + + try { + latch.await(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + return new Pair<>("", ""); + } + + return result[0] != null ? result[0] : new Pair<>("", ""); + } + + @NonNull + @Override + public String getEditorName() { + return GUTENBERG_EDITOR_NAME; + } + + @Override + public boolean isActionInProgress() { + return false; + } + + /** + * Returns the contents of the content field from the JavaScript editor. Should be called from a background thread + * where possible. + */ + @Override + public CharSequence getContent(CharSequence originalContent) throws EditorFragmentNotAddedException { + if (!isAdded()) { + throw new EditorFragmentNotAddedException(); + } + + return ""; + } + + @Override + public void showContentInfo() throws EditorFragmentNotAddedException { + if (!isAdded()) { + throw new EditorFragmentNotAddedException(); + } + } + + public void onEditorContentChanged(@NonNull ContentChangeListener listener) { + mContentChangeListener = listener; + } + + public void onOpenMediaLibrary(@NonNull OpenMediaLibraryListener listener) { + mOpenMediaLibraryListener = listener; + } + + @Override + public LiveData getTitleOrContentChanged() { + return mTextWatcher.getAfterTextChanged(); + } + + @Override + public void appendMediaFile(final MediaFile mediaFile, final String mediaUrl, ImageLoader imageLoader) { + // noop implementation for shared interface with Aztec + } + + @Override + public void appendMediaFiles(Map mediaList) { + if (getActivity() == null) { + // appendMediaFile may be called from a background thread (example: EditPostActivity.java#L2165) and + // Activity may have already be gone. + // Ticket: https://github.com/wordpress-mobile/WordPress-Android/issues/7386 + AppLog.d(T.MEDIA, "appendMediaFiles() called but Activity is null!"); + return; + } + + // Get media URL of first of media first to check if it is network or local one. + String mediaUrl = ""; + Object[] mediaUrls = mediaList.keySet().toArray(); + if (mediaUrls.length > 0) { + mediaUrl = (String) mediaUrls[0]; + } + + boolean isNetworkUrl = URLUtil.isNetworkUrl(mediaUrl); + + // Disable upload handling until supported--e.g., media shared to the app + if (mGutenbergView == null || !isNetworkUrl) { + return; + } + + ArrayList processedMediaList = new ArrayList<>(); + + for (Map.Entry mediaEntry : mediaList.entrySet()) { + int mediaId = Integer.parseInt(mediaEntry.getValue().getMediaId()); + String url = mediaEntry.getKey(); + MediaFile mediaFile = mediaEntry.getValue(); + Bundle metadata = new Bundle(); + String videoPressGuid = mediaFile.getVideoPressGuid(); + if (videoPressGuid != null) { + metadata.putString("videopressGUID", videoPressGuid); + } + processedMediaList.add(createMediaUsingMimeType(mediaId, + url, + mediaFile.getMimeType(), + mediaFile.getCaption(), + mediaFile.getTitle(), + mediaFile.getAlt())); + } + + String mediaString = new Gson().toJson(processedMediaList); + mGutenbergView.setMediaUploadAttachment(mediaString); + } + + @Override + public void appendGallery(MediaGallery mediaGallery) { + // Unused, no-op retained for the shared interface with Gutenberg + } + + @Override + public void setUrlForVideoPressId(final String videoId, final String videoUrl, final String posterUrl) { + // Unused, no-op retained for the shared interface with Gutenberg + } + + @Override + public boolean isUploadingMedia() { + // Unused, no-op retained for the shared interface with Gutenberg + return false; + } + + @Override + public boolean hasFailedMediaUploads() { + // Unused, no-op retained for the shared interface with Gutenberg + return false; + } + + @Override + public void removeAllFailedMediaUploads() { + // Unused, no-op retained for the shared interface with Gutenberg + } + + @Override + public void removeMedia(String mediaId) { + // Unused, no-op retained for the shared interface with Gutenberg + } + + @Override + public void onDestroy() { + if (mGutenbergView != null) { + GutenbergWebViewPool.recycleWebView(mGutenbergView); + mContentChangeListener = null; + } + super.onDestroy(); + } + + @Override public void mediaSelectionCancelled() { + // Unused, no-op retained for the shared interface with Gutenberg + } + + @Override + public void onMediaUploadReattached(String localMediaId, float currentProgress) { + // Unused, no-op retained for the shared interface with Gutenberg + } + + @Override + public void onMediaUploadRetry(String localMediaId, MediaType mediaType) { + // Unused, no-op retained for the shared interface with Gutenberg + } + + @Override + public void onMediaUploadSucceeded(final String localMediaId, final MediaFile mediaFile) { + // Unused, no-op retained for the shared interface with Gutenberg + } + + @Override + public void onMediaUploadProgress(final String localMediaId, final float progress) { + // Unused, no-op retained for the shared interface with Gutenberg + } + + @Override + public void onMediaUploadFailed(final String localMediaId) { + // Unused, no-op retained for the shared interface with Gutenberg + } + + @Override + public void onMediaUploadPaused(final String localMediaId) { + // Unused, no-op retained for the shared interface with Gutenberg + } + + @Override + public void onGalleryMediaUploadSucceeded(final long galleryId, long remoteMediaId, int remaining) { + // Unused, no-op retained for the shared interface with Gutenberg + } + + @Override + public void onEditorThemeUpdated(Bundle editorTheme) { + // Unused, no-op retained for the shared interface with Gutenberg + } + + @Override + public void showNotice(String message) { + // Unused, no-op retained for the shared interface with Gutenberg + } + + @Override + public void showEditorHelp() { + // Unused, no-op retained for the shared interface with Gutenberg + } + + @Override public void onUndoPressed() { + // Currently unsupported + } + + @Override public void onRedoPressed() { + // Currently unsupported + } + + @Override + public void onGutenbergDialogPositiveClicked(@NonNull String instanceTag, int mediaId) { + // Unused, no-op retained for the shared interface with Gutenberg + } + + @Override + public void onGutenbergDialogNegativeClicked(@NonNull String instanceTag) { + // Unused, no-op retained for the shared interface with Gutenberg + } + + @Override + public void onConnectionStatusChange(boolean isConnected) { + // Unused, no-op retained for the shared interface with Gutenberg + } +}