Merge "Listen to folding state change for SplitController" into tm-qpr-dev
This commit is contained in:
@@ -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. */
|
||||||
|
|||||||
@@ -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();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user