Refactor MainActivity to use compose as basis

This commit is contained in:
Niels van Velzen
2026-03-28 17:32:52 +01:00
committed by Niels van Velzen
parent 72ff0cfae8
commit b9f8757123
3 changed files with 73 additions and 61 deletions

View File

@@ -6,7 +6,7 @@ import android.view.KeyEvent
import android.view.MotionEvent
import android.view.View
import android.view.WindowManager
import androidx.activity.OnBackPressedCallback
import androidx.activity.compose.setContent
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
import androidx.lifecycle.Lifecycle
@@ -20,11 +20,11 @@ import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
import org.jellyfin.androidtv.auth.repository.SessionRepository
import org.jellyfin.androidtv.auth.repository.UserRepository
import org.jellyfin.androidtv.databinding.ActivityMainBinding
import org.jellyfin.androidtv.integration.LeanbackChannelWorker
import org.jellyfin.androidtv.ui.InteractionTrackerViewModel
import org.jellyfin.androidtv.ui.background.AppBackground
import org.jellyfin.androidtv.ui.navigation.NavigationAction
import org.jellyfin.androidtv.ui.base.JellyfinTheme
import org.jellyfin.androidtv.ui.composable.compat.AppNavigationHost
import org.jellyfin.androidtv.ui.navigation.NavigationRepository
import org.jellyfin.androidtv.ui.screensaver.InAppScreensaver
import org.jellyfin.androidtv.ui.settings.compat.MainActivitySettings
@@ -42,14 +42,6 @@ class MainActivity : FragmentActivity() {
private val interactionTrackerViewModel by viewModel<InteractionTrackerViewModel>()
private val workManager by inject<WorkManager>()
private lateinit var binding: ActivityMainBinding
private val backPressedCallback = object : OnBackPressedCallback(false) {
override fun handleOnBackPressed() {
if (navigationRepository.canGoBack) navigationRepository.goBack()
}
}
override fun onCreate(savedInstanceState: Bundle?) {
applyTheme()
@@ -63,22 +55,24 @@ class MainActivity : FragmentActivity() {
else window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
}.launchIn(lifecycleScope)
onBackPressedDispatcher.addCallback(this, backPressedCallback)
if (savedInstanceState == null && navigationRepository.canGoBack) navigationRepository.reset(clearHistory = true)
navigationRepository.currentAction
.flowWithLifecycle(lifecycle, Lifecycle.State.STARTED)
.onEach { action ->
handleNavigationAction(action)
backPressedCallback.isEnabled = navigationRepository.canGoBack
.onEach {
interactionTrackerViewModel.notifyInteraction(canCancel = false, userInitiated = false)
}.launchIn(lifecycleScope)
binding = ActivityMainBinding.inflate(layoutInflater)
binding.background.setContent { AppBackground() }
binding.settings.setContent { MainActivitySettings() }
binding.screensaver.setContent { InAppScreensaver() }
setContentView(binding.root)
setContent {
JellyfinTheme {
AppBackground()
AppNavigationHost(
navigationRepository = navigationRepository,
)
InAppScreensaver()
MainActivitySettings()
}
}
}
override fun onResume() {
@@ -119,17 +113,6 @@ class MainActivity : FragmentActivity() {
}
}
private fun handleNavigationAction(action: NavigationAction) {
interactionTrackerViewModel.notifyInteraction(canCancel = true, userInitiated = false)
when (action) {
is NavigationAction.NavigateFragment -> binding.contentView.navigate(action)
NavigationAction.GoBack -> binding.contentView.goBack()
NavigationAction.Nothing -> Unit
}
}
// Forward key events to fragments
private fun Fragment.onKeyEvent(keyCode: Int, event: KeyEvent?): Boolean {
var result = childFragmentManager.fragments.any { it.onKeyEvent(keyCode, event) }

View File

@@ -0,0 +1,59 @@
package org.jellyfin.androidtv.ui.composable.compat
import android.content.Context
import android.view.View
import androidx.activity.compose.BackHandler
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.viewinterop.AndroidView
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map
import org.jellyfin.androidtv.ui.browsing.DestinationFragmentView
import org.jellyfin.androidtv.ui.navigation.NavigationAction
import org.jellyfin.androidtv.ui.navigation.NavigationRepository
import org.koin.compose.koinInject
@Composable
fun AppNavigationHost(
modifier: Modifier = Modifier,
navigationRepository: NavigationRepository = koinInject(),
) {
val factory = remember { AppNavigationHostViewFactory() }
val currentAction by navigationRepository.currentAction.collectAsState(NavigationAction.Nothing)
val canGoBack by remember {
navigationRepository.currentAction.map { navigationRepository.canGoBack }.distinctUntilChanged()
}.collectAsState(navigationRepository.canGoBack)
BackHandler(canGoBack) { navigationRepository.goBack() }
AndroidView(
factory = factory,
modifier = modifier,
)
LaunchedEffect(currentAction) {
when (val currentAction = currentAction) {
is NavigationAction.NavigateFragment -> factory.view.navigate(currentAction)
NavigationAction.GoBack -> factory.view.goBack()
NavigationAction.Nothing -> Unit
}
}
}
private class AppNavigationHostViewFactory : (Context) -> View {
private var _view: DestinationFragmentView? = null
val view get() = requireNotNull(_view)
override operator fun invoke(
context: Context
): View = DestinationFragmentView(context).also { view ->
_view = view
}
}

View File

@@ -1,30 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
tools:context=".ui.startup.MainActivity">
<androidx.compose.ui.platform.ComposeView
android:id="@+id/background"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:descendantFocusability="blocksDescendants"
android:focusable="false" />
<org.jellyfin.androidtv.ui.browsing.DestinationFragmentView
android:id="@+id/content_view"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<androidx.compose.ui.platform.ComposeView
android:id="@+id/settings"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<androidx.compose.ui.platform.ComposeView
android:id="@+id/screensaver"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</RelativeLayout>