From 16b235c40800e44690643a9ff983c39cd1700205 Mon Sep 17 00:00:00 2001 From: Chris Banes Date: Mon, 23 Nov 2020 20:13:51 +0000 Subject: [PATCH 01/17] Refactor MultipleBackStackNavigation away from BottomNav --- .../main/java/app/tivi/home/MainActivity.kt | 21 +- .../MultipleBackStackNavigationExtensions.kt | 264 ++++++++---------- 2 files changed, 141 insertions(+), 144 deletions(-) diff --git a/app/src/main/java/app/tivi/home/MainActivity.kt b/app/src/main/java/app/tivi/home/MainActivity.kt index 9442b13751..345d3fc9d2 100644 --- a/app/src/main/java/app/tivi/home/MainActivity.kt +++ b/app/src/main/java/app/tivi/home/MainActivity.kt @@ -25,8 +25,8 @@ import app.tivi.AppNavigator import app.tivi.R import app.tivi.TiviActivity import app.tivi.databinding.ActivityMainBinding +import app.tivi.extensions.MultipleBackStackNavigation import app.tivi.extensions.hideSoftInput -import app.tivi.extensions.setupWithNavController import app.tivi.trakt.TraktConstants import dagger.hilt.android.AndroidEntryPoint import dev.chrisbanes.insetter.Insetter @@ -86,7 +86,7 @@ class MainActivity : TiviActivity() { } private fun setupBottomNavigationBar() { - currentNavController = binding.homeBottomNavigation.setupWithNavController( + val multiBackStackNavigation = MultipleBackStackNavigation( navGraphIds = listOf( R.navigation.discover_nav_graph, R.navigation.watched_nav_graph, @@ -95,9 +95,24 @@ class MainActivity : TiviActivity() { ), fragmentManager = supportFragmentManager, containerId = R.id.home_nav_container, - intent = intent + intent = intent, + getSelectedItemId = { + binding.homeBottomNavigation.selectedItemId + }, + setSelectedItemId = { + binding.homeBottomNavigation.selectedItemId = it + }, ) + binding.homeBottomNavigation.setOnNavigationItemReselectedListener { + multiBackStackNavigation.onReselected(it.itemId) + } + binding.homeBottomNavigation.setOnNavigationItemSelectedListener { + multiBackStackNavigation.onItemSelected(it.itemId) + } + + currentNavController = multiBackStackNavigation.selectedNavController + currentNavController?.observe(this) { navController -> navController.addOnDestinationChangedListener { _, destination, _ -> if (destination.id != R.id.navigation_search) { diff --git a/common-ui-view/src/main/java/app/tivi/extensions/MultipleBackStackNavigationExtensions.kt b/common-ui-view/src/main/java/app/tivi/extensions/MultipleBackStackNavigationExtensions.kt index 3212fc055b..5e617e16b2 100644 --- a/common-ui-view/src/main/java/app/tivi/extensions/MultipleBackStackNavigationExtensions.kt +++ b/common-ui-view/src/main/java/app/tivi/extensions/MultipleBackStackNavigationExtensions.kt @@ -26,169 +26,133 @@ import androidx.lifecycle.MutableLiveData import androidx.navigation.NavController import androidx.navigation.fragment.NavHostFragment import app.tivi.common.ui.R -import com.google.android.material.bottomnavigation.BottomNavigationView - -/** - * Manages the various graphs needed for a [BottomNavigationView]. - * - * This sample is a workaround until the Navigation Component supports multiple back stacks. - */ -fun BottomNavigationView.setupWithNavController( - navGraphIds: List, - fragmentManager: FragmentManager, - containerId: Int, - intent: Intent -): LiveData { +class MultipleBackStackNavigation( + private val navGraphIds: List, + private val fragmentManager: FragmentManager, + private val containerId: Int, + private val intent: Intent, + private val getSelectedItemId: () -> Int, + private val setSelectedItemId: (Int) -> Unit, +) { // Map of tags - val graphIdToTagMap = SparseArray() - // Result. Mutable live data with the selected controlled - val selectedNavController = MutableLiveData() + private val graphIdToTagMap = SparseArray() - var firstFragmentGraphId = 0 - - // First create a NavHostFragment for each NavGraph ID - navGraphIds.forEachIndexed { index, navGraphId -> - val fragmentTag = getFragmentTag(index) + // Result. Mutable live data with the selected controlled + private val _selectedNavController = MutableLiveData() - // Find or create the Navigation host fragment - val navHostFragment = obtainNavHostFragment( - fragmentManager, - fragmentTag, - navGraphId, - containerId - ) + val selectedNavController: LiveData + get() = _selectedNavController - // Obtain its id - val graphId = navHostFragment.navController.graph.id + private var firstFragmentGraphId = 0 + private val firstFragmentTag: String - if (index == 0) { - firstFragmentGraphId = graphId + // Now connect selecting an item with swapping Fragments + private var selectedItemTag: String + private var isOnFirstFragment: Boolean + + init { + // First create a NavHostFragment for each NavGraph ID + navGraphIds.forEachIndexed { index, navGraphId -> + val fragmentTag = getFragmentTag(index) + + // Find or create the Navigation host fragment + val navHostFragment = obtainNavHostFragment( + fragmentManager, + fragmentTag, + navGraphId, + containerId + ) + + // Obtain its id + val graphId = navHostFragment.navController.graph.id + + if (index == 0) firstFragmentGraphId = graphId + + // Save to the map + graphIdToTagMap[graphId] = fragmentTag + + // Attach or detach nav host fragment depending on whether it's the selected item. + if (getSelectedItemId() == graphId) { + // Update livedata with the selected graph + _selectedNavController.value = navHostFragment.navController + attachNavHostFragment(fragmentManager, navHostFragment, index == 0) + } else { + detachNavHostFragment(fragmentManager, navHostFragment) + } } - // Save to the map - graphIdToTagMap[graphId] = fragmentTag - - // Attach or detach nav host fragment depending on whether it's the selected item. - if (this.selectedItemId == graphId) { - // Update livedata with the selected graph - selectedNavController.value = navHostFragment.navController - attachNavHostFragment(fragmentManager, navHostFragment, index == 0) - } else { - detachNavHostFragment(fragmentManager, navHostFragment) - } - } + selectedItemTag = graphIdToTagMap[getSelectedItemId()] + firstFragmentTag = graphIdToTagMap[firstFragmentGraphId] + isOnFirstFragment = selectedItemTag == firstFragmentTag - // Now connect selecting an item with swapping Fragments - var selectedItemTag = graphIdToTagMap[this.selectedItemId] - val firstFragmentTag = graphIdToTagMap[firstFragmentGraphId] - var isOnFirstFragment = selectedItemTag == firstFragmentTag + // Finally, ensure that we update our BottomNavigationView when the back stack changes + fragmentManager.addOnBackStackChangedListener { + if (!isOnFirstFragment && !fragmentManager.isOnBackStack(firstFragmentTag)) { + setSelectedItemId(firstFragmentGraphId) + } - // When a navigation item is selected - setOnNavigationItemSelectedListener { item -> - // Don't do anything if the state is state has already been saved. - if (fragmentManager.isStateSaved) { - false - } else { - val newlySelectedItemTag = graphIdToTagMap[item.itemId] - if (selectedItemTag != newlySelectedItemTag) { - // Pop everything above the first fragment (the "fixed start destination") - fragmentManager.popBackStack( - firstFragmentTag, - FragmentManager.POP_BACK_STACK_INCLUSIVE - ) - val selectedFragment = fragmentManager.findFragmentByTag(newlySelectedItemTag) - as NavHostFragment - - // Exclude the first fragment tag because it's always in the back stack. - if (firstFragmentTag != newlySelectedItemTag) { - // Commit a transaction that cleans the back stack and adds the first fragment - // to it, creating the fixed started destination. - fragmentManager.beginTransaction() - .setCustomAnimations( - R.anim.tivi_enter_anim, - R.anim.tivi_exit_anim, - R.anim.tivi_pop_enter_anim, - R.anim.tivi_pop_exit_anim - ) - .attach(selectedFragment) - .setPrimaryNavigationFragment(selectedFragment) - .apply { - // Detach all other Fragments - graphIdToTagMap.forEach { _, fragmentTagIter -> - if (fragmentTagIter != newlySelectedItemTag) { - detach(fragmentManager.findFragmentByTag(firstFragmentTag)!!) - } - } - } - .addToBackStack(firstFragmentTag) - .setReorderingAllowed(true) - .commit() + // Reset the graph if the currentDestination is not valid (happens when the back + // stack is popped after using the back button). + selectedNavController.value?.let { controller -> + if (controller.currentDestination == null) { + controller.navigate(controller.graph.id) } - selectedItemTag = newlySelectedItemTag - isOnFirstFragment = selectedItemTag == firstFragmentTag - selectedNavController.value = selectedFragment.navController - true - } else { - false } } + + setupDeepLinks() } - // Optional: on item reselected, pop back stack to the destination of the graph - setupItemReselected(graphIdToTagMap, fragmentManager) + fun onItemSelected(itemId: Int): Boolean { + // Don't do anything if the state is state has already been saved. + if (fragmentManager.isStateSaved) return false - // Handle deep link - setupDeepLinks(navGraphIds, fragmentManager, containerId, intent) + val newlySelectedItemTag = graphIdToTagMap[itemId] + if (selectedItemTag == newlySelectedItemTag) return false - // Finally, ensure that we update our BottomNavigationView when the back stack changes - fragmentManager.addOnBackStackChangedListener { - if (!isOnFirstFragment && !fragmentManager.isOnBackStack(firstFragmentTag)) { - this.selectedItemId = firstFragmentGraphId - } + // Pop everything above the first fragment (the "fixed start destination") + fragmentManager.popBackStack( + firstFragmentTag, + FragmentManager.POP_BACK_STACK_INCLUSIVE + ) + val selectedFragment = fragmentManager.findFragmentByTag(newlySelectedItemTag) + as NavHostFragment - // Reset the graph if the currentDestination is not valid (happens when the back - // stack is popped after using the back button). - selectedNavController.value?.let { controller -> - if (controller.currentDestination == null) { - controller.navigate(controller.graph.id) - } + // Exclude the first fragment tag because it's always in the back stack. + if (firstFragmentTag != newlySelectedItemTag) { + // Commit a transaction that cleans the back stack and adds the first fragment + // to it, creating the fixed started destination. + fragmentManager.beginTransaction() + .setCustomAnimations( + R.anim.tivi_enter_anim, + R.anim.tivi_exit_anim, + R.anim.tivi_pop_enter_anim, + R.anim.tivi_pop_exit_anim + ) + .attach(selectedFragment) + .setPrimaryNavigationFragment(selectedFragment) + .apply { + // Detach all other Fragments + graphIdToTagMap.forEach { _, fragmentTagIter -> + if (fragmentTagIter != newlySelectedItemTag) { + detach(fragmentManager.findFragmentByTag(firstFragmentTag)!!) + } + } + } + .addToBackStack(firstFragmentTag) + .setReorderingAllowed(true) + .commit() } - } - return selectedNavController -} + selectedItemTag = newlySelectedItemTag + isOnFirstFragment = selectedItemTag == firstFragmentTag + _selectedNavController.value = selectedFragment.navController -private fun BottomNavigationView.setupDeepLinks( - navGraphIds: List, - fragmentManager: FragmentManager, - containerId: Int, - intent: Intent -) { - navGraphIds.forEachIndexed { index, navGraphId -> - val fragmentTag = getFragmentTag(index) - - // Find or create the Navigation host fragment - val navHostFragment = obtainNavHostFragment( - fragmentManager, - fragmentTag, - navGraphId, - containerId - ) - // Handle Intent - if (navHostFragment.navController.handleDeepLink(intent) && - selectedItemId != navHostFragment.navController.graph.id - ) { - this.selectedItemId = navHostFragment.navController.graph.id - } + return true } -} -private fun BottomNavigationView.setupItemReselected( - graphIdToTagMap: SparseArray, - fragmentManager: FragmentManager -) { - setOnNavigationItemReselectedListener { item -> - val newlySelectedItemTag = graphIdToTagMap[item.itemId] + fun onReselected(itemId: Int) { + val newlySelectedItemTag = graphIdToTagMap[itemId] val selectedFragment = fragmentManager.findFragmentByTag(newlySelectedItemTag) as NavHostFragment val navController = selectedFragment.navController @@ -198,6 +162,24 @@ private fun BottomNavigationView.setupItemReselected( false ) } + + private fun setupDeepLinks() { + navGraphIds.forEachIndexed { index, navGraphId -> + val fragmentTag = getFragmentTag(index) + + // Find or create the Navigation host fragment + val navHostFragment = obtainNavHostFragment( + fragmentManager, + fragmentTag, + navGraphId, + containerId + ) + // Handle Intent + if (navHostFragment.navController.handleDeepLink(intent)) { + setSelectedItemId(navHostFragment.navController.graph.id) + } + } + } } private fun detachNavHostFragment( From 325dfb6ade6f0720879cccf6c55c8b000676f75a Mon Sep 17 00:00:00 2001 From: Chris Banes Date: Tue, 24 Nov 2020 09:26:09 +0000 Subject: [PATCH 02/17] Start implementing bottom nav in Compose --- app/build.gradle | 15 +++ app/src/main/java/app/tivi/home/Home.kt | 76 ++++++++++++ .../main/java/app/tivi/home/MainActivity.kt | 108 +++++++++--------- app/src/main/res/layout/activity_main.xml | 8 +- app/src/main/res/menu/home_bottomnav.xml | 40 ------- .../MultipleBackStackNavigationExtensions.kt | 23 ++-- 6 files changed, 159 insertions(+), 111 deletions(-) create mode 100644 app/src/main/java/app/tivi/home/Home.kt delete mode 100644 app/src/main/res/menu/home_bottomnav.xml diff --git a/app/build.gradle b/app/build.gradle index 0bcae37978..08cab1218e 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -118,6 +118,12 @@ android { buildFeatures { viewBinding true + compose true + } + + composeOptions { + kotlinCompilerVersion Libs.Kotlin.version + kotlinCompilerExtensionVersion Libs.AndroidX.Compose.version } testOptions { @@ -163,6 +169,7 @@ dependencies { implementation project(':base-android') implementation project(':common-ui-view') implementation project(':common-imageloading') + implementation project(':common-ui-compose') implementation project(':data') implementation project(':data-android') implementation project(':trakt') @@ -193,6 +200,14 @@ dependencies { implementation Libs.AndroidX.Navigation.fragment implementation Libs.AndroidX.Navigation.ui + implementation Libs.AndroidX.Compose.foundation + implementation Libs.AndroidX.Compose.ui + implementation Libs.AndroidX.Compose.layout + implementation Libs.AndroidX.Compose.material + implementation Libs.AndroidX.Compose.animation + implementation Libs.AndroidX.Compose.tooling + implementation Libs.AndroidX.Compose.livedata + implementation Libs.Mdc.material implementation Libs.Insetter.ktx diff --git a/app/src/main/java/app/tivi/home/Home.kt b/app/src/main/java/app/tivi/home/Home.kt new file mode 100644 index 0000000000..0a7bea7449 --- /dev/null +++ b/app/src/main/java/app/tivi/home/Home.kt @@ -0,0 +1,76 @@ +/* + * Copyright 2020 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package app.tivi.home + +import androidx.compose.material.BottomNavigation +import androidx.compose.material.BottomNavigationItem +import androidx.compose.material.Icon +import androidx.compose.material.Text +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.FavoriteBorder +import androidx.compose.material.icons.filled.Search +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import app.tivi.R +import app.tivi.common.compose.IconResource + +internal enum class HomeNavigation { + Discover, + Following, + Watched, + Search, +} + +@Composable +internal fun HomeBottomNavigation( + selectedNavigation: HomeNavigation, + onNavigationSelected: (HomeNavigation) -> Unit, + modifier: Modifier = Modifier +) { + BottomNavigation( + modifier = modifier + ) { + BottomNavigationItem( + icon = { IconResource(R.drawable.ic_weekend_black_24dp) }, + label = { Text(stringResource(R.string.discover_title)) }, + selected = selectedNavigation == HomeNavigation.Discover, + onClick = { onNavigationSelected(HomeNavigation.Discover) }, + ) + + BottomNavigationItem( + icon = { Icon(Icons.Default.FavoriteBorder) }, + label = { Text(stringResource(R.string.following_shows_title)) }, + selected = selectedNavigation == HomeNavigation.Following, + onClick = { onNavigationSelected(HomeNavigation.Following) }, + ) + + BottomNavigationItem( + icon = { IconResource(R.drawable.ic_visibility) }, + label = { Text(stringResource(R.string.watched_shows_title)) }, + selected = selectedNavigation == HomeNavigation.Watched, + onClick = { onNavigationSelected(HomeNavigation.Watched) }, + ) + + BottomNavigationItem( + icon = { Icon(Icons.Default.Search) }, + label = { Text(stringResource(R.string.search_navigation_title)) }, + selected = selectedNavigation == HomeNavigation.Search, + onClick = { onNavigationSelected(HomeNavigation.Search) }, + ) + } +} diff --git a/app/src/main/java/app/tivi/home/MainActivity.kt b/app/src/main/java/app/tivi/home/MainActivity.kt index 345d3fc9d2..39250f5726 100644 --- a/app/src/main/java/app/tivi/home/MainActivity.kt +++ b/app/src/main/java/app/tivi/home/MainActivity.kt @@ -18,6 +18,12 @@ package app.tivi.home import android.content.Intent import android.os.Bundle +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.core.view.WindowCompat import androidx.lifecycle.LiveData import androidx.lifecycle.ViewModelProvider import androidx.navigation.NavController @@ -29,8 +35,6 @@ import app.tivi.extensions.MultipleBackStackNavigation import app.tivi.extensions.hideSoftInput import app.tivi.trakt.TraktConstants import dagger.hilt.android.AndroidEntryPoint -import dev.chrisbanes.insetter.Insetter -import dev.chrisbanes.insetter.Side import javax.inject.Inject @AndroidEntryPoint @@ -44,75 +48,35 @@ class MainActivity : TiviActivity() { @Inject lateinit var navigator: AppNavigator + private var currentSelectedItem by mutableStateOf(HomeNavigation.Discover) + + private lateinit var multiBackStackNavigation: MultipleBackStackNavigation + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) + WindowCompat.setDecorFitsSystemWindows(window, false) + viewModel = ViewModelProvider(this).get(MainActivityViewModel::class.java) binding = ActivityMainBinding.inflate(layoutInflater) setContentView(binding.root) - Insetter.builder() - .applySystemWindowInsetsToPadding(Side.LEFT or Side.RIGHT) - .consumeSystemWindowInsets(Insetter.CONSUME_AUTO) - .applyToView(binding.root) - - Insetter.builder() - .applySystemWindowInsetsToPadding(Side.BOTTOM) - .consumeSystemWindowInsets(Insetter.CONSUME_AUTO) - .applyToView(binding.homeBottomNavigation) - - Insetter.setEdgeToEdgeSystemUiFlags(binding.homeRoot, true) - - if (savedInstanceState == null) { - setupBottomNavigationBar() - } - } - - override fun onRestoreInstanceState(savedInstanceState: Bundle) { - super.onRestoreInstanceState(savedInstanceState) - // Now that BottomNavigationBar has restored its instance state - // and its selectedItemId, we can proceed with setting up the - // BottomNavigationBar with Navigation - setupBottomNavigationBar() - } - - override fun handleIntent(intent: Intent) { - when (intent.action) { - TraktConstants.INTENT_ACTION_HANDLE_AUTH_RESPONSE -> { - navigator.onAuthResponse(intent) - } - } - } - - private fun setupBottomNavigationBar() { - val multiBackStackNavigation = MultipleBackStackNavigation( - navGraphIds = listOf( + multiBackStackNavigation = MultipleBackStackNavigation( + navGraphIds = intArrayOf( R.navigation.discover_nav_graph, - R.navigation.watched_nav_graph, R.navigation.following_nav_graph, + R.navigation.watched_nav_graph, R.navigation.search_nav_graph ), fragmentManager = supportFragmentManager, containerId = R.id.home_nav_container, intent = intent, - getSelectedItemId = { - binding.homeBottomNavigation.selectedItemId - }, - setSelectedItemId = { - binding.homeBottomNavigation.selectedItemId = it - }, + getSelectedItemId = { currentSelectedItem.toNavigationId() }, + setSelectedItemId = { currentSelectedItem = navigationIdToHomeNavigation(it) }, ) - binding.homeBottomNavigation.setOnNavigationItemReselectedListener { - multiBackStackNavigation.onReselected(it.itemId) - } - binding.homeBottomNavigation.setOnNavigationItemSelectedListener { - multiBackStackNavigation.onItemSelected(it.itemId) - } - currentNavController = multiBackStackNavigation.selectedNavController - currentNavController?.observe(this) { navController -> navController.addOnDestinationChangedListener { _, destination, _ -> if (destination.id != R.id.navigation_search) { @@ -120,9 +84,47 @@ class MainActivity : TiviActivity() { } } } + + binding.homeBottomNavigation.setContent { + HomeBottomNavigation( + selectedNavigation = currentSelectedItem, + onNavigationSelected = { item -> + if (currentSelectedItem == item) { + multiBackStackNavigation.onReselected(item.toNavigationId()) + } else { + currentSelectedItem = item + multiBackStackNavigation.onItemSelected(item.toNavigationId()) + } + }, + modifier = Modifier.fillMaxWidth() + ) + } + } + + override fun handleIntent(intent: Intent) { + when (intent.action) { + TraktConstants.INTENT_ACTION_HANDLE_AUTH_RESPONSE -> { + navigator.onAuthResponse(intent) + } + } } override fun onSupportNavigateUp(): Boolean { return currentNavController?.value?.navigateUp() ?: super.onSupportNavigateUp() } } + +private fun HomeNavigation.toNavigationId(): Int = when (this) { + HomeNavigation.Discover -> R.navigation.discover_nav_graph + HomeNavigation.Following -> R.navigation.following_nav_graph + HomeNavigation.Watched -> R.navigation.watched_nav_graph + HomeNavigation.Search -> R.navigation.search_nav_graph +} + +private fun navigationIdToHomeNavigation(id: Int): HomeNavigation = when (id) { + R.navigation.discover_nav_graph -> HomeNavigation.Discover + R.navigation.following_nav_graph -> HomeNavigation.Following + R.navigation.watched_nav_graph -> HomeNavigation.Watched + R.navigation.search_nav_graph -> HomeNavigation.Search + else -> throw IllegalArgumentException("Navigation graph with id not found: $id") +} diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 9950874202..1ee9df12ed 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -24,15 +24,13 @@ tools:context=".home.MainActivity"> - + app:layout_constraintStart_toStartOf="parent" /> - - - - - - - - - - - - - \ No newline at end of file diff --git a/common-ui-view/src/main/java/app/tivi/extensions/MultipleBackStackNavigationExtensions.kt b/common-ui-view/src/main/java/app/tivi/extensions/MultipleBackStackNavigationExtensions.kt index 5e617e16b2..ca30f0a4fd 100644 --- a/common-ui-view/src/main/java/app/tivi/extensions/MultipleBackStackNavigationExtensions.kt +++ b/common-ui-view/src/main/java/app/tivi/extensions/MultipleBackStackNavigationExtensions.kt @@ -28,7 +28,7 @@ import androidx.navigation.fragment.NavHostFragment import app.tivi.common.ui.R class MultipleBackStackNavigation( - private val navGraphIds: List, + private val navGraphIds: IntArray, private val fragmentManager: FragmentManager, private val containerId: Int, private val intent: Intent, @@ -52,6 +52,8 @@ class MultipleBackStackNavigation( private var isOnFirstFragment: Boolean init { + val currentSelectedId = getSelectedItemId() + // First create a NavHostFragment for each NavGraph ID navGraphIds.forEachIndexed { index, navGraphId -> val fragmentTag = getFragmentTag(index) @@ -61,19 +63,16 @@ class MultipleBackStackNavigation( fragmentManager, fragmentTag, navGraphId, - containerId + containerId, ) - // Obtain its id - val graphId = navHostFragment.navController.graph.id - - if (index == 0) firstFragmentGraphId = graphId + if (index == 0) firstFragmentGraphId = navGraphId // Save to the map - graphIdToTagMap[graphId] = fragmentTag + graphIdToTagMap[navGraphId] = fragmentTag // Attach or detach nav host fragment depending on whether it's the selected item. - if (getSelectedItemId() == graphId) { + if (currentSelectedId == navGraphId) { // Update livedata with the selected graph _selectedNavController.value = navHostFragment.navController attachNavHostFragment(fragmentManager, navHostFragment, index == 0) @@ -82,7 +81,7 @@ class MultipleBackStackNavigation( } } - selectedItemTag = graphIdToTagMap[getSelectedItemId()] + selectedItemTag = graphIdToTagMap[currentSelectedId] firstFragmentTag = graphIdToTagMap[firstFragmentGraphId] isOnFirstFragment = selectedItemTag == firstFragmentTag @@ -112,10 +111,8 @@ class MultipleBackStackNavigation( if (selectedItemTag == newlySelectedItemTag) return false // Pop everything above the first fragment (the "fixed start destination") - fragmentManager.popBackStack( - firstFragmentTag, - FragmentManager.POP_BACK_STACK_INCLUSIVE - ) + fragmentManager.popBackStack(firstFragmentTag, FragmentManager.POP_BACK_STACK_INCLUSIVE) + val selectedFragment = fragmentManager.findFragmentByTag(newlySelectedItemTag) as NavHostFragment From ef3e033e08276e98c0ec16cebf3d7cad3d7cc895 Mon Sep 17 00:00:00 2001 From: Chris Banes Date: Tue, 24 Nov 2020 10:44:17 +0000 Subject: [PATCH 03/17] Update bottom nav visuals --- app/build.gradle | 2 + app/src/main/java/app/tivi/home/Home.kt | 70 ++++++++++++------- .../main/java/app/tivi/home/MainActivity.kt | 27 +++---- 3 files changed, 61 insertions(+), 38 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 08cab1218e..83b01c3321 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -208,6 +208,8 @@ dependencies { implementation Libs.AndroidX.Compose.tooling implementation Libs.AndroidX.Compose.livedata + implementation Libs.Accompanist.insets + implementation Libs.Mdc.material implementation Libs.Insetter.ktx diff --git a/app/src/main/java/app/tivi/home/Home.kt b/app/src/main/java/app/tivi/home/Home.kt index 0a7bea7449..06c2bb361f 100644 --- a/app/src/main/java/app/tivi/home/Home.kt +++ b/app/src/main/java/app/tivi/home/Home.kt @@ -16,18 +16,26 @@ package app.tivi.home -import androidx.compose.material.BottomNavigation +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.preferredHeight import androidx.compose.material.BottomNavigationItem import androidx.compose.material.Icon +import androidx.compose.material.MaterialTheme +import androidx.compose.material.Surface import androidx.compose.material.Text +import androidx.compose.material.contentColorFor import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.FavoriteBorder import androidx.compose.material.icons.filled.Search import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp import app.tivi.R import app.tivi.common.compose.IconResource +import dev.chrisbanes.accompanist.insets.navigationBarsPadding internal enum class HomeNavigation { Discover, @@ -42,35 +50,45 @@ internal fun HomeBottomNavigation( onNavigationSelected: (HomeNavigation) -> Unit, modifier: Modifier = Modifier ) { - BottomNavigation( + Surface( + color = MaterialTheme.colors.surface, + contentColor = contentColorFor(MaterialTheme.colors.surface), + elevation = 8.dp, modifier = modifier ) { - BottomNavigationItem( - icon = { IconResource(R.drawable.ic_weekend_black_24dp) }, - label = { Text(stringResource(R.string.discover_title)) }, - selected = selectedNavigation == HomeNavigation.Discover, - onClick = { onNavigationSelected(HomeNavigation.Discover) }, - ) + Row( + Modifier.fillMaxWidth() + .preferredHeight(56.dp) + .navigationBarsPadding(), + horizontalArrangement = Arrangement.SpaceBetween, + ) { + BottomNavigationItem( + icon = { IconResource(R.drawable.ic_weekend_black_24dp) }, + label = { Text(stringResource(R.string.discover_title)) }, + selected = selectedNavigation == HomeNavigation.Discover, + onClick = { onNavigationSelected(HomeNavigation.Discover) }, + ) - BottomNavigationItem( - icon = { Icon(Icons.Default.FavoriteBorder) }, - label = { Text(stringResource(R.string.following_shows_title)) }, - selected = selectedNavigation == HomeNavigation.Following, - onClick = { onNavigationSelected(HomeNavigation.Following) }, - ) + BottomNavigationItem( + icon = { Icon(Icons.Default.FavoriteBorder) }, + label = { Text(stringResource(R.string.following_shows_title)) }, + selected = selectedNavigation == HomeNavigation.Following, + onClick = { onNavigationSelected(HomeNavigation.Following) }, + ) - BottomNavigationItem( - icon = { IconResource(R.drawable.ic_visibility) }, - label = { Text(stringResource(R.string.watched_shows_title)) }, - selected = selectedNavigation == HomeNavigation.Watched, - onClick = { onNavigationSelected(HomeNavigation.Watched) }, - ) + BottomNavigationItem( + icon = { IconResource(R.drawable.ic_visibility) }, + label = { Text(stringResource(R.string.watched_shows_title)) }, + selected = selectedNavigation == HomeNavigation.Watched, + onClick = { onNavigationSelected(HomeNavigation.Watched) }, + ) - BottomNavigationItem( - icon = { Icon(Icons.Default.Search) }, - label = { Text(stringResource(R.string.search_navigation_title)) }, - selected = selectedNavigation == HomeNavigation.Search, - onClick = { onNavigationSelected(HomeNavigation.Search) }, - ) + BottomNavigationItem( + icon = { Icon(Icons.Default.Search) }, + label = { Text(stringResource(R.string.search_navigation_title)) }, + selected = selectedNavigation == HomeNavigation.Search, + onClick = { onNavigationSelected(HomeNavigation.Search) }, + ) + } } } diff --git a/app/src/main/java/app/tivi/home/MainActivity.kt b/app/src/main/java/app/tivi/home/MainActivity.kt index 39250f5726..0522fa5902 100644 --- a/app/src/main/java/app/tivi/home/MainActivity.kt +++ b/app/src/main/java/app/tivi/home/MainActivity.kt @@ -30,6 +30,7 @@ import androidx.navigation.NavController import app.tivi.AppNavigator import app.tivi.R import app.tivi.TiviActivity +import app.tivi.common.compose.TiviContentSetup import app.tivi.databinding.ActivityMainBinding import app.tivi.extensions.MultipleBackStackNavigation import app.tivi.extensions.hideSoftInput @@ -86,18 +87,20 @@ class MainActivity : TiviActivity() { } binding.homeBottomNavigation.setContent { - HomeBottomNavigation( - selectedNavigation = currentSelectedItem, - onNavigationSelected = { item -> - if (currentSelectedItem == item) { - multiBackStackNavigation.onReselected(item.toNavigationId()) - } else { - currentSelectedItem = item - multiBackStackNavigation.onItemSelected(item.toNavigationId()) - } - }, - modifier = Modifier.fillMaxWidth() - ) + TiviContentSetup { + HomeBottomNavigation( + selectedNavigation = currentSelectedItem, + onNavigationSelected = { item -> + if (currentSelectedItem == item) { + multiBackStackNavigation.onReselected(item.toNavigationId()) + } else { + currentSelectedItem = item + multiBackStackNavigation.onItemSelected(item.toNavigationId()) + } + }, + modifier = Modifier.fillMaxWidth() + ) + } } } From 1b8527c73f03ee9b30d6cf5f6f66f88528fbcc1c Mon Sep 17 00:00:00 2001 From: Chris Banes Date: Tue, 24 Nov 2020 11:47:41 +0000 Subject: [PATCH 04/17] Start removing AppCompat + MDC --- app/build.gradle | 8 --- .../main/java/app/tivi/home/MainActivity.kt | 4 +- .../navigation/account_settings_nav_graph.xml | 9 +-- app/src/main/res/values-night/themes.xml | 16 +++-- app/src/main/res/values-v27/themes.xml | 24 ------- app/src/main/res/values/attrs.xml | 36 ---------- app/src/main/res/values/themes.xml | 44 +++--------- .../java/app/tivi/buildsrc/dependencies.kt | 11 +-- common-ui-compose/build.gradle | 3 - .../java/app/tivi/common/compose/TiviSetup.kt | 7 +- common-ui-resources/build.gradle | 5 -- .../main/res/color/color_on_background_10.xml | 20 ------ .../main/res/drawable/ic_calendar_today.xml | 2 +- .../src/main/res/drawable/ic_cloud_upload.xml | 2 +- .../src/main/res/drawable/ic_delete_sweep.xml | 2 +- .../src/main/res/drawable/ic_favorite.xml | 2 +- .../main/res/drawable/ic_favorite_outline.xml | 2 +- .../src/main/res/drawable/ic_publish.xml | 2 +- .../src/main/res/drawable/ic_search_24dp.xml | 2 +- .../main/res/drawable/ic_sort_black_24dp.xml | 2 +- .../main/res/drawable/ic_visibility_off.xml | 2 +- common-ui-view/build.gradle | 3 - .../src/main/java/app/tivi/TiviActivity.kt | 4 +- .../java/app/tivi/home/HomeTextCreator.kt | 39 ++--------- common-ui-view/src/main/res/values/ids.xml | 1 - ui-account/build.gradle | 3 - .../app/tivi/account/AccountUiFragment.kt | 4 +- ui-discover/build.gradle | 4 -- ui-episodedetails/build.gradle | 3 - .../episodedetails/EpisodeDetailsFragment.kt | 7 +- ui-followed/build.gradle | 4 -- ui-popular/build.gradle | 3 - ui-recommended/build.gradle | 3 - ui-search/build.gradle | 4 -- ui-settings/build.gradle | 14 +--- ui-settings/src/main/AndroidManifest.xml | 7 ++ .../app/tivi/settings/SettingsActivity.kt | 48 +++++++++++++ .../app/tivi/settings/SettingsFragment.kt | 51 -------------- .../java/app/tivi/settings/SettingsInject.kt | 4 +- .../settings/SettingsPreferenceFragment.kt | 69 +++++++++---------- .../app/tivi/settings/TiviPreferencesImpl.kt | 13 ++-- .../src/main/res/layout/fragment_settings.xml | 46 ------------- ui-settings/src/main/res/xml/preferences.xml | 20 +----- ui-showdetails/build.gradle | 4 -- ui-trending/build.gradle | 3 - ui-watched/build.gradle | 4 -- 46 files changed, 149 insertions(+), 421 deletions(-) delete mode 100644 app/src/main/res/values-v27/themes.xml delete mode 100644 app/src/main/res/values/attrs.xml delete mode 100644 common-ui-resources/src/main/res/color/color_on_background_10.xml create mode 100644 ui-settings/src/main/java/app/tivi/settings/SettingsActivity.kt delete mode 100644 ui-settings/src/main/java/app/tivi/settings/SettingsFragment.kt delete mode 100644 ui-settings/src/main/res/layout/fragment_settings.xml diff --git a/app/build.gradle b/app/build.gradle index 83b01c3321..54da334d72 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -26,9 +26,6 @@ plugins { id 'kotlin-android' id 'kotlin-kapt' - - id 'com.google.android.gms.oss-licenses-plugin' - id 'androidx.navigation.safeargs.kotlin' } play { @@ -193,7 +190,6 @@ dependencies { implementation Libs.AndroidX.Lifecycle.livedata implementation Libs.AndroidX.Lifecycle.viewmodel - implementation Libs.AndroidX.appcompat implementation Libs.AndroidX.emoji implementation Libs.AndroidX.constraintlayout implementation Libs.AndroidX.Fragment.fragmentKtx @@ -210,10 +206,6 @@ dependencies { implementation Libs.Accompanist.insets - implementation Libs.Mdc.material - - implementation Libs.Insetter.ktx - implementation Libs.threeTenAbp implementation Libs.timber diff --git a/app/src/main/java/app/tivi/home/MainActivity.kt b/app/src/main/java/app/tivi/home/MainActivity.kt index 0522fa5902..65fcda99d1 100644 --- a/app/src/main/java/app/tivi/home/MainActivity.kt +++ b/app/src/main/java/app/tivi/home/MainActivity.kt @@ -112,8 +112,8 @@ class MainActivity : TiviActivity() { } } - override fun onSupportNavigateUp(): Boolean { - return currentNavController?.value?.navigateUp() ?: super.onSupportNavigateUp() + override fun onNavigateUp(): Boolean { + return currentNavController?.value?.navigateUp() ?: super.onNavigateUp() } } diff --git a/app/src/main/res/navigation/account_settings_nav_graph.xml b/app/src/main/res/navigation/account_settings_nav_graph.xml index cfae7abeb5..258f45e03b 100644 --- a/app/src/main/res/navigation/account_settings_nav_graph.xml +++ b/app/src/main/res/navigation/account_settings_nav_graph.xml @@ -29,14 +29,9 @@ - - - \ No newline at end of file diff --git a/app/src/main/res/values-night/themes.xml b/app/src/main/res/values-night/themes.xml index 0e3b6cb6a0..da16907623 100644 --- a/app/src/main/res/values-night/themes.xml +++ b/app/src/main/res/values-night/themes.xml @@ -16,15 +16,17 @@ --> + diff --git a/app/src/main/res/values-v27/themes.xml b/app/src/main/res/values-v27/themes.xml deleted file mode 100644 index 7196417501..0000000000 --- a/app/src/main/res/values-v27/themes.xml +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - diff --git a/app/src/main/res/values/attrs.xml b/app/src/main/res/values/attrs.xml deleted file mode 100644 index 371bb56e20..0000000000 --- a/app/src/main/res/values/attrs.xml +++ /dev/null @@ -1,36 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml index 6bcfdecc26..8ac8b7cb7c 100644 --- a/app/src/main/res/values/themes.xml +++ b/app/src/main/res/values/themes.xml @@ -18,47 +18,27 @@ - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/build.gradle b/build.gradle index cb4ed1beb1..2dc5dd90df 100644 --- a/build.gradle +++ b/build.gradle @@ -44,9 +44,6 @@ buildscript { classpath Libs.Google.gmsGoogleServices classpath Libs.Google.crashlyticsGradle - classpath Libs.Google.openSourceLicensesPlugin - - classpath Libs.AndroidX.Navigation.safeArgs classpath Libs.Hilt.gradlePlugin } @@ -78,6 +75,12 @@ allprojects { subprojects { configurations.configureEach { + // We forcefully exclude AppCompat + MDC from any transitive dependencies. + // This is a Compose app, so there's no need for these. + exclude group: 'androidx.appcompat', module: 'appcompat' + exclude group: 'com.google.android.material', module: 'material' + exclude group: 'com.google.android.material', module: 'material' + resolutionStrategy.eachDependency { DependencyResolveDetails details -> def group = details.requested.group def module = details.requested.module.name diff --git a/buildSrc/src/main/java/app/tivi/buildsrc/dependencies.kt b/buildSrc/src/main/java/app/tivi/buildsrc/dependencies.kt index 82f038ee62..7daf938abe 100644 --- a/buildSrc/src/main/java/app/tivi/buildsrc/dependencies.kt +++ b/buildSrc/src/main/java/app/tivi/buildsrc/dependencies.kt @@ -69,9 +69,6 @@ object Libs { const val crashlyticsGradle = "com.google.firebase:firebase-crashlytics-gradle:2.3.0" const val gmsGoogleServices = "com.google.gms:google-services:4.3.4" - - const val openSourceLicensesPlugin = "com.google.android.gms:oss-licenses-plugin:0.10.2" - const val openSourceLicensesLibrary = "com.google.android.gms:play-services-oss-licenses:17.0.0" } object Kotlin { @@ -97,8 +94,6 @@ object Libs { object Navigation { private const val version = "2.3.1" const val fragment = "androidx.navigation:navigation-fragment-ktx:$version" - const val ui = "androidx.navigation:navigation-ui-ktx:$version" - const val safeArgs = "androidx.navigation:navigation-safe-args-gradle-plugin:$version" } object Fragment { @@ -133,8 +128,6 @@ object Libs { const val compose = "androidx.paging:paging-compose:1.0.0-alpha02" } - const val constraintlayout = "androidx.constraintlayout:constraintlayout:2.0.2" - const val coreKtx = "androidx.core:core-ktx:1.5.0-alpha04" object Lifecycle { diff --git a/common-ui-compose/src/main/java/app/tivi/common/compose/TiviSetup.kt b/common-ui-compose/src/main/java/app/tivi/common/compose/TiviSetup.kt index aa615a5b92..b9731cc256 100644 --- a/common-ui-compose/src/main/java/app/tivi/common/compose/TiviSetup.kt +++ b/common-ui-compose/src/main/java/app/tivi/common/compose/TiviSetup.kt @@ -18,6 +18,7 @@ package app.tivi.common.compose +import androidx.compose.material.MaterialTheme import androidx.compose.runtime.Composable import dev.chrisbanes.accompanist.insets.ProvideWindowInsets @@ -26,7 +27,9 @@ import dev.chrisbanes.accompanist.insets.ProvideWindowInsets */ @Composable inline fun TiviContentSetup(noinline content: @Composable () -> Unit) { - ProvideWindowInsets { - content() + MaterialTheme { + ProvideWindowInsets { + content() + } } } diff --git a/common-ui-view/build.gradle b/common-ui-view/build.gradle index a223ef4602..195c1cb2c6 100644 --- a/common-ui-view/build.gradle +++ b/common-ui-view/build.gradle @@ -48,7 +48,6 @@ dependencies { implementation Libs.AndroidX.Fragment.fragment implementation Libs.AndroidX.Fragment.fragmentKtx implementation Libs.AndroidX.Navigation.fragment - implementation Libs.AndroidX.Navigation.ui implementation Libs.Hilt.library } \ No newline at end of file From 30ab83beaf9d122d3c6d736c4923bbfe447a6516 Mon Sep 17 00:00:00 2001 From: Chris Banes Date: Tue, 1 Dec 2020 10:48:04 +0000 Subject: [PATCH 07/17] Implement Tivi's theme in Compose --- .../java/app/tivi/common/compose/TiviSetup.kt | 4 +- .../app/tivi/common/compose/theme/Color.kt | 58 +++++++++++++++++++ .../app/tivi/common/compose/theme/Shape.kt | 24 ++++++++ .../app/tivi/common/compose/theme/Theme.kt | 34 +++++++++++ .../app/tivi/common/compose/theme/Type.kt | 32 ++++++++++ 5 files changed, 150 insertions(+), 2 deletions(-) create mode 100644 common-ui-compose/src/main/java/app/tivi/common/compose/theme/Color.kt create mode 100644 common-ui-compose/src/main/java/app/tivi/common/compose/theme/Shape.kt create mode 100644 common-ui-compose/src/main/java/app/tivi/common/compose/theme/Theme.kt create mode 100644 common-ui-compose/src/main/java/app/tivi/common/compose/theme/Type.kt diff --git a/common-ui-compose/src/main/java/app/tivi/common/compose/TiviSetup.kt b/common-ui-compose/src/main/java/app/tivi/common/compose/TiviSetup.kt index b9731cc256..d273186f38 100644 --- a/common-ui-compose/src/main/java/app/tivi/common/compose/TiviSetup.kt +++ b/common-ui-compose/src/main/java/app/tivi/common/compose/TiviSetup.kt @@ -18,8 +18,8 @@ package app.tivi.common.compose -import androidx.compose.material.MaterialTheme import androidx.compose.runtime.Composable +import app.tivi.common.compose.theme.TiviTheme import dev.chrisbanes.accompanist.insets.ProvideWindowInsets /** @@ -27,7 +27,7 @@ import dev.chrisbanes.accompanist.insets.ProvideWindowInsets */ @Composable inline fun TiviContentSetup(noinline content: @Composable () -> Unit) { - MaterialTheme { + TiviTheme { ProvideWindowInsets { content() } diff --git a/common-ui-compose/src/main/java/app/tivi/common/compose/theme/Color.kt b/common-ui-compose/src/main/java/app/tivi/common/compose/theme/Color.kt new file mode 100644 index 0000000000..057a063965 --- /dev/null +++ b/common-ui-compose/src/main/java/app/tivi/common/compose/theme/Color.kt @@ -0,0 +1,58 @@ +/* + * Copyright 2020 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package app.tivi.common.compose.theme + +import androidx.compose.material.Colors +import androidx.compose.material.darkColors +import androidx.compose.material.lightColors +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.compositeOver + +/** + * Return the fully opaque color that results from compositing [onSurface] atop [surface] with the + * given [alpha]. Useful for situations where semi-transparent colors are undesirable. + */ +fun Colors.compositedOnSurface(alpha: Float): Color { + return onSurface.copy(alpha = alpha).compositeOver(surface) +} + +private val Slate200 = Color(0xFF81A9B3) +private val Slate600 = Color(0xFF4A6572) +private val Slate800 = Color(0xFF232F34) + +private val Orange500 = Color(0xFFF9AA33) +private val Orange700 = Color(0xFFC78522) + +val TiviLightColors = lightColors( + primary = Slate800, + onPrimary = Color.White, + primaryVariant = Slate600, + secondary = Orange700, + secondaryVariant = Orange500, + onSecondary = Color.Black, +) + +val TiviDarkColors = darkColors( + primary = Slate200, + onPrimary = Color.Black, + secondary = Orange500, + onSecondary = Color.Black, +).withBrandedSurface() + +fun Colors.withBrandedSurface() = copy( + surface = primary.copy(alpha = 0.08f).compositeOver(this.surface), +) diff --git a/common-ui-compose/src/main/java/app/tivi/common/compose/theme/Shape.kt b/common-ui-compose/src/main/java/app/tivi/common/compose/theme/Shape.kt new file mode 100644 index 0000000000..9a7d009b68 --- /dev/null +++ b/common-ui-compose/src/main/java/app/tivi/common/compose/theme/Shape.kt @@ -0,0 +1,24 @@ +/* + * Copyright 2020 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package app.tivi.common.compose.theme + +import androidx.compose.material.Shapes + +/** + * We intentionally use the defaults for now. + */ +val TiviShapes = Shapes() diff --git a/common-ui-compose/src/main/java/app/tivi/common/compose/theme/Theme.kt b/common-ui-compose/src/main/java/app/tivi/common/compose/theme/Theme.kt new file mode 100644 index 0000000000..d7ea73d49c --- /dev/null +++ b/common-ui-compose/src/main/java/app/tivi/common/compose/theme/Theme.kt @@ -0,0 +1,34 @@ +/* + * Copyright 2020 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package app.tivi.common.compose.theme + +import androidx.compose.foundation.isSystemInDarkTheme +import androidx.compose.material.MaterialTheme +import androidx.compose.runtime.Composable + +@Composable +fun TiviTheme( + useDarkColors: Boolean = isSystemInDarkTheme(), + content: @Composable () -> Unit +) { + MaterialTheme( + colors = if (useDarkColors) TiviDarkColors else TiviLightColors, + typography = TiviTypography, + shapes = TiviShapes, + content = content + ) +} diff --git a/common-ui-compose/src/main/java/app/tivi/common/compose/theme/Type.kt b/common-ui-compose/src/main/java/app/tivi/common/compose/theme/Type.kt new file mode 100644 index 0000000000..894ab20617 --- /dev/null +++ b/common-ui-compose/src/main/java/app/tivi/common/compose/theme/Type.kt @@ -0,0 +1,32 @@ +/* + * Copyright 2020 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package app.tivi.common.compose.theme + +import androidx.compose.material.Typography +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.font.font +import androidx.compose.ui.text.font.fontFamily +import app.tivi.common.compose.R + +private val Inter = fontFamily( + font(R.font.inter_300, FontWeight.Light), + font(R.font.inter_400, FontWeight.Normal), + font(R.font.inter_500, FontWeight.Medium), + font(R.font.inter_700, FontWeight.Bold) +) + +val TiviTypography = Typography(defaultFontFamily = Inter) From 8a53e489b271b8e5f366848137de500c8c2265f6 Mon Sep 17 00:00:00 2001 From: Chris Banes Date: Tue, 1 Dec 2020 10:54:10 +0000 Subject: [PATCH 08/17] Add account ui button layout --- .../main/java/app/tivi/account/AccountUi.kt | 35 ++++++++++++------- 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/ui-account/src/main/java/app/tivi/account/AccountUi.kt b/ui-account/src/main/java/app/tivi/account/AccountUi.kt index 99021c08b5..930fac14ff 100644 --- a/ui-account/src/main/java/app/tivi/account/AccountUi.kt +++ b/ui-account/src/main/java/app/tivi/account/AccountUi.kt @@ -17,7 +17,11 @@ package app.tivi.account import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.ExperimentalLayout +import androidx.compose.foundation.layout.FlowMainAxisAlignment +import androidx.compose.foundation.layout.FlowRow import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth @@ -26,6 +30,7 @@ import androidx.compose.foundation.layout.preferredHeight import androidx.compose.foundation.layout.preferredSize import androidx.compose.foundation.layout.preferredSizeIn import androidx.compose.foundation.layout.preferredWidth +import androidx.compose.foundation.layout.wrapContentSize import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.AmbientContentAlpha import androidx.compose.material.ContentAlpha @@ -53,6 +58,7 @@ import dev.chrisbanes.accompanist.coil.CoilImage import org.threeten.bp.OffsetDateTime import org.threeten.bp.ZoneOffset +@OptIn(ExperimentalLayout::class) @Composable fun AccountUi( viewState: AccountUiViewState, @@ -70,20 +76,25 @@ fun AccountUi( Spacer(modifier = Modifier.preferredHeight(16.dp)) } - Row( - modifier = Modifier.align(Alignment.End) - .padding(horizontal = 16.dp) + Box( + modifier = Modifier.padding(horizontal = 16.dp) + .wrapContentSize(Alignment.CenterEnd) + .align(Alignment.End) ) { - if (viewState.authState == TraktAuthState.LOGGED_OUT) { - OutlinedButton(onClick = { actioner(Login) }) { - Text(text = stringResource(R.string.login)) + FlowRow( + mainAxisAlignment = FlowMainAxisAlignment.End, + mainAxisSpacing = 8.dp, + crossAxisSpacing = 4.dp, + ) { + if (viewState.authState == TraktAuthState.LOGGED_OUT) { + OutlinedButton(onClick = { actioner(Login) }) { + Text(text = stringResource(R.string.login)) + } + } else { + TextButton(onClick = { actioner(Login) }) { + Text(text = stringResource(R.string.refresh_credentials)) + } } - } else { - TextButton(onClick = { actioner(Login) }) { - Text(text = stringResource(R.string.refresh_credentials)) - } - - Spacer(modifier = Modifier.preferredWidth(8.dp)) OutlinedButton(onClick = { actioner(Logout) }) { Text(text = stringResource(R.string.logout)) From 32682328e93bcafb82ecc12607d40eeae2fd1ca0 Mon Sep 17 00:00:00 2001 From: Chris Banes Date: Tue, 1 Dec 2020 11:47:17 +0000 Subject: [PATCH 09/17] Move episode details to normal fragment --- .../res/navigation/show_details_nav_graph.xml | 4 +-- .../main/res/anim/tivi_enter_bottom_anim.xml | 28 +++++++++++++++++++ .../main/res/anim/tivi_exit_bottom_anim.xml | 28 +++++++++++++++++++ ui-episodedetails/build.gradle | 1 + .../app/tivi/episodedetails/EpisodeDetails.kt | 4 ++- .../episodedetails/EpisodeDetailsFragment.kt | 7 +++-- .../details/ShowDetailsFragment.kt | 9 +++++- 7 files changed, 74 insertions(+), 7 deletions(-) create mode 100644 common-ui-view/src/main/res/anim/tivi_enter_bottom_anim.xml create mode 100644 common-ui-view/src/main/res/anim/tivi_exit_bottom_anim.xml diff --git a/app/src/main/res/navigation/show_details_nav_graph.xml b/app/src/main/res/navigation/show_details_nav_graph.xml index 4e9b323e98..0ad2f7d765 100644 --- a/app/src/main/res/navigation/show_details_nav_graph.xml +++ b/app/src/main/res/navigation/show_details_nav_graph.xml @@ -39,7 +39,7 @@ - @@ -50,6 +50,6 @@ - + \ No newline at end of file diff --git a/common-ui-view/src/main/res/anim/tivi_enter_bottom_anim.xml b/common-ui-view/src/main/res/anim/tivi_enter_bottom_anim.xml new file mode 100644 index 0000000000..e2cec494df --- /dev/null +++ b/common-ui-view/src/main/res/anim/tivi_enter_bottom_anim.xml @@ -0,0 +1,28 @@ + + + + + + + + \ No newline at end of file diff --git a/common-ui-view/src/main/res/anim/tivi_exit_bottom_anim.xml b/common-ui-view/src/main/res/anim/tivi_exit_bottom_anim.xml new file mode 100644 index 0000000000..8773820fbe --- /dev/null +++ b/common-ui-view/src/main/res/anim/tivi_exit_bottom_anim.xml @@ -0,0 +1,28 @@ + + + + + + + + \ No newline at end of file diff --git a/ui-episodedetails/build.gradle b/ui-episodedetails/build.gradle index e66b26b0e7..4330221d39 100644 --- a/ui-episodedetails/build.gradle +++ b/ui-episodedetails/build.gradle @@ -72,6 +72,7 @@ dependencies { implementation Libs.AndroidX.Fragment.fragment implementation Libs.AndroidX.Fragment.fragmentKtx + implementation Libs.AndroidX.Navigation.fragment implementation Libs.AndroidX.Compose.runtime implementation Libs.AndroidX.Compose.foundation diff --git a/ui-episodedetails/src/main/java/app/tivi/episodedetails/EpisodeDetails.kt b/ui-episodedetails/src/main/java/app/tivi/episodedetails/EpisodeDetails.kt index 889cd2989c..5bd69d881e 100644 --- a/ui-episodedetails/src/main/java/app/tivi/episodedetails/EpisodeDetails.kt +++ b/ui-episodedetails/src/main/java/app/tivi/episodedetails/EpisodeDetails.kt @@ -99,6 +99,7 @@ import app.tivi.ui.animations.lerp import dev.chrisbanes.accompanist.coil.CoilImage import dev.chrisbanes.accompanist.insets.navigationBarsHeight import dev.chrisbanes.accompanist.insets.navigationBarsPadding +import dev.chrisbanes.accompanist.insets.statusBarsPadding import kotlinx.coroutines.launch import org.threeten.bp.OffsetDateTime import kotlin.math.absoluteValue @@ -124,7 +125,8 @@ fun EpisodeDetails( backgroundColor = Color.Transparent, isRefreshing = viewState.refreshing, actioner = actioner, - elevation = 0.dp + elevation = 0.dp, + modifier = Modifier.fillMaxWidth().statusBarsPadding() ) } ScrollableColumn { diff --git a/ui-episodedetails/src/main/java/app/tivi/episodedetails/EpisodeDetailsFragment.kt b/ui-episodedetails/src/main/java/app/tivi/episodedetails/EpisodeDetailsFragment.kt index 28b6a9621d..b6deee9170 100644 --- a/ui-episodedetails/src/main/java/app/tivi/episodedetails/EpisodeDetailsFragment.kt +++ b/ui-episodedetails/src/main/java/app/tivi/episodedetails/EpisodeDetailsFragment.kt @@ -26,9 +26,10 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.livedata.observeAsState import androidx.compose.ui.platform.ComposeView import androidx.core.os.bundleOf -import androidx.fragment.app.DialogFragment +import androidx.fragment.app.Fragment import androidx.fragment.app.viewModels import androidx.lifecycle.lifecycleScope +import androidx.navigation.fragment.findNavController import app.tivi.common.compose.AmbientTiviDateFormatter import app.tivi.common.compose.TiviContentSetup import app.tivi.extensions.viewModelProviderFactoryOf @@ -41,7 +42,7 @@ import kotlinx.coroutines.launch import javax.inject.Inject @AndroidEntryPoint -class EpisodeDetailsFragment : DialogFragment() { +class EpisodeDetailsFragment : Fragment() { @Inject internal lateinit var vmFactory: EpisodeDetailsViewModel.Factory companion object { @@ -93,7 +94,7 @@ class EpisodeDetailsFragment : DialogFragment() { lifecycleScope.launch { pendingActions.consumeAsFlow().collect { action -> when (action) { - is Close -> requireView().post { dismiss() } + is Close -> requireView().post { findNavController().navigateUp() } else -> viewModel.submitAction(action) } } diff --git a/ui-showdetails/src/main/java/app/tivi/showdetails/details/ShowDetailsFragment.kt b/ui-showdetails/src/main/java/app/tivi/showdetails/details/ShowDetailsFragment.kt index f5183ad124..ca04add347 100644 --- a/ui-showdetails/src/main/java/app/tivi/showdetails/details/ShowDetailsFragment.kt +++ b/ui-showdetails/src/main/java/app/tivi/showdetails/details/ShowDetailsFragment.kt @@ -29,6 +29,7 @@ import androidx.core.net.toUri import androidx.fragment.app.Fragment import androidx.fragment.app.viewModels import androidx.lifecycle.lifecycleScope +import androidx.navigation.NavOptions import androidx.navigation.fragment.findNavController import app.tivi.common.compose.LogCompositions import app.tivi.common.compose.TiviContentSetup @@ -107,7 +108,13 @@ class ShowDetailsFragment : Fragment() { viewModel.submitAction(ClearPendingUiEffect(effect)) } is OpenEpisodeUiEffect -> { - findNavController().navigate("app.tivi://episode/${effect.episodeId}".toUri()) + findNavController().navigate( + "app.tivi://episode/${effect.episodeId}".toUri(), + NavOptions.Builder() + .setEnterAnim(R.anim.tivi_enter_bottom_anim) + .setExitAnim(R.anim.tivi_exit_bottom_anim) + .build() + ) viewModel.submitAction(ClearPendingUiEffect(effect)) } } From f6efdc105b0e7ca58693539420d262dc9e79a18e Mon Sep 17 00:00:00 2001 From: Chris Banes Date: Tue, 1 Dec 2020 12:02:18 +0000 Subject: [PATCH 10/17] Remove TiviContentSetup() and use TiviTheme directly --- .../main/java/app/tivi/home/MainActivity.kt | 31 ++++++++++-------- .../java/app/tivi/common/compose/TiviSetup.kt | 32 ------------------- .../app/tivi/account/AccountUiFragment.kt | 4 +-- .../tivi/home/discover/DiscoverFragment.kt | 4 +-- .../episodedetails/EpisodeDetailsFragment.kt | 4 +-- .../tivi/home/followed/FollowedFragment.kt | 4 +-- .../tivi/home/popular/PopularShowsFragment.kt | 4 +-- .../recommended/RecommendedShowsFragment.kt | 4 +-- .../app/tivi/home/search/SearchFragment.kt | 4 +-- .../details/ShowDetailsFragment.kt | 4 +-- .../home/trending/TrendingShowsFragment.kt | 4 +-- .../app/tivi/home/watched/WatchedFragment.kt | 4 +-- 12 files changed, 37 insertions(+), 66 deletions(-) delete mode 100644 common-ui-compose/src/main/java/app/tivi/common/compose/TiviSetup.kt diff --git a/app/src/main/java/app/tivi/home/MainActivity.kt b/app/src/main/java/app/tivi/home/MainActivity.kt index 65fcda99d1..66e932c1e3 100644 --- a/app/src/main/java/app/tivi/home/MainActivity.kt +++ b/app/src/main/java/app/tivi/home/MainActivity.kt @@ -30,12 +30,13 @@ import androidx.navigation.NavController import app.tivi.AppNavigator import app.tivi.R import app.tivi.TiviActivity -import app.tivi.common.compose.TiviContentSetup +import app.tivi.common.compose.theme.TiviTheme import app.tivi.databinding.ActivityMainBinding import app.tivi.extensions.MultipleBackStackNavigation import app.tivi.extensions.hideSoftInput import app.tivi.trakt.TraktConstants import dagger.hilt.android.AndroidEntryPoint +import dev.chrisbanes.accompanist.insets.ProvideWindowInsets import javax.inject.Inject @AndroidEntryPoint @@ -87,19 +88,21 @@ class MainActivity : TiviActivity() { } binding.homeBottomNavigation.setContent { - TiviContentSetup { - HomeBottomNavigation( - selectedNavigation = currentSelectedItem, - onNavigationSelected = { item -> - if (currentSelectedItem == item) { - multiBackStackNavigation.onReselected(item.toNavigationId()) - } else { - currentSelectedItem = item - multiBackStackNavigation.onItemSelected(item.toNavigationId()) - } - }, - modifier = Modifier.fillMaxWidth() - ) + ProvideWindowInsets { + TiviTheme { + HomeBottomNavigation( + selectedNavigation = currentSelectedItem, + onNavigationSelected = { item -> + if (currentSelectedItem == item) { + multiBackStackNavigation.onReselected(item.toNavigationId()) + } else { + currentSelectedItem = item + multiBackStackNavigation.onItemSelected(item.toNavigationId()) + } + }, + modifier = Modifier.fillMaxWidth() + ) + } } } } diff --git a/common-ui-compose/src/main/java/app/tivi/common/compose/TiviSetup.kt b/common-ui-compose/src/main/java/app/tivi/common/compose/TiviSetup.kt deleted file mode 100644 index 7749d7f62a..0000000000 --- a/common-ui-compose/src/main/java/app/tivi/common/compose/TiviSetup.kt +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright 2020 Google LLC - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -@file:Suppress("NOTHING_TO_INLINE") - -package app.tivi.common.compose - -import androidx.compose.runtime.Composable -import com.google.android.material.composethemeadapter.MdcTheme - -/** - * Just groups some common Compose content setup - */ -@Composable -inline fun TiviContentSetup(noinline content: @Composable () -> Unit) { - MdcTheme { - content() - } -} diff --git a/ui-account/src/main/java/app/tivi/account/AccountUiFragment.kt b/ui-account/src/main/java/app/tivi/account/AccountUiFragment.kt index 6ebd498c9d..6a27e2750e 100644 --- a/ui-account/src/main/java/app/tivi/account/AccountUiFragment.kt +++ b/ui-account/src/main/java/app/tivi/account/AccountUiFragment.kt @@ -31,7 +31,7 @@ import androidx.fragment.app.viewModels import androidx.lifecycle.lifecycleScope import androidx.navigation.fragment.findNavController import app.tivi.common.compose.AmbientTiviDateFormatter -import app.tivi.common.compose.TiviContentSetup +import app.tivi.common.compose.theme.TiviTheme import app.tivi.extensions.navigateToNavDestination import app.tivi.util.TiviDateFormatter import dagger.hilt.android.AndroidEntryPoint @@ -66,7 +66,7 @@ class AccountUiFragment : DialogFragment() { AmbientTiviDateFormatter provides tiviDateFormatter, AmbientWindowInsets provides windowInsets, ) { - TiviContentSetup { + TiviTheme { val viewState by viewModel.liveData.observeAsState() if (viewState != null) { AccountUi(viewState!!) { diff --git a/ui-discover/src/main/java/app/tivi/home/discover/DiscoverFragment.kt b/ui-discover/src/main/java/app/tivi/home/discover/DiscoverFragment.kt index 9e2048cd7f..3e56c5fda3 100644 --- a/ui-discover/src/main/java/app/tivi/home/discover/DiscoverFragment.kt +++ b/ui-discover/src/main/java/app/tivi/home/discover/DiscoverFragment.kt @@ -31,7 +31,7 @@ import androidx.fragment.app.viewModels import androidx.lifecycle.lifecycleScope import androidx.navigation.fragment.findNavController import app.tivi.common.compose.AmbientTiviDateFormatter -import app.tivi.common.compose.TiviContentSetup +import app.tivi.common.compose.theme.TiviTheme import app.tivi.util.TiviDateFormatter import dagger.hilt.android.AndroidEntryPoint import dev.chrisbanes.accompanist.insets.AmbientWindowInsets @@ -67,7 +67,7 @@ class DiscoverFragment : Fragment() { AmbientDiscoverTextCreator provides textCreator, AmbientWindowInsets provides windowInsets, ) { - TiviContentSetup { + TiviTheme { val viewState by viewModel.liveData.observeAsState() if (viewState != null) { Discover( diff --git a/ui-episodedetails/src/main/java/app/tivi/episodedetails/EpisodeDetailsFragment.kt b/ui-episodedetails/src/main/java/app/tivi/episodedetails/EpisodeDetailsFragment.kt index 6b996505ce..67682a5ab7 100644 --- a/ui-episodedetails/src/main/java/app/tivi/episodedetails/EpisodeDetailsFragment.kt +++ b/ui-episodedetails/src/main/java/app/tivi/episodedetails/EpisodeDetailsFragment.kt @@ -31,7 +31,7 @@ import androidx.fragment.app.viewModels import androidx.lifecycle.lifecycleScope import androidx.navigation.fragment.findNavController import app.tivi.common.compose.AmbientTiviDateFormatter -import app.tivi.common.compose.TiviContentSetup +import app.tivi.common.compose.theme.TiviTheme import app.tivi.extensions.viewModelProviderFactoryOf import app.tivi.util.TiviDateFormatter import dagger.hilt.android.AndroidEntryPoint @@ -85,7 +85,7 @@ class EpisodeDetailsFragment : Fragment() { AmbientTiviDateFormatter provides tiviDateFormatter, AmbientWindowInsets provides windowInsets, ) { - TiviContentSetup { + TiviTheme { val viewState by viewModel.liveData.observeAsState() if (viewState != null) { EpisodeDetails(viewState!!) { diff --git a/ui-followed/src/main/java/app/tivi/home/followed/FollowedFragment.kt b/ui-followed/src/main/java/app/tivi/home/followed/FollowedFragment.kt index f686ed7b50..ed9bdd4a03 100644 --- a/ui-followed/src/main/java/app/tivi/home/followed/FollowedFragment.kt +++ b/ui-followed/src/main/java/app/tivi/home/followed/FollowedFragment.kt @@ -33,7 +33,7 @@ import androidx.navigation.fragment.findNavController import androidx.paging.compose.collectAsLazyPagingItems import app.tivi.common.compose.AmbientHomeTextCreator import app.tivi.common.compose.AmbientTiviDateFormatter -import app.tivi.common.compose.TiviContentSetup +import app.tivi.common.compose.theme.TiviTheme import app.tivi.home.HomeTextCreator import app.tivi.util.TiviDateFormatter import dagger.hilt.android.AndroidEntryPoint @@ -70,7 +70,7 @@ class FollowedFragment : Fragment() { AmbientHomeTextCreator provides homeTextCreator, AmbientWindowInsets provides windowInsets, ) { - TiviContentSetup { + TiviTheme { val viewState by viewModel.liveData.observeAsState() if (viewState != null) { Followed( diff --git a/ui-popular/src/main/java/app/tivi/home/popular/PopularShowsFragment.kt b/ui-popular/src/main/java/app/tivi/home/popular/PopularShowsFragment.kt index e5549a8c80..85bf8af56d 100644 --- a/ui-popular/src/main/java/app/tivi/home/popular/PopularShowsFragment.kt +++ b/ui-popular/src/main/java/app/tivi/home/popular/PopularShowsFragment.kt @@ -30,8 +30,8 @@ import androidx.lifecycle.lifecycleScope import androidx.navigation.fragment.findNavController import app.tivi.common.compose.AmbientHomeTextCreator import app.tivi.common.compose.AmbientTiviDateFormatter -import app.tivi.common.compose.TiviContentSetup import app.tivi.common.compose.paging.collectAsLazyPagingItems +import app.tivi.common.compose.theme.TiviTheme import app.tivi.home.HomeTextCreator import app.tivi.util.TiviDateFormatter import dagger.hilt.android.AndroidEntryPoint @@ -72,7 +72,7 @@ class PopularShowsFragment : Fragment() { AmbientHomeTextCreator provides homeTextCreator, AmbientWindowInsets provides windowInsets, ) { - TiviContentSetup { + TiviTheme { Popular( lazyPagingItems = pagedList.collectAsLazyPagingItems { old, new -> old.entry.id == new.entry.id diff --git a/ui-recommended/src/main/java/app/tivi/home/recommended/RecommendedShowsFragment.kt b/ui-recommended/src/main/java/app/tivi/home/recommended/RecommendedShowsFragment.kt index 222f1925be..a35871c0d6 100644 --- a/ui-recommended/src/main/java/app/tivi/home/recommended/RecommendedShowsFragment.kt +++ b/ui-recommended/src/main/java/app/tivi/home/recommended/RecommendedShowsFragment.kt @@ -30,8 +30,8 @@ import androidx.lifecycle.lifecycleScope import androidx.navigation.fragment.findNavController import app.tivi.common.compose.AmbientHomeTextCreator import app.tivi.common.compose.AmbientTiviDateFormatter -import app.tivi.common.compose.TiviContentSetup import app.tivi.common.compose.paging.collectAsLazyPagingItems +import app.tivi.common.compose.theme.TiviTheme import app.tivi.home.HomeTextCreator import app.tivi.util.TiviDateFormatter import dagger.hilt.android.AndroidEntryPoint @@ -72,7 +72,7 @@ class RecommendedShowsFragment : Fragment() { AmbientHomeTextCreator provides homeTextCreator, AmbientWindowInsets provides windowInsets, ) { - TiviContentSetup { + TiviTheme { Recommended( lazyPagingItems = pagedList.collectAsLazyPagingItems { old, new -> old.entry.id == new.entry.id diff --git a/ui-search/src/main/java/app/tivi/home/search/SearchFragment.kt b/ui-search/src/main/java/app/tivi/home/search/SearchFragment.kt index c817733d39..8b5411c627 100644 --- a/ui-search/src/main/java/app/tivi/home/search/SearchFragment.kt +++ b/ui-search/src/main/java/app/tivi/home/search/SearchFragment.kt @@ -31,7 +31,7 @@ import androidx.fragment.app.viewModels import androidx.lifecycle.lifecycleScope import androidx.navigation.fragment.findNavController import app.tivi.common.compose.LogCompositions -import app.tivi.common.compose.TiviContentSetup +import app.tivi.common.compose.theme.TiviTheme import dagger.hilt.android.AndroidEntryPoint import dev.chrisbanes.accompanist.insets.AmbientWindowInsets import dev.chrisbanes.accompanist.insets.ViewWindowInsetObserver @@ -70,7 +70,7 @@ internal class SearchFragment : Fragment() { setContent { Providers(AmbientWindowInsets provides windowInsets) { - TiviContentSetup { + TiviTheme { val viewState by viewModel.liveData.observeAsState() if (viewState != null) { LogCompositions("ViewState observeAsState") diff --git a/ui-showdetails/src/main/java/app/tivi/showdetails/details/ShowDetailsFragment.kt b/ui-showdetails/src/main/java/app/tivi/showdetails/details/ShowDetailsFragment.kt index c164ef2188..9986191c03 100644 --- a/ui-showdetails/src/main/java/app/tivi/showdetails/details/ShowDetailsFragment.kt +++ b/ui-showdetails/src/main/java/app/tivi/showdetails/details/ShowDetailsFragment.kt @@ -32,7 +32,7 @@ import androidx.lifecycle.lifecycleScope import androidx.navigation.NavOptions import androidx.navigation.fragment.findNavController import app.tivi.common.compose.LogCompositions -import app.tivi.common.compose.TiviContentSetup +import app.tivi.common.compose.theme.TiviTheme import app.tivi.extensions.viewModelProviderFactoryOf import app.tivi.util.TiviDateFormatter import dagger.hilt.android.AndroidEntryPoint @@ -81,7 +81,7 @@ class ShowDetailsFragment : Fragment() { AmbientShowDetailsTextCreator provides textCreator, AmbientWindowInsets provides windowInsets, ) { - TiviContentSetup { + TiviTheme { val viewState by viewModel.liveData.observeAsState() if (viewState != null) { LogCompositions("ViewState observeAsState") diff --git a/ui-trending/src/main/java/app/tivi/home/trending/TrendingShowsFragment.kt b/ui-trending/src/main/java/app/tivi/home/trending/TrendingShowsFragment.kt index cd1a9ac8fa..9ced0e7731 100644 --- a/ui-trending/src/main/java/app/tivi/home/trending/TrendingShowsFragment.kt +++ b/ui-trending/src/main/java/app/tivi/home/trending/TrendingShowsFragment.kt @@ -30,8 +30,8 @@ import androidx.lifecycle.lifecycleScope import androidx.navigation.fragment.findNavController import app.tivi.common.compose.AmbientHomeTextCreator import app.tivi.common.compose.AmbientTiviDateFormatter -import app.tivi.common.compose.TiviContentSetup import app.tivi.common.compose.paging.collectAsLazyPagingItems +import app.tivi.common.compose.theme.TiviTheme import app.tivi.home.HomeTextCreator import app.tivi.util.TiviDateFormatter import dagger.hilt.android.AndroidEntryPoint @@ -72,7 +72,7 @@ class TrendingShowsFragment : Fragment() { AmbientHomeTextCreator provides homeTextCreator, AmbientWindowInsets provides windowInsets, ) { - TiviContentSetup { + TiviTheme { Trending( lazyPagingItems = pagedList.collectAsLazyPagingItems { old, new -> old.entry.id == new.entry.id diff --git a/ui-watched/src/main/java/app/tivi/home/watched/WatchedFragment.kt b/ui-watched/src/main/java/app/tivi/home/watched/WatchedFragment.kt index 206a78c2fc..093ed0093f 100644 --- a/ui-watched/src/main/java/app/tivi/home/watched/WatchedFragment.kt +++ b/ui-watched/src/main/java/app/tivi/home/watched/WatchedFragment.kt @@ -33,7 +33,7 @@ import androidx.navigation.fragment.findNavController import androidx.paging.compose.collectAsLazyPagingItems import app.tivi.common.compose.AmbientHomeTextCreator import app.tivi.common.compose.AmbientTiviDateFormatter -import app.tivi.common.compose.TiviContentSetup +import app.tivi.common.compose.theme.TiviTheme import app.tivi.home.HomeTextCreator import app.tivi.util.TiviDateFormatter import dagger.hilt.android.AndroidEntryPoint @@ -70,7 +70,7 @@ class WatchedFragment : Fragment() { AmbientHomeTextCreator provides homeTextCreator, AmbientWindowInsets provides windowInsets, ) { - TiviContentSetup { + TiviTheme { val viewState by viewModel.liveData.observeAsState() if (viewState != null) { Watched( From c75bd060c6e91994202c835d960a753a382170f9 Mon Sep 17 00:00:00 2001 From: Chris Banes Date: Tue, 1 Dec 2020 12:22:05 +0000 Subject: [PATCH 11/17] Fix episode details animation --- common-ui-view/src/main/res/anim/tivi_enter_bottom_anim.xml | 2 +- common-ui-view/src/main/res/anim/tivi_exit_bottom_anim.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/common-ui-view/src/main/res/anim/tivi_enter_bottom_anim.xml b/common-ui-view/src/main/res/anim/tivi_enter_bottom_anim.xml index e2cec494df..01dbb59242 100644 --- a/common-ui-view/src/main/res/anim/tivi_enter_bottom_anim.xml +++ b/common-ui-view/src/main/res/anim/tivi_enter_bottom_anim.xml @@ -19,7 +19,7 @@ android:interpolator="@android:interpolator/fast_out_slow_in"> Date: Tue, 1 Dec 2020 12:22:27 +0000 Subject: [PATCH 12/17] Re-work show details ui effect event propagation We no longer rely on the ViewModel removing the effect from the list. Each effect now has a consumed variable, which allows us to track whether the effect has been consumed. --- .../showdetails/details/ShowDetailsAction.kt | 1 - .../details/ShowDetailsFragment.kt | 34 ++++++++++--------- .../details/ShowDetailsFragmentViewModel.kt | 16 +++++---- .../details/ShowDetailsViewState.kt | 15 +++++++- 4 files changed, 41 insertions(+), 25 deletions(-) diff --git a/ui-showdetails/src/main/java/app/tivi/showdetails/details/ShowDetailsAction.kt b/ui-showdetails/src/main/java/app/tivi/showdetails/details/ShowDetailsAction.kt index 8fb65d2b16..bd178b511b 100644 --- a/ui-showdetails/src/main/java/app/tivi/showdetails/details/ShowDetailsAction.kt +++ b/ui-showdetails/src/main/java/app/tivi/showdetails/details/ShowDetailsAction.kt @@ -43,6 +43,5 @@ data class ChangeSeasonExpandedAction( data class OpenShowDetails(val showId: Long) : ShowDetailsAction() data class OpenEpisodeDetails(val episodeId: Long) : ShowDetailsAction() -data class ClearPendingUiEffect(val effect: UiEffect) : ShowDetailsAction() object ClearError : ShowDetailsAction() object NavigateUp : ShowDetailsAction() diff --git a/ui-showdetails/src/main/java/app/tivi/showdetails/details/ShowDetailsFragment.kt b/ui-showdetails/src/main/java/app/tivi/showdetails/details/ShowDetailsFragment.kt index 9986191c03..cb37c9f724 100644 --- a/ui-showdetails/src/main/java/app/tivi/showdetails/details/ShowDetailsFragment.kt +++ b/ui-showdetails/src/main/java/app/tivi/showdetails/details/ShowDetailsFragment.kt @@ -110,23 +110,25 @@ class ShowDetailsFragment : Fragment() { } private fun render(state: ShowDetailsViewState) { - state.pendingUiEffects.forEach { effect -> - when (effect) { - is OpenShowUiEffect -> { - findNavController().navigate("app.tivi://show/${effect.showId}".toUri()) - viewModel.submitAction(ClearPendingUiEffect(effect)) - } - is OpenEpisodeUiEffect -> { - findNavController().navigate( - "app.tivi://episode/${effect.episodeId}".toUri(), - NavOptions.Builder() - .setEnterAnim(R.anim.tivi_enter_bottom_anim) - .setExitAnim(R.anim.tivi_exit_bottom_anim) - .build() - ) - viewModel.submitAction(ClearPendingUiEffect(effect)) + state.pendingUiEffects.asSequence() + .filter { !it.consumed } + .forEach { effect -> + when (effect) { + is OpenShowUiEffect -> { + findNavController().navigate("app.tivi://show/${effect.showId}".toUri()) + effect.consume() + } + is OpenEpisodeUiEffect -> { + findNavController().navigate( + "app.tivi://episode/${effect.episodeId}".toUri(), + NavOptions.Builder() + .setEnterAnim(R.anim.tivi_enter_bottom_anim) + .setExitAnim(R.anim.tivi_exit_bottom_anim) + .build() + ) + effect.consume() + } } } - } } } diff --git a/ui-showdetails/src/main/java/app/tivi/showdetails/details/ShowDetailsFragmentViewModel.kt b/ui-showdetails/src/main/java/app/tivi/showdetails/details/ShowDetailsFragmentViewModel.kt index 34081d0799..204c0127f6 100644 --- a/ui-showdetails/src/main/java/app/tivi/showdetails/details/ShowDetailsFragmentViewModel.kt +++ b/ui-showdetails/src/main/java/app/tivi/showdetails/details/ShowDetailsFragmentViewModel.kt @@ -138,7 +138,6 @@ internal class ShowDetailsFragmentViewModel @AssistedInject constructor( is UnfollowPreviousSeasonsFollowedAction -> onUnfollowPreviousSeasonsFollowState(action) is OpenEpisodeDetails -> openEpisodeDetails(action) is OpenShowDetails -> openShowDetails(action) - is ClearPendingUiEffect -> clearPendingUiEffect(action) is ClearError -> snackbarManager.removeCurrentError() } } @@ -150,6 +149,15 @@ internal class ShowDetailsFragmentViewModel @AssistedInject constructor( } } + selectSubscribe(ShowDetailsViewState::pendingUiEffects) { effects -> + if (effects.any(UiEffect::consumed)) { + // Remove any consumed effects once they've been consumed + viewModelScope.launchSetState { + copy(pendingUiEffects = pendingUiEffects.filter { !it.consumed }) + } + } + } + selectSubscribe(ShowDetailsViewState::showId) { showId -> observeShowFollowStatus(ObserveShowFollowStatus.Params(showId)) observeShowDetails(ObserveShowDetails.Params(showId)) @@ -221,12 +229,6 @@ internal class ShowDetailsFragmentViewModel @AssistedInject constructor( } } - private fun clearPendingUiEffect(action: ClearPendingUiEffect) { - viewModelScope.launchSetState { - copy(pendingUiEffects = pendingUiEffects - action.effect) - } - } - private fun onMarkSeasonWatched(action: MarkSeasonWatchedAction) { changeSeasonWatchedStatus(Params(action.seasonId, Action.WATCHED, action.onlyAired, action.date)) .watchStatus() diff --git a/ui-showdetails/src/main/java/app/tivi/showdetails/details/ShowDetailsViewState.kt b/ui-showdetails/src/main/java/app/tivi/showdetails/details/ShowDetailsViewState.kt index 57d3cdea2e..aec406cec0 100644 --- a/ui-showdetails/src/main/java/app/tivi/showdetails/details/ShowDetailsViewState.kt +++ b/ui-showdetails/src/main/java/app/tivi/showdetails/details/ShowDetailsViewState.kt @@ -42,7 +42,20 @@ data class ShowDetailsViewState( val refreshError: UiError? = null ) -sealed class UiEffect +sealed class UiEffect { + /** + * Whether the effect has been consumed or not. + */ + var consumed: Boolean = false + private set + + /** + * Consume the UI effect so that nothing else runs the effect + */ + fun consume() { + consumed = true + } +} data class OpenEpisodeUiEffect(val episodeId: Long, val seasonId: Long) : UiEffect() data class OpenShowUiEffect(val showId: Long) : UiEffect() data class FocusSeasonUiEffect(val seasonId: Long) : UiEffect() From 91f1adbabc1d64cfc09dc810b071a8189e45096b Mon Sep 17 00:00:00 2001 From: Chris Banes Date: Tue, 1 Dec 2020 12:32:41 +0000 Subject: [PATCH 13/17] Rename ShowDetailsFragmentViewModel to ShowDetailsViewModel --- .../{ShowDetailsFragmentViewModel.kt => ShowDetailsViewModel.kt} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename ui-showdetails/src/main/java/app/tivi/showdetails/details/{ShowDetailsFragmentViewModel.kt => ShowDetailsViewModel.kt} (100%) diff --git a/ui-showdetails/src/main/java/app/tivi/showdetails/details/ShowDetailsFragmentViewModel.kt b/ui-showdetails/src/main/java/app/tivi/showdetails/details/ShowDetailsViewModel.kt similarity index 100% rename from ui-showdetails/src/main/java/app/tivi/showdetails/details/ShowDetailsFragmentViewModel.kt rename to ui-showdetails/src/main/java/app/tivi/showdetails/details/ShowDetailsViewModel.kt From b7c7ae7c40a56909b39735c4d0c052f23222de71 Mon Sep 17 00:00:00 2001 From: Chris Banes Date: Tue, 1 Dec 2020 12:49:26 +0000 Subject: [PATCH 14/17] Move to UiEffect Flow This removes the consumable (mutable) UiEffects which we were propagating through UI state. --- .../main/res/anim/tivi_enter_bottom_anim.xml | 5 +- .../main/res/anim/tivi_exit_bottom_anim.xml | 7 +- .../details/ShowDetailsFragment.kt | 78 +++++++++---------- .../details/ShowDetailsViewModel.kt | 55 ++++++------- .../details/ShowDetailsViewState.kt | 16 +--- 5 files changed, 71 insertions(+), 90 deletions(-) diff --git a/common-ui-view/src/main/res/anim/tivi_enter_bottom_anim.xml b/common-ui-view/src/main/res/anim/tivi_enter_bottom_anim.xml index 01dbb59242..7a14530b13 100644 --- a/common-ui-view/src/main/res/anim/tivi_enter_bottom_anim.xml +++ b/common-ui-view/src/main/res/anim/tivi_enter_bottom_anim.xml @@ -1,4 +1,5 @@ - - + + + + \ No newline at end of file diff --git a/common-ui-view/src/main/res/anim/tivi_pop_exit_anim.xml b/common-ui-view/src/main/res/anim/tivi_fade_scale_exit.xml similarity index 65% rename from common-ui-view/src/main/res/anim/tivi_pop_exit_anim.xml rename to common-ui-view/src/main/res/anim/tivi_fade_scale_exit.xml index 96844e403f..c498229e01 100644 --- a/common-ui-view/src/main/res/anim/tivi_pop_exit_anim.xml +++ b/common-ui-view/src/main/res/anim/tivi_fade_scale_exit.xml @@ -15,10 +15,19 @@ ~ limitations under the License. --> - + + + + \ No newline at end of file diff --git a/ui-discover/src/main/java/app/tivi/home/discover/DiscoverFragment.kt b/ui-discover/src/main/java/app/tivi/home/discover/DiscoverFragment.kt index 3e56c5fda3..20ca219846 100644 --- a/ui-discover/src/main/java/app/tivi/home/discover/DiscoverFragment.kt +++ b/ui-discover/src/main/java/app/tivi/home/discover/DiscoverFragment.kt @@ -32,6 +32,7 @@ import androidx.lifecycle.lifecycleScope import androidx.navigation.fragment.findNavController import app.tivi.common.compose.AmbientTiviDateFormatter import app.tivi.common.compose.theme.TiviTheme +import app.tivi.extensions.DefaultNavOptions import app.tivi.util.TiviDateFormatter import dagger.hilt.android.AndroidEntryPoint import dev.chrisbanes.accompanist.insets.AmbientWindowInsets @@ -93,11 +94,29 @@ class DiscoverFragment : Fragment() { if (action.episodeId != null) { uri += "/episode/${action.episodeId}" } - findNavController().navigate(uri.toUri()) + findNavController().navigate(uri.toUri(), DefaultNavOptions) + } + OpenTrendingShows -> { + findNavController().navigate( + R.id.navigation_trending, + null, + DefaultNavOptions + ) + } + OpenPopularShows -> { + findNavController().navigate( + R.id.navigation_popular, + null, + DefaultNavOptions + ) + } + OpenRecommendedShows -> { + findNavController().navigate( + R.id.navigation_recommended, + null, + DefaultNavOptions + ) } - OpenTrendingShows -> findNavController().navigate(R.id.navigation_trending) - OpenPopularShows -> findNavController().navigate(R.id.navigation_popular) - OpenRecommendedShows -> findNavController().navigate(R.id.navigation_recommended) else -> viewModel.submitAction(action) } } diff --git a/ui-followed/src/main/java/app/tivi/home/followed/FollowedFragment.kt b/ui-followed/src/main/java/app/tivi/home/followed/FollowedFragment.kt index ed9bdd4a03..bbae84f419 100644 --- a/ui-followed/src/main/java/app/tivi/home/followed/FollowedFragment.kt +++ b/ui-followed/src/main/java/app/tivi/home/followed/FollowedFragment.kt @@ -34,6 +34,7 @@ import androidx.paging.compose.collectAsLazyPagingItems import app.tivi.common.compose.AmbientHomeTextCreator import app.tivi.common.compose.AmbientTiviDateFormatter import app.tivi.common.compose.theme.TiviTheme +import app.tivi.extensions.DefaultNavOptions import app.tivi.home.HomeTextCreator import app.tivi.util.TiviDateFormatter import dagger.hilt.android.AndroidEntryPoint @@ -91,9 +92,14 @@ class FollowedFragment : Fragment() { pendingActions.consumeAsFlow().collect { action -> when (action) { FollowedAction.LoginAction, - FollowedAction.OpenUserDetails -> findNavController().navigate("app.tivi://account".toUri()) + FollowedAction.OpenUserDetails -> { + findNavController().navigate("app.tivi://account".toUri()) + } is FollowedAction.OpenShowDetails -> { - findNavController().navigate("app.tivi://show/${action.showId}".toUri()) + findNavController().navigate( + "app.tivi://show/${action.showId}".toUri(), + DefaultNavOptions + ) } else -> viewModel.submitAction(action) } diff --git a/ui-popular/src/main/java/app/tivi/home/popular/PopularShowsFragment.kt b/ui-popular/src/main/java/app/tivi/home/popular/PopularShowsFragment.kt index 85bf8af56d..ca900b486f 100644 --- a/ui-popular/src/main/java/app/tivi/home/popular/PopularShowsFragment.kt +++ b/ui-popular/src/main/java/app/tivi/home/popular/PopularShowsFragment.kt @@ -32,6 +32,7 @@ import app.tivi.common.compose.AmbientHomeTextCreator import app.tivi.common.compose.AmbientTiviDateFormatter import app.tivi.common.compose.paging.collectAsLazyPagingItems import app.tivi.common.compose.theme.TiviTheme +import app.tivi.extensions.DefaultNavOptions import app.tivi.home.HomeTextCreator import app.tivi.util.TiviDateFormatter import dagger.hilt.android.AndroidEntryPoint @@ -91,7 +92,10 @@ class PopularShowsFragment : Fragment() { pendingActions.consumeAsFlow().collect { action -> when (action) { is PopularAction.OpenShowDetails -> { - findNavController().navigate("app.tivi://show/${action.showId}".toUri()) + findNavController().navigate( + "app.tivi://show/${action.showId}".toUri(), + DefaultNavOptions + ) } // else -> viewModel.submitAction(action) } diff --git a/ui-recommended/src/main/java/app/tivi/home/recommended/RecommendedShowsFragment.kt b/ui-recommended/src/main/java/app/tivi/home/recommended/RecommendedShowsFragment.kt index a35871c0d6..a89bf67036 100644 --- a/ui-recommended/src/main/java/app/tivi/home/recommended/RecommendedShowsFragment.kt +++ b/ui-recommended/src/main/java/app/tivi/home/recommended/RecommendedShowsFragment.kt @@ -32,6 +32,7 @@ import app.tivi.common.compose.AmbientHomeTextCreator import app.tivi.common.compose.AmbientTiviDateFormatter import app.tivi.common.compose.paging.collectAsLazyPagingItems import app.tivi.common.compose.theme.TiviTheme +import app.tivi.extensions.DefaultNavOptions import app.tivi.home.HomeTextCreator import app.tivi.util.TiviDateFormatter import dagger.hilt.android.AndroidEntryPoint @@ -91,7 +92,10 @@ class RecommendedShowsFragment : Fragment() { pendingActions.consumeAsFlow().collect { action -> when (action) { is RecommendedAction.OpenShowDetails -> { - findNavController().navigate("app.tivi://show/${action.showId}".toUri()) + findNavController().navigate( + "app.tivi://show/${action.showId}".toUri(), + DefaultNavOptions + ) } // else -> viewModel.submitAction(action) } diff --git a/ui-search/src/main/java/app/tivi/home/search/SearchFragment.kt b/ui-search/src/main/java/app/tivi/home/search/SearchFragment.kt index 8b5411c627..5ba68ac2fb 100644 --- a/ui-search/src/main/java/app/tivi/home/search/SearchFragment.kt +++ b/ui-search/src/main/java/app/tivi/home/search/SearchFragment.kt @@ -32,6 +32,7 @@ import androidx.lifecycle.lifecycleScope import androidx.navigation.fragment.findNavController import app.tivi.common.compose.LogCompositions import app.tivi.common.compose.theme.TiviTheme +import app.tivi.extensions.DefaultNavOptions import dagger.hilt.android.AndroidEntryPoint import dev.chrisbanes.accompanist.insets.AmbientWindowInsets import dev.chrisbanes.accompanist.insets.ViewWindowInsetObserver @@ -49,7 +50,10 @@ internal class SearchFragment : Fragment() { for (action in pendingActions) { when (action) { is SearchAction.OpenShowDetails -> { - findNavController().navigate("app.tivi://show/${action.showId}".toUri()) + findNavController().navigate( + "app.tivi://show/${action.showId}".toUri(), + DefaultNavOptions + ) } else -> viewModel.submitAction(action) } diff --git a/ui-showdetails/src/main/java/app/tivi/showdetails/details/ShowDetailsFragment.kt b/ui-showdetails/src/main/java/app/tivi/showdetails/details/ShowDetailsFragment.kt index 9cdb71d9e2..963219aec4 100644 --- a/ui-showdetails/src/main/java/app/tivi/showdetails/details/ShowDetailsFragment.kt +++ b/ui-showdetails/src/main/java/app/tivi/showdetails/details/ShowDetailsFragment.kt @@ -29,10 +29,11 @@ import androidx.core.net.toUri import androidx.fragment.app.Fragment import androidx.fragment.app.viewModels import androidx.lifecycle.lifecycleScope -import androidx.navigation.NavOptions import androidx.navigation.fragment.findNavController +import androidx.navigation.navOptions import app.tivi.common.compose.LogCompositions import app.tivi.common.compose.theme.TiviTheme +import app.tivi.extensions.DefaultNavOptions import app.tivi.extensions.viewModelProviderFactoryOf import app.tivi.util.TiviDateFormatter import dagger.hilt.android.AndroidEntryPoint @@ -80,18 +81,21 @@ class ShowDetailsFragment : Fragment() { viewModel.uiEffects.collect { effect -> when (effect) { is OpenShowUiEffect -> { - findNavController() - .navigate("app.tivi://show/${effect.showId}".toUri()) + findNavController().navigate( + "app.tivi://show/${effect.showId}".toUri(), + DefaultNavOptions + ) } is OpenEpisodeUiEffect -> { - findNavController() - .navigate( - "app.tivi://episode/${effect.episodeId}".toUri(), - NavOptions.Builder() - .setEnterAnim(R.anim.tivi_enter_bottom_anim) - .setPopExitAnim(R.anim.tivi_exit_bottom_anim) - .build() - ) + findNavController().navigate( + "app.tivi://episode/${effect.episodeId}".toUri(), + navOptions { + anim { + enter = R.anim.tivi_enter_bottom_anim + popExit = R.anim.tivi_exit_bottom_anim + } + } + ) } else -> { // TODO: any remaining ui effects need to be passed down to the UI diff --git a/ui-trending/src/main/java/app/tivi/home/trending/TrendingShowsFragment.kt b/ui-trending/src/main/java/app/tivi/home/trending/TrendingShowsFragment.kt index 9ced0e7731..8f9f5bc50f 100644 --- a/ui-trending/src/main/java/app/tivi/home/trending/TrendingShowsFragment.kt +++ b/ui-trending/src/main/java/app/tivi/home/trending/TrendingShowsFragment.kt @@ -32,6 +32,7 @@ import app.tivi.common.compose.AmbientHomeTextCreator import app.tivi.common.compose.AmbientTiviDateFormatter import app.tivi.common.compose.paging.collectAsLazyPagingItems import app.tivi.common.compose.theme.TiviTheme +import app.tivi.extensions.DefaultNavOptions import app.tivi.home.HomeTextCreator import app.tivi.util.TiviDateFormatter import dagger.hilt.android.AndroidEntryPoint @@ -91,7 +92,10 @@ class TrendingShowsFragment : Fragment() { pendingActions.consumeAsFlow().collect { action -> when (action) { is TrendingAction.OpenShowDetails -> { - findNavController().navigate("app.tivi://show/${action.showId}".toUri()) + findNavController().navigate( + "app.tivi://show/${action.showId}".toUri(), + DefaultNavOptions + ) } // else -> viewModel.submitAction(action) } diff --git a/ui-watched/src/main/java/app/tivi/home/watched/WatchedFragment.kt b/ui-watched/src/main/java/app/tivi/home/watched/WatchedFragment.kt index 093ed0093f..fa611686c9 100644 --- a/ui-watched/src/main/java/app/tivi/home/watched/WatchedFragment.kt +++ b/ui-watched/src/main/java/app/tivi/home/watched/WatchedFragment.kt @@ -34,6 +34,7 @@ import androidx.paging.compose.collectAsLazyPagingItems import app.tivi.common.compose.AmbientHomeTextCreator import app.tivi.common.compose.AmbientTiviDateFormatter import app.tivi.common.compose.theme.TiviTheme +import app.tivi.extensions.DefaultNavOptions import app.tivi.home.HomeTextCreator import app.tivi.util.TiviDateFormatter import dagger.hilt.android.AndroidEntryPoint @@ -95,7 +96,10 @@ class WatchedFragment : Fragment() { findNavController().navigate("app.tivi://account".toUri()) } is WatchedAction.OpenShowDetails -> { - findNavController().navigate("app.tivi://show/${action.showId}".toUri()) + findNavController().navigate( + "app.tivi://show/${action.showId}".toUri(), + DefaultNavOptions + ) } else -> viewModel.submitAction(action) } From bc5683eab69bb8674f95a04832811242e57ecac7 Mon Sep 17 00:00:00 2001 From: Chris Banes Date: Tue, 1 Dec 2020 15:52:15 +0000 Subject: [PATCH 17/17] Fix theme changing from preference --- .../main/java/app/tivi/home/MainActivity.kt | 6 ++- .../java/app/tivi/settings/TiviPreferences.kt | 7 ++- .../compose/TiviPreferenceExtensions.kt | 32 +++++++++++++ .../app/tivi/account/AccountUiFragment.kt | 6 ++- .../tivi/home/discover/DiscoverFragment.kt | 5 ++- .../episodedetails/EpisodeDetailsFragment.kt | 5 ++- .../tivi/home/followed/FollowedFragment.kt | 5 ++- .../tivi/home/popular/PopularShowsFragment.kt | 5 ++- .../recommended/RecommendedShowsFragment.kt | 5 ++- .../app/tivi/home/search/SearchFragment.kt | 7 ++- .../app/tivi/settings/TiviPreferencesImpl.kt | 45 ++++++++++++------- .../details/ShowDetailsFragment.kt | 5 ++- .../home/trending/TrendingShowsFragment.kt | 5 ++- .../app/tivi/home/watched/WatchedFragment.kt | 7 ++- 14 files changed, 117 insertions(+), 28 deletions(-) create mode 100644 common-ui-compose/src/main/java/app/tivi/common/compose/TiviPreferenceExtensions.kt diff --git a/app/src/main/java/app/tivi/home/MainActivity.kt b/app/src/main/java/app/tivi/home/MainActivity.kt index 66e932c1e3..509d3424f8 100644 --- a/app/src/main/java/app/tivi/home/MainActivity.kt +++ b/app/src/main/java/app/tivi/home/MainActivity.kt @@ -30,10 +30,12 @@ import androidx.navigation.NavController import app.tivi.AppNavigator import app.tivi.R import app.tivi.TiviActivity +import app.tivi.common.compose.shouldUseDarkColors import app.tivi.common.compose.theme.TiviTheme import app.tivi.databinding.ActivityMainBinding import app.tivi.extensions.MultipleBackStackNavigation import app.tivi.extensions.hideSoftInput +import app.tivi.settings.TiviPreferences import app.tivi.trakt.TraktConstants import dagger.hilt.android.AndroidEntryPoint import dev.chrisbanes.accompanist.insets.ProvideWindowInsets @@ -50,6 +52,8 @@ class MainActivity : TiviActivity() { @Inject lateinit var navigator: AppNavigator + @Inject lateinit var preferences: TiviPreferences + private var currentSelectedItem by mutableStateOf(HomeNavigation.Discover) private lateinit var multiBackStackNavigation: MultipleBackStackNavigation @@ -89,7 +93,7 @@ class MainActivity : TiviActivity() { binding.homeBottomNavigation.setContent { ProvideWindowInsets { - TiviTheme { + TiviTheme(useDarkColors = preferences.shouldUseDarkColors()) { HomeBottomNavigation( selectedNavigation = currentSelectedItem, onNavigationSelected = { item -> diff --git a/base/src/main/java/app/tivi/settings/TiviPreferences.kt b/base/src/main/java/app/tivi/settings/TiviPreferences.kt index c182233ea9..01802beee5 100644 --- a/base/src/main/java/app/tivi/settings/TiviPreferences.kt +++ b/base/src/main/java/app/tivi/settings/TiviPreferences.kt @@ -16,12 +16,17 @@ package app.tivi.settings +import kotlinx.coroutines.flow.Flow + interface TiviPreferences { fun setup() - var themePreference: Theme + var theme: Theme + fun observeTheme(): Flow + var useLessData: Boolean + fun observeUseLessData(): Flow enum class Theme { LIGHT, diff --git a/common-ui-compose/src/main/java/app/tivi/common/compose/TiviPreferenceExtensions.kt b/common-ui-compose/src/main/java/app/tivi/common/compose/TiviPreferenceExtensions.kt new file mode 100644 index 0000000000..db62bdc267 --- /dev/null +++ b/common-ui-compose/src/main/java/app/tivi/common/compose/TiviPreferenceExtensions.kt @@ -0,0 +1,32 @@ +/* + * Copyright 2020 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package app.tivi.common.compose + +import androidx.compose.foundation.isSystemInDarkTheme +import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import app.tivi.settings.TiviPreferences + +@Composable +fun TiviPreferences.shouldUseDarkColors(): Boolean { + val themePreference = observeTheme().collectAsState(initial = TiviPreferences.Theme.SYSTEM) + return when (themePreference.value) { + TiviPreferences.Theme.LIGHT -> false + TiviPreferences.Theme.DARK -> true + else -> isSystemInDarkTheme() + } +} diff --git a/ui-account/src/main/java/app/tivi/account/AccountUiFragment.kt b/ui-account/src/main/java/app/tivi/account/AccountUiFragment.kt index 6a27e2750e..db6f60deaa 100644 --- a/ui-account/src/main/java/app/tivi/account/AccountUiFragment.kt +++ b/ui-account/src/main/java/app/tivi/account/AccountUiFragment.kt @@ -31,8 +31,10 @@ import androidx.fragment.app.viewModels import androidx.lifecycle.lifecycleScope import androidx.navigation.fragment.findNavController import app.tivi.common.compose.AmbientTiviDateFormatter +import app.tivi.common.compose.shouldUseDarkColors import app.tivi.common.compose.theme.TiviTheme import app.tivi.extensions.navigateToNavDestination +import app.tivi.settings.TiviPreferences import app.tivi.util.TiviDateFormatter import dagger.hilt.android.AndroidEntryPoint import dev.chrisbanes.accompanist.insets.AmbientWindowInsets @@ -50,6 +52,8 @@ class AccountUiFragment : DialogFragment() { @Inject internal lateinit var tiviDateFormatter: TiviDateFormatter + @Inject lateinit var preferences: TiviPreferences + override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, @@ -66,7 +70,7 @@ class AccountUiFragment : DialogFragment() { AmbientTiviDateFormatter provides tiviDateFormatter, AmbientWindowInsets provides windowInsets, ) { - TiviTheme { + TiviTheme(useDarkColors = preferences.shouldUseDarkColors()) { val viewState by viewModel.liveData.observeAsState() if (viewState != null) { AccountUi(viewState!!) { diff --git a/ui-discover/src/main/java/app/tivi/home/discover/DiscoverFragment.kt b/ui-discover/src/main/java/app/tivi/home/discover/DiscoverFragment.kt index 20ca219846..be8a851bc8 100644 --- a/ui-discover/src/main/java/app/tivi/home/discover/DiscoverFragment.kt +++ b/ui-discover/src/main/java/app/tivi/home/discover/DiscoverFragment.kt @@ -31,8 +31,10 @@ import androidx.fragment.app.viewModels import androidx.lifecycle.lifecycleScope import androidx.navigation.fragment.findNavController import app.tivi.common.compose.AmbientTiviDateFormatter +import app.tivi.common.compose.shouldUseDarkColors import app.tivi.common.compose.theme.TiviTheme import app.tivi.extensions.DefaultNavOptions +import app.tivi.settings.TiviPreferences import app.tivi.util.TiviDateFormatter import dagger.hilt.android.AndroidEntryPoint import dev.chrisbanes.accompanist.insets.AmbientWindowInsets @@ -46,6 +48,7 @@ import javax.inject.Inject class DiscoverFragment : Fragment() { @Inject internal lateinit var tiviDateFormatter: TiviDateFormatter @Inject internal lateinit var textCreator: DiscoverTextCreator + @Inject lateinit var preferences: TiviPreferences private val viewModel: DiscoverViewModel by viewModels() @@ -68,7 +71,7 @@ class DiscoverFragment : Fragment() { AmbientDiscoverTextCreator provides textCreator, AmbientWindowInsets provides windowInsets, ) { - TiviTheme { + TiviTheme(useDarkColors = preferences.shouldUseDarkColors()) { val viewState by viewModel.liveData.observeAsState() if (viewState != null) { Discover( diff --git a/ui-episodedetails/src/main/java/app/tivi/episodedetails/EpisodeDetailsFragment.kt b/ui-episodedetails/src/main/java/app/tivi/episodedetails/EpisodeDetailsFragment.kt index 67682a5ab7..7fb0dcd863 100644 --- a/ui-episodedetails/src/main/java/app/tivi/episodedetails/EpisodeDetailsFragment.kt +++ b/ui-episodedetails/src/main/java/app/tivi/episodedetails/EpisodeDetailsFragment.kt @@ -31,8 +31,10 @@ import androidx.fragment.app.viewModels import androidx.lifecycle.lifecycleScope import androidx.navigation.fragment.findNavController import app.tivi.common.compose.AmbientTiviDateFormatter +import app.tivi.common.compose.shouldUseDarkColors import app.tivi.common.compose.theme.TiviTheme import app.tivi.extensions.viewModelProviderFactoryOf +import app.tivi.settings.TiviPreferences import app.tivi.util.TiviDateFormatter import dagger.hilt.android.AndroidEntryPoint import dev.chrisbanes.accompanist.insets.AmbientWindowInsets @@ -46,6 +48,7 @@ import javax.inject.Inject @AndroidEntryPoint class EpisodeDetailsFragment : Fragment() { @Inject internal lateinit var vmFactory: EpisodeDetailsViewModel.Factory + @Inject lateinit var preferences: TiviPreferences companion object { private const val ARG_KEY_ID = "episode_id" @@ -85,7 +88,7 @@ class EpisodeDetailsFragment : Fragment() { AmbientTiviDateFormatter provides tiviDateFormatter, AmbientWindowInsets provides windowInsets, ) { - TiviTheme { + TiviTheme(useDarkColors = preferences.shouldUseDarkColors()) { val viewState by viewModel.liveData.observeAsState() if (viewState != null) { EpisodeDetails(viewState!!) { diff --git a/ui-followed/src/main/java/app/tivi/home/followed/FollowedFragment.kt b/ui-followed/src/main/java/app/tivi/home/followed/FollowedFragment.kt index bbae84f419..9c5e053642 100644 --- a/ui-followed/src/main/java/app/tivi/home/followed/FollowedFragment.kt +++ b/ui-followed/src/main/java/app/tivi/home/followed/FollowedFragment.kt @@ -33,9 +33,11 @@ import androidx.navigation.fragment.findNavController import androidx.paging.compose.collectAsLazyPagingItems import app.tivi.common.compose.AmbientHomeTextCreator import app.tivi.common.compose.AmbientTiviDateFormatter +import app.tivi.common.compose.shouldUseDarkColors import app.tivi.common.compose.theme.TiviTheme import app.tivi.extensions.DefaultNavOptions import app.tivi.home.HomeTextCreator +import app.tivi.settings.TiviPreferences import app.tivi.util.TiviDateFormatter import dagger.hilt.android.AndroidEntryPoint import dev.chrisbanes.accompanist.insets.AmbientWindowInsets @@ -49,6 +51,7 @@ import javax.inject.Inject class FollowedFragment : Fragment() { @Inject internal lateinit var tiviDateFormatter: TiviDateFormatter @Inject internal lateinit var homeTextCreator: HomeTextCreator + @Inject lateinit var preferences: TiviPreferences private val viewModel: FollowedViewModel by viewModels() @@ -71,7 +74,7 @@ class FollowedFragment : Fragment() { AmbientHomeTextCreator provides homeTextCreator, AmbientWindowInsets provides windowInsets, ) { - TiviTheme { + TiviTheme(useDarkColors = preferences.shouldUseDarkColors()) { val viewState by viewModel.liveData.observeAsState() if (viewState != null) { Followed( diff --git a/ui-popular/src/main/java/app/tivi/home/popular/PopularShowsFragment.kt b/ui-popular/src/main/java/app/tivi/home/popular/PopularShowsFragment.kt index ca900b486f..1b45cc3a4f 100644 --- a/ui-popular/src/main/java/app/tivi/home/popular/PopularShowsFragment.kt +++ b/ui-popular/src/main/java/app/tivi/home/popular/PopularShowsFragment.kt @@ -31,9 +31,11 @@ import androidx.navigation.fragment.findNavController import app.tivi.common.compose.AmbientHomeTextCreator import app.tivi.common.compose.AmbientTiviDateFormatter import app.tivi.common.compose.paging.collectAsLazyPagingItems +import app.tivi.common.compose.shouldUseDarkColors import app.tivi.common.compose.theme.TiviTheme import app.tivi.extensions.DefaultNavOptions import app.tivi.home.HomeTextCreator +import app.tivi.settings.TiviPreferences import app.tivi.util.TiviDateFormatter import dagger.hilt.android.AndroidEntryPoint import dev.chrisbanes.accompanist.insets.AmbientWindowInsets @@ -47,6 +49,7 @@ import javax.inject.Inject class PopularShowsFragment : Fragment() { @Inject internal lateinit var tiviDateFormatter: TiviDateFormatter @Inject internal lateinit var homeTextCreator: HomeTextCreator + @Inject lateinit var preferences: TiviPreferences private val pendingActions = Channel(Channel.BUFFERED) @@ -73,7 +76,7 @@ class PopularShowsFragment : Fragment() { AmbientHomeTextCreator provides homeTextCreator, AmbientWindowInsets provides windowInsets, ) { - TiviTheme { + TiviTheme(useDarkColors = preferences.shouldUseDarkColors()) { Popular( lazyPagingItems = pagedList.collectAsLazyPagingItems { old, new -> old.entry.id == new.entry.id diff --git a/ui-recommended/src/main/java/app/tivi/home/recommended/RecommendedShowsFragment.kt b/ui-recommended/src/main/java/app/tivi/home/recommended/RecommendedShowsFragment.kt index a89bf67036..4994dc1935 100644 --- a/ui-recommended/src/main/java/app/tivi/home/recommended/RecommendedShowsFragment.kt +++ b/ui-recommended/src/main/java/app/tivi/home/recommended/RecommendedShowsFragment.kt @@ -31,9 +31,11 @@ import androidx.navigation.fragment.findNavController import app.tivi.common.compose.AmbientHomeTextCreator import app.tivi.common.compose.AmbientTiviDateFormatter import app.tivi.common.compose.paging.collectAsLazyPagingItems +import app.tivi.common.compose.shouldUseDarkColors import app.tivi.common.compose.theme.TiviTheme import app.tivi.extensions.DefaultNavOptions import app.tivi.home.HomeTextCreator +import app.tivi.settings.TiviPreferences import app.tivi.util.TiviDateFormatter import dagger.hilt.android.AndroidEntryPoint import dev.chrisbanes.accompanist.insets.AmbientWindowInsets @@ -47,6 +49,7 @@ import javax.inject.Inject class RecommendedShowsFragment : Fragment() { @Inject internal lateinit var tiviDateFormatter: TiviDateFormatter @Inject internal lateinit var homeTextCreator: HomeTextCreator + @Inject lateinit var preferences: TiviPreferences private val pendingActions = Channel(Channel.BUFFERED) @@ -73,7 +76,7 @@ class RecommendedShowsFragment : Fragment() { AmbientHomeTextCreator provides homeTextCreator, AmbientWindowInsets provides windowInsets, ) { - TiviTheme { + TiviTheme(useDarkColors = preferences.shouldUseDarkColors()) { Recommended( lazyPagingItems = pagedList.collectAsLazyPagingItems { old, new -> old.entry.id == new.entry.id diff --git a/ui-search/src/main/java/app/tivi/home/search/SearchFragment.kt b/ui-search/src/main/java/app/tivi/home/search/SearchFragment.kt index 5ba68ac2fb..579241c17f 100644 --- a/ui-search/src/main/java/app/tivi/home/search/SearchFragment.kt +++ b/ui-search/src/main/java/app/tivi/home/search/SearchFragment.kt @@ -31,15 +31,20 @@ import androidx.fragment.app.viewModels import androidx.lifecycle.lifecycleScope import androidx.navigation.fragment.findNavController import app.tivi.common.compose.LogCompositions +import app.tivi.common.compose.shouldUseDarkColors import app.tivi.common.compose.theme.TiviTheme import app.tivi.extensions.DefaultNavOptions +import app.tivi.settings.TiviPreferences import dagger.hilt.android.AndroidEntryPoint import dev.chrisbanes.accompanist.insets.AmbientWindowInsets import dev.chrisbanes.accompanist.insets.ViewWindowInsetObserver import kotlinx.coroutines.channels.Channel +import javax.inject.Inject @AndroidEntryPoint internal class SearchFragment : Fragment() { + @Inject lateinit var preferences: TiviPreferences + private val viewModel: SearchViewModel by viewModels() private val pendingActions = Channel(Channel.BUFFERED) @@ -74,7 +79,7 @@ internal class SearchFragment : Fragment() { setContent { Providers(AmbientWindowInsets provides windowInsets) { - TiviTheme { + TiviTheme(useDarkColors = preferences.shouldUseDarkColors()) { val viewState by viewModel.liveData.observeAsState() if (viewState != null) { LogCompositions("ViewState observeAsState") diff --git a/ui-settings/src/main/java/app/tivi/settings/TiviPreferencesImpl.kt b/ui-settings/src/main/java/app/tivi/settings/TiviPreferencesImpl.kt index 6aa1223598..2b8775d1ff 100644 --- a/ui-settings/src/main/java/app/tivi/settings/TiviPreferencesImpl.kt +++ b/ui-settings/src/main/java/app/tivi/settings/TiviPreferencesImpl.kt @@ -21,6 +21,12 @@ import android.content.SharedPreferences import androidx.core.content.edit import app.tivi.settings.TiviPreferences.Theme import dagger.hilt.android.qualifiers.ApplicationContext +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.filter +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.onStart import javax.inject.Inject import javax.inject.Named @@ -28,25 +34,24 @@ class TiviPreferencesImpl @Inject constructor( @ApplicationContext private val context: Context, @Named("app") private val sharedPreferences: SharedPreferences ) : TiviPreferences { + private val defaultThemeValue = context.getString(R.string.pref_theme_default_value) + + private val preferenceKeyChangedFlow = MutableSharedFlow(extraBufferCapacity = 1) + private val listener = SharedPreferences.OnSharedPreferenceChangeListener { _, key -> - when (key) { - KEY_THEME -> updateUsingThemePreference() - } + preferenceKeyChangedFlow.tryEmit(key) } - private val defaultThemeValue = context.getString(R.string.pref_theme_default_value) - companion object { const val KEY_THEME = "pref_theme" const val KEY_DATA_SAVER = "pref_data_saver" } override fun setup() { - updateUsingThemePreference() sharedPreferences.registerOnSharedPreferenceChangeListener(listener) } - override var themePreference: Theme + override var theme: Theme get() = getThemeForStorageValue(sharedPreferences.getString(KEY_THEME, defaultThemeValue)!!) set(value) = sharedPreferences.edit { putString(KEY_THEME, value.storageKey) @@ -58,6 +63,24 @@ class TiviPreferencesImpl @Inject constructor( putBoolean(KEY_DATA_SAVER, value) } + override fun observeUseLessData(): Flow { + return preferenceKeyChangedFlow + // Emit on start so that we always send the initial value + .onStart { emit(KEY_DATA_SAVER) } + .filter { it == KEY_DATA_SAVER } + .map { useLessData } + .distinctUntilChanged() + } + + override fun observeTheme(): Flow { + return preferenceKeyChangedFlow + // Emit on start so that we always send the initial value + .onStart { emit(KEY_THEME) } + .filter { it == KEY_THEME } + .map { theme } + .distinctUntilChanged() + } + private val Theme.storageKey: String get() = when (this) { Theme.LIGHT -> context.getString(R.string.pref_theme_light_value) @@ -70,12 +93,4 @@ class TiviPreferencesImpl @Inject constructor( context.getString(R.string.pref_theme_dark_value) -> Theme.DARK else -> Theme.SYSTEM } - - private fun updateUsingThemePreference() { - // when (themePreference) { - // Theme.DARK -> AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES) - // Theme.LIGHT -> AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO) - // Theme.SYSTEM -> AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM) - // } - } } diff --git a/ui-showdetails/src/main/java/app/tivi/showdetails/details/ShowDetailsFragment.kt b/ui-showdetails/src/main/java/app/tivi/showdetails/details/ShowDetailsFragment.kt index 963219aec4..94aef0b74e 100644 --- a/ui-showdetails/src/main/java/app/tivi/showdetails/details/ShowDetailsFragment.kt +++ b/ui-showdetails/src/main/java/app/tivi/showdetails/details/ShowDetailsFragment.kt @@ -32,9 +32,11 @@ import androidx.lifecycle.lifecycleScope import androidx.navigation.fragment.findNavController import androidx.navigation.navOptions import app.tivi.common.compose.LogCompositions +import app.tivi.common.compose.shouldUseDarkColors import app.tivi.common.compose.theme.TiviTheme import app.tivi.extensions.DefaultNavOptions import app.tivi.extensions.viewModelProviderFactoryOf +import app.tivi.settings.TiviPreferences import app.tivi.util.TiviDateFormatter import dagger.hilt.android.AndroidEntryPoint import dev.chrisbanes.accompanist.insets.AmbientWindowInsets @@ -49,6 +51,7 @@ class ShowDetailsFragment : Fragment() { @Inject internal lateinit var vmFactory: ShowDetailsFragmentViewModel.Factory @Inject internal lateinit var textCreator: ShowDetailsTextCreator @Inject internal lateinit var tiviDateFormatter: TiviDateFormatter + @Inject lateinit var preferences: TiviPreferences private val pendingActions = Channel(Channel.BUFFERED) @@ -121,7 +124,7 @@ class ShowDetailsFragment : Fragment() { AmbientShowDetailsTextCreator provides textCreator, AmbientWindowInsets provides windowInsets, ) { - TiviTheme { + TiviTheme(useDarkColors = preferences.shouldUseDarkColors()) { val viewState by viewModel.liveData.observeAsState() if (viewState != null) { LogCompositions("ViewState observeAsState") diff --git a/ui-trending/src/main/java/app/tivi/home/trending/TrendingShowsFragment.kt b/ui-trending/src/main/java/app/tivi/home/trending/TrendingShowsFragment.kt index 8f9f5bc50f..526a2ce8b4 100644 --- a/ui-trending/src/main/java/app/tivi/home/trending/TrendingShowsFragment.kt +++ b/ui-trending/src/main/java/app/tivi/home/trending/TrendingShowsFragment.kt @@ -31,9 +31,11 @@ import androidx.navigation.fragment.findNavController import app.tivi.common.compose.AmbientHomeTextCreator import app.tivi.common.compose.AmbientTiviDateFormatter import app.tivi.common.compose.paging.collectAsLazyPagingItems +import app.tivi.common.compose.shouldUseDarkColors import app.tivi.common.compose.theme.TiviTheme import app.tivi.extensions.DefaultNavOptions import app.tivi.home.HomeTextCreator +import app.tivi.settings.TiviPreferences import app.tivi.util.TiviDateFormatter import dagger.hilt.android.AndroidEntryPoint import dev.chrisbanes.accompanist.insets.AmbientWindowInsets @@ -47,6 +49,7 @@ import javax.inject.Inject class TrendingShowsFragment : Fragment() { @Inject internal lateinit var tiviDateFormatter: TiviDateFormatter @Inject internal lateinit var homeTextCreator: HomeTextCreator + @Inject lateinit var preferences: TiviPreferences private val pendingActions = Channel(Channel.BUFFERED) @@ -73,7 +76,7 @@ class TrendingShowsFragment : Fragment() { AmbientHomeTextCreator provides homeTextCreator, AmbientWindowInsets provides windowInsets, ) { - TiviTheme { + TiviTheme(useDarkColors = preferences.shouldUseDarkColors()) { Trending( lazyPagingItems = pagedList.collectAsLazyPagingItems { old, new -> old.entry.id == new.entry.id diff --git a/ui-watched/src/main/java/app/tivi/home/watched/WatchedFragment.kt b/ui-watched/src/main/java/app/tivi/home/watched/WatchedFragment.kt index fa611686c9..fb05594d46 100644 --- a/ui-watched/src/main/java/app/tivi/home/watched/WatchedFragment.kt +++ b/ui-watched/src/main/java/app/tivi/home/watched/WatchedFragment.kt @@ -33,9 +33,11 @@ import androidx.navigation.fragment.findNavController import androidx.paging.compose.collectAsLazyPagingItems import app.tivi.common.compose.AmbientHomeTextCreator import app.tivi.common.compose.AmbientTiviDateFormatter +import app.tivi.common.compose.shouldUseDarkColors import app.tivi.common.compose.theme.TiviTheme import app.tivi.extensions.DefaultNavOptions import app.tivi.home.HomeTextCreator +import app.tivi.settings.TiviPreferences import app.tivi.util.TiviDateFormatter import dagger.hilt.android.AndroidEntryPoint import dev.chrisbanes.accompanist.insets.AmbientWindowInsets @@ -49,6 +51,7 @@ import javax.inject.Inject class WatchedFragment : Fragment() { @Inject internal lateinit var tiviDateFormatter: TiviDateFormatter @Inject internal lateinit var homeTextCreator: HomeTextCreator + @Inject lateinit var preferences: TiviPreferences private val viewModel: WatchedViewModel by viewModels() @@ -58,7 +61,7 @@ class WatchedFragment : Fragment() { inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? - ): View? = ComposeView(requireContext()).apply { + ): View = ComposeView(requireContext()).apply { layoutParams = ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT) // We use ViewWindowInsetObserver rather than ProvideWindowInsets @@ -71,7 +74,7 @@ class WatchedFragment : Fragment() { AmbientHomeTextCreator provides homeTextCreator, AmbientWindowInsets provides windowInsets, ) { - TiviTheme { + TiviTheme(useDarkColors = preferences.shouldUseDarkColors()) { val viewState by viewModel.liveData.observeAsState() if (viewState != null) { Watched(