Merge "Renames customization content provider (1/7)." into tm-qpr-dev am: bba346acb4 am: e0687a474a

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/20821842

Change-Id: I331a10c0a1802a9dd70a1f5761d4f96accde20e7
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
Ale Nijamkin
2022-12-28 19:17:20 +00:00
committed by Automerger Merge Worker
18 changed files with 466 additions and 356 deletions

View File

@@ -193,7 +193,7 @@
<permission android:name="com.android.systemui.permission.FLAGS"
android:protectionLevel="signature" />
<permission android:name="android.permission.ACCESS_KEYGUARD_QUICK_AFFORDANCES"
<permission android:name="android.permission.CUSTOMIZE_SYSTEM_UI"
android:protectionLevel="signature|privileged" />
<!-- Adding Quick Settings tiles -->
@@ -1020,10 +1020,10 @@
</activity>
<provider
android:authorities="com.android.systemui.keyguard.quickaffordance"
android:name="com.android.systemui.keyguard.KeyguardQuickAffordanceProvider"
android:authorities="com.android.systemui.customization"
android:name="com.android.systemui.keyguard.CustomizationProvider"
android:exported="true"
android:permission="android.permission.ACCESS_KEYGUARD_QUICK_AFFORDANCES"
android:permission="android.permission.CUSTOMIZE_SYSTEM_UI"
/>
</application>
</manifest>

View File

@@ -35,7 +35,7 @@
android:enabled="false"
tools:replace="android:authorities"
tools:node="remove" />
<provider android:name="com.android.systemui.keyguard.KeyguardQuickAffordanceProvider"
<provider android:name="com.android.systemui.keyguard.CustomizationProvider"
android:authorities="com.android.systemui.test.keyguard.quickaffordance.disabled"
android:enabled="false"
tools:replace="android:authorities"

View File

@@ -15,7 +15,7 @@
*
*/
package com.android.systemui.shared.quickaffordance.data.content
package com.android.systemui.shared.customization.data.content
import android.annotation.SuppressLint
import android.content.ContentValues
@@ -25,7 +25,7 @@ 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 com.android.systemui.shared.customization.data.content.CustomizationProviderContract as Contract
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
@@ -35,7 +35,7 @@ import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.withContext
/** Client for using a content provider implementing the [Contract]. */
interface KeyguardQuickAffordanceProviderClient {
interface CustomizationProviderClient {
/**
* Selects an affordance with the given ID for a slot on the lock screen with the given ID.
@@ -190,10 +190,10 @@ interface KeyguardQuickAffordanceProviderClient {
)
}
class KeyguardQuickAffordanceProviderClientImpl(
class CustomizationProviderClientImpl(
private val context: Context,
private val backgroundDispatcher: CoroutineDispatcher,
) : KeyguardQuickAffordanceProviderClient {
) : CustomizationProviderClient {
override suspend fun insertSelection(
slotId: String,
@@ -201,20 +201,23 @@ class KeyguardQuickAffordanceProviderClientImpl(
) {
withContext(backgroundDispatcher) {
context.contentResolver.insert(
Contract.SelectionTable.URI,
Contract.LockScreenQuickAffordances.SelectionTable.URI,
ContentValues().apply {
put(Contract.SelectionTable.Columns.SLOT_ID, slotId)
put(Contract.SelectionTable.Columns.AFFORDANCE_ID, affordanceId)
put(Contract.LockScreenQuickAffordances.SelectionTable.Columns.SLOT_ID, slotId)
put(
Contract.LockScreenQuickAffordances.SelectionTable.Columns.AFFORDANCE_ID,
affordanceId
)
}
)
}
}
override suspend fun querySlots(): List<KeyguardQuickAffordanceProviderClient.Slot> {
override suspend fun querySlots(): List<CustomizationProviderClient.Slot> {
return withContext(backgroundDispatcher) {
context.contentResolver
.query(
Contract.SlotTable.URI,
Contract.LockScreenQuickAffordances.SlotTable.URI,
null,
null,
null,
@@ -222,16 +225,21 @@ class KeyguardQuickAffordanceProviderClientImpl(
)
?.use { cursor ->
buildList {
val idColumnIndex = cursor.getColumnIndex(Contract.SlotTable.Columns.ID)
val idColumnIndex =
cursor.getColumnIndex(
Contract.LockScreenQuickAffordances.SlotTable.Columns.ID
)
val capacityColumnIndex =
cursor.getColumnIndex(Contract.SlotTable.Columns.CAPACITY)
cursor.getColumnIndex(
Contract.LockScreenQuickAffordances.SlotTable.Columns.CAPACITY
)
if (idColumnIndex == -1 || capacityColumnIndex == -1) {
return@buildList
}
while (cursor.moveToNext()) {
add(
KeyguardQuickAffordanceProviderClient.Slot(
CustomizationProviderClient.Slot(
id = cursor.getString(idColumnIndex),
capacity = cursor.getInt(capacityColumnIndex),
)
@@ -243,7 +251,7 @@ class KeyguardQuickAffordanceProviderClientImpl(
?: emptyList()
}
override suspend fun queryFlags(): List<KeyguardQuickAffordanceProviderClient.Flag> {
override suspend fun queryFlags(): List<CustomizationProviderClient.Flag> {
return withContext(backgroundDispatcher) {
context.contentResolver
.query(
@@ -265,7 +273,7 @@ class KeyguardQuickAffordanceProviderClientImpl(
while (cursor.moveToNext()) {
add(
KeyguardQuickAffordanceProviderClient.Flag(
CustomizationProviderClient.Flag(
name = cursor.getString(nameColumnIndex),
value = cursor.getInt(valueColumnIndex) == 1,
)
@@ -277,20 +285,19 @@ class KeyguardQuickAffordanceProviderClientImpl(
?: emptyList()
}
override fun observeSlots(): Flow<List<KeyguardQuickAffordanceProviderClient.Slot>> {
return observeUri(Contract.SlotTable.URI).map { querySlots() }
override fun observeSlots(): Flow<List<CustomizationProviderClient.Slot>> {
return observeUri(Contract.LockScreenQuickAffordances.SlotTable.URI).map { querySlots() }
}
override fun observeFlags(): Flow<List<KeyguardQuickAffordanceProviderClient.Flag>> {
override fun observeFlags(): Flow<List<CustomizationProviderClient.Flag>> {
return observeUri(Contract.FlagsTable.URI).map { queryFlags() }
}
override suspend fun queryAffordances():
List<KeyguardQuickAffordanceProviderClient.Affordance> {
override suspend fun queryAffordances(): List<CustomizationProviderClient.Affordance> {
return withContext(backgroundDispatcher) {
context.contentResolver
.query(
Contract.AffordanceTable.URI,
Contract.LockScreenQuickAffordances.AffordanceTable.URI,
null,
null,
null,
@@ -299,24 +306,36 @@ class KeyguardQuickAffordanceProviderClientImpl(
?.use { cursor ->
buildList {
val idColumnIndex =
cursor.getColumnIndex(Contract.AffordanceTable.Columns.ID)
cursor.getColumnIndex(
Contract.LockScreenQuickAffordances.AffordanceTable.Columns.ID
)
val nameColumnIndex =
cursor.getColumnIndex(Contract.AffordanceTable.Columns.NAME)
cursor.getColumnIndex(
Contract.LockScreenQuickAffordances.AffordanceTable.Columns.NAME
)
val iconColumnIndex =
cursor.getColumnIndex(Contract.AffordanceTable.Columns.ICON)
cursor.getColumnIndex(
Contract.LockScreenQuickAffordances.AffordanceTable.Columns.ICON
)
val isEnabledColumnIndex =
cursor.getColumnIndex(Contract.AffordanceTable.Columns.IS_ENABLED)
cursor.getColumnIndex(
Contract.LockScreenQuickAffordances.AffordanceTable.Columns
.IS_ENABLED
)
val enablementInstructionsColumnIndex =
cursor.getColumnIndex(
Contract.AffordanceTable.Columns.ENABLEMENT_INSTRUCTIONS
Contract.LockScreenQuickAffordances.AffordanceTable.Columns
.ENABLEMENT_INSTRUCTIONS
)
val enablementActionTextColumnIndex =
cursor.getColumnIndex(
Contract.AffordanceTable.Columns.ENABLEMENT_ACTION_TEXT
Contract.LockScreenQuickAffordances.AffordanceTable.Columns
.ENABLEMENT_ACTION_TEXT
)
val enablementComponentNameColumnIndex =
cursor.getColumnIndex(
Contract.AffordanceTable.Columns.ENABLEMENT_COMPONENT_NAME
Contract.LockScreenQuickAffordances.AffordanceTable.Columns
.ENABLEMENT_COMPONENT_NAME
)
if (
idColumnIndex == -1 ||
@@ -332,7 +351,7 @@ class KeyguardQuickAffordanceProviderClientImpl(
while (cursor.moveToNext()) {
add(
KeyguardQuickAffordanceProviderClient.Affordance(
CustomizationProviderClient.Affordance(
id = cursor.getString(idColumnIndex),
name = cursor.getString(nameColumnIndex),
iconResourceId = cursor.getInt(iconColumnIndex),
@@ -341,7 +360,7 @@ class KeyguardQuickAffordanceProviderClientImpl(
cursor
.getString(enablementInstructionsColumnIndex)
?.split(
Contract.AffordanceTable
Contract.LockScreenQuickAffordances.AffordanceTable
.ENABLEMENT_INSTRUCTIONS_DELIMITER
),
enablementActionText =
@@ -357,16 +376,17 @@ class KeyguardQuickAffordanceProviderClientImpl(
?: emptyList()
}
override fun observeAffordances():
Flow<List<KeyguardQuickAffordanceProviderClient.Affordance>> {
return observeUri(Contract.AffordanceTable.URI).map { queryAffordances() }
override fun observeAffordances(): Flow<List<CustomizationProviderClient.Affordance>> {
return observeUri(Contract.LockScreenQuickAffordances.AffordanceTable.URI).map {
queryAffordances()
}
}
override suspend fun querySelections(): List<KeyguardQuickAffordanceProviderClient.Selection> {
override suspend fun querySelections(): List<CustomizationProviderClient.Selection> {
return withContext(backgroundDispatcher) {
context.contentResolver
.query(
Contract.SelectionTable.URI,
Contract.LockScreenQuickAffordances.SelectionTable.URI,
null,
null,
null,
@@ -375,11 +395,19 @@ class KeyguardQuickAffordanceProviderClientImpl(
?.use { cursor ->
buildList {
val slotIdColumnIndex =
cursor.getColumnIndex(Contract.SelectionTable.Columns.SLOT_ID)
cursor.getColumnIndex(
Contract.LockScreenQuickAffordances.SelectionTable.Columns.SLOT_ID
)
val affordanceIdColumnIndex =
cursor.getColumnIndex(Contract.SelectionTable.Columns.AFFORDANCE_ID)
cursor.getColumnIndex(
Contract.LockScreenQuickAffordances.SelectionTable.Columns
.AFFORDANCE_ID
)
val affordanceNameColumnIndex =
cursor.getColumnIndex(Contract.SelectionTable.Columns.AFFORDANCE_NAME)
cursor.getColumnIndex(
Contract.LockScreenQuickAffordances.SelectionTable.Columns
.AFFORDANCE_NAME
)
if (
slotIdColumnIndex == -1 ||
affordanceIdColumnIndex == -1 ||
@@ -390,7 +418,7 @@ class KeyguardQuickAffordanceProviderClientImpl(
while (cursor.moveToNext()) {
add(
KeyguardQuickAffordanceProviderClient.Selection(
CustomizationProviderClient.Selection(
slotId = cursor.getString(slotIdColumnIndex),
affordanceId = cursor.getString(affordanceIdColumnIndex),
affordanceName = cursor.getString(affordanceNameColumnIndex),
@@ -403,8 +431,10 @@ class KeyguardQuickAffordanceProviderClientImpl(
?: emptyList()
}
override fun observeSelections(): Flow<List<KeyguardQuickAffordanceProviderClient.Selection>> {
return observeUri(Contract.SelectionTable.URI).map { querySelections() }
override fun observeSelections(): Flow<List<CustomizationProviderClient.Selection>> {
return observeUri(Contract.LockScreenQuickAffordances.SelectionTable.URI).map {
querySelections()
}
}
override suspend fun deleteSelection(
@@ -413,9 +443,10 @@ class KeyguardQuickAffordanceProviderClientImpl(
) {
withContext(backgroundDispatcher) {
context.contentResolver.delete(
Contract.SelectionTable.URI,
"${Contract.SelectionTable.Columns.SLOT_ID} = ? AND" +
" ${Contract.SelectionTable.Columns.AFFORDANCE_ID} = ?",
Contract.LockScreenQuickAffordances.SelectionTable.URI,
"${Contract.LockScreenQuickAffordances.SelectionTable.Columns.SLOT_ID} = ? AND" +
" ${Contract.LockScreenQuickAffordances.SelectionTable.Columns.AFFORDANCE_ID}" +
" = ?",
arrayOf(
slotId,
affordanceId,
@@ -429,8 +460,8 @@ class KeyguardQuickAffordanceProviderClientImpl(
) {
withContext(backgroundDispatcher) {
context.contentResolver.delete(
Contract.SelectionTable.URI,
Contract.SelectionTable.Columns.SLOT_ID,
Contract.LockScreenQuickAffordances.SelectionTable.URI,
Contract.LockScreenQuickAffordances.SelectionTable.Columns.SLOT_ID,
arrayOf(
slotId,
),

View File

@@ -0,0 +1,183 @@
/*
* 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.customization.data.content
import android.content.ContentResolver
import android.net.Uri
/** Contract definitions for querying content about keyguard quick affordances. */
object CustomizationProviderContract {
const val AUTHORITY = "com.android.systemui.customization"
const val PERMISSION = "android.permission.CUSTOMIZE_SYSTEM_UI"
private val BASE_URI: Uri =
Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT).authority(AUTHORITY).build()
/** Namespace for lock screen shortcut (quick affordance) tables. */
object LockScreenQuickAffordances {
const val NAMESPACE = "lockscreen_quickaffordance"
private val LOCK_SCREEN_QUICK_AFFORDANCE_BASE_URI: Uri =
BASE_URI.buildUpon().path(NAMESPACE).build()
fun qualifiedTablePath(tableName: String): String {
return "$NAMESPACE/$tableName"
}
/**
* Table for slots.
*
* Slots are positions where affordances can be placed on the lock screen. Affordances that
* are placed on slots are said to be "selected". The system supports the idea of multiple
* affordances per slot, though the implementation may limit the number of affordances on
* each slot.
*
* Supported operations:
* - Query - to know which slots are available, query the [SlotTable.URI] [Uri]. The result
* set will contain rows with the [SlotTable.Columns] columns.
*/
object SlotTable {
const val TABLE_NAME = "slots"
val URI: Uri =
LOCK_SCREEN_QUICK_AFFORDANCE_BASE_URI.buildUpon().appendPath(TABLE_NAME).build()
object Columns {
/** String. Unique ID for this slot. */
const val ID = "id"
/** Integer. The maximum number of affordances that can be placed in the slot. */
const val CAPACITY = "capacity"
}
}
/**
* Table for affordances.
*
* Affordances are actions/buttons that the user can execute. They are placed on slots on
* the lock screen.
*
* Supported operations:
* - Query - to know about all the affordances that are available on the device, regardless
* of which ones are currently selected, query the [AffordanceTable.URI] [Uri]. The result
* set will contain rows, each with the columns specified in [AffordanceTable.Columns].
*/
object AffordanceTable {
const val TABLE_NAME = "affordances"
val URI: Uri =
LOCK_SCREEN_QUICK_AFFORDANCE_BASE_URI.buildUpon().appendPath(TABLE_NAME).build()
const val ENABLEMENT_INSTRUCTIONS_DELIMITER = "]["
const val COMPONENT_NAME_SEPARATOR = "/"
object Columns {
/** String. Unique ID for this affordance. */
const val ID = "id"
/** String. User-visible name for this affordance. */
const val NAME = "name"
/**
* Integer. Resource ID for the drawable to load for this affordance. This is a
* resource ID from the system UI package.
*/
const val ICON = "icon"
/** Integer. `1` if the affordance is enabled or `0` if it disabled. */
const val IS_ENABLED = "is_enabled"
/**
* String. List of strings, delimited by [ENABLEMENT_INSTRUCTIONS_DELIMITER] to be
* shown to the user if the affordance is disabled and the user selects the
* affordance.
*/
const val ENABLEMENT_INSTRUCTIONS = "enablement_instructions"
/**
* String. Optional label for a button that, when clicked, opens a destination
* activity where the user can re-enable the disabled affordance.
*/
const val ENABLEMENT_ACTION_TEXT = "enablement_action_text"
/**
* String. Optional package name and activity action string, delimited by
* [COMPONENT_NAME_SEPARATOR] to use with an `Intent` to start an activity that
* opens a destination where the user can re-enable the disabled affordance.
*/
const val ENABLEMENT_COMPONENT_NAME = "enablement_action_intent"
}
}
/**
* Table for selections.
*
* Selections are pairs of slot and affordance IDs.
*
* Supported operations:
* - Insert - to insert an affordance and place it in a slot, insert values for the columns
* into the [SelectionTable.URI] [Uri]. The maximum capacity rule is enforced by the system.
* Selecting a new affordance for a slot that is already full will automatically remove the
* oldest affordance from the slot.
* - Query - to know which affordances are set on which slots, query the
* [SelectionTable.URI] [Uri]. The result set will contain rows, each of which with the
* columns from [SelectionTable.Columns].
* - Delete - to unselect an affordance, removing it from a slot, delete from the
* [SelectionTable.URI] [Uri], passing in values for each column.
*/
object SelectionTable {
const val TABLE_NAME = "selections"
val URI: Uri =
LOCK_SCREEN_QUICK_AFFORDANCE_BASE_URI.buildUpon().appendPath(TABLE_NAME).build()
object Columns {
/** String. Unique ID for the slot. */
const val SLOT_ID = "slot_id"
/** String. Unique ID for the selected affordance. */
const val AFFORDANCE_ID = "affordance_id"
/** String. Human-readable name for the affordance. */
const val AFFORDANCE_NAME = "affordance_name"
}
}
}
/**
* Table for flags.
*
* Flags are key-value pairs.
*
* Supported operations:
* - Query - to know the values of flags, query the [FlagsTable.URI] [Uri]. The result set will
* contain rows, each of which with the columns from [FlagsTable.Columns].
*/
object FlagsTable {
const val TABLE_NAME = "flags"
val URI: Uri = BASE_URI.buildUpon().path(TABLE_NAME).build()
/** Flag denoting whether the Wallpaper Picker should use the new, revamped UI. */
const val FLAG_NAME_REVAMPED_WALLPAPER_UI = "revamped_wallpaper_ui"
/**
* Flag denoting whether the customizable lock screen quick affordances feature is enabled.
*/
const val FLAG_NAME_CUSTOM_LOCK_SCREEN_QUICK_AFFORDANCES_ENABLED =
"is_custom_lock_screen_quick_affordances_feature_enabled"
/** Flag denoting whether the customizable clocks feature is enabled. */
const val FLAG_NAME_CUSTOM_CLOCKS_ENABLED = "is_custom_clocks_feature_enabled"
object Columns {
/** String. Unique ID for the flag. */
const val NAME = "name"
/** Int. Value of the flag. `1` means `true` and `0` means `false`. */
const val VALUE = "value"
}
}
}

View File

@@ -15,7 +15,7 @@
*
*/
package com.android.systemui.shared.quickaffordance.data.content
package com.android.systemui.shared.customization.data.content
import android.graphics.drawable.BitmapDrawable
import android.graphics.drawable.Drawable
@@ -25,46 +25,46 @@ import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.combine
class FakeKeyguardQuickAffordanceProviderClient(
slots: List<KeyguardQuickAffordanceProviderClient.Slot> =
class FakeCustomizationProviderClient(
slots: List<CustomizationProviderClient.Slot> =
listOf(
KeyguardQuickAffordanceProviderClient.Slot(
CustomizationProviderClient.Slot(
id = KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START,
capacity = 1,
),
KeyguardQuickAffordanceProviderClient.Slot(
CustomizationProviderClient.Slot(
id = KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_END,
capacity = 1,
),
),
affordances: List<KeyguardQuickAffordanceProviderClient.Affordance> =
affordances: List<CustomizationProviderClient.Affordance> =
listOf(
KeyguardQuickAffordanceProviderClient.Affordance(
CustomizationProviderClient.Affordance(
id = AFFORDANCE_1,
name = AFFORDANCE_1,
iconResourceId = 1,
),
KeyguardQuickAffordanceProviderClient.Affordance(
CustomizationProviderClient.Affordance(
id = AFFORDANCE_2,
name = AFFORDANCE_2,
iconResourceId = 2,
),
KeyguardQuickAffordanceProviderClient.Affordance(
CustomizationProviderClient.Affordance(
id = AFFORDANCE_3,
name = AFFORDANCE_3,
iconResourceId = 3,
),
),
flags: List<KeyguardQuickAffordanceProviderClient.Flag> =
flags: List<CustomizationProviderClient.Flag> =
listOf(
KeyguardQuickAffordanceProviderClient.Flag(
CustomizationProviderClient.Flag(
name =
KeyguardQuickAffordanceProviderContract.FlagsTable
CustomizationProviderContract.FlagsTable
.FLAG_NAME_CUSTOM_LOCK_SCREEN_QUICK_AFFORDANCES_ENABLED,
value = true,
)
),
) : KeyguardQuickAffordanceProviderClient {
) : CustomizationProviderClient {
private val slots = MutableStateFlow(slots)
private val affordances = MutableStateFlow(affordances)
@@ -85,37 +85,35 @@ class FakeKeyguardQuickAffordanceProviderClient(
selections.value = selections.value.toMutableMap().apply { this[slotId] = affordances }
}
override suspend fun querySlots(): List<KeyguardQuickAffordanceProviderClient.Slot> {
override suspend fun querySlots(): List<CustomizationProviderClient.Slot> {
return slots.value
}
override suspend fun queryFlags(): List<KeyguardQuickAffordanceProviderClient.Flag> {
override suspend fun queryFlags(): List<CustomizationProviderClient.Flag> {
return flags.value
}
override fun observeSlots(): Flow<List<KeyguardQuickAffordanceProviderClient.Slot>> {
override fun observeSlots(): Flow<List<CustomizationProviderClient.Slot>> {
return slots.asStateFlow()
}
override fun observeFlags(): Flow<List<KeyguardQuickAffordanceProviderClient.Flag>> {
override fun observeFlags(): Flow<List<CustomizationProviderClient.Flag>> {
return flags.asStateFlow()
}
override suspend fun queryAffordances():
List<KeyguardQuickAffordanceProviderClient.Affordance> {
override suspend fun queryAffordances(): List<CustomizationProviderClient.Affordance> {
return affordances.value
}
override fun observeAffordances():
Flow<List<KeyguardQuickAffordanceProviderClient.Affordance>> {
override fun observeAffordances(): Flow<List<CustomizationProviderClient.Affordance>> {
return affordances.asStateFlow()
}
override suspend fun querySelections(): List<KeyguardQuickAffordanceProviderClient.Selection> {
override suspend fun querySelections(): List<CustomizationProviderClient.Selection> {
return toSelectionList(selections.value, affordances.value)
}
override fun observeSelections(): Flow<List<KeyguardQuickAffordanceProviderClient.Selection>> {
override fun observeSelections(): Flow<List<CustomizationProviderClient.Selection>> {
return combine(selections, affordances) { selections, affordances ->
toSelectionList(selections, affordances)
}
@@ -148,7 +146,7 @@ class FakeKeyguardQuickAffordanceProviderClient(
flags.value =
flags.value.toMutableList().apply {
removeIf { it.name == name }
add(KeyguardQuickAffordanceProviderClient.Flag(name = name, value = value))
add(CustomizationProviderClient.Flag(name = name, value = value))
}
}
@@ -157,29 +155,26 @@ class FakeKeyguardQuickAffordanceProviderClient(
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)
)
set(index, CustomizationProviderClient.Slot(id = slotId, capacity = capacity))
}
}
fun addAffordance(affordance: KeyguardQuickAffordanceProviderClient.Affordance): Int {
fun addAffordance(affordance: CustomizationProviderClient.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> {
affordances: List<CustomizationProviderClient.Affordance>,
): List<CustomizationProviderClient.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(
CustomizationProviderClient.Selection(
slotId = slotId,
affordanceId = affordanceId,
affordanceName = affordanceName,

View File

@@ -1,166 +0,0 @@
/*
* 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.content.ContentResolver
import android.net.Uri
/** Contract definitions for querying content about keyguard quick affordances. */
object KeyguardQuickAffordanceProviderContract {
const val AUTHORITY = "com.android.systemui.keyguard.quickaffordance"
const val PERMISSION = "android.permission.ACCESS_KEYGUARD_QUICK_AFFORDANCES"
private val BASE_URI: Uri =
Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT).authority(AUTHORITY).build()
/**
* Table for slots.
*
* Slots are positions where affordances can be placed on the lock screen. Affordances that are
* placed on slots are said to be "selected". The system supports the idea of multiple
* affordances per slot, though the implementation may limit the number of affordances on each
* slot.
*
* Supported operations:
* - Query - to know which slots are available, query the [SlotTable.URI] [Uri]. The result set
* will contain rows with the [SlotTable.Columns] columns.
*/
object SlotTable {
const val TABLE_NAME = "slots"
val URI: Uri = BASE_URI.buildUpon().path(TABLE_NAME).build()
object Columns {
/** String. Unique ID for this slot. */
const val ID = "id"
/** Integer. The maximum number of affordances that can be placed in the slot. */
const val CAPACITY = "capacity"
}
}
/**
* Table for affordances.
*
* Affordances are actions/buttons that the user can execute. They are placed on slots on the
* lock screen.
*
* Supported operations:
* - Query - to know about all the affordances that are available on the device, regardless of
* which ones are currently selected, query the [AffordanceTable.URI] [Uri]. The result set will
* contain rows, each with the columns specified in [AffordanceTable.Columns].
*/
object AffordanceTable {
const val TABLE_NAME = "affordances"
val URI: Uri = BASE_URI.buildUpon().path(TABLE_NAME).build()
const val ENABLEMENT_INSTRUCTIONS_DELIMITER = "]["
const val COMPONENT_NAME_SEPARATOR = "/"
object Columns {
/** String. Unique ID for this affordance. */
const val ID = "id"
/** String. User-visible name for this affordance. */
const val NAME = "name"
/**
* Integer. Resource ID for the drawable to load for this affordance. This is a resource
* ID from the system UI package.
*/
const val ICON = "icon"
/** Integer. `1` if the affordance is enabled or `0` if it disabled. */
const val IS_ENABLED = "is_enabled"
/**
* String. List of strings, delimited by [ENABLEMENT_INSTRUCTIONS_DELIMITER] to be shown
* to the user if the affordance is disabled and the user selects the affordance.
*/
const val ENABLEMENT_INSTRUCTIONS = "enablement_instructions"
/**
* String. Optional label for a button that, when clicked, opens a destination activity
* where the user can re-enable the disabled affordance.
*/
const val ENABLEMENT_ACTION_TEXT = "enablement_action_text"
/**
* String. Optional package name and activity action string, delimited by
* [COMPONENT_NAME_SEPARATOR] to use with an `Intent` to start an activity that opens a
* destination where the user can re-enable the disabled affordance.
*/
const val ENABLEMENT_COMPONENT_NAME = "enablement_action_intent"
}
}
/**
* Table for selections.
*
* Selections are pairs of slot and affordance IDs.
*
* Supported operations:
* - Insert - to insert an affordance and place it in a slot, insert values for the columns into
* the [SelectionTable.URI] [Uri]. The maximum capacity rule is enforced by the system.
* Selecting a new affordance for a slot that is already full will automatically remove the
* oldest affordance from the slot.
* - Query - to know which affordances are set on which slots, query the [SelectionTable.URI]
* [Uri]. The result set will contain rows, each of which with the columns from
* [SelectionTable.Columns].
* - Delete - to unselect an affordance, removing it from a slot, delete from the
* [SelectionTable.URI] [Uri], passing in values for each column.
*/
object SelectionTable {
const val TABLE_NAME = "selections"
val URI: Uri = BASE_URI.buildUpon().path(TABLE_NAME).build()
object Columns {
/** String. Unique ID for the slot. */
const val SLOT_ID = "slot_id"
/** String. Unique ID for the selected affordance. */
const val AFFORDANCE_ID = "affordance_id"
/** String. Human-readable name for the affordance. */
const val AFFORDANCE_NAME = "affordance_name"
}
}
/**
* Table for flags.
*
* Flags are key-value pairs.
*
* Supported operations:
* - Query - to know the values of flags, query the [FlagsTable.URI] [Uri]. The result set will
* contain rows, each of which with the columns from [FlagsTable.Columns].
*/
object FlagsTable {
const val TABLE_NAME = "flags"
val URI: Uri = BASE_URI.buildUpon().path(TABLE_NAME).build()
/** Flag denoting whether the Wallpaper Picker should use the new, revamped UI. */
const val FLAG_NAME_REVAMPED_WALLPAPER_UI = "revamped_wallpaper_ui"
/**
* Flag denoting whether the customizable lock screen quick affordances feature is enabled.
*/
const val FLAG_NAME_CUSTOM_LOCK_SCREEN_QUICK_AFFORDANCES_ENABLED =
"is_custom_lock_screen_quick_affordances_feature_enabled"
/** Flag denoting whether the customizable clocks feature is enabled. */
const val FLAG_NAME_CUSTOM_CLOCKS_ENABLED = "is_custom_clocks_feature_enabled"
object Columns {
/** String. Unique ID for the flag. */
const val NAME = "name"
/** Int. Value of the flag. `1` means `true` and `0` means `false`. */
const val VALUE = "value"
}
}
}

View File

@@ -31,7 +31,7 @@ This section describes how to implement a potential picker, selector, or configu
### Accessing Quick Affordance Data
Quick Affordances structured data are exposed to other applications through the `KeyguardQuickAffordanceProvider` content provider which is owned by the System UI process.
To access this content provider, applications must have the `android.permission.ACCESS_KEYGUARD_QUICK_AFFORDANCES` permission which is a signature and privileged permission, limiting access to system apps or apps signed by the same signature as System UI. The `KeyguardQuickAffordanceProviderContract` file defines the content provider schema for consumers.
To access this content provider, applications must have the `android.permission.CUSTOMIZE_SYSTEM_UI` permission which is a signature and privileged permission, limiting access to system apps or apps signed by the same signature as System UI. The `KeyguardQuickAffordanceProviderContract` file defines the content provider schema for consumers.
Generally speaking, there are three important tables served by the content provider: `slots`, `affordances`, and `selections`. There is also a `flags` table, but that's not important and may be ignored.

View File

@@ -16,7 +16,7 @@
package com.android.systemui.dagger;
import com.android.systemui.keyguard.KeyguardQuickAffordanceProvider;
import com.android.systemui.keyguard.CustomizationProvider;
import com.android.systemui.statusbar.NotificationInsetsModule;
import com.android.systemui.statusbar.QsFrameTranslateModule;
@@ -49,5 +49,5 @@ public interface ReferenceSysUIComponent extends SysUIComponent {
/**
* Member injection into the supplied argument.
*/
void inject(KeyguardQuickAffordanceProvider keyguardQuickAffordanceProvider);
void inject(CustomizationProvider customizationProvider);
}

View File

@@ -33,11 +33,11 @@ import com.android.systemui.SystemUIAppComponentFactoryBase
import com.android.systemui.SystemUIAppComponentFactoryBase.ContextAvailableCallback
import com.android.systemui.keyguard.domain.interactor.KeyguardQuickAffordanceInteractor
import com.android.systemui.keyguard.ui.preview.KeyguardRemotePreviewManager
import com.android.systemui.shared.quickaffordance.data.content.KeyguardQuickAffordanceProviderContract as Contract
import com.android.systemui.shared.customization.data.content.CustomizationProviderContract as Contract
import javax.inject.Inject
import kotlinx.coroutines.runBlocking
class KeyguardQuickAffordanceProvider :
class CustomizationProvider :
ContentProvider(), SystemUIAppComponentFactoryBase.ContextInitializer {
@Inject lateinit var interactor: KeyguardQuickAffordanceInteractor
@@ -49,17 +49,23 @@ class KeyguardQuickAffordanceProvider :
UriMatcher(UriMatcher.NO_MATCH).apply {
addURI(
Contract.AUTHORITY,
Contract.SlotTable.TABLE_NAME,
Contract.LockScreenQuickAffordances.qualifiedTablePath(
Contract.LockScreenQuickAffordances.SlotTable.TABLE_NAME,
),
MATCH_CODE_ALL_SLOTS,
)
addURI(
Contract.AUTHORITY,
Contract.AffordanceTable.TABLE_NAME,
Contract.LockScreenQuickAffordances.qualifiedTablePath(
Contract.LockScreenQuickAffordances.AffordanceTable.TABLE_NAME,
),
MATCH_CODE_ALL_AFFORDANCES,
)
addURI(
Contract.AUTHORITY,
Contract.SelectionTable.TABLE_NAME,
Contract.LockScreenQuickAffordances.qualifiedTablePath(
Contract.LockScreenQuickAffordances.SelectionTable.TABLE_NAME,
),
MATCH_CODE_ALL_SELECTIONS,
)
addURI(
@@ -94,9 +100,18 @@ class KeyguardQuickAffordanceProvider :
val tableName =
when (uriMatcher.match(uri)) {
MATCH_CODE_ALL_SLOTS -> Contract.SlotTable.TABLE_NAME
MATCH_CODE_ALL_AFFORDANCES -> Contract.AffordanceTable.TABLE_NAME
MATCH_CODE_ALL_SELECTIONS -> Contract.SelectionTable.TABLE_NAME
MATCH_CODE_ALL_SLOTS ->
Contract.LockScreenQuickAffordances.qualifiedTablePath(
Contract.LockScreenQuickAffordances.SlotTable.TABLE_NAME,
)
MATCH_CODE_ALL_AFFORDANCES ->
Contract.LockScreenQuickAffordances.qualifiedTablePath(
Contract.LockScreenQuickAffordances.AffordanceTable.TABLE_NAME,
)
MATCH_CODE_ALL_SELECTIONS ->
Contract.LockScreenQuickAffordances.qualifiedTablePath(
Contract.LockScreenQuickAffordances.SelectionTable.TABLE_NAME,
)
MATCH_CODE_ALL_FLAGS -> Contract.FlagsTable.TABLE_NAME
else -> null
}
@@ -174,22 +189,34 @@ class KeyguardQuickAffordanceProvider :
throw IllegalArgumentException("Cannot insert selection, no values passed in!")
}
if (!values.containsKey(Contract.SelectionTable.Columns.SLOT_ID)) {
if (
!values.containsKey(Contract.LockScreenQuickAffordances.SelectionTable.Columns.SLOT_ID)
) {
throw IllegalArgumentException(
"Cannot insert selection, " +
"\"${Contract.SelectionTable.Columns.SLOT_ID}\" not specified!"
"\"${Contract.LockScreenQuickAffordances.SelectionTable.Columns.SLOT_ID}\"" +
" not specified!"
)
}
if (!values.containsKey(Contract.SelectionTable.Columns.AFFORDANCE_ID)) {
if (
!values.containsKey(
Contract.LockScreenQuickAffordances.SelectionTable.Columns.AFFORDANCE_ID
)
) {
throw IllegalArgumentException(
"Cannot insert selection, " +
"\"${Contract.SelectionTable.Columns.AFFORDANCE_ID}\" not specified!"
"\"${Contract.LockScreenQuickAffordances
.SelectionTable.Columns.AFFORDANCE_ID}\" not specified!"
)
}
val slotId = values.getAsString(Contract.SelectionTable.Columns.SLOT_ID)
val affordanceId = values.getAsString(Contract.SelectionTable.Columns.AFFORDANCE_ID)
val slotId =
values.getAsString(Contract.LockScreenQuickAffordances.SelectionTable.Columns.SLOT_ID)
val affordanceId =
values.getAsString(
Contract.LockScreenQuickAffordances.SelectionTable.Columns.AFFORDANCE_ID
)
if (slotId.isNullOrEmpty()) {
throw IllegalArgumentException("Cannot insert selection, slot ID was empty!")
@@ -207,8 +234,10 @@ class KeyguardQuickAffordanceProvider :
return if (success) {
Log.d(TAG, "Successfully selected $affordanceId for slot $slotId")
context?.contentResolver?.notifyChange(Contract.SelectionTable.URI, null)
Contract.SelectionTable.URI
context
?.contentResolver
?.notifyChange(Contract.LockScreenQuickAffordances.SelectionTable.URI, null)
Contract.LockScreenQuickAffordances.SelectionTable.URI
} else {
Log.d(TAG, "Failed to select $affordanceId for slot $slotId")
null
@@ -218,9 +247,9 @@ class KeyguardQuickAffordanceProvider :
private suspend fun querySelections(): Cursor {
return MatrixCursor(
arrayOf(
Contract.SelectionTable.Columns.SLOT_ID,
Contract.SelectionTable.Columns.AFFORDANCE_ID,
Contract.SelectionTable.Columns.AFFORDANCE_NAME,
Contract.LockScreenQuickAffordances.SelectionTable.Columns.SLOT_ID,
Contract.LockScreenQuickAffordances.SelectionTable.Columns.AFFORDANCE_ID,
Contract.LockScreenQuickAffordances.SelectionTable.Columns.AFFORDANCE_NAME,
)
)
.apply {
@@ -243,13 +272,16 @@ class KeyguardQuickAffordanceProvider :
private suspend fun queryAffordances(): Cursor {
return MatrixCursor(
arrayOf(
Contract.AffordanceTable.Columns.ID,
Contract.AffordanceTable.Columns.NAME,
Contract.AffordanceTable.Columns.ICON,
Contract.AffordanceTable.Columns.IS_ENABLED,
Contract.AffordanceTable.Columns.ENABLEMENT_INSTRUCTIONS,
Contract.AffordanceTable.Columns.ENABLEMENT_ACTION_TEXT,
Contract.AffordanceTable.Columns.ENABLEMENT_COMPONENT_NAME,
Contract.LockScreenQuickAffordances.AffordanceTable.Columns.ID,
Contract.LockScreenQuickAffordances.AffordanceTable.Columns.NAME,
Contract.LockScreenQuickAffordances.AffordanceTable.Columns.ICON,
Contract.LockScreenQuickAffordances.AffordanceTable.Columns.IS_ENABLED,
Contract.LockScreenQuickAffordances.AffordanceTable.Columns
.ENABLEMENT_INSTRUCTIONS,
Contract.LockScreenQuickAffordances.AffordanceTable.Columns
.ENABLEMENT_ACTION_TEXT,
Contract.LockScreenQuickAffordances.AffordanceTable.Columns
.ENABLEMENT_COMPONENT_NAME,
)
)
.apply {
@@ -261,7 +293,8 @@ class KeyguardQuickAffordanceProvider :
representation.iconResourceId,
if (representation.isEnabled) 1 else 0,
representation.instructions?.joinToString(
Contract.AffordanceTable.ENABLEMENT_INSTRUCTIONS_DELIMITER
Contract.LockScreenQuickAffordances.AffordanceTable
.ENABLEMENT_INSTRUCTIONS_DELIMITER
),
representation.actionText,
representation.actionComponentName,
@@ -274,8 +307,8 @@ class KeyguardQuickAffordanceProvider :
private fun querySlots(): Cursor {
return MatrixCursor(
arrayOf(
Contract.SlotTable.Columns.ID,
Contract.SlotTable.Columns.CAPACITY,
Contract.LockScreenQuickAffordances.SlotTable.Columns.ID,
Contract.LockScreenQuickAffordances.SlotTable.Columns.CAPACITY,
)
)
.apply {

View File

@@ -22,7 +22,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.quickaffordance.data.content.KeyguardQuickAffordanceProviderContract as Contract
import com.android.systemui.shared.customization.data.content.CustomizationProviderContract as Contract
import kotlinx.coroutines.flow.Flow
/** Defines interface that can act as data source for a single quick affordance model. */
@@ -90,8 +90,9 @@ interface KeyguardQuickAffordanceConfig {
* the user to be able to set up the quick affordance and make it enabled.
*
* This is either just an action for the `Intent` or a package name and action,
* separated by [Contract.AffordanceTable.COMPONENT_NAME_SEPARATOR] for convenience, you
* can use the [componentName] function.
* separated by
* [Contract.LockScreenQuickAffordances.AffordanceTable.COMPONENT_NAME_SEPARATOR] for
* convenience, you can use the [componentName] function.
*/
val actionComponentName: String? = null,
) : PickerScreenState() {
@@ -145,8 +146,8 @@ interface KeyguardQuickAffordanceConfig {
/**
* Returning this as a result from the [onTriggered] method means that the implementation
* has _not_ taken care of the action and the system should show a Dialog using the
* given [AlertDialog] and [Expandable].
* has _not_ taken care of the action and the system should show a Dialog using the given
* [AlertDialog] and [Expandable].
*/
data class ShowDialog(
val dialog: AlertDialog,
@@ -162,7 +163,8 @@ interface KeyguardQuickAffordanceConfig {
return when {
action.isNullOrEmpty() -> null
!packageName.isNullOrEmpty() ->
"$packageName${Contract.AffordanceTable.COMPONENT_NAME_SEPARATOR}$action"
"$packageName${Contract.LockScreenQuickAffordances.AffordanceTable
.COMPONENT_NAME_SEPARATOR}$action"
else -> action
}
}

View File

@@ -19,13 +19,13 @@ package com.android.systemui.keyguard.data.quickaffordance
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.settings.UserTracker
import com.android.systemui.shared.quickaffordance.data.content.KeyguardQuickAffordanceProviderClient
import com.android.systemui.shared.quickaffordance.data.content.KeyguardQuickAffordanceProviderClientImpl
import com.android.systemui.shared.customization.data.content.CustomizationProviderClient
import com.android.systemui.shared.customization.data.content.CustomizationProviderClientImpl
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
interface KeyguardQuickAffordanceProviderClientFactory {
fun create(): KeyguardQuickAffordanceProviderClient
fun create(): CustomizationProviderClient
}
class KeyguardQuickAffordanceProviderClientFactoryImpl
@@ -34,8 +34,8 @@ constructor(
private val userTracker: UserTracker,
@Background private val backgroundDispatcher: CoroutineDispatcher,
) : KeyguardQuickAffordanceProviderClientFactory {
override fun create(): KeyguardQuickAffordanceProviderClient {
return KeyguardQuickAffordanceProviderClientImpl(
override fun create(): CustomizationProviderClient {
return CustomizationProviderClientImpl(
context = userTracker.userContext,
backgroundDispatcher = backgroundDispatcher,
)

View File

@@ -24,7 +24,7 @@ import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCall
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.settings.UserTracker
import com.android.systemui.shared.quickaffordance.data.content.KeyguardQuickAffordanceProviderClient
import com.android.systemui.shared.customization.data.content.CustomizationProviderClient
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -69,7 +69,7 @@ constructor(
awaitClose { userTracker.removeCallback(callback) }
}
private val clientOrNull: StateFlow<KeyguardQuickAffordanceProviderClient?> =
private val clientOrNull: StateFlow<CustomizationProviderClient?> =
userId
.distinctUntilChanged()
.map { selectedUserId ->

View File

@@ -36,7 +36,7 @@ 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.quickaffordance.data.content.KeyguardQuickAffordanceProviderContract as Contract
import com.android.systemui.shared.customization.data.content.CustomizationProviderContract as Contract
import com.android.systemui.statusbar.phone.SystemUIDialog
import com.android.systemui.statusbar.policy.KeyguardStateController
import dagger.Lazy

View File

@@ -146,7 +146,7 @@
tools:replace="android:authorities"
tools:node="remove" />
<provider android:name="com.android.systemui.keyguard.KeyguardQuickAffordanceProvider"
<provider android:name="com.android.systemui.keyguard.CustomizationProvider"
android:authorities="com.android.systemui.test.keyguard.quickaffordance.disabled"
android:enabled="false"
tools:replace="android:authorities"

View File

@@ -49,8 +49,8 @@ import com.android.systemui.keyguard.ui.preview.KeyguardRemotePreviewManager
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.settings.UserFileManager
import com.android.systemui.settings.UserTracker
import com.android.systemui.shared.customization.data.content.CustomizationProviderContract 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.any
@@ -75,7 +75,7 @@ import org.mockito.MockitoAnnotations
@SmallTest
@RunWith(AndroidTestingRunner::class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
class KeyguardQuickAffordanceProviderTest : SysuiTestCase() {
class CustomizationProviderTest : SysuiTestCase() {
@Mock private lateinit var lockPatternUtils: LockPatternUtils
@Mock private lateinit var keyguardStateController: KeyguardStateController
@@ -87,7 +87,7 @@ class KeyguardQuickAffordanceProviderTest : SysuiTestCase() {
@Mock private lateinit var previewSurfacePackage: SurfaceControlViewHost.SurfacePackage
@Mock private lateinit var launchAnimator: DialogLaunchAnimator
private lateinit var underTest: KeyguardQuickAffordanceProvider
private lateinit var underTest: CustomizationProvider
private lateinit var testScope: TestScope
@@ -98,7 +98,7 @@ class KeyguardQuickAffordanceProviderTest : SysuiTestCase() {
whenever(previewRendererFactory.create(any())).thenReturn(previewRenderer)
whenever(backgroundHandler.looper).thenReturn(TestableLooper.get(this).looper)
underTest = KeyguardQuickAffordanceProvider()
underTest = CustomizationProvider()
val testDispatcher = StandardTestDispatcher()
testScope = TestScope(testDispatcher)
val localUserSelectionManager =
@@ -205,19 +205,34 @@ class KeyguardQuickAffordanceProviderTest : SysuiTestCase() {
@Test
fun getType() {
assertThat(underTest.getType(Contract.AffordanceTable.URI))
assertThat(underTest.getType(Contract.LockScreenQuickAffordances.AffordanceTable.URI))
.isEqualTo(
"vnd.android.cursor.dir/vnd." +
"${Contract.AUTHORITY}.${Contract.AffordanceTable.TABLE_NAME}"
"${Contract.AUTHORITY}." +
Contract.LockScreenQuickAffordances.qualifiedTablePath(
Contract.LockScreenQuickAffordances.AffordanceTable.TABLE_NAME
)
)
assertThat(underTest.getType(Contract.SlotTable.URI))
assertThat(underTest.getType(Contract.LockScreenQuickAffordances.SlotTable.URI))
.isEqualTo(
"vnd.android.cursor.dir/vnd.${Contract.AUTHORITY}.${Contract.SlotTable.TABLE_NAME}"
"vnd.android.cursor.dir/vnd.${Contract.AUTHORITY}." +
Contract.LockScreenQuickAffordances.qualifiedTablePath(
Contract.LockScreenQuickAffordances.SlotTable.TABLE_NAME
)
)
assertThat(underTest.getType(Contract.SelectionTable.URI))
assertThat(underTest.getType(Contract.LockScreenQuickAffordances.SelectionTable.URI))
.isEqualTo(
"vnd.android.cursor.dir/vnd." +
"${Contract.AUTHORITY}.${Contract.SelectionTable.TABLE_NAME}"
"${Contract.AUTHORITY}." +
Contract.LockScreenQuickAffordances.qualifiedTablePath(
Contract.LockScreenQuickAffordances.SelectionTable.TABLE_NAME
)
)
assertThat(underTest.getType(Contract.FlagsTable.URI))
.isEqualTo(
"vnd.android.cursor.dir/vnd." +
"${Contract.AUTHORITY}." +
Contract.FlagsTable.TABLE_NAME
)
}
@@ -296,9 +311,10 @@ class KeyguardQuickAffordanceProviderTest : SysuiTestCase() {
)
context.contentResolver.delete(
Contract.SelectionTable.URI,
"${Contract.SelectionTable.Columns.SLOT_ID} = ? AND" +
" ${Contract.SelectionTable.Columns.AFFORDANCE_ID} = ?",
Contract.LockScreenQuickAffordances.SelectionTable.URI,
"${Contract.LockScreenQuickAffordances.SelectionTable.Columns.SLOT_ID} = ? AND" +
" ${Contract.LockScreenQuickAffordances.SelectionTable.Columns.AFFORDANCE_ID}" +
" = ?",
arrayOf(
KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_END,
AFFORDANCE_2,
@@ -330,8 +346,8 @@ class KeyguardQuickAffordanceProviderTest : SysuiTestCase() {
)
context.contentResolver.delete(
Contract.SelectionTable.URI,
Contract.SelectionTable.Columns.SLOT_ID,
Contract.LockScreenQuickAffordances.SelectionTable.URI,
Contract.LockScreenQuickAffordances.SelectionTable.Columns.SLOT_ID,
arrayOf(
KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_END,
),
@@ -371,10 +387,13 @@ class KeyguardQuickAffordanceProviderTest : SysuiTestCase() {
affordanceId: String,
) {
context.contentResolver.insert(
Contract.SelectionTable.URI,
Contract.LockScreenQuickAffordances.SelectionTable.URI,
ContentValues().apply {
put(Contract.SelectionTable.Columns.SLOT_ID, slotId)
put(Contract.SelectionTable.Columns.AFFORDANCE_ID, affordanceId)
put(Contract.LockScreenQuickAffordances.SelectionTable.Columns.SLOT_ID, slotId)
put(
Contract.LockScreenQuickAffordances.SelectionTable.Columns.AFFORDANCE_ID,
affordanceId
)
}
)
}
@@ -382,7 +401,7 @@ class KeyguardQuickAffordanceProviderTest : SysuiTestCase() {
private fun querySelections(): List<Selection> {
return context.contentResolver
.query(
Contract.SelectionTable.URI,
Contract.LockScreenQuickAffordances.SelectionTable.URI,
null,
null,
null,
@@ -391,11 +410,18 @@ class KeyguardQuickAffordanceProviderTest : SysuiTestCase() {
?.use { cursor ->
buildList {
val slotIdColumnIndex =
cursor.getColumnIndex(Contract.SelectionTable.Columns.SLOT_ID)
cursor.getColumnIndex(
Contract.LockScreenQuickAffordances.SelectionTable.Columns.SLOT_ID
)
val affordanceIdColumnIndex =
cursor.getColumnIndex(Contract.SelectionTable.Columns.AFFORDANCE_ID)
cursor.getColumnIndex(
Contract.LockScreenQuickAffordances.SelectionTable.Columns.AFFORDANCE_ID
)
val affordanceNameColumnIndex =
cursor.getColumnIndex(Contract.SelectionTable.Columns.AFFORDANCE_NAME)
cursor.getColumnIndex(
Contract.LockScreenQuickAffordances.SelectionTable.Columns
.AFFORDANCE_NAME
)
if (
slotIdColumnIndex == -1 ||
affordanceIdColumnIndex == -1 ||
@@ -421,7 +447,7 @@ class KeyguardQuickAffordanceProviderTest : SysuiTestCase() {
private fun querySlots(): List<Slot> {
return context.contentResolver
.query(
Contract.SlotTable.URI,
Contract.LockScreenQuickAffordances.SlotTable.URI,
null,
null,
null,
@@ -429,9 +455,14 @@ class KeyguardQuickAffordanceProviderTest : SysuiTestCase() {
)
?.use { cursor ->
buildList {
val idColumnIndex = cursor.getColumnIndex(Contract.SlotTable.Columns.ID)
val idColumnIndex =
cursor.getColumnIndex(
Contract.LockScreenQuickAffordances.SlotTable.Columns.ID
)
val capacityColumnIndex =
cursor.getColumnIndex(Contract.SlotTable.Columns.CAPACITY)
cursor.getColumnIndex(
Contract.LockScreenQuickAffordances.SlotTable.Columns.CAPACITY
)
if (idColumnIndex == -1 || capacityColumnIndex == -1) {
return@buildList
}
@@ -452,7 +483,7 @@ class KeyguardQuickAffordanceProviderTest : SysuiTestCase() {
private fun queryAffordances(): List<Affordance> {
return context.contentResolver
.query(
Contract.AffordanceTable.URI,
Contract.LockScreenQuickAffordances.AffordanceTable.URI,
null,
null,
null,
@@ -460,11 +491,18 @@ class KeyguardQuickAffordanceProviderTest : SysuiTestCase() {
)
?.use { cursor ->
buildList {
val idColumnIndex = cursor.getColumnIndex(Contract.AffordanceTable.Columns.ID)
val idColumnIndex =
cursor.getColumnIndex(
Contract.LockScreenQuickAffordances.AffordanceTable.Columns.ID
)
val nameColumnIndex =
cursor.getColumnIndex(Contract.AffordanceTable.Columns.NAME)
cursor.getColumnIndex(
Contract.LockScreenQuickAffordances.AffordanceTable.Columns.NAME
)
val iconColumnIndex =
cursor.getColumnIndex(Contract.AffordanceTable.Columns.ICON)
cursor.getColumnIndex(
Contract.LockScreenQuickAffordances.AffordanceTable.Columns.ICON
)
if (idColumnIndex == -1 || nameColumnIndex == -1 || iconColumnIndex == -1) {
return@buildList
}

View File

@@ -22,8 +22,8 @@ import android.os.UserHandle
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.settings.FakeUserTracker
import com.android.systemui.shared.customization.data.content.FakeCustomizationProviderClient
import com.android.systemui.shared.keyguard.shared.model.KeyguardQuickAffordanceSlots
import com.android.systemui.shared.quickaffordance.data.content.FakeKeyguardQuickAffordanceProviderClient
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -54,16 +54,16 @@ class KeyguardQuickAffordanceRemoteUserSelectionManagerTest : SysuiTestCase() {
private lateinit var testScope: TestScope
private lateinit var testDispatcher: TestDispatcher
private lateinit var userTracker: FakeUserTracker
private lateinit var client1: FakeKeyguardQuickAffordanceProviderClient
private lateinit var client2: FakeKeyguardQuickAffordanceProviderClient
private lateinit var client1: FakeCustomizationProviderClient
private lateinit var client2: FakeCustomizationProviderClient
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
whenever(userHandle.identifier).thenReturn(UserHandle.USER_SYSTEM)
whenever(userHandle.isSystem).thenReturn(true)
client1 = FakeKeyguardQuickAffordanceProviderClient()
client2 = FakeKeyguardQuickAffordanceProviderClient()
client1 = FakeCustomizationProviderClient()
client2 = FakeCustomizationProviderClient()
userTracker = FakeUserTracker()
userTracker.set(
@@ -122,11 +122,11 @@ class KeyguardQuickAffordanceRemoteUserSelectionManagerTest : SysuiTestCase() {
client1.insertSelection(
slotId = KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START,
affordanceId = FakeKeyguardQuickAffordanceProviderClient.AFFORDANCE_1,
affordanceId = FakeCustomizationProviderClient.AFFORDANCE_1,
)
client2.insertSelection(
slotId = KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_END,
affordanceId = FakeKeyguardQuickAffordanceProviderClient.AFFORDANCE_2,
affordanceId = FakeCustomizationProviderClient.AFFORDANCE_2,
)
userTracker.set(
@@ -139,7 +139,7 @@ class KeyguardQuickAffordanceRemoteUserSelectionManagerTest : SysuiTestCase() {
mapOf(
KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START to
listOf(
FakeKeyguardQuickAffordanceProviderClient.AFFORDANCE_1,
FakeCustomizationProviderClient.AFFORDANCE_1,
),
)
)
@@ -154,7 +154,7 @@ class KeyguardQuickAffordanceRemoteUserSelectionManagerTest : SysuiTestCase() {
mapOf(
KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_END to
listOf(
FakeKeyguardQuickAffordanceProviderClient.AFFORDANCE_2,
FakeCustomizationProviderClient.AFFORDANCE_2,
),
)
)
@@ -174,7 +174,7 @@ class KeyguardQuickAffordanceRemoteUserSelectionManagerTest : SysuiTestCase() {
client1.insertSelection(
slotId = KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START,
affordanceId = FakeKeyguardQuickAffordanceProviderClient.AFFORDANCE_1,
affordanceId = FakeCustomizationProviderClient.AFFORDANCE_1,
)
userTracker.set(
userInfos = userTracker.userProfiles,
@@ -197,7 +197,7 @@ class KeyguardQuickAffordanceRemoteUserSelectionManagerTest : SysuiTestCase() {
underTest.setSelections(
slotId = KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START,
affordanceIds = listOf(FakeKeyguardQuickAffordanceProviderClient.AFFORDANCE_1),
affordanceIds = listOf(FakeCustomizationProviderClient.AFFORDANCE_1),
)
runCurrent()
@@ -206,7 +206,7 @@ class KeyguardQuickAffordanceRemoteUserSelectionManagerTest : SysuiTestCase() {
mapOf(
KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START to
listOf(
FakeKeyguardQuickAffordanceProviderClient.AFFORDANCE_1,
FakeCustomizationProviderClient.AFFORDANCE_1,
),
)
)

View File

@@ -32,8 +32,8 @@ import com.android.systemui.keyguard.shared.model.KeyguardQuickAffordancePickerR
import com.android.systemui.keyguard.shared.model.KeyguardSlotPickerRepresentation
import com.android.systemui.settings.FakeUserTracker
import com.android.systemui.settings.UserFileManager
import com.android.systemui.shared.customization.data.content.FakeCustomizationProviderClient
import com.android.systemui.shared.keyguard.shared.model.KeyguardQuickAffordanceSlots
import com.android.systemui.shared.quickaffordance.data.content.FakeKeyguardQuickAffordanceProviderClient
import com.android.systemui.util.FakeSharedPreferences
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
@@ -63,19 +63,13 @@ class KeyguardQuickAffordanceRepositoryTest : SysuiTestCase() {
private lateinit var config1: FakeKeyguardQuickAffordanceConfig
private lateinit var config2: FakeKeyguardQuickAffordanceConfig
private lateinit var userTracker: FakeUserTracker
private lateinit var client1: FakeKeyguardQuickAffordanceProviderClient
private lateinit var client2: FakeKeyguardQuickAffordanceProviderClient
private lateinit var client1: FakeCustomizationProviderClient
private lateinit var client2: FakeCustomizationProviderClient
@Before
fun setUp() {
config1 =
FakeKeyguardQuickAffordanceConfig(
FakeKeyguardQuickAffordanceProviderClient.AFFORDANCE_1
)
config2 =
FakeKeyguardQuickAffordanceConfig(
FakeKeyguardQuickAffordanceProviderClient.AFFORDANCE_2
)
config1 = FakeKeyguardQuickAffordanceConfig(FakeCustomizationProviderClient.AFFORDANCE_1)
config2 = FakeKeyguardQuickAffordanceConfig(FakeCustomizationProviderClient.AFFORDANCE_2)
val scope = CoroutineScope(IMMEDIATE)
userTracker = FakeUserTracker()
val localUserSelectionManager =
@@ -95,8 +89,8 @@ class KeyguardQuickAffordanceRepositoryTest : SysuiTestCase() {
userTracker = userTracker,
broadcastDispatcher = fakeBroadcastDispatcher,
)
client1 = FakeKeyguardQuickAffordanceProviderClient()
client2 = FakeKeyguardQuickAffordanceProviderClient()
client1 = FakeCustomizationProviderClient()
client2 = FakeCustomizationProviderClient()
val remoteUserSelectionManager =
KeyguardQuickAffordanceRemoteUserSelectionManager(
scope = scope,
@@ -256,7 +250,7 @@ class KeyguardQuickAffordanceRepositoryTest : SysuiTestCase() {
)
client2.insertSelection(
slotId = KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START,
affordanceId = FakeKeyguardQuickAffordanceProviderClient.AFFORDANCE_2,
affordanceId = FakeCustomizationProviderClient.AFFORDANCE_2,
)
val observed = mutableListOf<Map<String, List<KeyguardQuickAffordanceConfig>>>()
val job = underTest.selections.onEach { observed.add(it) }.launchIn(this)

View File

@@ -18,17 +18,17 @@
package com.android.systemui.keyguard.data.quickaffordance
import com.android.systemui.settings.UserTracker
import com.android.systemui.shared.quickaffordance.data.content.FakeKeyguardQuickAffordanceProviderClient
import com.android.systemui.shared.quickaffordance.data.content.KeyguardQuickAffordanceProviderClient
import com.android.systemui.shared.customization.data.content.CustomizationProviderClient
import com.android.systemui.shared.customization.data.content.FakeCustomizationProviderClient
class FakeKeyguardQuickAffordanceProviderClientFactory(
private val userTracker: UserTracker,
private val callback: (Int) -> KeyguardQuickAffordanceProviderClient = {
FakeKeyguardQuickAffordanceProviderClient()
private val callback: (Int) -> CustomizationProviderClient = {
FakeCustomizationProviderClient()
},
) : KeyguardQuickAffordanceProviderClientFactory {
override fun create(): KeyguardQuickAffordanceProviderClient {
override fun create(): CustomizationProviderClient {
return callback(userTracker.userId)
}
}