Move coalescing to callback, optimize KeyguardStatusView.

Move the recent keyguard hidden coalescing down to the callback
level.  The lifetime of each callback can be short, make sure
they see visibility changes at least once for each change local
to their lifetime.

KeyguardStatusView.refresh() is called multiple times, and instances
are recreated often.  This results in expensive computations
filling the sysui/keyguard ui queue, adding to overall sluggishness.
Traceview points to DateFormat.getBestDateTimePattern as the
main culprit.

As of this change, refresh() will only call the expensive date pattern
computations when absolutely necessary, resulting in better
performance turning the screen off/on.

Bug:11221659
Bug:11447043
Change-Id: I3d4105af7db608803b82d8ef0ff141e42c154257
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardService.java b/packages/Keyguard/src/com/android/keyguard/KeyguardService.java
index 8ccd6fe..36b2446 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardService.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardService.java
@@ -68,8 +68,6 @@
     }
 
     private final IKeyguardService.Stub mBinder = new IKeyguardService.Stub() {
-        private boolean mSetHiddenCalled;
-        private boolean mIsHidden;
         public boolean isShowing() {
             return mKeyguardViewMediator.isShowing();
         }
@@ -91,10 +89,7 @@
         }
         public void setHidden(boolean isHidden) {
             checkPermission();
-            if (mSetHiddenCalled && mIsHidden == isHidden) return;
             mKeyguardViewMediator.setHidden(isHidden);
-            mSetHiddenCalled = true;
-            mIsHidden = isHidden;
         }
         public void dismiss() {
             mKeyguardViewMediator.dismiss();
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardStatusView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardStatusView.java
index d933275..0bfee38 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardStatusView.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardStatusView.java
@@ -98,26 +98,13 @@
     }
 
     protected void refresh() {
-        Resources res = mContext.getResources();
-        Locale locale = Locale.getDefault();
-        final String dateFormat = DateFormat.getBestDateTimePattern(locale,
-                res.getString(R.string.abbrev_wday_month_day_no_year));
+        Patterns.update(mContext);
 
-        mDateView.setFormat24Hour(dateFormat);
-        mDateView.setFormat12Hour(dateFormat);
+        mDateView.setFormat24Hour(Patterns.dateView);
+        mDateView.setFormat12Hour(Patterns.dateView);
 
-        // 12-hour clock.
-        // CLDR insists on adding an AM/PM indicator even though it wasn't in the skeleton
-        // format.  The following code removes the AM/PM indicator if we didn't want it.
-        final String clock12skel = res.getString(R.string.clock_12hr_format);
-        String clock12hr = DateFormat.getBestDateTimePattern(locale, clock12skel);
-        clock12hr = clock12skel.contains("a") ? clock12hr : clock12hr.replaceAll("a", "").trim();
-        mClockView.setFormat12Hour(clock12hr);
-
-        // 24-hour clock
-        final String clock24skel = res.getString(R.string.clock_24hr_format);
-        final String clock24hr = DateFormat.getBestDateTimePattern(locale, clock24skel);
-        mClockView.setFormat24Hour(clock24hr);
+        mClockView.setFormat12Hour(Patterns.clockView12);
+        mClockView.setFormat24Hour(Patterns.clockView24);
 
         refreshAlarmStatus();
     }
@@ -149,4 +136,35 @@
         return LockPatternUtils.ID_DEFAULT_STATUS_WIDGET;
     }
 
+    // DateFormat.getBestDateTimePattern is extremely expensive, and refresh is called often.
+    // This is an optimization to ensure we only recompute the patterns when the inputs change.
+    private static final class Patterns {
+        static String dateView;
+        static String clockView12;
+        static String clockView24;
+        static String cacheKey;
+
+        static void update(Context context) {
+            final Locale locale = Locale.getDefault();
+            final Resources res = context.getResources();
+            final String dateViewSkel = res.getString(R.string.abbrev_wday_month_day_no_year);
+            final String clockView12Skel = res.getString(R.string.clock_12hr_format);
+            final String clockView24Skel = res.getString(R.string.clock_24hr_format);
+            final String key = locale.toString() + dateViewSkel + clockView12Skel + clockView24Skel;
+            if (key.equals(cacheKey)) return;
+
+            dateView = DateFormat.getBestDateTimePattern(locale, dateViewSkel);
+
+            clockView12 = DateFormat.getBestDateTimePattern(locale, clockView12Skel);
+            // CLDR insists on adding an AM/PM indicator even though it wasn't in the skeleton
+            // format.  The following code removes the AM/PM indicator if we didn't want it.
+            if (!clockView12Skel.contains("a")) {
+                clockView12 = clockView12.replaceAll("a", "").trim();
+            }
+
+            clockView24 = DateFormat.getBestDateTimePattern(locale, clockView24Skel);
+
+            cacheKey = key;
+        }
+    }
 }
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 520cea3..a849316 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -815,7 +815,7 @@
         for (int i = 0; i < mCallbacks.size(); i++) {
             KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
             if (cb != null) {
-                cb.onKeyguardVisibilityChanged(isShowing);
+                cb.onKeyguardVisibilityChangedRaw(isShowing);
             }
         }
     }
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
index 76f9637..c08880d 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
@@ -19,6 +19,7 @@
 import android.app.admin.DevicePolicyManager;
 import android.graphics.Bitmap;
 import android.media.AudioManager;
+import android.os.SystemClock;
 import android.view.WindowManagerPolicy;
 
 import com.android.internal.telephony.IccCardConstants;
@@ -27,6 +28,11 @@
  * Callback for general information relevant to lock screen.
  */
 class KeyguardUpdateMonitorCallback {
+
+    private static final long VISIBILITY_CHANGED_COLLAPSE_MS = 1000;
+    private long mVisibilityChangedCalled;
+    private boolean mShowing;
+
     /**
      * Called when the battery status changes, e.g. when plugged in or unplugged, charge
      * level, etc. changes.
@@ -70,6 +76,15 @@
      */
     void onKeyguardVisibilityChanged(boolean showing) { }
 
+    void onKeyguardVisibilityChangedRaw(boolean showing) {
+        final long now = SystemClock.elapsedRealtime();
+        if (showing == mShowing
+                && (now - mVisibilityChangedCalled) < VISIBILITY_CHANGED_COLLAPSE_MS) return;
+        onKeyguardVisibilityChanged(showing);
+        mVisibilityChangedCalled = now;
+        mShowing = showing;
+    }
+
     /**
      * Called when visibility of lockscreen clock changes, such as when
      * obscured by a widget.