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

Commit fe23ec48 authored by Charles Chen's avatar Charles Chen
Browse files

Listen to folding state change for SplitController

This CL enables to update the current top SplitContainer if
there's a folding state change.

Test: manual - following CLs use this CL
Bug: 207494880
Bug: 241043111
Change-Id: Ia4d08d7f71d935101f3aeda94384295db2fa2214
parent 6012e9b7
Loading
Loading
Loading
Loading
+50 −5
Original line number Original line Diff line number Diff line
@@ -19,6 +19,7 @@ package androidx.window.extensions.embedding;
import static android.app.ActivityManager.START_SUCCESS;
import static android.app.ActivityManager.START_SUCCESS;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.window.TaskFragmentOrganizer.KEY_ERROR_CALLBACK_OP_TYPE;
import static android.window.TaskFragmentOrganizer.KEY_ERROR_CALLBACK_OP_TYPE;
import static android.window.TaskFragmentOrganizer.KEY_ERROR_CALLBACK_TASK_FRAGMENT_INFO;
import static android.window.TaskFragmentOrganizer.KEY_ERROR_CALLBACK_TASK_FRAGMENT_INFO;
import static android.window.TaskFragmentOrganizer.KEY_ERROR_CALLBACK_THROWABLE;
import static android.window.TaskFragmentOrganizer.KEY_ERROR_CALLBACK_THROWABLE;
@@ -46,6 +47,7 @@ import android.app.Activity;
import android.app.ActivityClient;
import android.app.ActivityClient;
import android.app.ActivityOptions;
import android.app.ActivityOptions;
import android.app.ActivityThread;
import android.app.ActivityThread;
import android.app.Application;
import android.app.Instrumentation;
import android.app.Instrumentation;
import android.content.ComponentName;
import android.content.ComponentName;
import android.content.Context;
import android.content.Context;
@@ -70,12 +72,16 @@ import android.window.WindowContainerTransaction;
import androidx.annotation.GuardedBy;
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.EmptyLifecycleCallbacksAdapter;
import androidx.window.common.EmptyLifecycleCallbacksAdapter;
import androidx.window.extensions.WindowExtensionsProvider;
import androidx.window.extensions.layout.WindowLayoutComponentImpl;


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


import java.util.ArrayList;
import java.util.ArrayList;
import java.util.List;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.Set;
import java.util.concurrent.Executor;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
import java.util.function.Consumer;
@@ -106,26 +112,65 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
    @GuardedBy("mLock")
    @GuardedBy("mLock")
    final SparseArray<TaskContainer> mTaskContainers = new SparseArray<>();
    final SparseArray<TaskContainer> mTaskContainers = new SparseArray<>();


    // Callback to Jetpack to notify about changes to split states.
    /** Callback to Jetpack to notify about changes to split states. */
    @NonNull
    @Nullable
    private Consumer<List<SplitInfo>> mEmbeddingCallback;
    private Consumer<List<SplitInfo>> mEmbeddingCallback;
    private final List<SplitInfo> mLastReportedSplitStates = new ArrayList<>();
    private final List<SplitInfo> mLastReportedSplitStates = new ArrayList<>();
    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() {
    public SplitController() {
        this((WindowLayoutComponentImpl) Objects.requireNonNull(WindowExtensionsProvider
                .getWindowExtensions().getWindowLayoutComponent()));
    }

    @VisibleForTesting
    SplitController(@NonNull WindowLayoutComponentImpl windowLayoutComponent) {
        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, this);
        ActivityThread activityThread = ActivityThread.currentActivityThread();
        final ActivityThread activityThread = ActivityThread.currentActivityThread();
        final Application application = activityThread.getApplication();
        // Register a callback to be notified about activities being created.
        // Register a callback to be notified about activities being created.
        activityThread.getApplication().registerActivityLifecycleCallbacks(
        application.registerActivityLifecycleCallbacks(new LifecycleCallbacks());
                new LifecycleCallbacks());
        // Intercept activity starts to route activities to new containers if necessary.
        // Intercept activity starts to route activities to new containers if necessary.
        Instrumentation instrumentation = activityThread.getInstrumentation();
        Instrumentation instrumentation = activityThread.getInstrumentation();

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

    private class FoldingFeatureListener implements Consumer<List<CommonFoldingFeature>> {
        @Override
        public void accept(List<CommonFoldingFeature> foldingFeatures) {
            synchronized (mLock) {
                final WindowContainerTransaction wct = new WindowContainerTransaction();
                for (int i = 0; i < mTaskContainers.size(); i++) {
                    final TaskContainer taskContainer = mTaskContainers.valueAt(i);
                    if (!taskContainer.isVisible()) {
                        continue;
                    }
                    if (taskContainer.getDisplayId() != DEFAULT_DISPLAY) {
                        continue;
                    }
                    // TODO(b/238948678): Support reporting display features in all windowing modes.
                    if (taskContainer.isInMultiWindow()) {
                        continue;
                    }
                    if (taskContainer.isEmpty()) {
                        continue;
                    }
                    updateContainersInTask(wct, taskContainer);
                    updateAnimationOverride(taskContainer);
                }
                mPresenter.applyTransaction(wct);
            }
        }
    }
    }


    /** Updates the embedding rules applied to future activity launches. */
    /** Updates the embedding rules applied to future activity launches. */
+22 −3
Original line number Original line Diff line number Diff line
@@ -47,6 +47,7 @@ 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.List;
import java.util.List;
import java.util.Map;
import java.util.Map;
import java.util.Set;
import java.util.Set;
@@ -80,6 +81,11 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent {
        mFoldingFeatureProducer.addDataChangedCallback(this::onDisplayFeaturesChanged);
        mFoldingFeatureProducer.addDataChangedCallback(this::onDisplayFeaturesChanged);
    }
    }


    /** Registers to listen to {@link CommonFoldingFeature} changes */
    public void addFoldingStateChangedCallback(Consumer<List<CommonFoldingFeature>> consumer) {
        mFoldingFeatureProducer.addDataChangedCallback(consumer);
    }

    /**
    /**
     * Adds a listener interested in receiving updates to {@link WindowLayoutInfo}
     * Adds a listener interested in receiving updates to {@link WindowLayoutInfo}
     *
     *
@@ -225,12 +231,23 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent {
     */
     */
    private List<DisplayFeature> getDisplayFeatures(
    private List<DisplayFeature> getDisplayFeatures(
            @NonNull @UiContext Context context, List<CommonFoldingFeature> storedFeatures) {
            @NonNull @UiContext Context context, List<CommonFoldingFeature> storedFeatures) {
        List<DisplayFeature> features = new ArrayList<>();
        if (!shouldReportDisplayFeatures(context)) {
        if (!shouldReportDisplayFeatures(context)) {
            return Collections.emptyList();
        }
        return getDisplayFeatures(context.getDisplayId(),
                context.getResources().getConfiguration().windowConfiguration,
                storedFeatures);
    }

    /** @see #getDisplayFeatures(Context, List) */
    private List<DisplayFeature> getDisplayFeatures(int displayId,
            @NonNull WindowConfiguration windowConfiguration,
            List<CommonFoldingFeature> storedFeatures) {
        List<DisplayFeature> features = new ArrayList<>();
        if (displayId != DEFAULT_DISPLAY) {
            return features;
            return features;
        }
        }


        int displayId = context.getDisplay().getDisplayId();
        for (CommonFoldingFeature baseFeature : storedFeatures) {
        for (CommonFoldingFeature baseFeature : storedFeatures) {
            Integer state = convertToExtensionState(baseFeature.getState());
            Integer state = convertToExtensionState(baseFeature.getState());
            if (state == null) {
            if (state == null) {
@@ -238,7 +255,7 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent {
            }
            }
            Rect featureRect = baseFeature.getRect();
            Rect featureRect = baseFeature.getRect();
            rotateRectToDisplayRotation(displayId, featureRect);
            rotateRectToDisplayRotation(displayId, featureRect);
            transformToWindowSpaceRect(context, featureRect);
            transformToWindowSpaceRect(windowConfiguration, featureRect);


            if (!isZero(featureRect)) {
            if (!isZero(featureRect)) {
                // TODO(b/228641877): Remove guarding when fixed.
                // TODO(b/228641877): Remove guarding when fixed.
@@ -263,6 +280,8 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent {
            windowingMode = ActivityClient.getInstance().getTaskWindowingMode(
            windowingMode = ActivityClient.getInstance().getTaskWindowingMode(
                    context.getActivityToken());
                    context.getActivityToken());
        } else {
        } else {
            // TODO(b/242674941): use task windowing mode for window context that associates with
            //  activity.
            windowingMode = context.getResources().getConfiguration().windowConfiguration
            windowingMode = context.getResources().getConfiguration().windowConfiguration
                    .getWindowingMode();
                    .getWindowingMode();
        }
        }
+13 −4
Original line number Original line Diff line number Diff line
@@ -21,6 +21,7 @@ import static android.view.Surface.ROTATION_180;
import static android.view.Surface.ROTATION_270;
import static android.view.Surface.ROTATION_270;
import static android.view.Surface.ROTATION_90;
import static android.view.Surface.ROTATION_90;


import android.app.WindowConfiguration;
import android.content.Context;
import android.content.Context;
import android.graphics.Rect;
import android.graphics.Rect;
import android.hardware.display.DisplayManagerGlobal;
import android.hardware.display.DisplayManagerGlobal;
@@ -89,13 +90,21 @@ public final class ExtensionHelper {
    /** Transforms rectangle from absolute coordinate space to the window coordinate space. */
    /** Transforms rectangle from absolute coordinate space to the window coordinate space. */
    public static void transformToWindowSpaceRect(@NonNull @UiContext Context context,
    public static void transformToWindowSpaceRect(@NonNull @UiContext Context context,
            Rect inOutRect) {
            Rect inOutRect) {
        Rect windowRect = getWindowBounds(context);
        transformToWindowSpaceRect(getWindowBounds(context), inOutRect);
        if (!Rect.intersects(inOutRect, windowRect)) {
    }

    /** @see ExtensionHelper#transformToWindowSpaceRect(Context, Rect) */
    public static void transformToWindowSpaceRect(@NonNull WindowConfiguration windowConfiguration,
            Rect inOutRect) {
        transformToWindowSpaceRect(windowConfiguration.getBounds(), inOutRect);
    }

    private static void transformToWindowSpaceRect(@NonNull Rect bounds, @NonNull Rect inOutRect) {
        if (!inOutRect.intersect(bounds)) {
            inOutRect.setEmpty();
            inOutRect.setEmpty();
            return;
            return;
        }
        }
        inOutRect.intersect(windowRect);
        inOutRect.offset(-bounds.left, -bounds.top);
        inOutRect.offset(-windowRect.left, -windowRect.top);
    }
    }


    /**
    /**