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

Commit 7fc8d28d authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Generalize change transition into WindowContainer"

parents 74f681ee e55b9e0f
Loading
Loading
Loading
Loading
+6 −0
Original line number Original line Diff line number Diff line
@@ -697,6 +697,12 @@
      "group": "WM_DEBUG_FOCUS_LIGHT",
      "group": "WM_DEBUG_FOCUS_LIGHT",
      "at": "com\/android\/server\/wm\/RootWindowContainer.java"
      "at": "com\/android\/server\/wm\/RootWindowContainer.java"
    },
    },
    "-668956537": {
      "message": "  THUMBNAIL %s: CREATE",
      "level": "INFO",
      "group": "WM_SHOW_TRANSACTIONS",
      "at": "com\/android\/server\/wm\/SurfaceFreezer.java"
    },
    "-666510420": {
    "-666510420": {
      "message": "With display frozen, orientationChangeComplete=%b",
      "message": "With display frozen, orientationChangeComplete=%b",
      "level": "VERBOSE",
      "level": "VERBOSE",
+9 −8
Original line number Original line Diff line number Diff line
@@ -275,8 +275,9 @@ class ActivityMetricsLogger {
        }
        }


        /** @return {@code true} if the activity matches a launched activity in this transition. */
        /** @return {@code true} if the activity matches a launched activity in this transition. */
        boolean contains(ActivityRecord r) {
        boolean contains(WindowContainer wc) {
            return r == mLastLaunchedActivity || mPendingDrawActivities.contains(r);
            final ActivityRecord r = AppTransitionController.getAppFromContainer(wc);
            return r != null && (r == mLastLaunchedActivity || mPendingDrawActivities.contains(r));
        }
        }


        /** Called when the activity is drawn or won't be drawn. */
        /** Called when the activity is drawn or won't be drawn. */
@@ -435,10 +436,10 @@ class ActivityMetricsLogger {


    /** @return Non-null {@link TransitionInfo} if the activity is found in an active transition. */
    /** @return Non-null {@link TransitionInfo} if the activity is found in an active transition. */
    @Nullable
    @Nullable
    private TransitionInfo getActiveTransitionInfo(ActivityRecord r) {
    private TransitionInfo getActiveTransitionInfo(WindowContainer wc) {
        for (int i = mTransitionInfoList.size() - 1; i >= 0; i--) {
        for (int i = mTransitionInfoList.size() - 1; i >= 0; i--) {
            final TransitionInfo info = mTransitionInfoList.get(i);
            final TransitionInfo info = mTransitionInfoList.get(i);
            if (info.contains(r)) {
            if (info.contains(wc)) {
                return info;
                return info;
            }
            }
        }
        }
@@ -623,19 +624,19 @@ class ActivityMetricsLogger {
     * @param activityToReason A map from activity to a reason integer, which must be on of
     * @param activityToReason A map from activity to a reason integer, which must be on of
     *                         ActivityTaskManagerInternal.APP_TRANSITION_* reasons.
     *                         ActivityTaskManagerInternal.APP_TRANSITION_* reasons.
     */
     */
    void notifyTransitionStarting(ArrayMap<ActivityRecord, Integer> activityToReason) {
    void notifyTransitionStarting(ArrayMap<WindowContainer, Integer> activityToReason) {
        if (DEBUG_METRICS) Slog.i(TAG, "notifyTransitionStarting");
        if (DEBUG_METRICS) Slog.i(TAG, "notifyTransitionStarting");


        final long timestampNs = SystemClock.elapsedRealtimeNanos();
        final long timestampNs = SystemClock.elapsedRealtimeNanos();
        for (int index = activityToReason.size() - 1; index >= 0; index--) {
        for (int index = activityToReason.size() - 1; index >= 0; index--) {
            final ActivityRecord r = activityToReason.keyAt(index);
            final WindowContainer wc = activityToReason.keyAt(index);
            final TransitionInfo info = getActiveTransitionInfo(r);
            final TransitionInfo info = getActiveTransitionInfo(wc);
            if (info == null || info.mLoggedTransitionStarting) {
            if (info == null || info.mLoggedTransitionStarting) {
                // Ignore any subsequent notifyTransitionStarting.
                // Ignore any subsequent notifyTransitionStarting.
                continue;
                continue;
            }
            }
            if (DEBUG_METRICS) {
            if (DEBUG_METRICS) {
                Slog.i(TAG, "notifyTransitionStarting activity=" + r + " info=" + info);
                Slog.i(TAG, "notifyTransitionStarting activity=" + wc + " info=" + info);
            }
            }


            info.mCurrentTransitionDelayMs = info.calculateDelay(timestampNs);
            info.mCurrentTransitionDelayMs = info.calculateDelay(timestampNs);
+20 −148
Original line number Original line Diff line number Diff line
@@ -40,7 +40,6 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.app.WindowConfiguration.activityTypeToString;
import static android.app.WindowConfiguration.activityTypeToString;
@@ -106,7 +105,6 @@ import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
import static android.view.WindowManager.TRANSIT_ACTIVITY_CLOSE;
import static android.view.WindowManager.TRANSIT_ACTIVITY_CLOSE;
import static android.view.WindowManager.TRANSIT_DOCK_TASK_FROM_RECENTS;
import static android.view.WindowManager.TRANSIT_DOCK_TASK_FROM_RECENTS;
import static android.view.WindowManager.TRANSIT_TASK_CHANGE_WINDOWING_MODE;
import static android.view.WindowManager.TRANSIT_TASK_CLOSE;
import static android.view.WindowManager.TRANSIT_TASK_CLOSE;
import static android.view.WindowManager.TRANSIT_TASK_OPEN_BEHIND;
import static android.view.WindowManager.TRANSIT_TASK_OPEN_BEHIND;
import static android.view.WindowManager.TRANSIT_UNSET;
import static android.view.WindowManager.TRANSIT_UNSET;
@@ -284,7 +282,6 @@ import android.view.DisplayInfo;
import android.view.IAppTransitionAnimationSpecsFuture;
import android.view.IAppTransitionAnimationSpecsFuture;
import android.view.IApplicationToken;
import android.view.IApplicationToken;
import android.view.InputApplicationHandle;
import android.view.InputApplicationHandle;
import android.view.RemoteAnimationAdapter;
import android.view.RemoteAnimationDefinition;
import android.view.RemoteAnimationDefinition;
import android.view.RemoteAnimationTarget;
import android.view.RemoteAnimationTarget;
import android.view.SurfaceControl;
import android.view.SurfaceControl;
@@ -577,12 +574,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
     */
     */
    private boolean mCurrentLaunchCanTurnScreenOn = true;
    private boolean mCurrentLaunchCanTurnScreenOn = true;


    /**
     * This leash is used to "freeze" the app surface in place after the state change, but before
     * the animation is ready to start.
     */
    private SurfaceControl mTransitChangeLeash = null;

    /** Whether our surface was set to be showing in the last call to {@link #prepareSurfaces} */
    /** Whether our surface was set to be showing in the last call to {@link #prepareSurfaces} */
    private boolean mLastSurfaceShowing = true;
    private boolean mLastSurfaceShowing = true;


@@ -1329,15 +1320,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
            mDisplayContent.executeAppTransition();
            mDisplayContent.executeAppTransition();
        }
        }


        if (prevDc.mChangingApps.remove(this)) {
            // This gets called *after* the ActivityRecord has been reparented to the new display.
            // That reparenting resulted in this window changing modes (eg. FREEFORM -> FULLSCREEN),
            // so this token is now "frozen" while waiting for the animation to start on prevDc
            // (which will be cancelled since the window is no-longer a child). However, since this
            // is no longer a child of prevDc, this won't be notified of the cancelled animation,
            // so we need to cancel the change transition here.
            clearChangeLeash(getPendingTransaction(), true /* cancel */);
        }
        prevDc.mClosingApps.remove(this);
        prevDc.mClosingApps.remove(this);


        if (prevDc.mFocusedApp == this) {
        if (prevDc.mFocusedApp == this) {
@@ -3092,7 +3074,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
        commitVisibility(false /* visible */, true /* performLayout */);
        commitVisibility(false /* visible */, true /* performLayout */);


        getDisplayContent().mOpeningApps.remove(this);
        getDisplayContent().mOpeningApps.remove(this);
        getDisplayContent().mChangingApps.remove(this);
        getDisplayContent().mChangingContainers.remove(this);
        getDisplayContent().mUnknownAppVisibilityController.appRemovedOrHidden(this);
        getDisplayContent().mUnknownAppVisibilityController.appRemovedOrHidden(this);
        mWmService.mTaskSnapshotController.onAppRemoved(this);
        mWmService.mTaskSnapshotController.onAppRemoved(this);
        mStackSupervisor.getActivityMetricsLogger().notifyActivityRemoved(this);
        mStackSupervisor.getActivityMetricsLogger().notifyActivityRemoved(this);
@@ -4002,13 +3984,11 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
                appToken, visible, appTransition, isVisible(), mVisibleRequested,
                appToken, visible, appTransition, isVisible(), mVisibleRequested,
                Debug.getCallers(6));
                Debug.getCallers(6));


        onChildVisibilityRequested(visible);

        final DisplayContent displayContent = getDisplayContent();
        final DisplayContent displayContent = getDisplayContent();
        displayContent.mOpeningApps.remove(this);
        displayContent.mOpeningApps.remove(this);
        displayContent.mClosingApps.remove(this);
        displayContent.mClosingApps.remove(this);
        if (isInChangeTransition()) {
            clearChangeLeash(getPendingTransaction(), true /* cancel */);
        }
        displayContent.mChangingApps.remove(this);
        waitingToShow = false;
        waitingToShow = false;
        mVisibleRequested = visible;
        mVisibleRequested = visible;
        mLastDeferHidingClient = deferHidingClient;
        mLastDeferHidingClient = deferHidingClient;
@@ -5812,11 +5792,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
        return !isSplitScreenPrimary || allowSplitScreenPrimaryAnimation;
        return !isSplitScreenPrimary || allowSplitScreenPrimaryAnimation;
    }
    }


    @Override
    boolean isChangingAppTransition() {
        return task != null ? task.isChangingAppTransition() : super.isChangingAppTransition();
    }

    /**
    /**
     * Creates a layer to apply crop to an animation.
     * Creates a layer to apply crop to an animation.
     */
     */
@@ -5837,84 +5812,19 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
                this, endDeferFinishCallback);
                this, endDeferFinishCallback);
    }
    }


    private boolean shouldStartChangeTransition(int prevWinMode, int newWinMode) {
        if (mWmService.mDisableTransitionAnimation
                || !isVisible()
                || getDisplayContent().mAppTransition.isTransitionSet()
                || getSurfaceControl() == null) {
            return false;
        }
        // Only do an animation into and out-of freeform mode for now. Other mode
        // transition animations are currently handled by system-ui.
        return (prevWinMode == WINDOWING_MODE_FREEFORM) != (newWinMode == WINDOWING_MODE_FREEFORM);
    }

    @Override
    @Override
    boolean isWaitingForTransitionStart() {
    boolean isWaitingForTransitionStart() {
        final DisplayContent dc = getDisplayContent();
        final DisplayContent dc = getDisplayContent();
        return dc != null && dc.mAppTransition.isTransitionSet()
        return dc != null && dc.mAppTransition.isTransitionSet()
                && (dc.mOpeningApps.contains(this)
                && (dc.mOpeningApps.contains(this)
                || dc.mClosingApps.contains(this)
                || dc.mClosingApps.contains(this)
                || dc.mChangingApps.contains(this));
                || dc.mChangingContainers.contains(this));
    }

    /**
     * Initializes a change transition. Because the app is visible already, there is a small period
     * of time where the user can see the app content/window update before the transition starts.
     * To prevent this, we immediately take a snapshot and place the app/snapshot into a leash which
     * "freezes" the location/crop until the transition starts.
     * <p>
     * Here's a walk-through of the process:
     * 1. Create a temporary leash ("interim-change-leash") and reparent the app to it.
     * 2. Set the temporary leash's position/crop to the current state.
     * 3. Create a snapshot and place that at the top of the leash to cover up content changes.
     * 4. Once the transition is ready, it will reparent the app to the animation leash.
     * 5. Detach the interim-change-leash.
     */
    private void initializeChangeTransition(Rect startBounds) {
        mDisplayContent.prepareAppTransition(TRANSIT_TASK_CHANGE_WINDOWING_MODE,
                false /* alwaysKeepCurrent */, 0, false /* forceOverride */);
        mDisplayContent.mChangingApps.add(this);
        mTransitStartRect.set(startBounds);

        final SurfaceControl.Builder builder = makeAnimationLeash()
                .setParent(getAnimationLeashParent())
                .setName(getSurfaceControl() + " - interim-change-leash");
        mTransitChangeLeash = builder.build();
        Transaction t = getPendingTransaction();
        t.setWindowCrop(mTransitChangeLeash, startBounds.width(), startBounds.height());
        t.setPosition(mTransitChangeLeash, startBounds.left, startBounds.top);
        t.show(mTransitChangeLeash);
        t.reparent(getSurfaceControl(), mTransitChangeLeash);
        onAnimationLeashCreated(t, mTransitChangeLeash);

        // Skip creating snapshot if this transition is controlled by a remote animator which
        // doesn't need it.
        ArraySet<Integer> activityTypes = new ArraySet<>();
        activityTypes.add(getActivityType());
        RemoteAnimationAdapter adapter =
                mDisplayContent.mAppTransitionController.getRemoteAnimationOverride(
                        this, TRANSIT_TASK_CHANGE_WINDOWING_MODE, activityTypes);
        if (adapter != null && !adapter.getChangeNeedsSnapshot()) {
            return;
        }

        if (mThumbnail == null && task != null && !hasCommittedReparentToAnimationLeash()) {
            SurfaceControl.ScreenshotGraphicBuffer snapshot =
                    mWmService.mTaskSnapshotController.createTaskSnapshot(
                            task, 1 /* scaleFraction */);
            if (snapshot != null) {
                mThumbnail = new WindowContainerThumbnail(mWmService.mSurfaceFactory, t, this,
                        snapshot.getGraphicBuffer(), true /* relative */);
            }
        }
    }
    }


    @Override
    private int getAnimationLayer() {
    public void onAnimationLeashCreated(Transaction t, SurfaceControl leash) {
        // The leash is parented to the animation layer. We need to preserve the z-order by using
        // The leash is parented to the animation layer. We need to preserve the z-order by using
        // the prefix order index, but we boost if necessary.
        // the prefix order index, but we boost if necessary.
        int layer = 0;
        int layer;
        if (!inPinnedWindowingMode()) {
        if (!inPinnedWindowingMode()) {
            layer = getPrefixOrderIndex();
            layer = getPrefixOrderIndex();
        } else {
        } else {
@@ -5927,21 +5837,17 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
        if (mNeedsZBoost) {
        if (mNeedsZBoost) {
            layer += Z_BOOST_BASE;
            layer += Z_BOOST_BASE;
        }
        }
        if (!mNeedsAnimationBoundsLayer) {
        return layer;
            t.setLayer(leash, layer);
    }
    }


        final DisplayContent dc = getDisplayContent();
    @Override
        dc.assignStackOrdering();
    public void onAnimationLeashCreated(Transaction t, SurfaceControl leash) {

        t.setLayer(leash, getAnimationLayer());
        if (leash == mTransitChangeLeash) {
        getDisplayContent().assignStackOrdering();
            // This is a temporary state so skip any animation notifications
            return;
        } else if (mTransitChangeLeash != null) {
            // unparent mTransitChangeLeash for clean-up
            clearChangeLeash(t, false /* cancel */);
    }
    }


    @Override
    public void onLeashAnimationStarting(Transaction t, SurfaceControl leash) {
        if (mAnimatingActivityRegistry != null) {
        if (mAnimatingActivityRegistry != null) {
            mAnimatingActivityRegistry.notifyStarting(this);
            mAnimatingActivityRegistry.notifyStarting(this);
        }
        }
@@ -5969,7 +5875,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
                // surface size has already same as the animating container.
                // surface size has already same as the animating container.
                t.setWindowCrop(mAnimationBoundsLayer, mTmpRect);
                t.setWindowCrop(mAnimationBoundsLayer, mTmpRect);
            }
            }
            t.setLayer(mAnimationBoundsLayer, layer);
            t.setLayer(leash, 0);
            t.setLayer(mAnimationBoundsLayer, getAnimationLayer());


            // Reparent leash to animation bounds layer.
            // Reparent leash to animation bounds layer.
            t.reparent(leash, mAnimationBoundsLayer);
            t.reparent(leash, mAnimationBoundsLayer);
@@ -6001,10 +5908,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
        return mLastSurfaceShowing;
        return mLastSurfaceShowing;
    }
    }


    boolean isInChangeTransition() {
        return mTransitChangeLeash != null || AppTransition.isChangeTransit(mTransit);
    }

    void attachThumbnailAnimation() {
    void attachThumbnailAnimation() {
        if (!isAnimating(PARENTS)) {
        if (!isAnimating(PARENTS)) {
            return;
            return;
@@ -6141,31 +6044,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
        Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
        Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
    }
    }


    /**
     * @param cancel {@code true} if clearing the leash due to cancelling instead of transferring
     *                            to another leash.
     */
    private void clearChangeLeash(Transaction t, boolean cancel) {
        if (mTransitChangeLeash == null) {
            return;
        }
        if (cancel) {
            clearThumbnail();
            SurfaceControl sc = getSurfaceControl();
            SurfaceControl parentSc = getParentSurfaceControl();
            // Don't reparent if surface is getting destroyed
            if (parentSc != null && sc != null) {
                t.reparent(sc, getParentSurfaceControl());
            }
        }
        t.hide(mTransitChangeLeash);
        t.remove(mTransitChangeLeash);
        mTransitChangeLeash = null;
        if (cancel) {
            onAnimationLeashLost(t);
        }
    }

    void clearAnimatingFlags() {
    void clearAnimatingFlags() {
        boolean wallpaperMightChange = false;
        boolean wallpaperMightChange = false;
        for (int i = mChildren.size() - 1; i >= 0; i--) {
        for (int i = mChildren.size() - 1; i >= 0; i--) {
@@ -6181,7 +6059,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
    void cancelAnimation() {
    void cancelAnimation() {
        cancelAnimationOnly();
        cancelAnimationOnly();
        clearThumbnail();
        clearThumbnail();
        clearChangeLeash(getPendingTransaction(), true /* cancel */);
        mSurfaceFreezer.unfreeze(getPendingTransaction());
    }
    }


    /**
    /**
@@ -6226,6 +6104,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
        mRemoteAnimationDefinition = null;
        mRemoteAnimationDefinition = null;
    }
    }


    @Override
    RemoteAnimationDefinition getRemoteAnimationDefinition() {
    RemoteAnimationDefinition getRemoteAnimationDefinition() {
        return mRemoteAnimationDefinition;
        return mRemoteAnimationDefinition;
    }
    }
@@ -6686,8 +6565,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
                return;
                return;
            }
            }
        }
        }
        final int prevWinMode = getWindowingMode();
        mTmpPrevBounds.set(getBounds());
        super.onConfigurationChanged(newParentConfig);
        super.onConfigurationChanged(newParentConfig);


        if (shouldUseSizeCompatMode()) {
        if (shouldUseSizeCompatMode()) {
@@ -6712,12 +6589,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
            }
            }
        }
        }


        final int newWinMode = getWindowingMode();
        if ((prevWinMode != newWinMode) && (mDisplayContent != null)
                && shouldStartChangeTransition(prevWinMode, newWinMode)) {
            initializeChangeTransition(mTmpPrevBounds);
        }

        // Configuration's equality doesn't consider seq so if only seq number changes in resolved
        // Configuration's equality doesn't consider seq so if only seq number changes in resolved
        // override configuration. Therefore ConfigurationContainer doesn't change merged override
        // override configuration. Therefore ConfigurationContainer doesn't change merged override
        // configuration, but it's used to push configuration changes so explicitly update that.
        // configuration, but it's used to push configuration changes so explicitly update that.
@@ -7612,6 +7483,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
        }
        }
    }
    }


    @Override
    void writeIdentifierToProto(ProtoOutputStream proto, long fieldId) {
    void writeIdentifierToProto(ProtoOutputStream proto, long fieldId) {
        final long token = proto.start(fieldId);
        final long token = proto.start(fieldId);
        proto.write(HASH_CODE, System.identityHashCode(this));
        proto.write(HASH_CODE, System.identityHashCode(this));
+2 −2
Original line number Original line Diff line number Diff line
@@ -2308,14 +2308,14 @@ public class AppTransition implements Dump {
            }
            }
            notifyAppTransitionTimeoutLocked();
            notifyAppTransitionTimeoutLocked();
            if (isTransitionSet() || !dc.mOpeningApps.isEmpty() || !dc.mClosingApps.isEmpty()
            if (isTransitionSet() || !dc.mOpeningApps.isEmpty() || !dc.mClosingApps.isEmpty()
                    || !dc.mChangingApps.isEmpty()) {
                    || !dc.mChangingContainers.isEmpty()) {
                ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
                ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
                            "*** APP TRANSITION TIMEOUT. displayId=%d isTransitionSet()=%b "
                            "*** APP TRANSITION TIMEOUT. displayId=%d isTransitionSet()=%b "
                                    + "mOpeningApps.size()=%d mClosingApps.size()=%d "
                                    + "mOpeningApps.size()=%d mClosingApps.size()=%d "
                                    + "mChangingApps.size()=%d",
                                    + "mChangingApps.size()=%d",
                            dc.getDisplayId(), dc.mAppTransition.isTransitionSet(),
                            dc.getDisplayId(), dc.mAppTransition.isTransitionSet(),
                            dc.mOpeningApps.size(), dc.mClosingApps.size(),
                            dc.mOpeningApps.size(), dc.mClosingApps.size(),
                            dc.mChangingApps.size());
                            dc.mChangingContainers.size());


                setTimeout();
                setTimeout();
                mService.mWindowPlacerLocked.performSurfacePlacement();
                mService.mWindowPlacerLocked.performSurfacePlacement();
+51 −43

File changed.

Preview size limit exceeded, changes collapsed.

Loading