diff --git a/app/src/main/java/com/aurora/store/AuroraApp.kt b/app/src/main/java/com/aurora/store/AuroraApp.kt index 9b8239098..3c1ede502 100644 --- a/app/src/main/java/com/aurora/store/AuroraApp.kt +++ b/app/src/main/java/com/aurora/store/AuroraApp.kt @@ -25,7 +25,7 @@ import androidx.core.content.ContextCompat import androidx.hilt.work.HiltWorkerFactory import androidx.work.Configuration 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.util.CommonUtil import com.aurora.store.util.DownloadWorkerUtil @@ -58,7 +58,7 @@ class AuroraApp : Application(), Configuration.Provider { private set val enqueuedInstalls: MutableSet = mutableSetOf() - val flowEvent = FlowEvent() + val events = EventFlow() } override fun onCreate() { diff --git a/app/src/main/java/com/aurora/store/data/event/BusEvent.kt b/app/src/main/java/com/aurora/store/data/event/BusEvent.kt index d512f4211..9dcb2282e 100644 --- a/app/src/main/java/com/aurora/store/data/event/BusEvent.kt +++ b/app/src/main/java/com/aurora/store/data/event/BusEvent.kt @@ -21,48 +21,27 @@ package com.aurora.store.data.event abstract class Event -sealed class BusEvent: Event() { - data class InstallEvent( - var packageName: String, - var extra: String? = "" - ) : BusEvent() +sealed class BusEvent : Event() { + lateinit var extra: String + lateinit var error: String - data class UninstallEvent( - var packageName: String, - 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() + data class Blacklisted(val packageName: String) : BusEvent() + data class ManualDownload(val packageName: String, val versionCode: Int) : BusEvent() } -sealed class InstallerEvent: Event() { - data class Success( - var packageName: String? = "", - var extra: String? = "" - ) : InstallerEvent() - - data class Cancelled( - var packageName: String? = "", - var extra: String? = "" - ) : InstallerEvent() - - data class Failed( - var packageName: String? = "", - var error: String? = "", - var extra: String? = "" - ) : InstallerEvent() +sealed class AuthEvent : Event() { + data class GoogleLogin(val success: Boolean, val email: String, val token: String) : AuthEvent() +} + +sealed class InstallerEvent : Event() { + lateinit var extra: String + lateinit var error: String + + var progress: Int = -1 + + data class Installed(val packageName: String) : InstallerEvent() + data class Uninstalled(val packageName: String) : InstallerEvent() + data class Installing(val packageName: String) : InstallerEvent() + data class Cancelled(val packageName: String) : InstallerEvent() + data class Failed(val packageName: String) : InstallerEvent() } diff --git a/app/src/main/java/com/aurora/store/data/event/FlowEvent.kt b/app/src/main/java/com/aurora/store/data/event/EventFlow.kt similarity index 70% rename from app/src/main/java/com/aurora/store/data/event/FlowEvent.kt rename to app/src/main/java/com/aurora/store/data/event/EventFlow.kt index 7a251cf21..3c3105d12 100644 --- a/app/src/main/java/com/aurora/store/data/event/FlowEvent.kt +++ b/app/src/main/java/com/aurora/store/data/event/EventFlow.kt @@ -6,9 +6,9 @@ import kotlinx.coroutines.flow.asSharedFlow import javax.inject.Singleton @Singleton -class FlowEvent { +class EventFlow { - private val TAG = FlowEvent::class.java.simpleName + private val TAG = EventFlow::class.java.simpleName private val _busEvent = MutableSharedFlow(extraBufferCapacity = 1) val busEvent = _busEvent.asSharedFlow() @@ -16,10 +16,14 @@ class FlowEvent { private val _installerEvent = MutableSharedFlow(extraBufferCapacity = 1) val installerEvent = _installerEvent.asSharedFlow() - fun emitEvent(event: Event) { + private val _authEvent = MutableSharedFlow(extraBufferCapacity = 1) + val authEvent = _authEvent.asSharedFlow() + + fun send(event: Event) { when (event) { is InstallerEvent -> _installerEvent.tryEmit(event) is BusEvent -> _busEvent.tryEmit(event) + is AuthEvent -> _authEvent.tryEmit(event) else -> Log.e(TAG, "Got an unhandled event") } } diff --git a/app/src/main/java/com/aurora/store/data/installer/InstallerBase.kt b/app/src/main/java/com/aurora/store/data/installer/InstallerBase.kt index d39553dc7..0376895f6 100644 --- a/app/src/main/java/com/aurora/store/data/installer/InstallerBase.kt +++ b/app/src/main/java/com/aurora/store/data/installer/InstallerBase.kt @@ -66,16 +66,19 @@ abstract class InstallerBase(protected var context: Context) : IInstaller { open fun postError(packageName: String, error: String?, extra: String?) { Log.e("Service Error :$error") - val event = InstallerEvent.Failed( - packageName, - error, - extra - ) + val event = InstallerEvent.Failed(packageName).apply { + this.error = error ?: "" + this.extra = extra ?: "" + } - AuroraApp.flowEvent.emitEvent(event) + AuroraApp.events.send(event) } - open fun getFiles(packageName: String, versionCode: Int, sharedLibPackageName: String = ""): List { + open fun getFiles( + packageName: String, + versionCode: Int, + sharedLibPackageName: String = "" + ): List { val downloadDir = if (sharedLibPackageName.isNotBlank()) { PathUtil.getLibDownloadDir(context, packageName, versionCode, sharedLibPackageName) } else { diff --git a/app/src/main/java/com/aurora/store/data/installer/RootInstaller.kt b/app/src/main/java/com/aurora/store/data/installer/RootInstaller.kt index 4f5437a3d..39689c749 100644 --- a/app/src/main/java/com/aurora/store/data/installer/RootInstaller.kt +++ b/app/src/main/java/com/aurora/store/data/installer/RootInstaller.kt @@ -104,12 +104,11 @@ class RootInstaller @Inject constructor( if (packageName == download?.packageName) onInstallationSuccess() } else { removeFromInstallQueue(packageName) - val event = InstallerEvent.Failed( - packageName, - context.getString(R.string.installer_status_failure), - parseError(shellResult) - ) - AuroraApp.flowEvent.emitEvent(event) + val event = InstallerEvent.Failed(packageName).apply { + this.extra = context.getString(R.string.installer_status_failure) + this.error = parseError(shellResult) + } + AuroraApp.events.send(event) } } else { removeFromInstallQueue(packageName) diff --git a/app/src/main/java/com/aurora/store/data/installer/ServiceInstaller.kt b/app/src/main/java/com/aurora/store/data/installer/ServiceInstaller.kt index 12947888f..dac829c3b 100644 --- a/app/src/main/java/com/aurora/store/data/installer/ServiceInstaller.kt +++ b/app/src/main/java/com/aurora/store/data/installer/ServiceInstaller.kt @@ -35,7 +35,6 @@ import com.aurora.services.IPrivilegedService import com.aurora.store.AuroraApp import com.aurora.store.BuildConfig import com.aurora.store.R -import com.aurora.store.data.event.BusEvent import com.aurora.store.data.event.InstallerEvent import com.aurora.store.data.model.InstallerInfo import com.aurora.store.data.room.download.Download @@ -91,6 +90,7 @@ class ServiceInstaller @Inject constructor( fileList.map { it.absolutePath } ) } + else -> { postError( download.packageName, @@ -148,7 +148,11 @@ class ServiceInstaller @Inject constructor( ) } catch (e: RemoteException) { removeFromInstallQueue(packageName) - postError(packageName, e.localizedMessage, e.stackTraceToString()) + postError( + packageName, + e.localizedMessage, + e.stackTraceToString() + ) readyWithAction.set(true) } } else { @@ -166,7 +170,11 @@ class ServiceInstaller @Inject constructor( ) } catch (e: RemoteException) { removeFromInstallQueue(packageName) - postError(packageName, e.localizedMessage, e.stackTraceToString()) + postError( + packageName, + e.localizedMessage, + e.stackTraceToString() + ) readyWithAction.set(true) } } @@ -210,13 +218,13 @@ class ServiceInstaller @Inject constructor( try { when (returnCode) { PackageInstaller.STATUS_SUCCESS -> { - AuroraApp.flowEvent.emitEvent( - BusEvent.UninstallEvent( - packageName, - context.getString(R.string.installer_status_success) - ) + AuroraApp.events.send( + InstallerEvent.Uninstalled(packageName).apply { + this.extra = context.getString(R.string.action_uninstall_success) + } ) } + else -> { val error = AppInstaller.getErrorString( context, @@ -240,15 +248,15 @@ class ServiceInstaller @Inject constructor( try { when (returnCode) { PackageInstaller.STATUS_SUCCESS -> { - AuroraApp.flowEvent.emitEvent( - InstallerEvent.Success( - packageName, - context.getString(R.string.installer_status_success) - ) + AuroraApp.events.send( + InstallerEvent.Installed(packageName).apply { + this.extra = context.getString(R.string.installer_status_success) + } ) // Installation is not yet finished if this is a shared library if (packageName == download?.packageName) onInstallationSuccess() } + else -> { val error = AppInstaller.getErrorString( context, diff --git a/app/src/main/java/com/aurora/store/data/installer/SessionInstaller.kt b/app/src/main/java/com/aurora/store/data/installer/SessionInstaller.kt index 3d9614058..2cd60b71d 100644 --- a/app/src/main/java/com/aurora/store/data/installer/SessionInstaller.kt +++ b/app/src/main/java/com/aurora/store/data/installer/SessionInstaller.kt @@ -36,7 +36,9 @@ import com.aurora.extensions.isSAndAbove import com.aurora.extensions.isTAndAbove import com.aurora.extensions.isUAndAbove import com.aurora.extensions.runOnUiThread +import com.aurora.store.AuroraApp 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.EXTRA_DISPLAY_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.PackageUtil.isSharedLibraryInstalled import dagger.hilt.android.qualifiers.ApplicationContext +import java.io.IOException import javax.inject.Inject import javax.inject.Singleton @@ -69,27 +72,38 @@ class SessionInstaller @Inject constructor( 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) { - enqueuedSessions.find { set -> set.any { it.sessionId == sessionId } }?.let { sessionSet -> - sessionSet.remove(sessionSet.first { it.sessionId == sessionId }) + enqueuedSessions.find { set -> set.any { 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 (sessionSet.isNotEmpty() && success) { - commitInstall(sessionSet.first()); return - } else { - enqueuedSessions.remove(sessionSet) + // If this was a shared lib, proceed installing other libs or actual package + if (sessionSet.isNotEmpty() && success) { + commitInstall(sessionSet.first()); return + } else { + enqueuedSessions.remove(sessionSet) + } } - } if (enqueuedSessions.isNotEmpty()) { enqueuedSessions.firstOrNull()?.let { sessionSet -> 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) { 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) { Log.i("${download.packageName} already queued") commitInstall(sessionSet.first()) @@ -124,7 +139,11 @@ class SessionInstaller @Inject constructor( download.sharedLibs.forEach { // Shared library packages cannot be updated 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)) } } @@ -147,18 +166,50 @@ class SessionInstaller @Inject constructor( } } - private fun stageInstall(packageName: String, versionCode: Int, sharedLibPkgName: String = ""): Int? { - val packageInstaller = context.packageManager.packageInstaller + private fun stageInstall( + packageName: String, + versionCode: Int, + sharedLibPkgName: String = "" + ): Int? { + val resolvedPackageName = sharedLibPkgName.ifBlank { packageName } - val sessionParams = SessionParams(SessionParams.MODE_FULL_INSTALL).apply { - setAppPackageName(sharedLibPkgName.ifBlank { packageName }) - setInstallLocation(PackageInfo.INSTALL_LOCATION_AUTO) - if (isOAndAbove()) { - setInstallReason(PackageManager.INSTALL_REASON_USER) + val sessionParams = buildSessionParams(resolvedPackageName) + 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 { 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()) { setOriginatingUid(Process.myUid()) } + if (isOAndAbove()) { + setInstallReason(PackageManager.INSTALL_REASON_USER) + } if (isSAndAbove()) { setRequireUserAction(SessionParams.USER_ACTION_NOT_REQUIRED) } @@ -170,26 +221,6 @@ class SessionInstaller @Inject constructor( 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) { diff --git a/app/src/main/java/com/aurora/store/data/model/Enumerations.kt b/app/src/main/java/com/aurora/store/data/model/Enumerations.kt index 0c12e432b..9cb35a6c4 100644 --- a/app/src/main/java/com/aurora/store/data/model/Enumerations.kt +++ b/app/src/main/java/com/aurora/store/data/model/Enumerations.kt @@ -29,5 +29,6 @@ enum class State { QUEUED, PROGRESS, COMPLETE, - CANCELED + CANCELED, + INSTALLING, } diff --git a/app/src/main/java/com/aurora/store/data/model/PaginatedAppList.kt b/app/src/main/java/com/aurora/store/data/model/PaginatedAppList.kt new file mode 100644 index 000000000..186da0fdc --- /dev/null +++ b/app/src/main/java/com/aurora/store/data/model/PaginatedAppList.kt @@ -0,0 +1,8 @@ +package com.aurora.store.data.model + +import com.aurora.gplayapi.data.models.App + +data class PaginatedAppList( + val appList: MutableList = mutableListOf(), + var hasMore: Boolean +) \ No newline at end of file diff --git a/app/src/main/java/com/aurora/store/data/receiver/InstallerStatusReceiver.kt b/app/src/main/java/com/aurora/store/data/receiver/InstallerStatusReceiver.kt index 6d134b7d6..e097b9097 100644 --- a/app/src/main/java/com/aurora/store/data/receiver/InstallerStatusReceiver.kt +++ b/app/src/main/java/com/aurora/store/data/receiver/InstallerStatusReceiver.kt @@ -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 = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager val notification = NotificationUtil.getInstallerStatusNotification( @@ -109,23 +114,24 @@ class InstallerStatusReceiver : BroadcastReceiver() { private fun postStatus(status: Int, packageName: String?, extra: String?, context: Context) { val event = when (status) { PackageInstaller.STATUS_SUCCESS -> { - InstallerEvent.Success( - packageName, context.getString(R.string.installer_status_success) - ) + InstallerEvent.Installed(packageName!!).apply { + this.extra = context.getString(R.string.installer_status_success) + } } PackageInstaller.STATUS_FAILURE_ABORTED -> { - InstallerEvent.Cancelled( - packageName, AppInstaller.getErrorString(context, status) - ) + InstallerEvent.Cancelled(packageName!!).apply { + this.extra = AppInstaller.getErrorString(context, status) + } } else -> { - InstallerEvent.Failed( - packageName, AppInstaller.getErrorString(context, status), extra - ) + InstallerEvent.Failed(packageName!!).apply { + this.error = AppInstaller.getErrorString(context, status) + this.extra = extra ?: "" + } } } - AuroraApp.flowEvent.emitEvent(event) + AuroraApp.events.send(event) } } diff --git a/app/src/main/java/com/aurora/store/data/receiver/PackageManagerReceiver.kt b/app/src/main/java/com/aurora/store/data/receiver/PackageManagerReceiver.kt index 5bd4f0905..0d134dbe7 100644 --- a/app/src/main/java/com/aurora/store/data/receiver/PackageManagerReceiver.kt +++ b/app/src/main/java/com/aurora/store/data/receiver/PackageManagerReceiver.kt @@ -23,8 +23,7 @@ import android.content.BroadcastReceiver import android.content.Context import android.content.Intent import com.aurora.store.AuroraApp -import com.aurora.store.data.event.BusEvent.InstallEvent -import com.aurora.store.data.event.BusEvent.UninstallEvent +import com.aurora.store.data.event.InstallerEvent import com.aurora.store.data.installer.AppInstaller import dagger.hilt.android.AndroidEntryPoint import javax.inject.Inject @@ -41,11 +40,11 @@ open class PackageManagerReceiver : BroadcastReceiver() { when (intent.action) { Intent.ACTION_PACKAGE_ADDED -> { - AuroraApp.flowEvent.emitEvent(InstallEvent(packageName, "")) + AuroraApp.events.send(InstallerEvent.Installed(packageName)) } Intent.ACTION_PACKAGE_REMOVED -> { - AuroraApp.flowEvent.emitEvent(UninstallEvent(packageName, "")) + AuroraApp.events.send(InstallerEvent.Uninstalled(packageName)) } } diff --git a/app/src/main/java/com/aurora/store/view/custom/layouts/button/ActionButton.kt b/app/src/main/java/com/aurora/store/view/custom/layouts/button/ActionButton.kt index f00394091..b9263fd41 100644 --- a/app/src/main/java/com/aurora/store/view/custom/layouts/button/ActionButton.kt +++ b/app/src/main/java/com/aurora/store/view/custom/layouts/button/ActionButton.kt @@ -25,7 +25,6 @@ import android.util.AttributeSet import android.widget.RelativeLayout import androidx.core.content.ContextCompat import com.aurora.extensions.getString -import com.aurora.extensions.runOnUiThread import com.aurora.store.R import com.aurora.store.data.model.State import com.aurora.store.databinding.ViewActionButtonBinding @@ -101,19 +100,21 @@ class ActionButton : RelativeLayout { binding.btn.text = getString(text) } + fun setButtonState(enabled: Boolean = true) { + binding.btn.isEnabled = enabled + } + fun updateState(state: State) { val displayChild = when (state) { - State.IDLE -> 0 State.PROGRESS -> 1 State.COMPLETE -> 2 else -> 0 } if (binding.viewFlipper.displayedChild != displayChild) { - runOnUiThread { - binding.viewFlipper.displayedChild = displayChild - if (displayChild == 2) updateState(State.IDLE) - } + binding.viewFlipper.displayedChild = displayChild + + if (displayChild == 2) updateState(State.IDLE) } } diff --git a/app/src/main/java/com/aurora/store/view/epoxy/groups/CarouselModelGroup.kt b/app/src/main/java/com/aurora/store/view/epoxy/groups/CarouselModelGroup.kt index 1e3675fc2..4dc471284 100644 --- a/app/src/main/java/com/aurora/store/view/epoxy/groups/CarouselModelGroup.kt +++ b/app/src/main/java/com/aurora/store/view/epoxy/groups/CarouselModelGroup.kt @@ -47,7 +47,7 @@ class CarouselModelGroup( val models = ArrayList>() val clusterViewModels = mutableListOf>() - val idPrefix = streamCluster.id + val idPrefix = streamCluster.clusterTitle.hashCode() models.add( HeaderViewModel_() diff --git a/app/src/main/java/com/aurora/store/view/epoxy/views/app/AppUpdateView.kt b/app/src/main/java/com/aurora/store/view/epoxy/views/app/AppUpdateView.kt index f7d7643c1..e79cc9779 100644 --- a/app/src/main/java/com/aurora/store/view/epoxy/views/app/AppUpdateView.kt +++ b/app/src/main/java/com/aurora/store/view/epoxy/views/app/AppUpdateView.kt @@ -19,13 +19,17 @@ package com.aurora.store.view.epoxy.views.app +import android.animation.ObjectAnimator import android.content.Context +import android.graphics.drawable.Drawable import android.util.AttributeSet import android.view.View +import android.view.animation.AccelerateDecelerateInterpolator import androidx.core.content.ContextCompat import androidx.core.text.HtmlCompat import androidx.core.view.isVisible import coil.load +import coil.transform.CircleCropTransformation import coil.transform.RoundedCornersTransformation import com.airbnb.epoxy.CallbackProp import com.airbnb.epoxy.ModelProp @@ -51,6 +55,8 @@ class AppUpdateView @JvmOverloads constructor( attrs: AttributeSet? = null, defStyleAttr: Int = 0 ) : BaseView(context, attrs, defStyleAttr) { + private var iconDrawable: Drawable? = null + private val cornersTransformation = RoundedCornersTransformation(8.px.toFloat()) @ModelProp fun app(app: App) { @@ -59,7 +65,10 @@ class AppUpdateView @JvmOverloads constructor( binding.txtLine1.text = displayName binding.imgIcon.load(iconArtwork.url) { placeholder(R.drawable.bg_placeholder) - transformations(RoundedCornersTransformation(8.px.toFloat())) + transformations(cornersTransformation) + listener { _, result -> + result.drawable.let { iconDrawable = it } + } } binding.txtLine2.text = developerName @@ -90,29 +99,22 @@ class AppUpdateView @JvmOverloads constructor( @ModelProp fun download(download: Download?) { if (download != null) { - /*Inflate Download details*/ binding.btnAction.updateState(download.downloadStatus) - binding.progressDownload.isIndeterminate = download.progress < 1 when (download.downloadStatus) { DownloadStatus.QUEUED -> { - binding.progressDownload.progress = 0 - binding.progressDownload.show() + binding.progressDownload.isIndeterminate = true + animateImageView(scaleFactor = 0.75f) } DownloadStatus.DOWNLOADING -> { - if (download.progress > 0) { - if (download.progress == 100) { - binding.progressDownload.invisible() - } else { - binding.progressDownload.progress = download.progress - binding.progressDownload.show() - } - } + binding.progressDownload.isIndeterminate = false + binding.progressDownload.progress = download.progress + animateImageView(scaleFactor = 0.75f) } else -> { - binding.progressDownload.progress = 0 - binding.progressDownload.invisible() + binding.progressDownload.isIndeterminate = true + animateImageView(scaleFactor = 1f) } } } @@ -141,7 +143,48 @@ class AppUpdateView @JvmOverloads constructor( @OnViewRecycled fun clear() { binding.headerIndicator.removeCallbacks { } - binding.progressDownload.progress = 0 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() + ) + } + } } } diff --git a/app/src/main/java/com/aurora/store/view/ui/account/GoogleFragment.kt b/app/src/main/java/com/aurora/store/view/ui/account/GoogleFragment.kt index 10e486a4f..4f5f68dcb 100644 --- a/app/src/main/java/com/aurora/store/view/ui/account/GoogleFragment.kt +++ b/app/src/main/java/com/aurora/store/view/ui/account/GoogleFragment.kt @@ -34,7 +34,7 @@ import androidx.lifecycle.lifecycleScope import androidx.navigation.fragment.findNavController import com.aurora.store.AuroraApp 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.util.AC2DMUtil import com.aurora.store.view.ui.commons.BaseFragment @@ -110,15 +110,15 @@ class GoogleFragment : BaseFragment() { } 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) { - is BusEvent.GoogleAAS -> { + is AuthEvent.GoogleLogin -> { if (event.success) { - viewModel.buildGoogleAuthData(event.email, event.aasToken) + viewModel.buildGoogleAuthData(event.email, event.token) } else { Toast.makeText( requireContext(), diff --git a/app/src/main/java/com/aurora/store/view/ui/all/AppsGamesFragment.kt b/app/src/main/java/com/aurora/store/view/ui/all/AppsGamesFragment.kt index a4b118032..22e6eb2db 100644 --- a/app/src/main/java/com/aurora/store/view/ui/all/AppsGamesFragment.kt +++ b/app/src/main/java/com/aurora/store/view/ui/all/AppsGamesFragment.kt @@ -65,8 +65,6 @@ class AppsGamesFragment : BaseFragment() { ) { tab: TabLayout.Tab, position: Int -> when (position) { 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 -> {} } }.attach() @@ -81,14 +79,12 @@ class AppsGamesFragment : BaseFragment() { override fun createFragment(position: Int): Fragment { return when (position) { 0 -> InstalledAppsFragment.newInstance() - 1 -> LibraryAppsFragment.newInstance() - 2 -> PurchasedAppsFragment.newInstance() else -> Fragment() } } override fun getItemCount(): Int { - return if (isAnonymous) 1 else 3 + return 1 } } } diff --git a/app/src/main/java/com/aurora/store/view/ui/all/InstalledAppsFragment.kt b/app/src/main/java/com/aurora/store/view/ui/all/InstalledAppsFragment.kt index 0579b7f45..6074f5bf2 100644 --- a/app/src/main/java/com/aurora/store/view/ui/all/InstalledAppsFragment.kt +++ b/app/src/main/java/com/aurora/store/view/ui/all/InstalledAppsFragment.kt @@ -27,6 +27,8 @@ import androidx.lifecycle.lifecycleScope import com.aurora.gplayapi.data.models.App import com.aurora.store.AuroraApp 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.view.epoxy.views.HeaderViewModel_ import com.aurora.store.view.epoxy.views.app.AppListViewModel_ @@ -49,10 +51,10 @@ class InstalledAppsFragment : BaseFragment() { } } - private fun onEvent(event: BusEvent) { + private fun onEvent(event: Event) { when (event) { - is BusEvent.InstallEvent, - is BusEvent.UninstallEvent, + is InstallerEvent.Installed, + is InstallerEvent.Uninstalled, is BusEvent.Blacklisted -> { viewModel.fetchApps() } @@ -73,7 +75,7 @@ class InstalledAppsFragment : BaseFragment() { } viewLifecycleOwner.lifecycleScope.launch { - AuroraApp.flowEvent.busEvent.collect { onEvent(it) } + AuroraApp.events.busEvent.collect { onEvent(it) } } } diff --git a/app/src/main/java/com/aurora/store/view/ui/all/LibraryAppsFragment.kt b/app/src/main/java/com/aurora/store/view/ui/all/LibraryAppsFragment.kt deleted file mode 100644 index aa4155620..000000000 --- a/app/src/main/java/com/aurora/store/view/ui/all/LibraryAppsFragment.kt +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Aurora Store - * Copyright (C) 2021, Rahul Kumar Patel - * - * 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 . - * - */ - -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() { - 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") - ) - } - } - } - } -} diff --git a/app/src/main/java/com/aurora/store/view/ui/all/PurchasedAppsFragment.kt b/app/src/main/java/com/aurora/store/view/ui/all/PurchasedAppsFragment.kt deleted file mode 100644 index bd8893765..000000000 --- a/app/src/main/java/com/aurora/store/view/ui/all/PurchasedAppsFragment.kt +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Aurora Store - * Copyright (C) 2021, Rahul Kumar Patel - * - * 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 . - * - */ - -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() { - 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") - ) - } - } - } - } -} diff --git a/app/src/main/java/com/aurora/store/view/ui/apps/AppsContainerFragment.kt b/app/src/main/java/com/aurora/store/view/ui/apps/AppsContainerFragment.kt index a9f887574..ea529f1ad 100644 --- a/app/src/main/java/com/aurora/store/view/ui/apps/AppsContainerFragment.kt +++ b/app/src/main/java/com/aurora/store/view/ui/apps/AppsContainerFragment.kt @@ -33,7 +33,6 @@ import com.aurora.store.databinding.FragmentAppsGamesBinding import com.aurora.store.util.Preferences import com.aurora.store.view.ui.commons.BaseFragment 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.TopChartContainerFragment import com.google.android.material.tabs.TabLayout @@ -92,10 +91,6 @@ class AppsContainerFragment : BaseFragment() { add(getString(R.string.tab_top_charts)) add(getString(R.string.tab_categories)) - - if (!authProvider.isAnonymous) { - add(getString(R.string.tab_editor_choice)) - } } TabLayoutMediator( @@ -130,10 +125,6 @@ class AppsContainerFragment : BaseFragment() { } add(TopChartContainerFragment.newInstance(0)) add(CategoryFragment.newInstance(0)) - - if (isGoogleAccount) { - add(EditorChoiceFragment.newInstance(0)) - } } override fun createFragment(position: Int): Fragment { diff --git a/app/src/main/java/com/aurora/store/view/ui/commons/BaseFragment.kt b/app/src/main/java/com/aurora/store/view/ui/commons/BaseFragment.kt index 454460917..fdcff455b 100644 --- a/app/src/main/java/com/aurora/store/view/ui/commons/BaseFragment.kt +++ b/app/src/main/java/com/aurora/store/view/ui/commons/BaseFragment.kt @@ -28,6 +28,7 @@ import androidx.navigation.fragment.findNavController import androidx.viewbinding.ViewBinding import com.aurora.gplayapi.data.models.App import com.aurora.gplayapi.data.models.Category +import com.aurora.gplayapi.data.models.StreamCluster import com.aurora.store.MobileNavigationDirections import com.google.gson.Gson import java.lang.reflect.ParameterizedType @@ -93,19 +94,12 @@ abstract class BaseFragment : Fragment() { title ) ) - } else { - findNavController().navigate( - MobileNavigationDirections.actionGlobalStreamBrowseFragment( - browseUrl, - title - ) - ) } } - fun openEditorStreamBrowseFragment(browseUrl: String, title: String = "") { + fun openStreamBrowseFragment(streamCluster: StreamCluster) { findNavController().navigate( - MobileNavigationDirections.actionGlobalEditorStreamBrowseFragment(title, browseUrl) + MobileNavigationDirections.actionGlobalStreamBrowseFragment(streamCluster) ) } diff --git a/app/src/main/java/com/aurora/store/view/ui/commons/CategoryBrowseFragment.kt b/app/src/main/java/com/aurora/store/view/ui/commons/CategoryBrowseFragment.kt index d54f0baa9..deb827db2 100644 --- a/app/src/main/java/com/aurora/store/view/ui/commons/CategoryBrowseFragment.kt +++ b/app/src/main/java/com/aurora/store/view/ui/commons/CategoryBrowseFragment.kt @@ -28,20 +28,21 @@ import com.aurora.gplayapi.data.models.App import com.aurora.gplayapi.data.models.StreamBundle import com.aurora.gplayapi.data.models.StreamCluster import com.aurora.gplayapi.helpers.contracts.StreamContract +import com.aurora.gplayapi.utils.CategoryUtil import com.aurora.store.data.model.ViewState import com.aurora.store.data.model.ViewState.Loading.getDataAs import com.aurora.store.databinding.FragmentGenericWithToolbarBinding import com.aurora.store.view.custom.recycler.EndlessRecyclerOnScrollListener import com.aurora.store.view.epoxy.controller.CategoryCarouselController 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 @AndroidEntryPoint class CategoryBrowseFragment : BaseFragment(), GenericCarouselController.Callbacks { 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 var streamBundle: StreamBundle? = StreamBundle() @@ -49,8 +50,7 @@ class CategoryBrowseFragment : BaseFragment() override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - val rawCategory = args.browseUrl.split("/").last() - category = StreamContract.Category.NONE.apply { value = rawCategory } + category = CategoryUtil.getCategoryFromUrl(args.browseUrl) val genericCarouselController = CategoryCarouselController(this) @@ -68,6 +68,7 @@ class CategoryBrowseFragment : BaseFragment() } }) + viewModel.getStreamBundle(category) viewModel.liveData.observe(viewLifecycleOwner) { when (it) { is ViewState.Loading -> { @@ -84,12 +85,10 @@ class CategoryBrowseFragment : BaseFragment() else -> {} } } - - viewModel.observe(category) } override fun onHeaderClicked(streamCluster: StreamCluster) { - + openStreamBrowseFragment(streamCluster) } override fun onClusterScrolled(streamCluster: StreamCluster) { diff --git a/app/src/main/java/com/aurora/store/view/ui/commons/EditorChoiceFragment.kt b/app/src/main/java/com/aurora/store/view/ui/commons/EditorChoiceFragment.kt deleted file mode 100644 index 676128e83..000000000 --- a/app/src/main/java/com/aurora/store/view/ui/commons/EditorChoiceFragment.kt +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Aurora Store - * Copyright (C) 2021, Rahul Kumar Patel - * - * 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 . - * - */ - -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(), - 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 - ) - } -} diff --git a/app/src/main/java/com/aurora/store/view/ui/commons/EditorStreamBrowseFragment.kt b/app/src/main/java/com/aurora/store/view/ui/commons/EditorStreamBrowseFragment.kt deleted file mode 100644 index 5f1a4371a..000000000 --- a/app/src/main/java/com/aurora/store/view/ui/commons/EditorStreamBrowseFragment.kt +++ /dev/null @@ -1,139 +0,0 @@ -/* - * Aurora Store - * Copyright (C) 2021, Rahul Kumar Patel - * - * 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 . - * - */ - -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() { - 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?) { - binding.recycler.withModels { - setFilterDuplicates(true) - if (appList == null) { - for (i in 1..6) { - add( - AppListViewShimmerModel_() - .id(i) - ) - } - } else { - appList.forEach { app -> - val screenshotsViewModels = mutableListOf>() - - 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}") - ) - } - } - } - } -} diff --git a/app/src/main/java/com/aurora/store/view/ui/commons/ForYouFragment.kt b/app/src/main/java/com/aurora/store/view/ui/commons/ForYouFragment.kt index efdbe2c04..92fe7cb5c 100644 --- a/app/src/main/java/com/aurora/store/view/ui/commons/ForYouFragment.kt +++ b/app/src/main/java/com/aurora/store/view/ui/commons/ForYouFragment.kt @@ -67,27 +67,18 @@ class ForYouFragment : BaseFragment(), pageType = bundle.getInt(Constants.PAGE_TYPE, 0) } - when (pageType) { - 0 -> { - category = Category.APPLICATION - viewModel.getStreamBundle(category, Type.HOME) - } - - 1 -> { - category = Category.GAME - viewModel.getStreamBundle(category, Type.HOME) - } - } + category = if (pageType == 0) Category.APPLICATION else Category.GAME binding.recycler.setController(genericCarouselController) binding.recycler.addOnScrollListener( - object : EndlessRecyclerOnScrollListener() { + object : EndlessRecyclerOnScrollListener(visibleThreshold = 4) { override fun onLoadMore(currentPage: Int) { viewModel.observe(category, Type.HOME) } } ) + viewModel.getStreamBundle(category, Type.HOME) viewModel.liveData.observe(viewLifecycleOwner) { when (it) { is ViewState.Loading -> { @@ -107,7 +98,7 @@ class ForYouFragment : BaseFragment(), } override fun onHeaderClicked(streamCluster: StreamCluster) { - + openStreamBrowseFragment(streamCluster) } override fun onClusterScrolled(streamCluster: StreamCluster) { diff --git a/app/src/main/java/com/aurora/store/view/ui/commons/MoreDialogFragment.kt b/app/src/main/java/com/aurora/store/view/ui/commons/MoreDialogFragment.kt index 7a7d51f94..16bdbed8d 100644 --- a/app/src/main/java/com/aurora/store/view/ui/commons/MoreDialogFragment.kt +++ b/app/src/main/java/com/aurora/store/view/ui/commons/MoreDialogFragment.kt @@ -270,11 +270,6 @@ class MoreDialogFragment : DialogFragment() { icon = R.drawable.ic_apps, destinationID = R.id.appsGamesFragment ), - Option( - title = R.string.title_apps_sale, - icon = R.drawable.ic_sale, - destinationID = R.id.appSalesFragment - ), Option( title = R.string.title_blacklist_manager, icon = R.drawable.ic_blacklist, diff --git a/app/src/main/java/com/aurora/store/view/ui/commons/StreamBrowseFragment.kt b/app/src/main/java/com/aurora/store/view/ui/commons/StreamBrowseFragment.kt index a42ee51dc..ba10d4442 100644 --- a/app/src/main/java/com/aurora/store/view/ui/commons/StreamBrowseFragment.kt +++ b/app/src/main/java/com/aurora/store/view/ui/commons/StreamBrowseFragment.kt @@ -38,38 +38,32 @@ class StreamBrowseFragment : BaseFragment() { private val args: StreamBrowseFragmentArgs by navArgs() private val viewModel: StreamBrowseViewModel by viewModels() - private lateinit var endlessRecyclerOnScrollListener: EndlessRecyclerOnScrollListener - private lateinit var cluster: StreamCluster + private lateinit var streamCluster: StreamCluster override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) + streamCluster = args.cluster + // Toolbar binding.layoutToolbarAction.apply { - txtTitle.text = args.title + txtTitle.text = streamCluster.clusterTitle imgActionPrimary.setOnClickListener { findNavController().navigateUp() } } - viewModel.liveData.observe(viewLifecycleOwner) { - if (!::cluster.isInitialized) { - endlessRecyclerOnScrollListener = object : EndlessRecyclerOnScrollListener() { - override fun onLoadMore(currentPage: Int) { - viewModel.nextCluster() - } - } - binding.recycler.addOnScrollListener(endlessRecyclerOnScrollListener) + binding.recycler.addOnScrollListener(object : + EndlessRecyclerOnScrollListener(visibleThreshold = 4) { + override fun onLoadMore(currentPage: Int) { + viewModel.nextCluster() } + }) - cluster = it - - updateController(cluster) - binding.layoutToolbarAction.txtTitle.text = it.clusterTitle + viewModel.initCluster(streamCluster) + viewModel.liveData.observe(viewLifecycleOwner) { + updateController(streamCluster) } - - viewModel.getStreamBundle(args.browseUrl) - updateController(null) } private fun updateController(streamCluster: StreamCluster?) { diff --git a/app/src/main/java/com/aurora/store/view/ui/details/AppDetailsFragment.kt b/app/src/main/java/com/aurora/store/view/ui/details/AppDetailsFragment.kt index 53eccf6bb..230286b19 100644 --- a/app/src/main/java/com/aurora/store/view/ui/details/AppDetailsFragment.kt +++ b/app/src/main/java/com/aurora/store/view/ui/details/AppDetailsFragment.kt @@ -55,8 +55,8 @@ import com.aurora.gplayapi.data.models.App import com.aurora.gplayapi.data.models.Review import com.aurora.gplayapi.data.models.StreamBundle import com.aurora.gplayapi.data.models.StreamCluster -import com.aurora.store.AuroraApp import com.aurora.store.AppStreamStash +import com.aurora.store.AuroraApp import com.aurora.store.R import com.aurora.store.data.event.BusEvent import com.aurora.store.data.event.Event @@ -135,7 +135,7 @@ class AppDetailsFragment : BaseFragment() { private fun onEvent(event: Event) { when (event) { - is BusEvent.InstallEvent -> { + is InstallerEvent.Installed -> { if (app.packageName == event.packageName) { attachActions() binding.layoutDetailsToolbar.toolbar.menu.apply { @@ -147,7 +147,7 @@ class AppDetailsFragment : BaseFragment() { } } - is BusEvent.UninstallEvent -> { + is InstallerEvent.Uninstalled -> { if (app.packageName == event.packageName) { attachActions() binding.layoutDetailsToolbar.toolbar.menu.apply { @@ -166,14 +166,23 @@ class AppDetailsFragment : BaseFragment() { } is InstallerEvent.Failed -> { - findNavController().navigate( - AppDetailsFragmentDirections.actionAppDetailsFragmentToInstallErrorDialogSheet( - app, - event.packageName ?: "", - event.error ?: "", - event.extra ?: "" + if (app.packageName == event.packageName) { + findNavController().navigate( + AppDetailsFragmentDirections.actionAppDetailsFragmentToInstallErrorDialogSheet( + app, + event.packageName ?: "", + event.error ?: "", + event.extra ?: "" + ) ) - ) + } + } + + is InstallerEvent.Installing -> { + if (event.packageName == app.packageName) { + attachActions() + updateActionState(State.INSTALLING) + } } else -> { @@ -356,10 +365,10 @@ class AppDetailsFragment : BaseFragment() { } viewLifecycleOwner.lifecycleScope.launch { - AuroraApp.flowEvent.busEvent.collect { onEvent(it) } + AuroraApp.events.busEvent.collect { onEvent(it) } } viewLifecycleOwner.lifecycleScope.launch { - AuroraApp.flowEvent.installerEvent.collect { onEvent(it) } + AuroraApp.events.installerEvent.collect { onEvent(it) } } } @@ -507,7 +516,15 @@ class AppDetailsFragment : BaseFragment() { } 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() { @@ -589,9 +606,8 @@ class AppDetailsFragment : BaseFragment() { app.isInstalled = PackageUtil.isInstalled(requireContext(), app.packageName) runOnUiThread { - app.isInstalled = PackageUtil.isInstalled(requireContext(), app.packageName) - binding.layoutDetailsInstall.btnDownload.let { btn -> + btn.setButtonState(true) if (app.isInstalled) { isUpdatable = PackageUtil.isUpdatable( requireContext(), diff --git a/app/src/main/java/com/aurora/store/view/ui/games/GamesContainerFragment.kt b/app/src/main/java/com/aurora/store/view/ui/games/GamesContainerFragment.kt index 0e574f1cb..cc5fa5ee3 100644 --- a/app/src/main/java/com/aurora/store/view/ui/games/GamesContainerFragment.kt +++ b/app/src/main/java/com/aurora/store/view/ui/games/GamesContainerFragment.kt @@ -33,7 +33,6 @@ import com.aurora.store.databinding.FragmentAppsGamesBinding import com.aurora.store.util.Preferences import com.aurora.store.view.ui.commons.BaseFragment 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.TopChartContainerFragment import com.google.android.material.tabs.TabLayout @@ -91,10 +90,6 @@ class GamesContainerFragment : BaseFragment() { add(getString(R.string.tab_top_charts)) add(getString(R.string.tab_categories)) - - if (!authProvider.isAnonymous) { - add(getString(R.string.tab_editor_choice)) - } } TabLayoutMediator( @@ -129,10 +124,6 @@ class GamesContainerFragment : BaseFragment() { add(TopChartContainerFragment.newInstance(1)) add(CategoryFragment.newInstance(1)) - - if (isGoogleAccount) { - add(EditorChoiceFragment.newInstance(1)) - } } override fun createFragment(position: Int): Fragment { diff --git a/app/src/main/java/com/aurora/store/view/ui/sale/AppSalesFragment.kt b/app/src/main/java/com/aurora/store/view/ui/sale/AppSalesFragment.kt deleted file mode 100644 index dc03a3316..000000000 --- a/app/src/main/java/com/aurora/store/view/ui/sale/AppSalesFragment.kt +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Aurora Store - * Copyright (C) 2021, Rahul Kumar Patel - * - * 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 . - * - */ - -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() { - - 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)) - ) - } - } - } - } -} diff --git a/app/src/main/java/com/aurora/store/view/ui/sheets/AppMenuSheet.kt b/app/src/main/java/com/aurora/store/view/ui/sheets/AppMenuSheet.kt index 34f073cd8..778d856cb 100644 --- a/app/src/main/java/com/aurora/store/view/ui/sheets/AppMenuSheet.kt +++ b/app/src/main/java/com/aurora/store/view/ui/sheets/AppMenuSheet.kt @@ -94,8 +94,8 @@ class AppMenuSheet : BaseDialogSheet() { } dismissAllowingStateLoss() - AuroraApp.flowEvent.emitEvent( - BusEvent.Blacklisted(args.app.packageName, "") + AuroraApp.events.send( + BusEvent.Blacklisted(args.app.packageName) ) } diff --git a/app/src/main/java/com/aurora/store/view/ui/updates/UpdatesFragment.kt b/app/src/main/java/com/aurora/store/view/ui/updates/UpdatesFragment.kt index 9e4d2cb60..b733e260f 100644 --- a/app/src/main/java/com/aurora/store/view/ui/updates/UpdatesFragment.kt +++ b/app/src/main/java/com/aurora/store/view/ui/updates/UpdatesFragment.kt @@ -37,7 +37,8 @@ import com.aurora.gplayapi.data.models.App import com.aurora.store.AuroraApp import com.aurora.store.MobileNavigationDirections 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.databinding.FragmentUpdatesBinding import com.aurora.store.util.PackageUtil @@ -143,13 +144,13 @@ class UpdatesFragment : BaseFragment() { } 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) { - is BusEvent.InstallEvent, is BusEvent.UninstallEvent -> { + is InstallerEvent.Installed, is InstallerEvent.Uninstalled -> { viewModel.fetchUpdates() } diff --git a/app/src/main/java/com/aurora/store/viewmodel/all/LibraryAppsViewModel.kt b/app/src/main/java/com/aurora/store/viewmodel/all/LibraryAppsViewModel.kt deleted file mode 100644 index 4574e72ce..000000000 --- a/app/src/main/java/com/aurora/store/viewmodel/all/LibraryAppsViewModel.kt +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Aurora Store - * Copyright (C) 2021, Rahul Kumar Patel - * - * 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 . - * - */ - -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 = 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 - } - } -} diff --git a/app/src/main/java/com/aurora/store/viewmodel/all/PurchasedViewModel.kt b/app/src/main/java/com/aurora/store/viewmodel/all/PurchasedViewModel.kt deleted file mode 100644 index a5bb01b7a..000000000 --- a/app/src/main/java/com/aurora/store/viewmodel/all/PurchasedViewModel.kt +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Aurora Store - * Copyright (C) 2021, Rahul Kumar Patel - * - * 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 . - * - */ - -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 = 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 = mutableListOf() - - val liveData: MutableLiveData = 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) { - } - } - } - } -} diff --git a/app/src/main/java/com/aurora/store/viewmodel/auth/AuthViewModel.kt b/app/src/main/java/com/aurora/store/viewmodel/auth/AuthViewModel.kt index bee5ae0f0..123d8b4e3 100644 --- a/app/src/main/java/com/aurora/store/viewmodel/auth/AuthViewModel.kt +++ b/app/src/main/java/com/aurora/store/viewmodel/auth/AuthViewModel.kt @@ -32,7 +32,7 @@ import com.aurora.gplayapi.data.providers.DeviceInfoProvider import com.aurora.gplayapi.helpers.AuthHelper import com.aurora.store.AuroraApp 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.AuthState import com.aurora.store.data.model.InsecureAuth @@ -201,18 +201,18 @@ class AuthViewModel @Inject constructor( if (aasToken != null) { Preferences.putString(context, Constants.ACCOUNT_EMAIL_PLAIN, email) 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 { Preferences.putString(context, Constants.ACCOUNT_EMAIL_PLAIN, "") Preferences.putString(context, Constants.ACCOUNT_AAS_PLAIN, "") - AuroraApp.flowEvent.emitEvent(BusEvent.GoogleAAS(false)) + AuroraApp.events.send(AuthEvent.GoogleLogin(false, "", "")) } } else { - AuroraApp.flowEvent.emitEvent(BusEvent.GoogleAAS(false)) + AuroraApp.events.send(AuthEvent.GoogleLogin(false, "", "")) } } catch (exception: 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) } + AccountType.ANONYMOUS -> { buildAnonymousAuthData() } @@ -248,9 +249,11 @@ class AuthViewModel @Inject constructor( is UnknownHostException -> { context.getString(R.string.title_no_network) } + is ConnectException -> { context.getString(R.string.server_unreachable) } + else -> { context.getString(R.string.bad_request) } diff --git a/app/src/main/java/com/aurora/store/viewmodel/browse/StreamBrowseViewModel.kt b/app/src/main/java/com/aurora/store/viewmodel/browse/StreamBrowseViewModel.kt index 786dab18b..8ab78f08b 100644 --- a/app/src/main/java/com/aurora/store/viewmodel/browse/StreamBrowseViewModel.kt +++ b/app/src/main/java/com/aurora/store/viewmodel/browse/StreamBrowseViewModel.kt @@ -25,7 +25,8 @@ import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope 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.providers.AuthProvider import com.aurora.store.util.Log @@ -43,31 +44,16 @@ class StreamBrowseViewModel @Inject constructor( private val authProvider: AuthProvider ) : ViewModel() { - private val streamHelper: StreamHelper = StreamHelper(authProvider.authData) + private val streamHelper: StreamContract = WebStreamHelper() .using(HttpClient.getPreferredClient(context)) val liveData: MutableLiveData = MutableLiveData() - var streamCluster: StreamCluster = StreamCluster() - fun getStreamBundle(browseUrl: String) { - viewModelScope.launch(Dispatchers.IO) { - liveData.postValue(getInitialCluster(browseUrl)) - } - } + private lateinit var streamCluster: StreamCluster - private fun getInitialCluster(browseUrl: String): StreamCluster { - val browseResponse = streamHelper.getBrowseStreamResponse(browseUrl) - - 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 initCluster(cluster: StreamCluster) { + streamCluster = cluster + liveData.postValue(streamCluster) } fun nextCluster() { @@ -75,18 +61,18 @@ class StreamBrowseViewModel @Inject constructor( supervisorScope { try { if (streamCluster.hasNext()) { - val newCluster = streamHelper.getNextStreamCluster( + val nextCluster = streamHelper.nextStreamCluster( streamCluster.clusterNextPageUrl ) streamCluster.apply { - clusterAppList.addAll(newCluster.clusterAppList) - clusterNextPageUrl = newCluster.clusterNextPageUrl + clusterAppList.addAll(nextCluster.clusterAppList) + clusterNextPageUrl = nextCluster.clusterNextPageUrl } liveData.postValue(streamCluster) } else { - Log.i("End of Bundle") + Log.i("End of Cluster") } } catch (_: Exception) { } diff --git a/app/src/main/java/com/aurora/store/viewmodel/category/CategoryViewModel.kt b/app/src/main/java/com/aurora/store/viewmodel/category/CategoryViewModel.kt index bde951601..f654e2774 100644 --- a/app/src/main/java/com/aurora/store/viewmodel/category/CategoryViewModel.kt +++ b/app/src/main/java/com/aurora/store/viewmodel/category/CategoryViewModel.kt @@ -28,7 +28,6 @@ import androidx.lifecycle.viewModelScope import com.aurora.gplayapi.data.models.Category import com.aurora.gplayapi.helpers.CategoryHelper import com.aurora.gplayapi.helpers.contracts.CategoryContract -import com.aurora.gplayapi.helpers.web.WebCategoryHelper import com.aurora.store.CategoryStash import com.aurora.store.data.model.ViewState import com.aurora.store.data.network.HttpClient @@ -50,9 +49,6 @@ class CategoryViewModel @Inject constructor( private val categoryHelper: CategoryHelper = CategoryHelper(authProvider.authData) .using(HttpClient.getPreferredClient(context)) - private val webCategoryHelper: CategoryContract = WebCategoryHelper() - .using(HttpClient.getPreferredClient(context)) - private var stash: CategoryStash = mutableMapOf( Category.Type.APPLICATION to emptyList(), Category.Type.GAME to emptyList() @@ -61,11 +57,7 @@ class CategoryViewModel @Inject constructor( val liveData = MutableLiveData() private fun contract(): CategoryContract { - return if (authProvider.isAnonymous) { - webCategoryHelper - } else { - categoryHelper - } + return categoryHelper } fun getCategoryList(type: Category.Type) { @@ -74,10 +66,11 @@ class CategoryViewModel @Inject constructor( if (categories.isNotEmpty()) { liveData.postValue(ViewState.Success(stash)) + return@launch } try { - stash[type] = contract().getAllCategoriesList(type) + stash[type] = contract().getAllCategories(type) liveData.postValue(ViewState.Success(stash)) } catch (exception: Exception) { Log.e(TAG, "Failed fetching list of categories", exception) diff --git a/app/src/main/java/com/aurora/store/viewmodel/editorschoice/EditorBrowseViewModel.kt b/app/src/main/java/com/aurora/store/viewmodel/editorschoice/EditorBrowseViewModel.kt deleted file mode 100644 index b408566c3..000000000 --- a/app/src/main/java/com/aurora/store/viewmodel/editorschoice/EditorBrowseViewModel.kt +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Aurora Store - * Copyright (C) 2021, Rahul Kumar Patel - * - * 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 . - * - */ - -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> = MutableLiveData() - val appList: MutableList = 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) { - } - } - } - } -} diff --git a/app/src/main/java/com/aurora/store/viewmodel/editorschoice/EditorChoiceViewModel.kt b/app/src/main/java/com/aurora/store/viewmodel/editorschoice/EditorChoiceViewModel.kt deleted file mode 100644 index 5e516ea40..000000000 --- a/app/src/main/java/com/aurora/store/viewmodel/editorschoice/EditorChoiceViewModel.kt +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Aurora Store - * Copyright (C) 2021, Rahul Kumar Patel - * - * 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 . - * - */ - -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> = 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) - } - } - } - } -} diff --git a/app/src/main/java/com/aurora/store/viewmodel/homestream/StreamViewModel.kt b/app/src/main/java/com/aurora/store/viewmodel/homestream/StreamViewModel.kt index 4b46fbb73..beaccc101 100644 --- a/app/src/main/java/com/aurora/store/viewmodel/homestream/StreamViewModel.kt +++ b/app/src/main/java/com/aurora/store/viewmodel/homestream/StreamViewModel.kt @@ -26,13 +26,11 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.aurora.gplayapi.data.models.StreamBundle 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.HomeStash import com.aurora.store.data.model.ViewState import com.aurora.store.data.network.HttpClient -import com.aurora.store.data.providers.AuthProvider import com.aurora.store.util.Log import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.qualifiers.ApplicationContext @@ -44,14 +42,10 @@ import javax.inject.Inject @HiltViewModel @SuppressLint("StaticFieldLeak") // false positive, see https://github.com/google/dagger/issues/3253 class StreamViewModel @Inject constructor( - @ApplicationContext private val context: Context, - private val authProvider: AuthProvider + @ApplicationContext private val context: Context ) : ViewModel() { - private var streamHelper: StreamContract = StreamHelper(authProvider.authData) - .using(HttpClient.getPreferredClient(context)) - - private var webStreamHelper: StreamContract = WebStreamHelper() + private var webStreamHelper = WebStreamHelper() .using(HttpClient.getPreferredClient(context)) val liveData: MutableLiveData = MutableLiveData() @@ -59,11 +53,7 @@ class StreamViewModel @Inject constructor( private var stash: HomeStash = mutableMapOf() fun contract(): StreamContract { - return if (authProvider.isAnonymous) { - webStreamHelper - } else { - streamHelper - } + return webStreamHelper } fun getStreamBundle(category: StreamContract.Category, type: StreamContract.Type) { @@ -75,7 +65,7 @@ class StreamViewModel @Inject constructor( viewModelScope.launch(Dispatchers.IO) { supervisorScope { val bundle = targetBundle(category) - if (bundle.streamClusters.isNotEmpty()) { + if (bundle.hasCluster()) { liveData.postValue(ViewState.Success(stash)) } @@ -83,13 +73,13 @@ class StreamViewModel @Inject constructor( if (!bundle.hasCluster() || bundle.hasNext()) { //Fetch new stream bundle - val newBundle = if (bundle.streamClusters.isEmpty()) { - contract().fetch(type, category) - } else { + val newBundle = if (bundle.hasCluster()) { contract().nextStreamBundle( category, bundle.streamNextPageUrl ) + } else { + contract().fetch(type, category) } //Update old bundle @@ -142,7 +132,7 @@ class StreamViewModel @Inject constructor( } private fun targetBundle(category: StreamContract.Category): StreamBundle { - val streamBundle = stash.getOrPut(category){ + val streamBundle = stash.getOrPut(category) { StreamBundle() } diff --git a/app/src/main/java/com/aurora/store/viewmodel/sale/AppSalesViewModel.kt b/app/src/main/java/com/aurora/store/viewmodel/sale/AppSalesViewModel.kt deleted file mode 100644 index 9d4ca4db3..000000000 --- a/app/src/main/java/com/aurora/store/viewmodel/sale/AppSalesViewModel.kt +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Aurora Store - * Copyright (C) 2021, Rahul Kumar Patel - * - * 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 . - * - */ - -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 = mutableListOf() - - val liveData: MutableLiveData = 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 { - return try { - appSalesHelper.getAppsOnSale(page = page++, offer = 100) - } catch (e: Exception) { - e.printStackTrace() - emptyList() - } - } -} diff --git a/app/src/main/java/com/aurora/store/viewmodel/sheets/SheetsViewModel.kt b/app/src/main/java/com/aurora/store/viewmodel/sheets/SheetsViewModel.kt index bde111b3f..0005eb08b 100644 --- a/app/src/main/java/com/aurora/store/viewmodel/sheets/SheetsViewModel.kt +++ b/app/src/main/java/com/aurora/store/viewmodel/sheets/SheetsViewModel.kt @@ -35,7 +35,7 @@ class SheetsViewModel @Inject constructor( val purchaseHelper = PurchaseHelper(authProvider.authData) val files = purchaseHelper.purchase(app.packageName, customVersion, app.offerType) if (files.isNotEmpty()) { - AuroraApp.flowEvent.emitEvent( + AuroraApp.events.send( BusEvent.ManualDownload(app.packageName, customVersion) ) } diff --git a/app/src/main/java/com/aurora/store/viewmodel/subcategory/SubCategoryClusterViewModel.kt b/app/src/main/java/com/aurora/store/viewmodel/subcategory/CategoryStreamViewModel.kt similarity index 82% rename from app/src/main/java/com/aurora/store/viewmodel/subcategory/SubCategoryClusterViewModel.kt rename to app/src/main/java/com/aurora/store/viewmodel/subcategory/CategoryStreamViewModel.kt index 6bca2979e..03d9c9275 100644 --- a/app/src/main/java/com/aurora/store/viewmodel/subcategory/SubCategoryClusterViewModel.kt +++ b/app/src/main/java/com/aurora/store/viewmodel/subcategory/CategoryStreamViewModel.kt @@ -26,8 +26,9 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.aurora.gplayapi.data.models.StreamBundle 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.web.WebStreamHelper +import com.aurora.gplayapi.helpers.web.WebCategoryStreamHelper import com.aurora.store.data.model.ViewState import com.aurora.store.data.network.HttpClient import com.aurora.store.util.Log @@ -40,25 +41,24 @@ import javax.inject.Inject @HiltViewModel @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 ) : ViewModel() { - var contract: StreamContract = WebStreamHelper() + private var webCategoryStreamHelper = WebCategoryStreamHelper() .using(HttpClient.getPreferredClient(context)) val liveData: MutableLiveData = MutableLiveData() private var stash: MutableMap = mutableMapOf() - private fun getCategoryStreamBundle( - category: StreamContract.Category, - nextPageUrl: String - ): StreamBundle { - return if (targetBundle(category).streamClusters.isEmpty()) - contract.fetch(StreamContract.Type.HOME, category) - else - contract.nextStreamBundle(category, nextPageUrl) + fun contract(): CategoryStreamContract { + return webCategoryStreamHelper + } + + fun getStreamBundle(category: StreamContract.Category) { + liveData.postValue(ViewState.Loading) + observe(category) } fun observe(category: StreamContract.Category) { @@ -66,18 +66,22 @@ class SubCategoryClusterViewModel @Inject constructor( viewModelScope.launch(Dispatchers.IO) { supervisorScope { val bundle = targetBundle(category) - if (bundle.streamClusters.isNotEmpty()) { liveData.postValue(ViewState.Success(stash)) } try { if (!bundle.hasCluster() || bundle.hasNext()) { + //Fetch new stream bundle - val newBundle = getCategoryStreamBundle( - category, - bundle.streamNextPageUrl - ) + val newBundle = if (bundle.streamClusters.isEmpty()) { + contract().fetch(category.value) + } else { + contract().nextStreamBundle( + category, + bundle.streamNextPageUrl + ) + } //Update old bundle bundle.apply { @@ -103,7 +107,7 @@ class SubCategoryClusterViewModel @Inject constructor( try { if (streamCluster.hasNext()) { val newCluster = - contract.nextStreamCluster(streamCluster.clusterNextPageUrl) + contract().nextStreamCluster(streamCluster.clusterNextPageUrl) updateCluster(category, streamCluster.id, newCluster) liveData.postValue(ViewState.Success(stash)) } else { diff --git a/app/src/main/java/com/aurora/store/viewmodel/topchart/TopChartViewModel.kt b/app/src/main/java/com/aurora/store/viewmodel/topchart/TopChartViewModel.kt index 838e08bbd..96a8b7e93 100644 --- a/app/src/main/java/com/aurora/store/viewmodel/topchart/TopChartViewModel.kt +++ b/app/src/main/java/com/aurora/store/viewmodel/topchart/TopChartViewModel.kt @@ -25,13 +25,11 @@ import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope 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.web.WebTopChartsHelper import com.aurora.store.TopChartStash import com.aurora.store.data.model.ViewState 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 @@ -41,13 +39,8 @@ import javax.inject.Inject @HiltViewModel @SuppressLint("StaticFieldLeak") // false positive, see https://github.com/google/dagger/issues/3253 -class TopChartViewModel @Inject constructor( - @ApplicationContext private val context: Context, - private val authProvider: AuthProvider -) : ViewModel() { - - private val topChartsHelper: TopChartsHelper = TopChartsHelper(authProvider.authData) - .using(HttpClient.getPreferredClient(context)) +class TopChartViewModel @Inject constructor(@ApplicationContext private val context: Context) : + ViewModel() { private val webTopChartsHelper: TopChartsContract = WebTopChartsHelper() .using(HttpClient.getPreferredClient(context)) @@ -57,11 +50,7 @@ class TopChartViewModel @Inject constructor( val liveData: MutableLiveData = MutableLiveData() private fun contract(): TopChartsContract { - return if (authProvider.isAnonymous) { - webTopChartsHelper - } else { - topChartsHelper - } + return webTopChartsHelper } fun getStreamCluster(type: TopChartsContract.Type, chart: TopChartsContract.Chart) { @@ -85,7 +74,7 @@ class TopChartViewModel @Inject constructor( try { val target = targetCluster(type, chart) if (target.hasNext()) { - val newCluster = topChartsHelper.getNextStreamCluster( + val newCluster = contract().getNextStreamCluster( target.clusterNextPageUrl ) diff --git a/app/src/main/res/layout/view_app_update.xml b/app/src/main/res/layout/view_app_update.xml index c7807e9ad..57f3ed6ec 100644 --- a/app/src/main/res/layout/view_app_update.xml +++ b/app/src/main/res/layout/view_app_update.xml @@ -52,15 +52,12 @@ diff --git a/app/src/main/res/navigation/mobile_navigation.xml b/app/src/main/res/navigation/mobile_navigation.xml index e3621f9fa..6b0386396 100644 --- a/app/src/main/res/navigation/mobile_navigation.xml +++ b/app/src/main/res/navigation/mobile_navigation.xml @@ -29,9 +29,6 @@ - @@ -78,11 +75,6 @@ android:name="com.aurora.store.view.ui.all.AppsGamesFragment" android:label="@string/title_apps_games" tools:layout="@layout/fragment_generic_with_pager" /> - - - - - - + android:name="cluster" + app:argType="com.aurora.gplayapi.data.models.StreamCluster" /> "Search" "Share" "Uninstall" + "Successfully uninstalled" Add to Home screen "Do you want to uninstall this app ?" "App Info" diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 7bf11c331..74c6ba833 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -7,7 +7,7 @@ composeBom = "2024.06.00" coreVersion = "1.13.1" epoxyVersion = "5.1.4" espressoVersion = "3.6.1" -gplayapiVersion = "3.3.0" +gplayapiVersion = "3.3.1" gsonVersion = "2.10.1" hiddenapibypassVersion = "4.3" hiltVersion = "2.51.1"