diff --git a/wearApp/src/main/java/com/surrus/peopleinspace/MainActivity.kt b/wearApp/src/main/java/com/surrus/peopleinspace/MainActivity.kt index 897cc03..89199d9 100644 --- a/wearApp/src/main/java/com/surrus/peopleinspace/MainActivity.kt +++ b/wearApp/src/main/java/com/surrus/peopleinspace/MainActivity.kt @@ -34,16 +34,11 @@ class MainActivity : ComponentActivity() { super.onCreate(savedInstanceState) setContent { - val rotaryEventDispatcher = RotaryEventDispatcher() - CompositionLocalProvider( LocalImageLoader provides imageLoader, - LocalRotaryEventDispatcher provides rotaryEventDispatcher, ) { val navController = rememberSwipeDismissableNavController() - RotaryEventHandlerSetup(rotaryEventDispatcher) - SwipeDismissableNavHost( navController = navController, startDestination = Screen.PersonList.route diff --git a/wearApp/src/main/java/com/surrus/peopleinspace/PersonDetailsScreen.kt b/wearApp/src/main/java/com/surrus/peopleinspace/PersonDetailsScreen.kt index 1f8df2d..2c3b5fb 100644 --- a/wearApp/src/main/java/com/surrus/peopleinspace/PersonDetailsScreen.kt +++ b/wearApp/src/main/java/com/surrus/peopleinspace/PersonDetailsScreen.kt @@ -23,6 +23,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalConfiguration +import androidx.compose.ui.platform.LocalView import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp @@ -50,7 +51,6 @@ fun PersonDetailsScreen(personName: String) { } val scrollState = rememberScrollState() - RotaryEventState(scrollState) PersonDetailsScreen(person, scrollState) } @@ -60,6 +60,9 @@ private fun PersonDetailsScreen( person: Assignment?, scrollState: ScrollState = rememberScrollState(), ) { + // Activate scrolling + LocalView.current.requestFocus() + MaterialTheme { Scaffold( vignette = { @@ -71,6 +74,7 @@ private fun PersonDetailsScreen( ) { Column( modifier = Modifier + .scrollHandler(scrollState) .fillMaxSize() .padding(horizontal = if (LocalConfiguration.current.isScreenRound) 18.dp else 8.dp) .verticalScroll(scrollState), diff --git a/wearApp/src/main/java/com/surrus/peopleinspace/PersonListScreen.kt b/wearApp/src/main/java/com/surrus/peopleinspace/PersonListScreen.kt index 1005a2a..23dac8c 100644 --- a/wearApp/src/main/java/com/surrus/peopleinspace/PersonListScreen.kt +++ b/wearApp/src/main/java/com/surrus/peopleinspace/PersonListScreen.kt @@ -16,6 +16,7 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.wrapContentSize import androidx.compose.runtime.Composable +import androidx.compose.runtime.SideEffect import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment @@ -23,8 +24,8 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.scale import androidx.compose.ui.graphics.Color -import androidx.compose.ui.platform.LocalConfiguration import androidx.compose.ui.platform.LocalInspectionMode +import androidx.compose.ui.platform.LocalView import androidx.compose.ui.platform.testTag import androidx.compose.ui.res.painterResource import androidx.compose.ui.semantics.contentDescription @@ -55,9 +56,7 @@ fun PersonListScreen( peopleInSpaceViewModel: PeopleInSpaceViewModel = getViewModel() ) { val peopleState by peopleInSpaceViewModel.peopleInSpace.collectAsState() - val scrollState = rememberScalingLazyListState() - RotaryEventState(scrollState) - PersonListScreen(peopleState, personSelected, issMapClick, scrollState) + PersonListScreen(peopleState, personSelected, issMapClick) } @OptIn(ExperimentalAnimationApi::class) @@ -66,7 +65,6 @@ fun PersonListScreen( people: List?, personSelected: (person: Assignment) -> Unit, issMapClick: () -> Unit, - scrollState: ScalingLazyListState = rememberScalingLazyListState(), ) { MaterialTheme { AnimatedVisibility( @@ -75,7 +73,7 @@ fun PersonListScreen( ) { if (people != null) { if (people.isNotEmpty()) { - PersonList(people, personSelected, issMapClick, scrollState) + PersonList(people, personSelected, issMapClick) } else { Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) { Card( @@ -98,15 +96,22 @@ fun PersonList( issMapClick: () -> Unit, scrollState: ScalingLazyListState = rememberScalingLazyListState(), ) { + // Activate scrolling + LocalView.current.requestFocus() + ScalingLazyColumn( + modifier = Modifier + .testTag(PersonListTag) + .scrollHandler(scrollState), contentPadding = PaddingValues(8.dp), - modifier = Modifier.testTag(PersonListTag), state = scrollState, ) { item { Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.Center) { Button( - modifier = Modifier.size(ButtonDefaults.SmallButtonSize).wrapContentSize(), + modifier = Modifier + .size(ButtonDefaults.SmallButtonSize) + .wrapContentSize(), onClick = issMapClick ) { // https://www.svgrepo.com/svg/170716/international-space-station diff --git a/wearApp/src/main/java/com/surrus/peopleinspace/RotaryEventDispatcher.kt b/wearApp/src/main/java/com/surrus/peopleinspace/RotaryEventDispatcher.kt index 331d9ed..30fc35d 100644 --- a/wearApp/src/main/java/com/surrus/peopleinspace/RotaryEventDispatcher.kt +++ b/wearApp/src/main/java/com/surrus/peopleinspace/RotaryEventDispatcher.kt @@ -4,72 +4,37 @@ 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.remember import androidx.compose.runtime.rememberCoroutineScope -import androidx.compose.runtime.staticCompositionLocalOf +import androidx.compose.ui.ExperimentalComposeUiApi +import androidx.compose.ui.Modifier +import androidx.compose.ui.composed +import androidx.compose.ui.input.pointer.RequestDisallowInterceptTouchEvent +import androidx.compose.ui.input.pointer.pointerInteropFilter 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 { - noLocalProvidedFor("LocalRotaryEventDispatcher") -} - -/** - * Dispatcher to link rotary event to [ScrollableState]. - * The instance should be set up by calling [RotaryEventHandlerSetup] function. - */ -class RotaryEventDispatcher( - var scrollState: ScrollableState? = null -) { - suspend fun onRotate(delta: Float): Float? = - scrollState?.scrollBy(delta) -} - -/** - * Custom rotary event setup (Currently, Column / LazyColumn doesn't handle rotary event.) - * Refer to https://developer.android.com/training/wearables/user-input/rotary-input - */ -@Composable -fun RotaryEventHandlerSetup(rotaryEventDispatcher: RotaryEventDispatcher) { - val view = LocalView.current +@OptIn(ExperimentalComposeUiApi::class) +fun Modifier.scrollHandler(scrollState: ScrollableState): Modifier = composed { val context = LocalContext.current val scope = rememberCoroutineScope() + val scaledVerticalScrollFactor = + remember { ViewConfiguration.get(context).getScaledVerticalScrollFactor() } - view.requestFocus() - view.setOnGenericMotionListener { _, event -> - if (event?.action != MotionEvent.ACTION_SCROLL || + this.pointerInteropFilter(RequestDisallowInterceptTouchEvent()) { event -> + if (event.action != MotionEvent.ACTION_SCROLL || !event.isFromSource(InputDeviceCompat.SOURCE_ROTARY_ENCODER) ) { - return@setOnGenericMotionListener false + false + } else { + val delta = -event.getAxisValue(MotionEventCompat.AXIS_SCROLL) * + scaledVerticalScrollFactor + scope.launch { + scrollState.scrollBy(delta) + } + true } - - val delta = -event.getAxisValue(MotionEventCompat.AXIS_SCROLL) * - ViewConfigurationCompat.getScaledVerticalScrollFactor( - ViewConfiguration.get(context), context - ) - scope.launch { - rotaryEventDispatcher.onRotate(delta) - } - true } -} - -/** - * Register a [ScrollableState] to [LocalRotaryEventDispatcher] - */ -@Composable -fun RotaryEventState(scrollState: ScrollableState?) { - val dispatcher = LocalRotaryEventDispatcher.current - SideEffect { - dispatcher.scrollState = scrollState - } -} - -private fun noLocalProvidedFor(name: String): Nothing { - error("CompositionLocal $name not present") } \ No newline at end of file