Loading packages/SystemUI/src/com/android/systemui/qs/external/CustomTileStatePersister.kt +31 −20 Original line number Diff line number Diff line Loading @@ -18,17 +18,19 @@ package com.android.systemui.qs.external import android.content.ComponentName import android.content.Context import android.content.SharedPreferences import android.service.quicksettings.Tile import android.util.Log import com.android.internal.annotations.VisibleForTesting import javax.inject.Inject import org.json.JSONException import org.json.JSONObject import javax.inject.Inject data class TileServiceKey(val componentName: ComponentName, val user: Int) { private val string = "${componentName.flattenToString()}:$user" override fun toString() = string } private const val STATE = "state" private const val LABEL = "label" private const val SUBTITLE = "subtitle" Loading @@ -44,12 +46,7 @@ private const val STATE_DESCRIPTION = "state_description" * It persists the state from a [Tile] necessary to present the view in the same state when * retrieved, with the exception of the icon. */ class CustomTileStatePersister @Inject constructor(context: Context) { companion object { private const val FILE_NAME = "custom_tiles_state" } private val sharedPreferences = context.getSharedPreferences(FILE_NAME, 0) interface CustomTileStatePersister { /** * Read the state from [SharedPreferences]. Loading @@ -58,7 +55,31 @@ class CustomTileStatePersister @Inject constructor(context: Context) { * * Any fields that have not been saved will be set to `null` */ fun readState(key: TileServiceKey): Tile? { fun readState(key: TileServiceKey): Tile? /** * Persists the state into [SharedPreferences]. * * The implementation does not store fields that are `null` or icons. */ fun persistState(key: TileServiceKey, tile: Tile) /** * Removes the state for a given tile, user pair. * * Used when the tile is removed by the user. */ fun removeState(key: TileServiceKey) } // TODO(b/299909989) Merge this class into into CustomTileRepository class CustomTileStatePersisterImpl @Inject constructor(context: Context) : CustomTileStatePersister { companion object { private const val FILE_NAME = "custom_tiles_state" } private val sharedPreferences: SharedPreferences = context.getSharedPreferences(FILE_NAME, 0) override fun readState(key: TileServiceKey): Tile? { val state = sharedPreferences.getString(key.toString(), null) ?: return null return try { readTileFromString(state) Loading @@ -68,23 +89,13 @@ class CustomTileStatePersister @Inject constructor(context: Context) { } } /** * Persists the state into [SharedPreferences]. * * The implementation does not store fields that are `null` or icons. */ fun persistState(key: TileServiceKey, tile: Tile) { override fun persistState(key: TileServiceKey, tile: Tile) { val state = writeToString(tile) sharedPreferences.edit().putString(key.toString(), state).apply() } /** * Removes the state for a given tile, user pair. * * Used when the tile is removed by the user. */ fun removeState(key: TileServiceKey) { override fun removeState(key: TileServiceKey) { sharedPreferences.edit().remove(key.toString()).apply() } } Loading packages/SystemUI/src/com/android/systemui/qs/tiles/di/QSTilesModule.kt +5 −0 Original line number Diff line number Diff line Loading @@ -16,6 +16,8 @@ package com.android.systemui.qs.tiles.di import com.android.systemui.qs.external.CustomTileStatePersister import com.android.systemui.qs.external.CustomTileStatePersisterImpl import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandler import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandlerImpl import com.android.systemui.qs.tiles.impl.custom.di.CustomTileComponent Loading Loading @@ -52,4 +54,7 @@ interface QSTilesModule { fun bindQSTileIntentUserInputHandler( impl: QSTileIntentUserInputHandlerImpl ): QSTileIntentUserInputHandler @Binds fun bindCustomTileStatePersister(impl: CustomTileStatePersisterImpl): CustomTileStatePersister } packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/commons/TileExt.kt 0 → 100644 +50 −0 Original line number Diff line number Diff line /* * Copyright (C) 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.systemui.qs.tiles.impl.custom.commons import android.service.quicksettings.Tile fun Tile.copy(): Tile = Tile().also { it.icon = icon it.label = label it.subtitle = subtitle it.contentDescription = contentDescription it.stateDescription = stateDescription it.activityLaunchForClick = activityLaunchForClick it.state = state } fun Tile.setFrom(otherTile: Tile) { if (otherTile.icon != null) { icon = otherTile.icon } if (otherTile.customLabel != null) { label = otherTile.customLabel } if (otherTile.subtitle != null) { subtitle = otherTile.subtitle } if (otherTile.contentDescription != null) { contentDescription = otherTile.contentDescription } if (otherTile.stateDescription != null) { stateDescription = otherTile.stateDescription } activityLaunchForClick = otherTile.activityLaunchForClick state = otherTile.state } packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/data/repository/CustomTileRepository.kt 0 → 100644 +196 −0 Original line number Diff line number Diff line /* * Copyright (C) 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.systemui.qs.tiles.impl.custom.data.repository import android.graphics.drawable.Icon import android.os.UserHandle import android.service.quicksettings.Tile import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.qs.external.CustomTileStatePersister import com.android.systemui.qs.external.TileServiceKey import com.android.systemui.qs.pipeline.shared.TileSpec import com.android.systemui.qs.tiles.impl.custom.commons.copy import com.android.systemui.qs.tiles.impl.custom.commons.setFrom import com.android.systemui.qs.tiles.impl.custom.data.entity.CustomTileDefaults import com.android.systemui.qs.tiles.impl.di.QSTileScope import javax.inject.Inject import kotlin.coroutines.CoroutineContext import kotlinx.coroutines.channels.BufferOverflow import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.map import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock import kotlinx.coroutines.withContext /** * Repository store the [Tile] associated with the custom tile. It lives on [QSTileScope] which * allows it to survive service rebinding. Given that, it provides the last received state when * connected again. */ interface CustomTileRepository { /** * Restores the [Tile] if it's [isPersistable]. Restored [Tile] will be available via [getTile] * (but there is no guarantee that restoration is synchronous) and emitted in [getTiles] for a * corresponding [user]. */ suspend fun restoreForTheUserIfNeeded(user: UserHandle, isPersistable: Boolean) /** Returns [Tile] updates for a [user]. */ fun getTiles(user: UserHandle): Flow<Tile> /** * Return current [Tile] for a [user] or null if the [user] doesn't match currently cached one. * Suspending until [getTiles] returns something is a way to wait for this to become available. * * @throws IllegalStateException when there is no current tile. */ fun getTile(user: UserHandle): Tile? /** * Updates tile with the non-null values from [newTile]. Overwrites the current cache when * [user] differs from the cached one. [isPersistable] tile will be persisted to be possibly * loaded when the [restoreForTheUserIfNeeded]. */ suspend fun updateWithTile( user: UserHandle, newTile: Tile, isPersistable: Boolean, ) /** * Updates tile with the values from [defaults]. Overwrites the current cache when [user] * differs from the cached one. [isPersistable] tile will be persisted to be possibly loaded * when the [restoreForTheUserIfNeeded]. */ suspend fun updateWithDefaults( user: UserHandle, defaults: CustomTileDefaults, isPersistable: Boolean, ) } @QSTileScope class CustomTileRepositoryImpl @Inject constructor( private val tileSpec: TileSpec.CustomTileSpec, private val customTileStatePersister: CustomTileStatePersister, @Background private val backgroundContext: CoroutineContext, ) : CustomTileRepository { private val tileUpdateMutex = Mutex() private val tileWithUserState = MutableSharedFlow<TileWithUser>(onBufferOverflow = BufferOverflow.DROP_OLDEST, replay = 1) override suspend fun restoreForTheUserIfNeeded(user: UserHandle, isPersistable: Boolean) { if (isPersistable && getCurrentTileWithUser()?.user != user) { withContext(backgroundContext) { customTileStatePersister.readState(user.getKey())?.let { updateWithTile( user, it, true, ) } } } } override fun getTiles(user: UserHandle): Flow<Tile> = tileWithUserState.filter { it.user == user }.map { it.tile } override fun getTile(user: UserHandle): Tile? { val tileWithUser = getCurrentTileWithUser() ?: throw IllegalStateException("Tile is not set") return if (tileWithUser.user == user) { tileWithUser.tile } else { null } } override suspend fun updateWithTile( user: UserHandle, newTile: Tile, isPersistable: Boolean, ) = updateTile(user, isPersistable) { setFrom(newTile) } override suspend fun updateWithDefaults( user: UserHandle, defaults: CustomTileDefaults, isPersistable: Boolean, ) { if (defaults is CustomTileDefaults.Result) { updateTile(user, isPersistable) { // Update the icon if it's not set or is the default icon. val updateIcon = (icon == null || icon.isResourceEqual(defaults.icon)) if (updateIcon) { icon = defaults.icon } setDefaultLabel(defaults.label) } } } private suspend fun updateTile( user: UserHandle, isPersistable: Boolean, update: Tile.() -> Unit ): Unit = tileUpdateMutex.withLock { val currentTileWithUser = getCurrentTileWithUser() val tileToUpdate = if (currentTileWithUser?.user == user) { currentTileWithUser.tile.copy() } else { Tile() } tileToUpdate.update() if (isPersistable) { withContext(backgroundContext) { customTileStatePersister.persistState(user.getKey(), tileToUpdate) } } tileWithUserState.tryEmit(TileWithUser(user, tileToUpdate)) } private fun getCurrentTileWithUser(): TileWithUser? = tileWithUserState.replayCache.lastOrNull() /** Compare two icons, only works for resources. */ private fun Icon.isResourceEqual(icon2: Icon?): Boolean { if (icon2 == null) { return false } if (this === icon2) { return true } if (type != Icon.TYPE_RESOURCE || icon2.type != Icon.TYPE_RESOURCE) { return false } if (resId != icon2.resId) { return false } return resPackage == icon2.resPackage } private fun UserHandle.getKey() = TileServiceKey(tileSpec.componentName, this.identifier) private data class TileWithUser(val user: UserHandle, val tile: Tile) } packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/CustomTileModule.kt +4 −0 Original line number Diff line number Diff line Loading @@ -24,6 +24,8 @@ import com.android.systemui.qs.tiles.impl.custom.CustomTileMapper import com.android.systemui.qs.tiles.impl.custom.CustomTileUserActionInteractor import com.android.systemui.qs.tiles.impl.custom.data.repository.CustomTileDefaultsRepository import com.android.systemui.qs.tiles.impl.custom.data.repository.CustomTileDefaultsRepositoryImpl import com.android.systemui.qs.tiles.impl.custom.data.repository.CustomTileRepository import com.android.systemui.qs.tiles.impl.custom.data.repository.CustomTileRepositoryImpl import com.android.systemui.qs.tiles.impl.custom.di.bound.CustomTileBoundComponent import com.android.systemui.qs.tiles.impl.custom.domain.entity.CustomTileDataModel import dagger.Binds Loading @@ -50,4 +52,6 @@ interface CustomTileModule { fun bindCustomTileDefaultsRepository( impl: CustomTileDefaultsRepositoryImpl ): CustomTileDefaultsRepository @Binds fun bindCustomTileRepository(impl: CustomTileRepositoryImpl): CustomTileRepository } Loading
packages/SystemUI/src/com/android/systemui/qs/external/CustomTileStatePersister.kt +31 −20 Original line number Diff line number Diff line Loading @@ -18,17 +18,19 @@ package com.android.systemui.qs.external import android.content.ComponentName import android.content.Context import android.content.SharedPreferences import android.service.quicksettings.Tile import android.util.Log import com.android.internal.annotations.VisibleForTesting import javax.inject.Inject import org.json.JSONException import org.json.JSONObject import javax.inject.Inject data class TileServiceKey(val componentName: ComponentName, val user: Int) { private val string = "${componentName.flattenToString()}:$user" override fun toString() = string } private const val STATE = "state" private const val LABEL = "label" private const val SUBTITLE = "subtitle" Loading @@ -44,12 +46,7 @@ private const val STATE_DESCRIPTION = "state_description" * It persists the state from a [Tile] necessary to present the view in the same state when * retrieved, with the exception of the icon. */ class CustomTileStatePersister @Inject constructor(context: Context) { companion object { private const val FILE_NAME = "custom_tiles_state" } private val sharedPreferences = context.getSharedPreferences(FILE_NAME, 0) interface CustomTileStatePersister { /** * Read the state from [SharedPreferences]. Loading @@ -58,7 +55,31 @@ class CustomTileStatePersister @Inject constructor(context: Context) { * * Any fields that have not been saved will be set to `null` */ fun readState(key: TileServiceKey): Tile? { fun readState(key: TileServiceKey): Tile? /** * Persists the state into [SharedPreferences]. * * The implementation does not store fields that are `null` or icons. */ fun persistState(key: TileServiceKey, tile: Tile) /** * Removes the state for a given tile, user pair. * * Used when the tile is removed by the user. */ fun removeState(key: TileServiceKey) } // TODO(b/299909989) Merge this class into into CustomTileRepository class CustomTileStatePersisterImpl @Inject constructor(context: Context) : CustomTileStatePersister { companion object { private const val FILE_NAME = "custom_tiles_state" } private val sharedPreferences: SharedPreferences = context.getSharedPreferences(FILE_NAME, 0) override fun readState(key: TileServiceKey): Tile? { val state = sharedPreferences.getString(key.toString(), null) ?: return null return try { readTileFromString(state) Loading @@ -68,23 +89,13 @@ class CustomTileStatePersister @Inject constructor(context: Context) { } } /** * Persists the state into [SharedPreferences]. * * The implementation does not store fields that are `null` or icons. */ fun persistState(key: TileServiceKey, tile: Tile) { override fun persistState(key: TileServiceKey, tile: Tile) { val state = writeToString(tile) sharedPreferences.edit().putString(key.toString(), state).apply() } /** * Removes the state for a given tile, user pair. * * Used when the tile is removed by the user. */ fun removeState(key: TileServiceKey) { override fun removeState(key: TileServiceKey) { sharedPreferences.edit().remove(key.toString()).apply() } } Loading
packages/SystemUI/src/com/android/systemui/qs/tiles/di/QSTilesModule.kt +5 −0 Original line number Diff line number Diff line Loading @@ -16,6 +16,8 @@ package com.android.systemui.qs.tiles.di import com.android.systemui.qs.external.CustomTileStatePersister import com.android.systemui.qs.external.CustomTileStatePersisterImpl import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandler import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandlerImpl import com.android.systemui.qs.tiles.impl.custom.di.CustomTileComponent Loading Loading @@ -52,4 +54,7 @@ interface QSTilesModule { fun bindQSTileIntentUserInputHandler( impl: QSTileIntentUserInputHandlerImpl ): QSTileIntentUserInputHandler @Binds fun bindCustomTileStatePersister(impl: CustomTileStatePersisterImpl): CustomTileStatePersister }
packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/commons/TileExt.kt 0 → 100644 +50 −0 Original line number Diff line number Diff line /* * Copyright (C) 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.systemui.qs.tiles.impl.custom.commons import android.service.quicksettings.Tile fun Tile.copy(): Tile = Tile().also { it.icon = icon it.label = label it.subtitle = subtitle it.contentDescription = contentDescription it.stateDescription = stateDescription it.activityLaunchForClick = activityLaunchForClick it.state = state } fun Tile.setFrom(otherTile: Tile) { if (otherTile.icon != null) { icon = otherTile.icon } if (otherTile.customLabel != null) { label = otherTile.customLabel } if (otherTile.subtitle != null) { subtitle = otherTile.subtitle } if (otherTile.contentDescription != null) { contentDescription = otherTile.contentDescription } if (otherTile.stateDescription != null) { stateDescription = otherTile.stateDescription } activityLaunchForClick = otherTile.activityLaunchForClick state = otherTile.state }
packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/data/repository/CustomTileRepository.kt 0 → 100644 +196 −0 Original line number Diff line number Diff line /* * Copyright (C) 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.systemui.qs.tiles.impl.custom.data.repository import android.graphics.drawable.Icon import android.os.UserHandle import android.service.quicksettings.Tile import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.qs.external.CustomTileStatePersister import com.android.systemui.qs.external.TileServiceKey import com.android.systemui.qs.pipeline.shared.TileSpec import com.android.systemui.qs.tiles.impl.custom.commons.copy import com.android.systemui.qs.tiles.impl.custom.commons.setFrom import com.android.systemui.qs.tiles.impl.custom.data.entity.CustomTileDefaults import com.android.systemui.qs.tiles.impl.di.QSTileScope import javax.inject.Inject import kotlin.coroutines.CoroutineContext import kotlinx.coroutines.channels.BufferOverflow import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.map import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock import kotlinx.coroutines.withContext /** * Repository store the [Tile] associated with the custom tile. It lives on [QSTileScope] which * allows it to survive service rebinding. Given that, it provides the last received state when * connected again. */ interface CustomTileRepository { /** * Restores the [Tile] if it's [isPersistable]. Restored [Tile] will be available via [getTile] * (but there is no guarantee that restoration is synchronous) and emitted in [getTiles] for a * corresponding [user]. */ suspend fun restoreForTheUserIfNeeded(user: UserHandle, isPersistable: Boolean) /** Returns [Tile] updates for a [user]. */ fun getTiles(user: UserHandle): Flow<Tile> /** * Return current [Tile] for a [user] or null if the [user] doesn't match currently cached one. * Suspending until [getTiles] returns something is a way to wait for this to become available. * * @throws IllegalStateException when there is no current tile. */ fun getTile(user: UserHandle): Tile? /** * Updates tile with the non-null values from [newTile]. Overwrites the current cache when * [user] differs from the cached one. [isPersistable] tile will be persisted to be possibly * loaded when the [restoreForTheUserIfNeeded]. */ suspend fun updateWithTile( user: UserHandle, newTile: Tile, isPersistable: Boolean, ) /** * Updates tile with the values from [defaults]. Overwrites the current cache when [user] * differs from the cached one. [isPersistable] tile will be persisted to be possibly loaded * when the [restoreForTheUserIfNeeded]. */ suspend fun updateWithDefaults( user: UserHandle, defaults: CustomTileDefaults, isPersistable: Boolean, ) } @QSTileScope class CustomTileRepositoryImpl @Inject constructor( private val tileSpec: TileSpec.CustomTileSpec, private val customTileStatePersister: CustomTileStatePersister, @Background private val backgroundContext: CoroutineContext, ) : CustomTileRepository { private val tileUpdateMutex = Mutex() private val tileWithUserState = MutableSharedFlow<TileWithUser>(onBufferOverflow = BufferOverflow.DROP_OLDEST, replay = 1) override suspend fun restoreForTheUserIfNeeded(user: UserHandle, isPersistable: Boolean) { if (isPersistable && getCurrentTileWithUser()?.user != user) { withContext(backgroundContext) { customTileStatePersister.readState(user.getKey())?.let { updateWithTile( user, it, true, ) } } } } override fun getTiles(user: UserHandle): Flow<Tile> = tileWithUserState.filter { it.user == user }.map { it.tile } override fun getTile(user: UserHandle): Tile? { val tileWithUser = getCurrentTileWithUser() ?: throw IllegalStateException("Tile is not set") return if (tileWithUser.user == user) { tileWithUser.tile } else { null } } override suspend fun updateWithTile( user: UserHandle, newTile: Tile, isPersistable: Boolean, ) = updateTile(user, isPersistable) { setFrom(newTile) } override suspend fun updateWithDefaults( user: UserHandle, defaults: CustomTileDefaults, isPersistable: Boolean, ) { if (defaults is CustomTileDefaults.Result) { updateTile(user, isPersistable) { // Update the icon if it's not set or is the default icon. val updateIcon = (icon == null || icon.isResourceEqual(defaults.icon)) if (updateIcon) { icon = defaults.icon } setDefaultLabel(defaults.label) } } } private suspend fun updateTile( user: UserHandle, isPersistable: Boolean, update: Tile.() -> Unit ): Unit = tileUpdateMutex.withLock { val currentTileWithUser = getCurrentTileWithUser() val tileToUpdate = if (currentTileWithUser?.user == user) { currentTileWithUser.tile.copy() } else { Tile() } tileToUpdate.update() if (isPersistable) { withContext(backgroundContext) { customTileStatePersister.persistState(user.getKey(), tileToUpdate) } } tileWithUserState.tryEmit(TileWithUser(user, tileToUpdate)) } private fun getCurrentTileWithUser(): TileWithUser? = tileWithUserState.replayCache.lastOrNull() /** Compare two icons, only works for resources. */ private fun Icon.isResourceEqual(icon2: Icon?): Boolean { if (icon2 == null) { return false } if (this === icon2) { return true } if (type != Icon.TYPE_RESOURCE || icon2.type != Icon.TYPE_RESOURCE) { return false } if (resId != icon2.resId) { return false } return resPackage == icon2.resPackage } private fun UserHandle.getKey() = TileServiceKey(tileSpec.componentName, this.identifier) private data class TileWithUser(val user: UserHandle, val tile: Tile) }
packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/CustomTileModule.kt +4 −0 Original line number Diff line number Diff line Loading @@ -24,6 +24,8 @@ import com.android.systemui.qs.tiles.impl.custom.CustomTileMapper import com.android.systemui.qs.tiles.impl.custom.CustomTileUserActionInteractor import com.android.systemui.qs.tiles.impl.custom.data.repository.CustomTileDefaultsRepository import com.android.systemui.qs.tiles.impl.custom.data.repository.CustomTileDefaultsRepositoryImpl import com.android.systemui.qs.tiles.impl.custom.data.repository.CustomTileRepository import com.android.systemui.qs.tiles.impl.custom.data.repository.CustomTileRepositoryImpl import com.android.systemui.qs.tiles.impl.custom.di.bound.CustomTileBoundComponent import com.android.systemui.qs.tiles.impl.custom.domain.entity.CustomTileDataModel import dagger.Binds Loading @@ -50,4 +52,6 @@ interface CustomTileModule { fun bindCustomTileDefaultsRepository( impl: CustomTileDefaultsRepositoryImpl ): CustomTileDefaultsRepository @Binds fun bindCustomTileRepository(impl: CustomTileRepositoryImpl): CustomTileRepository }