summaryrefslogtreecommitdiff
path: root/packages/SystemUI/src/com/android/systemui/controls/start/ControlsStartable.kt
blob: 0218f452a13b1437048feaf7e1a8ab7726e7a25a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
/*
 * 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.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.os.UserHandle
import android.os.UserManager
import androidx.annotation.WorkerThread
import com.android.systemui.CoreStartable
import com.android.systemui.broadcast.BroadcastDispatcher
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.panels.AuthorizedPanelsRepository
import com.android.systemui.controls.panels.SelectedComponentRepository
import com.android.systemui.controls.ui.SelectedItem
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
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(
        @Background private val executor: Executor,
        private val controlsComponent: ControlsComponent,
        private val userTracker: UserTracker,
        private val authorizedPanelsRepository: AuthorizedPanelsRepository,
        private val selectedComponentRepository: SelectedComponentRepository,
        private val userManager: UserManager,
        private val broadcastDispatcher: BroadcastDispatcher,
) : 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() {}

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

    @WorkerThread
    private fun startForUser() {
        controlsListingController.forceReload()
        selectDefaultPanelIfNecessary()
        bindToPanel()
    }

    private fun selectDefaultPanelIfNecessary() {
        if (!selectedComponentRepository.shouldAddDefaultComponent()) {
            return
        }
        val currentSelection = controlsController.getPreferredSelection()
        if (currentSelection == SelectedItem.EMPTY_SELECTION) {
            val availableServices = controlsListingController.getCurrentServices()
            val panels = availableServices.filter { it.panelActivity != null }
            authorizedPanelsRepository
                .getPreferredPackages()
                // 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() {
        if (userManager.isUserUnlocked(userTracker.userId)) {
            bindToPanelInternal()
        } else {
            broadcastDispatcher.registerReceiver(
                    receiver = object : BroadcastReceiver() {
                        override fun onReceive(context: Context?, intent: Intent?) {
                            if (userManager.isUserUnlocked(userTracker.userId)) {
                                bindToPanelInternal()
                                broadcastDispatcher.unregisterReceiver(this)
                            }
                        }
                    },
                    filter = IntentFilter(Intent.ACTION_USER_UNLOCKED),
                    executor = executor,
                    user = userTracker.userHandle,
            )
        }
    }

    private fun bindToPanelInternal() {
        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)
        }
    }
}