Add ExpandedStreamBrowse page

This commit is contained in:
Rahul Kumar Patel
2021-02-20 20:39:39 +05:30
parent 833bbb554f
commit 0471e52b65
10 changed files with 287 additions and 14 deletions

View File

@@ -172,7 +172,7 @@ dependencies {
implementation "com.github.topjohnwu.libsu:core:${versions.libsu}"
//Love <3
api("com.gitlab.AuroraOSS:gplayapi:-SNAPSHOT")
api("com.gitlab.AuroraOSS:gplayapi:d7ebd3855a")
}
Properties props = new Properties()

View File

@@ -143,6 +143,7 @@
<activity android:name=".view.ui.commons.BlacklistActivity" />
<activity android:name=".view.ui.preferences.SettingsActivity" />
<activity android:name=".view.ui.about.AboutActivity" />
<activity android:name=".view.ui.commons.ExpandedStreamBrowseActivity" />
<service android:name=".data.service.NotificationService" />
<service android:name=".data.installer.InstallerService" />

View File

@@ -37,6 +37,7 @@ import com.aurora.store.view.ui.account.GoogleActivity
import com.aurora.store.view.ui.details.AppDetailsActivity
import com.aurora.store.view.ui.details.DetailsMoreActivity
import com.aurora.store.view.ui.details.DetailsReviewActivity
import com.aurora.store.view.ui.details.DevProfileActivity
import com.aurora.store.view.ui.sheets.NetworkDialogSheet
import com.google.gson.Gson
import com.google.gson.GsonBuilder
@@ -99,9 +100,15 @@ abstract class BaseActivity : AppCompatActivity(), NetworkProvider.NetworkListen
}
}
fun openStreamBrowseActivity(browseUrl: String) {
val intent = Intent(this, StreamBrowseActivity::class.java)
fun openStreamBrowseActivity(browseUrl: String, title: String = "") {
val intent = if (browseUrl.toLowerCase().contains("expanded"))
Intent(this, ExpandedStreamBrowseActivity::class.java)
else if (browseUrl.toLowerCase().contains("developer"))
Intent(this, DevProfileActivity::class.java)
else
Intent(this, StreamBrowseActivity::class.java)
intent.putExtra(Constants.BROWSE_EXTRA, browseUrl)
intent.putExtra(Constants.STRING_EXTRA, title)
startActivity(
intent,
ViewUtil.getEmptyActivityBundle(this)

View File

@@ -28,6 +28,7 @@ import com.aurora.gplayapi.data.models.App
import com.aurora.gplayapi.data.models.Category
import com.aurora.store.util.ViewUtil
import com.aurora.store.view.ui.details.AppDetailsActivity
import com.aurora.store.view.ui.details.DevProfileActivity
import com.google.gson.Gson
import com.google.gson.GsonBuilder
import java.lang.reflect.Modifier
@@ -63,9 +64,16 @@ open class BaseFragment : Fragment() {
}
}
fun openStreamBrowseActivity(browseUrl: String) {
val intent = Intent(requireContext(), StreamBrowseActivity::class.java)
fun openStreamBrowseActivity(browseUrl: String, title: String = "") {
val intent = if (browseUrl.toLowerCase().contains("expanded"))
Intent(requireContext(), ExpandedStreamBrowseActivity::class.java)
else if (browseUrl.toLowerCase().contains("developer"))
Intent(requireContext(), DevProfileActivity::class.java)
else
Intent(requireContext(), StreamBrowseActivity::class.java)
intent.putExtra(Constants.BROWSE_EXTRA, browseUrl)
intent.putExtra(Constants.STRING_EXTRA, title)
startActivity(
intent,
ViewUtil.getEmptyActivityBundle(requireContext())

View File

@@ -0,0 +1,139 @@
/*
* 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/>.
*
*/
package com.aurora.store.view.ui.commons
import android.os.Bundle
import androidx.lifecycle.ViewModelProvider
import com.aurora.Constants
import com.aurora.gplayapi.data.models.StreamCluster
import com.aurora.store.databinding.ActivityGenericRecyclerBinding
import com.aurora.store.util.extensions.close
import com.aurora.store.view.custom.recycler.EndlessRecyclerOnScrollListener
import com.aurora.store.view.epoxy.views.AppProgressViewModel_
import com.aurora.store.view.epoxy.views.app.AppListViewModel_
import com.aurora.store.view.epoxy.views.shimmer.AppListViewShimmerModel_
import com.aurora.store.viewmodel.browse.ExpandedStreamBrowseViewModel
class ExpandedStreamBrowseActivity : BaseActivity() {
lateinit var B: ActivityGenericRecyclerBinding
lateinit var VM: ExpandedStreamBrowseViewModel
lateinit var endlessRecyclerOnScrollListener: EndlessRecyclerOnScrollListener
lateinit var title: String
lateinit var cluster: StreamCluster
override fun onConnected() {
hideNetworkConnectivitySheet()
}
override fun onDisconnected() {
showNetworkConnectivitySheet()
}
override fun onReconnected() {
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
B = ActivityGenericRecyclerBinding.inflate(layoutInflater)
VM = ViewModelProvider(this).get(ExpandedStreamBrowseViewModel::class.java)
setContentView(B.root)
attachToolbar()
VM.liveData.observe(this, {
if (!::cluster.isInitialized)
attachRecycler()
cluster = it
updateController(cluster)
updateTitle(cluster)
})
intent.apply {
getStringExtra(Constants.BROWSE_EXTRA)?.let {
VM.getInitialCluster(it)
}
getStringExtra(Constants.STRING_EXTRA)?.let {
B.layoutToolbarAction.txtTitle.text = it
}
}
updateController(null)
}
private fun updateTitle(streamCluster: StreamCluster) {
if (streamCluster.clusterTitle.isNotEmpty())
B.layoutToolbarAction.txtTitle.text = streamCluster.clusterTitle
}
private fun attachToolbar() {
B.layoutToolbarAction.imgActionPrimary.setOnClickListener {
close()
}
}
private fun attachRecycler() {
endlessRecyclerOnScrollListener = object : EndlessRecyclerOnScrollListener() {
override fun onLoadMore(currentPage: Int) {
VM.next()
}
}
B.recycler.addOnScrollListener(endlessRecyclerOnScrollListener)
}
private fun updateController(streamCluster: StreamCluster?) {
B.recycler.withModels {
setFilterDuplicates(true)
if (streamCluster == null) {
for (i in 1..6) {
add(
AppListViewShimmerModel_()
.id(i)
)
}
} else {
streamCluster.clusterAppList.forEach {
add(
AppListViewModel_()
.id(it.packageName.hashCode())
.app(it)
.click { _ -> openDetailsActivity(it) }
)
}
if (streamCluster.hasNext()) {
add(
AppProgressViewModel_()
.id("progress")
)
}
}
}
}
}

View File

@@ -46,6 +46,7 @@ class ForYouFragment : BaseFragment(), GenericCarouselController.Callbacks {
private lateinit var C: GenericCarouselController
private lateinit var VM: BaseClusterViewModel
private lateinit var streamBundle: StreamBundle
private lateinit var endlessRecyclerOnScrollListener: EndlessRecyclerOnScrollListener
private var pageType = 0
@@ -110,11 +111,18 @@ class ForYouFragment : BaseFragment(), GenericCarouselController.Callbacks {
}
is ViewState.Success<*> -> {
updateController(it.data as StreamBundle)
if (!::streamBundle.isInitialized)
attachRecycler()
streamBundle = it.data as StreamBundle
updateController(streamBundle)
}
}
})
}
private fun attachRecycler() {
endlessRecyclerOnScrollListener =
object : EndlessRecyclerOnScrollListener() {
override fun onLoadMore(currentPage: Int) {
@@ -131,7 +139,7 @@ class ForYouFragment : BaseFragment(), GenericCarouselController.Callbacks {
override fun onHeaderClicked(streamCluster: StreamCluster) {
if (streamCluster.clusterBrowseUrl.isNotEmpty())
openStreamBrowseActivity(streamCluster.clusterBrowseUrl)
openStreamBrowseActivity(streamCluster.clusterBrowseUrl, streamCluster.clusterTitle)
else
Toast.makeText(requireContext(), "Browse page unavailable", Toast.LENGTH_SHORT).show()
}

View File

@@ -26,8 +26,8 @@ import com.aurora.gplayapi.data.models.StreamCluster
import com.aurora.store.databinding.ActivityGenericRecyclerBinding
import com.aurora.store.util.extensions.close
import com.aurora.store.view.custom.recycler.EndlessRecyclerOnScrollListener
import com.aurora.store.view.epoxy.views.app.AppListViewModel_
import com.aurora.store.view.epoxy.views.AppProgressViewModel_
import com.aurora.store.view.epoxy.views.app.AppListViewModel_
import com.aurora.store.view.epoxy.views.shimmer.AppListViewShimmerModel_
import com.aurora.store.viewmodel.browse.StreamBrowseViewModel
@@ -40,6 +40,7 @@ class StreamBrowseActivity : BaseActivity() {
lateinit var endlessRecyclerOnScrollListener: EndlessRecyclerOnScrollListener
lateinit var title: String
lateinit var cluster: StreamCluster
override fun onConnected() {
hideNetworkConnectivitySheet()
@@ -63,11 +64,15 @@ class StreamBrowseActivity : BaseActivity() {
setContentView(B.root)
attachToolbar()
attachRecycler()
VM.liveData.observe(this, {
updateController(it)
updateTitle(it)
if (!::cluster.isInitialized)
attachRecycler()
cluster = it
updateController(cluster)
updateTitle(cluster)
})
intent.apply {

View File

@@ -294,7 +294,10 @@ abstract class BaseDetailsActivity : BaseActivity() {
DetailsCarouselController(object : GenericCarouselController.Callbacks {
override fun onHeaderClicked(streamCluster: StreamCluster) {
if (streamCluster.clusterBrowseUrl.isNotEmpty())
openStreamBrowseActivity(streamCluster.clusterBrowseUrl)
openStreamBrowseActivity(
streamCluster.clusterBrowseUrl,
streamCluster.clusterTitle
)
else
toast("Browse page unavailable")
}

View File

@@ -22,6 +22,7 @@ package com.aurora.store.view.ui.details
import android.content.Intent
import android.os.Bundle
import androidx.lifecycle.ViewModelProvider
import com.aurora.Constants
import com.aurora.gplayapi.data.models.App
import com.aurora.gplayapi.data.models.AuthData
import com.aurora.gplayapi.data.models.StreamCluster
@@ -33,7 +34,6 @@ import com.aurora.store.databinding.ActivityDevProfileBinding
import com.aurora.store.util.extensions.close
import com.aurora.store.util.extensions.load
import com.aurora.store.view.epoxy.controller.DeveloperCarouselController
import com.aurora.store.view.epoxy.controller.EarlyAccessCarouselController
import com.aurora.store.view.epoxy.controller.GenericCarouselController
import com.aurora.store.view.ui.commons.BaseActivity
import com.aurora.store.viewmodel.details.DevProfileViewModel
@@ -104,6 +104,13 @@ class DevProfileActivity : BaseActivity(), GenericCarouselController.Callbacks {
} else {
VM.getStreamBundle(devId)
}
} else {
intent.getStringExtra(Constants.BROWSE_EXTRA)?.let {
VM.getStreamBundle(it.substringAfter("developer-"))
}
intent.getStringExtra(Constants.STRING_EXTRA)?.let {
B.layoutToolbarAction.txtTitle.text = it
}
}
} else {
close()
@@ -134,7 +141,7 @@ class DevProfileActivity : BaseActivity(), GenericCarouselController.Callbacks {
}
override fun onHeaderClicked(streamCluster: StreamCluster) {
openStreamBrowseActivity(streamCluster.clusterBrowseUrl)
openStreamBrowseActivity(streamCluster.clusterBrowseUrl, streamCluster.clusterTitle)
}
override fun onClusterScrolled(streamCluster: StreamCluster) {

View File

@@ -0,0 +1,95 @@
/*
* 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/>.
*
*/
package com.aurora.store.viewmodel.browse
import android.app.Application
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope
import com.aurora.gplayapi.data.models.AuthData
import com.aurora.gplayapi.data.models.StreamCluster
import com.aurora.gplayapi.helpers.ExpandedBrowseHelper
import com.aurora.store.data.RequestState
import com.aurora.store.data.network.HttpClient
import com.aurora.store.data.providers.AuthProvider
import com.aurora.store.util.Log
import com.aurora.store.viewmodel.BaseAndroidViewModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.supervisorScope
class ExpandedStreamBrowseViewModel(application: Application) : BaseAndroidViewModel(application) {
private val authData: AuthData = AuthProvider.with(application).getAuthData()
private val streamHelper: ExpandedBrowseHelper = ExpandedBrowseHelper(authData)
.using(HttpClient.getPreferredClient())
val liveData: MutableLiveData<StreamCluster> = MutableLiveData()
var streamCluster: StreamCluster = StreamCluster()
override fun observe() {
requestState = RequestState.Init
}
fun getInitialCluster(expandedStreamUrl: String) {
viewModelScope.launch(Dispatchers.IO) {
supervisorScope {
try {
val browseResponse = streamHelper.getBrowseStreamResponse(expandedStreamUrl)
if (browseResponse.hasBrowseTab()) {
streamCluster =
streamHelper.getExpandedBrowseClusters(browseResponse.browseTab.listUrl)
liveData.postValue(streamCluster)
} else {
requestState = RequestState.Complete
}
} catch (e: Exception) {
requestState = RequestState.Pending
}
}
}
}
fun next() {
Log.e("NEXT CALED")
viewModelScope.launch(Dispatchers.IO) {
supervisorScope {
try {
val newCluster = streamHelper.getExpandedBrowseClusters(
streamCluster.clusterNextPageUrl
)
streamCluster.apply {
clusterAppList.addAll(newCluster.clusterAppList)
clusterNextPageUrl = newCluster.clusterNextPageUrl
}
liveData.postValue(streamCluster)
if (!streamCluster.hasNext()) {
Log.i("End of Bundle")
requestState = RequestState.Complete
}
} catch (e: Exception) {
requestState = RequestState.Pending
}
}
}
}
}