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;