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

Commit c9309197 authored by Eric Lin's avatar Eric Lin Committed by Android (Google) Code Review
Browse files

Merge "Refactor and share TransitionInfoBuilder for shell tests." into main

parents 02019a20 74a1163a
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -44,14 +44,14 @@ android_robolectric_test {
    // TODO(b/323188766): Include BubbleStackViewTest once the robolectric issue is fixed.
    exclude_srcs: ["src/com/android/wm/shell/bubbles/BubbleStackViewTest.kt"],
    static_libs: [
        "WMShellTests-utils",
        "junit",
        "androidx.core_core-animation-testing",
        "androidx.test.runner",
        "androidx.test.rules",
        "androidx.test.ext.junit",
        "mockito-robolectric-prebuilt",
        "mockito-robolectric-prebuilt", // mockito deps order matters!
        "mockito-kotlin2",
        "WMShellTests-utils", // order matters because it depends on mockito-kotlin2
        "platform-parametric-runner-lib",
        "truth",
        "flag-junit-base",
+1 −2
Original line number Diff line number Diff line
@@ -168,8 +168,7 @@ public class StartingWindowControllerTests extends ShellTestCase {
        st.clear();
        final IBinder secondToken = new Binder();
        final TransitionInfo secondInfo = new TransitionInfoBuilder(TRANSIT_OPEN)
                .addChange(TRANSIT_OPEN, FLAG_IS_BEHIND_STARTING_WINDOW,
                        null, null, null).build();
                .addChange(TRANSIT_OPEN, FLAG_IS_BEHIND_STARTING_WINDOW).build();
        observer.onAddingWindow(taskId, token, appToken);
        observer.onTransitionReady(token, info, st, st);
        waitTransactionCommit(st);
+0 −115
Original line number Diff line number Diff line
/*
 * Copyright (C) 2023 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.transition;

import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;

import static org.mockito.Mockito.mock;

import android.annotation.Nullable;
import android.app.ActivityManager;
import android.content.ComponentName;
import android.os.IBinder;
import android.view.SurfaceControl;
import android.view.WindowManager;
import android.window.TransitionInfo;

/**
 * Utility for creating/editing synthetic TransitionInfos for tests.
 */
public class TransitionInfoBuilder {
    final TransitionInfo mInfo;
    static final int DISPLAY_ID = 0;

    public TransitionInfoBuilder(@WindowManager.TransitionType int type) {
        this(type, 0 /* flags */);
    }

    public TransitionInfoBuilder(@WindowManager.TransitionType int type,
            @WindowManager.TransitionFlags int flags) {
        this(type, flags, false /* asNoOp */);
    }

    public TransitionInfoBuilder(@WindowManager.TransitionType int type,
            @WindowManager.TransitionFlags int flags, boolean asNoOp) {
        mInfo = new TransitionInfo(type, flags);
        if (!asNoOp) {
            mInfo.addRootLeash(DISPLAY_ID, createMockSurface(true /* valid */), 0, 0);
        }
    }

    public TransitionInfoBuilder addChange(@WindowManager.TransitionType int mode,
            @TransitionInfo.ChangeFlags int flags,
            @Nullable ActivityManager.RunningTaskInfo taskInfo,
            @Nullable ComponentName activityComponent, @Nullable IBinder taskFragmentToken) {
        final TransitionInfo.Change change = new TransitionInfo.Change(
                taskInfo != null ? taskInfo.token : null, createMockSurface(true /* valid */));
        change.setMode(mode);
        change.setFlags(flags);
        change.setTaskInfo(taskInfo);
        change.setActivityComponent(activityComponent);
        change.setTaskFragmentToken(taskFragmentToken);
        return addChange(change);
    }

    /** Add a change to the TransitionInfo */
    public TransitionInfoBuilder addChange(@WindowManager.TransitionType int mode,
            @TransitionInfo.ChangeFlags int flags, ActivityManager.RunningTaskInfo taskInfo) {
        return addChange(mode, flags, taskInfo, null /* activityComponent */,
                null /* taskFragmentToken */);
    }

    public TransitionInfoBuilder addChange(@WindowManager.TransitionType int mode,
            ActivityManager.RunningTaskInfo taskInfo) {
        return addChange(mode, TransitionInfo.FLAG_NONE, taskInfo);
    }

    /** Add a change to the TransitionInfo */
    public TransitionInfoBuilder addChange(@WindowManager.TransitionType int mode,
            ComponentName activityComponent) {
        return addChange(mode, TransitionInfo.FLAG_NONE, null /* taskinfo */, activityComponent,
                null /* taskFragmentToken */);
    }

    public TransitionInfoBuilder addChange(@WindowManager.TransitionType int mode) {
        return addChange(mode, TransitionInfo.FLAG_NONE, null /* taskInfo */);
    }

    /** Add a change with a TaskFragment token to the TransitionInfo */
    public TransitionInfoBuilder addChange(@WindowManager.TransitionType int mode,
            @Nullable IBinder taskFragmentToken) {
        return addChange(mode, TransitionInfo.FLAG_NONE, null /* taskInfo */,
                null /* activityComponent */, taskFragmentToken);
    }

    public TransitionInfoBuilder addChange(TransitionInfo.Change change) {
        change.setDisplayId(DISPLAY_ID, DISPLAY_ID);
        mInfo.addChange(change);
        return this;
    }

    public TransitionInfo build() {
        return mInfo;
    }

    private static SurfaceControl createMockSurface(boolean valid) {
        SurfaceControl sc = mock(SurfaceControl.class);
        doReturn(valid).when(sc).isValid();
        doReturn("TestSurface").when(sc).toString();
        return sc;
    }
}
+1 −0
Original line number Diff line number Diff line
@@ -32,6 +32,7 @@ java_library {
    ],
    static_libs: [
        "WindowManager-Shell",
        "mockito-kotlin2",
        "truth",
    ],
}
+149 −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.transition

import android.app.ActivityManager
import android.content.ComponentName
import android.os.IBinder
import android.view.SurfaceControl
import android.view.WindowManager
import android.window.TransitionInfo
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.mock

/**
 * Utility for creating/editing synthetic [TransitionInfo] for tests.
 *
 * @param type the type of the transition. See [WindowManager.TransitionType].
 * @param flags the flags for the transition. See [WindowManager.TransitionFlags].
 * @param asNoOp if true, the root leash will not be added.
 */
class TransitionInfoBuilder @JvmOverloads constructor(
    @WindowManager.TransitionType type: Int,
    @WindowManager.TransitionFlags flags: Int = 0,
    asNoOp: Boolean = false,
) {
    // The underlying TransitionInfo object being built.
    private val info: TransitionInfo = TransitionInfo(type, flags).apply {
        if (asNoOp) {
            return@apply
        }
        // Add a root leash by default, unless asNoOp is true.
        addRootLeash(
            DISPLAY_ID,
            createMockSurface(), /* leash */
            0, /* offsetLeft */
            0, /* offsetTop */
        )
    }

    /** Adds a change to the [TransitionInfo]. */
    @JvmOverloads
    fun addChange(
        @WindowManager.TransitionType mode: Int,
        @TransitionInfo.ChangeFlags flags: Int = TransitionInfo.FLAG_NONE,
    ) = addChange(mode, flags, activityComponent = null, taskInfo = null)

    /** Adds a change to the [TransitionInfo] for task transition with [flags]. */
    fun addChange(
        @WindowManager.TransitionType mode: Int,
        @TransitionInfo.ChangeFlags flags: Int,
        taskInfo: ActivityManager.RunningTaskInfo?,
    ) = addChange(mode, flags, activityComponent = null, taskInfo = taskInfo)

    /** Adds a change to the [TransitionInfo] for task transition. */
    fun addChange(
        @WindowManager.TransitionType mode: Int,
        taskInfo: ActivityManager.RunningTaskInfo?,
    ) = addChange(mode, activityComponent = null, taskInfo = taskInfo)

    /** Adds a change to the [TransitionInfo] for activity transition. */
    fun addChange(@WindowManager.TransitionType mode: Int, activityComponent: ComponentName?) =
        addChange(mode, activityComponent = activityComponent, taskInfo = null)

    /** Add a change to the [TransitionInfo] for task fragment. */
    fun addChange(@WindowManager.TransitionType mode: Int, taskFragmentToken: IBinder?) =
        addChange(
            mode,
            activityComponent = null,
            taskInfo = null,
            taskFragmentToken = taskFragmentToken,
        )

    /**
     * Adds a change to the [TransitionInfo].
     *
     * @param mode the mode of the change. See [WindowManager.TransitionType].
     * @param flags the flags for this change. See [TransitionInfo.ChangeFlags].
     * @param activityComponent the component associated with this change for activity transition.
     * @param taskInfo the task info associated with this change for task transition.
     * @param taskFragmentToken the task fragment token associated with this change.
     * @return this [TransitionInfoBuilder] instance for chaining.
     */
    private fun addChange(
        @WindowManager.TransitionType mode: Int,
        @TransitionInfo.ChangeFlags flags: Int = TransitionInfo.FLAG_NONE,
        activityComponent: ComponentName? = null,
        taskInfo: ActivityManager.RunningTaskInfo? = null,
        taskFragmentToken: IBinder? = null,
    ): TransitionInfoBuilder {
        val container = taskInfo?.token
        val leash = createMockSurface()
        val change = TransitionInfo.Change(container, leash).apply {
            setMode(mode)
            setFlags(flags)
            setActivityComponent(activityComponent)
            setTaskInfo(taskInfo)
            setTaskFragmentToken(taskFragmentToken)
        }
        return addChange(change)
    }

    /**
     * Adds a pre-configured change to the [TransitionInfo].
     *
     * @param change the TransitionInfo.Change object to add.
     * @return this TransitionInfoBuilder instance for chaining.
     */
    fun addChange(change: TransitionInfo.Change): TransitionInfoBuilder {
        // Set the display ID for the change.
        change.setDisplayId(DISPLAY_ID /* start */, DISPLAY_ID /* end */)
        // Add the change to the internal TransitionInfo object.
        info.addChange(change)
        return this // Return this for fluent builder pattern.
    }

    /**
     * Builds and returns the configured [TransitionInfo] object.
     *
     * @return the constructed [TransitionInfo].
     */
    fun build(): TransitionInfo {
        return info
    }

    companion object {
        // Default display ID for root leashes and changes.
        const val DISPLAY_ID = 0

        // Create a mock SurfaceControl for testing.
        private fun createMockSurface() = mock<SurfaceControl> {
            on { isValid } doReturn true
            on { toString() } doReturn "TestSurface"
        }
    }
}