mirror of
https://github.com/home-assistant/android
synced 2024-10-15 20:43:06 +00:00
Implement support for multiple Shortcut Tiles on Wear OS (#3697)
* Extract JSONArray -> List<String> conversion to extension fun * Refactor navigation around Shortcuts Tile settings * Add ShortcutsTileId enum class * Introduce multiple ShortcutTile subclasses and modify settings UI and storage to support multiple Tiles * Check if correct ShortcutsTileId is passed as parameter to BaseShortcutTile * Update TileUpdateRequester usages to account for multiple Tiles * Add entity count to Shortcut Tile list in Settings * Fix ktlint errors * Fix more ktlint errors * Extract string resource * Add MULTI_INSTANCES_SUPPORTED to ShortcutsTile to be able to use any number of Tiles * Refresh the list of Shortcut Tiles in the Settings without needing to restart the app * Remove test logs * Update androidx.wear.tiles:tiles to the latest version which doesn't yet require API 34 compileSdk version * Fix crash when the preference's value is "{}" * Fix crash when key String is "null" and converting to Int * Rename placeholder variable name * Add a comment explaining why to save the tiles in a getter * Return emptyList() directly for clarity * Remove icons from "Shortcut tile #n" entries in Settings * Pass emptyList instead of using the non-null assertion operator to prevent NPEs in edge cases * Refactor getTileShortcuts and getAllTileShortcuts in WearPrefsRepositoryImpl. Make the code more readable and understandable, and reduce code duplication. * Make it explicit that intKey is only intended to be null if stringKey == "null" * Rename getTileShortcuts to getTileShortcutsAndSaveTileId and make it save the tileId not only when there's a null key in the map, but also when the tileId is not yet in the map. This way, actual Tiles and the tileId keys will be more in sync. * Handle adding Shortcuts Tile immediately after updating the app to a new version which introduces support for multiple Shortcuts Tiles. * Show message in the Settings when there are no Shortcuts tiles added yet. * Refine message in the Settings when there are no Shortcuts tiles added yet * WIP: ConfigShortcutsTile action * Update comments about Wear OS versions * Finalize ConfigShortcutsTile feature by applying @jpelgrom's suggestion to OpenShortcutTileSettingsActivity * Only wrap the code in runCatching which is expected to throw an Exception under normal circumstances, when the pref value needs to be migrated from the old format. * Call getTileShortcutsAndSaveTileId in OpenShortcutTileSettingsActivity * Remove unnecessary stuff
This commit is contained in:
parent
fcab330115
commit
a8a7363317
|
@ -1,8 +1,10 @@
|
||||||
package io.homeassistant.companion.android.common.data.prefs
|
package io.homeassistant.companion.android.common.data.prefs
|
||||||
|
|
||||||
interface WearPrefsRepository {
|
interface WearPrefsRepository {
|
||||||
suspend fun getTileShortcuts(): List<String>
|
suspend fun getAllTileShortcuts(): Map<Int?, List<String>>
|
||||||
suspend fun setTileShortcuts(entities: List<String>)
|
suspend fun getTileShortcutsAndSaveTileId(tileId: Int): List<String>
|
||||||
|
suspend fun setTileShortcuts(tileId: Int?, entities: List<String>)
|
||||||
|
suspend fun removeTileShortcuts(tileId: Int?): List<String>?
|
||||||
suspend fun getShowShortcutText(): Boolean
|
suspend fun getShowShortcutText(): Boolean
|
||||||
suspend fun setShowShortcutTextEnabled(enabled: Boolean)
|
suspend fun setShowShortcutTextEnabled(enabled: Boolean)
|
||||||
suspend fun getTemplateTileContent(): String
|
suspend fun getTemplateTileContent(): String
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
package io.homeassistant.companion.android.common.data.prefs
|
package io.homeassistant.companion.android.common.data.prefs
|
||||||
|
|
||||||
import io.homeassistant.companion.android.common.data.LocalStorage
|
import io.homeassistant.companion.android.common.data.LocalStorage
|
||||||
|
import io.homeassistant.companion.android.common.util.toStringList
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
import org.json.JSONArray
|
import org.json.JSONArray
|
||||||
|
import org.json.JSONObject
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import javax.inject.Named
|
import javax.inject.Named
|
||||||
|
|
||||||
|
@ -52,15 +54,68 @@ class WearPrefsRepositoryImpl @Inject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun getTileShortcuts(): List<String> {
|
override suspend fun getTileShortcutsAndSaveTileId(tileId: Int): List<String> {
|
||||||
val jsonArray = JSONArray(localStorage.getString(PREF_TILE_SHORTCUTS) ?: "[]")
|
val tileIdToShortcutsMap = getAllTileShortcuts()
|
||||||
return List(jsonArray.length()) {
|
return if (null in tileIdToShortcutsMap && tileId !in tileIdToShortcutsMap) {
|
||||||
jsonArray.getString(it)
|
// if there are shortcuts with an unknown (null) tileId key from a previous installation,
|
||||||
|
// and the tileId parameter is not already present in the map, associate it with those shortcuts
|
||||||
|
val entities = removeTileShortcuts(null)!!
|
||||||
|
setTileShortcuts(tileId, entities)
|
||||||
|
entities
|
||||||
|
} else {
|
||||||
|
val entities = tileIdToShortcutsMap[tileId]
|
||||||
|
if (entities == null) {
|
||||||
|
setTileShortcuts(tileId, emptyList())
|
||||||
|
}
|
||||||
|
entities ?: emptyList()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun setTileShortcuts(entities: List<String>) {
|
override suspend fun getAllTileShortcuts(): Map<Int?, List<String>> {
|
||||||
localStorage.putString(PREF_TILE_SHORTCUTS, JSONArray(entities).toString())
|
return localStorage.getString(PREF_TILE_SHORTCUTS)?.let { jsonStr ->
|
||||||
|
runCatching {
|
||||||
|
JSONObject(jsonStr)
|
||||||
|
}.fold(
|
||||||
|
onSuccess = { jsonObject ->
|
||||||
|
buildMap {
|
||||||
|
jsonObject.keys().forEach { stringKey ->
|
||||||
|
val intKey = stringKey.takeUnless { it == "null" }?.toInt()
|
||||||
|
val jsonArray = jsonObject.getJSONArray(stringKey)
|
||||||
|
val entities = jsonArray.toStringList()
|
||||||
|
put(intKey, entities)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onFailure = {
|
||||||
|
// backward compatibility with the previous format when there was only one Shortcut Tile:
|
||||||
|
val jsonArray = JSONArray(jsonStr)
|
||||||
|
val entities = jsonArray.toStringList()
|
||||||
|
mapOf(
|
||||||
|
null to entities // the key is null since we don't (yet) have the tileId
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
} ?: emptyMap()
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun setTileShortcuts(tileId: Int?, entities: List<String>) {
|
||||||
|
val map = getAllTileShortcuts() + mapOf(tileId to entities)
|
||||||
|
setTileShortcuts(map)
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun setTileShortcuts(map: Map<Int?, List<String>>) {
|
||||||
|
val jsonArrayMap = map.map { (tileId, entities) ->
|
||||||
|
tileId.toString() to JSONArray(entities)
|
||||||
|
}.toMap()
|
||||||
|
val jsonStr = JSONObject(jsonArrayMap).toString()
|
||||||
|
localStorage.putString(PREF_TILE_SHORTCUTS, jsonStr)
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun removeTileShortcuts(tileId: Int?): List<String>? {
|
||||||
|
val tileShortcutsMap = getAllTileShortcuts().toMutableMap()
|
||||||
|
val entities = tileShortcutsMap.remove(tileId)
|
||||||
|
setTileShortcuts(tileShortcutsMap)
|
||||||
|
return entities
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun getTemplateTileContent(): String {
|
override suspend fun getTemplateTileContent(): String {
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
package io.homeassistant.companion.android.common.util
|
||||||
|
|
||||||
|
import org.json.JSONArray
|
||||||
|
|
||||||
|
fun JSONArray.toStringList(): List<String> =
|
||||||
|
List(length()) { i ->
|
||||||
|
getString(i)
|
||||||
|
}
|
|
@ -217,6 +217,10 @@
|
||||||
<item quantity="one">%d entity found</item>
|
<item quantity="one">%d entity found</item>
|
||||||
<item quantity="other">%d entities found</item>
|
<item quantity="other">%d entities found</item>
|
||||||
</plurals>
|
</plurals>
|
||||||
|
<plurals name="n_entities">
|
||||||
|
<item quantity="one">1 entity</item>
|
||||||
|
<item quantity="other">%d entities</item>
|
||||||
|
</plurals>
|
||||||
<string name="entity_attribute_checkbox">Append Attribute Value</string>
|
<string name="entity_attribute_checkbox">Append Attribute Value</string>
|
||||||
<string name="entity_id">Entity ID</string>
|
<string name="entity_id">Entity ID</string>
|
||||||
<string name="entity_id_name">Entity ID: %1$s</string>
|
<string name="entity_id_name">Entity ID: %1$s</string>
|
||||||
|
@ -732,11 +736,14 @@
|
||||||
<string name="shortcuts_choose">Choose shortcuts</string>
|
<string name="shortcuts_choose">Choose shortcuts</string>
|
||||||
<string name="shortcut5_note">Not all launchers support displaying 5 shortcuts, you may not see this shortcut if the above 4 are already added.</string>
|
<string name="shortcut5_note">Not all launchers support displaying 5 shortcuts, you may not see this shortcut if the above 4 are already added.</string>
|
||||||
<string name="shortcuts_summary">Add shortcuts to launcher</string>
|
<string name="shortcuts_summary">Add shortcuts to launcher</string>
|
||||||
<string name="shortcuts_tile">Shortcuts tile</string>
|
<string name="shortcut_tiles">Shortcut tiles</string>
|
||||||
|
<string name="shortcuts_tile_n">Shortcuts tile #%d</string>
|
||||||
|
<string name="shortcuts_tile_select">Select shortcuts tile to manage</string>
|
||||||
<string name="shortcuts_tile_description">Select up to 7 entities</string>
|
<string name="shortcuts_tile_description">Select up to 7 entities</string>
|
||||||
<string name="shortcuts_tile_empty">Choose entities in settings</string>
|
<string name="shortcuts_tile_empty">Choose entities in settings</string>
|
||||||
<string name="shortcuts_tile_text_setting">Show names on shortcuts tile</string>
|
<string name="shortcuts_tile_text_setting">Show names on shortcuts tile</string>
|
||||||
<string name="shortcuts_tile_log_in">Log in to Home Assistant to add your first shortcut</string>
|
<string name="shortcuts_tile_log_in">Log in to Home Assistant to add your first shortcut</string>
|
||||||
|
<string name="shortcuts_tile_no_tiles_yet">There are no Shortcut tiles added yet</string>
|
||||||
<string name="shortcuts">Shortcuts</string>
|
<string name="shortcuts">Shortcuts</string>
|
||||||
<string name="show">Show</string>
|
<string name="show">Show</string>
|
||||||
<string name="show_share_logs_summary">Sharing logs with the Home Assistant team will help to solve issues. Please share the logs only if you have been asked to do so by a Home Assistant developer</string>
|
<string name="show_share_logs_summary">Sharing logs with the Home Assistant team will help to solve issues. Please share the logs only if you have been asked to do so by a Home Assistant developer</string>
|
||||||
|
|
|
@ -50,7 +50,7 @@ sentry-android = "6.26.0"
|
||||||
watchfaceComplicationsDataSourceKtx = "1.1.1"
|
watchfaceComplicationsDataSourceKtx = "1.1.1"
|
||||||
wear = "1.2.0"
|
wear = "1.2.0"
|
||||||
wear-compose-foundation = "1.1.2"
|
wear-compose-foundation = "1.1.2"
|
||||||
wear-tiles = "1.1.0"
|
wear-tiles = "1.2.0-alpha07"
|
||||||
wearPhoneInteractions = "1.0.1"
|
wearPhoneInteractions = "1.0.1"
|
||||||
wearInput = "1.2.0-alpha02"
|
wearInput = "1.2.0-alpha02"
|
||||||
webkit = "1.7.0"
|
webkit = "1.7.0"
|
||||||
|
|
|
@ -83,6 +83,17 @@
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
|
<activity
|
||||||
|
android:name=".tiles.OpenShortcutTileSettingsActivity"
|
||||||
|
android:exported="true"
|
||||||
|
android:excludeFromRecents="true">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="ConfigShortcutsTile" />
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
<category android:name="com.google.android.clockwork.tiles.category.PROVIDER_CONFIG" />
|
||||||
|
</intent-filter>
|
||||||
|
</activity>
|
||||||
|
|
||||||
<!-- To show confirmations and failures -->
|
<!-- To show confirmations and failures -->
|
||||||
<activity android:name="androidx.wear.activity.ConfirmationActivity" />
|
<activity android:name="androidx.wear.activity.ConfirmationActivity" />
|
||||||
|
|
||||||
|
@ -122,6 +133,14 @@
|
||||||
|
|
||||||
<meta-data android:name="androidx.wear.tiles.PREVIEW"
|
<meta-data android:name="androidx.wear.tiles.PREVIEW"
|
||||||
android:resource="@drawable/favorite_entities_tile_example" />
|
android:resource="@drawable/favorite_entities_tile_example" />
|
||||||
|
|
||||||
|
<meta-data
|
||||||
|
android:name="com.google.android.clockwork.tiles.MULTI_INSTANCES_SUPPORTED"
|
||||||
|
android:value="true" /> <!-- This is supported starting from Wear OS 3 -->
|
||||||
|
|
||||||
|
<meta-data
|
||||||
|
android:name="com.google.android.clockwork.tiles.PROVIDER_CONFIG_ACTION"
|
||||||
|
android:value="ConfigShortcutsTile" />
|
||||||
</service>
|
</service>
|
||||||
<service
|
<service
|
||||||
android:name=".tiles.TemplateTile"
|
android:name=".tiles.TemplateTile"
|
||||||
|
|
|
@ -6,10 +6,12 @@ import android.os.Bundle
|
||||||
import androidx.activity.ComponentActivity
|
import androidx.activity.ComponentActivity
|
||||||
import androidx.activity.compose.setContent
|
import androidx.activity.compose.setContent
|
||||||
import androidx.activity.viewModels
|
import androidx.activity.viewModels
|
||||||
|
import androidx.core.net.toUri
|
||||||
import androidx.lifecycle.Lifecycle
|
import androidx.lifecycle.Lifecycle
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.lifecycle.repeatOnLifecycle
|
import androidx.lifecycle.repeatOnLifecycle
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
|
import io.homeassistant.companion.android.home.views.DEEPLINK_PREFIX_SET_SHORTCUT_TILE
|
||||||
import io.homeassistant.companion.android.home.views.LoadHomePage
|
import io.homeassistant.companion.android.home.views.LoadHomePage
|
||||||
import io.homeassistant.companion.android.onboarding.OnboardingActivity
|
import io.homeassistant.companion.android.onboarding.OnboardingActivity
|
||||||
import io.homeassistant.companion.android.sensors.SensorReceiver
|
import io.homeassistant.companion.android.sensors.SensorReceiver
|
||||||
|
@ -34,6 +36,16 @@ class HomeActivity : ComponentActivity(), HomeView {
|
||||||
fun newInstance(context: Context): Intent {
|
fun newInstance(context: Context): Intent {
|
||||||
return Intent(context, HomeActivity::class.java)
|
return Intent(context, HomeActivity::class.java)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getShortcutsTileSettingsIntent(
|
||||||
|
context: Context,
|
||||||
|
tileId: Int
|
||||||
|
) = Intent(
|
||||||
|
Intent.ACTION_VIEW,
|
||||||
|
"$DEEPLINK_PREFIX_SET_SHORTCUT_TILE/$tileId".toUri(),
|
||||||
|
context,
|
||||||
|
HomeActivity::class.java
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
|
|
@ -38,8 +38,9 @@ interface HomePresenter {
|
||||||
suspend fun getDeviceRegistryUpdates(): Flow<DeviceRegistryUpdatedEvent>?
|
suspend fun getDeviceRegistryUpdates(): Flow<DeviceRegistryUpdatedEvent>?
|
||||||
suspend fun getEntityRegistryUpdates(): Flow<EntityRegistryUpdatedEvent>?
|
suspend fun getEntityRegistryUpdates(): Flow<EntityRegistryUpdatedEvent>?
|
||||||
|
|
||||||
suspend fun getTileShortcuts(): List<SimplifiedEntity>
|
suspend fun getAllTileShortcuts(): Map<Int?, List<SimplifiedEntity>>
|
||||||
suspend fun setTileShortcuts(entities: List<SimplifiedEntity>)
|
suspend fun getTileShortcuts(tileId: Int): List<SimplifiedEntity>
|
||||||
|
suspend fun setTileShortcuts(tileId: Int?, entities: List<SimplifiedEntity>)
|
||||||
|
|
||||||
suspend fun getWearHapticFeedback(): Boolean
|
suspend fun getWearHapticFeedback(): Boolean
|
||||||
suspend fun setWearHapticFeedback(enabled: Boolean)
|
suspend fun setWearHapticFeedback(enabled: Boolean)
|
||||||
|
|
|
@ -233,12 +233,20 @@ class HomePresenterImpl @Inject constructor(
|
||||||
return serverManager.webSocketRepository().getEntityRegistryUpdates()
|
return serverManager.webSocketRepository().getEntityRegistryUpdates()
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun getTileShortcuts(): List<SimplifiedEntity> {
|
override suspend fun getAllTileShortcuts(): Map<Int?, List<SimplifiedEntity>> {
|
||||||
return wearPrefsRepository.getTileShortcuts().map { SimplifiedEntity(it) }
|
return wearPrefsRepository.getAllTileShortcuts().mapValues { (_, entities) ->
|
||||||
|
entities.map {
|
||||||
|
SimplifiedEntity(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun setTileShortcuts(entities: List<SimplifiedEntity>) {
|
override suspend fun getTileShortcuts(tileId: Int): List<SimplifiedEntity> {
|
||||||
wearPrefsRepository.setTileShortcuts(entities.map { it.entityString })
|
return wearPrefsRepository.getTileShortcutsAndSaveTileId(tileId).map { SimplifiedEntity(it) }
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun setTileShortcuts(tileId: Int?, entities: List<SimplifiedEntity>) {
|
||||||
|
wearPrefsRepository.setTileShortcuts(tileId, entities.map { it.entityString })
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun getWearHapticFeedback(): Boolean {
|
override suspend fun getWearHapticFeedback(): Boolean {
|
||||||
|
|
|
@ -11,6 +11,7 @@ import androidx.compose.runtime.mutableStateMapOf
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.runtime.snapshots.SnapshotStateList
|
import androidx.compose.runtime.snapshots.SnapshotStateList
|
||||||
|
import androidx.compose.runtime.toMutableStateList
|
||||||
import androidx.lifecycle.AndroidViewModel
|
import androidx.lifecycle.AndroidViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
|
@ -85,8 +86,8 @@ class MainViewModel @Inject constructor(
|
||||||
val favoriteEntityIds = favoritesDao.getAllFlow().collectAsState()
|
val favoriteEntityIds = favoritesDao.getAllFlow().collectAsState()
|
||||||
private val favoriteCaches = favoriteCachesDao.getAll()
|
private val favoriteCaches = favoriteCachesDao.getAll()
|
||||||
|
|
||||||
var shortcutEntities = mutableStateListOf<SimplifiedEntity>()
|
val shortcutEntitiesMap = mutableStateMapOf<Int?, SnapshotStateList<SimplifiedEntity>>()
|
||||||
private set
|
|
||||||
var areas = mutableListOf<AreaRegistryResponse>()
|
var areas = mutableListOf<AreaRegistryResponse>()
|
||||||
private set
|
private set
|
||||||
|
|
||||||
|
@ -136,7 +137,7 @@ class MainViewModel @Inject constructor(
|
||||||
if (!homePresenter.isConnected()) {
|
if (!homePresenter.isConnected()) {
|
||||||
return@launch
|
return@launch
|
||||||
}
|
}
|
||||||
shortcutEntities.addAll(homePresenter.getTileShortcuts())
|
loadShortcutTileEntities()
|
||||||
isHapticEnabled.value = homePresenter.getWearHapticFeedback()
|
isHapticEnabled.value = homePresenter.getWearHapticFeedback()
|
||||||
isToastEnabled.value = homePresenter.getWearToastConfirmation()
|
isToastEnabled.value = homePresenter.getWearToastConfirmation()
|
||||||
isShowShortcutTextEnabled.value = homePresenter.getShowShortcutText()
|
isShowShortcutTextEnabled.value = homePresenter.getShowShortcutText()
|
||||||
|
@ -153,6 +154,16 @@ class MainViewModel @Inject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun loadShortcutTileEntities() {
|
||||||
|
viewModelScope.launch {
|
||||||
|
val map = homePresenter.getAllTileShortcuts().mapValues { (_, entities) ->
|
||||||
|
entities.toMutableStateList()
|
||||||
|
}
|
||||||
|
shortcutEntitiesMap.clear()
|
||||||
|
shortcutEntitiesMap.putAll(map)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun loadEntities() {
|
fun loadEntities() {
|
||||||
if (!homePresenter.isConnected()) return
|
if (!homePresenter.isConnected()) return
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
|
@ -401,22 +412,24 @@ class MainViewModel @Inject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setTileShortcut(index: Int, entity: SimplifiedEntity) {
|
fun setTileShortcut(tileId: Int?, index: Int, entity: SimplifiedEntity) {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
|
val shortcutEntities = shortcutEntitiesMap[tileId]!!
|
||||||
if (index < shortcutEntities.size) {
|
if (index < shortcutEntities.size) {
|
||||||
shortcutEntities[index] = entity
|
shortcutEntities[index] = entity
|
||||||
} else {
|
} else {
|
||||||
shortcutEntities.add(entity)
|
shortcutEntities.add(entity)
|
||||||
}
|
}
|
||||||
homePresenter.setTileShortcuts(shortcutEntities)
|
homePresenter.setTileShortcuts(tileId, entities = shortcutEntities)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun clearTileShortcut(index: Int) {
|
fun clearTileShortcut(tileId: Int?, index: Int) {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
|
val shortcutEntities = shortcutEntitiesMap[tileId]!!
|
||||||
if (index < shortcutEntities.size) {
|
if (index < shortcutEntities.size) {
|
||||||
shortcutEntities.removeAt(index)
|
shortcutEntities.removeAt(index)
|
||||||
homePresenter.setTileShortcuts(shortcutEntities)
|
homePresenter.setTileShortcuts(tileId, entities = shortcutEntities)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,6 @@
|
||||||
package io.homeassistant.companion.android.home.views
|
package io.homeassistant.companion.android.home.views
|
||||||
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.getValue
|
|
||||||
import androidx.compose.runtime.mutableStateOf
|
|
||||||
import androidx.compose.runtime.remember
|
|
||||||
import androidx.compose.runtime.setValue
|
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.navigation.NavType
|
import androidx.navigation.NavType
|
||||||
import androidx.navigation.navArgument
|
import androidx.navigation.navArgument
|
||||||
|
@ -21,6 +17,8 @@ import io.homeassistant.companion.android.tiles.TemplateTile
|
||||||
import io.homeassistant.companion.android.views.ChooseEntityView
|
import io.homeassistant.companion.android.views.ChooseEntityView
|
||||||
|
|
||||||
private const val ARG_SCREEN_SENSOR_MANAGER_ID = "sensorManagerId"
|
private const val ARG_SCREEN_SENSOR_MANAGER_ID = "sensorManagerId"
|
||||||
|
private const val ARG_SCREEN_SHORTCUTS_TILE_ID = "shortcutsTileId"
|
||||||
|
private const val ARG_SCREEN_SHORTCUTS_TILE_ENTITY_INDEX = "shortcutsTileEntityIndex"
|
||||||
|
|
||||||
private const val SCREEN_LANDING = "landing"
|
private const val SCREEN_LANDING = "landing"
|
||||||
private const val SCREEN_ENTITY_DETAIL = "entity_detail"
|
private const val SCREEN_ENTITY_DETAIL = "entity_detail"
|
||||||
|
@ -29,18 +27,20 @@ private const val SCREEN_MANAGE_SENSORS = "manage_all_sensors"
|
||||||
private const val SCREEN_SINGLE_SENSOR_MANAGER = "sensor_manager"
|
private const val SCREEN_SINGLE_SENSOR_MANAGER = "sensor_manager"
|
||||||
private const val SCREEN_SETTINGS = "settings"
|
private const val SCREEN_SETTINGS = "settings"
|
||||||
private const val SCREEN_SET_FAVORITES = "set_favorites"
|
private const val SCREEN_SET_FAVORITES = "set_favorites"
|
||||||
private const val SCREEN_SET_TILE_SHORTCUTS = "set_tile_shortcuts"
|
private const val ROUTE_SHORTCUTS_TILE = "shortcuts_tile"
|
||||||
private const val SCREEN_SELECT_TILE_SHORTCUT = "select_tile_shortcut"
|
private const val SCREEN_SELECT_SHORTCUTS_TILE = "select_shortcuts_tile"
|
||||||
|
private const val SCREEN_SET_SHORTCUTS_TILE = "set_shortcuts_tile"
|
||||||
|
private const val SCREEN_SHORTCUTS_TILE_CHOOSE_ENTITY = "shortcuts_tile_choose_entity"
|
||||||
private const val SCREEN_SET_TILE_TEMPLATE = "set_tile_template"
|
private const val SCREEN_SET_TILE_TEMPLATE = "set_tile_template"
|
||||||
private const val SCREEN_SET_TILE_TEMPLATE_REFRESH_INTERVAL = "set_tile_template_refresh_interval"
|
private const val SCREEN_SET_TILE_TEMPLATE_REFRESH_INTERVAL = "set_tile_template_refresh_interval"
|
||||||
|
|
||||||
const val DEEPLINK_SENSOR_MANAGER = "ha_wear://$SCREEN_SINGLE_SENSOR_MANAGER"
|
const val DEEPLINK_SENSOR_MANAGER = "ha_wear://$SCREEN_SINGLE_SENSOR_MANAGER"
|
||||||
|
const val DEEPLINK_PREFIX_SET_SHORTCUT_TILE = "ha_wear://$SCREEN_SET_SHORTCUTS_TILE"
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun LoadHomePage(
|
fun LoadHomePage(
|
||||||
mainViewModel: MainViewModel
|
mainViewModel: MainViewModel
|
||||||
) {
|
) {
|
||||||
var shortcutEntitySelectionIndex: Int by remember { mutableStateOf(0) }
|
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
|
|
||||||
WearAppTheme {
|
WearAppTheme {
|
||||||
|
@ -129,8 +129,9 @@ fun LoadHomePage(
|
||||||
},
|
},
|
||||||
onClearFavorites = { mainViewModel.clearFavorites() },
|
onClearFavorites = { mainViewModel.clearFavorites() },
|
||||||
onClickSetShortcuts = {
|
onClickSetShortcuts = {
|
||||||
|
mainViewModel.loadShortcutTileEntities()
|
||||||
swipeDismissableNavController.navigate(
|
swipeDismissableNavController.navigate(
|
||||||
SCREEN_SET_TILE_SHORTCUTS
|
"$ROUTE_SHORTCUTS_TILE/$SCREEN_SELECT_SHORTCUTS_TILE"
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
onClickSensors = {
|
onClickSensors = {
|
||||||
|
@ -162,33 +163,63 @@ fun LoadHomePage(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
composable(SCREEN_SET_TILE_SHORTCUTS) {
|
composable("$ROUTE_SHORTCUTS_TILE/$SCREEN_SELECT_SHORTCUTS_TILE") {
|
||||||
SetTileShortcutsView(
|
SelectShortcutsTileView(
|
||||||
shortcutEntities = mainViewModel.shortcutEntities,
|
shortcutTileEntitiesCountById = mainViewModel.shortcutEntitiesMap.mapValues { (_, entities) -> entities.size },
|
||||||
onShortcutEntitySelectionChange = {
|
onSelectShortcutsTile = { tileId ->
|
||||||
shortcutEntitySelectionIndex = it
|
swipeDismissableNavController.navigate("$ROUTE_SHORTCUTS_TILE/$tileId/$SCREEN_SET_SHORTCUTS_TILE")
|
||||||
swipeDismissableNavController.navigate(SCREEN_SELECT_TILE_SHORTCUT)
|
|
||||||
},
|
},
|
||||||
isShowShortcutTextEnabled = mainViewModel.isShowShortcutTextEnabled.value,
|
isShowShortcutTextEnabled = mainViewModel.isShowShortcutTextEnabled.value,
|
||||||
onShowShortcutTextEnabled = {
|
onShowShortcutTextEnabled = {
|
||||||
mainViewModel.setShowShortcutTextEnabled(it)
|
mainViewModel.setShowShortcutTextEnabled(it)
|
||||||
TileService.getUpdater(context).requestUpdate(ShortcutsTile::class.java)
|
ShortcutsTile.requestUpdate(context)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
composable(SCREEN_SELECT_TILE_SHORTCUT) {
|
composable(
|
||||||
|
route = "$ROUTE_SHORTCUTS_TILE/{$ARG_SCREEN_SHORTCUTS_TILE_ID}/$SCREEN_SET_SHORTCUTS_TILE",
|
||||||
|
arguments = listOf(
|
||||||
|
navArgument(name = ARG_SCREEN_SHORTCUTS_TILE_ID) {
|
||||||
|
type = NavType.StringType
|
||||||
|
}
|
||||||
|
),
|
||||||
|
deepLinks = listOf(
|
||||||
|
navDeepLink { uriPattern = "$DEEPLINK_PREFIX_SET_SHORTCUT_TILE/{$ARG_SCREEN_SHORTCUTS_TILE_ID}" }
|
||||||
|
)
|
||||||
|
) { backStackEntry ->
|
||||||
|
val tileId = backStackEntry.arguments!!.getString(ARG_SCREEN_SHORTCUTS_TILE_ID)!!.toIntOrNull()
|
||||||
|
SetShortcutsTileView(
|
||||||
|
shortcutEntities = mainViewModel.shortcutEntitiesMap[tileId] ?: emptyList(),
|
||||||
|
onShortcutEntitySelectionChange = { entityIndex ->
|
||||||
|
swipeDismissableNavController.navigate("$ROUTE_SHORTCUTS_TILE/$tileId/$SCREEN_SHORTCUTS_TILE_CHOOSE_ENTITY/$entityIndex")
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
composable(
|
||||||
|
route = "$ROUTE_SHORTCUTS_TILE/{$ARG_SCREEN_SHORTCUTS_TILE_ID}/$SCREEN_SHORTCUTS_TILE_CHOOSE_ENTITY/{$ARG_SCREEN_SHORTCUTS_TILE_ENTITY_INDEX}",
|
||||||
|
arguments = listOf(
|
||||||
|
navArgument(name = ARG_SCREEN_SHORTCUTS_TILE_ID) {
|
||||||
|
type = NavType.StringType
|
||||||
|
},
|
||||||
|
navArgument(name = ARG_SCREEN_SHORTCUTS_TILE_ENTITY_INDEX) {
|
||||||
|
type = NavType.IntType
|
||||||
|
}
|
||||||
|
)
|
||||||
|
) { backStackEntry ->
|
||||||
|
val entityIndex = backStackEntry.arguments!!.getInt(ARG_SCREEN_SHORTCUTS_TILE_ENTITY_INDEX)
|
||||||
|
val tileId = backStackEntry.arguments!!.getString(ARG_SCREEN_SHORTCUTS_TILE_ID)!!.toIntOrNull()
|
||||||
ChooseEntityView(
|
ChooseEntityView(
|
||||||
entitiesByDomainOrder = mainViewModel.entitiesByDomainOrder,
|
entitiesByDomainOrder = mainViewModel.entitiesByDomainOrder,
|
||||||
entitiesByDomain = mainViewModel.entitiesByDomain,
|
entitiesByDomain = mainViewModel.entitiesByDomain,
|
||||||
favoriteEntityIds = mainViewModel.favoriteEntityIds,
|
favoriteEntityIds = mainViewModel.favoriteEntityIds,
|
||||||
onNoneClicked = {
|
onNoneClicked = {
|
||||||
mainViewModel.clearTileShortcut(shortcutEntitySelectionIndex)
|
mainViewModel.clearTileShortcut(tileId, entityIndex)
|
||||||
TileService.getUpdater(context).requestUpdate(ShortcutsTile::class.java)
|
ShortcutsTile.requestUpdate(context)
|
||||||
swipeDismissableNavController.navigateUp()
|
swipeDismissableNavController.navigateUp()
|
||||||
},
|
},
|
||||||
onEntitySelected = { entity ->
|
onEntitySelected = { entity ->
|
||||||
mainViewModel.setTileShortcut(shortcutEntitySelectionIndex, entity)
|
mainViewModel.setTileShortcut(tileId, entityIndex, entity)
|
||||||
TileService.getUpdater(context).requestUpdate(ShortcutsTile::class.java)
|
ShortcutsTile.requestUpdate(context)
|
||||||
swipeDismissableNavController.navigateUp()
|
swipeDismissableNavController.navigateUp()
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
|
@ -0,0 +1,136 @@
|
||||||
|
package io.homeassistant.companion.android.home.views
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.ColorFilter
|
||||||
|
import androidx.compose.ui.res.pluralStringResource
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.tooling.preview.Devices
|
||||||
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
|
import androidx.wear.compose.material.Chip
|
||||||
|
import androidx.wear.compose.material.ChipDefaults
|
||||||
|
import androidx.wear.compose.material.Icon
|
||||||
|
import androidx.wear.compose.material.PositionIndicator
|
||||||
|
import androidx.wear.compose.material.Scaffold
|
||||||
|
import androidx.wear.compose.material.Text
|
||||||
|
import androidx.wear.compose.material.ToggleChip
|
||||||
|
import androidx.wear.compose.material.ToggleChipDefaults
|
||||||
|
import androidx.wear.compose.material.itemsIndexed
|
||||||
|
import androidx.wear.compose.material.rememberScalingLazyListState
|
||||||
|
import com.mikepenz.iconics.compose.Image
|
||||||
|
import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial
|
||||||
|
import io.homeassistant.companion.android.theme.WearAppTheme
|
||||||
|
import io.homeassistant.companion.android.theme.wearColorPalette
|
||||||
|
import io.homeassistant.companion.android.views.ListHeader
|
||||||
|
import io.homeassistant.companion.android.views.ThemeLazyColumn
|
||||||
|
import io.homeassistant.companion.android.common.R as commonR
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun SelectShortcutsTileView(
|
||||||
|
shortcutTileEntitiesCountById: Map<Int?, Int>,
|
||||||
|
onSelectShortcutsTile: (tileId: Int?) -> Unit,
|
||||||
|
isShowShortcutTextEnabled: Boolean,
|
||||||
|
onShowShortcutTextEnabled: (Boolean) -> Unit
|
||||||
|
) {
|
||||||
|
val scalingLazyListState = rememberScalingLazyListState()
|
||||||
|
WearAppTheme {
|
||||||
|
Scaffold(
|
||||||
|
positionIndicator = {
|
||||||
|
if (scalingLazyListState.isScrollInProgress) {
|
||||||
|
PositionIndicator(scalingLazyListState = scalingLazyListState)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
timeText = { TimeText(scalingLazyListState = scalingLazyListState) }
|
||||||
|
) {
|
||||||
|
ThemeLazyColumn(state = scalingLazyListState) {
|
||||||
|
item {
|
||||||
|
ListHeader(id = commonR.string.shortcut_tiles)
|
||||||
|
}
|
||||||
|
item {
|
||||||
|
ToggleChip(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
checked = isShowShortcutTextEnabled,
|
||||||
|
onCheckedChange = { onShowShortcutTextEnabled(it) },
|
||||||
|
label = {
|
||||||
|
Text(stringResource(commonR.string.shortcuts_tile_text_setting))
|
||||||
|
},
|
||||||
|
appIcon = {
|
||||||
|
Image(
|
||||||
|
asset =
|
||||||
|
if (isShowShortcutTextEnabled) {
|
||||||
|
CommunityMaterial.Icon.cmd_alphabetical
|
||||||
|
} else {
|
||||||
|
CommunityMaterial.Icon.cmd_alphabetical_off
|
||||||
|
},
|
||||||
|
colorFilter = ColorFilter.tint(wearColorPalette.onSurface)
|
||||||
|
)
|
||||||
|
},
|
||||||
|
toggleControl = {
|
||||||
|
Icon(
|
||||||
|
imageVector = ToggleChipDefaults.checkboxIcon(isShowShortcutTextEnabled),
|
||||||
|
contentDescription = if (isShowShortcutTextEnabled) {
|
||||||
|
stringResource(commonR.string.show)
|
||||||
|
} else {
|
||||||
|
stringResource(commonR.string.hide)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
item {
|
||||||
|
ListHeader(id = commonR.string.shortcuts_tile_select)
|
||||||
|
}
|
||||||
|
if (shortcutTileEntitiesCountById.isEmpty()) {
|
||||||
|
item {
|
||||||
|
Text(stringResource(commonR.string.shortcuts_tile_no_tiles_yet))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
itemsIndexed(shortcutTileEntitiesCountById.keys.toList()) { index, shortcutsTileId ->
|
||||||
|
Chip(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth(),
|
||||||
|
label = {
|
||||||
|
Text(stringResource(commonR.string.shortcuts_tile_n, index + 1))
|
||||||
|
},
|
||||||
|
secondaryLabel = {
|
||||||
|
val entityCount = shortcutTileEntitiesCountById[shortcutsTileId] ?: 0
|
||||||
|
if (entityCount > 0) {
|
||||||
|
Text(pluralStringResource(commonR.plurals.n_entities, entityCount, entityCount))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onClick = { onSelectShortcutsTile(shortcutsTileId) },
|
||||||
|
colors = ChipDefaults.secondaryChipColors()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Preview(device = Devices.WEAR_OS_LARGE_ROUND)
|
||||||
|
@Composable
|
||||||
|
private fun PreviewSelectShortcutsTileView() {
|
||||||
|
SelectShortcutsTileView(
|
||||||
|
shortcutTileEntitiesCountById = mapOf(
|
||||||
|
null to 7,
|
||||||
|
1111 to 1,
|
||||||
|
2222 to 0
|
||||||
|
),
|
||||||
|
onSelectShortcutsTile = {},
|
||||||
|
isShowShortcutTextEnabled = true,
|
||||||
|
onShowShortcutTextEnabled = {}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Preview(device = Devices.WEAR_OS_LARGE_ROUND)
|
||||||
|
@Composable
|
||||||
|
private fun PreviewSelectShortcutsTileEmptyView() {
|
||||||
|
SelectShortcutsTileView(
|
||||||
|
shortcutTileEntitiesCountById = emptyMap(),
|
||||||
|
onSelectShortcutsTile = {},
|
||||||
|
isShowShortcutTextEnabled = true,
|
||||||
|
onShowShortcutTextEnabled = {}
|
||||||
|
)
|
||||||
|
}
|
|
@ -16,18 +16,14 @@ import androidx.wear.compose.material.Button
|
||||||
import androidx.wear.compose.material.ButtonDefaults
|
import androidx.wear.compose.material.ButtonDefaults
|
||||||
import androidx.wear.compose.material.Chip
|
import androidx.wear.compose.material.Chip
|
||||||
import androidx.wear.compose.material.ChipDefaults
|
import androidx.wear.compose.material.ChipDefaults
|
||||||
import androidx.wear.compose.material.Icon
|
|
||||||
import androidx.wear.compose.material.PositionIndicator
|
import androidx.wear.compose.material.PositionIndicator
|
||||||
import androidx.wear.compose.material.Scaffold
|
import androidx.wear.compose.material.Scaffold
|
||||||
import androidx.wear.compose.material.Text
|
import androidx.wear.compose.material.Text
|
||||||
import androidx.wear.compose.material.ToggleChip
|
|
||||||
import androidx.wear.compose.material.ToggleChipDefaults
|
|
||||||
import androidx.wear.compose.material.rememberScalingLazyListState
|
import androidx.wear.compose.material.rememberScalingLazyListState
|
||||||
import com.mikepenz.iconics.compose.Image
|
import com.mikepenz.iconics.compose.Image
|
||||||
import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial
|
import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial
|
||||||
import io.homeassistant.companion.android.data.SimplifiedEntity
|
import io.homeassistant.companion.android.data.SimplifiedEntity
|
||||||
import io.homeassistant.companion.android.theme.WearAppTheme
|
import io.homeassistant.companion.android.theme.WearAppTheme
|
||||||
import io.homeassistant.companion.android.theme.wearColorPalette
|
|
||||||
import io.homeassistant.companion.android.util.getIcon
|
import io.homeassistant.companion.android.util.getIcon
|
||||||
import io.homeassistant.companion.android.util.simplifiedEntity
|
import io.homeassistant.companion.android.util.simplifiedEntity
|
||||||
import io.homeassistant.companion.android.views.ListHeader
|
import io.homeassistant.companion.android.views.ListHeader
|
||||||
|
@ -35,11 +31,9 @@ import io.homeassistant.companion.android.views.ThemeLazyColumn
|
||||||
import io.homeassistant.companion.android.common.R as commonR
|
import io.homeassistant.companion.android.common.R as commonR
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun SetTileShortcutsView(
|
fun SetShortcutsTileView(
|
||||||
shortcutEntities: MutableList<SimplifiedEntity>,
|
shortcutEntities: List<SimplifiedEntity>,
|
||||||
onShortcutEntitySelectionChange: (Int) -> Unit,
|
onShortcutEntitySelectionChange: (Int) -> Unit
|
||||||
isShowShortcutTextEnabled: Boolean,
|
|
||||||
onShowShortcutTextEnabled: (Boolean) -> Unit
|
|
||||||
) {
|
) {
|
||||||
val scalingLazyListState = rememberScalingLazyListState()
|
val scalingLazyListState = rememberScalingLazyListState()
|
||||||
WearAppTheme {
|
WearAppTheme {
|
||||||
|
@ -52,40 +46,6 @@ fun SetTileShortcutsView(
|
||||||
timeText = { TimeText(scalingLazyListState = scalingLazyListState) }
|
timeText = { TimeText(scalingLazyListState = scalingLazyListState) }
|
||||||
) {
|
) {
|
||||||
ThemeLazyColumn(state = scalingLazyListState) {
|
ThemeLazyColumn(state = scalingLazyListState) {
|
||||||
item {
|
|
||||||
ListHeader(id = commonR.string.shortcuts_tile)
|
|
||||||
}
|
|
||||||
item {
|
|
||||||
ToggleChip(
|
|
||||||
modifier = Modifier.fillMaxWidth(),
|
|
||||||
checked = isShowShortcutTextEnabled,
|
|
||||||
onCheckedChange = { onShowShortcutTextEnabled(it) },
|
|
||||||
label = {
|
|
||||||
Text(stringResource(commonR.string.shortcuts_tile_text_setting))
|
|
||||||
},
|
|
||||||
appIcon = {
|
|
||||||
Image(
|
|
||||||
asset =
|
|
||||||
if (isShowShortcutTextEnabled) {
|
|
||||||
CommunityMaterial.Icon.cmd_alphabetical
|
|
||||||
} else {
|
|
||||||
CommunityMaterial.Icon.cmd_alphabetical_off
|
|
||||||
},
|
|
||||||
colorFilter = ColorFilter.tint(wearColorPalette.onSurface)
|
|
||||||
)
|
|
||||||
},
|
|
||||||
toggleControl = {
|
|
||||||
Icon(
|
|
||||||
imageVector = ToggleChipDefaults.checkboxIcon(isShowShortcutTextEnabled),
|
|
||||||
contentDescription = if (isShowShortcutTextEnabled) {
|
|
||||||
stringResource(commonR.string.show)
|
|
||||||
} else {
|
|
||||||
stringResource(commonR.string.hide)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
item {
|
item {
|
||||||
ListHeader(id = commonR.string.shortcuts_choose)
|
ListHeader(id = commonR.string.shortcuts_choose)
|
||||||
}
|
}
|
||||||
|
@ -143,10 +103,8 @@ fun SetTileShortcutsView(
|
||||||
@Preview(device = Devices.WEAR_OS_LARGE_ROUND)
|
@Preview(device = Devices.WEAR_OS_LARGE_ROUND)
|
||||||
@Composable
|
@Composable
|
||||||
private fun PreviewSetTileShortcutsView() {
|
private fun PreviewSetTileShortcutsView() {
|
||||||
SetTileShortcutsView(
|
SetShortcutsTileView(
|
||||||
shortcutEntities = mutableListOf(simplifiedEntity),
|
shortcutEntities = mutableListOf(simplifiedEntity),
|
||||||
onShortcutEntitySelectionChange = {},
|
onShortcutEntitySelectionChange = {}
|
||||||
isShowShortcutTextEnabled = true,
|
|
||||||
onShowShortcutTextEnabled = {}
|
|
||||||
)
|
)
|
||||||
}
|
}
|
|
@ -216,7 +216,7 @@ fun SettingsView(
|
||||||
item {
|
item {
|
||||||
SecondarySettingsChip(
|
SecondarySettingsChip(
|
||||||
icon = CommunityMaterial.Icon3.cmd_star_circle_outline,
|
icon = CommunityMaterial.Icon3.cmd_star_circle_outline,
|
||||||
label = stringResource(commonR.string.shortcuts_tile),
|
label = stringResource(commonR.string.shortcut_tiles),
|
||||||
onClick = onClickSetShortcuts
|
onClick = onClickSetShortcuts
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -58,7 +58,8 @@ class MobileAppIntegrationPresenterImpl @Inject constructor(
|
||||||
|
|
||||||
private fun updateTiles() = mainScope.launch {
|
private fun updateTiles() = mainScope.launch {
|
||||||
try {
|
try {
|
||||||
val updater = TileService.getUpdater(view as Context)
|
val context = view as Context
|
||||||
|
val updater = TileService.getUpdater(context)
|
||||||
updater.requestUpdate(ConversationTile::class.java)
|
updater.requestUpdate(ConversationTile::class.java)
|
||||||
updater.requestUpdate(ShortcutsTile::class.java)
|
updater.requestUpdate(ShortcutsTile::class.java)
|
||||||
updater.requestUpdate(TemplateTile::class.java)
|
updater.requestUpdate(TemplateTile::class.java)
|
||||||
|
|
|
@ -0,0 +1,33 @@
|
||||||
|
package io.homeassistant.companion.android.tiles
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
|
import io.homeassistant.companion.android.common.data.prefs.WearPrefsRepositoryImpl
|
||||||
|
import io.homeassistant.companion.android.home.HomeActivity
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@AndroidEntryPoint
|
||||||
|
class OpenShortcutTileSettingsActivity : AppCompatActivity() {
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
lateinit var wearPrefsRepository: WearPrefsRepositoryImpl
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
val tileId = intent.extras?.getInt("com.google.android.clockwork.EXTRA_PROVIDER_CONFIG_TILE_ID")
|
||||||
|
tileId?.takeIf { it != 0 }?.let {
|
||||||
|
lifecycleScope.launch {
|
||||||
|
wearPrefsRepository.getTileShortcutsAndSaveTileId(tileId)
|
||||||
|
}
|
||||||
|
val intent = HomeActivity.getShortcutsTileSettingsIntent(
|
||||||
|
context = this,
|
||||||
|
tileId = it
|
||||||
|
)
|
||||||
|
startActivity(intent)
|
||||||
|
}
|
||||||
|
finish()
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,6 @@
|
||||||
package io.homeassistant.companion.android.tiles
|
package io.homeassistant.companion.android.tiles
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.graphics.Bitmap
|
import android.graphics.Bitmap
|
||||||
import android.graphics.Color
|
import android.graphics.Color
|
||||||
|
@ -9,6 +10,7 @@ import androidx.wear.tiles.ActionBuilders
|
||||||
import androidx.wear.tiles.ColorBuilders.argb
|
import androidx.wear.tiles.ColorBuilders.argb
|
||||||
import androidx.wear.tiles.DimensionBuilders.dp
|
import androidx.wear.tiles.DimensionBuilders.dp
|
||||||
import androidx.wear.tiles.DimensionBuilders.sp
|
import androidx.wear.tiles.DimensionBuilders.sp
|
||||||
|
import androidx.wear.tiles.EventBuilders
|
||||||
import androidx.wear.tiles.LayoutElementBuilders
|
import androidx.wear.tiles.LayoutElementBuilders
|
||||||
import androidx.wear.tiles.LayoutElementBuilders.Box
|
import androidx.wear.tiles.LayoutElementBuilders.Box
|
||||||
import androidx.wear.tiles.LayoutElementBuilders.Column
|
import androidx.wear.tiles.LayoutElementBuilders.Column
|
||||||
|
@ -41,6 +43,7 @@ import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
import kotlinx.coroutines.guava.future
|
import kotlinx.coroutines.guava.future
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
import java.nio.ByteBuffer
|
import java.nio.ByteBuffer
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import kotlin.math.min
|
import kotlin.math.min
|
||||||
|
@ -78,13 +81,14 @@ class ShortcutsTile : TileService() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val entities = getEntities()
|
val tileId = requestParams.tileId
|
||||||
|
val entities = getEntities(tileId)
|
||||||
|
|
||||||
Tile.Builder()
|
Tile.Builder()
|
||||||
.setResourcesVersion(entities.toString())
|
.setResourcesVersion(entities.toString())
|
||||||
.setTimeline(
|
.setTimeline(
|
||||||
if (serverManager.isRegistered()) {
|
if (serverManager.isRegistered()) {
|
||||||
timeline()
|
timeline(tileId)
|
||||||
} else {
|
} else {
|
||||||
loggedOutTimeline(
|
loggedOutTimeline(
|
||||||
this@ShortcutsTile,
|
this@ShortcutsTile,
|
||||||
|
@ -102,7 +106,7 @@ class ShortcutsTile : TileService() {
|
||||||
val iconSize = if (showLabels) ICON_SIZE_SMALL else ICON_SIZE_FULL
|
val iconSize = if (showLabels) ICON_SIZE_SMALL else ICON_SIZE_FULL
|
||||||
val density = requestParams.deviceParameters!!.screenDensity
|
val density = requestParams.deviceParameters!!.screenDensity
|
||||||
val iconSizePx = (iconSize * density).roundToInt()
|
val iconSizePx = (iconSize * density).roundToInt()
|
||||||
val entities = getEntities()
|
val entities = getEntities(requestParams.tileId)
|
||||||
|
|
||||||
Resources.Builder()
|
Resources.Builder()
|
||||||
.setVersion(entities.toString())
|
.setVersion(entities.toString())
|
||||||
|
@ -143,18 +147,45 @@ class ShortcutsTile : TileService() {
|
||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onTileAddEvent(requestParams: EventBuilders.TileAddEvent) {
|
||||||
|
serviceScope.launch {
|
||||||
|
/**
|
||||||
|
* When the app is updated from an older version (which only supported a single Shortcut Tile),
|
||||||
|
* and the user is adding a new Shortcuts Tile, we can't tell for sure if it's the 1st or 2nd Tile.
|
||||||
|
* Even though we may have the shortcut list stored in the prefs, it doesn't guarantee that
|
||||||
|
* the tile was actually added to the Tiles carousel.
|
||||||
|
* The [WearPrefsRepositoryImpl::getTileShortcutsAndSaveTileId] method will handle both of the following cases:
|
||||||
|
* 1. There was no Tile added, but there were shortcuts stored in the prefs.
|
||||||
|
* In this case, the stored shortcuts will be associated to the new tileId.
|
||||||
|
* 2. There was a single Tile added, and there were shortcuts stored in the prefs.
|
||||||
|
* If there was a Tile update since updating the app, the tileId will be already
|
||||||
|
* associated to the shortcuts, because it also calls [getTileShortcutsAndSaveTileId].
|
||||||
|
* If there was no Tile update yet, the new Tile will "steal" the shortcuts from the existing Tile,
|
||||||
|
* and the old Tile will behave as it is the new Tile. This is needed because
|
||||||
|
* we don't know if it's the 1st or 2nd Tile.
|
||||||
|
*/
|
||||||
|
wearPrefsRepository.getTileShortcutsAndSaveTileId(requestParams.tileId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onTileRemoveEvent(requestParams: EventBuilders.TileRemoveEvent) {
|
||||||
|
serviceScope.launch {
|
||||||
|
wearPrefsRepository.removeTileShortcuts(requestParams.tileId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun onDestroy() {
|
override fun onDestroy() {
|
||||||
super.onDestroy()
|
super.onDestroy()
|
||||||
// Cleans up the coroutine
|
// Cleans up the coroutine
|
||||||
serviceJob.cancel()
|
serviceJob.cancel()
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun getEntities(): List<SimplifiedEntity> {
|
private suspend fun getEntities(tileId: Int): List<SimplifiedEntity> {
|
||||||
return wearPrefsRepository.getTileShortcuts().map { SimplifiedEntity(it) }
|
return wearPrefsRepository.getTileShortcutsAndSaveTileId(tileId).map { SimplifiedEntity(it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun timeline(): Timeline {
|
private suspend fun timeline(tileId: Int): Timeline {
|
||||||
val entities = getEntities()
|
val entities = getEntities(tileId)
|
||||||
val showLabels = wearPrefsRepository.getShowShortcutText()
|
val showLabels = wearPrefsRepository.getShowShortcutText()
|
||||||
|
|
||||||
return Timeline.fromLayoutElement(layout(entities, showLabels))
|
return Timeline.fromLayoutElement(layout(entities, showLabels))
|
||||||
|
@ -253,4 +284,10 @@ class ShortcutsTile : TileService() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun requestUpdate(context: Context) {
|
||||||
|
getUpdater(context).requestUpdate(ShortcutsTile::class.java)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue