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

Commit 3532c4f7 authored by Treehugger Robot's avatar Treehugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Ensure talkback focus goes to OpenByDefault dialog" into main

parents c5a8bbb5 10e7dee8
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -68,6 +68,7 @@
                    tools:text="Gmail" />

                <TextView
                    android:id="@+id/dialog_subheader"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:textSize="12sp"
+100 −52
Original line number Diff line number Diff line
@@ -20,25 +20,31 @@ import android.app.ActivityManager.RunningTaskInfo
import android.content.Context
import android.content.pm.PackageManager.NameNotFoundException
import android.content.pm.verify.domain.DomainVerificationManager
import android.content.res.Configuration
import android.graphics.Bitmap
import android.graphics.PixelFormat
import android.graphics.Rect
import android.os.Binder
import android.util.Slog
import android.view.IWindow
import android.view.LayoutInflater
import android.view.SurfaceControl
import android.view.SurfaceControlViewHost
import android.view.WindowManager
import android.view.WindowManager.LayoutParams
import android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
import android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
import android.view.WindowManager.LayoutParams.TYPE_APPLICATION_PANEL
import android.view.WindowlessWindowManager
import android.view.accessibility.AccessibilityEvent
import android.widget.ImageView
import android.widget.RadioButton
import android.widget.TextView
import android.window.TaskConstants
import com.android.wm.shell.R
import com.android.wm.shell.common.DisplayController
import com.android.wm.shell.compatui.DialogAnimationController
import com.android.wm.shell.shared.annotations.ShellMainThread
import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalViewHostViewContainer
import com.android.wm.shell.transition.Transitions
import com.android.wm.shell.windowdecor.common.WindowDecorTaskResourceLoader
import java.util.function.Supplier
import kotlinx.coroutines.CoroutineScope
@@ -54,6 +60,7 @@ import kotlinx.coroutines.launch
internal class OpenByDefaultDialog(
    private val context: Context,
    private val userContext: Context,
    private val transitions: Transitions,
    private val taskInfo: RunningTaskInfo,
    private val taskSurface: SurfaceControl,
    private val displayController: DisplayController,
@@ -65,14 +72,15 @@ internal class OpenByDefaultDialog(
) {
    private lateinit var dialog: OpenByDefaultDialogView
    private lateinit var viewHost: SurfaceControlViewHost
    private lateinit var dialogSurfaceControl: SurfaceControl
    private var dialogContainer: AdditionalViewHostViewContainer? = null
    private lateinit var dialogWindowManager: DialogWindowManager
    private lateinit var appIconView: ImageView
    private lateinit var appNameView: TextView

    private lateinit var openInAppButton: RadioButton
    private lateinit var openInBrowserButton: RadioButton

    private val animationController =
        DialogAnimationController<OpenByDefaultDialogView>(context, "OpenByDefaultDialog")
    private val domainVerificationManager =
        userContext.getSystemService(DomainVerificationManager::class.java)!!
    private val packageName = taskInfo.baseActivity?.packageName!!
@@ -91,9 +99,6 @@ internal class OpenByDefaultDialog(

    /** Creates an open by default settings dialog. */
    fun createDialog() {
        val t = SurfaceControl.Transaction()
        val taskBounds = taskInfo.configuration.windowConfiguration.bounds

        dialog = LayoutInflater.from(context)
            .inflate(
                R.layout.open_by_default_settings_dialog,
@@ -102,47 +107,48 @@ internal class OpenByDefaultDialog(
        appIconView = dialog.requireViewById(R.id.application_icon)
        appNameView = dialog.requireViewById(R.id.application_name)

        // TODO: ag/34061541 - once landed, can refactor with simpler fix
        transitions.runOnIdle(this::createDialogWindow)

        dialog.setDismissOnClickListener { closeMenu() }
        dialog.setConfirmButtonClickListener {
            setDefaultLinkHandlingSetting()
            closeMenu()
        }

        listener.onDialogCreated()
    }

    private fun createDialogWindow() {
        val display = displayController.getDisplay(taskInfo.displayId)
        val builder: SurfaceControl.Builder = SurfaceControl.Builder()
        dialogSurfaceControl = builder
            .setName("Open by Default Dialog of Task=" + taskInfo.taskId)
            .setContainerLayer()
            .setParent(taskSurface)
            .setCallsite("OpenByDefaultDialog#createDialog")
            .build()
        t.setPosition(dialogSurfaceControl, 0f, 0f)
            .setWindowCrop(dialogSurfaceControl, taskBounds.width(), taskBounds.height())
            .setLayer(dialogSurfaceControl, TaskConstants.TASK_CHILD_LAYER_SETTINGS_DIALOG)
            .show(dialogSurfaceControl)
        val lp = WindowManager.LayoutParams(
        val taskBounds = taskInfo.configuration.windowConfiguration.bounds
        val lp = LayoutParams(
            taskBounds.width(),
            taskBounds.height(),
            TYPE_APPLICATION_PANEL,
            FLAG_NOT_FOCUSABLE or FLAG_NOT_TOUCH_MODAL,
            PixelFormat.TRANSLUCENT)
        lp.title = "Open by default settings dialog of task=" + taskInfo.taskId
        lp.setTrustedOverlay()
        val windowManager = WindowlessWindowManager(
            taskInfo.configuration,
            dialogSurfaceControl, null /* hostInputToken */
        )
        viewHost = SurfaceControlViewHost(context, display, windowManager, "Dialog").apply {
            setView(dialog, lp)
            rootSurfaceControl.applyTransactionOnDraw(t)
            PixelFormat.TRANSLUCENT
        ).apply {
            token = Binder()
            title = "Open by default settings dialog of task=${taskInfo.taskId}"
            setTrustedOverlay()
        }
        dialogContainer = AdditionalViewHostViewContainer(
            dialogSurfaceControl, viewHost, surfaceControlTransactionSupplier)

        dialog.setDismissOnClickListener{
            closeMenu()
        dialogWindowManager = DialogWindowManager(taskInfo.configuration)
        viewHost = SurfaceControlViewHost(context, display, dialogWindowManager, "Dialog").apply {
            setView(dialog, lp)
        }

        dialog.setConfirmButtonClickListener {
            setDefaultLinkHandlingSetting()
            closeMenu()
        animationController.startEnterAnimation(dialog, this::onAnimationEnded)
    }

        listener.onDialogCreated()
    private fun onAnimationEnded() {
        dialog.post {
            dialog.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED)
            val subHeader: TextView = dialog.requireViewById(R.id.dialog_subheader)
            subHeader.requestFocus()
            subHeader.requestAccessibilityFocus()
        }
    }

    private fun initializeRadioButtons() {
@@ -163,22 +169,22 @@ internal class OpenByDefaultDialog(
        } catch (e: NameNotFoundException) {
            Slog.e(
                TAG,
                "Failed to change link handling policy due to the package name is not found: " + e
                "Failed to change link handling policy due to the package name is not found: $e"
            )
        }
    }

    private fun closeMenu() {
        loadAppInfoJob?.cancel()
        dialogContainer?.releaseView()
        dialogContainer = null
        animationController.startExitAnimation(dialog) {
            // Release the host and manager after the exit animation
            viewHost.release()
            dialogWindowManager.release()
            listener.onDialogDismissed()
        }
    }

     private fun bindAppInfo(
        appIconBitmap: Bitmap,
        appName: CharSequence
    ) {
     private fun bindAppInfo(appIconBitmap: Bitmap, appName: CharSequence) {
        appIconView.setImageBitmap(appIconBitmap)
        appNameView.text = appName
    }
@@ -186,16 +192,58 @@ internal class OpenByDefaultDialog(
    /**
     * Relayout the dialog to the new task bounds.
     */
    fun relayout(
        taskInfo: RunningTaskInfo,
    ) {
        val t = surfaceControlTransactionSupplier.get()
    fun relayout(taskInfo: RunningTaskInfo) {
        val taskBounds = taskInfo.configuration.windowConfiguration.bounds
        t.setWindowCrop(dialogSurfaceControl, taskBounds.width(), taskBounds.height())
        viewHost.rootSurfaceControl.applyTransactionOnDraw(t)
        dialogWindowManager.relayout(taskBounds)
        viewHost.relayout(taskBounds.width(), taskBounds.height())
    }

    /**
     * Handles showing, positioning and tearing down the dialog surface
     */
    private inner class DialogWindowManager(config: Configuration) :
        WindowlessWindowManager(config, null, null) {

        private var leash: SurfaceControl? = null

        override fun getParentSurface(
            window: IWindow,
            attrs: LayoutParams
        ): SurfaceControl {
            val builder = SurfaceControl.Builder()
                .setContainerLayer()
                .setName("OpenByDefaultDialogLeash")
                .setParent(taskSurface)
                .setCallsite("OpenByDefaultDialog.getParentSurface")

            val newLeash = builder.build()
            leash = newLeash

            val t = surfaceControlTransactionSupplier.get()
            val taskBounds = taskInfo.configuration.windowConfiguration.bounds
            t.setPosition(newLeash, 0f, 0f)
                .setWindowCrop(newLeash, taskBounds.width(), taskBounds.height())
                .setLayer(newLeash, TaskConstants.TASK_CHILD_LAYER_SETTINGS_DIALOG)
                .show(newLeash)
                .apply()

            return newLeash
        }

        fun relayout(taskBounds: Rect) {
            leash?.let {
                surfaceControlTransactionSupplier.get()
                    .setWindowCrop(it, taskBounds.width(), taskBounds.height())
                    .apply()
            }
        }

        fun release() {
            leash?.let { surfaceControlTransactionSupplier.get().remove(it).apply() }
            leash = null
        }
    }

    /**
     * Defines interface for classes that can listen to lifecycle events of open by default settings
     * dialog.
+7 −1
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@ import android.view.View
import android.widget.Button
import androidx.constraintlayout.widget.ConstraintLayout
import com.android.wm.shell.R
import com.android.wm.shell.compatui.DialogContainerSupplier

/** View for open by default settings dialog for an application which allows the user to change
 * where links will open by default, in the default browser or in the application. */
@@ -30,7 +31,7 @@ class OpenByDefaultDialogView @JvmOverloads constructor(
    attrs: AttributeSet? = null,
    defStyleAttr: Int = 0,
    defStyleRes: Int = 0
) : ConstraintLayout(context, attrs, defStyleAttr, defStyleRes) {
) : ConstraintLayout(context, attrs, defStyleAttr, defStyleRes), DialogContainerSupplier {

    private lateinit var dialogContainer: View
    private lateinit var backgroundDim: Drawable
@@ -50,8 +51,13 @@ class OpenByDefaultDialogView @JvmOverloads constructor(
        dismissButton.setOnClickListener(callback)
    }

    override fun getDialogContainerView(): View = dialogContainer

    override fun getBackgroundDimDrawable(): Drawable = backgroundDim

    override fun onFinishInflate() {
        super.onFinishInflate()
        accessibilityPaneTitle = context.getString(R.string.open_by_default_settings_text)
        dialogContainer = requireViewById(R.id.open_by_default_dialog_container)
        backgroundDim = background.mutate()
        backgroundDim.alpha = 128
+2 −0
Original line number Diff line number Diff line
@@ -890,6 +890,7 @@ class DefaultWindowDecoration @JvmOverloads constructor(
                taskResourceLoader,
                splitScreenController,
                desktopUserRepositories,
                transitions,
                taskSurface,
                checkNotNull(decorationContainerSurface) {
                    "Expected non-null decoration container surface"
@@ -922,6 +923,7 @@ class DefaultWindowDecoration @JvmOverloads constructor(
                windowDecorViewHostSupplier,
                context,
                userContext,
                transitions,
                displayController,
                taskResourceLoader,
                splitScreenController,
+1 −0
Original line number Diff line number Diff line
@@ -1281,6 +1281,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
        mOpenByDefaultDialog = new OpenByDefaultDialog(
                mContext,
                mUserContext,
                mTransitions,
                mTaskInfo,
                mTaskSurface,
                mDisplayController,
Loading