Loading libs/WindowManager/Shell/res/layout/split_outline.xml +2 −2 Original line number Diff line number Diff line Loading @@ -13,7 +13,7 @@ See the License for the specific language governing permissions and limitations under the License. --> <com.android.wm.shell.splitscreen.OutlineRoot <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> Loading @@ -23,4 +23,4 @@ android:layout_height="match_parent" android:layout_width="match_parent" /> </com.android.wm.shell.splitscreen.OutlineRoot> </FrameLayout> libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/OutlineManager.java +93 −39 Original line number Diff line number Diff line Loading @@ -22,19 +22,23 @@ import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMA import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; import android.annotation.Nullable; import android.content.Context; import android.content.res.Configuration; import android.graphics.PixelFormat; import android.graphics.Rect; import android.os.Binder; import android.view.IWindow; import android.view.InsetsSource; import android.view.InsetsState; import android.view.LayoutInflater; import android.view.SurfaceControl; import android.view.SurfaceControlViewHost; import android.view.WindowInsets; import android.view.View; import android.view.ViewGroup; import android.view.WindowManager; import android.view.WindowMetrics; import android.view.WindowlessWindowManager; import android.widget.FrameLayout; import com.android.wm.shell.R; Loading @@ -45,17 +49,22 @@ import com.android.wm.shell.R; class OutlineManager extends WindowlessWindowManager { private static final String WINDOW_NAME = "SplitOutlineLayer"; private final Context mContext; private final Rect mOutlineBounds = new Rect(); private final Rect mTmpBounds = new Rect(); private final Rect mRootBounds = new Rect(); private final Rect mTempRect = new Rect(); private final Rect mLastOutlineBounds = new Rect(); private final InsetsState mInsetsState = new InsetsState(); private final int mExpandedTaskBarHeight; private OutlineView mOutlineView; private SurfaceControlViewHost mViewHost; private SurfaceControl mHostLeash; private SurfaceControl mLeash; private int mOutlineColor; OutlineManager(Context context, Configuration configuration) { super(configuration, null /* rootSurface */, null /* hostInputToken */); mContext = context.createWindowContext(context.getDisplay(), TYPE_APPLICATION_OVERLAY, null /* options */); mExpandedTaskBarHeight = mContext.getResources().getDimensionPixelSize( com.android.internal.R.dimen.taskbar_frame_height); } @Override Loading @@ -63,65 +72,110 @@ class OutlineManager extends WindowlessWindowManager { b.setParent(mHostLeash); } boolean drawOutlineBounds(Rect rootBounds) { if (mLeash == null || mViewHost == null) return false; computeOutlineBounds(mContext, rootBounds, mTmpBounds); if (mOutlineBounds.equals(mTmpBounds)) { return false; } mOutlineBounds.set(mTmpBounds); ((OutlineRoot) mViewHost.getView()).updateOutlineBounds(mOutlineBounds, mOutlineColor); final WindowManager.LayoutParams lp = (WindowManager.LayoutParams) mViewHost.getView().getLayoutParams(); lp.width = rootBounds.width(); lp.height = rootBounds.height(); mViewHost.relayout(lp); return true; } void inflate(SurfaceControl.Transaction t, SurfaceControl hostLeash, int color) { void inflate(SurfaceControl rootLeash, Rect rootBounds) { if (mLeash != null || mViewHost != null) return; mHostLeash = hostLeash; mOutlineColor = color; mHostLeash = rootLeash; mRootBounds.set(rootBounds); mViewHost = new SurfaceControlViewHost(mContext, mContext.getDisplay(), this); final OutlineRoot rootView = (OutlineRoot) LayoutInflater.from(mContext) final FrameLayout rootLayout = (FrameLayout) LayoutInflater.from(mContext) .inflate(R.layout.split_outline, null); mOutlineView = rootLayout.findViewById(R.id.split_outline); final WindowManager.LayoutParams lp = new WindowManager.LayoutParams( 0 /* width */, 0 /* height */, TYPE_APPLICATION_OVERLAY, FLAG_NOT_FOCUSABLE | FLAG_NOT_TOUCHABLE, PixelFormat.TRANSLUCENT); lp.width = mRootBounds.width(); lp.height = mRootBounds.height(); lp.token = new Binder(); lp.setTitle(WINDOW_NAME); lp.privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION | PRIVATE_FLAG_TRUSTED_OVERLAY; // TODO(b/189839391): Set INPUT_FEATURE_NO_INPUT_CHANNEL after WM supports // TRUSTED_OVERLAY for windowless window without input channel. mViewHost.setView(rootView, lp); mViewHost.setView(rootLayout, lp); mLeash = getSurfaceControl(mViewHost.getWindowToken()); t.setLayer(mLeash, Integer.MAX_VALUE); drawOutline(); } void release() { if (mViewHost != null) { mViewHost.release(); mViewHost = null; } mRootBounds.setEmpty(); mLastOutlineBounds.setEmpty(); mOutlineView = null; mHostLeash = null; mLeash = null; } @Nullable SurfaceControl getOutlineLeash() { return mLeash; } void setVisibility(boolean visible) { if (mOutlineView != null) { mOutlineView.setVisibility(visible ? View.VISIBLE : View.INVISIBLE); } } void setRootBounds(Rect rootBounds) { if (mViewHost == null || mViewHost.getView() == null) { return; } if (!mRootBounds.equals(rootBounds)) { WindowManager.LayoutParams lp = (WindowManager.LayoutParams) mViewHost.getView().getLayoutParams(); lp.width = rootBounds.width(); lp.height = rootBounds.height(); mViewHost.relayout(lp); mRootBounds.set(rootBounds); drawOutline(); } } void onInsetsChanged(InsetsState insetsState) { if (!mInsetsState.equals(insetsState)) { mInsetsState.set(insetsState); drawOutline(); } } private void computeOutlineBounds(Rect rootBounds, InsetsState insetsState, Rect outBounds) { outBounds.set(rootBounds); final InsetsSource taskBarInsetsSource = insetsState.getSource(InsetsState.ITYPE_EXTRA_NAVIGATION_BAR); // Only insets the divider bar with task bar when it's expanded so that the rounded corners // will be drawn against task bar. if (taskBarInsetsSource.getFrame().height() >= mExpandedTaskBarHeight) { outBounds.inset(taskBarInsetsSource.calculateVisibleInsets(outBounds)); } private static void computeOutlineBounds(Context context, Rect rootBounds, Rect outBounds) { computeDisplayStableBounds(context, outBounds); outBounds.intersect(rootBounds); // Offset the coordinate from screen based to surface based. outBounds.offset(-rootBounds.left, -rootBounds.top); } private static void computeDisplayStableBounds(Context context, Rect outBounds) { final WindowMetrics windowMetrics = context.getSystemService(WindowManager.class).getMaximumWindowMetrics(); outBounds.set(windowMetrics.getBounds()); outBounds.inset(windowMetrics.getWindowInsets().getInsets( WindowInsets.Type.systemBars() | WindowInsets.Type.displayCutout())); void drawOutline() { if (mOutlineView == null) { return; } computeOutlineBounds(mRootBounds, mInsetsState, mTempRect); if (mTempRect.equals(mLastOutlineBounds)) { return; } ViewGroup.MarginLayoutParams lp = (ViewGroup.MarginLayoutParams) mOutlineView.getLayoutParams(); lp.leftMargin = mTempRect.left; lp.topMargin = mTempRect.top; lp.width = mTempRect.width(); lp.height = mTempRect.height(); mOutlineView.setLayoutParams(lp); mLastOutlineBounds.set(mTempRect); } } libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/OutlineRoot.javadeleted 100644 → 0 +0 −62 Original line number Diff line number Diff line /* * Copyright (C) 2021 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.wm.shell.splitscreen; import android.content.Context; import android.graphics.Rect; import android.util.AttributeSet; import android.widget.FrameLayout; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.android.wm.shell.R; /** Root layout for holding split outline. */ public class OutlineRoot extends FrameLayout { public OutlineRoot(@NonNull Context context) { super(context); } public OutlineRoot(@NonNull Context context, @Nullable AttributeSet attrs) { super(context, attrs); } public OutlineRoot(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } public OutlineRoot(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); } private OutlineView mOutlineView; @Override protected void onFinishInflate() { super.onFinishInflate(); mOutlineView = findViewById(R.id.split_outline); } void updateOutlineBounds(Rect bounds, int color) { mOutlineView.updateOutlineBounds(bounds, color); } } libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/OutlineView.java +33 −27 Original line number Diff line number Diff line Loading @@ -16,13 +16,17 @@ package com.android.wm.shell.splitscreen; import static android.view.RoundedCorner.POSITION_BOTTOM_LEFT; import static android.view.RoundedCorner.POSITION_BOTTOM_RIGHT; import static android.view.RoundedCorner.POSITION_TOP_LEFT; import static android.view.RoundedCorner.POSITION_TOP_RIGHT; import android.content.Context; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Path; import android.graphics.Rect; import android.graphics.Region; import android.util.AttributeSet; import android.view.RoundedCorner; import android.view.View; import androidx.annotation.NonNull; Loading @@ -33,44 +37,46 @@ import com.android.internal.R; /** View for drawing split outline. */ public class OutlineView extends View { private final Paint mPaint = new Paint(); private final Rect mBounds = new Rect(); public OutlineView(@NonNull Context context) { super(context); } private final Path mPath = new Path(); private final float[] mRadii = new float[8]; public OutlineView(@NonNull Context context, @Nullable AttributeSet attrs) { public OutlineView(@NonNull Context context, @Nullable AttributeSet attrs) { super(context, attrs); mPaint.setStyle(Paint.Style.STROKE); mPaint.setStrokeWidth( getResources().getDimension(R.dimen.accessibility_focus_highlight_stroke_width)); mPaint.setColor(getResources().getColor(R.color.system_accent1_100, null)); } public OutlineView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); @Override protected void onAttachedToWindow() { // TODO(b/200850654): match the screen corners with the actual display decor. mRadii[0] = mRadii[1] = getCornerRadius(POSITION_TOP_LEFT); mRadii[2] = mRadii[3] = getCornerRadius(POSITION_TOP_RIGHT); mRadii[4] = mRadii[5] = getCornerRadius(POSITION_BOTTOM_RIGHT); mRadii[6] = mRadii[7] = getCornerRadius(POSITION_BOTTOM_LEFT); } public OutlineView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); private int getCornerRadius(@RoundedCorner.Position int position) { final RoundedCorner roundedCorner = getDisplay().getRoundedCorner(position); return roundedCorner == null ? 0 : roundedCorner.getRadius(); } @Override protected void onFinishInflate() { super.onFinishInflate(); mPaint.setStyle(Paint.Style.STROKE); mPaint.setStrokeWidth(getResources() .getDimension(R.dimen.accessibility_focus_highlight_stroke_width)); protected void onLayout(boolean changed, int left, int top, int right, int bottom) { if (changed) { mPath.reset(); mPath.addRoundRect(0, 0, getWidth(), getHeight(), mRadii, Path.Direction.CW); } void updateOutlineBounds(Rect bounds, int color) { if (mBounds.equals(bounds) && mPaint.getColor() == color) return; mBounds.set(bounds); mPaint.setColor(color); } @Override protected void onDraw(Canvas canvas) { if (mBounds.isEmpty()) return; final Path path = new Region(mBounds).getBoundaryPath(); canvas.drawPath(path, mPaint); canvas.drawPath(mPath, mPaint); } @Override public boolean hasOverlappingRendering() { return false; } } libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java +53 −20 Original line number Diff line number Diff line Loading @@ -17,15 +17,19 @@ package com.android.wm.shell.splitscreen; import android.annotation.CallSuper; import android.annotation.Nullable; import android.app.ActivityManager; import android.content.Context; import android.graphics.Color; import android.graphics.Rect; import android.view.InsetsSourceControl; import android.view.InsetsState; import android.view.SurfaceControl; import android.view.SurfaceSession; import android.window.WindowContainerToken; import android.window.WindowContainerTransaction; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.common.DisplayInsetsController; import com.android.wm.shell.common.SyncTransactionQueue; /** Loading @@ -34,7 +38,8 @@ import com.android.wm.shell.common.SyncTransactionQueue; * * @see StageCoordinator */ class SideStage extends StageTaskListener { class SideStage extends StageTaskListener implements DisplayInsetsController.OnInsetsChangedListener { private static final String TAG = SideStage.class.getSimpleName(); private final Context mContext; private OutlineManager mOutlineManager; Loading Loading @@ -77,33 +82,61 @@ class SideStage extends StageTaskListener { return true; } @Nullable public SurfaceControl getOutlineLeash() { return mOutlineManager.getOutlineLeash(); } @Override @CallSuper public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) { super.onTaskAppeared(taskInfo, leash); if (isRootTask(taskInfo)) { mOutlineManager = new OutlineManager(mContext, taskInfo.configuration); enableOutline(true); } } @Override @CallSuper public void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo) { super.onTaskInfoChanged(taskInfo); if (isRootTask(taskInfo)) { mOutlineManager.setRootBounds(taskInfo.configuration.windowConfiguration.getBounds()); } } private boolean isRootTask(ActivityManager.RunningTaskInfo taskInfo) { return mRootTaskInfo != null && mRootTaskInfo.taskId == taskInfo.taskId; } void enableOutline(boolean enable) { if (mOutlineManager == null) { return; } if (enable) { if (mOutlineManager == null && mRootTaskInfo != null) { mOutlineManager = new OutlineManager(mContext, mRootTaskInfo.configuration); mSyncQueue.runInSync(t -> mOutlineManager.inflate(t, mRootLeash, Color.YELLOW)); updateOutlineBounds(); if (mRootTaskInfo != null) { mOutlineManager.inflate(mRootLeash, mRootTaskInfo.configuration.windowConfiguration.getBounds()); } } else { if (mOutlineManager != null) { mOutlineManager.release(); mOutlineManager = null; } } } private void updateOutlineBounds() { if (mOutlineManager == null || mRootTaskInfo == null || !mRootTaskInfo.isVisible) return; mOutlineManager.drawOutlineBounds( mRootTaskInfo.configuration.windowConfiguration.getBounds()); void setOutlineVisibility(boolean visible) { mOutlineManager.setVisibility(visible); } @Override @CallSuper public void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo) { super.onTaskInfoChanged(taskInfo); if (mRootTaskInfo != null && mRootTaskInfo.taskId == taskInfo.taskId) { updateOutlineBounds(); public void insetsChanged(InsetsState insetsState) { mOutlineManager.onInsetsChanged(insetsState); } @Override public void insetsControlChanged(InsetsState insetsState, InsetsSourceControl[] activeControls) { insetsChanged(insetsState); } } Loading
libs/WindowManager/Shell/res/layout/split_outline.xml +2 −2 Original line number Diff line number Diff line Loading @@ -13,7 +13,7 @@ See the License for the specific language governing permissions and limitations under the License. --> <com.android.wm.shell.splitscreen.OutlineRoot <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> Loading @@ -23,4 +23,4 @@ android:layout_height="match_parent" android:layout_width="match_parent" /> </com.android.wm.shell.splitscreen.OutlineRoot> </FrameLayout>
libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/OutlineManager.java +93 −39 Original line number Diff line number Diff line Loading @@ -22,19 +22,23 @@ import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMA import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; import android.annotation.Nullable; import android.content.Context; import android.content.res.Configuration; import android.graphics.PixelFormat; import android.graphics.Rect; import android.os.Binder; import android.view.IWindow; import android.view.InsetsSource; import android.view.InsetsState; import android.view.LayoutInflater; import android.view.SurfaceControl; import android.view.SurfaceControlViewHost; import android.view.WindowInsets; import android.view.View; import android.view.ViewGroup; import android.view.WindowManager; import android.view.WindowMetrics; import android.view.WindowlessWindowManager; import android.widget.FrameLayout; import com.android.wm.shell.R; Loading @@ -45,17 +49,22 @@ import com.android.wm.shell.R; class OutlineManager extends WindowlessWindowManager { private static final String WINDOW_NAME = "SplitOutlineLayer"; private final Context mContext; private final Rect mOutlineBounds = new Rect(); private final Rect mTmpBounds = new Rect(); private final Rect mRootBounds = new Rect(); private final Rect mTempRect = new Rect(); private final Rect mLastOutlineBounds = new Rect(); private final InsetsState mInsetsState = new InsetsState(); private final int mExpandedTaskBarHeight; private OutlineView mOutlineView; private SurfaceControlViewHost mViewHost; private SurfaceControl mHostLeash; private SurfaceControl mLeash; private int mOutlineColor; OutlineManager(Context context, Configuration configuration) { super(configuration, null /* rootSurface */, null /* hostInputToken */); mContext = context.createWindowContext(context.getDisplay(), TYPE_APPLICATION_OVERLAY, null /* options */); mExpandedTaskBarHeight = mContext.getResources().getDimensionPixelSize( com.android.internal.R.dimen.taskbar_frame_height); } @Override Loading @@ -63,65 +72,110 @@ class OutlineManager extends WindowlessWindowManager { b.setParent(mHostLeash); } boolean drawOutlineBounds(Rect rootBounds) { if (mLeash == null || mViewHost == null) return false; computeOutlineBounds(mContext, rootBounds, mTmpBounds); if (mOutlineBounds.equals(mTmpBounds)) { return false; } mOutlineBounds.set(mTmpBounds); ((OutlineRoot) mViewHost.getView()).updateOutlineBounds(mOutlineBounds, mOutlineColor); final WindowManager.LayoutParams lp = (WindowManager.LayoutParams) mViewHost.getView().getLayoutParams(); lp.width = rootBounds.width(); lp.height = rootBounds.height(); mViewHost.relayout(lp); return true; } void inflate(SurfaceControl.Transaction t, SurfaceControl hostLeash, int color) { void inflate(SurfaceControl rootLeash, Rect rootBounds) { if (mLeash != null || mViewHost != null) return; mHostLeash = hostLeash; mOutlineColor = color; mHostLeash = rootLeash; mRootBounds.set(rootBounds); mViewHost = new SurfaceControlViewHost(mContext, mContext.getDisplay(), this); final OutlineRoot rootView = (OutlineRoot) LayoutInflater.from(mContext) final FrameLayout rootLayout = (FrameLayout) LayoutInflater.from(mContext) .inflate(R.layout.split_outline, null); mOutlineView = rootLayout.findViewById(R.id.split_outline); final WindowManager.LayoutParams lp = new WindowManager.LayoutParams( 0 /* width */, 0 /* height */, TYPE_APPLICATION_OVERLAY, FLAG_NOT_FOCUSABLE | FLAG_NOT_TOUCHABLE, PixelFormat.TRANSLUCENT); lp.width = mRootBounds.width(); lp.height = mRootBounds.height(); lp.token = new Binder(); lp.setTitle(WINDOW_NAME); lp.privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION | PRIVATE_FLAG_TRUSTED_OVERLAY; // TODO(b/189839391): Set INPUT_FEATURE_NO_INPUT_CHANNEL after WM supports // TRUSTED_OVERLAY for windowless window without input channel. mViewHost.setView(rootView, lp); mViewHost.setView(rootLayout, lp); mLeash = getSurfaceControl(mViewHost.getWindowToken()); t.setLayer(mLeash, Integer.MAX_VALUE); drawOutline(); } void release() { if (mViewHost != null) { mViewHost.release(); mViewHost = null; } mRootBounds.setEmpty(); mLastOutlineBounds.setEmpty(); mOutlineView = null; mHostLeash = null; mLeash = null; } @Nullable SurfaceControl getOutlineLeash() { return mLeash; } void setVisibility(boolean visible) { if (mOutlineView != null) { mOutlineView.setVisibility(visible ? View.VISIBLE : View.INVISIBLE); } } void setRootBounds(Rect rootBounds) { if (mViewHost == null || mViewHost.getView() == null) { return; } if (!mRootBounds.equals(rootBounds)) { WindowManager.LayoutParams lp = (WindowManager.LayoutParams) mViewHost.getView().getLayoutParams(); lp.width = rootBounds.width(); lp.height = rootBounds.height(); mViewHost.relayout(lp); mRootBounds.set(rootBounds); drawOutline(); } } void onInsetsChanged(InsetsState insetsState) { if (!mInsetsState.equals(insetsState)) { mInsetsState.set(insetsState); drawOutline(); } } private void computeOutlineBounds(Rect rootBounds, InsetsState insetsState, Rect outBounds) { outBounds.set(rootBounds); final InsetsSource taskBarInsetsSource = insetsState.getSource(InsetsState.ITYPE_EXTRA_NAVIGATION_BAR); // Only insets the divider bar with task bar when it's expanded so that the rounded corners // will be drawn against task bar. if (taskBarInsetsSource.getFrame().height() >= mExpandedTaskBarHeight) { outBounds.inset(taskBarInsetsSource.calculateVisibleInsets(outBounds)); } private static void computeOutlineBounds(Context context, Rect rootBounds, Rect outBounds) { computeDisplayStableBounds(context, outBounds); outBounds.intersect(rootBounds); // Offset the coordinate from screen based to surface based. outBounds.offset(-rootBounds.left, -rootBounds.top); } private static void computeDisplayStableBounds(Context context, Rect outBounds) { final WindowMetrics windowMetrics = context.getSystemService(WindowManager.class).getMaximumWindowMetrics(); outBounds.set(windowMetrics.getBounds()); outBounds.inset(windowMetrics.getWindowInsets().getInsets( WindowInsets.Type.systemBars() | WindowInsets.Type.displayCutout())); void drawOutline() { if (mOutlineView == null) { return; } computeOutlineBounds(mRootBounds, mInsetsState, mTempRect); if (mTempRect.equals(mLastOutlineBounds)) { return; } ViewGroup.MarginLayoutParams lp = (ViewGroup.MarginLayoutParams) mOutlineView.getLayoutParams(); lp.leftMargin = mTempRect.left; lp.topMargin = mTempRect.top; lp.width = mTempRect.width(); lp.height = mTempRect.height(); mOutlineView.setLayoutParams(lp); mLastOutlineBounds.set(mTempRect); } }
libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/OutlineRoot.javadeleted 100644 → 0 +0 −62 Original line number Diff line number Diff line /* * Copyright (C) 2021 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.wm.shell.splitscreen; import android.content.Context; import android.graphics.Rect; import android.util.AttributeSet; import android.widget.FrameLayout; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.android.wm.shell.R; /** Root layout for holding split outline. */ public class OutlineRoot extends FrameLayout { public OutlineRoot(@NonNull Context context) { super(context); } public OutlineRoot(@NonNull Context context, @Nullable AttributeSet attrs) { super(context, attrs); } public OutlineRoot(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } public OutlineRoot(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); } private OutlineView mOutlineView; @Override protected void onFinishInflate() { super.onFinishInflate(); mOutlineView = findViewById(R.id.split_outline); } void updateOutlineBounds(Rect bounds, int color) { mOutlineView.updateOutlineBounds(bounds, color); } }
libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/OutlineView.java +33 −27 Original line number Diff line number Diff line Loading @@ -16,13 +16,17 @@ package com.android.wm.shell.splitscreen; import static android.view.RoundedCorner.POSITION_BOTTOM_LEFT; import static android.view.RoundedCorner.POSITION_BOTTOM_RIGHT; import static android.view.RoundedCorner.POSITION_TOP_LEFT; import static android.view.RoundedCorner.POSITION_TOP_RIGHT; import android.content.Context; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Path; import android.graphics.Rect; import android.graphics.Region; import android.util.AttributeSet; import android.view.RoundedCorner; import android.view.View; import androidx.annotation.NonNull; Loading @@ -33,44 +37,46 @@ import com.android.internal.R; /** View for drawing split outline. */ public class OutlineView extends View { private final Paint mPaint = new Paint(); private final Rect mBounds = new Rect(); public OutlineView(@NonNull Context context) { super(context); } private final Path mPath = new Path(); private final float[] mRadii = new float[8]; public OutlineView(@NonNull Context context, @Nullable AttributeSet attrs) { public OutlineView(@NonNull Context context, @Nullable AttributeSet attrs) { super(context, attrs); mPaint.setStyle(Paint.Style.STROKE); mPaint.setStrokeWidth( getResources().getDimension(R.dimen.accessibility_focus_highlight_stroke_width)); mPaint.setColor(getResources().getColor(R.color.system_accent1_100, null)); } public OutlineView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); @Override protected void onAttachedToWindow() { // TODO(b/200850654): match the screen corners with the actual display decor. mRadii[0] = mRadii[1] = getCornerRadius(POSITION_TOP_LEFT); mRadii[2] = mRadii[3] = getCornerRadius(POSITION_TOP_RIGHT); mRadii[4] = mRadii[5] = getCornerRadius(POSITION_BOTTOM_RIGHT); mRadii[6] = mRadii[7] = getCornerRadius(POSITION_BOTTOM_LEFT); } public OutlineView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); private int getCornerRadius(@RoundedCorner.Position int position) { final RoundedCorner roundedCorner = getDisplay().getRoundedCorner(position); return roundedCorner == null ? 0 : roundedCorner.getRadius(); } @Override protected void onFinishInflate() { super.onFinishInflate(); mPaint.setStyle(Paint.Style.STROKE); mPaint.setStrokeWidth(getResources() .getDimension(R.dimen.accessibility_focus_highlight_stroke_width)); protected void onLayout(boolean changed, int left, int top, int right, int bottom) { if (changed) { mPath.reset(); mPath.addRoundRect(0, 0, getWidth(), getHeight(), mRadii, Path.Direction.CW); } void updateOutlineBounds(Rect bounds, int color) { if (mBounds.equals(bounds) && mPaint.getColor() == color) return; mBounds.set(bounds); mPaint.setColor(color); } @Override protected void onDraw(Canvas canvas) { if (mBounds.isEmpty()) return; final Path path = new Region(mBounds).getBoundaryPath(); canvas.drawPath(path, mPaint); canvas.drawPath(mPath, mPaint); } @Override public boolean hasOverlappingRendering() { return false; } }
libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java +53 −20 Original line number Diff line number Diff line Loading @@ -17,15 +17,19 @@ package com.android.wm.shell.splitscreen; import android.annotation.CallSuper; import android.annotation.Nullable; import android.app.ActivityManager; import android.content.Context; import android.graphics.Color; import android.graphics.Rect; import android.view.InsetsSourceControl; import android.view.InsetsState; import android.view.SurfaceControl; import android.view.SurfaceSession; import android.window.WindowContainerToken; import android.window.WindowContainerTransaction; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.common.DisplayInsetsController; import com.android.wm.shell.common.SyncTransactionQueue; /** Loading @@ -34,7 +38,8 @@ import com.android.wm.shell.common.SyncTransactionQueue; * * @see StageCoordinator */ class SideStage extends StageTaskListener { class SideStage extends StageTaskListener implements DisplayInsetsController.OnInsetsChangedListener { private static final String TAG = SideStage.class.getSimpleName(); private final Context mContext; private OutlineManager mOutlineManager; Loading Loading @@ -77,33 +82,61 @@ class SideStage extends StageTaskListener { return true; } @Nullable public SurfaceControl getOutlineLeash() { return mOutlineManager.getOutlineLeash(); } @Override @CallSuper public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) { super.onTaskAppeared(taskInfo, leash); if (isRootTask(taskInfo)) { mOutlineManager = new OutlineManager(mContext, taskInfo.configuration); enableOutline(true); } } @Override @CallSuper public void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo) { super.onTaskInfoChanged(taskInfo); if (isRootTask(taskInfo)) { mOutlineManager.setRootBounds(taskInfo.configuration.windowConfiguration.getBounds()); } } private boolean isRootTask(ActivityManager.RunningTaskInfo taskInfo) { return mRootTaskInfo != null && mRootTaskInfo.taskId == taskInfo.taskId; } void enableOutline(boolean enable) { if (mOutlineManager == null) { return; } if (enable) { if (mOutlineManager == null && mRootTaskInfo != null) { mOutlineManager = new OutlineManager(mContext, mRootTaskInfo.configuration); mSyncQueue.runInSync(t -> mOutlineManager.inflate(t, mRootLeash, Color.YELLOW)); updateOutlineBounds(); if (mRootTaskInfo != null) { mOutlineManager.inflate(mRootLeash, mRootTaskInfo.configuration.windowConfiguration.getBounds()); } } else { if (mOutlineManager != null) { mOutlineManager.release(); mOutlineManager = null; } } } private void updateOutlineBounds() { if (mOutlineManager == null || mRootTaskInfo == null || !mRootTaskInfo.isVisible) return; mOutlineManager.drawOutlineBounds( mRootTaskInfo.configuration.windowConfiguration.getBounds()); void setOutlineVisibility(boolean visible) { mOutlineManager.setVisibility(visible); } @Override @CallSuper public void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo) { super.onTaskInfoChanged(taskInfo); if (mRootTaskInfo != null && mRootTaskInfo.taskId == taskInfo.taskId) { updateOutlineBounds(); public void insetsChanged(InsetsState insetsState) { mOutlineManager.onInsetsChanged(insetsState); } @Override public void insetsControlChanged(InsetsState insetsState, InsetsSourceControl[] activeControls) { insetsChanged(insetsState); } }