AppDetailsFragment: Improve data states
This commit is contained in:
committed by
Aayush Gupta
parent
83d9895f17
commit
660c544f18
@@ -8,4 +8,5 @@ import com.aurora.gplayapi.helpers.contracts.TopChartsContract
|
||||
|
||||
typealias TopChartStash = MutableMap<TopChartsContract.Type, MutableMap<TopChartsContract.Chart, StreamCluster>>
|
||||
typealias HomeStash = MutableMap<StreamContract.Category, StreamBundle>
|
||||
typealias CategoryStash = MutableMap<Category.Type, List<Category>>
|
||||
typealias CategoryStash = MutableMap<Category.Type, List<Category>>
|
||||
typealias AppStreamStash = MutableMap<String, StreamBundle>
|
||||
@@ -35,7 +35,7 @@ import android.widget.Toast
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.text.HtmlCompat
|
||||
import androidx.fragment.app.viewModels
|
||||
import androidx.fragment.app.activityViewModels
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import androidx.navigation.fragment.navArgs
|
||||
@@ -56,6 +56,7 @@ import com.aurora.gplayapi.data.models.Review
|
||||
import com.aurora.gplayapi.data.models.StreamBundle
|
||||
import com.aurora.gplayapi.data.models.StreamCluster
|
||||
import com.aurora.store.AuroraApp
|
||||
import com.aurora.store.AppStreamStash
|
||||
import com.aurora.store.R
|
||||
import com.aurora.store.data.event.BusEvent
|
||||
import com.aurora.store.data.event.Event
|
||||
@@ -64,6 +65,7 @@ import com.aurora.store.data.installer.AppInstaller
|
||||
import com.aurora.store.data.model.DownloadStatus
|
||||
import com.aurora.store.data.model.State
|
||||
import com.aurora.store.data.model.ViewState
|
||||
import com.aurora.store.data.model.ViewState.Loading.getDataAs
|
||||
import com.aurora.store.data.providers.AuthProvider
|
||||
import com.aurora.store.databinding.FragmentDetailsBinding
|
||||
import com.aurora.store.util.CommonUtil
|
||||
@@ -92,8 +94,8 @@ import javax.inject.Inject
|
||||
@AndroidEntryPoint
|
||||
class AppDetailsFragment : BaseFragment<FragmentDetailsBinding>() {
|
||||
|
||||
private val viewModel: AppDetailsViewModel by viewModels()
|
||||
private val detailsClusterViewModel: DetailsClusterViewModel by viewModels()
|
||||
private val viewModel: AppDetailsViewModel by activityViewModels()
|
||||
private val detailsClusterViewModel: DetailsClusterViewModel by activityViewModels()
|
||||
|
||||
private val args: AppDetailsFragmentArgs by navArgs()
|
||||
|
||||
@@ -123,6 +125,8 @@ class AppDetailsFragment : BaseFragment<FragmentDetailsBinding>() {
|
||||
|
||||
private lateinit var app: App
|
||||
|
||||
private var streamBundle: StreamBundle? = StreamBundle()
|
||||
|
||||
private var isExternal = false
|
||||
private var downloadStatus = DownloadStatus.UNAVAILABLE
|
||||
private var isUpdatable: Boolean = false
|
||||
@@ -196,7 +200,7 @@ class AppDetailsFragment : BaseFragment<FragmentDetailsBinding>() {
|
||||
app.isInstalled = PackageUtil.isInstalled(requireContext(), app.packageName)
|
||||
|
||||
// App Details
|
||||
viewModel.fetchAppDetails(view.context, app.packageName)
|
||||
viewModel.fetchAppDetails(app.packageName)
|
||||
|
||||
viewLifecycleOwner.lifecycleScope.launch {
|
||||
viewModel.app.collect {
|
||||
@@ -204,7 +208,7 @@ class AppDetailsFragment : BaseFragment<FragmentDetailsBinding>() {
|
||||
app = it
|
||||
inflatePartialApp() // Re-inflate the app details, as web data may vary.
|
||||
inflateExtraDetails(app)
|
||||
viewModel.fetchAppReviews(view.context, app.packageName)
|
||||
viewModel.fetchAppReviews(app.packageName)
|
||||
} else {
|
||||
toast("Failed to fetch app details")
|
||||
}
|
||||
@@ -266,7 +270,7 @@ class AppDetailsFragment : BaseFragment<FragmentDetailsBinding>() {
|
||||
|
||||
// Exodus Privacy Report
|
||||
viewLifecycleOwner.lifecycleScope.launch {
|
||||
viewModel.report.collect { report ->
|
||||
viewModel.exodusReport.collect { report ->
|
||||
if (report == null) {
|
||||
binding.layoutDetailsPrivacy.txtStatus.text =
|
||||
getString(R.string.failed_to_fetch_report)
|
||||
@@ -801,7 +805,7 @@ class AppDetailsFragment : BaseFragment<FragmentDetailsBinding>() {
|
||||
}
|
||||
|
||||
private fun inflateAppPrivacy(app: App) {
|
||||
viewModel.fetchAppReport(requireContext(), app.packageName)
|
||||
viewModel.fetchAppReport(app.packageName)
|
||||
}
|
||||
|
||||
private fun inflateAppDevInfo(app: App) {
|
||||
@@ -854,7 +858,6 @@ class AppDetailsFragment : BaseFragment<FragmentDetailsBinding>() {
|
||||
btnBetaAction.text = getString(R.string.action_pending)
|
||||
btnBetaAction.isEnabled = false
|
||||
viewModel.fetchTestingProgramStatus(
|
||||
requireContext(),
|
||||
app.packageName,
|
||||
!betaProgram.isSubscribed
|
||||
)
|
||||
@@ -881,43 +884,34 @@ class AppDetailsFragment : BaseFragment<FragmentDetailsBinding>() {
|
||||
}
|
||||
|
||||
override fun onClusterScrolled(streamCluster: StreamCluster) {
|
||||
detailsClusterViewModel.observeCluster(streamCluster)
|
||||
detailsClusterViewModel.observeCluster(
|
||||
it,
|
||||
streamCluster
|
||||
)
|
||||
}
|
||||
|
||||
override fun onAppClick(app: App) {
|
||||
openDetailsFragment(app.packageName, app)
|
||||
}
|
||||
|
||||
override fun onAppLongClick(app: App) {
|
||||
|
||||
}
|
||||
override fun onAppLongClick(app: App) {}
|
||||
})
|
||||
|
||||
detailsClusterViewModel.liveData.observe(viewLifecycleOwner) {
|
||||
when (it) {
|
||||
is ViewState.Empty -> {
|
||||
}
|
||||
|
||||
is ViewState.Loading -> {
|
||||
|
||||
}
|
||||
|
||||
is ViewState.Error -> {
|
||||
|
||||
}
|
||||
|
||||
is ViewState.Status -> {
|
||||
|
||||
}
|
||||
|
||||
detailsClusterViewModel.liveData.observe(viewLifecycleOwner) { state ->
|
||||
when (state) {
|
||||
is ViewState.Success<*> -> {
|
||||
carouselController.setData(it.data as StreamBundle)
|
||||
val stash = state.getDataAs<AppStreamStash>()
|
||||
streamBundle = stash[it]
|
||||
carouselController.setData(streamBundle)
|
||||
}
|
||||
|
||||
else -> {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
binding.epoxyRecyclerStream.setController(carouselController)
|
||||
|
||||
detailsClusterViewModel.getStreamBundle(it)
|
||||
}
|
||||
}
|
||||
@@ -925,7 +919,7 @@ class AppDetailsFragment : BaseFragment<FragmentDetailsBinding>() {
|
||||
private fun inflateAppPermission(app: App) {
|
||||
binding.layoutDetailsPermissions.apply {
|
||||
headerPermission.addClickListener {
|
||||
if (app.permissions.size > 0) {
|
||||
if (app.permissions.isNotEmpty()) {
|
||||
findNavController().navigate(
|
||||
AppDetailsFragmentDirections.actionAppDetailsFragmentToPermissionBottomSheet(
|
||||
app
|
||||
@@ -956,6 +950,6 @@ class AppDetailsFragment : BaseFragment<FragmentDetailsBinding>() {
|
||||
}
|
||||
|
||||
private fun addOrUpdateReview(app: App, review: Review, isBeta: Boolean = false) {
|
||||
viewModel.postAppReview(requireContext(), app.packageName, review, isBeta)
|
||||
viewModel.postAppReview(app.packageName, review, isBeta)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ import com.aurora.store.data.room.favourites.FavouriteDao
|
||||
import com.aurora.store.util.DownloadWorkerUtil
|
||||
import com.google.gson.GsonBuilder
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
@@ -30,6 +31,7 @@ import javax.inject.Inject
|
||||
|
||||
@HiltViewModel
|
||||
class AppDetailsViewModel @Inject constructor(
|
||||
@ApplicationContext private val context: Context,
|
||||
private val downloadWorkerUtil: DownloadWorkerUtil,
|
||||
private val authProvider: AuthProvider,
|
||||
private val favouriteDao: FavouriteDao
|
||||
@@ -40,18 +42,27 @@ class AppDetailsViewModel @Inject constructor(
|
||||
private val exodusBaseUrl = "https://reports.exodus-privacy.eu.org/api/search/"
|
||||
private val exodusApiKey = "Token bbe6ebae4ad45a9cbacb17d69739799b8df2c7ae"
|
||||
|
||||
private val httpClient = HttpClient.getPreferredClient(context)
|
||||
private val appDetailsHelper = AppDetailsHelper(authProvider.authData).using(httpClient)
|
||||
private val reviewsHelper = ReviewsHelper(authProvider.authData).using(httpClient)
|
||||
|
||||
private val appStash: MutableMap<String, App> = mutableMapOf()
|
||||
private val _app = MutableSharedFlow<App>()
|
||||
val app = _app.asSharedFlow()
|
||||
|
||||
private val reviewsStash = mutableMapOf<String, List<Review>>()
|
||||
private val _reviews = MutableSharedFlow<List<Review>>()
|
||||
val reviews = _reviews.asSharedFlow()
|
||||
|
||||
private val userReviewStash = mutableMapOf<String, Review?>()
|
||||
private val _userReview = MutableSharedFlow<Review>()
|
||||
val userReview = _userReview.asSharedFlow()
|
||||
|
||||
private val _report = MutableSharedFlow<Report?>()
|
||||
val report = _report.asSharedFlow()
|
||||
private val exodusReportStash = mutableMapOf<String, Report?>()
|
||||
private val _exodusReport = MutableSharedFlow<Report?>()
|
||||
val exodusReport = _exodusReport.asSharedFlow()
|
||||
|
||||
private val testProgramStatusStash = mutableMapOf<String, TestingProgramStatus?>()
|
||||
private val _testingProgramStatus = MutableSharedFlow<TestingProgramStatus?>()
|
||||
val testingProgramStatus = _testingProgramStatus.asSharedFlow()
|
||||
|
||||
@@ -60,16 +71,16 @@ class AppDetailsViewModel @Inject constructor(
|
||||
|
||||
val downloadsList get() = downloadWorkerUtil.downloadsList
|
||||
|
||||
fun fetchAppDetails(context: Context, packageName: String) {
|
||||
fun fetchAppDetails(packageName: String) {
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
try {
|
||||
isFavourite(packageName)
|
||||
checkFavourite(packageName)
|
||||
|
||||
_app.emit(
|
||||
AppDetailsHelper(authProvider.authData)
|
||||
.using(HttpClient.getPreferredClient(context))
|
||||
.getAppByPackageName(packageName)
|
||||
)
|
||||
val app: App = appStash.getOrPut(packageName) {
|
||||
appDetailsHelper.getAppByPackageName(packageName)
|
||||
}
|
||||
|
||||
_app.emit(app)
|
||||
} catch (exception: Exception) {
|
||||
Log.e(TAG, "Failed to fetch app details", exception)
|
||||
_app.emit(App(""))
|
||||
@@ -77,14 +88,14 @@ class AppDetailsViewModel @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
fun fetchAppReviews(context: Context, packageName: String) {
|
||||
fun fetchAppReviews(packageName: String) {
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
try {
|
||||
_reviews.emit(
|
||||
ReviewsHelper(authProvider.authData)
|
||||
.using(HttpClient.getPreferredClient(context))
|
||||
.getReviewSummary(packageName)
|
||||
)
|
||||
val reviews = reviewsStash.getOrPut(packageName) {
|
||||
reviewsHelper.getReviewSummary(packageName)
|
||||
}
|
||||
|
||||
_reviews.emit(reviews)
|
||||
} catch (exception: Exception) {
|
||||
Log.e(TAG, "Failed to fetch app reviews", exception)
|
||||
_reviews.emit(emptyList())
|
||||
@@ -92,63 +103,60 @@ class AppDetailsViewModel @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
fun postAppReview(context: Context, packageName: String, review: Review, isBeta: Boolean) {
|
||||
fun fetchAppReport(packageName: String) {
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
try {
|
||||
_userReview.emit(
|
||||
ReviewsHelper(authProvider.authData)
|
||||
.using(HttpClient.getPreferredClient(context))
|
||||
.addOrEditReview(
|
||||
packageName,
|
||||
review.title,
|
||||
review.comment,
|
||||
review.rating,
|
||||
isBeta
|
||||
)!!
|
||||
)
|
||||
} catch (exception: Exception) {
|
||||
Log.e(TAG, "Failed to post review", exception)
|
||||
_userReview.emit(Review())
|
||||
}
|
||||
}
|
||||
}
|
||||
val exodusReport = exodusReportStash.getOrPut(packageName) {
|
||||
getLatestExodusReport(packageName)
|
||||
}
|
||||
|
||||
|
||||
fun fetchAppReport(context: Context, packageName: String) {
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
try {
|
||||
val headers: MutableMap<String, String> = mutableMapOf()
|
||||
headers["Content-Type"] = "application/json"
|
||||
headers["Accept"] = "application/json"
|
||||
headers["Authorization"] = exodusApiKey
|
||||
|
||||
val url = exodusBaseUrl + packageName
|
||||
val playResponse = HttpClient.getPreferredClient(context).get(url, headers)
|
||||
|
||||
_report.emit(parseResponse(String(playResponse.responseBytes), packageName)[0])
|
||||
_exodusReport.emit(exodusReport)
|
||||
} catch (exception: Exception) {
|
||||
Log.e(TAG, "Failed to fetch privacy report", exception)
|
||||
_report.emit(null)
|
||||
exodusReportStash[packageName] = null
|
||||
_exodusReport.emit(null)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun fetchTestingProgramStatus(context: Context, packageName: String, subscribe: Boolean) {
|
||||
fun fetchTestingProgramStatus(packageName: String, subscribe: Boolean) {
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
try {
|
||||
_testingProgramStatus.emit(
|
||||
AppDetailsHelper(authProvider.authData).testingProgram(
|
||||
packageName,
|
||||
subscribe
|
||||
)
|
||||
)
|
||||
val testingProgramStatus = testProgramStatusStash.getOrPut(packageName) {
|
||||
appDetailsHelper.testingProgram(packageName, subscribe)
|
||||
}
|
||||
|
||||
_testingProgramStatus.emit(testingProgramStatus)
|
||||
} catch (exception: Exception) {
|
||||
Log.e(TAG, "Failed to fetch testing program status", exception)
|
||||
testProgramStatusStash[packageName] = null
|
||||
_testingProgramStatus.emit(null)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun postAppReview(packageName: String, review: Review, isBeta: Boolean) {
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
try {
|
||||
val userReview = userReviewStash.getOrPut(packageName) {
|
||||
reviewsHelper.addOrEditReview(
|
||||
packageName,
|
||||
review.title,
|
||||
review.comment,
|
||||
review.rating,
|
||||
isBeta
|
||||
)
|
||||
}
|
||||
|
||||
if (userReview != null) {
|
||||
_userReview.emit(userReview)
|
||||
}
|
||||
} catch (exception: Exception) {
|
||||
Log.e(TAG, "Failed to post review", exception)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun download(app: App) {
|
||||
viewModelScope.launch { downloadWorkerUtil.enqueueApp(app) }
|
||||
}
|
||||
@@ -157,12 +165,6 @@ class AppDetailsViewModel @Inject constructor(
|
||||
viewModelScope.launch { downloadWorkerUtil.cancelDownload(app.packageName) }
|
||||
}
|
||||
|
||||
private fun isFavourite(packageName: String) {
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
_favourite.value = favouriteDao.isFavourite(packageName)
|
||||
}
|
||||
}
|
||||
|
||||
fun toggleFavourite(app: App) {
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
if (favourite.value) {
|
||||
@@ -183,9 +185,31 @@ class AppDetailsViewModel @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
private fun parseResponse(response: String, packageName: String): List<Report> {
|
||||
private fun checkFavourite(packageName: String) {
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
_favourite.value = favouriteDao.isFavourite(packageName)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getLatestExodusReport(packageName: String): Report? {
|
||||
val headers: MutableMap<String, String> = mutableMapOf()
|
||||
headers["Content-Type"] = "application/json"
|
||||
headers["Accept"] = "application/json"
|
||||
headers["Authorization"] = exodusApiKey
|
||||
|
||||
val url = exodusBaseUrl + packageName
|
||||
val playResponse = httpClient.get(url, headers)
|
||||
|
||||
val report = parseExodusResponse(String(playResponse.responseBytes), packageName)
|
||||
.firstOrNull()
|
||||
|
||||
return report
|
||||
}
|
||||
|
||||
private fun parseExodusResponse(response: String, packageName: String): List<Report> {
|
||||
try {
|
||||
val gson = GsonBuilder().excludeFieldsWithModifiers(Modifier.TRANSIENT, Modifier.STATIC)
|
||||
val gson = GsonBuilder()
|
||||
.excludeFieldsWithModifiers(Modifier.TRANSIENT, Modifier.STATIC)
|
||||
.create()
|
||||
|
||||
val jsonObject = JSONObject(response)
|
||||
@@ -193,9 +217,10 @@ class AppDetailsViewModel @Inject constructor(
|
||||
val exodusReport: ExodusReport = gson.fromJson(
|
||||
exodusObject.toString(), ExodusReport::class.java
|
||||
)
|
||||
|
||||
return exodusReport.reports
|
||||
} catch (e: Exception) {
|
||||
throw Exception("No reports found")
|
||||
return emptyList()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
|
||||
package com.aurora.store.viewmodel.details
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
@@ -29,6 +28,7 @@ import com.aurora.gplayapi.data.models.StreamCluster
|
||||
import com.aurora.gplayapi.helpers.AppDetailsHelper
|
||||
import com.aurora.gplayapi.helpers.StreamHelper
|
||||
import com.aurora.gplayapi.helpers.contracts.StreamContract
|
||||
import com.aurora.store.AppStreamStash
|
||||
import com.aurora.store.data.model.ViewState
|
||||
import com.aurora.store.data.network.HttpClient
|
||||
import com.aurora.store.data.providers.AuthProvider
|
||||
@@ -41,18 +41,18 @@ import kotlinx.coroutines.supervisorScope
|
||||
import javax.inject.Inject
|
||||
|
||||
@HiltViewModel
|
||||
@SuppressLint("StaticFieldLeak") // false positive, see https://github.com/google/dagger/issues/3253
|
||||
class DetailsClusterViewModel @Inject constructor(
|
||||
@ApplicationContext private val context: Context,
|
||||
private val authProvider: AuthProvider
|
||||
authProvider: AuthProvider
|
||||
) : ViewModel() {
|
||||
|
||||
private var appDetailsHelper = AppDetailsHelper(authProvider.authData)
|
||||
.using(HttpClient.getPreferredClient(context))
|
||||
private var streamHelper = StreamHelper(authProvider.authData)
|
||||
|
||||
|
||||
val liveData: MutableLiveData<ViewState> = MutableLiveData()
|
||||
var streamBundle: StreamBundle = StreamBundle()
|
||||
private val stash: AppStreamStash = mutableMapOf()
|
||||
|
||||
lateinit var type: StreamContract.Type
|
||||
lateinit var category: StreamContract.Category
|
||||
@@ -60,9 +60,22 @@ class DetailsClusterViewModel @Inject constructor(
|
||||
fun getStreamBundle(streamUrl: String) {
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
supervisorScope {
|
||||
val bundle = targetBundle(streamUrl)
|
||||
if (bundle.streamClusters.isNotEmpty()) {
|
||||
liveData.postValue(ViewState.Success(stash))
|
||||
}
|
||||
|
||||
try {
|
||||
streamBundle = appDetailsHelper.getDetailsStream(streamUrl)
|
||||
liveData.postValue(ViewState.Success(streamBundle))
|
||||
if (!bundle.hasCluster() || bundle.hasNext()) {
|
||||
val newBundle = appDetailsHelper.getDetailsStream(streamUrl)
|
||||
|
||||
bundle.apply {
|
||||
streamClusters.putAll(newBundle.streamClusters)
|
||||
streamNextPageUrl = newBundle.streamNextPageUrl
|
||||
}
|
||||
|
||||
liveData.postValue(ViewState.Success(stash))
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
liveData.postValue(ViewState.Error(e.message))
|
||||
}
|
||||
@@ -70,15 +83,15 @@ class DetailsClusterViewModel @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
fun observeCluster(streamCluster: StreamCluster) {
|
||||
fun observeCluster(url: String, streamCluster: StreamCluster) {
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
supervisorScope {
|
||||
try {
|
||||
if (streamCluster.hasNext()) {
|
||||
val newCluster =
|
||||
streamHelper.getNextStreamCluster(streamCluster.clusterNextPageUrl)
|
||||
updateCluster(newCluster)
|
||||
liveData.postValue(ViewState.Success(streamBundle))
|
||||
updateCluster(url, streamCluster.id, newCluster)
|
||||
liveData.postValue(ViewState.Success(stash))
|
||||
} else {
|
||||
Log.i("End of cluster")
|
||||
streamCluster.clusterNextPageUrl = String()
|
||||
@@ -90,10 +103,22 @@ class DetailsClusterViewModel @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateCluster(newCluster: StreamCluster) {
|
||||
streamBundle.streamClusters[newCluster.id]?.apply {
|
||||
private fun updateCluster(
|
||||
url: String,
|
||||
clusterID: Int,
|
||||
newCluster: StreamCluster
|
||||
) {
|
||||
targetBundle(url).streamClusters[clusterID]?.apply {
|
||||
clusterAppList.addAll(newCluster.clusterAppList)
|
||||
clusterNextPageUrl = newCluster.clusterNextPageUrl
|
||||
}
|
||||
}
|
||||
|
||||
private fun targetBundle(url: String): StreamBundle {
|
||||
val streamBundle = stash.getOrPut(url) {
|
||||
StreamBundle()
|
||||
}
|
||||
|
||||
return streamBundle
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
package com.aurora.store.viewmodel.details
|
||||
|
||||
import android.content.Context
|
||||
import android.util.Log
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.aurora.gplayapi.data.models.App
|
||||
import com.aurora.gplayapi.helpers.AppDetailsHelper
|
||||
import com.aurora.store.data.network.HttpClient
|
||||
import com.aurora.store.data.providers.AuthProvider
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.asSharedFlow
|
||||
@@ -15,21 +18,30 @@ import javax.inject.Inject
|
||||
|
||||
@HiltViewModel
|
||||
class DetailsMoreViewModel @Inject constructor(
|
||||
@ApplicationContext private val context: Context,
|
||||
private val authProvider: AuthProvider
|
||||
) : ViewModel() {
|
||||
|
||||
private val TAG = DetailsMoreViewModel::class.java.simpleName
|
||||
|
||||
private val appDetailsHelper = AppDetailsHelper(authProvider.authData)
|
||||
.using(HttpClient.getPreferredClient(context))
|
||||
|
||||
private val dependantAppsStash = mutableMapOf<String, List<App>>()
|
||||
private val _dependentApps = MutableSharedFlow<List<App>>()
|
||||
val dependentApps = _dependentApps.asSharedFlow()
|
||||
|
||||
fun fetchDependentApps(app: App) {
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
try {
|
||||
_dependentApps.emit(AppDetailsHelper(authProvider.authData)
|
||||
.getAppByPackageName(app.dependencies.dependentPackages))
|
||||
val dependantApps = dependantAppsStash.getOrPut(app.packageName) {
|
||||
appDetailsHelper.getAppByPackageName(app.dependencies.dependentPackages)
|
||||
}
|
||||
|
||||
_dependentApps.emit(dependantApps)
|
||||
} catch (exception: Exception) {
|
||||
Log.e(TAG, "Failed to fetch dependencies", exception)
|
||||
dependantAppsStash[app.packageName] = emptyList()
|
||||
_dependentApps.emit(emptyList())
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user