Merge "Disable fallback webview package before downgrading it." into nyc-dev
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 2c3e563..440ef21 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -3474,7 +3474,6 @@
                 popupBackground.getPadding(mTempRect);
                 width += mTempRect.left + mTempRect.right;
             }
-            mSuggestionListView.getLayoutParams().width = width;
             mPopupWindow.setWidth(width);
         }
 
diff --git a/core/jni/android/graphics/Paint.cpp b/core/jni/android/graphics/Paint.cpp
index 1844a98..f46f45c 100644
--- a/core/jni/android/graphics/Paint.cpp
+++ b/core/jni/android/graphics/Paint.cpp
@@ -768,6 +768,22 @@
         return false;
     }
 
+    // Returns true if the given string is exact one pair of regional indicators.
+    static bool isFlag(const jchar* str, size_t length) {
+        const jchar RI_LEAD_SURROGATE = 0xD83C;
+        const jchar RI_TRAIL_SURROGATE_MIN = 0xDDE6;
+        const jchar RI_TRAIL_SURROGATE_MAX = 0xDDFF;
+
+        if (length != 4) {
+            return false;
+        }
+        if (str[0] != RI_LEAD_SURROGATE || str[2] != RI_LEAD_SURROGATE) {
+            return false;
+        }
+        return RI_TRAIL_SURROGATE_MIN <= str[1] && str[1] <= RI_TRAIL_SURROGATE_MAX &&
+            RI_TRAIL_SURROGATE_MIN <= str[3] && str[3] <= RI_TRAIL_SURROGATE_MAX;
+    }
+
     static jboolean hasGlyph(JNIEnv *env, jclass, jlong paintHandle, jlong typefaceHandle,
             jint bidiFlags, jstring string) {
         const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
@@ -817,7 +833,26 @@
             // in joining scripts, such as Arabic and Mongolian.
             return false;
         }
-        return nGlyphs > 0 && !layoutContainsNotdef(layout);
+
+        if (nGlyphs == 0 || layoutContainsNotdef(layout)) {
+            return false;  // The collection doesn't have a glyph.
+        }
+
+        if (nChars == 2 && isFlag(str.get(), str.size())) {
+            // Some font may have a special glyph for unsupported regional indicator pairs.
+            // To return false for this case, need to compare the glyph id with the one of ZZ
+            // since ZZ is reserved for unknown or invalid territory.
+            // U+1F1FF (REGIONAL INDICATOR SYMBOL LETTER Z) is \uD83C\uDDFF in UTF16.
+            static const jchar ZZ_FLAG_STR[] = { 0xD83C, 0xDDFF, 0xD83C, 0xDDFF };
+            Layout zzLayout;
+            MinikinUtils::doLayout(&zzLayout, paint, bidiFlags, typeface, ZZ_FLAG_STR, 0, 4, 4);
+            if (zzLayout.nGlyphs() != 1 || layoutContainsNotdef(zzLayout)) {
+                // The font collection doesn't have a glyph for unknown flag. Just return true.
+                return true;
+            }
+            return zzLayout.getGlyphId(0) != layout.getGlyphId(0);
+        }
+        return true;
     }
 
     static jfloat doRunAdvance(const Paint* paint, Typeface* typeface, const jchar buf[],
diff --git a/packages/SystemUI/res/drawable/ic_ksh_key_backspace.xml b/packages/SystemUI/res/drawable/ic_ksh_key_backspace.xml
new file mode 100644
index 0000000..6519673
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_ksh_key_backspace.xml
@@ -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>
diff --git a/packages/SystemUI/res/drawable/ic_ksh_key_down.xml b/packages/SystemUI/res/drawable/ic_ksh_key_down.xml
new file mode 100644
index 0000000..25a2560
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_ksh_key_down.xml
@@ -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>
diff --git a/packages/SystemUI/res/drawable/ic_ksh_key_enter.xml b/packages/SystemUI/res/drawable/ic_ksh_key_enter.xml
new file mode 100644
index 0000000..599f350
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_ksh_key_enter.xml
@@ -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>
diff --git a/packages/SystemUI/res/drawable/ic_ksh_key_left.xml b/packages/SystemUI/res/drawable/ic_ksh_key_left.xml
new file mode 100644
index 0000000..038187e
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_ksh_key_left.xml
@@ -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>
diff --git a/packages/SystemUI/res/drawable/ic_ksh_key_meta.xml b/packages/SystemUI/res/drawable/ic_ksh_key_meta.xml
new file mode 100644
index 0000000..1e2195e
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_ksh_key_meta.xml
@@ -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>
diff --git a/packages/SystemUI/res/drawable/ic_ksh_key_right.xml b/packages/SystemUI/res/drawable/ic_ksh_key_right.xml
new file mode 100644
index 0000000..f2d7315
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_ksh_key_right.xml
@@ -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>
diff --git a/packages/SystemUI/res/drawable/ic_ksh_key_up.xml b/packages/SystemUI/res/drawable/ic_ksh_key_up.xml
new file mode 100644
index 0000000..36a83b1
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_ksh_key_up.xml
@@ -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>
diff --git a/packages/SystemUI/res/layout/keyboard_shortcuts_key_icon_view.xml b/packages/SystemUI/res/layout/keyboard_shortcuts_key_icon_view.xml
new file mode 100644
index 0000000..0cecb96
--- /dev/null
+++ b/packages/SystemUI/res/layout/keyboard_shortcuts_key_icon_view.xml
@@ -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"/>
diff --git a/packages/SystemUI/res/layout/keyboard_shortcuts_key_view.xml b/packages/SystemUI/res/layout/keyboard_shortcuts_key_view.xml
index 5002c12..1215029 100644
--- a/packages/SystemUI/res/layout/keyboard_shortcuts_key_view.xml
+++ b/packages/SystemUI/res/layout/keyboard_shortcuts_key_view.xml
@@ -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"/>
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index b9aa26b..b874e7c 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -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>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index c094da9..75e57b0 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -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 -->
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java
index 2b365dc..71e97a7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java
@@ -21,6 +21,9 @@
 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.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 @@
 /**
  * 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 @@
         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 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 @@
             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 @@
                     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 @@
                         .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 @@
         }
     }
 
-    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 @@
                 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 @@
         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 @@
         }
         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;
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index fbf4c0c..31eac1f 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -110,6 +110,7 @@
 import android.app.admin.IDevicePolicyManager;
 import android.app.admin.SecurityLog;
 import android.app.backup.IBackupManager;
+import android.app.usage.UsageStatsManager;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.Context;
@@ -7005,11 +7006,28 @@
             pkgs = PackageManagerServiceUtils.getPackagesForDexopt(mPackages.values(), this);
         }
 
+        UsageStatsManager usageMgr =
+                (UsageStatsManager) mContext.getSystemService(Context.USAGE_STATS_SERVICE);
+
         int curr = 0;
         int total = pkgs.size();
         for (PackageParser.Package pkg : pkgs) {
             curr++;
 
+            if (!PackageDexOptimizer.canOptimizePackage(pkg)) {
+                if (DEBUG_DEXOPT) {
+                    Log.i(TAG, "Skipping update of of non-optimizable app " + pkg.packageName);
+                }
+                continue;
+            }
+
+            if (!causeFirstBoot && usageMgr.isAppInactive(pkg.packageName)) {
+                if (DEBUG_DEXOPT) {
+                    Log.i(TAG, "Skipping update of of idle app " + pkg.packageName);
+                }
+                continue;
+            }
+
             if (DEBUG_DEXOPT) {
                 Log.i(TAG, "Extracting app " + curr + " of " + total + ": " + pkg.packageName);
             }
@@ -7023,16 +7041,11 @@
                 }
             }
 
-            if (PackageDexOptimizer.canOptimizePackage(pkg)) {
-                // If the cache was pruned, any compiled odex files will likely be out of date
-                // and would have to be patched (would be SELF_PATCHOAT, which is deprecated).
-                // Instead, force the extraction in this case.
-                performDexOpt(pkg.packageName,
-                        null /* instructionSet */,
-                        false /* checkProfiles */,
-                        causeFirstBoot ? REASON_FIRST_BOOT : REASON_BOOT,
-                        false /* force */);
-            }
+            performDexOpt(pkg.packageName,
+                    null /* instructionSet */,
+                    false /* checkProfiles */,
+                    causeFirstBoot ? REASON_FIRST_BOOT : REASON_BOOT,
+                    false /* force */);
         }
     }
 
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index beec40f..0aeb96f 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -144,6 +144,7 @@
     private long mLastAppIdleParoledTime;
 
     private volatile boolean mPendingOneTimeCheckIdleStates;
+    private boolean mSystemServicesReady = false;
 
     @GuardedBy("mLock")
     private AppIdleHistory mAppIdleHistory;
@@ -232,6 +233,8 @@
             if (mPendingOneTimeCheckIdleStates) {
                 postOneTimeCheckIdleStates();
             }
+
+            mSystemServicesReady = true;
         } else if (phase == PHASE_BOOT_COMPLETED) {
             setAppIdleParoled(getContext().getSystemService(BatteryManager.class).isCharging());
         }
@@ -810,28 +813,30 @@
             // retain this for safety).
             return false;
         }
-        try {
-            // We allow all whitelisted apps, including those that don't want to be whitelisted
-            // for idle mode, because app idle (aka app standby) is really not as big an issue
-            // for controlling who participates vs. doze mode.
-            if (mDeviceIdleController.isPowerSaveWhitelistExceptIdleApp(packageName)) {
+        if (mSystemServicesReady) {
+            try {
+                // We allow all whitelisted apps, including those that don't want to be whitelisted
+                // for idle mode, because app idle (aka app standby) is really not as big an issue
+                // for controlling who participates vs. doze mode.
+                if (mDeviceIdleController.isPowerSaveWhitelistExceptIdleApp(packageName)) {
+                    return false;
+                }
+            } catch (RemoteException re) {
+                throw re.rethrowFromSystemServer();
+            }
+
+            if (isActiveDeviceAdmin(packageName, userId)) {
                 return false;
             }
-        } catch (RemoteException re) {
-            throw re.rethrowFromSystemServer();
-        }
 
-        if (isActiveDeviceAdmin(packageName, userId)) {
-            return false;
-        }
+            if (isActiveNetworkScorer(packageName)) {
+                return false;
+            }
 
-        if (isActiveNetworkScorer(packageName)) {
-            return false;
-        }
-
-        if (mAppWidgetManager != null
-                && mAppWidgetManager.isBoundWidgetPackage(packageName, userId)) {
-            return false;
+            if (mAppWidgetManager != null
+                    && mAppWidgetManager.isBoundWidgetPackage(packageName, userId)) {
+                return false;
+            }
         }
 
         if (!isAppIdleUnfiltered(packageName, userId, elapsedRealtime)) {