Loading libs/WindowManager/Shell/src/com/android/wm/shell/ShellInitImpl.java +9 −0 Original line number Diff line number Diff line Loading @@ -24,6 +24,7 @@ import com.android.wm.shell.common.DisplayImeController; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.annotations.ExternalThread; import com.android.wm.shell.draganddrop.DragAndDropController; import com.android.wm.shell.freeform.FreeformTaskListener; import com.android.wm.shell.legacysplitscreen.LegacySplitScreenController; import com.android.wm.shell.pip.phone.PipTouchHandler; import com.android.wm.shell.splitscreen.SplitScreenController; Loading @@ -47,6 +48,7 @@ public class ShellInitImpl { private final Optional<AppPairsController> mAppPairsOptional; private final Optional<PipTouchHandler> mPipTouchHandlerOptional; private final FullscreenTaskListener mFullscreenTaskListener; private final Optional<FreeformTaskListener> mFreeformTaskListenerOptional; private final ShellExecutor mMainExecutor; private final Transitions mTransitions; private final StartingWindowController mStartingWindow; Loading @@ -62,6 +64,7 @@ public class ShellInitImpl { Optional<AppPairsController> appPairsOptional, Optional<PipTouchHandler> pipTouchHandlerOptional, FullscreenTaskListener fullscreenTaskListener, Optional<Optional<FreeformTaskListener>> freeformTaskListenerOptional, Transitions transitions, StartingWindowController startingWindow, ShellExecutor mainExecutor) { Loading @@ -74,6 +77,7 @@ public class ShellInitImpl { mAppPairsOptional = appPairsOptional; mFullscreenTaskListener = fullscreenTaskListener; mPipTouchHandlerOptional = pipTouchHandlerOptional; mFreeformTaskListenerOptional = freeformTaskListenerOptional.flatMap(f -> f); mTransitions = transitions; mMainExecutor = mainExecutor; mStartingWindow = startingWindow; Loading Loading @@ -108,6 +112,11 @@ public class ShellInitImpl { // controller instead of the feature interface, can just initialize the touch handler if // needed mPipTouchHandlerOptional.ifPresent((handler) -> handler.init()); // Initialize optional freeform mFreeformTaskListenerOptional.ifPresent(f -> mShellTaskOrganizer.addListenerForType( f, ShellTaskOrganizer.TASK_LISTENER_TYPE_FREEFORM)); } @ExternalThread Loading libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java +5 −0 Original line number Diff line number Diff line Loading @@ -71,12 +71,14 @@ public class ShellTaskOrganizer extends TaskOrganizer implements public static final int TASK_LISTENER_TYPE_FULLSCREEN = -2; public static final int TASK_LISTENER_TYPE_MULTI_WINDOW = -3; public static final int TASK_LISTENER_TYPE_PIP = -4; public static final int TASK_LISTENER_TYPE_FREEFORM = -5; @IntDef(prefix = {"TASK_LISTENER_TYPE_"}, value = { TASK_LISTENER_TYPE_UNDEFINED, TASK_LISTENER_TYPE_FULLSCREEN, TASK_LISTENER_TYPE_MULTI_WINDOW, TASK_LISTENER_TYPE_PIP, TASK_LISTENER_TYPE_FREEFORM, }) public @interface TaskListenerType {} Loading Loading @@ -572,6 +574,7 @@ public class ShellTaskOrganizer extends TaskOrganizer implements case WINDOWING_MODE_PINNED: return TASK_LISTENER_TYPE_PIP; case WINDOWING_MODE_FREEFORM: return TASK_LISTENER_TYPE_FREEFORM; case WINDOWING_MODE_UNDEFINED: default: return TASK_LISTENER_TYPE_UNDEFINED; Loading @@ -586,6 +589,8 @@ public class ShellTaskOrganizer extends TaskOrganizer implements return "TASK_LISTENER_TYPE_MULTI_WINDOW"; case TASK_LISTENER_TYPE_PIP: return "TASK_LISTENER_TYPE_PIP"; case TASK_LISTENER_TYPE_FREEFORM: return "TASK_LISTENER_TYPE_FREEFORM"; case TASK_LISTENER_TYPE_UNDEFINED: return "TASK_LISTENER_TYPE_UNDEFINED"; default: Loading libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java 0 → 100644 +147 −0 Original line number Diff line number Diff line /* * Copyright (C) 2021 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.freeform; import static android.content.pm.PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT; import static android.provider.Settings.Global.DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT; import android.app.ActivityManager.RunningTaskInfo; import android.content.Context; import android.graphics.Point; import android.graphics.Rect; import android.provider.Settings; import android.util.Slog; import android.util.SparseArray; import android.view.SurfaceControl; import com.android.internal.protolog.common.ProtoLog; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.common.SyncTransactionQueue; import com.android.wm.shell.protolog.ShellProtoLogGroup; import java.io.PrintWriter; /** * {@link ShellTaskOrganizer.TaskListener} for {@link * ShellTaskOrganizer#TASK_LISTENER_TYPE_FREEFORM}. */ public class FreeformTaskListener implements ShellTaskOrganizer.TaskListener { private static final String TAG = "FreeformTaskListener"; private final SyncTransactionQueue mSyncQueue; private final SparseArray<State> mTasks = new SparseArray<>(); private static class State { RunningTaskInfo mTaskInfo; SurfaceControl mLeash; } public FreeformTaskListener(SyncTransactionQueue syncQueue) { mSyncQueue = syncQueue; } @Override public void onTaskAppeared(RunningTaskInfo taskInfo, SurfaceControl leash) { if (mTasks.get(taskInfo.taskId) != null) { throw new RuntimeException("Task appeared more than once: #" + taskInfo.taskId); } ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Freeform Task Appeared: #%d", taskInfo.taskId); final State state = new State(); state.mTaskInfo = taskInfo; state.mLeash = leash; mTasks.put(taskInfo.taskId, state); final Rect taskBounds = taskInfo.configuration.windowConfiguration.getBounds(); mSyncQueue.runInSync(t -> { Point taskPosition = taskInfo.positionInParent; t.setPosition(leash, taskPosition.x, taskPosition.y) .setWindowCrop(leash, taskBounds.width(), taskBounds.height()) .show(leash); }); } @Override public void onTaskVanished(RunningTaskInfo taskInfo) { State state = mTasks.get(taskInfo.taskId); if (state == null) { Slog.e(TAG, "Task already vanished: #" + taskInfo.taskId); return; } ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Freeform Task Vanished: #%d", taskInfo.taskId); mTasks.remove(taskInfo.taskId); } @Override public void onTaskInfoChanged(RunningTaskInfo taskInfo) { State state = mTasks.get(taskInfo.taskId); if (state == null) { throw new RuntimeException( "Task info changed before appearing: #" + taskInfo.taskId); } ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Freeform Task Info Changed: #%d", taskInfo.taskId); state.mTaskInfo = taskInfo; final Rect taskBounds = taskInfo.configuration.windowConfiguration.getBounds(); final SurfaceControl leash = state.mLeash; mSyncQueue.runInSync(t -> { Point taskPosition = taskInfo.positionInParent; t.setPosition(leash, taskPosition.x, taskPosition.y) .setWindowCrop(leash, taskBounds.width(), taskBounds.height()) .show(leash); }); } @Override public void dump(PrintWriter pw, String prefix) { final String innerPrefix = prefix + " "; pw.println(prefix + this); pw.println(innerPrefix + mTasks.size() + " tasks"); } @Override public String toString() { return TAG; } /** * Checks if freeform support is enabled in system. * * @param context context used to check settings and package manager. * @return {@code true} if freeform is enabled, {@code false} if not. */ public static boolean isFreeformEnabled(Context context) { return context.getPackageManager().hasSystemFeature(FEATURE_FREEFORM_WINDOW_MANAGEMENT) || Settings.Global.getInt(context.getContentResolver(), DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT, 0) != 0; } /** * Creates {@link FreeformTaskListener} if freeform is enabled. */ public static FreeformTaskListener create(Context context, SyncTransactionQueue syncQueue) { if (!isFreeformEnabled(context)) { return null; } return new FreeformTaskListener(syncQueue); } } packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java +10 −0 Original line number Diff line number Diff line Loading @@ -55,6 +55,7 @@ import com.android.wm.shell.common.annotations.ShellAnimationThread; import com.android.wm.shell.common.annotations.ShellMainThread; import com.android.wm.shell.common.annotations.ShellSplashscreenThread; import com.android.wm.shell.draganddrop.DragAndDropController; import com.android.wm.shell.freeform.FreeformTaskListener; import com.android.wm.shell.hidedisplaycutout.HideDisplayCutout; import com.android.wm.shell.hidedisplaycutout.HideDisplayCutoutController; import com.android.wm.shell.legacysplitscreen.LegacySplitScreen; Loading Loading @@ -212,6 +213,13 @@ public abstract class WMShellBaseModule { return new FullscreenTaskListener(syncQueue); } // // Freeform (optional feature) // @BindsOptionalOf abstract Optional<FreeformTaskListener> optionalFreeformTaskListener(); // // Hide display cutout // Loading Loading @@ -453,6 +461,7 @@ public abstract class WMShellBaseModule { Optional<AppPairsController> appPairsOptional, Optional<PipTouchHandler> pipTouchHandlerOptional, FullscreenTaskListener fullscreenTaskListener, Optional<Optional<FreeformTaskListener>> freeformTaskListener, Transitions transitions, StartingWindowController startingWindow, @ShellMainThread ShellExecutor mainExecutor) { Loading @@ -465,6 +474,7 @@ public abstract class WMShellBaseModule { appPairsOptional, pipTouchHandlerOptional, fullscreenTaskListener, freeformTaskListener, transitions, startingWindow, mainExecutor); Loading packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java +13 −0 Original line number Diff line number Diff line Loading @@ -36,6 +36,7 @@ import com.android.wm.shell.common.TaskStackListenerImpl; import com.android.wm.shell.common.TransactionPool; import com.android.wm.shell.common.annotations.ChoreographerSfVsync; import com.android.wm.shell.common.annotations.ShellMainThread; import com.android.wm.shell.freeform.FreeformTaskListener; import com.android.wm.shell.legacysplitscreen.LegacySplitScreenController; import com.android.wm.shell.onehanded.OneHandedController; import com.android.wm.shell.pip.Pip; Loading Loading @@ -88,6 +89,18 @@ public class WMShellModule { transactionPool); } // // Freeform // @WMSingleton @Provides static Optional<FreeformTaskListener> provideFreeformTaskListener( Context context, SyncTransactionQueue syncQueue) { return Optional.ofNullable(FreeformTaskListener.create(context, syncQueue)); } // // Split/multiwindow // Loading Loading
libs/WindowManager/Shell/src/com/android/wm/shell/ShellInitImpl.java +9 −0 Original line number Diff line number Diff line Loading @@ -24,6 +24,7 @@ import com.android.wm.shell.common.DisplayImeController; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.annotations.ExternalThread; import com.android.wm.shell.draganddrop.DragAndDropController; import com.android.wm.shell.freeform.FreeformTaskListener; import com.android.wm.shell.legacysplitscreen.LegacySplitScreenController; import com.android.wm.shell.pip.phone.PipTouchHandler; import com.android.wm.shell.splitscreen.SplitScreenController; Loading @@ -47,6 +48,7 @@ public class ShellInitImpl { private final Optional<AppPairsController> mAppPairsOptional; private final Optional<PipTouchHandler> mPipTouchHandlerOptional; private final FullscreenTaskListener mFullscreenTaskListener; private final Optional<FreeformTaskListener> mFreeformTaskListenerOptional; private final ShellExecutor mMainExecutor; private final Transitions mTransitions; private final StartingWindowController mStartingWindow; Loading @@ -62,6 +64,7 @@ public class ShellInitImpl { Optional<AppPairsController> appPairsOptional, Optional<PipTouchHandler> pipTouchHandlerOptional, FullscreenTaskListener fullscreenTaskListener, Optional<Optional<FreeformTaskListener>> freeformTaskListenerOptional, Transitions transitions, StartingWindowController startingWindow, ShellExecutor mainExecutor) { Loading @@ -74,6 +77,7 @@ public class ShellInitImpl { mAppPairsOptional = appPairsOptional; mFullscreenTaskListener = fullscreenTaskListener; mPipTouchHandlerOptional = pipTouchHandlerOptional; mFreeformTaskListenerOptional = freeformTaskListenerOptional.flatMap(f -> f); mTransitions = transitions; mMainExecutor = mainExecutor; mStartingWindow = startingWindow; Loading Loading @@ -108,6 +112,11 @@ public class ShellInitImpl { // controller instead of the feature interface, can just initialize the touch handler if // needed mPipTouchHandlerOptional.ifPresent((handler) -> handler.init()); // Initialize optional freeform mFreeformTaskListenerOptional.ifPresent(f -> mShellTaskOrganizer.addListenerForType( f, ShellTaskOrganizer.TASK_LISTENER_TYPE_FREEFORM)); } @ExternalThread Loading
libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java +5 −0 Original line number Diff line number Diff line Loading @@ -71,12 +71,14 @@ public class ShellTaskOrganizer extends TaskOrganizer implements public static final int TASK_LISTENER_TYPE_FULLSCREEN = -2; public static final int TASK_LISTENER_TYPE_MULTI_WINDOW = -3; public static final int TASK_LISTENER_TYPE_PIP = -4; public static final int TASK_LISTENER_TYPE_FREEFORM = -5; @IntDef(prefix = {"TASK_LISTENER_TYPE_"}, value = { TASK_LISTENER_TYPE_UNDEFINED, TASK_LISTENER_TYPE_FULLSCREEN, TASK_LISTENER_TYPE_MULTI_WINDOW, TASK_LISTENER_TYPE_PIP, TASK_LISTENER_TYPE_FREEFORM, }) public @interface TaskListenerType {} Loading Loading @@ -572,6 +574,7 @@ public class ShellTaskOrganizer extends TaskOrganizer implements case WINDOWING_MODE_PINNED: return TASK_LISTENER_TYPE_PIP; case WINDOWING_MODE_FREEFORM: return TASK_LISTENER_TYPE_FREEFORM; case WINDOWING_MODE_UNDEFINED: default: return TASK_LISTENER_TYPE_UNDEFINED; Loading @@ -586,6 +589,8 @@ public class ShellTaskOrganizer extends TaskOrganizer implements return "TASK_LISTENER_TYPE_MULTI_WINDOW"; case TASK_LISTENER_TYPE_PIP: return "TASK_LISTENER_TYPE_PIP"; case TASK_LISTENER_TYPE_FREEFORM: return "TASK_LISTENER_TYPE_FREEFORM"; case TASK_LISTENER_TYPE_UNDEFINED: return "TASK_LISTENER_TYPE_UNDEFINED"; default: Loading
libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java 0 → 100644 +147 −0 Original line number Diff line number Diff line /* * Copyright (C) 2021 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.freeform; import static android.content.pm.PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT; import static android.provider.Settings.Global.DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT; import android.app.ActivityManager.RunningTaskInfo; import android.content.Context; import android.graphics.Point; import android.graphics.Rect; import android.provider.Settings; import android.util.Slog; import android.util.SparseArray; import android.view.SurfaceControl; import com.android.internal.protolog.common.ProtoLog; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.common.SyncTransactionQueue; import com.android.wm.shell.protolog.ShellProtoLogGroup; import java.io.PrintWriter; /** * {@link ShellTaskOrganizer.TaskListener} for {@link * ShellTaskOrganizer#TASK_LISTENER_TYPE_FREEFORM}. */ public class FreeformTaskListener implements ShellTaskOrganizer.TaskListener { private static final String TAG = "FreeformTaskListener"; private final SyncTransactionQueue mSyncQueue; private final SparseArray<State> mTasks = new SparseArray<>(); private static class State { RunningTaskInfo mTaskInfo; SurfaceControl mLeash; } public FreeformTaskListener(SyncTransactionQueue syncQueue) { mSyncQueue = syncQueue; } @Override public void onTaskAppeared(RunningTaskInfo taskInfo, SurfaceControl leash) { if (mTasks.get(taskInfo.taskId) != null) { throw new RuntimeException("Task appeared more than once: #" + taskInfo.taskId); } ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Freeform Task Appeared: #%d", taskInfo.taskId); final State state = new State(); state.mTaskInfo = taskInfo; state.mLeash = leash; mTasks.put(taskInfo.taskId, state); final Rect taskBounds = taskInfo.configuration.windowConfiguration.getBounds(); mSyncQueue.runInSync(t -> { Point taskPosition = taskInfo.positionInParent; t.setPosition(leash, taskPosition.x, taskPosition.y) .setWindowCrop(leash, taskBounds.width(), taskBounds.height()) .show(leash); }); } @Override public void onTaskVanished(RunningTaskInfo taskInfo) { State state = mTasks.get(taskInfo.taskId); if (state == null) { Slog.e(TAG, "Task already vanished: #" + taskInfo.taskId); return; } ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Freeform Task Vanished: #%d", taskInfo.taskId); mTasks.remove(taskInfo.taskId); } @Override public void onTaskInfoChanged(RunningTaskInfo taskInfo) { State state = mTasks.get(taskInfo.taskId); if (state == null) { throw new RuntimeException( "Task info changed before appearing: #" + taskInfo.taskId); } ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Freeform Task Info Changed: #%d", taskInfo.taskId); state.mTaskInfo = taskInfo; final Rect taskBounds = taskInfo.configuration.windowConfiguration.getBounds(); final SurfaceControl leash = state.mLeash; mSyncQueue.runInSync(t -> { Point taskPosition = taskInfo.positionInParent; t.setPosition(leash, taskPosition.x, taskPosition.y) .setWindowCrop(leash, taskBounds.width(), taskBounds.height()) .show(leash); }); } @Override public void dump(PrintWriter pw, String prefix) { final String innerPrefix = prefix + " "; pw.println(prefix + this); pw.println(innerPrefix + mTasks.size() + " tasks"); } @Override public String toString() { return TAG; } /** * Checks if freeform support is enabled in system. * * @param context context used to check settings and package manager. * @return {@code true} if freeform is enabled, {@code false} if not. */ public static boolean isFreeformEnabled(Context context) { return context.getPackageManager().hasSystemFeature(FEATURE_FREEFORM_WINDOW_MANAGEMENT) || Settings.Global.getInt(context.getContentResolver(), DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT, 0) != 0; } /** * Creates {@link FreeformTaskListener} if freeform is enabled. */ public static FreeformTaskListener create(Context context, SyncTransactionQueue syncQueue) { if (!isFreeformEnabled(context)) { return null; } return new FreeformTaskListener(syncQueue); } }
packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java +10 −0 Original line number Diff line number Diff line Loading @@ -55,6 +55,7 @@ import com.android.wm.shell.common.annotations.ShellAnimationThread; import com.android.wm.shell.common.annotations.ShellMainThread; import com.android.wm.shell.common.annotations.ShellSplashscreenThread; import com.android.wm.shell.draganddrop.DragAndDropController; import com.android.wm.shell.freeform.FreeformTaskListener; import com.android.wm.shell.hidedisplaycutout.HideDisplayCutout; import com.android.wm.shell.hidedisplaycutout.HideDisplayCutoutController; import com.android.wm.shell.legacysplitscreen.LegacySplitScreen; Loading Loading @@ -212,6 +213,13 @@ public abstract class WMShellBaseModule { return new FullscreenTaskListener(syncQueue); } // // Freeform (optional feature) // @BindsOptionalOf abstract Optional<FreeformTaskListener> optionalFreeformTaskListener(); // // Hide display cutout // Loading Loading @@ -453,6 +461,7 @@ public abstract class WMShellBaseModule { Optional<AppPairsController> appPairsOptional, Optional<PipTouchHandler> pipTouchHandlerOptional, FullscreenTaskListener fullscreenTaskListener, Optional<Optional<FreeformTaskListener>> freeformTaskListener, Transitions transitions, StartingWindowController startingWindow, @ShellMainThread ShellExecutor mainExecutor) { Loading @@ -465,6 +474,7 @@ public abstract class WMShellBaseModule { appPairsOptional, pipTouchHandlerOptional, fullscreenTaskListener, freeformTaskListener, transitions, startingWindow, mainExecutor); Loading
packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java +13 −0 Original line number Diff line number Diff line Loading @@ -36,6 +36,7 @@ import com.android.wm.shell.common.TaskStackListenerImpl; import com.android.wm.shell.common.TransactionPool; import com.android.wm.shell.common.annotations.ChoreographerSfVsync; import com.android.wm.shell.common.annotations.ShellMainThread; import com.android.wm.shell.freeform.FreeformTaskListener; import com.android.wm.shell.legacysplitscreen.LegacySplitScreenController; import com.android.wm.shell.onehanded.OneHandedController; import com.android.wm.shell.pip.Pip; Loading Loading @@ -88,6 +89,18 @@ public class WMShellModule { transactionPool); } // // Freeform // @WMSingleton @Provides static Optional<FreeformTaskListener> provideFreeformTaskListener( Context context, SyncTransactionQueue syncQueue) { return Optional.ofNullable(FreeformTaskListener.create(context, syncQueue)); } // // Split/multiwindow // Loading