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

Commit 69742008 authored by Diego Vela's avatar Diego Vela Committed by Automerger Merge Worker
Browse files

Merge "Use bounds to proxy if an app is full screen." into tm-qpr-dev am: 78c5bd25 am: f1225512

parents 2fc80ac0 f1225512
Loading
Loading
Loading
Loading
+56 −11
Original line number Original line Diff line number Diff line
@@ -17,9 +17,13 @@
package androidx.window.extensions;
package androidx.window.extensions;


import android.app.ActivityThread;
import android.app.ActivityThread;
import android.app.Application;
import android.content.Context;
import android.content.Context;
import android.window.TaskFragmentOrganizer;


import androidx.annotation.NonNull;
import androidx.annotation.NonNull;
import androidx.window.common.DeviceStateManagerFoldingFeatureProducer;
import androidx.window.common.RawFoldingFeatureProducer;
import androidx.window.extensions.area.WindowAreaComponent;
import androidx.window.extensions.area.WindowAreaComponent;
import androidx.window.extensions.area.WindowAreaComponentImpl;
import androidx.window.extensions.area.WindowAreaComponentImpl;
import androidx.window.extensions.embedding.ActivityEmbeddingComponent;
import androidx.window.extensions.embedding.ActivityEmbeddingComponent;
@@ -27,6 +31,8 @@ import androidx.window.extensions.embedding.SplitController;
import androidx.window.extensions.layout.WindowLayoutComponent;
import androidx.window.extensions.layout.WindowLayoutComponent;
import androidx.window.extensions.layout.WindowLayoutComponentImpl;
import androidx.window.extensions.layout.WindowLayoutComponentImpl;


import java.util.Objects;



/**
/**
 * The reference implementation of {@link WindowExtensions} that implements the initial API version.
 * The reference implementation of {@link WindowExtensions} that implements the initial API version.
@@ -34,7 +40,8 @@ import androidx.window.extensions.layout.WindowLayoutComponentImpl;
public class WindowExtensionsImpl implements WindowExtensions {
public class WindowExtensionsImpl implements WindowExtensions {


    private final Object mLock = new Object();
    private final Object mLock = new Object();
    private volatile WindowLayoutComponent mWindowLayoutComponent;
    private volatile DeviceStateManagerFoldingFeatureProducer mFoldingFeatureProducer;
    private volatile WindowLayoutComponentImpl mWindowLayoutComponent;
    private volatile SplitController mSplitController;
    private volatile SplitController mSplitController;
    private volatile WindowAreaComponent mWindowAreaComponent;
    private volatile WindowAreaComponent mWindowAreaComponent;


@@ -44,6 +51,49 @@ public class WindowExtensionsImpl implements WindowExtensions {
        return 2;
        return 2;
    }
    }


    @NonNull
    private Application getApplication() {
        return Objects.requireNonNull(ActivityThread.currentApplication());
    }

    @NonNull
    private DeviceStateManagerFoldingFeatureProducer getFoldingFeatureProducer() {
        if (mFoldingFeatureProducer == null) {
            synchronized (mLock) {
                if (mFoldingFeatureProducer == null) {
                    Context context = getApplication();
                    RawFoldingFeatureProducer foldingFeatureProducer =
                            new RawFoldingFeatureProducer(context);
                    mFoldingFeatureProducer =
                            new DeviceStateManagerFoldingFeatureProducer(context,
                                    foldingFeatureProducer);
                }
            }
        }
        return mFoldingFeatureProducer;
    }

    @NonNull
    private WindowLayoutComponentImpl getWindowLayoutComponentImpl() {
        if (mWindowLayoutComponent == null) {
            synchronized (mLock) {
                if (mWindowLayoutComponent == null) {
                    Context context = getApplication();
                    DeviceStateManagerFoldingFeatureProducer producer =
                            getFoldingFeatureProducer();
                    // TODO(b/263263909) Use the organizer to tell if an Activity is embededed.
                    // Need to improve our Dependency Injection and centralize the logic.
                    TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(command -> {
                        throw new RuntimeException("Not allowed!");
                    });
                    mWindowLayoutComponent = new WindowLayoutComponentImpl(context, organizer,
                            producer);
                }
            }
        }
        return mWindowLayoutComponent;
    }

    /**
    /**
     * Returns a reference implementation of {@link WindowLayoutComponent} if available,
     * Returns a reference implementation of {@link WindowLayoutComponent} if available,
     * {@code null} otherwise. The implementation must match the API level reported in
     * {@code null} otherwise. The implementation must match the API level reported in
@@ -52,15 +102,7 @@ public class WindowExtensionsImpl implements WindowExtensions {
     */
     */
    @Override
    @Override
    public WindowLayoutComponent getWindowLayoutComponent() {
    public WindowLayoutComponent getWindowLayoutComponent() {
        if (mWindowLayoutComponent == null) {
        return getWindowLayoutComponentImpl();
            synchronized (mLock) {
                if (mWindowLayoutComponent == null) {
                    Context context = ActivityThread.currentApplication();
                    mWindowLayoutComponent = new WindowLayoutComponentImpl(context);
                }
            }
        }
        return mWindowLayoutComponent;
    }
    }


    /**
    /**
@@ -74,7 +116,10 @@ public class WindowExtensionsImpl implements WindowExtensions {
        if (mSplitController == null) {
        if (mSplitController == null) {
            synchronized (mLock) {
            synchronized (mLock) {
                if (mSplitController == null) {
                if (mSplitController == null) {
                    mSplitController = new SplitController();
                    mSplitController = new SplitController(
                            getWindowLayoutComponentImpl(),
                            getFoldingFeatureProducer()
                    );
                }
                }
            }
            }
        }
        }
+5 −13
Original line number Original line Diff line number Diff line
@@ -75,8 +75,8 @@ import androidx.annotation.GuardedBy;
import androidx.annotation.NonNull;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.Nullable;
import androidx.window.common.CommonFoldingFeature;
import androidx.window.common.CommonFoldingFeature;
import androidx.window.common.DeviceStateManagerFoldingFeatureProducer;
import androidx.window.common.EmptyLifecycleCallbacksAdapter;
import androidx.window.common.EmptyLifecycleCallbacksAdapter;
import androidx.window.extensions.WindowExtensionsProvider;
import androidx.window.extensions.embedding.TransactionManager.TransactionRecord;
import androidx.window.extensions.embedding.TransactionManager.TransactionRecord;
import androidx.window.extensions.layout.WindowLayoutComponentImpl;
import androidx.window.extensions.layout.WindowLayoutComponentImpl;


@@ -145,19 +145,12 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
    private final Handler mHandler;
    private final Handler mHandler;
    final Object mLock = new Object();
    final Object mLock = new Object();
    private final ActivityStartMonitor mActivityStartMonitor;
    private final ActivityStartMonitor mActivityStartMonitor;
    @NonNull
    final WindowLayoutComponentImpl mWindowLayoutComponent;

    public SplitController() {
        this((WindowLayoutComponentImpl) Objects.requireNonNull(WindowExtensionsProvider
                .getWindowExtensions().getWindowLayoutComponent()));
    }


    @VisibleForTesting
    public SplitController(@NonNull WindowLayoutComponentImpl windowLayoutComponent,
    SplitController(@NonNull WindowLayoutComponentImpl windowLayoutComponent) {
            @NonNull DeviceStateManagerFoldingFeatureProducer foldingFeatureProducer) {
        final MainThreadExecutor executor = new MainThreadExecutor();
        final MainThreadExecutor executor = new MainThreadExecutor();
        mHandler = executor.mHandler;
        mHandler = executor.mHandler;
        mPresenter = new SplitPresenter(executor, this);
        mPresenter = new SplitPresenter(executor, windowLayoutComponent, this);
        mTransactionManager = new TransactionManager(mPresenter);
        mTransactionManager = new TransactionManager(mPresenter);
        final ActivityThread activityThread = ActivityThread.currentActivityThread();
        final ActivityThread activityThread = ActivityThread.currentActivityThread();
        final Application application = activityThread.getApplication();
        final Application application = activityThread.getApplication();
@@ -168,8 +161,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen


        mActivityStartMonitor = new ActivityStartMonitor();
        mActivityStartMonitor = new ActivityStartMonitor();
        instrumentation.addMonitor(mActivityStartMonitor);
        instrumentation.addMonitor(mActivityStartMonitor);
        mWindowLayoutComponent = windowLayoutComponent;
        foldingFeatureProducer.addDataChangedCallback(new FoldingFeatureListener());
        mWindowLayoutComponent.addFoldingStateChangedCallback(new FoldingFeatureListener());
    }
    }


    private class FoldingFeatureListener implements Consumer<List<CommonFoldingFeature>> {
    private class FoldingFeatureListener implements Consumer<List<CommonFoldingFeature>> {
+8 −3
Original line number Original line Diff line number Diff line
@@ -51,6 +51,7 @@ import androidx.window.extensions.embedding.SplitAttributesCalculator.SplitAttri
import androidx.window.extensions.embedding.TaskContainer.TaskProperties;
import androidx.window.extensions.embedding.TaskContainer.TaskProperties;
import androidx.window.extensions.layout.DisplayFeature;
import androidx.window.extensions.layout.DisplayFeature;
import androidx.window.extensions.layout.FoldingFeature;
import androidx.window.extensions.layout.FoldingFeature;
import androidx.window.extensions.layout.WindowLayoutComponentImpl;
import androidx.window.extensions.layout.WindowLayoutInfo;
import androidx.window.extensions.layout.WindowLayoutInfo;


import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.annotations.VisibleForTesting;
@@ -137,10 +138,14 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
            .setSplitType(new ExpandContainersSplitType())
            .setSplitType(new ExpandContainersSplitType())
            .build();
            .build();


    private final WindowLayoutComponentImpl mWindowLayoutComponent;
    private final SplitController mController;
    private final SplitController mController;


    SplitPresenter(@NonNull Executor executor, @NonNull SplitController controller) {
    SplitPresenter(@NonNull Executor executor,
            @NonNull WindowLayoutComponentImpl windowLayoutComponent,
            @NonNull SplitController controller) {
        super(executor, controller);
        super(executor, controller);
        mWindowLayoutComponent = windowLayoutComponent;
        mController = controller;
        mController = controller;
        registerOrganizer();
        registerOrganizer();
        if (!SplitController.ENABLE_SHELL_TRANSITIONS) {
        if (!SplitController.ENABLE_SHELL_TRANSITIONS) {
@@ -556,7 +561,7 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
            return sanitizeSplitAttributes(taskProperties, defaultSplitAttributes,
            return sanitizeSplitAttributes(taskProperties, defaultSplitAttributes,
                    minDimensionsPair);
                    minDimensionsPair);
        }
        }
        final WindowLayoutInfo windowLayoutInfo = mController.mWindowLayoutComponent
        final WindowLayoutInfo windowLayoutInfo = mWindowLayoutComponent
                .getCurrentWindowLayoutInfo(taskProperties.getDisplayId(),
                .getCurrentWindowLayoutInfo(taskProperties.getDisplayId(),
                        taskConfiguration.windowConfiguration);
                        taskConfiguration.windowConfiguration);
        final SplitAttributesCalculatorParams params = new SplitAttributesCalculatorParams(
        final SplitAttributesCalculatorParams params = new SplitAttributesCalculatorParams(
@@ -838,7 +843,7 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
        final int displayId = taskProperties.getDisplayId();
        final int displayId = taskProperties.getDisplayId();
        final WindowConfiguration windowConfiguration = taskProperties.getConfiguration()
        final WindowConfiguration windowConfiguration = taskProperties.getConfiguration()
                .windowConfiguration;
                .windowConfiguration;
        final WindowLayoutInfo info = mController.mWindowLayoutComponent
        final WindowLayoutInfo info = mWindowLayoutComponent
                .getCurrentWindowLayoutInfo(displayId, windowConfiguration);
                .getCurrentWindowLayoutInfo(displayId, windowConfiguration);
        final List<DisplayFeature> displayFeatures = info.getDisplayFeatures();
        final List<DisplayFeature> displayFeatures = info.getDisplayFeatures();
        if (displayFeatures.isEmpty()) {
        if (displayFeatures.isEmpty()) {
+44 −13
Original line number Original line Diff line number Diff line
@@ -35,6 +35,8 @@ import android.graphics.Rect;
import android.os.Bundle;
import android.os.Bundle;
import android.os.IBinder;
import android.os.IBinder;
import android.util.ArrayMap;
import android.util.ArrayMap;
import android.view.WindowManager;
import android.window.TaskFragmentOrganizer;


import androidx.annotation.GuardedBy;
import androidx.annotation.GuardedBy;
import androidx.annotation.NonNull;
import androidx.annotation.NonNull;
@@ -43,13 +45,13 @@ import androidx.annotation.UiContext;
import androidx.window.common.CommonFoldingFeature;
import androidx.window.common.CommonFoldingFeature;
import androidx.window.common.DeviceStateManagerFoldingFeatureProducer;
import androidx.window.common.DeviceStateManagerFoldingFeatureProducer;
import androidx.window.common.EmptyLifecycleCallbacksAdapter;
import androidx.window.common.EmptyLifecycleCallbacksAdapter;
import androidx.window.common.RawFoldingFeatureProducer;
import androidx.window.util.DataProducer;
import androidx.window.util.DataProducer;


import java.util.ArrayList;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Collections;
import java.util.List;
import java.util.List;
import java.util.Map;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.Set;
import java.util.function.Consumer;
import java.util.function.Consumer;


@@ -80,13 +82,16 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent {
    private final Map<IBinder, ConfigurationChangeListener> mConfigurationChangeListeners =
    private final Map<IBinder, ConfigurationChangeListener> mConfigurationChangeListeners =
            new ArrayMap<>();
            new ArrayMap<>();


    public WindowLayoutComponentImpl(@NonNull Context context) {
    private final TaskFragmentOrganizer mTaskFragmentOrganizer;

    public WindowLayoutComponentImpl(@NonNull Context context,
            @NonNull TaskFragmentOrganizer taskFragmentOrganizer,
            @NonNull DeviceStateManagerFoldingFeatureProducer foldingFeatureProducer) {
        ((Application) context.getApplicationContext())
        ((Application) context.getApplicationContext())
                .registerActivityLifecycleCallbacks(new NotifyOnConfigurationChanged());
                .registerActivityLifecycleCallbacks(new NotifyOnConfigurationChanged());
        RawFoldingFeatureProducer foldingFeatureProducer = new RawFoldingFeatureProducer(context);
        mFoldingFeatureProducer = foldingFeatureProducer;
        mFoldingFeatureProducer = new DeviceStateManagerFoldingFeatureProducer(context,
                foldingFeatureProducer);
        mFoldingFeatureProducer.addDataChangedCallback(this::onDisplayFeaturesChanged);
        mFoldingFeatureProducer.addDataChangedCallback(this::onDisplayFeaturesChanged);
        mTaskFragmentOrganizer = taskFragmentOrganizer;
    }
    }


    /** Registers to listen to {@link CommonFoldingFeature} changes */
    /** Registers to listen to {@link CommonFoldingFeature} changes */
@@ -230,6 +235,7 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent {
    /**
    /**
     * Translates the {@link DisplayFeature} into a {@link WindowLayoutInfo} when a
     * Translates the {@link DisplayFeature} into a {@link WindowLayoutInfo} when a
     * valid state is found.
     * valid state is found.
     *
     * @param context a proxy for the {@link android.view.Window} that contains the
     * @param context a proxy for the {@link android.view.Window} that contains the
     *                {@link DisplayFeature}.
     *                {@link DisplayFeature}.
     */
     */
@@ -278,7 +284,8 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent {
     *
     *
     * @param context a proxy for the {@link android.view.Window} that contains the
     * @param context a proxy for the {@link android.view.Window} that contains the
     * {@link DisplayFeature}.
     * {@link DisplayFeature}.
     * are within the {@link android.view.Window} of the {@link Activity}
     * @return a {@link List}  of {@link DisplayFeature}s that are within the
     * {@link android.view.Window} of the {@link Activity}
     */
     */
    private List<DisplayFeature> getDisplayFeatures(
    private List<DisplayFeature> getDisplayFeatures(
            @NonNull @UiContext Context context, List<CommonFoldingFeature> storedFeatures) {
            @NonNull @UiContext Context context, List<CommonFoldingFeature> storedFeatures) {
@@ -317,8 +324,11 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent {
    }
    }


    /**
    /**
     * Checks whether display features should be reported for the activity.
     * Calculates if the display features should be reported for the UI Context. The calculation
     * uses the task information because that is accurate for Activities in ActivityEmbedding mode.
     * TODO(b/238948678): Support reporting display features in all windowing modes.
     * TODO(b/238948678): Support reporting display features in all windowing modes.
     *
     * @return true if the display features should be reported for the UI Context, false otherwise.
     */
     */
    private boolean shouldReportDisplayFeatures(@NonNull @UiContext Context context) {
    private boolean shouldReportDisplayFeatures(@NonNull @UiContext Context context) {
        int displayId = context.getDisplay().getDisplayId();
        int displayId = context.getDisplay().getDisplayId();
@@ -337,7 +347,27 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent {
                // bounds in this case can't be computed correctly, so we should skip.
                // bounds in this case can't be computed correctly, so we should skip.
                return false;
                return false;
            }
            }
            windowingMode = taskConfig.windowConfiguration.getWindowingMode();
            final Rect taskBounds = taskConfig.windowConfiguration.getBounds();
            final WindowManager windowManager = Objects.requireNonNull(
                    context.getSystemService(WindowManager.class));
            final Rect currentBounds = windowManager.getCurrentWindowMetrics().getBounds();
            final Rect maxBounds = windowManager.getMaximumWindowMetrics().getBounds();
            boolean isTaskExpanded = maxBounds.equals(taskBounds);
            boolean isActivityExpanded = maxBounds.equals(currentBounds);
            /*
             * We need to proxy being in full screen because when a user enters PiP and exits PiP
             * the task windowingMode will report multi-window/pinned until the transition is
             * finished in WM Shell.
             * maxBounds == taskWindowBounds is a proxy check to verify the window is full screen
             * For tasks that are letterboxed, we use currentBounds == maxBounds to filter these
             * out.
             */
            // TODO(b/262900133) remove currentBounds check when letterboxed apps report bounds.
            // currently we don't want to report to letterboxed apps since they do not update the
            // window bounds when the Activity is moved.  An inaccurate fold will be reported so
            // we skip.
            return isTaskExpanded && (isActivityExpanded
                    || mTaskFragmentOrganizer.isActivityEmbedded(activityToken));
        } else {
        } else {
            // TODO(b/242674941): use task windowing mode for window context that associates with
            // TODO(b/242674941): use task windowing mode for window context that associates with
            //  activity.
            //  activity.
@@ -390,6 +420,7 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent {
        }
        }


        @Override
        @Override
        public void onLowMemory() {}
        public void onLowMemory() {
        }
    }
    }
}
}
+4 −1
Original line number Original line Diff line number Diff line
@@ -90,6 +90,7 @@ import android.window.WindowContainerTransaction;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import androidx.test.filters.SmallTest;
import androidx.window.common.DeviceStateManagerFoldingFeatureProducer;
import androidx.window.extensions.layout.WindowLayoutComponentImpl;
import androidx.window.extensions.layout.WindowLayoutComponentImpl;
import androidx.window.extensions.layout.WindowLayoutInfo;
import androidx.window.extensions.layout.WindowLayoutInfo;


@@ -142,7 +143,9 @@ public class SplitControllerTest {
        MockitoAnnotations.initMocks(this);
        MockitoAnnotations.initMocks(this);
        doReturn(new WindowLayoutInfo(new ArrayList<>())).when(mWindowLayoutComponent)
        doReturn(new WindowLayoutInfo(new ArrayList<>())).when(mWindowLayoutComponent)
                .getCurrentWindowLayoutInfo(anyInt(), any());
                .getCurrentWindowLayoutInfo(anyInt(), any());
        mSplitController = new SplitController(mWindowLayoutComponent);
        DeviceStateManagerFoldingFeatureProducer producer =
                mock(DeviceStateManagerFoldingFeatureProducer.class);
        mSplitController = new SplitController(mWindowLayoutComponent, producer);
        mSplitPresenter = mSplitController.mPresenter;
        mSplitPresenter = mSplitController.mPresenter;
        mSplitInfos = new ArrayList<>();
        mSplitInfos = new ArrayList<>();
        mEmbeddingCallback = splitInfos -> {
        mEmbeddingCallback = splitInfos -> {
Loading