mirror of
https://github.com/rafaelvcaetano/melonDS-android.git
synced 2024-11-26 23:20:40 +00:00
Migrate app update logic from RxJava to Coroutines
This commit is contained in:
parent
c03c02f0af
commit
4b67d6c3be
@ -1,13 +1,12 @@
|
||||
package me.magnum.melonds.github
|
||||
|
||||
import io.reactivex.Single
|
||||
import me.magnum.melonds.github.dtos.ReleaseDto
|
||||
import retrofit2.http.GET
|
||||
|
||||
interface GitHubApi {
|
||||
@GET("/repos/rafaelvcaetano/melonDS-android/releases/latest")
|
||||
fun getLatestRelease(): Single<ReleaseDto>
|
||||
suspend fun getLatestRelease(): ReleaseDto
|
||||
|
||||
@GET("/repos/rafaelvcaetano/melonDS-android/releases/tags/nightly-release")
|
||||
fun getLatestNightlyRelease(): Single<ReleaseDto>
|
||||
suspend fun getLatestNightlyRelease(): ReleaseDto
|
||||
}
|
@ -8,10 +8,13 @@ import android.content.IntentFilter
|
||||
import android.database.ContentObserver
|
||||
import android.net.Uri
|
||||
import androidx.core.content.getSystemService
|
||||
import io.reactivex.Observable
|
||||
import kotlinx.coroutines.channels.awaitClose
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.callbackFlow
|
||||
import kotlinx.coroutines.flow.flow
|
||||
import me.magnum.melonds.common.providers.UpdateContentProvider
|
||||
import me.magnum.melonds.domain.model.appupdate.AppUpdate
|
||||
import me.magnum.melonds.domain.model.DownloadProgress
|
||||
import me.magnum.melonds.domain.model.appupdate.AppUpdate
|
||||
import me.magnum.melonds.domain.services.UpdateInstallManager
|
||||
import java.io.File
|
||||
|
||||
@ -32,77 +35,77 @@ class GitHubUpdateInstallManager(private val context: Context) : UpdateInstallMa
|
||||
context.registerReceiver(downloadCompleteReceiver, IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE))
|
||||
}
|
||||
|
||||
override fun downloadAndInstallUpdate(update: AppUpdate): Observable<DownloadProgress> {
|
||||
return Observable.create { emitter ->
|
||||
val updatesFolder = context.externalCacheDir?.let { File(it, "updates") }
|
||||
if (updatesFolder == null) {
|
||||
emitter.onComplete()
|
||||
return@create
|
||||
}
|
||||
override fun downloadAndInstallUpdate(update: AppUpdate): Flow<DownloadProgress> = flow {
|
||||
val updatesFolder = context.externalCacheDir?.let { File(it, "updates") }
|
||||
if (updatesFolder == null) {
|
||||
return@flow
|
||||
}
|
||||
|
||||
if (!updatesFolder.isDirectory && !updatesFolder.mkdirs()) {
|
||||
emitter.onComplete()
|
||||
return@create
|
||||
}
|
||||
if (!updatesFolder.isDirectory && !updatesFolder.mkdirs()) {
|
||||
return@flow
|
||||
}
|
||||
|
||||
val destinationFile = File(updatesFolder, "update.apk")
|
||||
if (destinationFile.isFile) {
|
||||
destinationFile.delete()
|
||||
}
|
||||
val destinationFile = File(updatesFolder, "update.apk")
|
||||
if (destinationFile.isFile) {
|
||||
destinationFile.delete()
|
||||
}
|
||||
|
||||
val destinationUri = UpdateContentProvider.getUpdateFileUri(context, destinationFile)
|
||||
val downloadManager = context.getSystemService<DownloadManager>()!!
|
||||
val request = DownloadManager.Request(update.downloadUri).apply {
|
||||
setDestinationUri(destinationUri)
|
||||
setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED)
|
||||
setMimeType("application/vnd.android.package-archive")
|
||||
setTitle("Downloading update ${update.newVersion}...")
|
||||
}
|
||||
val downloadId = downloadManager.enqueue(request)
|
||||
pendingDownloadId = downloadId
|
||||
val destinationUri = UpdateContentProvider.getUpdateFileUri(context, destinationFile)
|
||||
val downloadManager = context.getSystemService<DownloadManager>()!!
|
||||
val request = DownloadManager.Request(update.downloadUri).apply {
|
||||
setDestinationUri(destinationUri)
|
||||
setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED)
|
||||
setMimeType("application/vnd.android.package-archive")
|
||||
setTitle("Downloading update ${update.newVersion}...")
|
||||
}
|
||||
val downloadId = downloadManager.enqueue(request)
|
||||
pendingDownloadId = downloadId
|
||||
|
||||
val downloadUri = Uri.parse("content://downloads/my_downloads/${downloadId}")
|
||||
context.contentResolver.registerContentObserver(downloadUri, false, object : ContentObserver(null) {
|
||||
init {
|
||||
emitter.setCancellable {
|
||||
context.contentResolver.unregisterContentObserver(this)
|
||||
}
|
||||
startDownload(downloadManager, downloadId).collect(this)
|
||||
}
|
||||
|
||||
private suspend fun startDownload(downloadManager: DownloadManager, downloadId: Long): Flow<DownloadProgress> = callbackFlow {
|
||||
val downloadContentObserver = object : ContentObserver(null) {
|
||||
|
||||
override fun onChange(selfChange: Boolean, uri: Uri?) {
|
||||
val query = DownloadManager.Query().apply {
|
||||
setFilterById(downloadId)
|
||||
}
|
||||
|
||||
override fun onChange(selfChange: Boolean, uri: Uri?) {
|
||||
val query = DownloadManager.Query().apply {
|
||||
setFilterById(downloadId)
|
||||
val cursor = downloadManager.query(query)
|
||||
if (cursor.moveToNext()) {
|
||||
val sizeIndex = cursor.getColumnIndex(DownloadManager.COLUMN_TOTAL_SIZE_BYTES)
|
||||
val downloadedIndex = cursor.getColumnIndex(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR)
|
||||
val statusIndex = cursor.getColumnIndex(DownloadManager.COLUMN_STATUS)
|
||||
|
||||
val size = cursor.getLong(sizeIndex)
|
||||
val downloaded = cursor.getLong(downloadedIndex)
|
||||
val status = cursor.getInt(statusIndex)
|
||||
|
||||
val isFinished = status == DownloadManager.STATUS_FAILED || status == DownloadManager.STATUS_SUCCESSFUL
|
||||
|
||||
if (size >= 0) {
|
||||
channel.trySend(DownloadProgress.DownloadUpdate(size, downloaded))
|
||||
}
|
||||
|
||||
val cursor = downloadManager.query(query)
|
||||
if (cursor.moveToNext()) {
|
||||
val sizeIndex = cursor.getColumnIndex(DownloadManager.COLUMN_TOTAL_SIZE_BYTES)
|
||||
val downloadedIndex = cursor.getColumnIndex(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR)
|
||||
val statusIndex = cursor.getColumnIndex(DownloadManager.COLUMN_STATUS)
|
||||
|
||||
val size = cursor.getLong(sizeIndex)
|
||||
val downloaded = cursor.getLong(downloadedIndex)
|
||||
val status = cursor.getInt(statusIndex)
|
||||
|
||||
val isFinished = status == DownloadManager.STATUS_FAILED || status == DownloadManager.STATUS_SUCCESSFUL
|
||||
|
||||
if (size >= 0) {
|
||||
emitter.onNext(DownloadProgress.DownloadUpdate(size, downloaded))
|
||||
if (isFinished) {
|
||||
if (status == DownloadManager.STATUS_SUCCESSFUL) {
|
||||
channel.trySend(DownloadProgress.DownloadComplete)
|
||||
} else {
|
||||
channel.trySend(DownloadProgress.DownloadFailed)
|
||||
}
|
||||
|
||||
if (isFinished) {
|
||||
if (status == DownloadManager.STATUS_SUCCESSFUL) {
|
||||
emitter.onNext(DownloadProgress.DownloadComplete)
|
||||
} else {
|
||||
emitter.onNext(DownloadProgress.DownloadFailed)
|
||||
}
|
||||
|
||||
emitter.onComplete()
|
||||
context.contentResolver.unregisterContentObserver(this)
|
||||
}
|
||||
channel.close()
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
val downloadUri = Uri.parse("content://downloads/my_downloads/${downloadId}")
|
||||
context.contentResolver.registerContentObserver(downloadUri, false, downloadContentObserver)
|
||||
|
||||
awaitClose {
|
||||
context.contentResolver.unregisterContentObserver(downloadContentObserver)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3,8 +3,8 @@ package me.magnum.melonds.github.repositories
|
||||
import android.content.SharedPreferences
|
||||
import androidx.core.content.edit
|
||||
import androidx.core.net.toUri
|
||||
import io.reactivex.Maybe
|
||||
import io.reactivex.Single
|
||||
import me.magnum.melonds.common.suspendMapCatching
|
||||
import me.magnum.melonds.common.suspendRunCatching
|
||||
import me.magnum.melonds.domain.model.Version
|
||||
import me.magnum.melonds.domain.model.appupdate.AppUpdate
|
||||
import me.magnum.melonds.domain.repositories.UpdatesRepository
|
||||
@ -21,35 +21,33 @@ class GitHubNightlyUpdatesRepository(private val api: GitHubApi, private val pre
|
||||
private const val KEY_LAST_RELEASE_DATE = "github_updates_nightly_last_release_date"
|
||||
}
|
||||
|
||||
override fun checkNewUpdate(): Maybe<AppUpdate> {
|
||||
return shouldCheckUpdates()
|
||||
.flatMapMaybe { checkUpdates ->
|
||||
if (checkUpdates) {
|
||||
api.getLatestNightlyRelease().flatMapMaybe { release ->
|
||||
if (shouldUpdate(release)) {
|
||||
val apkBinary = release.assets.firstOrNull { it.contentType == APK_CONTENT_TYPE }
|
||||
if (apkBinary != null) {
|
||||
val update = AppUpdate(
|
||||
AppUpdate.Type.NIGHTLY,
|
||||
apkBinary.id,
|
||||
apkBinary.url.toUri(),
|
||||
Version.fromString(release.tagName),
|
||||
release.body,
|
||||
apkBinary.size,
|
||||
Instant.parse(release.createdAt),
|
||||
)
|
||||
Maybe.just(update)
|
||||
} else {
|
||||
Maybe.empty()
|
||||
}
|
||||
} else {
|
||||
Maybe.empty()
|
||||
}
|
||||
}
|
||||
override suspend fun checkNewUpdate(): Result<AppUpdate?> {
|
||||
if (!shouldCheckUpdates()) {
|
||||
return Result.success(null)
|
||||
}
|
||||
|
||||
return suspendRunCatching {
|
||||
api.getLatestNightlyRelease()
|
||||
}.suspendMapCatching { release ->
|
||||
if (shouldUpdate(release)) {
|
||||
val apkBinary = release.assets.firstOrNull { it.contentType == APK_CONTENT_TYPE }
|
||||
if (apkBinary != null) {
|
||||
AppUpdate(
|
||||
AppUpdate.Type.NIGHTLY,
|
||||
apkBinary.id,
|
||||
apkBinary.url.toUri(),
|
||||
Version.fromString(release.tagName),
|
||||
release.body,
|
||||
apkBinary.size,
|
||||
Instant.parse(release.createdAt),
|
||||
)
|
||||
} else {
|
||||
Maybe.empty()
|
||||
null
|
||||
}
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun skipUpdate(update: AppUpdate) {
|
||||
@ -64,25 +62,19 @@ class GitHubNightlyUpdatesRepository(private val api: GitHubApi, private val pre
|
||||
}
|
||||
}
|
||||
|
||||
private fun shouldCheckUpdates(): Single<Boolean> {
|
||||
return Single.create { emitter ->
|
||||
val updateCheckEnabled = preferences.getBoolean(PREF_KEY_GITHUB_CHECK_FOR_UPDATES, true)
|
||||
if (!updateCheckEnabled) {
|
||||
emitter.onSuccess(false)
|
||||
return@create
|
||||
}
|
||||
|
||||
val nextUpdateCheckTime = preferences.getLong(KEY_NEXT_CHECK_DATE, -1)
|
||||
if (nextUpdateCheckTime == (-1).toLong()) {
|
||||
emitter.onSuccess(true)
|
||||
return@create
|
||||
}
|
||||
|
||||
val now = Instant.now()
|
||||
val shouldCheckUpdates = now.toEpochMilli() > nextUpdateCheckTime
|
||||
|
||||
emitter.onSuccess(shouldCheckUpdates)
|
||||
private fun shouldCheckUpdates(): Boolean {
|
||||
val updateCheckEnabled = preferences.getBoolean(PREF_KEY_GITHUB_CHECK_FOR_UPDATES, true)
|
||||
if (!updateCheckEnabled) {
|
||||
return false
|
||||
}
|
||||
|
||||
val nextUpdateCheckTime = preferences.getLong(KEY_NEXT_CHECK_DATE, -1)
|
||||
if (nextUpdateCheckTime == (-1).toLong()) {
|
||||
return true
|
||||
}
|
||||
|
||||
val now = Instant.now()
|
||||
return now.toEpochMilli() > nextUpdateCheckTime
|
||||
}
|
||||
|
||||
private fun scheduleNextUpdate() {
|
||||
|
@ -4,10 +4,10 @@ import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
import androidx.core.content.edit
|
||||
import androidx.core.net.toUri
|
||||
import io.reactivex.Maybe
|
||||
import io.reactivex.Single
|
||||
import me.magnum.melonds.domain.model.appupdate.AppUpdate
|
||||
import me.magnum.melonds.common.suspendMapCatching
|
||||
import me.magnum.melonds.common.suspendRunCatching
|
||||
import me.magnum.melonds.domain.model.Version
|
||||
import me.magnum.melonds.domain.model.appupdate.AppUpdate
|
||||
import me.magnum.melonds.domain.repositories.UpdatesRepository
|
||||
import me.magnum.melonds.github.GitHubApi
|
||||
import me.magnum.melonds.github.PREF_KEY_GITHUB_CHECK_FOR_UPDATES
|
||||
@ -15,7 +15,7 @@ import me.magnum.melonds.github.dtos.ReleaseDto
|
||||
import me.magnum.melonds.utils.PackageManagerCompat
|
||||
import me.magnum.melonds.utils.enumValueOfIgnoreCase
|
||||
import java.time.Instant
|
||||
import java.util.*
|
||||
import java.util.Calendar
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
class GitHubProdUpdatesRepository(private val context: Context, private val api: GitHubApi, private val preferences: SharedPreferences) : UpdatesRepository {
|
||||
@ -27,36 +27,35 @@ class GitHubProdUpdatesRepository(private val context: Context, private val api:
|
||||
private const val UPDATE_CHECK_DELAY_HOURS = 22
|
||||
}
|
||||
|
||||
override fun checkNewUpdate(): Maybe<AppUpdate> {
|
||||
return shouldCheckUpdates()
|
||||
.flatMapMaybe { checkUpdates ->
|
||||
if (checkUpdates) {
|
||||
api.getLatestRelease().flatMapMaybe { release ->
|
||||
updateLastUpdateCheckTime()
|
||||
if (isReleaseNewUpdate(release) && !shouldSkipUpdate(release)) {
|
||||
val apkBinary = release.assets.firstOrNull { it.contentType == APK_CONTENT_TYPE }
|
||||
if (apkBinary != null) {
|
||||
val update = AppUpdate(
|
||||
AppUpdate.Type.PRODUCTION,
|
||||
apkBinary.id,
|
||||
apkBinary.url.toUri(),
|
||||
Version.fromString(release.tagName),
|
||||
release.body,
|
||||
apkBinary.size,
|
||||
Instant.parse(release.createdAt),
|
||||
)
|
||||
Maybe.just(update)
|
||||
} else {
|
||||
Maybe.empty()
|
||||
}
|
||||
} else {
|
||||
Maybe.empty()
|
||||
}
|
||||
}
|
||||
override suspend fun checkNewUpdate(): Result<AppUpdate?> {
|
||||
if (!shouldCheckUpdates()) {
|
||||
return Result.success(null)
|
||||
}
|
||||
|
||||
return suspendRunCatching {
|
||||
api.getLatestRelease()
|
||||
}.suspendMapCatching { release ->
|
||||
updateLastUpdateCheckTime()
|
||||
|
||||
if (isReleaseNewUpdate(release) && !shouldSkipUpdate(release)) {
|
||||
val apkBinary = release.assets.firstOrNull { it.contentType == APK_CONTENT_TYPE }
|
||||
if (apkBinary != null) {
|
||||
AppUpdate(
|
||||
AppUpdate.Type.PRODUCTION,
|
||||
apkBinary.id,
|
||||
apkBinary.url.toUri(),
|
||||
Version.fromString(release.tagName),
|
||||
release.body,
|
||||
apkBinary.size,
|
||||
Instant.parse(release.createdAt),
|
||||
)
|
||||
} else {
|
||||
Maybe.empty()
|
||||
null
|
||||
}
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun skipUpdate(update: AppUpdate) {
|
||||
@ -69,28 +68,23 @@ class GitHubProdUpdatesRepository(private val context: Context, private val api:
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
private fun shouldCheckUpdates(): Single<Boolean> {
|
||||
return Single.create { emitter ->
|
||||
val updateCheckEnabled = preferences.getBoolean(PREF_KEY_GITHUB_CHECK_FOR_UPDATES, true)
|
||||
if (!updateCheckEnabled) {
|
||||
emitter.onSuccess(false)
|
||||
return@create
|
||||
}
|
||||
|
||||
val lastCheckUpdateTimestamp = preferences.getLong(KEY_LAST_UPDATE_CHECK, -1)
|
||||
if (lastCheckUpdateTimestamp == (-1).toLong()) {
|
||||
emitter.onSuccess(true)
|
||||
return@create
|
||||
}
|
||||
|
||||
val currentDate = Calendar.getInstance().time
|
||||
|
||||
val difference = currentDate.time - lastCheckUpdateTimestamp
|
||||
val hoursDifference = TimeUnit.HOURS.convert(difference, TimeUnit.MILLISECONDS)
|
||||
|
||||
val shouldCheckUpdates = hoursDifference >= UPDATE_CHECK_DELAY_HOURS
|
||||
emitter.onSuccess(shouldCheckUpdates)
|
||||
private fun shouldCheckUpdates(): Boolean {
|
||||
val updateCheckEnabled = preferences.getBoolean(PREF_KEY_GITHUB_CHECK_FOR_UPDATES, true)
|
||||
if (!updateCheckEnabled) {
|
||||
return true
|
||||
}
|
||||
|
||||
val lastCheckUpdateTimestamp = preferences.getLong(KEY_LAST_UPDATE_CHECK, -1)
|
||||
if (lastCheckUpdateTimestamp == (-1).toLong()) {
|
||||
return true
|
||||
}
|
||||
|
||||
val currentDate = Calendar.getInstance().time
|
||||
|
||||
val difference = currentDate.time - lastCheckUpdateTimestamp
|
||||
val hoursDifference = TimeUnit.HOURS.convert(difference, TimeUnit.MILLISECONDS)
|
||||
|
||||
return hoursDifference >= UPDATE_CHECK_DELAY_HOURS
|
||||
}
|
||||
|
||||
private fun updateLastUpdateCheckTime() {
|
||||
|
@ -4,10 +4,14 @@ data class Version(val type: ReleaseType, val major: Int, val minor: Int, val pa
|
||||
enum class ReleaseType {
|
||||
ALPHA,
|
||||
BETA,
|
||||
FINAL
|
||||
FINAL,
|
||||
NIGHTLY,
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
val Nightly = Version(ReleaseType.NIGHTLY, -1, -1, -1)
|
||||
|
||||
/**
|
||||
* Converts a version string in the format of `[alpha|beta-]major.minor.patch` to a [Version]. If a string with an invalid format is provided, an exception will be
|
||||
* thrown.
|
||||
@ -19,8 +23,12 @@ data class Version(val type: ReleaseType, val major: Int, val minor: Int, val pa
|
||||
Version(ReleaseType.FINAL, intParts[0], intParts[1], intParts[2])
|
||||
} else if (parts.size == 2) {
|
||||
val versionType = releaseTypeStringToValue(parts[0])
|
||||
val intParts = ensureMinimumVersionParts(parts[1].split('.').map { it.toInt() })
|
||||
Version(versionType, intParts[0], intParts[1], intParts[2])
|
||||
if (versionType == ReleaseType.NIGHTLY) {
|
||||
Nightly
|
||||
} else {
|
||||
val intParts = ensureMinimumVersionParts(parts[1].split('.').map { it.toInt() })
|
||||
Version(versionType, intParts[0], intParts[1], intParts[2])
|
||||
}
|
||||
} else {
|
||||
throw Exception("Invalid version string format")
|
||||
}
|
||||
@ -77,6 +85,7 @@ data class Version(val type: ReleaseType, val major: Int, val minor: Int, val pa
|
||||
ReleaseType.ALPHA -> "alpha"
|
||||
ReleaseType.BETA -> "beta"
|
||||
ReleaseType.FINAL -> ""
|
||||
ReleaseType.NIGHTLY -> return "nightly"
|
||||
}
|
||||
return "$typeString${if (typeString.isEmpty()) "" else "-"}$major.$minor.$patch"
|
||||
}
|
||||
|
@ -1,10 +1,9 @@
|
||||
package me.magnum.melonds.domain.repositories
|
||||
|
||||
import io.reactivex.Maybe
|
||||
import me.magnum.melonds.domain.model.appupdate.AppUpdate
|
||||
|
||||
interface UpdatesRepository {
|
||||
fun checkNewUpdate(): Maybe<AppUpdate>
|
||||
suspend fun checkNewUpdate(): Result<AppUpdate?>
|
||||
fun skipUpdate(update: AppUpdate)
|
||||
fun notifyUpdateDownloaded(update: AppUpdate)
|
||||
}
|
@ -1,9 +1,9 @@
|
||||
package me.magnum.melonds.domain.services
|
||||
|
||||
import io.reactivex.Observable
|
||||
import me.magnum.melonds.domain.model.appupdate.AppUpdate
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import me.magnum.melonds.domain.model.DownloadProgress
|
||||
import me.magnum.melonds.domain.model.appupdate.AppUpdate
|
||||
|
||||
interface UpdateInstallManager {
|
||||
fun downloadAndInstallUpdate(update: AppUpdate): Observable<DownloadProgress>
|
||||
fun downloadAndInstallUpdate(update: AppUpdate): Flow<DownloadProgress>
|
||||
}
|
@ -26,7 +26,12 @@ import me.magnum.melonds.R
|
||||
import me.magnum.melonds.common.Permission
|
||||
import me.magnum.melonds.common.contracts.DirectoryPickerContract
|
||||
import me.magnum.melonds.databinding.ActivityRomListBinding
|
||||
import me.magnum.melonds.domain.model.*
|
||||
import me.magnum.melonds.domain.model.ConfigurationDirResult
|
||||
import me.magnum.melonds.domain.model.ConsoleType
|
||||
import me.magnum.melonds.domain.model.DownloadProgress
|
||||
import me.magnum.melonds.domain.model.Rom
|
||||
import me.magnum.melonds.domain.model.SortingMode
|
||||
import me.magnum.melonds.domain.model.Version
|
||||
import me.magnum.melonds.domain.model.appupdate.AppUpdate
|
||||
import me.magnum.melonds.ui.dsiwaremanager.DSiWareManagerActivity
|
||||
import me.magnum.melonds.ui.emulator.EmulatorActivity
|
||||
@ -98,14 +103,22 @@ class RomListActivity : AppCompatActivity() {
|
||||
}
|
||||
}
|
||||
|
||||
updatesViewModel.getAppUpdate().observe(this) {
|
||||
when (it.type) {
|
||||
AppUpdate.Type.PRODUCTION -> showProdUpdateAvailableDialog(it)
|
||||
AppUpdate.Type.NIGHTLY -> showNightlyUpdateAvailableDialog(it)
|
||||
lifecycleScope.launch {
|
||||
repeatOnLifecycle(Lifecycle.State.STARTED) {
|
||||
updatesViewModel.appUpdate.collectLatest {
|
||||
when (it.type) {
|
||||
AppUpdate.Type.PRODUCTION -> showProdUpdateAvailableDialog(it)
|
||||
AppUpdate.Type.NIGHTLY -> showNightlyUpdateAvailableDialog(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
updatesViewModel.getDownloadProgress().observe(this) {
|
||||
onDownloadProgressUpdated(it)
|
||||
lifecycleScope.launch {
|
||||
repeatOnLifecycle(Lifecycle.State.STARTED) {
|
||||
updatesViewModel.updateDownloadProgressEvent.collectLatest {
|
||||
onDownloadProgressUpdated(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -282,6 +295,7 @@ class RomListActivity : AppCompatActivity() {
|
||||
Version.ReleaseType.ALPHA -> getString(R.string.version_alpha)
|
||||
Version.ReleaseType.BETA -> getString(R.string.version_beta)
|
||||
Version.ReleaseType.FINAL -> ""
|
||||
Version.ReleaseType.NIGHTLY -> return getString(R.string.version_nightly)
|
||||
}
|
||||
return "$typeString${if (typeString.isEmpty()) "" else " "}${version.major}.${version.minor}.${version.patch}"
|
||||
}
|
||||
|
@ -1,65 +1,52 @@
|
||||
package me.magnum.melonds.ui.romlist
|
||||
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import io.reactivex.disposables.CompositeDisposable
|
||||
import me.magnum.melonds.common.Schedulers
|
||||
import me.magnum.melonds.domain.model.appupdate.AppUpdate
|
||||
import kotlinx.coroutines.channels.Channel
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.flow.receiveAsFlow
|
||||
import kotlinx.coroutines.launch
|
||||
import me.magnum.melonds.domain.model.DownloadProgress
|
||||
import me.magnum.melonds.domain.model.appupdate.AppUpdate
|
||||
import me.magnum.melonds.domain.repositories.UpdatesRepository
|
||||
import me.magnum.melonds.domain.services.UpdateInstallManager
|
||||
import me.magnum.melonds.extensions.addTo
|
||||
import me.magnum.melonds.utils.SingleLiveEvent
|
||||
import javax.inject.Inject
|
||||
|
||||
@HiltViewModel
|
||||
class UpdatesViewModel @Inject constructor(
|
||||
private val updatesRepository: UpdatesRepository,
|
||||
private val updateInstallManager: UpdateInstallManager,
|
||||
private val schedulers: Schedulers
|
||||
) : ViewModel() {
|
||||
|
||||
private val disposables = CompositeDisposable()
|
||||
private val appUpdateLiveData = SingleLiveEvent<AppUpdate>()
|
||||
private val updateDownloadProgressLiveData = SingleLiveEvent<DownloadProgress>()
|
||||
private val _appUpdate = Channel<AppUpdate>(Channel.CONFLATED)
|
||||
val appUpdate = _appUpdate.receiveAsFlow()
|
||||
|
||||
private val _updateDownloadProgressEvent = Channel<DownloadProgress>(Channel.CONFLATED)
|
||||
val updateDownloadProgressEvent = _updateDownloadProgressEvent.receiveAsFlow()
|
||||
|
||||
init {
|
||||
updatesRepository.checkNewUpdate()
|
||||
.onErrorComplete()
|
||||
.subscribeOn(schedulers.backgroundThreadScheduler)
|
||||
.subscribe {
|
||||
appUpdateLiveData.postValue(it)
|
||||
viewModelScope.launch {
|
||||
updatesRepository.checkNewUpdate().map {
|
||||
if (it != null) {
|
||||
_appUpdate.send(it)
|
||||
}
|
||||
}
|
||||
.addTo(disposables)
|
||||
}
|
||||
|
||||
fun getAppUpdate(): LiveData<AppUpdate> {
|
||||
return appUpdateLiveData
|
||||
}
|
||||
|
||||
fun getDownloadProgress(): LiveData<DownloadProgress> {
|
||||
return updateDownloadProgressLiveData
|
||||
}
|
||||
}
|
||||
|
||||
fun downloadUpdate(update: AppUpdate) {
|
||||
updateInstallManager.downloadAndInstallUpdate(update)
|
||||
.subscribeOn(schedulers.backgroundThreadScheduler)
|
||||
.subscribe {
|
||||
updateDownloadProgressLiveData.postValue(it)
|
||||
viewModelScope.launch {
|
||||
updateInstallManager.downloadAndInstallUpdate(update).collectLatest {
|
||||
_updateDownloadProgressEvent.send(it)
|
||||
if (it is DownloadProgress.DownloadComplete) {
|
||||
updatesRepository.notifyUpdateDownloaded(update)
|
||||
}
|
||||
}
|
||||
.addTo(disposables)
|
||||
}
|
||||
}
|
||||
|
||||
fun skipUpdate(update: AppUpdate) {
|
||||
updatesRepository.skipUpdate(update)
|
||||
}
|
||||
|
||||
override fun onCleared() {
|
||||
super.onCleared()
|
||||
disposables.clear()
|
||||
}
|
||||
}
|
@ -21,6 +21,7 @@
|
||||
|
||||
<string name="version_alpha">Alpha</string>
|
||||
<string name="version_beta">Beta</string>
|
||||
<string name="version_nightly">Nightly</string>
|
||||
|
||||
<string name="console_ds">DS</string>
|
||||
<string name="console_dsi">DSi (Experimental)</string>
|
||||
|
@ -1,12 +1,11 @@
|
||||
package me.magnum.melonds.playstore
|
||||
|
||||
import io.reactivex.Maybe
|
||||
import me.magnum.melonds.domain.model.appupdate.AppUpdate
|
||||
import me.magnum.melonds.domain.repositories.UpdatesRepository
|
||||
|
||||
class PlayStoreUpdatesRepository : UpdatesRepository {
|
||||
override fun checkNewUpdate(): Maybe<AppUpdate> {
|
||||
return Maybe.empty()
|
||||
override suspend fun checkNewUpdate(): Result<AppUpdate?> {
|
||||
return Result.success(null)
|
||||
}
|
||||
|
||||
override fun skipUpdate(update: AppUpdate) {
|
||||
|
@ -1,12 +1,13 @@
|
||||
package me.magnum.melonds.services
|
||||
|
||||
import io.reactivex.Observable
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.emptyFlow
|
||||
import me.magnum.melonds.domain.model.DownloadProgress
|
||||
import me.magnum.melonds.domain.model.appupdate.AppUpdate
|
||||
import me.magnum.melonds.domain.services.UpdateInstallManager
|
||||
|
||||
class PlayStoreUpdateInstallManager : UpdateInstallManager {
|
||||
override fun downloadAndInstallUpdate(update: AppUpdate): Observable<DownloadProgress> {
|
||||
return Observable.error(UnsupportedOperationException("Cannot automatically update from PlayStore"))
|
||||
override fun downloadAndInstallUpdate(update: AppUpdate): Flow<DownloadProgress> {
|
||||
return emptyFlow()
|
||||
}
|
||||
}
|
@ -99,6 +99,13 @@ class VersionTest {
|
||||
assertEquals(3, version.patch)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testNightlyFromString() {
|
||||
val version = Version.fromString("nightly-release")
|
||||
|
||||
assertEquals(Version.Nightly, version)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testInvalidFromString() {
|
||||
try {
|
||||
|
Loading…
Reference in New Issue
Block a user