POC: Retain streambundle across fragment navigations
This commit is contained in:
committed by
Aayush Gupta
parent
c6850b176e
commit
2325b6e4a3
3
.gitignore
vendored
3
.gitignore
vendored
@@ -44,3 +44,6 @@ app/nightly/*
|
|||||||
#Exclude signing configurations & keystore
|
#Exclude signing configurations & keystore
|
||||||
signing.properties
|
signing.properties
|
||||||
*.keystore
|
*.keystore
|
||||||
|
|
||||||
|
|
||||||
|
.kotlin/*
|
||||||
@@ -21,20 +21,20 @@ package com.aurora.store.data.model
|
|||||||
|
|
||||||
|
|
||||||
sealed class ViewState {
|
sealed class ViewState {
|
||||||
object Loading : ViewState()
|
data object Loading : ViewState()
|
||||||
object Empty : ViewState()
|
data object Empty : ViewState()
|
||||||
data class Error(val error: String?) : ViewState()
|
data class Error(val error: String?) : ViewState()
|
||||||
data class Status(val status: String?) : ViewState()
|
data class Status(val status: String?) : ViewState()
|
||||||
data class Success<T>(val data: T) : ViewState()
|
data class Success<T>(val data: T) : ViewState()
|
||||||
}
|
}
|
||||||
|
|
||||||
sealed class AuthState {
|
sealed class AuthState {
|
||||||
object Available : AuthState()
|
data object Available : AuthState()
|
||||||
object Unavailable : AuthState()
|
data object Unavailable : AuthState()
|
||||||
object SignedIn : AuthState()
|
data object SignedIn : AuthState()
|
||||||
object SignedOut : AuthState()
|
data object SignedOut : AuthState()
|
||||||
object Valid : AuthState()
|
data object Valid : AuthState()
|
||||||
object Fetching: AuthState()
|
data object Fetching: AuthState()
|
||||||
object Verifying: AuthState()
|
data object Verifying: AuthState()
|
||||||
data class Failed(val status: String) : AuthState()
|
data class Failed(val status: String) : AuthState()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ package com.aurora.store.view.ui.commons
|
|||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import androidx.fragment.app.viewModels
|
import androidx.fragment.app.activityViewModels
|
||||||
import com.aurora.Constants
|
import com.aurora.Constants
|
||||||
import com.aurora.gplayapi.data.models.App
|
import com.aurora.gplayapi.data.models.App
|
||||||
import com.aurora.gplayapi.data.models.StreamBundle
|
import com.aurora.gplayapi.data.models.StreamBundle
|
||||||
@@ -33,7 +33,7 @@ import com.aurora.store.data.model.ViewState
|
|||||||
import com.aurora.store.databinding.FragmentForYouBinding
|
import com.aurora.store.databinding.FragmentForYouBinding
|
||||||
import com.aurora.store.view.custom.recycler.EndlessRecyclerOnScrollListener
|
import com.aurora.store.view.custom.recycler.EndlessRecyclerOnScrollListener
|
||||||
import com.aurora.store.view.epoxy.controller.GenericCarouselController
|
import com.aurora.store.view.epoxy.controller.GenericCarouselController
|
||||||
import com.aurora.store.viewmodel.homestream.BaseClusterViewModel
|
import com.aurora.store.viewmodel.homestream.StreamViewModel
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
|
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
@@ -43,7 +43,8 @@ class ForYouFragment : BaseFragment(R.layout.fragment_for_you),
|
|||||||
private var _binding: FragmentForYouBinding? = null
|
private var _binding: FragmentForYouBinding? = null
|
||||||
private val binding get() = _binding!!
|
private val binding get() = _binding!!
|
||||||
|
|
||||||
private val viewModel: BaseClusterViewModel by viewModels()
|
private var category: Category = Category.APPLICATION
|
||||||
|
private val viewModel: StreamViewModel by activityViewModels()
|
||||||
|
|
||||||
private lateinit var streamBundle: StreamBundle
|
private lateinit var streamBundle: StreamBundle
|
||||||
|
|
||||||
@@ -71,35 +72,31 @@ class ForYouFragment : BaseFragment(R.layout.fragment_for_you),
|
|||||||
}
|
}
|
||||||
|
|
||||||
when (pageType) {
|
when (pageType) {
|
||||||
0 -> viewModel.getStreamBundle(Category.APPLICATION, Type.HOME)
|
0 -> {
|
||||||
1 -> viewModel.getStreamBundle(Category.GAME, Type.HOME)
|
category = Category.APPLICATION
|
||||||
|
viewModel.getStreamBundle(category, Type.HOME)
|
||||||
|
}
|
||||||
|
|
||||||
|
1 -> {
|
||||||
|
category = Category.GAME
|
||||||
|
viewModel.getStreamBundle(category, Type.HOME)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
binding.recycler.setController(genericCarouselController)
|
binding.recycler.setController(genericCarouselController)
|
||||||
|
|
||||||
viewModel.liveData.observe(viewLifecycleOwner) {
|
viewModel.liveData.observe(viewLifecycleOwner) {
|
||||||
when (it) {
|
when (it) {
|
||||||
is ViewState.Empty -> {
|
|
||||||
}
|
|
||||||
|
|
||||||
is ViewState.Loading -> {
|
is ViewState.Loading -> {
|
||||||
genericCarouselController.setData(null)
|
genericCarouselController.setData(null)
|
||||||
}
|
}
|
||||||
|
|
||||||
is ViewState.Error -> {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
is ViewState.Status -> {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
is ViewState.Success<*> -> {
|
is ViewState.Success<*> -> {
|
||||||
if (!::streamBundle.isInitialized) {
|
if (!::streamBundle.isInitialized) {
|
||||||
binding.recycler.addOnScrollListener(
|
binding.recycler.addOnScrollListener(
|
||||||
object : EndlessRecyclerOnScrollListener() {
|
object : EndlessRecyclerOnScrollListener() {
|
||||||
override fun onLoadMore(currentPage: Int) {
|
override fun onLoadMore(currentPage: Int) {
|
||||||
viewModel.observe()
|
viewModel.observe(category, Type.HOME)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@@ -108,6 +105,8 @@ class ForYouFragment : BaseFragment(R.layout.fragment_for_you),
|
|||||||
streamBundle = it.data as StreamBundle
|
streamBundle = it.data as StreamBundle
|
||||||
genericCarouselController.setData(streamBundle)
|
genericCarouselController.setData(streamBundle)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
else -> {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -123,7 +122,7 @@ class ForYouFragment : BaseFragment(R.layout.fragment_for_you),
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onClusterScrolled(streamCluster: StreamCluster) {
|
override fun onClusterScrolled(streamCluster: StreamCluster) {
|
||||||
viewModel.observeCluster(streamCluster)
|
viewModel.observeCluster(category, streamCluster)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onAppClick(app: App) {
|
override fun onAppClick(app: App) {
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ import javax.inject.Inject
|
|||||||
|
|
||||||
@HiltViewModel
|
@HiltViewModel
|
||||||
@SuppressLint("StaticFieldLeak") // false positive, see https://github.com/google/dagger/issues/3253
|
@SuppressLint("StaticFieldLeak") // false positive, see https://github.com/google/dagger/issues/3253
|
||||||
class BaseClusterViewModel @Inject constructor(
|
class StreamViewModel @Inject constructor(
|
||||||
@ApplicationContext private val context: Context,
|
@ApplicationContext private val context: Context,
|
||||||
private val authProvider: AuthProvider
|
private val authProvider: AuthProvider
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
@@ -54,10 +54,11 @@ class BaseClusterViewModel @Inject constructor(
|
|||||||
.using(HttpClient.getPreferredClient(context))
|
.using(HttpClient.getPreferredClient(context))
|
||||||
|
|
||||||
val liveData: MutableLiveData<ViewState> = MutableLiveData()
|
val liveData: MutableLiveData<ViewState> = MutableLiveData()
|
||||||
var streamBundle: StreamBundle = StreamBundle()
|
|
||||||
|
|
||||||
lateinit var type: StreamContract.Type
|
private var streamBundleMap: MutableMap<StreamContract.Category, StreamBundle> = mutableMapOf(
|
||||||
lateinit var category: StreamContract.Category
|
StreamContract.Category.APPLICATION to StreamBundle(),
|
||||||
|
StreamContract.Category.GAME to StreamBundle()
|
||||||
|
)
|
||||||
|
|
||||||
fun contract(): StreamContract {
|
fun contract(): StreamContract {
|
||||||
return if (authProvider.isAnonymous) {
|
return if (authProvider.isAnonymous) {
|
||||||
@@ -68,33 +69,38 @@ class BaseClusterViewModel @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun getStreamBundle(category: StreamContract.Category, type: StreamContract.Type) {
|
fun getStreamBundle(category: StreamContract.Category, type: StreamContract.Type) {
|
||||||
this.type = type
|
|
||||||
this.category = category
|
|
||||||
liveData.postValue(ViewState.Loading)
|
liveData.postValue(ViewState.Loading)
|
||||||
observe()
|
observe(category, type)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun observe() {
|
fun observe(category: StreamContract.Category, type: StreamContract.Type) {
|
||||||
viewModelScope.launch(Dispatchers.IO) {
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
supervisorScope {
|
supervisorScope {
|
||||||
|
if (targetBundle(category).streamClusters.isNotEmpty()) {
|
||||||
|
liveData.postValue(ViewState.Success(targetBundle(category)))
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (!streamBundle.hasCluster() || streamBundle.hasNext()) {
|
if (!targetBundle(category).hasCluster() || targetBundle(category).hasNext()) {
|
||||||
|
|
||||||
//Fetch new stream bundle
|
//Fetch new stream bundle
|
||||||
val newBundle = if (streamBundle.streamClusters.isEmpty()) {
|
val newBundle = if (targetBundle(category).streamClusters.isEmpty()) {
|
||||||
contract().fetch(type, category)
|
contract().fetch(type, category)
|
||||||
} else {
|
} else {
|
||||||
contract().nextStreamBundle(category, streamBundle.streamNextPageUrl)
|
contract().nextStreamBundle(
|
||||||
|
category,
|
||||||
|
targetBundle((category)).streamNextPageUrl
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
//Update old bundle
|
//Update old bundle
|
||||||
streamBundle.apply {
|
targetBundle(category).apply {
|
||||||
streamClusters.putAll(newBundle.streamClusters)
|
streamClusters.putAll(newBundle.streamClusters)
|
||||||
streamNextPageUrl = newBundle.streamNextPageUrl
|
streamNextPageUrl = newBundle.streamNextPageUrl
|
||||||
}
|
}
|
||||||
|
|
||||||
//Post updated to UI
|
//Post updated to UI
|
||||||
liveData.postValue(ViewState.Success(streamBundle))
|
liveData.postValue(ViewState.Success(targetBundle(category)))
|
||||||
} else {
|
} else {
|
||||||
Log.i("End of Bundle")
|
Log.i("End of Bundle")
|
||||||
}
|
}
|
||||||
@@ -105,15 +111,15 @@ class BaseClusterViewModel @Inject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun observeCluster(streamCluster: StreamCluster) {
|
fun observeCluster(category: StreamContract.Category, streamCluster: StreamCluster) {
|
||||||
viewModelScope.launch(Dispatchers.IO) {
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
supervisorScope {
|
supervisorScope {
|
||||||
try {
|
try {
|
||||||
if (streamCluster.hasNext()) {
|
if (streamCluster.hasNext()) {
|
||||||
val newCluster =
|
val newCluster =
|
||||||
contract().nextStreamCluster(streamCluster.clusterNextPageUrl)
|
contract().nextStreamCluster(streamCluster.clusterNextPageUrl)
|
||||||
updateCluster(streamCluster.id, newCluster)
|
updateCluster(category, streamCluster.id, newCluster)
|
||||||
liveData.postValue(ViewState.Success(streamBundle))
|
liveData.postValue(ViewState.Success(targetBundle(category)))
|
||||||
} else {
|
} else {
|
||||||
Log.i("End of cluster")
|
Log.i("End of cluster")
|
||||||
streamCluster.clusterNextPageUrl = String()
|
streamCluster.clusterNextPageUrl = String()
|
||||||
@@ -125,10 +131,20 @@ class BaseClusterViewModel @Inject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateCluster(clusterID: Int, newCluster: StreamCluster) {
|
private fun updateCluster(
|
||||||
streamBundle.streamClusters[clusterID]?.apply {
|
category: StreamContract.Category,
|
||||||
|
clusterID: Int,
|
||||||
|
newCluster: StreamCluster
|
||||||
|
) {
|
||||||
|
targetBundle(category).streamClusters[clusterID]?.apply {
|
||||||
clusterAppList.addAll(newCluster.clusterAppList)
|
clusterAppList.addAll(newCluster.clusterAppList)
|
||||||
clusterNextPageUrl = newCluster.clusterNextPageUrl
|
clusterNextPageUrl = newCluster.clusterNextPageUrl
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun targetBundle(category: StreamContract.Category): StreamBundle {
|
||||||
|
val streamBundle = streamBundleMap[category]
|
||||||
|
|
||||||
|
return streamBundle!!
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user