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

Commit 5b0287be authored by Miranda Kephart's avatar Miranda Kephart
Browse files

Dismiss system windows on screenshot action

Bug: 309933761
Fix: 309933761
Flag: ACONFIG com.android.systemui.screenshot_action_dismiss_system_windows DEVELOPMENT
Test: take a screenshot with system overlay visible (e.g. power menu)
and verify that after tapping screenshot buttons, the overlay disappears
Test: atest ActionIntentExecutorTest

Change-Id: I4502fe84de520c6ebaf06749807bead0f023bfae
parent 068ef593
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -376,6 +376,13 @@ flag {
    bug: "202262476"
}

flag {
    name: "screenshot_action_dismiss_system_windows"
    namespace: "systemui"
    description: "Dismiss existing system windows when starting action from screenshot UI"
    bug: "309933761"
}

flag {
   name: "run_fingerprint_detect_on_dismissible_keyguard"
   namespace: "systemui"
+13 −1
Original line number Diff line number Diff line
@@ -31,10 +31,13 @@ import android.view.WindowManager
import android.view.WindowManagerGlobal
import com.android.app.tracing.coroutines.launch
import com.android.internal.infra.ServiceConnector
import com.android.systemui.Flags.screenshotActionDismissSystemWindows
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.settings.DisplayTracker
import com.android.systemui.shared.system.ActivityManagerWrapper
import com.android.systemui.statusbar.phone.CentralSurfaces
import javax.inject.Inject
import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.CoroutineDispatcher
@@ -46,9 +49,11 @@ class ActionIntentExecutor
@Inject
constructor(
    private val context: Context,
    private val activityManagerWrapper: ActivityManagerWrapper,
    @Application private val applicationScope: CoroutineScope,
    @Main private val mainDispatcher: CoroutineDispatcher,
    private val displayTracker: DisplayTracker,
    private val keyguardController: ScreenshotKeyguardController,
) {
    /**
     * Execute the given intent with startActivity while performing operations for screenshot action
@@ -74,7 +79,14 @@ constructor(
        user: UserHandle,
        overrideTransition: Boolean,
    ) {
        if (screenshotActionDismissSystemWindows()) {
            keyguardController.dismiss()
            activityManagerWrapper.closeSystemWindows(
                CentralSurfaces.SYSTEM_DIALOG_REASON_SCREENSHOT
            )
        } else {
            dismissKeyguard()
        }

        if (user == myUserHandle()) {
            withContext(mainDispatcher) { context.startActivity(intent, options) }
+46 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2023 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.screenshot

import android.content.Context
import android.content.Intent
import com.android.internal.infra.ServiceConnector
import javax.inject.Inject
import kotlinx.coroutines.CompletableDeferred

open class ScreenshotKeyguardController @Inject constructor(context: Context) {
    private val proxyConnector: ServiceConnector<IScreenshotProxy> =
        ServiceConnector.Impl(
            context,
            Intent(context, ScreenshotProxyService::class.java),
            Context.BIND_AUTO_CREATE or Context.BIND_WAIVE_PRIORITY or Context.BIND_NOT_VISIBLE,
            context.userId,
            IScreenshotProxy.Stub::asInterface
        )

    suspend fun dismiss() {
        val completion = CompletableDeferred<Unit>()
        val onDoneBinder =
            object : IOnDoneCallback.Stub() {
                override fun onDone(success: Boolean) {
                    completion.complete(Unit)
                }
            }
        proxyConnector.post { it.dismissKeyguard(onDoneBinder) }
        completion.await()
    }
}
+73 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2023 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.screenshot

import android.content.Intent
import android.os.Process.myUserHandle
import android.platform.test.annotations.EnableFlags
import android.testing.AndroidTestingRunner
import android.testing.TestableContext
import com.android.systemui.Flags
import com.android.systemui.SysuiTestCase
import com.android.systemui.settings.DisplayTracker
import com.android.systemui.shared.system.ActivityManagerWrapper
import com.android.systemui.statusbar.phone.CentralSurfaces
import com.android.systemui.util.mockito.mock
import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.TestCoroutineScheduler
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runTest
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mockito.verify

@RunWith(AndroidTestingRunner::class)
class ActionIntentExecutorTest : SysuiTestCase() {

    private val scheduler = TestCoroutineScheduler()
    private val mainDispatcher = StandardTestDispatcher(scheduler)
    private val testScope = TestScope(mainDispatcher)
    private val testableContext = TestableContext(mContext)

    private val activityManagerWrapper = mock<ActivityManagerWrapper>()
    private val displayTracker = mock<DisplayTracker>()
    private val keyguardController = mock<ScreenshotKeyguardController>()

    private val actionIntentExecutor =
        ActionIntentExecutor(
            testableContext,
            activityManagerWrapper,
            testScope,
            mainDispatcher,
            displayTracker,
            keyguardController,
        )

    @Test
    @EnableFlags(Flags.FLAG_SCREENSHOT_ACTION_DISMISS_SYSTEM_WINDOWS)
    fun launchIntent_callsCloseSystemWindows() =
        testScope.runTest {
            val intent = Intent(Intent.ACTION_EDIT).apply { flags = Intent.FLAG_ACTIVITY_NEW_TASK }
            val userHandle = myUserHandle()

            actionIntentExecutor.launchIntent(intent, null, userHandle, false)
            scheduler.advanceUntilIdle()

            verify(activityManagerWrapper)
                .closeSystemWindows(CentralSurfaces.SYSTEM_DIALOG_REASON_SCREENSHOT)
        }
}