1/* 2 * Copyright (C) 2014 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 19 20import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_SURFACE_TRACE; 21import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; 22import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; 23 24import android.graphics.Canvas; 25import android.graphics.Color; 26import android.graphics.Paint; 27import android.graphics.PixelFormat; 28import android.graphics.Point; 29import android.graphics.PorterDuff; 30import android.graphics.PorterDuffXfermode; 31import android.graphics.Rect; 32import android.util.Slog; 33import android.view.Display; 34import android.view.Surface; 35import android.view.Surface.OutOfResourcesException; 36import android.view.SurfaceControl; 37import android.view.SurfaceSession; 38 39class CircularDisplayMask { 40 private static final String TAG = TAG_WITH_CLASS_NAME ? "CircularDisplayMask" : TAG_WM; 41 42 // size of the chin 43 private int mScreenOffset = 0; 44 // Display dimensions 45 private Point mScreenSize; 46 47 private final SurfaceControl mSurfaceControl; 48 private final Surface mSurface = new Surface(); 49 private int mLastDW; 50 private int mLastDH; 51 private boolean mDrawNeeded; 52 private Paint mPaint; 53 private int mRotation; 54 private boolean mVisible; 55 private boolean mDimensionsUnequal = false; 56 private int mMaskThickness; 57 58 public CircularDisplayMask(Display display, SurfaceSession session, int zOrder, 59 int screenOffset, int maskThickness) { 60 mScreenSize = new Point(); 61 display.getSize(mScreenSize); 62 if (mScreenSize.x != mScreenSize.y + screenOffset) { 63 Slog.w(TAG, "Screen dimensions of displayId = " + display.getDisplayId() + 64 "are not equal, circularMask will not be drawn."); 65 mDimensionsUnequal = true; 66 } 67 68 SurfaceControl ctrl = null; 69 try { 70 if (DEBUG_SURFACE_TRACE) { 71 ctrl = new WindowSurfaceController.SurfaceTrace(session, "CircularDisplayMask", 72 mScreenSize.x, mScreenSize.y, PixelFormat.TRANSLUCENT, 73 SurfaceControl.HIDDEN); 74 } else { 75 ctrl = new SurfaceControl(session, "CircularDisplayMask", mScreenSize.x, 76 mScreenSize.y, PixelFormat.TRANSLUCENT, SurfaceControl.HIDDEN); 77 } 78 ctrl.setLayerStack(display.getLayerStack()); 79 ctrl.setLayer(zOrder); 80 ctrl.setPosition(0, 0); 81 ctrl.show(); 82 mSurface.copyFrom(ctrl); 83 } catch (OutOfResourcesException e) { 84 } 85 mSurfaceControl = ctrl; 86 mDrawNeeded = true; 87 mPaint = new Paint(); 88 mPaint.setAntiAlias(true); 89 mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR)); 90 mScreenOffset = screenOffset; 91 mMaskThickness = maskThickness; 92 } 93 94 private void drawIfNeeded() { 95 if (!mDrawNeeded || !mVisible || mDimensionsUnequal) { 96 return; 97 } 98 mDrawNeeded = false; 99 100 Rect dirty = new Rect(0, 0, mScreenSize.x, mScreenSize.y); 101 Canvas c = null; 102 try { 103 c = mSurface.lockCanvas(dirty); 104 } catch (IllegalArgumentException e) { 105 } catch (Surface.OutOfResourcesException e) { 106 } 107 if (c == null) { 108 return; 109 } 110 switch (mRotation) { 111 case Surface.ROTATION_0: 112 case Surface.ROTATION_90: 113 // chin bottom or right 114 mSurfaceControl.setPosition(0, 0); 115 break; 116 case Surface.ROTATION_180: 117 // chin top 118 mSurfaceControl.setPosition(0, -mScreenOffset); 119 break; 120 case Surface.ROTATION_270: 121 // chin left 122 mSurfaceControl.setPosition(-mScreenOffset, 0); 123 break; 124 } 125 126 int circleRadius = mScreenSize.x / 2; 127 c.drawColor(Color.BLACK); 128 129 // The radius is reduced by mMaskThickness to provide an anti aliasing effect on the display edges. 130 c.drawCircle(circleRadius, circleRadius, circleRadius - mMaskThickness, mPaint); 131 mSurface.unlockCanvasAndPost(c); 132 } 133 134 // Note: caller responsible for being inside 135 // Surface.openTransaction() / closeTransaction() 136 public void setVisibility(boolean on) { 137 if (mSurfaceControl == null) { 138 return; 139 } 140 mVisible = on; 141 drawIfNeeded(); 142 if (on) { 143 mSurfaceControl.show(); 144 } else { 145 mSurfaceControl.hide(); 146 } 147 } 148 149 void positionSurface(int dw, int dh, int rotation) { 150 if (mLastDW == dw && mLastDH == dh && mRotation == rotation) { 151 return; 152 } 153 mLastDW = dw; 154 mLastDH = dh; 155 mDrawNeeded = true; 156 mRotation = rotation; 157 drawIfNeeded(); 158 } 159 160} 161