diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 199e5b1d6d371..8c51c4a5914f2 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -1490,6 +1490,9 @@ true + + false + -1px + + + 0px diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 79dcde9999839..e4e458398430a 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -292,6 +292,7 @@ + @@ -356,6 +357,7 @@ + diff --git a/services/core/java/com/android/server/wm/CircularDisplayMask.java b/services/core/java/com/android/server/wm/CircularDisplayMask.java new file mode 100644 index 0000000000000..64b852b9de06d --- /dev/null +++ b/services/core/java/com/android/server/wm/CircularDisplayMask.java @@ -0,0 +1,149 @@ +/* + * Copyright (C) 2014 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.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.PixelFormat; +import android.graphics.Point; +import android.graphics.PorterDuff; +import android.graphics.Rect; +import android.view.Display; +import android.view.Surface; +import android.view.Surface.OutOfResourcesException; +import android.view.SurfaceControl; +import android.view.SurfaceSession; +import android.util.Slog; + +class CircularDisplayMask { + private static final String TAG = "CircularDisplayMask"; + + private static final int STROKE_WIDTH = 2; + // size of the chin + private int mScreenOffset = 0; + // Display dimensions + private Point mScreenSize; + + private final SurfaceControl mSurfaceControl; + private final Surface mSurface = new Surface(); + private int mLastDW; + private int mLastDH; + private boolean mDrawNeeded; + private Paint mPaint; + private int mRotation; + private boolean mVisible; + private boolean mDimensionsUnequal = false; + + public CircularDisplayMask(Display display, SurfaceSession session, int zOrder, + int screenOffset) { + mScreenSize = new Point(); + display.getSize(mScreenSize); + if (mScreenSize.x != mScreenSize.y) { + Slog.w(TAG, "Screen dimensions of displayId = " + display.getDisplayId() + + "are not equal, circularMask will not be drawn."); + mDimensionsUnequal = true; + } + + SurfaceControl ctrl = null; + try { + ctrl = new SurfaceControl(session, "CircularDisplayMask", + mScreenSize.x, mScreenSize.y, PixelFormat.TRANSLUCENT, SurfaceControl.HIDDEN); + ctrl.setLayerStack(display.getLayerStack()); + ctrl.setLayer(zOrder); + ctrl.setPosition(0, 0); + ctrl.show(); + mSurface.copyFrom(ctrl); + } catch (OutOfResourcesException e) { + } + mSurfaceControl = ctrl; + mDrawNeeded = true; + mPaint = new Paint(); + mPaint.setAntiAlias(true); + mPaint.setStyle(Paint.Style.STROKE); + mPaint.setColor(Color.BLACK); + mPaint.setStrokeWidth(STROKE_WIDTH); + mScreenOffset = screenOffset; + } + + private void drawIfNeeded() { + if (!mDrawNeeded || !mVisible || mDimensionsUnequal) { + return; + } + mDrawNeeded = false; + + Rect dirty = new Rect(0, 0, mScreenSize.x, mScreenSize.y); + Canvas c = null; + try { + c = mSurface.lockCanvas(dirty); + } catch (IllegalArgumentException e) { + } catch (Surface.OutOfResourcesException e) { + } + if (c == null) { + return; + } + c.drawColor(Color.TRANSPARENT, PorterDuff.Mode.SRC); + switch (mRotation) { + case Surface.ROTATION_0: + case Surface.ROTATION_90: + // chin bottom or right + mSurfaceControl.setPosition(0, 0); + break; + case Surface.ROTATION_180: + // chin top + mSurfaceControl.setPosition(0, -mScreenOffset); + break; + case Surface.ROTATION_270: + // chin left + mSurfaceControl.setPosition(-mScreenOffset, 0); + break; + } + + int circleRadius = mScreenSize.x / 2; + c.drawCircle(circleRadius, circleRadius, circleRadius, mPaint); + mSurface.unlockCanvasAndPost(c); + } + + // Note: caller responsible for being inside + // Surface.openTransaction() / closeTransaction() + public void setVisibility(boolean on) { + if (mSurfaceControl == null) { + return; + } + mVisible = on; + drawIfNeeded(); + if (on) { + mSurfaceControl.show(); + } else { + mSurfaceControl.hide(); + } + } + + void positionSurface(int dw, int dh, int rotation) { + if (mLastDW == dw && mLastDH == dh && mRotation == rotation) { + return; + } + mLastDW = dw; + mLastDH = dh; + mDrawNeeded = true; + mRotation = rotation; + drawIfNeeded(); + } + +} diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 5626c7a2aad03..751919bfe6a6f 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -422,6 +422,7 @@ public class WindowManagerService extends IWindowManager.Stub final SurfaceSession mFxSession; Watermark mWatermark; StrictModeFlash mStrictModeFlash; + CircularDisplayMask mCircularDisplayMask; FocusedStackFrame mFocusedStackFrame; int mFocusedStackLayer; @@ -818,6 +819,8 @@ public class WindowManagerService extends IWindowManager.Stub } finally { SurfaceControl.closeTransaction(); } + + showCircularDisplayMaskIfNeeded(); } public InputMonitor getInputMonitor() { @@ -5549,6 +5552,44 @@ public class WindowManagerService extends IWindowManager.Stub } } + public void showCircularDisplayMaskIfNeeded() { + // we're fullscreen and not hosted in an ActivityView + if (mContext.getResources().getBoolean( + com.android.internal.R.bool.config_windowIsRound) + && mContext.getResources().getBoolean( + com.android.internal.R.bool.config_windowShowCircularMask)) { + mH.sendMessage(mH.obtainMessage(H.SHOW_DISPLAY_MASK)); + } + } + + public void showCircularMask() { + synchronized(mWindowMap) { + + if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG, + ">>> OPEN TRANSACTION showDisplayMask"); + SurfaceControl.openTransaction(); + try { + // TODO(multi-display): support multiple displays + if (mCircularDisplayMask == null) { + int screenOffset = (int) mContext.getResources().getDimensionPixelSize( + com.android.internal.R.dimen.circular_display_mask_offset); + + mCircularDisplayMask = new CircularDisplayMask( + getDefaultDisplayContentLocked().getDisplay(), + mFxSession, + mPolicy.windowTypeToLayerLw( + WindowManager.LayoutParams.TYPE_POINTER) + * TYPE_LAYER_MULTIPLIER + 10, screenOffset); + } + mCircularDisplayMask.setVisibility(true); + } finally { + SurfaceControl.closeTransaction(); + if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG, + "<<< CLOSE TRANSACTION showDisplayMask"); + } + } + } + // TODO: more accounting of which pid(s) turned it on, keep count, // only allow disables from pids which have count on, etc. @Override @@ -7164,7 +7205,8 @@ public class WindowManagerService extends IWindowManager.Stub public static final int REMOVE_STARTING_TIMEOUT = 33; - public static final int ALL_WINDOWS_DRAWN = 34; + public static final int SHOW_DISPLAY_MASK = 34; + public static final int ALL_WINDOWS_DRAWN = 35; @Override public void handleMessage(Message msg) { @@ -7568,6 +7610,11 @@ public class WindowManagerService extends IWindowManager.Stub break; } + case SHOW_DISPLAY_MASK: { + showCircularMask(); + break; + } + case DO_ANIMATION_CALLBACK: { try { ((IRemoteCallback)msg.obj).sendResult(null); @@ -9052,6 +9099,9 @@ public class WindowManagerService extends IWindowManager.Stub if (mStrictModeFlash != null) { mStrictModeFlash.positionSurface(defaultDw, defaultDh); } + if (mCircularDisplayMask != null) { + mCircularDisplayMask.positionSurface(defaultDw, defaultDh, mRotation); + } boolean focusDisplayed = false;