blob: 382d7b4a94aff80ce765dc9874e9865bab47c800 [file] [log] [blame]
/*
* Copyright (C) 2012 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.wm;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.app.Service;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PixelFormat;
import android.graphics.Point;
import android.graphics.PorterDuff.Mode;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Region;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
import android.util.Pools.SimplePool;
import android.util.Slog;
import android.util.SparseArray;
import android.util.TypedValue;
import android.view.IMagnificationCallbacks;
import android.view.MagnificationSpec;
import android.view.Surface;
import android.view.Surface.OutOfResourcesException;
import android.view.SurfaceControl;
import android.view.WindowManager;
import android.view.WindowManagerPolicy;
import android.view.animation.DecelerateInterpolator;
import android.view.animation.Interpolator;
import com.android.internal.R;
import com.android.internal.os.SomeArgs;
/**
* This class is a part of the window manager and encapsulates the
* functionality related to display magnification.
*/
final class DisplayMagnifier {
private static final String LOG_TAG = DisplayMagnifier.class.getSimpleName();
private static final boolean DEBUG_WINDOW_TRANSITIONS = false;
private static final boolean DEBUG_ROTATION = false;
private static final boolean DEBUG_LAYERS = false;
private static final boolean DEBUG_RECTANGLE_REQUESTED = false;
private static final boolean DEBUG_VIEWPORT_WINDOW = false;
private final Rect mTempRect1 = new Rect();
private final Rect mTempRect2 = new Rect();
private final Region mTempRegion1 = new Region();
private final Region mTempRegion2 = new Region();
private final Region mTempRegion3 = new Region();
private final Region mTempRegion4 = new Region();
private final Context mContext;
private final WindowManagerService mWindowManagerService;
private final MagnifiedViewport mMagnifedViewport;
private final Handler mHandler;
private final IMagnificationCallbacks mCallbacks;
private final long mLongAnimationDuration;
public DisplayMagnifier(WindowManagerService windowManagerService,
IMagnificationCallbacks callbacks) {
mContext = windowManagerService.mContext;
mWindowManagerService = windowManagerService;
mCallbacks = callbacks;
mHandler = new MyHandler(mWindowManagerService.mH.getLooper());
mMagnifedViewport = new MagnifiedViewport();
mLongAnimationDuration = mContext.getResources().getInteger(
com.android.internal.R.integer.config_longAnimTime);
}
public void setMagnificationSpecLocked(MagnificationSpec spec) {
mMagnifedViewport.updateMagnificationSpecLocked(spec);
mMagnifedViewport.recomputeBoundsLocked();
mWindowManagerService.scheduleAnimationLocked();
}
public void onRectangleOnScreenRequestedLocked(Rect rectangle, boolean immediate) {
if (DEBUG_RECTANGLE_REQUESTED) {
Slog.i(LOG_TAG, "Rectangle on screen requested: " + rectangle);
}
if (!mMagnifedViewport.isMagnifyingLocked()) {
return;
}
Rect magnifiedRegionBounds = mTempRect2;
mMagnifedViewport.getMagnifiedFrameInContentCoordsLocked(magnifiedRegionBounds);
if (magnifiedRegionBounds.contains(rectangle)) {
return;
}
SomeArgs args = SomeArgs.obtain();
args.argi1 = rectangle.left;
args.argi2 = rectangle.top;
args.argi3 = rectangle.right;
args.argi4 = rectangle.bottom;
mHandler.obtainMessage(MyHandler.MESSAGE_NOTIFY_RECTANGLE_ON_SCREEN_REQUESTED,
args).sendToTarget();
}
public void onWindowLayersChangedLocked() {
if (DEBUG_LAYERS) {
Slog.i(LOG_TAG, "Layers changed.");
}
mMagnifedViewport.recomputeBoundsLocked();
mWindowManagerService.scheduleAnimationLocked();
}
public void onRotationChangedLocked(DisplayContent displayContent, int rotation) {
if (DEBUG_ROTATION) {
Slog.i(LOG_TAG, "Rotaton: " + Surface.rotationToString(rotation)
+ " displayId: " + displayContent.getDisplayId());
}
mMagnifedViewport.onRotationChangedLocked();
mHandler.sendEmptyMessage(MyHandler.MESSAGE_NOTIFY_ROTATION_CHANGED);
}
public void onAppWindowTransitionLocked(WindowState windowState, int transition) {
if (DEBUG_WINDOW_TRANSITIONS) {
Slog.i(LOG_TAG, "Window transition: "
+ AppTransition.appTransitionToString(transition)
+ " displayId: " + windowState.getDisplayId());
}
final boolean magnifying = mMagnifedViewport.isMagnifyingLocked();
if (magnifying) {
switch (transition) {
case AppTransition.TRANSIT_ACTIVITY_OPEN:
case AppTransition.TRANSIT_TASK_OPEN:
case AppTransition.TRANSIT_TASK_TO_FRONT:
case AppTransition.TRANSIT_WALLPAPER_OPEN:
case AppTransition.TRANSIT_WALLPAPER_CLOSE:
case AppTransition.TRANSIT_WALLPAPER_INTRA_OPEN: {
mHandler.sendEmptyMessage(MyHandler.MESSAGE_NOTIFY_USER_CONTEXT_CHANGED);
}
}
}
}
public void onWindowTransitionLocked(WindowState windowState, int transition) {
if (DEBUG_WINDOW_TRANSITIONS) {
Slog.i(LOG_TAG, "Window transition: "
+ AppTransition.appTransitionToString(transition)
+ " displayId: " + windowState.getDisplayId());
}
final boolean magnifying = mMagnifedViewport.isMagnifyingLocked();
final int type = windowState.mAttrs.type;
switch (transition) {
case WindowManagerPolicy.TRANSIT_ENTER:
case WindowManagerPolicy.TRANSIT_SHOW: {
if (!magnifying) {
break;
}
switch (type) {
case WindowManager.LayoutParams.TYPE_APPLICATION:
case WindowManager.LayoutParams.TYPE_APPLICATION_PANEL:
case WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA:
case WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL:
case WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG:
case WindowManager.LayoutParams.TYPE_SEARCH_BAR:
case WindowManager.LayoutParams.TYPE_PHONE:
case WindowManager.LayoutParams.TYPE_SYSTEM_ALERT:
case WindowManager.LayoutParams.TYPE_TOAST:
case WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY:
case WindowManager.LayoutParams.TYPE_PRIORITY_PHONE:
case WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG:
case WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG:
case WindowManager.LayoutParams.TYPE_SYSTEM_ERROR:
case WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY:
case WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL:
case WindowManager.LayoutParams.TYPE_RECENTS_OVERLAY: {
Rect magnifiedRegionBounds = mTempRect2;
mMagnifedViewport.getMagnifiedFrameInContentCoordsLocked(
magnifiedRegionBounds);
Rect touchableRegionBounds = mTempRect1;
windowState.getTouchableRegion(mTempRegion1);
mTempRegion1.getBounds(touchableRegionBounds);
if (!magnifiedRegionBounds.intersect(touchableRegionBounds)) {
try {
mCallbacks.onRectangleOnScreenRequested(
touchableRegionBounds.left,
touchableRegionBounds.top,
touchableRegionBounds.right,
touchableRegionBounds.bottom);
} catch (RemoteException re) {
/* ignore */
}
}
} break;
} break;
}
}
}
public MagnificationSpec getMagnificationSpecForWindowLocked(WindowState windowState) {
MagnificationSpec spec = mMagnifedViewport.getMagnificationSpecLocked();
if (spec != null && !spec.isNop()) {
WindowManagerPolicy policy = mWindowManagerService.mPolicy;
final int windowType = windowState.mAttrs.type;
if (!policy.isTopLevelWindow(windowType) && windowState.mAttachedWindow != null
&& !policy.canMagnifyWindow(windowType)) {
return null;
}
if (!policy.canMagnifyWindow(windowState.mAttrs.type)) {
return null;
}
}
return spec;
}
public void destroyLocked() {
mMagnifedViewport.destroyWindow();
}
/** NOTE: This has to be called within a surface transaction. */
public void drawMagnifiedRegionBorderIfNeededLocked() {
mMagnifedViewport.drawWindowIfNeededLocked();
}
private final class MagnifiedViewport {
private static final int DEFAUTLT_BORDER_WIDTH_DIP = 5;
private final SparseArray<WindowStateInfo> mTempWindowStateInfos =
new SparseArray<WindowStateInfo>();
private final float[] mTempFloats = new float[9];
private final RectF mTempRectF = new RectF();
private final Point mTempPoint = new Point();
private final Matrix mTempMatrix = new Matrix();
private final Region mMagnifiedBounds = new Region();
private final Region mOldMagnifiedBounds = new Region();
private final MagnificationSpec mMagnificationSpec = MagnificationSpec.obtain();
private final WindowManager mWindowManager;
private final int mBorderWidth;
private final int mHalfBorderWidth;
private final ViewportWindow mWindow;
private boolean mFullRedrawNeeded;
public MagnifiedViewport() {
mWindowManager = (WindowManager) mContext.getSystemService(Service.WINDOW_SERVICE);
mBorderWidth = (int) TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP, DEFAUTLT_BORDER_WIDTH_DIP,
mContext.getResources().getDisplayMetrics());
mHalfBorderWidth = (int) (mBorderWidth + 0.5) / 2;
mWindow = new ViewportWindow(mContext);
recomputeBoundsLocked();
}
public void updateMagnificationSpecLocked(MagnificationSpec spec) {
if (spec != null) {
mMagnificationSpec.initialize(spec.scale, spec.offsetX, spec.offsetY);
} else {
mMagnificationSpec.clear();
}
// If this message is pending we are in a rotation animation and do not want
// to show the border. We will do so when the pending message is handled.
if (!mHandler.hasMessages(MyHandler.MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED)) {
setMagnifiedRegionBorderShownLocked(isMagnifyingLocked(), true);
}
}
public void recomputeBoundsLocked() {
mWindowManager.getDefaultDisplay().getRealSize(mTempPoint);
final int screenWidth = mTempPoint.x;
final int screenHeight = mTempPoint.y;
Region magnifiedBounds = mMagnifiedBounds;
magnifiedBounds.set(0, 0, 0, 0);
Region availableBounds = mTempRegion1;
availableBounds.set(0, 0, screenWidth, screenHeight);
Region nonMagnifiedBounds = mTempRegion4;
nonMagnifiedBounds.set(0, 0, 0, 0);
SparseArray<WindowStateInfo> visibleWindows = mTempWindowStateInfos;
visibleWindows.clear();
getWindowsOnScreenLocked(visibleWindows);
final int visibleWindowCount = visibleWindows.size();
for (int i = visibleWindowCount - 1; i >= 0; i--) {
WindowStateInfo info = visibleWindows.valueAt(i);
if (info.mWindowState.mAttrs.type == WindowManager
.LayoutParams.TYPE_MAGNIFICATION_OVERLAY) {
continue;
}
Region windowBounds = mTempRegion2;
Matrix matrix = mTempMatrix;
populateTransformationMatrix(info.mWindowState, matrix);
RectF windowFrame = mTempRectF;
if (mWindowManagerService.mPolicy.canMagnifyWindow(info.mWindowState.mAttrs.type)) {
windowFrame.set(info.mWindowState.mFrame);
windowFrame.offset(-windowFrame.left, -windowFrame.top);
matrix.mapRect(windowFrame);
windowBounds.set((int) windowFrame.left, (int) windowFrame.top,
(int) windowFrame.right, (int) windowFrame.bottom);
magnifiedBounds.op(windowBounds, Region.Op.UNION);
magnifiedBounds.op(availableBounds, Region.Op.INTERSECT);
} else {
windowFrame.set(info.mTouchableRegion);
windowFrame.offset(-info.mWindowState.mFrame.left,
-info.mWindowState.mFrame.top);
matrix.mapRect(windowFrame);
windowBounds.set((int) windowFrame.left, (int) windowFrame.top,
(int) windowFrame.right, (int) windowFrame.bottom);
nonMagnifiedBounds.op(windowBounds, Region.Op.UNION);
windowBounds.op(magnifiedBounds, Region.Op.DIFFERENCE);
availableBounds.op(windowBounds, Region.Op.DIFFERENCE);
}
Region accountedBounds = mTempRegion2;
accountedBounds.set(magnifiedBounds);
accountedBounds.op(nonMagnifiedBounds, Region.Op.UNION);
accountedBounds.op(0, 0, screenWidth, screenHeight, Region.Op.INTERSECT);
if (accountedBounds.isRect()) {
Rect accountedFrame = mTempRect1;
accountedBounds.getBounds(accountedFrame);
if (accountedFrame.width() == screenWidth
&& accountedFrame.height() == screenHeight) {
break;
}
}
}
for (int i = visibleWindowCount - 1; i >= 0; i--) {
WindowStateInfo info = visibleWindows.valueAt(i);
info.recycle();
visibleWindows.removeAt(i);
}
magnifiedBounds.op(mHalfBorderWidth, mHalfBorderWidth,
screenWidth - mHalfBorderWidth, screenHeight - mHalfBorderWidth,
Region.Op.INTERSECT);
if (!mOldMagnifiedBounds.equals(magnifiedBounds)) {
Region bounds = Region.obtain();
bounds.set(magnifiedBounds);
mHandler.obtainMessage(MyHandler.MESSAGE_NOTIFY_MAGNIFIED_BOUNDS_CHANGED,
bounds).sendToTarget();
mWindow.setBounds(magnifiedBounds);
Rect dirtyRect = mTempRect1;
if (mFullRedrawNeeded) {
mFullRedrawNeeded = false;
dirtyRect.set(mHalfBorderWidth, mHalfBorderWidth,
screenWidth - mHalfBorderWidth, screenHeight - mHalfBorderWidth);
mWindow.invalidate(dirtyRect);
} else {
Region dirtyRegion = mTempRegion3;
dirtyRegion.set(magnifiedBounds);
dirtyRegion.op(mOldMagnifiedBounds, Region.Op.UNION);
dirtyRegion.op(nonMagnifiedBounds, Region.Op.INTERSECT);
dirtyRegion.getBounds(dirtyRect);
mWindow.invalidate(dirtyRect);
}
mOldMagnifiedBounds.set(magnifiedBounds);
}
}
private void populateTransformationMatrix(WindowState windowState, Matrix outMatrix) {
mTempFloats[Matrix.MSCALE_X] = windowState.mWinAnimator.mDsDx;
mTempFloats[Matrix.MSKEW_Y] = windowState.mWinAnimator.mDtDx;
mTempFloats[Matrix.MSKEW_X] = windowState.mWinAnimator.mDsDy;
mTempFloats[Matrix.MSCALE_Y] = windowState.mWinAnimator.mDtDy;
mTempFloats[Matrix.MTRANS_X] = windowState.mShownFrame.left;
mTempFloats[Matrix.MTRANS_Y] = windowState.mShownFrame.top;
mTempFloats[Matrix.MPERSP_0] = 0;
mTempFloats[Matrix.MPERSP_1] = 0;
mTempFloats[Matrix.MPERSP_2] = 1;
outMatrix.setValues(mTempFloats);
}
private void getWindowsOnScreenLocked(SparseArray<WindowStateInfo> outWindowStates) {
DisplayContent displayContent = mWindowManagerService.getDefaultDisplayContentLocked();
WindowList windowList = displayContent.getWindowList();
final int windowCount = windowList.size();
for (int i = 0; i < windowCount; i++) {
WindowState windowState = windowList.get(i);
if ((windowState.isOnScreen() || windowState.mAttrs.type == WindowManager
.LayoutParams.TYPE_UNIVERSE_BACKGROUND)
&& !windowState.mWinAnimator.mEnterAnimationPending) {
outWindowStates.put(windowState.mLayer, WindowStateInfo.obtain(windowState));
}
}
}
public void onRotationChangedLocked() {
// If we are magnifying, hide the magnified border window immediately so
// the user does not see strange artifacts during rotation. The screenshot
// used for rotation has already the border. After the rotation is complete
// we will show the border.
if (isMagnifyingLocked()) {
setMagnifiedRegionBorderShownLocked(false, false);
final long delay = (long) (mLongAnimationDuration
* mWindowManagerService.mWindowAnimationScale);
Message message = mHandler.obtainMessage(
MyHandler.MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED);
mHandler.sendMessageDelayed(message, delay);
}
recomputeBoundsLocked();
mWindow.updateSize();
}
public void setMagnifiedRegionBorderShownLocked(boolean shown, boolean animate) {
if (shown) {
mFullRedrawNeeded = true;
mOldMagnifiedBounds.set(0, 0, 0, 0);
}
mWindow.setShown(shown, animate);
}
public void getMagnifiedFrameInContentCoordsLocked(Rect rect) {
MagnificationSpec spec = mMagnificationSpec;
mMagnifiedBounds.getBounds(rect);
rect.offset((int) -spec.offsetX, (int) -spec.offsetY);
rect.scale(1.0f / spec.scale);
}
public boolean isMagnifyingLocked() {
return mMagnificationSpec.scale > 1.0f;
}
public MagnificationSpec getMagnificationSpecLocked() {
return mMagnificationSpec;
}
/** NOTE: This has to be called within a surface transaction. */
public void drawWindowIfNeededLocked() {
recomputeBoundsLocked();
mWindow.drawIfNeeded();
}
public void destroyWindow() {
mWindow.releaseSurface();
}
private final class ViewportWindow {
private static final String SURFACE_TITLE = "Magnification Overlay";
private static final String PROPERTY_NAME_ALPHA = "alpha";
private static final int MIN_ALPHA = 0;
private static final int MAX_ALPHA = 255;
private final Region mBounds = new Region();
private final Rect mDirtyRect = new Rect();
private final Paint mPaint = new Paint();
private final ValueAnimator mShowHideFrameAnimator;
private final SurfaceControl mSurfaceControl;
private final Surface mSurface = new Surface();
private boolean mShown;
private int mAlpha;
private boolean mInvalidated;
public ViewportWindow(Context context) {
SurfaceControl surfaceControl = null;
try {
mWindowManager.getDefaultDisplay().getRealSize(mTempPoint);
surfaceControl = new SurfaceControl(mWindowManagerService.mFxSession, SURFACE_TITLE,
mTempPoint.x, mTempPoint.y, PixelFormat.TRANSLUCENT, SurfaceControl.HIDDEN);
} catch (OutOfResourcesException oore) {
/* ignore */
}
mSurfaceControl = surfaceControl;
mSurfaceControl.setLayerStack(mWindowManager.getDefaultDisplay().getLayerStack());
mSurfaceControl.setLayer(mWindowManagerService.mPolicy.windowTypeToLayerLw(
WindowManager.LayoutParams.TYPE_MAGNIFICATION_OVERLAY)
* WindowManagerService.TYPE_LAYER_MULTIPLIER);
mSurfaceControl.setPosition(0, 0);
mSurface.copyFrom(mSurfaceControl);
TypedValue typedValue = new TypedValue();
context.getTheme().resolveAttribute(R.attr.colorActivatedHighlight,
typedValue, true);
final int borderColor = context.getResources().getColor(typedValue.resourceId);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(mBorderWidth);
mPaint.setColor(borderColor);
Interpolator interpolator = new DecelerateInterpolator(2.5f);
final long longAnimationDuration = context.getResources().getInteger(
com.android.internal.R.integer.config_longAnimTime);
mShowHideFrameAnimator = ObjectAnimator.ofInt(this, PROPERTY_NAME_ALPHA,
MIN_ALPHA, MAX_ALPHA);
mShowHideFrameAnimator.setInterpolator(interpolator);
mShowHideFrameAnimator.setDuration(longAnimationDuration);
mInvalidated = true;
}
public void setShown(boolean shown, boolean animate) {
synchronized (mWindowManagerService.mWindowMap) {
if (mShown == shown) {
return;
}
mShown = shown;
if (animate) {
if (mShowHideFrameAnimator.isRunning()) {
mShowHideFrameAnimator.reverse();
} else {
if (shown) {
mShowHideFrameAnimator.start();
} else {
mShowHideFrameAnimator.reverse();
}
}
} else {
mShowHideFrameAnimator.cancel();
if (shown) {
setAlpha(MAX_ALPHA);
} else {
setAlpha(MIN_ALPHA);
}
}
if (DEBUG_VIEWPORT_WINDOW) {
Slog.i(LOG_TAG, "ViewportWindow shown: " + mShown);
}
}
}
@SuppressWarnings("unused")
// Called reflectively from an animator.
public int getAlpha() {
synchronized (mWindowManagerService.mWindowMap) {
return mAlpha;
}
}
public void setAlpha(int alpha) {
synchronized (mWindowManagerService.mWindowMap) {
if (mAlpha == alpha) {
return;
}
mAlpha = alpha;
invalidate(null);
if (DEBUG_VIEWPORT_WINDOW) {
Slog.i(LOG_TAG, "ViewportWindow set alpha: " + alpha);
}
}
}
public void setBounds(Region bounds) {
synchronized (mWindowManagerService.mWindowMap) {
if (mBounds.equals(bounds)) {
return;
}
mBounds.set(bounds);
invalidate(mDirtyRect);
if (DEBUG_VIEWPORT_WINDOW) {
Slog.i(LOG_TAG, "ViewportWindow set bounds: " + bounds);
}
}
}
public void updateSize() {
synchronized (mWindowManagerService.mWindowMap) {
mWindowManager.getDefaultDisplay().getRealSize(mTempPoint);
mSurfaceControl.setSize(mTempPoint.x, mTempPoint.y);
invalidate(mDirtyRect);
}
}
public void invalidate(Rect dirtyRect) {
if (dirtyRect != null) {
mDirtyRect.set(dirtyRect);
} else {
mDirtyRect.setEmpty();
}
mInvalidated = true;
mWindowManagerService.scheduleAnimationLocked();
}
/** NOTE: This has to be called within a surface transaction. */
public void drawIfNeeded() {
synchronized (mWindowManagerService.mWindowMap) {
if (!mInvalidated) {
return;
}
mInvalidated = false;
Canvas canvas = null;
try {
// Empty dirty rectangle means unspecified.
if (mDirtyRect.isEmpty()) {
mBounds.getBounds(mDirtyRect);
}
mDirtyRect.inset(- mHalfBorderWidth, - mHalfBorderWidth);
canvas = mSurface.lockCanvas(mDirtyRect);
if (DEBUG_VIEWPORT_WINDOW) {
Slog.i(LOG_TAG, "Dirty rect: " + mDirtyRect);
}
} catch (IllegalArgumentException iae) {
/* ignore */
} catch (Surface.OutOfResourcesException oore) {
/* ignore */
}
if (canvas == null) {
return;
}
if (DEBUG_VIEWPORT_WINDOW) {
Slog.i(LOG_TAG, "Bounds: " + mBounds);
}
canvas.drawColor(Color.TRANSPARENT, Mode.CLEAR);
mPaint.setAlpha(mAlpha);
Path path = mBounds.getBoundaryPath();
canvas.drawPath(path, mPaint);
mSurface.unlockCanvasAndPost(canvas);
if (mAlpha > 0) {
mSurfaceControl.show();
} else {
mSurfaceControl.hide();
}
}
}
public void releaseSurface() {
mSurfaceControl.release();
mSurface.release();
}
}
}
private static final class WindowStateInfo {
private static final int MAX_POOL_SIZE = 30;
private static final SimplePool<WindowStateInfo> sPool =
new SimplePool<WindowStateInfo>(MAX_POOL_SIZE);
private static final Region mTempRegion = new Region();
public WindowState mWindowState;
public final Rect mTouchableRegion = new Rect();
public static WindowStateInfo obtain(WindowState windowState) {
WindowStateInfo info = sPool.acquire();
if (info == null) {
info = new WindowStateInfo();
}
info.mWindowState = windowState;
windowState.getTouchableRegion(mTempRegion);
mTempRegion.getBounds(info.mTouchableRegion);
return info;
}
public void recycle() {
mWindowState = null;
mTouchableRegion.setEmpty();
sPool.release(this);
}
}
private class MyHandler extends Handler {
public static final int MESSAGE_NOTIFY_MAGNIFIED_BOUNDS_CHANGED = 1;
public static final int MESSAGE_NOTIFY_RECTANGLE_ON_SCREEN_REQUESTED = 2;
public static final int MESSAGE_NOTIFY_USER_CONTEXT_CHANGED = 3;
public static final int MESSAGE_NOTIFY_ROTATION_CHANGED = 4;
public static final int MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED = 5;
public MyHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message message) {
switch (message.what) {
case MESSAGE_NOTIFY_MAGNIFIED_BOUNDS_CHANGED: {
Region bounds = (Region) message.obj;
try {
mCallbacks.onMagnifedBoundsChanged(bounds);
} catch (RemoteException re) {
/* ignore */
} finally {
bounds.recycle();
}
} break;
case MESSAGE_NOTIFY_RECTANGLE_ON_SCREEN_REQUESTED: {
SomeArgs args = (SomeArgs) message.obj;
final int left = args.argi1;
final int top = args.argi2;
final int right = args.argi3;
final int bottom = args.argi4;
try {
mCallbacks.onRectangleOnScreenRequested(left, top, right, bottom);
} catch (RemoteException re) {
/* ignore */
} finally {
args.recycle();
}
} break;
case MESSAGE_NOTIFY_USER_CONTEXT_CHANGED: {
try {
mCallbacks.onUserContextChanged();
} catch (RemoteException re) {
/* ignore */
}
} break;
case MESSAGE_NOTIFY_ROTATION_CHANGED: {
final int rotation = message.arg1;
try {
mCallbacks.onRotationChanged(rotation);
} catch (RemoteException re) {
/* ignore */
}
} break;
case MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED : {
synchronized (mWindowManagerService.mWindowMap) {
if (mMagnifedViewport.isMagnifyingLocked()) {
mMagnifedViewport.setMagnifiedRegionBorderShownLocked(true, true);
mWindowManagerService.scheduleAnimationLocked();
}
}
} break;
}
}
}
}