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

Commit aafdeb1f authored by Eric Lin's avatar Eric Lin
Browse files

Add ActivityTransitionInfo for activity tansition.

This change introduces the ActivityTransitionInfo object, which
encapsulates activity transition details within TransitionInfo. This new
object clarifies the task ID, pertains specifically to an activity. For
task transitions, TransitionInfo already includes a task ID in
mTaskInfo, so separating activity-specific information into
ActivityTransitionInfo prevents confusion and allows transition
observers to perform more precise checks based on the activity's task
ID.

Bug: 390047887
Flag: EXEMPT bug fix
Test: atest FrameworksCoreTests:ConfigurationChangeSettingTest
Test: atest FrameworksCoreTests:TransitionInfoTest
Test: atest PlatformAnimationLibCoreTests:OriginTransitionSessionTest
Test: atest WmTests:TransitionTests
Change-Id: Ie82abd0f9bcde63afea38d46f8ee9d8cc6e749a9
parent a302a178
Loading
Loading
Loading
Loading
+25 −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 android.window;

/**
 * Holds information specific to an activity during a transition.
 *
 * @see TransitionInfo.Change
 * @hide
 */
parcelable ActivityTransitionInfo;
+184 −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 android.window;

import static com.android.internal.annotations.VisibleForTesting.Visibility.PROTECTED;

import android.annotation.NonNull;
import android.content.ComponentName;
import android.os.Parcelable;

import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.DataClass;

/**
 * Holds information specific to an activity during a transition.
 *
 * <p>This class is used within {@link TransitionInfo.Change} to encapsulate
 * details relevant only when the transitioning container is an activity.
 *
 * @hide
 */
@DataClass(genConstructor = true, genGetters = true, genEqualsHashCode = true,
        genParcelable = true, genToString = true)
public final class ActivityTransitionInfo implements Parcelable {
    @NonNull
    private final ComponentName mComponent;
    private final int mTaskId;

    @VisibleForTesting(visibility = PROTECTED)
    public ActivityTransitionInfo(@NonNull ActivityTransitionInfo other) {
        this(other.mComponent.clone(), other.mTaskId);
    }



    // Code below generated by codegen v1.0.23.
    //
    // DO NOT MODIFY!
    // CHECKSTYLE:OFF Generated code
    //
    // To regenerate run:
    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/window/ActivityTransitionInfo.java
    //
    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
    //   Settings > Editor > Code Style > Formatter Control
    //@formatter:off


    @DataClass.Generated.Member
    public ActivityTransitionInfo(
            @NonNull ComponentName component,
            int taskId) {
        this.mComponent = component;
        com.android.internal.util.AnnotationValidations.validate(
                NonNull.class, null, mComponent);
        this.mTaskId = taskId;

        // onConstructed(); // You can define this method to get a callback
    }

    @DataClass.Generated.Member
    public @NonNull ComponentName getComponent() {
        return mComponent;
    }

    @DataClass.Generated.Member
    public int getTaskId() {
        return mTaskId;
    }

    @Override
    @DataClass.Generated.Member
    public String toString() {
        // You can override field toString logic by defining methods like:
        // String fieldNameToString() { ... }

        return "ActivityTransitionInfo { " +
                "component = " + mComponent + ", " +
                "taskId = " + mTaskId +
        " }";
    }

    @Override
    @DataClass.Generated.Member
    public boolean equals(@android.annotation.Nullable Object o) {
        // You can override field equality logic by defining either of the methods like:
        // boolean fieldNameEquals(ActivityTransitionInfo other) { ... }
        // boolean fieldNameEquals(FieldType otherValue) { ... }

        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        @SuppressWarnings("unchecked")
        ActivityTransitionInfo that = (ActivityTransitionInfo) o;
        //noinspection PointlessBooleanExpression
        return true
                && java.util.Objects.equals(mComponent, that.mComponent)
                && mTaskId == that.mTaskId;
    }

    @Override
    @DataClass.Generated.Member
    public int hashCode() {
        // You can override field hashCode logic by defining methods like:
        // int fieldNameHashCode() { ... }

        int _hash = 1;
        _hash = 31 * _hash + java.util.Objects.hashCode(mComponent);
        _hash = 31 * _hash + mTaskId;
        return _hash;
    }

    @Override
    @DataClass.Generated.Member
    public void writeToParcel(@NonNull android.os.Parcel dest, int flags) {
        // You can override field parcelling by defining methods like:
        // void parcelFieldName(Parcel dest, int flags) { ... }

        dest.writeTypedObject(mComponent, flags);
        dest.writeInt(mTaskId);
    }

    @Override
    @DataClass.Generated.Member
    public int describeContents() { return 0; }

    /** @hide */
    @SuppressWarnings({"unchecked", "RedundantCast"})
    @DataClass.Generated.Member
    /* package-private */ ActivityTransitionInfo(@NonNull android.os.Parcel in) {
        // You can override field unparcelling by defining methods like:
        // static FieldType unparcelFieldName(Parcel in) { ... }

        ComponentName component = (ComponentName) in.readTypedObject(ComponentName.CREATOR);
        int taskId = in.readInt();

        this.mComponent = component;
        com.android.internal.util.AnnotationValidations.validate(
                NonNull.class, null, mComponent);
        this.mTaskId = taskId;

        // onConstructed(); // You can define this method to get a callback
    }

    @DataClass.Generated.Member
    public static final @NonNull Parcelable.Creator<ActivityTransitionInfo> CREATOR
            = new Parcelable.Creator<ActivityTransitionInfo>() {
        @Override
        public ActivityTransitionInfo[] newArray(int size) {
            return new ActivityTransitionInfo[size];
        }

        @Override
        public ActivityTransitionInfo createFromParcel(@NonNull android.os.Parcel in) {
            return new ActivityTransitionInfo(in);
        }
    };

    @DataClass.Generated(
            time = 1747732592428L,
            codegenVersion = "1.0.23",
            sourceFile = "frameworks/base/core/java/android/window/ActivityTransitionInfo.java",
            inputSignatures = "private final @android.annotation.NonNull android.content.ComponentName mComponent\nprivate final  int mTaskId\nclass ActivityTransitionInfo extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genConstructor=true, genGetters=true, genEqualsHashCode=true, genParcelable=true, genToString=true)")
    @Deprecated
    private void __metadata() {}


    //@formatter:on
    // End of generated code

}
+22 −12
Original line number Diff line number Diff line
@@ -655,7 +655,7 @@ public final class TransitionInfo implements Parcelable {
        private @ColorInt int mBackgroundColor;
        private SurfaceControl mSnapshot = null;
        private float mSnapshotLuma;
        private ComponentName mActivityComponent = null;
        private ActivityTransitionInfo mActivityTransitionInfo = null;
        private AnimationOptions mAnimationOptions = null;
        private IBinder mTaskFragmentToken = null;

@@ -664,7 +664,7 @@ public final class TransitionInfo implements Parcelable {
            mLeash = leash;
        }

        private Change(Parcel in) {
        private Change(@NonNull Parcel in) {
            mContainer = in.readTypedObject(WindowContainerToken.CREATOR);
            mParent = in.readTypedObject(WindowContainerToken.CREATOR);
            mLastParent = in.readTypedObject(WindowContainerToken.CREATOR);
@@ -687,7 +687,7 @@ public final class TransitionInfo implements Parcelable {
            mBackgroundColor = in.readInt();
            mSnapshot = in.readTypedObject(SurfaceControl.CREATOR);
            mSnapshotLuma = in.readFloat();
            mActivityComponent = in.readTypedObject(ComponentName.CREATOR);
            mActivityTransitionInfo = in.readTypedObject(ActivityTransitionInfo.CREATOR);
            mAnimationOptions = in.readTypedObject(AnimationOptions.CREATOR);
            mTaskFragmentToken = in.readStrongBinder();
        }
@@ -713,7 +713,9 @@ public final class TransitionInfo implements Parcelable {
            out.mBackgroundColor = mBackgroundColor;
            out.mSnapshot = mSnapshot != null ? new SurfaceControl(mSnapshot, "localRemote") : null;
            out.mSnapshotLuma = mSnapshotLuma;
            out.mActivityComponent = mActivityComponent;
            if (mActivityTransitionInfo != null) {
                out.mActivityTransitionInfo = new ActivityTransitionInfo(mActivityTransitionInfo);
            }
            out.mAnimationOptions = mAnimationOptions;
            out.mTaskFragmentToken = mTaskFragmentToken;
            return out;
@@ -818,9 +820,9 @@ public final class TransitionInfo implements Parcelable {
            mSnapshotLuma = luma;
        }

        /** Sets the component-name of the container. Container must be an Activity. */
        public void setActivityComponent(@Nullable ComponentName component) {
            mActivityComponent = component;
        /** Sets the activity-specific transition information. Container must be an Activity. */
        public void setActivityTransitionInfo(@Nullable ActivityTransitionInfo info) {
            mActivityTransitionInfo = info;
        }

        /**
@@ -982,7 +984,16 @@ public final class TransitionInfo implements Parcelable {
        /** @return the component-name of this container (if it is an activity). */
        @Nullable
        public ComponentName getActivityComponent() {
            return mActivityComponent;
            return mActivityTransitionInfo != null ? mActivityTransitionInfo.getComponent() : null;
        }

        /**
         * @return the activity-specific transition information, or {@code null} if this container
         * is not an activity.
         */
        @Nullable
        public ActivityTransitionInfo getActivityTransitionInfo() {
            return mActivityTransitionInfo;
        }

        /**
@@ -1026,7 +1037,7 @@ public final class TransitionInfo implements Parcelable {
            dest.writeInt(mBackgroundColor);
            dest.writeTypedObject(mSnapshot, flags);
            dest.writeFloat(mSnapshotLuma);
            dest.writeTypedObject(mActivityComponent, flags);
            dest.writeTypedObject(mActivityTransitionInfo, flags);
            dest.writeTypedObject(mAnimationOptions, flags);
            dest.writeStrongBinder(mTaskFragmentToken);
        }
@@ -1098,9 +1109,8 @@ public final class TransitionInfo implements Parcelable {
            if (mLastParent != null) {
                sb.append(" lastParent="); sb.append(mLastParent);
            }
            if (mActivityComponent != null) {
                sb.append(" component=");
                sb.append(mActivityComponent.flattenToShortString());
            if (mActivityTransitionInfo != null) {
                sb.append(" activity=").append(mActivityTransitionInfo);
            }
            if (mTaskInfo != null) {
                sb.append(" taskParent=");
+6 −17
Original line number Diff line number Diff line
@@ -17,7 +17,7 @@
package android.window

import android.os.Parcel
import android.os.Parcelable
import android.os.test.recreateFromParcel
import android.platform.test.annotations.Presubmit
import android.view.Display.DEFAULT_DISPLAY
import android.window.ConfigurationChangeSetting.SETTING_TYPE_UNKNOWN
@@ -42,6 +42,8 @@ import org.mockito.kotlin.stub
import org.mockito.kotlin.verify

/**
 * Unit tests for [ConfigurationChangeSetting].
 *
 * Build/Install/Run:
 * atest FrameworksCoreTests:ConfigurationChangeSettingTest
 */
@@ -84,7 +86,7 @@ class ConfigurationChangeSettingTest {
    fun densitySettingParcelable_appClient_recreatesSucceeds() {
        val setting = ConfigurationChangeSetting.DensitySetting(DEFAULT_DISPLAY, TEST_DENSITY)

        val recreated = setting.recreateFromParcel()
        val recreated = setting.recreateFromParcel(DEFAULT_CREATOR)

        verify(mMockConfigurationChangeSettingInternal, never()).createImplFromParcel(any(), any())
        assertThat(recreated).isEqualTo(setting)
@@ -111,7 +113,7 @@ class ConfigurationChangeSettingTest {
    fun fontScaleSettingParcelable_appClient_recreatesSucceeds() {
        val setting = ConfigurationChangeSetting.FontScaleSetting(TEST_FONT_SCALE)

        val recreated = setting.recreateFromParcel()
        val recreated = setting.recreateFromParcel(DEFAULT_CREATOR)

        verify(mMockConfigurationChangeSettingInternal, never()).createImplFromParcel(any(), any())
        assertThat(recreated).isEqualTo(setting)
@@ -144,18 +146,5 @@ class ConfigurationChangeSettingTest {

        private fun tearDownLocalService() =
            LocalServices.removeServiceForTest(ConfigurationChangeSettingInternal::class.java)

        private fun ConfigurationChangeSetting.recreateFromParcel(
            creator: Parcelable.Creator<ConfigurationChangeSetting> = DEFAULT_CREATOR,
        ): ConfigurationChangeSetting {
            val parcel = Parcel.obtain()
            try {
                writeToParcel(parcel, 0)
                parcel.setDataPosition(0)
                return creator.createFromParcel(parcel)
            } finally {
                parcel.recycle()
            }
        }
    }
}
+78 −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 android.window

import android.content.ComponentName
import android.os.test.recreateFromParcel
import android.platform.test.annotations.Presubmit
import android.view.SurfaceControl
import android.view.WindowManager.TRANSIT_OPEN
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.google.common.truth.Truth.assertThat
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.kotlin.mock

/**
 * Unit tests for [TransitionInfo].
 *
 * Build/Install/Run:
 * atest FrameworksCoreTests:TransitionInfoTest
 */
@SmallTest
@Presubmit
@RunWith(AndroidJUnit4::class)
class TransitionInfoTest {
    private val token = WindowContainerToken(mock<IWindowContainerToken>())
    private val change = TransitionInfo.Change(token, SurfaceControl() /* leash */)
    private val activityTransitionInfo = ActivityTransitionInfo(component, TASK_ID)

    @Test
    fun parcelable_recreatewithActivityTransitionInfoSucceeds() {
        change.activityTransitionInfo = activityTransitionInfo
        val transitionInfo = TransitionInfo(TRANSIT_OPEN, 0 /* flags */)
        transitionInfo.addChange(change)

        val createdFromParcel = transitionInfo.recreateFromParcel(TransitionInfo.CREATOR)

        assertThat(createdFromParcel.changes).hasSize(1)
        val chg = createdFromParcel.changes[0]
        assertThat(chg.activityTransitionInfo).isEqualTo(activityTransitionInfo)
    }

    @Test
    fun localRemoteCopy_copiesActivityTransitionInfo() {
        change.activityTransitionInfo = activityTransitionInfo
        val transitionInfo = TransitionInfo(TRANSIT_OPEN, 0 /* flags */)
        transitionInfo.addChange(change)

        val copiedTransitionInfo = transitionInfo.localRemoteCopy()

        assertThat(copiedTransitionInfo.changes).hasSize(1)
        val chg = copiedTransitionInfo.changes[0]
        assertThat(chg.activityTransitionInfo).isEqualTo(activityTransitionInfo)
        assertThat(chg.activityTransitionInfo).isNotSameInstanceAs(activityTransitionInfo)
    }

    companion object {
        private const val TASK_ID = 123
        private const val TEST_PACKAGE_NAME = "com.example.app"
        private const val TEST_CLASS_NAME = "com.example.app.MainActivity"
        private val component = ComponentName(TEST_PACKAGE_NAME, TEST_CLASS_NAME)
    }
}
 No newline at end of file
Loading