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

Commit eae74138 authored by Fabian Kozynski's avatar Fabian Kozynski
Browse files

Add new add flow for panels

This adds a flow for adding panels that will query consent from the
user. Note that this needs Flags.APP_PANELS_ALL_APPS_ALLOWED for those
panels to show in the first place.

Test: manual, trigger dialog
Test: atest com.android.systemui.controls
Bug: 265180342

Change-Id: I5a0c382f98595daffbd7e4dfe1e52adb43b2f1c5
parent 694d2d57
Loading
Loading
Loading
Loading
+10 −0
Original line number Diff line number Diff line
@@ -2238,6 +2238,14 @@
    <!-- Removed control in management screen [CHAR LIMIT=20] -->
    <string name="controls_removed">Removed</string>

    <!-- Title for the dialog presented to the user to authorize this app to display a Device
         controls panel (embedded activity) instead of controls rendered by SystemUI [CHAR LIMIT=30] -->
    <string name="controls_panel_authorization_title">Add <xliff:g id="appName" example="My app">%s</xliff:g>?</string>

    <!-- Shows in a dialog presented to the user to authorize this app to display a Device controls
         panel (embedded activity) instead of controls rendered by SystemUI [CHAR LIMIT=NONE] -->
    <string name="controls_panel_authorization">When you add <xliff:g id="appName" example="My app">%s</xliff:g>, it can add controls and content to this panel. In some apps, you can choose which controls show up here.</string>

    <!-- a11y state description for a control that is currently favorited [CHAR LIMIT=NONE] -->
    <string name="accessibility_control_favorite">Favorited</string>
    <!-- a11y state description for a control that is currently favorited with its position [CHAR LIMIT=NONE] -->
@@ -2387,6 +2395,8 @@
    <string name="controls_menu_add">Add controls</string>
    <!-- Controls menu, edit [CHAR_LIMIT=30] -->
    <string name="controls_menu_edit">Edit controls</string>
    <!-- Controls menu, add another app [CHAR LIMIT=30] -->
    <string name="controls_menu_add_another_app">Add app</string>

    <!-- Title for the media output dialog with media related devices [CHAR LIMIT=50] -->
    <string name="media_output_dialog_add_output">Add outputs</string>
+2 −0
Original line number Diff line number Diff line
@@ -188,6 +188,8 @@ interface ControlsController : UserAwareController {
    /** See [ControlsUiController.getPreferredSelectedItem]. */
    fun getPreferredSelection(): SelectedItem

    fun setPreferredSelection(selectedItem: SelectedItem)

    /**
     * Bind to a service that provides a Device Controls panel (embedded activity). This will allow
     * the app to remain "warm", and reduce latency.
+12 −0
Original line number Diff line number Diff line
@@ -36,6 +36,7 @@ import com.android.systemui.backup.BackupHelper
import com.android.systemui.controls.ControlStatus
import com.android.systemui.controls.ControlsServiceInfo
import com.android.systemui.controls.management.ControlsListingController
import com.android.systemui.controls.panels.AuthorizedPanelsRepository
import com.android.systemui.controls.ui.ControlsUiController
import com.android.systemui.controls.ui.SelectedItem
import com.android.systemui.dagger.SysUISingleton
@@ -61,6 +62,7 @@ class ControlsControllerImpl @Inject constructor (
    private val listingController: ControlsListingController,
    private val userFileManager: UserFileManager,
    private val userTracker: UserTracker,
    private val authorizedPanelsRepository: AuthorizedPanelsRepository,
    optionalWrapper: Optional<ControlsFavoritePersistenceWrapper>,
    dumpManager: DumpManager,
) : Dumpable, ControlsController {
@@ -249,6 +251,11 @@ class ControlsControllerImpl @Inject constructor (
    private fun resetFavorites() {
        Favorites.clear()
        Favorites.load(persistenceWrapper.readFavorites())
        // After loading favorites, add the package names of any apps with favorites to the list
        // of authorized panels. That way, if the user has previously favorited controls for an app,
        // that panel will be authorized.
        authorizedPanelsRepository.addAuthorizedPanels(
                Favorites.getAllStructures().map { it.componentName.packageName }.toSet())
    }

    private fun confirmAvailability(): Boolean {
@@ -489,6 +496,7 @@ class ControlsControllerImpl @Inject constructor (
        if (!confirmAvailability()) return
        executor.execute {
            if (Favorites.addFavorite(componentName, structureName, controlInfo)) {
                authorizedPanelsRepository.addAuthorizedPanels(setOf(componentName.packageName))
                persistenceWrapper.storeFavorites(Favorites.getAllStructures())
            }
        }
@@ -555,6 +563,10 @@ class ControlsControllerImpl @Inject constructor (
        return uiController.getPreferredSelectedItem(getFavorites())
    }

    override fun setPreferredSelection(selectedItem: SelectedItem) {
        uiController.updatePreferences(selectedItem)
    }

    override fun dump(pw: PrintWriter, args: Array<out String>) {
        pw.println("ControlsController state:")
        pw.println("  Changing users: $userChanging")
+22 −13
Original line number Diff line number Diff line
@@ -50,9 +50,10 @@ class AppAdapter(
        lifecycle: Lifecycle,
        controlsListingController: ControlsListingController,
        private val layoutInflater: LayoutInflater,
    private val onAppSelected: (ComponentName?) -> Unit = {},
        private val onAppSelected: (ControlsServiceInfo) -> Unit = {},
        private val favoritesRenderer: FavoritesRenderer,
    private val resources: Resources
        private val resources: Resources,
        private val authorizedPanels: Set<String> = emptySet(),
) : RecyclerView.Adapter<AppAdapter.Holder>() {

    private var listOfServices = emptyList<ControlsServiceInfo>()
@@ -64,8 +65,10 @@ class AppAdapter(
                val localeComparator = compareBy<ControlsServiceInfo, CharSequence>(collator) {
                    it.loadLabel() ?: ""
                }
                listOfServices = serviceInfos.filter { it.panelActivity == null }
                        .sortedWith(localeComparator)
                // No panel or the panel is not authorized
                listOfServices = serviceInfos.filter {
                    it.panelActivity == null || it.panelActivity?.packageName !in authorizedPanels
                }.sortedWith(localeComparator)
                uiExecutor.execute(::notifyDataSetChanged)
            }
        }
@@ -86,8 +89,8 @@ class AppAdapter(

    override fun onBindViewHolder(holder: Holder, index: Int) {
        holder.bindData(listOfServices[index])
        holder.itemView.setOnClickListener {
            onAppSelected(ComponentName.unflattenFromString(listOfServices[index].key))
        holder.view.setOnClickListener {
            onAppSelected(listOfServices[index])
        }
    }

@@ -95,6 +98,8 @@ class AppAdapter(
     * Holder for binding views in the [RecyclerView]-
     */
    class Holder(view: View, val favRenderer: FavoritesRenderer) : RecyclerView.ViewHolder(view) {
        val view: View = itemView

        private val icon: ImageView = itemView.requireViewById(com.android.internal.R.id.icon)
        private val title: TextView = itemView.requireViewById(com.android.internal.R.id.title)
        private val favorites: TextView = itemView.requireViewById(R.id.favorites)
@@ -106,7 +111,11 @@ class AppAdapter(
        fun bindData(data: ControlsServiceInfo) {
            icon.setImageDrawable(data.loadIcon())
            title.text = data.loadLabel()
            val text = favRenderer.renderFavoritesForComponent(data.componentName)
            val text = if (data.panelActivity == null) {
                favRenderer.renderFavoritesForComponent(data.componentName)
            } else {
                null
            }
            favorites.text = text
            favorites.visibility = if (text == null) View.GONE else View.VISIBLE
        }
+44 −6
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
package com.android.systemui.controls.management

import android.app.ActivityOptions
import android.app.Dialog
import android.content.ComponentName
import android.content.Context
import android.content.Intent
@@ -31,12 +32,15 @@ import android.widget.TextView
import android.window.OnBackInvokedCallback
import android.window.OnBackInvokedDispatcher
import androidx.activity.ComponentActivity
import androidx.annotation.VisibleForTesting
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.android.systemui.R
import com.android.systemui.controls.ControlsServiceInfo
import com.android.systemui.controls.controller.ControlsController
import com.android.systemui.controls.panels.AuthorizedPanelsRepository
import com.android.systemui.controls.ui.ControlsActivity
import com.android.systemui.controls.ui.ControlsUiController
import com.android.systemui.controls.ui.SelectedItem
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.settings.UserTracker
@@ -52,7 +56,8 @@ open class ControlsProviderSelectorActivity @Inject constructor(
    private val listingController: ControlsListingController,
    private val controlsController: ControlsController,
    private val userTracker: UserTracker,
    private val uiController: ControlsUiController
    private val authorizedPanelsRepository: AuthorizedPanelsRepository,
    private val panelConfirmationDialogFactory: PanelConfirmationDialogFactory
) : ComponentActivity() {

    companion object {
@@ -72,6 +77,7 @@ open class ControlsProviderSelectorActivity @Inject constructor(
            }
        }
    }
    private var dialog: Dialog? = null

    private val mOnBackInvokedCallback = OnBackInvokedCallback {
        if (DEBUG) {
@@ -138,9 +144,11 @@ open class ControlsProviderSelectorActivity @Inject constructor(
                lifecycle,
                listingController,
                LayoutInflater.from(this),
                ::launchFavoritingActivity,
                ::onAppSelected,
                FavoritesRenderer(resources, controlsController::countFavoritesForComponent),
                resources).apply {
                resources,
                authorizedPanelsRepository.getAuthorizedPanels()
        ).apply {
            registerAdapterDataObserver(object : RecyclerView.AdapterDataObserver() {
                var hasAnimated = false
                override fun onChanged() {
@@ -167,13 +175,35 @@ open class ControlsProviderSelectorActivity @Inject constructor(
            Log.d(TAG, "Unregistered onBackInvokedCallback")
        }
        onBackInvokedDispatcher.unregisterOnBackInvokedCallback(mOnBackInvokedCallback)
        dialog?.cancel()
    }

    fun onAppSelected(serviceInfo: ControlsServiceInfo) {
        dialog?.cancel()
        if (serviceInfo.panelActivity == null) {
            launchFavoritingActivity(serviceInfo.componentName)
        } else {
            val appName = serviceInfo.loadLabel() ?: ""
            dialog = panelConfirmationDialogFactory.createConfirmationDialog(this, appName) { ok ->
                if (ok) {
                    authorizedPanelsRepository.addAuthorizedPanels(
                            setOf(serviceInfo.componentName.packageName)
                    )
                    animateExitAndFinish()
                    val selected = SelectedItem.PanelItem(appName, componentName)
                    controlsController.setPreferredSelection(selected)
                    openControlsOrigin()
                }
                dialog = null
            }.also { it.show() }
        }
    }

    /**
     * Launch the [ControlsFavoritingActivity] for the specified component.
     * @param component a component name for a [ControlsProviderService]
     */
    fun launchFavoritingActivity(component: ComponentName?) {
    private fun launchFavoritingActivity(component: ComponentName?) {
        executor.execute {
            component?.let {
                val intent = Intent(applicationContext, ControlsFavoritingActivity::class.java)
@@ -194,7 +224,15 @@ open class ControlsProviderSelectorActivity @Inject constructor(
        super.onDestroy()
    }

    private fun animateExitAndFinish() {
    private fun openControlsOrigin() {
        startActivity(
                Intent(applicationContext, ControlsActivity::class.java),
                ActivityOptions.makeSceneTransitionAnimation(this).toBundle()
        )
    }

    @VisibleForTesting
    internal open fun animateExitAndFinish() {
        val rootView = requireViewById<ViewGroup>(R.id.controls_management_root)
        ControlsAnimations.exitAnimation(
                rootView,
Loading