Skip to content

Commit

Permalink
Add option to make tap on entity state complication toggle the corres…
Browse files Browse the repository at this point in the history
…ponding device.

Currently, tapping a complication only refreshes the entity state. This PR adds a "toggle entity when tapped" option to the complication configuration page. When this option is enabled, tapping on the complication triggers a call to the #onPressed method of the corresponding device entity, which in turns executes a RPC to the corresponding service. This lets users quickly toggle home assistant devices with a single click on their watch face.
  • Loading branch information
clju committed Dec 26, 2023
1 parent 1162a82 commit 76b2b45
Show file tree
Hide file tree
Showing 8 changed files with 1,194 additions and 16 deletions.
1,121 changes: 1,121 additions & 0 deletions common/schemas/io.homeassistant.companion.android.database.AppDatabase/46.json

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ import io.homeassistant.companion.android.common.R as commonR
Server::class,
Setting::class
],
version = 45,
version = 46,
autoMigrations = [
AutoMigration(from = 24, to = 25),
AutoMigration(from = 25, to = 26),
Expand All @@ -118,7 +118,8 @@ import io.homeassistant.companion.android.common.R as commonR
AutoMigration(from = 41, to = 42),
AutoMigration(from = 42, to = 43),
AutoMigration(from = 43, to = 44),
AutoMigration(from = 44, to = 45)
AutoMigration(from = 44, to = 45),
AutoMigration(from = 45, to = 46)
]
)
@TypeConverters(
Expand Down Expand Up @@ -193,7 +194,7 @@ abstract class AppDatabase : RoomDatabase() {
MIGRATION_21_22,
MIGRATION_22_23,
MIGRATION_23_24,
Migration40to41(context.assets)
Migration40to41(context.assets),
)
.fallbackToDestructiveMigration()
.build()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,7 @@ data class EntityStateComplications(
@ColumnInfo(name = "show_title", defaultValue = "1")
val showTitle: Boolean,
@ColumnInfo(name = "show_unit", defaultValue = "0")
val showUnit: Boolean
val showUnit: Boolean,
@ColumnInfo(name = "forward_taps", defaultValue = "0")
val forwardTaps: Boolean
)
1 change: 1 addition & 0 deletions common/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -795,6 +795,7 @@
<string name="show_share_logs">Show and share logs</string>
<string name="show_entity_title">Show entity name</string>
<string name="show_unit_title">Show entity unit</string>
<string name="forward_taps_title">Toggle entity when tapped</string>
<string name="sign_in_on_phone">Sign in on phone</string>
<string name="slider_decreased">%1$s decreased</string>
<string name="slider_increased">%1$s increased</string>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,8 @@ class ComplicationConfigViewModel @Inject constructor(
private set
var entityShowUnit by mutableStateOf(true)
private set

var entityForwardTaps by mutableStateOf(true)
private set
init {
loadEntities()
}
Expand All @@ -74,6 +75,7 @@ class ComplicationConfigViewModel @Inject constructor(
selectedEntity = SimplifiedEntity(entityId = it.entityId)
entityShowTitle = it.showTitle
entityShowUnit = it.showUnit
entityForwardTaps = it.forwardTaps
if (loadingState == LoadingState.READY) {
updateSelectedEntity()
}
Expand Down Expand Up @@ -160,9 +162,13 @@ class ComplicationConfigViewModel @Inject constructor(
entityShowUnit = show
}

fun setForwardTaps(show: Boolean) {
entityForwardTaps = show
}

fun addEntityStateComplication(id: Int, entity: SimplifiedEntity) {
viewModelScope.launch {
entityStateComplicationsDao.add(EntityStateComplications(id, entity.entityId, entityShowTitle, entityShowUnit))
entityStateComplicationsDao.add(EntityStateComplications(id, entity.entityId, entityShowTitle, entityShowUnit, entityForwardTaps))
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@ import android.content.BroadcastReceiver
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.util.Log
import androidx.wear.watchface.complications.datasource.ComplicationDataSourceService.Companion.EXTRA_CONFIG_COMPLICATION_ID
import androidx.wear.watchface.complications.datasource.ComplicationDataSourceUpdateRequester
import dagger.hilt.android.AndroidEntryPoint
import io.homeassistant.companion.android.common.data.integration.onPressed
import io.homeassistant.companion.android.common.data.servers.ServerManager
import io.homeassistant.companion.android.conversation.ConversationActivity
import kotlinx.coroutines.CoroutineScope
Expand All @@ -28,23 +30,44 @@ class ComplicationReceiver : BroadcastReceiver() {

try {
when (intent.action) {
UPDATE_COMPLICATION -> updateComplication(context, intent.getIntExtra(EXTRA_ID, -1))
TOGGLE_COMPLICATION -> toggleComplication(
context,
intent.getIntExtra(EXTRA_COMPLICATION_ID, -1),
intent.getStringExtra(EXTRA_ENTITY_ID)
)
Intent.ACTION_SCREEN_ON -> onScreenOn(context)
}
} finally {
result.finish()
}
}

private fun updateComplication(context: Context, id: Int) {
private fun toggleComplication(context: Context, complicationId: Int, entityId: String?) {
scope.launch {
// Forward tap to entity if entityId is specified.
if (entityId != null) {
val entity = try {
serverManager.integrationRepository().getEntity(entityId)
?: return@launch
} catch (t: Throwable) {
Log.e(
EntityStateDataSourceService.TAG,
"Unable to get entity for $entityId: ${t.message}"
)
return@launch
}

// Press!
entity.onPressed(serverManager.integrationRepository())
}

// Request an update for the complication that has just been toggled.
ComplicationDataSourceUpdateRequester
.create(
context = context,
complicationDataSourceComponent = ComponentName(context, EntityStateDataSourceService::class.java)
)
.requestUpdate(id)
.requestUpdate(complicationId)
}
}

Expand All @@ -67,20 +90,23 @@ class ComplicationReceiver : BroadcastReceiver() {
companion object {
private const val TAG = "ComplicationReceiver"

const val UPDATE_COMPLICATION = "update_complication"
private const val EXTRA_ID = "complication_instance_id"
const val TOGGLE_COMPLICATION = "toggle_complication"
private const val EXTRA_COMPLICATION_ID = "complication_instance_id"
private const val EXTRA_ENTITY_ID = "entity_id"

/**
* Returns a pending intent, suitable for use as a tap intent, that causes a complication to be
* toggled and updated.
*/
fun getComplicationToggleIntent(
context: Context,
complicationInstanceId: Int
complicationInstanceId: Int,
entityId: String?
): PendingIntent {
val intent = Intent(context, ComplicationReceiver::class.java).apply {
action = UPDATE_COMPLICATION
putExtra(EXTRA_ID, complicationInstanceId)
action = TOGGLE_COMPLICATION
putExtra(EXTRA_COMPLICATION_ID, complicationInstanceId)
putExtra(EXTRA_ENTITY_ID, entityId)
}

// Pass complicationId as the requestCode to ensure that different complications get
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,11 @@ class EntityStateDataSourceService : SuspendingComplicationDataSourceService() {

val contentDescription = PlainComplicationText.Builder(getText(R.string.complication_entity_state_content_description)).build()
val monochromaticImage = MonochromaticImage.Builder(Icon.createWithBitmap(iconBitmap)).build()
val tapAction = ComplicationReceiver.getComplicationToggleIntent(this, request.complicationInstanceId)
val tapAction = ComplicationReceiver.getComplicationToggleIntent(
this,
request.complicationInstanceId,
if (settings.forwardTaps) entity.entityId else null
)

return when (request.complicationType) {
ComplicationType.SHORT_TEXT -> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,12 +57,14 @@ fun LoadConfigView(
entity = complicationConfigViewModel.selectedEntity,
showTitle = complicationConfigViewModel.entityShowTitle,
showUnit = complicationConfigViewModel.entityShowUnit,
forwardTaps = complicationConfigViewModel.entityForwardTaps,
loadingState = complicationConfigViewModel.loadingState,
onChooseEntityClicked = {
swipeDismissableNavController.navigate(SCREEN_CHOOSE_ENTITY)
},
onShowTitleClicked = complicationConfigViewModel::setShowTitle,
onShowUnitClicked = complicationConfigViewModel::setShowUnit,
onForwardTapsClicked = complicationConfigViewModel::setForwardTaps,
onAcceptClicked = onAcceptClicked
)
}
Expand All @@ -88,10 +90,12 @@ fun MainConfigView(
entity: SimplifiedEntity?,
showTitle: Boolean,
showUnit: Boolean,
forwardTaps: Boolean,
loadingState: ComplicationConfigViewModel.LoadingState,
onChooseEntityClicked: () -> Unit,
onShowTitleClicked: (Boolean) -> Unit,
onShowUnitClicked: (Boolean) -> Unit,
onForwardTapsClicked: (Boolean) -> Unit,
onAcceptClicked: () -> Unit
) {
ThemeLazyColumn {
Expand Down Expand Up @@ -153,7 +157,18 @@ fun MainConfigView(
colors = getToggleButtonColors()
)
}

item {
val isChecked = if (loaded) forwardTaps else false
ToggleButton(
checked = isChecked,
onCheckedChange = onForwardTapsClicked,
label = { Text(stringResource(R.string.forward_taps_title)) },
selectionControl = { ToggleSwitch(isChecked) },
modifier = Modifier.fillMaxWidth(),
enabled = loaded && entity != null,
colors = getToggleButtonColors()
)
}
item {
FilledIconButton(
modifier = Modifier.padding(top = 8.dp).touchTargetAwareSize(IconButtonDefaults.SmallButtonSize),
Expand Down Expand Up @@ -182,10 +197,12 @@ fun PreviewMainConfigView() {
entity = simplifiedEntity,
showTitle = true,
showUnit = false,
forwardTaps = false,
loadingState = ComplicationConfigViewModel.LoadingState.READY,
onChooseEntityClicked = {},
onShowTitleClicked = {},
onShowUnitClicked = {},
onForwardTapsClicked = {},
onAcceptClicked = {}
)
}

0 comments on commit 76b2b45

Please sign in to comment.