Dispatch screen state change events to Views
Bug #6120957

Using this new callback, views can interrupt and resume their
animations or other periodic tasks based on the current state
of the display.

Change-Id: I398f4abd421e9c5f207107bf1009a7b92cf45daa
diff --git a/api/current.txt b/api/current.txt
index dbe57d1..a18e4f8 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -23370,6 +23370,7 @@
     method public void onResolvedTextDirectionReset();
     method protected void onRestoreInstanceState(android.os.Parcelable);
     method protected android.os.Parcelable onSaveInstanceState();
+    method public void onScreenStateChanged(int);
     method protected void onScrollChanged(int, int, int, int);
     method protected boolean onSetAlpha(int);
     method protected void onSizeChanged(int, int, int, int);
@@ -23579,6 +23580,8 @@
     field public static final android.util.Property ROTATION_Y;
     field public static final android.util.Property SCALE_X;
     field public static final android.util.Property SCALE_Y;
+    field public static final int SCREEN_STATE_OFF = 2; // 0x2
+    field public static final int SCREEN_STATE_ON = 1; // 0x1
     field public static final int SCROLLBARS_INSIDE_INSET = 16777216; // 0x1000000
     field public static final int SCROLLBARS_INSIDE_OVERLAY = 0; // 0x0
     field public static final int SCROLLBARS_OUTSIDE_INSET = 50331648; // 0x3000000
diff --git a/core/java/android/view/IWindow.aidl b/core/java/android/view/IWindow.aidl
index acb1387..497bc90b 100644
--- a/core/java/android/view/IWindow.aidl
+++ b/core/java/android/view/IWindow.aidl
@@ -49,7 +49,7 @@
             boolean reportDraw, in Configuration newConfig);
     void dispatchAppVisibility(boolean visible);
     void dispatchGetNewSurface();
-    void dispatchScreenStatus(boolean on);
+    void dispatchScreenState(boolean on);
 
     /**
      * Tell the window that it is either gaining or losing focus.  Keep it up
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 94531c8..14e2d6f 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -1994,6 +1994,20 @@
     public static final int FIND_VIEWS_WITH_ACCESSIBILITY_NODE_PROVIDERS = 0x00000004;
 
     /**
+     * Indicates that the screen has changed state and is now off.
+     *
+     * @see #onScreenStateChanged(int)
+     */
+    public static final int SCREEN_STATE_OFF = 0x0;
+
+    /**
+     * Indicates that the screen has changed state and is now on.
+     *
+     * @see #onScreenStateChanged(int 
+     */
+    public static final int SCREEN_STATE_ON = 0x1;
+
+    /**
      * Controls the over-scroll mode for this view.
      * See {@link #overScrollBy(int, int, int, int, int, int, int, int, boolean)},
      * {@link #OVER_SCROLL_ALWAYS}, {@link #OVER_SCROLL_IF_CONTENT_SCROLLS},
@@ -9604,6 +9618,25 @@
     }
 
     /**
+     * @see #onScreenStateChanged(int)
+     */
+    void dispatchScreenStateChanged(int screenState) {
+        onScreenStateChanged(screenState);
+    }
+
+    /**
+     * This method is called whenever the state of the screen this view is
+     * attached to changes. A state change will usually occurs when the screen
+     * turns on or off (whether it happens automatically or the user does it
+     * manually.)
+     *
+     * @param screenState The new state of the screen. Can be either
+     *                    {@link #SCREEN_STATE_ON} or {@link #SCREEN_STATE_OFF}
+     */
+    public void onScreenStateChanged(int screenState) {
+    }
+
+    /**
      * Resolve and cache the layout direction. LTR is set initially. This is implicitly supposing
      * that the parent directionality can and will be resolved before its children.
      */
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 0c63286..c9e0242 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -2254,6 +2254,17 @@
     }
 
     @Override
+    void dispatchScreenStateChanged(int screenState) {
+        super.dispatchScreenStateChanged(screenState);
+
+        final int count = mChildrenCount;
+        final View[] children = mChildren;
+        for (int i = 0; i < count; i++) {
+            children[i].dispatchScreenStateChanged(screenState);
+        }
+    }
+
+    @Override
     boolean dispatchPopulateAccessibilityEventInternal(AccessibilityEvent event) {
         boolean handled = super.dispatchPopulateAccessibilityEventInternal(event);
         if (handled) {
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 7fd05c3..9cde153 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -761,9 +761,12 @@
         scheduleTraversals();
     }
 
-    void handleScreenStatusChange(boolean on) {
+    void handleScreenStateChange(boolean on) {
         if (on != mAttachInfo.mScreenOn) {
             mAttachInfo.mScreenOn = on;
+            if (mView != null) {
+                mView.dispatchScreenStateChanged(on ? View.SCREEN_STATE_ON : View.SCREEN_STATE_OFF);
+            }
             if (on) {
                 mFullRedrawNeeded = true;
                 scheduleTraversals();
@@ -2500,7 +2503,7 @@
     private final static int MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_ID = 21;
     private final static int MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_TEXT = 22;
     private final static int MSG_PROCESS_INPUT_EVENTS = 23;
-    private final static int MSG_DISPATCH_SCREEN_STATUS = 24;
+    private final static int MSG_DISPATCH_SCREEN_STATE = 24;
 
     final class ViewRootHandler extends Handler {
         @Override
@@ -2757,9 +2760,9 @@
                         .findAccessibilityNodeInfosByTextUiThread(msg);
                 }
             } break;
-            case MSG_DISPATCH_SCREEN_STATUS: {
+            case MSG_DISPATCH_SCREEN_STATE: {
                 if (mView != null) {
-                    handleScreenStatusChange(msg.arg1 == 1);
+                    handleScreenStateChange(msg.arg1 == 1);
                 }
             } break;
             }
@@ -4142,8 +4145,8 @@
         mHandler.sendMessage(msg);
     }
 
-    public void dispatchScreenStatusChange(boolean on) {
-        Message msg = mHandler.obtainMessage(MSG_DISPATCH_SCREEN_STATUS);
+    public void dispatchScreenStateChange(boolean on) {
+        Message msg = mHandler.obtainMessage(MSG_DISPATCH_SCREEN_STATE);
         msg.arg1 = on ? 1 : 0;
         mHandler.sendMessage(msg);
     }
@@ -4349,13 +4352,13 @@
             }
         }
 
-        public void dispatchScreenStatus(boolean on) {
+        public void dispatchScreenState(boolean on) {
             final ViewRootImpl viewAncestor = mViewAncestor.get();
             if (viewAncestor != null) {
-                viewAncestor.dispatchScreenStatusChange(on);
+                viewAncestor.dispatchScreenStateChange(on);
             }
         }
-        
+
         public void dispatchGetNewSurface() {
             final ViewRootImpl viewAncestor = mViewAncestor.get();
             if (viewAncestor != null) {
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 47e10cc..6ac889a 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -4266,6 +4266,12 @@
     }
 
     @Override
+    public void onScreenStateChanged(int screenState) {
+        super.onScreenStateChanged(screenState);
+        if (mEditor != null) getEditor().onScreenStateChanged(screenState);
+    }
+
+    @Override
     protected boolean isPaddingOffsetRequired() {
         return mShadowRadius != 0 || mDrawables != null;
     }
@@ -11386,6 +11392,30 @@
             hideControllers();
         }
 
+        void onScreenStateChanged(int screenState) {
+            switch (screenState) {
+                case SCREEN_STATE_ON:
+                    resumeBlink();
+                    break;
+                case SCREEN_STATE_OFF:
+                    suspendBlink();
+                    break;
+            }
+        }
+
+        private void suspendBlink() {
+            if (mBlink != null) {
+                mBlink.cancel();
+            }
+        }
+
+        private void resumeBlink() {
+            if (mBlink != null) {
+                mBlink.uncancel();
+                makeBlink();
+            }
+        }
+
         void adjustInputType(boolean password, boolean passwordInputType,
                 boolean webPasswordInputType, boolean numberPasswordInputType) {
             // mInputType has been set from inputType, possibly modified by mInputMethod.
diff --git a/core/java/com/android/internal/view/BaseIWindow.java b/core/java/com/android/internal/view/BaseIWindow.java
index e695f8e..15d11d8 100644
--- a/core/java/com/android/internal/view/BaseIWindow.java
+++ b/core/java/com/android/internal/view/BaseIWindow.java
@@ -49,7 +49,7 @@
     public void dispatchGetNewSurface() {
     }
 
-    public void dispatchScreenStatus(boolean on) {
+    public void dispatchScreenState(boolean on) {
     }
 
     public void windowFocusChanged(boolean hasFocus, boolean touchEnabled) {
diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java
index 023f97d..def22a9 100644
--- a/services/java/com/android/server/wm/WindowManagerService.java
+++ b/services/java/com/android/server/wm/WindowManagerService.java
@@ -6579,7 +6579,7 @@
         for (int i = count - 1; i >= 0; i--) {
             WindowState win = mWindows.get(i);
             try {
-                win.mClient.dispatchScreenStatus(on);
+                win.mClient.dispatchScreenState(on);
             } catch (RemoteException e) {
                 // Ignored
             }
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindow.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindow.java
index 965f553..7c683c9 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindow.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindow.java
@@ -53,7 +53,8 @@
     }
 
     @Override
-    public void dispatchScreenStatus(boolean on) throws RemoteException {
+    public void dispatchScreenState(boolean on) throws RemoteException {
+        // pass for now.
     }
 
     @Override