diff options
| author | Adam Powell <adamp@google.com> | 2017-02-08 16:21:01 +0000 |
|---|---|---|
| committer | Android (Google) Code Review <android-gerrit@google.com> | 2017-02-08 16:21:06 +0000 |
| commit | 2ec94fde4feb564c9903142ebe37cebde0b201b4 (patch) | |
| tree | a820a0e4f13c27513bf0d1a81d437c6f7d574e8a /core/java/android | |
| parent | b94a914c9e2446f791cf165a38dd6683c4251f0a (diff) | |
| parent | 0f552f4d50c40c60b5de315d6f3dc480ebd97888 (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.java | 96 | ||||
| -rw-r--r-- | core/java/android/view/ViewGroup.java | 13 | ||||
| -rw-r--r-- | core/java/android/widget/AbsListView.java | 8 |
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(); } |
