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

Commit 2f59f928 authored by Adrian Roos's avatar Adrian Roos Committed by Automerger Merge Worker
Browse files

Merge "GlobalActions: Disable IME focusability" into rvc-dev am: 48695505 am: a44bbba4

Change-Id: I8064331488562eac4e01bbc6d75fd1aac9d09a82
parents d47d7b4a a44bbba4
Loading
Loading
Loading
Loading
+3 −0
Original line number Original line Diff line number Diff line
@@ -15,6 +15,7 @@
package com.android.systemui.globalactions;
package com.android.systemui.globalactions;


import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
import static android.view.WindowManager.ScreenshotSource.SCREENSHOT_GLOBAL_ACTIONS;
import static android.view.WindowManager.ScreenshotSource.SCREENSHOT_GLOBAL_ACTIONS;
import static android.view.WindowManager.TAKE_SCREENSHOT_FULLSCREEN;
import static android.view.WindowManager.TAKE_SCREENSHOT_FULLSCREEN;
@@ -485,6 +486,8 @@ public class GlobalActionsDialog implements DialogInterface.OnDismissListener,
        attrs.setTitle("ActionsDialog");
        attrs.setTitle("ActionsDialog");
        attrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
        attrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
        mDialog.getWindow().setAttributes(attrs);
        mDialog.getWindow().setAttributes(attrs);
        // Don't acquire soft keyboard focus, to avoid destroying state when capturing bugreports
        mDialog.getWindow().setFlags(FLAG_ALT_FOCUSABLE_IM, FLAG_ALT_FOCUSABLE_IM);
        mDialog.show();
        mDialog.show();
        mWindowManagerFuncs.onGlobalActionsShown();
        mWindowManagerFuncs.onGlobalActionsShown();
    }
    }
+5 −0
Original line number Original line Diff line number Diff line
@@ -69,6 +69,11 @@
            android:exported="false"
            android:exported="false"
            android:resizeableActivity="true" />
            android:resizeableActivity="true" />


        <activity
            android:name="com.android.systemui.globalactions.GlobalActionsImeTest$TestActivity"
            android:excludeFromRecents="true"
            android:exported="false" />

        <provider
        <provider
            android:name="androidx.lifecycle.ProcessLifecycleOwnerInitializer"
            android:name="androidx.lifecycle.ProcessLifecycleOwnerInitializer"
            tools:replace="android:authorities"
            tools:replace="android:authorities"
+152 −0
Original line number Original line Diff line number Diff line
/*
 * Copyright (C) 2020 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.systemui.globalactions;

import static android.view.WindowInsets.Type.ime;

import static org.junit.Assert.assertTrue;

import android.app.Activity;
import android.os.Bundle;
import android.os.SystemClock;
import android.view.View;
import android.view.WindowInsets;
import android.view.WindowInsetsController;
import android.widget.EditText;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.test.filters.LargeTest;
import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.rule.ActivityTestRule;

import org.junit.Rule;
import org.junit.Test;

import java.util.concurrent.TimeUnit;
import java.util.function.BooleanSupplier;

@LargeTest
public class GlobalActionsImeTest {

    @Rule
    public ActivityTestRule<TestActivity> mActivityTestRule = new ActivityTestRule<>(
            TestActivity.class, false, false);

    /**
     * This test verifies that GlobalActions, which is frequently used to capture bugreports,
     * doesn't interfere with the IME, i.e. soft-keyboard state.
     */
    @Test
    public void testGlobalActions_doesntStealImeControl() {
        final TestActivity activity = mActivityTestRule.launchActivity(null);

        activity.waitFor(() -> activity.mHasFocus && activity.mControlsIme && activity.mImeVisible);

        InstrumentationRegistry.getInstrumentation().getUiAutomation().executeShellCommand(
                "input keyevent --longpress POWER"
        );

        activity.waitFor(() -> !activity.mHasFocus);
        // Give the dialog time to animate in, and steal IME focus. Unfortunately, there's currently
        // no better way to wait for this.
        SystemClock.sleep(TimeUnit.SECONDS.toMillis(2));

        runAssertionOnMainThread(() -> {
            assertTrue("IME should remain visible behind GlobalActions, but didn't",
                    activity.mControlsIme);
            assertTrue("App behind GlobalActions should remain in control of IME, but didn't",
                    activity.mImeVisible);
        });
    }

    /** Like Instrumentation.runOnMainThread(), but forwards AssertionErrors to the caller. */
    private static void runAssertionOnMainThread(Runnable r) {
        AssertionError[] t = new AssertionError[1];
        InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
            try {
                r.run();
            } catch (AssertionError e) {
                t[0] = e;
                // Ignore assertion - throwing it here would crash the main thread.
            }
        });
        if (t[0] != null) {
            throw t[0];
        }
    }

    public static class TestActivity extends Activity implements
            WindowInsetsController.OnControllableInsetsChangedListener,
            View.OnApplyWindowInsetsListener {

        private EditText mContent;
        boolean mHasFocus;
        boolean mControlsIme;
        boolean mImeVisible;

        @Override
        protected void onCreate(@Nullable Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);

            mContent = new EditText(this);
            mContent.setCursorVisible(false);  // Otherwise, main thread doesn't go idle.
            setContentView(mContent);
            mContent.requestFocus();

            getWindow().getDecorView().setOnApplyWindowInsetsListener(this);
            WindowInsetsController wic = mContent.getWindowInsetsController();
            wic.addOnControllableInsetsChangedListener(this);
            wic.show(ime());
        }

        @Override
        public void onWindowFocusChanged(boolean hasFocus) {
            synchronized (this) {
                mHasFocus = hasFocus;
                notifyAll();
            }
        }

        @Override
        public void onControllableInsetsChanged(@NonNull WindowInsetsController controller,
                int typeMask) {
            synchronized (this) {
                mControlsIme = (typeMask & ime()) != 0;
                notifyAll();
            }
        }

        void waitFor(BooleanSupplier condition) {
            synchronized (this) {
                while (!condition.getAsBoolean()) {
                    try {
                        wait(TimeUnit.SECONDS.toMillis(5));
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
            }
        }

        @Override
        public WindowInsets onApplyWindowInsets(View v, WindowInsets insets) {
            mImeVisible = insets.isVisible(ime());
            return v.onApplyWindowInsets(insets);
        }
    }
}
+4 −0
Original line number Original line Diff line number Diff line
@@ -16,6 +16,8 @@


package com.android.server.policy;
package com.android.server.policy;


import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;

import android.app.ActivityManager;
import android.app.ActivityManager;
import android.content.BroadcastReceiver;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Context;
@@ -336,6 +338,8 @@ class LegacyGlobalActions implements DialogInterface.OnDismissListener, DialogIn
                    }
                    }
        });
        });
        dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
        dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
        // Don't acquire soft keyboard focus, to avoid destroying state when capturing bugreports
        mDialog.getWindow().setFlags(FLAG_ALT_FOCUSABLE_IM, FLAG_ALT_FOCUSABLE_IM);


        dialog.setOnDismissListener(this);
        dialog.setOnDismissListener(this);