From 1f0391cb68c7dc9481f5621c0bc9f2fa30c25e4e Mon Sep 17 00:00:00 2001 From: Satyajit Sahoo Date: Fri, 15 Jan 2016 03:30:16 +0530 Subject: [PATCH 1/5] Initial implementation of Modal on Android --- Libraries/Modal/Modal.js | 2 - .../react/shell/MainReactPackage.java | 10 +-- .../react/views/modalhost/DismissEvent.java | 26 +++++++ .../modalhost/ReactModalHostManager.java | 68 +++++++++++++++++++ .../views/modalhost/ReactModalHostView.java | 67 ++++++++++++++++++ 5 files changed, 167 insertions(+), 6 deletions(-) create mode 100644 ReactAndroid/src/main/java/com/facebook/react/views/modalhost/DismissEvent.java create mode 100644 ReactAndroid/src/main/java/com/facebook/react/views/modalhost/ReactModalHostManager.java create mode 100644 ReactAndroid/src/main/java/com/facebook/react/views/modalhost/ReactModalHostView.java diff --git a/Libraries/Modal/Modal.js b/Libraries/Modal/Modal.js index 9df6208d7860..dfbfdb685542 100644 --- a/Libraries/Modal/Modal.js +++ b/Libraries/Modal/Modal.js @@ -31,8 +31,6 @@ var RCTModalHostView = requireNativeComponent('RCTModalHostView', null); * Navigator instead of Modal. With a top-level Navigator, you have more control * over how to present the modal scene over the rest of your app by using the * configureScene property. - * - * This component is only available in iOS at this time. */ class Modal extends React.Component { render(): ?ReactElement { diff --git a/ReactAndroid/src/main/java/com/facebook/react/shell/MainReactPackage.java b/ReactAndroid/src/main/java/com/facebook/react/shell/MainReactPackage.java index 6d98ea60a65b..0034cd544d68 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/shell/MainReactPackage.java +++ b/ReactAndroid/src/main/java/com/facebook/react/shell/MainReactPackage.java @@ -18,6 +18,7 @@ import com.facebook.react.bridge.NativeModule; import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.modules.camera.CameraRollManager; +import com.facebook.react.modules.clipboard.ClipboardModule; import com.facebook.react.modules.dialog.DialogModule; import com.facebook.react.modules.fresco.FrescoModule; import com.facebook.react.modules.intent.IntentModule; @@ -32,22 +33,22 @@ import com.facebook.react.views.art.ARTSurfaceViewManager; import com.facebook.react.views.drawer.ReactDrawerLayoutManager; import com.facebook.react.views.image.ReactImageManager; +import com.facebook.react.views.modalhost.ReactModalHostManager; import com.facebook.react.views.progressbar.ReactProgressBarViewManager; import com.facebook.react.views.recyclerview.RecyclerViewBackedScrollViewManager; import com.facebook.react.views.scroll.ReactHorizontalScrollViewManager; import com.facebook.react.views.scroll.ReactScrollViewManager; +import com.facebook.react.views.swiperefresh.SwipeRefreshLayoutManager; import com.facebook.react.views.switchview.ReactSwitchManager; import com.facebook.react.views.text.ReactRawTextManager; -import com.facebook.react.views.text.ReactTextViewManager; import com.facebook.react.views.text.ReactTextInlineImageViewManager; +import com.facebook.react.views.text.ReactTextViewManager; import com.facebook.react.views.text.ReactVirtualTextViewManager; import com.facebook.react.views.textinput.ReactTextInputManager; import com.facebook.react.views.toolbar.ReactToolbarManager; import com.facebook.react.views.view.ReactViewManager; import com.facebook.react.views.viewpager.ReactViewPagerManager; -import com.facebook.react.views.swiperefresh.SwipeRefreshLayoutManager; import com.facebook.react.views.webview.ReactWebViewManager; -import com.facebook.react.modules.clipboard.ClipboardModule; /** * Package defining basic modules and view managers. @@ -98,6 +99,7 @@ public List createViewManagers(ReactApplicationContext reactContext new ReactTextInlineImageViewManager(), new ReactVirtualTextViewManager(), new SwipeRefreshLayoutManager(), - new ReactWebViewManager()); + new ReactWebViewManager(), + new ReactModalHostManager()); } } diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/modalhost/DismissEvent.java b/ReactAndroid/src/main/java/com/facebook/react/views/modalhost/DismissEvent.java new file mode 100644 index 000000000000..4992efe948e3 --- /dev/null +++ b/ReactAndroid/src/main/java/com/facebook/react/views/modalhost/DismissEvent.java @@ -0,0 +1,26 @@ +package com.facebook.react.views.modalhost; + +import com.facebook.react.uimanager.events.Event; +import com.facebook.react.uimanager.events.RCTEventEmitter; + +/** + * {@link Event} for dismissing a Dialog. + */ +public class DismissEvent extends Event { + + public static final String EVENT_NAME = "topDismiss"; + + protected DismissEvent(int viewTag, long timestampMs) { + super(viewTag, timestampMs); + } + + @Override + public String getEventName() { + return EVENT_NAME; + } + + @Override + public void dispatch(RCTEventEmitter rctEventEmitter) { + rctEventEmitter.receiveEvent(getViewTag(), getEventName(), null); + } +} diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/modalhost/ReactModalHostManager.java b/ReactAndroid/src/main/java/com/facebook/react/views/modalhost/ReactModalHostManager.java new file mode 100644 index 000000000000..7a24c9ba6838 --- /dev/null +++ b/ReactAndroid/src/main/java/com/facebook/react/views/modalhost/ReactModalHostManager.java @@ -0,0 +1,68 @@ +// Copyright 2004-present Facebook. All Rights Reserved. + +package com.facebook.react.views.modalhost; + +import android.content.DialogInterface; +import android.os.SystemClock; + +import com.facebook.react.common.MapBuilder; +import com.facebook.react.uimanager.ThemedReactContext; +import com.facebook.react.uimanager.UIManagerModule; +import com.facebook.react.uimanager.ViewGroupManager; +import com.facebook.react.uimanager.annotations.ReactProp; + +import java.util.Map; + +/** + * View manager for {@link ReactModalHostView} components. + * + * Emits an {@code onDismiss} event when the Dialog host is dismissed. + * + * TODO(8776300): Refactor this class to use @ReactProp + */ +public class ReactModalHostManager extends ViewGroupManager { + + private static final String REACT_CLASS = "RCTModalHostView"; + + @ReactProp(name = "animated") + public void setAnimated(ReactModalHostView view, boolean animated) { + // TODO(8776300): Implement this + } + + @ReactProp(name = "transparent") + public void setTransparent(ReactModalHostView view, boolean transparent) { + // TODO(8776300): Implement this + } + + @Override + public String getName() { + return REACT_CLASS; + } + + @Override + protected ReactModalHostView createViewInstance(ThemedReactContext reactContext) { + return new ReactModalHostView(reactContext); + } + + @Override + protected void addEventEmitters( + final ThemedReactContext reactContext, + final ReactModalHostView view) { + view.setOnDismissListener( + new DialogInterface.OnDismissListener() { + @Override + public void onDismiss(DialogInterface dialog) { + reactContext.getNativeModule(UIManagerModule.class) + .getEventDispatcher() + .dispatchEvent(new DismissEvent(view.getId(), SystemClock.uptimeMillis())); + } + }); + } + + @Override + public Map getExportedCustomDirectEventTypeConstants() { + return MapBuilder.builder() + .put(DismissEvent.EVENT_NAME, MapBuilder.of("registrationName", "onDismiss")) + .build(); + } +} diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/modalhost/ReactModalHostView.java b/ReactAndroid/src/main/java/com/facebook/react/views/modalhost/ReactModalHostView.java new file mode 100644 index 000000000000..5bc5f215708e --- /dev/null +++ b/ReactAndroid/src/main/java/com/facebook/react/views/modalhost/ReactModalHostView.java @@ -0,0 +1,67 @@ +package com.facebook.react.views.modalhost; + +import android.app.Dialog; +import android.content.Context; +import android.content.DialogInterface; +import android.view.View; +import android.view.ViewGroup; +import android.view.WindowManager; + +import com.facebook.react.R; +import com.facebook.react.common.annotations.VisibleForTesting; +import com.facebook.react.views.view.ReactViewGroup; + +/** + * A Modal view that works as a base ViewGroup to host other views. + */ +public class ReactModalHostView extends ViewGroup { + + private ReactViewGroup mHostView; + private Dialog mDialog; + + public ReactModalHostView(Context context) { + super(context); + + mHostView = new ReactViewGroup(context); + + mDialog = new Dialog(context, R.style.Theme_ReactNative_AppCompat_Light_NoActionBar_FullScreen); + mDialog.setContentView(mHostView); + mDialog.show(); + mDialog.getWindow().setLayout( + WindowManager.LayoutParams.MATCH_PARENT, + WindowManager.LayoutParams.MATCH_PARENT); + } + + @Override + protected void onLayout(boolean changed, int l, int t, int r, int b) { + } + + @Override + public void addView(View child, int index) { + mHostView.addView(child, index); + } + + @Override + public int getChildCount() { + return mHostView.getChildCount(); + } + + @Override + public View getChildAt(int index) { + return mHostView.getChildAt(index); + } + + @Override + public void removeView(View child) { + mHostView.removeView(child); + } + + /*package*/ void setOnDismissListener(DialogInterface.OnDismissListener listener) { + mDialog.setOnDismissListener(listener); + } + + @VisibleForTesting + public Dialog getDialog() { + return mDialog; + } +} From 750c7f13b190d4005d1c7978010fe71ddc05027d Mon Sep 17 00:00:00 2001 From: Satyajit Sahoo Date: Fri, 15 Jan 2016 03:18:29 +0530 Subject: [PATCH 2/5] Implement 'transparent' prop for --- .../react/views/modalhost/ReactModalHostManager.java | 10 +++++++--- .../react/views/modalhost/ReactModalHostView.java | 9 +++++++-- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/modalhost/ReactModalHostManager.java b/ReactAndroid/src/main/java/com/facebook/react/views/modalhost/ReactModalHostManager.java index 7a24c9ba6838..4dcf91ebde38 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/modalhost/ReactModalHostManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/modalhost/ReactModalHostManager.java @@ -3,6 +3,8 @@ package com.facebook.react.views.modalhost; import android.content.DialogInterface; +import android.graphics.Color; +import android.graphics.drawable.ColorDrawable; import android.os.SystemClock; import com.facebook.react.common.MapBuilder; @@ -17,8 +19,6 @@ * View manager for {@link ReactModalHostView} components. * * Emits an {@code onDismiss} event when the Dialog host is dismissed. - * - * TODO(8776300): Refactor this class to use @ReactProp */ public class ReactModalHostManager extends ViewGroupManager { @@ -31,7 +31,11 @@ public void setAnimated(ReactModalHostView view, boolean animated) { @ReactProp(name = "transparent") public void setTransparent(ReactModalHostView view, boolean transparent) { - // TODO(8776300): Implement this + if (transparent) { + view.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT)); + } else { + view.setBackgroundDrawable(new ColorDrawable(Color.WHITE)); + } } @Override diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/modalhost/ReactModalHostView.java b/ReactAndroid/src/main/java/com/facebook/react/views/modalhost/ReactModalHostView.java index 5bc5f215708e..c8f7f2acdd91 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/modalhost/ReactModalHostView.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/modalhost/ReactModalHostView.java @@ -3,11 +3,11 @@ import android.app.Dialog; import android.content.Context; import android.content.DialogInterface; +import android.graphics.drawable.ColorDrawable; import android.view.View; import android.view.ViewGroup; import android.view.WindowManager; -import com.facebook.react.R; import com.facebook.react.common.annotations.VisibleForTesting; import com.facebook.react.views.view.ReactViewGroup; @@ -23,8 +23,9 @@ public ReactModalHostView(Context context) { super(context); mHostView = new ReactViewGroup(context); + mHostView.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)); - mDialog = new Dialog(context, R.style.Theme_ReactNative_AppCompat_Light_NoActionBar_FullScreen); + mDialog = new Dialog(context, android.R.style.Theme_DeviceDefault_Dialog_NoActionBar); mDialog.setContentView(mHostView); mDialog.show(); mDialog.getWindow().setLayout( @@ -32,6 +33,10 @@ public ReactModalHostView(Context context) { WindowManager.LayoutParams.MATCH_PARENT); } + public void setBackgroundDrawable(ColorDrawable color) { + mDialog.getWindow().setBackgroundDrawable(color); + } + @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { } From 0f9427edd9df4e4c8e7b7398eec65b87fa3647ba Mon Sep 17 00:00:00 2001 From: Satyajit Sahoo Date: Fri, 15 Jan 2016 05:25:38 +0530 Subject: [PATCH 3/5] Add license headers --- .../com/facebook/react/views/modalhost/DismissEvent.java | 9 +++++++++ .../react/views/modalhost/ReactModalHostManager.java | 9 ++++++++- .../react/views/modalhost/ReactModalHostView.java | 9 +++++++++ 3 files changed, 26 insertions(+), 1 deletion(-) diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/modalhost/DismissEvent.java b/ReactAndroid/src/main/java/com/facebook/react/views/modalhost/DismissEvent.java index 4992efe948e3..b7111a3d58d2 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/modalhost/DismissEvent.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/modalhost/DismissEvent.java @@ -1,3 +1,12 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + package com.facebook.react.views.modalhost; import com.facebook.react.uimanager.events.Event; diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/modalhost/ReactModalHostManager.java b/ReactAndroid/src/main/java/com/facebook/react/views/modalhost/ReactModalHostManager.java index 4dcf91ebde38..4086a8d9849a 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/modalhost/ReactModalHostManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/modalhost/ReactModalHostManager.java @@ -1,4 +1,11 @@ -// Copyright 2004-present Facebook. All Rights Reserved. +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ package com.facebook.react.views.modalhost; diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/modalhost/ReactModalHostView.java b/ReactAndroid/src/main/java/com/facebook/react/views/modalhost/ReactModalHostView.java index c8f7f2acdd91..d3973bfa1060 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/modalhost/ReactModalHostView.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/modalhost/ReactModalHostView.java @@ -1,3 +1,12 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + package com.facebook.react.views.modalhost; import android.app.Dialog; From e20013e76a93936fd9acc49f7cf4be0e2f8bacf8 Mon Sep 17 00:00:00 2001 From: Satyajit Sahoo Date: Fri, 15 Jan 2016 07:47:28 +0530 Subject: [PATCH 4/5] Fix transparency of modal --- .../views/modalhost/ReactModalHostManager.java | 8 +------- .../react/views/modalhost/ReactModalHostView.java | 13 +++++++++++-- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/modalhost/ReactModalHostManager.java b/ReactAndroid/src/main/java/com/facebook/react/views/modalhost/ReactModalHostManager.java index 4086a8d9849a..5df2d28abd48 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/modalhost/ReactModalHostManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/modalhost/ReactModalHostManager.java @@ -10,8 +10,6 @@ package com.facebook.react.views.modalhost; import android.content.DialogInterface; -import android.graphics.Color; -import android.graphics.drawable.ColorDrawable; import android.os.SystemClock; import com.facebook.react.common.MapBuilder; @@ -38,11 +36,7 @@ public void setAnimated(ReactModalHostView view, boolean animated) { @ReactProp(name = "transparent") public void setTransparent(ReactModalHostView view, boolean transparent) { - if (transparent) { - view.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT)); - } else { - view.setBackgroundDrawable(new ColorDrawable(Color.WHITE)); - } + view.setTransparent(transparent); } @Override diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/modalhost/ReactModalHostView.java b/ReactAndroid/src/main/java/com/facebook/react/views/modalhost/ReactModalHostView.java index d3973bfa1060..8e1c00750232 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/modalhost/ReactModalHostView.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/modalhost/ReactModalHostView.java @@ -12,6 +12,7 @@ import android.app.Dialog; import android.content.Context; import android.content.DialogInterface; +import android.graphics.Color; import android.graphics.drawable.ColorDrawable; import android.view.View; import android.view.ViewGroup; @@ -37,13 +38,21 @@ public ReactModalHostView(Context context) { mDialog = new Dialog(context, android.R.style.Theme_DeviceDefault_Dialog_NoActionBar); mDialog.setContentView(mHostView); mDialog.show(); + mDialog.getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT)); mDialog.getWindow().setLayout( WindowManager.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.MATCH_PARENT); } - public void setBackgroundDrawable(ColorDrawable color) { - mDialog.getWindow().setBackgroundDrawable(color); + public void setTransparent(boolean transparent) { + if (transparent) { + mDialog.getWindow().clearFlags( + WindowManager.LayoutParams.FLAG_DIM_BEHIND); + } else { + mDialog.getWindow().setFlags( + WindowManager.LayoutParams.FLAG_DIM_BEHIND, + WindowManager.LayoutParams.FLAG_DIM_BEHIND); + } } @Override From a16b73161b92fa1cf07a83a30e15a889c3dd1f53 Mon Sep 17 00:00:00 2001 From: Satyajit Sahoo Date: Sat, 16 Jan 2016 04:48:35 +0530 Subject: [PATCH 5/5] Dismiss Modal properly --- .../views/modalhost/ReactModalHostManager.java | 6 ++++++ .../react/views/modalhost/ReactModalHostView.java | 14 ++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/modalhost/ReactModalHostManager.java b/ReactAndroid/src/main/java/com/facebook/react/views/modalhost/ReactModalHostManager.java index 5df2d28abd48..924f2b0bdc7f 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/modalhost/ReactModalHostManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/modalhost/ReactModalHostManager.java @@ -49,6 +49,12 @@ protected ReactModalHostView createViewInstance(ThemedReactContext reactContext) return new ReactModalHostView(reactContext); } + @Override + public void onDropViewInstance(ReactModalHostView view) { + super.onDropViewInstance(view); + view.dismissModal(); + } + @Override protected void addEventEmitters( final ThemedReactContext reactContext, diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/modalhost/ReactModalHostView.java b/ReactAndroid/src/main/java/com/facebook/react/views/modalhost/ReactModalHostView.java index 8e1c00750232..2150ee35a1a2 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/modalhost/ReactModalHostView.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/modalhost/ReactModalHostView.java @@ -79,6 +79,20 @@ public void removeView(View child) { mHostView.removeView(child); } + @Override + public void removeViewAt(int index) { + mHostView.removeViewAt(index); + } + + @Override + public void removeAllViews() { + mHostView.removeAllViews(); + } + + public void dismissModal() { + mDialog.dismiss(); + } + /*package*/ void setOnDismissListener(DialogInterface.OnDismissListener listener) { mDialog.setOnDismissListener(listener); }