Loading libs/WindowManager/Shell/src/com/android/wm/shell/common/MultiInstanceHelper.kt 0 → 100644 +124 −0 Original line number Diff line number Diff line /* * 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.common import android.app.PendingIntent import android.content.ComponentName import android.content.Context import android.content.pm.LauncherApps import android.content.pm.PackageManager import android.os.UserHandle import android.view.WindowManager import android.view.WindowManager.PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI import com.android.internal.annotations.VisibleForTesting import com.android.wm.shell.R import com.android.wm.shell.protolog.ShellProtoLogGroup import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL import com.android.wm.shell.util.KtProtoLog import java.util.Arrays /** * Helper for multi-instance related checks. */ class MultiInstanceHelper @JvmOverloads constructor( private val context: Context, private val packageManager: PackageManager, private val staticAppsSupportingMultiInstance: Array<String> = context.resources .getStringArray(R.array.config_appsSupportMultiInstancesSplit)) { /** * Returns whether a specific component desires to be launched in multiple instances. */ @VisibleForTesting fun supportsMultiInstanceSplit(componentName: ComponentName?): Boolean { if (componentName == null || componentName.packageName == null) { // TODO(b/262864589): Handle empty component case return false } // Check the pre-defined allow list val packageName = componentName.packageName for (pkg in staticAppsSupportingMultiInstance) { if (pkg == packageName) { KtProtoLog.v(WM_SHELL, "application=%s in allowlist supports multi-instance", packageName) return true } } // Check the activity property first try { val activityProp = packageManager.getProperty( PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI, componentName) // If the above call doesn't throw a NameNotFoundException, then the activity property // should override the application property value if (activityProp.isBoolean) { KtProtoLog.v(WM_SHELL, "activity=%s supports multi-instance", componentName) return activityProp.boolean } else { KtProtoLog.w(WM_SHELL, "Warning: property=%s for activity=%s has non-bool type=%d", PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI, packageName, activityProp.type) } } catch (nnfe: PackageManager.NameNotFoundException) { // Not specified in the activity, fall through } // Check the application property otherwise try { val appProp = packageManager.getProperty( PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI, packageName) if (appProp.isBoolean) { KtProtoLog.v(WM_SHELL, "application=%s supports multi-instance", packageName) return appProp.boolean } else { KtProtoLog.w(WM_SHELL, "Warning: property=%s for application=%s has non-bool type=%d", PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI, packageName, appProp.type) } } catch (nnfe: PackageManager.NameNotFoundException) { // Not specified in either application or activity } return false } companion object { /** Returns the component from a PendingIntent */ @JvmStatic fun getComponent(pendingIntent: PendingIntent?): ComponentName? { return pendingIntent?.intent?.component } /** Returns the component from a shortcut */ @JvmStatic fun getShortcutComponent(packageName: String, shortcutId: String, user: UserHandle, launcherApps: LauncherApps): ComponentName? { val query = LauncherApps.ShortcutQuery() query.setPackage(packageName) query.setShortcutIds(Arrays.asList(shortcutId)) query.setQueryFlags(LauncherApps.ShortcutQuery.FLAG_MATCH_ALL_KINDS_WITH_ALL_PINNED) val shortcuts = launcherApps.getShortcuts(query, user) val info = if (shortcuts != null && shortcuts.size > 0) shortcuts[0] else null return info?.activity } /** Returns true if package names and user ids match. */ @JvmStatic fun samePackage(packageName1: String?, packageName2: String?, userId1: Int, userId2: Int): Boolean { return (packageName1 != null && packageName1 == packageName2) && (userId1 == userId2) } } } libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenUtils.java +0 −30 Original line number Diff line number Diff line Loading @@ -99,12 +99,6 @@ public class SplitScreenUtils { return taskInfo != null ? taskInfo.userId : -1; } /** Returns true if package names and user ids match. */ public static boolean samePackage(String packageName1, String packageName2, int userId1, int userId2) { return (packageName1 != null && packageName1.equals(packageName2)) && (userId1 == userId2); } /** Generates a common log message for split screen failures */ public static String splitFailureMessage(String caller, String reason) { return "(" + caller + ") Splitscreen aborted: " + reason; Loading Loading @@ -143,28 +137,4 @@ public class SplitScreenUtils { return isLandscape; } } /** Returns the component from a PendingIntent */ @Nullable public static ComponentName getComponent(@Nullable PendingIntent pendingIntent) { if (pendingIntent == null || pendingIntent.getIntent() == null) { return null; } return pendingIntent.getIntent().getComponent(); } /** Returns the component from a shortcut */ @Nullable public static ComponentName getShortcutComponent(@NonNull String packageName, String shortcutId, @NonNull UserHandle user, @NonNull LauncherApps launcherApps) { LauncherApps.ShortcutQuery query = new LauncherApps.ShortcutQuery(); query.setPackage(packageName); query.setShortcutIds(Arrays.asList(shortcutId)); query.setQueryFlags(FLAG_MATCH_ALL_KINDS_WITH_ALL_PINNED); List<ShortcutInfo> shortcuts = launcherApps.getShortcuts(query, user); ShortcutInfo info = shortcuts != null && shortcuts.size() > 0 ? shortcuts.get(0) : null; return info != null ? info.getActivity() : null; } } libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvWMShellModule.java +4 −2 Original line number Diff line number Diff line Loading @@ -26,6 +26,7 @@ import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.DisplayImeController; import com.android.wm.shell.common.DisplayInsetsController; import com.android.wm.shell.common.LaunchAdjacentController; import com.android.wm.shell.common.MultiInstanceHelper; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.SyncTransactionQueue; import com.android.wm.shell.common.SystemWindows; Loading Loading @@ -88,13 +89,14 @@ public class TvWMShellModule { IconProvider iconProvider, Optional<RecentTasksController> recentTasks, LaunchAdjacentController launchAdjacentController, MultiInstanceHelper multiInstanceHelper, @ShellMainThread ShellExecutor mainExecutor, Handler mainHandler, SystemWindows systemWindows) { return new TvSplitScreenController(context, shellInit, shellCommandHandler, shellController, shellTaskOrganizer, syncQueue, rootTDAOrganizer, displayController, displayImeController, displayInsetsController, transitions, transactionPool, iconProvider, recentTasks, launchAdjacentController, mainExecutor, mainHandler, systemWindows); iconProvider, recentTasks, launchAdjacentController, multiInstanceHelper, mainExecutor, mainHandler, systemWindows); } } libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java +7 −0 Original line number Diff line number Diff line Loading @@ -50,6 +50,7 @@ import com.android.wm.shell.common.DisplayLayout; import com.android.wm.shell.common.DockStateReader; import com.android.wm.shell.common.FloatingContentCoordinator; import com.android.wm.shell.common.LaunchAdjacentController; import com.android.wm.shell.common.MultiInstanceHelper; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.SyncTransactionQueue; import com.android.wm.shell.common.SystemWindows; Loading Loading @@ -322,6 +323,12 @@ public abstract class WMShellBaseModule { return Optional.of(perfHintController.getHinter()); } @WMSingleton @Provides static MultiInstanceHelper provideMultiInstanceHelper(Context context) { return new MultiInstanceHelper(context, context.getPackageManager()); } // // Back animation // Loading libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java +4 −1 Original line number Diff line number Diff line Loading @@ -47,6 +47,7 @@ import com.android.wm.shell.common.DisplayInsetsController; import com.android.wm.shell.common.DisplayLayout; import com.android.wm.shell.common.FloatingContentCoordinator; import com.android.wm.shell.common.LaunchAdjacentController; import com.android.wm.shell.common.MultiInstanceHelper; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.SyncTransactionQueue; import com.android.wm.shell.common.TaskStackListenerImpl; Loading Loading @@ -347,12 +348,14 @@ public abstract class WMShellModule { LaunchAdjacentController launchAdjacentController, Optional<WindowDecorViewModel> windowDecorViewModel, Optional<DesktopTasksController> desktopTasksController, MultiInstanceHelper multiInstanceHelper, @ShellMainThread ShellExecutor mainExecutor) { return new SplitScreenController(context, shellInit, shellCommandHandler, shellController, shellTaskOrganizer, syncQueue, rootTaskDisplayAreaOrganizer, displayController, displayImeController, displayInsetsController, dragAndDropController, transitions, transactionPool, iconProvider, recentTasks, launchAdjacentController, windowDecorViewModel, desktopTasksController, mainExecutor); windowDecorViewModel, desktopTasksController, null /* stageCoordinator */, multiInstanceHelper, mainExecutor); } // Loading Loading
libs/WindowManager/Shell/src/com/android/wm/shell/common/MultiInstanceHelper.kt 0 → 100644 +124 −0 Original line number Diff line number Diff line /* * 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.common import android.app.PendingIntent import android.content.ComponentName import android.content.Context import android.content.pm.LauncherApps import android.content.pm.PackageManager import android.os.UserHandle import android.view.WindowManager import android.view.WindowManager.PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI import com.android.internal.annotations.VisibleForTesting import com.android.wm.shell.R import com.android.wm.shell.protolog.ShellProtoLogGroup import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL import com.android.wm.shell.util.KtProtoLog import java.util.Arrays /** * Helper for multi-instance related checks. */ class MultiInstanceHelper @JvmOverloads constructor( private val context: Context, private val packageManager: PackageManager, private val staticAppsSupportingMultiInstance: Array<String> = context.resources .getStringArray(R.array.config_appsSupportMultiInstancesSplit)) { /** * Returns whether a specific component desires to be launched in multiple instances. */ @VisibleForTesting fun supportsMultiInstanceSplit(componentName: ComponentName?): Boolean { if (componentName == null || componentName.packageName == null) { // TODO(b/262864589): Handle empty component case return false } // Check the pre-defined allow list val packageName = componentName.packageName for (pkg in staticAppsSupportingMultiInstance) { if (pkg == packageName) { KtProtoLog.v(WM_SHELL, "application=%s in allowlist supports multi-instance", packageName) return true } } // Check the activity property first try { val activityProp = packageManager.getProperty( PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI, componentName) // If the above call doesn't throw a NameNotFoundException, then the activity property // should override the application property value if (activityProp.isBoolean) { KtProtoLog.v(WM_SHELL, "activity=%s supports multi-instance", componentName) return activityProp.boolean } else { KtProtoLog.w(WM_SHELL, "Warning: property=%s for activity=%s has non-bool type=%d", PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI, packageName, activityProp.type) } } catch (nnfe: PackageManager.NameNotFoundException) { // Not specified in the activity, fall through } // Check the application property otherwise try { val appProp = packageManager.getProperty( PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI, packageName) if (appProp.isBoolean) { KtProtoLog.v(WM_SHELL, "application=%s supports multi-instance", packageName) return appProp.boolean } else { KtProtoLog.w(WM_SHELL, "Warning: property=%s for application=%s has non-bool type=%d", PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI, packageName, appProp.type) } } catch (nnfe: PackageManager.NameNotFoundException) { // Not specified in either application or activity } return false } companion object { /** Returns the component from a PendingIntent */ @JvmStatic fun getComponent(pendingIntent: PendingIntent?): ComponentName? { return pendingIntent?.intent?.component } /** Returns the component from a shortcut */ @JvmStatic fun getShortcutComponent(packageName: String, shortcutId: String, user: UserHandle, launcherApps: LauncherApps): ComponentName? { val query = LauncherApps.ShortcutQuery() query.setPackage(packageName) query.setShortcutIds(Arrays.asList(shortcutId)) query.setQueryFlags(LauncherApps.ShortcutQuery.FLAG_MATCH_ALL_KINDS_WITH_ALL_PINNED) val shortcuts = launcherApps.getShortcuts(query, user) val info = if (shortcuts != null && shortcuts.size > 0) shortcuts[0] else null return info?.activity } /** Returns true if package names and user ids match. */ @JvmStatic fun samePackage(packageName1: String?, packageName2: String?, userId1: Int, userId2: Int): Boolean { return (packageName1 != null && packageName1 == packageName2) && (userId1 == userId2) } } }
libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenUtils.java +0 −30 Original line number Diff line number Diff line Loading @@ -99,12 +99,6 @@ public class SplitScreenUtils { return taskInfo != null ? taskInfo.userId : -1; } /** Returns true if package names and user ids match. */ public static boolean samePackage(String packageName1, String packageName2, int userId1, int userId2) { return (packageName1 != null && packageName1.equals(packageName2)) && (userId1 == userId2); } /** Generates a common log message for split screen failures */ public static String splitFailureMessage(String caller, String reason) { return "(" + caller + ") Splitscreen aborted: " + reason; Loading Loading @@ -143,28 +137,4 @@ public class SplitScreenUtils { return isLandscape; } } /** Returns the component from a PendingIntent */ @Nullable public static ComponentName getComponent(@Nullable PendingIntent pendingIntent) { if (pendingIntent == null || pendingIntent.getIntent() == null) { return null; } return pendingIntent.getIntent().getComponent(); } /** Returns the component from a shortcut */ @Nullable public static ComponentName getShortcutComponent(@NonNull String packageName, String shortcutId, @NonNull UserHandle user, @NonNull LauncherApps launcherApps) { LauncherApps.ShortcutQuery query = new LauncherApps.ShortcutQuery(); query.setPackage(packageName); query.setShortcutIds(Arrays.asList(shortcutId)); query.setQueryFlags(FLAG_MATCH_ALL_KINDS_WITH_ALL_PINNED); List<ShortcutInfo> shortcuts = launcherApps.getShortcuts(query, user); ShortcutInfo info = shortcuts != null && shortcuts.size() > 0 ? shortcuts.get(0) : null; return info != null ? info.getActivity() : null; } }
libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvWMShellModule.java +4 −2 Original line number Diff line number Diff line Loading @@ -26,6 +26,7 @@ import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.DisplayImeController; import com.android.wm.shell.common.DisplayInsetsController; import com.android.wm.shell.common.LaunchAdjacentController; import com.android.wm.shell.common.MultiInstanceHelper; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.SyncTransactionQueue; import com.android.wm.shell.common.SystemWindows; Loading Loading @@ -88,13 +89,14 @@ public class TvWMShellModule { IconProvider iconProvider, Optional<RecentTasksController> recentTasks, LaunchAdjacentController launchAdjacentController, MultiInstanceHelper multiInstanceHelper, @ShellMainThread ShellExecutor mainExecutor, Handler mainHandler, SystemWindows systemWindows) { return new TvSplitScreenController(context, shellInit, shellCommandHandler, shellController, shellTaskOrganizer, syncQueue, rootTDAOrganizer, displayController, displayImeController, displayInsetsController, transitions, transactionPool, iconProvider, recentTasks, launchAdjacentController, mainExecutor, mainHandler, systemWindows); iconProvider, recentTasks, launchAdjacentController, multiInstanceHelper, mainExecutor, mainHandler, systemWindows); } }
libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java +7 −0 Original line number Diff line number Diff line Loading @@ -50,6 +50,7 @@ import com.android.wm.shell.common.DisplayLayout; import com.android.wm.shell.common.DockStateReader; import com.android.wm.shell.common.FloatingContentCoordinator; import com.android.wm.shell.common.LaunchAdjacentController; import com.android.wm.shell.common.MultiInstanceHelper; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.SyncTransactionQueue; import com.android.wm.shell.common.SystemWindows; Loading Loading @@ -322,6 +323,12 @@ public abstract class WMShellBaseModule { return Optional.of(perfHintController.getHinter()); } @WMSingleton @Provides static MultiInstanceHelper provideMultiInstanceHelper(Context context) { return new MultiInstanceHelper(context, context.getPackageManager()); } // // Back animation // Loading
libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java +4 −1 Original line number Diff line number Diff line Loading @@ -47,6 +47,7 @@ import com.android.wm.shell.common.DisplayInsetsController; import com.android.wm.shell.common.DisplayLayout; import com.android.wm.shell.common.FloatingContentCoordinator; import com.android.wm.shell.common.LaunchAdjacentController; import com.android.wm.shell.common.MultiInstanceHelper; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.SyncTransactionQueue; import com.android.wm.shell.common.TaskStackListenerImpl; Loading Loading @@ -347,12 +348,14 @@ public abstract class WMShellModule { LaunchAdjacentController launchAdjacentController, Optional<WindowDecorViewModel> windowDecorViewModel, Optional<DesktopTasksController> desktopTasksController, MultiInstanceHelper multiInstanceHelper, @ShellMainThread ShellExecutor mainExecutor) { return new SplitScreenController(context, shellInit, shellCommandHandler, shellController, shellTaskOrganizer, syncQueue, rootTaskDisplayAreaOrganizer, displayController, displayImeController, displayInsetsController, dragAndDropController, transitions, transactionPool, iconProvider, recentTasks, launchAdjacentController, windowDecorViewModel, desktopTasksController, mainExecutor); windowDecorViewModel, desktopTasksController, null /* stageCoordinator */, multiInstanceHelper, mainExecutor); } // Loading