Merge changes from topic "caption_in_wm_shell" into tm-qpr-dev am: 20f893dd1d
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/18699935 Change-Id: I1e8c2290fa897121f33fe8b9af7e693294c8fd24 Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
@@ -0,0 +1,21 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ Copyright (C) 2022 The Android Open Source Project
|
||||||
|
~
|
||||||
|
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
~ you may not use this file except in compliance with the License.
|
||||||
|
~ You may obtain a copy of the License at
|
||||||
|
~
|
||||||
|
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
~
|
||||||
|
~ Unless required by applicable law or agreed to in writing, software
|
||||||
|
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
~ See the License for the specific language governing permissions and
|
||||||
|
~ limitations under the License.
|
||||||
|
-->
|
||||||
|
<selector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
|
<item app:state_task_focused="true" android:color="#FF000000" />
|
||||||
|
<item android:color="#33000000" />
|
||||||
|
</selector>
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ Copyright (C) 2022 The Android Open Source Project
|
||||||
|
~
|
||||||
|
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
~ you may not use this file except in compliance with the License.
|
||||||
|
~ You may obtain a copy of the License at
|
||||||
|
~
|
||||||
|
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
~
|
||||||
|
~ Unless required by applicable law or agreed to in writing, software
|
||||||
|
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
~ See the License for the specific language governing permissions and
|
||||||
|
~ limitations under the License.
|
||||||
|
-->
|
||||||
|
<selector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
|
<item app:state_task_focused="true" android:color="#FFFFFFFF" />
|
||||||
|
<item android:color="#33FFFFFF" />
|
||||||
|
</selector>
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ Copyright (C) 2022 The Android Open Source Project
|
||||||
|
~
|
||||||
|
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
~ you may not use this file except in compliance with the License.
|
||||||
|
~ You may obtain a copy of the License at
|
||||||
|
~
|
||||||
|
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
~
|
||||||
|
~ Unless required by applicable law or agreed to in writing, software
|
||||||
|
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
~ See the License for the specific language governing permissions and
|
||||||
|
~ limitations under the License.
|
||||||
|
-->
|
||||||
|
<selector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
|
<!-- Fading the to 85% blackness -->
|
||||||
|
<item app:state_task_focused="true" android:color="#D8D8D8" />
|
||||||
|
<!-- Fading the to 95% blackness -->
|
||||||
|
<item android:color="#F2F2F2" />
|
||||||
|
</selector>
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ Copyright (C) 2022 The Android Open Source Project
|
||||||
|
~
|
||||||
|
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
~ you may not use this file except in compliance with the License.
|
||||||
|
~ You may obtain a copy of the License at
|
||||||
|
~
|
||||||
|
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
~
|
||||||
|
~ Unless required by applicable law or agreed to in writing, software
|
||||||
|
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
~ See the License for the specific language governing permissions and
|
||||||
|
~ limitations under the License.
|
||||||
|
-->
|
||||||
|
<shape android:shape="rectangle"
|
||||||
|
android:tintMode="multiply"
|
||||||
|
android:tint="@color/decor_caption_title_color"
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<solid android:color="?android:attr/colorPrimary" />
|
||||||
|
</shape>
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ Copyright (C) 2022 The Android Open Source Project
|
||||||
|
~
|
||||||
|
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
~ you may not use this file except in compliance with the License.
|
||||||
|
~ You may obtain a copy of the License at
|
||||||
|
~
|
||||||
|
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
~
|
||||||
|
~ Unless required by applicable law or agreed to in writing, software
|
||||||
|
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
~ See the License for the specific language governing permissions and
|
||||||
|
~ limitations under the License.
|
||||||
|
-->
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="32.0dp"
|
||||||
|
android:height="32.0dp"
|
||||||
|
android:viewportWidth="32.0"
|
||||||
|
android:viewportHeight="32.0"
|
||||||
|
android:tint="@color/decor_button_dark_color"
|
||||||
|
>
|
||||||
|
<group android:scaleX="0.5"
|
||||||
|
android:scaleY="0.5"
|
||||||
|
android:translateX="8.0"
|
||||||
|
android:translateY="8.0" >
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M6.9,4.0l-2.9,2.9 9.1,9.1 -9.1,9.200001 2.9,2.799999 9.1,-9.1 9.1,9.1 2.9,-2.799999 -9.1,-9.200001 9.1,-9.1 -2.9,-2.9 -9.1,9.2z"/>
|
||||||
|
</group>
|
||||||
|
</vector>
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ Copyright (C) 2022 The Android Open Source Project
|
||||||
|
~
|
||||||
|
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
~ you may not use this file except in compliance with the License.
|
||||||
|
~ You may obtain a copy of the License at
|
||||||
|
~
|
||||||
|
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
~
|
||||||
|
~ Unless required by applicable law or agreed to in writing, software
|
||||||
|
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
~ See the License for the specific language governing permissions and
|
||||||
|
~ limitations under the License.
|
||||||
|
-->
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="32.0dp"
|
||||||
|
android:height="32.0dp"
|
||||||
|
android:viewportWidth="32.0"
|
||||||
|
android:viewportHeight="32.0"
|
||||||
|
android:tint="@color/decor_button_dark_color">
|
||||||
|
<group android:scaleX="0.5"
|
||||||
|
android:scaleY="0.5"
|
||||||
|
android:translateX="8.0"
|
||||||
|
android:translateY="8.0" >
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M2.0,4.0l0.0,16.0l28.0,0.0L30.0,4.0L2.0,4.0zM26.0,16.0L6.0,16.0L6.0,8.0l20.0,0.0L26.0,16.0z"/>
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M2.0,24.0l28.0,0.0l0.0,4.0l-28.0,0.0z"/>
|
||||||
|
</group>
|
||||||
|
</vector>
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ Copyright (C) 2022 The Android Open Source Project
|
||||||
|
~
|
||||||
|
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
~ you may not use this file except in compliance with the License.
|
||||||
|
~ You may obtain a copy of the License at
|
||||||
|
~
|
||||||
|
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
~
|
||||||
|
~ Unless required by applicable law or agreed to in writing, software
|
||||||
|
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
~ See the License for the specific language governing permissions and
|
||||||
|
~ limitations under the License.
|
||||||
|
-->
|
||||||
|
<com.android.wm.shell.windowdecor.WindowDecorLinearLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:id="@+id/caption"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:gravity="end"
|
||||||
|
android:background="@drawable/decor_caption_title">
|
||||||
|
<Button
|
||||||
|
android:id="@+id/maximize_window"
|
||||||
|
android:layout_width="32dp"
|
||||||
|
android:layout_height="32dp"
|
||||||
|
android:layout_margin="5dp"
|
||||||
|
android:padding="4dp"
|
||||||
|
android:layout_gravity="center_vertical|end"
|
||||||
|
android:contentDescription="@string/maximize_button_text"
|
||||||
|
android:background="@drawable/decor_maximize_button_dark"
|
||||||
|
android:duplicateParentState="true"/>
|
||||||
|
<Button
|
||||||
|
android:id="@+id/close_window"
|
||||||
|
android:layout_width="32dp"
|
||||||
|
android:layout_height="32dp"
|
||||||
|
android:layout_margin="5dp"
|
||||||
|
android:padding="4dp"
|
||||||
|
android:layout_gravity="center_vertical|end"
|
||||||
|
android:contentDescription="@string/close_button_text"
|
||||||
|
android:background="@drawable/decor_close_button_dark"
|
||||||
|
android:duplicateParentState="true"/>
|
||||||
|
</com.android.wm.shell.windowdecor.WindowDecorLinearLayout>
|
||||||
|
|
||||||
@@ -19,4 +19,8 @@
|
|||||||
<attr name="icon" format="reference" />
|
<attr name="icon" format="reference" />
|
||||||
<attr name="text" format="string" />
|
<attr name="text" format="string" />
|
||||||
</declare-styleable>
|
</declare-styleable>
|
||||||
|
|
||||||
|
<declare-styleable name="MessageState">
|
||||||
|
<attr name="state_task_focused" format="boolean"/>
|
||||||
|
</declare-styleable>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -186,4 +186,12 @@
|
|||||||
<!-- Button text for dismissing the letterbox education dialog. [CHAR LIMIT=20] -->
|
<!-- Button text for dismissing the letterbox education dialog. [CHAR LIMIT=20] -->
|
||||||
<string name="letterbox_education_got_it">Got it</string>
|
<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>
|
</resources>
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ public class ShellInitImpl {
|
|||||||
private final FullscreenTaskListener mFullscreenTaskListener;
|
private final FullscreenTaskListener mFullscreenTaskListener;
|
||||||
private final Optional<FullscreenUnfoldController> mFullscreenUnfoldController;
|
private final Optional<FullscreenUnfoldController> mFullscreenUnfoldController;
|
||||||
private final Optional<UnfoldTransitionHandler> mUnfoldTransitionHandler;
|
private final Optional<UnfoldTransitionHandler> mUnfoldTransitionHandler;
|
||||||
private final Optional<FreeformTaskListener> mFreeformTaskListenerOptional;
|
private final Optional<FreeformTaskListener<?>> mFreeformTaskListenerOptional;
|
||||||
private final ShellExecutor mMainExecutor;
|
private final ShellExecutor mMainExecutor;
|
||||||
private final Transitions mTransitions;
|
private final Transitions mTransitions;
|
||||||
private final StartingWindowController mStartingWindow;
|
private final StartingWindowController mStartingWindow;
|
||||||
@@ -78,7 +78,7 @@ public class ShellInitImpl {
|
|||||||
FullscreenTaskListener fullscreenTaskListener,
|
FullscreenTaskListener fullscreenTaskListener,
|
||||||
Optional<FullscreenUnfoldController> fullscreenUnfoldTransitionController,
|
Optional<FullscreenUnfoldController> fullscreenUnfoldTransitionController,
|
||||||
Optional<UnfoldTransitionHandler> unfoldTransitionHandler,
|
Optional<UnfoldTransitionHandler> unfoldTransitionHandler,
|
||||||
Optional<FreeformTaskListener> freeformTaskListenerOptional,
|
Optional<FreeformTaskListener<?>> freeformTaskListenerOptional,
|
||||||
Optional<RecentTasksController> recentTasks,
|
Optional<RecentTasksController> recentTasks,
|
||||||
Transitions transitions,
|
Transitions transitions,
|
||||||
StartingWindowController startingWindow,
|
StartingWindowController startingWindow,
|
||||||
|
|||||||
@@ -347,12 +347,12 @@ public abstract class WMShellBaseModule {
|
|||||||
// Workaround for dynamic overriding with a default implementation, see {@link DynamicOverride}
|
// Workaround for dynamic overriding with a default implementation, see {@link DynamicOverride}
|
||||||
@BindsOptionalOf
|
@BindsOptionalOf
|
||||||
@DynamicOverride
|
@DynamicOverride
|
||||||
abstract FreeformTaskListener optionalFreeformTaskListener();
|
abstract FreeformTaskListener<?> optionalFreeformTaskListener();
|
||||||
|
|
||||||
@WMSingleton
|
@WMSingleton
|
||||||
@Provides
|
@Provides
|
||||||
static Optional<FreeformTaskListener> provideFreeformTaskListener(
|
static Optional<FreeformTaskListener<?>> provideFreeformTaskListener(
|
||||||
@DynamicOverride Optional<FreeformTaskListener> freeformTaskListener,
|
@DynamicOverride Optional<FreeformTaskListener<?>> freeformTaskListener,
|
||||||
Context context) {
|
Context context) {
|
||||||
if (FreeformTaskListener.isFreeformEnabled(context)) {
|
if (FreeformTaskListener.isFreeformEnabled(context)) {
|
||||||
return freeformTaskListener;
|
return freeformTaskListener;
|
||||||
@@ -642,7 +642,7 @@ public abstract class WMShellBaseModule {
|
|||||||
FullscreenTaskListener fullscreenTaskListener,
|
FullscreenTaskListener fullscreenTaskListener,
|
||||||
Optional<FullscreenUnfoldController> appUnfoldTransitionController,
|
Optional<FullscreenUnfoldController> appUnfoldTransitionController,
|
||||||
Optional<UnfoldTransitionHandler> unfoldTransitionHandler,
|
Optional<UnfoldTransitionHandler> unfoldTransitionHandler,
|
||||||
Optional<FreeformTaskListener> freeformTaskListener,
|
Optional<FreeformTaskListener<?>> freeformTaskListener,
|
||||||
Optional<RecentTasksController> recentTasksOptional,
|
Optional<RecentTasksController> recentTasksOptional,
|
||||||
Transitions transitions,
|
Transitions transitions,
|
||||||
StartingWindowController startingWindow,
|
StartingWindowController startingWindow,
|
||||||
|
|||||||
@@ -74,6 +74,8 @@ import com.android.wm.shell.unfold.ShellUnfoldProgressProvider;
|
|||||||
import com.android.wm.shell.unfold.UnfoldBackgroundController;
|
import com.android.wm.shell.unfold.UnfoldBackgroundController;
|
||||||
import com.android.wm.shell.unfold.UnfoldTransitionHandler;
|
import com.android.wm.shell.unfold.UnfoldTransitionHandler;
|
||||||
import com.android.wm.shell.unfold.animation.FullscreenUnfoldTaskAnimator;
|
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;
|
import java.util.Optional;
|
||||||
|
|
||||||
@@ -127,6 +129,26 @@ public class WMShellModule {
|
|||||||
taskViewTransitions, syncQueue);
|
taskViewTransitions, syncQueue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Window decoration
|
||||||
|
//
|
||||||
|
|
||||||
|
@WMSingleton
|
||||||
|
@Provides
|
||||||
|
static WindowDecorViewModel<?> provideWindowDecorViewModel(
|
||||||
|
Context context,
|
||||||
|
@ShellMainThread Handler mainHandler,
|
||||||
|
ShellTaskOrganizer taskOrganizer,
|
||||||
|
DisplayController displayController,
|
||||||
|
SyncTransactionQueue syncQueue) {
|
||||||
|
return new CaptionWindowDecorViewModel(
|
||||||
|
context,
|
||||||
|
mainHandler,
|
||||||
|
taskOrganizer,
|
||||||
|
displayController,
|
||||||
|
syncQueue);
|
||||||
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// Freeform
|
// Freeform
|
||||||
//
|
//
|
||||||
@@ -134,9 +156,10 @@ public class WMShellModule {
|
|||||||
@WMSingleton
|
@WMSingleton
|
||||||
@Provides
|
@Provides
|
||||||
@DynamicOverride
|
@DynamicOverride
|
||||||
static FreeformTaskListener provideFreeformTaskListener(
|
static FreeformTaskListener<?> provideFreeformTaskListener(
|
||||||
|
WindowDecorViewModel<?> windowDecorViewModel,
|
||||||
SyncTransactionQueue syncQueue) {
|
SyncTransactionQueue syncQueue) {
|
||||||
return new FreeformTaskListener(syncQueue);
|
return new FreeformTaskListener<>(windowDecorViewModel, syncQueue);
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -21,8 +21,6 @@ import static android.provider.Settings.Global.DEVELOPMENT_ENABLE_FREEFORM_WINDO
|
|||||||
|
|
||||||
import android.app.ActivityManager.RunningTaskInfo;
|
import android.app.ActivityManager.RunningTaskInfo;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.graphics.Point;
|
|
||||||
import android.graphics.Rect;
|
|
||||||
import android.provider.Settings;
|
import android.provider.Settings;
|
||||||
import android.util.Slog;
|
import android.util.Slog;
|
||||||
import android.util.SparseArray;
|
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.ShellTaskOrganizer;
|
||||||
import com.android.wm.shell.common.SyncTransactionQueue;
|
import com.android.wm.shell.common.SyncTransactionQueue;
|
||||||
import com.android.wm.shell.protolog.ShellProtoLogGroup;
|
import com.android.wm.shell.protolog.ShellProtoLogGroup;
|
||||||
|
import com.android.wm.shell.windowdecor.WindowDecorViewModel;
|
||||||
|
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@link ShellTaskOrganizer.TaskListener} for {@link
|
* {@link ShellTaskOrganizer.TaskListener} for {@link
|
||||||
* ShellTaskOrganizer#TASK_LISTENER_TYPE_FREEFORM}.
|
* 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 static final String TAG = "FreeformTaskListener";
|
||||||
|
|
||||||
|
private final WindowDecorViewModel<T> mWindowDecorationViewModel;
|
||||||
private final SyncTransactionQueue mSyncQueue;
|
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;
|
RunningTaskInfo mTaskInfo;
|
||||||
SurfaceControl mLeash;
|
SurfaceControl mLeash;
|
||||||
|
T mWindowDecoration;
|
||||||
}
|
}
|
||||||
|
|
||||||
public FreeformTaskListener(SyncTransactionQueue syncQueue) {
|
public FreeformTaskListener(
|
||||||
|
WindowDecorViewModel<T> windowDecorationViewModel,
|
||||||
|
SyncTransactionQueue syncQueue) {
|
||||||
|
mWindowDecorationViewModel = windowDecorationViewModel;
|
||||||
mSyncQueue = syncQueue;
|
mSyncQueue = syncQueue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -62,23 +69,17 @@ public class FreeformTaskListener implements ShellTaskOrganizer.TaskListener {
|
|||||||
}
|
}
|
||||||
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Freeform Task Appeared: #%d",
|
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Freeform Task Appeared: #%d",
|
||||||
taskInfo.taskId);
|
taskInfo.taskId);
|
||||||
final State state = new State();
|
final State<T> state = new State<>();
|
||||||
state.mTaskInfo = taskInfo;
|
state.mTaskInfo = taskInfo;
|
||||||
state.mLeash = leash;
|
state.mLeash = leash;
|
||||||
|
state.mWindowDecoration =
|
||||||
|
mWindowDecorationViewModel.createWindowDecoration(taskInfo, leash);
|
||||||
mTasks.put(taskInfo.taskId, state);
|
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
|
@Override
|
||||||
public void onTaskVanished(RunningTaskInfo taskInfo) {
|
public void onTaskVanished(RunningTaskInfo taskInfo) {
|
||||||
State state = mTasks.get(taskInfo.taskId);
|
State<T> state = mTasks.get(taskInfo.taskId);
|
||||||
if (state == null) {
|
if (state == null) {
|
||||||
Slog.e(TAG, "Task already vanished: #" + taskInfo.taskId);
|
Slog.e(TAG, "Task already vanished: #" + taskInfo.taskId);
|
||||||
return;
|
return;
|
||||||
@@ -86,11 +87,17 @@ public class FreeformTaskListener implements ShellTaskOrganizer.TaskListener {
|
|||||||
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Freeform Task Vanished: #%d",
|
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Freeform Task Vanished: #%d",
|
||||||
taskInfo.taskId);
|
taskInfo.taskId);
|
||||||
mTasks.remove(taskInfo.taskId);
|
mTasks.remove(taskInfo.taskId);
|
||||||
|
|
||||||
|
try {
|
||||||
|
state.mWindowDecoration.close();
|
||||||
|
} catch (Exception e) {
|
||||||
|
Slog.e(TAG, "Failed to release window decoration.", e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onTaskInfoChanged(RunningTaskInfo taskInfo) {
|
public void onTaskInfoChanged(RunningTaskInfo taskInfo) {
|
||||||
State state = mTasks.get(taskInfo.taskId);
|
State<T> state = mTasks.get(taskInfo.taskId);
|
||||||
if (state == null) {
|
if (state == null) {
|
||||||
throw new RuntimeException(
|
throw new RuntimeException(
|
||||||
"Task info changed before appearing: #" + taskInfo.taskId);
|
"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",
|
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Freeform Task Info Changed: #%d",
|
||||||
taskInfo.taskId);
|
taskInfo.taskId);
|
||||||
state.mTaskInfo = taskInfo;
|
state.mTaskInfo = taskInfo;
|
||||||
|
mWindowDecorationViewModel.onTaskInfoChanged(state.mTaskInfo, state.mWindowDecoration);
|
||||||
final Rect taskBounds = taskInfo.configuration.windowConfiguration.getBounds();
|
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Freeform Task Info Changed: #%d",
|
||||||
final SurfaceControl leash = state.mLeash;
|
taskInfo.taskId);
|
||||||
mSyncQueue.runInSync(t -> {
|
|
||||||
Point taskPosition = taskInfo.positionInParent;
|
|
||||||
t.setPosition(leash, taskPosition.x, taskPosition.y)
|
|
||||||
.setWindowCrop(leash, taskBounds.width(), taskBounds.height())
|
|
||||||
.show(leash);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -0,0 +1,174 @@
|
|||||||
|
/*
|
||||||
|
* 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.os.Handler;
|
||||||
|
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 Handler mMainHandler;
|
||||||
|
private final DisplayController mDisplayController;
|
||||||
|
private final SyncTransactionQueue mSyncQueue;
|
||||||
|
|
||||||
|
public CaptionWindowDecorViewModel(
|
||||||
|
Context context,
|
||||||
|
Handler mainHandler,
|
||||||
|
ShellTaskOrganizer taskOrganizer,
|
||||||
|
DisplayController displayController,
|
||||||
|
SyncTransactionQueue syncQueue) {
|
||||||
|
mContext = context;
|
||||||
|
mMainHandler = mainHandler;
|
||||||
|
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,
|
||||||
|
mMainHandler,
|
||||||
|
mSyncQueue);
|
||||||
|
TaskPositioner taskPositioner = new TaskPositioner(mTaskOrganizer, windowDecoration);
|
||||||
|
CaptionTouchEventListener touchEventListener =
|
||||||
|
new CaptionTouchEventListener(taskInfo, taskPositioner);
|
||||||
|
windowDecoration.setCaptionListeners(touchEventListener, touchEventListener);
|
||||||
|
windowDecoration.setDragResizeCallback(taskPositioner);
|
||||||
|
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 final DragResizeCallback mDragResizeCallback;
|
||||||
|
|
||||||
|
private int mDragPointerId = -1;
|
||||||
|
|
||||||
|
private CaptionTouchEventListener(
|
||||||
|
RunningTaskInfo taskInfo, DragResizeCallback dragResizeCallback) {
|
||||||
|
mTaskId = taskInfo.taskId;
|
||||||
|
mTaskToken = taskInfo.token;
|
||||||
|
mDragResizeCallback = dragResizeCallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
@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;
|
||||||
|
}
|
||||||
|
handleEventForMove(e);
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleEventForMove(MotionEvent e) {
|
||||||
|
switch (e.getActionMasked()) {
|
||||||
|
case MotionEvent.ACTION_DOWN:
|
||||||
|
mDragPointerId = e.getPointerId(0);
|
||||||
|
mDragResizeCallback.onDragResizeStart(
|
||||||
|
0 /* ctrlType */, e.getRawX(0), e.getRawY(0));
|
||||||
|
break;
|
||||||
|
case MotionEvent.ACTION_MOVE: {
|
||||||
|
int dragPointerIdx = e.findPointerIndex(mDragPointerId);
|
||||||
|
mDragResizeCallback.onDragResizeMove(
|
||||||
|
e.getRawX(dragPointerIdx), e.getRawY(dragPointerIdx));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case MotionEvent.ACTION_UP:
|
||||||
|
case MotionEvent.ACTION_CANCEL: {
|
||||||
|
int dragPointerIdx = e.findPointerIndex(mDragPointerId);
|
||||||
|
mDragResizeCallback.onDragResizeEnd(
|
||||||
|
e.getRawX(dragPointerIdx), e.getRawY(dragPointerIdx));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,199 @@
|
|||||||
|
/*
|
||||||
|
* 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.app.WindowConfiguration;
|
||||||
|
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.os.Handler;
|
||||||
|
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;
|
||||||
|
|
||||||
|
// Height of button (32dp) + 2 * margin (5dp each)
|
||||||
|
private static final int DECOR_CAPTION_HEIGHT_IN_DIP = 42;
|
||||||
|
private static final int RESIZE_HANDLE_IN_DIP = 30;
|
||||||
|
|
||||||
|
private static final Rect EMPTY_OUTSET = new Rect();
|
||||||
|
private static final Rect RESIZE_HANDLE_OUTSET = new Rect(
|
||||||
|
RESIZE_HANDLE_IN_DIP, RESIZE_HANDLE_IN_DIP, RESIZE_HANDLE_IN_DIP, RESIZE_HANDLE_IN_DIP);
|
||||||
|
|
||||||
|
private final Handler mHandler;
|
||||||
|
private final SyncTransactionQueue mSyncQueue;
|
||||||
|
|
||||||
|
private View.OnClickListener mOnCaptionButtonClickListener;
|
||||||
|
private View.OnTouchListener mOnCaptionTouchListener;
|
||||||
|
private DragResizeCallback mDragResizeCallback;
|
||||||
|
|
||||||
|
private DragResizeInputListener mDragResizeListener;
|
||||||
|
|
||||||
|
private final WindowDecoration.RelayoutResult<WindowDecorLinearLayout> mResult =
|
||||||
|
new WindowDecoration.RelayoutResult<>();
|
||||||
|
|
||||||
|
CaptionWindowDecoration(
|
||||||
|
Context context,
|
||||||
|
DisplayController displayController,
|
||||||
|
ShellTaskOrganizer taskOrganizer,
|
||||||
|
ActivityManager.RunningTaskInfo taskInfo,
|
||||||
|
SurfaceControl taskSurface,
|
||||||
|
Handler handler,
|
||||||
|
SyncTransactionQueue syncQueue) {
|
||||||
|
super(context, displayController, taskOrganizer, taskInfo, taskSurface);
|
||||||
|
|
||||||
|
mHandler = handler;
|
||||||
|
mSyncQueue = syncQueue;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setCaptionListeners(
|
||||||
|
View.OnClickListener onCaptionButtonClickListener,
|
||||||
|
View.OnTouchListener onCaptionTouchListener) {
|
||||||
|
mOnCaptionButtonClickListener = onCaptionButtonClickListener;
|
||||||
|
mOnCaptionTouchListener = onCaptionTouchListener;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setDragResizeCallback(DragResizeCallback dragResizeCallback) {
|
||||||
|
mDragResizeCallback = dragResizeCallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
void relayout(ActivityManager.RunningTaskInfo taskInfo) {
|
||||||
|
final int shadowRadiusDp = taskInfo.isFocused
|
||||||
|
? DECOR_SHADOW_FOCUSED_THICKNESS_IN_DIP : DECOR_SHADOW_UNFOCUSED_THICKNESS_IN_DIP;
|
||||||
|
final boolean isFreeform = mTaskInfo.configuration.windowConfiguration.getWindowingMode()
|
||||||
|
== WindowConfiguration.WINDOWING_MODE_FREEFORM;
|
||||||
|
final boolean isDragResizeable = isFreeform && mTaskInfo.isResizeable;
|
||||||
|
final Rect outset = isDragResizeable ? RESIZE_HANDLE_OUTSET : EMPTY_OUTSET;
|
||||||
|
|
||||||
|
WindowDecorLinearLayout oldRootView = mResult.mRootView;
|
||||||
|
final SurfaceControl oldDecorationSurface = mDecorationContainerSurface;
|
||||||
|
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, 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) {
|
||||||
|
// This means the task is hidden. Nothing is set up in this case including the
|
||||||
|
// decoration surface.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (oldRootView != mResult.mRootView) {
|
||||||
|
setupRootView();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isDragResizeable) {
|
||||||
|
closeDragResizeListener();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (oldDecorationSurface != mDecorationContainerSurface) {
|
||||||
|
closeDragResizeListener();
|
||||||
|
mDragResizeListener = new DragResizeInputListener(
|
||||||
|
mContext,
|
||||||
|
mHandler,
|
||||||
|
mDisplay.getDisplayId(),
|
||||||
|
mDecorationContainerSurface,
|
||||||
|
mDragResizeCallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
mDragResizeListener.setGeometry(
|
||||||
|
mResult.mWidth, mResult.mHeight, (int) (mResult.mDensity * RESIZE_HANDLE_IN_DIP));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void closeDragResizeListener() {
|
||||||
|
if (mDragResizeListener == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
mDragResizeListener.close();
|
||||||
|
mDragResizeListener = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() {
|
||||||
|
closeDragResizeListener();
|
||||||
|
super.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback called when receiving drag-resize or drag-move related input events.
|
||||||
|
*/
|
||||||
|
public interface DragResizeCallback {
|
||||||
|
/**
|
||||||
|
* Called when a drag resize starts.
|
||||||
|
*
|
||||||
|
* @param ctrlType {@link TaskPositioner.CtrlType} indicating the direction of resizing, use
|
||||||
|
* {@code 0} to indicate it's a move
|
||||||
|
* @param x x coordinate in window decoration coordinate system where the drag resize starts
|
||||||
|
* @param y y coordinate in window decoration coordinate system where the drag resize starts
|
||||||
|
*/
|
||||||
|
void onDragResizeStart(@TaskPositioner.CtrlType int ctrlType, float x, float y);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when the pointer moves during a drag resize.
|
||||||
|
* @param x x coordinate in window decoration coordinate system of the new pointer location
|
||||||
|
* @param y y coordinate in window decoration coordinate system of the new pointer location
|
||||||
|
*/
|
||||||
|
void onDragResizeMove(float x, float y);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when a drag resize stops.
|
||||||
|
* @param x x coordinate in window decoration coordinate system where the drag resize stops
|
||||||
|
* @param y y coordinate in window decoration coordinate system where the drag resize stops
|
||||||
|
*/
|
||||||
|
void onDragResizeEnd(float x, float y);
|
||||||
|
}
|
||||||
@@ -0,0 +1,288 @@
|
|||||||
|
/*
|
||||||
|
* 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.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
|
||||||
|
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY;
|
||||||
|
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.graphics.Rect;
|
||||||
|
import android.graphics.Region;
|
||||||
|
import android.hardware.input.InputManager;
|
||||||
|
import android.os.Binder;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.os.IBinder;
|
||||||
|
import android.os.Looper;
|
||||||
|
import android.os.RemoteException;
|
||||||
|
import android.view.IWindowSession;
|
||||||
|
import android.view.InputChannel;
|
||||||
|
import android.view.InputEvent;
|
||||||
|
import android.view.InputEventReceiver;
|
||||||
|
import android.view.MotionEvent;
|
||||||
|
import android.view.PointerIcon;
|
||||||
|
import android.view.SurfaceControl;
|
||||||
|
import android.view.WindowManagerGlobal;
|
||||||
|
|
||||||
|
import com.android.internal.view.BaseIWindow;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An input event listener registered to InputDispatcher to receive input events on task edges and
|
||||||
|
* convert them to drag resize requests.
|
||||||
|
*/
|
||||||
|
class DragResizeInputListener implements AutoCloseable {
|
||||||
|
private static final String TAG = "DragResizeInputListener";
|
||||||
|
|
||||||
|
private final IWindowSession mWindowSession = WindowManagerGlobal.getWindowSession();
|
||||||
|
private final Handler mHandler;
|
||||||
|
private final InputManager mInputManager;
|
||||||
|
|
||||||
|
private final int mDisplayId;
|
||||||
|
private final BaseIWindow mFakeWindow;
|
||||||
|
private final IBinder mFocusGrantToken;
|
||||||
|
private final SurfaceControl mDecorationSurface;
|
||||||
|
private final InputChannel mInputChannel;
|
||||||
|
private final TaskResizeInputEventReceiver mInputEventReceiver;
|
||||||
|
private final com.android.wm.shell.windowdecor.DragResizeCallback mCallback;
|
||||||
|
|
||||||
|
private int mWidth;
|
||||||
|
private int mHeight;
|
||||||
|
private int mResizeHandleThickness;
|
||||||
|
|
||||||
|
private int mDragPointerId = -1;
|
||||||
|
|
||||||
|
DragResizeInputListener(
|
||||||
|
Context context,
|
||||||
|
Handler handler,
|
||||||
|
int displayId,
|
||||||
|
SurfaceControl decorationSurface,
|
||||||
|
DragResizeCallback callback) {
|
||||||
|
mInputManager = context.getSystemService(InputManager.class);
|
||||||
|
mHandler = handler;
|
||||||
|
mDisplayId = displayId;
|
||||||
|
mDecorationSurface = decorationSurface;
|
||||||
|
// Use a fake window as the backing surface is a container layer and we don't want to create
|
||||||
|
// a buffer layer for it so we can't use ViewRootImpl.
|
||||||
|
mFakeWindow = new BaseIWindow();
|
||||||
|
mFakeWindow.setSession(mWindowSession);
|
||||||
|
mFocusGrantToken = new Binder();
|
||||||
|
mInputChannel = new InputChannel();
|
||||||
|
try {
|
||||||
|
mWindowSession.grantInputChannel(
|
||||||
|
mDisplayId,
|
||||||
|
new SurfaceControl(mDecorationSurface, TAG),
|
||||||
|
mFakeWindow,
|
||||||
|
null /* hostInputToken */,
|
||||||
|
FLAG_NOT_FOCUSABLE,
|
||||||
|
PRIVATE_FLAG_TRUSTED_OVERLAY,
|
||||||
|
TYPE_APPLICATION,
|
||||||
|
mFocusGrantToken,
|
||||||
|
TAG + " of " + decorationSurface.toString(),
|
||||||
|
mInputChannel);
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
e.rethrowFromSystemServer();
|
||||||
|
}
|
||||||
|
|
||||||
|
mInputEventReceiver = new TaskResizeInputEventReceiver(mInputChannel, mHandler.getLooper());
|
||||||
|
mCallback = callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates geometry of this drag resize handler. Needs to be called every time there is a size
|
||||||
|
* change to notify the input event receiver it's ready to take the next input event. Otherwise
|
||||||
|
* it'll keep batching move events and the drag resize process is stalled.
|
||||||
|
*
|
||||||
|
* This is also used to update the touch regions of this handler every event dispatched here is
|
||||||
|
* a potential resize request.
|
||||||
|
*
|
||||||
|
* @param width The width of the drag resize handler in pixels, including resize handle
|
||||||
|
* thickness. That is task width + 2 * resize handle thickness.
|
||||||
|
* @param height The height of the drag resize handler in pixels, including resize handle
|
||||||
|
* thickness. That is task height + 2 * resize handle thickness.
|
||||||
|
* @param resizeHandleThickness The thickness of the resize handle in pixels.
|
||||||
|
*/
|
||||||
|
void setGeometry(int width, int height, int resizeHandleThickness) {
|
||||||
|
if (mWidth == width && mHeight == height
|
||||||
|
&& mResizeHandleThickness == resizeHandleThickness) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
mWidth = width;
|
||||||
|
mHeight = height;
|
||||||
|
mResizeHandleThickness = resizeHandleThickness;
|
||||||
|
|
||||||
|
Region touchRegion = new Region();
|
||||||
|
final Rect topInputBounds = new Rect(0, 0, mWidth, mResizeHandleThickness);
|
||||||
|
touchRegion.union(topInputBounds);
|
||||||
|
|
||||||
|
final Rect leftInputBounds = new Rect(0, mResizeHandleThickness,
|
||||||
|
mResizeHandleThickness, mHeight - mResizeHandleThickness);
|
||||||
|
touchRegion.union(leftInputBounds);
|
||||||
|
|
||||||
|
final Rect rightInputBounds = new Rect(
|
||||||
|
mWidth - mResizeHandleThickness, mResizeHandleThickness,
|
||||||
|
mWidth, mHeight - mResizeHandleThickness);
|
||||||
|
touchRegion.union(rightInputBounds);
|
||||||
|
|
||||||
|
final Rect bottomInputBounds = new Rect(0, mHeight - mResizeHandleThickness,
|
||||||
|
mWidth, mHeight);
|
||||||
|
touchRegion.union(bottomInputBounds);
|
||||||
|
|
||||||
|
try {
|
||||||
|
mWindowSession.updateInputChannel(
|
||||||
|
mInputChannel.getToken(),
|
||||||
|
mDisplayId,
|
||||||
|
new SurfaceControl(
|
||||||
|
mDecorationSurface, "DragResizeInputListener#setTouchRegion"),
|
||||||
|
FLAG_NOT_FOCUSABLE,
|
||||||
|
PRIVATE_FLAG_TRUSTED_OVERLAY,
|
||||||
|
touchRegion);
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
e.rethrowFromSystemServer();
|
||||||
|
}
|
||||||
|
|
||||||
|
// This marks all relevant components have handled the previous resize event and can take
|
||||||
|
// the next one now.
|
||||||
|
mInputEventReceiver.onHandledLastResizeEvent();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() {
|
||||||
|
mInputChannel.dispose();
|
||||||
|
try {
|
||||||
|
mWindowSession.remove(mFakeWindow);
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
e.rethrowFromSystemServer();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TaskResizeInputEventReceiver extends InputEventReceiver {
|
||||||
|
private boolean mWaitingForLastResizeEventHandled;
|
||||||
|
|
||||||
|
private TaskResizeInputEventReceiver(InputChannel inputChannel, Looper looper) {
|
||||||
|
super(inputChannel, looper);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onHandledLastResizeEvent() {
|
||||||
|
mWaitingForLastResizeEventHandled = false;
|
||||||
|
consumeBatchedInputEvents(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBatchedInputEventPending(int source) {
|
||||||
|
// InputEventReceiver keeps continuous move events in a batched event until explicitly
|
||||||
|
// consuming it or an incompatible event shows up (likely an up event in this case). We
|
||||||
|
// continue to keep move events in the next batched event until we receive a geometry
|
||||||
|
// update so that we don't put too much pressure on the framework with excessive number
|
||||||
|
// of input events if it can't handle them fast enough. It's more responsive to always
|
||||||
|
// resize the task to the latest received coordinates.
|
||||||
|
if (!mWaitingForLastResizeEventHandled) {
|
||||||
|
consumeBatchedInputEvents(-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onInputEvent(InputEvent inputEvent) {
|
||||||
|
finishInputEvent(inputEvent, handleInputEvent(inputEvent));
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean handleInputEvent(InputEvent inputEvent) {
|
||||||
|
if (!(inputEvent instanceof MotionEvent)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
MotionEvent e = (MotionEvent) inputEvent;
|
||||||
|
switch (e.getActionMasked()) {
|
||||||
|
case MotionEvent.ACTION_DOWN: {
|
||||||
|
mDragPointerId = e.getPointerId(0);
|
||||||
|
mCallback.onDragResizeStart(
|
||||||
|
calculateCtrlType(e.getX(0), e.getY(0)), e.getRawX(0), e.getRawY(0));
|
||||||
|
mWaitingForLastResizeEventHandled = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case MotionEvent.ACTION_MOVE: {
|
||||||
|
int dragPointerIndex = e.findPointerIndex(mDragPointerId);
|
||||||
|
mCallback.onDragResizeMove(
|
||||||
|
e.getRawX(dragPointerIndex), e.getRawY(dragPointerIndex));
|
||||||
|
mWaitingForLastResizeEventHandled = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case MotionEvent.ACTION_UP:
|
||||||
|
case MotionEvent.ACTION_CANCEL: {
|
||||||
|
int dragPointerIndex = e.findPointerIndex(mDragPointerId);
|
||||||
|
mCallback.onDragResizeEnd(
|
||||||
|
e.getRawX(dragPointerIndex), e.getRawY(dragPointerIndex));
|
||||||
|
mWaitingForLastResizeEventHandled = false;
|
||||||
|
mDragPointerId = -1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case MotionEvent.ACTION_HOVER_ENTER:
|
||||||
|
case MotionEvent.ACTION_HOVER_MOVE: {
|
||||||
|
updateCursorType(e.getXCursorPosition(), e.getYCursorPosition());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case MotionEvent.ACTION_HOVER_EXIT:
|
||||||
|
mInputManager.setPointerIconType(PointerIcon.TYPE_DEFAULT);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@TaskPositioner.CtrlType
|
||||||
|
private int calculateCtrlType(float x, float y) {
|
||||||
|
int ctrlType = 0;
|
||||||
|
if (x < mResizeHandleThickness) {
|
||||||
|
ctrlType |= TaskPositioner.CTRL_TYPE_LEFT;
|
||||||
|
}
|
||||||
|
if (x > mWidth - mResizeHandleThickness) {
|
||||||
|
ctrlType |= TaskPositioner.CTRL_TYPE_RIGHT;
|
||||||
|
}
|
||||||
|
if (y < mResizeHandleThickness) {
|
||||||
|
ctrlType |= TaskPositioner.CTRL_TYPE_TOP;
|
||||||
|
}
|
||||||
|
if (y > mHeight - mResizeHandleThickness) {
|
||||||
|
ctrlType |= TaskPositioner.CTRL_TYPE_BOTTOM;
|
||||||
|
}
|
||||||
|
return ctrlType;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateCursorType(float x, float y) {
|
||||||
|
@TaskPositioner.CtrlType int ctrlType = calculateCtrlType(x, y);
|
||||||
|
|
||||||
|
int cursorType = PointerIcon.TYPE_DEFAULT;
|
||||||
|
switch (ctrlType) {
|
||||||
|
case TaskPositioner.CTRL_TYPE_LEFT:
|
||||||
|
case TaskPositioner.CTRL_TYPE_RIGHT:
|
||||||
|
cursorType = PointerIcon.TYPE_HORIZONTAL_DOUBLE_ARROW;
|
||||||
|
break;
|
||||||
|
case TaskPositioner.CTRL_TYPE_TOP:
|
||||||
|
case TaskPositioner.CTRL_TYPE_BOTTOM:
|
||||||
|
cursorType = PointerIcon.TYPE_VERTICAL_DOUBLE_ARROW;
|
||||||
|
break;
|
||||||
|
case TaskPositioner.CTRL_TYPE_LEFT | TaskPositioner.CTRL_TYPE_TOP:
|
||||||
|
case TaskPositioner.CTRL_TYPE_RIGHT | TaskPositioner.CTRL_TYPE_BOTTOM:
|
||||||
|
cursorType = PointerIcon.TYPE_TOP_LEFT_DIAGONAL_DOUBLE_ARROW;
|
||||||
|
break;
|
||||||
|
case TaskPositioner.CTRL_TYPE_LEFT | TaskPositioner.CTRL_TYPE_BOTTOM:
|
||||||
|
case TaskPositioner.CTRL_TYPE_RIGHT | TaskPositioner.CTRL_TYPE_TOP:
|
||||||
|
cursorType = PointerIcon.TYPE_TOP_RIGHT_DIAGONAL_DOUBLE_ARROW;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
mInputManager.setPointerIconType(cursorType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2022 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.android.wm.shell.windowdecor;
|
||||||
|
|
||||||
|
interface TaskFocusStateConsumer {
|
||||||
|
void setTaskFocusState(boolean focused);
|
||||||
|
}
|
||||||
@@ -0,0 +1,99 @@
|
|||||||
|
/*
|
||||||
|
* 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.annotation.IntDef;
|
||||||
|
import android.graphics.PointF;
|
||||||
|
import android.graphics.Rect;
|
||||||
|
import android.window.WindowContainerTransaction;
|
||||||
|
|
||||||
|
import com.android.wm.shell.ShellTaskOrganizer;
|
||||||
|
|
||||||
|
class TaskPositioner implements DragResizeCallback {
|
||||||
|
|
||||||
|
@IntDef({CTRL_TYPE_LEFT, CTRL_TYPE_RIGHT, CTRL_TYPE_TOP, CTRL_TYPE_BOTTOM})
|
||||||
|
@interface CtrlType {}
|
||||||
|
|
||||||
|
static final int CTRL_TYPE_LEFT = 1;
|
||||||
|
static final int CTRL_TYPE_RIGHT = 2;
|
||||||
|
static final int CTRL_TYPE_TOP = 4;
|
||||||
|
static final int CTRL_TYPE_BOTTOM = 8;
|
||||||
|
|
||||||
|
private final ShellTaskOrganizer mTaskOrganizer;
|
||||||
|
private final WindowDecoration mWindowDecoration;
|
||||||
|
|
||||||
|
private final Rect mTaskBoundsAtDragStart = new Rect();
|
||||||
|
private final PointF mResizeStartPoint = new PointF();
|
||||||
|
private final Rect mResizeTaskBounds = new Rect();
|
||||||
|
|
||||||
|
private int mCtrlType;
|
||||||
|
|
||||||
|
TaskPositioner(ShellTaskOrganizer taskOrganizer, WindowDecoration windowDecoration) {
|
||||||
|
mTaskOrganizer = taskOrganizer;
|
||||||
|
mWindowDecoration = windowDecoration;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDragResizeStart(int ctrlType, float x, float y) {
|
||||||
|
mCtrlType = ctrlType;
|
||||||
|
|
||||||
|
mTaskBoundsAtDragStart.set(
|
||||||
|
mWindowDecoration.mTaskInfo.configuration.windowConfiguration.getBounds());
|
||||||
|
mResizeStartPoint.set(x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDragResizeMove(float x, float y) {
|
||||||
|
changeBounds(x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDragResizeEnd(float x, float y) {
|
||||||
|
changeBounds(x, y);
|
||||||
|
|
||||||
|
mCtrlType = 0;
|
||||||
|
mTaskBoundsAtDragStart.setEmpty();
|
||||||
|
mResizeStartPoint.set(0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void changeBounds(float x, float y) {
|
||||||
|
float deltaX = x - mResizeStartPoint.x;
|
||||||
|
mResizeTaskBounds.set(mTaskBoundsAtDragStart);
|
||||||
|
if ((mCtrlType & CTRL_TYPE_LEFT) != 0) {
|
||||||
|
mResizeTaskBounds.left += deltaX;
|
||||||
|
}
|
||||||
|
if ((mCtrlType & CTRL_TYPE_RIGHT) != 0) {
|
||||||
|
mResizeTaskBounds.right += deltaX;
|
||||||
|
}
|
||||||
|
float deltaY = y - mResizeStartPoint.y;
|
||||||
|
if ((mCtrlType & CTRL_TYPE_TOP) != 0) {
|
||||||
|
mResizeTaskBounds.top += deltaY;
|
||||||
|
}
|
||||||
|
if ((mCtrlType & CTRL_TYPE_BOTTOM) != 0) {
|
||||||
|
mResizeTaskBounds.bottom += deltaY;
|
||||||
|
}
|
||||||
|
if (mCtrlType == 0) {
|
||||||
|
mResizeTaskBounds.offset((int) deltaX, (int) deltaY);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!mResizeTaskBounds.isEmpty()) {
|
||||||
|
final WindowContainerTransaction wct = new WindowContainerTransaction();
|
||||||
|
wct.setBounds(mWindowDecoration.mTaskInfo.token, mResizeTaskBounds);
|
||||||
|
mTaskOrganizer.applyTransaction(wct);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2022 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.android.wm.shell.windowdecor;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
|
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
|
import com.android.wm.shell.R;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A {@link LinearLayout} that takes an additional task focused drawable state. The new state is
|
||||||
|
* used to select the correct background color for views in the window decoration.
|
||||||
|
*/
|
||||||
|
public class WindowDecorLinearLayout extends LinearLayout implements TaskFocusStateConsumer {
|
||||||
|
private static final int[] TASK_FOCUSED_STATE = { R.attr.state_task_focused };
|
||||||
|
|
||||||
|
private boolean mIsTaskFocused;
|
||||||
|
|
||||||
|
public WindowDecorLinearLayout(Context context) {
|
||||||
|
super(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
public WindowDecorLinearLayout(Context context,
|
||||||
|
@Nullable AttributeSet attrs) {
|
||||||
|
super(context, attrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
public WindowDecorLinearLayout(Context context, @Nullable AttributeSet attrs,
|
||||||
|
int defStyleAttr) {
|
||||||
|
super(context, attrs, defStyleAttr);
|
||||||
|
}
|
||||||
|
|
||||||
|
public WindowDecorLinearLayout(Context context, AttributeSet attrs, int defStyleAttr,
|
||||||
|
int defStyleRes) {
|
||||||
|
super(context, attrs, defStyleAttr, defStyleRes);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setTaskFocusState(boolean focused) {
|
||||||
|
mIsTaskFocused = focused;
|
||||||
|
|
||||||
|
refreshDrawableState();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected int[] onCreateDrawableState(int extraSpace) {
|
||||||
|
if (!mIsTaskFocused) {
|
||||||
|
return super.onCreateDrawableState(extraSpace);
|
||||||
|
}
|
||||||
|
|
||||||
|
final int[] states = super.onCreateDrawableState(extraSpace + 1);
|
||||||
|
mergeDrawableStates(states, TASK_FOCUSED_STATE);
|
||||||
|
return states;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2022 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.android.wm.shell.windowdecor;
|
||||||
|
|
||||||
|
import android.app.ActivityManager;
|
||||||
|
import android.view.SurfaceControl;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The interface used by some {@link com.android.wm.shell.ShellTaskOrganizer.TaskListener} to help
|
||||||
|
* customize {@link WindowDecoration}. Its implementations are responsible to interpret user's
|
||||||
|
* interactions with UI widgets in window decorations and send corresponding requests to system
|
||||||
|
* servers.
|
||||||
|
*
|
||||||
|
* @param <T> The actual decoration type
|
||||||
|
*/
|
||||||
|
public interface WindowDecorViewModel<T extends AutoCloseable> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a window decoration for the given task.
|
||||||
|
*
|
||||||
|
* @param taskInfo the initial task info of the task
|
||||||
|
* @param taskSurface the surface of the task
|
||||||
|
* @return the window decoration object
|
||||||
|
*/
|
||||||
|
T createWindowDecoration(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl taskSurface);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notifies a task info update on the given task, with the window decoration created previously
|
||||||
|
* for this task by {@link #createWindowDecoration}.
|
||||||
|
*
|
||||||
|
* @param taskInfo the new task info of the task
|
||||||
|
* @param windowDecoration the window decoration created for the task
|
||||||
|
*/
|
||||||
|
void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo, T windowDecoration);
|
||||||
|
}
|
||||||
@@ -0,0 +1,265 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2022 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.android.wm.shell.windowdecor;
|
||||||
|
|
||||||
|
import android.app.ActivityManager.RunningTaskInfo;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.res.Configuration;
|
||||||
|
import android.graphics.Color;
|
||||||
|
import android.graphics.PixelFormat;
|
||||||
|
import android.graphics.Point;
|
||||||
|
import android.graphics.Rect;
|
||||||
|
import android.util.DisplayMetrics;
|
||||||
|
import android.view.Display;
|
||||||
|
import android.view.InsetsState;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.SurfaceControl;
|
||||||
|
import android.view.SurfaceControlViewHost;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.WindowManager;
|
||||||
|
import android.view.WindowlessWindowManager;
|
||||||
|
import android.window.WindowContainerTransaction;
|
||||||
|
|
||||||
|
import com.android.wm.shell.ShellTaskOrganizer;
|
||||||
|
import com.android.wm.shell.common.DisplayController;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Manages a container surface and a windowless window to show window decoration. Responsible to
|
||||||
|
* update window decoration window state and layout parameters on task info changes and so that
|
||||||
|
* window decoration is in correct state and bounds.
|
||||||
|
*
|
||||||
|
* The container surface is a child of the task display area in the same display, so that window
|
||||||
|
* decorations can be drawn out of the task bounds and receive input events from out of the task
|
||||||
|
* bounds to support drag resizing.
|
||||||
|
*
|
||||||
|
* The windowless window that hosts window decoration is positioned in front of all activities, to
|
||||||
|
* allow the foreground activity to draw its own background behind window decorations, such as
|
||||||
|
* the window captions.
|
||||||
|
*
|
||||||
|
* @param <T> The type of the root view
|
||||||
|
*/
|
||||||
|
public class WindowDecoration<T extends View & TaskFocusStateConsumer> implements AutoCloseable {
|
||||||
|
private static final int[] CAPTION_INSETS_TYPES = { InsetsState.ITYPE_CAPTION_BAR };
|
||||||
|
|
||||||
|
/**
|
||||||
|
* System-wide context. Only used to create context with overridden configurations.
|
||||||
|
*/
|
||||||
|
final Context mContext;
|
||||||
|
final DisplayController mDisplayController;
|
||||||
|
final ShellTaskOrganizer mTaskOrganizer;
|
||||||
|
|
||||||
|
RunningTaskInfo mTaskInfo;
|
||||||
|
final SurfaceControl mTaskSurface;
|
||||||
|
|
||||||
|
Display mDisplay;
|
||||||
|
Context mDecorWindowContext;
|
||||||
|
SurfaceControl mDecorationContainerSurface;
|
||||||
|
SurfaceControl mTaskBackgroundSurface;
|
||||||
|
|
||||||
|
private CaptionWindowManager mCaptionWindowManager;
|
||||||
|
private SurfaceControlViewHost mViewHost;
|
||||||
|
|
||||||
|
private final Rect mCaptionInsetsRect = new Rect();
|
||||||
|
private final Rect mTaskSurfaceCrop = new Rect();
|
||||||
|
private final float[] mTmpColor = new float[3];
|
||||||
|
|
||||||
|
WindowDecoration(
|
||||||
|
Context context,
|
||||||
|
DisplayController displayController,
|
||||||
|
ShellTaskOrganizer taskOrganizer,
|
||||||
|
RunningTaskInfo taskInfo,
|
||||||
|
SurfaceControl taskSurface) {
|
||||||
|
mContext = context;
|
||||||
|
mDisplayController = displayController;
|
||||||
|
mTaskOrganizer = taskOrganizer;
|
||||||
|
mTaskInfo = taskInfo;
|
||||||
|
mTaskSurface = taskSurface;
|
||||||
|
|
||||||
|
mDisplay = mDisplayController.getDisplay(mTaskInfo.displayId);
|
||||||
|
mDecorWindowContext = mContext.createConfigurationContext(mTaskInfo.getConfiguration());
|
||||||
|
|
||||||
|
// Put caption under task surface because ViewRootImpl sets the destination frame of
|
||||||
|
// windowless window layers and BLASTBufferQueue#update() doesn't support offset.
|
||||||
|
mCaptionWindowManager =
|
||||||
|
new CaptionWindowManager(mTaskInfo.getConfiguration(), mTaskSurface);
|
||||||
|
}
|
||||||
|
|
||||||
|
void relayout(RunningTaskInfo taskInfo, int layoutResId, T rootView, float captionHeightDp,
|
||||||
|
Rect outsetsDp, float shadowRadiusDp, SurfaceControl.Transaction t,
|
||||||
|
WindowContainerTransaction wct, RelayoutResult<T> outResult) {
|
||||||
|
outResult.reset();
|
||||||
|
|
||||||
|
final Configuration oldTaskConfig = mTaskInfo.getConfiguration();
|
||||||
|
if (taskInfo != null) {
|
||||||
|
mTaskInfo = taskInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!mTaskInfo.isVisible) {
|
||||||
|
close();
|
||||||
|
t.hide(mTaskSurface);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rootView == null && layoutResId == 0) {
|
||||||
|
throw new IllegalArgumentException("layoutResId and rootView can't both be invalid.");
|
||||||
|
}
|
||||||
|
|
||||||
|
outResult.mRootView = rootView;
|
||||||
|
rootView = null; // Clear it just in case we use it accidentally
|
||||||
|
final Configuration taskConfig = mTaskInfo.getConfiguration();
|
||||||
|
if (oldTaskConfig.densityDpi != taskConfig.densityDpi
|
||||||
|
|| mDisplay.getDisplayId() != mTaskInfo.displayId) {
|
||||||
|
close();
|
||||||
|
|
||||||
|
mDisplay = mDisplayController.getDisplay(mTaskInfo.displayId);
|
||||||
|
mDecorWindowContext = mContext.createConfigurationContext(taskConfig);
|
||||||
|
if (layoutResId != 0) {
|
||||||
|
outResult.mRootView =
|
||||||
|
(T) LayoutInflater.from(mDecorWindowContext).inflate(layoutResId, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (outResult.mRootView == null) {
|
||||||
|
outResult.mRootView =
|
||||||
|
(T) LayoutInflater.from(mDecorWindowContext).inflate(layoutResId, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
// DecorationContainerSurface
|
||||||
|
if (mDecorationContainerSurface == null) {
|
||||||
|
final SurfaceControl.Builder builder = new SurfaceControl.Builder();
|
||||||
|
mDecorationContainerSurface = builder
|
||||||
|
.setName("Decor container of Task=" + mTaskInfo.taskId)
|
||||||
|
.setContainerLayer()
|
||||||
|
.setParent(mTaskSurface)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
t.setTrustedOverlay(mDecorationContainerSurface, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
final Rect taskBounds = taskConfig.windowConfiguration.getBounds();
|
||||||
|
outResult.mDensity = taskConfig.densityDpi * DisplayMetrics.DENSITY_DEFAULT_SCALE;
|
||||||
|
final int decorContainerOffsetX = -(int) (outsetsDp.left * outResult.mDensity);
|
||||||
|
final int decorContainerOffsetY = -(int) (outsetsDp.top * outResult.mDensity);
|
||||||
|
outResult.mWidth = taskBounds.width()
|
||||||
|
+ (int) (outsetsDp.right * outResult.mDensity)
|
||||||
|
- decorContainerOffsetX;
|
||||||
|
outResult.mHeight = taskBounds.height()
|
||||||
|
+ (int) (outsetsDp.bottom * outResult.mDensity)
|
||||||
|
- decorContainerOffsetY;
|
||||||
|
t.setPosition(mDecorationContainerSurface, decorContainerOffsetX, decorContainerOffsetY)
|
||||||
|
.setWindowCrop(mDecorationContainerSurface, outResult.mWidth, outResult.mHeight)
|
||||||
|
.setLayer(mDecorationContainerSurface, mTaskInfo.numActivities + 1)
|
||||||
|
.show(mDecorationContainerSurface);
|
||||||
|
|
||||||
|
// TaskBackgroundSurface
|
||||||
|
if (mTaskBackgroundSurface == null) {
|
||||||
|
final SurfaceControl.Builder builder = new SurfaceControl.Builder();
|
||||||
|
mTaskBackgroundSurface = builder
|
||||||
|
.setName("Background of Task=" + mTaskInfo.taskId)
|
||||||
|
.setEffectLayer()
|
||||||
|
.setParent(mTaskSurface)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
float shadowRadius = outResult.mDensity * shadowRadiusDp;
|
||||||
|
int backgroundColorInt = mTaskInfo.taskDescription.getBackgroundColor();
|
||||||
|
mTmpColor[0] = Color.red(backgroundColorInt);
|
||||||
|
mTmpColor[1] = Color.green(backgroundColorInt);
|
||||||
|
mTmpColor[2] = Color.blue(backgroundColorInt);
|
||||||
|
t.setCrop(mTaskBackgroundSurface, taskBounds)
|
||||||
|
.setShadowRadius(mTaskBackgroundSurface, shadowRadius)
|
||||||
|
.setColor(mTaskBackgroundSurface, mTmpColor);
|
||||||
|
|
||||||
|
// Caption view
|
||||||
|
mCaptionWindowManager.setConfiguration(taskConfig);
|
||||||
|
final int captionHeight = (int) Math.ceil(captionHeightDp * outResult.mDensity);
|
||||||
|
final WindowManager.LayoutParams lp =
|
||||||
|
new WindowManager.LayoutParams(taskBounds.width(), captionHeight,
|
||||||
|
WindowManager.LayoutParams.TYPE_APPLICATION,
|
||||||
|
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, PixelFormat.TRANSPARENT);
|
||||||
|
lp.setTitle("Caption of Task=" + mTaskInfo.taskId);
|
||||||
|
lp.setTrustedOverlay();
|
||||||
|
if (mViewHost == null) {
|
||||||
|
mViewHost = new SurfaceControlViewHost(mDecorWindowContext, mDisplay,
|
||||||
|
mCaptionWindowManager, true);
|
||||||
|
mViewHost.setView(outResult.mRootView, lp);
|
||||||
|
} else {
|
||||||
|
mViewHost.relayout(lp);
|
||||||
|
}
|
||||||
|
|
||||||
|
outResult.mRootView.setTaskFocusState(mTaskInfo.isFocused);
|
||||||
|
|
||||||
|
// Caption insets
|
||||||
|
mCaptionInsetsRect.set(taskBounds);
|
||||||
|
mCaptionInsetsRect.bottom = mCaptionInsetsRect.top + captionHeight;
|
||||||
|
wct.addRectInsetsProvider(mTaskInfo.token, mCaptionInsetsRect, CAPTION_INSETS_TYPES);
|
||||||
|
|
||||||
|
// Task surface itself
|
||||||
|
Point taskPosition = mTaskInfo.positionInParent;
|
||||||
|
mTaskSurfaceCrop.set(
|
||||||
|
decorContainerOffsetX,
|
||||||
|
decorContainerOffsetY,
|
||||||
|
outResult.mWidth + decorContainerOffsetX,
|
||||||
|
outResult.mHeight + decorContainerOffsetY);
|
||||||
|
t.setPosition(mTaskSurface, taskPosition.x, taskPosition.y)
|
||||||
|
.setCrop(mTaskSurface, mTaskSurfaceCrop)
|
||||||
|
.show(mTaskSurface);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() {
|
||||||
|
if (mViewHost != null) {
|
||||||
|
mViewHost.release();
|
||||||
|
mViewHost = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mDecorationContainerSurface != null) {
|
||||||
|
mDecorationContainerSurface.release();
|
||||||
|
mDecorationContainerSurface = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mTaskBackgroundSurface != null) {
|
||||||
|
mTaskBackgroundSurface.release();
|
||||||
|
mTaskBackgroundSurface = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class RelayoutResult<T extends View & TaskFocusStateConsumer> {
|
||||||
|
int mWidth;
|
||||||
|
int mHeight;
|
||||||
|
float mDensity;
|
||||||
|
T mRootView;
|
||||||
|
|
||||||
|
void reset() {
|
||||||
|
mWidth = 0;
|
||||||
|
mHeight = 0;
|
||||||
|
mDensity = 0;
|
||||||
|
mRootView = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class CaptionWindowManager extends WindowlessWindowManager {
|
||||||
|
CaptionWindowManager(Configuration config, SurfaceControl rootSurface) {
|
||||||
|
super(config, rootSurface, null /* hostInputToken */);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setConfiguration(Configuration configuration) {
|
||||||
|
super.setConfiguration(configuration);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4880,9 +4880,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(b/165794880): Freeform task organizer doesn't support drag-resize yet. Remove
|
if (task.isOrganized()) {
|
||||||
// the special case when it does.
|
|
||||||
if (task.isOrganized() && task.getWindowingMode() != WINDOWING_MODE_FREEFORM) {
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -288,10 +288,7 @@ final class InputMonitor {
|
|||||||
SurfaceControl touchableRegionCrop = null;
|
SurfaceControl touchableRegionCrop = null;
|
||||||
final Task task = w.getTask();
|
final Task task = w.getTask();
|
||||||
if (task != null) {
|
if (task != null) {
|
||||||
// TODO(b/165794636): Remove the special case for freeform window once drag resizing is
|
if (task.isOrganized() && task.getWindowingMode() != WINDOWING_MODE_FULLSCREEN) {
|
||||||
// handled by WM shell.
|
|
||||||
if (task.isOrganized() && task.getWindowingMode() != WINDOWING_MODE_FULLSCREEN
|
|
||||||
&& !task.inFreeformWindowingMode()) {
|
|
||||||
// If the window is in a TaskManaged by a TaskOrganizer then most cropping will
|
// If the window is in a TaskManaged by a TaskOrganizer then most cropping will
|
||||||
// be applied using the SurfaceControl hierarchy from the Organizer. This means
|
// be applied using the SurfaceControl hierarchy from the Organizer. This means
|
||||||
// we need to make sure that these changes in crop are reflected in the input
|
// we need to make sure that these changes in crop are reflected in the input
|
||||||
|
|||||||
@@ -69,8 +69,6 @@ import static android.view.WindowManager.TRANSIT_TO_BACK;
|
|||||||
import static android.view.WindowManager.TRANSIT_TO_FRONT;
|
import static android.view.WindowManager.TRANSIT_TO_FRONT;
|
||||||
import static android.window.DisplayAreaOrganizer.FEATURE_UNDEFINED;
|
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_ADD_REMOVE;
|
||||||
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_BACK_PREVIEW;
|
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_BACK_PREVIEW;
|
||||||
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_LOCKTASK;
|
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_ROOT_TASK;
|
||||||
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TASK_MOVEMENT;
|
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.WindowManagerDebugConfig.TAG_WM;
|
||||||
import static com.android.server.wm.WindowManagerService.dipToPixel;
|
|
||||||
|
|
||||||
import static java.lang.Integer.MAX_VALUE;
|
import static java.lang.Integer.MAX_VALUE;
|
||||||
|
|
||||||
@@ -3277,7 +3274,6 @@ class Task extends TaskFragment {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final SurfaceControl.Transaction t = getSyncTransaction();
|
final SurfaceControl.Transaction t = getSyncTransaction();
|
||||||
updateShadowsRadius(isFocused(), t);
|
|
||||||
|
|
||||||
if (mDimmer.updateDims(t, mTmpDimBoundsRect)) {
|
if (mDimmer.updateDims(t, mTmpDimBoundsRect)) {
|
||||||
scheduleAnimation();
|
scheduleAnimation();
|
||||||
@@ -4248,49 +4244,11 @@ class Task extends TaskFragment {
|
|||||||
return getActivity(ActivityRecord::isVisible) != null;
|
return getActivity(ActivityRecord::isVisible) != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the desired shadow radius in pixels for the current task.
|
|
||||||
*/
|
|
||||||
private float getShadowRadius(boolean taskIsFocused) {
|
|
||||||
int elevation = 0;
|
|
||||||
|
|
||||||
// Get elevation for a specific windowing mode.
|
|
||||||
if (inFreeformWindowingMode()) {
|
|
||||||
elevation = taskIsFocused
|
|
||||||
? DECOR_SHADOW_FOCUSED_HEIGHT_IN_DIP : DECOR_SHADOW_UNFOCUSED_HEIGHT_IN_DIP;
|
|
||||||
} else {
|
|
||||||
// For all other windowing modes, do not draw a shadow.
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the task has no visible children, do not draw a shadow.
|
|
||||||
if (!hasVisibleChildren()) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return dipToPixel(elevation, getDisplayContent().getDisplayMetrics());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update the length of the shadow if needed based on windowing mode and task focus state.
|
|
||||||
*/
|
|
||||||
private void updateShadowsRadius(boolean taskIsFocused,
|
|
||||||
SurfaceControl.Transaction pendingTransaction) {
|
|
||||||
if (!isRootTask()) return;
|
|
||||||
|
|
||||||
final float newShadowRadius = getShadowRadius(taskIsFocused);
|
|
||||||
if (mShadowRadius != newShadowRadius) {
|
|
||||||
mShadowRadius = newShadowRadius;
|
|
||||||
pendingTransaction.setShadowRadius(getSurfaceControl(), mShadowRadius);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called on the task when it gained or lost focus.
|
* Called on the task when it gained or lost focus.
|
||||||
* @param hasFocus
|
* @param hasFocus
|
||||||
*/
|
*/
|
||||||
void onAppFocusChanged(boolean hasFocus) {
|
void onAppFocusChanged(boolean hasFocus) {
|
||||||
updateShadowsRadius(hasFocus, getSyncTransaction());
|
|
||||||
dispatchTaskInfoChangedIfNeeded(false /* force */);
|
dispatchTaskInfoChangedIfNeeded(false /* force */);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user