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
17package com.android.server.wm;
18
19import android.animation.ObjectAnimator;
20import android.animation.ValueAnimator;
21import android.app.Service;
22import android.content.Context;
23import android.graphics.Canvas;
24import android.graphics.Color;
25import android.graphics.Matrix;
26import android.graphics.Paint;
27import android.graphics.Path;
28import android.graphics.PixelFormat;
29import android.graphics.Point;
30import android.graphics.PorterDuff.Mode;
31import android.graphics.Rect;
32import android.graphics.RectF;
33import android.graphics.Region;
34import android.os.Handler;
35import android.os.Looper;
36import android.os.Message;
37import android.os.RemoteException;
38import android.util.Pools.SimplePool;
39import android.util.Slog;
40import android.util.SparseArray;
41import android.util.TypedValue;
42import android.view.IMagnificationCallbacks;
43import android.view.MagnificationSpec;
44import android.view.Surface;
45import android.view.Surface.OutOfResourcesException;
46import android.view.SurfaceControl;
47import android.view.WindowManager;
48import android.view.WindowManagerPolicy;
49import android.view.animation.DecelerateInterpolator;
50import android.view.animation.Interpolator;
51
52import com.android.internal.R;
53import 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 */
59final 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