Magnification bounds not always animated on a looper thread.

In display manigfication mode we are showing a frame around the
magnified region. Showing and hiding this frame is animated. In
some cases the code calling into the display magnifier is not
running on a looper thread which leads to a crash when interacting
with the property animator. Now the animation is explicitly handled
in a dedicated handler that runs on the window manager's handler
looper thread.

bug:15506701

Change-Id: I3fada775c9fac8820599dd31ac80720021c70cb3
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index 61ad7aa..fa1c0ff 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -457,7 +457,7 @@
                 availableBounds.set(0, 0, screenWidth, screenHeight);
 
                 Region nonMagnifiedBounds = mTempRegion4;
-                nonMagnifiedBounds.set(0,  0,  0,  0);
+                nonMagnifiedBounds.set(0, 0, 0, 0);
 
                 SparseArray<WindowState> visibleWindows = mTempWindowStates;
                 visibleWindows.clear();
@@ -566,7 +566,7 @@
             public void setMagnifiedRegionBorderShownLocked(boolean shown, boolean animate) {
                 if (shown) {
                     mFullRedrawNeeded = true;
-                    mOldMagnifiedBounds.set(0,  0,  0,  0);
+                    mOldMagnifiedBounds.set(0, 0, 0, 0);
                 }
                 mWindow.setShown(shown, animate);
             }
@@ -614,19 +614,15 @@
             private final class ViewportWindow {
                 private static final String SURFACE_TITLE = "Magnification Overlay";
 
-                private static final String PROPERTY_NAME_ALPHA = "alpha";
-
-                private static final int MIN_ALPHA = 0;
-                private static final int MAX_ALPHA = 255;
-
                 private final Region mBounds = new Region();
                 private final Rect mDirtyRect = new Rect();
                 private final Paint mPaint = new Paint();
 
-                private final ValueAnimator mShowHideFrameAnimator;
                 private final SurfaceControl mSurfaceControl;
                 private final Surface mSurface = new Surface();
 
+                private final AnimationController mAnimationController;
+
                 private boolean mShown;
                 private int mAlpha;
 
@@ -651,6 +647,9 @@
                     mSurfaceControl.setPosition(0, 0);
                     mSurface.copyFrom(mSurfaceControl);
 
+                    mAnimationController = new AnimationController(context,
+                            mWindowManagerService.mH.getLooper());
+
                     TypedValue typedValue = new TypedValue();
                     context.getTheme().resolveAttribute(R.attr.colorActivatedHighlight,
                             typedValue, true);
@@ -660,14 +659,6 @@
                     mPaint.setStrokeWidth(mBorderWidth);
                     mPaint.setColor(borderColor);
 
-                    Interpolator interpolator = new DecelerateInterpolator(2.5f);
-                    final long longAnimationDuration = context.getResources().getInteger(
-                            com.android.internal.R.integer.config_longAnimTime);
-
-                    mShowHideFrameAnimator = ObjectAnimator.ofInt(this, PROPERTY_NAME_ALPHA,
-                            MIN_ALPHA, MAX_ALPHA);
-                    mShowHideFrameAnimator.setInterpolator(interpolator);
-                    mShowHideFrameAnimator.setDuration(longAnimationDuration);
                     mInvalidated = true;
                 }
 
@@ -677,24 +668,7 @@
                             return;
                         }
                         mShown = shown;
-                        if (animate) {
-                            if (mShowHideFrameAnimator.isRunning()) {
-                                mShowHideFrameAnimator.reverse();
-                            } else {
-                                if (shown) {
-                                    mShowHideFrameAnimator.start();
-                                } else {
-                                    mShowHideFrameAnimator.reverse();
-                                }
-                            }
-                        } else {
-                            mShowHideFrameAnimator.cancel();
-                            if (shown) {
-                                setAlpha(MAX_ALPHA);
-                            } else {
-                                setAlpha(MIN_ALPHA);
-                            }
-                        }
+                        mAnimationController.onFrameShownStateChanged(shown, animate);
                         if (DEBUG_VIEWPORT_WINDOW) {
                             Slog.i(LOG_TAG, "ViewportWindow shown: " + mShown);
                         }
@@ -801,6 +775,64 @@
                     mSurfaceControl.release();
                     mSurface.release();
                 }
+
+                private final class AnimationController extends Handler {
+                    private static final String PROPERTY_NAME_ALPHA = "alpha";
+
+                    private static final int MIN_ALPHA = 0;
+                    private static final int MAX_ALPHA = 255;
+
+                    private static final int MSG_FRAME_SHOWN_STATE_CHANGED = 1;
+
+                    private final ValueAnimator mShowHideFrameAnimator;
+
+                    public AnimationController(Context context, Looper looper) {
+                        super(looper);
+                        mShowHideFrameAnimator = ObjectAnimator.ofInt(ViewportWindow.this,
+                                PROPERTY_NAME_ALPHA, MIN_ALPHA, MAX_ALPHA);
+
+                        Interpolator interpolator = new DecelerateInterpolator(2.5f);
+                        final long longAnimationDuration = context.getResources().getInteger(
+                                com.android.internal.R.integer.config_longAnimTime);
+
+                        mShowHideFrameAnimator.setInterpolator(interpolator);
+                        mShowHideFrameAnimator.setDuration(longAnimationDuration);
+                    }
+
+                    public void onFrameShownStateChanged(boolean shown, boolean animate) {
+                        obtainMessage(MSG_FRAME_SHOWN_STATE_CHANGED,
+                                shown ? 1 : 0, animate ? 1 : 0).sendToTarget();
+                    }
+
+                    @Override
+                    public void handleMessage(Message message) {
+                        switch (message.what) {
+                            case MSG_FRAME_SHOWN_STATE_CHANGED: {
+                                final boolean shown = message.arg1 == 1;
+                                final boolean animate = message.arg2 == 1;
+
+                                if (animate) {
+                                    if (mShowHideFrameAnimator.isRunning()) {
+                                        mShowHideFrameAnimator.reverse();
+                                    } else {
+                                        if (shown) {
+                                            mShowHideFrameAnimator.start();
+                                        } else {
+                                            mShowHideFrameAnimator.reverse();
+                                        }
+                                    }
+                                } else {
+                                    mShowHideFrameAnimator.cancel();
+                                    if (shown) {
+                                        setAlpha(MAX_ALPHA);
+                                    } else {
+                                        setAlpha(MIN_ALPHA);
+                                    }
+                                }
+                            } break;
+                        }
+                    }
+                }
             }
         }