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

Commit 060e8c4b authored by Jeremy Sim's avatar Jeremy Sim Committed by Android (Google) Code Review
Browse files

Merge "Fix bug with half-black screen when folding or waking with a...

Merge "Fix bug with half-black screen when folding or waking with a SHOW_WHEN_LOCKED app in splitscreen" into main
parents 2f54428d 6d05b47d
Loading
Loading
Loading
Loading
+128 −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.internal.policy;

import android.content.ContentResolver;
import android.content.Context;
import android.database.ContentObserver;
import android.os.Handler;
import android.os.UserHandle;
import android.provider.Settings;

import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;

import java.util.Set;

/**
 * A ContentObserver that listens for changes in the "Continue using apps on fold" setting. This
 * setting determines a device's behavior when the user folds the device.
 * @hide
 *
 * Keep the setting values in this class in sync with the values in
 * {@link com.android.server.utils.FoldSettingProvider} and
 * {@link com.android.settings.display.FoldLockBehaviorSettings}
 */
public class FoldLockSettingsObserver extends ContentObserver {
    /** The setting for "stay awake on fold". */
    public static final String SETTING_VALUE_STAY_AWAKE_ON_FOLD = "stay_awake_on_fold_key";
    /** The setting for "swipe up to continue". */
    public static final String SETTING_VALUE_SELECTIVE_STAY_AWAKE = "selective_stay_awake_key";
    /** The setting for "always sleep on fold". */
    public static final String SETTING_VALUE_SLEEP_ON_FOLD = "sleep_on_fold_key";
    public static final String SETTING_VALUE_DEFAULT = SETTING_VALUE_SELECTIVE_STAY_AWAKE;
    private static final Set<String> SETTING_VALUES = Set.of(SETTING_VALUE_STAY_AWAKE_ON_FOLD,
            SETTING_VALUE_SELECTIVE_STAY_AWAKE, SETTING_VALUE_SLEEP_ON_FOLD);

    private final Context mContext;

    /** The cached value of the setting. */
    @VisibleForTesting
    String mFoldLockSetting;

    public FoldLockSettingsObserver(Handler handler, Context context) {
        super(handler);
        mContext = context;
    }

    /** Registers the observer and updates the cache for the first time. */
    public void register() {
        final ContentResolver r = mContext.getContentResolver();
        r.registerContentObserver(
                Settings.System.getUriFor(Settings.System.FOLD_LOCK_BEHAVIOR),
                false, this, UserHandle.USER_ALL);
        requestAndCacheFoldLockSetting();
    }

    /** Unregisters the observer. */
    public void unregister() {
        mContext.getContentResolver().unregisterContentObserver(this);
    }

    /** Runs when settings changes. */
    @Override
    public void onChange(boolean selfChange) {
        requestAndCacheFoldLockSetting();
    }

    /**
     * Requests and caches the current FOLD_LOCK_BEHAVIOR setting, which should be one of three
     * values: SETTING_VALUE_STAY_AWAKE_ON_FOLD, SETTING_VALUE_SELECTIVE_STAY_AWAKE,
     * SETTING_VALUE_SLEEP_ON_FOLD. If null (not set), returns the system default.
     */
    @VisibleForTesting
    void requestAndCacheFoldLockSetting() {
        String currentSetting = request();

        if (currentSetting == null || !SETTING_VALUES.contains(currentSetting)) {
            currentSetting = SETTING_VALUE_DEFAULT;
        }

        setCurrentFoldSetting(currentSetting);
    }

    /**
     * Makes a binder call to request the current FOLD_LOCK_BEHAVIOR setting.
     */
    @VisibleForTesting
    @Nullable
    String request() {
        return Settings.System.getStringForUser(mContext.getContentResolver(),
                Settings.System.FOLD_LOCK_BEHAVIOR, UserHandle.USER_CURRENT);
    }

    /** Caches the fold-lock behavior received from Settings. */
    @VisibleForTesting
    void setCurrentFoldSetting(String newSetting) {
        mFoldLockSetting = newSetting;
    }

    /** Used by external requesters: checks if the current setting is "stay awake on fold". */
    public boolean isStayAwakeOnFold() {
        return mFoldLockSetting.equals(SETTING_VALUE_STAY_AWAKE_ON_FOLD);
    }

    /** Used by external requesters: checks if the current setting is "swipe up to continue". */
    public boolean isSelectiveStayAwake() {
        return mFoldLockSetting.equals(SETTING_VALUE_SELECTIVE_STAY_AWAKE);
    }

    /** Used by external requesters: checks if the current setting is "sleep on fold". */
    public boolean isSleepOnFold() {
        return mFoldLockSetting.equals(SETTING_VALUE_SLEEP_ON_FOLD);
    }
}
+110 −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.internal.policy;

import static com.android.internal.policy.FoldLockSettingsObserver.SETTING_VALUE_DEFAULT;
import static com.android.internal.policy.FoldLockSettingsObserver.SETTING_VALUE_SLEEP_ON_FOLD;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;

import android.content.ContentResolver;
import android.content.Context;
import android.os.Handler;
import android.os.UserHandle;
import android.provider.Settings;

import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;

/**
 * Tests for {@link FoldLockSettingsObserver}.
 */
@SmallTest
@RunWith(AndroidJUnit4.class)
public final class FoldLockSettingsObserverTest {
    @Mock
    private Context mContext;
    @Mock
    private Handler mHandler;
    @Mock
    private ContentResolver mContentResolver;

    private FoldLockSettingsObserver mFoldLockSettingsObserver;

    @Before
    public void setup() {
        MockitoAnnotations.initMocks(this);

        mFoldLockSettingsObserver =
                spy(new FoldLockSettingsObserver(mHandler, mContext));

        doReturn(mContentResolver).when(mContext).getContentResolver();
        doReturn(SETTING_VALUE_DEFAULT).when(mFoldLockSettingsObserver).request();

        mFoldLockSettingsObserver.register();
    }

    @Test
    public void shouldRegister() {
        doReturn(mContentResolver).when(mContext).getContentResolver();

        mFoldLockSettingsObserver.register();

        verify(mContentResolver).registerContentObserver(
                Settings.System.getUriFor(Settings.System.FOLD_LOCK_BEHAVIOR),
                false /*notifyForDescendants */,
                mFoldLockSettingsObserver,
                UserHandle.USER_ALL
        );
    }

    @Test
    public void shouldUnregister() {
        mFoldLockSettingsObserver.unregister();

        verify(mContentResolver).unregisterContentObserver(mFoldLockSettingsObserver);
    }

    @Test
    public void shouldCacheNewValue() {
        // Reset the mock's behavior and call count to zero.
        reset(mFoldLockSettingsObserver);
        doReturn(SETTING_VALUE_SLEEP_ON_FOLD).when(mFoldLockSettingsObserver).request();

        // Setting is DEFAULT at first.
        assertEquals(SETTING_VALUE_DEFAULT, mFoldLockSettingsObserver.mFoldLockSetting);

        // Cache new setting.
        mFoldLockSettingsObserver.requestAndCacheFoldLockSetting();

        // Check that setter was called once and change went through properly.
        verify(mFoldLockSettingsObserver).setCurrentFoldSetting(anyString());
        assertTrue(mFoldLockSettingsObserver.isSleepOnFold());
    }
}
+3 −2
Original line number Diff line number Diff line
@@ -369,13 +369,14 @@ public abstract class WMShellModule {
            Optional<WindowDecorViewModel> windowDecorViewModel,
            Optional<DesktopTasksController> desktopTasksController,
            MultiInstanceHelper multiInstanceHelper,
            @ShellMainThread ShellExecutor mainExecutor) {
            @ShellMainThread ShellExecutor mainExecutor,
            @ShellMainThread Handler mainHandler) {
        return new SplitScreenController(context, shellInit, shellCommandHandler, shellController,
                shellTaskOrganizer, syncQueue, rootTaskDisplayAreaOrganizer, displayController,
                displayImeController, displayInsetsController, dragAndDropController, transitions,
                transactionPool, iconProvider, recentTasks, launchAdjacentController,
                windowDecorViewModel, desktopTasksController, null /* stageCoordinator */,
                multiInstanceHelper, mainExecutor);
                multiInstanceHelper, mainExecutor, mainHandler);
    }

    //
+3 −0
Original line number Diff line number Diff line
@@ -113,6 +113,9 @@ public interface SplitScreen {
    /** Called when device waking up finished. */
    void onFinishedWakingUp();

    /** Called when device starts going to sleep (screen off). */
    void onStartedGoingToSleep();

    /** Called when requested to go to fullscreen from the current active split app. */
    void goToFullscreenFromSplit();

+16 −3
Original line number Diff line number Diff line
@@ -50,6 +50,7 @@ import android.content.pm.LauncherApps;
import android.content.pm.ShortcutInfo;
import android.graphics.Rect;
import android.os.Bundle;
import android.os.Handler;
import android.os.RemoteException;
import android.os.UserHandle;
import android.util.ArrayMap;
@@ -180,6 +181,7 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
    private final LauncherApps mLauncherApps;
    private final RootTaskDisplayAreaOrganizer mRootTDAOrganizer;
    private final ShellExecutor mMainExecutor;
    private final Handler mMainHandler;
    private final SplitScreenImpl mImpl = new SplitScreenImpl();
    private final DisplayController mDisplayController;
    private final DisplayImeController mDisplayImeController;
@@ -227,7 +229,8 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
            Optional<DesktopTasksController> desktopTasksController,
            @Nullable StageCoordinator stageCoordinator,
            MultiInstanceHelper multiInstanceHelper,
            ShellExecutor mainExecutor) {
            ShellExecutor mainExecutor,
            Handler mainHandler) {
        mShellCommandHandler = shellCommandHandler;
        mShellController = shellController;
        mTaskOrganizer = shellTaskOrganizer;
@@ -236,6 +239,7 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
        mLauncherApps = context.getSystemService(LauncherApps.class);
        mRootTDAOrganizer = rootTDAOrganizer;
        mMainExecutor = mainExecutor;
        mMainHandler = mainHandler;
        mDisplayController = displayController;
        mDisplayImeController = displayImeController;
        mDisplayInsetsController = displayInsetsController;
@@ -292,7 +296,7 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
        return new StageCoordinator(mContext, DEFAULT_DISPLAY, mSyncQueue,
                mTaskOrganizer, mDisplayController, mDisplayImeController,
                mDisplayInsetsController, mTransitions, mTransactionPool, mIconProvider,
                mMainExecutor, mRecentTasksOptional, mLaunchAdjacentController,
                mMainExecutor, mMainHandler, mRecentTasksOptional, mLaunchAdjacentController,
                mWindowDecorViewModel);
    }

@@ -448,13 +452,17 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
    @Override
    public void onKeyguardVisibilityChanged(boolean visible, boolean occluded,
            boolean animatingDismiss) {
        mStageCoordinator.onKeyguardVisibilityChanged(visible);
        mStageCoordinator.onKeyguardStateChanged(visible, occluded);
    }

    public void onFinishedWakingUp() {
        mStageCoordinator.onFinishedWakingUp();
    }

    public void onStartedGoingToSleep() {
        mStageCoordinator.onStartedGoingToSleep();
    }

    public void exitSplitScreenOnHide(boolean exitSplitScreenOnHide) {
        mStageCoordinator.exitSplitScreenOnHide(exitSplitScreenOnHide);
    }
@@ -1200,6 +1208,11 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
            mMainExecutor.execute(SplitScreenController.this::onFinishedWakingUp);
        }

        @Override
        public void onStartedGoingToSleep() {
            mMainExecutor.execute(SplitScreenController.this::onStartedGoingToSleep);
        }

        @Override
        public void goToFullscreenFromSplit() {
            mMainExecutor.execute(SplitScreenController.this::goToFullscreenFromSplit);
Loading