Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 06b54269 authored by Ivan Tkachenko's avatar Ivan Tkachenko
Browse files

Desktop windowing wallpaper

* Add translucent wallpaper activity to move Launcher in the correct
  state
* Handle back navigation to remove the wallpaper activity if the last
  freeform window is removed
* Handle window decoration close to remove wallpaper activity if the
  last window is closed

Bug: 309014605
Flag: ACONFIG com.android.window.flags.enable_desktop_windowing_wallpaper_activity DEVELOPMENT
Test: atest DesktopTasksControllerTest DesktopModeTaskRepositoryTest
Change-Id: I38271dc3903ec35d53b4607f034b27d8390ac839
parent 0471058f
Loading
Loading
Loading
Loading
+8 −0
Original line number Diff line number Diff line
@@ -23,4 +23,12 @@
    <uses-permission android:name="android.permission.ROTATE_SURFACE_FLINGER" />
    <uses-permission android:name="android.permission.WAKEUP_SURFACE_FLINGER" />
    <uses-permission android:name="android.permission.READ_FRAME_BUFFER" />

    <application>
        <activity
            android:name=".desktopmode.DesktopWallpaperActivity"
            android:excludeFromRecents="true"
            android:launchMode="singleInstance"
            android:theme="@style/DesktopWallpaperTheme" />
    </application>
</manifest>
+8 −0
Original line number Diff line number Diff line
@@ -23,6 +23,14 @@
        <item name="android:windowAnimationStyle">@style/Animation.ForcedResizable</item>
    </style>

    <!-- Theme used for the activity that shows below the desktop mode windows to show wallpaper -->
    <style name="DesktopWallpaperTheme" parent="@android:style/Theme.Wallpaper.NoTitleBar">
        <item name="android:statusBarColor">@android:color/transparent</item>
        <item name="android:navigationBarColor">@android:color/transparent</item>
        <item name="android:windowDrawsSystemBarBackgrounds">true</item>
        <item name="android:windowAnimationStyle">@null</item>
    </style>

    <style name="Animation.ForcedResizable" parent="@android:style/Animation">
        <item name="android:activityOpenEnterAnimation">@anim/forced_resizable_enter</item>

+15 −1
Original line number Diff line number Diff line
@@ -59,6 +59,7 @@ import com.android.wm.shell.desktopmode.DesktopModeLoggerTransitionObserver;
import com.android.wm.shell.desktopmode.DesktopModeStatus;
import com.android.wm.shell.desktopmode.DesktopModeTaskRepository;
import com.android.wm.shell.desktopmode.DesktopTasksController;
import com.android.wm.shell.desktopmode.DesktopTasksTransitionObserver;
import com.android.wm.shell.desktopmode.DragToDesktopTransitionHandler;
import com.android.wm.shell.desktopmode.EnterDesktopTaskTransitionHandler;
import com.android.wm.shell.desktopmode.ExitDesktopTaskTransitionHandler;
@@ -566,6 +567,18 @@ public abstract class WMShellModule {
        return new DesktopModeTaskRepository();
    }

    @WMSingleton
    @Provides
    static Optional<DesktopTasksTransitionObserver> provideDesktopTasksTransitionObserver(
            Optional<DesktopModeTaskRepository> desktopModeTaskRepository,
            Transitions transitions,
            ShellInit shellInit
    ) {
        return desktopModeTaskRepository.flatMap(repository ->
                Optional.of(new DesktopTasksTransitionObserver(repository, transitions, shellInit))
        );
    }

    @WMSingleton
    @Provides
    static DesktopModeLoggerTransitionObserver provideDesktopModeLoggerTransitionObserver(
@@ -622,7 +635,8 @@ public abstract class WMShellModule {
    @Provides
    static Object provideIndependentShellComponentsToCreate(
            DragAndDropController dragAndDropController,
            DefaultMixedHandler defaultMixedHandler) {
            DefaultMixedHandler defaultMixedHandler,
            Optional<DesktopTasksTransitionObserver> desktopTasksTransitionObserverOptional) {
        return new Object();
    }
}
+12 −0
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@ import android.util.ArrayMap
import android.util.ArraySet
import android.util.SparseArray
import android.view.Display.INVALID_DISPLAY
import android.window.WindowContainerToken
import androidx.core.util.forEach
import androidx.core.util.keyIterator
import androidx.core.util.valueIterator
@@ -49,6 +50,8 @@ class DesktopModeTaskRepository {
        var stashed: Boolean = false
    )

    // Token of the current wallpaper activity, used to remove it when the last task is removed
    var wallpaperActivityToken: WindowContainerToken? = null
    // Tasks currently in freeform mode, ordered from top to bottom (top is at index 0).
    private val freeformTasksInZOrder = mutableListOf<Int>()
    private val activeTasksListeners = ArraySet<ActiveTasksListener>()
@@ -199,6 +202,15 @@ class DesktopModeTaskRepository {
        }
    }

    /**
     *  Check if a task with the given [taskId] is the only active task on its display
     */
    fun isOnlyActiveTask(taskId: Int): Boolean {
        return displayData.valueIterator().asSequence().any { data ->
            data.activeTasks.singleOrNull() == taskId
        }
    }

    /**
     * Get a set of the active tasks for given [displayId]
     */
+70 −3
Original line number Diff line number Diff line
@@ -40,6 +40,7 @@ import android.view.SurfaceControl
import android.view.WindowManager.TRANSIT_CHANGE
import android.view.WindowManager.TRANSIT_NONE
import android.view.WindowManager.TRANSIT_OPEN
import android.view.WindowManager.TRANSIT_TO_BACK
import android.view.WindowManager.TRANSIT_TO_FRONT
import android.window.RemoteTransition
import android.window.TransitionInfo
@@ -381,7 +382,6 @@ class DesktopTasksController(
        )
        val wct = WindowContainerTransaction()
        exitSplitIfApplicable(wct, taskInfo)
        moveHomeTaskToFront(wct)
        bringDesktopAppsToFront(taskInfo.displayId, wct)
        addMoveToDesktopChanges(wct, taskInfo)
        wct.setBounds(taskInfo.token, freeformBounds)
@@ -401,6 +401,22 @@ class DesktopTasksController(
        shellTaskOrganizer.applyTransaction(wct)
    }

    /**
     * Perform clean up of the desktop wallpaper activity if the closed window task is
     * the last active task.
     *
     * @param wct transaction to modify if the last active task is closed
     * @param taskId task id of the window that's being closed
     */
    fun onDesktopWindowClose(
        wct: WindowContainerTransaction,
        taskId: Int
    ) {
        if (desktopModeTaskRepository.isOnlyActiveTask(taskId)) {
            removeWallpaperActivity(wct)
        }
    }

    /** Move a task with given `taskId` to fullscreen */
    fun moveToFullscreen(taskId: Int) {
        shellTaskOrganizer.getRunningTaskInfo(taskId)?.let { task ->
@@ -680,9 +696,15 @@ class DesktopTasksController(
        KtProtoLog.v(WM_SHELL_DESKTOP_MODE, "DesktopTasksController: bringDesktopAppsToFront")
        val activeTasks = desktopModeTaskRepository.getActiveTasks(displayId)

        // First move home to front and then other tasks on top of it
        if (Flags.enableDesktopWindowingWallpaperActivity()) {
            // Add translucent wallpaper activity to show the wallpaper underneath
            addWallpaperActivity(wct)
        } else {
            // Move home to front
            moveHomeTaskToFront(wct)
        }

        // Then move other tasks on top of it
        val allTasksInZOrder = desktopModeTaskRepository.getFreeformTasksInZOrder()
        activeTasks
            // Sort descending as the top task is at index 0. It should be ordered to top last
@@ -698,6 +720,26 @@ class DesktopTasksController(
            ?.let { homeTask -> wct.reorder(homeTask.getToken(), true /* onTop */) }
    }

    private fun addWallpaperActivity(wct: WindowContainerTransaction) {
        KtProtoLog.v(WM_SHELL_DESKTOP_MODE, "DesktopTasksController: addWallpaper")
        val intent = Intent(context, DesktopWallpaperActivity::class.java)
        val options = ActivityOptions.makeBasic().apply {
            isPendingIntentBackgroundActivityLaunchAllowedByPermission = true
            pendingIntentBackgroundActivityStartMode =
                ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED
        }
        val pendingIntent = PendingIntent.getActivity(context, /* requestCode = */ 0, intent,
            PendingIntent.FLAG_IMMUTABLE)
        wct.sendPendingIntent(pendingIntent, intent, options.toBundle())
    }

    private fun removeWallpaperActivity(wct: WindowContainerTransaction) {
        desktopModeTaskRepository.wallpaperActivityToken?.let { token ->
            KtProtoLog.v(WM_SHELL_DESKTOP_MODE, "DesktopTasksController: removeWallpaper")
            wct.removeTask(token)
        }
    }

    fun releaseVisualIndicator() {
        val t = SurfaceControl.Transaction()
        visualIndicator?.releaseVisualIndicator(t)
@@ -745,6 +787,9 @@ class DesktopTasksController(
                    reason = "recents animation is running"
                    false
                }
                // Handle back navigation for the last window if wallpaper available
                shouldRemoveWallpaper(request) ->
                    true
                // Only handle open or to front transitions
                request.type != TRANSIT_OPEN && request.type != TRANSIT_TO_FRONT -> {
                    reason = "transition type not handled (${request.type})"
@@ -781,6 +826,7 @@ class DesktopTasksController(

        val result = triggerTask?.let { task ->
            when {
                request.type == TRANSIT_TO_BACK -> handleBackNavigation(task)
                // If display has tasks stashed, handle as stashed launch
                task.isStashed -> handleStashedTaskLaunch(task)
                // Check if the task has a top transparent activity
@@ -828,6 +874,14 @@ class DesktopTasksController(
        return Flags.enableDesktopWindowingModalsPolicy() && isSingleTopActivityTranslucent(task)
    }

    private fun shouldRemoveWallpaper(request: TransitionRequestInfo): Boolean {
        return Flags.enableDesktopWindowingWallpaperActivity() &&
                request.type == TRANSIT_TO_BACK &&
                request.triggerTask?.let { task ->
                    desktopModeTaskRepository.isOnlyActiveTask(task.taskId)
                } ?: false
    }

    private fun handleFreeformTaskLaunch(task: RunningTaskInfo): WindowContainerTransaction? {
        KtProtoLog.v(WM_SHELL_DESKTOP_MODE, "DesktopTasksController: handleFreeformTaskLaunch")
        val activeTasks = desktopModeTaskRepository.getActiveTasks(task.displayId)
@@ -885,6 +939,19 @@ class DesktopTasksController(
        }
    }

    /** Handle back navigation by removing wallpaper activity if it's the last active task */
    private fun handleBackNavigation(task: RunningTaskInfo): WindowContainerTransaction? {
        if (desktopModeTaskRepository.isOnlyActiveTask(task.taskId) &&
            desktopModeTaskRepository.wallpaperActivityToken != null) {
            // Remove wallpaper activity when the last active task is removed
            return WindowContainerTransaction().also { wct ->
                removeWallpaperActivity(wct)
            }
        } else {
            return null
        }
    }

    private fun addMoveToDesktopChanges(
        wct: WindowContainerTransaction,
        taskInfo: RunningTaskInfo
Loading