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

Commit 03fdfe5c authored by Massimo Carli's avatar Massimo Carli
Browse files

[3/n] Add AppCompatTransitionInfo to ActivityTransitionInfo

The AppCompatTransitionInfo encapsulates information related
to App Compat in case the activity, target of a transition,
is letterboxed.
At the moment AppCompatTransitionInfo contains the letterboxBounds
which would not be available otherwise because of the missing
TaskInfo in the Transition's Change.

The AppCompatTransitionInfo is optional property of the existing
ActivityTransitionInfo.

Flag: EXEMPT bug fixing
Bug: 377875664
Test: atest FrameworksCoreTests:TransitionInfoTest
Test: atest WmTests:AppCompatUtilsTest
Test: atest WmTests:TransitionTests

Change-Id: I4f51148bd68e0afdd9257b670a03a6c354892688
parent cb0d5733
Loading
Loading
Loading
Loading
+30 −8
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ package android.window;
import static com.android.internal.annotations.VisibleForTesting.Visibility.PROTECTED;

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

@@ -39,13 +40,17 @@ public final class ActivityTransitionInfo implements Parcelable {
    @NonNull
    private final ComponentName mComponent;
    private final int mTaskId;
    @Nullable
    private final AppCompatTransitionInfo mAppCompatTransitionInfo;

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


    public ActivityTransitionInfo(@NonNull ComponentName component, int taskId) {
        this(component, taskId, null);
    }

    // Code below generated by codegen v1.0.23.
    //
@@ -63,11 +68,13 @@ public final class ActivityTransitionInfo implements Parcelable {
    @DataClass.Generated.Member
    public ActivityTransitionInfo(
            @NonNull ComponentName component,
            int taskId) {
            int taskId,
            @Nullable AppCompatTransitionInfo appCompatTransitionInfo) {
        this.mComponent = component;
        com.android.internal.util.AnnotationValidations.validate(
                NonNull.class, null, mComponent);
        this.mTaskId = taskId;
        this.mAppCompatTransitionInfo = appCompatTransitionInfo;

        // onConstructed(); // You can define this method to get a callback
    }
@@ -82,6 +89,11 @@ public final class ActivityTransitionInfo implements Parcelable {
        return mTaskId;
    }

    @DataClass.Generated.Member
    public @Nullable AppCompatTransitionInfo getAppCompatTransitionInfo() {
        return mAppCompatTransitionInfo;
    }

    @Override
    @DataClass.Generated.Member
    public String toString() {
@@ -90,13 +102,14 @@ public final class ActivityTransitionInfo implements Parcelable {

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

    @Override
    @DataClass.Generated.Member
    public boolean equals(@android.annotation.Nullable Object o) {
    public boolean equals(@Nullable Object o) {
        // You can override field equality logic by defining either of the methods like:
        // boolean fieldNameEquals(ActivityTransitionInfo other) { ... }
        // boolean fieldNameEquals(FieldType otherValue) { ... }
@@ -108,7 +121,8 @@ public final class ActivityTransitionInfo implements Parcelable {
        //noinspection PointlessBooleanExpression
        return true
                && java.util.Objects.equals(mComponent, that.mComponent)
                && mTaskId == that.mTaskId;
                && mTaskId == that.mTaskId
                && java.util.Objects.equals(mAppCompatTransitionInfo, that.mAppCompatTransitionInfo);
    }

    @Override
@@ -120,6 +134,7 @@ public final class ActivityTransitionInfo implements Parcelable {
        int _hash = 1;
        _hash = 31 * _hash + java.util.Objects.hashCode(mComponent);
        _hash = 31 * _hash + mTaskId;
        _hash = 31 * _hash + java.util.Objects.hashCode(mAppCompatTransitionInfo);
        return _hash;
    }

@@ -129,8 +144,12 @@ public final class ActivityTransitionInfo implements Parcelable {
        // You can override field parcelling by defining methods like:
        // void parcelFieldName(Parcel dest, int flags) { ... }

        byte flg = 0;
        if (mAppCompatTransitionInfo != null) flg |= 0x4;
        dest.writeByte(flg);
        dest.writeTypedObject(mComponent, flags);
        dest.writeInt(mTaskId);
        if (mAppCompatTransitionInfo != null) dest.writeTypedObject(mAppCompatTransitionInfo, flags);
    }

    @Override
@@ -144,13 +163,16 @@ public final class ActivityTransitionInfo implements Parcelable {
        // You can override field unparcelling by defining methods like:
        // static FieldType unparcelFieldName(Parcel in) { ... }

        byte flg = in.readByte();
        ComponentName component = (ComponentName) in.readTypedObject(ComponentName.CREATOR);
        int taskId = in.readInt();
        AppCompatTransitionInfo appCompatTransitionInfo = (flg & 0x4) == 0 ? null : (AppCompatTransitionInfo) in.readTypedObject(AppCompatTransitionInfo.CREATOR);

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

        // onConstructed(); // You can define this method to get a callback
    }
@@ -170,10 +192,10 @@ public final class ActivityTransitionInfo implements Parcelable {
    };

    @DataClass.Generated(
            time = 1747732592428L,
            time = 1748332897391L,
            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)")
            inputSignatures = "private final @android.annotation.NonNull android.content.ComponentName mComponent\nprivate final  int mTaskId\nprivate final @android.annotation.Nullable android.window.AppCompatTransitionInfo mAppCompatTransitionInfo\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() {}

+19 −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;

parcelable AppCompatTransitionInfo;
+159 −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.annotation.NonNull;
import android.graphics.Rect;
import android.os.Parcelable;

import com.android.internal.util.DataClass;

/**
 * Encapsulate all the information useful to AppCompat in an Activity transition.
 *
 * @hide
 */
@DataClass(genConstructor = true, genGetters = true, genEqualsHashCode = true,
        genParcelable = true, genToString = true)
public class AppCompatTransitionInfo implements Parcelable {

    @NonNull
    private final Rect mLetterboxBounds;


    // 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/AppCompatTransitionInfo.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 AppCompatTransitionInfo(
            @NonNull Rect letterboxBounds) {
        this.mLetterboxBounds = letterboxBounds;
        com.android.internal.util.AnnotationValidations.validate(
                NonNull.class, null, mLetterboxBounds);

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

    @DataClass.Generated.Member
    public @NonNull Rect getLetterboxBounds() {
        return mLetterboxBounds;
    }

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

        return "AppCompatTransitionInfo { " +
                "letterboxBounds = " + mLetterboxBounds +
        " }";
    }

    @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(AppCompatTransitionInfo other) { ... }
        // boolean fieldNameEquals(FieldType otherValue) { ... }

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

    @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(mLetterboxBounds);
        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(mLetterboxBounds, flags);
    }

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

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

        Rect letterboxBounds = (Rect) in.readTypedObject(Rect.CREATOR);

        this.mLetterboxBounds = letterboxBounds;
        com.android.internal.util.AnnotationValidations.validate(
                NonNull.class, null, mLetterboxBounds);

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

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

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

    @DataClass.Generated(
            time = 1748331474800L,
            codegenVersion = "1.0.23",
            sourceFile = "frameworks/base/core/java/android/window/AppCompatTransitionInfo.java",
            inputSignatures = "private final @android.annotation.NonNull android.graphics.Rect mLetterboxBounds\nclass AppCompatTransitionInfo 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

}
+35 −1
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
package android.window

import android.content.ComponentName
import android.graphics.Rect
import android.os.test.recreateFromParcel
import android.platform.test.annotations.Presubmit
import android.view.SurfaceControl
@@ -41,9 +42,12 @@ class TransitionInfoTest {
    private val token = WindowContainerToken(mock<IWindowContainerToken>())
    private val change = TransitionInfo.Change(token, SurfaceControl() /* leash */)
    private val activityTransitionInfo = ActivityTransitionInfo(component, TASK_ID)
    private val appCompatTransitionInfo = AppCompatTransitionInfo(letterboxBounds)
    private val activityTransitionInfoWithAppCompat =
        ActivityTransitionInfo(component, TASK_ID, appCompatTransitionInfo)

    @Test
    fun parcelable_recreatewithActivityTransitionInfoSucceeds() {
    fun parcelable_recreateWithActivityTransitionInfoSucceeds() {
        change.activityTransitionInfo = activityTransitionInfo
        val transitionInfo = TransitionInfo(TRANSIT_OPEN, 0 /* flags */)
        transitionInfo.addChange(change)
@@ -69,10 +73,40 @@ class TransitionInfoTest {
        assertThat(chg.activityTransitionInfo).isNotSameInstanceAs(activityTransitionInfo)
    }

    @Test
    fun parcelable_recreateWithActivityTransitionInfoWithAppCompatSucceeds() {
        change.activityTransitionInfo = activityTransitionInfoWithAppCompat
        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(activityTransitionInfoWithAppCompat)
    }

    @Test
    fun localRemoteCopy_copiesActivityTransitionInfoWithAppCompat() {
        change.activityTransitionInfo = activityTransitionInfoWithAppCompat
        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(activityTransitionInfoWithAppCompat)
        assertThat(chg.activityTransitionInfo).isNotSameInstanceAs(
            activityTransitionInfoWithAppCompat
        )
    }

    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)
        private val letterboxBounds = Rect(1, 2, 3, 4)
    }
}
 No newline at end of file
+18 −0
Original line number Diff line number Diff line
@@ -38,6 +38,7 @@ import android.graphics.Rect;
import android.view.InsetsSource;
import android.view.InsetsState;
import android.view.WindowInsets;
import android.window.AppCompatTransitionInfo;

import com.android.window.flags.Flags;

@@ -321,6 +322,23 @@ final class AppCompatUtils {
        return parentWindowingMode == WINDOWING_MODE_FREEFORM && canEnterDesktopMode(context);
    }

    /**
     * Creates a {@link AppCompatTransitionInfo} which encapsulate the letterbox
     * information if needed.
     *
     * @param activityRecord The {@link ActivityRecord} for the current activity.
     * @return The {@link AppCompatTransitionInfo} encapsulating AppCompat related
     * information if the activity is letterboxed or {@code null} otherwise.
     */
    @Nullable
    static AppCompatTransitionInfo createAppCompatTransitionInfo(
            @NonNull ActivityRecord activityRecord) {
        if (activityRecord.areBoundsLetterboxed()) {
            return new AppCompatTransitionInfo(new Rect(activityRecord.getBounds()));
        }
        return null;
    }

    private static void clearAppCompatTaskInfo(@NonNull AppCompatTaskInfo info) {
        info.topActivityLetterboxVerticalPosition = TaskInfo.PROPERTY_VALUE_UNSET;
        info.topActivityLetterboxHorizontalPosition = TaskInfo.PROPERTY_VALUE_UNSET;
Loading