diff options
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 */); } |