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

Commit 1feecae4 authored by Fabian Kozynski's avatar Fabian Kozynski
Browse files

Added core startable

This will make sure that panels of preferred packages are shown by
default if there are no selection of provider and that panels will be bound on
start.

Test: manual, remove provider selected and observe.
Test: atest ControlsStartableTest
Fixes: 266703958
Change-Id: I04b8e442eb6336b4f7095055cb82a88f6f7530bc
parent 7e9ff1ef
Loading
Loading
Loading
Loading
+3 −2
Original line number Diff line number Diff line
@@ -28,12 +28,13 @@ import android.content.pm.ResolveInfo
import android.content.pm.ServiceInfo
import android.os.UserHandle
import android.service.controls.ControlsProviderService
import androidx.annotation.VisibleForTesting
import androidx.annotation.WorkerThread
import com.android.settingslib.applications.DefaultAppInfo
import com.android.systemui.R
import java.util.Objects

class ControlsServiceInfo(
open class ControlsServiceInfo(
    private val context: Context,
    val serviceInfo: ServiceInfo
) : DefaultAppInfo(
@@ -64,7 +65,7 @@ class ControlsServiceInfo(
     * [R.array.config_controlsPreferredPackages] can declare activities for use as a panel.
     */
    var panelActivity: ComponentName? = null
        private set
        protected set

    private var resolved: Boolean = false

+6 −11
Original line number Diff line number Diff line
@@ -121,16 +121,13 @@ class ControlsControllerImpl @Inject constructor (
        userChanging = false
    }

    private val userTrackerCallback = object : UserTracker.Callback {
        override fun onUserChanged(newUser: Int, userContext: Context) {
    override fun changeUser(newUser: UserHandle) {
        userChanging = true
            val newUserHandle = UserHandle.of(newUser)
            if (currentUser == newUserHandle) {
        if (currentUser == newUser) {
            userChanging = false
            return
        }
            setValuesForUser(newUserHandle)
        }
        setValuesForUser(newUser)
    }

    @VisibleForTesting
@@ -231,7 +228,6 @@ class ControlsControllerImpl @Inject constructor (
        dumpManager.registerDumpable(javaClass.name, this)
        resetFavorites()
        userChanging = false
        userTracker.addCallback(userTrackerCallback, executor)
        context.registerReceiver(
            restoreFinishedReceiver,
            IntentFilter(BackupHelper.ACTION_RESTORE_FINISHED),
@@ -243,7 +239,6 @@ class ControlsControllerImpl @Inject constructor (
    }

    fun destroy() {
        userTracker.removeCallback(userTrackerCallback)
        context.unregisterReceiver(restoreFinishedReceiver)
        listingController.removeCallback(listingCallback)
    }
+33 −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.controls.dagger

import com.android.systemui.CoreStartable
import com.android.systemui.controls.start.ControlsStartable
import dagger.Binds
import dagger.Module
import dagger.multibindings.ClassKey
import dagger.multibindings.IntoMap

@Module
abstract class StartControlsStartableModule {
    @Binds
    @IntoMap
    @ClassKey(ControlsStartable::class)
    abstract fun bindFeature(impl: ControlsStartable): CoreStartable
}
+119 −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.controls.start

import android.content.Context
import android.content.res.Resources
import android.os.UserHandle
import com.android.systemui.CoreStartable
import com.android.systemui.R
import com.android.systemui.controls.controller.ControlsController
import com.android.systemui.controls.dagger.ControlsComponent
import com.android.systemui.controls.management.ControlsListingController
import com.android.systemui.controls.ui.SelectedItem
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.settings.UserTracker
import java.util.concurrent.Executor
import javax.inject.Inject

/**
 * Started with SystemUI to perform early operations for device controls subsystem (only if enabled)
 *
 * In particular, it will perform the following:
 * * If there is no preferred selection for provider and at least one of the preferred packages 
 * provides a panel, it will select the first one that does.
 * * If the preferred selection provides a panel, it will bind to that service (to reduce latency on
 * displaying the panel).
 *
 * It will also perform those operations on user change.
 */
@SysUISingleton
class ControlsStartable
@Inject
constructor(
    @Main private val resources: Resources,
    @Background private val executor: Executor,
    private val controlsComponent: ControlsComponent,
    private val userTracker: UserTracker
) : CoreStartable {

    // These two controllers can only be accessed after `start` method once we've checked if the
    // feature is enabled
    private val controlsController: ControlsController
        get() = controlsComponent.getControlsController().get()

    private val controlsListingController: ControlsListingController
        get() = controlsComponent.getControlsListingController().get()

    private val userTrackerCallback =
        object : UserTracker.Callback {
            override fun onUserChanged(newUser: Int, userContext: Context) {
                controlsController.changeUser(UserHandle.of(newUser))
                startForUser()
            }
        }

    override fun start() {
        if (!controlsComponent.isEnabled()) {
            // Controls is disabled, we don't need this anymore
            return
        }
        startForUser()
        userTracker.addCallback(userTrackerCallback, executor)
    }

    private fun startForUser() {
        selectDefaultPanelIfNecessary()
        bindToPanel()
    }

    private fun selectDefaultPanelIfNecessary() {
        val currentSelection = controlsController.getPreferredSelection()
        if (currentSelection == SelectedItem.EMPTY_SELECTION) {
            val availableServices = controlsListingController.getCurrentServices()
            val panels = availableServices.filter { it.panelActivity != null }
            resources
                .getStringArray(R.array.config_controlsPreferredPackages)
                // Looking for the first element in the string array such that there is one package
                // that has a panel. It will return null if there are no packages in the array,
                // or if no packages in the array have a panel associated with it.
                .firstNotNullOfOrNull { name ->
                    panels.firstOrNull { it.componentName.packageName == name }
                }
                ?.let { info ->
                    controlsController.setPreferredSelection(
                        SelectedItem.PanelItem(info.loadLabel(), info.componentName)
                    )
                }
        }
    }

    private fun bindToPanel() {
        val currentSelection = controlsController.getPreferredSelection()
        val panels =
            controlsListingController.getCurrentServices().filter { it.panelActivity != null }
        if (
            currentSelection is SelectedItem.PanelItem &&
                panels.firstOrNull { it.componentName == currentSelection.componentName } != null
        ) {
            controlsController.bindComponentForPanel(currentSelection.componentName)
        }
    }
}
+5 −1
Original line number Diff line number Diff line
@@ -26,6 +26,7 @@ import com.android.systemui.accessibility.SystemActions
import com.android.systemui.accessibility.WindowMagnification
import com.android.systemui.biometrics.AuthController
import com.android.systemui.clipboardoverlay.ClipboardListener
import com.android.systemui.controls.dagger.StartControlsStartableModule
import com.android.systemui.dagger.qualifiers.PerUser
import com.android.systemui.dreams.DreamMonitor
import com.android.systemui.globalactions.GlobalActionsComponent
@@ -64,7 +65,10 @@ import dagger.multibindings.IntoMap
/**
 * Collection of {@link CoreStartable}s that should be run on AOSP.
 */
@Module(includes = [MultiUserUtilsModule::class])
@Module(includes = [
    MultiUserUtilsModule::class,
    StartControlsStartableModule::class
])
abstract class SystemUICoreStartableModule {
    /** Inject into AuthController.  */
    @Binds
Loading