• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
DisplayMagnifier(WindowManagerService windowManagerService, IMagnificationCallbacks callbacks)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 
setMagnificationSpecLocked(MagnificationSpec spec)96     public void setMagnificationSpecLocked(MagnificationSpec spec) {
97         mMagnifedViewport.updateMagnificationSpecLocked(spec);
98         mMagnifedViewport.recomputeBoundsLocked();
99         mWindowManagerService.scheduleAnimationLocked();
100     }
101 
onRectangleOnScreenRequestedLocked(Rect rectangle, boolean immediate)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 
onWindowLayersChangedLocked()123     public void onWindowLayersChangedLocked() {
124         if (DEBUG_LAYERS) {
125             Slog.i(LOG_TAG, "Layers changed.");
126         }
127         mMagnifedViewport.recomputeBoundsLocked();
128         mWindowManagerService.scheduleAnimationLocked();
129     }
130 
onRotationChangedLocked(DisplayContent displayContent, int rotation)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 
onAppWindowTransitionLocked(WindowState windowState, int transition)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 
onWindowTransitionLocked(WindowState windowState, int transition)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 
getMagnificationSpecForWindowLocked(WindowState windowState)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 
destroyLocked()232     public void destroyLocked() {
233         mMagnifedViewport.destroyWindow();
234     }
235 
236     /** NOTE: This has to be called within a surface transaction. */
drawMagnifiedRegionBorderIfNeededLocked()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 
MagnifiedViewport()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 
updateMagnificationSpecLocked(MagnificationSpec spec)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 
recomputeBoundsLocked()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 
populateTransformationMatrix(WindowState windowState, Matrix outMatrix)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 
getWindowsOnScreenLocked(SparseArray<WindowStateInfo> outWindowStates)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 
onRotationChangedLocked()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 
setMagnifiedRegionBorderShownLocked(boolean shown, boolean animate)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 
getMagnifiedFrameInContentCoordsLocked(Rect rect)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 
isMagnifyingLocked()454         public boolean isMagnifyingLocked() {
455             return mMagnificationSpec.scale > 1.0f;
456         }
457 
getMagnificationSpecLocked()458         public MagnificationSpec getMagnificationSpecLocked() {
459             return mMagnificationSpec;
460         }
461 
462         /** NOTE: This has to be called within a surface transaction. */
drawWindowIfNeededLocked()463         public void drawWindowIfNeededLocked() {
464             recomputeBoundsLocked();
465             mWindow.drawIfNeeded();
466         }
467 
destroyWindow()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 
ViewportWindow(Context context)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 
setShown(boolean shown, boolean animate)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.
getAlpha()562             public int getAlpha() {
563                 synchronized (mWindowManagerService.mWindowMap) {
564                     return mAlpha;
565                 }
566             }
567 
setAlpha(int alpha)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 
setBounds(Region bounds)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 
updateSize()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 
invalidate(Rect dirtyRect)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. */
drawIfNeeded()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 
releaseSurface()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 
obtain(WindowState windowState)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 
recycle()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 
MyHandler(Looper looper)699         public MyHandler(Looper looper) {
700             super(looper);
701         }
702 
703         @Override
handleMessage(Message message)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