Keyboard shortcuts: one instance refactor

Make all the instance public methods private
and add static methods to control the behavior:
* show
* dismiss
* toggle

Make sure we only deal with one instance of the
KeyboardShortcuts (previously both the
KeyboardShortcutsReceiver and BaseStatusBar were
creating instances).

Makes sure the instance is destroyed when dismissing
and when showing it either creates a new one if none
exists or reuses the existing one.

Also fixes an existing nasty issue where in order
to dismiss the dialog from BaseStatusBar we were
always first creating a new instance.

Bug: 28012198
Change-Id: I207553dd45ae535edc64b6292a472fa0899029b0
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index 1b2393a..4e5ec9e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -1315,7 +1315,7 @@
     }
 
     protected void toggleKeyboardShortcuts(int deviceId) {
-        getKeyboardShortcuts().toggleKeyboardShortcuts(deviceId);
+        KeyboardShortcuts.toggle(mContext, deviceId);
     }
 
     protected void cancelPreloadingRecents() {
@@ -1712,14 +1712,6 @@
         }
     }
 
-    protected KeyboardShortcuts getKeyboardShortcuts() {
-        if (mKeyboardShortcuts == null) {
-            mKeyboardShortcuts = new KeyboardShortcuts(mContext);
-        }
-
-        return mKeyboardShortcuts;
-    }
-
     public void startPendingIntentDismissingKeyguard(final PendingIntent intent) {
         if (!isDeviceProvisioned()) return;
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java
index 86c1fca..47e749f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java
@@ -72,6 +72,10 @@
  */
 public final class KeyboardShortcuts {
     private static final String TAG = KeyboardShortcuts.class.getSimpleName();
+    private static final Object sLock = new Object();
+    private static KeyboardShortcuts sInstance;
+    private static boolean sIsShowing;
+
     private final SparseArray<String> mSpecialCharacterNames = new SparseArray<>();
     private final SparseArray<String> mModifierNames = new SparseArray<>();
     private final SparseArray<Drawable> mSpecialCharacterDrawables = new SparseArray<>();
@@ -82,7 +86,7 @@
     private final IPackageManager mPackageManager;
     private final OnClickListener mDialogCloseListener = new DialogInterface.OnClickListener() {
         public void onClick(DialogInterface dialog, int id) {
-            dismissKeyboardShortcutsDialog();
+            dismissKeyboardShortcuts();
         }
     };
     private final Comparator<KeyboardShortcutInfo> mApplicationItemsComparator =
@@ -110,12 +114,49 @@
     private Dialog mKeyboardShortcutsDialog;
     private KeyCharacterMap mKeyCharacterMap;
 
-    public KeyboardShortcuts(Context context) {
+    private KeyboardShortcuts(Context context) {
         this.mContext = new ContextThemeWrapper(context, android.R.style.Theme_Material_Light);
         this.mPackageManager = AppGlobals.getPackageManager();
         loadResources(context);
     }
 
+    private static KeyboardShortcuts getInstance(Context context) {
+        if (sInstance == null) {
+            sInstance = new KeyboardShortcuts(context);
+        }
+        return sInstance;
+    }
+
+    public static void show(Context context, int deviceId) {
+        synchronized (sLock) {
+            if (sInstance != null && !sInstance.mContext.equals(context)) {
+                dismiss();
+            }
+            getInstance(context).showKeyboardShortcuts(deviceId);
+            sIsShowing = true;
+        }
+    }
+
+    public static void toggle(Context context, int deviceId) {
+        synchronized (sLock) {
+            if (sIsShowing) {
+                dismiss();
+            } else {
+                show(context, deviceId);
+            }
+        }
+    }
+
+    public static void dismiss() {
+        synchronized (sLock) {
+            if (sInstance != null) {
+                sInstance.dismissKeyboardShortcuts();
+                sInstance = null;
+            }
+            sIsShowing = false;
+        }
+    }
+
     private void loadResources(Context context) {
         mSpecialCharacterNames.put(
                 KeyEvent.KEYCODE_HOME, context.getString(R.string.keyboard_key_home));
@@ -279,27 +320,6 @@
                 KeyEvent.META_META_ON, context.getDrawable(R.drawable.ic_ksh_key_meta));
     }
 
-    public void toggleKeyboardShortcuts(int deviceId) {
-        retrieveKeyCharacterMap(deviceId);
-        if (mKeyboardShortcutsDialog == null) {
-            Recents.getSystemServices().requestKeyboardShortcuts(mContext,
-                new KeyboardShortcutsReceiver() {
-                    @Override
-                    public void onKeyboardShortcutsReceived(
-                            final List<KeyboardShortcutGroup> result) {
-                        result.add(getSystemShortcuts());
-                        final KeyboardShortcutGroup appShortcuts = getDefaultApplicationShortcuts();
-                        if (appShortcuts != null) {
-                            result.add(appShortcuts);
-                        }
-                        showKeyboardShortcutsDialog(result);
-                    }
-                }, deviceId);
-        } else {
-            dismissKeyboardShortcutsDialog();
-        }
-    }
-
     /**
      * Retrieves a {@link KeyCharacterMap} and assigns it to mKeyCharacterMap. If the given id is an
      * existing device, that device's map is used. Otherwise, it checks first all available devices
@@ -329,7 +349,24 @@
         mKeyCharacterMap = inputDevice.getKeyCharacterMap();
     }
 
-    public void dismissKeyboardShortcutsDialog() {
+    private void showKeyboardShortcuts(int deviceId) {
+        retrieveKeyCharacterMap(deviceId);
+        Recents.getSystemServices().requestKeyboardShortcuts(mContext,
+                new KeyboardShortcutsReceiver() {
+                    @Override
+                    public void onKeyboardShortcutsReceived(
+                            final List<KeyboardShortcutGroup> result) {
+                        result.add(getSystemShortcuts());
+                        final KeyboardShortcutGroup appShortcuts = getDefaultApplicationShortcuts();
+                        if (appShortcuts != null) {
+                            result.add(appShortcuts);
+                        }
+                        showKeyboardShortcutsDialog(result);
+                    }
+                }, deviceId);
+    }
+
+    private void dismissKeyboardShortcuts() {
         if (mKeyboardShortcutsDialog != null) {
             mKeyboardShortcutsDialog.dismiss();
             mKeyboardShortcutsDialog = null;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutsReceiver.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutsReceiver.java
index 5d22faf..5f4ebd8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutsReceiver.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutsReceiver.java
@@ -26,8 +26,7 @@
     @Override
     public void onReceive(Context context, Intent intent) {
         if (Intent.ACTION_SHOW_KEYBOARD_SHORTCUTS.equals(intent.getAction())) {
-            final KeyboardShortcuts keyboardShortcuts = new KeyboardShortcuts(context);
-            keyboardShortcuts.toggleKeyboardShortcuts(-1 /* deviceId unknown */);
+            KeyboardShortcuts.show(context, -1 /* deviceId unknown */);
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index 75430ff..3d0bace 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -133,6 +133,7 @@
 import com.android.systemui.statusbar.EmptyShadeView;
 import com.android.systemui.statusbar.ExpandableNotificationRow;
 import com.android.systemui.statusbar.GestureRecorder;
+import com.android.systemui.statusbar.KeyboardShortcuts;
 import com.android.systemui.statusbar.KeyguardIndicationController;
 import com.android.systemui.statusbar.NotificationData;
 import com.android.systemui.statusbar.NotificationData.Entry;
@@ -3161,7 +3162,7 @@
             if (DEBUG) Log.v(TAG, "onReceive: " + intent);
             String action = intent.getAction();
             if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)) {
-                getKeyboardShortcuts().dismissKeyboardShortcutsDialog();
+                KeyboardShortcuts.dismiss();
                 if (isCurrentProfile(getSendingUserId())) {
                     int flags = CommandQueue.FLAG_EXCLUDE_NONE;
                     String reason = intent.getStringExtra("reason");