diff --git a/packages/react-native-editor/android/app/src/main/java/com/gutenberg/MainActivity.java b/packages/react-native-editor/android/app/src/main/java/com/gutenberg/MainActivity.java deleted file mode 100644 index 3ea19fa97b3831..00000000000000 --- a/packages/react-native-editor/android/app/src/main/java/com/gutenberg/MainActivity.java +++ /dev/null @@ -1,233 +0,0 @@ -package com.gutenberg; - -import android.os.Bundle; -import android.util.Log; -import android.view.Menu; -import android.view.MenuItem; -import android.view.View; -import android.view.ViewGroup; -import android.widget.LinearLayout; - -import androidx.appcompat.widget.Toolbar; -import androidx.core.content.ContextCompat; - -import com.facebook.react.ReactActivity; -import com.facebook.react.ReactInstanceManager; -import com.facebook.react.ReactRootView; - -import org.json.JSONException; -import org.json.JSONObject; -import org.wordpress.mobile.WPAndroidGlue.GutenbergProps; - -import java.util.Locale; - -public class MainActivity extends ReactActivity { - private static MainActivity currentInstance; - - private ReactRootView mReactRootView; - private Menu mMenu; - - private static final String EXTRAS_INITIAL_PROPS = "initialProps"; - - private void openReactNativeDebugMenu() { - ReactInstanceManager devSettingsModule = getReactInstanceManager(); - if (devSettingsModule != null) { - devSettingsModule.showDevOptionsDialog(); - } - } - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - mMenu = menu; - getMenuInflater().inflate(R.menu.toolbar_menu, menu); - - // Set opacity for menu items - MenuItem undoItem = menu.findItem(R.id.menuUndo); - undoItem.getIcon().setAlpha(76); - undoItem.setEnabled(false); - - MenuItem redoItem = menu.findItem(R.id.menuRedo); - redoItem.getIcon().setAlpha(76); - redoItem.setEnabled(false); - return true; - } - - public void updateUndoItem(boolean isDisabled) { - if (mMenu != null) { - runOnUiThread(new Runnable() { - @Override - public void run() { - MenuItem undoItem = mMenu.findItem(R.id.menuUndo); - - undoItem.setEnabled(!isDisabled); - undoItem.getIcon().setAlpha(!isDisabled ? 255 : 76); - } - }); - } - } - - public void updateRedoItem(boolean isDisabled) { - if (mMenu != null) { - runOnUiThread(new Runnable() { - @Override - public void run() { - MenuItem redoItem = mMenu.findItem(R.id.menuRedo); - - redoItem.setEnabled(!isDisabled); - redoItem.getIcon().setAlpha(!isDisabled ? 255 : 76); - } - }); - } - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - MainApplication mainApplication = (MainApplication) getApplication(); - - int itemId = item.getItemId(); - if (itemId == R.id.menuUndo) { - mainApplication.toggleUndo(); - return true; - } - if (itemId == R.id.menuRedo) { - mainApplication.toggleRedo(); - return true; - } - if (itemId == R.id.menuButton) { - openReactNativeDebugMenu(); - return true; - } - return super.onOptionsItemSelected(item); - } - - public static MainActivity getInstance() { - return currentInstance; - } - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - currentInstance = this; - - // Create a LinearLayout that will hold both the toolbar and React Native content - LinearLayout linearLayout = new LinearLayout(this); - linearLayout.setOrientation(LinearLayout.VERTICAL); - linearLayout.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); - linearLayout.setFocusable(false); - linearLayout.setFocusableInTouchMode(true); - - // Create a Toolbar instance - Toolbar toolbar = new Toolbar(this); - - // Set toolbar properties (you can customize this as you want) - toolbar.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)); - - // Set the toolbar as the Activity's action bar - setSupportActionBar(toolbar); - - // Add the toolbar to the linear layout - linearLayout.addView(toolbar); - - // Create a View to be used as the border - View borderView = new View(this); - borderView.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 1)); - borderView.setBackgroundColor(ContextCompat.getColor(this, R.color.toolbarBorder)); - - // Add the border view to the linear layout - linearLayout.addView(borderView); - - // Create a ReactRootView and assign it to mReactRootView - mReactRootView = new ReactRootView(this); - LinearLayout.LayoutParams reactViewParams = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 0, 1); - mReactRootView.setLayoutParams(reactViewParams); - - // Add ReactView to the linear layout - linearLayout.addView(mReactRootView); - - // Set the linear layout as the content view - setContentView(linearLayout); - - // Load the React application - mReactRootView.startReactApplication( - ((MainApplication) getApplication()).getReactNativeHost().getReactInstanceManager(), - getMainComponentName(), - getAppOptions() - ); - } - - /** - * Returns the name of the main component registered from JavaScript. - * This is used to schedule rendering of the component. - */ - @Override - protected String getMainComponentName() { - return "gutenberg"; - } - - private Bundle getAppOptions() { - Bundle bundle = new Bundle(); - - // Parse initial props from launch arguments - String initialTitle = null; - String initialData = null; - String rawStyles = null; - String rawFeatures = null; - Bundle extrasBundle = getIntent().getExtras(); - - if(extrasBundle != null) { - String initialProps = extrasBundle.getString(EXTRAS_INITIAL_PROPS, "{}"); - try { - JSONObject jsonObject = new JSONObject(initialProps); - if (jsonObject.has(GutenbergProps.PROP_INITIAL_TITLE)) { - initialTitle = jsonObject.getString(GutenbergProps.PROP_INITIAL_TITLE); - } - if (jsonObject.has(GutenbergProps.PROP_INITIAL_DATA)) { - initialData = jsonObject.getString(GutenbergProps.PROP_INITIAL_DATA); - } - if (jsonObject.has(GutenbergProps.PROP_STYLES)) { - rawStyles = jsonObject.getString(GutenbergProps.PROP_STYLES); - } - if (jsonObject.has(GutenbergProps.PROP_FEATURES)) { - rawFeatures = jsonObject.getString(GutenbergProps.PROP_FEATURES); - } - } catch (final JSONException e) { - Log.e("MainActivity", "Json parsing error: " + e.getMessage()); - } - } - - // Add locale - String languageString = Locale.getDefault().toString(); - String localeSlug = languageString.replace("_", "-").toLowerCase(Locale.ENGLISH); - bundle.putString(GutenbergProps.PROP_LOCALE, localeSlug); - - // Add capabilities - Bundle capabilities = new Bundle(); - capabilities.putBoolean(GutenbergProps.PROP_CAPABILITIES_MENTIONS, true); - capabilities.putBoolean(GutenbergProps.PROP_CAPABILITIES_XPOSTS, true); - capabilities.putBoolean(GutenbergProps.PROP_CAPABILITIES_UNSUPPORTED_BLOCK_EDITOR, true); - capabilities.putBoolean(GutenbergProps.PROP_CAPABILITIES_REUSABLE_BLOCK, false); - capabilities.putBoolean(GutenbergProps.PROP_CAPABILITIES_IS_AUDIO_BLOCK_MEDIA_UPLOAD_ENABLED, true); - capabilities.putBoolean(GutenbergProps.PROP_CAPABILITIES_TILED_GALLERY_BLOCK, true); - capabilities.putBoolean(GutenbergProps.PROP_CAPABILITIES_VIDEOPRESS_BLOCK, true); - capabilities.putBoolean(GutenbergProps.PROP_CAPABILITIES_FACEBOOK_EMBED_BLOCK, true); - capabilities.putBoolean(GutenbergProps.PROP_CAPABILITIES_INSTAGRAM_EMBED_BLOCK, true); - capabilities.putBoolean(GutenbergProps.PROP_CAPABILITIES_LOOM_EMBED_BLOCK, true); - capabilities.putBoolean(GutenbergProps.PROP_CAPABILITIES_SMARTFRAME_EMBED_BLOCK, true); - bundle.putBundle(GutenbergProps.PROP_CAPABILITIES, capabilities); - - if(initialTitle != null) { - bundle.putString(GutenbergProps.PROP_INITIAL_TITLE, initialTitle); - } - if(initialData != null) { - bundle.putString(GutenbergProps.PROP_INITIAL_DATA, initialData); - } - if(rawStyles != null) { - bundle.putString(GutenbergProps.PROP_STYLES, rawStyles); - } - if(rawFeatures != null) { - bundle.putString(GutenbergProps.PROP_FEATURES, rawFeatures); - } - - return bundle; - } -} diff --git a/packages/react-native-editor/android/app/src/main/java/com/gutenberg/MainActivity.kt b/packages/react-native-editor/android/app/src/main/java/com/gutenberg/MainActivity.kt new file mode 100644 index 00000000000000..29b047bf01f7d7 --- /dev/null +++ b/packages/react-native-editor/android/app/src/main/java/com/gutenberg/MainActivity.kt @@ -0,0 +1,207 @@ +package com.gutenberg + +import android.os.Bundle +import android.util.Log +import android.view.Menu +import android.view.MenuItem +import android.view.View +import android.view.ViewGroup +import android.widget.LinearLayout +import androidx.appcompat.widget.Toolbar +import androidx.core.content.ContextCompat +import com.facebook.react.ReactActivity +import com.facebook.react.ReactRootView +import org.json.JSONException +import org.json.JSONObject +import org.wordpress.mobile.WPAndroidGlue.GutenbergProps +import java.util.Locale + +class MainActivity : ReactActivity() { + private var mReactRootView: ReactRootView? = null + private var mMenu: Menu? = null + private fun openReactNativeDebugMenu() { + val devSettingsModule = reactInstanceManager + devSettingsModule?.showDevOptionsDialog() + } + + override fun onCreateOptionsMenu(menu: Menu): Boolean { + mMenu = menu + menuInflater.inflate(R.menu.toolbar_menu, menu) + + // Set opacity for menu items + val undoItem = menu.findItem(R.id.menuUndo) + undoItem.icon!!.alpha = 76 + undoItem.setEnabled(false) + val redoItem = menu.findItem(R.id.menuRedo) + redoItem.icon!!.alpha = 76 + redoItem.setEnabled(false) + return true + } + + fun updateUndoItem(isDisabled: Boolean) { + if (mMenu != null) { + runOnUiThread { + val undoItem = mMenu!!.findItem(R.id.menuUndo) + undoItem.setEnabled(!isDisabled) + undoItem.icon!!.alpha = if (!isDisabled) 255 else 76 + } + } + } + + fun updateRedoItem(isDisabled: Boolean) { + if (mMenu != null) { + runOnUiThread { + val redoItem = mMenu!!.findItem(R.id.menuRedo) + redoItem.setEnabled(!isDisabled) + redoItem.icon!!.alpha = if (!isDisabled) 255 else 76 + } + } + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + val mainApplication = application as MainApplication + val itemId = item.itemId + if (itemId == R.id.menuUndo) { + mainApplication.toggleUndo() + return true + } + if (itemId == R.id.menuRedo) { + mainApplication.toggleRedo() + return true + } + if (itemId == R.id.menuButton) { + openReactNativeDebugMenu() + return true + } + return super.onOptionsItemSelected(item) + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + instance = this + + // Create a LinearLayout that will hold both the toolbar and React Native content + val linearLayout = LinearLayout(this) + linearLayout.orientation = LinearLayout.VERTICAL + linearLayout.layoutParams = LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT) + linearLayout.isFocusable = false + linearLayout.isFocusableInTouchMode = true + + // Create a Toolbar instance + val toolbar = Toolbar(this) + + // Set toolbar properties (you can customize this as you want) + toolbar.layoutParams = LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT) + + // Set the toolbar as the Activity's action bar + setSupportActionBar(toolbar) + + // Add the toolbar to the linear layout + linearLayout.addView(toolbar) + + // Create a View to be used as the border + val borderView = View(this) + borderView.layoutParams = LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 1) + borderView.setBackgroundColor(ContextCompat.getColor(this, R.color.toolbarBorder)) + + // Add the border view to the linear layout + linearLayout.addView(borderView) + + // Create a ReactRootView and assign it to mReactRootView + mReactRootView = ReactRootView(this) + val reactViewParams = LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 0, 1f) + mReactRootView!!.layoutParams = reactViewParams + + // Add ReactView to the linear layout + linearLayout.addView(mReactRootView) + + // Set the linear layout as the content view + setContentView(linearLayout) + + // Load the React application + mReactRootView!!.startReactApplication( + (application as MainApplication).reactNativeHost.reactInstanceManager, + mainComponentName, + appOptions + ) + } + + /** + * Returns the name of the main component registered from JavaScript. + * This is used to schedule rendering of the component. + */ + override fun getMainComponentName(): String { + return "gutenberg" + } + + private val appOptions: Bundle + get() { + val bundle = Bundle() + + // Parse initial props from launch arguments + var initialTitle: String? = null + var initialData: String? = null + var rawStyles: String? = null + var rawFeatures: String? = null + val extrasBundle = intent.extras + if (extrasBundle != null) { + val initialProps = extrasBundle.getString(EXTRAS_INITIAL_PROPS, "{}") + try { + val jsonObject = JSONObject(initialProps) + if (jsonObject.has(GutenbergProps.PROP_INITIAL_TITLE)) { + initialTitle = jsonObject.getString(GutenbergProps.PROP_INITIAL_TITLE) + } + if (jsonObject.has(GutenbergProps.PROP_INITIAL_DATA)) { + initialData = jsonObject.getString(GutenbergProps.PROP_INITIAL_DATA) + } + if (jsonObject.has(GutenbergProps.PROP_STYLES)) { + rawStyles = jsonObject.getString(GutenbergProps.PROP_STYLES) + } + if (jsonObject.has(GutenbergProps.PROP_FEATURES)) { + rawFeatures = jsonObject.getString(GutenbergProps.PROP_FEATURES) + } + } catch (e: JSONException) { + Log.e("MainActivity", "Json parsing error: " + e.message) + } + } + + // Add locale + val languageString = Locale.getDefault().toString() + val localeSlug = languageString.replace("_", "-").lowercase() + bundle.putString(GutenbergProps.PROP_LOCALE, localeSlug) + + // Add capabilities + val capabilities = Bundle() + capabilities.putBoolean(GutenbergProps.PROP_CAPABILITIES_MENTIONS, true) + capabilities.putBoolean(GutenbergProps.PROP_CAPABILITIES_XPOSTS, true) + capabilities.putBoolean(GutenbergProps.PROP_CAPABILITIES_UNSUPPORTED_BLOCK_EDITOR, true) + capabilities.putBoolean(GutenbergProps.PROP_CAPABILITIES_REUSABLE_BLOCK, false) + capabilities.putBoolean(GutenbergProps.PROP_CAPABILITIES_IS_AUDIO_BLOCK_MEDIA_UPLOAD_ENABLED, true) + capabilities.putBoolean(GutenbergProps.PROP_CAPABILITIES_TILED_GALLERY_BLOCK, true) + capabilities.putBoolean(GutenbergProps.PROP_CAPABILITIES_VIDEOPRESS_BLOCK, true) + capabilities.putBoolean(GutenbergProps.PROP_CAPABILITIES_FACEBOOK_EMBED_BLOCK, true) + capabilities.putBoolean(GutenbergProps.PROP_CAPABILITIES_INSTAGRAM_EMBED_BLOCK, true) + capabilities.putBoolean(GutenbergProps.PROP_CAPABILITIES_LOOM_EMBED_BLOCK, true) + capabilities.putBoolean(GutenbergProps.PROP_CAPABILITIES_SMARTFRAME_EMBED_BLOCK, true) + bundle.putBundle(GutenbergProps.PROP_CAPABILITIES, capabilities) + if (initialTitle != null) { + bundle.putString(GutenbergProps.PROP_INITIAL_TITLE, initialTitle) + } + if (initialData != null) { + bundle.putString(GutenbergProps.PROP_INITIAL_DATA, initialData) + } + if (rawStyles != null) { + bundle.putString(GutenbergProps.PROP_STYLES, rawStyles) + } + if (rawFeatures != null) { + bundle.putString(GutenbergProps.PROP_FEATURES, rawFeatures) + } + return bundle + } + + companion object { + var instance: MainActivity? = null + private set + private const val EXTRAS_INITIAL_PROPS = "initialProps" + } +} diff --git a/packages/react-native-editor/android/app/src/main/java/com/gutenberg/MainApplication.java b/packages/react-native-editor/android/app/src/main/java/com/gutenberg/MainApplication.java deleted file mode 100644 index 4477f1cc1d9f35..00000000000000 --- a/packages/react-native-editor/android/app/src/main/java/com/gutenberg/MainApplication.java +++ /dev/null @@ -1,433 +0,0 @@ -package com.gutenberg; - -import android.app.Application; -import android.content.Intent; -import android.content.res.Configuration; -import android.os.Bundle; -import android.util.Log; -import android.widget.Toast; - -import androidx.core.util.Consumer; - -import com.facebook.react.ReactApplication; -import com.BV.LinearGradient.LinearGradientPackage; -import com.facebook.react.bridge.Arguments; -import com.facebook.react.bridge.ReadableMap; -import com.facebook.react.bridge.WritableNativeMap; -import com.reactnativecommunity.clipboard.ClipboardPackage; -import com.reactnativecommunity.slider.ReactSliderPackage; -import com.brentvatne.react.ReactVideoPackage; -import com.facebook.react.bridge.ReadableArray; -import com.facebook.react.devsupport.interfaces.DevOptionHandler; -import com.facebook.react.devsupport.interfaces.DevSupportManager; -import com.horcrux.svg.SvgPackage; -import org.linusu.RNGetRandomValuesPackage; -import com.dylanvann.fastimage.FastImageViewPackage; - -import org.wordpress.mobile.ReactNativeAztec.ReactAztecPackage; -import org.wordpress.mobile.ReactNativeGutenbergBridge.GutenbergBridgeInterface; -import org.wordpress.mobile.ReactNativeGutenbergBridge.GutenbergBridgeJS2Parent; -import org.wordpress.mobile.ReactNativeGutenbergBridge.GutenbergWebViewActivity; -import org.wordpress.mobile.ReactNativeGutenbergBridge.RNMedia; -import org.wordpress.mobile.ReactNativeGutenbergBridge.RNReactNativeGutenbergBridgePackage; - -import com.facebook.react.ReactNativeHost; -import com.facebook.react.ReactPackage; -import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint; -import com.facebook.react.defaults.DefaultReactNativeHost; -import com.facebook.react.shell.MainReactPackage; -import com.facebook.soloader.SoLoader; -import com.reactnativecommunity.webview.RNCWebViewPackage; -import com.swmansion.gesturehandler.RNGestureHandlerPackage; -import com.swmansion.reanimated.ReanimatedPackage; -import com.swmansion.rnscreens.RNScreensPackage; -import com.th3rdwave.safeareacontext.SafeAreaContextPackage; -import org.reactnative.maskedview.RNCMaskedViewPackage; -import org.wordpress.mobile.WPAndroidGlue.Media; -import org.wordpress.mobile.WPAndroidGlue.MediaOption; - -import java.lang.reflect.InvocationTargetException; -import java.util.Arrays; -import java.util.List; -import java.util.ArrayList; - -public class MainApplication extends Application implements ReactApplication, GutenbergBridgeInterface { - - private static final String TAG = "MainApplication"; - - private ReactNativeHost mReactNativeHost; - private RNReactNativeGutenbergBridgePackage mRnReactNativeGutenbergBridgePackage; - private GutenbergBridgeJS2Parent.ReplaceUnsupportedBlockCallback mReplaceUnsupportedBlockCallback; - - private ReactNativeHost createReactNativeHost() { - mRnReactNativeGutenbergBridgePackage = new RNReactNativeGutenbergBridgePackage(new GutenbergBridgeJS2Parent() { - @Override - public void responseHtml(String title, String html, boolean changed, ReadableMap contentInfo) { - } - - @Override - public void requestMediaImport(String url, MediaSelectedCallback mediaSelectedCallback) { - } - - @Override - public void requestMediaPickerFromDeviceCamera(MediaSelectedCallback mediaSelectedCallback, MediaType mediaType) { - } - - @Override - public void requestMediaPickFromDeviceLibrary(MediaSelectedCallback mediaSelectedCallback, Boolean allowMultipleSelection, MediaType mediaType) { - } - - @Override - public void requestMediaPickFromMediaLibrary(MediaSelectedCallback mediaSelectedCallback, Boolean allowMultipleSelection, MediaType mediaType) { - List<RNMedia> rnMediaList = new ArrayList<>(); - WritableNativeMap emptyMetadata = new WritableNativeMap(); - - switch (mediaType) { - case IMAGE: - Media image = new Media(1, "https://cldup.com/cXyG__fTLN.jpg", "image", "Mountain", "", "A snow-capped mountain top in a cloudy sky with red-leafed trees in the foreground", emptyMetadata); - rnMediaList.add(image); - break; - case VIDEO: - WritableNativeMap metadata = new WritableNativeMap(); - metadata.putString("extraID", "AbCdE"); - Media video = new Media(2, "https://i.cloudup.com/YtZFJbuQCE.mov", "video", "Cloudup", "", "", metadata); - rnMediaList.add(video); - break; - case ANY: - case OTHER: - Media other = new Media(3, "https://wordpress.org/latest.zip", "zip", "WordPress latest version", "WordPress.zip", "", emptyMetadata); - rnMediaList.add(other); - break; - case AUDIO: - Media audio = new Media(5, "https://cldup.com/59IrU0WJtq.mp3", "audio", "Summer presto", "", "", emptyMetadata); - rnMediaList.add(audio); - break; - } - mediaSelectedCallback.onMediaFileSelected(rnMediaList); - } - - - @Override - public void mediaUploadSync(MediaSelectedCallback mediaSelectedCallback) { - } - - @Override - public void mediaSaveSync(MediaSelectedCallback mediaSelectedCallback) { - } - - @Override - public void requestImageFailedRetryDialog(int mediaId) { - } - - @Override - public void requestImageUploadCancelDialog(int mediaId) { - } - - @Override - public void requestImageUploadCancel(int mediaId) { - } - - @Override - public void setFeaturedImage(int mediaId) { - } - - @Override - public void editorDidMount(ReadableArray unsupportedBlockNames) { - } - - @Override - public void editorDidAutosave() { - } - - @Override - public void getOtherMediaPickerOptions(OtherMediaOptionsReceivedCallback otherMediaOptionsReceivedCallback, MediaType mediaType) { - if (mediaType == MediaType.ANY) { - ArrayList<MediaOption> mediaOptions = new ArrayList<>(); - mediaOptions.add(new MediaOption("1", "Choose from device")); - otherMediaOptionsReceivedCallback.onOtherMediaOptionsReceived(mediaOptions); - } - } - - @Override - public void requestMediaPickFrom(String mediaSource, MediaSelectedCallback mediaSelectedCallback, Boolean allowMultipleSelection) { - if (mediaSource.equals("1")) { - List<RNMedia> rnMediaList = new ArrayList<>(); - Media pdf = new Media(1, "https://grad.illinois.edu/sites/default/files/pdfs/cvsamples.pdf", "other", "","cvsamples.pdf", "", new WritableNativeMap()); - rnMediaList.add(pdf); - mediaSelectedCallback.onMediaFileSelected(rnMediaList); - } - } - - @Override - public void requestImageFullscreenPreview(String mediaUrl) { - - } - - @Override - public void requestEmbedFullscreenPreview(String content, String title) { - - } - - @Override - public void requestMediaEditor(MediaSelectedCallback mediaSelectedCallback, String mediaUrl) { - - } - - @Override - public void setFocalPointPickerTooltipShown(boolean tooltipShown) { - } - - @Override - public void requestFocalPointPickerTooltipShown(FocalPointPickerTooltipShownCallback focalPointPickerTooltipShownCallback) { - focalPointPickerTooltipShownCallback.onRequestFocalPointPickerTooltipShown(false); - } - - @Override - public void editorDidEmitLog(String message, LogLevel logLevel) { - switch (logLevel) { - case TRACE: - Log.d(TAG, message); - break; - case INFO: - Log.i(TAG, message); - break; - case WARN: - Log.w(TAG, message); - break; - case ERROR: - Log.e(TAG, message); - break; - } - } - - @Override - public void performGetRequest(String path, boolean enableCaching, Consumer<String> onSuccess, Consumer<Bundle> onError) {} - - @Override - public void performPostRequest(String path, ReadableMap data, Consumer<String> onSuccess, Consumer<Bundle> onError) {} - - @Override - public void gutenbergDidRequestUnsupportedBlockFallback(ReplaceUnsupportedBlockCallback replaceUnsupportedBlockCallback, - String content, - String blockId, - String blockName, - String blockTitle) { - mReplaceUnsupportedBlockCallback = replaceUnsupportedBlockCallback; - openGutenbergWebView(content, blockId, blockTitle); - } - - @Override - public void onShowUserSuggestions(Consumer<String> onResult) { - onResult.accept("matt"); - } - - @Override - public void onShowXpostSuggestions(Consumer<String> onResult) { - onResult.accept("ma.tt"); - } - - @Override - public void requestMediaFilesEditorLoad( - ReadableArray mediaFiles, - String blockId - ) { - Toast.makeText(MainApplication.this, "requestMediaFilesEditorLoad called", Toast.LENGTH_SHORT).show(); - } - - @Override - public void requestMediaFilesFailedRetryDialog(ReadableArray mediaFiles) { - Toast.makeText(MainApplication.this, "requestMediaFilesFailedRetryDialog called", Toast.LENGTH_SHORT).show(); - } - - @Override - public void requestMediaFilesUploadCancelDialog(ReadableArray mediaFiles) { - Toast.makeText(MainApplication.this, "requestMediaFilesUploadCancelDialog called", Toast.LENGTH_SHORT).show(); - } - - @Override - public void requestMediaFilesSaveCancelDialog(ReadableArray mediaFiles) { - Toast.makeText(MainApplication.this, "requestMediaFilesSaveCancelDialog called", Toast.LENGTH_SHORT).show(); - } - - @Override - public void mediaFilesBlockReplaceSync( - ReadableArray mediaFiles, - String blockId - ) { - Toast.makeText(MainApplication.this, "mediaFilesBlockReplaceSync called", Toast.LENGTH_SHORT).show(); - } - - @Override - public void gutenbergDidSendButtonPressedAction(String buttonType) { - - } - - @Override - public void requestPreview() { - Toast.makeText(MainApplication.this, "requestPreview called", Toast.LENGTH_SHORT).show(); - } - - @Override - public void requestBlockTypeImpressions(BlockTypeImpressionsCallback blockTypeImpressionsCallback) { - ReadableMap impressions = Arguments.createMap(); - blockTypeImpressionsCallback.onRequestBlockTypeImpressions(impressions); - } - - @Override - public void setBlockTypeImpressions(ReadableMap impressions) { - Log.d("BlockTypeImpressions", String.format("Gutenberg requested setting block type impression to %s.", impressions)); - } - - @Override - public void requestContactCustomerSupport() { - Toast.makeText(MainApplication.this, "requestContactCustomerSupport called", Toast.LENGTH_SHORT).show(); - } - - @Override - public void requestGotoCustomerSupportOptions() { - Toast.makeText(MainApplication.this, "requestGotoCustomerSupportOptions called", Toast.LENGTH_SHORT).show(); - } - - @Override - public void sendEventToHost(final String eventName, final ReadableMap properties) { - Log.d("SendEventToHost", String.format("Gutenberg requested sending '%s' event to host with properties: %s", eventName, properties)); - } - - @Override - public void toggleUndoButton(boolean isDisabled) { - MainActivity mainActivity = MainActivity.getInstance(); - if (mainActivity != null) { - mainActivity.updateUndoItem(isDisabled); - } - } - - @Override - public void toggleRedoButton(boolean isDisabled) { - MainActivity mainActivity = MainActivity.getInstance(); - if (mainActivity != null) { - mainActivity.updateRedoItem(isDisabled); - } - } - - @Override - public void requestConnectionStatus(ConnectionStatusCallback connectionStatusCallback) { - connectionStatusCallback.onRequestConnectionStatus(true); - } - }, isDarkMode()); - - return new DefaultReactNativeHost(this) { - @Override - public boolean getUseDeveloperSupport() { - return BuildConfig.DEBUG; - } - - @Override - protected List<ReactPackage> getPackages() { - return Arrays.asList( - new MainReactPackage(), - new ReactSliderPackage(), - new ReactVideoPackage(), - new SvgPackage(), - // passing null because we do not need log handlers in the demo app - new ReactAztecPackage(null, null), - new LinearGradientPackage(), - new RNGetRandomValuesPackage(), - new RNCMaskedViewPackage(), - new RNGestureHandlerPackage(), - new ReanimatedPackage(), - new SafeAreaContextPackage(), - new RNScreensPackage(), - new RNCWebViewPackage(), - new ClipboardPackage(), - new FastImageViewPackage(), - mRnReactNativeGutenbergBridgePackage); - } - - @Override - protected String getJSMainModuleName() { - return "index"; - } - - @Override - protected boolean isNewArchEnabled() { - return BuildConfig.IS_NEW_ARCHITECTURE_ENABLED; - } - @Override - protected Boolean isHermesEnabled() { - return BuildConfig.IS_HERMES_ENABLED; - } - }; - } - - private boolean isDarkMode() { - Configuration configuration = getResources().getConfiguration(); - int currentNightMode = configuration.uiMode & Configuration.UI_MODE_NIGHT_MASK; - - return currentNightMode == Configuration.UI_MODE_NIGHT_YES; - } - - public void toggleUndo() { - mRnReactNativeGutenbergBridgePackage.getRNReactNativeGutenbergBridgeModule().onUndoPressed(); - } - - public void toggleRedo() { - mRnReactNativeGutenbergBridgePackage.getRNReactNativeGutenbergBridgeModule().onRedoPressed(); - } - - private void openGutenbergWebView(String content, - String blockId, - String blockName) { - Intent intent = new Intent(this, GutenbergWebViewActivity.class); - intent.putExtra(GutenbergWebViewActivity.ARG_BLOCK_CONTENT, content); - intent.putExtra(GutenbergWebViewActivity.ARG_BLOCK_ID, blockId); - intent.putExtra(GutenbergWebViewActivity.ARG_BLOCK_NAME, blockName); - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - startActivity(intent); - } - - @Override - public ReactNativeHost getReactNativeHost() { - if (mReactNativeHost == null) { - mReactNativeHost = createReactNativeHost(); - createCustomDevOptions(mReactNativeHost); - } - - return mReactNativeHost; - } - - @Override - public void onCreate() { - super.onCreate(); - SoLoader.init(this, /* native exopackage */ false); - if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) { - // If you opted-in for the New Architecture, we load the native entry point for this app. - DefaultNewArchitectureEntryPoint.load(); - } - ReactNativeFlipper.initializeFlipper(this, getReactNativeHost().getReactInstanceManager()); - } - - private void createCustomDevOptions(ReactNativeHost reactNativeHost) { - DevSupportManager devSupportManager = reactNativeHost.getReactInstanceManager().getDevSupportManager(); - - devSupportManager.addCustomDevOption("Show html", new DevOptionHandler() { - @Override - public void onOptionSelected() { - mRnReactNativeGutenbergBridgePackage.getRNReactNativeGutenbergBridgeModule().toggleEditorMode(); - } - }); - - devSupportManager.addCustomDevOption("Help", new DevOptionHandler() { - @Override - public void onOptionSelected() { - mRnReactNativeGutenbergBridgePackage.getRNReactNativeGutenbergBridgeModule().showEditorHelp(); - } - }); - } - - @Override - public void saveContent(String content, String blockId) { - if (mReplaceUnsupportedBlockCallback != null) { - mReplaceUnsupportedBlockCallback.replaceUnsupportedBlock(content, blockId); - } - } -} diff --git a/packages/react-native-editor/android/app/src/main/java/com/gutenberg/MainApplication.kt b/packages/react-native-editor/android/app/src/main/java/com/gutenberg/MainApplication.kt new file mode 100644 index 00000000000000..f1a86917b23867 --- /dev/null +++ b/packages/react-native-editor/android/app/src/main/java/com/gutenberg/MainApplication.kt @@ -0,0 +1,309 @@ +package com.gutenberg + +import android.app.Application +import android.content.Intent +import android.content.res.Configuration +import android.os.Bundle +import android.util.Log +import android.widget.Toast +import androidx.core.util.Consumer +import com.BV.LinearGradient.LinearGradientPackage +import com.brentvatne.react.ReactVideoPackage +import com.dylanvann.fastimage.FastImageViewPackage +import com.facebook.react.ReactApplication +import com.facebook.react.ReactNativeHost +import com.facebook.react.ReactPackage +import com.facebook.react.PackageList +import com.facebook.react.ReactHost +import com.facebook.react.bridge.Arguments +import com.facebook.react.bridge.ReadableArray +import com.facebook.react.bridge.ReadableMap +import com.facebook.react.bridge.WritableNativeMap +import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.load +import com.facebook.react.defaults.DefaultReactHost +import com.facebook.react.defaults.DefaultReactNativeHost +import com.facebook.react.flipper.ReactNativeFlipper +import com.facebook.soloader.SoLoader +import com.horcrux.svg.SvgPackage +import com.reactnativecommunity.clipboard.ClipboardPackage +import com.reactnativecommunity.slider.ReactSliderPackage +import com.reactnativecommunity.webview.RNCWebViewPackage +import com.swmansion.gesturehandler.RNGestureHandlerPackage +import com.swmansion.reanimated.ReanimatedPackage +import com.swmansion.rnscreens.RNScreensPackage +import com.th3rdwave.safeareacontext.SafeAreaContextPackage +import org.linusu.RNGetRandomValuesPackage +import org.reactnative.maskedview.RNCMaskedViewPackage +import org.wordpress.mobile.ReactNativeAztec.ReactAztecPackage +import org.wordpress.mobile.ReactNativeGutenbergBridge.GutenbergBridgeInterface +import org.wordpress.mobile.ReactNativeGutenbergBridge.GutenbergBridgeJS2Parent +import org.wordpress.mobile.ReactNativeGutenbergBridge.GutenbergBridgeJS2Parent.BlockTypeImpressionsCallback +import org.wordpress.mobile.ReactNativeGutenbergBridge.GutenbergBridgeJS2Parent.ConnectionStatusCallback +import org.wordpress.mobile.ReactNativeGutenbergBridge.GutenbergBridgeJS2Parent.FocalPointPickerTooltipShownCallback +import org.wordpress.mobile.ReactNativeGutenbergBridge.GutenbergBridgeJS2Parent.MediaSelectedCallback +import org.wordpress.mobile.ReactNativeGutenbergBridge.GutenbergBridgeJS2Parent.OtherMediaOptionsReceivedCallback +import org.wordpress.mobile.ReactNativeGutenbergBridge.GutenbergBridgeJS2Parent.ReplaceUnsupportedBlockCallback +import org.wordpress.mobile.ReactNativeGutenbergBridge.GutenbergWebViewActivity +import org.wordpress.mobile.ReactNativeGutenbergBridge.RNMedia +import org.wordpress.mobile.ReactNativeGutenbergBridge.RNReactNativeGutenbergBridgePackage +import org.wordpress.mobile.WPAndroidGlue.Media +import org.wordpress.mobile.WPAndroidGlue.MediaOption + +class MainApplication : Application(), ReactApplication, GutenbergBridgeInterface { + private var mRnReactNativeGutenbergBridgePackage: RNReactNativeGutenbergBridgePackage? = null + private var mReplaceUnsupportedBlockCallback: ReplaceUnsupportedBlockCallback? = null + + override val reactNativeHost: ReactNativeHost = + object : DefaultReactNativeHost(this) { + override fun getPackages(): List<ReactPackage> = + PackageList(this).packages.apply { + add(ReactSliderPackage()) + add(ReactVideoPackage()) + add(SvgPackage()) // passing null because we do not need log handlers in the demo ap)p + add(ReactAztecPackage(null, null)) + add(LinearGradientPackage()) + add(RNGetRandomValuesPackage()) + add(RNCMaskedViewPackage()) + add(RNGestureHandlerPackage()) + add(ReanimatedPackage()) + add(SafeAreaContextPackage()) + add(RNScreensPackage()) + add(RNCWebViewPackage()) + add(ClipboardPackage()) + add(FastImageViewPackage()) + add(mRnReactNativeGutenbergBridgePackage) + } + + override fun getJSMainModuleName(): String { + return "index" + } + + override fun getUseDeveloperSupport(): Boolean = BuildConfig.DEBUG + + override val isNewArchEnabled: Boolean = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED + override val isHermesEnabled: Boolean = BuildConfig.IS_HERMES_ENABLED + } + + private fun initializeGutenbergBridge() { + mRnReactNativeGutenbergBridgePackage = RNReactNativeGutenbergBridgePackage(object : GutenbergBridgeJS2Parent { + override fun responseHtml(title: String, html: String, changed: Boolean, contentInfo: ReadableMap) {} + override fun requestMediaImport(url: String, mediaSelectedCallback: MediaSelectedCallback) {} + override fun requestMediaPickerFromDeviceCamera(mediaSelectedCallback: MediaSelectedCallback, mediaType: GutenbergBridgeJS2Parent.MediaType) {} + override fun requestMediaPickFromDeviceLibrary(mediaSelectedCallback: MediaSelectedCallback, allowMultipleSelection: Boolean, mediaType: GutenbergBridgeJS2Parent.MediaType) {} + override fun requestMediaPickFromMediaLibrary(mediaSelectedCallback: MediaSelectedCallback, allowMultipleSelection: Boolean, mediaType: GutenbergBridgeJS2Parent.MediaType) { + val rnMediaList: MutableList<RNMedia> = ArrayList() + val emptyMetadata = WritableNativeMap() + when (mediaType) { + GutenbergBridgeJS2Parent.MediaType.IMAGE -> { + val image = Media(1, "https://cldup.com/cXyG__fTLN.jpg", "image", "Mountain", "", "A snow-capped mountain top in a cloudy sky with red-leafed trees in the foreground", emptyMetadata) + rnMediaList.add(image) + } + + GutenbergBridgeJS2Parent.MediaType.VIDEO -> { + val metadata = WritableNativeMap() + metadata.putString("extraID", "AbCdE") + val video = Media(2, "https://i.cloudup.com/YtZFJbuQCE.mov", "video", "Cloudup", "", "", metadata) + rnMediaList.add(video) + } + + GutenbergBridgeJS2Parent.MediaType.ANY, GutenbergBridgeJS2Parent.MediaType.OTHER -> { + val other = Media(3, "https://wordpress.org/latest.zip", "zip", "WordPress latest version", "WordPress.zip", "", emptyMetadata) + rnMediaList.add(other) + } + + GutenbergBridgeJS2Parent.MediaType.AUDIO -> { + val audio = Media(5, "https://cldup.com/59IrU0WJtq.mp3", "audio", "Summer presto", "", "", emptyMetadata) + rnMediaList.add(audio) + } + + else -> {} + } + mediaSelectedCallback.onMediaFileSelected(rnMediaList) + } + + override fun mediaUploadSync(mediaSelectedCallback: MediaSelectedCallback) {} + override fun mediaSaveSync(mediaSelectedCallback: MediaSelectedCallback) {} + override fun requestImageFailedRetryDialog(mediaId: Int) {} + override fun requestImageUploadCancelDialog(mediaId: Int) {} + override fun requestImageUploadCancel(mediaId: Int) {} + override fun setFeaturedImage(mediaId: Int) {} + override fun editorDidMount(unsupportedBlockNames: ReadableArray) {} + override fun editorDidAutosave() {} + override fun getOtherMediaPickerOptions(otherMediaOptionsReceivedCallback: OtherMediaOptionsReceivedCallback, mediaType: GutenbergBridgeJS2Parent.MediaType) { + if (mediaType == GutenbergBridgeJS2Parent.MediaType.ANY) { + val mediaOptions = ArrayList<MediaOption>() + mediaOptions.add(MediaOption("1", "Choose from device")) + otherMediaOptionsReceivedCallback.onOtherMediaOptionsReceived(mediaOptions) + } + } + + override fun requestMediaPickFrom(mediaSource: String, mediaSelectedCallback: MediaSelectedCallback, allowMultipleSelection: Boolean) { + if (mediaSource == "1") { + val rnMediaList: MutableList<RNMedia> = ArrayList() + val pdf = Media(1, "https://grad.illinois.edu/sites/default/files/pdfs/cvsamples.pdf", "other", "", "cvsamples.pdf", "", WritableNativeMap()) + rnMediaList.add(pdf) + mediaSelectedCallback.onMediaFileSelected(rnMediaList) + } + } + + override fun requestImageFullscreenPreview(mediaUrl: String) {} + override fun requestEmbedFullscreenPreview(content: String, title: String) {} + override fun requestMediaEditor(mediaSelectedCallback: MediaSelectedCallback, mediaUrl: String) {} + override fun setFocalPointPickerTooltipShown(tooltipShown: Boolean) {} + override fun requestFocalPointPickerTooltipShown(focalPointPickerTooltipShownCallback: FocalPointPickerTooltipShownCallback) { + focalPointPickerTooltipShownCallback.onRequestFocalPointPickerTooltipShown(false) + } + + override fun editorDidEmitLog(message: String, logLevel: GutenbergBridgeJS2Parent.LogLevel) { + when (logLevel) { + GutenbergBridgeJS2Parent.LogLevel.TRACE -> Log.d(TAG, message) + GutenbergBridgeJS2Parent.LogLevel.INFO -> Log.i(TAG, message) + GutenbergBridgeJS2Parent.LogLevel.WARN -> Log.w(TAG, message) + GutenbergBridgeJS2Parent.LogLevel.ERROR -> Log.e(TAG, message) + } + } + + override fun performGetRequest(path: String, enableCaching: Boolean, onSuccess: Consumer<String>, onError: Consumer<Bundle>) {} + override fun performPostRequest(path: String, data: ReadableMap, onSuccess: Consumer<String>, onError: Consumer<Bundle>) {} + override fun gutenbergDidRequestUnsupportedBlockFallback(replaceUnsupportedBlockCallback: ReplaceUnsupportedBlockCallback, + content: String, + blockId: String, + blockName: String, + blockTitle: String) { + mReplaceUnsupportedBlockCallback = replaceUnsupportedBlockCallback + openGutenbergWebView(content, blockId, blockTitle) + } + + override fun onShowUserSuggestions(onResult: Consumer<String>) { + onResult.accept("matt") + } + + override fun onShowXpostSuggestions(onResult: Consumer<String>) { + onResult.accept("ma.tt") + } + + override fun requestMediaFilesEditorLoad( + mediaFiles: ReadableArray, + blockId: String + ) { + Toast.makeText(this@MainApplication, "requestMediaFilesEditorLoad called", Toast.LENGTH_SHORT).show() + } + + override fun requestMediaFilesFailedRetryDialog(mediaFiles: ReadableArray) { + Toast.makeText(this@MainApplication, "requestMediaFilesFailedRetryDialog called", Toast.LENGTH_SHORT).show() + } + + override fun requestMediaFilesUploadCancelDialog(mediaFiles: ReadableArray) { + Toast.makeText(this@MainApplication, "requestMediaFilesUploadCancelDialog called", Toast.LENGTH_SHORT).show() + } + + override fun requestMediaFilesSaveCancelDialog(mediaFiles: ReadableArray) { + Toast.makeText(this@MainApplication, "requestMediaFilesSaveCancelDialog called", Toast.LENGTH_SHORT).show() + } + + override fun mediaFilesBlockReplaceSync( + mediaFiles: ReadableArray, + blockId: String + ) { + Toast.makeText(this@MainApplication, "mediaFilesBlockReplaceSync called", Toast.LENGTH_SHORT).show() + } + + override fun gutenbergDidSendButtonPressedAction(buttonType: String) {} + override fun requestPreview() { + Toast.makeText(this@MainApplication, "requestPreview called", Toast.LENGTH_SHORT).show() + } + + override fun requestBlockTypeImpressions(blockTypeImpressionsCallback: BlockTypeImpressionsCallback) { + val impressions: ReadableMap = Arguments.createMap() + blockTypeImpressionsCallback.onRequestBlockTypeImpressions(impressions) + } + + override fun setBlockTypeImpressions(impressions: ReadableMap) { + Log.d("BlockTypeImpressions", String.format("Gutenberg requested setting block type impression to %s.", impressions)) + } + + override fun requestContactCustomerSupport() { + Toast.makeText(this@MainApplication, "requestContactCustomerSupport called", Toast.LENGTH_SHORT).show() + } + + override fun requestGotoCustomerSupportOptions() { + Toast.makeText(this@MainApplication, "requestGotoCustomerSupportOptions called", Toast.LENGTH_SHORT).show() + } + + override fun sendEventToHost(eventName: String, properties: ReadableMap) { + Log.d("SendEventToHost", String.format("Gutenberg requested sending '%s' event to host with properties: %s", eventName, properties)) + } + + override fun toggleUndoButton(isDisabled: Boolean) { + MainActivity.instance?.updateUndoItem(isDisabled) + } + + override fun toggleRedoButton(isDisabled: Boolean) { + MainActivity.instance?.updateRedoItem(isDisabled) + } + + override fun requestConnectionStatus(connectionStatusCallback: ConnectionStatusCallback) { + connectionStatusCallback.onRequestConnectionStatus(true) + } + }, isDarkMode) + } + + private val isDarkMode: Boolean + get() { + val configuration = resources.configuration + val currentNightMode = configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK + return currentNightMode == Configuration.UI_MODE_NIGHT_YES + } + + fun toggleUndo() { + mRnReactNativeGutenbergBridgePackage!!.rnReactNativeGutenbergBridgeModule.onUndoPressed() + } + + fun toggleRedo() { + mRnReactNativeGutenbergBridgePackage!!.rnReactNativeGutenbergBridgeModule.onRedoPressed() + } + + private fun openGutenbergWebView(content: String, + blockId: String, + blockName: String) { + val intent = Intent(this, GutenbergWebViewActivity::class.java) + intent.putExtra(GutenbergWebViewActivity.ARG_BLOCK_CONTENT, content) + intent.putExtra(GutenbergWebViewActivity.ARG_BLOCK_ID, blockId) + intent.putExtra(GutenbergWebViewActivity.ARG_BLOCK_NAME, blockName) + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + startActivity(intent) + } + + override val reactHost: ReactHost + get() = DefaultReactHost.getDefaultReactHost(this.applicationContext, reactNativeHost) + + override fun onCreate() { + super.onCreate() + SoLoader.init(this, /* native exopackage */false) + + initializeGutenbergBridge() + + if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) { + // If you opted-in for the New Architecture, we load the native entry point for this app. + load() + } + ReactNativeFlipper.initializeFlipper(this, reactNativeHost.reactInstanceManager) + createCustomDevOptions(reactNativeHost) + } + + private fun createCustomDevOptions(reactNativeHost: ReactNativeHost?) { + val devSupportManager = reactNativeHost!!.reactInstanceManager.devSupportManager + devSupportManager.addCustomDevOption("Show html") { mRnReactNativeGutenbergBridgePackage!!.rnReactNativeGutenbergBridgeModule.toggleEditorMode() } + devSupportManager.addCustomDevOption("Help") { mRnReactNativeGutenbergBridgePackage!!.rnReactNativeGutenbergBridgeModule.showEditorHelp() } + } + + override fun saveContent(content: String, blockId: String) { + if (mReplaceUnsupportedBlockCallback != null) { + mReplaceUnsupportedBlockCallback!!.replaceUnsupportedBlock(content, blockId) + } + } + + companion object { + private const val TAG = "MainApplication" + } +}