Skip to content

Commit

Permalink
Bring back rotary input support for home screen (#1887)
Browse files Browse the repository at this point in the history
  • Loading branch information
dshokouhi authored Nov 8, 2021
1 parent dc80d31 commit 588e741
Show file tree
Hide file tree
Showing 2 changed files with 135 additions and 60 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
Expand Down Expand Up @@ -45,6 +46,10 @@ import io.homeassistant.companion.android.common.dagger.GraphComponentAccessor
import io.homeassistant.companion.android.common.data.integration.Entity
import io.homeassistant.companion.android.onboarding.OnboardingActivity
import io.homeassistant.companion.android.onboarding.integration.MobileAppIntegrationActivity
import io.homeassistant.companion.android.util.LocalRotaryEventDispatcher
import io.homeassistant.companion.android.util.RotaryEventDispatcher
import io.homeassistant.companion.android.util.RotaryEventHandlerSetup
import io.homeassistant.companion.android.util.RotaryEventState
import io.homeassistant.companion.android.viewModels.EntityViewModel
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
Expand Down Expand Up @@ -106,6 +111,7 @@ class HomeActivity : ComponentActivity(), HomeView {
@Composable
private fun LoadHomePage(entities: Array<Entity<Any>>) {

val rotaryEventDispatcher = RotaryEventDispatcher()
if (entities.isNullOrEmpty()) {
Column {
Spacer(
Expand Down Expand Up @@ -140,6 +146,7 @@ class HomeActivity : ComponentActivity(), HomeView {
entities.sortedBy { it.entityId }.filter { it.entityId.split(".")[0] == "switch" }

val scalingLazyListState: ScalingLazyListState = rememberScalingLazyListState()
RotaryEventDispatcher(scalingLazyListState)

MaterialTheme {
Scaffold(
Expand All @@ -148,76 +155,82 @@ class HomeActivity : ComponentActivity(), HomeView {
PositionIndicator(scalingLazyListState = scalingLazyListState)
}
) {
ScalingLazyColumn(
modifier = Modifier
.fillMaxSize(),
contentPadding = PaddingValues(
top = 10.dp,
start = 10.dp,
end = 10.dp,
bottom = 40.dp
),
horizontalAlignment = Alignment.CenterHorizontally,
state = scalingLazyListState
CompositionLocalProvider(
LocalRotaryEventDispatcher provides rotaryEventDispatcher
) {
if (inputBooleans.isNotEmpty()) {
items(inputBooleans.size) { index ->
if (index == 0)
SetTitle(R.string.input_booleans)
SetEntityUI(inputBooleans[index], index)
RotaryEventHandlerSetup(rotaryEventDispatcher)
RotaryEventState(scrollState = scalingLazyListState)
ScalingLazyColumn(
modifier = Modifier
.fillMaxSize(),
contentPadding = PaddingValues(
top = 10.dp,
start = 10.dp,
end = 10.dp,
bottom = 40.dp
),
horizontalAlignment = Alignment.CenterHorizontally,
state = scalingLazyListState
) {
if (inputBooleans.isNotEmpty()) {
items(inputBooleans.size) { index ->
if (index == 0)
SetTitle(R.string.input_booleans)
SetEntityUI(inputBooleans[index], index)
}
}
}
if (lights.isNotEmpty()) {
items(lights.size) { index ->
if (index == 0)
SetTitle(R.string.lights)
SetEntityUI(lights[index], index)
if (lights.isNotEmpty()) {
items(lights.size) { index ->
if (index == 0)
SetTitle(R.string.lights)
SetEntityUI(lights[index], index)
}
}
}
if (scenes.isNotEmpty()) {
items(scenes.size) { index ->
if (index == 0)
SetTitle(R.string.scenes)
if (scenes.isNotEmpty()) {
items(scenes.size) { index ->
if (index == 0)
SetTitle(R.string.scenes)

SetEntityUI(scenes[index], index)
SetEntityUI(scenes[index], index)
}
}
}
if (scripts.isNotEmpty()) {
items(scripts.size) { index ->
if (index == 0)
SetTitle(R.string.scripts)
SetEntityUI(scripts[index], index)
if (scripts.isNotEmpty()) {
items(scripts.size) { index ->
if (index == 0)
SetTitle(R.string.scripts)
SetEntityUI(scripts[index], index)
}
}
}
if (switches.isNotEmpty()) {
items(switches.size) { index ->
if (index == 0)
SetTitle(R.string.switches)
SetEntityUI(switches[index], index)
if (switches.isNotEmpty()) {
items(switches.size) { index ->
if (index == 0)
SetTitle(R.string.switches)
SetEntityUI(switches[index], index)
}
}
}

item {
Column {
SetTitle(R.string.other)
Chip(
modifier = Modifier
.fillMaxWidth()
.padding(top = 10.dp),
icon = {
Image(asset = CommunityMaterial.Icon.cmd_exit_run)
},
label = {
Text(
text = stringResource(id = R.string.logout)
item {
Column {
SetTitle(R.string.other)
Chip(
modifier = Modifier
.fillMaxWidth()
.padding(top = 10.dp),
icon = {
Image(asset = CommunityMaterial.Icon.cmd_exit_run)
},
label = {
Text(
text = stringResource(id = R.string.logout)
)
},
onClick = { presenter.onLogoutClicked() },
colors = ChipDefaults.primaryChipColors(
backgroundColor = Color.Red,
contentColor = Color.Black
)
},
onClick = { presenter.onLogoutClicked() },
colors = ChipDefaults.primaryChipColors(
backgroundColor = Color.Red,
contentColor = Color.Black
)
)
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package io.homeassistant.companion.android.util

import android.view.MotionEvent
import android.view.ViewConfiguration
import androidx.compose.foundation.gestures.ScrollableState
import androidx.compose.foundation.gestures.scrollBy
import androidx.compose.runtime.Composable
import androidx.compose.runtime.SideEffect
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.staticCompositionLocalOf
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalView
import androidx.core.view.InputDeviceCompat
import androidx.core.view.MotionEventCompat
import androidx.core.view.ViewConfigurationCompat
import kotlinx.coroutines.launch

val LocalRotaryEventDispatcher = staticCompositionLocalOf<RotaryEventDispatcher> {
noLocalProvidedFor("LocalRotaryEventDispatcher")
}

class RotaryEventDispatcher(
var scrollState: ScrollableState? = null
) {
suspend fun onRotate(delta: Float): Float? =
scrollState?.scrollBy(delta)
}

@Composable
fun RotaryEventHandlerSetup(rotaryEventDispatcher: RotaryEventDispatcher) {
val view = LocalView.current
val context = LocalContext.current
val scope = rememberCoroutineScope()

view.requestFocus()
view.setOnGenericMotionListener { _, event ->
if (event?.action != MotionEvent.ACTION_SCROLL || !event.isFromSource(InputDeviceCompat.SOURCE_ROTARY_ENCODER)) {
return@setOnGenericMotionListener false
}

val delta = -event.getAxisValue(MotionEventCompat.AXIS_SCROLL) * ViewConfigurationCompat.getScaledVerticalScrollFactor(
ViewConfiguration.get(context), context
)

scope.launch {
rotaryEventDispatcher.onRotate(delta)
}
true
}
}

@Composable
fun RotaryEventState(scrollState: ScrollableState?) {
val dispater = LocalRotaryEventDispatcher.current
SideEffect {
dispater.scrollState = scrollState
}
}

private fun noLocalProvidedFor(noOp: String): Nothing {
error("composition local $noOp not present")
}

0 comments on commit 588e741

Please sign in to comment.