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

Commit a681f86d authored by Anton Potapov's avatar Anton Potapov Committed by Android (Google) Code Review
Browse files

Merge "Fix TaskView usage in DetailsDialog" into main

parents 56167911 70eef43e
Loading
Loading
Loading
Loading
+15 −43
Original line number Diff line number Diff line
@@ -18,26 +18,25 @@ package com.android.systemui.controls.ui

import android.app.Activity
import android.app.ActivityOptions
import android.app.ActivityTaskManager
import android.app.ActivityTaskManager.INVALID_TASK_ID
import android.app.ComponentOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED
import android.app.Dialog
import android.app.PendingIntent
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.graphics.Rect
import android.view.View
import android.view.ViewGroup
import android.view.WindowInsets
import android.view.WindowInsets.Type
import android.view.WindowManager
import android.widget.ImageView
import androidx.annotation.VisibleForTesting
import com.android.internal.policy.ScreenDecorationsUtils
import com.android.systemui.res.R
import com.android.systemui.broadcast.BroadcastSender
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.util.boundsOnScreen
import com.android.wm.shell.taskview.TaskView

/**
@@ -65,8 +64,8 @@ class DetailDialog(
        private const val EXTRA_USE_PANEL = "controls.DISPLAY_IN_PANEL"
    }

    var detailTaskId = INVALID_TASK_ID
    private lateinit var taskViewContainer: View
    private lateinit var controlDetailRoot: View
    private val taskWidthPercentWidth = activityContext.resources.getFloat(
        R.dimen.controls_task_view_width_percentage
    )
@@ -79,12 +78,7 @@ class DetailDialog(
        addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK)
    }

    fun removeDetailTask() {
        if (detailTaskId == INVALID_TASK_ID) return
        ActivityTaskManager.getInstance().removeTask(detailTaskId)
        detailTaskId = INVALID_TASK_ID
    }

    @VisibleForTesting
    val stateCallback = object : TaskView.Listener {
        override fun onInitialized() {
            taskViewContainer.apply {
@@ -98,33 +92,29 @@ class DetailDialog(
                activityContext,
                0 /* enterResId */,
                0 /* exitResId */
            ).setPendingIntentBackgroundActivityStartMode(MODE_BACKGROUND_ACTIVITY_START_ALLOWED)
            options.isPendingIntentBackgroundActivityLaunchAllowedByPermission = true
            ).apply {
                pendingIntentBackgroundActivityStartMode = MODE_BACKGROUND_ACTIVITY_START_ALLOWED
                isPendingIntentBackgroundActivityLaunchAllowedByPermission = true
                taskAlwaysOnTop = true
            }

            taskView.startActivity(
                pendingIntent,
                fillInIntent,
                options,
                getTaskViewBounds()
                taskView.boundsOnScreen,
            )
        }

        override fun onTaskRemovalStarted(taskId: Int) {
            detailTaskId = INVALID_TASK_ID
            dismiss()
            taskView.release()
        }

        override fun onTaskCreated(taskId: Int, name: ComponentName?) {
            detailTaskId = taskId
            requireViewById<ViewGroup>(R.id.controls_activity_view).apply {
                setAlpha(1f)
            }
        }

        override fun onReleased() {
            removeDetailTask()
        }

        override fun onBackPressedOnTaskRoot(taskId: Int) {
            dismiss()
        }
@@ -138,6 +128,9 @@ class DetailDialog(
        setContentView(R.layout.controls_detail_dialog)

        taskViewContainer = requireViewById<ViewGroup>(R.id.control_task_view_container)
        controlDetailRoot = requireViewById<View>(R.id.control_detail_root).apply {
            setOnClickListener { _: View -> dismiss() }
        }

        requireViewById<ViewGroup>(R.id.controls_activity_view).apply {
            addView(taskView)
@@ -147,13 +140,9 @@ class DetailDialog(
        requireViewById<ImageView>(R.id.control_detail_close).apply {
            setOnClickListener { _: View -> dismiss() }
        }
        requireViewById<View>(R.id.control_detail_root).apply {
            setOnClickListener { _: View -> dismiss() }
        }

        requireViewById<ImageView>(R.id.control_detail_open_in_app).apply {
            setOnClickListener { v: View ->
                removeDetailTask()
                dismiss()

                val action = ActivityStarter.OnDismissAction {
@@ -201,26 +190,9 @@ class DetailDialog(
        taskView.setListener(cvh.uiExecutor, stateCallback)
    }

    fun getTaskViewBounds(): Rect {
        val wm = checkNotNull(context.getSystemService(WindowManager::class.java))
        val windowMetrics = wm.getCurrentWindowMetrics()
        val rect = windowMetrics.bounds
        val metricInsets = windowMetrics.windowInsets
        val insets = metricInsets.getInsetsIgnoringVisibility(Type.systemBars()
                or Type.displayCutout())
        val headerHeight = context.resources.getDimensionPixelSize(
                R.dimen.controls_detail_dialog_header_height)

        val finalRect = Rect(rect.left - insets.left /* left */,
                rect.top + insets.top + headerHeight /* top */,
                rect.right - insets.right /* right */,
                rect.bottom - insets.bottom /* bottom */)
        return finalRect
    }

    override fun dismiss() {
        if (!isShowing()) return
        taskView.release()
        taskView.removeTask()

        val isActivityFinishing =
            (activityContext as? Activity)?.let { it.isFinishing || it.isDestroyed }
+0 −6
Original line number Diff line number Diff line
@@ -18,7 +18,6 @@
package com.android.systemui.controls.ui

import android.app.ActivityOptions
import android.app.ActivityTaskManager.INVALID_TASK_ID
import android.app.PendingIntent
import android.content.ComponentName
import android.content.Context
@@ -45,8 +44,6 @@ class PanelTaskViewController(
        taskView.alpha = 0f
    }

    private var detailTaskId = INVALID_TASK_ID

    private val fillInIntent =
        Intent().apply {
            // Apply flags to make behaviour match documentLaunchMode=always.
@@ -57,7 +54,6 @@ class PanelTaskViewController(
    private val stateCallback =
        object : TaskView.Listener {
            override fun onInitialized() {

                val options =
                    ActivityOptions.makeCustomAnimation(
                        activityContext,
@@ -88,12 +84,10 @@ class PanelTaskViewController(
            }

            override fun onTaskRemovalStarted(taskId: Int) {
                detailTaskId = INVALID_TASK_ID
                release()
            }

            override fun onTaskCreated(taskId: Int, name: ComponentName?) {
                detailTaskId = taskId
                taskView.alpha = 1f
            }

+49 −23
Original line number Diff line number Diff line
@@ -18,10 +18,13 @@ package com.android.systemui.controls.ui

import android.app.ActivityOptions
import android.app.PendingIntent
import android.content.Context
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import androidx.test.ext.junit.rules.ActivityScenarioRule
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.activity.EmptyTestActivity
import com.android.systemui.broadcast.BroadcastSender
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.statusbar.policy.KeyguardStateController
@@ -30,11 +33,13 @@ import com.android.systemui.util.mockito.capture
import com.android.wm.shell.taskview.TaskView
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers.eq
import org.mockito.Mock
import org.mockito.Mockito.any
import org.mockito.Mockito.never
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations

@@ -43,31 +48,31 @@ import org.mockito.MockitoAnnotations
@TestableLooper.RunWithLooper
class DetailDialogTest : SysuiTestCase() {

    @Mock
    private lateinit var taskView: TaskView
    @Mock
    private lateinit var broadcastSender: BroadcastSender
    @Mock
    private lateinit var controlViewHolder: ControlViewHolder
    @Mock
    private lateinit var pendingIntent: PendingIntent
    @Mock
    private lateinit var keyguardStateController: KeyguardStateController
    @Mock
    private lateinit var activityStarter: ActivityStarter
    @Rule
    @JvmField
    val activityRule: ActivityScenarioRule<EmptyTestActivity> =
        ActivityScenarioRule(EmptyTestActivity::class.java)

    @Mock private lateinit var taskView: TaskView
    @Mock private lateinit var broadcastSender: BroadcastSender
    @Mock private lateinit var controlViewHolder: ControlViewHolder
    @Mock private lateinit var pendingIntent: PendingIntent
    @Mock private lateinit var keyguardStateController: KeyguardStateController
    @Mock private lateinit var activityStarter: ActivityStarter

    private lateinit var underTest: DetailDialog

    @Before
    fun setUp() {
        MockitoAnnotations.initMocks(this)

        underTest = createDialog(pendingIntent)
    }

    @Test
    fun testPendingIntentIsUnModified() {
        // GIVEN the dialog is created with a PendingIntent
        val dialog = createDialog(pendingIntent)

        // WHEN the TaskView is initialized
        dialog.stateCallback.onInitialized()
        underTest.stateCallback.onInitialized()

        // THEN the PendingIntent used to call startActivity is unmodified by systemui
        verify(taskView).startActivity(eq(pendingIntent), any(), any(), any())
@@ -75,11 +80,8 @@ class DetailDialogTest : SysuiTestCase() {

    @Test
    fun testActivityOptionsAllowBal() {
        // GIVEN the dialog is created with a PendingIntent
        val dialog = createDialog(pendingIntent)

        // WHEN the TaskView is initialized
        dialog.stateCallback.onInitialized()
        underTest.stateCallback.onInitialized()

        val optionsCaptor = argumentCaptor<ActivityOptions>()

@@ -90,17 +92,41 @@ class DetailDialogTest : SysuiTestCase() {
            .isEqualTo(ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED)
        assertThat(optionsCaptor.value.isPendingIntentBackgroundActivityLaunchAllowedByPermission)
            .isTrue()
        assertThat(optionsCaptor.value.taskAlwaysOnTop).isTrue()
    }

    @Test
    fun testDismissRemovesTheTask() {
        activityRule.scenario.onActivity {
            underTest = createDialog(pendingIntent, it)
            underTest.show()

            underTest.dismiss()

            verify(taskView).removeTask()
            verify(taskView, never()).release()
        }
    }

    @Test
    fun testTaskRemovalReleasesTaskView() {
        underTest.stateCallback.onTaskRemovalStarted(0)

        verify(taskView).release()
    }

    private fun createDialog(pendingIntent: PendingIntent): DetailDialog {
    private fun createDialog(
        pendingIntent: PendingIntent,
        context: Context = mContext,
    ): DetailDialog {
        return DetailDialog(
            mContext,
            context,
            broadcastSender,
            taskView,
            pendingIntent,
            controlViewHolder,
            keyguardStateController,
            activityStarter
            activityStarter,
        )
    }
}