diff options
Diffstat (limited to 'libs')
39 files changed, 977 insertions, 167 deletions
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/common/CommonFoldingFeature.java b/libs/WindowManager/Jetpack/src/androidx/window/common/CommonFoldingFeature.java index 65955b1d9bcc..e37dea4dfd69 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/common/CommonFoldingFeature.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/common/CommonFoldingFeature.java @@ -126,7 +126,7 @@ public final class CommonFoldingFeature { * @see #FEATURE_PATTERN * @return {@link List} of {@link CommonFoldingFeature}. */ - static List<CommonFoldingFeature> parseListFromString(@NonNull String value, + public static List<CommonFoldingFeature> parseListFromString(@NonNull String value, @State int hingeState) { List<CommonFoldingFeature> features = new ArrayList<>(); String[] featureStrings = value.split(";"); diff --git a/libs/WindowManager/Jetpack/src/androidx/window/common/DeviceStateManagerFoldingFeatureProducer.java b/libs/WindowManager/Jetpack/src/androidx/window/common/DeviceStateManagerFoldingFeatureProducer.java index a184dff5005b..88fd461debbe 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/common/DeviceStateManagerFoldingFeatureProducer.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/common/DeviceStateManagerFoldingFeatureProducer.java @@ -36,6 +36,7 @@ import androidx.window.util.BaseDataProducer; import com.android.internal.R; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Objects; import java.util.Optional; @@ -78,7 +79,9 @@ public final class DeviceStateManagerFoldingFeatureProducer private int mCurrentBaseDeviceState = INVALID_DEVICE_STATE; @NonNull - private final BaseDataProducer<String> mRawFoldSupplier; + private final RawFoldingFeatureProducer mRawFoldSupplier; + + private final boolean mIsHalfOpenedSupported; private final DeviceStateCallback mDeviceStateCallback = new DeviceStateCallback() { @Override @@ -101,10 +104,12 @@ public final class DeviceStateManagerFoldingFeatureProducer }; public DeviceStateManagerFoldingFeatureProducer(@NonNull Context context, - @NonNull BaseDataProducer<String> rawFoldSupplier) { + @NonNull RawFoldingFeatureProducer rawFoldSupplier, + @NonNull DeviceStateManager deviceStateManager) { mRawFoldSupplier = rawFoldSupplier; String[] deviceStatePosturePairs = context.getResources() .getStringArray(R.array.config_device_state_postures); + boolean isHalfOpenedSupported = false; for (String deviceStatePosturePair : deviceStatePosturePairs) { String[] deviceStatePostureMapping = deviceStatePosturePair.split(":"); if (deviceStatePostureMapping.length != 2) { @@ -128,12 +133,13 @@ public final class DeviceStateManagerFoldingFeatureProducer } continue; } - + isHalfOpenedSupported = isHalfOpenedSupported + || posture == CommonFoldingFeature.COMMON_STATE_HALF_OPENED; mDeviceStateToPostureMap.put(deviceState, posture); } - + mIsHalfOpenedSupported = isHalfOpenedSupported; if (mDeviceStateToPostureMap.size() > 0) { - Objects.requireNonNull(context.getSystemService(DeviceStateManager.class)) + Objects.requireNonNull(deviceStateManager) .registerCallback(context.getMainExecutor(), mDeviceStateCallback); } } @@ -188,6 +194,31 @@ public final class DeviceStateManagerFoldingFeatureProducer } /** + * Returns a {@link List} of all the {@link CommonFoldingFeature} with the state set to + * {@link CommonFoldingFeature#COMMON_STATE_UNKNOWN}. This method parses a {@link String} so a + * caller should consider caching the value or the derived value. + */ + @NonNull + public List<CommonFoldingFeature> getFoldsWithUnknownState() { + Optional<String> optionalFoldingFeatureString = mRawFoldSupplier.getCurrentData(); + + if (optionalFoldingFeatureString.isPresent()) { + return CommonFoldingFeature.parseListFromString( + optionalFoldingFeatureString.get(), CommonFoldingFeature.COMMON_STATE_UNKNOWN + ); + } + return Collections.emptyList(); + } + + + /** + * Returns {@code true} if the device supports half-opened mode, {@code false} otherwise. + */ + public boolean isHalfOpenedSupported() { + return mIsHalfOpenedSupported; + } + + /** * Adds the data to the storeFeaturesConsumer when the data is ready. * @param storeFeaturesConsumer a consumer to collect the data when it is first available. */ diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java index 29cf05407a61..6714263ad952 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java @@ -20,6 +20,7 @@ import android.app.ActivityTaskManager; import android.app.ActivityThread; import android.app.Application; import android.content.Context; +import android.hardware.devicestate.DeviceStateManager; import android.util.Log; import androidx.annotation.NonNull; @@ -64,6 +65,11 @@ public class WindowExtensionsImpl implements WindowExtensions { } @NonNull + private DeviceStateManager getDeviceStateManager() { + return Objects.requireNonNull(getApplication().getSystemService(DeviceStateManager.class)); + } + + @NonNull private DeviceStateManagerFoldingFeatureProducer getFoldingFeatureProducer() { if (mFoldingFeatureProducer == null) { synchronized (mLock) { @@ -73,7 +79,7 @@ public class WindowExtensionsImpl implements WindowExtensions { new RawFoldingFeatureProducer(context); mFoldingFeatureProducer = new DeviceStateManagerFoldingFeatureProducer(context, - foldingFeatureProducer); + foldingFeatureProducer, getDeviceStateManager()); } } } diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/DisplayFoldFeatureUtil.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/DisplayFoldFeatureUtil.java new file mode 100644 index 000000000000..a0f481a911ad --- /dev/null +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/DisplayFoldFeatureUtil.java @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2024 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 androidx.window.extensions.layout; + +import androidx.window.common.CommonFoldingFeature; +import androidx.window.common.DeviceStateManagerFoldingFeatureProducer; + +import java.util.ArrayList; +import java.util.List; + +/** + * Util functions for working with {@link androidx.window.extensions.layout.DisplayFoldFeature}. + */ +public class DisplayFoldFeatureUtil { + + private DisplayFoldFeatureUtil() {} + + private static DisplayFoldFeature create(CommonFoldingFeature foldingFeature, + boolean isHalfOpenedSupported) { + final int foldType; + if (foldingFeature.getType() == CommonFoldingFeature.COMMON_TYPE_HINGE) { + foldType = DisplayFoldFeature.TYPE_HINGE; + } else { + foldType = DisplayFoldFeature.TYPE_SCREEN_FOLD_IN; + } + DisplayFoldFeature.Builder featureBuilder = new DisplayFoldFeature.Builder(foldType); + + if (isHalfOpenedSupported) { + featureBuilder.addProperty(DisplayFoldFeature.FOLD_PROPERTY_SUPPORTS_HALF_OPENED); + } + return featureBuilder.build(); + } + + /** + * Returns the list of supported {@link DisplayFeature} calculated from the + * {@link DeviceStateManagerFoldingFeatureProducer}. + */ + public static List<DisplayFoldFeature> extractDisplayFoldFeatures( + DeviceStateManagerFoldingFeatureProducer producer) { + List<DisplayFoldFeature> foldFeatures = new ArrayList<>(); + List<CommonFoldingFeature> folds = producer.getFoldsWithUnknownState(); + + final boolean isHalfOpenedSupported = producer.isHalfOpenedSupported(); + for (CommonFoldingFeature fold : folds) { + foldFeatures.add(DisplayFoldFeatureUtil.create(fold, isHalfOpenedSupported)); + } + return foldFeatures; + } +} diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java index 6e704f35fb22..4fd11c495529 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java @@ -45,7 +45,6 @@ import androidx.window.common.CommonFoldingFeature; import androidx.window.common.DeviceStateManagerFoldingFeatureProducer; import androidx.window.common.EmptyLifecycleCallbacksAdapter; import androidx.window.extensions.core.util.function.Consumer; -import androidx.window.util.DataProducer; import java.util.ArrayList; import java.util.Collections; @@ -56,10 +55,6 @@ import java.util.Set; /** * Reference implementation of androidx.window.extensions.layout OEM interface for use with * WindowManager Jetpack. - * - * NOTE: This version is a work in progress and under active development. It MUST NOT be used in - * production builds since the interface can still change before reaching stable version. - * Please refer to {@link androidx.window.sidecar.SampleSidecarImpl} instead. */ public class WindowLayoutComponentImpl implements WindowLayoutComponent { private static final String TAG = WindowLayoutComponentImpl.class.getSimpleName(); @@ -71,7 +66,7 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent { new ArrayMap<>(); @GuardedBy("mLock") - private final DataProducer<List<CommonFoldingFeature>> mFoldingFeatureProducer; + private final DeviceStateManagerFoldingFeatureProducer mFoldingFeatureProducer; @GuardedBy("mLock") private final List<CommonFoldingFeature> mLastReportedFoldingFeatures = new ArrayList<>(); @@ -87,12 +82,17 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent { private final RawConfigurationChangedListener mRawConfigurationChangedListener = new RawConfigurationChangedListener(); + private final SupportedWindowFeatures mSupportedWindowFeatures; + public WindowLayoutComponentImpl(@NonNull Context context, @NonNull DeviceStateManagerFoldingFeatureProducer foldingFeatureProducer) { ((Application) context.getApplicationContext()) .registerActivityLifecycleCallbacks(new NotifyOnConfigurationChanged()); mFoldingFeatureProducer = foldingFeatureProducer; mFoldingFeatureProducer.addDataChangedCallback(this::onDisplayFeaturesChanged); + final List<DisplayFoldFeature> displayFoldFeatures = + DisplayFoldFeatureUtil.extractDisplayFoldFeatures(mFoldingFeatureProducer); + mSupportedWindowFeatures = new SupportedWindowFeatures.Builder(displayFoldFeatures).build(); } /** @@ -283,6 +283,15 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent { } } + /** + * Returns the {@link SupportedWindowFeatures} for the device. This list does not change over + * time. + */ + @NonNull + public SupportedWindowFeatures getSupportedWindowFeatures() { + return mSupportedWindowFeatures; + } + /** @see #getWindowLayoutInfo(Context, List) */ private WindowLayoutInfo getWindowLayoutInfo(int displayId, @NonNull WindowConfiguration windowConfiguration, diff --git a/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SampleSidecarImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SampleSidecarImpl.java index a836e05b2d66..56c3bce87d6e 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SampleSidecarImpl.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SampleSidecarImpl.java @@ -17,6 +17,7 @@ package androidx.window.sidecar; import static android.view.Display.DEFAULT_DISPLAY; + import static androidx.window.util.ExtensionHelper.rotateRectToDisplayRotation; import static androidx.window.util.ExtensionHelper.transformToWindowSpaceRect; @@ -25,6 +26,7 @@ import android.app.ActivityThread; import android.app.Application; import android.content.Context; import android.graphics.Rect; +import android.hardware.devicestate.DeviceStateManager; import android.os.Bundle; import android.os.IBinder; @@ -49,10 +51,11 @@ class SampleSidecarImpl extends StubSidecar { SampleSidecarImpl(Context context) { ((Application) context.getApplicationContext()) .registerActivityLifecycleCallbacks(new NotifyOnConfigurationChanged()); - BaseDataProducer<String> settingsFeatureProducer = new RawFoldingFeatureProducer(context); + RawFoldingFeatureProducer settingsFeatureProducer = new RawFoldingFeatureProducer(context); BaseDataProducer<List<CommonFoldingFeature>> foldingFeatureProducer = new DeviceStateManagerFoldingFeatureProducer(context, - settingsFeatureProducer); + settingsFeatureProducer, + context.getSystemService(DeviceStateManager.class)); foldingFeatureProducer.addDataChangedCallback(this::onDisplayFeaturesChanged); } diff --git a/libs/WindowManager/Shell/aconfig/multitasking.aconfig b/libs/WindowManager/Shell/aconfig/multitasking.aconfig index 0967f4e83c74..b61dda4c4e53 100644 --- a/libs/WindowManager/Shell/aconfig/multitasking.aconfig +++ b/libs/WindowManager/Shell/aconfig/multitasking.aconfig @@ -8,14 +8,6 @@ flag { } flag { - name: "enable_desktop_windowing" - namespace: "multitasking" - description: "Enables desktop windowing" - bug: "304778354" - is_fixed_read_only: true -} - -flag { name: "enable_split_contextual" namespace: "multitasking" description: "Enables invoking split contextually" diff --git a/libs/WindowManager/Shell/res/layout/desktop_mode_app_controls_window_decor.xml b/libs/WindowManager/Shell/res/layout/desktop_mode_app_controls_window_decor.xml index d1b1af3e77ab..490f0883fbfb 100644 --- a/libs/WindowManager/Shell/res/layout/desktop_mode_app_controls_window_decor.xml +++ b/libs/WindowManager/Shell/res/layout/desktop_mode_app_controls_window_decor.xml @@ -16,6 +16,7 @@ --> <com.android.wm.shell.windowdecor.WindowDecorLinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/desktop_mode_caption" android:layout_width="match_parent" @@ -27,6 +28,8 @@ android:id="@+id/open_menu_button" android:layout_width="wrap_content" android:layout_height="match_parent" + android:tint="?androidprv:attr/materialColorOnSurface" + android:background="?android:selectableItemBackground" android:orientation="horizontal" android:clickable="true" android:focusable="true" @@ -78,7 +81,9 @@ android:id="@+id/maximize_button_view" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_gravity="end"/> + android:layout_gravity="end" + android:clickable="true" + android:focusable="true" /> <ImageButton android:id="@+id/close_window" @@ -86,9 +91,10 @@ android:layout_height="40dp" android:padding="4dp" android:layout_marginEnd="8dp" + android:tint="?androidprv:attr/materialColorOnSurface" + android:background="?android:selectableItemBackgroundBorderless" android:contentDescription="@string/close_button_text" android:src="@drawable/decor_close_button_dark" android:scaleType="fitCenter" - android:gravity="end" - android:background="@null"/> + android:gravity="end"/> </com.android.wm.shell.windowdecor.WindowDecorLinearLayout>
\ No newline at end of file diff --git a/libs/WindowManager/Shell/res/layout/maximize_menu_button.xml b/libs/WindowManager/Shell/res/layout/maximize_menu_button.xml index bb6efcec1a70..e0057fe64fd2 100644 --- a/libs/WindowManager/Shell/res/layout/maximize_menu_button.xml +++ b/libs/WindowManager/Shell/res/layout/maximize_menu_button.xml @@ -14,7 +14,8 @@ ~ limitations under the License. --> -<merge xmlns:android="http://schemas.android.com/apk/res/android"> +<merge xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"> <ProgressBar android:id="@+id/progress_bar" style="?android:attr/progressBarStyleHorizontal" @@ -30,7 +31,8 @@ android:layout_height="40dp" android:padding="9dp" android:contentDescription="@string/maximize_button_text" + android:tint="?androidprv:attr/materialColorOnSurface" + android:background="?android:selectableItemBackgroundBorderless" android:src="@drawable/decor_desktop_mode_maximize_button_dark" - android:scaleType="fitCenter" - android:background="@drawable/rounded_button"/> + android:scaleType="fitCenter" /> </merge>
\ No newline at end of file diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml index cbfa74e9332b..48e6428524ae 100644 --- a/libs/WindowManager/Shell/res/values/dimen.xml +++ b/libs/WindowManager/Shell/res/values/dimen.xml @@ -101,6 +101,10 @@ <dimen name="split_divider_bar_width">10dp</dimen> <dimen name="split_divider_corner_size">42dp</dimen> + <!-- The distance from the edge of the screen to invoke splitscreen when the user is dragging + an intent that can be launched into split. --> + <dimen name="drag_launchable_intent_edge_margin">48dp</dimen> + <!-- One-Handed Mode --> <!-- Threshold for dragging distance to enable one-handed mode --> <dimen name="gestures_onehanded_drag_threshold">20dp</dimen> diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java index 15350fb19209..96aaf02cb5e3 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java @@ -1799,11 +1799,12 @@ public class BubbleController implements ConfigurationChangeListener, @Override public void removeBubble(Bubble removedBubble) { if (mLayerView != null) { - mLayerView.removeBubble(removedBubble); - if (!mBubbleData.hasBubbles() && !isStackExpanded()) { - mLayerView.setVisibility(INVISIBLE); - removeFromWindowManagerMaybe(); - } + mLayerView.removeBubble(removedBubble, () -> { + if (!mBubbleData.hasBubbles() && !isStackExpanded()) { + mLayerView.setVisibility(INVISIBLE); + removeFromWindowManagerMaybe(); + } + }); } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java index 78a41f759d96..42799d975e1b 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java @@ -242,13 +242,17 @@ public class BubbleBarLayerView extends FrameLayout } /** Removes the given {@code bubble}. */ - public void removeBubble(Bubble bubble) { + public void removeBubble(Bubble bubble, Runnable endAction) { + Runnable cleanUp = () -> { + bubble.cleanupViews(); + endAction.run(); + }; if (mBubbleData.getBubbles().isEmpty()) { // we're removing the last bubble. collapse the expanded view and cleanup bubble views // at the end. - collapse(bubble::cleanupViews); + collapse(cleanUp); } else { - bubble.cleanupViews(); + cleanUp.run(); } } @@ -264,6 +268,9 @@ public class BubbleBarLayerView extends FrameLayout */ public void collapse(@Nullable Runnable endAction) { if (!mIsExpanded) { + if (endAction != null) { + endAction.run(); + } return; } mIsExpanded = false; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipBoundsState.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipBoundsState.java index b57e2d2c6ac2..b87c2f6ebad5 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipBoundsState.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipBoundsState.java @@ -228,6 +228,14 @@ public class PipBoundsState { mExpandedMovementBounds.set(bounds); } + /** Updates the min and max sizes based on the size spec and aspect ratio. */ + public void updateMinMaxSize(float aspectRatio) { + final Size minSize = mSizeSpecSource.getMinSize(aspectRatio); + mMinSize.set(minSize.getWidth(), minSize.getHeight()); + final Size maxSize = mSizeSpecSource.getMaxSize(aspectRatio); + mMaxSize.set(maxSize.getWidth(), maxSize.getHeight()); + } + /** Sets the max possible size for resize. */ public void setMaxSize(int width, int height) { mMaxSize.set(width, height); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMode.java b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMode.java index 8305fa6b0fbf..1071d728a56d 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMode.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMode.java @@ -51,4 +51,6 @@ public interface DesktopMode { /** Called when requested to go to desktop mode from the current focused app. */ void enterDesktop(int displayId); + /** Called when requested to go to fullscreen from the current focused desktop app. */ + void moveFocusedTaskToFullscreen(int displayId); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeEventLogger.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeEventLogger.kt new file mode 100644 index 000000000000..95d47146e834 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeEventLogger.kt @@ -0,0 +1,218 @@ +/* + * Copyright (C) 2024 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.desktopmode + +import com.android.internal.util.FrameworkStatsLog +import com.android.wm.shell.protolog.ShellProtoLogGroup +import com.android.wm.shell.util.KtProtoLog + +/** + * Event logger for logging desktop mode session events + */ +class DesktopModeEventLogger { + /** + * Logs the enter of desktop mode having session id [sessionId] and the reason [enterReason] for + * entering desktop mode + */ + fun logSessionEnter(sessionId: Int, enterReason: EnterReason) { + KtProtoLog.v( + ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE, + "DesktopModeLogger: Logging session enter, session: %s reason: %s", + sessionId, enterReason.name + ) + FrameworkStatsLog.write(DESKTOP_MODE_ATOM_ID, + /* event */ FrameworkStatsLog.DESKTOP_MODE_UICHANGED__EVENT__ENTER, + /* enterReason */ enterReason.reason, + /* exitReason */ 0, + /* session_id */ sessionId) + } + + /** + * Logs the exit of desktop mode having session id [sessionId] and the reason [exitReason] for + * exiting desktop mode + */ + fun logSessionExit(sessionId: Int, exitReason: ExitReason) { + KtProtoLog.v( + ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE, + "DesktopModeLogger: Logging session exit, session: %s reason: %s", + sessionId, exitReason.name + ) + FrameworkStatsLog.write(DESKTOP_MODE_ATOM_ID, + /* event */ FrameworkStatsLog.DESKTOP_MODE_UICHANGED__EVENT__EXIT, + /* enterReason */ 0, + /* exitReason */ exitReason.reason, + /* session_id */ sessionId) + } + + /** + * Logs that the task with update [taskUpdate] was added in the desktop mode session having + * session id [sessionId] + */ + fun logTaskAdded(sessionId: Int, taskUpdate: TaskUpdate) { + KtProtoLog.v( + ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE, + "DesktopModeLogger: Logging task added, session: %s taskId: %s", + sessionId, taskUpdate.instanceId + ) + FrameworkStatsLog.write(DESKTOP_MODE_TASK_UPDATE_ATOM_ID, + /* task_event */ + FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_ADDED, + /* instance_id */ + taskUpdate.instanceId, + /* uid */ + taskUpdate.uid, + /* task_height */ + taskUpdate.taskHeight, + /* task_width */ + taskUpdate.taskWidth, + /* task_x */ + taskUpdate.taskX, + /* task_y */ + taskUpdate.taskY, + /* session_id */ + sessionId) + } + + /** + * Logs that the task with update [taskUpdate] was removed in the desktop mode session having + * session id [sessionId] + */ + fun logTaskRemoved(sessionId: Int, taskUpdate: TaskUpdate) { + KtProtoLog.v( + ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE, + "DesktopModeLogger: Logging task remove, session: %s taskId: %s", + sessionId, taskUpdate.instanceId + ) + FrameworkStatsLog.write(DESKTOP_MODE_TASK_UPDATE_ATOM_ID, + /* task_event */ + FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_REMOVED, + /* instance_id */ + taskUpdate.instanceId, + /* uid */ + taskUpdate.uid, + /* task_height */ + taskUpdate.taskHeight, + /* task_width */ + taskUpdate.taskWidth, + /* task_x */ + taskUpdate.taskX, + /* task_y */ + taskUpdate.taskY, + /* session_id */ + sessionId) + } + + /** + * Logs that the task with update [taskUpdate] had it's info changed in the desktop mode session + * having session id [sessionId] + */ + fun logTaskInfoChanged(sessionId: Int, taskUpdate: TaskUpdate) { + KtProtoLog.v( + ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE, + "DesktopModeLogger: Logging task info changed, session: %s taskId: %s", + sessionId, taskUpdate.instanceId + ) + FrameworkStatsLog.write(DESKTOP_MODE_TASK_UPDATE_ATOM_ID, + /* task_event */ + FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_INFO_CHANGED, + /* instance_id */ + taskUpdate.instanceId, + /* uid */ + taskUpdate.uid, + /* task_height */ + taskUpdate.taskHeight, + /* task_width */ + taskUpdate.taskWidth, + /* task_x */ + taskUpdate.taskX, + /* task_y */ + taskUpdate.taskY, + /* session_id */ + sessionId) + } + + companion object { + data class TaskUpdate( + val instanceId: Int, + val uid: Int, + val taskHeight: Int = Int.MIN_VALUE, + val taskWidth: Int = Int.MIN_VALUE, + val taskX: Int = Int.MIN_VALUE, + val taskY: Int = Int.MIN_VALUE, + ) + + /** + * Enum EnterReason mapped to the EnterReason definition in + * stats/atoms/desktopmode/desktopmode_extensions_atoms.proto + */ + enum class EnterReason(val reason: Int) { + UNKNOWN_ENTER( + FrameworkStatsLog.DESKTOP_MODE_UICHANGED__ENTER_REASON__UNKNOWN_ENTER + ), + OVERVIEW( + FrameworkStatsLog.DESKTOP_MODE_UICHANGED__ENTER_REASON__OVERVIEW + ), + APP_HANDLE_DRAG( + FrameworkStatsLog.DESKTOP_MODE_UICHANGED__ENTER_REASON__APP_HANDLE_DRAG + ), + APP_HANDLE_MENU_BUTTON( + FrameworkStatsLog.DESKTOP_MODE_UICHANGED__ENTER_REASON__APP_HANDLE_MENU_BUTTON + ), + APP_FREEFORM_INTENT( + FrameworkStatsLog.DESKTOP_MODE_UICHANGED__ENTER_REASON__APP_FREEFORM_INTENT + ), + KEYBOARD_SHORTCUT_ENTER( + FrameworkStatsLog.DESKTOP_MODE_UICHANGED__ENTER_REASON__KEYBOARD_SHORTCUT_ENTER + ), + SCREEN_ON( + FrameworkStatsLog.DESKTOP_MODE_UICHANGED__ENTER_REASON__SCREEN_ON + ); + } + + /** + * Enum ExitReason mapped to the ExitReason definition in + * stats/atoms/desktopmode/desktopmode_extensions_atoms.proto + */ + enum class ExitReason(val reason: Int) { + UNKNOWN_EXIT( + FrameworkStatsLog.DESKTOP_MODE_UICHANGED__EXIT_REASON__UNKNOWN_EXIT + ), + DRAG_TO_EXIT( + FrameworkStatsLog.DESKTOP_MODE_UICHANGED__EXIT_REASON__DRAG_TO_EXIT + ), + APP_HANDLE_MENU_BUTTON_EXIT( + FrameworkStatsLog.DESKTOP_MODE_UICHANGED__EXIT_REASON__APP_HANDLE_MENU_BUTTON_EXIT + ), + KEYBOARD_SHORTCUT_EXIT( + FrameworkStatsLog.DESKTOP_MODE_UICHANGED__EXIT_REASON__KEYBOARD_SHORTCUT_EXIT + ), + RETURN_HOME_OR_OVERVIEW( + FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__RETURN_HOME + ), + TASK_FINISHED( + FrameworkStatsLog.DESKTOP_MODE_UICHANGED__EXIT_REASON__TASK_FINISHED + ), + SCREEN_OFF( + FrameworkStatsLog.DESKTOP_MODE_UICHANGED__EXIT_REASON__SCREEN_OFF + ) + } + + private const val DESKTOP_MODE_ATOM_ID = FrameworkStatsLog.DESKTOP_MODE_UI_CHANGED + private const val DESKTOP_MODE_TASK_UPDATE_ATOM_ID = + FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE + } +}
\ No newline at end of file diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeStatus.java b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeStatus.java index 88949b2a5acd..22ba70860587 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeStatus.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeStatus.java @@ -18,21 +18,13 @@ package com.android.wm.shell.desktopmode; import android.os.SystemProperties; -import com.android.wm.shell.Flags; +import com.android.window.flags.Flags; /** * Constants for desktop mode feature */ public class DesktopModeStatus { - private static final boolean ENABLE_DESKTOP_WINDOWING = Flags.enableDesktopWindowing(); - - /** - * Flag to indicate whether desktop mode proto is available on the device - */ - private static final boolean IS_PROTO2_ENABLED = SystemProperties.getBoolean( - "persist.wm.debug.desktop_mode_2", false); - /** * Flag to indicate whether task resizing is veiled. */ @@ -75,16 +67,10 @@ public class DesktopModeStatus { "persist.wm.debug.desktop_use_rounded_corners", true); /** - * Return {@code true} is desktop windowing proto 2 is enabled + * Return {@code true} if desktop windowing is enabled */ public static boolean isEnabled() { - // Check for aconfig flag first - if (ENABLE_DESKTOP_WINDOWING) { - return true; - } - // Fall back to sysprop flag - // TODO(b/304778354): remove sysprop once desktop aconfig flag supports dynamic overriding - return IS_PROTO2_ENABLED; + return Flags.enableDesktopWindowingMode(); } /** diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt index dcffb2d3e8fa..b9d0342137c5 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt @@ -381,6 +381,18 @@ class DesktopTasksController( } } + /** Enter fullscreen by moving the focused freeform task in given `displayId` to fullscreen. */ + fun enterFullscreen(displayId: Int) { + if (DesktopModeStatus.isEnabled()) { + shellTaskOrganizer + .getRunningTasks(displayId) + .find { taskInfo -> + taskInfo.isFocused && taskInfo.windowingMode == WINDOWING_MODE_FREEFORM + } + ?.let { moveToFullscreenWithAnimation(it, it.positionInParent) } + } + } + /** Move a desktop app to split screen. */ fun moveToSplit(task: RunningTaskInfo) { KtProtoLog.v( @@ -1108,6 +1120,12 @@ class DesktopTasksController( this@DesktopTasksController.enterDesktop(displayId) } } + + override fun moveFocusedTaskToFullscreen(displayId: Int) { + mainExecutor.execute { + this@DesktopTasksController.enterFullscreen(displayId) + } + } } /** The interface for calls from outside the host process. */ diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java index 1afbdf90eac0..7da1b23dd5b1 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java @@ -59,7 +59,6 @@ import androidx.annotation.BinderThread; import androidx.annotation.NonNull; import androidx.annotation.VisibleForTesting; -import com.android.internal.logging.InstanceId; import com.android.internal.logging.UiEventLogger; import com.android.internal.protolog.common.ProtoLog; import com.android.launcher3.icons.IconProvider; @@ -316,12 +315,11 @@ public class DragAndDropController implements RemoteCallable<DragAndDropControll return false; } // TODO(b/290391688): Also update the session data with task stack changes - InstanceId loggerSessionId = mLogger.logStart(event); - pd.activeDragCount++; - pd.dragSession = new DragSession(mContext, ActivityTaskManager.getInstance(), + pd.dragSession = new DragSession(ActivityTaskManager.getInstance(), mDisplayController.getDisplayLayout(displayId), event.getClipData()); pd.dragSession.update(); - pd.dragLayout.prepare(pd.dragSession, loggerSessionId); + pd.activeDragCount++; + pd.dragLayout.prepare(pd.dragSession, mLogger.logStart(pd.dragSession)); setDropTargetWindowVisibility(pd, View.VISIBLE); notifyListeners(l -> { l.onDragStarted(); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropEventLogger.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropEventLogger.java index 2a7dd5aeb341..75b126c47690 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropEventLogger.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropEventLogger.java @@ -53,17 +53,21 @@ public class DragAndDropEventLogger { /** * Logs the start of a drag. */ - public InstanceId logStart(DragEvent event) { - final ClipDescription description = event.getClipDescription(); - final ClipData data = event.getClipData(); - final ClipData.Item item = data.getItemAt(0); - mInstanceId = item.getIntent().getParcelableExtra( - ClipDescription.EXTRA_LOGGING_INSTANCE_ID); + public InstanceId logStart(DragSession session) { + mInstanceId = session.appData != null + ? session.appData.getParcelableExtra(ClipDescription.EXTRA_LOGGING_INSTANCE_ID, + InstanceId.class) + : null; if (mInstanceId == null) { mInstanceId = mIdSequence.newInstanceId(); } - mActivityInfo = item.getActivityInfo(); - log(getStartEnum(description), mActivityInfo); + mActivityInfo = session.activityInfo; + if (session.appData != null) { + log(getStartEnum(session.getClipDescription()), mActivityInfo); + } else { + // TODO(b/255649902): Update this once we have a new enum + log(DragAndDropUiEventEnum.GLOBAL_APP_DRAG_START_ACTIVITY, mActivityInfo); + } return mInstanceId; } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java index a31a773a76a0..eb82da8a8e9f 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java @@ -29,6 +29,8 @@ import static android.content.Intent.EXTRA_PACKAGE_NAME; import static android.content.Intent.EXTRA_SHORTCUT_ID; import static android.content.Intent.EXTRA_TASK_ID; import static android.content.Intent.EXTRA_USER; +import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK; +import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT; import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT; @@ -52,9 +54,11 @@ import android.content.pm.LauncherApps; import android.graphics.Insets; import android.graphics.Rect; import android.graphics.RectF; +import android.os.Build; import android.os.Bundle; import android.os.RemoteException; import android.os.UserHandle; +import android.util.Log; import android.util.Slog; import androidx.annotation.IntDef; @@ -63,8 +67,10 @@ import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; import com.android.internal.logging.InstanceId; +import com.android.internal.protolog.common.ProtoLog; import com.android.wm.shell.R; import com.android.wm.shell.common.split.SplitScreenConstants.SplitPosition; +import com.android.wm.shell.protolog.ShellProtoLogGroup; import com.android.wm.shell.splitscreen.SplitScreenController; import java.lang.annotation.Retention; @@ -104,7 +110,9 @@ public class DragAndDropPolicy { void start(DragSession session, InstanceId loggerSessionId) { mLoggerSessionId = loggerSessionId; mSession = session; - RectF disallowHitRegion = (RectF) mSession.dragData.getExtra(EXTRA_DISALLOW_HIT_REGION); + RectF disallowHitRegion = mSession.appData != null + ? (RectF) mSession.appData.getExtra(EXTRA_DISALLOW_HIT_REGION) + : null; if (disallowHitRegion == null) { mDisallowHitRegion.setEmpty(); } else { @@ -223,7 +231,7 @@ public class DragAndDropPolicy { } @VisibleForTesting - void handleDrop(Target target, ClipData data) { + void handleDrop(Target target) { if (target == null || !mTargets.contains(target)) { return; } @@ -238,41 +246,77 @@ public class DragAndDropPolicy { mSplitScreen.onDroppedToSplit(position, mLoggerSessionId); } - final ClipDescription description = data.getDescription(); - final Intent dragData = mSession.dragData; - startClipDescription(description, dragData, position); + if (mSession.appData != null) { + launchApp(mSession, position); + } else { + launchIntent(mSession, position); + } } - private void startClipDescription(ClipDescription description, Intent intent, - @SplitPosition int position) { + /** + * Launches an app provided by SysUI. + */ + private void launchApp(DragSession session, @SplitPosition int position) { + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DRAG_AND_DROP, "Launching app data at position=%d", + position); + final ClipDescription description = session.getClipDescription(); final boolean isTask = description.hasMimeType(MIMETYPE_APPLICATION_TASK); final boolean isShortcut = description.hasMimeType(MIMETYPE_APPLICATION_SHORTCUT); final ActivityOptions baseActivityOpts = ActivityOptions.makeBasic(); baseActivityOpts.setDisallowEnterPictureInPictureWhileLaunching(true); final Bundle opts = baseActivityOpts.toBundle(); - if (intent.hasExtra(EXTRA_ACTIVITY_OPTIONS)) { - opts.putAll(intent.getBundleExtra(EXTRA_ACTIVITY_OPTIONS)); + if (session.appData.hasExtra(EXTRA_ACTIVITY_OPTIONS)) { + opts.putAll(session.appData.getBundleExtra(EXTRA_ACTIVITY_OPTIONS)); } // Put BAL flags to avoid activity start aborted. opts.putBoolean(KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWED, true); opts.putBoolean(KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWED_BY_PERMISSION, true); - final UserHandle user = intent.getParcelableExtra(EXTRA_USER); + final UserHandle user = session.appData.getParcelableExtra(EXTRA_USER); if (isTask) { - final int taskId = intent.getIntExtra(EXTRA_TASK_ID, INVALID_TASK_ID); + final int taskId = session.appData.getIntExtra(EXTRA_TASK_ID, INVALID_TASK_ID); mStarter.startTask(taskId, position, opts); } else if (isShortcut) { - final String packageName = intent.getStringExtra(EXTRA_PACKAGE_NAME); - final String id = intent.getStringExtra(EXTRA_SHORTCUT_ID); + final String packageName = session.appData.getStringExtra(EXTRA_PACKAGE_NAME); + final String id = session.appData.getStringExtra(EXTRA_SHORTCUT_ID); mStarter.startShortcut(packageName, id, position, opts, user); } else { - final PendingIntent launchIntent = intent.getParcelableExtra(EXTRA_PENDING_INTENT); + final PendingIntent launchIntent = + session.appData.getParcelableExtra(EXTRA_PENDING_INTENT); + if (Build.IS_DEBUGGABLE) { + if (!user.equals(launchIntent.getCreatorUserHandle())) { + Log.e(TAG, "Expected app intent's EXTRA_USER to match pending intent user"); + } + } mStarter.startIntent(launchIntent, user.getIdentifier(), null /* fillIntent */, position, opts); } } /** + * Launches an intent sender provided by an application. + */ + private void launchIntent(DragSession session, @SplitPosition int position) { + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DRAG_AND_DROP, "Launching intent at position=%d", + position); + final ActivityOptions baseActivityOpts = ActivityOptions.makeBasic(); + baseActivityOpts.setDisallowEnterPictureInPictureWhileLaunching(true); + // TODO(b/255649902): Rework this so that SplitScreenController can always use the options + // instead of a fillInIntent since it's assuming that the PendingIntent is mutable + baseActivityOpts.setPendingIntentLaunchFlags(FLAG_ACTIVITY_NEW_TASK + | FLAG_ACTIVITY_MULTIPLE_TASK); + + final Bundle opts = baseActivityOpts.toBundle(); + // Put BAL flags to avoid activity start aborted. + opts.putBoolean(KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWED, true); + opts.putBoolean(KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWED_BY_PERMISSION, true); + + mStarter.startIntent(session.launchableIntent, + session.launchableIntent.getCreatorUserHandle().getIdentifier(), + null /* fillIntent */, position, opts); + } + + /** * Interface for actually committing the task launches. */ public interface Starter { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java index 619f624ff3bc..ecb53dc17a48 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java @@ -22,6 +22,8 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; import static android.content.pm.ActivityInfo.CONFIG_ASSETS_PATHS; import static android.content.pm.ActivityInfo.CONFIG_UI_MODE; import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; +import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME; +import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION; import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT; import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT; @@ -38,12 +40,15 @@ import android.app.ActivityManager; import android.app.StatusBarManager; import android.content.Context; import android.content.res.Configuration; +import android.content.res.Resources; import android.graphics.Color; import android.graphics.Insets; import android.graphics.Rect; +import android.graphics.Region; import android.graphics.drawable.Drawable; import android.view.DragEvent; import android.view.SurfaceControl; +import android.view.ViewTreeObserver; import android.view.WindowInsets; import android.view.WindowInsets.Type; import android.widget.LinearLayout; @@ -65,7 +70,8 @@ import java.util.ArrayList; /** * Coordinates the visible drop targets for the current drag within a single display. */ -public class DragLayout extends LinearLayout { +public class DragLayout extends LinearLayout + implements ViewTreeObserver.OnComputeInternalInsetsListener { // While dragging the status bar is hidden. private static final int HIDE_STATUS_BAR_FLAGS = StatusBarManager.DISABLE_NOTIFICATION_ICONS @@ -90,7 +96,9 @@ public class DragLayout extends LinearLayout { private int mDisplayMargin; private int mDividerSize; + private int mLaunchIntentEdgeMargin; private Insets mInsets = Insets.NONE; + private Region mTouchableRegion; private boolean mIsShowing; private boolean mHasDropped; @@ -106,10 +114,11 @@ public class DragLayout extends LinearLayout { mStatusBarManager = context.getSystemService(StatusBarManager.class); mLastConfiguration.setTo(context.getResources().getConfiguration()); - mDisplayMargin = context.getResources().getDimensionPixelSize( - R.dimen.drop_layout_display_margin); - mDividerSize = context.getResources().getDimensionPixelSize( - R.dimen.split_divider_bar_width); + final Resources res = context.getResources(); + mDisplayMargin = res.getDimensionPixelSize(R.dimen.drop_layout_display_margin); + mDividerSize = res.getDimensionPixelSize(R.dimen.split_divider_bar_width); + mLaunchIntentEdgeMargin = + res.getDimensionPixelSize(R.dimen.drag_launchable_intent_edge_margin); // Always use LTR because we assume dropZoneView1 is on the left and 2 is on the right when // showing the highlight. @@ -131,6 +140,66 @@ public class DragLayout extends LinearLayout { } @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + mTouchableRegion = Region.obtain(); + getViewTreeObserver().addOnComputeInternalInsetsListener(this); + } + + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + getViewTreeObserver().removeOnComputeInternalInsetsListener(this); + mTouchableRegion.recycle(); + } + + @Override + public void onComputeInternalInsets(ViewTreeObserver.InternalInsetsInfo inOutInfo) { + if (mSession != null && mSession.launchableIntent != null) { + inOutInfo.touchableRegion.set(mTouchableRegion); + inOutInfo.setTouchableInsets(TOUCHABLE_INSETS_REGION); + } + } + + @Override + protected void onLayout(boolean changed, int l, int t, int r, int b) { + super.onLayout(changed, l, t, r, b); + updateTouchableRegion(); + } + + /** + * Updates the touchable region, this should be called after any configuration changes have + * been applied. + */ + private void updateTouchableRegion() { + mTouchableRegion.setEmpty(); + if (mSession != null && mSession.launchableIntent != null) { + final int width = getMeasuredWidth(); + final int height = getMeasuredHeight(); + if (mIsLeftRightSplit) { + mTouchableRegion.union( + new Rect(0, 0, mInsets.left + mLaunchIntentEdgeMargin, height)); + mTouchableRegion.union( + new Rect(width - mInsets.right - mLaunchIntentEdgeMargin, 0, width, + height)); + } else { + mTouchableRegion.union( + new Rect(0, 0, width, mInsets.top + mLaunchIntentEdgeMargin)); + mTouchableRegion.union( + new Rect(0, height - mInsets.bottom - mLaunchIntentEdgeMargin, width, + height)); + } + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DRAG_AND_DROP, + "Updating drag layout width=%d height=%d touchable region=%s", + width, height, mTouchableRegion); + + // Reapply insets to update the touchable region + requestApplyInsets(); + } + } + + + @Override public WindowInsets onApplyWindowInsets(WindowInsets insets) { mInsets = insets.getInsets(Type.tappableElement() | Type.displayCutout()); recomputeDropTargets(); @@ -164,6 +233,7 @@ public class DragLayout extends LinearLayout { mDropZoneView2.onThemeChange(); } mLastConfiguration.setTo(newConfig); + requestLayout(); } private void updateContainerMarginsForSingleTask() { @@ -242,6 +312,7 @@ public class DragLayout extends LinearLayout { mSplitScreenController.getStageBounds(topOrLeftBounds, bottomOrRightBounds); updateDropZoneSizes(topOrLeftBounds, bottomOrRightBounds); } + requestLayout(); } private void updateDropZoneSizesForSingleTask() { @@ -392,7 +463,7 @@ public class DragLayout extends LinearLayout { mHasDropped = true; // Process the drop - mPolicy.handleDrop(mCurrentTarget, event.getClipData()); + mPolicy.handleDrop(mCurrentTarget); // Start animating the drop UI out with the drag surface hide(event, dropCompleteCallback); @@ -505,5 +576,7 @@ public class DragLayout extends LinearLayout { pw.println(innerPrefix + "mIsShowing=" + mIsShowing); pw.println(innerPrefix + "mHasDropped=" + mHasDropped); pw.println(innerPrefix + "mCurrentTarget=" + mCurrentTarget); + pw.println(innerPrefix + "mInsets=" + mInsets); + pw.println(innerPrefix + "mTouchableRegion=" + mTouchableRegion); } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragSession.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragSession.java index 353d702e5bc4..8f1bc59af1ef 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragSession.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragSession.java @@ -21,12 +21,15 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import android.app.ActivityManager; import android.app.ActivityTaskManager; +import android.app.PendingIntent; import android.app.WindowConfiguration; import android.content.ClipData; -import android.content.Context; +import android.content.ClipDescription; import android.content.Intent; import android.content.pm.ActivityInfo; +import androidx.annotation.Nullable; + import com.android.wm.shell.common.DisplayLayout; import java.util.List; @@ -39,7 +42,18 @@ public class DragSession { private final ClipData mInitialDragData; final DisplayLayout displayLayout; - Intent dragData; + // The activity info associated with the activity in the appData or the launchableIntent + @Nullable + ActivityInfo activityInfo; + // The intent bundle that includes data about an app-type drag that is started by + // Launcher/SysUI. Only one of appDragData OR launchableIntent will be non-null for a session. + @Nullable + Intent appData; + // A launchable intent that is specified in the ClipData directly. + // Only one of appDragData OR launchableIntent will be non-null for a session. + @Nullable + PendingIntent launchableIntent; + // Stores the current running task at the time that the drag was initiated ActivityManager.RunningTaskInfo runningTaskInfo; @WindowConfiguration.WindowingMode int runningTaskWinMode = WINDOWING_MODE_UNDEFINED; @@ -47,7 +61,7 @@ public class DragSession { int runningTaskActType = ACTIVITY_TYPE_STANDARD; boolean dragItemSupportsSplitscreen; - DragSession(Context context, ActivityTaskManager activityTaskManager, + DragSession(ActivityTaskManager activityTaskManager, DisplayLayout dispLayout, ClipData data) { mActivityTaskManager = activityTaskManager; mInitialDragData = data; @@ -55,6 +69,14 @@ public class DragSession { } /** + * Returns the clip description associated with the drag. + * @return + */ + ClipDescription getClipDescription() { + return mInitialDragData.getDescription(); + } + + /** * Updates the session data based on the current state of the system. */ void update() { @@ -67,9 +89,11 @@ public class DragSession { runningTaskActType = task.getActivityType(); } - final ActivityInfo info = mInitialDragData.getItemAt(0).getActivityInfo(); - dragItemSupportsSplitscreen = info == null - || ActivityInfo.isResizeableMode(info.resizeMode); - dragData = mInitialDragData.getItemAt(0).getIntent(); + activityInfo = mInitialDragData.getItemAt(0).getActivityInfo(); + // TODO: This should technically check & respect config_supportsNonResizableMultiWindow + dragItemSupportsSplitscreen = activityInfo == null + || ActivityInfo.isResizeableMode(activityInfo.resizeMode); + appData = mInitialDragData.getItemAt(0).getIntent(); + launchableIntent = DragUtils.getLaunchIntent(mInitialDragData); } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragUtils.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragUtils.java index f7bcc9477aa1..24f8e186bf76 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragUtils.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragUtils.java @@ -36,8 +36,21 @@ public class DragUtils { * Returns whether we can handle this particular drag. */ public static boolean canHandleDrag(DragEvent event) { - return event.getClipData().getItemCount() > 0 - && (isAppDrag(event.getClipDescription())); + if (event.getClipData().getItemCount() <= 0) { + // No clip data, ignore this drag + return false; + } + if (isAppDrag(event.getClipDescription())) { + // Clip data contains an app drag initiated from SysUI, handle it + return true; + } + if (com.android.window.flags.Flags.delegateUnhandledDrags() + && getLaunchIntent(event) != null) { + // Clip data contains a launchable intent drag, handle it + return true; + } + // Otherwise ignore + return false; } /** diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java index e018ecc0f7e3..6a1a62ea30a1 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java @@ -1037,6 +1037,7 @@ public class PipTransition extends PipTransitionController { private void computeEnterPipRotatedBounds(int rotationDelta, int startRotation, int endRotation, TaskInfo taskInfo, Rect outDestinationBounds, @Nullable Rect outSourceHintRect) { mPipDisplayLayoutState.rotateTo(endRotation); + mPipBoundsState.updateMinMaxSize(mPipBoundsState.getAspectRatio()); final Rect displayBounds = mPipDisplayLayoutState.getDisplayBounds(); outDestinationBounds.set(mPipBoundsAlgorithm.getEntryDestinationBounds()); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java index 46840773cfc6..2cdec81d77ac 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java @@ -976,8 +976,16 @@ public class PipController implements PipTransitionController.PipTransitionCallb mPipBoundsState.addNamedUnrestrictedKeepClearArea(LAUNCHER_KEEP_CLEAR_AREA_TAG, hotseatKeepClearArea); onDisplayRotationChangedNotInPip(mContext, launcherRotation); + // cache current min/max size + Point minSize = mPipBoundsState.getMinSize(); + Point maxSize = mPipBoundsState.getMaxSize(); + mPipBoundsState.updateMinMaxSize(pictureInPictureParams.getAspectRatioFloat()); final Rect entryBounds = mPipTaskOrganizer.startSwipePipToHome(componentName, activityInfo, pictureInPictureParams); + // restore min/max size, as this is referenced later in OnDisplayChangingListener and needs + // to reflect the pre-rotation state for it to work + mPipBoundsState.setMinSize(minSize.x, minSize.y); + mPipBoundsState.setMaxSize(maxSize.x, maxSize.y); // sync mPipBoundsState with the newly calculated bounds. mPipBoundsState.setNormalBounds(entryBounds); return entryBounds; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java index e7dd31cc1fa9..c1adfffce074 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java @@ -467,17 +467,11 @@ public class PipTouchHandler { } private void updatePinchResizeSizeConstraints(float aspectRatio) { - final int minWidth, minHeight, maxWidth, maxHeight; - - minWidth = mSizeSpecSource.getMinSize(aspectRatio).getWidth(); - minHeight = mSizeSpecSource.getMinSize(aspectRatio).getHeight(); - maxWidth = mSizeSpecSource.getMaxSize(aspectRatio).getWidth(); - maxHeight = mSizeSpecSource.getMaxSize(aspectRatio).getHeight(); - - mPipResizeGestureHandler.updateMinSize(minWidth, minHeight); - mPipResizeGestureHandler.updateMaxSize(maxWidth, maxHeight); - mPipBoundsState.setMaxSize(maxWidth, maxHeight); - mPipBoundsState.setMinSize(minWidth, minHeight); + mPipBoundsState.updateMinMaxSize(aspectRatio); + mPipResizeGestureHandler.updateMinSize(mPipBoundsState.getMinSize().x, + mPipBoundsState.getMinSize().y); + mPipResizeGestureHandler.updateMaxSize(mPipBoundsState.getMaxSize().x, + mPipBoundsState.getMaxSize().y); } /** diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java index 53dd981755d2..952e2d4b3b9a 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java @@ -476,7 +476,9 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, } public void goToFullscreenFromSplit() { - mStageCoordinator.goToFullscreenFromSplit(); + if (mStageCoordinator.isSplitActive()) { + mStageCoordinator.goToFullscreenFromSplit(); + } } /** Move the specified task to fullscreen, regardless of focus state. */ @@ -806,6 +808,9 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, @Override public void startIntent(PendingIntent intent, int userId1, @Nullable Intent fillInIntent, @SplitPosition int position, @Nullable Bundle options) { + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, + "startIntent(): intent=%s user=%d fillInIntent=%s position=%d", intent, userId1, + fillInIntent, position); // Flag this as a no-user-action launch to prevent sending user leaving event to the current // top activity since it's going to be put into another side of the split. This prevents the // current top activity from going into pip mode due to user leaving event. @@ -824,6 +829,8 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, .map(recentTasks -> recentTasks.findTaskInBackground(component, userId1)) .orElse(null); if (taskInfo != null) { + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, + "Found suitable background task=%s", taskInfo); if (ENABLE_SHELL_TRANSITIONS) { mStageCoordinator.startTask(taskInfo.taskId, position, options); } else { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java index fa14b4c64fe0..2933cf48614a 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java @@ -3396,6 +3396,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, final int stageType = isMainStage ? STAGE_TYPE_MAIN : STAGE_TYPE_SIDE; final WindowContainerTransaction wct = new WindowContainerTransaction(); prepareExitSplitScreen(stageType, wct); + clearSplitPairedInRecents(EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW); mSplitTransitions.startDismissTransition(wct, StageCoordinator.this, stageType, EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW); Log.w(TAG, splitFailureMessage("onNoLongerSupportMultiWindow", diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java index c70a8219489e..9130edfa9f26 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java @@ -470,10 +470,12 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler { } final float cornerRadius; - if (a.hasRoundedCorners() && isTask) { - // hasRoundedCorners is currently only enabled for tasks + if (a.hasRoundedCorners()) { + final int displayId = isTask ? change.getTaskInfo().displayId + : info.getRoot(TransitionUtil.rootIndexFor(change, info)) + .getDisplayId(); final Context displayContext = - mDisplayController.getDisplayContext(change.getTaskInfo().displayId); + mDisplayController.getDisplayContext(displayId); cornerRadius = displayContext == null ? 0 : ScreenDecorationsUtils.getWindowCornerRadius(displayContext); } else { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java index c1406d052195..caa894fcbbc7 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java @@ -22,8 +22,10 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import static android.view.InputDevice.SOURCE_TOUCHSCREEN; +import static android.view.MotionEvent.ACTION_CANCEL; import static android.view.MotionEvent.ACTION_HOVER_ENTER; import static android.view.MotionEvent.ACTION_HOVER_EXIT; +import static android.view.MotionEvent.ACTION_UP; import static android.view.WindowInsets.Type.statusBars; import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT; @@ -59,7 +61,6 @@ import android.view.MotionEvent; import android.view.SurfaceControl; import android.view.SurfaceControl.Transaction; import android.view.View; -import android.view.ViewConfiguration; import android.window.WindowContainerToken; import android.window.WindowContainerTransaction; @@ -321,8 +322,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { private final GestureDetector mGestureDetector; private boolean mIsDragging; + private boolean mTouchscreenInUse; private boolean mHasLongClicked; - private boolean mShouldClick; private int mDragPointerId = -1; private final Runnable mCloseMaximizeWindowRunnable; @@ -343,6 +344,10 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { @Override public void onClick(View v) { + if (mIsDragging) { + mIsDragging = false; + return; + } final DesktopModeWindowDecoration decoration = mWindowDecorByTaskId.get(mTaskId); final int id = v.getId(); if (id == R.id.close_window) { @@ -421,6 +426,10 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { @Override public boolean onTouch(View v, MotionEvent e) { final int id = v.getId(); + if ((e.getSource() & SOURCE_TOUCHSCREEN) == SOURCE_TOUCHSCREEN) { + mTouchscreenInUse = e.getActionMasked() != ACTION_UP + && e.getActionMasked() != ACTION_CANCEL; + } if (id != R.id.caption_handle && id != R.id.desktop_mode_caption && id != R.id.open_menu_button && id != R.id.close_window && id != R.id.maximize_window) { @@ -432,31 +441,19 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { if (!mHasLongClicked && id != R.id.maximize_window) { decoration.closeMaximizeMenuIfNeeded(e); } - - final long eventDuration = e.getEventTime() - e.getDownTime(); - final boolean isTouchScreen = - (e.getSource() & SOURCE_TOUCHSCREEN) == SOURCE_TOUCHSCREEN; - final boolean shouldLongClick = isTouchScreen && id == R.id.maximize_window - && !mIsDragging && !mHasLongClicked - && eventDuration >= ViewConfiguration.getLongPressTimeout(); - if (shouldLongClick) { - v.performLongClick(); - mHasLongClicked = true; - return true; - } - return mDragDetector.onMotionEvent(v, e); } @Override public boolean onLongClick(View v) { final int id = v.getId(); - if (id == R.id.maximize_window) { + if (id == R.id.maximize_window && mTouchscreenInUse) { final DesktopModeWindowDecoration decoration = mWindowDecorByTaskId.get(mTaskId); moveTaskToFront(decoration.mTaskInfo); if (decoration.isMaximizeMenuActive()) { decoration.closeMaximizeMenu(); } else { + mHasLongClicked = true; decoration.createMaximizeMenu(); } return true; @@ -515,11 +512,9 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { if (mGestureDetector.onTouchEvent(e)) { return true; } - if (e.getActionMasked() == MotionEvent.ACTION_CANCEL) { - // If a motion event is cancelled, reset mShouldClick so a click is not accidentally - // performed. - mShouldClick = false; - } + final int id = v.getId(); + final boolean touchingButton = (id == R.id.close_window || id == R.id.maximize_window + || id == R.id.open_menu_button); switch (e.getActionMasked()) { case MotionEvent.ACTION_DOWN: { mDragPointerId = e.getPointerId(0); @@ -527,12 +522,12 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { 0 /* ctrlType */, e.getRawX(0), e.getRawY(0)); mIsDragging = false; - mShouldClick = true; mHasLongClicked = false; - return true; + // Do not consume input event if a button is touched, otherwise it would + // prevent the button's ripple effect from showing. + return !touchingButton; } case MotionEvent.ACTION_MOVE: { - mShouldClick = false; // If a decor's resize drag zone is active, don't also try to reposition it. if (decoration.isHandlingDragResize()) break; decoration.closeMaximizeMenu(); @@ -553,11 +548,6 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { case MotionEvent.ACTION_CANCEL: { final boolean wasDragging = mIsDragging; if (!wasDragging) { - if (mShouldClick && v != null && !mHasLongClicked) { - v.performClick(); - mShouldClick = false; - return true; - } return false; } if (e.findPointerIndex(mDragPointerId) == -1) { @@ -576,8 +566,15 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { position, new PointF(e.getRawX(dragPointerIdx), e.getRawY(dragPointerIdx)), newTaskBounds)); - mIsDragging = false; - return true; + if (touchingButton && !mHasLongClicked) { + // We need the input event to not be consumed here to end the ripple + // effect on the touched button. We will reset drag state in the ensuing + // onClick call that results. + return false; + } else { + mIsDragging = false; + return true; + } } } return true; diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientation.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientation.kt index ac7380f83233..9a1bd267ea1f 100644 --- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientation.kt +++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientation.kt @@ -206,6 +206,18 @@ class EnterPipToOtherOrientation(flicker: LegacyFlickerTest) : PipTransition(fli } } + @Presubmit + @Test + fun pipLayerRemainInsideVisibleBounds() { + // during the transition we assert the center point is within the display bounds, since it + // might go outside of bounds as we resize from landscape fullscreen to destination bounds, + // and once the animation is over we assert that it's fully within the display bounds, at + // which point the device also performs orientation change from landscape to portrait + flicker.assertLayersVisibleRegion(pipApp.or(ComponentNameMatcher.PIP_CONTENT_OVERLAY)) { + regionsCenterPointInside(startingBounds).then().coversAtMost(endingBounds) + } + } + /** {@inheritDoc} */ @FlakyTest(bugId = 267424412) @Test diff --git a/libs/WindowManager/Shell/tests/unittest/Android.bp b/libs/WindowManager/Shell/tests/unittest/Android.bp index 8c4711603904..32c070305e05 100644 --- a/libs/WindowManager/Shell/tests/unittest/Android.bp +++ b/libs/WindowManager/Shell/tests/unittest/Android.bp @@ -24,7 +24,10 @@ package { android_test { name: "WMShellUnitTests", - + defaults: [ + // For ExtendedMockito dependencies. + "modules-utils-testable-device-config-defaults", + ], srcs: [ "**/*.java", "**/*.kt", diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeEventLoggerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeEventLoggerTest.kt new file mode 100644 index 000000000000..4548fcb06c55 --- /dev/null +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeEventLoggerTest.kt @@ -0,0 +1,169 @@ +/* + * Copyright (C) 2024 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.desktopmode + +import com.android.dx.mockito.inline.extended.ExtendedMockito +import com.android.internal.util.FrameworkStatsLog +import com.android.modules.utils.testing.ExtendedMockitoRule +import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.EnterReason +import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.ExitReason +import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.TaskUpdate +import kotlinx.coroutines.runBlocking +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.mockito.kotlin.eq + +/** + * Tests for [DesktopModeEventLogger]. + */ +class DesktopModeEventLoggerTest { + + private val desktopModeEventLogger = DesktopModeEventLogger() + + @JvmField + @Rule + val extendedMockitoRule = ExtendedMockitoRule.Builder(this) + .mockStatic(FrameworkStatsLog::class.java).build()!! + + @Test + fun logSessionEnter_enterReason() = runBlocking { + desktopModeEventLogger.logSessionEnter(sessionId = SESSION_ID, EnterReason.UNKNOWN_ENTER) + + ExtendedMockito.verify { + FrameworkStatsLog.write( + eq(FrameworkStatsLog.DESKTOP_MODE_UI_CHANGED), + /* event */ + eq(FrameworkStatsLog.DESKTOP_MODE_UICHANGED__EVENT__ENTER), + /* enter_reason */ + eq(FrameworkStatsLog.DESKTOP_MODE_UICHANGED__ENTER_REASON__UNKNOWN_ENTER), + /* exit_reason */ + eq(0), + /* sessionId */ + eq(SESSION_ID) + ) + } + } + + @Test + fun logSessionExit_exitReason() = runBlocking { + desktopModeEventLogger.logSessionExit(sessionId = SESSION_ID, ExitReason.UNKNOWN_EXIT) + + ExtendedMockito.verify { + FrameworkStatsLog.write( + eq(FrameworkStatsLog.DESKTOP_MODE_UI_CHANGED), + /* event */ + eq(FrameworkStatsLog.DESKTOP_MODE_UICHANGED__EVENT__EXIT), + /* enter_reason */ + eq(0), + /* exit_reason */ + eq(FrameworkStatsLog.DESKTOP_MODE_UICHANGED__EXIT_REASON__UNKNOWN_EXIT), + /* sessionId */ + eq(SESSION_ID) + ) + } + } + + @Test + fun logTaskAdded_taskUpdate() = runBlocking { + desktopModeEventLogger.logTaskAdded(sessionId = SESSION_ID, TASK_UPDATE) + + ExtendedMockito.verify { + FrameworkStatsLog.write(eq(FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE), + /* task_event */ + eq(FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_ADDED), + /* instance_id */ + eq(TASK_UPDATE.instanceId), + /* uid */ + eq(TASK_UPDATE.uid), + /* task_height */ + eq(TASK_UPDATE.taskHeight), + /* task_width */ + eq(TASK_UPDATE.taskWidth), + /* task_x */ + eq(TASK_UPDATE.taskX), + /* task_y */ + eq(TASK_UPDATE.taskY), + /* session_id */ + eq(SESSION_ID)) + } + } + + @Test + fun logTaskRemoved_taskUpdate() = runBlocking { + desktopModeEventLogger.logTaskRemoved(sessionId = SESSION_ID, TASK_UPDATE) + + ExtendedMockito.verify { + FrameworkStatsLog.write(eq(FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE), + /* task_event */ + eq(FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_REMOVED), + /* instance_id */ + eq(TASK_UPDATE.instanceId), + /* uid */ + eq(TASK_UPDATE.uid), + /* task_height */ + eq(TASK_UPDATE.taskHeight), + /* task_width */ + eq(TASK_UPDATE.taskWidth), + /* task_x */ + eq(TASK_UPDATE.taskX), + /* task_y */ + eq(TASK_UPDATE.taskY), + /* session_id */ + eq(SESSION_ID)) + } + } + + @Test + fun logTaskInfoChanged_taskUpdate() = runBlocking { + desktopModeEventLogger.logTaskInfoChanged(sessionId = SESSION_ID, TASK_UPDATE) + + ExtendedMockito.verify { + FrameworkStatsLog.write(eq(FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE), + /* task_event */ + eq(FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_INFO_CHANGED), + /* instance_id */ + eq(TASK_UPDATE.instanceId), + /* uid */ + eq(TASK_UPDATE.uid), + /* task_height */ + eq(TASK_UPDATE.taskHeight), + /* task_width */ + eq(TASK_UPDATE.taskWidth), + /* task_x */ + eq(TASK_UPDATE.taskX), + /* task_y */ + eq(TASK_UPDATE.taskY), + /* session_id */ + eq(SESSION_ID)) + } + } + + companion object { + private const val SESSION_ID = 1 + private const val TASK_ID = 1 + private const val TASK_UID = 1 + private const val TASK_X = 0 + private const val TASK_Y = 0 + private const val TASK_HEIGHT = 100 + private const val TASK_WIDTH = 100 + + private val TASK_UPDATE = TaskUpdate( + TASK_ID, TASK_UID, TASK_HEIGHT, TASK_WIDTH, TASK_X, TASK_Y + ) + } +}
\ No newline at end of file diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt index 383621beca22..35c803b78674 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt @@ -781,6 +781,23 @@ class DesktopTasksControllerTest : ShellTestCase() { ) } + @Test + fun moveFocusedTaskToFullscreen() { + val task1 = setUpFreeformTask() + val task2 = setUpFreeformTask() + val task3 = setUpFreeformTask() + + task1.isFocused = false + task2.isFocused = true + task3.isFocused = false + + controller.enterFullscreen(DEFAULT_DISPLAY) + + val wct = getLatestExitDesktopWct() + assertThat(wct.changes[task2.token.asBinder()]?.windowingMode) + .isEqualTo(WINDOWING_MODE_FULLSCREEN) + } + private fun setUpFreeformTask(displayId: Int = DEFAULT_DISPLAY): RunningTaskInfo { val task = createFreeformTask(displayId) whenever(shellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(task) diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java index 1b347e01888e..5dd9d8a859d6 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java @@ -22,9 +22,11 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.content.ClipDescription.MIMETYPE_APPLICATION_ACTIVITY; import static android.content.ClipDescription.MIMETYPE_APPLICATION_SHORTCUT; import static android.content.ClipDescription.MIMETYPE_APPLICATION_TASK; +import static android.content.ClipDescription.MIMETYPE_TEXT_INTENT; import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession; import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT; import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT; import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED; @@ -46,6 +48,8 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.mockito.quality.Strictness.LENIENT; import android.app.ActivityManager; import android.app.ActivityTaskManager; @@ -61,6 +65,7 @@ import android.content.res.Resources; import android.graphics.Insets; import android.os.RemoteException; import android.view.DisplayInfo; +import android.view.DragEvent; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; @@ -70,12 +75,15 @@ import com.android.wm.shell.ShellTestCase; import com.android.wm.shell.common.DisplayLayout; import com.android.wm.shell.draganddrop.DragAndDropPolicy.Target; import com.android.wm.shell.splitscreen.SplitScreenController; +import com.android.wm.shell.startingsurface.TaskSnapshotWindow; +import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import org.mockito.MockitoSession; import java.util.ArrayList; import java.util.Collections; @@ -107,6 +115,7 @@ public class DragAndDropPolicyTest extends ShellTestCase { private DragAndDropPolicy mPolicy; private ClipData mActivityClipData; + private ClipData mLaunchableIntentClipData; private ClipData mNonResizeableActivityClipData; private ClipData mTaskClipData; private ClipData mShortcutClipData; @@ -115,9 +124,16 @@ public class DragAndDropPolicyTest extends ShellTestCase { private ActivityManager.RunningTaskInfo mFullscreenAppTask; private ActivityManager.RunningTaskInfo mNonResizeableFullscreenAppTask; + private MockitoSession mMockitoSession; + @Before public void setUp() throws RemoteException { MockitoAnnotations.initMocks(this); + mMockitoSession = mockitoSession() + .strictness(LENIENT) + .mockStatic(DragUtils.class) + .startMocking(); + when(DragUtils.canHandleDrag(any())).thenReturn(true); Resources res = mock(Resources.class); Configuration config = new Configuration(); @@ -134,11 +150,12 @@ public class DragAndDropPolicyTest extends ShellTestCase { mInsets = Insets.of(0, 0, 0, 0); mPolicy = spy(new DragAndDropPolicy(mContext, mSplitScreenStarter, mSplitScreenStarter)); - mActivityClipData = createClipData(MIMETYPE_APPLICATION_ACTIVITY); - mNonResizeableActivityClipData = createClipData(MIMETYPE_APPLICATION_ACTIVITY); + mActivityClipData = createAppClipData(MIMETYPE_APPLICATION_ACTIVITY); + mLaunchableIntentClipData = createIntentClipData(); + mNonResizeableActivityClipData = createAppClipData(MIMETYPE_APPLICATION_ACTIVITY); setClipDataResizeable(mNonResizeableActivityClipData, false); - mTaskClipData = createClipData(MIMETYPE_APPLICATION_TASK); - mShortcutClipData = createClipData(MIMETYPE_APPLICATION_SHORTCUT); + mTaskClipData = createAppClipData(MIMETYPE_APPLICATION_TASK); + mShortcutClipData = createAppClipData(MIMETYPE_APPLICATION_SHORTCUT); mHomeTask = createTaskInfo(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME); mFullscreenAppTask = createTaskInfo(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD); @@ -149,10 +166,15 @@ public class DragAndDropPolicyTest extends ShellTestCase { setRunningTask(mFullscreenAppTask); } + @After + public void tearDown() { + mMockitoSession.finishMocking(); + } + /** - * Creates a clip data that is by default resizeable. + * Creates an app-based clip data that is by default resizeable. */ - private ClipData createClipData(String mimeType) { + private ClipData createAppClipData(String mimeType) { ClipDescription clipDescription = new ClipDescription(mimeType, new String[] { mimeType }); Intent i = new Intent(); switch (mimeType) { @@ -164,7 +186,9 @@ public class DragAndDropPolicyTest extends ShellTestCase { i.putExtra(Intent.EXTRA_TASK_ID, 12345); break; case MIMETYPE_APPLICATION_ACTIVITY: - i.putExtra(ClipDescription.EXTRA_PENDING_INTENT, mock(PendingIntent.class)); + final PendingIntent pi = mock(PendingIntent.class); + doReturn(android.os.Process.myUserHandle()).when(pi).getCreatorUserHandle(); + i.putExtra(ClipDescription.EXTRA_PENDING_INTENT, pi); break; } i.putExtra(Intent.EXTRA_USER, android.os.Process.myUserHandle()); @@ -175,6 +199,22 @@ public class DragAndDropPolicyTest extends ShellTestCase { return data; } + /** + * Creates an intent-based clip data that is by default resizeable. + */ + private ClipData createIntentClipData() { + ClipDescription clipDescription = new ClipDescription("Intent", + new String[] { MIMETYPE_TEXT_INTENT }); + PendingIntent intent = mock(PendingIntent.class); + when(intent.getCreatorUserHandle()).thenReturn(android.os.Process.myUserHandle()); + ClipData.Item item = new ClipData.Item.Builder() + .setIntentSender(intent.getIntentSender()) + .build(); + ClipData data = new ClipData(clipDescription, item); + when(DragUtils.getLaunchIntent((ClipData) any())).thenReturn(intent); + return data; + } + private ActivityManager.RunningTaskInfo createTaskInfo(int winMode, int actType) { ActivityManager.RunningTaskInfo info = new ActivityManager.RunningTaskInfo(); info.configuration.windowConfiguration.setActivityType(actType); @@ -204,58 +244,85 @@ public class DragAndDropPolicyTest extends ShellTestCase { @Test public void testDragAppOverFullscreenHome_expectOnlyFullscreenTarget() { + dragOverFullscreenHome_expectOnlyFullscreenTarget(mActivityClipData); + } + + @Test + public void testDragAppOverFullscreenApp_expectSplitScreenTargets() { + dragOverFullscreenApp_expectSplitScreenTargets(mActivityClipData); + } + + @Test + public void testDragAppOverFullscreenAppPhone_expectVerticalSplitScreenTargets() { + dragOverFullscreenAppPhone_expectVerticalSplitScreenTargets(mActivityClipData); + } + + @Test + public void testDragIntentOverFullscreenHome_expectOnlyFullscreenTarget() { + dragOverFullscreenHome_expectOnlyFullscreenTarget(mLaunchableIntentClipData); + } + + @Test + public void testDragIntentOverFullscreenApp_expectSplitScreenTargets() { + dragOverFullscreenApp_expectSplitScreenTargets(mLaunchableIntentClipData); + } + + @Test + public void testDragIntentOverFullscreenAppPhone_expectVerticalSplitScreenTargets() { + dragOverFullscreenAppPhone_expectVerticalSplitScreenTargets(mLaunchableIntentClipData); + } + + private void dragOverFullscreenHome_expectOnlyFullscreenTarget(ClipData data) { doReturn(true).when(mSplitScreenStarter).isLeftRightSplit(); setRunningTask(mHomeTask); - DragSession dragSession = new DragSession(mContext, mActivityTaskManager, - mLandscapeDisplayLayout, mActivityClipData); + DragSession dragSession = new DragSession(mActivityTaskManager, + mLandscapeDisplayLayout, data); dragSession.update(); mPolicy.start(dragSession, mLoggerSessionId); ArrayList<Target> targets = assertExactTargetTypes( mPolicy.getTargets(mInsets), TYPE_FULLSCREEN); - mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN), mActivityClipData); + mPolicy.handleDrop(filterTargetByType(targets, TYPE_FULLSCREEN)); verify(mSplitScreenStarter).startIntent(any(), anyInt(), any(), eq(SPLIT_POSITION_UNDEFINED), any()); } - @Test - public void testDragAppOverFullscreenApp_expectSplitScreenTargets() { + private void dragOverFullscreenApp_expectSplitScreenTargets(ClipData data) { doReturn(true).when(mSplitScreenStarter).isLeftRightSplit(); setRunningTask(mFullscreenAppTask); - DragSession dragSession = new DragSession(mContext, mActivityTaskManager, - mLandscapeDisplayLayout, mActivityClipData); + DragSession dragSession = new DragSession(mActivityTaskManager, + mLandscapeDisplayLayout, data); dragSession.update(); mPolicy.start(dragSession, mLoggerSessionId); ArrayList<Target> targets = assertExactTargetTypes( mPolicy.getTargets(mInsets), TYPE_SPLIT_LEFT, TYPE_SPLIT_RIGHT); - mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_LEFT), mActivityClipData); + mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_LEFT)); verify(mSplitScreenStarter).startIntent(any(), anyInt(), any(), eq(SPLIT_POSITION_TOP_OR_LEFT), any()); reset(mSplitScreenStarter); - mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_RIGHT), mActivityClipData); + mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_RIGHT)); verify(mSplitScreenStarter).startIntent(any(), anyInt(), any(), eq(SPLIT_POSITION_BOTTOM_OR_RIGHT), any()); } - @Test - public void testDragAppOverFullscreenAppPhone_expectVerticalSplitScreenTargets() { + private void dragOverFullscreenAppPhone_expectVerticalSplitScreenTargets(ClipData data) { doReturn(false).when(mSplitScreenStarter).isLeftRightSplit(); setRunningTask(mFullscreenAppTask); - DragSession dragSession = new DragSession(mContext, mActivityTaskManager, - mPortraitDisplayLayout, mActivityClipData); + DragSession dragSession = new DragSession(mActivityTaskManager, + mPortraitDisplayLayout, data); dragSession.update(); mPolicy.start(dragSession, mLoggerSessionId); ArrayList<Target> targets = assertExactTargetTypes( mPolicy.getTargets(mInsets), TYPE_SPLIT_TOP, TYPE_SPLIT_BOTTOM); - mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_TOP), mActivityClipData); + mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_TOP)); verify(mSplitScreenStarter).startIntent(any(), anyInt(), any(), eq(SPLIT_POSITION_TOP_OR_LEFT), any()); reset(mSplitScreenStarter); - mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_BOTTOM), mActivityClipData); + mPolicy.handleDrop(filterTargetByType(targets, TYPE_SPLIT_BOTTOM)); verify(mSplitScreenStarter).startIntent(any(), anyInt(), any(), eq(SPLIT_POSITION_BOTTOM_OR_RIGHT), any()); } @@ -263,7 +330,7 @@ public class DragAndDropPolicyTest extends ShellTestCase { @Test public void testTargetHitRects() { setRunningTask(mFullscreenAppTask); - DragSession dragSession = new DragSession(mContext, mActivityTaskManager, + DragSession dragSession = new DragSession(mActivityTaskManager, mLandscapeDisplayLayout, mActivityClipData); dragSession.update(); mPolicy.start(dragSession, mLoggerSessionId); diff --git a/libs/hwui/DisplayListOps.in b/libs/hwui/DisplayListOps.in index d21f07efe36a..b12486e32bd4 100644 --- a/libs/hwui/DisplayListOps.in +++ b/libs/hwui/DisplayListOps.in @@ -26,6 +26,7 @@ X(ClipPath) X(ClipRect) X(ClipRRect) X(ClipRegion) +X(ClipShader) X(ResetClip) X(DrawPaint) X(DrawBehind) diff --git a/libs/hwui/RecordingCanvas.cpp b/libs/hwui/RecordingCanvas.cpp index 3b694c5d399b..54aef55f8b90 100644 --- a/libs/hwui/RecordingCanvas.cpp +++ b/libs/hwui/RecordingCanvas.cpp @@ -207,6 +207,13 @@ struct ClipRegion final : Op { SkClipOp op; void draw(SkCanvas* c, const SkMatrix&) const { c->clipRegion(region, op); } }; +struct ClipShader final : Op { + static const auto kType = Type::ClipShader; + ClipShader(const sk_sp<SkShader>& shader, SkClipOp op) : shader(shader), op(op) {} + sk_sp<SkShader> shader; + SkClipOp op; + void draw(SkCanvas* c, const SkMatrix&) const { c->clipShader(shader, op); } +}; struct ResetClip final : Op { static const auto kType = Type::ResetClip; ResetClip() {} @@ -822,6 +829,9 @@ void DisplayListData::clipRRect(const SkRRect& rrect, SkClipOp op, bool aa) { void DisplayListData::clipRegion(const SkRegion& region, SkClipOp op) { this->push<ClipRegion>(0, region, op); } +void DisplayListData::clipShader(const sk_sp<SkShader>& shader, SkClipOp op) { + this->push<ClipShader>(0, shader, op); +} void DisplayListData::resetClip() { this->push<ResetClip>(0); } @@ -1134,6 +1144,11 @@ void RecordingCanvas::onClipRegion(const SkRegion& region, SkClipOp op) { fDL->clipRegion(region, op); this->INHERITED::onClipRegion(region, op); } +void RecordingCanvas::onClipShader(sk_sp<SkShader> shader, SkClipOp op) { + setClipMayBeComplex(); + fDL->clipShader(shader, op); + this->INHERITED::onClipShader(shader, op); +} void RecordingCanvas::onResetClip() { // This is part of "replace op" emulation, but rely on the following intersection // clip to potentially mark the clip as complex. If we are already complex, we do diff --git a/libs/hwui/RecordingCanvas.h b/libs/hwui/RecordingCanvas.h index afadbfda7471..965264f31119 100644 --- a/libs/hwui/RecordingCanvas.h +++ b/libs/hwui/RecordingCanvas.h @@ -140,6 +140,7 @@ private: void translateZ(SkScalar); void clipPath(const SkPath&, SkClipOp, bool aa); + void clipShader(const sk_sp<SkShader>& shader, SkClipOp); void clipRect(const SkRect&, SkClipOp, bool aa); void clipRRect(const SkRRect&, SkClipOp, bool aa); void clipRegion(const SkRegion&, SkClipOp); @@ -216,6 +217,7 @@ public: void onClipRect(const SkRect&, SkClipOp, ClipEdgeStyle) override; void onClipRRect(const SkRRect&, SkClipOp, ClipEdgeStyle) override; void onClipPath(const SkPath&, SkClipOp, ClipEdgeStyle) override; + void onClipShader(sk_sp<SkShader>, SkClipOp) override; void onClipRegion(const SkRegion&, SkClipOp) override; void onResetClip() override; diff --git a/libs/input/PointerController.cpp b/libs/input/PointerController.cpp index f84107e8792c..f9dc5fac7e21 100644 --- a/libs/input/PointerController.cpp +++ b/libs/input/PointerController.cpp @@ -41,6 +41,8 @@ namespace android { namespace { +static const bool ENABLE_POINTER_CHOREOGRAPHER = input_flags::enable_pointer_choreographer(); + const ui::Transform kIdentityTransform; } // namespace @@ -224,7 +226,7 @@ void PointerController::setPresentation(Presentation presentation) { mLocked.presentation = presentation; - if (input_flags::enable_pointer_choreographer()) { + if (ENABLE_POINTER_CHOREOGRAPHER) { // When pointer choreographer is enabled, the presentation mode is only set once when the // PointerController is constructed, before the display viewport is provided. // TODO(b/293587049): Clean up the PointerController interface after pointer choreographer |