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

Commit caa61c11 authored by Massimo Carli's avatar Massimo Carli
Browse files

[2/n] Introduce main abstraction behind flag

Introduces main compat ui framework abstraction adapting existing code
behind a feature flag.

Flag: com.android.window.flags.app_compat_ui_framework
Fix: 304235607
Bug: 270361630
Test: atest WMShellUnitTests:ShellTaskOrganizerTests
Test: atest WMShellUnitTests:CompatUIControllerTest
Test: atest WMShellUnitTests:CompatUILayoutTest
Test: atest WMShellUnitTests:CompatUIWindowManagerTest
Test: atest WMShellUnitTests:LetterboxEduDialogLayoutTest
Test: atest WMShellUnitTests:LetterboxEduWindowManagerTest
Test: atest WMShellUnitTests:ReachabilityEduLayoutTest
Test: atest WMShellUnitTests:ReachabilityEduWindowManagerTest
Test: atest WMShellUnitTests:RestartDialogLayoutTest
Test: atest WMShellUnitTests:RestartDialogWindowManagerTest

Change-Id: Ib3156947b7d0761babef03a28fb4c5ecf64ba191
parent b7d980b2
Loading
Loading
Loading
Loading
+58 −34
Original line number Diff line number Diff line
@@ -24,6 +24,9 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;

import static com.android.wm.shell.compatui.impl.CompatUIEventsKt.CAMERA_CONTROL_STATE_UPDATE;
import static com.android.wm.shell.compatui.impl.CompatUIEventsKt.SIZE_COMPAT_RESTART_BUTTON_APPEARED;
import static com.android.wm.shell.compatui.impl.CompatUIEventsKt.SIZE_COMPAT_RESTART_BUTTON_CLICKED;
import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_TASK_ORG;
import static com.android.wm.shell.transition.Transitions.ENABLE_SHELL_TRANSITIONS;

@@ -31,7 +34,6 @@ import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager.RunningTaskInfo;
import android.app.CameraCompatTaskInfo.CameraCompatControlState;
import android.app.TaskInfo;
import android.app.WindowConfiguration;
import android.content.LocusId;
@@ -57,6 +59,11 @@ import com.android.internal.util.FrameworkStatsLog;
import com.android.wm.shell.common.ScreenshotUtils;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.compatui.CompatUIController;
import com.android.wm.shell.compatui.api.CompatUIHandler;
import com.android.wm.shell.compatui.api.CompatUIInfo;
import com.android.wm.shell.compatui.impl.CompatUIEvents.CameraControlStateUpdated;
import com.android.wm.shell.compatui.impl.CompatUIEvents.SizeCompatRestartButtonAppeared;
import com.android.wm.shell.compatui.impl.CompatUIEvents.SizeCompatRestartButtonClicked;
import com.android.wm.shell.recents.RecentTasksController;
import com.android.wm.shell.startingsurface.StartingWindowController;
import com.android.wm.shell.sysui.ShellCommandHandler;
@@ -75,8 +82,7 @@ import java.util.function.Consumer;
 * Unified task organizer for all components in the shell.
 * TODO(b/167582004): may consider consolidating this class and TaskOrganizer
 */
public class ShellTaskOrganizer extends TaskOrganizer implements
        CompatUIController.CompatUICallback {
public class ShellTaskOrganizer extends TaskOrganizer {
    private static final String TAG = "ShellTaskOrganizer";

    // Intentionally using negative numbers here so the positive numbers can be used
@@ -194,12 +200,11 @@ public class ShellTaskOrganizer extends TaskOrganizer implements
     * In charge of showing compat UI. Can be {@code null} if the device doesn't support size
     * compat or if this isn't the main {@link ShellTaskOrganizer}.
     *
     * <p>NOTE: only the main {@link ShellTaskOrganizer} should have a {@link CompatUIController},
     * and register itself as a {@link CompatUIController.CompatUICallback}. Subclasses should be
     * initialized with a {@code null} {@link CompatUIController}.
     * <p>NOTE: only the main {@link ShellTaskOrganizer} should have a {@link CompatUIHandler},
     * Subclasses should be initialized with a {@code null} {@link CompatUIHandler}.
     */
    @Nullable
    private final CompatUIController mCompatUI;
    private final CompatUIHandler mCompatUI;

    @NonNull
    private final ShellCommandHandler mShellCommandHandler;
@@ -223,7 +228,7 @@ public class ShellTaskOrganizer extends TaskOrganizer implements

    public ShellTaskOrganizer(ShellInit shellInit,
            ShellCommandHandler shellCommandHandler,
            @Nullable CompatUIController compatUI,
            @Nullable CompatUIHandler compatUI,
            Optional<UnfoldAnimationController> unfoldAnimationController,
            Optional<RecentTasksController> recentTasks,
            ShellExecutor mainExecutor) {
@@ -235,7 +240,7 @@ public class ShellTaskOrganizer extends TaskOrganizer implements
    protected ShellTaskOrganizer(ShellInit shellInit,
            ShellCommandHandler shellCommandHandler,
            ITaskOrganizerController taskOrganizerController,
            @Nullable CompatUIController compatUI,
            @Nullable CompatUIHandler compatUI,
            Optional<UnfoldAnimationController> unfoldAnimationController,
            Optional<RecentTasksController> recentTasks,
            ShellExecutor mainExecutor) {
@@ -252,7 +257,21 @@ public class ShellTaskOrganizer extends TaskOrganizer implements
    private void onInit() {
        mShellCommandHandler.addDumpCallback(this::dump, this);
        if (mCompatUI != null) {
            mCompatUI.setCompatUICallback(this);
            mCompatUI.setCallback(compatUIEvent -> {
                switch(compatUIEvent.getEventId()) {
                    case SIZE_COMPAT_RESTART_BUTTON_APPEARED:
                        onSizeCompatRestartButtonAppeared(compatUIEvent.asType());
                        break;
                    case SIZE_COMPAT_RESTART_BUTTON_CLICKED:
                        onSizeCompatRestartButtonClicked(compatUIEvent.asType());
                        break;
                    case CAMERA_CONTROL_STATE_UPDATE:
                        onCameraControlStateUpdated(compatUIEvent.asType());
                        break;
                    default:

                }
            });
        }
        registerOrganizer();
    }
@@ -727,8 +746,26 @@ public class ShellTaskOrganizer extends TaskOrganizer implements
        }
    }

    @Override
    public void onSizeCompatRestartButtonAppeared(int taskId) {
    /** Reparents a child window surface to the task surface. */
    public void reparentChildSurfaceToTask(int taskId, SurfaceControl sc,
            SurfaceControl.Transaction t) {
        final TaskListener taskListener;
        synchronized (mLock) {
            taskListener = mTasks.contains(taskId)
                    ? getTaskListener(mTasks.get(taskId).getTaskInfo())
                    : null;
        }
        if (taskListener == null) {
            ProtoLog.w(WM_SHELL_TASK_ORG, "Failed to find Task to reparent surface taskId=%d",
                    taskId);
            return;
        }
        taskListener.reparentChildSurfaceToTask(taskId, sc, t);
    }

    @VisibleForTesting
    void onSizeCompatRestartButtonAppeared(@NonNull SizeCompatRestartButtonAppeared compatUIEvent) {
        final int taskId = compatUIEvent.getTaskId();
        final TaskAppearedInfo info;
        synchronized (mLock) {
            info = mTasks.get(taskId);
@@ -740,8 +777,9 @@ public class ShellTaskOrganizer extends TaskOrganizer implements
                FrameworkStatsLog.SIZE_COMPAT_RESTART_BUTTON_EVENT_REPORTED__EVENT__APPEARED);
    }

    @Override
    public void onSizeCompatRestartButtonClicked(int taskId) {
    @VisibleForTesting
    void onSizeCompatRestartButtonClicked(@NonNull SizeCompatRestartButtonClicked compatUIEvent) {
        final int taskId = compatUIEvent.getTaskId();
        final TaskAppearedInfo info;
        synchronized (mLock) {
            info = mTasks.get(taskId);
@@ -754,8 +792,10 @@ public class ShellTaskOrganizer extends TaskOrganizer implements
        restartTaskTopActivityProcessIfVisible(info.getTaskInfo().token);
    }

    @Override
    public void onCameraControlStateUpdated(int taskId, @CameraCompatControlState int state) {
    @VisibleForTesting
    void onCameraControlStateUpdated(@NonNull CameraControlStateUpdated compatUIEvent) {
        final int taskId = compatUIEvent.getTaskId();
        final int state = compatUIEvent.getState();
        final TaskAppearedInfo info;
        synchronized (mLock) {
            info = mTasks.get(taskId);
@@ -766,22 +806,6 @@ public class ShellTaskOrganizer extends TaskOrganizer implements
        updateCameraCompatControlState(info.getTaskInfo().token, state);
    }

    /** Reparents a child window surface to the task surface. */
    public void reparentChildSurfaceToTask(int taskId, SurfaceControl sc,
            SurfaceControl.Transaction t) {
        final TaskListener taskListener;
        synchronized (mLock) {
            taskListener = mTasks.contains(taskId)
                    ? getTaskListener(mTasks.get(taskId).getTaskInfo())
                    : null;
        }
        if (taskListener == null) {
            ProtoLog.w(WM_SHELL_TASK_ORG, "Failed to find Task to reparent surface taskId=%d",
                    taskId);
            return;
        }
        taskListener.reparentChildSurfaceToTask(taskId, sc, t);
    }

    private void logSizeCompatRestartButtonEventReported(@NonNull TaskAppearedInfo info,
            int event) {
@@ -810,10 +834,10 @@ public class ShellTaskOrganizer extends TaskOrganizer implements
        // on this Task if there is any.
        if (taskListener == null || !taskListener.supportCompatUI()
                || !taskInfo.appCompatTaskInfo.hasCompatUI() || !taskInfo.isVisible) {
            mCompatUI.onCompatInfoChanged(taskInfo, null /* taskListener */);
            mCompatUI.onCompatInfoChanged(new CompatUIInfo(taskInfo, null /* taskListener */));
            return;
        }
        mCompatUI.onCompatInfoChanged(taskInfo, taskListener);
        mCompatUI.onCompatInfoChanged(new CompatUIInfo(taskInfo, taskListener));
    }

    private TaskListener getTaskListener(RunningTaskInfo runningTaskInfo) {
+19 −25
Original line number Diff line number Diff line
@@ -20,7 +20,6 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.CameraCompatTaskInfo.CameraCompatControlState;
import android.app.TaskInfo;
import android.content.ComponentName;
import android.content.Context;
@@ -50,6 +49,10 @@ import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.DockStateReader;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.compatui.api.CompatUIEvent;
import com.android.wm.shell.compatui.api.CompatUIHandler;
import com.android.wm.shell.compatui.api.CompatUIInfo;
import com.android.wm.shell.compatui.impl.CompatUIEvents.SizeCompatRestartButtonClicked;
import com.android.wm.shell.sysui.KeyguardChangeListener;
import com.android.wm.shell.sysui.ShellController;
import com.android.wm.shell.sysui.ShellInit;
@@ -71,17 +74,7 @@ import java.util.function.Predicate;
 * activities are in compatibility mode.
 */
public class CompatUIController implements OnDisplaysChangedListener,
        DisplayImeController.ImePositionProcessor, KeyguardChangeListener {

    /** Callback for compat UI interaction. */
    public interface CompatUICallback {
        /** Called when the size compat restart button appears. */
        void onSizeCompatRestartButtonAppeared(int taskId);
        /** Called when the size compat restart button is clicked. */
        void onSizeCompatRestartButtonClicked(int taskId);
        /** Called when the camera compat control state is updated. */
        void onCameraControlStateUpdated(int taskId, @CameraCompatControlState int state);
    }
        DisplayImeController.ImePositionProcessor, KeyguardChangeListener, CompatUIHandler {

    private static final String TAG = "CompatUIController";

@@ -170,7 +163,7 @@ public class CompatUIController implements OnDisplaysChangedListener,
    private final Function<Integer, Integer> mDisappearTimeSupplier;

    @Nullable
    private CompatUICallback mCompatUICallback;
    private Consumer<CompatUIEvent> mCallback;

    // Indicates if the keyguard is currently showing, in which case compat UIs shouldn't
    // be shown.
@@ -230,20 +223,21 @@ public class CompatUIController implements OnDisplaysChangedListener,
        mCompatUIShellCommandHandler.onInit();
    }

    /** Sets the callback for Compat UI interactions. */
    public void setCompatUICallback(@NonNull CompatUICallback compatUiCallback) {
        mCompatUICallback = compatUiCallback;
    /** Sets the callback for UI interactions. */
    @Override
    public void setCallback(@Nullable Consumer<CompatUIEvent> callback) {
        mCallback = callback;
    }

    /**
     * Called when the Task info changed. Creates and updates the compat UI if there is an
     * activity in size compat, or removes the UI if there is no size compat activity.
     *
     * @param taskInfo {@link TaskInfo} task the activity is in.
     * @param taskListener listener to handle the Task Surface placement.
     * @param compatUIInfo {@link CompatUIInfo} encapsulates information about the task and listener
     */
    public void onCompatInfoChanged(@NonNull TaskInfo taskInfo,
            @Nullable ShellTaskOrganizer.TaskListener taskListener) {
    public void onCompatInfoChanged(@NonNull CompatUIInfo compatUIInfo) {
        final TaskInfo taskInfo = compatUIInfo.getTaskInfo();
        final ShellTaskOrganizer.TaskListener taskListener = compatUIInfo.getListener();
        if (taskInfo != null && !taskInfo.appCompatTaskInfo.topActivityInSizeCompat) {
            mSetOfTaskIdsShowingRestartDialog.remove(taskInfo.taskId);
        }
@@ -466,7 +460,7 @@ public class CompatUIController implements OnDisplaysChangedListener,
    CompatUIWindowManager createCompatUiWindowManager(Context context, TaskInfo taskInfo,
            ShellTaskOrganizer.TaskListener taskListener) {
        return new CompatUIWindowManager(context,
                taskInfo, mSyncQueue, mCompatUICallback, taskListener,
                taskInfo, mSyncQueue, mCallback, taskListener,
                mDisplayController.getDisplayLayout(taskInfo.displayId), mCompatUIHintsState,
                mCompatUIConfiguration, this::onRestartButtonClicked);
    }
@@ -478,9 +472,9 @@ public class CompatUIController implements OnDisplaysChangedListener,
                taskInfoState.first)) {
            // We need to show the dialog
            mSetOfTaskIdsShowingRestartDialog.add(taskInfoState.first.taskId);
            onCompatInfoChanged(taskInfoState.first, taskInfoState.second);
            onCompatInfoChanged(new CompatUIInfo(taskInfoState.first, taskInfoState.second));
        } else {
            mCompatUICallback.onSizeCompatRestartButtonClicked(taskInfoState.first.taskId);
            mCallback.accept(new SizeCompatRestartButtonClicked(taskInfoState.first.taskId));
        }
    }

@@ -575,13 +569,13 @@ public class CompatUIController implements OnDisplaysChangedListener,
    private void onRestartDialogCallback(
            Pair<TaskInfo, ShellTaskOrganizer.TaskListener> stateInfo) {
        mTaskIdToRestartDialogWindowManagerMap.remove(stateInfo.first.taskId);
        mCompatUICallback.onSizeCompatRestartButtonClicked(stateInfo.first.taskId);
        mCallback.accept(new SizeCompatRestartButtonClicked(stateInfo.first.taskId));
    }

    private void onRestartDialogDismissCallback(
            Pair<TaskInfo, ShellTaskOrganizer.TaskListener> stateInfo) {
        mSetOfTaskIdsShowingRestartDialog.remove(stateInfo.first.taskId);
        onCompatInfoChanged(stateInfo.first, stateInfo.second);
        onCompatInfoChanged(new CompatUIInfo(stateInfo.first, stateInfo.second));
    }

    private void createOrUpdateReachabilityEduLayout(@NonNull TaskInfo taskInfo,
+20 −10
Original line number Diff line number Diff line
@@ -40,8 +40,10 @@ import com.android.wm.shell.R;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.compatui.CompatUIController.CompatUICallback;
import com.android.wm.shell.compatui.CompatUIController.CompatUIHintsState;
import com.android.wm.shell.compatui.api.CompatUIEvent;
import com.android.wm.shell.compatui.impl.CompatUIEvents.CameraControlStateUpdated;
import com.android.wm.shell.compatui.impl.CompatUIEvents.SizeCompatRestartButtonAppeared;

import java.util.function.Consumer;

@@ -50,10 +52,13 @@ import java.util.function.Consumer;
 */
class CompatUIWindowManager extends CompatUIWindowManagerAbstract {

    private final CompatUICallback mCallback;
    @NonNull
    private final Consumer<CompatUIEvent> mCallback;

    @NonNull
    private final CompatUIConfiguration mCompatUIConfiguration;

    @NonNull
    private final Consumer<Pair<TaskInfo, ShellTaskOrganizer.TaskListener>> mOnRestartButtonClicked;

    // Remember the last reported states in case visibility changes due to keyguard or IME updates.
@@ -65,6 +70,7 @@ class CompatUIWindowManager extends CompatUIWindowManagerAbstract {
    int mCameraCompatControlState = CAMERA_COMPAT_CONTROL_HIDDEN;

    @VisibleForTesting
    @NonNull
    CompatUIHintsState mCompatUIHintsState;

    @Nullable
@@ -73,11 +79,15 @@ class CompatUIWindowManager extends CompatUIWindowManagerAbstract {

    private final float mHideScmTolerance;

    CompatUIWindowManager(Context context, TaskInfo taskInfo,
            SyncTransactionQueue syncQueue, CompatUICallback callback,
            ShellTaskOrganizer.TaskListener taskListener, DisplayLayout displayLayout,
            CompatUIHintsState compatUIHintsState, CompatUIConfiguration compatUIConfiguration,
            Consumer<Pair<TaskInfo, ShellTaskOrganizer.TaskListener>> onRestartButtonClicked) {
    CompatUIWindowManager(@NonNull Context context, @NonNull TaskInfo taskInfo,
                          @NonNull SyncTransactionQueue syncQueue,
                          @NonNull Consumer<CompatUIEvent> callback,
                          @Nullable ShellTaskOrganizer.TaskListener taskListener,
                          @Nullable DisplayLayout displayLayout,
                          @NonNull CompatUIHintsState compatUIHintsState,
                          @NonNull CompatUIConfiguration compatUIConfiguration,
                          @NonNull Consumer<Pair<TaskInfo, ShellTaskOrganizer.TaskListener>>
                                  onRestartButtonClicked) {
        super(context, taskInfo, syncQueue, taskListener, displayLayout);
        mCallback = callback;
        mHasSizeCompat = taskInfo.appCompatTaskInfo.topActivityInSizeCompat;
@@ -122,7 +132,7 @@ class CompatUIWindowManager extends CompatUIWindowManagerAbstract {
        updateVisibilityOfViews();

        if (mHasSizeCompat) {
            mCallback.onSizeCompatRestartButtonAppeared(mTaskId);
            mCallback.accept(new SizeCompatRestartButtonAppeared(mTaskId));
        }

        return mLayout;
@@ -177,7 +187,7 @@ class CompatUIWindowManager extends CompatUIWindowManagerAbstract {
                mCameraCompatControlState == CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED
                        ? CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED
                        : CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED;
        mCallback.onCameraControlStateUpdated(mTaskId, mCameraCompatControlState);
        mCallback.accept(new CameraControlStateUpdated(mTaskId, mCameraCompatControlState));
        mLayout.updateCameraTreatmentButton(mCameraCompatControlState);
    }

@@ -188,7 +198,7 @@ class CompatUIWindowManager extends CompatUIWindowManagerAbstract {
            return;
        }
        mCameraCompatControlState = CAMERA_COMPAT_CONTROL_DISMISSED;
        mCallback.onCameraControlStateUpdated(mTaskId, CAMERA_COMPAT_CONTROL_DISMISSED);
        mCallback.accept(new CameraControlStateUpdated(mTaskId, CAMERA_COMPAT_CONTROL_DISMISSED));
        mLayout.setCameraControlVisibility(/* show= */ false);
    }

+34 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.wm.shell.compatui.api

/**
 * Abstraction for all the possible Compat UI Component events.
 */
interface CompatUIEvent {
    /**
     * Unique event identifier
     */
    val eventId: Int

    @Suppress("UNCHECKED_CAST")
    fun <T : CompatUIEvent> asType(): T? = this as? T

    fun <T : CompatUIEvent> asType(clazz: Class<T>): T? {
        return if (clazz.isInstance(this)) clazz.cast(this) else null
    }
}
 No newline at end of file
+34 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.android.wm.shell.compatui.api

import java.util.function.Consumer

/**
 * Abstraction for the objects responsible to handle all the CompatUI components and the
 * communication with the server.
 */
interface CompatUIHandler {
    /**
     * Invoked when a new model is coming from the server.
     */
    fun onCompatInfoChanged(compatUIInfo: CompatUIInfo)

    /**
     * Optional reference to the object responsible to send {@link CompatUIEvent}
     */
    fun setCallback(compatUIEventSender: Consumer<CompatUIEvent>?)
}
 No newline at end of file
Loading