Prevent foocus from escaping open navigation drawer

When contents of NewPipe navigation drawer change, NavigationMenuView
(which is actually a RecyclerView) removes and re-adds all its adapter
children, which leads to temporary loss of focus on currently focused drawer
child. This situation was not anticipated by developers of original
support library DrawerLayout: while NavigationMenuView itself is able
to keep focus from escaping via onRequestFocusInDescendants(),
the implementation of that method in DrawerLayout does not pass focus
to previously focused View. In fact it does not pass focus correctly at all
because the AOSP implementation of that method does not call addFocusables()
and simply focuses the first available VISIBLE View, without regard
to state of drawers.
This commit is contained in:
Alexander-- 2020-03-15 09:19:22 +06:59
parent 6aca344bf7
commit 381b491845

View file

@ -19,6 +19,7 @@ package org.schabi.newpipe.views;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.content.Context; import android.content.Context;
import android.graphics.Rect;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.view.Gravity; import android.view.Gravity;
import android.view.View; import android.view.View;
@ -44,6 +45,34 @@ public final class FocusAwareDrawerLayout extends DrawerLayout {
super(context, attrs, defStyle); super(context, attrs, defStyle);
} }
@Override
protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) {
// SDK implementation of this method picks whatever visible View takes the focus first without regard to addFocusables
// if the open drawer is temporarily empty, the focus escapes outside of it, which can be confusing
boolean hasOpenPanels = false;
for (int i = 0; i < getChildCount(); ++i) {
View child = getChildAt(i);
DrawerLayout.LayoutParams lp = (DrawerLayout.LayoutParams) child.getLayoutParams();
if (lp.gravity != 0 && isDrawerVisible(child)) {
hasOpenPanels = true;
if (child.requestFocus(direction, previouslyFocusedRect)) {
return true;
}
}
}
if (hasOpenPanels) {
return false;
}
return super.onRequestFocusInDescendants(direction, previouslyFocusedRect);
}
@Override @Override
public void addFocusables(ArrayList<View> views, int direction, int focusableMode) { public void addFocusables(ArrayList<View> views, int direction, int focusableMode) {
boolean hasOpenPanels = false; boolean hasOpenPanels = false;