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

Commit dd18ce17 authored by Tom Natan's avatar Tom Natan Committed by Automerger Merge Worker
Browse files

Merge "Remove all size compat UIs when the keyguard becomes occluded" into sc-v2-dev am: 1bbc9f63

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/16212776

Change-Id: Ib3c178b3484c9df0b6aeac1e2b497209bb3c7839
parents f4378943 1bbc9f63
Loading
Loading
Loading
Loading
+10 −2
Original line number Diff line number Diff line
@@ -74,6 +74,7 @@ import com.android.wm.shell.pip.phone.PipAppOpsListener;
import com.android.wm.shell.pip.phone.PipTouchHandler;
import com.android.wm.shell.recents.RecentTasks;
import com.android.wm.shell.recents.RecentTasksController;
import com.android.wm.shell.sizecompatui.SizeCompatUI;
import com.android.wm.shell.sizecompatui.SizeCompatUIController;
import com.android.wm.shell.splitscreen.SplitScreen;
import com.android.wm.shell.splitscreen.SplitScreenController;
@@ -169,13 +170,20 @@ public abstract class WMShellBaseModule {
        return new ShellTaskOrganizer(mainExecutor, context, sizeCompatUI, recentTasksOptional);
    }

    @WMSingleton
    @Provides
    static SizeCompatUI provideSizeCompatUI(SizeCompatUIController sizeCompatUIController) {
        return sizeCompatUIController.asSizeCompatUI();
    }

    @WMSingleton
    @Provides
    static SizeCompatUIController provideSizeCompatUIController(Context context,
            DisplayController displayController, DisplayInsetsController displayInsetsController,
            DisplayImeController imeController, SyncTransactionQueue syncQueue) {
            DisplayImeController imeController, SyncTransactionQueue syncQueue,
            @ShellMainThread ShellExecutor mainExecutor) {
        return new SizeCompatUIController(context, displayController, displayInsetsController,
                imeController, syncQueue);
                imeController, syncQueue, mainExecutor);
    }

    @WMSingleton
+32 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2021 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.sizecompatui;

import com.android.wm.shell.common.annotations.ExternalThread;

/**
 * Interface to engage size compat UI.
 */
@ExternalThread
public interface SizeCompatUI {
    /**
     * Called when the keyguard occluded state changes. Removes all size compat UIs if the
     * keyguard is now occluded.
     * @param occluded indicates if the keyguard is now occluded.
     */
    void onKeyguardOccludedChanged(boolean occluded);
}
+54 −6
Original line number Diff line number Diff line
@@ -35,13 +35,16 @@ import com.android.wm.shell.common.DisplayImeController;
import com.android.wm.shell.common.DisplayInsetsController;
import com.android.wm.shell.common.DisplayInsetsController.OnInsetsChangedListener;
import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.annotations.ExternalThread;

import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.function.Consumer;
import java.util.function.Predicate;

/**
 * Controls to show/update restart-activity buttons on Tasks based on whether the foreground
@@ -78,26 +81,37 @@ public class SizeCompatUIController implements OnDisplaysChangedListener,
    private final DisplayInsetsController mDisplayInsetsController;
    private final DisplayImeController mImeController;
    private final SyncTransactionQueue mSyncQueue;
    private final ShellExecutor mMainExecutor;
    private final SizeCompatUIImpl mImpl = new SizeCompatUIImpl();

    private SizeCompatUICallback mCallback;

    /** Only show once automatically in the process life. */
    private boolean mHasShownHint;
    /** Indicates if the keyguard is currently occluded, in which case size compat UIs shouldn't
     * be shown. */
    private boolean mKeyguardOccluded;

    public SizeCompatUIController(Context context,
            DisplayController displayController,
            DisplayInsetsController displayInsetsController,
            DisplayImeController imeController,
            SyncTransactionQueue syncQueue) {
            SyncTransactionQueue syncQueue,
            ShellExecutor mainExecutor) {
        mContext = context;
        mDisplayController = displayController;
        mDisplayInsetsController = displayInsetsController;
        mImeController = imeController;
        mSyncQueue = syncQueue;
        mMainExecutor = mainExecutor;
        mDisplayController.addDisplayWindowListener(this);
        mImeController.addPositionProcessor(this);
    }

    public SizeCompatUI asSizeCompatUI() {
        return mImpl;
    }

    /** Sets the callback for UI interactions. */
    public void setSizeCompatUICallback(SizeCompatUICallback callback) {
        mCallback = callback;
@@ -106,6 +120,7 @@ public class SizeCompatUIController implements OnDisplaysChangedListener,
    /**
     * Called when the Task info changed. Creates and updates the size compat UI if there is an
     * activity in size compat, or removes the UI if there is no size compat activity.
     *
     * @param displayId display the task and activity are in.
     * @param taskId task the activity is in.
     * @param taskConfig task config to place the size compat UI with.
@@ -180,7 +195,19 @@ public class SizeCompatUIController implements OnDisplaysChangedListener,
        }

        // Hide the size compat UIs when input method is showing.
        forAllLayoutsOnDisplay(displayId, layout -> layout.updateImeVisibility(isShowing));
        forAllLayoutsOnDisplay(displayId,
                layout -> layout.updateVisibility(showOnDisplay(displayId)));
    }

    @VisibleForTesting
    void onKeyguardOccludedChanged(boolean occluded) {
        mKeyguardOccluded = occluded;
        // Hide the size compat UIs when keyguard is occluded.
        forAllLayouts(layout -> layout.updateVisibility(showOnDisplay(layout.getDisplayId())));
    }

    private boolean showOnDisplay(int displayId) {
        return !mKeyguardOccluded && !isImeShowingOnDisplay(displayId);
    }

    private boolean isImeShowingOnDisplay(int displayId) {
@@ -198,7 +225,7 @@ public class SizeCompatUIController implements OnDisplaysChangedListener,
        final SizeCompatUILayout layout = createLayout(context, displayId, taskId, taskConfig,
                taskListener);
        mActiveLayouts.put(taskId, layout);
        layout.createSizeCompatButton(isImeShowingOnDisplay(displayId));
        layout.createSizeCompatButton(showOnDisplay(displayId));
    }

    @VisibleForTesting
@@ -218,8 +245,7 @@ public class SizeCompatUIController implements OnDisplaysChangedListener,
        if (layout == null) {
            return;
        }
        layout.updateSizeCompatInfo(taskConfig, taskListener,
                isImeShowingOnDisplay(layout.getDisplayId()));
        layout.updateSizeCompatInfo(taskConfig, taskListener, showOnDisplay(layout.getDisplayId()));
    }

    private void removeLayout(int taskId) {
@@ -250,15 +276,37 @@ public class SizeCompatUIController implements OnDisplaysChangedListener,
    }

    private void forAllLayoutsOnDisplay(int displayId, Consumer<SizeCompatUILayout> callback) {
        forAllLayouts(layout -> layout.getDisplayId() == displayId, callback);
    }

    private void forAllLayouts(Consumer<SizeCompatUILayout> callback) {
        forAllLayouts(layout -> true, callback);
    }

    private void forAllLayouts(Predicate<SizeCompatUILayout> condition,
            Consumer<SizeCompatUILayout> callback) {
        for (int i = 0; i < mActiveLayouts.size(); i++) {
            final int taskId = mActiveLayouts.keyAt(i);
            final SizeCompatUILayout layout = mActiveLayouts.get(taskId);
            if (layout != null && layout.getDisplayId() == displayId) {
            if (layout != null && condition.test(layout)) {
                callback.accept(layout);
            }
        }
    }

    /**
     * The interface for calls from outside the Shell, within the host process.
     */
    @ExternalThread
    private class SizeCompatUIImpl implements SizeCompatUI {
        @Override
        public void onKeyguardOccludedChanged(boolean occluded) {
            mMainExecutor.execute(() -> {
                SizeCompatUIController.this.onKeyguardOccludedChanged(occluded);
            });
        }
    }

    /** An implementation of {@link OnInsetsChangedListener} for a given display id. */
    private class PerDisplayOnInsetsChangedListener implements OnInsetsChangedListener {
        final int mDisplayId;
+10 −10
Original line number Diff line number Diff line
@@ -103,9 +103,9 @@ class SizeCompatUILayout {
    }

    /** Creates the activity restart button window. */
    void createSizeCompatButton(boolean isImeShowing) {
        if (isImeShowing || mButton != null) {
            // When ime is showing, wait until ime is dismiss to create UI.
    void createSizeCompatButton(boolean show) {
        if (!show || mButton != null) {
            // Wait until button should be visible.
            return;
        }
        mButton = mButtonWindowManager.createSizeCompatButton();
@@ -154,7 +154,7 @@ class SizeCompatUILayout {

    /** Called when size compat info changed. */
    void updateSizeCompatInfo(Configuration taskConfig,
            ShellTaskOrganizer.TaskListener taskListener, boolean isImeShowing) {
            ShellTaskOrganizer.TaskListener taskListener, boolean show) {
        final Configuration prevTaskConfig = mTaskConfig;
        final ShellTaskOrganizer.TaskListener prevTaskListener = mTaskListener;
        mTaskConfig = taskConfig;
@@ -170,7 +170,7 @@ class SizeCompatUILayout {
        if (mButton == null || prevTaskListener != taskListener) {
            // TaskListener changed, recreate the button for new surface parent.
            release();
            createSizeCompatButton(isImeShowing);
            createSizeCompatButton(show);
            return;
        }

@@ -204,16 +204,16 @@ class SizeCompatUILayout {
        }
    }

    /** Called when IME visibility changed. */
    void updateImeVisibility(boolean isImeShowing) {
    /** Called when the visibility of the UI should change. */
    void updateVisibility(boolean show) {
        if (mButton == null) {
            // Button may not be created because ime is previous showing.
            createSizeCompatButton(isImeShowing);
            // Button may not have been created because it was hidden previously.
            createSizeCompatButton(show);
            return;
        }

        // Hide size compat UIs when IME is showing.
        final int newVisibility = isImeShowing ? View.GONE : View.VISIBLE;
        final int newVisibility = show ? View.VISIBLE : View.GONE;
        if (mButton.getVisibility() != newVisibility) {
            mButton.setVisibility(newVisibility);
        }
+86 −10
Original line number Diff line number Diff line
@@ -26,6 +26,7 @@ import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;

import android.content.Context;
@@ -43,6 +44,7 @@ import com.android.wm.shell.common.DisplayImeController;
import com.android.wm.shell.common.DisplayInsetsController;
import com.android.wm.shell.common.DisplayInsetsController.OnInsetsChangedListener;
import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;

import org.junit.Before;
@@ -72,6 +74,7 @@ public class SizeCompatUIControllerTest extends ShellTestCase {
    private @Mock DisplayImeController mMockImeController;
    private @Mock ShellTaskOrganizer.TaskListener mMockTaskListener;
    private @Mock SyncTransactionQueue mMockSyncQueue;
    private @Mock ShellExecutor mMockExecutor;
    private @Mock SizeCompatUILayout mMockLayout;

    @Captor
@@ -85,7 +88,7 @@ public class SizeCompatUIControllerTest extends ShellTestCase {
        doReturn(DISPLAY_ID).when(mMockLayout).getDisplayId();
        doReturn(TASK_ID).when(mMockLayout).getTaskId();
        mController = new SizeCompatUIController(mContext, mMockDisplayController,
                mMockDisplayInsetsController, mMockImeController, mMockSyncQueue) {
                mMockDisplayInsetsController, mMockImeController, mMockSyncQueue, mMockExecutor) {
            @Override
            SizeCompatUILayout createLayout(Context context, int displayId, int taskId,
                    Configuration taskConfig, ShellTaskOrganizer.TaskListener taskListener) {
@@ -106,19 +109,17 @@ public class SizeCompatUIControllerTest extends ShellTestCase {
        final Configuration taskConfig = new Configuration();

        // Verify that the restart button is added with non-null size compat info.
        mController.onSizeCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig,
                mMockTaskListener);
        mController.onSizeCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig, mMockTaskListener);

        verify(mController).createLayout(any(), eq(DISPLAY_ID), eq(TASK_ID), eq(taskConfig),
                eq(mMockTaskListener));

        // Verify that the restart button is updated with non-null new size compat info.
        final Configuration newTaskConfig = new Configuration();
        mController.onSizeCompatInfoChanged(DISPLAY_ID, TASK_ID, newTaskConfig,
                mMockTaskListener);
        mController.onSizeCompatInfoChanged(DISPLAY_ID, TASK_ID, newTaskConfig, mMockTaskListener);

        verify(mMockLayout).updateSizeCompatInfo(taskConfig, mMockTaskListener,
                false /* isImeShowing */);
                true /* show */);

        // Verify that the restart button is removed with null size compat info.
        mController.onSizeCompatInfoChanged(DISPLAY_ID, TASK_ID, null, mMockTaskListener);
@@ -196,15 +197,90 @@ public class SizeCompatUIControllerTest extends ShellTestCase {
    @Test
    public void testChangeButtonVisibilityOnImeShowHide() {
        final Configuration taskConfig = new Configuration();
        mController.onSizeCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig,
                mMockTaskListener);
        mController.onSizeCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig, mMockTaskListener);

        // Verify that the restart button is hidden after IME is showing.
        mController.onImeVisibilityChanged(DISPLAY_ID, true /* isShowing */);

        verify(mMockLayout).updateImeVisibility(true);
        verify(mMockLayout).updateVisibility(false);

        // Verify button remains hidden while IME is showing.
        mController.onSizeCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig, mMockTaskListener);

        verify(mMockLayout).updateSizeCompatInfo(taskConfig, mMockTaskListener,
                false /* show */);

        // Verify button is shown after IME is hidden.
        mController.onImeVisibilityChanged(DISPLAY_ID, false /* isShowing */);

        verify(mMockLayout).updateImeVisibility(false);
        verify(mMockLayout).updateVisibility(true);
    }

    @Test
    public void testChangeButtonVisibilityOnKeyguardOccludedChanged() {
        final Configuration taskConfig = new Configuration();
        mController.onSizeCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig, mMockTaskListener);

        // Verify that the restart button is hidden after keyguard becomes occluded.
        mController.onKeyguardOccludedChanged(true);

        verify(mMockLayout).updateVisibility(false);

        // Verify button remains hidden while keyguard is occluded.
        mController.onSizeCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig, mMockTaskListener);

        verify(mMockLayout).updateSizeCompatInfo(taskConfig, mMockTaskListener,
                false /* show */);

        // Verify button is shown after keyguard becomes not occluded.
        mController.onKeyguardOccludedChanged(false);

        verify(mMockLayout).updateVisibility(true);
    }

    @Test
    public void testButtonRemainsHiddenOnKeyguardOccludedFalseWhenImeIsShowing() {
        final Configuration taskConfig = new Configuration();
        mController.onSizeCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig, mMockTaskListener);

        mController.onImeVisibilityChanged(DISPLAY_ID, true /* isShowing */);
        mController.onKeyguardOccludedChanged(true);

        verify(mMockLayout, times(2)).updateVisibility(false);

        clearInvocations(mMockLayout);

        // Verify button remains hidden after keyguard becomes not occluded since IME is showing.
        mController.onKeyguardOccludedChanged(false);

        verify(mMockLayout).updateVisibility(false);

        // Verify button is shown after IME is not showing.
        mController.onImeVisibilityChanged(DISPLAY_ID, false /* isShowing */);

        verify(mMockLayout).updateVisibility(true);
    }

    @Test
    public void testButtonRemainsHiddenOnImeHideWhenKeyguardIsOccluded() {
        final Configuration taskConfig = new Configuration();
        mController.onSizeCompatInfoChanged(DISPLAY_ID, TASK_ID, taskConfig, mMockTaskListener);

        mController.onImeVisibilityChanged(DISPLAY_ID, true /* isShowing */);
        mController.onKeyguardOccludedChanged(true);

        verify(mMockLayout, times(2)).updateVisibility(false);

        clearInvocations(mMockLayout);

        // Verify button remains hidden after IME is hidden since keyguard is occluded.
        mController.onImeVisibilityChanged(DISPLAY_ID, false /* isShowing */);

        verify(mMockLayout).updateVisibility(false);

        // Verify button is shown after keyguard becomes not occluded.
        mController.onKeyguardOccludedChanged(false);

        verify(mMockLayout).updateVisibility(true);
    }
}
Loading