Use fragment manager back stack to handle hw back button presses on Android. (#216)

Before this change we'd rely on new androidx OnBackPressedCallback mechanism. It turns out not to work well in a few cases. The biggest problem with it was that when registered it'd never allow for the hw back button press to fallback to a system default behaviour and instead would always "still" that event. After several attempts of trying to make it work I decided to revert back to a FragmentManager's default solution.

This change adds an ability to use FragmentManager's back stack API. There are also a few problems with it we need to workaround though. One of the problem is the fact that we can not modify back stack history. What we do because of that is that we try to keep at most one item on the back stack and reset it each time our native stack updates. In order for that to work we create a fake transaction that hides and shows the same screen that displays on top. Thanks to that when hw back is pressed the built in transaction rollback logic does nothing to the UI and allows us to handle back navigation using back stack change listener.
This commit is contained in:
Krzysztof Magiera
2019-11-06 21:54:19 +01:00
committed by GitHub
parent 5d5e8bfca6
commit 26384b625e
4 changed files with 82 additions and 25 deletions

View File

@@ -10,7 +10,6 @@ import android.view.ViewGroup;
import android.view.ViewParent;
import android.widget.TextView;
import androidx.activity.OnBackPressedCallback;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
@@ -36,18 +35,6 @@ public class ScreenStackHeaderConfig extends ViewGroup {
private boolean mIsAttachedToWindow = false;
private OnBackPressedCallback mBackCallback = new OnBackPressedCallback(false) {
@Override
public void handleOnBackPressed() {
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) {
@@ -116,27 +103,20 @@ public class ScreenStackHeaderConfig extends ViewGroup {
return null;
}
public boolean isDismissable() {
return mGestureEnabled;
}
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();