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

Commit 78c5bd25 authored by Diego Vela's avatar Diego Vela Committed by Android (Google) Code Review
Browse files

Merge "Use bounds to proxy if an app is full screen." into tm-qpr-dev

parents f62bbd6f 5e0dd149
Loading
Loading
Loading
Loading
+56 −11
Original line number Diff line number Diff line
@@ -17,9 +17,13 @@
package androidx.window.extensions;

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

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.WindowAreaComponentImpl;
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.WindowLayoutComponentImpl;

import java.util.Objects;


/**
 * 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 {

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

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

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

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

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

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

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

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

    private class FoldingFeatureListener implements Consumer<List<CommonFoldingFeature>> {
+8 −3
Original line number 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.layout.DisplayFeature;
import androidx.window.extensions.layout.FoldingFeature;
import androidx.window.extensions.layout.WindowLayoutComponentImpl;
import androidx.window.extensions.layout.WindowLayoutInfo;

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

    private final WindowLayoutComponentImpl mWindowLayoutComponent;
    private final SplitController mController;

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

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

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

@@ -80,13 +82,16 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent {
    private final Map<IBinder, ConfigurationChangeListener> mConfigurationChangeListeners =
            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())
                .registerActivityLifecycleCallbacks(new NotifyOnConfigurationChanged());
        RawFoldingFeatureProducer foldingFeatureProducer = new RawFoldingFeatureProducer(context);
        mFoldingFeatureProducer = new DeviceStateManagerFoldingFeatureProducer(context,
                foldingFeatureProducer);
        mFoldingFeatureProducer = foldingFeatureProducer;
        mFoldingFeatureProducer.addDataChangedCallback(this::onDisplayFeaturesChanged);
        mTaskFragmentOrganizer = taskFragmentOrganizer;
    }

    /** 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
     * valid state is found.
     *
     * @param context a proxy for the {@link android.view.Window} that contains the
     *                {@link DisplayFeature}.
     */
@@ -278,7 +284,8 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent {
     *
     * @param context a proxy for the {@link android.view.Window} that contains the
     * {@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(
            @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.
     *
     * @return true if the display features should be reported for the UI Context, false otherwise.
     */
    private boolean shouldReportDisplayFeatures(@NonNull @UiContext Context context) {
        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.
                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 {
            // TODO(b/242674941): use task windowing mode for window context that associates with
            //  activity.
@@ -390,6 +420,7 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent {
        }

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

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