Merge branch 'dev' into 'master'
Improve AppUpdate pregressbar UI See merge request AuroraOSS/AuroraStore!346
This commit is contained in:
@@ -25,7 +25,7 @@ import androidx.core.content.ContextCompat
|
|||||||
import androidx.hilt.work.HiltWorkerFactory
|
import androidx.hilt.work.HiltWorkerFactory
|
||||||
import androidx.work.Configuration
|
import androidx.work.Configuration
|
||||||
import com.aurora.extensions.isPAndAbove
|
import com.aurora.extensions.isPAndAbove
|
||||||
import com.aurora.store.data.event.FlowEvent
|
import com.aurora.store.data.event.EventFlow
|
||||||
import com.aurora.store.data.receiver.PackageManagerReceiver
|
import com.aurora.store.data.receiver.PackageManagerReceiver
|
||||||
import com.aurora.store.util.CommonUtil
|
import com.aurora.store.util.CommonUtil
|
||||||
import com.aurora.store.util.DownloadWorkerUtil
|
import com.aurora.store.util.DownloadWorkerUtil
|
||||||
@@ -58,7 +58,7 @@ class AuroraApp : Application(), Configuration.Provider {
|
|||||||
private set
|
private set
|
||||||
|
|
||||||
val enqueuedInstalls: MutableSet<String> = mutableSetOf()
|
val enqueuedInstalls: MutableSet<String> = mutableSetOf()
|
||||||
val flowEvent = FlowEvent()
|
val events = EventFlow()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreate() {
|
override fun onCreate() {
|
||||||
|
|||||||
@@ -21,48 +21,27 @@ package com.aurora.store.data.event
|
|||||||
|
|
||||||
abstract class Event
|
abstract class Event
|
||||||
|
|
||||||
sealed class BusEvent: Event() {
|
sealed class BusEvent : Event() {
|
||||||
data class InstallEvent(
|
lateinit var extra: String
|
||||||
var packageName: String,
|
lateinit var error: String
|
||||||
var extra: String? = ""
|
|
||||||
) : BusEvent()
|
|
||||||
|
|
||||||
data class UninstallEvent(
|
data class Blacklisted(val packageName: String) : BusEvent()
|
||||||
var packageName: String,
|
data class ManualDownload(val packageName: String, val versionCode: Int) : BusEvent()
|
||||||
var extra: String? = ""
|
|
||||||
) : BusEvent()
|
|
||||||
|
|
||||||
data class Blacklisted(
|
|
||||||
var packageName: String,
|
|
||||||
var error: String? = ""
|
|
||||||
) : BusEvent()
|
|
||||||
|
|
||||||
data class GoogleAAS(
|
|
||||||
var success: Boolean,
|
|
||||||
var email: String = String(),
|
|
||||||
var aasToken: String = String()
|
|
||||||
) : BusEvent()
|
|
||||||
|
|
||||||
data class ManualDownload(
|
|
||||||
var packageName: String,
|
|
||||||
var versionCode: Int
|
|
||||||
) : BusEvent()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sealed class InstallerEvent: Event() {
|
sealed class AuthEvent : Event() {
|
||||||
data class Success(
|
data class GoogleLogin(val success: Boolean, val email: String, val token: String) : AuthEvent()
|
||||||
var packageName: String? = "",
|
}
|
||||||
var extra: String? = ""
|
|
||||||
) : InstallerEvent()
|
sealed class InstallerEvent : Event() {
|
||||||
|
lateinit var extra: String
|
||||||
data class Cancelled(
|
lateinit var error: String
|
||||||
var packageName: String? = "",
|
|
||||||
var extra: String? = ""
|
var progress: Int = -1
|
||||||
) : InstallerEvent()
|
|
||||||
|
data class Installed(val packageName: String) : InstallerEvent()
|
||||||
data class Failed(
|
data class Uninstalled(val packageName: String) : InstallerEvent()
|
||||||
var packageName: String? = "",
|
data class Installing(val packageName: String) : InstallerEvent()
|
||||||
var error: String? = "",
|
data class Cancelled(val packageName: String) : InstallerEvent()
|
||||||
var extra: String? = ""
|
data class Failed(val packageName: String) : InstallerEvent()
|
||||||
) : InstallerEvent()
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,9 +6,9 @@ import kotlinx.coroutines.flow.asSharedFlow
|
|||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
class FlowEvent {
|
class EventFlow {
|
||||||
|
|
||||||
private val TAG = FlowEvent::class.java.simpleName
|
private val TAG = EventFlow::class.java.simpleName
|
||||||
|
|
||||||
private val _busEvent = MutableSharedFlow<BusEvent>(extraBufferCapacity = 1)
|
private val _busEvent = MutableSharedFlow<BusEvent>(extraBufferCapacity = 1)
|
||||||
val busEvent = _busEvent.asSharedFlow()
|
val busEvent = _busEvent.asSharedFlow()
|
||||||
@@ -16,10 +16,14 @@ class FlowEvent {
|
|||||||
private val _installerEvent = MutableSharedFlow<InstallerEvent>(extraBufferCapacity = 1)
|
private val _installerEvent = MutableSharedFlow<InstallerEvent>(extraBufferCapacity = 1)
|
||||||
val installerEvent = _installerEvent.asSharedFlow()
|
val installerEvent = _installerEvent.asSharedFlow()
|
||||||
|
|
||||||
fun emitEvent(event: Event) {
|
private val _authEvent = MutableSharedFlow<AuthEvent>(extraBufferCapacity = 1)
|
||||||
|
val authEvent = _authEvent.asSharedFlow()
|
||||||
|
|
||||||
|
fun send(event: Event) {
|
||||||
when (event) {
|
when (event) {
|
||||||
is InstallerEvent -> _installerEvent.tryEmit(event)
|
is InstallerEvent -> _installerEvent.tryEmit(event)
|
||||||
is BusEvent -> _busEvent.tryEmit(event)
|
is BusEvent -> _busEvent.tryEmit(event)
|
||||||
|
is AuthEvent -> _authEvent.tryEmit(event)
|
||||||
else -> Log.e(TAG, "Got an unhandled event")
|
else -> Log.e(TAG, "Got an unhandled event")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -66,16 +66,19 @@ abstract class InstallerBase(protected var context: Context) : IInstaller {
|
|||||||
open fun postError(packageName: String, error: String?, extra: String?) {
|
open fun postError(packageName: String, error: String?, extra: String?) {
|
||||||
Log.e("Service Error :$error")
|
Log.e("Service Error :$error")
|
||||||
|
|
||||||
val event = InstallerEvent.Failed(
|
val event = InstallerEvent.Failed(packageName).apply {
|
||||||
packageName,
|
this.error = error ?: ""
|
||||||
error,
|
this.extra = extra ?: ""
|
||||||
extra
|
}
|
||||||
)
|
|
||||||
|
|
||||||
AuroraApp.flowEvent.emitEvent(event)
|
AuroraApp.events.send(event)
|
||||||
}
|
}
|
||||||
|
|
||||||
open fun getFiles(packageName: String, versionCode: Int, sharedLibPackageName: String = ""): List<File> {
|
open fun getFiles(
|
||||||
|
packageName: String,
|
||||||
|
versionCode: Int,
|
||||||
|
sharedLibPackageName: String = ""
|
||||||
|
): List<File> {
|
||||||
val downloadDir = if (sharedLibPackageName.isNotBlank()) {
|
val downloadDir = if (sharedLibPackageName.isNotBlank()) {
|
||||||
PathUtil.getLibDownloadDir(context, packageName, versionCode, sharedLibPackageName)
|
PathUtil.getLibDownloadDir(context, packageName, versionCode, sharedLibPackageName)
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -104,12 +104,11 @@ class RootInstaller @Inject constructor(
|
|||||||
if (packageName == download?.packageName) onInstallationSuccess()
|
if (packageName == download?.packageName) onInstallationSuccess()
|
||||||
} else {
|
} else {
|
||||||
removeFromInstallQueue(packageName)
|
removeFromInstallQueue(packageName)
|
||||||
val event = InstallerEvent.Failed(
|
val event = InstallerEvent.Failed(packageName).apply {
|
||||||
packageName,
|
this.extra = context.getString(R.string.installer_status_failure)
|
||||||
context.getString(R.string.installer_status_failure),
|
this.error = parseError(shellResult)
|
||||||
parseError(shellResult)
|
}
|
||||||
)
|
AuroraApp.events.send(event)
|
||||||
AuroraApp.flowEvent.emitEvent(event)
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
removeFromInstallQueue(packageName)
|
removeFromInstallQueue(packageName)
|
||||||
|
|||||||
@@ -35,7 +35,6 @@ import com.aurora.services.IPrivilegedService
|
|||||||
import com.aurora.store.AuroraApp
|
import com.aurora.store.AuroraApp
|
||||||
import com.aurora.store.BuildConfig
|
import com.aurora.store.BuildConfig
|
||||||
import com.aurora.store.R
|
import com.aurora.store.R
|
||||||
import com.aurora.store.data.event.BusEvent
|
|
||||||
import com.aurora.store.data.event.InstallerEvent
|
import com.aurora.store.data.event.InstallerEvent
|
||||||
import com.aurora.store.data.model.InstallerInfo
|
import com.aurora.store.data.model.InstallerInfo
|
||||||
import com.aurora.store.data.room.download.Download
|
import com.aurora.store.data.room.download.Download
|
||||||
@@ -91,6 +90,7 @@ class ServiceInstaller @Inject constructor(
|
|||||||
fileList.map { it.absolutePath }
|
fileList.map { it.absolutePath }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> {
|
else -> {
|
||||||
postError(
|
postError(
|
||||||
download.packageName,
|
download.packageName,
|
||||||
@@ -148,7 +148,11 @@ class ServiceInstaller @Inject constructor(
|
|||||||
)
|
)
|
||||||
} catch (e: RemoteException) {
|
} catch (e: RemoteException) {
|
||||||
removeFromInstallQueue(packageName)
|
removeFromInstallQueue(packageName)
|
||||||
postError(packageName, e.localizedMessage, e.stackTraceToString())
|
postError(
|
||||||
|
packageName,
|
||||||
|
e.localizedMessage,
|
||||||
|
e.stackTraceToString()
|
||||||
|
)
|
||||||
readyWithAction.set(true)
|
readyWithAction.set(true)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -166,7 +170,11 @@ class ServiceInstaller @Inject constructor(
|
|||||||
)
|
)
|
||||||
} catch (e: RemoteException) {
|
} catch (e: RemoteException) {
|
||||||
removeFromInstallQueue(packageName)
|
removeFromInstallQueue(packageName)
|
||||||
postError(packageName, e.localizedMessage, e.stackTraceToString())
|
postError(
|
||||||
|
packageName,
|
||||||
|
e.localizedMessage,
|
||||||
|
e.stackTraceToString()
|
||||||
|
)
|
||||||
readyWithAction.set(true)
|
readyWithAction.set(true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -210,13 +218,13 @@ class ServiceInstaller @Inject constructor(
|
|||||||
try {
|
try {
|
||||||
when (returnCode) {
|
when (returnCode) {
|
||||||
PackageInstaller.STATUS_SUCCESS -> {
|
PackageInstaller.STATUS_SUCCESS -> {
|
||||||
AuroraApp.flowEvent.emitEvent(
|
AuroraApp.events.send(
|
||||||
BusEvent.UninstallEvent(
|
InstallerEvent.Uninstalled(packageName).apply {
|
||||||
packageName,
|
this.extra = context.getString(R.string.action_uninstall_success)
|
||||||
context.getString(R.string.installer_status_success)
|
}
|
||||||
)
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> {
|
else -> {
|
||||||
val error = AppInstaller.getErrorString(
|
val error = AppInstaller.getErrorString(
|
||||||
context,
|
context,
|
||||||
@@ -240,15 +248,15 @@ class ServiceInstaller @Inject constructor(
|
|||||||
try {
|
try {
|
||||||
when (returnCode) {
|
when (returnCode) {
|
||||||
PackageInstaller.STATUS_SUCCESS -> {
|
PackageInstaller.STATUS_SUCCESS -> {
|
||||||
AuroraApp.flowEvent.emitEvent(
|
AuroraApp.events.send(
|
||||||
InstallerEvent.Success(
|
InstallerEvent.Installed(packageName).apply {
|
||||||
packageName,
|
this.extra = context.getString(R.string.installer_status_success)
|
||||||
context.getString(R.string.installer_status_success)
|
}
|
||||||
)
|
|
||||||
)
|
)
|
||||||
// Installation is not yet finished if this is a shared library
|
// Installation is not yet finished if this is a shared library
|
||||||
if (packageName == download?.packageName) onInstallationSuccess()
|
if (packageName == download?.packageName) onInstallationSuccess()
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> {
|
else -> {
|
||||||
val error = AppInstaller.getErrorString(
|
val error = AppInstaller.getErrorString(
|
||||||
context,
|
context,
|
||||||
|
|||||||
@@ -36,7 +36,9 @@ import com.aurora.extensions.isSAndAbove
|
|||||||
import com.aurora.extensions.isTAndAbove
|
import com.aurora.extensions.isTAndAbove
|
||||||
import com.aurora.extensions.isUAndAbove
|
import com.aurora.extensions.isUAndAbove
|
||||||
import com.aurora.extensions.runOnUiThread
|
import com.aurora.extensions.runOnUiThread
|
||||||
|
import com.aurora.store.AuroraApp
|
||||||
import com.aurora.store.R
|
import com.aurora.store.R
|
||||||
|
import com.aurora.store.data.event.InstallerEvent
|
||||||
import com.aurora.store.data.installer.AppInstaller.Companion.ACTION_INSTALL_STATUS
|
import com.aurora.store.data.installer.AppInstaller.Companion.ACTION_INSTALL_STATUS
|
||||||
import com.aurora.store.data.installer.AppInstaller.Companion.EXTRA_DISPLAY_NAME
|
import com.aurora.store.data.installer.AppInstaller.Companion.EXTRA_DISPLAY_NAME
|
||||||
import com.aurora.store.data.installer.AppInstaller.Companion.EXTRA_PACKAGE_NAME
|
import com.aurora.store.data.installer.AppInstaller.Companion.EXTRA_PACKAGE_NAME
|
||||||
@@ -48,6 +50,7 @@ import com.aurora.store.data.room.download.Download
|
|||||||
import com.aurora.store.util.Log
|
import com.aurora.store.util.Log
|
||||||
import com.aurora.store.util.PackageUtil.isSharedLibraryInstalled
|
import com.aurora.store.util.PackageUtil.isSharedLibraryInstalled
|
||||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||||
|
import java.io.IOException
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
|
|
||||||
@@ -69,27 +72,38 @@ class SessionInstaller @Inject constructor(
|
|||||||
|
|
||||||
override fun onActiveChanged(sessionId: Int, active: Boolean) {}
|
override fun onActiveChanged(sessionId: Int, active: Boolean) {}
|
||||||
|
|
||||||
override fun onProgressChanged(sessionId: Int, progress: Float) {}
|
override fun onProgressChanged(sessionId: Int, progress: Float) {
|
||||||
|
val packageName = enqueuedSessions
|
||||||
|
.find { set -> set.any { it.sessionId == sessionId } }
|
||||||
|
?.first()
|
||||||
|
?.packageName
|
||||||
|
|
||||||
|
if (packageName != null && progress > 0.0) {
|
||||||
|
AuroraApp.events.send(
|
||||||
|
InstallerEvent.Installing(packageName).apply {
|
||||||
|
this.progress = (progress * 100).toInt()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun onFinished(sessionId: Int, success: Boolean) {
|
override fun onFinished(sessionId: Int, success: Boolean) {
|
||||||
enqueuedSessions.find { set -> set.any { it.sessionId == sessionId } }?.let { sessionSet ->
|
enqueuedSessions.find { set -> set.any { it.sessionId == sessionId } }
|
||||||
sessionSet.remove(sessionSet.first { it.sessionId == sessionId })
|
?.let { sessionSet ->
|
||||||
|
sessionSet.remove(sessionSet.first { it.sessionId == sessionId })
|
||||||
|
|
||||||
// If this was a shared lib, proceed installing other libs or actual package
|
// If this was a shared lib, proceed installing other libs or actual package
|
||||||
if (sessionSet.isNotEmpty() && success) {
|
if (sessionSet.isNotEmpty() && success) {
|
||||||
commitInstall(sessionSet.first()); return
|
commitInstall(sessionSet.first()); return
|
||||||
} else {
|
} else {
|
||||||
enqueuedSessions.remove(sessionSet)
|
enqueuedSessions.remove(sessionSet)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (enqueuedSessions.isNotEmpty()) {
|
if (enqueuedSessions.isNotEmpty()) {
|
||||||
enqueuedSessions.firstOrNull()?.let { sessionSet ->
|
enqueuedSessions.firstOrNull()?.let { sessionSet ->
|
||||||
commitInstall(sessionSet.first())
|
commitInstall(sessionSet.first())
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
// Nothing else in queue, unregister callback
|
|
||||||
packageInstaller.unregisterSessionCallback(this)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -113,7 +127,8 @@ class SessionInstaller @Inject constructor(
|
|||||||
override fun install(download: Download) {
|
override fun install(download: Download) {
|
||||||
super.install(download)
|
super.install(download)
|
||||||
|
|
||||||
val sessionSet = enqueuedSessions.find { set -> set.any { it.packageName == download.packageName } }
|
val sessionSet =
|
||||||
|
enqueuedSessions.find { set -> set.any { it.packageName == download.packageName } }
|
||||||
if (sessionSet != null) {
|
if (sessionSet != null) {
|
||||||
Log.i("${download.packageName} already queued")
|
Log.i("${download.packageName} already queued")
|
||||||
commitInstall(sessionSet.first())
|
commitInstall(sessionSet.first())
|
||||||
@@ -124,7 +139,11 @@ class SessionInstaller @Inject constructor(
|
|||||||
download.sharedLibs.forEach {
|
download.sharedLibs.forEach {
|
||||||
// Shared library packages cannot be updated
|
// Shared library packages cannot be updated
|
||||||
if (!isSharedLibraryInstalled(context, it.packageName, it.versionCode)) {
|
if (!isSharedLibraryInstalled(context, it.packageName, it.versionCode)) {
|
||||||
stageInstall(download.packageName, download.versionCode, it.packageName)?.let { sessionID ->
|
stageInstall(
|
||||||
|
download.packageName,
|
||||||
|
download.versionCode,
|
||||||
|
it.packageName
|
||||||
|
)?.let { sessionID ->
|
||||||
sessionInfoSet.add(SessionInfo(sessionID, it.packageName, it.versionCode))
|
sessionInfoSet.add(SessionInfo(sessionID, it.packageName, it.versionCode))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -147,18 +166,50 @@ class SessionInstaller @Inject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun stageInstall(packageName: String, versionCode: Int, sharedLibPkgName: String = ""): Int? {
|
private fun stageInstall(
|
||||||
val packageInstaller = context.packageManager.packageInstaller
|
packageName: String,
|
||||||
|
versionCode: Int,
|
||||||
|
sharedLibPkgName: String = ""
|
||||||
|
): Int? {
|
||||||
|
val resolvedPackageName = sharedLibPkgName.ifBlank { packageName }
|
||||||
|
|
||||||
val sessionParams = SessionParams(SessionParams.MODE_FULL_INSTALL).apply {
|
val sessionParams = buildSessionParams(resolvedPackageName)
|
||||||
setAppPackageName(sharedLibPkgName.ifBlank { packageName })
|
val sessionId = packageInstaller.createSession(sessionParams)
|
||||||
setInstallLocation(PackageInfo.INSTALL_LOCATION_AUTO)
|
val session = packageInstaller.openSession(sessionId)
|
||||||
if (isOAndAbove()) {
|
|
||||||
setInstallReason(PackageManager.INSTALL_REASON_USER)
|
return try {
|
||||||
|
Log.i("Writing splits to session for $packageName")
|
||||||
|
getFiles(packageName, versionCode, sharedLibPkgName).forEach { file ->
|
||||||
|
file.inputStream().use { input ->
|
||||||
|
session.openWrite(
|
||||||
|
"${resolvedPackageName}_${file.name}",
|
||||||
|
0,
|
||||||
|
file.length()
|
||||||
|
).use { output ->
|
||||||
|
input.copyTo(output)
|
||||||
|
session.fsync(output)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
sessionId
|
||||||
|
} catch (exception: IOException) {
|
||||||
|
session.abandon()
|
||||||
|
removeFromInstallQueue(packageName)
|
||||||
|
postError(packageName, exception.localizedMessage, exception.stackTraceToString())
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun buildSessionParams(packageName: String): SessionParams {
|
||||||
|
return SessionParams(SessionParams.MODE_FULL_INSTALL).apply {
|
||||||
|
setAppPackageName(packageName)
|
||||||
|
setInstallLocation(PackageInfo.INSTALL_LOCATION_AUTO)
|
||||||
if (isNAndAbove()) {
|
if (isNAndAbove()) {
|
||||||
setOriginatingUid(Process.myUid())
|
setOriginatingUid(Process.myUid())
|
||||||
}
|
}
|
||||||
|
if (isOAndAbove()) {
|
||||||
|
setInstallReason(PackageManager.INSTALL_REASON_USER)
|
||||||
|
}
|
||||||
if (isSAndAbove()) {
|
if (isSAndAbove()) {
|
||||||
setRequireUserAction(SessionParams.USER_ACTION_NOT_REQUIRED)
|
setRequireUserAction(SessionParams.USER_ACTION_NOT_REQUIRED)
|
||||||
}
|
}
|
||||||
@@ -170,26 +221,6 @@ class SessionInstaller @Inject constructor(
|
|||||||
setRequestUpdateOwnership(true)
|
setRequestUpdateOwnership(true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val sessionId = packageInstaller.createSession(sessionParams)
|
|
||||||
val session = packageInstaller.openSession(sessionId)
|
|
||||||
|
|
||||||
return try {
|
|
||||||
Log.i("Writing splits to session for $packageName")
|
|
||||||
getFiles(packageName, versionCode, sharedLibPkgName).forEach {
|
|
||||||
it.inputStream().use { input ->
|
|
||||||
session.openWrite("${sharedLibPkgName.ifBlank { packageName }}_${it.name}", 0, it.length()).use { output ->
|
|
||||||
input.copyTo(output)
|
|
||||||
session.fsync(output)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sessionId
|
|
||||||
} catch (exception: Exception) {
|
|
||||||
session.abandon()
|
|
||||||
removeFromInstallQueue(packageName)
|
|
||||||
postError(packageName, exception.localizedMessage, exception.stackTraceToString())
|
|
||||||
null
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun commitInstall(sessionInfo: SessionInfo) {
|
private fun commitInstall(sessionInfo: SessionInfo) {
|
||||||
|
|||||||
@@ -29,5 +29,6 @@ enum class State {
|
|||||||
QUEUED,
|
QUEUED,
|
||||||
PROGRESS,
|
PROGRESS,
|
||||||
COMPLETE,
|
COMPLETE,
|
||||||
CANCELED
|
CANCELED,
|
||||||
|
INSTALLING,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
package com.aurora.store.data.model
|
||||||
|
|
||||||
|
import com.aurora.gplayapi.data.models.App
|
||||||
|
|
||||||
|
data class PaginatedAppList(
|
||||||
|
val appList: MutableList<App> = mutableListOf(),
|
||||||
|
var hasMore: Boolean
|
||||||
|
)
|
||||||
@@ -80,7 +80,12 @@ class InstallerStatusReceiver : BroadcastReceiver() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun notifyUser(context: Context, packageName: String, displayName: String, status: Int) {
|
private fun notifyUser(
|
||||||
|
context: Context,
|
||||||
|
packageName: String,
|
||||||
|
displayName: String,
|
||||||
|
status: Int
|
||||||
|
) {
|
||||||
val notificationManager =
|
val notificationManager =
|
||||||
context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
||||||
val notification = NotificationUtil.getInstallerStatusNotification(
|
val notification = NotificationUtil.getInstallerStatusNotification(
|
||||||
@@ -109,23 +114,24 @@ class InstallerStatusReceiver : BroadcastReceiver() {
|
|||||||
private fun postStatus(status: Int, packageName: String?, extra: String?, context: Context) {
|
private fun postStatus(status: Int, packageName: String?, extra: String?, context: Context) {
|
||||||
val event = when (status) {
|
val event = when (status) {
|
||||||
PackageInstaller.STATUS_SUCCESS -> {
|
PackageInstaller.STATUS_SUCCESS -> {
|
||||||
InstallerEvent.Success(
|
InstallerEvent.Installed(packageName!!).apply {
|
||||||
packageName, context.getString(R.string.installer_status_success)
|
this.extra = context.getString(R.string.installer_status_success)
|
||||||
)
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
PackageInstaller.STATUS_FAILURE_ABORTED -> {
|
PackageInstaller.STATUS_FAILURE_ABORTED -> {
|
||||||
InstallerEvent.Cancelled(
|
InstallerEvent.Cancelled(packageName!!).apply {
|
||||||
packageName, AppInstaller.getErrorString(context, status)
|
this.extra = AppInstaller.getErrorString(context, status)
|
||||||
)
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> {
|
else -> {
|
||||||
InstallerEvent.Failed(
|
InstallerEvent.Failed(packageName!!).apply {
|
||||||
packageName, AppInstaller.getErrorString(context, status), extra
|
this.error = AppInstaller.getErrorString(context, status)
|
||||||
)
|
this.extra = extra ?: ""
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
AuroraApp.flowEvent.emitEvent(event)
|
AuroraApp.events.send(event)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,8 +23,7 @@ import android.content.BroadcastReceiver
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import com.aurora.store.AuroraApp
|
import com.aurora.store.AuroraApp
|
||||||
import com.aurora.store.data.event.BusEvent.InstallEvent
|
import com.aurora.store.data.event.InstallerEvent
|
||||||
import com.aurora.store.data.event.BusEvent.UninstallEvent
|
|
||||||
import com.aurora.store.data.installer.AppInstaller
|
import com.aurora.store.data.installer.AppInstaller
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
@@ -41,11 +40,11 @@ open class PackageManagerReceiver : BroadcastReceiver() {
|
|||||||
|
|
||||||
when (intent.action) {
|
when (intent.action) {
|
||||||
Intent.ACTION_PACKAGE_ADDED -> {
|
Intent.ACTION_PACKAGE_ADDED -> {
|
||||||
AuroraApp.flowEvent.emitEvent(InstallEvent(packageName, ""))
|
AuroraApp.events.send(InstallerEvent.Installed(packageName))
|
||||||
}
|
}
|
||||||
|
|
||||||
Intent.ACTION_PACKAGE_REMOVED -> {
|
Intent.ACTION_PACKAGE_REMOVED -> {
|
||||||
AuroraApp.flowEvent.emitEvent(UninstallEvent(packageName, ""))
|
AuroraApp.events.send(InstallerEvent.Uninstalled(packageName))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -25,7 +25,6 @@ import android.util.AttributeSet
|
|||||||
import android.widget.RelativeLayout
|
import android.widget.RelativeLayout
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import com.aurora.extensions.getString
|
import com.aurora.extensions.getString
|
||||||
import com.aurora.extensions.runOnUiThread
|
|
||||||
import com.aurora.store.R
|
import com.aurora.store.R
|
||||||
import com.aurora.store.data.model.State
|
import com.aurora.store.data.model.State
|
||||||
import com.aurora.store.databinding.ViewActionButtonBinding
|
import com.aurora.store.databinding.ViewActionButtonBinding
|
||||||
@@ -101,19 +100,21 @@ class ActionButton : RelativeLayout {
|
|||||||
binding.btn.text = getString(text)
|
binding.btn.text = getString(text)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun setButtonState(enabled: Boolean = true) {
|
||||||
|
binding.btn.isEnabled = enabled
|
||||||
|
}
|
||||||
|
|
||||||
fun updateState(state: State) {
|
fun updateState(state: State) {
|
||||||
val displayChild = when (state) {
|
val displayChild = when (state) {
|
||||||
State.IDLE -> 0
|
|
||||||
State.PROGRESS -> 1
|
State.PROGRESS -> 1
|
||||||
State.COMPLETE -> 2
|
State.COMPLETE -> 2
|
||||||
else -> 0
|
else -> 0
|
||||||
}
|
}
|
||||||
|
|
||||||
if (binding.viewFlipper.displayedChild != displayChild) {
|
if (binding.viewFlipper.displayedChild != displayChild) {
|
||||||
runOnUiThread {
|
binding.viewFlipper.displayedChild = displayChild
|
||||||
binding.viewFlipper.displayedChild = displayChild
|
|
||||||
if (displayChild == 2) updateState(State.IDLE)
|
if (displayChild == 2) updateState(State.IDLE)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ class CarouselModelGroup(
|
|||||||
val models = ArrayList<EpoxyModel<*>>()
|
val models = ArrayList<EpoxyModel<*>>()
|
||||||
val clusterViewModels = mutableListOf<EpoxyModel<*>>()
|
val clusterViewModels = mutableListOf<EpoxyModel<*>>()
|
||||||
|
|
||||||
val idPrefix = streamCluster.id
|
val idPrefix = streamCluster.clusterTitle.hashCode()
|
||||||
|
|
||||||
models.add(
|
models.add(
|
||||||
HeaderViewModel_()
|
HeaderViewModel_()
|
||||||
|
|||||||
@@ -19,13 +19,17 @@
|
|||||||
|
|
||||||
package com.aurora.store.view.epoxy.views.app
|
package com.aurora.store.view.epoxy.views.app
|
||||||
|
|
||||||
|
import android.animation.ObjectAnimator
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.graphics.drawable.Drawable
|
||||||
import android.util.AttributeSet
|
import android.util.AttributeSet
|
||||||
import android.view.View
|
import android.view.View
|
||||||
|
import android.view.animation.AccelerateDecelerateInterpolator
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.core.text.HtmlCompat
|
import androidx.core.text.HtmlCompat
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
import coil.load
|
import coil.load
|
||||||
|
import coil.transform.CircleCropTransformation
|
||||||
import coil.transform.RoundedCornersTransformation
|
import coil.transform.RoundedCornersTransformation
|
||||||
import com.airbnb.epoxy.CallbackProp
|
import com.airbnb.epoxy.CallbackProp
|
||||||
import com.airbnb.epoxy.ModelProp
|
import com.airbnb.epoxy.ModelProp
|
||||||
@@ -51,6 +55,8 @@ class AppUpdateView @JvmOverloads constructor(
|
|||||||
attrs: AttributeSet? = null,
|
attrs: AttributeSet? = null,
|
||||||
defStyleAttr: Int = 0
|
defStyleAttr: Int = 0
|
||||||
) : BaseView<ViewAppUpdateBinding>(context, attrs, defStyleAttr) {
|
) : BaseView<ViewAppUpdateBinding>(context, attrs, defStyleAttr) {
|
||||||
|
private var iconDrawable: Drawable? = null
|
||||||
|
private val cornersTransformation = RoundedCornersTransformation(8.px.toFloat())
|
||||||
|
|
||||||
@ModelProp
|
@ModelProp
|
||||||
fun app(app: App) {
|
fun app(app: App) {
|
||||||
@@ -59,7 +65,10 @@ class AppUpdateView @JvmOverloads constructor(
|
|||||||
binding.txtLine1.text = displayName
|
binding.txtLine1.text = displayName
|
||||||
binding.imgIcon.load(iconArtwork.url) {
|
binding.imgIcon.load(iconArtwork.url) {
|
||||||
placeholder(R.drawable.bg_placeholder)
|
placeholder(R.drawable.bg_placeholder)
|
||||||
transformations(RoundedCornersTransformation(8.px.toFloat()))
|
transformations(cornersTransformation)
|
||||||
|
listener { _, result ->
|
||||||
|
result.drawable.let { iconDrawable = it }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
binding.txtLine2.text = developerName
|
binding.txtLine2.text = developerName
|
||||||
@@ -90,29 +99,22 @@ class AppUpdateView @JvmOverloads constructor(
|
|||||||
@ModelProp
|
@ModelProp
|
||||||
fun download(download: Download?) {
|
fun download(download: Download?) {
|
||||||
if (download != null) {
|
if (download != null) {
|
||||||
/*Inflate Download details*/
|
|
||||||
binding.btnAction.updateState(download.downloadStatus)
|
binding.btnAction.updateState(download.downloadStatus)
|
||||||
binding.progressDownload.isIndeterminate = download.progress < 1
|
|
||||||
when (download.downloadStatus) {
|
when (download.downloadStatus) {
|
||||||
DownloadStatus.QUEUED -> {
|
DownloadStatus.QUEUED -> {
|
||||||
binding.progressDownload.progress = 0
|
binding.progressDownload.isIndeterminate = true
|
||||||
binding.progressDownload.show()
|
animateImageView(scaleFactor = 0.75f)
|
||||||
}
|
}
|
||||||
|
|
||||||
DownloadStatus.DOWNLOADING -> {
|
DownloadStatus.DOWNLOADING -> {
|
||||||
if (download.progress > 0) {
|
binding.progressDownload.isIndeterminate = false
|
||||||
if (download.progress == 100) {
|
binding.progressDownload.progress = download.progress
|
||||||
binding.progressDownload.invisible()
|
animateImageView(scaleFactor = 0.75f)
|
||||||
} else {
|
|
||||||
binding.progressDownload.progress = download.progress
|
|
||||||
binding.progressDownload.show()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> {
|
else -> {
|
||||||
binding.progressDownload.progress = 0
|
binding.progressDownload.isIndeterminate = true
|
||||||
binding.progressDownload.invisible()
|
animateImageView(scaleFactor = 1f)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -141,7 +143,48 @@ class AppUpdateView @JvmOverloads constructor(
|
|||||||
@OnViewRecycled
|
@OnViewRecycled
|
||||||
fun clear() {
|
fun clear() {
|
||||||
binding.headerIndicator.removeCallbacks { }
|
binding.headerIndicator.removeCallbacks { }
|
||||||
binding.progressDownload.progress = 0
|
|
||||||
binding.progressDownload.invisible()
|
binding.progressDownload.invisible()
|
||||||
|
iconDrawable = null
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun animateImageView(scaleFactor: Float = 1f) {
|
||||||
|
val isDownloadVisible = binding.progressDownload.isShown
|
||||||
|
|
||||||
|
// Avoids flickering when the download is in progress
|
||||||
|
if (isDownloadVisible && scaleFactor != 1f)
|
||||||
|
return
|
||||||
|
|
||||||
|
if (!isDownloadVisible && scaleFactor == 1f)
|
||||||
|
return
|
||||||
|
|
||||||
|
if (scaleFactor == 1f) {
|
||||||
|
binding.progressDownload.invisible()
|
||||||
|
} else {
|
||||||
|
binding.progressDownload.show()
|
||||||
|
}
|
||||||
|
|
||||||
|
val scale = listOf(
|
||||||
|
ObjectAnimator.ofFloat(binding.imgIcon, "scaleX", scaleFactor),
|
||||||
|
ObjectAnimator.ofFloat(binding.imgIcon, "scaleY", scaleFactor)
|
||||||
|
)
|
||||||
|
|
||||||
|
scale.forEach { animation ->
|
||||||
|
animation.apply {
|
||||||
|
interpolator = AccelerateDecelerateInterpolator()
|
||||||
|
duration = 250
|
||||||
|
start()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
iconDrawable?.let {
|
||||||
|
binding.imgIcon.load(it) {
|
||||||
|
transformations(
|
||||||
|
if (scaleFactor == 1f)
|
||||||
|
cornersTransformation
|
||||||
|
else
|
||||||
|
CircleCropTransformation()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ import androidx.lifecycle.lifecycleScope
|
|||||||
import androidx.navigation.fragment.findNavController
|
import androidx.navigation.fragment.findNavController
|
||||||
import com.aurora.store.AuroraApp
|
import com.aurora.store.AuroraApp
|
||||||
import com.aurora.store.R
|
import com.aurora.store.R
|
||||||
import com.aurora.store.data.event.BusEvent
|
import com.aurora.store.data.event.AuthEvent
|
||||||
import com.aurora.store.databinding.FragmentGoogleBinding
|
import com.aurora.store.databinding.FragmentGoogleBinding
|
||||||
import com.aurora.store.util.AC2DMUtil
|
import com.aurora.store.util.AC2DMUtil
|
||||||
import com.aurora.store.view.ui.commons.BaseFragment
|
import com.aurora.store.view.ui.commons.BaseFragment
|
||||||
@@ -110,15 +110,15 @@ class GoogleFragment : BaseFragment<FragmentGoogleBinding>() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
viewLifecycleOwner.lifecycleScope.launch {
|
viewLifecycleOwner.lifecycleScope.launch {
|
||||||
AuroraApp.flowEvent.busEvent.collect { onEventReceived(it) }
|
AuroraApp.events.authEvent.collect { onEventReceived(it) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun onEventReceived(event: BusEvent) {
|
private fun onEventReceived(event: AuthEvent) {
|
||||||
when (event) {
|
when (event) {
|
||||||
is BusEvent.GoogleAAS -> {
|
is AuthEvent.GoogleLogin -> {
|
||||||
if (event.success) {
|
if (event.success) {
|
||||||
viewModel.buildGoogleAuthData(event.email, event.aasToken)
|
viewModel.buildGoogleAuthData(event.email, event.token)
|
||||||
} else {
|
} else {
|
||||||
Toast.makeText(
|
Toast.makeText(
|
||||||
requireContext(),
|
requireContext(),
|
||||||
|
|||||||
@@ -65,8 +65,6 @@ class AppsGamesFragment : BaseFragment<FragmentGenericWithPagerBinding>() {
|
|||||||
) { tab: TabLayout.Tab, position: Int ->
|
) { tab: TabLayout.Tab, position: Int ->
|
||||||
when (position) {
|
when (position) {
|
||||||
0 -> tab.text = getString(R.string.title_installed)
|
0 -> tab.text = getString(R.string.title_installed)
|
||||||
1 -> tab.text = getString(R.string.title_library)
|
|
||||||
2 -> tab.text = getString(R.string.title_purchase_history)
|
|
||||||
else -> {}
|
else -> {}
|
||||||
}
|
}
|
||||||
}.attach()
|
}.attach()
|
||||||
@@ -81,14 +79,12 @@ class AppsGamesFragment : BaseFragment<FragmentGenericWithPagerBinding>() {
|
|||||||
override fun createFragment(position: Int): Fragment {
|
override fun createFragment(position: Int): Fragment {
|
||||||
return when (position) {
|
return when (position) {
|
||||||
0 -> InstalledAppsFragment.newInstance()
|
0 -> InstalledAppsFragment.newInstance()
|
||||||
1 -> LibraryAppsFragment.newInstance()
|
|
||||||
2 -> PurchasedAppsFragment.newInstance()
|
|
||||||
else -> Fragment()
|
else -> Fragment()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getItemCount(): Int {
|
override fun getItemCount(): Int {
|
||||||
return if (isAnonymous) 1 else 3
|
return 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,6 +27,8 @@ import androidx.lifecycle.lifecycleScope
|
|||||||
import com.aurora.gplayapi.data.models.App
|
import com.aurora.gplayapi.data.models.App
|
||||||
import com.aurora.store.AuroraApp
|
import com.aurora.store.AuroraApp
|
||||||
import com.aurora.store.data.event.BusEvent
|
import com.aurora.store.data.event.BusEvent
|
||||||
|
import com.aurora.store.data.event.Event
|
||||||
|
import com.aurora.store.data.event.InstallerEvent
|
||||||
import com.aurora.store.databinding.FragmentAppsBinding
|
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_
|
||||||
@@ -49,10 +51,10 @@ class InstalledAppsFragment : BaseFragment<FragmentAppsBinding>() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun onEvent(event: BusEvent) {
|
private fun onEvent(event: Event) {
|
||||||
when (event) {
|
when (event) {
|
||||||
is BusEvent.InstallEvent,
|
is InstallerEvent.Installed,
|
||||||
is BusEvent.UninstallEvent,
|
is InstallerEvent.Uninstalled,
|
||||||
is BusEvent.Blacklisted -> {
|
is BusEvent.Blacklisted -> {
|
||||||
viewModel.fetchApps()
|
viewModel.fetchApps()
|
||||||
}
|
}
|
||||||
@@ -73,7 +75,7 @@ class InstalledAppsFragment : BaseFragment<FragmentAppsBinding>() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
viewLifecycleOwner.lifecycleScope.launch {
|
viewLifecycleOwner.lifecycleScope.launch {
|
||||||
AuroraApp.flowEvent.busEvent.collect { onEvent(it) }
|
AuroraApp.events.busEvent.collect { onEvent(it) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,106 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.all
|
|
||||||
|
|
||||||
import android.os.Bundle
|
|
||||||
import android.view.View
|
|
||||||
import androidx.fragment.app.viewModels
|
|
||||||
import com.aurora.gplayapi.data.models.StreamCluster
|
|
||||||
import com.aurora.store.R
|
|
||||||
import com.aurora.store.databinding.FragmentAppsBinding
|
|
||||||
import com.aurora.store.view.custom.recycler.EndlessRecyclerOnScrollListener
|
|
||||||
import com.aurora.store.view.epoxy.views.AppProgressViewModel_
|
|
||||||
import com.aurora.store.view.epoxy.views.HeaderViewModel_
|
|
||||||
import com.aurora.store.view.epoxy.views.app.AppListViewModel_
|
|
||||||
import com.aurora.store.view.epoxy.views.shimmer.AppListViewShimmerModel_
|
|
||||||
import com.aurora.store.view.ui.commons.BaseFragment
|
|
||||||
import com.aurora.store.viewmodel.all.LibraryAppsViewModel
|
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
|
||||||
|
|
||||||
@AndroidEntryPoint
|
|
||||||
class LibraryAppsFragment : BaseFragment<FragmentAppsBinding>() {
|
|
||||||
private val viewModel: LibraryAppsViewModel by viewModels()
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
@JvmStatic
|
|
||||||
fun newInstance(): LibraryAppsFragment {
|
|
||||||
return LibraryAppsFragment().apply {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
|
||||||
super.onViewCreated(view, savedInstanceState)
|
|
||||||
|
|
||||||
val endlessRecyclerOnScrollListener = object : EndlessRecyclerOnScrollListener() {
|
|
||||||
override fun onLoadMore(currentPage: Int) {
|
|
||||||
viewModel.observe()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
binding.recycler.addOnScrollListener(endlessRecyclerOnScrollListener)
|
|
||||||
|
|
||||||
viewModel.liveData.observe(viewLifecycleOwner) {
|
|
||||||
updateController(it)
|
|
||||||
}
|
|
||||||
|
|
||||||
updateController(null)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun updateController(streamCluster: StreamCluster?) {
|
|
||||||
binding.recycler.withModels {
|
|
||||||
setFilterDuplicates(true)
|
|
||||||
if (streamCluster == null) {
|
|
||||||
for (i in 1..10) {
|
|
||||||
add(
|
|
||||||
AppListViewShimmerModel_()
|
|
||||||
.id(i)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
add(
|
|
||||||
HeaderViewModel_()
|
|
||||||
.id("header")
|
|
||||||
.title(
|
|
||||||
if (streamCluster.clusterTitle.isEmpty())
|
|
||||||
getString(R.string.title_apps_library)
|
|
||||||
else
|
|
||||||
streamCluster.clusterTitle
|
|
||||||
)
|
|
||||||
)
|
|
||||||
streamCluster.clusterAppList.forEach { app ->
|
|
||||||
add(
|
|
||||||
AppListViewModel_()
|
|
||||||
.id(app.id)
|
|
||||||
.app(app)
|
|
||||||
.click { _ -> openDetailsFragment(app.packageName, app) }
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (streamCluster.hasNext()) {
|
|
||||||
add(
|
|
||||||
AppProgressViewModel_()
|
|
||||||
.id("progress")
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,98 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.all
|
|
||||||
|
|
||||||
import android.os.Bundle
|
|
||||||
import android.view.View
|
|
||||||
import androidx.fragment.app.viewModels
|
|
||||||
import com.aurora.store.R
|
|
||||||
import com.aurora.store.databinding.FragmentAppsBinding
|
|
||||||
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.view.ui.commons.BaseFragment
|
|
||||||
import com.aurora.store.viewmodel.all.PaginatedAppList
|
|
||||||
import com.aurora.store.viewmodel.all.PurchasedViewModel
|
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
|
||||||
|
|
||||||
@AndroidEntryPoint
|
|
||||||
class PurchasedAppsFragment : BaseFragment<FragmentAppsBinding>() {
|
|
||||||
private val viewModel: PurchasedViewModel by viewModels()
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
@JvmStatic
|
|
||||||
fun newInstance(): PurchasedAppsFragment {
|
|
||||||
return PurchasedAppsFragment()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
|
||||||
super.onViewCreated(view, savedInstanceState)
|
|
||||||
|
|
||||||
val endlessRecyclerOnScrollListener = object : EndlessRecyclerOnScrollListener(8) {
|
|
||||||
override fun onLoadMore(currentPage: Int) {
|
|
||||||
viewModel.observe()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
binding.recycler.addOnScrollListener(endlessRecyclerOnScrollListener)
|
|
||||||
|
|
||||||
viewModel.liveData.observe(viewLifecycleOwner) {
|
|
||||||
updateController(it)
|
|
||||||
}
|
|
||||||
|
|
||||||
updateController(null)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun updateController(paginatedAppList: PaginatedAppList?) {
|
|
||||||
binding.recycler.withModels {
|
|
||||||
setFilterDuplicates(true)
|
|
||||||
if (paginatedAppList == null) {
|
|
||||||
for (i in 1..10) {
|
|
||||||
add(
|
|
||||||
AppListViewShimmerModel_()
|
|
||||||
.id(i)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
paginatedAppList.appList.forEach { app ->
|
|
||||||
add(
|
|
||||||
AppListViewModel_()
|
|
||||||
.id(app.id)
|
|
||||||
.app(app)
|
|
||||||
.click { _ -> openDetailsFragment(app.packageName, app) }
|
|
||||||
.longClick { _ ->
|
|
||||||
openAppMenuSheet(app)
|
|
||||||
false
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (paginatedAppList.hasMore) {
|
|
||||||
add(
|
|
||||||
AppProgressViewModel_()
|
|
||||||
.id("progress")
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -33,7 +33,6 @@ import com.aurora.store.databinding.FragmentAppsGamesBinding
|
|||||||
import com.aurora.store.util.Preferences
|
import com.aurora.store.util.Preferences
|
||||||
import com.aurora.store.view.ui.commons.BaseFragment
|
import com.aurora.store.view.ui.commons.BaseFragment
|
||||||
import com.aurora.store.view.ui.commons.CategoryFragment
|
import com.aurora.store.view.ui.commons.CategoryFragment
|
||||||
import com.aurora.store.view.ui.commons.EditorChoiceFragment
|
|
||||||
import com.aurora.store.view.ui.commons.ForYouFragment
|
import com.aurora.store.view.ui.commons.ForYouFragment
|
||||||
import com.aurora.store.view.ui.commons.TopChartContainerFragment
|
import com.aurora.store.view.ui.commons.TopChartContainerFragment
|
||||||
import com.google.android.material.tabs.TabLayout
|
import com.google.android.material.tabs.TabLayout
|
||||||
@@ -92,10 +91,6 @@ class AppsContainerFragment : BaseFragment<FragmentAppsGamesBinding>() {
|
|||||||
|
|
||||||
add(getString(R.string.tab_top_charts))
|
add(getString(R.string.tab_top_charts))
|
||||||
add(getString(R.string.tab_categories))
|
add(getString(R.string.tab_categories))
|
||||||
|
|
||||||
if (!authProvider.isAnonymous) {
|
|
||||||
add(getString(R.string.tab_editor_choice))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TabLayoutMediator(
|
TabLayoutMediator(
|
||||||
@@ -130,10 +125,6 @@ class AppsContainerFragment : BaseFragment<FragmentAppsGamesBinding>() {
|
|||||||
}
|
}
|
||||||
add(TopChartContainerFragment.newInstance(0))
|
add(TopChartContainerFragment.newInstance(0))
|
||||||
add(CategoryFragment.newInstance(0))
|
add(CategoryFragment.newInstance(0))
|
||||||
|
|
||||||
if (isGoogleAccount) {
|
|
||||||
add(EditorChoiceFragment.newInstance(0))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun createFragment(position: Int): Fragment {
|
override fun createFragment(position: Int): Fragment {
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ import androidx.navigation.fragment.findNavController
|
|||||||
import androidx.viewbinding.ViewBinding
|
import androidx.viewbinding.ViewBinding
|
||||||
import com.aurora.gplayapi.data.models.App
|
import com.aurora.gplayapi.data.models.App
|
||||||
import com.aurora.gplayapi.data.models.Category
|
import com.aurora.gplayapi.data.models.Category
|
||||||
|
import com.aurora.gplayapi.data.models.StreamCluster
|
||||||
import com.aurora.store.MobileNavigationDirections
|
import com.aurora.store.MobileNavigationDirections
|
||||||
import com.google.gson.Gson
|
import com.google.gson.Gson
|
||||||
import java.lang.reflect.ParameterizedType
|
import java.lang.reflect.ParameterizedType
|
||||||
@@ -93,19 +94,12 @@ abstract class BaseFragment<ViewBindingType : ViewBinding> : Fragment() {
|
|||||||
title
|
title
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
} else {
|
|
||||||
findNavController().navigate(
|
|
||||||
MobileNavigationDirections.actionGlobalStreamBrowseFragment(
|
|
||||||
browseUrl,
|
|
||||||
title
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun openEditorStreamBrowseFragment(browseUrl: String, title: String = "") {
|
fun openStreamBrowseFragment(streamCluster: StreamCluster) {
|
||||||
findNavController().navigate(
|
findNavController().navigate(
|
||||||
MobileNavigationDirections.actionGlobalEditorStreamBrowseFragment(title, browseUrl)
|
MobileNavigationDirections.actionGlobalStreamBrowseFragment(streamCluster)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -28,20 +28,21 @@ import com.aurora.gplayapi.data.models.App
|
|||||||
import com.aurora.gplayapi.data.models.StreamBundle
|
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
|
import com.aurora.gplayapi.helpers.contracts.StreamContract
|
||||||
|
import com.aurora.gplayapi.utils.CategoryUtil
|
||||||
import com.aurora.store.data.model.ViewState
|
import com.aurora.store.data.model.ViewState
|
||||||
import com.aurora.store.data.model.ViewState.Loading.getDataAs
|
import com.aurora.store.data.model.ViewState.Loading.getDataAs
|
||||||
import com.aurora.store.databinding.FragmentGenericWithToolbarBinding
|
import com.aurora.store.databinding.FragmentGenericWithToolbarBinding
|
||||||
import com.aurora.store.view.custom.recycler.EndlessRecyclerOnScrollListener
|
import com.aurora.store.view.custom.recycler.EndlessRecyclerOnScrollListener
|
||||||
import com.aurora.store.view.epoxy.controller.CategoryCarouselController
|
import com.aurora.store.view.epoxy.controller.CategoryCarouselController
|
||||||
import com.aurora.store.view.epoxy.controller.GenericCarouselController
|
import com.aurora.store.view.epoxy.controller.GenericCarouselController
|
||||||
import com.aurora.store.viewmodel.subcategory.SubCategoryClusterViewModel
|
import com.aurora.store.viewmodel.subcategory.CategoryStreamViewModel
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
|
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
class CategoryBrowseFragment : BaseFragment<FragmentGenericWithToolbarBinding>(),
|
class CategoryBrowseFragment : BaseFragment<FragmentGenericWithToolbarBinding>(),
|
||||||
GenericCarouselController.Callbacks {
|
GenericCarouselController.Callbacks {
|
||||||
private val args: CategoryBrowseFragmentArgs by navArgs()
|
private val args: CategoryBrowseFragmentArgs by navArgs()
|
||||||
private val viewModel: SubCategoryClusterViewModel by activityViewModels()
|
private val viewModel: CategoryStreamViewModel by activityViewModels()
|
||||||
|
|
||||||
private lateinit var category: StreamContract.Category
|
private lateinit var category: StreamContract.Category
|
||||||
private var streamBundle: StreamBundle? = StreamBundle()
|
private var streamBundle: StreamBundle? = StreamBundle()
|
||||||
@@ -49,8 +50,7 @@ class CategoryBrowseFragment : BaseFragment<FragmentGenericWithToolbarBinding>()
|
|||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
|
||||||
val rawCategory = args.browseUrl.split("/").last()
|
category = CategoryUtil.getCategoryFromUrl(args.browseUrl)
|
||||||
category = StreamContract.Category.NONE.apply { value = rawCategory }
|
|
||||||
|
|
||||||
val genericCarouselController = CategoryCarouselController(this)
|
val genericCarouselController = CategoryCarouselController(this)
|
||||||
|
|
||||||
@@ -68,6 +68,7 @@ class CategoryBrowseFragment : BaseFragment<FragmentGenericWithToolbarBinding>()
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
viewModel.getStreamBundle(category)
|
||||||
viewModel.liveData.observe(viewLifecycleOwner) {
|
viewModel.liveData.observe(viewLifecycleOwner) {
|
||||||
when (it) {
|
when (it) {
|
||||||
is ViewState.Loading -> {
|
is ViewState.Loading -> {
|
||||||
@@ -84,12 +85,10 @@ class CategoryBrowseFragment : BaseFragment<FragmentGenericWithToolbarBinding>()
|
|||||||
else -> {}
|
else -> {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
viewModel.observe(category)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onHeaderClicked(streamCluster: StreamCluster) {
|
override fun onHeaderClicked(streamCluster: StreamCluster) {
|
||||||
|
openStreamBrowseFragment(streamCluster)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onClusterScrolled(streamCluster: StreamCluster) {
|
override fun onClusterScrolled(streamCluster: StreamCluster) {
|
||||||
|
|||||||
@@ -1,77 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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 android.view.View
|
|
||||||
import androidx.fragment.app.viewModels
|
|
||||||
import com.aurora.Constants
|
|
||||||
import com.aurora.gplayapi.data.models.editor.EditorChoiceCluster
|
|
||||||
import com.aurora.gplayapi.helpers.contracts.StreamContract
|
|
||||||
import com.aurora.store.databinding.FragmentForYouBinding
|
|
||||||
import com.aurora.store.view.epoxy.controller.EditorChoiceController
|
|
||||||
import com.aurora.store.viewmodel.editorschoice.EditorChoiceViewModel
|
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
|
||||||
|
|
||||||
@AndroidEntryPoint
|
|
||||||
class EditorChoiceFragment : BaseFragment<FragmentForYouBinding>(),
|
|
||||||
EditorChoiceController.Callbacks {
|
|
||||||
private val viewModel: EditorChoiceViewModel by viewModels()
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
@JvmStatic
|
|
||||||
fun newInstance(pageType: Int): EditorChoiceFragment {
|
|
||||||
return EditorChoiceFragment().apply {
|
|
||||||
arguments = Bundle().apply {
|
|
||||||
putInt(Constants.PAGE_TYPE, pageType)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
|
||||||
super.onViewCreated(view, savedInstanceState)
|
|
||||||
|
|
||||||
var pageType = 0
|
|
||||||
val bundle = arguments
|
|
||||||
if (bundle != null) {
|
|
||||||
pageType = bundle.getInt(Constants.PAGE_TYPE, 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
val editorChoiceController = EditorChoiceController(this)
|
|
||||||
binding.recycler.setController(editorChoiceController)
|
|
||||||
|
|
||||||
when (pageType) {
|
|
||||||
0 -> viewModel.getEditorChoiceStream(StreamContract.Category.APPLICATION)
|
|
||||||
1 -> viewModel.getEditorChoiceStream(StreamContract.Category.GAME)
|
|
||||||
}
|
|
||||||
|
|
||||||
viewModel.liveData.observe(viewLifecycleOwner) {
|
|
||||||
editorChoiceController.setData(it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onClick(editorChoiceCluster: EditorChoiceCluster) {
|
|
||||||
openEditorStreamBrowseFragment(
|
|
||||||
editorChoiceCluster.clusterBrowseUrl,
|
|
||||||
editorChoiceCluster.clusterTitle
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,139 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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 android.view.View
|
|
||||||
import androidx.fragment.app.viewModels
|
|
||||||
import androidx.navigation.fragment.findNavController
|
|
||||||
import androidx.navigation.fragment.navArgs
|
|
||||||
import com.airbnb.epoxy.EpoxyModel
|
|
||||||
import com.aurora.gplayapi.data.models.App
|
|
||||||
import com.aurora.store.databinding.FragmentGenericWithToolbarBinding
|
|
||||||
import com.aurora.store.view.epoxy.groups.CarouselHorizontalModel_
|
|
||||||
import com.aurora.store.view.epoxy.views.EditorHeadViewModel_
|
|
||||||
import com.aurora.store.view.epoxy.views.HorizontalDividerViewModel_
|
|
||||||
import com.aurora.store.view.epoxy.views.app.AppListViewModel_
|
|
||||||
import com.aurora.store.view.epoxy.views.details.MiniScreenshotView
|
|
||||||
import com.aurora.store.view.epoxy.views.details.MiniScreenshotViewModel_
|
|
||||||
import com.aurora.store.view.epoxy.views.shimmer.AppListViewShimmerModel_
|
|
||||||
import com.aurora.store.viewmodel.editorschoice.EditorBrowseViewModel
|
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
|
||||||
|
|
||||||
@AndroidEntryPoint
|
|
||||||
class EditorStreamBrowseFragment : BaseFragment<FragmentGenericWithToolbarBinding>() {
|
|
||||||
private val args: EditorStreamBrowseFragmentArgs by navArgs()
|
|
||||||
private val viewModel: EditorBrowseViewModel by viewModels()
|
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
|
||||||
super.onViewCreated(view, savedInstanceState)
|
|
||||||
|
|
||||||
// Toolbar
|
|
||||||
binding.layoutToolbarAction.apply {
|
|
||||||
txtTitle.text = args.title
|
|
||||||
imgActionPrimary.setOnClickListener {
|
|
||||||
findNavController().navigateUp()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
viewModel.liveData.observe(viewLifecycleOwner) {
|
|
||||||
updateController(it)
|
|
||||||
}
|
|
||||||
|
|
||||||
viewModel.getEditorStreamBundle(args.browseUrl)
|
|
||||||
updateController(null)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun updateController(appList: MutableList<App>?) {
|
|
||||||
binding.recycler.withModels {
|
|
||||||
setFilterDuplicates(true)
|
|
||||||
if (appList == null) {
|
|
||||||
for (i in 1..6) {
|
|
||||||
add(
|
|
||||||
AppListViewShimmerModel_()
|
|
||||||
.id(i)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
appList.forEach { app ->
|
|
||||||
val screenshotsViewModels = mutableListOf<EpoxyModel<*>>()
|
|
||||||
|
|
||||||
for ((position, artwork) in app.screenshots.withIndex()) {
|
|
||||||
screenshotsViewModels.add(
|
|
||||||
MiniScreenshotViewModel_()
|
|
||||||
.id(artwork.url)
|
|
||||||
.position(position)
|
|
||||||
.artwork(artwork)
|
|
||||||
.callback(object : MiniScreenshotView.ScreenshotCallback {
|
|
||||||
override fun onClick(position: Int) {
|
|
||||||
openScreenshotFragment(app, position)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
add(
|
|
||||||
AppListViewModel_()
|
|
||||||
.id("app_${app.id}")
|
|
||||||
.app(app)
|
|
||||||
.click { _ -> openDetailsFragment(app.packageName, app) }
|
|
||||||
)
|
|
||||||
|
|
||||||
app.editorReason?.let { editorReason ->
|
|
||||||
add(
|
|
||||||
EditorHeadViewModel_()
|
|
||||||
.id("bulletin_${app.id}")
|
|
||||||
.title(
|
|
||||||
editorReason.bulletins
|
|
||||||
.joinToString(transform = { "\n• $it" })
|
|
||||||
.substringAfter(delimiter = "\n")
|
|
||||||
)
|
|
||||||
.click { _ -> openDetailsFragment(app.packageName, app) }
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (screenshotsViewModels.isNotEmpty()) {
|
|
||||||
add(
|
|
||||||
CarouselHorizontalModel_()
|
|
||||||
.id("screenshots_${app.id}")
|
|
||||||
.models(screenshotsViewModels)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
app.editorReason?.let { editorReason ->
|
|
||||||
if (editorReason.description.isNotEmpty()) {
|
|
||||||
add(
|
|
||||||
EditorHeadViewModel_()
|
|
||||||
.id("description_${app.id}")
|
|
||||||
.title(editorReason.description)
|
|
||||||
.click { _ -> openDetailsFragment(app.packageName, app) }
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
add(
|
|
||||||
HorizontalDividerViewModel_()
|
|
||||||
.id("divider_${app.id}")
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -67,27 +67,18 @@ class ForYouFragment : BaseFragment<FragmentForYouBinding>(),
|
|||||||
pageType = bundle.getInt(Constants.PAGE_TYPE, 0)
|
pageType = bundle.getInt(Constants.PAGE_TYPE, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
when (pageType) {
|
category = if (pageType == 0) Category.APPLICATION else Category.GAME
|
||||||
0 -> {
|
|
||||||
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)
|
||||||
binding.recycler.addOnScrollListener(
|
binding.recycler.addOnScrollListener(
|
||||||
object : EndlessRecyclerOnScrollListener() {
|
object : EndlessRecyclerOnScrollListener(visibleThreshold = 4) {
|
||||||
override fun onLoadMore(currentPage: Int) {
|
override fun onLoadMore(currentPage: Int) {
|
||||||
viewModel.observe(category, Type.HOME)
|
viewModel.observe(category, Type.HOME)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
viewModel.getStreamBundle(category, Type.HOME)
|
||||||
viewModel.liveData.observe(viewLifecycleOwner) {
|
viewModel.liveData.observe(viewLifecycleOwner) {
|
||||||
when (it) {
|
when (it) {
|
||||||
is ViewState.Loading -> {
|
is ViewState.Loading -> {
|
||||||
@@ -107,7 +98,7 @@ class ForYouFragment : BaseFragment<FragmentForYouBinding>(),
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onHeaderClicked(streamCluster: StreamCluster) {
|
override fun onHeaderClicked(streamCluster: StreamCluster) {
|
||||||
|
openStreamBrowseFragment(streamCluster)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onClusterScrolled(streamCluster: StreamCluster) {
|
override fun onClusterScrolled(streamCluster: StreamCluster) {
|
||||||
|
|||||||
@@ -270,11 +270,6 @@ class MoreDialogFragment : DialogFragment() {
|
|||||||
icon = R.drawable.ic_apps,
|
icon = R.drawable.ic_apps,
|
||||||
destinationID = R.id.appsGamesFragment
|
destinationID = R.id.appsGamesFragment
|
||||||
),
|
),
|
||||||
Option(
|
|
||||||
title = R.string.title_apps_sale,
|
|
||||||
icon = R.drawable.ic_sale,
|
|
||||||
destinationID = R.id.appSalesFragment
|
|
||||||
),
|
|
||||||
Option(
|
Option(
|
||||||
title = R.string.title_blacklist_manager,
|
title = R.string.title_blacklist_manager,
|
||||||
icon = R.drawable.ic_blacklist,
|
icon = R.drawable.ic_blacklist,
|
||||||
|
|||||||
@@ -38,38 +38,32 @@ class StreamBrowseFragment : BaseFragment<FragmentGenericWithToolbarBinding>() {
|
|||||||
private val args: StreamBrowseFragmentArgs by navArgs()
|
private val args: StreamBrowseFragmentArgs by navArgs()
|
||||||
private val viewModel: StreamBrowseViewModel by viewModels()
|
private val viewModel: StreamBrowseViewModel by viewModels()
|
||||||
|
|
||||||
private lateinit var endlessRecyclerOnScrollListener: EndlessRecyclerOnScrollListener
|
private lateinit var streamCluster: StreamCluster
|
||||||
private lateinit var cluster: StreamCluster
|
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
|
||||||
|
streamCluster = args.cluster
|
||||||
|
|
||||||
// Toolbar
|
// Toolbar
|
||||||
binding.layoutToolbarAction.apply {
|
binding.layoutToolbarAction.apply {
|
||||||
txtTitle.text = args.title
|
txtTitle.text = streamCluster.clusterTitle
|
||||||
imgActionPrimary.setOnClickListener {
|
imgActionPrimary.setOnClickListener {
|
||||||
findNavController().navigateUp()
|
findNavController().navigateUp()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
viewModel.liveData.observe(viewLifecycleOwner) {
|
binding.recycler.addOnScrollListener(object :
|
||||||
if (!::cluster.isInitialized) {
|
EndlessRecyclerOnScrollListener(visibleThreshold = 4) {
|
||||||
endlessRecyclerOnScrollListener = object : EndlessRecyclerOnScrollListener() {
|
override fun onLoadMore(currentPage: Int) {
|
||||||
override fun onLoadMore(currentPage: Int) {
|
viewModel.nextCluster()
|
||||||
viewModel.nextCluster()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
binding.recycler.addOnScrollListener(endlessRecyclerOnScrollListener)
|
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
|
||||||
cluster = it
|
viewModel.initCluster(streamCluster)
|
||||||
|
viewModel.liveData.observe(viewLifecycleOwner) {
|
||||||
updateController(cluster)
|
updateController(streamCluster)
|
||||||
binding.layoutToolbarAction.txtTitle.text = it.clusterTitle
|
|
||||||
}
|
}
|
||||||
|
|
||||||
viewModel.getStreamBundle(args.browseUrl)
|
|
||||||
updateController(null)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateController(streamCluster: StreamCluster?) {
|
private fun updateController(streamCluster: StreamCluster?) {
|
||||||
|
|||||||
@@ -55,8 +55,8 @@ import com.aurora.gplayapi.data.models.App
|
|||||||
import com.aurora.gplayapi.data.models.Review
|
import com.aurora.gplayapi.data.models.Review
|
||||||
import com.aurora.gplayapi.data.models.StreamBundle
|
import com.aurora.gplayapi.data.models.StreamBundle
|
||||||
import com.aurora.gplayapi.data.models.StreamCluster
|
import com.aurora.gplayapi.data.models.StreamCluster
|
||||||
import com.aurora.store.AuroraApp
|
|
||||||
import com.aurora.store.AppStreamStash
|
import com.aurora.store.AppStreamStash
|
||||||
|
import com.aurora.store.AuroraApp
|
||||||
import com.aurora.store.R
|
import com.aurora.store.R
|
||||||
import com.aurora.store.data.event.BusEvent
|
import com.aurora.store.data.event.BusEvent
|
||||||
import com.aurora.store.data.event.Event
|
import com.aurora.store.data.event.Event
|
||||||
@@ -135,7 +135,7 @@ class AppDetailsFragment : BaseFragment<FragmentDetailsBinding>() {
|
|||||||
|
|
||||||
private fun onEvent(event: Event) {
|
private fun onEvent(event: Event) {
|
||||||
when (event) {
|
when (event) {
|
||||||
is BusEvent.InstallEvent -> {
|
is InstallerEvent.Installed -> {
|
||||||
if (app.packageName == event.packageName) {
|
if (app.packageName == event.packageName) {
|
||||||
attachActions()
|
attachActions()
|
||||||
binding.layoutDetailsToolbar.toolbar.menu.apply {
|
binding.layoutDetailsToolbar.toolbar.menu.apply {
|
||||||
@@ -147,7 +147,7 @@ class AppDetailsFragment : BaseFragment<FragmentDetailsBinding>() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
is BusEvent.UninstallEvent -> {
|
is InstallerEvent.Uninstalled -> {
|
||||||
if (app.packageName == event.packageName) {
|
if (app.packageName == event.packageName) {
|
||||||
attachActions()
|
attachActions()
|
||||||
binding.layoutDetailsToolbar.toolbar.menu.apply {
|
binding.layoutDetailsToolbar.toolbar.menu.apply {
|
||||||
@@ -166,14 +166,23 @@ class AppDetailsFragment : BaseFragment<FragmentDetailsBinding>() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
is InstallerEvent.Failed -> {
|
is InstallerEvent.Failed -> {
|
||||||
findNavController().navigate(
|
if (app.packageName == event.packageName) {
|
||||||
AppDetailsFragmentDirections.actionAppDetailsFragmentToInstallErrorDialogSheet(
|
findNavController().navigate(
|
||||||
app,
|
AppDetailsFragmentDirections.actionAppDetailsFragmentToInstallErrorDialogSheet(
|
||||||
event.packageName ?: "",
|
app,
|
||||||
event.error ?: "",
|
event.packageName ?: "",
|
||||||
event.extra ?: ""
|
event.error ?: "",
|
||||||
|
event.extra ?: ""
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
is InstallerEvent.Installing -> {
|
||||||
|
if (event.packageName == app.packageName) {
|
||||||
|
attachActions()
|
||||||
|
updateActionState(State.INSTALLING)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> {
|
else -> {
|
||||||
@@ -356,10 +365,10 @@ class AppDetailsFragment : BaseFragment<FragmentDetailsBinding>() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
viewLifecycleOwner.lifecycleScope.launch {
|
viewLifecycleOwner.lifecycleScope.launch {
|
||||||
AuroraApp.flowEvent.busEvent.collect { onEvent(it) }
|
AuroraApp.events.busEvent.collect { onEvent(it) }
|
||||||
}
|
}
|
||||||
viewLifecycleOwner.lifecycleScope.launch {
|
viewLifecycleOwner.lifecycleScope.launch {
|
||||||
AuroraApp.flowEvent.installerEvent.collect { onEvent(it) }
|
AuroraApp.events.installerEvent.collect { onEvent(it) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -507,7 +516,15 @@ class AppDetailsFragment : BaseFragment<FragmentDetailsBinding>() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun updateActionState(state: State) {
|
private fun updateActionState(state: State) {
|
||||||
binding.layoutDetailsInstall.btnDownload.updateState(state)
|
runOnUiThread {
|
||||||
|
binding.layoutDetailsInstall.btnDownload.apply {
|
||||||
|
updateState(state)
|
||||||
|
if (state == State.INSTALLING) {
|
||||||
|
setButtonState(false)
|
||||||
|
setText(R.string.action_installing)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun openApp() {
|
private fun openApp() {
|
||||||
@@ -589,9 +606,8 @@ class AppDetailsFragment : BaseFragment<FragmentDetailsBinding>() {
|
|||||||
app.isInstalled = PackageUtil.isInstalled(requireContext(), app.packageName)
|
app.isInstalled = PackageUtil.isInstalled(requireContext(), app.packageName)
|
||||||
|
|
||||||
runOnUiThread {
|
runOnUiThread {
|
||||||
app.isInstalled = PackageUtil.isInstalled(requireContext(), app.packageName)
|
|
||||||
|
|
||||||
binding.layoutDetailsInstall.btnDownload.let { btn ->
|
binding.layoutDetailsInstall.btnDownload.let { btn ->
|
||||||
|
btn.setButtonState(true)
|
||||||
if (app.isInstalled) {
|
if (app.isInstalled) {
|
||||||
isUpdatable = PackageUtil.isUpdatable(
|
isUpdatable = PackageUtil.isUpdatable(
|
||||||
requireContext(),
|
requireContext(),
|
||||||
|
|||||||
@@ -33,7 +33,6 @@ import com.aurora.store.databinding.FragmentAppsGamesBinding
|
|||||||
import com.aurora.store.util.Preferences
|
import com.aurora.store.util.Preferences
|
||||||
import com.aurora.store.view.ui.commons.BaseFragment
|
import com.aurora.store.view.ui.commons.BaseFragment
|
||||||
import com.aurora.store.view.ui.commons.CategoryFragment
|
import com.aurora.store.view.ui.commons.CategoryFragment
|
||||||
import com.aurora.store.view.ui.commons.EditorChoiceFragment
|
|
||||||
import com.aurora.store.view.ui.commons.ForYouFragment
|
import com.aurora.store.view.ui.commons.ForYouFragment
|
||||||
import com.aurora.store.view.ui.commons.TopChartContainerFragment
|
import com.aurora.store.view.ui.commons.TopChartContainerFragment
|
||||||
import com.google.android.material.tabs.TabLayout
|
import com.google.android.material.tabs.TabLayout
|
||||||
@@ -91,10 +90,6 @@ class GamesContainerFragment : BaseFragment<FragmentAppsGamesBinding>() {
|
|||||||
|
|
||||||
add(getString(R.string.tab_top_charts))
|
add(getString(R.string.tab_top_charts))
|
||||||
add(getString(R.string.tab_categories))
|
add(getString(R.string.tab_categories))
|
||||||
|
|
||||||
if (!authProvider.isAnonymous) {
|
|
||||||
add(getString(R.string.tab_editor_choice))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TabLayoutMediator(
|
TabLayoutMediator(
|
||||||
@@ -129,10 +124,6 @@ class GamesContainerFragment : BaseFragment<FragmentAppsGamesBinding>() {
|
|||||||
|
|
||||||
add(TopChartContainerFragment.newInstance(1))
|
add(TopChartContainerFragment.newInstance(1))
|
||||||
add(CategoryFragment.newInstance(1))
|
add(CategoryFragment.newInstance(1))
|
||||||
|
|
||||||
if (isGoogleAccount) {
|
|
||||||
add(EditorChoiceFragment.newInstance(1))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun createFragment(position: Int): Fragment {
|
override fun createFragment(position: Int): Fragment {
|
||||||
|
|||||||
@@ -1,113 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.sale
|
|
||||||
|
|
||||||
import android.os.Bundle
|
|
||||||
import android.view.View
|
|
||||||
import androidx.fragment.app.viewModels
|
|
||||||
import androidx.navigation.fragment.findNavController
|
|
||||||
import com.aurora.store.R
|
|
||||||
import com.aurora.store.databinding.FragmentGenericWithToolbarBinding
|
|
||||||
import com.aurora.store.view.custom.recycler.EndlessRecyclerOnScrollListener
|
|
||||||
import com.aurora.store.view.epoxy.views.AppProgressViewModel_
|
|
||||||
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.NoAppViewModel_
|
|
||||||
import com.aurora.store.view.epoxy.views.shimmer.AppListViewShimmerModel_
|
|
||||||
import com.aurora.store.view.ui.commons.BaseFragment
|
|
||||||
import com.aurora.store.viewmodel.all.PaginatedAppList
|
|
||||||
import com.aurora.store.viewmodel.sale.AppSalesViewModel
|
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
|
||||||
|
|
||||||
@AndroidEntryPoint
|
|
||||||
class AppSalesFragment : BaseFragment<FragmentGenericWithToolbarBinding>() {
|
|
||||||
|
|
||||||
private val viewModel: AppSalesViewModel by viewModels()
|
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
|
||||||
super.onViewCreated(view, savedInstanceState)
|
|
||||||
|
|
||||||
binding.layoutToolbarAction.txtTitle.text = getString(R.string.title_apps_sale)
|
|
||||||
binding.layoutToolbarAction.imgActionPrimary.setOnClickListener {
|
|
||||||
findNavController().navigateUp()
|
|
||||||
}
|
|
||||||
|
|
||||||
val endlessRecyclerOnScrollListener = object : EndlessRecyclerOnScrollListener() {
|
|
||||||
override fun onLoadMore(currentPage: Int) {
|
|
||||||
viewModel.observe()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
binding.recycler.addOnScrollListener(endlessRecyclerOnScrollListener)
|
|
||||||
updateController(null)
|
|
||||||
|
|
||||||
viewModel.liveData.observe(viewLifecycleOwner) {
|
|
||||||
updateController(it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun updateController(paginatedAppList: PaginatedAppList?) {
|
|
||||||
binding.recycler
|
|
||||||
.withModels {
|
|
||||||
setFilterDuplicates(true)
|
|
||||||
if (paginatedAppList == null) {
|
|
||||||
for (i in 1..6) {
|
|
||||||
add(
|
|
||||||
AppListViewShimmerModel_()
|
|
||||||
.id(i)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
add(
|
|
||||||
HeaderViewModel_()
|
|
||||||
.id("header")
|
|
||||||
.title(getString(R.string.title_apps_sale_provider))
|
|
||||||
)
|
|
||||||
|
|
||||||
paginatedAppList.appList
|
|
||||||
.filter { it.packageName.isNotEmpty() }
|
|
||||||
.forEach {
|
|
||||||
add(
|
|
||||||
AppListViewModel_()
|
|
||||||
.id(it.packageName.hashCode())
|
|
||||||
.app(it)
|
|
||||||
.click { _ -> openDetailsFragment(it.packageName, it) }
|
|
||||||
)
|
|
||||||
setFilterDuplicates(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (paginatedAppList.hasMore) {
|
|
||||||
add(
|
|
||||||
AppProgressViewModel_()
|
|
||||||
.id("progress")
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!paginatedAppList.hasMore && paginatedAppList.appList.isEmpty()) {
|
|
||||||
add(
|
|
||||||
NoAppViewModel_()
|
|
||||||
.id("no_app_sale")
|
|
||||||
.icon(R.drawable.ic_apps)
|
|
||||||
.message(getString(R.string.details_no_apps_on_sale))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -94,8 +94,8 @@ class AppMenuSheet : BaseDialogSheet<SheetAppMenuBinding>() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dismissAllowingStateLoss()
|
dismissAllowingStateLoss()
|
||||||
AuroraApp.flowEvent.emitEvent(
|
AuroraApp.events.send(
|
||||||
BusEvent.Blacklisted(args.app.packageName, "")
|
BusEvent.Blacklisted(args.app.packageName)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -37,7 +37,8 @@ import com.aurora.gplayapi.data.models.App
|
|||||||
import com.aurora.store.AuroraApp
|
import com.aurora.store.AuroraApp
|
||||||
import com.aurora.store.MobileNavigationDirections
|
import com.aurora.store.MobileNavigationDirections
|
||||||
import com.aurora.store.R
|
import com.aurora.store.R
|
||||||
import com.aurora.store.data.event.BusEvent
|
import com.aurora.store.data.event.Event
|
||||||
|
import com.aurora.store.data.event.InstallerEvent
|
||||||
import com.aurora.store.data.room.download.Download
|
import com.aurora.store.data.room.download.Download
|
||||||
import com.aurora.store.databinding.FragmentUpdatesBinding
|
import com.aurora.store.databinding.FragmentUpdatesBinding
|
||||||
import com.aurora.store.util.PackageUtil
|
import com.aurora.store.util.PackageUtil
|
||||||
@@ -143,13 +144,13 @@ class UpdatesFragment : BaseFragment<FragmentUpdatesBinding>() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
viewLifecycleOwner.lifecycleScope.launch {
|
viewLifecycleOwner.lifecycleScope.launch {
|
||||||
AuroraApp.flowEvent.busEvent.collect { onEvent(it) }
|
AuroraApp.events.busEvent.collect { onEvent(it) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun onEvent(event: BusEvent) {
|
private fun onEvent(event: Event) {
|
||||||
when (event) {
|
when (event) {
|
||||||
is BusEvent.InstallEvent, is BusEvent.UninstallEvent -> {
|
is InstallerEvent.Installed, is InstallerEvent.Uninstalled -> {
|
||||||
viewModel.fetchUpdates()
|
viewModel.fetchUpdates()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,81 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.all
|
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
|
||||||
import android.content.Context
|
|
||||||
import androidx.lifecycle.MutableLiveData
|
|
||||||
import androidx.lifecycle.ViewModel
|
|
||||||
import androidx.lifecycle.viewModelScope
|
|
||||||
import com.aurora.gplayapi.data.models.StreamCluster
|
|
||||||
import com.aurora.gplayapi.helpers.ClusterHelper
|
|
||||||
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.launch
|
|
||||||
import kotlinx.coroutines.supervisorScope
|
|
||||||
import javax.inject.Inject
|
|
||||||
|
|
||||||
@HiltViewModel
|
|
||||||
@SuppressLint("StaticFieldLeak") // false positive, see https://github.com/google/dagger/issues/3253
|
|
||||||
class LibraryAppsViewModel @Inject constructor(
|
|
||||||
@ApplicationContext private val context: Context,
|
|
||||||
private val authProvider: AuthProvider
|
|
||||||
) : ViewModel() {
|
|
||||||
|
|
||||||
private val clusterHelper: ClusterHelper = ClusterHelper(authProvider.authData)
|
|
||||||
.using(HttpClient.getPreferredClient(context))
|
|
||||||
|
|
||||||
val liveData: MutableLiveData<StreamCluster> = MutableLiveData()
|
|
||||||
var streamCluster: StreamCluster = StreamCluster()
|
|
||||||
|
|
||||||
fun observe() {
|
|
||||||
viewModelScope.launch(Dispatchers.IO) {
|
|
||||||
supervisorScope {
|
|
||||||
try {
|
|
||||||
when {
|
|
||||||
streamCluster.clusterAppList.isEmpty() -> {
|
|
||||||
val newCluster =
|
|
||||||
clusterHelper.getCluster(ClusterHelper.Type.MY_APPS_LIBRARY)
|
|
||||||
updateCluster(newCluster)
|
|
||||||
liveData.postValue(streamCluster)
|
|
||||||
}
|
|
||||||
streamCluster.hasNext() -> {
|
|
||||||
val newCluster = clusterHelper.next(streamCluster.clusterNextPageUrl)
|
|
||||||
updateCluster(newCluster)
|
|
||||||
liveData.postValue(streamCluster)
|
|
||||||
}
|
|
||||||
else -> {}
|
|
||||||
}
|
|
||||||
} catch (_: Exception) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun updateCluster(newCluster: StreamCluster) {
|
|
||||||
streamCluster.apply {
|
|
||||||
clusterAppList.addAll(newCluster.clusterAppList)
|
|
||||||
clusterNextPageUrl = newCluster.clusterNextPageUrl
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,83 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.all
|
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
|
||||||
import android.content.Context
|
|
||||||
import androidx.lifecycle.MutableLiveData
|
|
||||||
import androidx.lifecycle.ViewModel
|
|
||||||
import androidx.lifecycle.viewModelScope
|
|
||||||
import com.aurora.gplayapi.data.models.App
|
|
||||||
import com.aurora.gplayapi.helpers.PurchaseHelper
|
|
||||||
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.launch
|
|
||||||
import kotlinx.coroutines.supervisorScope
|
|
||||||
import javax.inject.Inject
|
|
||||||
|
|
||||||
data class PaginatedAppList(
|
|
||||||
var appList: MutableList<App> = mutableListOf(),
|
|
||||||
var hasMore: Boolean
|
|
||||||
)
|
|
||||||
|
|
||||||
@HiltViewModel
|
|
||||||
@SuppressLint("StaticFieldLeak") // false positive, see https://github.com/google/dagger/issues/3253
|
|
||||||
class PurchasedViewModel @Inject constructor(
|
|
||||||
@ApplicationContext private val context: Context,
|
|
||||||
private val authProvider: AuthProvider
|
|
||||||
) : ViewModel() {
|
|
||||||
|
|
||||||
private val purchaseHelper = PurchaseHelper(authProvider.authData)
|
|
||||||
.using(HttpClient.getPreferredClient(context))
|
|
||||||
|
|
||||||
private var appList: MutableList<App> = mutableListOf()
|
|
||||||
|
|
||||||
val liveData: MutableLiveData<PaginatedAppList> = MutableLiveData()
|
|
||||||
|
|
||||||
fun observe() {
|
|
||||||
viewModelScope.launch(Dispatchers.IO) {
|
|
||||||
supervisorScope {
|
|
||||||
try {
|
|
||||||
val nextAppList = purchaseHelper.getPurchaseHistory(appList.size)
|
|
||||||
.filter { it.displayName.isNotEmpty() }
|
|
||||||
|
|
||||||
if (nextAppList.isEmpty()) {
|
|
||||||
liveData.postValue(PaginatedAppList(
|
|
||||||
appList,
|
|
||||||
false
|
|
||||||
))
|
|
||||||
} else {
|
|
||||||
appList.addAll(nextAppList)
|
|
||||||
liveData.postValue(
|
|
||||||
PaginatedAppList(
|
|
||||||
appList,
|
|
||||||
true
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
} catch (_: Exception) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -32,7 +32,7 @@ import com.aurora.gplayapi.data.providers.DeviceInfoProvider
|
|||||||
import com.aurora.gplayapi.helpers.AuthHelper
|
import com.aurora.gplayapi.helpers.AuthHelper
|
||||||
import com.aurora.store.AuroraApp
|
import com.aurora.store.AuroraApp
|
||||||
import com.aurora.store.R
|
import com.aurora.store.R
|
||||||
import com.aurora.store.data.event.BusEvent
|
import com.aurora.store.data.event.AuthEvent
|
||||||
import com.aurora.store.data.model.AccountType
|
import com.aurora.store.data.model.AccountType
|
||||||
import com.aurora.store.data.model.AuthState
|
import com.aurora.store.data.model.AuthState
|
||||||
import com.aurora.store.data.model.InsecureAuth
|
import com.aurora.store.data.model.InsecureAuth
|
||||||
@@ -201,18 +201,18 @@ class AuthViewModel @Inject constructor(
|
|||||||
if (aasToken != null) {
|
if (aasToken != null) {
|
||||||
Preferences.putString(context, Constants.ACCOUNT_EMAIL_PLAIN, email)
|
Preferences.putString(context, Constants.ACCOUNT_EMAIL_PLAIN, email)
|
||||||
Preferences.putString(context, Constants.ACCOUNT_AAS_PLAIN, aasToken)
|
Preferences.putString(context, Constants.ACCOUNT_AAS_PLAIN, aasToken)
|
||||||
AuroraApp.flowEvent.emitEvent(BusEvent.GoogleAAS(true, email, aasToken))
|
AuroraApp.events.send(AuthEvent.GoogleLogin(true, email, aasToken))
|
||||||
} else {
|
} else {
|
||||||
Preferences.putString(context, Constants.ACCOUNT_EMAIL_PLAIN, "")
|
Preferences.putString(context, Constants.ACCOUNT_EMAIL_PLAIN, "")
|
||||||
Preferences.putString(context, Constants.ACCOUNT_AAS_PLAIN, "")
|
Preferences.putString(context, Constants.ACCOUNT_AAS_PLAIN, "")
|
||||||
AuroraApp.flowEvent.emitEvent(BusEvent.GoogleAAS(false))
|
AuroraApp.events.send(AuthEvent.GoogleLogin(false, "", ""))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
AuroraApp.flowEvent.emitEvent(BusEvent.GoogleAAS(false))
|
AuroraApp.events.send(AuthEvent.GoogleLogin(false, "", ""))
|
||||||
}
|
}
|
||||||
} catch (exception: Exception) {
|
} catch (exception: Exception) {
|
||||||
Log.e(TAG, "Failed to build AuthData", exception)
|
Log.e(TAG, "Failed to build AuthData", exception)
|
||||||
AuroraApp.flowEvent.emitEvent(BusEvent.GoogleAAS(false))
|
AuroraApp.events.send(AuthEvent.GoogleLogin(false, "", ""))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -238,6 +238,7 @@ class AuthViewModel @Inject constructor(
|
|||||||
)
|
)
|
||||||
buildGoogleAuthData(email, aasToken)
|
buildGoogleAuthData(email, aasToken)
|
||||||
}
|
}
|
||||||
|
|
||||||
AccountType.ANONYMOUS -> {
|
AccountType.ANONYMOUS -> {
|
||||||
buildAnonymousAuthData()
|
buildAnonymousAuthData()
|
||||||
}
|
}
|
||||||
@@ -248,9 +249,11 @@ class AuthViewModel @Inject constructor(
|
|||||||
is UnknownHostException -> {
|
is UnknownHostException -> {
|
||||||
context.getString(R.string.title_no_network)
|
context.getString(R.string.title_no_network)
|
||||||
}
|
}
|
||||||
|
|
||||||
is ConnectException -> {
|
is ConnectException -> {
|
||||||
context.getString(R.string.server_unreachable)
|
context.getString(R.string.server_unreachable)
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> {
|
else -> {
|
||||||
context.getString(R.string.bad_request)
|
context.getString(R.string.bad_request)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,7 +25,8 @@ import androidx.lifecycle.MutableLiveData
|
|||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import com.aurora.gplayapi.data.models.StreamCluster
|
import com.aurora.gplayapi.data.models.StreamCluster
|
||||||
import com.aurora.gplayapi.helpers.StreamHelper
|
import com.aurora.gplayapi.helpers.contracts.StreamContract
|
||||||
|
import com.aurora.gplayapi.helpers.web.WebStreamHelper
|
||||||
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
|
||||||
@@ -43,31 +44,16 @@ class StreamBrowseViewModel @Inject constructor(
|
|||||||
private val authProvider: AuthProvider
|
private val authProvider: AuthProvider
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
|
|
||||||
private val streamHelper: StreamHelper = StreamHelper(authProvider.authData)
|
private val streamHelper: StreamContract = WebStreamHelper()
|
||||||
.using(HttpClient.getPreferredClient(context))
|
.using(HttpClient.getPreferredClient(context))
|
||||||
|
|
||||||
val liveData: MutableLiveData<StreamCluster> = MutableLiveData()
|
val liveData: MutableLiveData<StreamCluster> = MutableLiveData()
|
||||||
var streamCluster: StreamCluster = StreamCluster()
|
|
||||||
|
|
||||||
fun getStreamBundle(browseUrl: String) {
|
private lateinit var streamCluster: StreamCluster
|
||||||
viewModelScope.launch(Dispatchers.IO) {
|
|
||||||
liveData.postValue(getInitialCluster(browseUrl))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getInitialCluster(browseUrl: String): StreamCluster {
|
fun initCluster(cluster: StreamCluster) {
|
||||||
val browseResponse = streamHelper.getBrowseStreamResponse(browseUrl)
|
streamCluster = cluster
|
||||||
|
liveData.postValue(streamCluster)
|
||||||
if (browseResponse.contentsUrl.isNotEmpty())
|
|
||||||
streamCluster = streamHelper.getNextStreamCluster(browseResponse.contentsUrl)
|
|
||||||
else if (browseResponse.hasBrowseTab())
|
|
||||||
streamCluster = streamHelper.getNextStreamCluster(browseResponse.browseTab.listUrl)
|
|
||||||
|
|
||||||
streamCluster.apply {
|
|
||||||
clusterTitle = browseResponse.title
|
|
||||||
}
|
|
||||||
|
|
||||||
return streamCluster
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun nextCluster() {
|
fun nextCluster() {
|
||||||
@@ -75,18 +61,18 @@ class StreamBrowseViewModel @Inject constructor(
|
|||||||
supervisorScope {
|
supervisorScope {
|
||||||
try {
|
try {
|
||||||
if (streamCluster.hasNext()) {
|
if (streamCluster.hasNext()) {
|
||||||
val newCluster = streamHelper.getNextStreamCluster(
|
val nextCluster = streamHelper.nextStreamCluster(
|
||||||
streamCluster.clusterNextPageUrl
|
streamCluster.clusterNextPageUrl
|
||||||
)
|
)
|
||||||
|
|
||||||
streamCluster.apply {
|
streamCluster.apply {
|
||||||
clusterAppList.addAll(newCluster.clusterAppList)
|
clusterAppList.addAll(nextCluster.clusterAppList)
|
||||||
clusterNextPageUrl = newCluster.clusterNextPageUrl
|
clusterNextPageUrl = nextCluster.clusterNextPageUrl
|
||||||
}
|
}
|
||||||
|
|
||||||
liveData.postValue(streamCluster)
|
liveData.postValue(streamCluster)
|
||||||
} else {
|
} else {
|
||||||
Log.i("End of Bundle")
|
Log.i("End of Cluster")
|
||||||
}
|
}
|
||||||
} catch (_: Exception) {
|
} catch (_: Exception) {
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,7 +28,6 @@ import androidx.lifecycle.viewModelScope
|
|||||||
import com.aurora.gplayapi.data.models.Category
|
import com.aurora.gplayapi.data.models.Category
|
||||||
import com.aurora.gplayapi.helpers.CategoryHelper
|
import com.aurora.gplayapi.helpers.CategoryHelper
|
||||||
import com.aurora.gplayapi.helpers.contracts.CategoryContract
|
import com.aurora.gplayapi.helpers.contracts.CategoryContract
|
||||||
import com.aurora.gplayapi.helpers.web.WebCategoryHelper
|
|
||||||
import com.aurora.store.CategoryStash
|
import com.aurora.store.CategoryStash
|
||||||
import com.aurora.store.data.model.ViewState
|
import com.aurora.store.data.model.ViewState
|
||||||
import com.aurora.store.data.network.HttpClient
|
import com.aurora.store.data.network.HttpClient
|
||||||
@@ -50,9 +49,6 @@ class CategoryViewModel @Inject constructor(
|
|||||||
private val categoryHelper: CategoryHelper = CategoryHelper(authProvider.authData)
|
private val categoryHelper: CategoryHelper = CategoryHelper(authProvider.authData)
|
||||||
.using(HttpClient.getPreferredClient(context))
|
.using(HttpClient.getPreferredClient(context))
|
||||||
|
|
||||||
private val webCategoryHelper: CategoryContract = WebCategoryHelper()
|
|
||||||
.using(HttpClient.getPreferredClient(context))
|
|
||||||
|
|
||||||
private var stash: CategoryStash = mutableMapOf(
|
private var stash: CategoryStash = mutableMapOf(
|
||||||
Category.Type.APPLICATION to emptyList(),
|
Category.Type.APPLICATION to emptyList(),
|
||||||
Category.Type.GAME to emptyList()
|
Category.Type.GAME to emptyList()
|
||||||
@@ -61,11 +57,7 @@ class CategoryViewModel @Inject constructor(
|
|||||||
val liveData = MutableLiveData<ViewState>()
|
val liveData = MutableLiveData<ViewState>()
|
||||||
|
|
||||||
private fun contract(): CategoryContract {
|
private fun contract(): CategoryContract {
|
||||||
return if (authProvider.isAnonymous) {
|
return categoryHelper
|
||||||
webCategoryHelper
|
|
||||||
} else {
|
|
||||||
categoryHelper
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getCategoryList(type: Category.Type) {
|
fun getCategoryList(type: Category.Type) {
|
||||||
@@ -74,10 +66,11 @@ class CategoryViewModel @Inject constructor(
|
|||||||
|
|
||||||
if (categories.isNotEmpty()) {
|
if (categories.isNotEmpty()) {
|
||||||
liveData.postValue(ViewState.Success(stash))
|
liveData.postValue(ViewState.Success(stash))
|
||||||
|
return@launch
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
stash[type] = contract().getAllCategoriesList(type)
|
stash[type] = contract().getAllCategories(type)
|
||||||
liveData.postValue(ViewState.Success(stash))
|
liveData.postValue(ViewState.Success(stash))
|
||||||
} catch (exception: Exception) {
|
} catch (exception: Exception) {
|
||||||
Log.e(TAG, "Failed fetching list of categories", exception)
|
Log.e(TAG, "Failed fetching list of categories", exception)
|
||||||
|
|||||||
@@ -1,74 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.editorschoice
|
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
|
||||||
import android.content.Context
|
|
||||||
import androidx.lifecycle.MutableLiveData
|
|
||||||
import androidx.lifecycle.ViewModel
|
|
||||||
import androidx.lifecycle.viewModelScope
|
|
||||||
import com.aurora.gplayapi.data.models.App
|
|
||||||
import com.aurora.gplayapi.helpers.StreamHelper
|
|
||||||
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.launch
|
|
||||||
import kotlinx.coroutines.supervisorScope
|
|
||||||
import javax.inject.Inject
|
|
||||||
|
|
||||||
@HiltViewModel
|
|
||||||
@SuppressLint("StaticFieldLeak") // false positive, see https://github.com/google/dagger/issues/3253
|
|
||||||
class EditorBrowseViewModel @Inject constructor(
|
|
||||||
@ApplicationContext private val context: Context,
|
|
||||||
private val authProvider: AuthProvider
|
|
||||||
) : ViewModel() {
|
|
||||||
|
|
||||||
private val streamHelper: StreamHelper = StreamHelper(authProvider.authData)
|
|
||||||
.using(HttpClient.getPreferredClient(context))
|
|
||||||
|
|
||||||
val liveData: MutableLiveData<MutableList<App>> = MutableLiveData()
|
|
||||||
val appList: MutableList<App> = mutableListOf()
|
|
||||||
|
|
||||||
fun getEditorStreamBundle(browseUrl: String) {
|
|
||||||
viewModelScope.launch(Dispatchers.IO) {
|
|
||||||
supervisorScope {
|
|
||||||
try {
|
|
||||||
val browseResponse = streamHelper.getBrowseStreamResponse(browseUrl)
|
|
||||||
val listResponse =
|
|
||||||
streamHelper.getNextStreamResponse(browseResponse.browseTab.listUrl)
|
|
||||||
|
|
||||||
listResponse.itemList.forEach {
|
|
||||||
it?.let {
|
|
||||||
it.subItemList.forEach {
|
|
||||||
appList.addAll(streamHelper.getAppsFromItem(it))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
liveData.postValue(appList)
|
|
||||||
|
|
||||||
} catch (_: Exception) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,66 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.editorschoice
|
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
|
||||||
import android.content.Context
|
|
||||||
import android.util.Log
|
|
||||||
import androidx.lifecycle.MutableLiveData
|
|
||||||
import androidx.lifecycle.ViewModel
|
|
||||||
import androidx.lifecycle.viewModelScope
|
|
||||||
import com.aurora.gplayapi.data.models.editor.EditorChoiceBundle
|
|
||||||
import com.aurora.gplayapi.helpers.StreamHelper
|
|
||||||
import com.aurora.gplayapi.helpers.contracts.StreamContract
|
|
||||||
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.launch
|
|
||||||
import kotlinx.coroutines.supervisorScope
|
|
||||||
import javax.inject.Inject
|
|
||||||
|
|
||||||
@HiltViewModel
|
|
||||||
@SuppressLint("StaticFieldLeak") // false positive, see https://github.com/google/dagger/issues/3253
|
|
||||||
class EditorChoiceViewModel @Inject constructor(
|
|
||||||
@ApplicationContext private val context: Context,
|
|
||||||
private val authProvider: AuthProvider
|
|
||||||
) : ViewModel() {
|
|
||||||
|
|
||||||
private val TAG = EditorChoiceViewModel::class.java.simpleName
|
|
||||||
|
|
||||||
private val streamHelper: StreamHelper = StreamHelper(authProvider.authData)
|
|
||||||
.using(HttpClient.getPreferredClient(context))
|
|
||||||
|
|
||||||
val liveData: MutableLiveData<List<EditorChoiceBundle>> = MutableLiveData()
|
|
||||||
|
|
||||||
fun getEditorChoiceStream(category: StreamContract.Category) {
|
|
||||||
viewModelScope.launch(Dispatchers.IO) {
|
|
||||||
supervisorScope {
|
|
||||||
try {
|
|
||||||
val editorChoiceBundle = streamHelper.getEditorChoiceStream(category)
|
|
||||||
liveData.postValue(editorChoiceBundle)
|
|
||||||
} catch (exception: Exception) {
|
|
||||||
Log.e(TAG, "Failed fetching list of categories", exception)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -26,13 +26,11 @@ import androidx.lifecycle.ViewModel
|
|||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import com.aurora.gplayapi.data.models.StreamBundle
|
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.StreamHelper
|
|
||||||
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.HomeStash
|
||||||
import com.aurora.store.data.model.ViewState
|
import com.aurora.store.data.model.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.util.Log
|
import com.aurora.store.util.Log
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||||
@@ -44,14 +42,10 @@ 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 StreamViewModel @Inject constructor(
|
class StreamViewModel @Inject constructor(
|
||||||
@ApplicationContext private val context: Context,
|
@ApplicationContext private val context: Context
|
||||||
private val authProvider: AuthProvider
|
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
|
|
||||||
private var streamHelper: StreamContract = StreamHelper(authProvider.authData)
|
private var webStreamHelper = WebStreamHelper()
|
||||||
.using(HttpClient.getPreferredClient(context))
|
|
||||||
|
|
||||||
private var webStreamHelper: StreamContract = WebStreamHelper()
|
|
||||||
.using(HttpClient.getPreferredClient(context))
|
.using(HttpClient.getPreferredClient(context))
|
||||||
|
|
||||||
val liveData: MutableLiveData<ViewState> = MutableLiveData()
|
val liveData: MutableLiveData<ViewState> = MutableLiveData()
|
||||||
@@ -59,11 +53,7 @@ class StreamViewModel @Inject constructor(
|
|||||||
private var stash: HomeStash = mutableMapOf()
|
private var stash: HomeStash = mutableMapOf()
|
||||||
|
|
||||||
fun contract(): StreamContract {
|
fun contract(): StreamContract {
|
||||||
return if (authProvider.isAnonymous) {
|
return webStreamHelper
|
||||||
webStreamHelper
|
|
||||||
} else {
|
|
||||||
streamHelper
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getStreamBundle(category: StreamContract.Category, type: StreamContract.Type) {
|
fun getStreamBundle(category: StreamContract.Category, type: StreamContract.Type) {
|
||||||
@@ -75,7 +65,7 @@ class StreamViewModel @Inject constructor(
|
|||||||
viewModelScope.launch(Dispatchers.IO) {
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
supervisorScope {
|
supervisorScope {
|
||||||
val bundle = targetBundle(category)
|
val bundle = targetBundle(category)
|
||||||
if (bundle.streamClusters.isNotEmpty()) {
|
if (bundle.hasCluster()) {
|
||||||
liveData.postValue(ViewState.Success(stash))
|
liveData.postValue(ViewState.Success(stash))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -83,13 +73,13 @@ class StreamViewModel @Inject constructor(
|
|||||||
if (!bundle.hasCluster() || bundle.hasNext()) {
|
if (!bundle.hasCluster() || bundle.hasNext()) {
|
||||||
|
|
||||||
//Fetch new stream bundle
|
//Fetch new stream bundle
|
||||||
val newBundle = if (bundle.streamClusters.isEmpty()) {
|
val newBundle = if (bundle.hasCluster()) {
|
||||||
contract().fetch(type, category)
|
|
||||||
} else {
|
|
||||||
contract().nextStreamBundle(
|
contract().nextStreamBundle(
|
||||||
category,
|
category,
|
||||||
bundle.streamNextPageUrl
|
bundle.streamNextPageUrl
|
||||||
)
|
)
|
||||||
|
} else {
|
||||||
|
contract().fetch(type, category)
|
||||||
}
|
}
|
||||||
|
|
||||||
//Update old bundle
|
//Update old bundle
|
||||||
@@ -142,7 +132,7 @@ class StreamViewModel @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun targetBundle(category: StreamContract.Category): StreamBundle {
|
private fun targetBundle(category: StreamContract.Category): StreamBundle {
|
||||||
val streamBundle = stash.getOrPut(category){
|
val streamBundle = stash.getOrPut(category) {
|
||||||
StreamBundle()
|
StreamBundle()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,95 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.sale
|
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
|
||||||
import android.content.Context
|
|
||||||
import androidx.lifecycle.MutableLiveData
|
|
||||||
import androidx.lifecycle.ViewModel
|
|
||||||
import androidx.lifecycle.viewModelScope
|
|
||||||
import com.aurora.gplayapi.data.models.App
|
|
||||||
import com.aurora.gplayapi.helpers.AppSalesHelper
|
|
||||||
import com.aurora.store.data.network.HttpClient
|
|
||||||
import com.aurora.store.data.providers.AuthProvider
|
|
||||||
import com.aurora.store.viewmodel.all.PaginatedAppList
|
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
|
||||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import kotlinx.coroutines.supervisorScope
|
|
||||||
import javax.inject.Inject
|
|
||||||
|
|
||||||
@HiltViewModel
|
|
||||||
@SuppressLint("StaticFieldLeak") // false positive, see https://github.com/google/dagger/issues/3253
|
|
||||||
class AppSalesViewModel @Inject constructor(
|
|
||||||
@ApplicationContext private val context: Context,
|
|
||||||
private val authProvider: AuthProvider
|
|
||||||
) : ViewModel() {
|
|
||||||
|
|
||||||
private val appSalesHelper: AppSalesHelper = AppSalesHelper(authProvider.authData)
|
|
||||||
.using(HttpClient.getPreferredClient(context))
|
|
||||||
|
|
||||||
private var page: Int = 0
|
|
||||||
private val appList: MutableList<App> = mutableListOf()
|
|
||||||
|
|
||||||
val liveData: MutableLiveData<PaginatedAppList> = MutableLiveData()
|
|
||||||
|
|
||||||
init {
|
|
||||||
observe()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun observe() {
|
|
||||||
viewModelScope.launch(Dispatchers.IO) {
|
|
||||||
supervisorScope {
|
|
||||||
try {
|
|
||||||
val nextAppList = getSearchResults()
|
|
||||||
|
|
||||||
if (nextAppList.isEmpty()) {
|
|
||||||
liveData.postValue(
|
|
||||||
PaginatedAppList(
|
|
||||||
appList,
|
|
||||||
hasMore = false
|
|
||||||
)
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
appList.addAll(nextAppList)
|
|
||||||
liveData.postValue(
|
|
||||||
PaginatedAppList(
|
|
||||||
appList,
|
|
||||||
hasMore = true
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (_: Exception) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getSearchResults(): List<App> {
|
|
||||||
return try {
|
|
||||||
appSalesHelper.getAppsOnSale(page = page++, offer = 100)
|
|
||||||
} catch (e: Exception) {
|
|
||||||
e.printStackTrace()
|
|
||||||
emptyList()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -35,7 +35,7 @@ class SheetsViewModel @Inject constructor(
|
|||||||
val purchaseHelper = PurchaseHelper(authProvider.authData)
|
val purchaseHelper = PurchaseHelper(authProvider.authData)
|
||||||
val files = purchaseHelper.purchase(app.packageName, customVersion, app.offerType)
|
val files = purchaseHelper.purchase(app.packageName, customVersion, app.offerType)
|
||||||
if (files.isNotEmpty()) {
|
if (files.isNotEmpty()) {
|
||||||
AuroraApp.flowEvent.emitEvent(
|
AuroraApp.events.send(
|
||||||
BusEvent.ManualDownload(app.packageName, customVersion)
|
BusEvent.ManualDownload(app.packageName, customVersion)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,8 +26,9 @@ import androidx.lifecycle.ViewModel
|
|||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import com.aurora.gplayapi.data.models.StreamBundle
|
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.CategoryStreamContract
|
||||||
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.WebCategoryStreamHelper
|
||||||
import com.aurora.store.data.model.ViewState
|
import com.aurora.store.data.model.ViewState
|
||||||
import com.aurora.store.data.network.HttpClient
|
import com.aurora.store.data.network.HttpClient
|
||||||
import com.aurora.store.util.Log
|
import com.aurora.store.util.Log
|
||||||
@@ -40,25 +41,24 @@ 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 SubCategoryClusterViewModel @Inject constructor(
|
class CategoryStreamViewModel @Inject constructor(
|
||||||
@ApplicationContext private val context: Context
|
@ApplicationContext private val context: Context
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
|
|
||||||
var contract: StreamContract = WebStreamHelper()
|
private var webCategoryStreamHelper = WebCategoryStreamHelper()
|
||||||
.using(HttpClient.getPreferredClient(context))
|
.using(HttpClient.getPreferredClient(context))
|
||||||
|
|
||||||
val liveData: MutableLiveData<ViewState> = MutableLiveData()
|
val liveData: MutableLiveData<ViewState> = MutableLiveData()
|
||||||
|
|
||||||
private var stash: MutableMap<String, StreamBundle> = mutableMapOf()
|
private var stash: MutableMap<String, StreamBundle> = mutableMapOf()
|
||||||
|
|
||||||
private fun getCategoryStreamBundle(
|
fun contract(): CategoryStreamContract {
|
||||||
category: StreamContract.Category,
|
return webCategoryStreamHelper
|
||||||
nextPageUrl: String
|
}
|
||||||
): StreamBundle {
|
|
||||||
return if (targetBundle(category).streamClusters.isEmpty())
|
fun getStreamBundle(category: StreamContract.Category) {
|
||||||
contract.fetch(StreamContract.Type.HOME, category)
|
liveData.postValue(ViewState.Loading)
|
||||||
else
|
observe(category)
|
||||||
contract.nextStreamBundle(category, nextPageUrl)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun observe(category: StreamContract.Category) {
|
fun observe(category: StreamContract.Category) {
|
||||||
@@ -66,18 +66,22 @@ class SubCategoryClusterViewModel @Inject constructor(
|
|||||||
viewModelScope.launch(Dispatchers.IO) {
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
supervisorScope {
|
supervisorScope {
|
||||||
val bundle = targetBundle(category)
|
val bundle = targetBundle(category)
|
||||||
|
|
||||||
if (bundle.streamClusters.isNotEmpty()) {
|
if (bundle.streamClusters.isNotEmpty()) {
|
||||||
liveData.postValue(ViewState.Success(stash))
|
liveData.postValue(ViewState.Success(stash))
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (!bundle.hasCluster() || bundle.hasNext()) {
|
if (!bundle.hasCluster() || bundle.hasNext()) {
|
||||||
|
|
||||||
//Fetch new stream bundle
|
//Fetch new stream bundle
|
||||||
val newBundle = getCategoryStreamBundle(
|
val newBundle = if (bundle.streamClusters.isEmpty()) {
|
||||||
category,
|
contract().fetch(category.value)
|
||||||
bundle.streamNextPageUrl
|
} else {
|
||||||
)
|
contract().nextStreamBundle(
|
||||||
|
category,
|
||||||
|
bundle.streamNextPageUrl
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
//Update old bundle
|
//Update old bundle
|
||||||
bundle.apply {
|
bundle.apply {
|
||||||
@@ -103,7 +107,7 @@ class SubCategoryClusterViewModel @Inject constructor(
|
|||||||
try {
|
try {
|
||||||
if (streamCluster.hasNext()) {
|
if (streamCluster.hasNext()) {
|
||||||
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(stash))
|
liveData.postValue(ViewState.Success(stash))
|
||||||
} else {
|
} else {
|
||||||
@@ -25,13 +25,11 @@ import androidx.lifecycle.MutableLiveData
|
|||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import com.aurora.gplayapi.data.models.StreamCluster
|
import com.aurora.gplayapi.data.models.StreamCluster
|
||||||
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.TopChartStash
|
||||||
import com.aurora.store.data.model.ViewState
|
import com.aurora.store.data.model.ViewState
|
||||||
import com.aurora.store.data.network.HttpClient
|
import com.aurora.store.data.network.HttpClient
|
||||||
import com.aurora.store.data.providers.AuthProvider
|
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
@@ -41,13 +39,8 @@ 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 TopChartViewModel @Inject constructor(
|
class TopChartViewModel @Inject constructor(@ApplicationContext private val context: Context) :
|
||||||
@ApplicationContext private val context: Context,
|
ViewModel() {
|
||||||
private val authProvider: AuthProvider
|
|
||||||
) : ViewModel() {
|
|
||||||
|
|
||||||
private val topChartsHelper: TopChartsHelper = TopChartsHelper(authProvider.authData)
|
|
||||||
.using(HttpClient.getPreferredClient(context))
|
|
||||||
|
|
||||||
private val webTopChartsHelper: TopChartsContract = WebTopChartsHelper()
|
private val webTopChartsHelper: TopChartsContract = WebTopChartsHelper()
|
||||||
.using(HttpClient.getPreferredClient(context))
|
.using(HttpClient.getPreferredClient(context))
|
||||||
@@ -57,11 +50,7 @@ class TopChartViewModel @Inject constructor(
|
|||||||
val liveData: MutableLiveData<ViewState> = MutableLiveData()
|
val liveData: MutableLiveData<ViewState> = MutableLiveData()
|
||||||
|
|
||||||
private fun contract(): TopChartsContract {
|
private fun contract(): TopChartsContract {
|
||||||
return if (authProvider.isAnonymous) {
|
return webTopChartsHelper
|
||||||
webTopChartsHelper
|
|
||||||
} else {
|
|
||||||
topChartsHelper
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getStreamCluster(type: TopChartsContract.Type, chart: TopChartsContract.Chart) {
|
fun getStreamCluster(type: TopChartsContract.Type, chart: TopChartsContract.Chart) {
|
||||||
@@ -85,7 +74,7 @@ class TopChartViewModel @Inject constructor(
|
|||||||
try {
|
try {
|
||||||
val target = targetCluster(type, chart)
|
val target = targetCluster(type, chart)
|
||||||
if (target.hasNext()) {
|
if (target.hasNext()) {
|
||||||
val newCluster = topChartsHelper.getNextStreamCluster(
|
val newCluster = contract().getNextStreamCluster(
|
||||||
target.clusterNextPageUrl
|
target.clusterNextPageUrl
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -52,15 +52,12 @@
|
|||||||
|
|
||||||
<com.google.android.material.progressindicator.CircularProgressIndicator
|
<com.google.android.material.progressindicator.CircularProgressIndicator
|
||||||
android:id="@+id/progress_download"
|
android:id="@+id/progress_download"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="@dimen/icon_size_medium"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="@dimen/icon_size_medium"
|
||||||
android:layout_centerInParent="true"
|
android:layout_centerInParent="true"
|
||||||
android:indeterminate="true"
|
|
||||||
android:indeterminateTint="@color/colorScrimAlt"
|
|
||||||
android:visibility="gone"
|
android:visibility="gone"
|
||||||
app:indicatorColor="@color/colorScrimAlt"
|
app:indicatorSize="@dimen/icon_size_medium"
|
||||||
app:trackColor="@color/colorScrim"
|
app:trackThickness="3dp"
|
||||||
app:trackThickness="@dimen/icon_size_default"
|
|
||||||
tools:progress="40" />
|
tools:progress="40" />
|
||||||
</RelativeLayout>
|
</RelativeLayout>
|
||||||
|
|
||||||
|
|||||||
@@ -29,9 +29,6 @@
|
|||||||
<action
|
<action
|
||||||
android:id="@+id/action_global_categoryBrowseFragment"
|
android:id="@+id/action_global_categoryBrowseFragment"
|
||||||
app:destination="@id/categoryBrowseFragment" />
|
app:destination="@id/categoryBrowseFragment" />
|
||||||
<action
|
|
||||||
android:id="@+id/action_global_editorStreamBrowseFragment"
|
|
||||||
app:destination="@id/editorStreamBrowseFragment" />
|
|
||||||
<action
|
<action
|
||||||
android:id="@+id/action_global_expandedStreamBrowseFragment"
|
android:id="@+id/action_global_expandedStreamBrowseFragment"
|
||||||
app:destination="@id/expandedStreamBrowseFragment" />
|
app:destination="@id/expandedStreamBrowseFragment" />
|
||||||
@@ -78,11 +75,6 @@
|
|||||||
android:name="com.aurora.store.view.ui.all.AppsGamesFragment"
|
android:name="com.aurora.store.view.ui.all.AppsGamesFragment"
|
||||||
android:label="@string/title_apps_games"
|
android:label="@string/title_apps_games"
|
||||||
tools:layout="@layout/fragment_generic_with_pager" />
|
tools:layout="@layout/fragment_generic_with_pager" />
|
||||||
<fragment
|
|
||||||
android:id="@+id/appSalesFragment"
|
|
||||||
android:name="com.aurora.store.view.ui.sale.AppSalesFragment"
|
|
||||||
android:label="@string/title_apps_sale"
|
|
||||||
tools:layout="@layout/fragment_generic_with_toolbar" />
|
|
||||||
<fragment
|
<fragment
|
||||||
android:id="@+id/spoofFragment"
|
android:id="@+id/spoofFragment"
|
||||||
android:name="com.aurora.store.view.ui.spoof.SpoofFragment"
|
android:name="com.aurora.store.view.ui.spoof.SpoofFragment"
|
||||||
@@ -220,16 +212,6 @@
|
|||||||
android:id="@+id/action_categoryBrowseFragment_to_appPeekDialogSheet"
|
android:id="@+id/action_categoryBrowseFragment_to_appPeekDialogSheet"
|
||||||
app:destination="@id/appPeekDialogSheet" />
|
app:destination="@id/appPeekDialogSheet" />
|
||||||
</fragment>
|
</fragment>
|
||||||
<fragment
|
|
||||||
android:id="@+id/editorStreamBrowseFragment"
|
|
||||||
android:name="com.aurora.store.view.ui.commons.EditorStreamBrowseFragment"
|
|
||||||
tools:layout="@layout/fragment_generic_with_toolbar" >
|
|
||||||
<argument android:name="title"
|
|
||||||
app:argType="string" />
|
|
||||||
<argument
|
|
||||||
android:name="browseUrl"
|
|
||||||
app:argType="string" />
|
|
||||||
</fragment>
|
|
||||||
<fragment
|
<fragment
|
||||||
android:id="@+id/expandedStreamBrowseFragment"
|
android:id="@+id/expandedStreamBrowseFragment"
|
||||||
android:name="com.aurora.store.view.ui.commons.ExpandedStreamBrowseFragment"
|
android:name="com.aurora.store.view.ui.commons.ExpandedStreamBrowseFragment"
|
||||||
@@ -273,11 +255,8 @@
|
|||||||
android:name="com.aurora.store.view.ui.commons.StreamBrowseFragment"
|
android:name="com.aurora.store.view.ui.commons.StreamBrowseFragment"
|
||||||
tools:layout="@layout/fragment_generic_with_toolbar" >
|
tools:layout="@layout/fragment_generic_with_toolbar" >
|
||||||
<argument
|
<argument
|
||||||
android:name="browseUrl"
|
android:name="cluster"
|
||||||
app:argType="string" />
|
app:argType="com.aurora.gplayapi.data.models.StreamCluster" />
|
||||||
<argument
|
|
||||||
android:name="title"
|
|
||||||
app:argType="string" />
|
|
||||||
</fragment>
|
</fragment>
|
||||||
<fragment
|
<fragment
|
||||||
android:id="@+id/devAppsFragment"
|
android:id="@+id/devAppsFragment"
|
||||||
|
|||||||
@@ -93,6 +93,7 @@
|
|||||||
<string name="action_search">"Search"</string>
|
<string name="action_search">"Search"</string>
|
||||||
<string name="action_share">"Share"</string>
|
<string name="action_share">"Share"</string>
|
||||||
<string name="action_uninstall">"Uninstall"</string>
|
<string name="action_uninstall">"Uninstall"</string>
|
||||||
|
<string name="action_uninstall_success">"Successfully uninstalled"</string>
|
||||||
<string name="action_home_screen">Add to Home screen</string>
|
<string name="action_home_screen">Add to Home screen</string>
|
||||||
<string name="action_uninstall_confirmation">"Do you want to uninstall this app ?"</string>
|
<string name="action_uninstall_confirmation">"Do you want to uninstall this app ?"</string>
|
||||||
<string name="action_info">"App Info"</string>
|
<string name="action_info">"App Info"</string>
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ composeBom = "2024.06.00"
|
|||||||
coreVersion = "1.13.1"
|
coreVersion = "1.13.1"
|
||||||
epoxyVersion = "5.1.4"
|
epoxyVersion = "5.1.4"
|
||||||
espressoVersion = "3.6.1"
|
espressoVersion = "3.6.1"
|
||||||
gplayapiVersion = "3.3.0"
|
gplayapiVersion = "3.3.1"
|
||||||
gsonVersion = "2.10.1"
|
gsonVersion = "2.10.1"
|
||||||
hiddenapibypassVersion = "4.3"
|
hiddenapibypassVersion = "4.3"
|
||||||
hiltVersion = "2.51.1"
|
hiltVersion = "2.51.1"
|
||||||
|
|||||||
Reference in New Issue
Block a user