summaryrefslogtreecommitdiff
path: root/core/java/android
diff options
context:
space:
mode:
authorAdam Powell <adamp@google.com>2017-02-08 16:21:01 +0000
committerAndroid (Google) Code Review <android-gerrit@google.com>2017-02-08 16:21:06 +0000
commit2ec94fde4feb564c9903142ebe37cebde0b201b4 (patch)
treea820a0e4f13c27513bf0d1a81d437c6f7d574e8a /core/java/android
parentb94a914c9e2446f791cf165a38dd6683c4251f0a (diff)
parent0f552f4d50c40c60b5de315d6f3dc480ebd97888 (diff)
Merge "Provide old behavior for View#hasFocusable to old apps"
Diffstat (limited to 'core/java/android')
-rw-r--r--core/java/android/view/View.java96
-rw-r--r--core/java/android/view/ViewGroup.java13
-rw-r--r--core/java/android/widget/AbsListView.java8
3 files changed, 76 insertions, 41 deletions
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index f44f9238454f..23d1dbb1b923 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -16,12 +16,6 @@
package android.view;
-import static android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH;
-import static android.os.Build.VERSION_CODES.JELLY_BEAN_MR1;
-import static android.os.Build.VERSION_CODES.KITKAT;
-import static android.os.Build.VERSION_CODES.M;
-import static android.os.Build.VERSION_CODES.N;
-
import static java.lang.Math.max;
import android.animation.AnimatorInflater;
@@ -67,7 +61,7 @@ import android.graphics.Shader;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.hardware.display.DisplayManagerGlobal;
-import android.os.Build.VERSION_CODES;
+import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
@@ -857,6 +851,14 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
*/
static boolean sCascadedDragDrop;
+ /**
+ * Prior to O, auto-focusable didn't exist and widgets such as ListView use hasFocusable
+ * to determine things like whether or not to permit item click events. We can't break
+ * apps that do this just because more things (clickable things) are now auto-focusable
+ * and they would get different results, so give old behavior to old apps.
+ */
+ static boolean sHasFocusableExcludeAutoFocusable;
+
/** @hide */
@IntDef({NOT_FOCUSABLE, FOCUSABLE, FOCUSABLE_AUTO})
@Retention(RetentionPolicy.SOURCE)
@@ -4143,40 +4145,43 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
final int targetSdkVersion = context.getApplicationInfo().targetSdkVersion;
// Older apps may need this compatibility hack for measurement.
- sUseBrokenMakeMeasureSpec = targetSdkVersion <= JELLY_BEAN_MR1;
+ sUseBrokenMakeMeasureSpec = targetSdkVersion <= Build.VERSION_CODES.JELLY_BEAN_MR1;
// Older apps expect onMeasure() to always be called on a layout pass, regardless
// of whether a layout was requested on that View.
- sIgnoreMeasureCache = targetSdkVersion < KITKAT;
+ sIgnoreMeasureCache = targetSdkVersion < Build.VERSION_CODES.KITKAT;
- Canvas.sCompatibilityRestore = targetSdkVersion < M;
+ Canvas.sCompatibilityRestore = targetSdkVersion < Build.VERSION_CODES.M;
// In M and newer, our widgets can pass a "hint" value in the size
// for UNSPECIFIED MeasureSpecs. This lets child views of scrolling containers
// know what the expected parent size is going to be, so e.g. list items can size
// themselves at 1/3 the size of their container. It breaks older apps though,
// specifically apps that use some popular open source libraries.
- sUseZeroUnspecifiedMeasureSpec = targetSdkVersion < M;
+ sUseZeroUnspecifiedMeasureSpec = targetSdkVersion < Build.VERSION_CODES.M;
// Old versions of the platform would give different results from
// LinearLayout measurement passes using EXACTLY and non-EXACTLY
// modes, so we always need to run an additional EXACTLY pass.
- sAlwaysRemeasureExactly = targetSdkVersion <= M;
+ sAlwaysRemeasureExactly = targetSdkVersion <= Build.VERSION_CODES.M;
// Prior to N, layout params could change without requiring a
// subsequent call to setLayoutParams() and they would usually
// work. Partial layout breaks this assumption.
- sLayoutParamsAlwaysChanged = targetSdkVersion <= M;
+ sLayoutParamsAlwaysChanged = targetSdkVersion <= Build.VERSION_CODES.M;
// Prior to N, TextureView would silently ignore calls to setBackground/setForeground.
// On N+, we throw, but that breaks compatibility with apps that use these methods.
- sTextureViewIgnoresDrawableSetters = targetSdkVersion <= M;
+ sTextureViewIgnoresDrawableSetters = targetSdkVersion <= Build.VERSION_CODES.M;
// Prior to N, we would drop margins in LayoutParam conversions. The fix triggers bugs
// in apps so we target check it to avoid breaking existing apps.
- sPreserveMarginParamsInLayoutParamConversion = targetSdkVersion >= N;
+ sPreserveMarginParamsInLayoutParamConversion =
+ targetSdkVersion >= Build.VERSION_CODES.N;
- sCascadedDragDrop = targetSdkVersion < N;
+ sCascadedDragDrop = targetSdkVersion < Build.VERSION_CODES.N;
+
+ sHasFocusableExcludeAutoFocusable = targetSdkVersion < Build.VERSION_CODES.O;
sCompatibilityDone = true;
}
@@ -4525,7 +4530,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
break;
//noinspection deprecation
case R.styleable.View_fadingEdge:
- if (targetSdkVersion >= ICE_CREAM_SANDWICH) {
+ if (targetSdkVersion >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
// Ignore the attribute starting with ICS
break;
}
@@ -4663,27 +4668,27 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
PROVIDER_BACKGROUND));
break;
case R.styleable.View_foreground:
- if (targetSdkVersion >= VERSION_CODES.M || this instanceof FrameLayout) {
+ if (targetSdkVersion >= Build.VERSION_CODES.M || this instanceof FrameLayout) {
setForeground(a.getDrawable(attr));
}
break;
case R.styleable.View_foregroundGravity:
- if (targetSdkVersion >= VERSION_CODES.M || this instanceof FrameLayout) {
+ if (targetSdkVersion >= Build.VERSION_CODES.M || this instanceof FrameLayout) {
setForegroundGravity(a.getInt(attr, Gravity.NO_GRAVITY));
}
break;
case R.styleable.View_foregroundTintMode:
- if (targetSdkVersion >= VERSION_CODES.M || this instanceof FrameLayout) {
+ if (targetSdkVersion >= Build.VERSION_CODES.M || this instanceof FrameLayout) {
setForegroundTintMode(Drawable.parseTintMode(a.getInt(attr, -1), null));
}
break;
case R.styleable.View_foregroundTint:
- if (targetSdkVersion >= VERSION_CODES.M || this instanceof FrameLayout) {
+ if (targetSdkVersion >= Build.VERSION_CODES.M || this instanceof FrameLayout) {
setForegroundTintList(a.getColorStateList(attr));
}
break;
case R.styleable.View_foregroundInsidePadding:
- if (targetSdkVersion >= VERSION_CODES.M || this instanceof FrameLayout) {
+ if (targetSdkVersion >= Build.VERSION_CODES.M || this instanceof FrameLayout) {
if (mForegroundInfo == null) {
mForegroundInfo = new ForegroundInfo();
}
@@ -6344,26 +6349,51 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
/**
* Returns true if this view is focusable or if it contains a reachable View
- * for which {@link #hasFocusable()} returns true. A "reachable hasFocusable()"
- * is a View whose parents do not block descendants focus.
- *
+ * for which {@link #hasFocusable()} returns {@code true}. A "reachable hasFocusable()"
+ * is a view whose parents do not block descendants focus.
* Only {@link #VISIBLE} views are considered focusable.
*
- * @return True if the view is focusable or if the view contains a focusable
- * View, false otherwise.
+ * <p>As of {@link Build.VERSION_CODES#O} views that are determined to be focusable
+ * through {@link #FOCUSABLE_AUTO} will also cause this method to return {@code true}.
+ * Apps that declare a {@link android.content.pm.ApplicationInfo#targetSdkVersion} of
+ * earlier than {@link Build.VERSION_CODES#O} will continue to see this method return
+ * {@code false} for views not explicitly marked as focusable.
+ * Use {@link #hasExplicitFocusable()} if you require the pre-{@link Build.VERSION_CODES#O}
+ * behavior.</p>
+ *
+ * @return {@code true} if the view is focusable or if the view contains a focusable
+ * view, {@code false} otherwise
*
* @see ViewGroup#FOCUS_BLOCK_DESCENDANTS
* @see ViewGroup#getTouchscreenBlocksFocus()
+ * @see #hasExplicitFocusable()
*/
public boolean hasFocusable() {
- return hasFocusable(true);
+ return hasFocusable(!sHasFocusableExcludeAutoFocusable, false);
}
/**
- * @hide pending determination of whether this should be public or not.
- * Currently used for compatibility with old focusability expectations in ListView.
+ * Returns true if this view is focusable or if it contains a reachable View
+ * for which {@link #hasExplicitFocusable()} returns {@code true}.
+ * A "reachable hasExplicitFocusable()" is a view whose parents do not block descendants focus.
+ * Only {@link #VISIBLE} views for which {@link #getFocusable()} would return
+ * {@link #FOCUSABLE} are considered focusable.
+ *
+ * <p>This method preserves the pre-{@link Build.VERSION_CODES#O} behavior of
+ * {@link #hasFocusable()} in that only views explicitly set focusable will cause
+ * this method to return true. A view set to {@link #FOCUSABLE_AUTO} that resolves
+ * to focusable will not.</p>
+ *
+ * @return {@code true} if the view is focusable or if the view contains a focusable
+ * view, {@code false} otherwise
+ *
+ * @see #hasFocusable()
*/
- public boolean hasFocusable(boolean allowAutoFocus) {
+ public boolean hasExplicitFocusable() {
+ return hasFocusable(false, true);
+ }
+
+ boolean hasFocusable(boolean allowAutoFocus, boolean dispatchExplicit) {
if (!isFocusableInTouchMode()) {
for (ViewParent p = mParent; p instanceof ViewGroup; p = p.getParent()) {
final ViewGroup g = (ViewGroup) p;
@@ -8679,7 +8709,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
@ResolvedLayoutDir
public int getLayoutDirection() {
final int targetSdkVersion = getContext().getApplicationInfo().targetSdkVersion;
- if (targetSdkVersion < JELLY_BEAN_MR1) {
+ if (targetSdkVersion < Build.VERSION_CODES.JELLY_BEAN_MR1) {
mPrivateFlags2 |= PFLAG2_LAYOUT_DIRECTION_RESOLVED;
return LAYOUT_DIRECTION_RESOLVED_DEFAULT;
}
@@ -15730,7 +15760,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
*/
private boolean isRtlCompatibilityMode() {
final int targetSdkVersion = getContext().getApplicationInfo().targetSdkVersion;
- return targetSdkVersion < JELLY_BEAN_MR1 || !hasRtlSupport();
+ return targetSdkVersion < Build.VERSION_CODES.JELLY_BEAN_MR1 || !hasRtlSupport();
}
/**
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index d107d616b541..af095cf30ea9 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -1107,14 +1107,15 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
return null;
}
- /** @hide Overriding hidden method */
@Override
- public boolean hasFocusable(boolean allowAutoFocus) {
+ boolean hasFocusable(boolean allowAutoFocus, boolean dispatchExplicit) {
if ((mViewFlags & VISIBILITY_MASK) != VISIBLE) {
return false;
}
- // TODO This should probably be super.hasFocusable, but that would change behavior
+ // TODO This should probably be super.hasFocusable, but that would change behavior.
+ // The below is a much simpler check than we do in the superclass implementation,
+ // but it's been this way for a long time and other code likely relies on it.
if ((allowAutoFocus ? getFocusable() != NOT_FOCUSABLE : getFocusable() == FOCUSABLE)
&& isFocusable()) {
return true;
@@ -1127,7 +1128,11 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
for (int i = 0; i < count; i++) {
final View child = children[i];
- if (child.hasFocusable(allowAutoFocus)) {
+
+ // In case the subclass has overridden has[Explicit]Focusable, dispatch
+ // to the expected one for each child even though we share logic here.
+ if ((dispatchExplicit && child.hasExplicitFocusable())
+ || (!dispatchExplicit && child.hasFocusable())) {
return true;
}
}
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index 47c4cf383e61..4c9a86624468 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -2548,7 +2548,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
}
private boolean isItemClickable(View view) {
- return !view.hasFocusable(false);
+ return !view.hasExplicitFocusable();
}
/**
@@ -2824,7 +2824,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
final View v = getChildAt(mSelectedPosition - mFirstPosition);
if (v != null) {
- if (v.hasFocusable(false)) return;
+ if (v.hasExplicitFocusable()) return;
v.setPressed(true);
}
setPressed(true);
@@ -3428,7 +3428,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
if (mTouchMode == TOUCH_MODE_DOWN) {
mTouchMode = TOUCH_MODE_TAP;
final View child = getChildAt(mMotionPosition - mFirstPosition);
- if (child != null && !child.hasFocusable(false)) {
+ if (child != null && !child.hasExplicitFocusable()) {
mLayoutMode = LAYOUT_NORMAL;
if (!mDataChanged) {
@@ -4005,7 +4005,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
final float x = ev.getX();
final boolean inList = x > mListPadding.left && x < getWidth() - mListPadding.right;
- if (inList && !child.hasFocusable(false)) {
+ if (inList && !child.hasExplicitFocusable()) {
if (mPerformClick == null) {
mPerformClick = new PerformClick();
}