From 2b6a2d4fe4e4e0f4db359f66047b90b92dce7113 Mon Sep 17 00:00:00 2001 From: David Calhoun Date: Thu, 21 Nov 2024 10:44:44 -0500 Subject: [PATCH 1/8] refactor: Duplicate Gutenberg editor fragment --- .../gutenberg/GutenbergKitEditorFragment.java | 1675 +++++++++++++++++ 1 file changed, 1675 insertions(+) create mode 100644 libs/editor/src/main/java/org/wordpress/android/editor/gutenberg/GutenbergKitEditorFragment.java 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..032f9d2d126b --- /dev/null +++ b/libs/editor/src/main/java/org/wordpress/android/editor/gutenberg/GutenbergKitEditorFragment.java @@ -0,0 +1,1675 @@ +package org.wordpress.android.editor.gutenberg; + +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; +import android.text.Editable; +import android.text.TextUtils; +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.view.inputmethod.InputMethodManager; +import android.webkit.URLUtil; +import android.webkit.ValueCallback; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.annotation.UiThread; +import androidx.appcompat.app.ActionBar; +import androidx.appcompat.app.AlertDialog; +import androidx.appcompat.app.AppCompatActivity; +import androidx.core.util.Consumer; +import androidx.core.util.Pair; +import androidx.fragment.app.FragmentActivity; +import androidx.fragment.app.FragmentManager; +import androidx.fragment.app.FragmentTransaction; +import androidx.lifecycle.LiveData; + +import com.android.volley.toolbox.ImageLoader; +import com.automattic.android.tracks.crashlogging.JsException; +import com.automattic.android.tracks.crashlogging.JsExceptionCallback; +import com.automattic.android.tracks.crashlogging.JsExceptionStackTraceElement; +import com.facebook.react.bridge.ReadableMap; +import com.facebook.react.bridge.WritableNativeMap; +import com.google.android.material.dialog.MaterialAlertDialogBuilder; +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.EditorFragmentActivity; +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.WPGutenbergWebViewActivity; +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.DateTimeUtils; +import org.wordpress.android.util.NetworkUtils; +import org.wordpress.android.util.PermissionUtils; +import org.wordpress.android.util.ProfilingUtils; +import org.wordpress.android.util.StringUtils; +import org.wordpress.android.util.ToastUtils; +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 org.wordpress.mobile.ReactNativeGutenbergBridge.GutenbergBridgeJS2Parent.LogExceptionCallback; +import org.wordpress.mobile.ReactNativeGutenbergBridge.GutenbergEmbedWebViewActivity; +import org.wordpress.mobile.WPAndroidGlue.GutenbergJsException; +import org.wordpress.mobile.WPAndroidGlue.Media; +import org.wordpress.mobile.WPAndroidGlue.MediaOption; +import org.wordpress.mobile.WPAndroidGlue.RequestExecutor; +import org.wordpress.mobile.WPAndroidGlue.ShowSuggestionsUtil; +import org.wordpress.mobile.WPAndroidGlue.UnsupportedBlock; +import org.wordpress.mobile.WPAndroidGlue.WPAndroidGlueCode.OnBackHandlerEventListener; +import org.wordpress.mobile.WPAndroidGlue.WPAndroidGlueCode.OnBlockTypeImpressionsEventListener; +import org.wordpress.mobile.WPAndroidGlue.WPAndroidGlueCode.OnConnectionStatusEventListener; +import org.wordpress.mobile.WPAndroidGlue.WPAndroidGlueCode.OnContentInfoReceivedListener; +import org.wordpress.mobile.WPAndroidGlue.WPAndroidGlueCode.OnCustomerSupportOptionsListener; +import org.wordpress.mobile.WPAndroidGlue.WPAndroidGlueCode.OnEditorMountListener; +import org.wordpress.mobile.WPAndroidGlue.WPAndroidGlueCode.OnFocalPointPickerTooltipShownEventListener; +import org.wordpress.mobile.WPAndroidGlue.WPAndroidGlueCode.OnGetContentInterrupted; +import org.wordpress.mobile.WPAndroidGlue.WPAndroidGlueCode.OnGutenbergDidRequestEmbedFullscreenPreviewListener; +import org.wordpress.mobile.WPAndroidGlue.WPAndroidGlueCode.OnGutenbergDidRequestPreviewListener; +import org.wordpress.mobile.WPAndroidGlue.WPAndroidGlueCode.OnGutenbergDidRequestUnsupportedBlockFallbackListener; +import org.wordpress.mobile.WPAndroidGlue.WPAndroidGlueCode.OnGutenbergDidSendButtonPressedActionListener; +import org.wordpress.mobile.WPAndroidGlue.WPAndroidGlueCode.OnLogExceptionListener; +import org.wordpress.mobile.WPAndroidGlue.WPAndroidGlueCode.OnMediaLibraryButtonListener; +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; +import java.util.HashSet; +import java.util.List; +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.gutenberg.Media.createMediaUsingMimeType; +import static org.wordpress.mobile.WPAndroidGlue.Media.createRNMediaUsingMimeType; + +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"; + private static final String ARG_GUTENBERG_PROPS_BUILDER = "param_gutenberg_props_builder"; + public static final String ARG_STORY_BLOCK_EXTERNALLY_EDITED_ORIGINAL_HASH = "story_block_original_hash"; + 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; + + private static final String MEDIA_SOURCE_FILE = "MEDIA_SOURCE_FILE"; + private static final String MEDIA_SOURCE_AUDIO_FILE = "MEDIA_SOURCE_AUDIO_FILE"; + private static final String MEDIA_SOURCE_STOCK_MEDIA = "MEDIA_SOURCE_STOCK_MEDIA"; + private static final String GIF_MEDIA = "GIF_MEDIA"; + + private static final String USER_EVENT_KEY_TEMPLATE = "template"; + + private static final int UNSUPPORTED_BLOCK_REQUEST_CODE = 1001; + + private static final int EMBED_FULLSCREEN_PREVIEW_CODE = 1002; + + private static final String TAG_REPLACE_FEATURED_DIALOG = "REPLACE_FEATURED_DIALOG"; + + public static final int MEDIA_ID_NO_FEATURED_IMAGE_SET = 0; + + private boolean mHtmlModeEnabled; + + private Handler mInvalidateOptionsHandler; + 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. + // See discussion at https://github.com/wordpress-mobile/WordPress-Android/pull/9030#issuecomment-459447537 and on. + GutenbergContainerFragment mRetainedGutenbergContainerFragment; + + private ConcurrentHashMap mUploadingMediaProgressMax = new ConcurrentHashMap<>(); + private HashSet mFailedMediaIds = new HashSet<>(); + private ConcurrentHashMap mCancelledMediaIds = new ConcurrentHashMap<>(); + + private boolean mIsNewPost; + private boolean mIsJetpackSsoEnabled; + private static boolean mIsNewGutenbergEnabled; + + private boolean mEditorDidMount; + private GutenbergPropsBuilder mCurrentGutenbergPropsBuilder; + private boolean mUpdateCapabilitiesOnCreate = false; + private String mExternallyEditedBlockOriginalHash = null; + private boolean mStoryBlockReplacedSignalWait = false; + + private String mUpdatedStoryBlockContent = null; + + private ProgressDialog mSavingContentProgressDialog; + @Nullable private static Map mSettings; + + public static GutenbergKitEditorFragment newInstance(Context context, + boolean isNewPost, + GutenbergWebViewAuthorizationData webViewAuthorizationData, + GutenbergPropsBuilder gutenbergPropsBuilder, + boolean jetpackFeaturesEnabled, + boolean newGutenbergEnabled, + @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.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); + } + return fragment; + } + + private GutenbergContainerFragment getGutenbergContainerFragment() { + if (mIsNewGutenbergEnabled) { + return mRetainedGutenbergContainerFragment; + } + + if (mRetainedGutenbergContainerFragment == null) { + mRetainedGutenbergContainerFragment = (GutenbergContainerFragment) getChildFragmentManager() + .findFragmentByTag(GutenbergContainerFragment.TAG); + } else { + // Noop. Just use the cached reference. The container fragment might not be attached yet so, getting it from + // the fragment manager is not reliable. No need either; it's retained and outlives this EditorFragment. + } + + return mRetainedGutenbergContainerFragment; + } + + @SuppressWarnings("unchecked") + @Override + 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()); + if (db != null) { + gutenbergPropsBuilder = db.getParcel(ARG_GUTENBERG_PROPS_BUILDER, GutenbergPropsBuilder.CREATOR); + } + 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(); + } + } + + if (mUpdateCapabilitiesOnCreate && !mIsNewGutenbergEnabled) { + getGutenbergContainerFragment().updateCapabilities(mCurrentGutenbergPropsBuilder); + } + + 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); + mExternallyEditedBlockOriginalHash = savedInstanceState.getString( + 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); + } + } + + @SuppressWarnings("MethodLength") + @Override + 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(); + + ViewGroup gutenbergContainer = view.findViewById(R.id.gutenberg_container); + getGutenbergContainerFragment().attachToContainer(gutenbergContainer, + new OnMediaLibraryButtonListener() { + @Override public void onMediaLibraryImageButtonClicked(boolean allowMultipleSelection) { + mEditorFragmentListener.onTrackableEvent(TrackableEvent.MEDIA_BUTTON_TAPPED); + mEditorFragmentListener.onAddMediaImageClicked(allowMultipleSelection); + } + + @Override + public void onMediaLibraryVideoButtonClicked(boolean allowMultipleSelection) { + mEditorFragmentListener.onTrackableEvent(TrackableEvent.MEDIA_BUTTON_TAPPED); + mEditorFragmentListener.onAddMediaVideoClicked(allowMultipleSelection); + } + + @Override + public void onMediaLibraryMediaButtonClicked(boolean allowMultipleSelection) { + mEditorFragmentListener.onTrackableEvent(TrackableEvent.MEDIA_BUTTON_TAPPED); + mEditorFragmentListener.onAddLibraryMediaClicked(allowMultipleSelection); + } + + @Override public void onMediaLibraryFileButtonClicked(boolean allowMultipleSelection) { + mEditorFragmentListener.onTrackableEvent(TrackableEvent.MEDIA_BUTTON_TAPPED); + mEditorFragmentListener.onAddLibraryFileClicked(allowMultipleSelection); + } + + @Override public void onMediaLibraryAudioButtonClicked(boolean allowMultipleSelection) { + mEditorFragmentListener.onTrackableEvent(TrackableEvent.MEDIA_BUTTON_TAPPED); + mEditorFragmentListener.onAddLibraryAudioFileClicked(allowMultipleSelection); + } + + @Override + public void onUploadPhotoButtonClicked(boolean allowMultipleSelection) { + mEditorFragmentListener.onAddPhotoClicked(allowMultipleSelection); + } + + @Override + public void onUploadVideoButtonClicked(boolean allowMultipleSelection) { + mEditorFragmentListener.onAddVideoClicked(allowMultipleSelection); + } + + @Override + public void onUploadMediaButtonClicked(boolean allowMultipleSelection) { + mEditorFragmentListener.onAddDeviceMediaClicked(allowMultipleSelection); + } + + @Override + public void onCaptureVideoButtonClicked() { + checkAndRequestCameraAndStoragePermissions(CAPTURE_VIDEO_PERMISSION_REQUEST_CODE); + } + + @Override + public void onCapturePhotoButtonClicked() { + checkAndRequestCameraAndStoragePermissions(CAPTURE_PHOTO_PERMISSION_REQUEST_CODE); + } + + @Override + public void onRetryUploadForMediaClicked(int mediaId) { + if (getActivity() != null) { + getActivity().runOnUiThread(() -> { + showRetryMediaUploadDialog(mediaId); + }); + } + } + + @Override + public void onCancelUploadForMediaClicked(int mediaId) { + if (getActivity() != null) { + getActivity().runOnUiThread(() -> { + showCancelMediaUploadDialog(mediaId); + }); + } + } + + @Override + public void onCancelUploadForMediaDueToDeletedBlock(int mediaId) { + cancelMediaUploadForDeletedBlock(mediaId); + } + + @Override + public ArrayList onGetOtherMediaImageOptions() { + ArrayList otherMediaImageOptions = initOtherMediaImageOptions(); + return otherMediaImageOptions; + } + + @Override + public ArrayList onGetOtherMediaFileOptions() { + ArrayList otherMediaFileOptions = initOtherMediaFileOptions(); + return otherMediaFileOptions; + } + + @Override public ArrayList onGetOtherMediaAudioFileOptions() { + return initOtherMediaAudioFileOptions(); + } + + @Override + public void onOtherMediaButtonClicked(String mediaSource, boolean allowMultipleSelection) { + switch (mediaSource) { + case MEDIA_SOURCE_STOCK_MEDIA: + mEditorFragmentListener.onAddStockMediaClicked(allowMultipleSelection); + break; + case GIF_MEDIA: + mEditorFragmentListener.onAddGifClicked(allowMultipleSelection); + break; + case MEDIA_SOURCE_FILE: + mEditorFragmentListener.onAddFileClicked(allowMultipleSelection); + break; + case MEDIA_SOURCE_AUDIO_FILE: + mEditorFragmentListener.onAddAudioFileClicked(allowMultipleSelection); + break; + default: + AppLog.e(T.EDITOR, + "Unsupported media source " + mediaSource); + } + } + }, + new OnReattachMediaUploadQueryListener() { + @Override + public void onQueryCurrentProgressForUploadingMedia() { + updateFailedMediaState(); + updateMediaProgress(); + } + }, + new OnSetFeaturedImageListener() { + @Override + public void onSetFeaturedImageButtonClicked(int mediaId) { + if (mediaId == mFeaturedImageId) { + // nothing special to do, trying to set the image that's already set as featured + return; + } + + if (mediaId == MEDIA_ID_NO_FEATURED_IMAGE_SET) { + // user tries to clear the featured image setting + setFeaturedImage(mediaId); + return; + } + + if (mFeaturedImageId == MEDIA_ID_NO_FEATURED_IMAGE_SET) { + // current featured image is not set so, go ahead and set it to the provided one + setFeaturedImage(mediaId); + return; + } + + // ask the user to confirm changing the featured image since there's already one set + showFeaturedImageConfirmationDialog(mediaId); + } + }, + new OnEditorMountListener() { + @Override + public void onEditorDidMount(ArrayList unsupportedBlocks) { + mEditorDidMount = true; + mEditorFragmentListener.onEditorFragmentContentReady( + unsupportedBlocks, + mExternallyEditedBlockOriginalHash != null + ); + + // Hide the progress bar when editor is ready + new Handler(Looper.getMainLooper()).post(new Runnable() { + @Override + public void run() { + setEditorProgressBarVisibility(!mEditorDidMount); + } + }); + } + }, + mTextWatcher::postTextChanged, + mEditorFragmentListener::onAuthHeaderRequested, + new RequestExecutor() { + @Override + public void performGetRequest(String path, boolean enableCaching, Consumer onSuccess, + Consumer onError) { + mEditorFragmentListener.onPerformFetch(path, enableCaching, onSuccess, onError); + } + + @Override + public void performPostRequest( + String path, + ReadableMap data, + Consumer onSuccess, + Consumer onError) { + mEditorFragmentListener.onPerformPost(path, data.toHashMap(), onSuccess, onError); + } + }, + mEditorImagePreviewListener::onImagePreviewRequested, + mEditorEditMediaListener::onMediaEditorRequested, + new OnGutenbergDidRequestUnsupportedBlockFallbackListener() { + @Override + public void gutenbergDidRequestUnsupportedBlockFallback(UnsupportedBlock unsupportedBlock) { + openGutenbergWebViewActivity( + unsupportedBlock.getContent(), + unsupportedBlock.getId(), + unsupportedBlock.getName(), + unsupportedBlock.getTitle() + ); + } + }, + new OnGutenbergDidRequestEmbedFullscreenPreviewListener() { + @Override public void gutenbergDidRequestEmbedFullscreenPreview(String html, String title) { + openGutenbergEmbedWebViewActivity(html, title); + } + }, + new OnGutenbergDidSendButtonPressedActionListener() { + @Override + public void gutenbergDidSendButtonPressedAction(String buttonType) { + mEditorFragmentListener.showJetpackSettings(); + } + }, + + new ShowSuggestionsUtil() { + @Override public void showUserSuggestions(Consumer onResult) { + mEditorFragmentListener.showUserSuggestions(onResult); + } + + @Override public void showXpostSuggestions(Consumer onResult) { + mEditorFragmentListener.showXpostSuggestions(onResult); + } + }, + new OnFocalPointPickerTooltipShownEventListener() { + @Override + public void onSetFocalPointPickerTooltipShown(boolean tooltipShown) { + mEditorFragmentListener.onGutenbergEditorSetFocalPointPickerTooltipShown(tooltipShown); + } + + @Override + public boolean onRequestFocalPointPickerTooltipShown() { + return mEditorFragmentListener.onGutenbergEditorRequestFocalPointPickerTooltipShown(); + } + }, + new OnGutenbergDidRequestPreviewListener() { + @Override + public void gutenbergDidRequestPreview() { + mEditorFragmentListener.showPreview(); + } + }, + new OnBlockTypeImpressionsEventListener() { + @Override + public Map onRequestBlockTypeImpressions() { + return mEditorFragmentListener.onRequestBlockTypeImpressions(); + } + + @Override + public void onSetBlockTypeImpressions(Map impressions) { + mEditorFragmentListener.onSetBlockTypeImpressions(impressions); + } + }, + new OnCustomerSupportOptionsListener() { + @Override + public void onContactCustomerSupport() { + if (getActivity() != null) { + getActivity().runOnUiThread(() -> { + mEditorFragmentListener.onContactCustomerSupport(); + }); + } + } + + @Override + public void onGotoCustomerSupportOptions() { + mEditorFragmentListener.onGotoCustomerSupportOptions(); + } + }, + + mEditorFragmentListener::onSendEventToHost, + + mEditorFragmentListener::onToggleUndo, + + mEditorFragmentListener::onToggleRedo, + + new OnConnectionStatusEventListener() { + @Override public boolean onRequestConnectionStatus() { + return NetworkUtils.isNetworkAvailable(getActivity()); + } + }, + + new OnBackHandlerEventListener() { + @Override public void onBackHandler() { + mEditorFragmentListener.onBackHandlerButton(); + } + }, + + new OnLogExceptionListener() { + @Override public void onLogException(GutenbergJsException exception, + LogExceptionCallback logExceptionCallback) { + List stackTraceElements = exception.getStackTrace().stream().map( + stackTrace -> { + return new JsExceptionStackTraceElement( + stackTrace.getFileName(), + stackTrace.getLineNumber(), + stackTrace.getColNumber(), + stackTrace.getFunction() + ); + }).collect(Collectors.toList()); + + JsException jsException = new JsException( + exception.getType(), + exception.getMessage(), + stackTraceElements, + exception.getContext(), + exception.getTags(), + exception.isHandled(), + exception.getHandledBy() + ); + + JsExceptionCallback callback = new JsExceptionCallback() { + @Override + public void onReportSent(boolean success) { + logExceptionCallback.onLogException(success); + } + }; + + mEditorFragmentListener.onLogJsException(jsException, callback); + } + }, + + GutenbergUtils.isDarkMode(getActivity())); + + // request dependency injection. Do this after setting min/max dimensions + if (getActivity() instanceof EditorFragmentActivity) { + ((EditorFragmentActivity) getActivity()).initializeEditorFragment(); + } + + setHasOptionsMenu(true); + + mInvalidateOptionsHandler = new Handler(); + mInvalidateOptionsRunnable = new Runnable() { + @Override + public void run() { + if (isAdded()) { + getActivity().invalidateOptionsMenu(); + } + } + }; + + if (!getGutenbergContainerFragment().hasReceivedAnyContent()) { + // container is empty, which means it's a fresh instance so, signal to complete its init + mEditorFragmentListener.onEditorFragmentInitialized(); + } + + if (mIsNewPost) { + showImplicitKeyboard(); + } + + return view; + } + + private String calculateHashOnMediaCollectionBasedBlock(ArrayList mediaFiles) { + Gson gson = new Gson(); + // make sure to normalize ids to Strings as these may vary and make the hash not coincide + normalizeMediaFilesIds(mediaFiles); + return StringUtils.getMd5Hash(gson.toJson(mediaFiles)); + } + + @SuppressWarnings("unchecked") + private void normalizeMediaFilesIds(ArrayList mediaFiles) { + // iterate through all of mediaFiles objects and convert ids to String + for (Object mediaFile : mediaFiles) { + // this conversion is needed to strip off decimals that can come from RN when using int as + // string + if (((HashMap) mediaFile).get("id") instanceof Double) { + Double originalValue = (Double) ((HashMap) mediaFile).get("id"); + // now set it back to String to normalize with temporary ids + ((HashMap) mediaFile).put("id", String.valueOf(originalValue.longValue())); + } + } + } + + private void initializeSavingProgressDialog() { + if (mEditorFragmentListener != null) { + mEditorFragmentListener.getSavingInProgressDialogVisibility() + .observe(getViewLifecycleOwner(), visibility -> { + if (DialogVisibility.Showing == visibility) { + showSavingProgressDialogIfNeeded(); + } else { + hideSavingProgressDialog(); + } + }); + } + } + + private GutenbergWebViewAuthorizationData getGutenbergWebViewAuthorizationData() { + SavedInstanceDatabase db = SavedInstanceDatabase.Companion.getDatabase(getContext()); + if (db != null) { + return db.getParcel(ARG_GUTENBERG_WEB_VIEW_AUTH_DATA, GutenbergWebViewAuthorizationData.CREATOR); + } + return null; + } + + private void openGutenbergWebViewActivity(String content, String blockId, String blockName, String blockTitle) { + GutenbergWebViewAuthorizationData gutenbergWebViewAuthData = getGutenbergWebViewAuthorizationData(); + + // There is a chance that isJetpackSsoEnabled has changed on the server + // so we need to make sure that we have fresh value of it. + gutenbergWebViewAuthData.setJetpackSsoEnabled(mIsJetpackSsoEnabled); + + Intent intent = new Intent(getActivity(), WPGutenbergWebViewActivity.class); + intent.putExtra(WPGutenbergWebViewActivity.ARG_BLOCK_ID, blockId); + intent.putExtra(WPGutenbergWebViewActivity.ARG_BLOCK_TITLE, blockTitle); + intent.putExtra(WPGutenbergWebViewActivity.ARG_BLOCK_CONTENT, content); + intent.putExtra(WPGutenbergWebViewActivity.ARG_GUTENBERG_WEB_VIEW_AUTH_DATA, gutenbergWebViewAuthData); + + startActivityForResult(intent, UNSUPPORTED_BLOCK_REQUEST_CODE); + + HashMap properties = new HashMap<>(); + properties.put("block", blockName); + mEditorFragmentListener.onTrackableEvent( + TrackableEvent.EDITOR_GUTENBERG_UNSUPPORTED_BLOCK_WEBVIEW_SHOWN, + properties); + } + + private void trackWebViewClosed(String action) { + HashMap properties = new HashMap<>(); + properties.put("action", action); + mEditorFragmentListener.onTrackableEvent( + TrackableEvent.EDITOR_GUTENBERG_UNSUPPORTED_BLOCK_WEBVIEW_CLOSED, + properties); + } + + private void openGutenbergEmbedWebViewActivity(String html, String title) { + Activity activity = getActivity(); + + Intent intent = new Intent(activity, GutenbergEmbedWebViewActivity.class); + intent.putExtra(GutenbergEmbedWebViewActivity.ARG_CONTENT, html); + intent.putExtra(GutenbergEmbedWebViewActivity.ARG_TITLE, title); + activity.startActivityForResult(intent, EMBED_FULLSCREEN_PREVIEW_CODE); + } + + @Override + 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); + String content = data.getStringExtra(WPGutenbergWebViewActivity.ARG_BLOCK_CONTENT); + getGutenbergContainerFragment().replaceUnsupportedBlock(content, blockId); + if (mCurrentGutenbergPropsBuilder == null) { + SavedInstanceDatabase db = SavedInstanceDatabase.Companion.getDatabase(getContext()); + if (db != null) { + mCurrentGutenbergPropsBuilder = db.getParcel(ARG_GUTENBERG_PROPS_BUILDER, + GutenbergPropsBuilder.CREATOR); + } + } + // We need to send latest capabilities as JS side clears them + getGutenbergContainerFragment().updateCapabilities(mCurrentGutenbergPropsBuilder); + trackWebViewClosed("save"); + } else { + trackWebViewClosed("dismiss"); + } + } + } + + private ArrayList initOtherMediaImageOptions() { + ArrayList otherMediaOptions = new ArrayList<>(); + + Bundle arguments = getArguments(); + FragmentActivity activity = getActivity(); + final Context context = getContext(); + if (activity == null || context == null || arguments == null) { + AppLog.e(T.EDITOR, + "Failed to initialize other media options because the activity or getArguments() is null"); + return otherMediaOptions; + } + + boolean jetpackFeaturesEnabled = arguments.getBoolean(ARG_JETPACK_FEATURES_ENABLED); + GutenbergWebViewAuthorizationData gutenbergWebViewAuthorizationData = getGutenbergWebViewAuthorizationData(); + + boolean supportStockPhotos = gutenbergWebViewAuthorizationData.isSiteUsingWPComRestAPI() + && jetpackFeaturesEnabled; + boolean supportsTenor = jetpackFeaturesEnabled; + + String packageName = activity.getApplication().getPackageName(); + if (supportStockPhotos) { + int stockMediaResourceId = + context.getResources().getIdentifier("photo_picker_stock_media", "string", packageName); + + otherMediaOptions.add(new MediaOption(MEDIA_SOURCE_STOCK_MEDIA, getString(stockMediaResourceId))); + } + if (supportsTenor) { + int gifMediaResourceId = + context.getResources().getIdentifier("photo_picker_gif", "string", packageName); + otherMediaOptions.add(new MediaOption(GIF_MEDIA, getString(gifMediaResourceId))); + } + + return otherMediaOptions; + } + + private ArrayList initOtherMediaFileOptions() { + return initOtherMediaFileOptions(MEDIA_SOURCE_FILE); + } + + private ArrayList initOtherMediaAudioFileOptions() { + return initOtherMediaFileOptions(MEDIA_SOURCE_AUDIO_FILE); + } + + private ArrayList initOtherMediaFileOptions(String mediaOptionId) { + ArrayList otherMediaOptions = new ArrayList<>(); + + FragmentActivity activity = getActivity(); + if (activity == null) { + AppLog.e(T.EDITOR, + "Failed to initialize other media options because the activity is null"); + return otherMediaOptions; + } + + String packageName = activity.getApplication().getPackageName(); + + int chooseFileResourceId = + getResources().getIdentifier("photo_picker_choose_file", "string", packageName); + + otherMediaOptions.add(new MediaOption(mediaOptionId, getString(chooseFileResourceId))); + + return otherMediaOptions; + } + + @Override public void onResume() { + super.onResume(); + + setEditorProgressBarVisibility(!mEditorDidMount); + } + + @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(); + } + } + } + + private void setEditorProgressBarVisibility(boolean shown) { + if (mIsNewGutenbergEnabled) { + return; + } + + if (isAdded() && getView() != null) { + getView().findViewById(R.id.editor_progress).setVisibility(shown ? View.VISIBLE : View.GONE); + } + } + + public void resetUploadingMediaToFailed(Set failedMediaIds) { + // get all media failed for this post, and represent it on tje UI + if (failedMediaIds != null && !failedMediaIds.isEmpty()) { + for (Integer mediaId : failedMediaIds) { + // and keep track of failed ids around + mFailedMediaIds.add(String.valueOf(mediaId)); + } + } + } + + private void updateFailedMediaState() { + for (String mediaId : mFailedMediaIds) { + // upload progress should work on numeric mediaIds only + if (!TextUtils.isEmpty(mediaId) && TextUtils.isDigitsOnly(mediaId)) { + if (NetworkUtils.isNetworkAvailable(getActivity())) { + getGutenbergContainerFragment().mediaFileUploadFailed(Integer.valueOf(mediaId)); + } else { + getGutenbergContainerFragment().mediaFileUploadPaused(Integer.valueOf(mediaId)); + } + } else { + getGutenbergContainerFragment().mediaFileSaveFailed(mediaId); + } + } + } + + private void updateMediaProgress() { + for (String mediaId : mUploadingMediaProgressMax.keySet()) { + // upload progress should work on numeric mediaIds only + if (!TextUtils.isEmpty(mediaId) && TextUtils.isDigitsOnly(mediaId)) { + getGutenbergContainerFragment().mediaFileUploadProgress(Integer.valueOf(mediaId), + mUploadingMediaProgressMax.get(mediaId)); + } else { + getGutenbergContainerFragment().mediaFileSaveProgress(mediaId, + mUploadingMediaProgressMax.get(mediaId)); + } + } + } + + private void checkAndRequestCameraAndStoragePermissions(int permissionRequestCode) { + if (PermissionUtils.checkAndRequestCameraAndStoragePermissions(this, + permissionRequestCode)) { + if (permissionRequestCode == CAPTURE_PHOTO_PERMISSION_REQUEST_CODE) { + mEditorFragmentListener.onCapturePhotoClicked(); + } else if (permissionRequestCode == CAPTURE_VIDEO_PERMISSION_REQUEST_CODE) { + mEditorFragmentListener.onCaptureVideoClicked(); + } + } + } + + private void cancelMediaUploadForDeletedBlock(int localMediaId) { + if (mUploadingMediaProgressMax.containsKey(String.valueOf(localMediaId))) { + // first make sure to signal deletion + mEditorFragmentListener.onMediaDeleted(String.valueOf(localMediaId)); + // second also perform a media upload cancel action, through the onMediaUploadCancelClicked interface + mEditorFragmentListener.onMediaUploadCancelClicked(String.valueOf(localMediaId)); + mUploadingMediaProgressMax.remove(String.valueOf(localMediaId)); + } else { + // upload has already finished by the time the user deleted the block, so no op + } + } + + @UiThread + private void showCancelMediaUploadDialog(final int localMediaId) { + // Display 'cancel upload' dialog + AlertDialog.Builder builder = new MaterialAlertDialogBuilder(getActivity()); + builder.setTitle(getString(R.string.stop_upload_dialog_title)); + builder.setPositiveButton(R.string.stop_upload_dialog_button_yes, + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + if (mUploadingMediaProgressMax.containsKey(String.valueOf(localMediaId))) { + mEditorFragmentListener.onMediaUploadCancelClicked(String.valueOf(localMediaId)); + // remove from editor + mEditorFragmentListener.onMediaDeleted(String.valueOf(localMediaId)); + getGutenbergContainerFragment().clearMediaFileURL(localMediaId); + mCancelledMediaIds.put(String.valueOf(localMediaId), new Date()); + mUploadingMediaProgressMax.remove(String.valueOf(localMediaId)); + } else { + ToastUtils.showToast(getActivity(), R.string.upload_finished_toast).show(); + } + dialog.dismiss(); + } + }); + + builder.setNegativeButton(R.string.stop_upload_dialog_button_no, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + dialog.dismiss(); + } + }); + + AlertDialog dialog = builder.create(); + dialog.show(); + } + + @UiThread + private void showRetryMediaUploadDialog(final int mediaId) { + // Display 'retry upload' dialog + AlertDialog.Builder builder = new MaterialAlertDialogBuilder(getActivity()); + builder.setTitle(getString(R.string.retry_failed_upload_title)); + String mediaErrorMessage = mEditorFragmentListener.getErrorMessageFromMedia(mediaId); + if (!TextUtils.isEmpty(mediaErrorMessage)) { + builder.setMessage(mediaErrorMessage); + } + builder.setPositiveButton(R.string.retry_failed_upload_yes, + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + dialog.dismiss(); + mEditorFragmentListener.onMediaRetryAll(mFailedMediaIds); + } + }); + + builder.setNegativeButton(R.string.retry_failed_upload_remove, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + dialog.dismiss(); + mEditorFragmentListener.onMediaDeleted(String.valueOf(mediaId)); + mFailedMediaIds.remove(String.valueOf(mediaId)); + getGutenbergContainerFragment().clearMediaFileURL(mediaId); + } + }); + + AlertDialog dialog = builder.create(); + dialog.show(); + } + + @UiThread + public void showFeaturedImageConfirmationDialog(final int mediaId) { + if (isStateSaved()) { + return; + } + + GutenbergDialogFragment dialog = new GutenbergDialogFragment(); + dialog.initialize( + TAG_REPLACE_FEATURED_DIALOG, + getString(R.string.featured_image_replace_dialog_title), + getString(R.string.featured_image_replace_dialog_description), + getString(R.string.featured_image_replace_dialog_confirm), + getString(R.string.featured_image_replace_dialog_cancel), + mediaId + ); + + dialog.show(getChildFragmentManager(), TAG_REPLACE_FEATURED_DIALOG); + } + + private void setFeaturedImage(int mediaId) { + mEditorFragmentListener.updateFeaturedImage(mediaId, false); + setFeaturedImageId(mediaId); + + if (mediaId == MEDIA_ID_NO_FEATURED_IMAGE_SET) { + showNotice(getString(R.string.featured_image_removed_notice)); + } else { + showNotice(getString(R.string.featured_image_confirmation_notice)); + } + } + + public void sendToJSFeaturedImageId(int mediaId) { + getGutenbergContainerFragment().sendToJSFeaturedImageId(mediaId); + } + + @UiThread + private void showCancelMediaCollectionUploadDialog(ArrayList mediaFiles) { + // Display 'cancel upload' dialog + AlertDialog.Builder builder = new MaterialAlertDialogBuilder(getActivity()); + builder.setTitle(getString(R.string.stop_upload_dialog_title)); + builder.setPositiveButton(R.string.stop_upload_dialog_button_yes, + new DialogInterface.OnClickListener() { + @SuppressWarnings("unchecked") + public void onClick(DialogInterface dialog, int id) { + // now signal Gutenberg upload failed, and remove the mediaIds from our tracking map + for (Object mediaFile : mediaFiles) { + // this conversion is needed to strip off decimals that can come from RN when using int as + // string + int localMediaId + = StringUtils.stringToInt( + ((HashMap) mediaFile).get("id").toString(), 0); + getGutenbergContainerFragment().mediaFileUploadFailed(localMediaId); + mUploadingMediaProgressMax.remove(localMediaId); + } + dialog.dismiss(); + } + }); + + builder.setNegativeButton(R.string.stop_upload_dialog_button_no, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + dialog.dismiss(); + } + }); + + AlertDialog dialog = builder.create(); + dialog.show(); + } + + private void showImplicitKeyboard() { + InputMethodManager keyboard = (InputMethodManager) getActivity().getSystemService(Context.INPUT_METHOD_SERVICE); + keyboard.toggleSoftInput(InputMethodManager.SHOW_IMPLICIT, 0); + } + + @Override + public void onAttach(Activity activity) { + super.onAttach(activity); + + try { + mEditorDragAndDropListener = (EditorDragAndDropListener) activity; + } catch (ClassCastException e) { + throw new ClassCastException(activity.toString() + " must implement EditorDragAndDropListener"); + } + + try { + mEditorImagePreviewListener = (EditorImagePreviewListener) activity; + } catch (ClassCastException e) { + throw new ClassCastException(activity.toString() + " must implement EditorImagePreviewListener"); + } + + try { + mEditorEditMediaListener = (EditorEditMediaListener) activity; + } catch (ClassCastException e) { + throw new ClassCastException(activity.toString() + " must implement EditorEditMediaListener"); + } + } + + @Override + public void onSaveInstanceState(Bundle outState) { + outState.putBoolean(KEY_HTML_MODE_ENABLED, mHtmlModeEnabled); + outState.putBoolean(KEY_EDITOR_DID_MOUNT, mEditorDidMount); + outState.putString(ARG_STORY_BLOCK_EXTERNALLY_EDITED_ORIGINAL_HASH, mExternallyEditedBlockOriginalHash); + outState.putSerializable(ARG_FAILED_MEDIAS, mFailedMediaIds); + 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) { + if (item.getItemId() == R.id.debugmenu) { + getGutenbergContainerFragment().showDevOptionsDialog(); + return true; + } + + return false; + } + + @Override + public void onRedoEnabled() { + if (!isAdded()) { + return; + } + + mInvalidateOptionsHandler.removeCallbacks(mInvalidateOptionsRunnable); + mInvalidateOptionsHandler.postDelayed(mInvalidateOptionsRunnable, + getResources().getInteger(android.R.integer.config_mediumAnimTime)); + } + + @Override + public void onUndoEnabled() { + if (!isAdded()) { + return; + } + + mInvalidateOptionsHandler.removeCallbacks(mInvalidateOptionsRunnable); + mInvalidateOptionsHandler.postDelayed(mInvalidateOptionsRunnable, + getResources().getInteger(android.R.integer.config_mediumAnimTime)); + } + + @Override + public void onUndo() { + // Analytics tracking is not available in GB mobile + } + + @Override + public void onRedo() { + // Analytics tracking is not available in GB mobile + } + + private ActionBar getActionBar() { + if (!isAdded()) { + return null; + } + + if (getActivity() instanceof AppCompatActivity) { + return ((AppCompatActivity) getActivity()).getSupportActionBar(); + } else { + return null; + } + } + + @Override + public void setTitle(CharSequence title) { + if (title == null) { + title = ""; + } + + if (mIsNewGutenbergEnabled) { + return; + } + getGutenbergContainerFragment().setTitle(title.toString()); + } + + @Override + public void setContent(CharSequence text) { + if (text == null) { + text = ""; + } + + if (mIsNewGutenbergEnabled) { + mGutenbergView.setContent((String) text); + return; + } + + String postContent = removeVisualEditorProgressTag(text.toString()); + getGutenbergContainerFragment().setContent(postContent); + } + + @Override + public void updateContent(@Nullable CharSequence text) { + if (text == null) { + text = ""; + } + + if (getGutenbergContainerFragment() != null) { + getGutenbergContainerFragment().onContentUpdate(text.toString()); + } + } + + public void setJetpackSsoEnabled(boolean jetpackSsoEnabled) { + mIsJetpackSsoEnabled = jetpackSsoEnabled; + } + + public void updateCapabilities(GutenbergPropsBuilder gutenbergPropsBuilder) { + mCurrentGutenbergPropsBuilder = gutenbergPropsBuilder; + if (isAdded()) { + GutenbergContainerFragment containerFragment = getGutenbergContainerFragment(); + if (containerFragment != null) { + containerFragment.updateCapabilities(gutenbergPropsBuilder); + } + } else { + mUpdateCapabilitiesOnCreate = true; + } + } + + public void onToggleHtmlMode() { + if (!isAdded()) { + return; + } + + toggleHtmlMode(); + } + + private void toggleHtmlMode() { + mHtmlModeEnabled = !mHtmlModeEnabled; + + mEditorFragmentListener.onTrackableEvent(TrackableEvent.HTML_BUTTON_TAPPED); + + // Don't switch to HTML mode if currently uploading media + if (!mUploadingMediaProgressMax.isEmpty() || isActionInProgress()) { + ToastUtils.showToast(getActivity(), R.string.alert_action_while_uploading, ToastUtils.Duration.LONG); + return; + } + + mEditorFragmentListener.onHtmlModeToggledInToolbar(); + getGutenbergContainerFragment().toggleHtmlMode(); + } + + public void sendToJSPostSaveEvent() { + if (mIsNewGutenbergEnabled) { + return; + } + getGutenbergContainerFragment().sendToJSPostSaveEvent(); + } + + /* + * TODO: REMOVE THIS ONCE AZTEC COMPLETELY REPLACES THE VISUAL EDITOR IN WPANDROID APP + */ + private String removeVisualEditorProgressTag(String originalText) { + // this regex picks any tags and any opening tags for image containers + // as produced by the Visual Editor. Note that we don't care about closing tags + // as the AztecParser takes care of that, and it would be very difficult to accomplish with a + // regex (and using a proper XML crawler would be particularly overkill) + if (originalText != null && originalText.contains(""; + return originalText.replaceAll(regex, ""); + } else { + return 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(); + } + return getGutenbergContainerFragment().getTitleAndContent(originalContent, new OnGetContentInterrupted() { + @Override public void onGetContentInterrupted(InterruptedException ie) { + AppLog.e(T.EDITOR, ie); + Thread.currentThread().interrupt(); + } + }); + } + + @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 getGutenbergContainerFragment().getContent(originalContent, new OnGetContentInterrupted() { + @Override public void onGetContentInterrupted(InterruptedException ie) { + AppLog.e(T.EDITOR, ie); + Thread.currentThread().interrupt(); + } + }); + } + + @Override + public void showContentInfo() throws EditorFragmentNotAddedException { + if (!isAdded()) { + throw new EditorFragmentNotAddedException(); + } + + getGutenbergContainerFragment().triggerGetContentInfo(new OnContentInfoReceivedListener() { + @Override + public void onContentInfoFailed() { + if (getActivity() != null) { + getActivity().runOnUiThread(() -> { + ToastUtils.showToast(getActivity(), R.string.toast_content_info_failed); + }); + } + } + + @Override + public void onEditorNotReady() { + if (getActivity() != null) { + getActivity().runOnUiThread(() -> { + ToastUtils.showToast(getActivity(), R.string.toast_content_info_editor_not_ready); + }); + } + } + + @Override + public void onContentInfoReceived(HashMap contentInfo) { + int blockCount = (int) Double.parseDouble(contentInfo.get("blockCount").toString()); + int wordCount = (int) Double.parseDouble(contentInfo.get("wordCount").toString()); + int charCount = (int) Double.parseDouble(contentInfo.get("characterCount").toString()); + + if (getActivity() != null) { + getActivity().runOnUiThread(() -> { + AlertDialog.Builder builder = new MaterialAlertDialogBuilder(getActivity()); + builder.setTitle(getString(R.string.dialog_content_info_title)); + builder.setMessage( + getString(R.string.dialog_content_info_body, blockCount, wordCount, charCount)); + builder.setPositiveButton(getString(R.string.dialog_button_ok), null); + builder.show(); + }); + } + } + }); + } + + 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 != null && mediaUrls.length > 0) { + mediaUrl = (String) mediaUrls[0]; + } + + 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<>(); + + 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); + } + } + + 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)); + } + + getGutenbergContainerFragment().appendMediaFiles(processedMediaList); + } + } + + @Override + public void appendGallery(MediaGallery mediaGallery) { + } + + @Override + public void setUrlForVideoPressId(final String videoId, final String videoUrl, final String posterUrl) { + } + + @Override + public boolean isUploadingMedia() { + return false; + } + + @Override + public boolean hasFailedMediaUploads() { + return (mFailedMediaIds.size() > 0); + } + + @Override + public void removeAllFailedMediaUploads() { + } + + @Override + public void removeMedia(String mediaId) { + } + + @UiThread + private boolean showSavingProgressDialogIfNeeded() { + if (!isAdded()) { + return false; + } + + if (mSavingContentProgressDialog != null && mSavingContentProgressDialog.isShowing()) { + // Already on the screen? no need to show it again. + return true; + } + + if (mSavingContentProgressDialog == null) { + mSavingContentProgressDialog = new ProgressDialog(getActivity()); + mSavingContentProgressDialog.setCancelable(false); + mSavingContentProgressDialog.setIndeterminate(true); + mSavingContentProgressDialog.setMessage(getActivity().getString(R.string.long_post_dlg_saving)); + } + mSavingContentProgressDialog.show(); + return true; + } + + private boolean hideSavingProgressDialog() { + if (mSavingContentProgressDialog != null && mSavingContentProgressDialog.isShowing()) { + mSavingContentProgressDialog.dismiss(); + return true; + } + return false; + } + + @Override + public void onDestroy() { + if (mIsNewGutenbergEnabled && mGutenbergView != null) { + GutenbergWebViewPool.recycleWebView(mGutenbergView); + mContentChangeListener = null; + } + hideSavingProgressDialog(); + super.onDestroy(); + } + + @Override public void mediaSelectionCancelled() { + getGutenbergContainerFragment().mediaSelectionCancelled(); + } + + @Override + public void onMediaUploadReattached(String localMediaId, float currentProgress) { + mUploadingMediaProgressMax.put(localMediaId, currentProgress); + getGutenbergContainerFragment().mediaFileUploadProgress(Integer.valueOf(localMediaId), currentProgress); + } + + @Override + public void onMediaUploadRetry(String localMediaId, MediaType mediaType) { + if (mFailedMediaIds.contains(localMediaId)) { + mFailedMediaIds.remove(localMediaId); + mUploadingMediaProgressMax.put(localMediaId, 0f); + } + + // TODO request to start the upload again from the UploadService + } + + @Override + public void onMediaUploadSucceeded(final String localMediaId, final MediaFile mediaFile) { + mUploadingMediaProgressMax.remove(localMediaId); + + WritableNativeMap metadata = new WritableNativeMap(); + if (mediaFile.getVideoPressGuid() != null) { + metadata.putString("videopressGUID", mediaFile.getVideoPressGuid()); + } + + getGutenbergContainerFragment() + .mediaFileUploadSucceeded(Integer.parseInt(localMediaId), mediaFile.getOptimalFileURL(), + Integer.parseInt(mediaFile.getMediaId()), metadata); + } + + @Override + public void onMediaUploadProgress(final String localMediaId, final float progress) { + if (!mCancelledMediaIds.containsKey(localMediaId)) { + mUploadingMediaProgressMax.put(localMediaId, progress); + getGutenbergContainerFragment().mediaFileUploadProgress(Integer.valueOf(localMediaId), progress); + } else { + // checks to ensure that its been two seconds since the last progress event and if so then + // we treat the event as a new one and remove it from the cancelled media IDs being tracked. + Date startTime = mCancelledMediaIds.get(localMediaId); + if (DateTimeUtils.secondsBetween(startTime, new Date()) > 2) { + mCancelledMediaIds.remove(localMediaId); + } + } + } + + @Override + public void onMediaUploadFailed(final String localMediaId) { + getGutenbergContainerFragment().mediaFileUploadFailed(Integer.valueOf(localMediaId)); + mFailedMediaIds.add(localMediaId); + mUploadingMediaProgressMax.remove(localMediaId); + } + + @Override + public void onMediaUploadPaused(final String localMediaId) { + getGutenbergContainerFragment().mediaFileUploadPaused(Integer.valueOf(localMediaId)); + mFailedMediaIds.add(localMediaId); + mUploadingMediaProgressMax.remove(localMediaId); + } + + @Override + public void onGalleryMediaUploadSucceeded(final long galleryId, long remoteMediaId, int remaining) { + } + + @Override + public void onEditorThemeUpdated(Bundle editorTheme) { + if (mIsNewGutenbergEnabled) { + return; + } + getGutenbergContainerFragment().updateTheme(editorTheme); + } + + @Override + public void showNotice(String message) { + if (mIsNewGutenbergEnabled) { + return; + } + getGutenbergContainerFragment().showNotice(message); + } + + @Override + public void showEditorHelp() { + getGutenbergContainerFragment().showEditorHelp(); + } + + @Override public void onUndoPressed() { + getGutenbergContainerFragment().onUndoPressed(); + } + + @Override public void onRedoPressed() { + getGutenbergContainerFragment().onRedoPressed(); + } + + @Override + public void onGutenbergDialogPositiveClicked(@NonNull String instanceTag, int mediaId) { + switch (instanceTag) { + case TAG_REPLACE_FEATURED_DIALOG: + setFeaturedImage(mediaId); + break; + } + } + + @Override + public void onGutenbergDialogNegativeClicked(@NonNull String instanceTag) { + switch (instanceTag) { + case TAG_REPLACE_FEATURED_DIALOG: + // Dismiss dialog with no action. + break; + } + } + + @Override + public void onConnectionStatusChange(boolean isConnected) { + if (mIsNewGutenbergEnabled) { + return; + } + getGutenbergContainerFragment().onConnectionStatusChange(isConnected); + if (isConnected && hasFailedMediaUploads()) { + mEditorFragmentListener.onMediaRetryAll(mFailedMediaIds); + } + } +} From 96b0e1e9b7c7f6d91a0804a12a7451d16ebbb324 Mon Sep 17 00:00:00 2001 From: David Calhoun Date: Thu, 21 Nov 2024 11:22:43 -0500 Subject: [PATCH 2/8] refactor: Abstract Gutenberg editor fragment detection logic Enable simple checking for both GBM and GBK. --- .../android/ui/posts/EditPostActivity.kt | 35 +++++++++++-------- 1 file changed, 20 insertions(+), 15 deletions(-) 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..ea9fb6775531 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 @@ -925,7 +926,7 @@ class EditPostActivity : LocaleAwareActivity(), EditorFragmentActivity, EditorIm val isJetpackSsoEnabled = siteModel.isJetpackConnected && siteSettings?.isJetpackSsoEnabled == true if (this.isJetpackSsoEnabled != isJetpackSsoEnabled) { this.isJetpackSsoEnabled = isJetpackSsoEnabled - if (editorFragment is GutenbergEditorFragment) { + if (isGutenbergEditorFragment(editorFragment)) { val gutenbergFragment = editorFragment as GutenbergEditorFragment gutenbergFragment.setJetpackSsoEnabled(this.isJetpackSsoEnabled) gutenbergFragment.updateCapabilities(gutenbergPropsBuilder) @@ -1425,7 +1426,7 @@ class EditPostActivity : LocaleAwareActivity(), EditorFragmentActivity, EditorIm if (viewHtmlModeMenuItem != null) { viewHtmlModeMenuItem.setVisible( (((editorFragment is AztecEditorFragment) - || (editorFragment is GutenbergEditorFragment))) && !isNewGutenbergEditor && showMenuItems + || (isGutenbergEditorFragment(editorFragment)))) && !isNewGutenbergEditor && showMenuItems ) viewHtmlModeMenuItem.setTitle( if (htmlModeMenuStateOn) R.string.menu_visual_mode else R.string.menu_html_mode) @@ -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 (isGutenbergEditorFragment(editorFragment) && showMenuItems && !isNewGutenbergEditor) { helpMenuItem.setVisible(true) } else { helpMenuItem.setVisible(false) @@ -1618,7 +1619,7 @@ class EditPostActivity : LocaleAwareActivity(), EditorFragmentActivity, EditorIm // toggle HTML mode if (editorFragment is AztecEditorFragment) { (editorFragment as AztecEditorFragment).onToolbarHtmlButtonClicked() - } else if (editorFragment is GutenbergEditorFragment) { + } else if (isGutenbergEditorFragment(editorFragment)) { (editorFragment as GutenbergEditorFragment).onToggleHtmlMode() } } else if (itemId == R.id.menu_switch_to_gutenberg) { @@ -1636,16 +1637,16 @@ class EditPostActivity : LocaleAwareActivity(), EditorFragmentActivity, EditorIm } } else if (itemId == R.id.menu_editor_help) { // Display the editor help page -- option should only be available in the GutenbergEditor - if (editorFragment is GutenbergEditorFragment) { + if (isGutenbergEditorFragment(editorFragment)) { analyticsTrackerWrapper.track(Stat.EDITOR_HELP_SHOWN, siteModel) (editorFragment as GutenbergEditorFragment).showEditorHelp() } } else if (itemId == R.id.menu_undo_action) { - if (editorFragment is GutenbergEditorFragment) { + if (isGutenbergEditorFragment(editorFragment)) { (editorFragment as GutenbergEditorFragment).onUndoPressed() } } else if (itemId == R.id.menu_redo_action) { - if (editorFragment is GutenbergEditorFragment) { + if (isGutenbergEditorFragment(editorFragment)) { (editorFragment as GutenbergEditorFragment).onRedoPressed() } } @@ -1798,7 +1799,7 @@ class EditPostActivity : LocaleAwareActivity(), EditorFragmentActivity, EditorIm } private fun trackPostSessionEditorModeSwitch() { - val isGutenberg: Boolean = editorFragment is GutenbergEditorFragment + val isGutenberg: Boolean = isGutenbergEditorFragment(editorFragment) postEditorAnalyticsSession?.switchEditor( if (htmlModeMenuStateOn) PostEditorAnalyticsSession.Editor.HTML else @@ -1864,7 +1865,7 @@ class EditPostActivity : LocaleAwareActivity(), EditorFragmentActivity, EditorIm } private fun savePostOnline(isFirstTimePublish: Boolean): ActivityFinishState { - if (editorFragment is GutenbergEditorFragment) { + if (isGutenbergEditorFragment(editorFragment)) { (editorFragment as GutenbergEditorFragment).sendToJSPostSaveEvent() } return storePostViewModel.savePostOnline(isFirstTimePublish, this, (editPostRepository), siteModel) @@ -2519,7 +2520,7 @@ class EditPostActivity : LocaleAwareActivity(), EditorFragmentActivity, EditorIm private fun onXpostsSettingsCapability(isXpostsCapable: Boolean) { isXPostsCapable = isXpostsCapable - if (editorFragment is GutenbergEditorFragment) { + if (isGutenbergEditorFragment(editorFragment)) { (editorFragment as GutenbergEditorFragment).updateCapabilities(gutenbergPropsBuilder) } } @@ -2681,7 +2682,7 @@ class EditPostActivity : LocaleAwareActivity(), EditorFragmentActivity, EditorIm if (editPostRepository.hasPost()) { // don't avoid calling setContent() for GutenbergEditorFragment so RN gets initialized if (((!TextUtils.isEmpty(editPostRepository.content) - || editorFragment is GutenbergEditorFragment) + || isGutenbergEditorFragment(editorFragment)) && !hasSetPostContent) ) { hasSetPostContent = true @@ -2693,7 +2694,7 @@ class EditPostActivity : LocaleAwareActivity(), EditorFragmentActivity, EditorIm } if (!TextUtils.isEmpty(editPostRepository.title)) { editorFragment?.setTitle(editPostRepository.title) - } else if (editorFragment is GutenbergEditorFragment) { + } else if (isGutenbergEditorFragment(editorFragment)) { // don't avoid calling setTitle() for GutenbergEditorFragment so RN gets initialized val title: String? = intent.getStringExtra(EditPostActivityConstants.EXTRA_PAGE_TITLE) if (title != null) { @@ -2829,7 +2830,7 @@ class EditPostActivity : LocaleAwareActivity(), EditorFragmentActivity, EditorIm } else if (editPostSettingsFragment != null) { editPostSettingsFragment?.updateFeaturedImage(mediaId, imagePicked) } - if (editorFragment is GutenbergEditorFragment) { + if (isGutenbergEditorFragment(editorFragment)) { (editorFragment as GutenbergEditorFragment).sendToJSFeaturedImageId(mediaId.toInt()) } } @@ -3222,7 +3223,7 @@ class EditPostActivity : LocaleAwareActivity(), EditorFragmentActivity, EditorIm * EditorFragmentListener methods */ override fun clearFeaturedImage() { - if (editorFragment is GutenbergEditorFragment) { + if (isGutenbergEditorFragment(editorFragment)) { (editorFragment as GutenbergEditorFragment).sendToJSFeaturedImageId(0) } } @@ -3544,7 +3545,7 @@ class EditPostActivity : LocaleAwareActivity(), EditorFragmentActivity, EditorIm } private fun onEditorFinalTouchesBeforeShowingForGutenbergIfNeeded() { // probably here is best for Gutenberg to start interacting with - if (!(showGutenbergEditor && editorFragment is GutenbergEditorFragment)) + if (!(showGutenbergEditor && isGutenbergEditorFragment(editorFragment))) return refreshEditorTheme() @@ -4104,3 +4105,7 @@ fun mapAllowedTypesToMediaBrowserType(allowedTypes: Array, multiple: else -> if (multiple) MediaBrowserType.GUTENBERG_MEDIA_PICKER else MediaBrowserType.GUTENBERG_SINGLE_FILE_PICKER } } + +fun isGutenbergEditorFragment(fragment: EditorFragmentAbstract?): Boolean { + return fragment is GutenbergEditorFragment || fragment is GutenbergKitEditorFragment +} From b843c3c54c0966ca09226a7348e66a82d0783474 Mon Sep 17 00:00:00 2001 From: David Calhoun Date: Thu, 21 Nov 2024 11:48:39 -0500 Subject: [PATCH 3/8] refactor: Instantiate GutenbergKitEditorFragment --- .../android/ui/posts/EditPostActivity.kt | 58 ++++++++++++++++++- 1 file changed, 57 insertions(+), 1 deletion(-) 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 ea9fb6775531..7bbaae978b97 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 @@ -2392,7 +2392,9 @@ class EditPostActivity : LocaleAwareActivity(), EditorFragmentActivity, EditorIm override fun getItem(position: Int): Fragment { return when (position) { PAGE_CONTENT -> { - if (showGutenbergEditor) { + if (isNewGutenbergEditor && showGutenbergEditor) { + createGutenbergKitEditorFragment() + } else if (showGutenbergEditor) { createGutenbergEditorFragment() } else { // If gutenberg editor is not selected, default to Aztec. @@ -2406,6 +2408,60 @@ class EditPostActivity : LocaleAwareActivity(), EditorFragmentActivity, EditorIm } } + 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() + 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 + ) + + val postType = if (editPostRepository.isPage) "page" else "post" + val siteApiRoot = if (isWpCom) "https://public-api.wordpress.com/" else "" + val siteId = site.siteId + val authToken = accountStore.accessToken + val authHeader = "Bearer $authToken" + val siteApiNamespace = "sites/$siteId" + + val settings = mutableMapOf( + "postId" to editPostRepository.getPost()?.remotePostId?.toInt(), + "postType" to postType, + "postTitle" to editPostRepository.getPost()?.title, + "postContent" to editPostRepository.getPost()?.content, + "siteApiRoot" to siteApiRoot, + "authHeader" to authHeader, + "siteApiNamespace" to siteApiNamespace, + "themeStyles" to newGutenbergThemeStylesConfig.isEnabled() + ) + + 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 From b9278c7d2a22e7d06239027e5b670d84e3d778d1 Mon Sep 17 00:00:00 2001 From: David Calhoun Date: Thu, 21 Nov 2024 15:02:22 -0500 Subject: [PATCH 4/8] Revert "refactor: Abstract Gutenberg editor fragment detection logic" This reverts commit 6bb6165bc40cd9a4720cda95ab6cd3083481677d. --- .../android/ui/posts/EditPostActivity.kt | 35 ++++++++----------- 1 file changed, 15 insertions(+), 20 deletions(-) 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 7bbaae978b97..3d81736cbcbc 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,7 +72,6 @@ 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 @@ -926,7 +925,7 @@ class EditPostActivity : LocaleAwareActivity(), EditorFragmentActivity, EditorIm val isJetpackSsoEnabled = siteModel.isJetpackConnected && siteSettings?.isJetpackSsoEnabled == true if (this.isJetpackSsoEnabled != isJetpackSsoEnabled) { this.isJetpackSsoEnabled = isJetpackSsoEnabled - if (isGutenbergEditorFragment(editorFragment)) { + if (editorFragment is GutenbergEditorFragment) { val gutenbergFragment = editorFragment as GutenbergEditorFragment gutenbergFragment.setJetpackSsoEnabled(this.isJetpackSsoEnabled) gutenbergFragment.updateCapabilities(gutenbergPropsBuilder) @@ -1426,7 +1425,7 @@ class EditPostActivity : LocaleAwareActivity(), EditorFragmentActivity, EditorIm if (viewHtmlModeMenuItem != null) { viewHtmlModeMenuItem.setVisible( (((editorFragment is AztecEditorFragment) - || (isGutenbergEditorFragment(editorFragment)))) && !isNewGutenbergEditor && showMenuItems + || (editorFragment is GutenbergEditorFragment))) && !isNewGutenbergEditor && showMenuItems ) viewHtmlModeMenuItem.setTitle( if (htmlModeMenuStateOn) R.string.menu_visual_mode else R.string.menu_html_mode) @@ -1488,7 +1487,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 (isGutenbergEditorFragment(editorFragment) && showMenuItems && !isNewGutenbergEditor) { + if (editorFragment is GutenbergEditorFragment && showMenuItems && !isNewGutenbergEditor) { helpMenuItem.setVisible(true) } else { helpMenuItem.setVisible(false) @@ -1619,7 +1618,7 @@ class EditPostActivity : LocaleAwareActivity(), EditorFragmentActivity, EditorIm // toggle HTML mode if (editorFragment is AztecEditorFragment) { (editorFragment as AztecEditorFragment).onToolbarHtmlButtonClicked() - } else if (isGutenbergEditorFragment(editorFragment)) { + } else if (editorFragment is GutenbergEditorFragment) { (editorFragment as GutenbergEditorFragment).onToggleHtmlMode() } } else if (itemId == R.id.menu_switch_to_gutenberg) { @@ -1637,16 +1636,16 @@ class EditPostActivity : LocaleAwareActivity(), EditorFragmentActivity, EditorIm } } else if (itemId == R.id.menu_editor_help) { // Display the editor help page -- option should only be available in the GutenbergEditor - if (isGutenbergEditorFragment(editorFragment)) { + if (editorFragment is GutenbergEditorFragment) { analyticsTrackerWrapper.track(Stat.EDITOR_HELP_SHOWN, siteModel) (editorFragment as GutenbergEditorFragment).showEditorHelp() } } else if (itemId == R.id.menu_undo_action) { - if (isGutenbergEditorFragment(editorFragment)) { + if (editorFragment is GutenbergEditorFragment) { (editorFragment as GutenbergEditorFragment).onUndoPressed() } } else if (itemId == R.id.menu_redo_action) { - if (isGutenbergEditorFragment(editorFragment)) { + if (editorFragment is GutenbergEditorFragment) { (editorFragment as GutenbergEditorFragment).onRedoPressed() } } @@ -1799,7 +1798,7 @@ class EditPostActivity : LocaleAwareActivity(), EditorFragmentActivity, EditorIm } private fun trackPostSessionEditorModeSwitch() { - val isGutenberg: Boolean = isGutenbergEditorFragment(editorFragment) + val isGutenberg: Boolean = editorFragment is GutenbergEditorFragment postEditorAnalyticsSession?.switchEditor( if (htmlModeMenuStateOn) PostEditorAnalyticsSession.Editor.HTML else @@ -1865,7 +1864,7 @@ class EditPostActivity : LocaleAwareActivity(), EditorFragmentActivity, EditorIm } private fun savePostOnline(isFirstTimePublish: Boolean): ActivityFinishState { - if (isGutenbergEditorFragment(editorFragment)) { + if (editorFragment is GutenbergEditorFragment) { (editorFragment as GutenbergEditorFragment).sendToJSPostSaveEvent() } return storePostViewModel.savePostOnline(isFirstTimePublish, this, (editPostRepository), siteModel) @@ -2576,7 +2575,7 @@ class EditPostActivity : LocaleAwareActivity(), EditorFragmentActivity, EditorIm private fun onXpostsSettingsCapability(isXpostsCapable: Boolean) { isXPostsCapable = isXpostsCapable - if (isGutenbergEditorFragment(editorFragment)) { + if (editorFragment is GutenbergEditorFragment) { (editorFragment as GutenbergEditorFragment).updateCapabilities(gutenbergPropsBuilder) } } @@ -2738,7 +2737,7 @@ class EditPostActivity : LocaleAwareActivity(), EditorFragmentActivity, EditorIm if (editPostRepository.hasPost()) { // don't avoid calling setContent() for GutenbergEditorFragment so RN gets initialized if (((!TextUtils.isEmpty(editPostRepository.content) - || isGutenbergEditorFragment(editorFragment)) + || editorFragment is GutenbergEditorFragment) && !hasSetPostContent) ) { hasSetPostContent = true @@ -2750,7 +2749,7 @@ class EditPostActivity : LocaleAwareActivity(), EditorFragmentActivity, EditorIm } if (!TextUtils.isEmpty(editPostRepository.title)) { editorFragment?.setTitle(editPostRepository.title) - } else if (isGutenbergEditorFragment(editorFragment)) { + } else if (editorFragment is GutenbergEditorFragment) { // don't avoid calling setTitle() for GutenbergEditorFragment so RN gets initialized val title: String? = intent.getStringExtra(EditPostActivityConstants.EXTRA_PAGE_TITLE) if (title != null) { @@ -2886,7 +2885,7 @@ class EditPostActivity : LocaleAwareActivity(), EditorFragmentActivity, EditorIm } else if (editPostSettingsFragment != null) { editPostSettingsFragment?.updateFeaturedImage(mediaId, imagePicked) } - if (isGutenbergEditorFragment(editorFragment)) { + if (editorFragment is GutenbergEditorFragment) { (editorFragment as GutenbergEditorFragment).sendToJSFeaturedImageId(mediaId.toInt()) } } @@ -3279,7 +3278,7 @@ class EditPostActivity : LocaleAwareActivity(), EditorFragmentActivity, EditorIm * EditorFragmentListener methods */ override fun clearFeaturedImage() { - if (isGutenbergEditorFragment(editorFragment)) { + if (editorFragment is GutenbergEditorFragment) { (editorFragment as GutenbergEditorFragment).sendToJSFeaturedImageId(0) } } @@ -3601,7 +3600,7 @@ class EditPostActivity : LocaleAwareActivity(), EditorFragmentActivity, EditorIm } private fun onEditorFinalTouchesBeforeShowingForGutenbergIfNeeded() { // probably here is best for Gutenberg to start interacting with - if (!(showGutenbergEditor && isGutenbergEditorFragment(editorFragment))) + if (!(showGutenbergEditor && editorFragment is GutenbergEditorFragment)) return refreshEditorTheme() @@ -4161,7 +4160,3 @@ fun mapAllowedTypesToMediaBrowserType(allowedTypes: Array, multiple: else -> if (multiple) MediaBrowserType.GUTENBERG_MEDIA_PICKER else MediaBrowserType.GUTENBERG_SINGLE_FILE_PICKER } } - -fun isGutenbergEditorFragment(fragment: EditorFragmentAbstract?): Boolean { - return fragment is GutenbergEditorFragment || fragment is GutenbergKitEditorFragment -} From 4b67a3787d3c8bc59ca72ff54716fb1956dce993 Mon Sep 17 00:00:00 2001 From: David Calhoun Date: Mon, 25 Nov 2024 14:25:27 -0500 Subject: [PATCH 5/8] refactor: Remove unnecessary "new Gutenberg" branches There are now separate fragments for "new" and "old" Gutenberg, negating the need for branching logic within the fragments themselves. --- .../android/ui/posts/EditPostActivity.kt | 49 +- .../ui/posts/PostEditorAnalyticsSession.java | 1 + .../gutenberg/GutenbergEditorFragment.java | 239 +----- .../gutenberg/GutenbergKitEditorFragment.java | 690 +++--------------- 4 files changed, 137 insertions(+), 842 deletions(-) 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 3d81736cbcbc..367a18618c8f 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 @@ -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) @@ -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 + } ) } @@ -2456,7 +2466,6 @@ class EditPostActivity : LocaleAwareActivity(), EditorFragmentActivity, EditorIm gutenbergWebViewAuthorizationData, gutenbergPropsBuilder, jetpackFeatureRemovalPhaseHelper.shouldShowJetpackPoweredEditorFeatures(), - isNewGutenbergEditor, settings ) } @@ -2486,32 +2495,12 @@ class EditPostActivity : LocaleAwareActivity(), EditorFragmentActivity, EditorIm isJetpackSsoEnabled ) - val postType = if (editPostRepository.isPage) "page" else "post" - val siteApiRoot = if (isWpCom) "https://public-api.wordpress.com/" else "" - val siteId = site.siteId - val authToken = accountStore.accessToken - val authHeader = "Bearer $authToken" - val siteApiNamespace = "sites/$siteId" - - val settings = mutableMapOf( - "postId" to editPostRepository.getPost()?.remotePostId?.toInt(), - "postType" to postType, - "postTitle" to editPostRepository.getPost()?.title, - "postContent" to editPostRepository.getPost()?.content, - "siteApiRoot" to siteApiRoot, - "authHeader" to authHeader, - "siteApiNamespace" to siteApiNamespace, - "themeStyles" to newGutenbergThemeStylesConfig.isEnabled() - ) - return GutenbergEditorFragment.newInstance( getContext(), isNewPost, gutenbergWebViewAuthorizationData, gutenbergPropsBuilder, - jetpackFeatureRemovalPhaseHelper.shouldShowJetpackPoweredEditorFeatures(), - isNewGutenbergEditor, - settings + jetpackFeatureRemovalPhaseHelper.shouldShowJetpackPoweredEditorFeatures() ) } 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/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 index 032f9d2d126b..3731677ca31a 100644 --- 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 @@ -9,7 +9,6 @@ import android.net.Uri; import android.os.Bundle; import android.os.Handler; -import android.os.Looper; import android.text.Editable; import android.text.TextUtils; import android.view.LayoutInflater; @@ -28,18 +27,11 @@ import androidx.appcompat.app.ActionBar; import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AppCompatActivity; -import androidx.core.util.Consumer; import androidx.core.util.Pair; import androidx.fragment.app.FragmentActivity; -import androidx.fragment.app.FragmentManager; -import androidx.fragment.app.FragmentTransaction; import androidx.lifecycle.LiveData; import com.android.volley.toolbox.ImageLoader; -import com.automattic.android.tracks.crashlogging.JsException; -import com.automattic.android.tracks.crashlogging.JsExceptionCallback; -import com.automattic.android.tracks.crashlogging.JsExceptionStackTraceElement; -import com.facebook.react.bridge.ReadableMap; import com.facebook.react.bridge.WritableNativeMap; import com.google.android.material.dialog.MaterialAlertDialogBuilder; import com.google.gson.Gson; @@ -47,7 +39,6 @@ import org.wordpress.android.editor.BuildConfig; import org.wordpress.android.editor.EditorEditMediaListener; import org.wordpress.android.editor.EditorFragmentAbstract; -import org.wordpress.android.editor.EditorFragmentActivity; import org.wordpress.android.editor.EditorImagePreviewListener; import org.wordpress.android.editor.EditorMediaUploadListener; import org.wordpress.android.editor.EditorThemeUpdateListener; @@ -73,45 +64,22 @@ import org.wordpress.gutenberg.GutenbergView.OpenMediaLibraryListener; import org.wordpress.gutenberg.GutenbergView.TitleAndContentCallback; import org.wordpress.gutenberg.GutenbergWebViewPool; -import org.wordpress.mobile.ReactNativeGutenbergBridge.GutenbergBridgeJS2Parent.LogExceptionCallback; import org.wordpress.mobile.ReactNativeGutenbergBridge.GutenbergEmbedWebViewActivity; -import org.wordpress.mobile.WPAndroidGlue.GutenbergJsException; -import org.wordpress.mobile.WPAndroidGlue.Media; import org.wordpress.mobile.WPAndroidGlue.MediaOption; -import org.wordpress.mobile.WPAndroidGlue.RequestExecutor; -import org.wordpress.mobile.WPAndroidGlue.ShowSuggestionsUtil; -import org.wordpress.mobile.WPAndroidGlue.UnsupportedBlock; -import org.wordpress.mobile.WPAndroidGlue.WPAndroidGlueCode.OnBackHandlerEventListener; -import org.wordpress.mobile.WPAndroidGlue.WPAndroidGlueCode.OnBlockTypeImpressionsEventListener; -import org.wordpress.mobile.WPAndroidGlue.WPAndroidGlueCode.OnConnectionStatusEventListener; import org.wordpress.mobile.WPAndroidGlue.WPAndroidGlueCode.OnContentInfoReceivedListener; -import org.wordpress.mobile.WPAndroidGlue.WPAndroidGlueCode.OnCustomerSupportOptionsListener; -import org.wordpress.mobile.WPAndroidGlue.WPAndroidGlueCode.OnEditorMountListener; -import org.wordpress.mobile.WPAndroidGlue.WPAndroidGlueCode.OnFocalPointPickerTooltipShownEventListener; import org.wordpress.mobile.WPAndroidGlue.WPAndroidGlueCode.OnGetContentInterrupted; -import org.wordpress.mobile.WPAndroidGlue.WPAndroidGlueCode.OnGutenbergDidRequestEmbedFullscreenPreviewListener; -import org.wordpress.mobile.WPAndroidGlue.WPAndroidGlueCode.OnGutenbergDidRequestPreviewListener; -import org.wordpress.mobile.WPAndroidGlue.WPAndroidGlueCode.OnGutenbergDidRequestUnsupportedBlockFallbackListener; -import org.wordpress.mobile.WPAndroidGlue.WPAndroidGlueCode.OnGutenbergDidSendButtonPressedActionListener; -import org.wordpress.mobile.WPAndroidGlue.WPAndroidGlueCode.OnLogExceptionListener; -import org.wordpress.mobile.WPAndroidGlue.WPAndroidGlueCode.OnMediaLibraryButtonListener; -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; import java.util.HashSet; -import java.util.List; 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.gutenberg.Media.createMediaUsingMimeType; -import static org.wordpress.mobile.WPAndroidGlue.Media.createRNMediaUsingMimeType; public class GutenbergKitEditorFragment extends EditorFragmentAbstract implements EditorMediaUploadListener, @@ -131,7 +99,6 @@ public class GutenbergKitEditorFragment extends EditorFragmentAbstract implement 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; @@ -172,7 +139,6 @@ public class GutenbergKitEditorFragment extends EditorFragmentAbstract implement private boolean mIsNewPost; private boolean mIsJetpackSsoEnabled; - private static boolean mIsNewGutenbergEnabled; private boolean mEditorDidMount; private GutenbergPropsBuilder mCurrentGutenbergPropsBuilder; @@ -190,17 +156,14 @@ public static GutenbergKitEditorFragment newInstance(Context context, GutenbergWebViewAuthorizationData webViewAuthorizationData, GutenbergPropsBuilder gutenbergPropsBuilder, boolean jetpackFeaturesEnabled, - boolean newGutenbergEnabled, @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.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); @@ -210,18 +173,6 @@ public static GutenbergKitEditorFragment newInstance(Context context, } private GutenbergContainerFragment getGutenbergContainerFragment() { - if (mIsNewGutenbergEnabled) { - return mRetainedGutenbergContainerFragment; - } - - if (mRetainedGutenbergContainerFragment == null) { - mRetainedGutenbergContainerFragment = (GutenbergContainerFragment) getChildFragmentManager() - .findFragmentByTag(GutenbergContainerFragment.TAG); - } else { - // Noop. Just use the cached reference. The container fragment might not be attached yet so, getting it from - // the fragment manager is not reliable. No need either; it's retained and outlives this EditorFragment. - } - return mRetainedGutenbergContainerFragment; } @@ -230,10 +181,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()); @@ -241,20 +188,6 @@ public void onCreate(@Nullable Bundle savedInstanceState) { gutenbergPropsBuilder = db.getParcel(ARG_GUTENBERG_PROPS_BUILDER, GutenbergPropsBuilder.CREATOR); } 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(); - } - } - - if (mUpdateCapabilitiesOnCreate && !mIsNewGutenbergEnabled) { - getGutenbergContainerFragment().updateCapabilities(mCurrentGutenbergPropsBuilder); } ProfilingUtils.start("Visual Editor Startup"); @@ -267,7 +200,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,388 +208,40 @@ 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(); - - ViewGroup gutenbergContainer = view.findViewById(R.id.gutenberg_container); - getGutenbergContainerFragment().attachToContainer(gutenbergContainer, - new OnMediaLibraryButtonListener() { - @Override public void onMediaLibraryImageButtonClicked(boolean allowMultipleSelection) { - mEditorFragmentListener.onTrackableEvent(TrackableEvent.MEDIA_BUTTON_TAPPED); - mEditorFragmentListener.onAddMediaImageClicked(allowMultipleSelection); - } - - @Override - public void onMediaLibraryVideoButtonClicked(boolean allowMultipleSelection) { - mEditorFragmentListener.onTrackableEvent(TrackableEvent.MEDIA_BUTTON_TAPPED); - mEditorFragmentListener.onAddMediaVideoClicked(allowMultipleSelection); - } - - @Override - public void onMediaLibraryMediaButtonClicked(boolean allowMultipleSelection) { - mEditorFragmentListener.onTrackableEvent(TrackableEvent.MEDIA_BUTTON_TAPPED); - mEditorFragmentListener.onAddLibraryMediaClicked(allowMultipleSelection); - } - - @Override public void onMediaLibraryFileButtonClicked(boolean allowMultipleSelection) { - mEditorFragmentListener.onTrackableEvent(TrackableEvent.MEDIA_BUTTON_TAPPED); - mEditorFragmentListener.onAddLibraryFileClicked(allowMultipleSelection); - } - - @Override public void onMediaLibraryAudioButtonClicked(boolean allowMultipleSelection) { - mEditorFragmentListener.onTrackableEvent(TrackableEvent.MEDIA_BUTTON_TAPPED); - mEditorFragmentListener.onAddLibraryAudioFileClicked(allowMultipleSelection); - } - - @Override - public void onUploadPhotoButtonClicked(boolean allowMultipleSelection) { - mEditorFragmentListener.onAddPhotoClicked(allowMultipleSelection); - } - - @Override - public void onUploadVideoButtonClicked(boolean allowMultipleSelection) { - mEditorFragmentListener.onAddVideoClicked(allowMultipleSelection); - } - - @Override - public void onUploadMediaButtonClicked(boolean allowMultipleSelection) { - mEditorFragmentListener.onAddDeviceMediaClicked(allowMultipleSelection); - } - - @Override - public void onCaptureVideoButtonClicked() { - checkAndRequestCameraAndStoragePermissions(CAPTURE_VIDEO_PERMISSION_REQUEST_CODE); - } - - @Override - public void onCapturePhotoButtonClicked() { - checkAndRequestCameraAndStoragePermissions(CAPTURE_PHOTO_PERMISSION_REQUEST_CODE); - } - - @Override - public void onRetryUploadForMediaClicked(int mediaId) { - if (getActivity() != null) { - getActivity().runOnUiThread(() -> { - showRetryMediaUploadDialog(mediaId); - }); - } - } - - @Override - public void onCancelUploadForMediaClicked(int mediaId) { - if (getActivity() != null) { - getActivity().runOnUiThread(() -> { - showCancelMediaUploadDialog(mediaId); - }); - } - } - - @Override - public void onCancelUploadForMediaDueToDeletedBlock(int mediaId) { - cancelMediaUploadForDeletedBlock(mediaId); - } - - @Override - public ArrayList onGetOtherMediaImageOptions() { - ArrayList otherMediaImageOptions = initOtherMediaImageOptions(); - return otherMediaImageOptions; - } - - @Override - public ArrayList onGetOtherMediaFileOptions() { - ArrayList otherMediaFileOptions = initOtherMediaFileOptions(); - return otherMediaFileOptions; - } - - @Override public ArrayList onGetOtherMediaAudioFileOptions() { - return initOtherMediaAudioFileOptions(); - } - - @Override - public void onOtherMediaButtonClicked(String mediaSource, boolean allowMultipleSelection) { - switch (mediaSource) { - case MEDIA_SOURCE_STOCK_MEDIA: - mEditorFragmentListener.onAddStockMediaClicked(allowMultipleSelection); - break; - case GIF_MEDIA: - mEditorFragmentListener.onAddGifClicked(allowMultipleSelection); - break; - case MEDIA_SOURCE_FILE: - mEditorFragmentListener.onAddFileClicked(allowMultipleSelection); - break; - case MEDIA_SOURCE_AUDIO_FILE: - mEditorFragmentListener.onAddAudioFileClicked(allowMultipleSelection); - break; - default: - AppLog.e(T.EDITOR, - "Unsupported media source " + mediaSource); - } - } - }, - new OnReattachMediaUploadQueryListener() { - @Override - public void onQueryCurrentProgressForUploadingMedia() { - updateFailedMediaState(); - updateMediaProgress(); - } - }, - new OnSetFeaturedImageListener() { - @Override - public void onSetFeaturedImageButtonClicked(int mediaId) { - if (mediaId == mFeaturedImageId) { - // nothing special to do, trying to set the image that's already set as featured - return; - } - - if (mediaId == MEDIA_ID_NO_FEATURED_IMAGE_SET) { - // user tries to clear the featured image setting - setFeaturedImage(mediaId); - return; - } - - if (mFeaturedImageId == MEDIA_ID_NO_FEATURED_IMAGE_SET) { - // current featured image is not set so, go ahead and set it to the provided one - setFeaturedImage(mediaId); - return; - } - - // ask the user to confirm changing the featured image since there's already one set - showFeaturedImageConfirmationDialog(mediaId); - } - }, - new OnEditorMountListener() { - @Override - public void onEditorDidMount(ArrayList unsupportedBlocks) { - mEditorDidMount = true; - mEditorFragmentListener.onEditorFragmentContentReady( - unsupportedBlocks, - mExternallyEditedBlockOriginalHash != null - ); - - // Hide the progress bar when editor is ready - new Handler(Looper.getMainLooper()).post(new Runnable() { - @Override - public void run() { - setEditorProgressBarVisibility(!mEditorDidMount); - } - }); - } - }, - mTextWatcher::postTextChanged, - mEditorFragmentListener::onAuthHeaderRequested, - new RequestExecutor() { - @Override - public void performGetRequest(String path, boolean enableCaching, Consumer onSuccess, - Consumer onError) { - mEditorFragmentListener.onPerformFetch(path, enableCaching, onSuccess, onError); - } - - @Override - public void performPostRequest( - String path, - ReadableMap data, - Consumer onSuccess, - Consumer onError) { - mEditorFragmentListener.onPerformPost(path, data.toHashMap(), onSuccess, onError); - } - }, - mEditorImagePreviewListener::onImagePreviewRequested, - mEditorEditMediaListener::onMediaEditorRequested, - new OnGutenbergDidRequestUnsupportedBlockFallbackListener() { - @Override - public void gutenbergDidRequestUnsupportedBlockFallback(UnsupportedBlock unsupportedBlock) { - openGutenbergWebViewActivity( - unsupportedBlock.getContent(), - unsupportedBlock.getId(), - unsupportedBlock.getName(), - unsupportedBlock.getTitle() - ); - } - }, - new OnGutenbergDidRequestEmbedFullscreenPreviewListener() { - @Override public void gutenbergDidRequestEmbedFullscreenPreview(String html, String title) { - openGutenbergEmbedWebViewActivity(html, title); - } - }, - new OnGutenbergDidSendButtonPressedActionListener() { - @Override - public void gutenbergDidSendButtonPressedAction(String buttonType) { - mEditorFragmentListener.showJetpackSettings(); - } - }, - - new ShowSuggestionsUtil() { - @Override public void showUserSuggestions(Consumer onResult) { - mEditorFragmentListener.showUserSuggestions(onResult); - } - - @Override public void showXpostSuggestions(Consumer onResult) { - mEditorFragmentListener.showXpostSuggestions(onResult); - } - }, - new OnFocalPointPickerTooltipShownEventListener() { - @Override - public void onSetFocalPointPickerTooltipShown(boolean tooltipShown) { - mEditorFragmentListener.onGutenbergEditorSetFocalPointPickerTooltipShown(tooltipShown); - } - - @Override - public boolean onRequestFocalPointPickerTooltipShown() { - return mEditorFragmentListener.onGutenbergEditorRequestFocalPointPickerTooltipShown(); - } - }, - new OnGutenbergDidRequestPreviewListener() { - @Override - public void gutenbergDidRequestPreview() { - mEditorFragmentListener.showPreview(); - } - }, - new OnBlockTypeImpressionsEventListener() { - @Override - public Map onRequestBlockTypeImpressions() { - return mEditorFragmentListener.onRequestBlockTypeImpressions(); - } - - @Override - public void onSetBlockTypeImpressions(Map impressions) { - mEditorFragmentListener.onSetBlockTypeImpressions(impressions); - } - }, - new OnCustomerSupportOptionsListener() { - @Override - public void onContactCustomerSupport() { - if (getActivity() != null) { - getActivity().runOnUiThread(() -> { - mEditorFragmentListener.onContactCustomerSupport(); - }); - } - } - - @Override - public void onGotoCustomerSupportOptions() { - mEditorFragmentListener.onGotoCustomerSupportOptions(); - } - }, - - mEditorFragmentListener::onSendEventToHost, - - mEditorFragmentListener::onToggleUndo, - - mEditorFragmentListener::onToggleRedo, - - new OnConnectionStatusEventListener() { - @Override public boolean onRequestConnectionStatus() { - return NetworkUtils.isNetworkAvailable(getActivity()); - } - }, - - new OnBackHandlerEventListener() { - @Override public void onBackHandler() { - mEditorFragmentListener.onBackHandlerButton(); - } - }, - - new OnLogExceptionListener() { - @Override public void onLogException(GutenbergJsException exception, - LogExceptionCallback logExceptionCallback) { - List stackTraceElements = exception.getStackTrace().stream().map( - stackTrace -> { - return new JsExceptionStackTraceElement( - stackTrace.getFileName(), - stackTrace.getLineNumber(), - stackTrace.getColNumber(), - stackTrace.getFunction() - ); - }).collect(Collectors.toList()); - - JsException jsException = new JsException( - exception.getType(), - exception.getMessage(), - stackTraceElements, - exception.getContext(), - exception.getTags(), - exception.isHandled(), - exception.getHandledBy() - ); - - JsExceptionCallback callback = new JsExceptionCallback() { - @Override - public void onReportSent(boolean success) { - logExceptionCallback.onLogException(success); - } - }; - - mEditorFragmentListener.onLogJsException(jsException, callback); - } - }, - - GutenbergUtils.isDarkMode(getActivity())); - - // request dependency injection. Do this after setting min/max dimensions - if (getActivity() instanceof EditorFragmentActivity) { - ((EditorFragmentActivity) getActivity()).initializeEditorFragment(); - } - - setHasOptionsMenu(true); - - mInvalidateOptionsHandler = new Handler(); - mInvalidateOptionsRunnable = new Runnable() { - @Override - public void run() { - if (isAdded()) { - getActivity().invalidateOptionsMenu(); - } - } - }; - - if (!getGutenbergContainerFragment().hasReceivedAnyContent()) { - // container is empty, which means it's a fresh instance so, signal to complete its init - mEditorFragmentListener.onEditorFragmentInitialized(); - } + 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); + }); - if (mIsNewPost) { - showImplicitKeyboard(); - } + 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 view; + return mGutenbergView; } private String calculateHashOnMediaCollectionBasedBlock(ArrayList mediaFiles) { @@ -745,50 +329,28 @@ 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); + 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); } - mGutenbergView.resetFilePathCallback(); - } - } - } - - if (requestCode == UNSUPPORTED_BLOCK_REQUEST_CODE) { - if (resultCode == Activity.RESULT_OK) { - String blockId = data.getStringExtra(WPGutenbergWebViewActivity.ARG_BLOCK_ID); - String content = data.getStringExtra(WPGutenbergWebViewActivity.ARG_BLOCK_CONTENT); - getGutenbergContainerFragment().replaceUnsupportedBlock(content, blockId); - if (mCurrentGutenbergPropsBuilder == null) { - SavedInstanceDatabase db = SavedInstanceDatabase.Companion.getDatabase(getContext()); - if (db != null) { - mCurrentGutenbergPropsBuilder = db.getParcel(ARG_GUTENBERG_PROPS_BUILDER, - GutenbergPropsBuilder.CREATOR); - } + } else { + filePathCallback.onReceiveValue(null); } - // We need to send latest capabilities as JS side clears them - getGutenbergContainerFragment().updateCapabilities(mCurrentGutenbergPropsBuilder); - trackWebViewClosed("save"); - } else { - trackWebViewClosed("dismiss"); + mGutenbergView.resetFilePathCallback(); } } } @@ -875,13 +437,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); - } } public void resetUploadingMediaToFailed(Set failedMediaIds) { @@ -1180,14 +735,6 @@ private ActionBar getActionBar() { @Override public void setTitle(CharSequence title) { - if (title == null) { - title = ""; - } - - if (mIsNewGutenbergEnabled) { - return; - } - getGutenbergContainerFragment().setTitle(title.toString()); } @Override @@ -1196,13 +743,7 @@ public void setContent(CharSequence text) { text = ""; } - if (mIsNewGutenbergEnabled) { - mGutenbergView.setContent((String) text); - return; - } - - String postContent = removeVisualEditorProgressTag(text.toString()); - getGutenbergContainerFragment().setContent(postContent); + mGutenbergView.setContent((String) text); } @Override @@ -1256,10 +797,6 @@ private void toggleHtmlMode() { } public void sendToJSPostSaveEvent() { - if (mIsNewGutenbergEnabled) { - return; - } - getGutenbergContainerFragment().sendToJSPostSaveEvent(); } /* @@ -1282,36 +819,25 @@ 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); + final Pair[] result = new Pair[1]; + final CountDownLatch latch = new CountDownLatch(1); - try { - latch.await(); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - return new Pair<>("", ""); + mGutenbergView.getTitleAndContent(new TitleAndContentCallback() { + @Override + public void onResult(@Nullable String title, @NonNull String content) { + result[0] = new Pair<>(title, content); + latch.countDown(); } + }, true); - return result[0] != null ? result[0] : new Pair<>("", ""); - } - if (!isAdded()) { - throw new EditorFragmentNotAddedException(); + try { + latch.await(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + return new Pair<>("", ""); } - return getGutenbergContainerFragment().getTitleAndContent(originalContent, new OnGetContentInterrupted() { - @Override public void onGetContentInterrupted(InterruptedException ie) { - AppLog.e(T.EDITOR, ie); - Thread.currentThread().interrupt(); - } - }); + + return result[0] != null ? result[0] : new Pair<>("", ""); } @NonNull @@ -1424,63 +950,32 @@ 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<>(); - - 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<>(); + // Disable upload handling until supported--e.g., media shared to the app + if (mGutenbergView == null || !isNetworkUrl) { + return; + } - if (!isNetworkUrl) { - for (Media media : processedMediaList) { - mUploadingMediaProgressMax.put(String.valueOf(media.getId()), 0f); - } - } + ArrayList processedMediaList = new ArrayList<>(); - 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 = 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); } - - getGutenbergContainerFragment().appendMediaFiles(processedMediaList); + processedMediaList.add(createMediaUsingMimeType(mediaId, + url, + mediaFile.getMimeType(), + mediaFile.getCaption(), + mediaFile.getTitle(), + mediaFile.getAlt())); } + + String mediaString = new Gson().toJson(processedMediaList); + mGutenbergView.setMediaUploadAttachment(mediaString); } @Override @@ -1540,7 +1035,7 @@ private boolean hideSavingProgressDialog() { @Override public void onDestroy() { - if (mIsNewGutenbergEnabled && mGutenbergView != null) { + if (mGutenbergView != null) { GutenbergWebViewPool.recycleWebView(mGutenbergView); mContentChangeListener = null; } @@ -1617,18 +1112,10 @@ 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); } @Override @@ -1664,12 +1151,5 @@ public void onGutenbergDialogNegativeClicked(@NonNull String instanceTag) { @Override public void onConnectionStatusChange(boolean isConnected) { - if (mIsNewGutenbergEnabled) { - return; - } - getGutenbergContainerFragment().onConnectionStatusChange(isConnected); - if (isConnected && hasFailedMediaUploads()) { - mEditorFragmentListener.onMediaRetryAll(mFailedMediaIds); - } } } From 43a17b5020c10e1cf0ad80c1cf44cffe0f930bc4 Mon Sep 17 00:00:00 2001 From: David Calhoun Date: Mon, 25 Nov 2024 15:57:48 -0500 Subject: [PATCH 6/8] refactor: Remove unnecessary code The majority--if not all--of this code is unused by GutenbergKit. No-op methods were retained to satisfy the requirements of the class shared between Gutenberg Mobile and GutenbergKit. --- .../android/ui/posts/EditPostActivity.kt | 2 - .../gutenberg/GutenbergKitEditorFragment.java | 654 +----------------- 2 files changed, 9 insertions(+), 647 deletions(-) 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 367a18618c8f..2f064794a175 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 @@ -2426,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, @@ -2464,7 +2463,6 @@ class EditPostActivity : LocaleAwareActivity(), EditorFragmentActivity, EditorIm getContext(), isNewPost, gutenbergWebViewAuthorizationData, - gutenbergPropsBuilder, jetpackFeatureRemovalPhaseHelper.shouldShowJetpackPoweredEditorFeatures(), settings ) 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 index 3731677ca31a..8a01641e824a 100644 --- 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 @@ -1,39 +1,27 @@ package org.wordpress.android.editor.gutenberg; 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.text.Editable; -import android.text.TextUtils; 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.view.inputmethod.InputMethodManager; import android.webkit.URLUtil; import android.webkit.ValueCallback; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import androidx.annotation.UiThread; -import androidx.appcompat.app.ActionBar; -import androidx.appcompat.app.AlertDialog; -import androidx.appcompat.app.AppCompatActivity; import androidx.core.util.Pair; -import androidx.fragment.app.FragmentActivity; import androidx.lifecycle.LiveData; import com.android.volley.toolbox.ImageLoader; -import com.facebook.react.bridge.WritableNativeMap; -import com.google.android.material.dialog.MaterialAlertDialogBuilder; import com.google.gson.Gson; import org.wordpress.android.editor.BuildConfig; @@ -44,18 +32,13 @@ import org.wordpress.android.editor.EditorThemeUpdateListener; import org.wordpress.android.editor.LiveTextWatcher; import org.wordpress.android.editor.R; -import org.wordpress.android.editor.WPGutenbergWebViewActivity; 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.DateTimeUtils; -import org.wordpress.android.util.NetworkUtils; import org.wordpress.android.util.PermissionUtils; import org.wordpress.android.util.ProfilingUtils; -import org.wordpress.android.util.StringUtils; -import org.wordpress.android.util.ToastUtils; import org.wordpress.android.util.helpers.MediaFile; import org.wordpress.android.util.helpers.MediaGallery; import org.wordpress.aztec.IHistoryListener; @@ -64,19 +47,10 @@ import org.wordpress.gutenberg.GutenbergView.OpenMediaLibraryListener; import org.wordpress.gutenberg.GutenbergView.TitleAndContentCallback; import org.wordpress.gutenberg.GutenbergWebViewPool; -import org.wordpress.mobile.ReactNativeGutenbergBridge.GutenbergEmbedWebViewActivity; -import org.wordpress.mobile.WPAndroidGlue.MediaOption; -import org.wordpress.mobile.WPAndroidGlue.WPAndroidGlueCode.OnContentInfoReceivedListener; -import org.wordpress.mobile.WPAndroidGlue.WPAndroidGlueCode.OnGetContentInterrupted; import java.io.Serializable; import java.util.ArrayList; -import java.util.Date; -import java.util.HashMap; -import java.util.HashSet; import java.util.Map; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CountDownLatch; import static org.wordpress.gutenberg.Media.createMediaUsingMimeType; @@ -94,9 +68,6 @@ public class GutenbergKitEditorFragment extends EditorFragmentAbstract implement 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"; - private static final String ARG_GUTENBERG_PROPS_BUILDER = "param_gutenberg_props_builder"; - public static final String ARG_STORY_BLOCK_EXTERNALLY_EDITED_ORIGINAL_HASH = "story_block_original_hash"; - 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_NEW_GUTENBERG_SETTINGS = "new_gutenberg_settings"; @@ -104,57 +75,19 @@ public class GutenbergKitEditorFragment extends EditorFragmentAbstract implement private static final int CAPTURE_PHOTO_PERMISSION_REQUEST_CODE = 101; private static final int CAPTURE_VIDEO_PERMISSION_REQUEST_CODE = 102; - private static final String MEDIA_SOURCE_FILE = "MEDIA_SOURCE_FILE"; - private static final String MEDIA_SOURCE_AUDIO_FILE = "MEDIA_SOURCE_AUDIO_FILE"; - private static final String MEDIA_SOURCE_STOCK_MEDIA = "MEDIA_SOURCE_STOCK_MEDIA"; - private static final String GIF_MEDIA = "GIF_MEDIA"; - - private static final String USER_EVENT_KEY_TEMPLATE = "template"; - - private static final int UNSUPPORTED_BLOCK_REQUEST_CODE = 1001; - - private static final int EMBED_FULLSCREEN_PREVIEW_CODE = 1002; - - private static final String TAG_REPLACE_FEATURED_DIALOG = "REPLACE_FEATURED_DIALOG"; - - public static final int MEDIA_ID_NO_FEATURED_IMAGE_SET = 0; - private boolean mHtmlModeEnabled; - private Handler mInvalidateOptionsHandler; - private Runnable mInvalidateOptionsRunnable; - - private LiveTextWatcher mTextWatcher = new LiveTextWatcher(); + private final 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. - // See discussion at https://github.com/wordpress-mobile/WordPress-Android/pull/9030#issuecomment-459447537 and on. - GutenbergContainerFragment mRetainedGutenbergContainerFragment; - - private ConcurrentHashMap mUploadingMediaProgressMax = new ConcurrentHashMap<>(); - private HashSet mFailedMediaIds = new HashSet<>(); - private ConcurrentHashMap mCancelledMediaIds = new ConcurrentHashMap<>(); - - private boolean mIsNewPost; - private boolean mIsJetpackSsoEnabled; - private boolean mEditorDidMount; - private GutenbergPropsBuilder mCurrentGutenbergPropsBuilder; - private boolean mUpdateCapabilitiesOnCreate = false; - private String mExternallyEditedBlockOriginalHash = null; - private boolean mStoryBlockReplacedSignalWait = false; - private String mUpdatedStoryBlockContent = null; - - private ProgressDialog mSavingContentProgressDialog; @Nullable private static Map mSettings; public static GutenbergKitEditorFragment newInstance(Context context, boolean isNewPost, GutenbergWebViewAuthorizationData webViewAuthorizationData, - GutenbergPropsBuilder gutenbergPropsBuilder, boolean jetpackFeaturesEnabled, @Nullable Map settings) { GutenbergKitEditorFragment fragment = new GutenbergKitEditorFragment(); @@ -167,38 +100,20 @@ public static GutenbergKitEditorFragment newInstance(Context context, mSettings = settings; if (db != null) { db.addParcel(ARG_GUTENBERG_WEB_VIEW_AUTH_DATA, webViewAuthorizationData); - db.addParcel(ARG_GUTENBERG_PROPS_BUILDER, gutenbergPropsBuilder); } return fragment; } - private GutenbergContainerFragment getGutenbergContainerFragment() { - return mRetainedGutenbergContainerFragment; - } - - @SuppressWarnings("unchecked") @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); - if (getGutenbergContainerFragment() == null) { - GutenbergPropsBuilder gutenbergPropsBuilder = null; - SavedInstanceDatabase db = SavedInstanceDatabase.Companion.getDatabase(getContext()); - if (db != null) { - gutenbergPropsBuilder = db.getParcel(ARG_GUTENBERG_PROPS_BUILDER, GutenbergPropsBuilder.CREATOR); - } - mCurrentGutenbergPropsBuilder = gutenbergPropsBuilder; - } - 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); - mExternallyEditedBlockOriginalHash = savedInstanceState.getString( - ARG_STORY_BLOCK_EXTERNALLY_EDITED_ORIGINAL_HASH); - mFailedMediaIds = (HashSet) savedInstanceState.getSerializable(ARG_FAILED_MEDIAS); mFeaturedImageId = savedInstanceState.getLong(ARG_FEATURED_IMAGE_ID); } } @@ -207,7 +122,6 @@ public void onCreate(@Nullable Bundle savedInstanceState) { @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { if (getArguments() != null) { - mIsNewPost = getArguments().getBoolean(ARG_IS_NEW_POST); mSettings = (Map) getArguments().getSerializable(ARG_NEW_GUTENBERG_SETTINGS); } @@ -223,7 +137,7 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle sa mGutenbergView.setContentChangeListener(mContentChangeListener); mGutenbergView.setOpenMediaLibraryListener(mOpenMediaLibraryListener); mGutenbergView.setEditorDidBecomeAvailable(view -> { - mEditorFragmentListener.onEditorFragmentContentReady(new ArrayList(), false); + mEditorFragmentListener.onEditorFragmentContentReady(new ArrayList<>(), false); }); Integer postId = (Integer) mSettings.get("postId"); @@ -244,87 +158,6 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle sa return mGutenbergView; } - private String calculateHashOnMediaCollectionBasedBlock(ArrayList mediaFiles) { - Gson gson = new Gson(); - // make sure to normalize ids to Strings as these may vary and make the hash not coincide - normalizeMediaFilesIds(mediaFiles); - return StringUtils.getMd5Hash(gson.toJson(mediaFiles)); - } - - @SuppressWarnings("unchecked") - private void normalizeMediaFilesIds(ArrayList mediaFiles) { - // iterate through all of mediaFiles objects and convert ids to String - for (Object mediaFile : mediaFiles) { - // this conversion is needed to strip off decimals that can come from RN when using int as - // string - if (((HashMap) mediaFile).get("id") instanceof Double) { - Double originalValue = (Double) ((HashMap) mediaFile).get("id"); - // now set it back to String to normalize with temporary ids - ((HashMap) mediaFile).put("id", String.valueOf(originalValue.longValue())); - } - } - } - - private void initializeSavingProgressDialog() { - if (mEditorFragmentListener != null) { - mEditorFragmentListener.getSavingInProgressDialogVisibility() - .observe(getViewLifecycleOwner(), visibility -> { - if (DialogVisibility.Showing == visibility) { - showSavingProgressDialogIfNeeded(); - } else { - hideSavingProgressDialog(); - } - }); - } - } - - private GutenbergWebViewAuthorizationData getGutenbergWebViewAuthorizationData() { - SavedInstanceDatabase db = SavedInstanceDatabase.Companion.getDatabase(getContext()); - if (db != null) { - return db.getParcel(ARG_GUTENBERG_WEB_VIEW_AUTH_DATA, GutenbergWebViewAuthorizationData.CREATOR); - } - return null; - } - - private void openGutenbergWebViewActivity(String content, String blockId, String blockName, String blockTitle) { - GutenbergWebViewAuthorizationData gutenbergWebViewAuthData = getGutenbergWebViewAuthorizationData(); - - // There is a chance that isJetpackSsoEnabled has changed on the server - // so we need to make sure that we have fresh value of it. - gutenbergWebViewAuthData.setJetpackSsoEnabled(mIsJetpackSsoEnabled); - - Intent intent = new Intent(getActivity(), WPGutenbergWebViewActivity.class); - intent.putExtra(WPGutenbergWebViewActivity.ARG_BLOCK_ID, blockId); - intent.putExtra(WPGutenbergWebViewActivity.ARG_BLOCK_TITLE, blockTitle); - intent.putExtra(WPGutenbergWebViewActivity.ARG_BLOCK_CONTENT, content); - intent.putExtra(WPGutenbergWebViewActivity.ARG_GUTENBERG_WEB_VIEW_AUTH_DATA, gutenbergWebViewAuthData); - - startActivityForResult(intent, UNSUPPORTED_BLOCK_REQUEST_CODE); - - HashMap properties = new HashMap<>(); - properties.put("block", blockName); - mEditorFragmentListener.onTrackableEvent( - TrackableEvent.EDITOR_GUTENBERG_UNSUPPORTED_BLOCK_WEBVIEW_SHOWN, - properties); - } - - private void trackWebViewClosed(String action) { - HashMap properties = new HashMap<>(); - properties.put("action", action); - mEditorFragmentListener.onTrackableEvent( - TrackableEvent.EDITOR_GUTENBERG_UNSUPPORTED_BLOCK_WEBVIEW_CLOSED, - properties); - } - - private void openGutenbergEmbedWebViewActivity(String html, String title) { - Activity activity = getActivity(); - - Intent intent = new Intent(activity, GutenbergEmbedWebViewActivity.class); - intent.putExtra(GutenbergEmbedWebViewActivity.ARG_CONTENT, html); - intent.putExtra(GutenbergEmbedWebViewActivity.ARG_TITLE, title); - activity.startActivityForResult(intent, EMBED_FULLSCREEN_PREVIEW_CODE); - } - @Override public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { super.onActivityResult(requestCode, resultCode, data); @@ -355,73 +188,8 @@ public void onActivityResult(int requestCode, int resultCode, @Nullable Intent d } } - private ArrayList initOtherMediaImageOptions() { - ArrayList otherMediaOptions = new ArrayList<>(); - - Bundle arguments = getArguments(); - FragmentActivity activity = getActivity(); - final Context context = getContext(); - if (activity == null || context == null || arguments == null) { - AppLog.e(T.EDITOR, - "Failed to initialize other media options because the activity or getArguments() is null"); - return otherMediaOptions; - } - - boolean jetpackFeaturesEnabled = arguments.getBoolean(ARG_JETPACK_FEATURES_ENABLED); - GutenbergWebViewAuthorizationData gutenbergWebViewAuthorizationData = getGutenbergWebViewAuthorizationData(); - - boolean supportStockPhotos = gutenbergWebViewAuthorizationData.isSiteUsingWPComRestAPI() - && jetpackFeaturesEnabled; - boolean supportsTenor = jetpackFeaturesEnabled; - - String packageName = activity.getApplication().getPackageName(); - if (supportStockPhotos) { - int stockMediaResourceId = - context.getResources().getIdentifier("photo_picker_stock_media", "string", packageName); - - otherMediaOptions.add(new MediaOption(MEDIA_SOURCE_STOCK_MEDIA, getString(stockMediaResourceId))); - } - if (supportsTenor) { - int gifMediaResourceId = - context.getResources().getIdentifier("photo_picker_gif", "string", packageName); - otherMediaOptions.add(new MediaOption(GIF_MEDIA, getString(gifMediaResourceId))); - } - - return otherMediaOptions; - } - - private ArrayList initOtherMediaFileOptions() { - return initOtherMediaFileOptions(MEDIA_SOURCE_FILE); - } - - private ArrayList initOtherMediaAudioFileOptions() { - return initOtherMediaFileOptions(MEDIA_SOURCE_AUDIO_FILE); - } - - private ArrayList initOtherMediaFileOptions(String mediaOptionId) { - ArrayList otherMediaOptions = new ArrayList<>(); - - FragmentActivity activity = getActivity(); - if (activity == null) { - AppLog.e(T.EDITOR, - "Failed to initialize other media options because the activity is null"); - return otherMediaOptions; - } - - String packageName = activity.getApplication().getPackageName(); - - int chooseFileResourceId = - getResources().getIdentifier("photo_picker_choose_file", "string", packageName); - - otherMediaOptions.add(new MediaOption(mediaOptionId, getString(chooseFileResourceId))); - - return otherMediaOptions; - } - @Override public void onResume() { super.onResume(); - - setEditorProgressBarVisibility(!mEditorDidMount); } @Override @@ -436,204 +204,6 @@ public void onRequestPermissionsResult(int requestCode, @NonNull String[] permis } } - private void setEditorProgressBarVisibility(boolean shown) { - } - - public void resetUploadingMediaToFailed(Set failedMediaIds) { - // get all media failed for this post, and represent it on tje UI - if (failedMediaIds != null && !failedMediaIds.isEmpty()) { - for (Integer mediaId : failedMediaIds) { - // and keep track of failed ids around - mFailedMediaIds.add(String.valueOf(mediaId)); - } - } - } - - private void updateFailedMediaState() { - for (String mediaId : mFailedMediaIds) { - // upload progress should work on numeric mediaIds only - if (!TextUtils.isEmpty(mediaId) && TextUtils.isDigitsOnly(mediaId)) { - if (NetworkUtils.isNetworkAvailable(getActivity())) { - getGutenbergContainerFragment().mediaFileUploadFailed(Integer.valueOf(mediaId)); - } else { - getGutenbergContainerFragment().mediaFileUploadPaused(Integer.valueOf(mediaId)); - } - } else { - getGutenbergContainerFragment().mediaFileSaveFailed(mediaId); - } - } - } - - private void updateMediaProgress() { - for (String mediaId : mUploadingMediaProgressMax.keySet()) { - // upload progress should work on numeric mediaIds only - if (!TextUtils.isEmpty(mediaId) && TextUtils.isDigitsOnly(mediaId)) { - getGutenbergContainerFragment().mediaFileUploadProgress(Integer.valueOf(mediaId), - mUploadingMediaProgressMax.get(mediaId)); - } else { - getGutenbergContainerFragment().mediaFileSaveProgress(mediaId, - mUploadingMediaProgressMax.get(mediaId)); - } - } - } - - private void checkAndRequestCameraAndStoragePermissions(int permissionRequestCode) { - if (PermissionUtils.checkAndRequestCameraAndStoragePermissions(this, - permissionRequestCode)) { - if (permissionRequestCode == CAPTURE_PHOTO_PERMISSION_REQUEST_CODE) { - mEditorFragmentListener.onCapturePhotoClicked(); - } else if (permissionRequestCode == CAPTURE_VIDEO_PERMISSION_REQUEST_CODE) { - mEditorFragmentListener.onCaptureVideoClicked(); - } - } - } - - private void cancelMediaUploadForDeletedBlock(int localMediaId) { - if (mUploadingMediaProgressMax.containsKey(String.valueOf(localMediaId))) { - // first make sure to signal deletion - mEditorFragmentListener.onMediaDeleted(String.valueOf(localMediaId)); - // second also perform a media upload cancel action, through the onMediaUploadCancelClicked interface - mEditorFragmentListener.onMediaUploadCancelClicked(String.valueOf(localMediaId)); - mUploadingMediaProgressMax.remove(String.valueOf(localMediaId)); - } else { - // upload has already finished by the time the user deleted the block, so no op - } - } - - @UiThread - private void showCancelMediaUploadDialog(final int localMediaId) { - // Display 'cancel upload' dialog - AlertDialog.Builder builder = new MaterialAlertDialogBuilder(getActivity()); - builder.setTitle(getString(R.string.stop_upload_dialog_title)); - builder.setPositiveButton(R.string.stop_upload_dialog_button_yes, - new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int id) { - if (mUploadingMediaProgressMax.containsKey(String.valueOf(localMediaId))) { - mEditorFragmentListener.onMediaUploadCancelClicked(String.valueOf(localMediaId)); - // remove from editor - mEditorFragmentListener.onMediaDeleted(String.valueOf(localMediaId)); - getGutenbergContainerFragment().clearMediaFileURL(localMediaId); - mCancelledMediaIds.put(String.valueOf(localMediaId), new Date()); - mUploadingMediaProgressMax.remove(String.valueOf(localMediaId)); - } else { - ToastUtils.showToast(getActivity(), R.string.upload_finished_toast).show(); - } - dialog.dismiss(); - } - }); - - builder.setNegativeButton(R.string.stop_upload_dialog_button_no, new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int id) { - dialog.dismiss(); - } - }); - - AlertDialog dialog = builder.create(); - dialog.show(); - } - - @UiThread - private void showRetryMediaUploadDialog(final int mediaId) { - // Display 'retry upload' dialog - AlertDialog.Builder builder = new MaterialAlertDialogBuilder(getActivity()); - builder.setTitle(getString(R.string.retry_failed_upload_title)); - String mediaErrorMessage = mEditorFragmentListener.getErrorMessageFromMedia(mediaId); - if (!TextUtils.isEmpty(mediaErrorMessage)) { - builder.setMessage(mediaErrorMessage); - } - builder.setPositiveButton(R.string.retry_failed_upload_yes, - new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int id) { - dialog.dismiss(); - mEditorFragmentListener.onMediaRetryAll(mFailedMediaIds); - } - }); - - builder.setNegativeButton(R.string.retry_failed_upload_remove, new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int id) { - dialog.dismiss(); - mEditorFragmentListener.onMediaDeleted(String.valueOf(mediaId)); - mFailedMediaIds.remove(String.valueOf(mediaId)); - getGutenbergContainerFragment().clearMediaFileURL(mediaId); - } - }); - - AlertDialog dialog = builder.create(); - dialog.show(); - } - - @UiThread - public void showFeaturedImageConfirmationDialog(final int mediaId) { - if (isStateSaved()) { - return; - } - - GutenbergDialogFragment dialog = new GutenbergDialogFragment(); - dialog.initialize( - TAG_REPLACE_FEATURED_DIALOG, - getString(R.string.featured_image_replace_dialog_title), - getString(R.string.featured_image_replace_dialog_description), - getString(R.string.featured_image_replace_dialog_confirm), - getString(R.string.featured_image_replace_dialog_cancel), - mediaId - ); - - dialog.show(getChildFragmentManager(), TAG_REPLACE_FEATURED_DIALOG); - } - - private void setFeaturedImage(int mediaId) { - mEditorFragmentListener.updateFeaturedImage(mediaId, false); - setFeaturedImageId(mediaId); - - if (mediaId == MEDIA_ID_NO_FEATURED_IMAGE_SET) { - showNotice(getString(R.string.featured_image_removed_notice)); - } else { - showNotice(getString(R.string.featured_image_confirmation_notice)); - } - } - - public void sendToJSFeaturedImageId(int mediaId) { - getGutenbergContainerFragment().sendToJSFeaturedImageId(mediaId); - } - - @UiThread - private void showCancelMediaCollectionUploadDialog(ArrayList mediaFiles) { - // Display 'cancel upload' dialog - AlertDialog.Builder builder = new MaterialAlertDialogBuilder(getActivity()); - builder.setTitle(getString(R.string.stop_upload_dialog_title)); - builder.setPositiveButton(R.string.stop_upload_dialog_button_yes, - new DialogInterface.OnClickListener() { - @SuppressWarnings("unchecked") - public void onClick(DialogInterface dialog, int id) { - // now signal Gutenberg upload failed, and remove the mediaIds from our tracking map - for (Object mediaFile : mediaFiles) { - // this conversion is needed to strip off decimals that can come from RN when using int as - // string - int localMediaId - = StringUtils.stringToInt( - ((HashMap) mediaFile).get("id").toString(), 0); - getGutenbergContainerFragment().mediaFileUploadFailed(localMediaId); - mUploadingMediaProgressMax.remove(localMediaId); - } - dialog.dismiss(); - } - }); - - builder.setNegativeButton(R.string.stop_upload_dialog_button_no, new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int id) { - dialog.dismiss(); - } - }); - - AlertDialog dialog = builder.create(); - dialog.show(); - } - - private void showImplicitKeyboard() { - InputMethodManager keyboard = (InputMethodManager) getActivity().getSystemService(Context.INPUT_METHOD_SERVICE); - keyboard.toggleSoftInput(InputMethodManager.SHOW_IMPLICIT, 0); - } - @Override public void onAttach(Activity activity) { super.onAttach(activity); @@ -641,19 +211,19 @@ public void onAttach(Activity activity) { try { mEditorDragAndDropListener = (EditorDragAndDropListener) activity; } catch (ClassCastException e) { - throw new ClassCastException(activity.toString() + " must implement EditorDragAndDropListener"); + throw new ClassCastException(activity + " must implement EditorDragAndDropListener"); } try { mEditorImagePreviewListener = (EditorImagePreviewListener) activity; } catch (ClassCastException e) { - throw new ClassCastException(activity.toString() + " must implement EditorImagePreviewListener"); + throw new ClassCastException(activity + " must implement EditorImagePreviewListener"); } try { mEditorEditMediaListener = (EditorEditMediaListener) activity; } catch (ClassCastException e) { - throw new ClassCastException(activity.toString() + " must implement EditorEditMediaListener"); + throw new ClassCastException(activity + " must implement EditorEditMediaListener"); } } @@ -661,8 +231,6 @@ public void onAttach(Activity activity) { public void onSaveInstanceState(Bundle outState) { outState.putBoolean(KEY_HTML_MODE_ENABLED, mHtmlModeEnabled); outState.putBoolean(KEY_EDITOR_DID_MOUNT, mEditorDidMount); - outState.putString(ARG_STORY_BLOCK_EXTERNALLY_EDITED_ORIGINAL_HASH, mExternallyEditedBlockOriginalHash); - outState.putSerializable(ARG_FAILED_MEDIAS, mFailedMediaIds); outState.putLong(ARG_FEATURED_IMAGE_ID, mFeaturedImageId); } @@ -681,34 +249,15 @@ public void onPrepareOptionsMenu(@NonNull Menu menu) { @Override public boolean onOptionsItemSelected(@NonNull MenuItem item) { - if (item.getItemId() == R.id.debugmenu) { - getGutenbergContainerFragment().showDevOptionsDialog(); - return true; - } - return false; } @Override public void onRedoEnabled() { - if (!isAdded()) { - return; - } - - mInvalidateOptionsHandler.removeCallbacks(mInvalidateOptionsRunnable); - mInvalidateOptionsHandler.postDelayed(mInvalidateOptionsRunnable, - getResources().getInteger(android.R.integer.config_mediumAnimTime)); } @Override public void onUndoEnabled() { - if (!isAdded()) { - return; - } - - mInvalidateOptionsHandler.removeCallbacks(mInvalidateOptionsRunnable); - mInvalidateOptionsHandler.postDelayed(mInvalidateOptionsRunnable, - getResources().getInteger(android.R.integer.config_mediumAnimTime)); } @Override @@ -721,18 +270,6 @@ public void onRedo() { // Analytics tracking is not available in GB mobile } - private ActionBar getActionBar() { - if (!isAdded()) { - return null; - } - - if (getActivity() instanceof AppCompatActivity) { - return ((AppCompatActivity) getActivity()).getSupportActionBar(); - } else { - return null; - } - } - @Override public void setTitle(CharSequence title) { } @@ -748,29 +285,6 @@ public void setContent(CharSequence text) { @Override public void updateContent(@Nullable CharSequence text) { - if (text == null) { - text = ""; - } - - if (getGutenbergContainerFragment() != null) { - getGutenbergContainerFragment().onContentUpdate(text.toString()); - } - } - - public void setJetpackSsoEnabled(boolean jetpackSsoEnabled) { - mIsJetpackSsoEnabled = jetpackSsoEnabled; - } - - public void updateCapabilities(GutenbergPropsBuilder gutenbergPropsBuilder) { - mCurrentGutenbergPropsBuilder = gutenbergPropsBuilder; - if (isAdded()) { - GutenbergContainerFragment containerFragment = getGutenbergContainerFragment(); - if (containerFragment != null) { - containerFragment.updateCapabilities(gutenbergPropsBuilder); - } - } else { - mUpdateCapabilitiesOnCreate = true; - } } public void onToggleHtmlMode() { @@ -783,37 +297,8 @@ public void onToggleHtmlMode() { private void toggleHtmlMode() { mHtmlModeEnabled = !mHtmlModeEnabled; - mEditorFragmentListener.onTrackableEvent(TrackableEvent.HTML_BUTTON_TAPPED); - - // Don't switch to HTML mode if currently uploading media - if (!mUploadingMediaProgressMax.isEmpty() || isActionInProgress()) { - ToastUtils.showToast(getActivity(), R.string.alert_action_while_uploading, ToastUtils.Duration.LONG); - return; - } - mEditorFragmentListener.onHtmlModeToggledInToolbar(); - getGutenbergContainerFragment().toggleHtmlMode(); - } - - public void sendToJSPostSaveEvent() { - } - - /* - * TODO: REMOVE THIS ONCE AZTEC COMPLETELY REPLACES THE VISUAL EDITOR IN WPANDROID APP - */ - private String removeVisualEditorProgressTag(String originalText) { - // this regex picks any tags and any opening tags for image containers - // as produced by the Visual Editor. Note that we don't care about closing tags - // as the AztecParser takes care of that, and it would be very difficult to accomplish with a - // regex (and using a proper XML crawler would be particularly overkill) - if (originalText != null && originalText.contains(""; - return originalText.replaceAll(regex, ""); - } else { - return originalText; - } } @Override @@ -860,12 +345,8 @@ public CharSequence getContent(CharSequence originalContent) throws EditorFragme if (!isAdded()) { throw new EditorFragmentNotAddedException(); } - return getGutenbergContainerFragment().getContent(originalContent, new OnGetContentInterrupted() { - @Override public void onGetContentInterrupted(InterruptedException ie) { - AppLog.e(T.EDITOR, ie); - Thread.currentThread().interrupt(); - } - }); + + return ""; } @Override @@ -873,44 +354,6 @@ public void showContentInfo() throws EditorFragmentNotAddedException { if (!isAdded()) { throw new EditorFragmentNotAddedException(); } - - getGutenbergContainerFragment().triggerGetContentInfo(new OnContentInfoReceivedListener() { - @Override - public void onContentInfoFailed() { - if (getActivity() != null) { - getActivity().runOnUiThread(() -> { - ToastUtils.showToast(getActivity(), R.string.toast_content_info_failed); - }); - } - } - - @Override - public void onEditorNotReady() { - if (getActivity() != null) { - getActivity().runOnUiThread(() -> { - ToastUtils.showToast(getActivity(), R.string.toast_content_info_editor_not_ready); - }); - } - } - - @Override - public void onContentInfoReceived(HashMap contentInfo) { - int blockCount = (int) Double.parseDouble(contentInfo.get("blockCount").toString()); - int wordCount = (int) Double.parseDouble(contentInfo.get("wordCount").toString()); - int charCount = (int) Double.parseDouble(contentInfo.get("characterCount").toString()); - - if (getActivity() != null) { - getActivity().runOnUiThread(() -> { - AlertDialog.Builder builder = new MaterialAlertDialogBuilder(getActivity()); - builder.setTitle(getString(R.string.dialog_content_info_title)); - builder.setMessage( - getString(R.string.dialog_content_info_body, blockCount, wordCount, charCount)); - builder.setPositiveButton(getString(R.string.dialog_button_ok), null); - builder.show(); - }); - } - } - }); } public void onEditorContentChanged(@NonNull ContentChangeListener listener) { @@ -944,7 +387,7 @@ public void appendMediaFiles(Map mediaList) { // Get media URL of first of media first to check if it is network or local one. String mediaUrl = ""; Object[] mediaUrls = mediaList.keySet().toArray(); - if (mediaUrls != null && mediaUrls.length > 0) { + if (mediaUrls.length > 0) { mediaUrl = (String) mediaUrls[0]; } @@ -993,7 +436,7 @@ public boolean isUploadingMedia() { @Override public boolean hasFailedMediaUploads() { - return (mFailedMediaIds.size() > 0); + return false; } @Override @@ -1004,106 +447,40 @@ public void removeAllFailedMediaUploads() { public void removeMedia(String mediaId) { } - @UiThread - private boolean showSavingProgressDialogIfNeeded() { - if (!isAdded()) { - return false; - } - - if (mSavingContentProgressDialog != null && mSavingContentProgressDialog.isShowing()) { - // Already on the screen? no need to show it again. - return true; - } - - if (mSavingContentProgressDialog == null) { - mSavingContentProgressDialog = new ProgressDialog(getActivity()); - mSavingContentProgressDialog.setCancelable(false); - mSavingContentProgressDialog.setIndeterminate(true); - mSavingContentProgressDialog.setMessage(getActivity().getString(R.string.long_post_dlg_saving)); - } - mSavingContentProgressDialog.show(); - return true; - } - - private boolean hideSavingProgressDialog() { - if (mSavingContentProgressDialog != null && mSavingContentProgressDialog.isShowing()) { - mSavingContentProgressDialog.dismiss(); - return true; - } - return false; - } - @Override public void onDestroy() { if (mGutenbergView != null) { GutenbergWebViewPool.recycleWebView(mGutenbergView); mContentChangeListener = null; } - hideSavingProgressDialog(); super.onDestroy(); } @Override public void mediaSelectionCancelled() { - getGutenbergContainerFragment().mediaSelectionCancelled(); } @Override public void onMediaUploadReattached(String localMediaId, float currentProgress) { - mUploadingMediaProgressMax.put(localMediaId, currentProgress); - getGutenbergContainerFragment().mediaFileUploadProgress(Integer.valueOf(localMediaId), currentProgress); } @Override public void onMediaUploadRetry(String localMediaId, MediaType mediaType) { - if (mFailedMediaIds.contains(localMediaId)) { - mFailedMediaIds.remove(localMediaId); - mUploadingMediaProgressMax.put(localMediaId, 0f); - } - - // TODO request to start the upload again from the UploadService } @Override public void onMediaUploadSucceeded(final String localMediaId, final MediaFile mediaFile) { - mUploadingMediaProgressMax.remove(localMediaId); - - WritableNativeMap metadata = new WritableNativeMap(); - if (mediaFile.getVideoPressGuid() != null) { - metadata.putString("videopressGUID", mediaFile.getVideoPressGuid()); - } - - getGutenbergContainerFragment() - .mediaFileUploadSucceeded(Integer.parseInt(localMediaId), mediaFile.getOptimalFileURL(), - Integer.parseInt(mediaFile.getMediaId()), metadata); } @Override public void onMediaUploadProgress(final String localMediaId, final float progress) { - if (!mCancelledMediaIds.containsKey(localMediaId)) { - mUploadingMediaProgressMax.put(localMediaId, progress); - getGutenbergContainerFragment().mediaFileUploadProgress(Integer.valueOf(localMediaId), progress); - } else { - // checks to ensure that its been two seconds since the last progress event and if so then - // we treat the event as a new one and remove it from the cancelled media IDs being tracked. - Date startTime = mCancelledMediaIds.get(localMediaId); - if (DateTimeUtils.secondsBetween(startTime, new Date()) > 2) { - mCancelledMediaIds.remove(localMediaId); - } - } } @Override public void onMediaUploadFailed(final String localMediaId) { - getGutenbergContainerFragment().mediaFileUploadFailed(Integer.valueOf(localMediaId)); - mFailedMediaIds.add(localMediaId); - mUploadingMediaProgressMax.remove(localMediaId); } @Override public void onMediaUploadPaused(final String localMediaId) { - getGutenbergContainerFragment().mediaFileUploadPaused(Integer.valueOf(localMediaId)); - mFailedMediaIds.add(localMediaId); - mUploadingMediaProgressMax.remove(localMediaId); } @Override @@ -1120,33 +497,20 @@ public void showNotice(String message) { @Override public void showEditorHelp() { - getGutenbergContainerFragment().showEditorHelp(); } @Override public void onUndoPressed() { - getGutenbergContainerFragment().onUndoPressed(); } @Override public void onRedoPressed() { - getGutenbergContainerFragment().onRedoPressed(); } @Override public void onGutenbergDialogPositiveClicked(@NonNull String instanceTag, int mediaId) { - switch (instanceTag) { - case TAG_REPLACE_FEATURED_DIALOG: - setFeaturedImage(mediaId); - break; - } } @Override public void onGutenbergDialogNegativeClicked(@NonNull String instanceTag) { - switch (instanceTag) { - case TAG_REPLACE_FEATURED_DIALOG: - // Dismiss dialog with no action. - break; - } } @Override From a8a6937ce990141dfed1eda366cdb7ffd45443d8 Mon Sep 17 00:00:00 2001 From: David Calhoun Date: Tue, 26 Nov 2024 09:28:45 -0500 Subject: [PATCH 7/8] refactor: Rename "NewGutenberg" to "GutenbergKit" The use of "new" to uniquely describe something is problematic, as it provides little context and could become incorrect at some future time. --- WordPress/build.gradle | 4 +-- .../android/ui/posts/EditPostActivity.kt | 36 +++++++++---------- .../ui/posts/EditPostActivityConstants.kt | 2 +- .../util/config/GutenbergKitFeatureConfig.kt | 16 +++++++++ .../GutenbergKitThemeStylesFeatureConfig.kt | 16 +++++++++ .../util/config/NewGutenbergFeatureConfig.kt | 16 --------- .../NewGutenbergThemeStylesFeatureConfig.kt | 16 --------- .../gutenberg/GutenbergKitEditorFragment.java | 6 ++-- 8 files changed, 56 insertions(+), 56 deletions(-) create mode 100644 WordPress/src/main/java/org/wordpress/android/util/config/GutenbergKitFeatureConfig.kt create mode 100644 WordPress/src/main/java/org/wordpress/android/util/config/GutenbergKitThemeStylesFeatureConfig.kt delete mode 100644 WordPress/src/main/java/org/wordpress/android/util/config/NewGutenbergFeatureConfig.kt delete mode 100644 WordPress/src/main/java/org/wordpress/android/util/config/NewGutenbergThemeStylesFeatureConfig.kt 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 2f064794a175..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 @@ -235,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 @@ -315,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 @@ -410,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 @@ -517,7 +517,7 @@ class EditPostActivity : LocaleAwareActivity(), EditorFragmentActivity, EditorIm } onBackPressedDispatcher.addCallback(this, callback) dispatcher.register(this) - isNewGutenbergEditor = newGutenbergFeatureConfig.isEnabled() + isGutenbergKitEditor = gutenbergKitFeatureConfig.isEnabled() createEditShareMessageActivityResultLauncher() @@ -726,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( @@ -783,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 @@ -1001,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() @@ -1202,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 @@ -1412,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) @@ -1463,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? -> @@ -2171,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) } @@ -2401,7 +2401,7 @@ class EditPostActivity : LocaleAwareActivity(), EditorFragmentActivity, EditorIm override fun getItem(position: Int): Fragment { return when (position) { PAGE_CONTENT -> { - if (isNewGutenbergEditor && showGutenbergEditor) { + if (isGutenbergKitEditor && showGutenbergEditor) { createGutenbergKitEditorFragment() } else if (showGutenbergEditor) { createGutenbergEditorFragment() @@ -2456,7 +2456,7 @@ class EditPostActivity : LocaleAwareActivity(), EditorFragmentActivity, EditorIm "siteApiRoot" to siteApiRoot, "authHeader" to authHeader, "siteApiNamespace" to siteApiNamespace, - "themeStyles" to newGutenbergThemeStylesConfig.isEnabled() + "themeStyles" to gutenbergKitThemeStylesConfig.isEnabled() ) return GutenbergKitEditorFragment.newInstance( @@ -2508,7 +2508,7 @@ class EditPostActivity : LocaleAwareActivity(), EditorFragmentActivity, EditorIm 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() @@ -3635,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/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/GutenbergKitEditorFragment.java b/libs/editor/src/main/java/org/wordpress/android/editor/gutenberg/GutenbergKitEditorFragment.java index 8a01641e824a..22b362ee34f0 100644 --- 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 @@ -70,7 +70,7 @@ public class GutenbergKitEditorFragment extends EditorFragmentAbstract implement 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_NEW_GUTENBERG_SETTINGS = "new_gutenberg_settings"; + 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; @@ -94,7 +94,7 @@ public static GutenbergKitEditorFragment newInstance(Context context, Bundle args = new Bundle(); args.putBoolean(ARG_IS_NEW_POST, isNewPost); args.putBoolean(ARG_JETPACK_FEATURES_ENABLED, jetpackFeaturesEnabled); - args.putSerializable(ARG_NEW_GUTENBERG_SETTINGS, (Serializable) settings); + args.putSerializable(ARG_GUTENBERG_KIT_SETTINGS, (Serializable) settings); fragment.setArguments(args); SavedInstanceDatabase db = SavedInstanceDatabase.Companion.getDatabase(context); mSettings = settings; @@ -122,7 +122,7 @@ public void onCreate(@Nullable Bundle savedInstanceState) { @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { if (getArguments() != null) { - mSettings = (Map) getArguments().getSerializable(ARG_NEW_GUTENBERG_SETTINGS); + mSettings = (Map) getArguments().getSerializable(ARG_GUTENBERG_KIT_SETTINGS); } mGutenbergView = GutenbergWebViewPool.getPreloadedWebView(requireContext()); From e862f40803a139d28dd8d2c5f13b3e706bd33959 Mon Sep 17 00:00:00 2001 From: David Calhoun Date: Tue, 26 Nov 2024 09:45:28 -0500 Subject: [PATCH 8/8] docs: Note no-op reasons --- .../gutenberg/GutenbergKitEditorFragment.java | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) 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 index 22b362ee34f0..dbe53032e50b 100644 --- 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 @@ -254,10 +254,12 @@ public boolean onOptionsItemSelected(@NonNull MenuItem item) { @Override public void onRedoEnabled() { + // Currently unsupported } @Override public void onUndoEnabled() { + // Currently unsupported } @Override @@ -272,6 +274,7 @@ public void onRedo() { @Override public void setTitle(CharSequence title) { + // Unused, no-op retained for the shared interface with Gutenberg } @Override @@ -285,6 +288,7 @@ public void setContent(CharSequence text) { @Override public void updateContent(@Nullable CharSequence text) { + // Unused, no-op retained for the shared interface with Gutenberg } public void onToggleHtmlMode() { @@ -423,28 +427,34 @@ public void appendMediaFiles(Map mediaList) { @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 @@ -457,63 +467,79 @@ public void 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 } }