Render window decoration in WM shell
There are still several things missing in this CL: 1. Still need to wire up freeform resizing. This is done in a separate CL. 2. Check if a11y works as intended. 3. Wire up fullscreen and window decorations for freeform displays. 4. Immersive mode work. Bug: 165794880 Bug: 165794636 Test: Caption shows up. Change-Id: I7c0e1d55d6c9d03332a58d31ade88aef06a84048
This commit is contained in:
@@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (C) 2022 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.
|
||||
-->
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
<item app:state_task_focused="true" android:color="#FF000000" />
|
||||
<item android:color="#33000000" />
|
||||
</selector>
|
||||
@@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (C) 2022 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.
|
||||
-->
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
<item app:state_task_focused="true" android:color="#FFFFFFFF" />
|
||||
<item android:color="#33FFFFFF" />
|
||||
</selector>
|
||||
@@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (C) 2022 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.
|
||||
-->
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
<!-- Fading the to 85% blackness -->
|
||||
<item app:state_task_focused="true" android:color="#D8D8D8" />
|
||||
<!-- Fading the to 95% blackness -->
|
||||
<item android:color="#F2F2F2" />
|
||||
</selector>
|
||||
@@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (C) 2022 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.
|
||||
-->
|
||||
<shape android:shape="rectangle"
|
||||
android:tintMode="multiply"
|
||||
android:tint="@color/decor_caption_title_color"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<solid android:color="?android:attr/colorPrimary" />
|
||||
</shape>
|
||||
@@ -0,0 +1,32 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (C) 2022 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.
|
||||
-->
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="32.0dp"
|
||||
android:height="32.0dp"
|
||||
android:viewportWidth="32.0"
|
||||
android:viewportHeight="32.0"
|
||||
android:tint="@color/decor_button_dark_color"
|
||||
>
|
||||
<group android:scaleX="0.5"
|
||||
android:scaleY="0.5"
|
||||
android:translateX="8.0"
|
||||
android:translateY="8.0" >
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M6.9,4.0l-2.9,2.9 9.1,9.1 -9.1,9.200001 2.9,2.799999 9.1,-9.1 9.1,9.1 2.9,-2.799999 -9.1,-9.200001 9.1,-9.1 -2.9,-2.9 -9.1,9.2z"/>
|
||||
</group>
|
||||
</vector>
|
||||
@@ -0,0 +1,36 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (C) 2022 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.
|
||||
-->
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="32.0dp"
|
||||
android:height="32.0dp"
|
||||
android:viewportWidth="32.0"
|
||||
android:viewportHeight="32.0"
|
||||
android:tint="@color/decor_button_dark_color">
|
||||
<group android:scaleX="0.5"
|
||||
android:scaleY="0.5"
|
||||
android:translateX="8.0"
|
||||
android:translateY="8.0" >
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M2.0,4.0l0.0,16.0l28.0,0.0L30.0,4.0L2.0,4.0zM26.0,16.0L6.0,16.0L6.0,8.0l20.0,0.0L26.0,16.0z"/>
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M2.0,24.0l28.0,0.0l0.0,4.0l-28.0,0.0z"/>
|
||||
</group>
|
||||
</vector>
|
||||
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (C) 2022 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.
|
||||
-->
|
||||
<com.android.wm.shell.windowdecor.WindowDecorLinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/caption"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="end"
|
||||
android:background="@drawable/decor_caption_title">
|
||||
<Button
|
||||
android:id="@+id/maximize_window"
|
||||
android:layout_width="32dp"
|
||||
android:layout_height="32dp"
|
||||
android:layout_margin="5dp"
|
||||
android:padding="4dp"
|
||||
android:layout_gravity="center_vertical|end"
|
||||
android:contentDescription="@string/maximize_button_text"
|
||||
android:background="@drawable/decor_maximize_button_dark"
|
||||
android:duplicateParentState="true"/>
|
||||
<Button
|
||||
android:id="@+id/close_window"
|
||||
android:layout_width="32dp"
|
||||
android:layout_height="32dp"
|
||||
android:layout_margin="5dp"
|
||||
android:padding="4dp"
|
||||
android:layout_gravity="center_vertical|end"
|
||||
android:contentDescription="@string/close_button_text"
|
||||
android:background="@drawable/decor_close_button_dark"
|
||||
android:duplicateParentState="true"/>
|
||||
</com.android.wm.shell.windowdecor.WindowDecorLinearLayout>
|
||||
|
||||
@@ -19,4 +19,8 @@
|
||||
<attr name="icon" format="reference" />
|
||||
<attr name="text" format="string" />
|
||||
</declare-styleable>
|
||||
|
||||
<declare-styleable name="MessageState">
|
||||
<attr name="state_task_focused" format="boolean"/>
|
||||
</declare-styleable>
|
||||
</resources>
|
||||
|
||||
@@ -186,4 +186,12 @@
|
||||
<!-- Button text for dismissing the letterbox education dialog. [CHAR LIMIT=20] -->
|
||||
<string name="letterbox_education_got_it">Got it</string>
|
||||
|
||||
<!-- Accessibility description of the letterbox education toast expand to dialog button. [CHAR LIMIT=NONE] -->
|
||||
<string name="letterbox_education_expand_button_description">Expand for more information.</string>
|
||||
|
||||
<!-- Freeform window caption strings -->
|
||||
<!-- Accessibility text for the maximize window button [CHAR LIMIT=NONE] -->
|
||||
<string name="maximize_button_text">Maximize</string>
|
||||
<!-- Accessibility text for the close window button [CHAR LIMIT=NONE] -->
|
||||
<string name="close_button_text">Close</string>
|
||||
</resources>
|
||||
|
||||
@@ -57,7 +57,7 @@ public class ShellInitImpl {
|
||||
private final FullscreenTaskListener mFullscreenTaskListener;
|
||||
private final Optional<FullscreenUnfoldController> mFullscreenUnfoldController;
|
||||
private final Optional<UnfoldTransitionHandler> mUnfoldTransitionHandler;
|
||||
private final Optional<FreeformTaskListener> mFreeformTaskListenerOptional;
|
||||
private final Optional<FreeformTaskListener<?>> mFreeformTaskListenerOptional;
|
||||
private final ShellExecutor mMainExecutor;
|
||||
private final Transitions mTransitions;
|
||||
private final StartingWindowController mStartingWindow;
|
||||
@@ -78,7 +78,7 @@ public class ShellInitImpl {
|
||||
FullscreenTaskListener fullscreenTaskListener,
|
||||
Optional<FullscreenUnfoldController> fullscreenUnfoldTransitionController,
|
||||
Optional<UnfoldTransitionHandler> unfoldTransitionHandler,
|
||||
Optional<FreeformTaskListener> freeformTaskListenerOptional,
|
||||
Optional<FreeformTaskListener<?>> freeformTaskListenerOptional,
|
||||
Optional<RecentTasksController> recentTasks,
|
||||
Transitions transitions,
|
||||
StartingWindowController startingWindow,
|
||||
|
||||
@@ -347,12 +347,12 @@ public abstract class WMShellBaseModule {
|
||||
// Workaround for dynamic overriding with a default implementation, see {@link DynamicOverride}
|
||||
@BindsOptionalOf
|
||||
@DynamicOverride
|
||||
abstract FreeformTaskListener optionalFreeformTaskListener();
|
||||
abstract FreeformTaskListener<?> optionalFreeformTaskListener();
|
||||
|
||||
@WMSingleton
|
||||
@Provides
|
||||
static Optional<FreeformTaskListener> provideFreeformTaskListener(
|
||||
@DynamicOverride Optional<FreeformTaskListener> freeformTaskListener,
|
||||
static Optional<FreeformTaskListener<?>> provideFreeformTaskListener(
|
||||
@DynamicOverride Optional<FreeformTaskListener<?>> freeformTaskListener,
|
||||
Context context) {
|
||||
if (FreeformTaskListener.isFreeformEnabled(context)) {
|
||||
return freeformTaskListener;
|
||||
@@ -642,7 +642,7 @@ public abstract class WMShellBaseModule {
|
||||
FullscreenTaskListener fullscreenTaskListener,
|
||||
Optional<FullscreenUnfoldController> appUnfoldTransitionController,
|
||||
Optional<UnfoldTransitionHandler> unfoldTransitionHandler,
|
||||
Optional<FreeformTaskListener> freeformTaskListener,
|
||||
Optional<FreeformTaskListener<?>> freeformTaskListener,
|
||||
Optional<RecentTasksController> recentTasksOptional,
|
||||
Transitions transitions,
|
||||
StartingWindowController startingWindow,
|
||||
|
||||
@@ -74,6 +74,8 @@ import com.android.wm.shell.unfold.ShellUnfoldProgressProvider;
|
||||
import com.android.wm.shell.unfold.UnfoldBackgroundController;
|
||||
import com.android.wm.shell.unfold.UnfoldTransitionHandler;
|
||||
import com.android.wm.shell.unfold.animation.FullscreenUnfoldTaskAnimator;
|
||||
import com.android.wm.shell.windowdecor.CaptionWindowDecorViewModel;
|
||||
import com.android.wm.shell.windowdecor.WindowDecorViewModel;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
@@ -127,6 +129,24 @@ public class WMShellModule {
|
||||
taskViewTransitions, syncQueue);
|
||||
}
|
||||
|
||||
//
|
||||
// Window decoration
|
||||
//
|
||||
|
||||
@WMSingleton
|
||||
@Provides
|
||||
static WindowDecorViewModel<?> provideWindowDecorViewModel(
|
||||
Context context,
|
||||
ShellTaskOrganizer taskOrganizer,
|
||||
DisplayController displayController,
|
||||
SyncTransactionQueue syncQueue) {
|
||||
return new CaptionWindowDecorViewModel(
|
||||
context,
|
||||
taskOrganizer,
|
||||
displayController,
|
||||
syncQueue);
|
||||
}
|
||||
|
||||
//
|
||||
// Freeform
|
||||
//
|
||||
@@ -134,9 +154,10 @@ public class WMShellModule {
|
||||
@WMSingleton
|
||||
@Provides
|
||||
@DynamicOverride
|
||||
static FreeformTaskListener provideFreeformTaskListener(
|
||||
static FreeformTaskListener<?> provideFreeformTaskListener(
|
||||
WindowDecorViewModel<?> windowDecorViewModel,
|
||||
SyncTransactionQueue syncQueue) {
|
||||
return new FreeformTaskListener(syncQueue);
|
||||
return new FreeformTaskListener<>(windowDecorViewModel, syncQueue);
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
@@ -21,8 +21,6 @@ import static android.provider.Settings.Global.DEVELOPMENT_ENABLE_FREEFORM_WINDO
|
||||
|
||||
import android.app.ActivityManager.RunningTaskInfo;
|
||||
import android.content.Context;
|
||||
import android.graphics.Point;
|
||||
import android.graphics.Rect;
|
||||
import android.provider.Settings;
|
||||
import android.util.Slog;
|
||||
import android.util.SparseArray;
|
||||
@@ -32,26 +30,35 @@ import com.android.internal.protolog.common.ProtoLog;
|
||||
import com.android.wm.shell.ShellTaskOrganizer;
|
||||
import com.android.wm.shell.common.SyncTransactionQueue;
|
||||
import com.android.wm.shell.protolog.ShellProtoLogGroup;
|
||||
import com.android.wm.shell.windowdecor.WindowDecorViewModel;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
|
||||
/**
|
||||
* {@link ShellTaskOrganizer.TaskListener} for {@link
|
||||
* ShellTaskOrganizer#TASK_LISTENER_TYPE_FREEFORM}.
|
||||
*
|
||||
* @param <T> the type of window decoration instance
|
||||
*/
|
||||
public class FreeformTaskListener implements ShellTaskOrganizer.TaskListener {
|
||||
public class FreeformTaskListener<T extends AutoCloseable>
|
||||
implements ShellTaskOrganizer.TaskListener {
|
||||
private static final String TAG = "FreeformTaskListener";
|
||||
|
||||
private final WindowDecorViewModel<T> mWindowDecorationViewModel;
|
||||
private final SyncTransactionQueue mSyncQueue;
|
||||
|
||||
private final SparseArray<State> mTasks = new SparseArray<>();
|
||||
private final SparseArray<State<T>> mTasks = new SparseArray<>();
|
||||
|
||||
private static class State {
|
||||
private static class State<T extends AutoCloseable> {
|
||||
RunningTaskInfo mTaskInfo;
|
||||
SurfaceControl mLeash;
|
||||
T mWindowDecoration;
|
||||
}
|
||||
|
||||
public FreeformTaskListener(SyncTransactionQueue syncQueue) {
|
||||
public FreeformTaskListener(
|
||||
WindowDecorViewModel<T> windowDecorationViewModel,
|
||||
SyncTransactionQueue syncQueue) {
|
||||
mWindowDecorationViewModel = windowDecorationViewModel;
|
||||
mSyncQueue = syncQueue;
|
||||
}
|
||||
|
||||
@@ -62,23 +69,17 @@ public class FreeformTaskListener implements ShellTaskOrganizer.TaskListener {
|
||||
}
|
||||
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Freeform Task Appeared: #%d",
|
||||
taskInfo.taskId);
|
||||
final State state = new State();
|
||||
final State<T> state = new State<>();
|
||||
state.mTaskInfo = taskInfo;
|
||||
state.mLeash = leash;
|
||||
state.mWindowDecoration =
|
||||
mWindowDecorationViewModel.createWindowDecoration(taskInfo, leash);
|
||||
mTasks.put(taskInfo.taskId, state);
|
||||
|
||||
final Rect taskBounds = taskInfo.configuration.windowConfiguration.getBounds();
|
||||
mSyncQueue.runInSync(t -> {
|
||||
Point taskPosition = taskInfo.positionInParent;
|
||||
t.setPosition(leash, taskPosition.x, taskPosition.y)
|
||||
.setWindowCrop(leash, taskBounds.width(), taskBounds.height())
|
||||
.show(leash);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTaskVanished(RunningTaskInfo taskInfo) {
|
||||
State state = mTasks.get(taskInfo.taskId);
|
||||
State<T> state = mTasks.get(taskInfo.taskId);
|
||||
if (state == null) {
|
||||
Slog.e(TAG, "Task already vanished: #" + taskInfo.taskId);
|
||||
return;
|
||||
@@ -86,11 +87,17 @@ public class FreeformTaskListener implements ShellTaskOrganizer.TaskListener {
|
||||
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Freeform Task Vanished: #%d",
|
||||
taskInfo.taskId);
|
||||
mTasks.remove(taskInfo.taskId);
|
||||
|
||||
try {
|
||||
state.mWindowDecoration.close();
|
||||
} catch (Exception e) {
|
||||
Slog.e(TAG, "Failed to release window decoration.", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTaskInfoChanged(RunningTaskInfo taskInfo) {
|
||||
State state = mTasks.get(taskInfo.taskId);
|
||||
State<T> state = mTasks.get(taskInfo.taskId);
|
||||
if (state == null) {
|
||||
throw new RuntimeException(
|
||||
"Task info changed before appearing: #" + taskInfo.taskId);
|
||||
@@ -98,15 +105,9 @@ public class FreeformTaskListener implements ShellTaskOrganizer.TaskListener {
|
||||
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Freeform Task Info Changed: #%d",
|
||||
taskInfo.taskId);
|
||||
state.mTaskInfo = taskInfo;
|
||||
|
||||
final Rect taskBounds = taskInfo.configuration.windowConfiguration.getBounds();
|
||||
final SurfaceControl leash = state.mLeash;
|
||||
mSyncQueue.runInSync(t -> {
|
||||
Point taskPosition = taskInfo.positionInParent;
|
||||
t.setPosition(leash, taskPosition.x, taskPosition.y)
|
||||
.setWindowCrop(leash, taskBounds.width(), taskBounds.height())
|
||||
.show(leash);
|
||||
});
|
||||
mWindowDecorationViewModel.onTaskInfoChanged(state.mTaskInfo, state.mWindowDecoration);
|
||||
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Freeform Task Info Changed: #%d",
|
||||
taskInfo.taskId);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,136 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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.windowdecor;
|
||||
|
||||
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
|
||||
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
|
||||
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
|
||||
|
||||
import android.app.ActivityManager;
|
||||
import android.app.ActivityManager.RunningTaskInfo;
|
||||
import android.app.ActivityTaskManager;
|
||||
import android.content.Context;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.SurfaceControl;
|
||||
import android.view.View;
|
||||
import android.window.WindowContainerToken;
|
||||
import android.window.WindowContainerTransaction;
|
||||
|
||||
import com.android.wm.shell.R;
|
||||
import com.android.wm.shell.ShellTaskOrganizer;
|
||||
import com.android.wm.shell.common.DisplayController;
|
||||
import com.android.wm.shell.common.SyncTransactionQueue;
|
||||
|
||||
/**
|
||||
* View model for the window decoration with a caption and shadows. Works with
|
||||
* {@link CaptionWindowDecoration}.
|
||||
*/
|
||||
public class CaptionWindowDecorViewModel implements WindowDecorViewModel<CaptionWindowDecoration> {
|
||||
private final ActivityTaskManager mActivityTaskManager;
|
||||
private final ShellTaskOrganizer mTaskOrganizer;
|
||||
private final Context mContext;
|
||||
private final DisplayController mDisplayController;
|
||||
private final SyncTransactionQueue mSyncQueue;
|
||||
|
||||
public CaptionWindowDecorViewModel(
|
||||
Context context,
|
||||
ShellTaskOrganizer taskOrganizer,
|
||||
DisplayController displayController,
|
||||
SyncTransactionQueue syncQueue) {
|
||||
mContext = context;
|
||||
mActivityTaskManager = mContext.getSystemService(ActivityTaskManager.class);
|
||||
mTaskOrganizer = taskOrganizer;
|
||||
mDisplayController = displayController;
|
||||
mSyncQueue = syncQueue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CaptionWindowDecoration createWindowDecoration(
|
||||
ActivityManager.RunningTaskInfo taskInfo, SurfaceControl taskSurface) {
|
||||
final CaptionWindowDecoration windowDecoration = new CaptionWindowDecoration(
|
||||
mContext,
|
||||
mDisplayController,
|
||||
mTaskOrganizer,
|
||||
taskInfo,
|
||||
taskSurface,
|
||||
mSyncQueue);
|
||||
CaptionTouchEventListener touchEventListener = new CaptionTouchEventListener(taskInfo);
|
||||
windowDecoration.setCaptionListeners(touchEventListener, touchEventListener);
|
||||
onTaskInfoChanged(taskInfo, windowDecoration);
|
||||
return windowDecoration;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTaskInfoChanged(RunningTaskInfo taskInfo, CaptionWindowDecoration decoration) {
|
||||
decoration.relayout(taskInfo);
|
||||
|
||||
int statusBarColor = taskInfo.taskDescription.getStatusBarColor();
|
||||
decoration.setCaptionColor(statusBarColor);
|
||||
}
|
||||
|
||||
private class CaptionTouchEventListener implements
|
||||
View.OnClickListener, View.OnTouchListener {
|
||||
|
||||
private final int mTaskId;
|
||||
private final WindowContainerToken mTaskToken;
|
||||
|
||||
private CaptionTouchEventListener(RunningTaskInfo taskInfo) {
|
||||
mTaskId = taskInfo.taskId;
|
||||
mTaskToken = taskInfo.token;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
int id = v.getId();
|
||||
if (id == R.id.close_window) {
|
||||
mActivityTaskManager.removeTask(mTaskId);
|
||||
} else if (id == R.id.maximize_window) {
|
||||
WindowContainerTransaction wct = new WindowContainerTransaction();
|
||||
RunningTaskInfo taskInfo = mTaskOrganizer.getRunningTaskInfo(mTaskId);
|
||||
int targetWindowingMode = taskInfo.getWindowingMode() != WINDOWING_MODE_FULLSCREEN
|
||||
? WINDOWING_MODE_FULLSCREEN : WINDOWING_MODE_FREEFORM;
|
||||
int displayWindowingMode =
|
||||
taskInfo.configuration.windowConfiguration.getDisplayWindowingMode();
|
||||
wct.setWindowingMode(mTaskToken,
|
||||
targetWindowingMode == displayWindowingMode
|
||||
? WINDOWING_MODE_UNDEFINED : targetWindowingMode);
|
||||
if (targetWindowingMode == WINDOWING_MODE_FULLSCREEN) {
|
||||
wct.setBounds(mTaskToken, null);
|
||||
}
|
||||
mSyncQueue.queue(wct);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onTouch(View v, MotionEvent e) {
|
||||
if (v.getId() != R.id.caption) {
|
||||
return false;
|
||||
}
|
||||
if (e.getAction() != MotionEvent.ACTION_DOWN) {
|
||||
return false;
|
||||
}
|
||||
RunningTaskInfo taskInfo = mTaskOrganizer.getRunningTaskInfo(mTaskId);
|
||||
if (taskInfo.isFocused) {
|
||||
return false;
|
||||
}
|
||||
WindowContainerTransaction wct = new WindowContainerTransaction();
|
||||
wct.reorder(mTaskToken, true /* onTop */);
|
||||
mSyncQueue.queue(wct);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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.windowdecor;
|
||||
|
||||
import android.app.ActivityManager;
|
||||
import android.content.Context;
|
||||
import android.content.res.ColorStateList;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.drawable.GradientDrawable;
|
||||
import android.graphics.drawable.VectorDrawable;
|
||||
import android.view.SurfaceControl;
|
||||
import android.view.View;
|
||||
import android.window.WindowContainerTransaction;
|
||||
|
||||
import com.android.wm.shell.R;
|
||||
import com.android.wm.shell.ShellTaskOrganizer;
|
||||
import com.android.wm.shell.common.DisplayController;
|
||||
import com.android.wm.shell.common.SyncTransactionQueue;
|
||||
|
||||
/**
|
||||
* Defines visuals and behaviors of a window decoration of a caption bar and shadows. It works with
|
||||
* {@link CaptionWindowDecorViewModel}. The caption bar contains maximize and close buttons.
|
||||
*
|
||||
* {@link CaptionWindowDecorViewModel} can change the color of the caption bar based on the foremost
|
||||
* app's request through {@link #setCaptionColor(int)}, in which it changes the foreground color of
|
||||
* caption buttons according to the luminance of the background.
|
||||
*
|
||||
* The shadow's thickness is 20dp when the window is in focus and 5dp when the window isn't.
|
||||
*/
|
||||
public class CaptionWindowDecoration extends WindowDecoration<WindowDecorLinearLayout> {
|
||||
// The thickness of shadows of a window that has focus in DIP.
|
||||
private static final int DECOR_SHADOW_FOCUSED_THICKNESS_IN_DIP = 20;
|
||||
// The thickness of shadows of a window that doesn't have focus in DIP.
|
||||
private static final int DECOR_SHADOW_UNFOCUSED_THICKNESS_IN_DIP = 5;
|
||||
|
||||
private static final int DECOR_CAPTION_HEIGHT_IN_DIP = 42;
|
||||
private static final Rect EMPTY_OUTSET = new Rect();
|
||||
|
||||
private final SyncTransactionQueue mSyncQueue;
|
||||
|
||||
private View.OnClickListener mOnCaptionButtonClickListener;
|
||||
private View.OnTouchListener mOnCaptionTouchListener;
|
||||
|
||||
private final WindowDecoration.RelayoutResult<WindowDecorLinearLayout> mResult =
|
||||
new WindowDecoration.RelayoutResult<>();
|
||||
|
||||
CaptionWindowDecoration(
|
||||
Context context,
|
||||
DisplayController displayController,
|
||||
ShellTaskOrganizer taskOrganizer,
|
||||
ActivityManager.RunningTaskInfo taskInfo,
|
||||
SurfaceControl taskSurface,
|
||||
SyncTransactionQueue syncQueue) {
|
||||
super(context, displayController, taskOrganizer, taskInfo, taskSurface);
|
||||
|
||||
mSyncQueue = syncQueue;
|
||||
}
|
||||
|
||||
void setCaptionListeners(
|
||||
View.OnClickListener onCaptionButtonClickListener,
|
||||
View.OnTouchListener onCaptionTouchListener) {
|
||||
mOnCaptionButtonClickListener = onCaptionButtonClickListener;
|
||||
mOnCaptionTouchListener = onCaptionTouchListener;
|
||||
}
|
||||
|
||||
void relayout(ActivityManager.RunningTaskInfo taskInfo) {
|
||||
final int shadowRadiusDp = taskInfo.isFocused
|
||||
? DECOR_SHADOW_FOCUSED_THICKNESS_IN_DIP : DECOR_SHADOW_UNFOCUSED_THICKNESS_IN_DIP;
|
||||
|
||||
WindowDecorLinearLayout oldRootView = mResult.mRootView;
|
||||
final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
|
||||
final WindowContainerTransaction wct = new WindowContainerTransaction();
|
||||
relayout(taskInfo, R.layout.caption_window_decoration, oldRootView,
|
||||
DECOR_CAPTION_HEIGHT_IN_DIP, EMPTY_OUTSET, shadowRadiusDp, t, wct, mResult);
|
||||
taskInfo = null; // Clear it just in case we use it accidentally
|
||||
|
||||
mSyncQueue.runInSync(transaction -> {
|
||||
transaction.merge(t);
|
||||
t.close();
|
||||
|
||||
mTaskOrganizer.applyTransaction(wct);
|
||||
});
|
||||
|
||||
if (mResult.mRootView == null) {
|
||||
return;
|
||||
}
|
||||
if (oldRootView != mResult.mRootView) {
|
||||
setupRootView();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up listeners when a new root view is created.
|
||||
*/
|
||||
private void setupRootView() {
|
||||
View caption = mResult.mRootView.findViewById(R.id.caption);
|
||||
|
||||
caption.setOnTouchListener(mOnCaptionTouchListener);
|
||||
View maximize = caption.findViewById(R.id.maximize_window);
|
||||
maximize.setOnClickListener(mOnCaptionButtonClickListener);
|
||||
View close = caption.findViewById(R.id.close_window);
|
||||
close.setOnClickListener(mOnCaptionButtonClickListener);
|
||||
}
|
||||
|
||||
void setCaptionColor(int captionColor) {
|
||||
if (mResult.mRootView == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
View caption = mResult.mRootView.findViewById(R.id.caption);
|
||||
GradientDrawable captionDrawable = (GradientDrawable) caption.getBackground();
|
||||
captionDrawable.setColor(captionColor);
|
||||
|
||||
int buttonTintColorRes =
|
||||
Color.valueOf(captionColor).luminance() < 0.5
|
||||
? R.color.decor_button_light_color
|
||||
: R.color.decor_button_dark_color;
|
||||
ColorStateList buttonTintColor =
|
||||
caption.getResources().getColorStateList(buttonTintColorRes, null /* theme */);
|
||||
View maximize = caption.findViewById(R.id.maximize_window);
|
||||
VectorDrawable maximizeBackground = (VectorDrawable) maximize.getBackground();
|
||||
maximizeBackground.setTintList(buttonTintColor);
|
||||
|
||||
View close = caption.findViewById(R.id.close_window);
|
||||
VectorDrawable closeBackground = (VectorDrawable) close.getBackground();
|
||||
closeBackground.setTintList(buttonTintColor);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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.windowdecor;
|
||||
|
||||
interface TaskFocusStateConsumer {
|
||||
void setTaskFocusState(boolean focused);
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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.windowdecor;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.android.wm.shell.R;
|
||||
|
||||
/**
|
||||
* A {@link LinearLayout} that takes an additional task focused drawable state. The new state is
|
||||
* used to select the correct background color for views in the window decoration.
|
||||
*/
|
||||
public class WindowDecorLinearLayout extends LinearLayout implements TaskFocusStateConsumer {
|
||||
private static final int[] TASK_FOCUSED_STATE = { R.attr.state_task_focused };
|
||||
|
||||
private boolean mIsTaskFocused;
|
||||
|
||||
public WindowDecorLinearLayout(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
public WindowDecorLinearLayout(Context context,
|
||||
@Nullable AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
public WindowDecorLinearLayout(Context context, @Nullable AttributeSet attrs,
|
||||
int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
}
|
||||
|
||||
public WindowDecorLinearLayout(Context context, AttributeSet attrs, int defStyleAttr,
|
||||
int defStyleRes) {
|
||||
super(context, attrs, defStyleAttr, defStyleRes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTaskFocusState(boolean focused) {
|
||||
mIsTaskFocused = focused;
|
||||
|
||||
refreshDrawableState();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int[] onCreateDrawableState(int extraSpace) {
|
||||
if (!mIsTaskFocused) {
|
||||
return super.onCreateDrawableState(extraSpace);
|
||||
}
|
||||
|
||||
final int[] states = super.onCreateDrawableState(extraSpace + 1);
|
||||
mergeDrawableStates(states, TASK_FOCUSED_STATE);
|
||||
return states;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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.windowdecor;
|
||||
|
||||
import android.app.ActivityManager;
|
||||
import android.view.SurfaceControl;
|
||||
|
||||
/**
|
||||
* The interface used by some {@link com.android.wm.shell.ShellTaskOrganizer.TaskListener} to help
|
||||
* customize {@link WindowDecoration}. Its implementations are responsible to interpret user's
|
||||
* interactions with UI widgets in window decorations and send corresponding requests to system
|
||||
* servers.
|
||||
*
|
||||
* @param <T> The actual decoration type
|
||||
*/
|
||||
public interface WindowDecorViewModel<T extends AutoCloseable> {
|
||||
|
||||
/**
|
||||
* Creates a window decoration for the given task.
|
||||
*
|
||||
* @param taskInfo the initial task info of the task
|
||||
* @param taskSurface the surface of the task
|
||||
* @return the window decoration object
|
||||
*/
|
||||
T createWindowDecoration(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl taskSurface);
|
||||
|
||||
/**
|
||||
* Notifies a task info update on the given task, with the window decoration created previously
|
||||
* for this task by {@link #createWindowDecoration}.
|
||||
*
|
||||
* @param taskInfo the new task info of the task
|
||||
* @param windowDecoration the window decoration created for the task
|
||||
*/
|
||||
void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo, T windowDecoration);
|
||||
}
|
||||
@@ -0,0 +1,265 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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.windowdecor;
|
||||
|
||||
import android.app.ActivityManager.RunningTaskInfo;
|
||||
import android.content.Context;
|
||||
import android.content.res.Configuration;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.PixelFormat;
|
||||
import android.graphics.Point;
|
||||
import android.graphics.Rect;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.view.Display;
|
||||
import android.view.InsetsState;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.SurfaceControl;
|
||||
import android.view.SurfaceControlViewHost;
|
||||
import android.view.View;
|
||||
import android.view.WindowManager;
|
||||
import android.view.WindowlessWindowManager;
|
||||
import android.window.WindowContainerTransaction;
|
||||
|
||||
import com.android.wm.shell.ShellTaskOrganizer;
|
||||
import com.android.wm.shell.common.DisplayController;
|
||||
|
||||
/**
|
||||
* Manages a container surface and a windowless window to show window decoration. Responsible to
|
||||
* update window decoration window state and layout parameters on task info changes and so that
|
||||
* window decoration is in correct state and bounds.
|
||||
*
|
||||
* The container surface is a child of the task display area in the same display, so that window
|
||||
* decorations can be drawn out of the task bounds and receive input events from out of the task
|
||||
* bounds to support drag resizing.
|
||||
*
|
||||
* The windowless window that hosts window decoration is positioned in front of all activities, to
|
||||
* allow the foreground activity to draw its own background behind window decorations, such as
|
||||
* the window captions.
|
||||
*
|
||||
* @param <T> The type of the root view
|
||||
*/
|
||||
public class WindowDecoration<T extends View & TaskFocusStateConsumer> implements AutoCloseable {
|
||||
private static final int[] CAPTION_INSETS_TYPES = { InsetsState.ITYPE_CAPTION_BAR };
|
||||
|
||||
/**
|
||||
* System-wide context. Only used to create context with overridden configurations.
|
||||
*/
|
||||
final Context mContext;
|
||||
final DisplayController mDisplayController;
|
||||
final ShellTaskOrganizer mTaskOrganizer;
|
||||
|
||||
RunningTaskInfo mTaskInfo;
|
||||
final SurfaceControl mTaskSurface;
|
||||
|
||||
Display mDisplay;
|
||||
Context mDecorWindowContext;
|
||||
SurfaceControl mDecorationContainerSurface;
|
||||
SurfaceControl mTaskBackgroundSurface;
|
||||
|
||||
private CaptionWindowManager mCaptionWindowManager;
|
||||
private SurfaceControlViewHost mViewHost;
|
||||
|
||||
private final Rect mCaptionInsetsRect = new Rect();
|
||||
private final Rect mTaskSurfaceCrop = new Rect();
|
||||
private final float[] mTmpColor = new float[3];
|
||||
|
||||
WindowDecoration(
|
||||
Context context,
|
||||
DisplayController displayController,
|
||||
ShellTaskOrganizer taskOrganizer,
|
||||
RunningTaskInfo taskInfo,
|
||||
SurfaceControl taskSurface) {
|
||||
mContext = context;
|
||||
mDisplayController = displayController;
|
||||
mTaskOrganizer = taskOrganizer;
|
||||
mTaskInfo = taskInfo;
|
||||
mTaskSurface = taskSurface;
|
||||
|
||||
mDisplay = mDisplayController.getDisplay(mTaskInfo.displayId);
|
||||
mDecorWindowContext = mContext.createConfigurationContext(mTaskInfo.getConfiguration());
|
||||
|
||||
// Put caption under task surface because ViewRootImpl sets the destination frame of
|
||||
// windowless window layers and BLASTBufferQueue#update() doesn't support offset.
|
||||
mCaptionWindowManager =
|
||||
new CaptionWindowManager(mTaskInfo.getConfiguration(), mTaskSurface);
|
||||
}
|
||||
|
||||
void relayout(RunningTaskInfo taskInfo, int layoutResId, T rootView, float captionHeightDp,
|
||||
Rect outsetsDp, float shadowRadiusDp, SurfaceControl.Transaction t,
|
||||
WindowContainerTransaction wct, RelayoutResult<T> outResult) {
|
||||
outResult.reset();
|
||||
|
||||
final Configuration oldTaskConfig = mTaskInfo.getConfiguration();
|
||||
if (taskInfo != null) {
|
||||
mTaskInfo = taskInfo;
|
||||
}
|
||||
|
||||
if (!mTaskInfo.isVisible) {
|
||||
close();
|
||||
t.hide(mTaskSurface);
|
||||
return;
|
||||
}
|
||||
|
||||
if (rootView == null && layoutResId == 0) {
|
||||
throw new IllegalArgumentException("layoutResId and rootView can't both be invalid.");
|
||||
}
|
||||
|
||||
outResult.mRootView = rootView;
|
||||
rootView = null; // Clear it just in case we use it accidentally
|
||||
final Configuration taskConfig = mTaskInfo.getConfiguration();
|
||||
if (oldTaskConfig.densityDpi != taskConfig.densityDpi
|
||||
|| mDisplay.getDisplayId() != mTaskInfo.displayId) {
|
||||
close();
|
||||
|
||||
mDisplay = mDisplayController.getDisplay(mTaskInfo.displayId);
|
||||
mDecorWindowContext = mContext.createConfigurationContext(taskConfig);
|
||||
if (layoutResId != 0) {
|
||||
outResult.mRootView =
|
||||
(T) LayoutInflater.from(mDecorWindowContext).inflate(layoutResId, null);
|
||||
}
|
||||
}
|
||||
|
||||
if (outResult.mRootView == null) {
|
||||
outResult.mRootView =
|
||||
(T) LayoutInflater.from(mDecorWindowContext).inflate(layoutResId, null);
|
||||
}
|
||||
|
||||
// DecorationContainerSurface
|
||||
if (mDecorationContainerSurface == null) {
|
||||
final SurfaceControl.Builder builder = new SurfaceControl.Builder();
|
||||
mDecorationContainerSurface = builder
|
||||
.setName("Decor container of Task=" + mTaskInfo.taskId)
|
||||
.setContainerLayer()
|
||||
.setParent(mTaskSurface)
|
||||
.build();
|
||||
|
||||
t.setTrustedOverlay(mDecorationContainerSurface, true);
|
||||
}
|
||||
|
||||
final Rect taskBounds = taskConfig.windowConfiguration.getBounds();
|
||||
outResult.mDensity = taskConfig.densityDpi * DisplayMetrics.DENSITY_DEFAULT_SCALE;
|
||||
final int decorContainerOffsetX = -(int) (outsetsDp.left * outResult.mDensity);
|
||||
final int decorContainerOffsetY = -(int) (outsetsDp.top * outResult.mDensity);
|
||||
outResult.mWidth = taskBounds.width()
|
||||
+ (int) (outsetsDp.right * outResult.mDensity)
|
||||
- decorContainerOffsetX;
|
||||
outResult.mHeight = taskBounds.height()
|
||||
+ (int) (outsetsDp.bottom * outResult.mDensity)
|
||||
- decorContainerOffsetY;
|
||||
t.setPosition(mDecorationContainerSurface, decorContainerOffsetX, decorContainerOffsetY)
|
||||
.setWindowCrop(mDecorationContainerSurface, outResult.mWidth, outResult.mHeight)
|
||||
.setLayer(mDecorationContainerSurface, mTaskInfo.numActivities + 1)
|
||||
.show(mDecorationContainerSurface);
|
||||
|
||||
// TaskBackgroundSurface
|
||||
if (mTaskBackgroundSurface == null) {
|
||||
final SurfaceControl.Builder builder = new SurfaceControl.Builder();
|
||||
mTaskBackgroundSurface = builder
|
||||
.setName("Background of Task=" + mTaskInfo.taskId)
|
||||
.setEffectLayer()
|
||||
.setParent(mTaskSurface)
|
||||
.build();
|
||||
}
|
||||
|
||||
float shadowRadius = outResult.mDensity * shadowRadiusDp;
|
||||
int backgroundColorInt = mTaskInfo.taskDescription.getBackgroundColor();
|
||||
mTmpColor[0] = Color.red(backgroundColorInt);
|
||||
mTmpColor[1] = Color.green(backgroundColorInt);
|
||||
mTmpColor[2] = Color.blue(backgroundColorInt);
|
||||
t.setCrop(mTaskBackgroundSurface, taskBounds)
|
||||
.setShadowRadius(mTaskBackgroundSurface, shadowRadius)
|
||||
.setColor(mTaskBackgroundSurface, mTmpColor);
|
||||
|
||||
// Caption view
|
||||
mCaptionWindowManager.setConfiguration(taskConfig);
|
||||
final int captionHeight = (int) Math.ceil(captionHeightDp * outResult.mDensity);
|
||||
final WindowManager.LayoutParams lp =
|
||||
new WindowManager.LayoutParams(taskBounds.width(), captionHeight,
|
||||
WindowManager.LayoutParams.TYPE_APPLICATION,
|
||||
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, PixelFormat.TRANSPARENT);
|
||||
lp.setTitle("Caption of Task=" + mTaskInfo.taskId);
|
||||
lp.setTrustedOverlay();
|
||||
if (mViewHost == null) {
|
||||
mViewHost = new SurfaceControlViewHost(mDecorWindowContext, mDisplay,
|
||||
mCaptionWindowManager, true);
|
||||
mViewHost.setView(outResult.mRootView, lp);
|
||||
} else {
|
||||
mViewHost.relayout(lp);
|
||||
}
|
||||
|
||||
outResult.mRootView.setTaskFocusState(mTaskInfo.isFocused);
|
||||
|
||||
// Caption insets
|
||||
mCaptionInsetsRect.set(taskBounds);
|
||||
mCaptionInsetsRect.bottom = mCaptionInsetsRect.top + captionHeight;
|
||||
wct.addRectInsetsProvider(mTaskInfo.token, mCaptionInsetsRect, CAPTION_INSETS_TYPES);
|
||||
|
||||
// Task surface itself
|
||||
Point taskPosition = mTaskInfo.positionInParent;
|
||||
mTaskSurfaceCrop.set(
|
||||
decorContainerOffsetX,
|
||||
decorContainerOffsetY,
|
||||
outResult.mWidth + decorContainerOffsetX,
|
||||
outResult.mHeight + decorContainerOffsetY);
|
||||
t.setPosition(mTaskSurface, taskPosition.x, taskPosition.y)
|
||||
.setCrop(mTaskSurface, mTaskSurfaceCrop)
|
||||
.show(mTaskSurface);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
if (mViewHost != null) {
|
||||
mViewHost.release();
|
||||
mViewHost = null;
|
||||
}
|
||||
|
||||
if (mDecorationContainerSurface != null) {
|
||||
mDecorationContainerSurface.release();
|
||||
mDecorationContainerSurface = null;
|
||||
}
|
||||
|
||||
if (mTaskBackgroundSurface != null) {
|
||||
mTaskBackgroundSurface.release();
|
||||
mTaskBackgroundSurface = null;
|
||||
}
|
||||
}
|
||||
|
||||
static class RelayoutResult<T extends View & TaskFocusStateConsumer> {
|
||||
int mWidth;
|
||||
int mHeight;
|
||||
float mDensity;
|
||||
T mRootView;
|
||||
|
||||
void reset() {
|
||||
mWidth = 0;
|
||||
mHeight = 0;
|
||||
mDensity = 0;
|
||||
mRootView = null;
|
||||
}
|
||||
}
|
||||
|
||||
private static class CaptionWindowManager extends WindowlessWindowManager {
|
||||
CaptionWindowManager(Configuration config, SurfaceControl rootSurface) {
|
||||
super(config, rootSurface, null /* hostInputToken */);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setConfiguration(Configuration configuration) {
|
||||
super.setConfiguration(configuration);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -69,8 +69,6 @@ import static android.view.WindowManager.TRANSIT_TO_BACK;
|
||||
import static android.view.WindowManager.TRANSIT_TO_FRONT;
|
||||
import static android.window.DisplayAreaOrganizer.FEATURE_UNDEFINED;
|
||||
|
||||
import static com.android.internal.policy.DecorView.DECOR_SHADOW_FOCUSED_HEIGHT_IN_DIP;
|
||||
import static com.android.internal.policy.DecorView.DECOR_SHADOW_UNFOCUSED_HEIGHT_IN_DIP;
|
||||
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ADD_REMOVE;
|
||||
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_BACK_PREVIEW;
|
||||
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_LOCKTASK;
|
||||
@@ -131,7 +129,6 @@ import static com.android.server.wm.WindowContainerChildProto.TASK;
|
||||
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ROOT_TASK;
|
||||
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TASK_MOVEMENT;
|
||||
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
|
||||
import static com.android.server.wm.WindowManagerService.dipToPixel;
|
||||
|
||||
import static java.lang.Integer.MAX_VALUE;
|
||||
|
||||
@@ -3277,7 +3274,6 @@ class Task extends TaskFragment {
|
||||
}
|
||||
|
||||
final SurfaceControl.Transaction t = getSyncTransaction();
|
||||
updateShadowsRadius(isFocused(), t);
|
||||
|
||||
if (mDimmer.updateDims(t, mTmpDimBoundsRect)) {
|
||||
scheduleAnimation();
|
||||
@@ -4248,49 +4244,11 @@ class Task extends TaskFragment {
|
||||
return getActivity(ActivityRecord::isVisible) != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the desired shadow radius in pixels for the current task.
|
||||
*/
|
||||
private float getShadowRadius(boolean taskIsFocused) {
|
||||
int elevation = 0;
|
||||
|
||||
// Get elevation for a specific windowing mode.
|
||||
if (inFreeformWindowingMode()) {
|
||||
elevation = taskIsFocused
|
||||
? DECOR_SHADOW_FOCUSED_HEIGHT_IN_DIP : DECOR_SHADOW_UNFOCUSED_HEIGHT_IN_DIP;
|
||||
} else {
|
||||
// For all other windowing modes, do not draw a shadow.
|
||||
return 0;
|
||||
}
|
||||
|
||||
// If the task has no visible children, do not draw a shadow.
|
||||
if (!hasVisibleChildren()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return dipToPixel(elevation, getDisplayContent().getDisplayMetrics());
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the length of the shadow if needed based on windowing mode and task focus state.
|
||||
*/
|
||||
private void updateShadowsRadius(boolean taskIsFocused,
|
||||
SurfaceControl.Transaction pendingTransaction) {
|
||||
if (!isRootTask()) return;
|
||||
|
||||
final float newShadowRadius = getShadowRadius(taskIsFocused);
|
||||
if (mShadowRadius != newShadowRadius) {
|
||||
mShadowRadius = newShadowRadius;
|
||||
pendingTransaction.setShadowRadius(getSurfaceControl(), mShadowRadius);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called on the task when it gained or lost focus.
|
||||
* @param hasFocus
|
||||
*/
|
||||
void onAppFocusChanged(boolean hasFocus) {
|
||||
updateShadowsRadius(hasFocus, getSyncTransaction());
|
||||
dispatchTaskInfoChangedIfNeeded(false /* force */);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user