Merge "Allow cluster navigation to jump into touchscreenBlocksFocus"
This commit is contained in:
@@ -18,6 +18,7 @@ package android.view;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.graphics.Rect;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.SparseArray;
|
||||
@@ -88,8 +89,9 @@ public class FocusFinder {
|
||||
|
||||
private View findNextFocus(ViewGroup root, View focused, Rect focusedRect, int direction) {
|
||||
View next = null;
|
||||
ViewGroup effectiveRoot = getEffectiveRoot(root, focused);
|
||||
if (focused != null) {
|
||||
next = findNextUserSpecifiedFocus(root, focused, direction);
|
||||
next = findNextUserSpecifiedFocus(effectiveRoot, focused, direction);
|
||||
}
|
||||
if (next != null) {
|
||||
return next;
|
||||
@@ -97,9 +99,9 @@ public class FocusFinder {
|
||||
ArrayList<View> focusables = mTempList;
|
||||
try {
|
||||
focusables.clear();
|
||||
root.addFocusables(focusables, direction);
|
||||
effectiveRoot.addFocusables(focusables, direction);
|
||||
if (!focusables.isEmpty()) {
|
||||
next = findNextFocus(root, focused, focusedRect, direction, focusables);
|
||||
next = findNextFocus(effectiveRoot, focused, focusedRect, direction, focusables);
|
||||
}
|
||||
} finally {
|
||||
focusables.clear();
|
||||
@@ -107,6 +109,35 @@ public class FocusFinder {
|
||||
return next;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the "effective" root of a view. The "effective" root is the closest ancestor
|
||||
* within-which focus should cycle.
|
||||
* <p>
|
||||
* For example: normal focus navigation would stay within a ViewGroup marked as
|
||||
* touchscreenBlocksFocus and keyboardNavigationCluster until a cluster-jump out.
|
||||
* @return the "effective" root of {@param focused}
|
||||
*/
|
||||
private ViewGroup getEffectiveRoot(ViewGroup root, View focused) {
|
||||
if (focused == null) {
|
||||
return root;
|
||||
}
|
||||
ViewParent effective = focused.getParent();
|
||||
do {
|
||||
if (effective == root) {
|
||||
return root;
|
||||
}
|
||||
ViewGroup vg = (ViewGroup) effective;
|
||||
if (vg.getTouchscreenBlocksFocus()
|
||||
&& focused.getContext().getPackageManager().hasSystemFeature(
|
||||
PackageManager.FEATURE_TOUCHSCREEN)
|
||||
&& vg.isKeyboardNavigationCluster()) {
|
||||
return vg;
|
||||
}
|
||||
effective = effective.getParent();
|
||||
} while (effective != null);
|
||||
return root;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the root of the next keyboard navigation cluster after the current one.
|
||||
* @param root The view tree to look inside. Cannot be null
|
||||
|
||||
@@ -1190,7 +1190,8 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
|
||||
final int focusableCount = views.size();
|
||||
|
||||
final int descendantFocusability = getDescendantFocusability();
|
||||
final boolean focusSelf = (isFocusableInTouchMode() || !shouldBlockFocusForTouchscreen());
|
||||
final boolean blockFocusForTouchscreen = shouldBlockFocusForTouchscreen();
|
||||
final boolean focusSelf = (isFocusableInTouchMode() || !blockFocusForTouchscreen);
|
||||
|
||||
if (descendantFocusability == FOCUS_BLOCK_DESCENDANTS) {
|
||||
if (focusSelf) {
|
||||
@@ -1199,7 +1200,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
|
||||
return;
|
||||
}
|
||||
|
||||
if (shouldBlockFocusForTouchscreen()) {
|
||||
if (blockFocusForTouchscreen) {
|
||||
focusableMode |= FOCUSABLES_TOUCH_MODE;
|
||||
}
|
||||
|
||||
@@ -1234,7 +1235,19 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
|
||||
public void addKeyboardNavigationClusters(Collection<View> views, int direction) {
|
||||
final int focusableCount = views.size();
|
||||
|
||||
super.addKeyboardNavigationClusters(views, direction);
|
||||
if (isKeyboardNavigationCluster()) {
|
||||
// Cluster-navigation can enter a touchscreenBlocksFocus cluster, so temporarily
|
||||
// disable touchscreenBlocksFocus to evaluate whether it contains focusables.
|
||||
final boolean blockedFocus = getTouchscreenBlocksFocus();
|
||||
try {
|
||||
setTouchscreenBlocksFocusNoRefocus(false);
|
||||
super.addKeyboardNavigationClusters(views, direction);
|
||||
} finally {
|
||||
setTouchscreenBlocksFocusNoRefocus(blockedFocus);
|
||||
}
|
||||
} else {
|
||||
super.addKeyboardNavigationClusters(views, direction);
|
||||
}
|
||||
|
||||
if (focusableCount != views.size()) {
|
||||
// No need to look for groups inside a group.
|
||||
@@ -1280,6 +1293,14 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
|
||||
}
|
||||
}
|
||||
|
||||
private void setTouchscreenBlocksFocusNoRefocus(boolean touchscreenBlocksFocus) {
|
||||
if (touchscreenBlocksFocus) {
|
||||
mGroupFlags |= FLAG_TOUCHSCREEN_BLOCKS_FOCUS;
|
||||
} else {
|
||||
mGroupFlags &= ~FLAG_TOUCHSCREEN_BLOCKS_FOCUS;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether this ViewGroup should ignore focus requests for itself and its children.
|
||||
*/
|
||||
@@ -1288,8 +1309,12 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
|
||||
}
|
||||
|
||||
boolean shouldBlockFocusForTouchscreen() {
|
||||
// There is a special case for keyboard-navigation clusters. We allow cluster navigation
|
||||
// to jump into blockFocusForTouchscreen ViewGroups which are clusters. Once in the
|
||||
// cluster, focus is free to move around within it.
|
||||
return getTouchscreenBlocksFocus() &&
|
||||
mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN);
|
||||
mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN)
|
||||
&& (!hasFocus() || !isKeyboardNavigationCluster());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -3175,6 +3200,21 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
|
||||
@TestApi
|
||||
@Override
|
||||
public boolean restoreFocusInCluster(@FocusRealDirection int direction) {
|
||||
// Allow cluster-navigation to enter touchscreenBlocksFocus ViewGroups.
|
||||
if (isKeyboardNavigationCluster()) {
|
||||
final boolean blockedFocus = getTouchscreenBlocksFocus();
|
||||
try {
|
||||
setTouchscreenBlocksFocusNoRefocus(false);
|
||||
return restoreFocusInClusterInternal(direction);
|
||||
} finally {
|
||||
setTouchscreenBlocksFocusNoRefocus(blockedFocus);
|
||||
}
|
||||
} else {
|
||||
return restoreFocusInClusterInternal(direction);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean restoreFocusInClusterInternal(@FocusRealDirection int direction) {
|
||||
if (mFocusedInCluster != null && !mFocusedInCluster.isKeyboardNavigationCluster()
|
||||
&& getDescendantFocusability() != FOCUS_BLOCK_DESCENDANTS
|
||||
&& (mFocusedInCluster.mViewFlags & VISIBILITY_MASK) == VISIBLE
|
||||
|
||||
@@ -343,6 +343,9 @@ public class Toolbar extends ViewGroup {
|
||||
final ViewGroup vgParent = (ViewGroup) parent;
|
||||
if (vgParent.isKeyboardNavigationCluster()) {
|
||||
setKeyboardNavigationCluster(false);
|
||||
if (vgParent.getTouchscreenBlocksFocus()) {
|
||||
setTouchscreenBlocksFocus(false);
|
||||
}
|
||||
break;
|
||||
}
|
||||
parent = vgParent.getParent();
|
||||
|
||||
@@ -35,6 +35,7 @@ This is an optimized layout for a screen with the Action Bar enabled.
|
||||
android:layout_alignParentTop="true"
|
||||
style="?attr/actionBarStyle"
|
||||
android:transitionName="android:action_bar"
|
||||
android:touchscreenBlocksFocus="true"
|
||||
android:keyboardNavigationCluster="true"
|
||||
android:gravity="top">
|
||||
<com.android.internal.widget.ActionBarView
|
||||
@@ -54,6 +55,7 @@ This is an optimized layout for a screen with the Action Bar enabled.
|
||||
android:layout_height="wrap_content"
|
||||
style="?attr/actionBarSplitStyle"
|
||||
android:visibility="gone"
|
||||
android:touchscreenBlocksFocus="true"
|
||||
android:keyboardNavigationCluster="true"
|
||||
android:gravity="center"/>
|
||||
</com.android.internal.widget.ActionBarOverlayLayout>
|
||||
|
||||
@@ -35,6 +35,7 @@ This is an optimized layout for a screen with a toolbar enabled.
|
||||
android:layout_alignParentTop="true"
|
||||
style="?attr/actionBarStyle"
|
||||
android:transitionName="android:action_bar"
|
||||
android:touchscreenBlocksFocus="true"
|
||||
android:keyboardNavigationCluster="true"
|
||||
android:gravity="top">
|
||||
<Toolbar
|
||||
|
||||
@@ -1249,6 +1249,7 @@ please see styles_device_defaults.xml.
|
||||
<item name="collapseContentDescription">@string/toolbar_collapse_description</item>
|
||||
<item name="contentInsetStart">16dp</item>
|
||||
<item name="contentInsetStartWithNavigation">@dimen/action_bar_content_inset_with_nav</item>
|
||||
<item name="touchscreenBlocksFocus">true</item>
|
||||
<item name="keyboardNavigationCluster">true</item>
|
||||
</style>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user