Experiment with stashing
This commit is contained in:
committed by
Aayush Gupta
parent
2325b6e4a3
commit
6f425fe641
9
app/src/main/java/com/aurora/store/Aliases.kt
Normal file
9
app/src/main/java/com/aurora/store/Aliases.kt
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
package com.aurora.store
|
||||||
|
|
||||||
|
import com.aurora.gplayapi.data.models.StreamBundle
|
||||||
|
import com.aurora.gplayapi.data.models.StreamCluster
|
||||||
|
import com.aurora.gplayapi.helpers.contracts.StreamContract
|
||||||
|
import com.aurora.gplayapi.helpers.contracts.TopChartsContract
|
||||||
|
|
||||||
|
typealias TopChartStash = Map<TopChartsContract.Type, Map<TopChartsContract.Chart, StreamCluster>>
|
||||||
|
typealias HomeStash = MutableMap<StreamContract.Category, StreamBundle>
|
||||||
@@ -21,6 +21,10 @@ package com.aurora.store.data.model
|
|||||||
|
|
||||||
|
|
||||||
sealed class ViewState {
|
sealed class ViewState {
|
||||||
|
inline fun <reified T> ViewState.getDataAs(): T {
|
||||||
|
return (this as? Success<*>)?.data as T
|
||||||
|
}
|
||||||
|
|
||||||
data object Loading : ViewState()
|
data object Loading : ViewState()
|
||||||
data object Empty : ViewState()
|
data object Empty : ViewState()
|
||||||
data class Error(val error: String?) : ViewState()
|
data class Error(val error: String?) : ViewState()
|
||||||
@@ -34,7 +38,7 @@ sealed class AuthState {
|
|||||||
data object SignedIn : AuthState()
|
data object SignedIn : AuthState()
|
||||||
data object SignedOut : AuthState()
|
data object SignedOut : AuthState()
|
||||||
data object Valid : AuthState()
|
data object Valid : AuthState()
|
||||||
data object Fetching: AuthState()
|
data object Fetching : AuthState()
|
||||||
data object Verifying: AuthState()
|
data object Verifying : AuthState()
|
||||||
data class Failed(val status: String) : AuthState()
|
data class Failed(val status: String) : AuthState()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,8 +28,10 @@ import com.aurora.gplayapi.data.models.StreamBundle
|
|||||||
import com.aurora.gplayapi.data.models.StreamCluster
|
import com.aurora.gplayapi.data.models.StreamCluster
|
||||||
import com.aurora.gplayapi.helpers.contracts.StreamContract.Category
|
import com.aurora.gplayapi.helpers.contracts.StreamContract.Category
|
||||||
import com.aurora.gplayapi.helpers.contracts.StreamContract.Type
|
import com.aurora.gplayapi.helpers.contracts.StreamContract.Type
|
||||||
|
import com.aurora.store.HomeStash
|
||||||
import com.aurora.store.R
|
import com.aurora.store.R
|
||||||
import com.aurora.store.data.model.ViewState
|
import com.aurora.store.data.model.ViewState
|
||||||
|
import com.aurora.store.data.ViewState.Loading.getDataAs
|
||||||
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
|
||||||
@@ -43,10 +45,10 @@ 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 var category: Category = Category.APPLICATION
|
|
||||||
private val viewModel: StreamViewModel by activityViewModels()
|
private val viewModel: StreamViewModel by activityViewModels()
|
||||||
|
|
||||||
private lateinit var streamBundle: StreamBundle
|
private var category: Category = Category.APPLICATION
|
||||||
|
private var streamBundle: StreamBundle? = StreamBundle()
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
@@ -84,6 +86,13 @@ class ForYouFragment : BaseFragment(R.layout.fragment_for_you),
|
|||||||
}
|
}
|
||||||
|
|
||||||
binding.recycler.setController(genericCarouselController)
|
binding.recycler.setController(genericCarouselController)
|
||||||
|
binding.recycler.addOnScrollListener(
|
||||||
|
object : EndlessRecyclerOnScrollListener() {
|
||||||
|
override fun onLoadMore(currentPage: Int) {
|
||||||
|
viewModel.observe(category, Type.HOME)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
viewModel.liveData.observe(viewLifecycleOwner) {
|
viewModel.liveData.observe(viewLifecycleOwner) {
|
||||||
when (it) {
|
when (it) {
|
||||||
@@ -92,17 +101,9 @@ class ForYouFragment : BaseFragment(R.layout.fragment_for_you),
|
|||||||
}
|
}
|
||||||
|
|
||||||
is ViewState.Success<*> -> {
|
is ViewState.Success<*> -> {
|
||||||
if (!::streamBundle.isInitialized) {
|
val stash = it.getDataAs<HomeStash>()
|
||||||
binding.recycler.addOnScrollListener(
|
streamBundle = stash[category]
|
||||||
object : EndlessRecyclerOnScrollListener() {
|
|
||||||
override fun onLoadMore(currentPage: Int) {
|
|
||||||
viewModel.observe(category, Type.HOME)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
streamBundle = it.data as StreamBundle
|
|
||||||
genericCarouselController.setData(streamBundle)
|
genericCarouselController.setData(streamBundle)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,12 +21,15 @@ 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.StreamCluster
|
import com.aurora.gplayapi.data.models.StreamCluster
|
||||||
import com.aurora.gplayapi.helpers.contracts.TopChartsContract.Chart
|
import com.aurora.gplayapi.helpers.contracts.TopChartsContract.Chart
|
||||||
import com.aurora.gplayapi.helpers.contracts.TopChartsContract.Type
|
import com.aurora.gplayapi.helpers.contracts.TopChartsContract.Type
|
||||||
import com.aurora.store.R
|
import com.aurora.store.R
|
||||||
|
import com.aurora.store.TopChartStash
|
||||||
|
import com.aurora.store.data.ViewState
|
||||||
|
import com.aurora.store.data.ViewState.Empty.getDataAs
|
||||||
import com.aurora.store.databinding.FragmentTopContainerBinding
|
import com.aurora.store.databinding.FragmentTopContainerBinding
|
||||||
import com.aurora.store.view.custom.recycler.EndlessRecyclerOnScrollListener
|
import com.aurora.store.view.custom.recycler.EndlessRecyclerOnScrollListener
|
||||||
import com.aurora.store.view.epoxy.views.AppProgressViewModel_
|
import com.aurora.store.view.epoxy.views.AppProgressViewModel_
|
||||||
@@ -41,7 +44,9 @@ class TopChartFragment : BaseFragment(R.layout.fragment_top_container) {
|
|||||||
private var _binding: FragmentTopContainerBinding? = null
|
private var _binding: FragmentTopContainerBinding? = null
|
||||||
private val binding get() = _binding!!
|
private val binding get() = _binding!!
|
||||||
|
|
||||||
private val viewModel: TopChartViewModel by viewModels()
|
private val viewModel: TopChartViewModel by activityViewModels()
|
||||||
|
|
||||||
|
private var streamCluster: StreamCluster? = StreamCluster()
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
@@ -59,42 +64,52 @@ class TopChartFragment : BaseFragment(R.layout.fragment_top_container) {
|
|||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
_binding = FragmentTopContainerBinding.bind(view)
|
_binding = FragmentTopContainerBinding.bind(view)
|
||||||
|
|
||||||
var chartType = 0
|
var type = 0
|
||||||
var chartCategory = 0
|
var category = 0
|
||||||
val bundle = arguments
|
val bundle = arguments
|
||||||
|
|
||||||
if (bundle != null) {
|
if (bundle != null) {
|
||||||
chartType = bundle.getInt(Constants.TOP_CHART_TYPE, 0)
|
type = bundle.getInt(Constants.TOP_CHART_TYPE, 0)
|
||||||
chartCategory = bundle.getInt(Constants.TOP_CHART_CATEGORY, 0)
|
category = bundle.getInt(Constants.TOP_CHART_CATEGORY, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
when (chartType) {
|
val chartType = when (type) {
|
||||||
0 -> when (chartCategory) {
|
1 -> Type.GAME
|
||||||
0 -> viewModel.getStreamCluster(Type.APPLICATION, Chart.TOP_SELLING_FREE)
|
else -> Type.APPLICATION
|
||||||
1 -> viewModel.getStreamCluster(Type.APPLICATION, Chart.TOP_GROSSING)
|
|
||||||
2 -> viewModel.getStreamCluster(Type.APPLICATION, Chart.MOVERS_SHAKERS)
|
|
||||||
3 -> viewModel.getStreamCluster(Type.APPLICATION, Chart.TOP_SELLING_PAID)
|
|
||||||
}
|
|
||||||
|
|
||||||
1 -> when (chartCategory) {
|
|
||||||
0 -> viewModel.getStreamCluster(Type.GAME, Chart.TOP_SELLING_FREE)
|
|
||||||
1 -> viewModel.getStreamCluster(Type.GAME, Chart.TOP_GROSSING)
|
|
||||||
2 -> viewModel.getStreamCluster(Type.GAME, Chart.MOVERS_SHAKERS)
|
|
||||||
3 -> viewModel.getStreamCluster(Type.GAME, Chart.TOP_SELLING_PAID)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
viewModel.liveData.observe(viewLifecycleOwner) {
|
val chartCategory = when (category) {
|
||||||
updateController(it)
|
1 -> Chart.TOP_GROSSING
|
||||||
|
2 -> Chart.MOVERS_SHAKERS
|
||||||
|
3 -> Chart.TOP_SELLING_PAID
|
||||||
|
else -> Chart.TOP_SELLING_FREE
|
||||||
}
|
}
|
||||||
|
|
||||||
binding.recycler.addOnScrollListener(object : EndlessRecyclerOnScrollListener() {
|
binding.recycler.addOnScrollListener(object : EndlessRecyclerOnScrollListener() {
|
||||||
override fun onLoadMore(currentPage: Int) {
|
override fun onLoadMore(currentPage: Int) {
|
||||||
viewModel.nextCluster()
|
viewModel.nextCluster(chartType, chartCategory)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
updateController(null)
|
updateController(null)
|
||||||
|
|
||||||
|
viewModel.getStreamCluster(chartType, chartCategory)
|
||||||
|
viewModel.liveData.observe(viewLifecycleOwner) {
|
||||||
|
when (it) {
|
||||||
|
is ViewState.Loading, is ViewState.Error -> {
|
||||||
|
updateController(null)
|
||||||
|
}
|
||||||
|
|
||||||
|
is ViewState.Success<*> -> {
|
||||||
|
val stash = it.getDataAs<TopChartStash>()
|
||||||
|
streamCluster = stash[chartType]?.get(chartCategory)
|
||||||
|
|
||||||
|
updateController(streamCluster)
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> {}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDestroyView() {
|
override fun onDestroyView() {
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ import com.aurora.gplayapi.helpers.StreamHelper
|
|||||||
import com.aurora.store.data.model.ViewState
|
import com.aurora.store.data.model.ViewState
|
||||||
import com.aurora.gplayapi.helpers.contracts.StreamContract
|
import com.aurora.gplayapi.helpers.contracts.StreamContract
|
||||||
import com.aurora.gplayapi.helpers.web.WebStreamHelper
|
import com.aurora.gplayapi.helpers.web.WebStreamHelper
|
||||||
|
import com.aurora.store.HomeStash
|
||||||
import com.aurora.store.data.network.HttpClient
|
import com.aurora.store.data.network.HttpClient
|
||||||
import com.aurora.store.data.providers.AuthProvider
|
import com.aurora.store.data.providers.AuthProvider
|
||||||
import com.aurora.store.util.Log
|
import com.aurora.store.util.Log
|
||||||
@@ -55,7 +56,7 @@ class StreamViewModel @Inject constructor(
|
|||||||
|
|
||||||
val liveData: MutableLiveData<ViewState> = MutableLiveData()
|
val liveData: MutableLiveData<ViewState> = MutableLiveData()
|
||||||
|
|
||||||
private var streamBundleMap: MutableMap<StreamContract.Category, StreamBundle> = mutableMapOf(
|
private var stash: HomeStash = mutableMapOf(
|
||||||
StreamContract.Category.APPLICATION to StreamBundle(),
|
StreamContract.Category.APPLICATION to StreamBundle(),
|
||||||
StreamContract.Category.GAME to StreamBundle()
|
StreamContract.Category.GAME to StreamBundle()
|
||||||
)
|
)
|
||||||
@@ -77,7 +78,7 @@ class StreamViewModel @Inject constructor(
|
|||||||
viewModelScope.launch(Dispatchers.IO) {
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
supervisorScope {
|
supervisorScope {
|
||||||
if (targetBundle(category).streamClusters.isNotEmpty()) {
|
if (targetBundle(category).streamClusters.isNotEmpty()) {
|
||||||
liveData.postValue(ViewState.Success(targetBundle(category)))
|
liveData.postValue(ViewState.Success(stash))
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -100,7 +101,7 @@ class StreamViewModel @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
//Post updated to UI
|
//Post updated to UI
|
||||||
liveData.postValue(ViewState.Success(targetBundle(category)))
|
liveData.postValue(ViewState.Success(stash))
|
||||||
} else {
|
} else {
|
||||||
Log.i("End of Bundle")
|
Log.i("End of Bundle")
|
||||||
}
|
}
|
||||||
@@ -119,7 +120,7 @@ class StreamViewModel @Inject constructor(
|
|||||||
val newCluster =
|
val newCluster =
|
||||||
contract().nextStreamCluster(streamCluster.clusterNextPageUrl)
|
contract().nextStreamCluster(streamCluster.clusterNextPageUrl)
|
||||||
updateCluster(category, streamCluster.id, newCluster)
|
updateCluster(category, streamCluster.id, newCluster)
|
||||||
liveData.postValue(ViewState.Success(targetBundle(category)))
|
liveData.postValue(ViewState.Success(stash))
|
||||||
} else {
|
} else {
|
||||||
Log.i("End of cluster")
|
Log.i("End of cluster")
|
||||||
streamCluster.clusterNextPageUrl = String()
|
streamCluster.clusterNextPageUrl = String()
|
||||||
@@ -143,8 +144,9 @@ class StreamViewModel @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun targetBundle(category: StreamContract.Category): StreamBundle {
|
private fun targetBundle(category: StreamContract.Category): StreamBundle {
|
||||||
val streamBundle = streamBundleMap[category]
|
// stash is initialized with empty StreamBundle so this will never return null or throw an exception
|
||||||
|
val streamBundle = stash.getValue(category)
|
||||||
|
|
||||||
return streamBundle!!
|
return streamBundle
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,6 +28,8 @@ import com.aurora.gplayapi.data.models.StreamCluster
|
|||||||
import com.aurora.gplayapi.helpers.TopChartsHelper
|
import com.aurora.gplayapi.helpers.TopChartsHelper
|
||||||
import com.aurora.gplayapi.helpers.contracts.TopChartsContract
|
import com.aurora.gplayapi.helpers.contracts.TopChartsContract
|
||||||
import com.aurora.gplayapi.helpers.web.WebTopChartsHelper
|
import com.aurora.gplayapi.helpers.web.WebTopChartsHelper
|
||||||
|
import com.aurora.store.TopChartStash
|
||||||
|
import com.aurora.store.data.ViewState
|
||||||
import com.aurora.store.data.network.HttpClient
|
import com.aurora.store.data.network.HttpClient
|
||||||
import com.aurora.store.data.providers.AuthProvider
|
import com.aurora.store.data.providers.AuthProvider
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
@@ -50,11 +52,22 @@ class TopChartViewModel @Inject constructor(
|
|||||||
private val webTopChartsHelper: TopChartsContract = WebTopChartsHelper()
|
private val webTopChartsHelper: TopChartsContract = WebTopChartsHelper()
|
||||||
.using(HttpClient.getPreferredClient(context))
|
.using(HttpClient.getPreferredClient(context))
|
||||||
|
|
||||||
val liveData: MutableLiveData<StreamCluster> = MutableLiveData()
|
private val dummyChart = mapOf(
|
||||||
var streamCluster: StreamCluster = StreamCluster()
|
TopChartsContract.Chart.TOP_GROSSING to StreamCluster(),
|
||||||
|
TopChartsContract.Chart.TOP_SELLING_FREE to StreamCluster(),
|
||||||
|
TopChartsContract.Chart.TOP_SELLING_PAID to StreamCluster(),
|
||||||
|
TopChartsContract.Chart.MOVERS_SHAKERS to StreamCluster(),
|
||||||
|
)
|
||||||
|
|
||||||
|
private var stash: TopChartStash = mapOf(
|
||||||
|
TopChartsContract.Type.APPLICATION to dummyChart,
|
||||||
|
TopChartsContract.Type.GAME to dummyChart
|
||||||
|
)
|
||||||
|
|
||||||
|
val liveData: MutableLiveData<ViewState> = MutableLiveData()
|
||||||
|
|
||||||
private fun contract(): TopChartsContract {
|
private fun contract(): TopChartsContract {
|
||||||
return if(authProvider.isAnonymous){
|
return if (authProvider.isAnonymous) {
|
||||||
webTopChartsHelper
|
webTopChartsHelper
|
||||||
} else {
|
} else {
|
||||||
topChartsHelper
|
topChartsHelper
|
||||||
@@ -63,34 +76,56 @@ class TopChartViewModel @Inject constructor(
|
|||||||
|
|
||||||
fun getStreamCluster(type: TopChartsContract.Type, chart: TopChartsContract.Chart) {
|
fun getStreamCluster(type: TopChartsContract.Type, chart: TopChartsContract.Chart) {
|
||||||
viewModelScope.launch(Dispatchers.IO) {
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
|
if (targetCluster(type, chart).clusterAppList.isNotEmpty()) {
|
||||||
|
liveData.postValue(ViewState.Success(stash))
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
streamCluster = contract().getCluster(type.value, chart.value)
|
val cluster = contract().getCluster(type.value, chart.value)
|
||||||
liveData.postValue(streamCluster)
|
updateCluster(type, chart, cluster)
|
||||||
|
liveData.postValue(ViewState.Success(stash))
|
||||||
} catch (_: Exception) {
|
} catch (_: Exception) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun nextCluster() {
|
fun nextCluster(type: TopChartsContract.Type, chart: TopChartsContract.Chart) {
|
||||||
viewModelScope.launch(Dispatchers.IO) {
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
supervisorScope {
|
supervisorScope {
|
||||||
try {
|
try {
|
||||||
if (streamCluster.hasNext()) {
|
val target = targetCluster(type, chart)
|
||||||
|
if (target.hasNext()) {
|
||||||
val newCluster = topChartsHelper.getNextStreamCluster(
|
val newCluster = topChartsHelper.getNextStreamCluster(
|
||||||
streamCluster.clusterNextPageUrl
|
target.clusterNextPageUrl
|
||||||
)
|
)
|
||||||
|
|
||||||
streamCluster.apply {
|
updateCluster(type, chart, newCluster)
|
||||||
clusterAppList.addAll(newCluster.clusterAppList)
|
|
||||||
clusterNextPageUrl = newCluster.clusterNextPageUrl
|
|
||||||
}
|
|
||||||
|
|
||||||
liveData.postValue(streamCluster)
|
liveData.postValue(ViewState.Success(stash))
|
||||||
} else {
|
|
||||||
}
|
}
|
||||||
} catch (_: Exception) {
|
} catch (_: Exception) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun updateCluster(
|
||||||
|
type: TopChartsContract.Type,
|
||||||
|
chart: TopChartsContract.Chart,
|
||||||
|
newCluster: StreamCluster
|
||||||
|
) {
|
||||||
|
targetCluster(type, chart).apply {
|
||||||
|
clusterAppList.addAll(newCluster.clusterAppList)
|
||||||
|
clusterNextPageUrl = newCluster.clusterNextPageUrl
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun targetCluster(
|
||||||
|
type: TopChartsContract.Type,
|
||||||
|
chart: TopChartsContract.Chart
|
||||||
|
): StreamCluster {
|
||||||
|
// Stash is initialized with empty clusters so this will never return null or throw an exception
|
||||||
|
val cluster = stash.getValue(type).getValue(chart)
|
||||||
|
return cluster
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user