Moves client and contract into customization lib.
We want to share this code between System UI and ThemePicker. Fix: 259728815 Test: System UI and WPPG both build and work end-to-end with customizable lock screen quick affordances. Change-Id: If2fe99c5d679da57e00c6fcece10a1d2fc9ca807
This commit is contained in:
@@ -0,0 +1,190 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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.shared.quickaffordance.data.content
|
||||
|
||||
import android.graphics.drawable.BitmapDrawable
|
||||
import android.graphics.drawable.Drawable
|
||||
import com.android.systemui.shared.keyguard.shared.model.KeyguardQuickAffordanceSlots
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.flow.combine
|
||||
|
||||
class FakeKeyguardQuickAffordanceProviderClient(
|
||||
slots: List<KeyguardQuickAffordanceProviderClient.Slot> =
|
||||
listOf(
|
||||
KeyguardQuickAffordanceProviderClient.Slot(
|
||||
id = KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START,
|
||||
capacity = 1,
|
||||
),
|
||||
KeyguardQuickAffordanceProviderClient.Slot(
|
||||
id = KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_END,
|
||||
capacity = 1,
|
||||
),
|
||||
),
|
||||
affordances: List<KeyguardQuickAffordanceProviderClient.Affordance> =
|
||||
listOf(
|
||||
KeyguardQuickAffordanceProviderClient.Affordance(
|
||||
id = AFFORDANCE_1,
|
||||
name = AFFORDANCE_1,
|
||||
iconResourceId = 0,
|
||||
),
|
||||
KeyguardQuickAffordanceProviderClient.Affordance(
|
||||
id = AFFORDANCE_2,
|
||||
name = AFFORDANCE_2,
|
||||
iconResourceId = 0,
|
||||
),
|
||||
KeyguardQuickAffordanceProviderClient.Affordance(
|
||||
id = AFFORDANCE_3,
|
||||
name = AFFORDANCE_3,
|
||||
iconResourceId = 0,
|
||||
),
|
||||
),
|
||||
flags: List<KeyguardQuickAffordanceProviderClient.Flag> =
|
||||
listOf(
|
||||
KeyguardQuickAffordanceProviderClient.Flag(
|
||||
name = KeyguardQuickAffordanceProviderContract.FlagsTable.FLAG_NAME_FEATURE_ENABLED,
|
||||
value = true,
|
||||
)
|
||||
),
|
||||
) : KeyguardQuickAffordanceProviderClient {
|
||||
|
||||
private val slots = MutableStateFlow(slots)
|
||||
private val affordances = MutableStateFlow(affordances)
|
||||
private val flags = MutableStateFlow(flags)
|
||||
|
||||
private val selections = MutableStateFlow<Map<String, List<String>>>(emptyMap())
|
||||
|
||||
override suspend fun insertSelection(slotId: String, affordanceId: String) {
|
||||
val slotCapacity =
|
||||
querySlots().find { it.id == slotId }?.capacity
|
||||
?: error("Slot with ID \"$slotId\" not found!")
|
||||
val affordances = selections.value.getOrDefault(slotId, mutableListOf()).toMutableList()
|
||||
while (affordances.size + 1 > slotCapacity) {
|
||||
affordances.removeAt(0)
|
||||
}
|
||||
affordances.remove(affordanceId)
|
||||
affordances.add(affordanceId)
|
||||
selections.value = selections.value.toMutableMap().apply { this[slotId] = affordances }
|
||||
}
|
||||
|
||||
override suspend fun querySlots(): List<KeyguardQuickAffordanceProviderClient.Slot> {
|
||||
return slots.value
|
||||
}
|
||||
|
||||
override suspend fun queryFlags(): List<KeyguardQuickAffordanceProviderClient.Flag> {
|
||||
return flags.value
|
||||
}
|
||||
|
||||
override fun observeSlots(): Flow<List<KeyguardQuickAffordanceProviderClient.Slot>> {
|
||||
return slots.asStateFlow()
|
||||
}
|
||||
|
||||
override fun observeFlags(): Flow<List<KeyguardQuickAffordanceProviderClient.Flag>> {
|
||||
return flags.asStateFlow()
|
||||
}
|
||||
|
||||
override suspend fun queryAffordances():
|
||||
List<KeyguardQuickAffordanceProviderClient.Affordance> {
|
||||
return affordances.value
|
||||
}
|
||||
|
||||
override fun observeAffordances():
|
||||
Flow<List<KeyguardQuickAffordanceProviderClient.Affordance>> {
|
||||
return affordances.asStateFlow()
|
||||
}
|
||||
|
||||
override suspend fun querySelections(): List<KeyguardQuickAffordanceProviderClient.Selection> {
|
||||
return toSelectionList(selections.value, affordances.value)
|
||||
}
|
||||
|
||||
override fun observeSelections(): Flow<List<KeyguardQuickAffordanceProviderClient.Selection>> {
|
||||
return combine(selections, affordances) { selections, affordances ->
|
||||
toSelectionList(selections, affordances)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun deleteSelection(slotId: String, affordanceId: String) {
|
||||
val affordances = selections.value.getOrDefault(slotId, mutableListOf()).toMutableList()
|
||||
affordances.remove(affordanceId)
|
||||
|
||||
selections.value = selections.value.toMutableMap().apply { this[slotId] = affordances }
|
||||
}
|
||||
|
||||
override suspend fun deleteAllSelections(slotId: String) {
|
||||
selections.value = selections.value.toMutableMap().apply { this[slotId] = emptyList() }
|
||||
}
|
||||
|
||||
override suspend fun getAffordanceIcon(iconResourceId: Int, tintColor: Int): Drawable {
|
||||
return BitmapDrawable()
|
||||
}
|
||||
|
||||
fun setFlag(
|
||||
name: String,
|
||||
value: Boolean,
|
||||
) {
|
||||
flags.value =
|
||||
flags.value.toMutableList().apply {
|
||||
removeIf { it.name == name }
|
||||
add(KeyguardQuickAffordanceProviderClient.Flag(name = name, value = value))
|
||||
}
|
||||
}
|
||||
|
||||
fun setSlotCapacity(slotId: String, capacity: Int) {
|
||||
slots.value =
|
||||
slots.value.toMutableList().apply {
|
||||
val index = indexOfFirst { it.id == slotId }
|
||||
check(index != -1) { "Slot with ID \"$slotId\" doesn't exist!" }
|
||||
set(
|
||||
index,
|
||||
KeyguardQuickAffordanceProviderClient.Slot(id = slotId, capacity = capacity)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun addAffordance(affordance: KeyguardQuickAffordanceProviderClient.Affordance): Int {
|
||||
affordances.value = affordances.value + listOf(affordance)
|
||||
return affordances.value.size - 1
|
||||
}
|
||||
|
||||
private fun toSelectionList(
|
||||
selections: Map<String, List<String>>,
|
||||
affordances: List<KeyguardQuickAffordanceProviderClient.Affordance>,
|
||||
): List<KeyguardQuickAffordanceProviderClient.Selection> {
|
||||
return selections
|
||||
.map { (slotId, affordanceIds) ->
|
||||
affordanceIds.map { affordanceId ->
|
||||
val affordanceName =
|
||||
affordances.find { it.id == affordanceId }?.name
|
||||
?: error("No affordance with ID of \"$affordanceId\"!")
|
||||
KeyguardQuickAffordanceProviderClient.Selection(
|
||||
slotId = slotId,
|
||||
affordanceId = affordanceId,
|
||||
affordanceName = affordanceName,
|
||||
)
|
||||
}
|
||||
}
|
||||
.flatten()
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val AFFORDANCE_1 = "affordance_1"
|
||||
const val AFFORDANCE_2 = "affordance_2"
|
||||
const val AFFORDANCE_3 = "affordance_3"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,479 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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.shared.quickaffordance.data.content
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.ContentValues
|
||||
import android.content.Context
|
||||
import android.database.ContentObserver
|
||||
import android.graphics.Color
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.net.Uri
|
||||
import androidx.annotation.DrawableRes
|
||||
import com.android.systemui.shared.quickaffordance.data.content.KeyguardQuickAffordanceProviderContract as Contract
|
||||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
import kotlinx.coroutines.channels.awaitClose
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.callbackFlow
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.onStart
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
/** Client for using a content provider implementing the [Contract]. */
|
||||
interface KeyguardQuickAffordanceProviderClient {
|
||||
|
||||
/**
|
||||
* Selects an affordance with the given ID for a slot on the lock screen with the given ID.
|
||||
*
|
||||
* Note that the maximum number of selected affordances on this slot is automatically enforced.
|
||||
* Selecting a slot that is already full (e.g. already has a number of selected affordances at
|
||||
* its maximum capacity) will automatically remove the oldest selected affordance before adding
|
||||
* the one passed in this call. Additionally, selecting an affordance that's already one of the
|
||||
* selected affordances on the slot will move the selected affordance to the newest location in
|
||||
* the slot.
|
||||
*/
|
||||
suspend fun insertSelection(
|
||||
slotId: String,
|
||||
affordanceId: String,
|
||||
)
|
||||
|
||||
/** Returns all available slots supported by the device. */
|
||||
suspend fun querySlots(): List<Slot>
|
||||
|
||||
/** Returns the list of flags. */
|
||||
suspend fun queryFlags(): List<Flag>
|
||||
|
||||
/**
|
||||
* Returns [Flow] for observing the collection of slots.
|
||||
*
|
||||
* @see [querySlots]
|
||||
*/
|
||||
fun observeSlots(): Flow<List<Slot>>
|
||||
|
||||
/**
|
||||
* Returns [Flow] for observing the collection of flags.
|
||||
*
|
||||
* @see [queryFlags]
|
||||
*/
|
||||
fun observeFlags(): Flow<List<Flag>>
|
||||
|
||||
/**
|
||||
* Returns all available affordances supported by the device, regardless of current slot
|
||||
* placement.
|
||||
*/
|
||||
suspend fun queryAffordances(): List<Affordance>
|
||||
|
||||
/**
|
||||
* Returns [Flow] for observing the collection of affordances.
|
||||
*
|
||||
* @see [queryAffordances]
|
||||
*/
|
||||
fun observeAffordances(): Flow<List<Affordance>>
|
||||
|
||||
/** Returns the current slot-affordance selections. */
|
||||
suspend fun querySelections(): List<Selection>
|
||||
|
||||
/**
|
||||
* Returns [Flow] for observing the collection of selections.
|
||||
*
|
||||
* @see [querySelections]
|
||||
*/
|
||||
fun observeSelections(): Flow<List<Selection>>
|
||||
|
||||
/** Unselects an affordance with the given ID from the slot with the given ID. */
|
||||
suspend fun deleteSelection(
|
||||
slotId: String,
|
||||
affordanceId: String,
|
||||
)
|
||||
|
||||
/** Unselects all affordances from the slot with the given ID. */
|
||||
suspend fun deleteAllSelections(
|
||||
slotId: String,
|
||||
)
|
||||
|
||||
/** Returns a [Drawable] with the given ID, loaded from the system UI package. */
|
||||
suspend fun getAffordanceIcon(
|
||||
@DrawableRes iconResourceId: Int,
|
||||
tintColor: Int = Color.WHITE,
|
||||
): Drawable
|
||||
|
||||
/** Models a slot. A position that quick affordances can be positioned in. */
|
||||
data class Slot(
|
||||
/** Unique ID of the slot. */
|
||||
val id: String,
|
||||
/**
|
||||
* The maximum number of quick affordances that are allowed to be positioned in this slot.
|
||||
*/
|
||||
val capacity: Int,
|
||||
)
|
||||
|
||||
/**
|
||||
* Models a quick affordance. An action that can be selected by the user to appear in one or
|
||||
* more slots on the lock screen.
|
||||
*/
|
||||
data class Affordance(
|
||||
/** Unique ID of the quick affordance. */
|
||||
val id: String,
|
||||
/** User-facing label for this affordance. */
|
||||
val name: String,
|
||||
/**
|
||||
* Resource ID for the user-facing icon for this affordance. This resource is hosted by the
|
||||
* System UI process so it must be used with
|
||||
* `PackageManager.getResourcesForApplication(String)`.
|
||||
*/
|
||||
val iconResourceId: Int,
|
||||
/**
|
||||
* Whether the affordance is enabled. Disabled affordances should be shown on the picker but
|
||||
* should be rendered as "disabled". When tapped, the enablement properties should be used
|
||||
* to populate UI that would explain to the user what to do in order to re-enable this
|
||||
* affordance.
|
||||
*/
|
||||
val isEnabled: Boolean = true,
|
||||
/**
|
||||
* If the affordance is disabled, this is a set of instruction messages to be shown to the
|
||||
* user when the disabled affordance is selected. The instructions should help the user
|
||||
* figure out what to do in order to re-neable this affordance.
|
||||
*/
|
||||
val enablementInstructions: List<String>? = null,
|
||||
/**
|
||||
* If the affordance is disabled, this is a label for a button shown together with the set
|
||||
* of instruction messages when the disabled affordance is selected. The button should help
|
||||
* send the user to a flow that would help them achieve the instructions and re-enable this
|
||||
* affordance.
|
||||
*
|
||||
* If `null`, the button should not be shown.
|
||||
*/
|
||||
val enablementActionText: String? = null,
|
||||
/**
|
||||
* If the affordance is disabled, this is a "component name" of the format
|
||||
* `packageName/action` to be used as an `Intent` for `startActivity` when the action button
|
||||
* (shown together with the set of instruction messages when the disabled affordance is
|
||||
* selected) is clicked by the user. The button should help send the user to a flow that
|
||||
* would help them achieve the instructions and re-enable this affordance.
|
||||
*
|
||||
* If `null`, the button should not be shown.
|
||||
*/
|
||||
val enablementActionComponentName: String? = null,
|
||||
)
|
||||
|
||||
/** Models a selection of a quick affordance on a slot. */
|
||||
data class Selection(
|
||||
/** The unique ID of the slot. */
|
||||
val slotId: String,
|
||||
/** The unique ID of the quick affordance. */
|
||||
val affordanceId: String,
|
||||
/** The user-visible label for the quick affordance. */
|
||||
val affordanceName: String,
|
||||
)
|
||||
|
||||
/** Models a System UI flag. */
|
||||
data class Flag(
|
||||
/** The name of the flag. */
|
||||
val name: String,
|
||||
/** The value of the flag. */
|
||||
val value: Boolean,
|
||||
)
|
||||
}
|
||||
|
||||
class KeyguardQuickAffordanceProviderClientImpl(
|
||||
private val context: Context,
|
||||
private val backgroundDispatcher: CoroutineDispatcher,
|
||||
) : KeyguardQuickAffordanceProviderClient {
|
||||
|
||||
override suspend fun insertSelection(
|
||||
slotId: String,
|
||||
affordanceId: String,
|
||||
) {
|
||||
withContext(backgroundDispatcher) {
|
||||
context.contentResolver.insert(
|
||||
Contract.SelectionTable.URI,
|
||||
ContentValues().apply {
|
||||
put(Contract.SelectionTable.Columns.SLOT_ID, slotId)
|
||||
put(Contract.SelectionTable.Columns.AFFORDANCE_ID, affordanceId)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun querySlots(): List<KeyguardQuickAffordanceProviderClient.Slot> {
|
||||
return withContext(backgroundDispatcher) {
|
||||
context.contentResolver
|
||||
.query(
|
||||
Contract.SlotTable.URI,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
)
|
||||
?.use { cursor ->
|
||||
buildList {
|
||||
val idColumnIndex = cursor.getColumnIndex(Contract.SlotTable.Columns.ID)
|
||||
val capacityColumnIndex =
|
||||
cursor.getColumnIndex(Contract.SlotTable.Columns.CAPACITY)
|
||||
if (idColumnIndex == -1 || capacityColumnIndex == -1) {
|
||||
return@buildList
|
||||
}
|
||||
|
||||
while (cursor.moveToNext()) {
|
||||
add(
|
||||
KeyguardQuickAffordanceProviderClient.Slot(
|
||||
id = cursor.getString(idColumnIndex),
|
||||
capacity = cursor.getInt(capacityColumnIndex),
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
?: emptyList()
|
||||
}
|
||||
|
||||
override suspend fun queryFlags(): List<KeyguardQuickAffordanceProviderClient.Flag> {
|
||||
return withContext(backgroundDispatcher) {
|
||||
context.contentResolver
|
||||
.query(
|
||||
Contract.FlagsTable.URI,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
)
|
||||
?.use { cursor ->
|
||||
buildList {
|
||||
val nameColumnIndex =
|
||||
cursor.getColumnIndex(Contract.FlagsTable.Columns.NAME)
|
||||
val valueColumnIndex =
|
||||
cursor.getColumnIndex(Contract.FlagsTable.Columns.VALUE)
|
||||
if (nameColumnIndex == -1 || valueColumnIndex == -1) {
|
||||
return@buildList
|
||||
}
|
||||
|
||||
while (cursor.moveToNext()) {
|
||||
add(
|
||||
KeyguardQuickAffordanceProviderClient.Flag(
|
||||
name = cursor.getString(nameColumnIndex),
|
||||
value = cursor.getInt(valueColumnIndex) == 1,
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
?: emptyList()
|
||||
}
|
||||
|
||||
override fun observeSlots(): Flow<List<KeyguardQuickAffordanceProviderClient.Slot>> {
|
||||
return observeUri(Contract.SlotTable.URI).map { querySlots() }
|
||||
}
|
||||
|
||||
override fun observeFlags(): Flow<List<KeyguardQuickAffordanceProviderClient.Flag>> {
|
||||
return observeUri(Contract.FlagsTable.URI).map { queryFlags() }
|
||||
}
|
||||
|
||||
override suspend fun queryAffordances():
|
||||
List<KeyguardQuickAffordanceProviderClient.Affordance> {
|
||||
return withContext(backgroundDispatcher) {
|
||||
context.contentResolver
|
||||
.query(
|
||||
Contract.AffordanceTable.URI,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
)
|
||||
?.use { cursor ->
|
||||
buildList {
|
||||
val idColumnIndex =
|
||||
cursor.getColumnIndex(Contract.AffordanceTable.Columns.ID)
|
||||
val nameColumnIndex =
|
||||
cursor.getColumnIndex(Contract.AffordanceTable.Columns.NAME)
|
||||
val iconColumnIndex =
|
||||
cursor.getColumnIndex(Contract.AffordanceTable.Columns.ICON)
|
||||
val isEnabledColumnIndex =
|
||||
cursor.getColumnIndex(Contract.AffordanceTable.Columns.IS_ENABLED)
|
||||
val enablementInstructionsColumnIndex =
|
||||
cursor.getColumnIndex(
|
||||
Contract.AffordanceTable.Columns.ENABLEMENT_INSTRUCTIONS
|
||||
)
|
||||
val enablementActionTextColumnIndex =
|
||||
cursor.getColumnIndex(
|
||||
Contract.AffordanceTable.Columns.ENABLEMENT_ACTION_TEXT
|
||||
)
|
||||
val enablementComponentNameColumnIndex =
|
||||
cursor.getColumnIndex(
|
||||
Contract.AffordanceTable.Columns.ENABLEMENT_COMPONENT_NAME
|
||||
)
|
||||
if (
|
||||
idColumnIndex == -1 ||
|
||||
nameColumnIndex == -1 ||
|
||||
iconColumnIndex == -1 ||
|
||||
isEnabledColumnIndex == -1 ||
|
||||
enablementInstructionsColumnIndex == -1 ||
|
||||
enablementActionTextColumnIndex == -1 ||
|
||||
enablementComponentNameColumnIndex == -1
|
||||
) {
|
||||
return@buildList
|
||||
}
|
||||
|
||||
while (cursor.moveToNext()) {
|
||||
add(
|
||||
KeyguardQuickAffordanceProviderClient.Affordance(
|
||||
id = cursor.getString(idColumnIndex),
|
||||
name = cursor.getString(nameColumnIndex),
|
||||
iconResourceId = cursor.getInt(iconColumnIndex),
|
||||
isEnabled = cursor.getInt(isEnabledColumnIndex) == 1,
|
||||
enablementInstructions =
|
||||
cursor
|
||||
.getString(enablementInstructionsColumnIndex)
|
||||
?.split(
|
||||
Contract.AffordanceTable
|
||||
.ENABLEMENT_INSTRUCTIONS_DELIMITER
|
||||
),
|
||||
enablementActionText =
|
||||
cursor.getString(enablementActionTextColumnIndex),
|
||||
enablementActionComponentName =
|
||||
cursor.getString(enablementComponentNameColumnIndex),
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
?: emptyList()
|
||||
}
|
||||
|
||||
override fun observeAffordances():
|
||||
Flow<List<KeyguardQuickAffordanceProviderClient.Affordance>> {
|
||||
return observeUri(Contract.AffordanceTable.URI).map { queryAffordances() }
|
||||
}
|
||||
|
||||
override suspend fun querySelections(): List<KeyguardQuickAffordanceProviderClient.Selection> {
|
||||
return withContext(backgroundDispatcher) {
|
||||
context.contentResolver
|
||||
.query(
|
||||
Contract.SelectionTable.URI,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
)
|
||||
?.use { cursor ->
|
||||
buildList {
|
||||
val slotIdColumnIndex =
|
||||
cursor.getColumnIndex(Contract.SelectionTable.Columns.SLOT_ID)
|
||||
val affordanceIdColumnIndex =
|
||||
cursor.getColumnIndex(Contract.SelectionTable.Columns.AFFORDANCE_ID)
|
||||
val affordanceNameColumnIndex =
|
||||
cursor.getColumnIndex(Contract.SelectionTable.Columns.AFFORDANCE_NAME)
|
||||
if (
|
||||
slotIdColumnIndex == -1 ||
|
||||
affordanceIdColumnIndex == -1 ||
|
||||
affordanceNameColumnIndex == -1
|
||||
) {
|
||||
return@buildList
|
||||
}
|
||||
|
||||
while (cursor.moveToNext()) {
|
||||
add(
|
||||
KeyguardQuickAffordanceProviderClient.Selection(
|
||||
slotId = cursor.getString(slotIdColumnIndex),
|
||||
affordanceId = cursor.getString(affordanceIdColumnIndex),
|
||||
affordanceName = cursor.getString(affordanceNameColumnIndex),
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
?: emptyList()
|
||||
}
|
||||
|
||||
override fun observeSelections(): Flow<List<KeyguardQuickAffordanceProviderClient.Selection>> {
|
||||
return observeUri(Contract.SelectionTable.URI).map { querySelections() }
|
||||
}
|
||||
|
||||
override suspend fun deleteSelection(
|
||||
slotId: String,
|
||||
affordanceId: String,
|
||||
) {
|
||||
withContext(backgroundDispatcher) {
|
||||
context.contentResolver.delete(
|
||||
Contract.SelectionTable.URI,
|
||||
"${Contract.SelectionTable.Columns.SLOT_ID} = ? AND" +
|
||||
" ${Contract.SelectionTable.Columns.AFFORDANCE_ID} = ?",
|
||||
arrayOf(
|
||||
slotId,
|
||||
affordanceId,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun deleteAllSelections(
|
||||
slotId: String,
|
||||
) {
|
||||
withContext(backgroundDispatcher) {
|
||||
context.contentResolver.delete(
|
||||
Contract.SelectionTable.URI,
|
||||
Contract.SelectionTable.Columns.SLOT_ID,
|
||||
arrayOf(
|
||||
slotId,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("UseCompatLoadingForDrawables")
|
||||
override suspend fun getAffordanceIcon(
|
||||
@DrawableRes iconResourceId: Int,
|
||||
tintColor: Int,
|
||||
): Drawable {
|
||||
return withContext(backgroundDispatcher) {
|
||||
context.packageManager
|
||||
.getResourcesForApplication(SYSTEM_UI_PACKAGE_NAME)
|
||||
.getDrawable(iconResourceId, context.theme)
|
||||
.apply { setTint(tintColor) }
|
||||
}
|
||||
}
|
||||
|
||||
private fun observeUri(
|
||||
uri: Uri,
|
||||
): Flow<Unit> {
|
||||
return callbackFlow {
|
||||
val observer =
|
||||
object : ContentObserver(null) {
|
||||
override fun onChange(selfChange: Boolean) {
|
||||
trySend(Unit)
|
||||
}
|
||||
}
|
||||
|
||||
context.contentResolver.registerContentObserver(
|
||||
uri,
|
||||
/* notifyForDescendants= */ true,
|
||||
observer,
|
||||
)
|
||||
|
||||
awaitClose { context.contentResolver.unregisterContentObserver(observer) }
|
||||
}
|
||||
.onStart { emit(Unit) }
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val SYSTEM_UI_PACKAGE_NAME = "com.android.systemui"
|
||||
}
|
||||
}
|
||||
@@ -15,7 +15,7 @@
|
||||
*
|
||||
*/
|
||||
|
||||
package com.android.systemui.shared.keyguard.data.content
|
||||
package com.android.systemui.shared.quickaffordance.data.content
|
||||
|
||||
import android.content.ContentResolver
|
||||
import android.net.Uri
|
||||
@@ -29,7 +29,7 @@ import android.util.Log
|
||||
import com.android.systemui.SystemUIAppComponentFactoryBase
|
||||
import com.android.systemui.SystemUIAppComponentFactoryBase.ContextAvailableCallback
|
||||
import com.android.systemui.keyguard.domain.interactor.KeyguardQuickAffordanceInteractor
|
||||
import com.android.systemui.shared.keyguard.data.content.KeyguardQuickAffordanceProviderContract as Contract
|
||||
import com.android.systemui.shared.quickaffordance.data.content.KeyguardQuickAffordanceProviderContract as Contract
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.runBlocking
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ import android.content.Intent
|
||||
import com.android.systemui.animation.Expandable
|
||||
import com.android.systemui.common.shared.model.Icon
|
||||
import com.android.systemui.keyguard.shared.quickaffordance.ActivationState
|
||||
import com.android.systemui.shared.keyguard.data.content.KeyguardQuickAffordanceProviderContract as Contract
|
||||
import com.android.systemui.shared.quickaffordance.data.content.KeyguardQuickAffordanceProviderContract as Contract
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
/** Defines interface that can act as data source for a single quick affordance model. */
|
||||
|
||||
@@ -34,8 +34,8 @@ import com.android.systemui.keyguard.shared.model.KeyguardSlotPickerRepresentati
|
||||
import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancePosition
|
||||
import com.android.systemui.plugins.ActivityStarter
|
||||
import com.android.systemui.settings.UserTracker
|
||||
import com.android.systemui.shared.keyguard.data.content.KeyguardQuickAffordanceProviderContract
|
||||
import com.android.systemui.shared.keyguard.shared.model.KeyguardQuickAffordanceSlots
|
||||
import com.android.systemui.shared.quickaffordance.data.content.KeyguardQuickAffordanceProviderContract
|
||||
import com.android.systemui.statusbar.policy.KeyguardStateController
|
||||
import dagger.Lazy
|
||||
import javax.inject.Inject
|
||||
|
||||
@@ -36,8 +36,8 @@ import com.android.systemui.keyguard.domain.interactor.KeyguardQuickAffordanceIn
|
||||
import com.android.systemui.plugins.ActivityStarter
|
||||
import com.android.systemui.settings.UserFileManager
|
||||
import com.android.systemui.settings.UserTracker
|
||||
import com.android.systemui.shared.keyguard.data.content.KeyguardQuickAffordanceProviderContract as Contract
|
||||
import com.android.systemui.shared.keyguard.shared.model.KeyguardQuickAffordanceSlots
|
||||
import com.android.systemui.shared.quickaffordance.data.content.KeyguardQuickAffordanceProviderContract as Contract
|
||||
import com.android.systemui.statusbar.policy.KeyguardStateController
|
||||
import com.android.systemui.util.FakeSharedPreferences
|
||||
import com.android.systemui.util.mockito.mock
|
||||
|
||||
Reference in New Issue
Block a user