diff options
| author | 2015-12-15 13:36:01 -0800 | |
|---|---|---|
| committer | 2015-12-18 07:44:50 -0800 | |
| commit | 5beb2617f91e28c45917ea48109b8350f4e62140 (patch) | |
| tree | 31165cd487fa75e5d04bacf898a6fd2c9b552487 | |
| parent | d5f94468bae6813c9ab559de9a166f7cf87a7a77 (diff) | |
Alt-up moves focus to the toolbar in action bar mode.
Bug 24811572
Change-Id: Ie065fa13ba76131e700e73984a99fc60a7aa513c
| -rw-r--r-- | core/java/android/app/ActionBar.java | 97 | ||||
| -rw-r--r-- | core/java/android/app/Activity.java | 19 | ||||
| -rw-r--r-- | core/java/com/android/internal/app/ToolbarActionBar.java | 10 | ||||
| -rw-r--r-- | core/java/com/android/internal/app/WindowDecorActionBar.java | 8 |
4 files changed, 133 insertions, 1 deletions
diff --git a/core/java/android/app/ActionBar.java b/core/java/android/app/ActionBar.java index 72f8c779227a..3e6b595da00e 100644 --- a/core/java/android/app/ActionBar.java +++ b/core/java/android/app/ActionBar.java @@ -31,9 +31,11 @@ import android.view.ActionMode; import android.view.Gravity; import android.view.KeyEvent; import android.view.View; +import android.view.View.OnFocusChangeListener; import android.view.ViewDebug; import android.view.ViewGroup; import android.view.ViewHierarchyEncoder; +import android.view.ViewParent; import android.view.Window; import android.widget.SpinnerAdapter; import java.lang.annotation.Retention; @@ -1071,6 +1073,62 @@ public abstract class ActionBar { } /** + * Attempts to move focus to the ActionBar if it does not already contain the focus. + * + * @return {@code true} if focus changes or {@code false} if focus doesn't change. + * @hide + */ + public boolean requestFocus() { + return false; + } + + /** + * Common implementation for requestFocus that takes in the Toolbar and moves focus + * to the contents. This makes the ViewGroups containing the toolbar allow focus while it stays + * in the ActionBar and then prevents it again once it leaves. + * + * @param viewGroup The toolbar ViewGroup + * @return {@code true} if focus changes or {@code false} if focus doesn't change. + * @hide + */ + protected boolean requestFocus(ViewGroup viewGroup) { + if (viewGroup != null && !viewGroup.hasFocus()) { + final ViewGroup toolbar = viewGroup.getTouchscreenBlocksFocus() ? viewGroup : null; + ViewParent parent = viewGroup.getParent(); + ViewGroup container = null; + while (parent != null && parent instanceof ViewGroup) { + final ViewGroup vgParent = (ViewGroup) parent; + if (vgParent.getTouchscreenBlocksFocus()) { + container = vgParent; + break; + } + parent = vgParent.getParent(); + } + if (container != null) { + container.setTouchscreenBlocksFocus(false); + } + if (toolbar != null) { + toolbar.setTouchscreenBlocksFocus(false); + } + viewGroup.requestFocus(); + final View focused = viewGroup.findFocus(); + if (focused != null) { + focused.setOnFocusChangeListener(new FollowOutOfActionBar(viewGroup, + container, toolbar)); + } else { + if (container != null) { + container.setTouchscreenBlocksFocus(true); + } + if (toolbar != null) { + toolbar.setTouchscreenBlocksFocus(true); + } + } + return true; + } + return false; + } + + /** * Listener interface for ActionBar navigation events. * * @deprecated Action bar navigation modes are deprecated and not supported by inline @@ -1388,4 +1446,43 @@ public abstract class ActionBar { encoder.addProperty("gravity", gravity); } } + + /** + * Tracks the focused View until it leaves the ActionBar, then it resets the + * touchscreenBlocksFocus value. + */ + private static class FollowOutOfActionBar implements OnFocusChangeListener, Runnable { + private final ViewGroup mFocusRoot; + private final ViewGroup mContainer; + private final ViewGroup mToolbar; + + public FollowOutOfActionBar(ViewGroup focusRoot, ViewGroup container, ViewGroup toolbar) { + mContainer = container; + mToolbar = toolbar; + mFocusRoot = focusRoot; + } + + @Override + public void onFocusChange(View v, boolean hasFocus) { + if (!hasFocus) { + v.setOnFocusChangeListener(null); + final View focused = mFocusRoot.findFocus(); + if (focused != null) { + focused.setOnFocusChangeListener(this); + } else { + mFocusRoot.post(this); + } + } + } + + @Override + public void run() { + if (mContainer != null) { + mContainer.setTouchscreenBlocksFocus(true); + } + if (mToolbar != null) { + mToolbar.setTouchscreenBlocksFocus(true); + } + } + } } diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index ef84ab05c2b2..f7787f59f850 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -816,6 +816,7 @@ public class Activity extends ContextThemeWrapper SharedElementCallback mExitTransitionListener = SharedElementCallback.NULL_CALLBACK; private boolean mHasCurrentPermissionsRequest; + private boolean mEatKeyUpEvent; /** Return the intent that started this activity. */ public Intent getIntent() { @@ -2816,9 +2817,25 @@ public class Activity extends ContextThemeWrapper // Let action bars open menus in response to the menu key prioritized over // the window handling it - if (event.getKeyCode() == KeyEvent.KEYCODE_MENU && + final int keyCode = event.getKeyCode(); + if (keyCode == KeyEvent.KEYCODE_MENU && mActionBar != null && mActionBar.onMenuKeyEvent(event)) { return true; + } else if (keyCode == KeyEvent.KEYCODE_DPAD_UP) { + // Capture the Alt-up and send focus to the ActionBar + final int action = event.getAction(); + if (action == KeyEvent.ACTION_DOWN) { + if (event.hasModifiers(KeyEvent.META_ALT_ON)) { + final ActionBar actionBar = getActionBar(); + if (actionBar != null && actionBar.isShowing() && actionBar.requestFocus()) { + mEatKeyUpEvent = true; + return true; + } + } + } else if (action == KeyEvent.ACTION_UP && mEatKeyUpEvent) { + mEatKeyUpEvent = false; + return true; + } } Window win = getWindow(); diff --git a/core/java/com/android/internal/app/ToolbarActionBar.java b/core/java/com/android/internal/app/ToolbarActionBar.java index 9d128035a250..4b6e7e4a03d2 100644 --- a/core/java/com/android/internal/app/ToolbarActionBar.java +++ b/core/java/com/android/internal/app/ToolbarActionBar.java @@ -29,10 +29,14 @@ import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; import android.view.View; +import android.view.View.OnFocusChangeListener; +import android.view.ViewGroup; +import android.view.ViewParent; import android.view.Window; import android.view.WindowCallbackWrapper; import android.widget.SpinnerAdapter; import android.widget.Toolbar; + import com.android.internal.view.menu.MenuBuilder; import com.android.internal.view.menu.MenuPresenter; import com.android.internal.widget.DecorToolbar; @@ -499,6 +503,12 @@ public class ToolbarActionBar extends ActionBar { } } + /** @hide */ + @Override + public boolean requestFocus() { + return requestFocus(mDecorToolbar.getViewGroup()); + } + private class ToolbarCallbackWrapper extends WindowCallbackWrapper { public ToolbarCallbackWrapper(Window.Callback wrapped) { super(wrapped); diff --git a/core/java/com/android/internal/app/WindowDecorActionBar.java b/core/java/com/android/internal/app/WindowDecorActionBar.java index 05cfd81766ab..c6bf1b46becf 100644 --- a/core/java/com/android/internal/app/WindowDecorActionBar.java +++ b/core/java/com/android/internal/app/WindowDecorActionBar.java @@ -18,6 +18,8 @@ package com.android.internal.app; import android.animation.ValueAnimator; import android.content.res.TypedArray; +import android.view.View.OnFocusChangeListener; +import android.view.ViewGroup; import android.view.ViewParent; import android.widget.Toolbar; @@ -950,6 +952,12 @@ public class WindowDecorActionBar extends ActionBar implements return false; } + /** @hide */ + @Override + public boolean requestFocus() { + return requestFocus(mDecorToolbar.getViewGroup()); + } + /** * @hide */ |