Home | History | Annotate | Download | only in wm
      1 /*
      2  * Copyright (C) 2012 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.server.wm;
     18 
     19 import android.animation.ObjectAnimator;
     20 import android.animation.ValueAnimator;
     21 import android.app.Service;
     22 import android.content.Context;
     23 import android.graphics.Canvas;
     24 import android.graphics.Color;
     25 import android.graphics.Matrix;
     26 import android.graphics.Paint;
     27 import android.graphics.Path;
     28 import android.graphics.PixelFormat;
     29 import android.graphics.Point;
     30 import android.graphics.PorterDuff.Mode;
     31 import android.graphics.Rect;
     32 import android.graphics.RectF;
     33 import android.graphics.Region;
     34 import android.os.Handler;
     35 import android.os.Looper;
     36 import android.os.Message;
     37 import android.os.RemoteException;
     38 import android.util.Pools.SimplePool;
     39 import android.util.Slog;
     40 import android.util.SparseArray;
     41 import android.util.TypedValue;
     42 import android.view.IMagnificationCallbacks;
     43 import android.view.MagnificationSpec;
     44 import android.view.Surface;
     45 import android.view.Surface.OutOfResourcesException;
     46 import android.view.SurfaceControl;
     47 import android.view.WindowManager;
     48 import android.view.WindowManagerPolicy;
     49 import android.view.animation.DecelerateInterpolator;
     50 import android.view.animation.Interpolator;
     51 
     52 import com.android.internal.R;
     53 import com.android.internal.os.SomeArgs;
     54 
     55 /**
     56  * This class is a part of the window manager and encapsulates the
     57  * functionality related to display magnification.
     58  */
     59 final class DisplayMagnifier {
     60     private static final String LOG_TAG = DisplayMagnifier.class.getSimpleName();
     61 
     62     private static final boolean DEBUG_WINDOW_TRANSITIONS = false;
     63     private static final boolean DEBUG_ROTATION = false;
     64     private static final boolean DEBUG_LAYERS = false;
     65     private static final boolean DEBUG_RECTANGLE_REQUESTED = false;
     66     private static final boolean DEBUG_VIEWPORT_WINDOW = false;
     67 
     68     private final Rect mTempRect1 = new Rect();
     69     private final Rect mTempRect2 = new Rect();
     70 
     71     private final Region mTempRegion1 = new Region();
     72     private final Region mTempRegion2 = new Region();
     73     private final Region mTempRegion3 = new Region();
     74     private final Region mTempRegion4 = new Region();
     75 
     76     private final Context mContext;
     77     private final WindowManagerService mWindowManagerService;
     78     private final MagnifiedViewport mMagnifedViewport;
     79     private final Handler mHandler;
     80 
     81     private final IMagnificationCallbacks mCallbacks;
     82 
     83     private final long mLongAnimationDuration;
     84 
     85     public DisplayMagnifier(WindowManagerService windowManagerService,
     86             IMagnificationCallbacks callbacks) {
     87         mContext = windowManagerService.mContext;
     88         mWindowManagerService = windowManagerService;
     89         mCallbacks = callbacks;
     90         mHandler = new MyHandler(mWindowManagerService.mH.getLooper());
     91         mMagnifedViewport = new MagnifiedViewport();
     92         mLongAnimationDuration = mContext.getResources().getInteger(
     93                 com.android.internal.R.integer.config_longAnimTime);
     94     }
     95 
     96     public void setMagnificationSpecLocked(MagnificationSpec spec) {
     97         mMagnifedViewport.updateMagnificationSpecLocked(spec);
     98         mMagnifedViewport.recomputeBoundsLocked();
     99         mWindowManagerService.scheduleAnimationLocked();
    100     }
    101 
    102     public void onRectangleOnScreenRequestedLocked(Rect rectangle, boolean immediate) {
    103         if (DEBUG_RECTANGLE_REQUESTED) {
    104             Slog.i(LOG_TAG, "Rectangle on screen requested: " + rectangle);
    105         }
    106         if (!mMagnifedViewport.isMagnifyingLocked()) {
    107             return;
    108         }
    109         Rect magnifiedRegionBounds = mTempRect2;
    110         mMagnifedViewport.getMagnifiedFrameInContentCoordsLocked(magnifiedRegionBounds);
    111         if (magnifiedRegionBounds.contains(rectangle)) {
    112             return;
    113         }
    114         SomeArgs args = SomeArgs.obtain();
    115         args.argi1 = rectangle.left;
    116         args.argi2 = rectangle.top;
    117         args.argi3 = rectangle.right;
    118         args.argi4 = rectangle.bottom;
    119         mHandler.obtainMessage(MyHandler.MESSAGE_NOTIFY_RECTANGLE_ON_SCREEN_REQUESTED,
    120                 args).sendToTarget();
    121     }
    122 
    123     public void onWindowLayersChangedLocked() {
    124         if (DEBUG_LAYERS) {
    125             Slog.i(LOG_TAG, "Layers changed.");
    126         }
    127         mMagnifedViewport.recomputeBoundsLocked();
    128         mWindowManagerService.scheduleAnimationLocked();
    129     }
    130 
    131     public void onRotationChangedLocked(DisplayContent displayContent, int rotation) {
    132         if (DEBUG_ROTATION) {
    133             Slog.i(LOG_TAG, "Rotaton: " + Surface.rotationToString(rotation)
    134                     + " displayId: " + displayContent.getDisplayId());
    135         }
    136         mMagnifedViewport.onRotationChangedLocked();
    137         mHandler.sendEmptyMessage(MyHandler.MESSAGE_NOTIFY_ROTATION_CHANGED);
    138     }
    139 
    140     public void onAppWindowTransitionLocked(WindowState windowState, int transition) {
    141         if (DEBUG_WINDOW_TRANSITIONS) {
    142             Slog.i(LOG_TAG, "Window transition: "
    143                     + AppTransition.appTransitionToString(transition)
    144                     + " displayId: " + windowState.getDisplayId());
    145         }
    146         final boolean magnifying = mMagnifedViewport.isMagnifyingLocked();
    147         if (magnifying) {
    148             switch (transition) {
    149                 case AppTransition.TRANSIT_ACTIVITY_OPEN:
    150                 case AppTransition.TRANSIT_TASK_OPEN:
    151                 case AppTransition.TRANSIT_TASK_TO_FRONT:
    152                 case AppTransition.TRANSIT_WALLPAPER_OPEN:
    153                 case AppTransition.TRANSIT_WALLPAPER_CLOSE:
    154                 case AppTransition.TRANSIT_WALLPAPER_INTRA_OPEN: {
    155                     mHandler.sendEmptyMessage(MyHandler.MESSAGE_NOTIFY_USER_CONTEXT_CHANGED);
    156                 }
    157             }
    158         }
    159     }
    160 
    161     public void onWindowTransitionLocked(WindowState windowState, int transition) {
    162         if (DEBUG_WINDOW_TRANSITIONS) {
    163             Slog.i(LOG_TAG, "Window transition: "
    164                     + AppTransition.appTransitionToString(transition)
    165                     + " displayId: " + windowState.getDisplayId());
    166         }
    167         final boolean magnifying = mMagnifedViewport.isMagnifyingLocked();
    168         final int type = windowState.mAttrs.type;
    169         switch (transition) {
    170             case WindowManagerPolicy.TRANSIT_ENTER:
    171             case WindowManagerPolicy.TRANSIT_SHOW: {
    172                 if (!magnifying) {
    173                     break;
    174                 }
    175                 switch (type) {
    176                     case WindowManager.LayoutParams.TYPE_APPLICATION:
    177                     case WindowManager.LayoutParams.TYPE_APPLICATION_PANEL:
    178                     case WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA:
    179                     case WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL:
    180                     case WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG:
    181                     case WindowManager.LayoutParams.TYPE_SEARCH_BAR:
    182                     case WindowManager.LayoutParams.TYPE_PHONE:
    183                     case WindowManager.LayoutParams.TYPE_SYSTEM_ALERT:
    184                     case WindowManager.LayoutParams.TYPE_TOAST:
    185                     case WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY:
    186                     case WindowManager.LayoutParams.TYPE_PRIORITY_PHONE:
    187                     case WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG:
    188                     case WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG:
    189                     case WindowManager.LayoutParams.TYPE_SYSTEM_ERROR:
    190                     case WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY:
    191                     case WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL:
    192                     case WindowManager.LayoutParams.TYPE_RECENTS_OVERLAY: {
    193                         Rect magnifiedRegionBounds = mTempRect2;
    194                         mMagnifedViewport.getMagnifiedFrameInContentCoordsLocked(
    195                                 magnifiedRegionBounds);
    196                         Rect touchableRegionBounds = mTempRect1;
    197                         windowState.getTouchableRegion(mTempRegion1);
    198                         mTempRegion1.getBounds(touchableRegionBounds);
    199                         if (!magnifiedRegionBounds.intersect(touchableRegionBounds)) {
    200                             try {
    201                                 mCallbacks.onRectangleOnScreenRequested(
    202                                         touchableRegionBounds.left,
    203                                         touchableRegionBounds.top,
    204                                         touchableRegionBounds.right,
    205                                         touchableRegionBounds.bottom);
    206                             } catch (RemoteException re) {
    207                                 /* ignore */
    208                             }
    209                         }
    210                     } break;
    211                 } break;
    212             }
    213         }
    214     }
    215 
    216     public MagnificationSpec getMagnificationSpecForWindowLocked(WindowState windowState) {
    217         MagnificationSpec spec = mMagnifedViewport.getMagnificationSpecLocked();
    218         if (spec != null && !spec.isNop()) {
    219             WindowManagerPolicy policy = mWindowManagerService.mPolicy;
    220             final int windowType = windowState.mAttrs.type;
    221             if (!policy.isTopLevelWindow(windowType) && windowState.mAttachedWindow != null
    222                     && !policy.canMagnifyWindow(windowType)) {
    223                 return null;
    224             }
    225             if (!policy.canMagnifyWindow(windowState.mAttrs.type)) {
    226                 return null;
    227             }
    228         }
    229         return spec;
    230     }
    231 
    232     public void destroyLocked() {
    233         mMagnifedViewport.destroyWindow();
    234     }
    235 
    236     /** NOTE: This has to be called within a surface transaction. */
    237     public void drawMagnifiedRegionBorderIfNeededLocked() {
    238         mMagnifedViewport.drawWindowIfNeededLocked();
    239     }
    240 
    241     private final class MagnifiedViewport {
    242 
    243         private static final int DEFAUTLT_BORDER_WIDTH_DIP = 5;
    244 
    245         private final SparseArray<WindowStateInfo> mTempWindowStateInfos =
    246                 new SparseArray<WindowStateInfo>();
    247 
    248         private final float[] mTempFloats = new float[9];
    249 
    250         private final RectF mTempRectF = new RectF();
    251 
    252         private final Point mTempPoint = new Point();
    253 
    254         private final Matrix mTempMatrix = new Matrix();
    255 
    256         private final Region mMagnifiedBounds = new Region();
    257         private final Region mOldMagnifiedBounds = new Region();
    258 
    259         private final MagnificationSpec mMagnificationSpec = MagnificationSpec.obtain();
    260 
    261         private final WindowManager mWindowManager;
    262 
    263         private final int mBorderWidth;
    264         private final int mHalfBorderWidth;
    265 
    266         private final ViewportWindow mWindow;
    267 
    268         private boolean mFullRedrawNeeded;
    269 
    270         public MagnifiedViewport() {
    271             mWindowManager = (WindowManager) mContext.getSystemService(Service.WINDOW_SERVICE);
    272             mBorderWidth = (int) TypedValue.applyDimension(
    273                     TypedValue.COMPLEX_UNIT_DIP, DEFAUTLT_BORDER_WIDTH_DIP,
    274                             mContext.getResources().getDisplayMetrics());
    275             mHalfBorderWidth = (int) (mBorderWidth + 0.5) / 2;
    276             mWindow = new ViewportWindow(mContext);
    277             recomputeBoundsLocked();
    278         }
    279 
    280         public void updateMagnificationSpecLocked(MagnificationSpec spec) {
    281             if (spec != null) {
    282                 mMagnificationSpec.initialize(spec.scale, spec.offsetX, spec.offsetY);
    283             } else {
    284                 mMagnificationSpec.clear();
    285             }
    286             // If this message is pending we are in a rotation animation and do not want
    287             // to show the border. We will do so when the pending message is handled.
    288             if (!mHandler.hasMessages(MyHandler.MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED)) {
    289                 setMagnifiedRegionBorderShownLocked(isMagnifyingLocked(), true);
    290             }
    291         }
    292 
    293         public void recomputeBoundsLocked() {
    294             mWindowManager.getDefaultDisplay().getRealSize(mTempPoint);
    295             final int screenWidth = mTempPoint.x;
    296             final int screenHeight = mTempPoint.y;
    297 
    298             Region magnifiedBounds = mMagnifiedBounds;
    299             magnifiedBounds.set(0, 0, 0, 0);
    300 
    301             Region availableBounds = mTempRegion1;
    302             availableBounds.set(0, 0, screenWidth, screenHeight);
    303 
    304             Region nonMagnifiedBounds = mTempRegion4;
    305             nonMagnifiedBounds.set(0,  0,  0,  0);
    306 
    307             SparseArray<WindowStateInfo> visibleWindows = mTempWindowStateInfos;
    308             visibleWindows.clear();
    309             getWindowsOnScreenLocked(visibleWindows);
    310 
    311             final int visibleWindowCount = visibleWindows.size();
    312             for (int i = visibleWindowCount - 1; i >= 0; i--) {
    313                 WindowStateInfo info = visibleWindows.valueAt(i);
    314                 if (info.mWindowState.mAttrs.type == WindowManager
    315                         .LayoutParams.TYPE_MAGNIFICATION_OVERLAY) {
    316                     continue;
    317                 }
    318 
    319                 Region windowBounds = mTempRegion2;
    320                 Matrix matrix = mTempMatrix;
    321                 populateTransformationMatrix(info.mWindowState, matrix);
    322                 RectF windowFrame = mTempRectF;
    323 
    324                 if (mWindowManagerService.mPolicy.canMagnifyWindow(info.mWindowState.mAttrs.type)) {
    325                     windowFrame.set(info.mWindowState.mFrame);
    326                     windowFrame.offset(-windowFrame.left, -windowFrame.top);
    327                     matrix.mapRect(windowFrame);
    328                     windowBounds.set((int) windowFrame.left, (int) windowFrame.top,
    329                             (int) windowFrame.right, (int) windowFrame.bottom);
    330                     magnifiedBounds.op(windowBounds, Region.Op.UNION);
    331                     magnifiedBounds.op(availableBounds, Region.Op.INTERSECT);
    332                 } else {
    333                     windowFrame.set(info.mTouchableRegion);
    334                     windowFrame.offset(-info.mWindowState.mFrame.left,
    335                             -info.mWindowState.mFrame.top);
    336                     matrix.mapRect(windowFrame);
    337                     windowBounds.set((int) windowFrame.left, (int) windowFrame.top,
    338                             (int) windowFrame.right, (int) windowFrame.bottom);
    339                     nonMagnifiedBounds.op(windowBounds, Region.Op.UNION);
    340                     windowBounds.op(magnifiedBounds, Region.Op.DIFFERENCE);
    341                     availableBounds.op(windowBounds, Region.Op.DIFFERENCE);
    342                 }
    343 
    344                 Region accountedBounds = mTempRegion2;
    345                 accountedBounds.set(magnifiedBounds);
    346                 accountedBounds.op(nonMagnifiedBounds, Region.Op.UNION);
    347                 accountedBounds.op(0, 0, screenWidth, screenHeight, Region.Op.INTERSECT);
    348 
    349                 if (accountedBounds.isRect()) {
    350                     Rect accountedFrame = mTempRect1;
    351                     accountedBounds.getBounds(accountedFrame);
    352                     if (accountedFrame.width() == screenWidth
    353                             && accountedFrame.height() == screenHeight) {
    354                         break;
    355                     }
    356                 }
    357             }
    358 
    359             for (int i = visibleWindowCount - 1; i >= 0; i--) {
    360                 WindowStateInfo info = visibleWindows.valueAt(i);
    361                 info.recycle();
    362                 visibleWindows.removeAt(i);
    363             }
    364 
    365             magnifiedBounds.op(mHalfBorderWidth, mHalfBorderWidth,
    366                     screenWidth - mHalfBorderWidth, screenHeight - mHalfBorderWidth,
    367                     Region.Op.INTERSECT);
    368 
    369             if (!mOldMagnifiedBounds.equals(magnifiedBounds)) {
    370                 Region bounds = Region.obtain();
    371                 bounds.set(magnifiedBounds);
    372                 mHandler.obtainMessage(MyHandler.MESSAGE_NOTIFY_MAGNIFIED_BOUNDS_CHANGED,
    373                         bounds).sendToTarget();
    374 
    375                 mWindow.setBounds(magnifiedBounds);
    376                 Rect dirtyRect = mTempRect1;
    377                 if (mFullRedrawNeeded) {
    378                     mFullRedrawNeeded = false;
    379                     dirtyRect.set(mHalfBorderWidth, mHalfBorderWidth,
    380                             screenWidth - mHalfBorderWidth, screenHeight - mHalfBorderWidth);
    381                     mWindow.invalidate(dirtyRect);
    382                 } else {
    383                     Region dirtyRegion = mTempRegion3;
    384                     dirtyRegion.set(magnifiedBounds);
    385                     dirtyRegion.op(mOldMagnifiedBounds, Region.Op.UNION);
    386                     dirtyRegion.op(nonMagnifiedBounds, Region.Op.INTERSECT);
    387                     dirtyRegion.getBounds(dirtyRect);
    388                     mWindow.invalidate(dirtyRect);
    389                 }
    390 
    391                 mOldMagnifiedBounds.set(magnifiedBounds);
    392             }
    393         }
    394 
    395         private void populateTransformationMatrix(WindowState windowState, Matrix outMatrix) {
    396             mTempFloats[Matrix.MSCALE_X] = windowState.mWinAnimator.mDsDx;
    397             mTempFloats[Matrix.MSKEW_Y] = windowState.mWinAnimator.mDtDx;
    398             mTempFloats[Matrix.MSKEW_X] = windowState.mWinAnimator.mDsDy;
    399             mTempFloats[Matrix.MSCALE_Y] = windowState.mWinAnimator.mDtDy;
    400             mTempFloats[Matrix.MTRANS_X] = windowState.mShownFrame.left;
    401             mTempFloats[Matrix.MTRANS_Y] = windowState.mShownFrame.top;
    402             mTempFloats[Matrix.MPERSP_0] = 0;
    403             mTempFloats[Matrix.MPERSP_1] = 0;
    404             mTempFloats[Matrix.MPERSP_2] = 1;
    405             outMatrix.setValues(mTempFloats);
    406         }
    407 
    408         private void getWindowsOnScreenLocked(SparseArray<WindowStateInfo> outWindowStates) {
    409             DisplayContent displayContent = mWindowManagerService.getDefaultDisplayContentLocked();
    410             WindowList windowList = displayContent.getWindowList();
    411             final int windowCount = windowList.size();
    412             for (int i = 0; i < windowCount; i++) {
    413                 WindowState windowState = windowList.get(i);
    414                 if ((windowState.isOnScreen() || windowState.mAttrs.type == WindowManager
    415                         .LayoutParams.TYPE_UNIVERSE_BACKGROUND)
    416                         && !windowState.mWinAnimator.mEnterAnimationPending) {
    417                     outWindowStates.put(windowState.mLayer, WindowStateInfo.obtain(windowState));
    418                 }
    419             }
    420         }
    421 
    422         public void onRotationChangedLocked() {
    423             // If we are magnifying, hide the magnified border window immediately so
    424             // the user does not see strange artifacts during rotation. The screenshot
    425             // used for rotation has already the border. After the rotation is complete
    426             // we will show the border.
    427             if (isMagnifyingLocked()) {
    428                 setMagnifiedRegionBorderShownLocked(false, false);
    429                 final long delay = (long) (mLongAnimationDuration
    430                         * mWindowManagerService.mWindowAnimationScale);
    431                 Message message = mHandler.obtainMessage(
    432                         MyHandler.MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED);
    433                 mHandler.sendMessageDelayed(message, delay);
    434             }
    435             recomputeBoundsLocked();
    436             mWindow.updateSize();
    437         }
    438 
    439         public void setMagnifiedRegionBorderShownLocked(boolean shown, boolean animate) {
    440             if (shown) {
    441                 mFullRedrawNeeded = true;
    442                 mOldMagnifiedBounds.set(0,  0,  0,  0);
    443             }
    444             mWindow.setShown(shown, animate);
    445         }
    446 
    447         public void getMagnifiedFrameInContentCoordsLocked(Rect rect) {
    448             MagnificationSpec spec = mMagnificationSpec;
    449             mMagnifiedBounds.getBounds(rect);
    450             rect.offset((int) -spec.offsetX, (int) -spec.offsetY);
    451             rect.scale(1.0f / spec.scale);
    452         }
    453 
    454         public boolean isMagnifyingLocked() {
    455             return mMagnificationSpec.scale > 1.0f;
    456         }
    457 
    458         public MagnificationSpec getMagnificationSpecLocked() {
    459             return mMagnificationSpec;
    460         }
    461 
    462         /** NOTE: This has to be called within a surface transaction. */
    463         public void drawWindowIfNeededLocked() {
    464             recomputeBoundsLocked();
    465             mWindow.drawIfNeeded();
    466         }
    467 
    468         public void destroyWindow() {
    469             mWindow.releaseSurface();
    470         }
    471 
    472         private final class ViewportWindow {
    473             private static final String SURFACE_TITLE = "Magnification Overlay";
    474 
    475             private static final String PROPERTY_NAME_ALPHA = "alpha";
    476 
    477             private static final int MIN_ALPHA = 0;
    478             private static final int MAX_ALPHA = 255;
    479 
    480             private final Region mBounds = new Region();
    481             private final Rect mDirtyRect = new Rect();
    482             private final Paint mPaint = new Paint();
    483 
    484             private final ValueAnimator mShowHideFrameAnimator;
    485             private final SurfaceControl mSurfaceControl;
    486             private final Surface mSurface = new Surface();
    487 
    488             private boolean mShown;
    489             private int mAlpha;
    490 
    491             private boolean mInvalidated;
    492 
    493             public ViewportWindow(Context context) {
    494                 SurfaceControl surfaceControl = null;
    495                 try {
    496                     mWindowManager.getDefaultDisplay().getRealSize(mTempPoint);
    497                     surfaceControl = new SurfaceControl(mWindowManagerService.mFxSession, SURFACE_TITLE,
    498                             mTempPoint.x, mTempPoint.y, PixelFormat.TRANSLUCENT, SurfaceControl.HIDDEN);
    499                 } catch (OutOfResourcesException oore) {
    500                     /* ignore */
    501                 }
    502                 mSurfaceControl = surfaceControl;
    503                 mSurfaceControl.setLayerStack(mWindowManager.getDefaultDisplay().getLayerStack());
    504                 mSurfaceControl.setLayer(mWindowManagerService.mPolicy.windowTypeToLayerLw(
    505                         WindowManager.LayoutParams.TYPE_MAGNIFICATION_OVERLAY)
    506                         * WindowManagerService.TYPE_LAYER_MULTIPLIER);
    507                 mSurfaceControl.setPosition(0, 0);
    508                 mSurface.copyFrom(mSurfaceControl);
    509 
    510                 TypedValue typedValue = new TypedValue();
    511                 context.getTheme().resolveAttribute(R.attr.colorActivatedHighlight,
    512                         typedValue, true);
    513                 final int borderColor = context.getResources().getColor(typedValue.resourceId);
    514 
    515                 mPaint.setStyle(Paint.Style.STROKE);
    516                 mPaint.setStrokeWidth(mBorderWidth);
    517                 mPaint.setColor(borderColor);
    518 
    519                 Interpolator interpolator = new DecelerateInterpolator(2.5f);
    520                 final long longAnimationDuration = context.getResources().getInteger(
    521                         com.android.internal.R.integer.config_longAnimTime);
    522 
    523                 mShowHideFrameAnimator = ObjectAnimator.ofInt(this, PROPERTY_NAME_ALPHA,
    524                         MIN_ALPHA, MAX_ALPHA);
    525                 mShowHideFrameAnimator.setInterpolator(interpolator);
    526                 mShowHideFrameAnimator.setDuration(longAnimationDuration);
    527                 mInvalidated = true;
    528             }
    529 
    530             public void setShown(boolean shown, boolean animate) {
    531                 synchronized (mWindowManagerService.mWindowMap) {
    532                     if (mShown == shown) {
    533                         return;
    534                     }
    535                     mShown = shown;
    536                     if (animate) {
    537                         if (mShowHideFrameAnimator.isRunning()) {
    538                             mShowHideFrameAnimator.reverse();
    539                         } else {
    540                             if (shown) {
    541                                 mShowHideFrameAnimator.start();
    542                             } else {
    543                                 mShowHideFrameAnimator.reverse();
    544                             }
    545                         }
    546                     } else {
    547                         mShowHideFrameAnimator.cancel();
    548                         if (shown) {
    549                             setAlpha(MAX_ALPHA);
    550                         } else {
    551                             setAlpha(MIN_ALPHA);
    552                         }
    553                     }
    554                     if (DEBUG_VIEWPORT_WINDOW) {
    555                         Slog.i(LOG_TAG, "ViewportWindow shown: " + mShown);
    556                     }
    557                 }
    558             }
    559 
    560             @SuppressWarnings("unused")
    561             // Called reflectively from an animator.
    562             public int getAlpha() {
    563                 synchronized (mWindowManagerService.mWindowMap) {
    564                     return mAlpha;
    565                 }
    566             }
    567 
    568             public void setAlpha(int alpha) {
    569                 synchronized (mWindowManagerService.mWindowMap) {
    570                     if (mAlpha == alpha) {
    571                         return;
    572                     }
    573                     mAlpha = alpha;
    574                     invalidate(null);
    575                     if (DEBUG_VIEWPORT_WINDOW) {
    576                         Slog.i(LOG_TAG, "ViewportWindow set alpha: " + alpha);
    577                     }
    578                 }
    579             }
    580 
    581             public void setBounds(Region bounds) {
    582                 synchronized (mWindowManagerService.mWindowMap) {
    583                     if (mBounds.equals(bounds)) {
    584                         return;
    585                     }
    586                     mBounds.set(bounds);
    587                     invalidate(mDirtyRect);
    588                     if (DEBUG_VIEWPORT_WINDOW) {
    589                         Slog.i(LOG_TAG, "ViewportWindow set bounds: " + bounds);
    590                     }
    591                 }
    592             }
    593 
    594             public void updateSize() {
    595                 synchronized (mWindowManagerService.mWindowMap) {
    596                     mWindowManager.getDefaultDisplay().getRealSize(mTempPoint);
    597                     mSurfaceControl.setSize(mTempPoint.x, mTempPoint.y);
    598                     invalidate(mDirtyRect);
    599                 }
    600             }
    601 
    602             public void invalidate(Rect dirtyRect) {
    603                 if (dirtyRect != null) {
    604                     mDirtyRect.set(dirtyRect);
    605                 } else {
    606                     mDirtyRect.setEmpty();
    607                 }
    608                 mInvalidated = true;
    609                 mWindowManagerService.scheduleAnimationLocked();
    610             }
    611 
    612             /** NOTE: This has to be called within a surface transaction. */
    613             public void drawIfNeeded() {
    614                 synchronized (mWindowManagerService.mWindowMap) {
    615                     if (!mInvalidated) {
    616                         return;
    617                     }
    618                     mInvalidated = false;
    619                     Canvas canvas = null;
    620                     try {
    621                         // Empty dirty rectangle means unspecified.
    622                         if (mDirtyRect.isEmpty()) {
    623                             mBounds.getBounds(mDirtyRect);
    624                         }
    625                         mDirtyRect.inset(- mHalfBorderWidth, - mHalfBorderWidth);
    626                         canvas = mSurface.lockCanvas(mDirtyRect);
    627                         if (DEBUG_VIEWPORT_WINDOW) {
    628                             Slog.i(LOG_TAG, "Dirty rect: " + mDirtyRect);
    629                         }
    630                     } catch (IllegalArgumentException iae) {
    631                         /* ignore */
    632                     } catch (Surface.OutOfResourcesException oore) {
    633                         /* ignore */
    634                     }
    635                     if (canvas == null) {
    636                         return;
    637                     }
    638                     if (DEBUG_VIEWPORT_WINDOW) {
    639                         Slog.i(LOG_TAG, "Bounds: " + mBounds);
    640                     }
    641                     canvas.drawColor(Color.TRANSPARENT, Mode.CLEAR);
    642                     mPaint.setAlpha(mAlpha);
    643                     Path path = mBounds.getBoundaryPath();
    644                     canvas.drawPath(path, mPaint);
    645 
    646                     mSurface.unlockCanvasAndPost(canvas);
    647 
    648                     if (mAlpha > 0) {
    649                         mSurfaceControl.show();
    650                     } else {
    651                         mSurfaceControl.hide();
    652                     }
    653                 }
    654             }
    655 
    656             public void releaseSurface() {
    657                 mSurfaceControl.release();
    658                 mSurface.release();
    659             }
    660         }
    661     }
    662 
    663     private static final class WindowStateInfo {
    664         private static final int MAX_POOL_SIZE = 30;
    665 
    666         private static final SimplePool<WindowStateInfo> sPool =
    667                 new SimplePool<WindowStateInfo>(MAX_POOL_SIZE);
    668 
    669         private static final Region mTempRegion = new Region();
    670 
    671         public WindowState mWindowState;
    672         public final Rect mTouchableRegion = new Rect();
    673 
    674         public static WindowStateInfo obtain(WindowState windowState) {
    675             WindowStateInfo info = sPool.acquire();
    676             if (info == null) {
    677                 info = new WindowStateInfo();
    678             }
    679             info.mWindowState = windowState;
    680             windowState.getTouchableRegion(mTempRegion);
    681             mTempRegion.getBounds(info.mTouchableRegion);
    682             return info;
    683         }
    684 
    685         public void recycle() {
    686             mWindowState = null;
    687             mTouchableRegion.setEmpty();
    688             sPool.release(this);
    689         }
    690     }
    691 
    692     private class MyHandler extends Handler {
    693         public static final int MESSAGE_NOTIFY_MAGNIFIED_BOUNDS_CHANGED = 1;
    694         public static final int MESSAGE_NOTIFY_RECTANGLE_ON_SCREEN_REQUESTED = 2;
    695         public static final int MESSAGE_NOTIFY_USER_CONTEXT_CHANGED = 3;
    696         public static final int MESSAGE_NOTIFY_ROTATION_CHANGED = 4;
    697         public static final int MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED = 5;
    698 
    699         public MyHandler(Looper looper) {
    700             super(looper);
    701         }
    702 
    703         @Override
    704         public void handleMessage(Message message) {
    705             switch (message.what) {
    706                 case MESSAGE_NOTIFY_MAGNIFIED_BOUNDS_CHANGED: {
    707                     Region bounds = (Region) message.obj;
    708                     try {
    709                         mCallbacks.onMagnifedBoundsChanged(bounds);
    710                     } catch (RemoteException re) {
    711                         /* ignore */
    712                     } finally {
    713                         bounds.recycle();
    714                     }
    715                 } break;
    716                 case MESSAGE_NOTIFY_RECTANGLE_ON_SCREEN_REQUESTED: {
    717                     SomeArgs args = (SomeArgs) message.obj;
    718                     final int left = args.argi1;
    719                     final int top = args.argi2;
    720                     final int right = args.argi3;
    721                     final int bottom = args.argi4;
    722                     try {
    723                         mCallbacks.onRectangleOnScreenRequested(left, top, right, bottom);
    724                     } catch (RemoteException re) {
    725                         /* ignore */
    726                     } finally {
    727                         args.recycle();
    728                     }
    729                 } break;
    730                 case MESSAGE_NOTIFY_USER_CONTEXT_CHANGED: {
    731                     try {
    732                         mCallbacks.onUserContextChanged();
    733                     } catch (RemoteException re) {
    734                         /* ignore */
    735                     }
    736                 } break;
    737                 case MESSAGE_NOTIFY_ROTATION_CHANGED: {
    738                     final int rotation = message.arg1;
    739                     try {
    740                         mCallbacks.onRotationChanged(rotation);
    741                     } catch (RemoteException re) {
    742                         /* ignore */
    743                     }
    744                 } break;
    745                 case MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED : {
    746                     synchronized (mWindowManagerService.mWindowMap) {
    747                         if (mMagnifedViewport.isMagnifyingLocked()) {
    748                             mMagnifedViewport.setMagnifiedRegionBorderShownLocked(true, true);
    749                             mWindowManagerService.scheduleAnimationLocked();
    750                         }
    751                     }
    752                 } break;
    753             }
    754         }
    755     }
    756 }
    757