summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Garfield Tan <xutan@google.com> 2021-08-04 14:17:31 -0700
committer Garfield Tan <xutan@google.com> 2022-06-06 18:51:52 -0700
commit2d3382a8b41b6649e4062d2cc41f9c94d8487eca (patch)
tree9933ea83d8e83c3280ca954b9301f4e5c2aebed8
parent3b628d7c69e764592d8145d532ff219531e4d958 (diff)
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
-rw-r--r--libs/WindowManager/Shell/res/color/decor_button_dark_color.xml21
-rw-r--r--libs/WindowManager/Shell/res/color/decor_button_light_color.xml21
-rw-r--r--libs/WindowManager/Shell/res/color/decor_caption_title_color.xml23
-rw-r--r--libs/WindowManager/Shell/res/drawable/decor_caption_title.xml22
-rw-r--r--libs/WindowManager/Shell/res/drawable/decor_close_button_dark.xml32
-rw-r--r--libs/WindowManager/Shell/res/drawable/decor_maximize_button_dark.xml36
-rw-r--r--libs/WindowManager/Shell/res/layout/caption_window_decoration.xml45
-rw-r--r--libs/WindowManager/Shell/res/values/attrs.xml4
-rw-r--r--libs/WindowManager/Shell/res/values/strings.xml8
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/ShellInitImpl.java4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java8
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java25
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java53
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java136
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java143
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskFocusStateConsumer.java21
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecorLinearLayout.java72
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecorViewModel.java49
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java265
-rw-r--r--services/core/java/com/android/server/wm/Task.java42
20 files changed, 954 insertions, 76 deletions
diff --git a/libs/WindowManager/Shell/res/color/decor_button_dark_color.xml b/libs/WindowManager/Shell/res/color/decor_button_dark_color.xml
new file mode 100644
index 000000000000..bf325bd84c00
--- /dev/null
+++ b/libs/WindowManager/Shell/res/color/decor_button_dark_color.xml
@@ -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>
diff --git a/libs/WindowManager/Shell/res/color/decor_button_light_color.xml b/libs/WindowManager/Shell/res/color/decor_button_light_color.xml
new file mode 100644
index 000000000000..2e48bca7786a
--- /dev/null
+++ b/libs/WindowManager/Shell/res/color/decor_button_light_color.xml
@@ -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> \ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/color/decor_caption_title_color.xml b/libs/WindowManager/Shell/res/color/decor_caption_title_color.xml
new file mode 100644
index 000000000000..1ecc13e4da38
--- /dev/null
+++ b/libs/WindowManager/Shell/res/color/decor_caption_title_color.xml
@@ -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>
diff --git a/libs/WindowManager/Shell/res/drawable/decor_caption_title.xml b/libs/WindowManager/Shell/res/drawable/decor_caption_title.xml
new file mode 100644
index 000000000000..8207365a737d
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/decor_caption_title.xml
@@ -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>
diff --git a/libs/WindowManager/Shell/res/drawable/decor_close_button_dark.xml b/libs/WindowManager/Shell/res/drawable/decor_close_button_dark.xml
new file mode 100644
index 000000000000..f2f1a1d55dee
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/decor_close_button_dark.xml
@@ -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>
diff --git a/libs/WindowManager/Shell/res/drawable/decor_maximize_button_dark.xml b/libs/WindowManager/Shell/res/drawable/decor_maximize_button_dark.xml
new file mode 100644
index 000000000000..ab4e29ac97e5
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/decor_maximize_button_dark.xml
@@ -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>
+
+
diff --git a/libs/WindowManager/Shell/res/layout/caption_window_decoration.xml b/libs/WindowManager/Shell/res/layout/caption_window_decoration.xml
new file mode 100644
index 000000000000..a112f1933dd1
--- /dev/null
+++ b/libs/WindowManager/Shell/res/layout/caption_window_decoration.xml
@@ -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>
+
diff --git a/libs/WindowManager/Shell/res/values/attrs.xml b/libs/WindowManager/Shell/res/values/attrs.xml
index 4aaeef8afcb0..2aad4c1c1805 100644
--- a/libs/WindowManager/Shell/res/values/attrs.xml
+++ b/libs/WindowManager/Shell/res/values/attrs.xml
@@ -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>
diff --git a/libs/WindowManager/Shell/res/values/strings.xml b/libs/WindowManager/Shell/res/values/strings.xml
index a24311fb1f21..b2f09895d7d8 100644
--- a/libs/WindowManager/Shell/res/values/strings.xml
+++ b/libs/WindowManager/Shell/res/values/strings.xml
@@ -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>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInitImpl.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInitImpl.java
index f6a3e7fb54d9..bebafb9dd39a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInitImpl.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInitImpl.java
@@ -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,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
index 1d10bbe37438..12bff8d4a360 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
@@ -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,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
index 47c9214bcae9..a4d1ce18f32b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
@@ -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;
@@ -128,15 +130,34 @@ public class WMShellModule {
}
//
+ // Window decoration
+ //
+
+ @WMSingleton
+ @Provides
+ static WindowDecorViewModel<?> provideWindowDecorViewModel(
+ Context context,
+ ShellTaskOrganizer taskOrganizer,
+ DisplayController displayController,
+ SyncTransactionQueue syncQueue) {
+ return new CaptionWindowDecorViewModel(
+ context,
+ taskOrganizer,
+ displayController,
+ syncQueue);
+ }
+
+ //
// Freeform
//
@WMSingleton
@Provides
@DynamicOverride
- static FreeformTaskListener provideFreeformTaskListener(
+ static FreeformTaskListener<?> provideFreeformTaskListener(
+ WindowDecorViewModel<?> windowDecorViewModel,
SyncTransactionQueue syncQueue) {
- return new FreeformTaskListener(syncQueue);
+ return new FreeformTaskListener<>(windowDecorViewModel, syncQueue);
}
//
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java
index fef9be36a35f..fb26cb64f0e7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java
@@ -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
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
new file mode 100644
index 000000000000..d678f031698e
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
@@ -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;
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java
new file mode 100644
index 000000000000..569be23b9f52
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java
@@ -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);
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskFocusStateConsumer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskFocusStateConsumer.java
new file mode 100644
index 000000000000..1c61802bbd5c
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskFocusStateConsumer.java
@@ -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);
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecorLinearLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecorLinearLayout.java
new file mode 100644
index 000000000000..6d8001a2f92b
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecorLinearLayout.java
@@ -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;
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecorViewModel.java
new file mode 100644
index 000000000000..6f9ceff722ac
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecorViewModel.java
@@ -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);
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
new file mode 100644
index 000000000000..00ecdf481343
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
@@ -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);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 7c1488b71bf5..fc1b89348c25 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -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();
@@ -4249,48 +4245,10 @@ class Task extends TaskFragment {
}
/**
- * @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 */);
}