Skip to content

Commit

Permalink
Android native stack bugfixes. (#190)
Browse files Browse the repository at this point in the history
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
  • Loading branch information
kmagiera committed Oct 22, 2019
1 parent 4749405 commit d4636d3
Show file tree
Hide file tree
Showing 6 changed files with 66 additions and 22 deletions.
2 changes: 0 additions & 2 deletions android/src/main/java/com/swmansion/rnscreens/Screen.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

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;
Expand All @@ -13,7 +12,6 @@

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;
Expand Down
12 changes: 9 additions & 3 deletions android/src/main/java/com/swmansion/rnscreens/ScreenStack.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ public void dismiss(ScreenStackFragment screenFragment) {
onUpdate();
}

public Screen getTopScreen() {
return mTopScreen.getScreen();
}

public Screen getRootScreen() {
for (int i = 0, size = getScreenCount(); i < size; i++) {
Screen screen = getScreenAt(i);
Expand All @@ -51,9 +55,7 @@ protected void onLayout(boolean changed, int l, int t, int r, int b) {
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);
}
}

Expand Down Expand Up @@ -169,5 +171,9 @@ public void run() {
mStack.addAll(mScreenFragments);

tryCommitTransaction();

for (ScreenStackFragment screen : mStack) {
screen.onStackUpdate();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,13 @@ public void setToolbarShadowHidden(boolean hidden) {
}
}

public void onStackUpdate() {
View child = mScreenView.getChildAt(0);
if (child instanceof ScreenStackHeaderConfig) {
((ScreenStackHeaderConfig) child).onUpdate();
}
}

@Override
public View onCreateView(LayoutInflater inflater,
@Nullable ViewGroup container,
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -68,7 +76,14 @@ protected void onLayout(boolean changed, int l, int t, int r, int b) {
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
update();
mIsAttachedToWindow = true;
onUpdate();
}

@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
mIsAttachedToWindow = false;
}

private Screen getScreen() {
Expand Down Expand Up @@ -101,14 +116,27 @@ private ScreenStackFragment getScreenFragment() {
return null;
}

private void installBackCallback() {
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();
Fragment fragment = getScreenFragment();
fragment.requireActivity().getOnBackPressedDispatcher().addCallback(fragment, mBackCallback);
}

private void update() {
Screen parent = (Screen) getParent();
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();
Expand All @@ -125,13 +153,7 @@ private void update() {
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
Expand Down Expand Up @@ -268,6 +290,10 @@ public void setHideShadow(boolean hideShadow) {
mIsShadowHidden = hideShadow;
}

public void setGestureEnabled(boolean gestureEnabled) {
mGestureEnabled = gestureEnabled;
}

public void setHideBackButton(boolean hideBackButton) {
mIsBackButtonHidden = hideBackButton;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,11 @@ public void setHideShadow(ScreenStackHeaderConfig config, boolean hideShadow) {
config.setHideShadow(hideShadow);
}

@ReactProp(name = "gestureEnabled", defaultBoolean = true)
public void setGestureEnabled(ScreenStackHeaderConfig config, boolean gestureEnabled) {
config.setGestureEnabled(gestureEnabled);
}

@ReactProp(name = "hideBackButton")
public void setHideBackButton(ScreenStackHeaderConfig config, boolean hideBackButton) {
config.setHideBackButton(hideBackButton);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,11 @@ public void setActive(Screen view, float active) {
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);
Expand Down

0 comments on commit d4636d3

Please sign in to comment.