From d4636d31302da01a12a711510ebad729d5d7558b Mon Sep 17 00:00:00 2001 From: Krzysztof Magiera Date: Tue, 22 Oct 2019 23:22:48 +0200 Subject: [PATCH] Android native stack bugfixes. (#190) A few bugs fixed with android native stack in this commit: - fixed a problem with views not resizing when keyboard appears, the bug was due to onMeasure forwardin getHeight which was the old height of the container instead of the changed one - fixed a problem with back button behavior not being consistent. Now back button is controlled by 'gestureEnabled' to emulate iOS behavior where there is no hw back button but you can swipe back instead. - added compatibility for "contained" modal styles - for now we always render "containted" fragments on Android, plan to add a way to render in dialogs in the future --- .../java/com/swmansion/rnscreens/Screen.java | 2 - .../com/swmansion/rnscreens/ScreenStack.java | 12 +++- .../rnscreens/ScreenStackFragment.java | 7 +++ .../rnscreens/ScreenStackHeaderConfig.java | 58 ++++++++++++++----- .../ScreenStackHeaderConfigViewManager.java | 5 ++ .../rnscreens/ScreenViewManager.java | 6 +- 6 files changed, 67 insertions(+), 23 deletions(-) diff --git a/android/src/main/java/com/swmansion/rnscreens/Screen.java b/android/src/main/java/com/swmansion/rnscreens/Screen.java index cf89ac4d..9c9074c7 100644 --- a/android/src/main/java/com/swmansion/rnscreens/Screen.java +++ b/android/src/main/java/com/swmansion/rnscreens/Screen.java @@ -2,7 +2,6 @@ package com.swmansion.rnscreens; import android.content.Context; import android.graphics.Paint; -import android.util.Log; import android.view.View; import android.view.ViewGroup; import android.view.inputmethod.InputMethodManager; @@ -13,7 +12,6 @@ import androidx.fragment.app.Fragment; import com.facebook.react.bridge.GuardedRunnable; import com.facebook.react.bridge.ReactContext; -import com.facebook.react.uimanager.MeasureSpecAssertions; import com.facebook.react.uimanager.PointerEvents; import com.facebook.react.uimanager.ReactPointerEventsView; import com.facebook.react.uimanager.UIManagerModule; diff --git a/android/src/main/java/com/swmansion/rnscreens/ScreenStack.java b/android/src/main/java/com/swmansion/rnscreens/ScreenStack.java index 15884fa1..af68298d 100644 --- a/android/src/main/java/com/swmansion/rnscreens/ScreenStack.java +++ b/android/src/main/java/com/swmansion/rnscreens/ScreenStack.java @@ -25,6 +25,10 @@ public class ScreenStack extends ScreenContainer { onUpdate(); } + public Screen getTopScreen() { + return mTopScreen.getScreen(); + } + public Screen getRootScreen() { for (int i = 0, size = getScreenCount(); i < size; i++) { Screen screen = getScreenAt(i); @@ -51,9 +55,7 @@ public class ScreenStack extends ScreenContainer { protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); for (int i = 0, size = getChildCount(); i < size; i++) { - getChildAt(i).measure( - MeasureSpec.makeMeasureSpec(getWidth(), MeasureSpec.EXACTLY), - MeasureSpec.makeMeasureSpec(getHeight(), MeasureSpec.EXACTLY)); + getChildAt(i).measure(widthMeasureSpec, heightMeasureSpec); } } @@ -169,5 +171,9 @@ public class ScreenStack extends ScreenContainer { mStack.addAll(mScreenFragments); tryCommitTransaction(); + + for (ScreenStackFragment screen : mStack) { + screen.onStackUpdate(); + } } } diff --git a/android/src/main/java/com/swmansion/rnscreens/ScreenStackFragment.java b/android/src/main/java/com/swmansion/rnscreens/ScreenStackFragment.java index d8292503..ea6ab79c 100644 --- a/android/src/main/java/com/swmansion/rnscreens/ScreenStackFragment.java +++ b/android/src/main/java/com/swmansion/rnscreens/ScreenStackFragment.java @@ -52,6 +52,13 @@ public class ScreenStackFragment extends ScreenFragment { } } + public void onStackUpdate() { + View child = mScreenView.getChildAt(0); + if (child instanceof ScreenStackHeaderConfig) { + ((ScreenStackHeaderConfig) child).onUpdate(); + } + } + @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, diff --git a/android/src/main/java/com/swmansion/rnscreens/ScreenStackHeaderConfig.java b/android/src/main/java/com/swmansion/rnscreens/ScreenStackHeaderConfig.java index f349f33b..4a6695c4 100644 --- a/android/src/main/java/com/swmansion/rnscreens/ScreenStackHeaderConfig.java +++ b/android/src/main/java/com/swmansion/rnscreens/ScreenStackHeaderConfig.java @@ -1,7 +1,6 @@ package com.swmansion.rnscreens; import android.content.Context; -import android.graphics.Color; import android.graphics.PorterDuff; import android.graphics.drawable.Drawable; import android.util.TypedValue; @@ -29,17 +28,26 @@ public class ScreenStackHeaderConfig extends ViewGroup { private int mTitleFontSize; private int mBackgroundColor; private boolean mIsHidden; + private boolean mGestureEnabled = true; private boolean mIsBackButtonHidden; private boolean mIsShadowHidden; private int mTintColor; private final Toolbar mToolbar; + private boolean mIsAttachedToWindow = false; + private OnBackPressedCallback mBackCallback = new OnBackPressedCallback(false) { @Override public void handleOnBackPressed() { - getScreenStack().dismiss(getScreenFragment()); + ScreenStack stack = getScreenStack(); + Screen current = getScreen(); + if (stack.getTopScreen() == current) { + stack.dismiss(getScreenFragment()); + } + mBackCallback.remove(); } }; + private OnClickListener mBackClickListener = new OnClickListener() { @Override public void onClick(View view) { @@ -68,7 +76,14 @@ public class ScreenStackHeaderConfig extends ViewGroup { @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); - update(); + mIsAttachedToWindow = true; + onUpdate(); + } + + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + mIsAttachedToWindow = false; } private Screen getScreen() { @@ -101,14 +116,27 @@ public class ScreenStackHeaderConfig extends ViewGroup { return null; } - private void installBackCallback() { - mBackCallback.remove(); - Fragment fragment = getScreenFragment(); - fragment.requireActivity().getOnBackPressedDispatcher().addCallback(fragment, mBackCallback); - } - - private void update() { + public void onUpdate() { Screen parent = (Screen) getParent(); + final ScreenStack stack = getScreenStack(); + boolean isRoot = stack == null ? true : stack.getRootScreen() == parent; + boolean isTop = stack == null ? true : stack.getTopScreen() == parent; + + // we need to clean up back handler especially in the case given screen is no longer on top + // because we don't want it to capture back event if it is not responsible for handling it + // as that would block other handlers from running + mBackCallback.remove(); + + if (!mIsAttachedToWindow || !isTop) { + return; + } + + if (!isRoot && isTop && mGestureEnabled) { + Fragment fragment = getScreenFragment(); + fragment.requireActivity().getOnBackPressedDispatcher().addCallback(fragment, mBackCallback); + mBackCallback.setEnabled(true); + } + if (mIsHidden) { if (mToolbar.getParent() != null) { getScreenFragment().removeToolbar(); @@ -125,13 +153,7 @@ public class ScreenStackHeaderConfig extends ViewGroup { ActionBar actionBar = activity.getSupportActionBar(); // hide back button - final ScreenStack stack = getScreenStack(); - boolean isRoot = stack == null ? true : stack.getRootScreen() == parent; actionBar.setDisplayHomeAsUpEnabled(isRoot ? false : !mIsBackButtonHidden); - if (!isRoot) { - installBackCallback(); - } - mBackCallback.setEnabled(!isRoot); // when setSupportActionBar is called a toolbar wrapper gets initialized that overwrites // navigation click listener. The default behavior set in the wrapper is to call into @@ -268,6 +290,10 @@ public class ScreenStackHeaderConfig extends ViewGroup { mIsShadowHidden = hideShadow; } + public void setGestureEnabled(boolean gestureEnabled) { + mGestureEnabled = gestureEnabled; + } + public void setHideBackButton(boolean hideBackButton) { mIsBackButtonHidden = hideBackButton; } diff --git a/android/src/main/java/com/swmansion/rnscreens/ScreenStackHeaderConfigViewManager.java b/android/src/main/java/com/swmansion/rnscreens/ScreenStackHeaderConfigViewManager.java index 1adb924c..88a968d7 100644 --- a/android/src/main/java/com/swmansion/rnscreens/ScreenStackHeaderConfigViewManager.java +++ b/android/src/main/java/com/swmansion/rnscreens/ScreenStackHeaderConfigViewManager.java @@ -82,6 +82,11 @@ public class ScreenStackHeaderConfigViewManager extends ViewGroupManager { public void setStackPresentation(Screen view, String presentation) { if ("push".equals(presentation)) { view.setStackPresentation(Screen.StackPresentation.PUSH); - } else if ("modal".equals(presentation)) { + } else if ("modal".equals(presentation) || "containedModal".equals(presentation)) { + // at the moment Android implementation does not handle contained vs regular modals view.setStackPresentation(Screen.StackPresentation.MODAL); - } else if ("transparentModal".equals(presentation)) { + } else if ("transparentModal".equals(presentation) || "containedTransparentModal".equals((presentation))) { + // at the moment Android implementation does not handle contained vs regular modals view.setStackPresentation(Screen.StackPresentation.TRANSPARENT_MODAL); } else { throw new JSApplicationIllegalArgumentException("Unknown presentation type " + presentation);