diff options
23 files changed, 626 insertions, 36 deletions
diff --git a/api/current.txt b/api/current.txt index 629d832c0c3c..1dcd309b16ca 100644 --- a/api/current.txt +++ b/api/current.txt @@ -3505,6 +3505,7 @@ package android.app { method public boolean onPreparePanel(int, android.view.View, android.view.Menu); method public void onProvideAssistContent(android.app.assist.AssistContent); method public void onProvideAssistData(android.os.Bundle); + method public void onProvideKeyboardShortcuts(java.util.List<android.view.KeyboardShortcutGroup>, android.view.Menu); method public android.net.Uri onProvideReferrer(); method public void onRequestPermissionsResult(int, java.lang.String[], int[]); method protected void onRestart(); @@ -4189,6 +4190,7 @@ package android.app { method public void onPanelClosed(int, android.view.Menu); method public boolean onPrepareOptionsMenu(android.view.Menu); method public boolean onPreparePanel(int, android.view.View, android.view.Menu); + method public void onProvideKeyboardShortcuts(java.util.List<android.view.KeyboardShortcutGroup>, android.view.Menu); method public void onRestoreInstanceState(android.os.Bundle); method public android.os.Bundle onSaveInstanceState(); method public boolean onSearchRequested(android.view.SearchEvent); @@ -33502,6 +33504,7 @@ package android.service.dreams { method public boolean onMenuOpened(int, android.view.Menu); method public void onPanelClosed(int, android.view.Menu); method public boolean onPreparePanel(int, android.view.View, android.view.Menu); + method public void onProvideKeyboardShortcuts(java.util.List<android.view.KeyboardShortcutGroup>, android.view.Menu); method public boolean onSearchRequested(android.view.SearchEvent); method public boolean onSearchRequested(); method public void onWakeUp(); @@ -40390,6 +40393,27 @@ package android.view { method public void startTracking(android.view.KeyEvent, java.lang.Object); } + public final class KeyboardShortcutGroup implements android.os.Parcelable { + ctor public KeyboardShortcutGroup(java.lang.CharSequence, java.util.List<android.view.KeyboardShortcutInfo>); + ctor public KeyboardShortcutGroup(java.lang.CharSequence); + method public void addItem(android.view.KeyboardShortcutInfo); + method public int describeContents(); + method public java.util.List<android.view.KeyboardShortcutInfo> getItems(); + method public java.lang.CharSequence getLabel(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.view.KeyboardShortcutGroup> CREATOR; + } + + public final class KeyboardShortcutInfo implements android.os.Parcelable { + ctor public KeyboardShortcutInfo(java.lang.CharSequence, char, int); + method public int describeContents(); + method public char getBaseCharacter(); + method public java.lang.CharSequence getLabel(); + method public int getModifiers(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.view.KeyboardShortcutInfo> CREATOR; + } + public abstract class LayoutInflater { ctor protected LayoutInflater(android.content.Context); ctor protected LayoutInflater(android.view.LayoutInflater, android.content.Context); @@ -42391,6 +42415,7 @@ package android.view { method public abstract boolean onMenuOpened(int, android.view.Menu); method public abstract void onPanelClosed(int, android.view.Menu); method public abstract boolean onPreparePanel(int, android.view.View, android.view.Menu); + method public abstract void onProvideKeyboardShortcuts(java.util.List<android.view.KeyboardShortcutGroup>, android.view.Menu); method public abstract boolean onSearchRequested(); method public abstract boolean onSearchRequested(android.view.SearchEvent); method public abstract void onWindowAttributesChanged(android.view.WindowManager.LayoutParams); diff --git a/api/system-current.txt b/api/system-current.txt index 6a36ce05acca..adce4806c654 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -3611,6 +3611,7 @@ package android.app { method public boolean onPreparePanel(int, android.view.View, android.view.Menu); method public void onProvideAssistContent(android.app.assist.AssistContent); method public void onProvideAssistData(android.os.Bundle); + method public void onProvideKeyboardShortcuts(java.util.List<android.view.KeyboardShortcutGroup>, android.view.Menu); method public android.net.Uri onProvideReferrer(); method public void onRequestPermissionsResult(int, java.lang.String[], int[]); method protected void onRestart(); @@ -4310,6 +4311,7 @@ package android.app { method public void onPanelClosed(int, android.view.Menu); method public boolean onPrepareOptionsMenu(android.view.Menu); method public boolean onPreparePanel(int, android.view.View, android.view.Menu); + method public void onProvideKeyboardShortcuts(java.util.List<android.view.KeyboardShortcutGroup>, android.view.Menu); method public void onRestoreInstanceState(android.os.Bundle); method public android.os.Bundle onSaveInstanceState(); method public boolean onSearchRequested(android.view.SearchEvent); @@ -35640,6 +35642,7 @@ package android.service.dreams { method public boolean onMenuOpened(int, android.view.Menu); method public void onPanelClosed(int, android.view.Menu); method public boolean onPreparePanel(int, android.view.View, android.view.Menu); + method public void onProvideKeyboardShortcuts(java.util.List<android.view.KeyboardShortcutGroup>, android.view.Menu); method public boolean onSearchRequested(android.view.SearchEvent); method public boolean onSearchRequested(); method public void onWakeUp(); @@ -42741,6 +42744,27 @@ package android.view { method public void startTracking(android.view.KeyEvent, java.lang.Object); } + public final class KeyboardShortcutGroup implements android.os.Parcelable { + ctor public KeyboardShortcutGroup(java.lang.CharSequence, java.util.List<android.view.KeyboardShortcutInfo>); + ctor public KeyboardShortcutGroup(java.lang.CharSequence); + method public void addItem(android.view.KeyboardShortcutInfo); + method public int describeContents(); + method public java.util.List<android.view.KeyboardShortcutInfo> getItems(); + method public java.lang.CharSequence getLabel(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.view.KeyboardShortcutGroup> CREATOR; + } + + public final class KeyboardShortcutInfo implements android.os.Parcelable { + ctor public KeyboardShortcutInfo(java.lang.CharSequence, char, int); + method public int describeContents(); + method public char getBaseCharacter(); + method public java.lang.CharSequence getLabel(); + method public int getModifiers(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.view.KeyboardShortcutInfo> CREATOR; + } + public abstract class LayoutInflater { ctor protected LayoutInflater(android.content.Context); ctor protected LayoutInflater(android.view.LayoutInflater, android.content.Context); @@ -44743,6 +44767,7 @@ package android.view { method public abstract boolean onMenuOpened(int, android.view.Menu); method public abstract void onPanelClosed(int, android.view.Menu); method public abstract boolean onPreparePanel(int, android.view.View, android.view.Menu); + method public abstract void onProvideKeyboardShortcuts(java.util.List<android.view.KeyboardShortcutGroup>, android.view.Menu); method public abstract boolean onSearchRequested(); method public abstract boolean onSearchRequested(android.view.SearchEvent); method public abstract void onWindowAttributesChanged(android.view.WindowManager.LayoutParams); diff --git a/api/test-current.txt b/api/test-current.txt index 21b101fef449..3fed63215746 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -3505,6 +3505,7 @@ package android.app { method public boolean onPreparePanel(int, android.view.View, android.view.Menu); method public void onProvideAssistContent(android.app.assist.AssistContent); method public void onProvideAssistData(android.os.Bundle); + method public void onProvideKeyboardShortcuts(java.util.List<android.view.KeyboardShortcutGroup>, android.view.Menu); method public android.net.Uri onProvideReferrer(); method public void onRequestPermissionsResult(int, java.lang.String[], int[]); method protected void onRestart(); @@ -4189,6 +4190,7 @@ package android.app { method public void onPanelClosed(int, android.view.Menu); method public boolean onPrepareOptionsMenu(android.view.Menu); method public boolean onPreparePanel(int, android.view.View, android.view.Menu); + method public void onProvideKeyboardShortcuts(java.util.List<android.view.KeyboardShortcutGroup>, android.view.Menu); method public void onRestoreInstanceState(android.os.Bundle); method public android.os.Bundle onSaveInstanceState(); method public boolean onSearchRequested(android.view.SearchEvent); @@ -33516,6 +33518,7 @@ package android.service.dreams { method public boolean onMenuOpened(int, android.view.Menu); method public void onPanelClosed(int, android.view.Menu); method public boolean onPreparePanel(int, android.view.View, android.view.Menu); + method public void onProvideKeyboardShortcuts(java.util.List<android.view.KeyboardShortcutGroup>, android.view.Menu); method public boolean onSearchRequested(android.view.SearchEvent); method public boolean onSearchRequested(); method public void onWakeUp(); @@ -40406,6 +40409,27 @@ package android.view { method public void startTracking(android.view.KeyEvent, java.lang.Object); } + public final class KeyboardShortcutGroup implements android.os.Parcelable { + ctor public KeyboardShortcutGroup(java.lang.CharSequence, java.util.List<android.view.KeyboardShortcutInfo>); + ctor public KeyboardShortcutGroup(java.lang.CharSequence); + method public void addItem(android.view.KeyboardShortcutInfo); + method public int describeContents(); + method public java.util.List<android.view.KeyboardShortcutInfo> getItems(); + method public java.lang.CharSequence getLabel(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.view.KeyboardShortcutGroup> CREATOR; + } + + public final class KeyboardShortcutInfo implements android.os.Parcelable { + ctor public KeyboardShortcutInfo(java.lang.CharSequence, char, int); + method public int describeContents(); + method public char getBaseCharacter(); + method public java.lang.CharSequence getLabel(); + method public int getModifiers(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.view.KeyboardShortcutInfo> CREATOR; + } + public abstract class LayoutInflater { ctor protected LayoutInflater(android.content.Context); ctor protected LayoutInflater(android.view.LayoutInflater, android.content.Context); @@ -42407,6 +42431,7 @@ package android.view { method public abstract boolean onMenuOpened(int, android.view.Menu); method public abstract void onPanelClosed(int, android.view.Menu); method public abstract boolean onPreparePanel(int, android.view.View, android.view.Menu); + method public abstract void onProvideKeyboardShortcuts(java.util.List<android.view.KeyboardShortcutGroup>, android.view.Menu); method public abstract boolean onSearchRequested(); method public abstract boolean onSearchRequested(android.view.SearchEvent); method public abstract void onWindowAttributesChanged(android.view.WindowManager.LayoutParams); diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 34527c2623c6..e31259692501 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -62,6 +62,7 @@ import android.database.Cursor; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.drawable.Drawable; +import android.graphics.drawable.Icon; import android.media.AudioManager; import android.media.session.MediaController; import android.net.Uri; @@ -71,6 +72,7 @@ import android.os.Handler; import android.os.IBinder; import android.os.Looper; import android.os.Parcelable; +import android.os.PersistableBundle; import android.os.RemoteException; import android.os.StrictMode; import android.os.UserHandle; @@ -78,23 +80,28 @@ import android.text.Selection; import android.text.SpannableStringBuilder; import android.text.TextUtils; import android.text.method.TextKeyListener; +import android.transition.Scene; +import android.transition.TransitionManager; +import android.util.ArrayMap; import android.util.AttributeSet; import android.util.EventLog; import android.util.Log; import android.util.PrintWriterPrinter; import android.util.Slog; import android.util.SparseArray; +import android.util.SuperNotCalledException; import android.view.ActionMode; import android.view.ContextMenu; import android.view.ContextMenu.ContextMenuInfo; import android.view.ContextThemeWrapper; import android.view.KeyEvent; +import android.view.KeyboardShortcutGroup; +import android.view.KeyboardShortcutInfo; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.MotionEvent; -import com.android.internal.policy.PhoneWindow; import android.view.SearchEvent; import android.view.View; import android.view.View.OnCreateContextMenuListener; @@ -103,10 +110,17 @@ import android.view.ViewGroup.LayoutParams; import android.view.ViewManager; import android.view.ViewRootImpl; import android.view.Window; +import android.view.Window.WindowControllerCallback; import android.view.WindowManager; import android.view.WindowManagerGlobal; import android.view.accessibility.AccessibilityEvent; import android.widget.AdapterView; +import android.widget.Toolbar; + +import com.android.internal.app.IVoiceInteractor; +import com.android.internal.app.ToolbarActionBar; +import com.android.internal.app.WindowDecorActionBar; +import com.android.internal.policy.PhoneWindow; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -116,6 +130,8 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; +import static java.lang.Character.MIN_VALUE; + /** * An activity is a single, focused thing that the user can do. Almost all * activities interact with the user, so the Activity class takes care of @@ -1594,6 +1610,30 @@ public class Activity extends ContextThemeWrapper public void onProvideAssistContent(AssistContent outContent) { } + @Override + public void onProvideKeyboardShortcuts(List<KeyboardShortcutGroup> data, Menu menu) { + if (menu == null) { + return; + } + KeyboardShortcutGroup group = null; + int menuSize = menu.size(); + for (int i = 0; i < menuSize; ++i) { + final MenuItem item = menu.getItem(i); + final CharSequence title = item.getTitle(); + final char alphaShortcut = item.getAlphabeticShortcut(); + if (title != null && alphaShortcut != MIN_VALUE) { + if (group == null) { + group = new KeyboardShortcutGroup(null /* no label */); + } + group.addItem(new KeyboardShortcutInfo( + title, alphaShortcut, KeyEvent.META_CTRL_ON)); + } + } + if (group != null) { + data.add(group); + } + } + /** * Ask to have the current assistant shown to the user. This only works if the calling * activity is the current foreground activity. It is the same as calling diff --git a/core/java/android/app/Dialog.java b/core/java/android/app/Dialog.java index 6e8e2c44ce37..79461b4863ef 100644 --- a/core/java/android/app/Dialog.java +++ b/core/java/android/app/Dialog.java @@ -21,9 +21,8 @@ import android.annotation.DrawableRes; import android.annotation.IdRes; import android.annotation.LayoutRes; import android.annotation.NonNull; -import android.annotation.StringRes; - import android.annotation.Nullable; +import android.annotation.StringRes; import android.annotation.StyleRes; import android.content.ComponentName; import android.content.Context; @@ -44,11 +43,11 @@ import android.view.ContextMenu.ContextMenuInfo; import android.view.ContextThemeWrapper; import android.view.Gravity; import android.view.KeyEvent; +import android.view.KeyboardShortcutGroup; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; import android.view.MotionEvent; -import com.android.internal.policy.PhoneWindow; import android.view.SearchEvent; import android.view.View; import android.view.View.OnCreateContextMenuListener; @@ -60,8 +59,10 @@ import android.view.accessibility.AccessibilityEvent; import com.android.internal.R; import com.android.internal.app.WindowDecorActionBar; +import com.android.internal.policy.PhoneWindow; import java.lang.ref.WeakReference; +import java.util.List; /** * Base class for Dialogs. @@ -1081,6 +1082,13 @@ public class Dialog implements DialogInterface, Window.Callback, } /** + * {@inheritDoc} + */ + @Override + public void onProvideKeyboardShortcuts(List<KeyboardShortcutGroup> data, Menu menu) { + } + + /** * @return The activity associated with this dialog, or null if there is no associated activity. */ private ComponentName getAssociatedActivity() { diff --git a/core/java/android/service/dreams/DreamService.java b/core/java/android/service/dreams/DreamService.java index bb46e8302de6..816ecde84377 100644 --- a/core/java/android/service/dreams/DreamService.java +++ b/core/java/android/service/dreams/DreamService.java @@ -15,9 +15,6 @@ */ package android.service.dreams; -import java.io.FileDescriptor; -import java.io.PrintWriter; - import android.annotation.IdRes; import android.annotation.LayoutRes; import android.annotation.Nullable; @@ -33,27 +30,32 @@ import android.os.IBinder; import android.os.PowerManager; import android.os.RemoteException; import android.os.ServiceManager; +import android.util.MathUtils; import android.util.Slog; import android.view.ActionMode; import android.view.Display; import android.view.KeyEvent; +import android.view.KeyboardShortcutGroup; import android.view.Menu; import android.view.MenuItem; import android.view.MotionEvent; -import com.android.internal.policy.PhoneWindow; import android.view.SearchEvent; import android.view.View; import android.view.ViewGroup; import android.view.Window; import android.view.WindowManager; -import android.view.WindowManagerGlobal; import android.view.WindowManager.LayoutParams; +import android.view.WindowManagerGlobal; import android.view.accessibility.AccessibilityEvent; -import android.util.MathUtils; +import com.android.internal.policy.PhoneWindow; import com.android.internal.util.DumpUtils; import com.android.internal.util.DumpUtils.Dump; +import java.io.FileDescriptor; +import java.io.PrintWriter; +import java.util.List; + /** * Extend this class to implement a custom dream (available to the user as a "Daydream"). * @@ -365,6 +367,11 @@ public class DreamService extends Service implements Window.Callback { @Override public void onActionModeFinished(ActionMode mode) { } + + /** {@inheritDoc} */ + @Override + public void onProvideKeyboardShortcuts(List<KeyboardShortcutGroup> data, Menu menu) { + } // end Window.Callback methods // begin public api diff --git a/core/java/android/view/IWindow.aidl b/core/java/android/view/IWindow.aidl index 9e478c1d1e73..923139406088 100644 --- a/core/java/android/view/IWindow.aidl +++ b/core/java/android/view/IWindow.aidl @@ -25,6 +25,8 @@ import android.view.DragEvent; import android.view.KeyEvent; import android.view.MotionEvent; +import com.android.internal.os.IResultReceiver; + /** * API back to a client window that the Window Manager uses to inform it of * interesting things happening. @@ -83,4 +85,9 @@ oneway interface IWindow { * Called for non-application windows when the enter animation has completed. */ void dispatchWindowShown(); + + /** + * Called when Keyboard Shortcuts are requested for the window. + */ + void requestAppKeyboardShortcuts(IResultReceiver receiver); } diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl index 84d312d59b64..b045c17a9295 100644 --- a/core/java/android/view/IWindowManager.aidl +++ b/core/java/android/view/IWindowManager.aidl @@ -17,6 +17,7 @@ package android.view; import com.android.internal.app.IAssistScreenshotReceiver; +import com.android.internal.os.IResultReceiver; import com.android.internal.view.IInputContext; import com.android.internal.view.IInputMethodClient; @@ -370,4 +371,11 @@ interface IWindowManager * @param alpha The translucency of the dim layer, between 0 and 1. */ void setResizeDimLayer(boolean visible, int targetStackId, float alpha); + + /** + * Requests Keyboard Shortcuts from the displayed window. + * + * @param receiver The receiver to deliver the results to. + */ + void requestAppKeyboardShortcuts(IResultReceiver receiver); } diff --git a/core/java/android/view/KeyboardShortcutGroup.java b/core/java/android/view/KeyboardShortcutGroup.java new file mode 100644 index 000000000000..013255b9a612 --- /dev/null +++ b/core/java/android/view/KeyboardShortcutGroup.java @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.view; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import static com.android.internal.util.Preconditions.checkNotNull; + +/** + * A group of {@link KeyboardShortcutInfo}. + */ +public final class KeyboardShortcutGroup implements Parcelable { + private final CharSequence mLabel; + private final List<KeyboardShortcutInfo> mItems; + + /** + * @param label The title to be used for this group, or null if there is none. + * @param items The set of items to be included. + */ + public KeyboardShortcutGroup(@Nullable CharSequence label, + @NonNull List<KeyboardShortcutInfo> items) { + mLabel = label; + mItems = new ArrayList<>(checkNotNull(items)); + } + + /** + * @param label The title to be used for this group, or null if there is none. + */ + public KeyboardShortcutGroup(@Nullable CharSequence label) { + this(label, Collections.<KeyboardShortcutInfo>emptyList()); + } + + private KeyboardShortcutGroup(Parcel source) { + mItems = new ArrayList<>(); + mLabel = source.readCharSequence(); + source.readTypedList(mItems, KeyboardShortcutInfo.CREATOR); + } + + /** + * Returns the label to be used to describe this group. + */ + public CharSequence getLabel() { + return mLabel; + } + + /** + * Returns the list of items included in this group. + */ + public List<KeyboardShortcutInfo> getItems() { + return mItems; + } + + /** + * Adds an item to the existing list. + * + * @param item The item to be added. + */ + public void addItem(KeyboardShortcutInfo item) { + mItems.add(item); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeCharSequence(mLabel); + dest.writeTypedList(mItems); + } + + public static final Creator<KeyboardShortcutGroup> CREATOR = + new Creator<KeyboardShortcutGroup>() { + public KeyboardShortcutGroup createFromParcel(Parcel source) { + return new KeyboardShortcutGroup(source); + } + public KeyboardShortcutGroup[] newArray(int size) { + return new KeyboardShortcutGroup[size]; + } + }; +}
\ No newline at end of file diff --git a/core/java/android/view/KeyboardShortcutInfo.java b/core/java/android/view/KeyboardShortcutInfo.java new file mode 100644 index 000000000000..2c9006deb189 --- /dev/null +++ b/core/java/android/view/KeyboardShortcutInfo.java @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.view; + +import android.annotation.Nullable; +import android.graphics.drawable.Icon; +import android.os.Parcel; +import android.os.Parcelable; + +import static com.android.internal.util.Preconditions.checkArgument; +import static java.lang.Character.MIN_VALUE; + +/** + * Information about a Keyboard Shortcut. + */ +public final class KeyboardShortcutInfo implements Parcelable { + private final CharSequence mLabel; + private final Icon mIcon; + private final char mBaseCharacter; + private final int mModifiers; + + /** + * @param label The label that identifies the action performed by this shortcut. + * @param icon An icon that identifies the action performed by this shortcut. + * @param baseCharacter The character that triggers the shortcut. + * @param modifiers The set of modifiers that, combined with the key, trigger the shortcut. + * These should be a combination of {@link KeyEvent#META_CTRL_ON}, + * {@link KeyEvent#META_SHIFT_ON}, {@link KeyEvent#META_META_ON} and + * {@link KeyEvent#META_ALT_ON}. + * + * @hide + */ + public KeyboardShortcutInfo( + @Nullable CharSequence label, @Nullable Icon icon, char baseCharacter, int modifiers) { + mLabel = label; + mIcon = icon; + checkArgument(baseCharacter != MIN_VALUE); + mBaseCharacter = baseCharacter; + mModifiers = modifiers; + } + + /** + * Convenience constructor for shortcuts with a label and no icon. + * + * @param label The label that identifies the action performed by this shortcut. + * @param baseCharacter The character that triggers the shortcut. + * @param modifiers The set of modifiers that, combined with the key, trigger the shortcut. + * These should be a combination of {@link KeyEvent#META_CTRL_ON}, + * {@link KeyEvent#META_SHIFT_ON}, {@link KeyEvent#META_META_ON} and + * {@link KeyEvent#META_ALT_ON}. + */ + public KeyboardShortcutInfo(CharSequence label, char baseCharacter, int modifiers) { + mLabel = label; + checkArgument(baseCharacter != MIN_VALUE); + mBaseCharacter = baseCharacter; + mModifiers = modifiers; + mIcon = null; + } + + private KeyboardShortcutInfo(Parcel source) { + mLabel = source.readCharSequence(); + mIcon = (Icon) source.readParcelable(null); + mBaseCharacter = (char) source.readInt(); + mModifiers = source.readInt(); + } + + /** + * Returns the label to be used to describe this shortcut. + */ + @Nullable + public CharSequence getLabel() { + return mLabel; + } + + /** + * Returns the icon to be used to describe this shortcut. + * + * @hide + */ + @Nullable + public Icon getIcon() { + return mIcon; + } + + /** + * Returns the base character that, combined with the modifiers, triggers this shortcut. + */ + public char getBaseCharacter() { + return mBaseCharacter; + } + + /** + * Returns the set of modifiers that, combined with the key, trigger this shortcut. + */ + public int getModifiers() { + return mModifiers; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeCharSequence(mLabel); + dest.writeParcelable(mIcon, 0); + dest.writeInt(mBaseCharacter); + dest.writeInt(mModifiers); + } + + public static final Creator<KeyboardShortcutInfo> CREATOR = + new Creator<KeyboardShortcutInfo>() { + public KeyboardShortcutInfo createFromParcel(Parcel source) { + return new KeyboardShortcutInfo(source); + } + public KeyboardShortcutInfo[] newArray(int size) { + return new KeyboardShortcutInfo[size]; + } + }; +}
\ No newline at end of file diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 68f1ac3c1108..5559d4dbe705 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -79,10 +79,11 @@ import android.util.StateSet; import android.util.SuperNotCalledException; import android.util.TypedValue; import android.view.ContextMenu.ContextMenuInfo; -import android.view.AccessibilityIterators.TextSegmentIterator; import android.view.AccessibilityIterators.CharacterTextSegmentIterator; -import android.view.AccessibilityIterators.WordTextSegmentIterator; import android.view.AccessibilityIterators.ParagraphTextSegmentIterator; +import android.view.AccessibilityIterators.TextSegmentIterator; +import android.view.AccessibilityIterators.WordTextSegmentIterator; +import android.view.ViewGroup.LayoutParams; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityEventSource; import android.view.accessibility.AccessibilityManager; @@ -21715,6 +21716,13 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } /** + * @hide + */ + public void requestKeyboardShortcuts(List<KeyboardShortcutGroup> data) { + // Do nothing. + } + + /** * Interface definition for a callback to be invoked when a hardware key event is * dispatched to this view. The callback will be invoked before the key event is * given to the view. This is only useful for hardware keyboards; a software input diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 3eb2e37be781..a14f0dcc2541 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -79,6 +79,7 @@ import android.view.inputmethod.InputMethodManager; import android.widget.Scroller; import com.android.internal.R; +import com.android.internal.os.IResultReceiver; import com.android.internal.os.SomeArgs; import com.android.internal.policy.PhoneFallbackEventHandler; import com.android.internal.view.BaseSurfaceHolder; @@ -3235,6 +3236,7 @@ public final class ViewRootImpl implements ViewParent, private final static int MSG_WINDOW_MOVED = 23; private final static int MSG_SYNTHESIZE_INPUT_EVENT = 24; private final static int MSG_DISPATCH_WINDOW_SHOWN = 25; + private final static int MSG_REQUEST_KEYBOARD_SHORTCUTS = 26; final class ViewRootHandler extends Handler { @Override @@ -3511,7 +3513,11 @@ public final class ViewRootImpl implements ViewParent, } break; case MSG_DISPATCH_WINDOW_SHOWN: { handleDispatchWindowShown(); - } + } break; + case MSG_REQUEST_KEYBOARD_SHORTCUTS: { + IResultReceiver receiver = (IResultReceiver) msg.obj; + handleRequestKeyboardShortcuts(receiver); + } break; } } } @@ -5404,6 +5410,19 @@ public final class ViewRootImpl implements ViewParent, mAttachInfo.mTreeObserver.dispatchOnWindowShown(); } + public void handleRequestKeyboardShortcuts(IResultReceiver receiver) { + Bundle data = new Bundle(); + ArrayList<KeyboardShortcutGroup> list = new ArrayList<>(); + if (mView != null) { + mView.requestKeyboardShortcuts(list); + } + data.putParcelableArrayList(WindowManager.PARCEL_KEY_SHORTCUTS_ARRAY, list); + try { + receiver.send(0, data); + } catch (RemoteException e) { + } + } + public void getLastTouchPoint(Point outLocation) { outLocation.x = (int) mLastTouchPoint.x; outLocation.y = (int) mLastTouchPoint.y; @@ -6333,6 +6352,10 @@ public final class ViewRootImpl implements ViewParent, } } + public void dispatchRequestKeyboardShortcuts(IResultReceiver receiver) { + mHandler.obtainMessage(MSG_REQUEST_KEYBOARD_SHORTCUTS, receiver).sendToTarget(); + } + /** * Post a callback to send a * {@link AccessibilityEvent#TYPE_WINDOW_CONTENT_CHANGED} event. @@ -6906,6 +6929,14 @@ public final class ViewRootImpl implements ViewParent, viewAncestor.dispatchWindowShown(); } } + + @Override + public void requestAppKeyboardShortcuts(IResultReceiver receiver) { + ViewRootImpl viewAncestor = mViewAncestor.get(); + if (viewAncestor != null) { + viewAncestor.dispatchRequestKeyboardShortcuts(receiver); + } + } } public static final class CalledFromWrongThreadException extends AndroidRuntimeException { diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java index 0b06d1591731..d89369bef170 100644 --- a/core/java/android/view/Window.java +++ b/core/java/android/view/Window.java @@ -42,6 +42,8 @@ import android.transition.Transition; import android.transition.TransitionManager; import android.view.accessibility.AccessibilityEvent; +import java.util.List; + /** * Abstract base class for a top-level window look and behavior policy. An * instance of this class should be used as the top-level view added to the @@ -556,6 +558,15 @@ public abstract class Window { * @param mode The mode that was just finished. */ public void onActionModeFinished(ActionMode mode); + + /** + * Called when Keyboard Shortcuts are requested for the current window. + * + * @param data The data list to populate with shortcuts. + * @param menu The current menu, which may be null. + */ + public void onProvideKeyboardShortcuts( + List<KeyboardShortcutGroup> data, @Nullable Menu menu); } /** @hide */ diff --git a/core/java/android/view/WindowCallbackWrapper.java b/core/java/android/view/WindowCallbackWrapper.java index 8ce1f8c7e310..bed74e95ae0a 100644 --- a/core/java/android/view/WindowCallbackWrapper.java +++ b/core/java/android/view/WindowCallbackWrapper.java @@ -19,6 +19,8 @@ package android.view; import android.view.accessibility.AccessibilityEvent; +import java.util.List; + /** * A simple decorator stub for Window.Callback that passes through any calls * to the wrapped instance as a base implementation. Call super.foo() to call into @@ -150,5 +152,10 @@ public class WindowCallbackWrapper implements Window.Callback { public void onActionModeFinished(ActionMode mode) { mWrapped.onActionModeFinished(mode); } + + @Override + public void onProvideKeyboardShortcuts(List<KeyboardShortcutGroup> data, Menu menu) { + mWrapped.onProvideKeyboardShortcuts(data, menu); + } } diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java index 251f4c802ce6..772eeec880ee 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -29,6 +29,8 @@ import android.os.Parcelable; import android.text.TextUtils; import android.util.Log; +import java.util.List; + /** * The interface that apps use to talk to the window manager. @@ -118,6 +120,34 @@ public interface WindowManager extends ViewManager { */ public void removeViewImmediate(View view); + /** + * Used to asynchronously request Keyboard Shortcuts from the focused window. + * + * @hide + */ + public interface KeyboardShortcutsReceiver { + /** + * Callback used when the focused window keyboard shortcuts are ready to be displayed. + * + * @param result The keyboard shortcuts to be displayed. + */ + void onKeyboardShortcutsReceived(List<KeyboardShortcutGroup> result); + } + + /** + * @hide + */ + public static final String PARCEL_KEY_SHORTCUTS_ARRAY = "shortcuts_array"; + + /** + * Request for keyboard shortcuts to be retrieved asynchronously. + * + * @param receiver The callback to be triggered when the result is ready. + * + * @hide + */ + public void requestAppKeyboardShortcuts(final KeyboardShortcutsReceiver receiver); + public static class LayoutParams extends ViewGroup.LayoutParams implements Parcelable { /** * X position for this window. With the default gravity it is ignored. diff --git a/core/java/android/view/WindowManagerImpl.java b/core/java/android/view/WindowManagerImpl.java index 98e9f54c13f9..6e11671016b4 100644 --- a/core/java/android/view/WindowManagerImpl.java +++ b/core/java/android/view/WindowManagerImpl.java @@ -17,7 +17,15 @@ package android.view; import android.annotation.NonNull; +import android.content.Context; +import android.os.Bundle; import android.os.IBinder; +import android.os.RemoteException; + +import com.android.internal.os.IResultReceiver; +import com.android.internal.R; + +import java.util.List; /** * Provides low-level communication with the system window manager for @@ -117,6 +125,23 @@ public final class WindowManagerImpl implements WindowManager { } @Override + public void requestAppKeyboardShortcuts(final KeyboardShortcutsReceiver receiver) { + IResultReceiver resultReceiver = new IResultReceiver.Stub() { + @Override + public void send(int resultCode, Bundle resultData) throws RemoteException { + List<KeyboardShortcutGroup> result = + resultData.getParcelableArrayList(PARCEL_KEY_SHORTCUTS_ARRAY); + receiver.onKeyboardShortcutsReceived(result); + } + }; + try { + WindowManagerGlobal.getWindowManagerService() + .requestAppKeyboardShortcuts(resultReceiver); + } catch (RemoteException e) { + } + } + + @Override public Display getDefaultDisplay() { return mDisplay; } diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java index cea9867b8d88..7ae0efb6030b 100644 --- a/core/java/com/android/internal/policy/DecorView.java +++ b/core/java/com/android/internal/policy/DecorView.java @@ -17,6 +17,7 @@ package com.android.internal.policy; import com.android.internal.R; +import com.android.internal.policy.PhoneWindow.PanelFeatureState; import com.android.internal.policy.PhoneWindow.PhoneWindowMenuCallback; import com.android.internal.view.FloatingActionMode; import com.android.internal.view.RootViewSurfaceTaker; @@ -28,6 +29,8 @@ import com.android.internal.widget.BackgroundFallback; import com.android.internal.widget.DecorCaptionView; import com.android.internal.widget.FloatingToolbar; +import java.util.List; + import android.animation.Animator; import android.animation.ObjectAnimator; import android.app.ActivityManager; @@ -48,6 +51,7 @@ import android.view.ContextThemeWrapper; import android.view.Gravity; import android.view.InputQueue; import android.view.KeyEvent; +import android.view.KeyboardShortcutGroup; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; @@ -85,6 +89,7 @@ import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATIO import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; +import static com.android.internal.policy.PhoneWindow.FEATURE_OPTIONS_PANEL; /** @hide */ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, WindowCallbacks { @@ -1956,6 +1961,21 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind res.getConfiguration().screenWidthDp, res.getDisplayMetrics()); } + /** + * @hide + */ + @Override + public void requestKeyboardShortcuts(List<KeyboardShortcutGroup> list) { + final PanelFeatureState st = mWindow.getPanelState(FEATURE_OPTIONS_PANEL, false); + if (!mWindow.isDestroyed() && st != null && mWindow.getCallback() != null) { + try { + mWindow.getCallback().onProvideKeyboardShortcuts(list, st.menu); + } catch (AbstractMethodError e) { + // We run into this if the app is using supportlib. + } + } + } + private static class ColorViewState { View view = null; int targetVisibility = View.INVISIBLE; diff --git a/core/java/com/android/internal/view/BaseIWindow.java b/core/java/com/android/internal/view/BaseIWindow.java index 8699843eb83b..aa4b564c3800 100644 --- a/core/java/com/android/internal/view/BaseIWindow.java +++ b/core/java/com/android/internal/view/BaseIWindow.java @@ -25,6 +25,8 @@ import android.view.DragEvent; import android.view.IWindow; import android.view.IWindowSession; +import com.android.internal.os.IResultReceiver; + public class BaseIWindow extends IWindow.Stub { private IWindowSession mSession; public int mSeq; @@ -103,4 +105,8 @@ public class BaseIWindow extends IWindow.Stub { @Override public void dispatchWindowShown() { } + + @Override + public void requestAppKeyboardShortcuts(IResultReceiver receiver) { + } } diff --git a/core/tests/coretests/src/com/android/internal/policy/PhoneWindowActionModeTest.java b/core/tests/coretests/src/com/android/internal/policy/PhoneWindowActionModeTest.java index 48590c1ec463..1966313a41f0 100644 --- a/core/tests/coretests/src/com/android/internal/policy/PhoneWindowActionModeTest.java +++ b/core/tests/coretests/src/com/android/internal/policy/PhoneWindowActionModeTest.java @@ -22,6 +22,7 @@ import android.test.suitebuilder.annotation.SmallTest; import android.view.ActionMode; import android.view.ActionMode.Callback; import android.view.KeyEvent; +import android.view.KeyboardShortcutGroup; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; @@ -32,6 +33,8 @@ import android.view.Window; import android.view.WindowManager.LayoutParams; import android.view.accessibility.AccessibilityEvent; +import java.util.List; + /** * Tests {@link PhoneWindow}'s {@link ActionMode} related methods. */ @@ -288,6 +291,9 @@ public final class PhoneWindowActionModeTest @Override public void onActionModeFinished(ActionMode mode) {} + + @Override + public void onProvideKeyboardShortcuts(List<KeyboardShortcutGroup> data, Menu menu) {} } private static final class MockActionModeCallback implements ActionMode.Callback { diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 4136c110c649..0e4f98f9c715 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -1281,4 +1281,13 @@ <!-- Summary of switch for battery saver [CHAR LIMIT=NONE] --> <string name="battery_detail_switch_summary">Reduces performance and background data</string> + <!-- User visible title for the system-wide keyboard shortcuts list. --> + <string name="keyboard_shortcut_group_system">System</string> + <!-- User visible title for the keyboard shortcut that takes the user to the home screen. --> + <string name="keyboard_shortcut_group_system_home">Home</string> + <!-- User visible title for the the keyboard shortcut that takes the user to the recents screen. --> + <string name="keyboard_shortcut_group_system_recents">Recents</string> + <!-- User visible title for the the keyboard shortcut that triggers the back action. --> + <string name="keyboard_shortcut_group_system_back">Back</string> + </resources> diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java index 3406da94e53c..f9e825aaf064 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java +++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java @@ -60,6 +60,7 @@ import android.util.Pair; import android.view.Display; import android.view.IDockedStackListener; import android.view.WindowManager; +import android.view.WindowManager.KeyboardShortcutsReceiver; import android.view.WindowManagerGlobal; import android.view.accessibility.AccessibilityManager; import com.android.internal.app.AssistUtils; @@ -915,4 +916,8 @@ public class SystemServicesProxy { e.printStackTrace(); } } + + public void requestKeyboardShortcuts(Context context, KeyboardShortcutsReceiver receiver) { + mWm.requestAppKeyboardShortcuts(receiver); + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java index 3e0ea90f7694..b36fb7e65f7e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java @@ -20,50 +20,92 @@ import android.app.AlertDialog; import android.app.Dialog; import android.content.Context; import android.graphics.drawable.ColorDrawable; -import android.view.Gravity; +import android.os.Handler; +import android.util.Log; +import android.view.KeyEvent; +import android.view.KeyboardShortcutGroup; +import android.view.KeyboardShortcutInfo; import android.view.LayoutInflater; import android.view.View; import android.view.Window; import android.view.WindowManager; +import android.view.WindowManager.KeyboardShortcutsReceiver; import com.android.systemui.R; +import com.android.systemui.recents.Recents; + +import java.util.List; + +import static android.content.Context.LAYOUT_INFLATER_SERVICE; +import static android.graphics.Color.TRANSPARENT; +import static android.view.Gravity.TOP; +import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG; /** * Contains functionality for handling keyboard shortcuts. */ public class KeyboardShortcuts { + private static final String TAG = "KeyboardShortcuts"; + private Dialog mKeyboardShortcutsDialog; public KeyboardShortcuts() {} - public void toggleKeyboardShortcuts(Context context) { + public void toggleKeyboardShortcuts(final Context context) { if (mKeyboardShortcutsDialog == null) { - // Create dialog. - AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(context); - LayoutInflater inflater = (LayoutInflater) context.getSystemService( - Context.LAYOUT_INFLATER_SERVICE); - final View keyboardShortcutsView = inflater.inflate( - R.layout.keyboard_shortcuts_view, null); - - populateKeyboardShortcuts(keyboardShortcutsView.findViewById( - R.id.keyboard_shortcuts_wrapper)); - dialogBuilder.setView(keyboardShortcutsView); - mKeyboardShortcutsDialog = dialogBuilder.create(); - mKeyboardShortcutsDialog.setCanceledOnTouchOutside(true); - - // Setup window. - Window keyboardShortcutsWindow = mKeyboardShortcutsDialog.getWindow(); - keyboardShortcutsWindow.setType( - WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG); - keyboardShortcutsWindow.setBackgroundDrawable( - new ColorDrawable(android.graphics.Color.TRANSPARENT)); - keyboardShortcutsWindow.setGravity(Gravity.TOP); - mKeyboardShortcutsDialog.show(); + Recents.getSystemServices().requestKeyboardShortcuts(context, + new KeyboardShortcutsReceiver() { + @Override + public void onKeyboardShortcutsReceived( + final List<KeyboardShortcutGroup> result) { + KeyboardShortcutGroup systemGroup = new KeyboardShortcutGroup( + context.getString(R.string.keyboard_shortcut_group_system)); + systemGroup.addItem(new KeyboardShortcutInfo( + context.getString(R.string.keyboard_shortcut_group_system_home), + '\u2386', KeyEvent.META_META_ON)); + systemGroup.addItem(new KeyboardShortcutInfo( + context.getString(R.string.keyboard_shortcut_group_system_back), + '\u007F', KeyEvent.META_META_ON)); + systemGroup.addItem(new KeyboardShortcutInfo( + context.getString(R.string.keyboard_shortcut_group_system_recents), + '\u0009', KeyEvent.META_ALT_ON)); + result.add(systemGroup); + Log.i(TAG, "Keyboard shortcuts received: " + String.valueOf(result)); + showKeyboardShortcutsDialog(context); + } + }); } else { dismissKeyboardShortcutsDialog(); } } + private void showKeyboardShortcutsDialog(Context context) { + // Create dialog. + AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(context); + LayoutInflater inflater = (LayoutInflater) context.getSystemService( + LAYOUT_INFLATER_SERVICE); + final View keyboardShortcutsView = inflater.inflate( + R.layout.keyboard_shortcuts_view, null); + + populateKeyboardShortcuts(keyboardShortcutsView.findViewById( + R.id.keyboard_shortcuts_wrapper)); + dialogBuilder.setView(keyboardShortcutsView); + mKeyboardShortcutsDialog = dialogBuilder.create(); + mKeyboardShortcutsDialog.setCanceledOnTouchOutside(true); + + // Setup window. + Window keyboardShortcutsWindow = mKeyboardShortcutsDialog.getWindow(); + keyboardShortcutsWindow.setType(TYPE_SYSTEM_DIALOG); + keyboardShortcutsWindow.setBackgroundDrawable( + new ColorDrawable(TRANSPARENT)); + keyboardShortcutsWindow.setGravity(TOP); + keyboardShortcutsView.post(new Runnable() { + public void run() { + mKeyboardShortcutsDialog.show(); + } + }); + } + public void dismissKeyboardShortcutsDialog() { if (mKeyboardShortcutsDialog != null) { mKeyboardShortcutsDialog.dismiss(); diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 685df25910d6..7d142eca0bb4 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -119,6 +119,7 @@ import android.widget.Toast; import com.android.internal.R; import com.android.internal.app.IAssistScreenshotReceiver; +import com.android.internal.os.IResultReceiver; import com.android.internal.util.FastPrintWriter; import com.android.internal.view.IInputContext; import com.android.internal.view.IInputMethodClient; @@ -10269,6 +10270,14 @@ public class WindowManagerService extends IWindowManager.Stub listener); } + @Override + public void requestAppKeyboardShortcuts(IResultReceiver receiver) { + try { + getFocusedWindow().mClient.requestAppKeyboardShortcuts(receiver); + } catch (RemoteException e) { + } + } + private final class LocalService extends WindowManagerInternal { @Override public void requestTraversalFromDisplayManager() { |