Improve Apps & Games page

This commit is contained in:
Rahul Patel
2023-10-14 01:14:49 +05:30
parent bd621163cc
commit 72241bd444
12 changed files with 111 additions and 81 deletions

View File

@@ -48,21 +48,12 @@ abstract class EndlessRecyclerOnScrollListener : RecyclerView.OnScrollListener {
constructor() constructor()
constructor(layoutManager: RecyclerView.LayoutManager) {
this.layoutManager = layoutManager
}
constructor(visibleThreshold: Int) { constructor(visibleThreshold: Int) {
this.visibleThreshold = visibleThreshold this.visibleThreshold = visibleThreshold
} }
constructor(layoutManager: RecyclerView.LayoutManager, visibleThreshold: Int) {
this.layoutManager = layoutManager
this.visibleThreshold = visibleThreshold
}
private fun findFirstVisibleItemPosition(recyclerView: RecyclerView): Int { private fun findFirstVisibleItemPosition(recyclerView: RecyclerView): Int {
val child = findOneVisibleChild(0, layoutManager.childCount, false, true) val child = findOneVisibleChild(0, layoutManager.childCount, true, false)
return if (child == null) RecyclerView.NO_POSITION else recyclerView.getChildAdapterPosition( return if (child == null) RecyclerView.NO_POSITION else recyclerView.getChildAdapterPosition(
child child
) )

View File

@@ -26,7 +26,7 @@ import android.view.ViewGroup
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import com.aurora.gplayapi.data.models.App import com.aurora.gplayapi.data.models.App
import com.aurora.store.R import com.aurora.store.R
import com.aurora.store.databinding.FragmentUpdatesBinding import com.aurora.store.databinding.FragmentAppsBinding
import com.aurora.store.view.epoxy.views.HeaderViewModel_ import com.aurora.store.view.epoxy.views.HeaderViewModel_
import com.aurora.store.view.epoxy.views.app.AppListViewModel_ import com.aurora.store.view.epoxy.views.app.AppListViewModel_
import com.aurora.store.view.epoxy.views.shimmer.AppListViewShimmerModel_ import com.aurora.store.view.epoxy.views.shimmer.AppListViewShimmerModel_
@@ -36,14 +36,12 @@ import com.aurora.store.viewmodel.all.InstalledViewModel
class InstalledAppsFragment : BaseFragment() { class InstalledAppsFragment : BaseFragment() {
private lateinit var VM: InstalledViewModel private lateinit var VM: InstalledViewModel
private lateinit var B: FragmentUpdatesBinding private lateinit var B: FragmentAppsBinding
companion object { companion object {
@JvmStatic @JvmStatic
fun newInstance(): InstalledAppsFragment { fun newInstance(): InstalledAppsFragment {
return InstalledAppsFragment().apply { return InstalledAppsFragment()
}
} }
} }
@@ -52,15 +50,15 @@ class InstalledAppsFragment : BaseFragment() {
container: ViewGroup?, container: ViewGroup?,
savedInstanceState: Bundle? savedInstanceState: Bundle?
): View { ): View {
B = FragmentUpdatesBinding.bind( B = FragmentAppsBinding.bind(
inflater.inflate( inflater.inflate(
R.layout.fragment_updates, R.layout.fragment_apps,
container, container,
false false
) )
) )
VM = ViewModelProvider(requireActivity()).get(InstalledViewModel::class.java) VM = ViewModelProvider(requireActivity())[InstalledViewModel::class.java]
return B.root return B.root
} }
@@ -69,11 +67,6 @@ class InstalledAppsFragment : BaseFragment() {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
VM.liveData.observe(viewLifecycleOwner) { VM.liveData.observe(viewLifecycleOwner) {
updateController(it) updateController(it)
B.swipeRefreshLayout.isRefreshing = false
}
B.swipeRefreshLayout.setOnRefreshListener {
VM.observe()
} }
updateController(null) updateController(null)
@@ -83,7 +76,7 @@ class InstalledAppsFragment : BaseFragment() {
B.recycler.withModels { B.recycler.withModels {
setFilterDuplicates(true) setFilterDuplicates(true)
if (appList == null) { if (appList == null) {
for (i in 1..6) { for (i in 1..10) {
add( add(
AppListViewShimmerModel_() AppListViewShimmerModel_()
.id(i) .id(i)

View File

@@ -26,6 +26,7 @@ import android.view.ViewGroup
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import com.aurora.gplayapi.data.models.StreamCluster import com.aurora.gplayapi.data.models.StreamCluster
import com.aurora.store.R import com.aurora.store.R
import com.aurora.store.databinding.FragmentAppsBinding
import com.aurora.store.databinding.FragmentUpdatesBinding import com.aurora.store.databinding.FragmentUpdatesBinding
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_
@@ -38,7 +39,7 @@ import com.aurora.store.viewmodel.all.LibraryAppsViewModel
class LibraryAppsFragment : BaseFragment() { class LibraryAppsFragment : BaseFragment() {
private lateinit var VM: LibraryAppsViewModel private lateinit var VM: LibraryAppsViewModel
private lateinit var B: FragmentUpdatesBinding private lateinit var B: FragmentAppsBinding
lateinit var endlessRecyclerOnScrollListener: EndlessRecyclerOnScrollListener lateinit var endlessRecyclerOnScrollListener: EndlessRecyclerOnScrollListener
companion object { companion object {
@@ -55,9 +56,9 @@ class LibraryAppsFragment : BaseFragment() {
container: ViewGroup?, container: ViewGroup?,
savedInstanceState: Bundle? savedInstanceState: Bundle?
): View { ): View {
B = FragmentUpdatesBinding.bind( B = FragmentAppsBinding.bind(
inflater.inflate( inflater.inflate(
R.layout.fragment_updates, R.layout.fragment_apps,
container, container,
false false
) )
@@ -70,7 +71,6 @@ class LibraryAppsFragment : BaseFragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
B.swipeRefreshLayout.isEnabled = false
VM.liveData.observe(viewLifecycleOwner) { VM.liveData.observe(viewLifecycleOwner) {
updateController(it) updateController(it)
} }
@@ -92,7 +92,7 @@ class LibraryAppsFragment : BaseFragment() {
B.recycler.withModels { B.recycler.withModels {
setFilterDuplicates(true) setFilterDuplicates(true)
if (streamCluster == null) { if (streamCluster == null) {
for (i in 1..6) { for (i in 1..10) {
add( add(
AppListViewShimmerModel_() AppListViewShimmerModel_()
.id(i) .id(i)

View File

@@ -24,28 +24,26 @@ import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import com.aurora.gplayapi.data.models.App
import com.aurora.store.R import com.aurora.store.R
import com.aurora.store.databinding.FragmentUpdatesBinding import com.aurora.store.databinding.FragmentAppsBinding
import com.aurora.store.view.custom.recycler.EndlessRecyclerOnScrollListener import com.aurora.store.view.custom.recycler.EndlessRecyclerOnScrollListener
import com.aurora.store.view.epoxy.views.HeaderViewModel_ import com.aurora.store.view.epoxy.views.AppProgressViewModel_
import com.aurora.store.view.epoxy.views.app.AppListViewModel_ import com.aurora.store.view.epoxy.views.app.AppListViewModel_
import com.aurora.store.view.epoxy.views.shimmer.AppListViewShimmerModel_ import com.aurora.store.view.epoxy.views.shimmer.AppListViewShimmerModel_
import com.aurora.store.view.ui.commons.BaseFragment import com.aurora.store.view.ui.commons.BaseFragment
import com.aurora.store.viewmodel.all.PaginatedAppList
import com.aurora.store.viewmodel.all.PurchasedViewModel import com.aurora.store.viewmodel.all.PurchasedViewModel
class PurchasedAppsFragment : BaseFragment() { class PurchasedAppsFragment : BaseFragment() {
private lateinit var VM: PurchasedViewModel private lateinit var VM: PurchasedViewModel
private lateinit var B: FragmentUpdatesBinding private lateinit var B: FragmentAppsBinding
lateinit var endlessRecyclerOnScrollListener: EndlessRecyclerOnScrollListener lateinit var endlessRecyclerOnScrollListener: EndlessRecyclerOnScrollListener
companion object { companion object {
@JvmStatic @JvmStatic
fun newInstance(): PurchasedAppsFragment { fun newInstance(): PurchasedAppsFragment {
return PurchasedAppsFragment().apply { return PurchasedAppsFragment()
}
} }
} }
@@ -54,15 +52,15 @@ class PurchasedAppsFragment : BaseFragment() {
container: ViewGroup?, container: ViewGroup?,
savedInstanceState: Bundle? savedInstanceState: Bundle?
): View { ): View {
B = FragmentUpdatesBinding.bind( B = FragmentAppsBinding.bind(
inflater.inflate( inflater.inflate(
R.layout.fragment_updates, R.layout.fragment_apps,
container, container,
false false
) )
) )
VM = ViewModelProvider(requireActivity()).get(PurchasedViewModel::class.java) VM = ViewModelProvider(requireActivity())[PurchasedViewModel::class.java]
return B.root return B.root
} }
@@ -71,11 +69,6 @@ class PurchasedAppsFragment : BaseFragment() {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
VM.liveData.observe(viewLifecycleOwner) { VM.liveData.observe(viewLifecycleOwner) {
updateController(it) updateController(it)
B.swipeRefreshLayout.isRefreshing = false
}
B.swipeRefreshLayout.setOnRefreshListener {
VM.observe()
} }
attachRecycler() attachRecycler()
@@ -84,32 +77,28 @@ class PurchasedAppsFragment : BaseFragment() {
} }
private fun attachRecycler() { private fun attachRecycler() {
endlessRecyclerOnScrollListener = object : EndlessRecyclerOnScrollListener() { val endlessRecyclerOnScrollListener = object : EndlessRecyclerOnScrollListener(8) {
override fun onLoadMore(currentPage: Int) { override fun onLoadMore(currentPage: Int) {
VM.observe() VM.observe()
} }
} }
B.recycler.addOnScrollListener(endlessRecyclerOnScrollListener) B.recycler.addOnScrollListener(endlessRecyclerOnScrollListener)
} }
private fun updateController(appList: List<App>?) { private fun updateController(paginatedAppList: PaginatedAppList?) {
B.recycler.withModels { B.recycler.withModels {
setFilterDuplicates(true) setFilterDuplicates(true)
if (appList == null) { if (paginatedAppList == null) {
for (i in 1..6) { for (i in 1..10) {
add( add(
AppListViewShimmerModel_() AppListViewShimmerModel_()
.id(i) .id(i)
) )
} }
} else { } else {
add( paginatedAppList.appList.forEach { app ->
HeaderViewModel_()
.id("header")
.title("${appList.size} apps purchased")
)
appList.forEach { app ->
add( add(
AppListViewModel_() AppListViewModel_()
.id(app.id) .id(app.id)
@@ -121,6 +110,13 @@ class PurchasedAppsFragment : BaseFragment() {
} }
) )
} }
if (paginatedAppList.hasMore){
add(
AppProgressViewModel_()
.id("progress")
)
}
} }
} }
} }

View File

@@ -74,7 +74,7 @@ class BlacklistFragment : Fragment(R.layout.activity_generic_recycler) {
binding.recycler.withModels { binding.recycler.withModels {
setFilterDuplicates(true) setFilterDuplicates(true)
if (blackList == null) { if (blackList == null) {
for (i in 1..6) { for (i in 1..10) {
add( add(
AppListViewShimmerModel_() AppListViewShimmerModel_()
.id(i) .id(i)

View File

@@ -185,7 +185,7 @@ class UpdatesFragment : BaseFragment(R.layout.fragment_updates) {
binding.recycler.withModels { binding.recycler.withModels {
setFilterDuplicates(true) setFilterDuplicates(true)
if (updateFileMap == null) { if (updateFileMap == null) {
for (i in 1..6) { for (i in 1..10) {
add( add(
AppListViewShimmerModel_() AppListViewShimmerModel_()
.id(i) .id(i)

View File

@@ -80,8 +80,4 @@ class BlacklistViewModel(application: Application) : BaseAndroidViewModel(applic
} }
} }
} }
override fun onCleared() {
super.onCleared()
}
} }

View File

@@ -42,12 +42,12 @@ class InstalledViewModel(application: Application) : BaseAppsViewModel(applicati
override fun observe() { override fun observe() {
viewModelScope.launch(Dispatchers.IO) { viewModelScope.launch(Dispatchers.IO) {
try { requestState = try {
appList.flushAndAdd(getFilteredApps()) appList.flushAndAdd(getFilteredApps())
liveData.postValue(appList.sortedBy { it.displayName.lowercase(Locale.getDefault()) }) liveData.postValue(appList.sortedBy { it.displayName.lowercase(Locale.getDefault()) })
requestState = RequestState.Complete RequestState.Complete
} catch (e: Exception) { } catch (e: Exception) {
requestState = RequestState.Pending RequestState.Pending
} }
} }
} }
@@ -58,12 +58,15 @@ class InstalledViewModel(application: Application) : BaseAppsViewModel(applicati
is BusEvent.InstallEvent -> { is BusEvent.InstallEvent -> {
updateListAndPost(event.packageName) updateListAndPost(event.packageName)
} }
is BusEvent.UninstallEvent -> { is BusEvent.UninstallEvent -> {
updateListAndPost(event.packageName) updateListAndPost(event.packageName)
} }
is BusEvent.Blacklisted -> { is BusEvent.Blacklisted -> {
observe() observe()
} }
else -> { else -> {
} }

View File

@@ -43,7 +43,7 @@ class LibraryAppsViewModel(application: Application) : BaseAndroidViewModel(appl
var streamCluster: StreamCluster = StreamCluster() var streamCluster: StreamCluster = StreamCluster()
init { init {
observe() requestState = RequestState.Init
} }
override fun observe() { override fun observe() {

View File

@@ -32,39 +32,51 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.supervisorScope import kotlinx.coroutines.supervisorScope
data class PaginatedAppList(
var appList: MutableList<App> = mutableListOf(),
var hasMore: Boolean
)
class PurchasedViewModel(application: Application) : BaseAndroidViewModel(application) { class PurchasedViewModel(application: Application) : BaseAndroidViewModel(application) {
private val authData = AuthProvider private val authData = AuthProvider.with(application).getAuthData()
.with(application)
.getAuthData()
private val purchaseHelper = PurchaseHelper(authData) private val purchaseHelper = PurchaseHelper(authData).using(HttpClient.getPreferredClient())
.using(HttpClient.getPreferredClient())
val liveData: MutableLiveData<List<App>> = MutableLiveData() private var appList: MutableList<App> = mutableListOf()
var appList: MutableList<App> = mutableListOf() val liveData: MutableLiveData<PaginatedAppList> = MutableLiveData()
init { init {
requestState = RequestState.Init requestState = RequestState.Init
observe()
} }
override fun observe() { override fun observe() {
viewModelScope.launch(Dispatchers.IO) { viewModelScope.launch(Dispatchers.IO) {
supervisorScope { supervisorScope {
try { requestState = try {
appList.addAll(purchaseHelper.getPurchaseHistory(appList.size)) val nextAppList = purchaseHelper.getPurchaseHistory(appList.size)
liveData.postValue(appList) .filter { it.displayName.isNotEmpty() }
requestState = RequestState.Complete
if (nextAppList.isEmpty()) {
liveData.postValue(PaginatedAppList(
appList,
false
))
} else {
appList.addAll(nextAppList)
liveData.postValue(
PaginatedAppList(
appList,
true
)
)
}
RequestState.Complete
} catch (e: Exception) { } catch (e: Exception) {
requestState = RequestState.Pending RequestState.Pending
} }
} }
} }
} }
override fun onCleared() {
super.onCleared()
}
} }

View File

@@ -0,0 +1,36 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Aurora Store
~ Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
~
~ Aurora Store is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, either version 2 of the License, or
~ (at your option) any later version.
~
~ Aurora Store is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with Aurora Store. If not, see <http://www.gnu.org/licenses/>.
~
-->
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<com.airbnb.epoxy.EpoxyRecyclerView
android:id="@+id/recycler"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false"
app:itemSpacing="@dimen/margin_normal"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
app:stackFromEnd="false"
tools:listitem="@layout/view_app_list" />
</RelativeLayout>

View File

@@ -20,6 +20,7 @@
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_gravity="center_horizontal" android:layout_gravity="center_horizontal"
android:layout_margin="@dimen/margin_xsmall"> android:layout_margin="@dimen/margin_xsmall">
@@ -27,5 +28,7 @@
android:id="@+id/img_icon" android:id="@+id/img_icon"
android:layout_width="@dimen/icon_size_cluster" android:layout_width="@dimen/icon_size_cluster"
android:layout_height="@dimen/icon_size_cluster" android:layout_height="@dimen/icon_size_cluster"
android:scaleX="0.5"
android:scaleY="0.5"
android:layout_centerInParent="true" /> android:layout_centerInParent="true" />
</RelativeLayout> </RelativeLayout>