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

Commit 598415b0 authored by Eric Lin's avatar Eric Lin
Browse files

Refactor BubbleUtils with Kotlin extensions.

To improve readability and adopt Kotlin idioms, this change refactors
the static utility methods in BubbleUtils.kt into extension functions on
ActivityManager.RunningTaskInfo. This provides a more ergonomic way to
call these utilities, changing calls from isBubbleToFullscreen(task) to
the more natural task.isBubbleToFullscreen().

This refactoring also centralizes the logic for determining if a bubble
is moving into a split-screen stage. The new isBubbleToSplit() extension
function is now shared between BubblesTransitionObserver and
BubbleTaskStackListener, which eliminates duplicated code.

For seamless Java interoperability and to prevent breaking existing
callers, the new extension functions are annotated with @JvmStatic. They
also operate on a nullable receiver (ActivityManager.RunningTaskInfo?)
to ensure null safety.

Bug: 387193964
Flag: EXEMPT PURE_REFACTOR
Test: atest WMShellRobolectricTests:BubbleBarExpandedViewTest
Test: atest WMShellRobolectricTests:BubbleControllerTest
Test: atest WMShellRobolectricTests:BubbleTaskStackListenerTest
Test: atest WMShellRobolectricTests:BubbleTaskViewTest
Test: atest WMShellMultivalentTestsOnDevice:BubbleBarExpandedViewTest
Test: atest WMShellMultivalentTestsOnDevice:BubbleControllerTest
Test: atest WMShellMultivalentTestsOnDevice:BubbleTaskStackListenerTest
Test: atest WMShellMultivalentTestsOnDevice:BubbleTaskViewTest
Test: atest WMShellUnitTests:BubblesTransitionObserverTest
Change-Id: I85661ef39f91374a7468b64a3ded4138e1945c57
parent 42bdf1c4
Loading
Loading
Loading
Loading
+8 −16
Original line number Diff line number Diff line
@@ -14,9 +14,6 @@
 * limitations under the License.
 */

// Exports bubble task utilities (e.g., `isBubbleToFullscreen`) for Java interop.
@file:JvmName("BubbleTaskUtils")

package com.android.wm.shell.bubbles

import android.app.ActivityManager
@@ -25,6 +22,7 @@ import com.android.internal.protolog.ProtoLog
import com.android.wm.shell.ShellTaskOrganizer
import com.android.wm.shell.bubbles.util.BubbleUtils.getExitBubbleTransaction
import com.android.wm.shell.bubbles.util.BubbleUtils.isBubbleToFullscreen
import com.android.wm.shell.bubbles.util.BubbleUtils.isBubbleToSplit
import com.android.wm.shell.common.TaskStackListenerCallback
import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_BUBBLES
import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_BUBBLES_NOISY
@@ -63,9 +61,9 @@ class BubbleTaskStackListener(
            taskId)
        bubbleData.getBubbleInStackWithTaskId(taskId)?.let { bubble ->
            when {
                isBubbleToFullscreen(task) -> moveCollapsedInStackBubbleToFullscreen(bubble, task)
                isBubbleToSplit(task) -> return // skip split task restarts
                !isAppBubbleMovingToFront(task) -> selectAndExpandInStackBubble(bubble, task)
                task.isBubbleToFullscreen() -> moveCollapsedInStackBubbleToFullscreen(bubble, task)
                task.isBubbleToSplit(splitScreenController) -> return // skip split task restarts
                !task.isAppBubbleMovingToFront() -> selectAndExpandInStackBubble(bubble, task)
            }
        }
    }
@@ -78,15 +76,9 @@ class BubbleTaskStackListener(
            taskId)
        bubbleData.getBubbleInStackWithTaskId(taskId)?.let { bubble ->
            when {
                isBubbleToFullscreen(task) -> moveCollapsedInStackBubbleToFullscreen(bubble, task)
            }
                task.isBubbleToFullscreen() -> moveCollapsedInStackBubbleToFullscreen(bubble, task)
            }
        }

    private fun isBubbleToSplit(task: ActivityManager.RunningTaskInfo): Boolean {
        return task.hasParentTask() && splitScreenController.get()
            .map { it.isTaskRootOrStageRoot(task.parentTaskId) }
            .orElse(false)
    }

    /**
@@ -95,9 +87,9 @@ class BubbleTaskStackListener(
     * This occurs when a startActivity call resolves to an existing activity, causing the
     * task to move to front, and the mixed transition will then expand the bubble.
     */
    private fun isAppBubbleMovingToFront(task: ActivityManager.RunningTaskInfo): Boolean {
        return task.activityType == ACTIVITY_TYPE_STANDARD
                && bubbleController.shouldBeAppBubble(task)
    private fun ActivityManager.RunningTaskInfo?.isAppBubbleMovingToFront(): Boolean {
        return this?.activityType == ACTIVITY_TYPE_STANDARD
                && bubbleController.shouldBeAppBubble(this)
    }

    /** Selects and expands a bubble that is currently in the stack. */
+2 −1
Original line number Diff line number Diff line
@@ -108,7 +108,8 @@ class BubbleTaskView(val taskView: TaskView, executor: Executor) {
     * This should be called after all other cleanup animations have finished.
     */
    fun cleanup() {
        if (isBubbleToFullscreen(taskView.taskInfo)) {
        val task = taskView.taskInfo
        if (task.isBubbleToFullscreen()) {
            taskView.unregisterTask()
        } else {
            taskView.removeTask()
+11 −11
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@ import static android.app.ActivityTaskManager.INVALID_TASK_ID;

import static com.android.wm.shell.Flags.enableEnterSplitRemoveBubble;
import static com.android.wm.shell.bubbles.util.BubbleUtils.getExitBubbleTransaction;
import static com.android.wm.shell.bubbles.util.BubbleUtils.isBubbleToSplit;
import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_BUBBLES_NOISY;

import android.app.ActivityManager;
@@ -159,29 +160,28 @@ public class BubblesTransitionObserver implements Transitions.TransitionObserver

    private void removeBubbleIfLaunchingToSplit(@NonNull TransitionInfo info) {
        if (mSplitScreenController.get().isEmpty()) return;
        SplitScreenController splitScreenController = mSplitScreenController.get().get();
        for (TransitionInfo.Change change : info.getChanges()) {
            ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo();
        for (final TransitionInfo.Change change : info.getChanges()) {
            final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo();
            if (taskInfo == null) continue;
            Bubble bubble = mBubbleData.getBubbleInStackWithTaskId(taskInfo.taskId);
            final Bubble bubble = mBubbleData.getBubbleInStackWithTaskId(taskInfo.taskId);
            if (bubble == null) continue;
            if (!splitScreenController.isTaskRootOrStageRoot(taskInfo.parentTaskId)) continue;
            if (!isBubbleToSplit(taskInfo, mSplitScreenController)) continue;
            // There is a bubble task that is moving to split screen
            ProtoLog.d(WM_SHELL_BUBBLES_NOISY, "BubblesTransitionObserver.onTransitionReady(): "
                    + "removing bubble for task launching into split taskId=%d", taskInfo.taskId);
            TaskViewTaskController taskViewTaskController = bubble.getTaskView().getController();
            ShellTaskOrganizer taskOrganizer = taskViewTaskController.getTaskOrganizer();
            WindowContainerTransaction wct = getExitBubbleTransaction(taskInfo.token,
            final TaskViewTaskController controller = bubble.getTaskView().getController();
            final ShellTaskOrganizer taskOrganizer = controller.getTaskOrganizer();
            final WindowContainerTransaction wct = getExitBubbleTransaction(taskInfo.token,
                    bubble.getTaskView().getCaptionInsetsOwner());

            // Notify the task removal, but block all TaskViewTransitions during removal so we can
            // clear them without triggering
            final IBinder gate = new Binder();
            mTaskViewTransitions.enqueueExternal(taskViewTaskController, () -> gate);
            mTaskViewTransitions.enqueueExternal(controller, () -> gate);

            taskOrganizer.applyTransaction(wct);
            taskViewTaskController.notifyTaskRemovalStarted(taskInfo);
            mTaskViewTransitions.removePendingTransitions(taskViewTaskController);
            controller.notifyTaskRemovalStarted(taskInfo);
            mTaskViewTransitions.removePendingTransitions(controller);
            mTaskViewTransitions.onExternalDone(gate);
        }
    }
+18 −4
Original line number Diff line number Diff line
@@ -25,6 +25,9 @@ import android.view.WindowInsets
import android.window.WindowContainerToken
import android.window.WindowContainerTransaction
import com.android.wm.shell.shared.bubbles.BubbleAnythingFlagHelper
import com.android.wm.shell.splitscreen.SplitScreenController
import dagger.Lazy
import java.util.Optional

object BubbleUtils {

@@ -126,13 +129,24 @@ object BubbleUtils {

    /** Returns true if the task is valid for Bubble. */
    @JvmStatic
    fun isValidToBubble(taskInfo: ActivityManager.RunningTaskInfo?): Boolean {
        return taskInfo != null && taskInfo.supportsMultiWindow
    fun ActivityManager.RunningTaskInfo?.isValidToBubble(): Boolean {
        return this?.supportsMultiWindow == true
    }

    /** Determines if a bubble task is moving to fullscreen based on its windowing mode. */
    fun isBubbleToFullscreen(task: ActivityManager.RunningTaskInfo?): Boolean {
    @JvmStatic
    fun ActivityManager.RunningTaskInfo?.isBubbleToFullscreen(): Boolean {
        return BubbleAnythingFlagHelper.enableCreateAnyBubble()
                && task?.windowingMode == WINDOWING_MODE_FULLSCREEN
                && this?.windowingMode == WINDOWING_MODE_FULLSCREEN
    }

    /** Determines if a bubble task is moving to split-screen based on its parent task. */
    @JvmStatic
    fun ActivityManager.RunningTaskInfo?.isBubbleToSplit(
        splitScreenController: Lazy<Optional<SplitScreenController>>,
    ): Boolean {
        return this?.hasParentTask() == true && splitScreenController.get()
            .map { it.isTaskRootOrStageRoot(parentTaskId) }
            .orElse(false)
    }
}