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

Commit 58ce52be authored by Liran Binyamin's avatar Liran Binyamin
Browse files

Merge bubble transition with unfold

This change merges the bubble task view bounds change with the unfold
transition in order to avoid clipped expanded view.

For bubble bar, the task view will switch surfaces after unfolding
so nothing is merged in that case.

Bug: 390050545
Flag: EXEMPT bug fix
Test: atest TaskViewTransitionsTest
Test: atest UnfoldTransitionHandlerTest
Test: manual
       - disable bubble bar flag
       - create a floating bubble in folded state
       - pull up the IME
       - unfold and observe expanded view bounds
Test: repeat steps above with bubble bar flag enabled
Test: repeat steps above with task view repo flag disabled
Change-Id: I22787311159f2940d37b5aaa071c9b05aae15d17
parent cd65552d
Loading
Loading
Loading
Loading
+28 −1
Original line number Diff line number Diff line
@@ -81,6 +81,7 @@ import android.view.WindowInsets;
import android.view.WindowManager;
import android.window.ScreenCapture;
import android.window.ScreenCapture.SynchronousScreenCaptureListener;
import android.window.TransitionInfo;
import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;

@@ -155,7 +156,7 @@ import java.util.function.IntConsumer;
 */
public class BubbleController implements ConfigurationChangeListener,
        RemoteCallable<BubbleController>, Bubbles.SysuiProxy.Provider,
        BubbleBarDragListener {
        BubbleBarDragListener, BubbleTaskUnfoldTransitionMerger {

    private static final String TAG = TAG_WITH_CLASS_NAME ? "BubbleController" : TAG_BUBBLES;

@@ -2174,6 +2175,32 @@ public class BubbleController implements ConfigurationChangeListener,
        });
    }

    @Override
    public boolean mergeTaskWithUnfold(@NonNull ActivityManager.RunningTaskInfo taskInfo,
            @NonNull TransitionInfo.Change change,
            @NonNull SurfaceControl.Transaction startT,
            @NonNull SurfaceControl.Transaction finishT) {
        if (!mBubbleTransitions.mTaskViewTransitions.isTaskViewTask(taskInfo)) {
            // if this task isn't managed by bubble transitions just bail.
            return false;
        }
        if (isShowingAsBubbleBar()) {
            // if bubble bar is enabled, the task view will switch to a new surface on unfold, so we
            // should not merge the transition.
            return false;
        }

        boolean merged = mBubbleTransitions.mTaskViewTransitions.updateBoundsForUnfold(
                change.getEndAbsBounds(), startT, finishT, change.getTaskInfo(), change.getLeash());
        if (merged) {
            BubbleViewProvider selectedBubble = mBubbleData.getSelectedBubble();
            if (selectedBubble != null && selectedBubble.getExpandedView() != null) {
                selectedBubble.getExpandedView().onContainerClipUpdate();
            }
        }
        return merged;
    }

    /** When bubbles are floating, this will be used to notify the floating views. */
    private final BubbleViewCallback mBubbleStackViewCallback = new BubbleViewCallback() {
        @Override
+2 −1
Original line number Diff line number Diff line
@@ -843,7 +843,8 @@ public class BubbleExpandedView extends LinearLayout {
        onContainerClipUpdate();
    }

    private void onContainerClipUpdate() {
    /** Updates the clip bounds. */
    public void onContainerClipUpdate() {
        if (mTopClip == 0 && mBottomClip == 0 && mRightClip == 0 && mLeftClip == 0) {
            if (mIsClipping) {
                mIsClipping = false;
+33 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2025 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.bubbles

import android.app.ActivityManager
import android.view.SurfaceControl
import android.window.TransitionInfo

/** Merges a bubble task transition with the unfold transition. */
interface BubbleTaskUnfoldTransitionMerger {

    /** Attempts to merge the transition. Returns `true` if the change was merged. */
    fun mergeTaskWithUnfold(
        taskInfo: ActivityManager.RunningTaskInfo,
        change: TransitionInfo.Change,
        startT: SurfaceControl.Transaction,
        finishT: SurfaceControl.Transaction
    ): Boolean
}
+12 −2
Original line number Diff line number Diff line
@@ -62,6 +62,7 @@ import com.android.wm.shell.bubbles.BubbleEducationController;
import com.android.wm.shell.bubbles.BubbleLogger;
import com.android.wm.shell.bubbles.BubblePositioner;
import com.android.wm.shell.bubbles.BubbleResizabilityChecker;
import com.android.wm.shell.bubbles.BubbleTaskUnfoldTransitionMerger;
import com.android.wm.shell.bubbles.bar.BubbleBarDragListener;
import com.android.wm.shell.bubbles.storage.BubblePersistentRepository;
import com.android.wm.shell.common.DisplayController;
@@ -242,6 +243,13 @@ public abstract class WMShellModule {
                context, logger, positioner, educationController, mainExecutor, bgExecutor);
    }

    @WMSingleton
    @Provides
    static Optional<BubbleTaskUnfoldTransitionMerger> provideBubbleTaskUnfoldTransitionMerger(
            Optional<BubbleController> bubbleController) {
        return bubbleController.map(controller -> controller);
    }

    // Note: Handler needed for LauncherApps.register
    @WMSingleton
    @Provides
@@ -703,7 +711,8 @@ public abstract class WMShellModule {
            Transitions transitions,
            @ShellMainThread ShellExecutor executor,
            @ShellMainThread Handler handler,
            ShellInit shellInit) {
            ShellInit shellInit,
            Optional<BubbleTaskUnfoldTransitionMerger> bubbleTaskUnfoldTransitionMerger) {
        return new UnfoldTransitionHandler(
                shellInit,
                progressProvider.get(),
@@ -712,7 +721,8 @@ public abstract class WMShellModule {
                transactionPool,
                executor,
                handler,
                transitions);
                transitions,
                bubbleTaskUnfoldTransitionMerger);
    }

    @WMSingleton
+42 −0
Original line number Diff line number Diff line
@@ -728,6 +728,48 @@ public class TaskViewTransitions implements Transitions.TransitionHandler, TaskV
        taskView.notifyAppeared(newTask);
    }

    /**
     * Updates bounds for the task view during an unfold transition.
     *
     * @return true if the task was found and a transition for this task is pending. false
     * otherwise.
     */
    public boolean updateBoundsForUnfold(Rect bounds, SurfaceControl.Transaction startTransaction,
            SurfaceControl.Transaction finishTransaction,
            ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) {
        final TaskViewTaskController taskView = findTaskView(taskInfo);
        if (taskView == null) {
            return false;
        }

        final PendingTransition pendingTransition = findPending(taskView, TRANSIT_CHANGE);
        if (pendingTransition == null) {
            return false;
        }

        mPending.remove(pendingTransition);

        // reparent the task under the task view surface and set the bounds on it
        startTransaction.reparent(leash, taskView.getSurfaceControl())
                .setPosition(leash, 0, 0)
                .setWindowCrop(leash, bounds.width(), bounds.height())
                .show(leash);
        // the finish transaction would reparent the task back to the transition root, so reparent
        // it again to the task view surface
        finishTransaction.reparent(leash, taskView.getSurfaceControl())
                .setPosition(leash, 0, 0)
                .setWindowCrop(leash, bounds.width(), bounds.height());
        if (useRepo()) {
            final TaskViewRepository.TaskViewState state = mTaskViewRepo.byTaskView(taskView);
            if (state != null) {
                state.mBounds.set(bounds);
            }
        } else {
            updateBoundsState(taskView, bounds);
        }
        return true;
    }

    private void updateBounds(TaskViewTaskController taskView, Rect boundsOnScreen,
            SurfaceControl.Transaction startTransaction,
            SurfaceControl.Transaction finishTransaction,
Loading