Keyboard shortcuts: icons for shortcut items

Introduces the ability to display a shortcut item as
an image (if it has a resource attached). Also added
vector drawables for: up, down, left, right, enter, meta
and backspace.
Note that accessibilty wise the drawables dynamically scale
to the height of the text items (which have the text size
set in sp).

Bug: 27455410
Change-Id: I49739313d83cf661bea9378108a1fa3d6c51bd2e
This commit is contained in:
Andrei Stingaceanu
2016-03-31 15:53:33 +01:00
parent 8c83a85e26
commit d15191076d
12 changed files with 310 additions and 23 deletions

View File

@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2016 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.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path android:pathData="M0 0h24v24H0z" />
<path android:fillColor="@color/ksh_key_item_color"
android:pathData="M22 3H7c-.69 0-1.23 .35 -1.59 .88 L0 12l5.41 8.11c.36 .53 .9 .89
1.59 .89 h15c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-3 12.59L17.59 17 14 13.41 10.41 17 9 15.59
12.59 12 9 8.41 10.41 7 14 10.59 17.59 7 19 8.41 15.41 12 19 15.59z" />
</vector>

View File

@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2016 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
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path android:fillColor="@color/ksh_key_item_color"
android:pathData="M7.41 7.84L12 12.42l4.59-4.58L18 9.25l-6 6-6-6z" />
<path android:pathData="M0-.75h24v24H0z" />
</vector>

View File

@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2016 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
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path android:pathData="M0 0h24v24H0z" />
<path android:fillColor="@color/ksh_key_item_color"
android:pathData="M19 7v4H5.83l3.58-3.59L8 6l-6 6 6 6 1.41-1.41L5.83 13H21V7z" />
</vector>

View File

@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2016 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
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path android:fillColor="@color/ksh_key_item_color"
android:pathData="M15.41 7.41L14 6l-6 6 6 6 1.41-1.41L10.83 12z" />
<path android:pathData="M0 0h24v24H0z" />
</vector>

View File

@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2016 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
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path android:fillColor="@color/ksh_key_item_color"
android:pathData="M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91
3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27 .28 v.79l5 4.99L20.49
19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5
14z" />
<path android:pathData="M0 0h24v24H0z" />
</vector>

View File

@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2016 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
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path android:fillColor="@color/ksh_key_item_color"
android:pathData="M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z" />
<path android:pathData="M0 0h24v24H0z" />
</vector>

View File

@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2016 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
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path android:fillColor="@color/ksh_key_item_color"
android:pathData="M7.41 15.41L12 10.83l4.59 4.58L18 14l-6-6-6 6z" />
<path android:pathData="M0 0h24v24H0z" />
</vector>

View File

@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2016 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
-->
<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="@dimen/ksh_item_padding"
android:layout_marginStart="@dimen/ksh_item_margin_start"
android:scaleType="fitXY"
android:background="@color/ksh_key_item_background"/>

View File

@@ -17,9 +17,9 @@
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="4dp"
android:padding="4dp"
android:background="#EEEEEE"
android:textColor="#8C000000"
android:padding="@dimen/ksh_item_padding"
android:layout_marginStart="@dimen/ksh_item_margin_start"
android:background="@color/ksh_key_item_background"
android:textColor="@color/ksh_key_item_color"
android:singleLine="true"
android:textSize="14sp"/>
android:textSize="@dimen/ksh_item_text_size"/>

View File

@@ -169,6 +169,8 @@
<color name="ksh_system_group_color">#ff00bcd4</color>
<color name="ksh_application_group_color">#fff44336</color>
<color name="ksh_keyword_color">#d9000000</color>
<color name="ksh_key_item_color">@color/material_grey_600</color>
<color name="ksh_key_item_background">#eeeeee</color>
<!-- Background color of edit overflow -->
<color name="qs_edit_overflow_bg">#455A64</color>

View File

@@ -561,6 +561,9 @@
<!-- Keyboard shortcuts helper -->
<dimen name="ksh_layout_width">@dimen/match_parent</dimen>
<dimen name="ksh_item_text_size">14sp</dimen>
<dimen name="ksh_item_padding">4dp</dimen>
<dimen name="ksh_item_margin_start">4dp</dimen>
<!-- Recents Layout -->

View File

@@ -21,6 +21,9 @@ import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
import android.hardware.input.InputManager;
import android.os.Handler;
import android.os.Looper;
@@ -37,6 +40,7 @@ import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowManager.KeyboardShortcutsReceiver;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
@@ -52,13 +56,15 @@ import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG;
/**
* Contains functionality for handling keyboard shortcuts.
*/
public class KeyboardShortcuts {
public final class KeyboardShortcuts {
private static final String TAG = KeyboardShortcuts.class.getSimpleName();
private static final SparseArray<String> SPECIAL_CHARACTER_NAMES = new SparseArray<>();
private static final SparseArray<Drawable> SPECIAL_CHARACTER_DRAWABLES = new SparseArray<>();
private static final SparseArray<String> MODIFIER_NAMES = new SparseArray<>();
private static final SparseArray<Drawable> MODIFIER_DRAWABLES = new SparseArray<>();
private static boolean resourcesLoaded = false;
private static void loadSpecialCharacterNames(Context context) {
private static void loadResources(Context context) {
SPECIAL_CHARACTER_NAMES.put(
KeyEvent.KEYCODE_HOME, context.getString(R.string.keyboard_key_home));
SPECIAL_CHARACTER_NAMES.put(
@@ -197,12 +203,30 @@ public class KeyboardShortcuts {
SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_HENKAN, "変換");
SPECIAL_CHARACTER_NAMES.put(KeyEvent.KEYCODE_KATAKANA_HIRAGANA, "かな");
SPECIAL_CHARACTER_DRAWABLES.put(
KeyEvent.KEYCODE_DEL, context.getDrawable(R.drawable.ic_ksh_key_backspace));
SPECIAL_CHARACTER_DRAWABLES.put(
KeyEvent.KEYCODE_ENTER, context.getDrawable(R.drawable.ic_ksh_key_enter));
SPECIAL_CHARACTER_DRAWABLES.put(
KeyEvent.KEYCODE_DPAD_UP, context.getDrawable(R.drawable.ic_ksh_key_up));
SPECIAL_CHARACTER_DRAWABLES.put(
KeyEvent.KEYCODE_DPAD_RIGHT, context.getDrawable(R.drawable.ic_ksh_key_right));
SPECIAL_CHARACTER_DRAWABLES.put(
KeyEvent.KEYCODE_DPAD_DOWN, context.getDrawable(R.drawable.ic_ksh_key_down));
SPECIAL_CHARACTER_DRAWABLES.put(
KeyEvent.KEYCODE_DPAD_LEFT, context.getDrawable(R.drawable.ic_ksh_key_left));
MODIFIER_NAMES.put(KeyEvent.META_META_ON, "Meta");
MODIFIER_NAMES.put(KeyEvent.META_CTRL_ON, "Ctrl");
MODIFIER_NAMES.put(KeyEvent.META_ALT_ON, "Alt");
MODIFIER_NAMES.put(KeyEvent.META_SHIFT_ON, "Shift");
MODIFIER_NAMES.put(KeyEvent.META_SYM_ON, "Sym");
MODIFIER_NAMES.put(KeyEvent.META_FUNCTION_ON, "Fn");
MODIFIER_DRAWABLES.put(
KeyEvent.META_META_ON, context.getDrawable(R.drawable.ic_ksh_key_meta));
resourcesLoaded = true;
}
private final Handler mHandler = new Handler(Looper.getMainLooper());
@@ -218,8 +242,8 @@ public class KeyboardShortcuts {
public KeyboardShortcuts(Context context) {
this.mContext = new ContextThemeWrapper(context, android.R.style.Theme_Material_Light);
if (SPECIAL_CHARACTER_NAMES.size() == 0) {
loadSpecialCharacterNames(context);
if (!resourcesLoaded) {
loadResources(context);
}
}
@@ -343,6 +367,8 @@ public class KeyboardShortcuts {
List<KeyboardShortcutGroup> keyboardShortcutGroups) {
LayoutInflater inflater = LayoutInflater.from(mContext);
final int keyboardShortcutGroupsSize = keyboardShortcutGroups.size();
// Needed to be able to scale the image items to the same height as the text items.
final int shortcutTextItemHeight = getShortcutTextItemHeight(inflater);
for (int i = 0; i < keyboardShortcutGroupsSize; i++) {
KeyboardShortcutGroup group = keyboardShortcutGroups.get(i);
TextView categoryTitle = (TextView) inflater.inflate(
@@ -364,7 +390,7 @@ public class KeyboardShortcuts {
Log.w(TAG, "Keyboard Shortcut contains key not on device, skipping.");
continue;
}
List<String> shortcutKeys = getHumanReadableShortcutKeys(info);
List<StringOrDrawable> shortcutKeys = getHumanReadableShortcutKeys(info);
if (shortcutKeys == null) {
// Ignore shortcuts we can't display keys for.
Log.w(TAG, "Keyboard Shortcut contains unsupported keys, skipping.");
@@ -380,11 +406,26 @@ public class KeyboardShortcuts {
.findViewById(R.id.keyboard_shortcuts_item_container);
final int shortcutKeysSize = shortcutKeys.size();
for (int k = 0; k < shortcutKeysSize; k++) {
String shortcutKey = shortcutKeys.get(k);
TextView shortcutKeyView = (TextView) inflater.inflate(
R.layout.keyboard_shortcuts_key_view, shortcutItemsContainer, false);
shortcutKeyView.setText(shortcutKey);
shortcutItemsContainer.addView(shortcutKeyView);
StringOrDrawable shortcutRepresentation = shortcutKeys.get(k);
if (shortcutRepresentation.drawable != null) {
ImageView shortcutKeyIconView = (ImageView) inflater.inflate(
R.layout.keyboard_shortcuts_key_icon_view, shortcutItemsContainer,
false);
Bitmap bitmap = Bitmap.createBitmap(shortcutTextItemHeight,
shortcutTextItemHeight, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
shortcutRepresentation.drawable.setBounds(0, 0, canvas.getWidth(),
canvas.getHeight());
shortcutRepresentation.drawable.draw(canvas);
shortcutKeyIconView.setImageBitmap(bitmap);
shortcutItemsContainer.addView(shortcutKeyIconView);
} else if (shortcutRepresentation.string != null) {
TextView shortcutKeyTextView = (TextView) inflater.inflate(
R.layout.keyboard_shortcuts_key_view, shortcutItemsContainer,
false);
shortcutKeyTextView.setText(shortcutRepresentation.string);
shortcutItemsContainer.addView(shortcutKeyTextView);
}
}
shortcutContainer.addView(shortcutView);
}
@@ -398,14 +439,27 @@ public class KeyboardShortcuts {
}
}
private List<String> getHumanReadableShortcutKeys(KeyboardShortcutInfo info) {
List<String> shortcutKeys = getHumanReadableModifiers(info);
private int getShortcutTextItemHeight(LayoutInflater inflater) {
TextView shortcutKeyTextView = (TextView) inflater.inflate(
R.layout.keyboard_shortcuts_key_view, null, false);
shortcutKeyTextView.measure(
View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
return shortcutKeyTextView.getMeasuredHeight()
- shortcutKeyTextView.getPaddingTop()
- shortcutKeyTextView.getPaddingBottom();
}
private List<StringOrDrawable> getHumanReadableShortcutKeys(KeyboardShortcutInfo info) {
List<StringOrDrawable> shortcutKeys = getHumanReadableModifiers(info);
if (shortcutKeys == null) {
return null;
}
String displayLabelString;
String displayLabelString = null;
Drawable displayLabelDrawable = null;
if (info.getBaseCharacter() > Character.MIN_VALUE) {
displayLabelString = String.valueOf(info.getBaseCharacter());
} else if (SPECIAL_CHARACTER_DRAWABLES.get(info.getKeycode()) != null) {
displayLabelDrawable = SPECIAL_CHARACTER_DRAWABLES.get(info.getKeycode());
} else if (SPECIAL_CHARACTER_NAMES.get(info.getKeycode()) != null) {
displayLabelString = SPECIAL_CHARACTER_NAMES.get(info.getKeycode());
} else {
@@ -422,12 +476,17 @@ public class KeyboardShortcuts {
return null;
}
}
shortcutKeys.add(displayLabelString.toUpperCase());
if (displayLabelDrawable != null) {
shortcutKeys.add(new StringOrDrawable(displayLabelDrawable));
} else if (displayLabelString != null) {
shortcutKeys.add(new StringOrDrawable(displayLabelString.toUpperCase()));
}
return shortcutKeys;
}
private List<String> getHumanReadableModifiers(KeyboardShortcutInfo info) {
final List<String> shortcutKeys = new ArrayList<>();
private List<StringOrDrawable> getHumanReadableModifiers(KeyboardShortcutInfo info) {
final List<StringOrDrawable> shortcutKeys = new ArrayList<>();
int modifiers = info.getModifiers();
if (modifiers == 0) {
return shortcutKeys;
@@ -435,7 +494,13 @@ public class KeyboardShortcuts {
for(int i = 0; i < MODIFIER_NAMES.size(); ++i) {
final int supportedModifier = MODIFIER_NAMES.keyAt(i);
if ((modifiers & supportedModifier) != 0) {
shortcutKeys.add(MODIFIER_NAMES.get(supportedModifier).toUpperCase());
if (MODIFIER_DRAWABLES.get(supportedModifier) != null) {
shortcutKeys.add(new StringOrDrawable(
MODIFIER_DRAWABLES.get(supportedModifier)));
} else {
shortcutKeys.add(new StringOrDrawable(
MODIFIER_NAMES.get(supportedModifier).toUpperCase()));
}
modifiers &= ~supportedModifier;
}
}
@@ -445,4 +510,17 @@ public class KeyboardShortcuts {
}
return shortcutKeys;
}
private static final class StringOrDrawable {
public String string;
public Drawable drawable;
public StringOrDrawable(String string) {
this.string = string;
}
public StringOrDrawable(Drawable drawable) {
this.drawable = drawable;
}
}
}