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
This commit is contained in:
Charles Chen
2022-09-14 01:02:31 +08:00
parent 6012e9b748
commit fe23ec48f5
3 changed files with 85 additions and 12 deletions

View File

@@ -19,6 +19,7 @@ package androidx.window.extensions.embedding;
import static android.app.ActivityManager.START_SUCCESS;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
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_TASK_FRAGMENT_INFO;
import static android.window.TaskFragmentOrganizer.KEY_ERROR_CALLBACK_THROWABLE;
@@ -46,6 +47,7 @@ import android.app.Activity;
import android.app.ActivityClient;
import android.app.ActivityOptions;
import android.app.ActivityThread;
import android.app.Application;
import android.app.Instrumentation;
import android.content.ComponentName;
import android.content.Context;
@@ -70,12 +72,16 @@ import android.window.WindowContainerTransaction;
import androidx.annotation.GuardedBy;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.window.common.CommonFoldingFeature;
import androidx.window.common.EmptyLifecycleCallbacksAdapter;
import androidx.window.extensions.WindowExtensionsProvider;
import androidx.window.extensions.layout.WindowLayoutComponentImpl;
import com.android.internal.annotations.VisibleForTesting;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
@@ -106,26 +112,65 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
@GuardedBy("mLock")
final SparseArray<TaskContainer> mTaskContainers = new SparseArray<>();
// Callback to Jetpack to notify about changes to split states.
@NonNull
/** Callback to Jetpack to notify about changes to split states. */
@Nullable
private Consumer<List<SplitInfo>> mEmbeddingCallback;
private final List<SplitInfo> mLastReportedSplitStates = new ArrayList<>();
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) {
final MainThreadExecutor executor = new MainThreadExecutor();
mHandler = executor.mHandler;
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.
activityThread.getApplication().registerActivityLifecycleCallbacks(
new LifecycleCallbacks());
application.registerActivityLifecycleCallbacks(new LifecycleCallbacks());
// Intercept activity starts to route activities to new containers if necessary.
Instrumentation instrumentation = activityThread.getInstrumentation();
mActivityStartMonitor = new ActivityStartMonitor();
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. */

View File

@@ -47,6 +47,7 @@ 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.Set;
@@ -80,6 +81,11 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent {
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}
*
@@ -225,12 +231,23 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent {
*/
private List<DisplayFeature> getDisplayFeatures(
@NonNull @UiContext Context context, List<CommonFoldingFeature> storedFeatures) {
List<DisplayFeature> features = new ArrayList<>();
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;
}
int displayId = context.getDisplay().getDisplayId();
for (CommonFoldingFeature baseFeature : storedFeatures) {
Integer state = convertToExtensionState(baseFeature.getState());
if (state == null) {
@@ -238,7 +255,7 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent {
}
Rect featureRect = baseFeature.getRect();
rotateRectToDisplayRotation(displayId, featureRect);
transformToWindowSpaceRect(context, featureRect);
transformToWindowSpaceRect(windowConfiguration, featureRect);
if (!isZero(featureRect)) {
// TODO(b/228641877): Remove guarding when fixed.
@@ -263,6 +280,8 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent {
windowingMode = ActivityClient.getInstance().getTaskWindowingMode(
context.getActivityToken());
} else {
// TODO(b/242674941): use task windowing mode for window context that associates with
// activity.
windowingMode = context.getResources().getConfiguration().windowConfiguration
.getWindowingMode();
}

View File

@@ -21,6 +21,7 @@ import static android.view.Surface.ROTATION_180;
import static android.view.Surface.ROTATION_270;
import static android.view.Surface.ROTATION_90;
import android.app.WindowConfiguration;
import android.content.Context;
import android.graphics.Rect;
import android.hardware.display.DisplayManagerGlobal;
@@ -89,13 +90,21 @@ public final class ExtensionHelper {
/** Transforms rectangle from absolute coordinate space to the window coordinate space. */
public static void transformToWindowSpaceRect(@NonNull @UiContext Context context,
Rect inOutRect) {
Rect windowRect = getWindowBounds(context);
if (!Rect.intersects(inOutRect, windowRect)) {
transformToWindowSpaceRect(getWindowBounds(context), inOutRect);
}
/** @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();
return;
}
inOutRect.intersect(windowRect);
inOutRect.offset(-windowRect.left, -windowRect.top);
inOutRect.offset(-bounds.left, -bounds.top);
}
/**