diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml index 17be98b..4131f4e 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/main.yaml @@ -51,7 +51,7 @@ jobs: echo "MELONDS_KEY_ALIAS=melonds" >> local.properties echo "MELONDS_KEY_PASSWORD=$MELONDS_KEYSTORE_PASSWORD" >> local.properties chmod +x ./gradlew - ./gradlew :app:assembleGitHubRelease + ./gradlew :app:assembleGitHubNightlyRelease git tag -m "Nightly Release" -f -a nightly-release git push -f origin refs/tags/nightly-release @@ -64,12 +64,12 @@ jobs: token: ${{ secrets.GITHUB_TOKEN }} tag: nightly-release name: 'Nightly Build' - body: 'The currently Nightly Build. Whenever new changes are pushed, you can find the latest build here.' - artifacts: app/build/outputs/apk/gitHub/release/app-gitHub-release.apk + body: 'The currently Nightly Build. Whenever new changes are pushed, you can find the latest build here. You can keep this installation alongside your main one.' + artifacts: app/build/outputs/apk/gitHubNightly/release/app-gitHub-nightly-release.apk artifactContentType: application/vnd.android.package-archive - name: Upload Artifact uses: actions/upload-artifact@v3 with: name: melonDS-android - path: app/build/outputs/apk/gitHub/release/app-gitHub-release.apk + path: app/build/outputs/apk/gitHubNightly/release/app-gitHub-nightly-release.apk diff --git a/.github/workflows/release-playstore.yaml b/.github/workflows/release-playstore.yaml index b110768..9c2e205 100644 --- a/.github/workflows/release-playstore.yaml +++ b/.github/workflows/release-playstore.yaml @@ -35,7 +35,7 @@ jobs: echo "MELONDS_KEY_ALIAS=melonds-playstore" >> local.properties echo "MELONDS_KEY_PASSWORD=$MELONDS_KEY_PASSWORD" >> local.properties chmod +x ./gradlew - ./gradlew :app:assemblePlayStoreRelease + ./gradlew :app:assemblePlayStoreProdRelease - name: Get Version id: release_params @@ -47,19 +47,19 @@ jobs: serviceAccountJsonPlainText: ${{ secrets.MELONDS_PLAYSTORE_ACCOUNT_JSON }} packageName: me.magnum.melonds releaseName: ${{ steps.release_params.outputs.VERSION }} - releaseFiles: app/build/outputs/apk/playStore/release/app-playStore-release.apk + releaseFiles: app/build/outputs/apk/playStoreProd/release/app-playStore-prod-release.apk track: beta inAppUpdatePriority: 2 status: draft whatsNewDirectory: ./.github/changelog/playStore - mappingFile: app/build/outputs/mapping/playStoreRelease/mapping.txt - debugSymbols: app/build/outputs/native-debug-symbols/playStoreRelease/native-debug-symbols.zip + mappingFile: app/build/outputs/mapping/playStoreProdRelease/mapping.txt + debugSymbols: app/build/outputs/native-debug-symbols/playStoreProdRelease/native-debug-symbols.zip - name: Upload APK, Mapping and Debug Symbols uses: actions/upload-artifact@v3 with: name: playstore-release path: | - app/build/outputs/apk/playStore/release/app-playStore-release.apk - app/build/outputs/mapping/playStoreRelease/mapping.txt - app/build/outputs/native-debug-symbols/playStoreRelease/native-debug-symbols.zip \ No newline at end of file + app/build/outputs/apk/playStoreProd/release/app-playStore-prod-release.apk + app/build/outputs/mapping/playStoreProdRelease/mapping.txt + app/build/outputs/native-debug-symbols/playStoreProdRelease/native-debug-symbols.zip \ No newline at end of file diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 51eb374..d82b33a 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -36,7 +36,7 @@ jobs: echo "MELONDS_KEY_ALIAS=melonds" >> local.properties echo "MELONDS_KEY_PASSWORD=$MELONDS_KEYSTORE_PASSWORD" >> local.properties chmod +x ./gradlew - ./gradlew :app:assembleGitHubRelease + ./gradlew :app:assembleGitHubProdRelease - name: Get Tag and Version id: release_params @@ -52,7 +52,7 @@ jobs: tag: ${{ steps.release_params.outputs.TAG }} name: ${{ steps.release_params.outputs.VERSION }} bodyFile: ./.github/changelog/gitHub.md - artifacts: app/build/outputs/apk/gitHub/release/app-gitHub-release.apk + artifacts: app/build/outputs/apk/gitHubProd/release/app-gitHub-prod-release.apk artifactContentType: application/vnd.android.package-archive - name: Upload APK, Mapping and Debug Symbols @@ -60,9 +60,9 @@ jobs: with: name: github-release path: | - app/build/outputs/apk/gitHub/release/app-gitHub-release.apk - app/build/outputs/mapping/gitHubRelease/mapping.txt - app/build/outputs/native-debug-symbols/gitHubRelease/native-debug-symbols.zip + app/build/outputs/apk/gitHubProd/release/app-gitHub-prod-release.apk + app/build/outputs/mapping/gitHubProdRelease/mapping.txt + app/build/outputs/native-debug-symbols/gitHubProdRelease/native-debug-symbols.zip playstore-release: runs-on: ubuntu-latest @@ -93,7 +93,7 @@ jobs: echo "MELONDS_KEY_ALIAS=melonds-playstore" >> local.properties echo "MELONDS_KEY_PASSWORD=$MELONDS_KEY_PASSWORD" >> local.properties chmod +x ./gradlew - ./gradlew :app:assemblePlayStoreRelease + ./gradlew :app:assemblePlayStoreProdRelease - name: Get Version id: release_params @@ -105,19 +105,19 @@ jobs: serviceAccountJsonPlainText: ${{ secrets.MELONDS_PLAYSTORE_ACCOUNT_JSON }} packageName: me.magnum.melonds releaseName: ${{ steps.release_params.outputs.VERSION }} - releaseFiles: app/build/outputs/apk/playStore/release/app-playStore-release.apk + releaseFiles: app/build/outputs/apk/playStoreProd/release/app-playStore-prod-release.apk track: beta inAppUpdatePriority: 2 status: draft whatsNewDirectory: ./.github/changelog/playStore - mappingFile: app/build/outputs/mapping/playStoreRelease/mapping.txt - debugSymbols: app/build/outputs/native-debug-symbols/playStoreRelease/native-debug-symbols.zip + mappingFile: app/build/outputs/mapping/playStoreProdRelease/mapping.txt + debugSymbols: app/build/outputs/native-debug-symbols/playStoreProdRelease/native-debug-symbols.zip - name: Upload APK, Mapping and Debug Symbols uses: actions/upload-artifact@v3 with: name: playstore-release path: | - app/build/outputs/apk/playStore/release/app-playStore-release.apk - app/build/outputs/mapping/playStoreRelease/mapping.txt - app/build/outputs/native-debug-symbols/playStoreRelease/native-debug-symbols.zip \ No newline at end of file + app/build/outputs/apk/playStoreProd/release/app-playStore-prod-release.apk + app/build/outputs/mapping/playStoreProdRelease/mapping.txt + app/build/outputs/native-debug-symbols/playStoreProdRelease/native-debug-symbols.zip \ No newline at end of file diff --git a/README.md b/README.md index bf1683c..5c41834 100644 --- a/README.md +++ b/README.md @@ -27,6 +27,12 @@ third-party frontend with the following configuration: * `uri` (preferred) - a string with the [SAF](https://developer.android.com/guide/topics/providers/create-document-provider) URI of the NDS ROM (ZIP files are supported) * `PATH` - a string with the absolute path to the NDS ROM (ZIP files are supported) +# Nightly Builds + +To have access to the latest changes, you can install nightly builds that you can find [here](https://github.com/rafaelvcaetano/melonDS-android/releases/tag/nightly-release). + +Be aware that these builds can contain more bugs than usual and you may need to clear your app data to get it to work properly. + # Building To build the project you will need Android SDK, NDK and CMake. @@ -36,9 +42,9 @@ To build the project you will need Android SDK, NDK and CMake. `git clone --recurse-submodules https://github.com/rafaelvcaetano/melonDS-android.git` 2. Install the Android SDK, NDK and CMake 3. Build with: - 1. Unix: `./gradlew :app:assembleGitHubDebug` - 2. Windows: `gradlew.bat :app:assembleGitHubDebug` -4. The generated APK can be found at `app/gitHub/debug` + 1. Unix: `./gradlew :app:assembleGitHubProdDebug` + 2. Windows: `gradlew.bat :app:assembleGitHubProdDebug` +4. The generated APK can be found at `app/gitHubProd/debug` If you want to create a release build, you will need to modify your `local.properties` file to include the following fields: * `MELONDS_KEYSTORE=` diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 6637177..65efbf6 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -63,6 +63,7 @@ android { } flavorDimensions.add("version") + flavorDimensions.add("build") productFlavors { create("playStore") { dimension = "version" @@ -70,8 +71,19 @@ android { } create("gitHub") { dimension = "version" + isDefault = true versionNameSuffix = " GH" } + + create("prod") { + dimension = "build" + isDefault = true + } + create("nightly") { + dimension = "build" + applicationIdSuffix = ".nightly" + versionNameSuffix = " (NIGHTLY)" + } } externalNativeBuild { cmake { diff --git a/app/src/gitHub/java/me/magnum/melonds/di/GitHubModule.kt b/app/src/gitHub/java/me/magnum/melonds/di/GitHubModule.kt index 69d9d23..3857df6 100644 --- a/app/src/gitHub/java/me/magnum/melonds/di/GitHubModule.kt +++ b/app/src/gitHub/java/me/magnum/melonds/di/GitHubModule.kt @@ -7,10 +7,8 @@ import dagger.Provides import dagger.hilt.InstallIn import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.components.SingletonComponent -import me.magnum.melonds.domain.repositories.UpdatesRepository import me.magnum.melonds.domain.services.UpdateInstallManager import me.magnum.melonds.github.GitHubApi -import me.magnum.melonds.github.repositories.GitHubUpdatesRepository import me.magnum.melonds.github.services.GitHubUpdateInstallManager import retrofit2.Retrofit import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory @@ -32,13 +30,6 @@ object GitHubModule { return retrofit.create(GitHubApi::class.java) } - @Provides - @Singleton - fun provideUpdatesRepository(@ApplicationContext context: Context, gitHubApi: GitHubApi): UpdatesRepository { - val gitHubPreferences = context.getSharedPreferences("preferences-github", Context.MODE_PRIVATE) - return GitHubUpdatesRepository(context, gitHubApi, gitHubPreferences) - } - @Provides @Singleton fun provideUpdateInstallManager(@ApplicationContext context: Context): UpdateInstallManager { diff --git a/app/src/gitHub/java/me/magnum/melonds/github/GitHubApi.kt b/app/src/gitHub/java/me/magnum/melonds/github/GitHubApi.kt index d1ee78e..a42cba7 100644 --- a/app/src/gitHub/java/me/magnum/melonds/github/GitHubApi.kt +++ b/app/src/gitHub/java/me/magnum/melonds/github/GitHubApi.kt @@ -7,4 +7,7 @@ import retrofit2.http.GET interface GitHubApi { @GET("/repos/rafaelvcaetano/melonDS-android/releases/latest") fun getLatestRelease(): Single + + @GET("/repos/rafaelvcaetano/melonDS-android/releases/tags/nightly-release") + fun getLatestNightlyRelease(): Single } \ No newline at end of file diff --git a/app/src/gitHub/java/me/magnum/melonds/github/GitHubConstants.kt b/app/src/gitHub/java/me/magnum/melonds/github/GitHubConstants.kt new file mode 100644 index 0000000..12e9a50 --- /dev/null +++ b/app/src/gitHub/java/me/magnum/melonds/github/GitHubConstants.kt @@ -0,0 +1,5 @@ +package me.magnum.melonds.github + +const val APK_CONTENT_TYPE = "application/vnd.android.package-archive" + +const val PREF_KEY_GITHUB_CHECK_FOR_UPDATES = "github_check_for_updates" \ No newline at end of file diff --git a/app/src/gitHub/java/me/magnum/melonds/github/dtos/ReleaseDto.kt b/app/src/gitHub/java/me/magnum/melonds/github/dtos/ReleaseDto.kt index 1615694..fa54dff 100644 --- a/app/src/gitHub/java/me/magnum/melonds/github/dtos/ReleaseDto.kt +++ b/app/src/gitHub/java/me/magnum/melonds/github/dtos/ReleaseDto.kt @@ -6,5 +6,6 @@ data class ReleaseDto( @SerializedName("tag_name") val tagName: String, @SerializedName("name") val name: String, @SerializedName("body") val body: String, + @SerializedName("created_at") val createdAt: String, @SerializedName("assets") val assets: List ) \ No newline at end of file diff --git a/app/src/gitHub/java/me/magnum/melonds/github/services/GitHubUpdateInstallManager.kt b/app/src/gitHub/java/me/magnum/melonds/github/services/GitHubUpdateInstallManager.kt index e09da94..c7da016 100644 --- a/app/src/gitHub/java/me/magnum/melonds/github/services/GitHubUpdateInstallManager.kt +++ b/app/src/gitHub/java/me/magnum/melonds/github/services/GitHubUpdateInstallManager.kt @@ -10,7 +10,7 @@ import android.net.Uri import androidx.core.content.getSystemService import io.reactivex.Observable import me.magnum.melonds.common.providers.UpdateContentProvider -import me.magnum.melonds.domain.model.AppUpdate +import me.magnum.melonds.domain.model.appupdate.AppUpdate import me.magnum.melonds.domain.model.DownloadProgress import me.magnum.melonds.domain.services.UpdateInstallManager import java.io.File diff --git a/app/src/gitHub/res/xml/pref_general_updates.xml b/app/src/gitHub/res/xml/pref_general_updates.xml new file mode 100644 index 0000000..00cc464 --- /dev/null +++ b/app/src/gitHub/res/xml/pref_general_updates.xml @@ -0,0 +1,12 @@ + + + + + \ No newline at end of file diff --git a/app/src/gitHubNightly/java/me/magnum/melonds/di/GitHubNightlyModule.kt b/app/src/gitHubNightly/java/me/magnum/melonds/di/GitHubNightlyModule.kt new file mode 100644 index 0000000..2201dc8 --- /dev/null +++ b/app/src/gitHubNightly/java/me/magnum/melonds/di/GitHubNightlyModule.kt @@ -0,0 +1,24 @@ +package me.magnum.melonds.di + +import android.content.Context +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.android.qualifiers.ApplicationContext +import dagger.hilt.components.SingletonComponent +import me.magnum.melonds.domain.repositories.UpdatesRepository +import me.magnum.melonds.github.GitHubApi +import me.magnum.melonds.github.repositories.GitHubNightlyUpdatesRepository +import javax.inject.Singleton + +@Module +@InstallIn(SingletonComponent::class) +object GitHubNightlyModule { + + @Provides + @Singleton + fun provideUpdatesRepository(@ApplicationContext context: Context, gitHubApi: GitHubApi): UpdatesRepository { + val gitHubPreferences = context.getSharedPreferences("preferences-github", Context.MODE_PRIVATE) + return GitHubNightlyUpdatesRepository(gitHubApi, gitHubPreferences) + } +} \ No newline at end of file diff --git a/app/src/gitHubNightly/java/me/magnum/melonds/github/repositories/GitHubNightlyUpdatesRepository.kt b/app/src/gitHubNightly/java/me/magnum/melonds/github/repositories/GitHubNightlyUpdatesRepository.kt new file mode 100644 index 0000000..ebaa43a --- /dev/null +++ b/app/src/gitHubNightly/java/me/magnum/melonds/github/repositories/GitHubNightlyUpdatesRepository.kt @@ -0,0 +1,115 @@ +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.domain.model.Version +import me.magnum.melonds.domain.model.appupdate.AppUpdate +import me.magnum.melonds.domain.repositories.UpdatesRepository +import me.magnum.melonds.github.APK_CONTENT_TYPE +import me.magnum.melonds.github.GitHubApi +import me.magnum.melonds.github.PREF_KEY_GITHUB_CHECK_FOR_UPDATES +import me.magnum.melonds.github.dtos.ReleaseDto +import java.time.Duration +import java.time.Instant + +class GitHubNightlyUpdatesRepository(private val api: GitHubApi, private val preferences: SharedPreferences) : UpdatesRepository { + companion object { + private const val KEY_NEXT_CHECK_DATE = "github_updates_nightly_next_check_date" + private const val KEY_LAST_RELEASE_DATE = "github_updates_nightly_last_release_date" + } + + override fun checkNewUpdate(): Maybe { + 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() + } + } + } else { + Maybe.empty() + } + } + } + + override fun skipUpdate(update: AppUpdate) { + scheduleNextUpdate() + } + + override fun notifyUpdateDownloaded(update: AppUpdate) { + // This doesn't mean that the user has actually installed the update, but we have no way to determine that. As such, just assume that the update will be installed and + // store the date of the update + preferences.edit { + putLong(KEY_LAST_RELEASE_DATE, update.updateDate.toEpochMilli()) + } + } + + private fun shouldCheckUpdates(): Single { + 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 scheduleNextUpdate() { + val now = Instant.now() + val nextUpdateDate = now + Duration.ofDays(1) + + preferences.edit { + putLong(KEY_NEXT_CHECK_DATE, nextUpdateDate.toEpochMilli()) + } + } + + private fun shouldUpdate(releaseDto: ReleaseDto): Boolean { + val lastReleaseDate = preferences.getLong(KEY_LAST_RELEASE_DATE, -1) + if (lastReleaseDate == -1L) { + // If there is no last release date, then it's the first time the user is running the app and checking for updates. Ignore this release since we can't check if + // it's actually different from the one the user has installed, and save the release date in the preferences so that we can have a future reference + + val releaseDate = Instant.parse(releaseDto.createdAt) + preferences.edit { + putLong(KEY_LAST_RELEASE_DATE, releaseDate.toEpochMilli()) + } + scheduleNextUpdate() + return false + } + + val thisReleaseDate = Instant.parse(releaseDto.createdAt) + + return thisReleaseDate.toEpochMilli() > lastReleaseDate + } +} \ No newline at end of file diff --git a/app/src/gitHubProd/java/me/magnum/melonds/di/GitHubProdModule.kt b/app/src/gitHubProd/java/me/magnum/melonds/di/GitHubProdModule.kt new file mode 100644 index 0000000..ddd2281 --- /dev/null +++ b/app/src/gitHubProd/java/me/magnum/melonds/di/GitHubProdModule.kt @@ -0,0 +1,24 @@ +package me.magnum.melonds.di + +import android.content.Context +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.android.qualifiers.ApplicationContext +import dagger.hilt.components.SingletonComponent +import me.magnum.melonds.domain.repositories.UpdatesRepository +import me.magnum.melonds.github.GitHubApi +import me.magnum.melonds.github.repositories.GitHubProdUpdatesRepository +import javax.inject.Singleton + +@Module +@InstallIn(SingletonComponent::class) +object GitHubProdModule { + + @Provides + @Singleton + fun provideUpdatesRepository(@ApplicationContext context: Context, gitHubApi: GitHubApi): UpdatesRepository { + val gitHubPreferences = context.getSharedPreferences("preferences-github", Context.MODE_PRIVATE) + return GitHubProdUpdatesRepository(context, gitHubApi, gitHubPreferences) + } +} \ No newline at end of file diff --git a/app/src/gitHub/java/me/magnum/melonds/github/repositories/GitHubUpdatesRepository.kt b/app/src/gitHubProd/java/me/magnum/melonds/github/repositories/GitHubProdUpdatesRepository.kt similarity index 86% rename from app/src/gitHub/java/me/magnum/melonds/github/repositories/GitHubUpdatesRepository.kt rename to app/src/gitHubProd/java/me/magnum/melonds/github/repositories/GitHubProdUpdatesRepository.kt index ff3446d..8230464 100644 --- a/app/src/gitHub/java/me/magnum/melonds/github/repositories/GitHubUpdatesRepository.kt +++ b/app/src/gitHubProd/java/me/magnum/melonds/github/repositories/GitHubProdUpdatesRepository.kt @@ -6,17 +6,19 @@ 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 +import me.magnum.melonds.domain.model.appupdate.AppUpdate import me.magnum.melonds.domain.model.Version import me.magnum.melonds.domain.repositories.UpdatesRepository import me.magnum.melonds.github.GitHubApi +import me.magnum.melonds.github.PREF_KEY_GITHUB_CHECK_FOR_UPDATES 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.concurrent.TimeUnit -class GitHubUpdatesRepository(private val context: Context, private val api: GitHubApi, private val preferences: SharedPreferences) : UpdatesRepository { +class GitHubProdUpdatesRepository(private val context: Context, private val api: GitHubApi, private val preferences: SharedPreferences) : UpdatesRepository { companion object { private const val APK_CONTENT_TYPE = "application/vnd.android.package-archive" private const val KEY_SKIP_VERSION = "github_updates_skip_version" @@ -35,11 +37,13 @@ class GitHubUpdatesRepository(private val context: Context, private val api: Git 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 + apkBinary.size, + Instant.parse(release.createdAt), ) Maybe.just(update) } else { @@ -61,8 +65,18 @@ class GitHubUpdatesRepository(private val context: Context, private val api: Git } } + override fun notifyUpdateDownloaded(update: AppUpdate) { + // Do nothing + } + private fun shouldCheckUpdates(): Single { 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) diff --git a/app/src/main/java/me/magnum/melonds/domain/model/AppUpdate.kt b/app/src/main/java/me/magnum/melonds/domain/model/AppUpdate.kt deleted file mode 100644 index 63fa4bd..0000000 --- a/app/src/main/java/me/magnum/melonds/domain/model/AppUpdate.kt +++ /dev/null @@ -1,11 +0,0 @@ -package me.magnum.melonds.domain.model - -import android.net.Uri - -data class AppUpdate( - val id: Long, - val downloadUri: Uri, - val newVersion: Version, - val description: String, - val binarySize: Long -) \ No newline at end of file diff --git a/app/src/main/java/me/magnum/melonds/domain/model/appupdate/AppUpdate.kt b/app/src/main/java/me/magnum/melonds/domain/model/appupdate/AppUpdate.kt new file mode 100644 index 0000000..8715fdd --- /dev/null +++ b/app/src/main/java/me/magnum/melonds/domain/model/appupdate/AppUpdate.kt @@ -0,0 +1,21 @@ +package me.magnum.melonds.domain.model.appupdate + +import android.net.Uri +import me.magnum.melonds.domain.model.Version +import java.time.Instant + +data class AppUpdate( + val type: Type, + val id: Long, + val downloadUri: Uri, + val newVersion: Version, + val description: String, + val binarySize: Long, + val updateDate: Instant, +) { + + enum class Type { + PRODUCTION, + NIGHTLY, + } +} \ No newline at end of file diff --git a/app/src/main/java/me/magnum/melonds/domain/repositories/UpdatesRepository.kt b/app/src/main/java/me/magnum/melonds/domain/repositories/UpdatesRepository.kt index 160d681..2757b24 100644 --- a/app/src/main/java/me/magnum/melonds/domain/repositories/UpdatesRepository.kt +++ b/app/src/main/java/me/magnum/melonds/domain/repositories/UpdatesRepository.kt @@ -1,10 +1,10 @@ package me.magnum.melonds.domain.repositories import io.reactivex.Maybe -import io.reactivex.Observable -import me.magnum.melonds.domain.model.AppUpdate +import me.magnum.melonds.domain.model.appupdate.AppUpdate interface UpdatesRepository { fun checkNewUpdate(): Maybe fun skipUpdate(update: AppUpdate) + fun notifyUpdateDownloaded(update: AppUpdate) } \ No newline at end of file diff --git a/app/src/main/java/me/magnum/melonds/domain/services/UpdateInstallManager.kt b/app/src/main/java/me/magnum/melonds/domain/services/UpdateInstallManager.kt index a0c3c5a..ef280d2 100644 --- a/app/src/main/java/me/magnum/melonds/domain/services/UpdateInstallManager.kt +++ b/app/src/main/java/me/magnum/melonds/domain/services/UpdateInstallManager.kt @@ -1,7 +1,7 @@ package me.magnum.melonds.domain.services import io.reactivex.Observable -import me.magnum.melonds.domain.model.AppUpdate +import me.magnum.melonds.domain.model.appupdate.AppUpdate import me.magnum.melonds.domain.model.DownloadProgress interface UpdateInstallManager { diff --git a/app/src/main/java/me/magnum/melonds/ui/romlist/RomListActivity.kt b/app/src/main/java/me/magnum/melonds/ui/romlist/RomListActivity.kt index 426e7c2..c9e7f85 100644 --- a/app/src/main/java/me/magnum/melonds/ui/romlist/RomListActivity.kt +++ b/app/src/main/java/me/magnum/melonds/ui/romlist/RomListActivity.kt @@ -27,6 +27,7 @@ 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.appupdate.AppUpdate import me.magnum.melonds.ui.dsiwaremanager.DSiWareManagerActivity import me.magnum.melonds.ui.emulator.EmulatorActivity import me.magnum.melonds.ui.settings.SettingsActivity @@ -98,7 +99,10 @@ class RomListActivity : AppCompatActivity() { } updatesViewModel.getAppUpdate().observe(this) { - showUpdateAvailableDialog(it) + when (it.type) { + AppUpdate.Type.PRODUCTION -> showProdUpdateAvailableDialog(it) + AppUpdate.Type.NIGHTLY -> showNightlyUpdateAvailableDialog(it) + } } updatesViewModel.getDownloadProgress().observe(this) { onDownloadProgressUpdated(it) @@ -199,7 +203,7 @@ class RomListActivity : AppCompatActivity() { romListFragment.setRomSelectedListener { rom -> loadRom(rom) } } - private fun showUpdateAvailableDialog(update: AppUpdate) { + private fun showProdUpdateAvailableDialog(update: AppUpdate) { val message = markwon.toMarkdown(update.description) AlertDialog.Builder(this) @@ -215,6 +219,19 @@ class RomListActivity : AppCompatActivity() { .show() } + private fun showNightlyUpdateAvailableDialog(update: AppUpdate) { + AlertDialog.Builder(this) + .setTitle(getString(R.string.nightly_update_available)) + .setMessage(getString(R.string.nightly_update_available_message)) + .setPositiveButton(R.string.update) { _, _ -> + startUpdateDownload(update) + } + .setNegativeButton(R.string.remind_later_update) { _, _ -> + updatesViewModel.skipUpdate(update) + } + .show() + } + private fun startUpdateDownload(update: AppUpdate) { downloadProgressDialog?.dismiss() downloadProgressDialog = AlertDialog.Builder(this) diff --git a/app/src/main/java/me/magnum/melonds/ui/romlist/UpdatesViewModel.kt b/app/src/main/java/me/magnum/melonds/ui/romlist/UpdatesViewModel.kt index 6f96d92..a24d72c 100644 --- a/app/src/main/java/me/magnum/melonds/ui/romlist/UpdatesViewModel.kt +++ b/app/src/main/java/me/magnum/melonds/ui/romlist/UpdatesViewModel.kt @@ -5,7 +5,7 @@ import androidx.lifecycle.ViewModel import dagger.hilt.android.lifecycle.HiltViewModel import io.reactivex.disposables.CompositeDisposable import me.magnum.melonds.common.Schedulers -import me.magnum.melonds.domain.model.AppUpdate +import me.magnum.melonds.domain.model.appupdate.AppUpdate import me.magnum.melonds.domain.model.DownloadProgress import me.magnum.melonds.domain.repositories.UpdatesRepository import me.magnum.melonds.domain.services.UpdateInstallManager @@ -47,6 +47,9 @@ class UpdatesViewModel @Inject constructor( .subscribeOn(schedulers.backgroundThreadScheduler) .subscribe { updateDownloadProgressLiveData.postValue(it) + if (it is DownloadProgress.DownloadComplete) { + updatesRepository.notifyUpdateDownloaded(update) + } } .addTo(disposables) } diff --git a/app/src/main/java/me/magnum/melonds/ui/settings/fragments/GeneralPreferencesFragment.kt b/app/src/main/java/me/magnum/melonds/ui/settings/fragments/GeneralPreferencesFragment.kt index 3871441..e1267d5 100644 --- a/app/src/main/java/me/magnum/melonds/ui/settings/fragments/GeneralPreferencesFragment.kt +++ b/app/src/main/java/me/magnum/melonds/ui/settings/fragments/GeneralPreferencesFragment.kt @@ -24,6 +24,7 @@ class GeneralPreferencesFragment : PreferenceFragmentCompat(), PreferenceFragmen override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { setPreferencesFromResource(R.xml.pref_general, rootKey) + addPreferencesFromResource(R.xml.pref_general_updates) rewindPreference = findPreference("enable_rewind")!! val sustainedPerformancePreference = findPreference("enable_sustained_performance")!! diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 784c936..0ba1dd9 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -49,7 +49,10 @@ Refresh ROM list Search ROMs… Update available: %1$s + Nightly update available + Do you want to download it now? Skip + Later Downloading update… Starting download… %1$.1fMB/%2$.1fMB @@ -148,6 +151,8 @@ Memory usage above recommended limit Sustained performance mode When enabled, peak performance will be lower but thermal throttling will be less pronounced. + Check for updates + When enabled, the app will periodically check for available updates Icon filtering Max. ROM cache size Clear extracted ROM cache diff --git a/app/src/main/res/xml/pref_general_updates.xml b/app/src/main/res/xml/pref_general_updates.xml new file mode 100644 index 0000000..26928b9 --- /dev/null +++ b/app/src/main/res/xml/pref_general_updates.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/app/src/nightly/res/mipmap-hdpi/ic_launcher.png b/app/src/nightly/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000..0373e18 Binary files /dev/null and b/app/src/nightly/res/mipmap-hdpi/ic_launcher.png differ diff --git a/app/src/nightly/res/mipmap-hdpi/ic_launcher_foreground.png b/app/src/nightly/res/mipmap-hdpi/ic_launcher_foreground.png new file mode 100644 index 0000000..d9bb085 Binary files /dev/null and b/app/src/nightly/res/mipmap-hdpi/ic_launcher_foreground.png differ diff --git a/app/src/nightly/res/mipmap-mdpi/ic_launcher.png b/app/src/nightly/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000..8705b47 Binary files /dev/null and b/app/src/nightly/res/mipmap-mdpi/ic_launcher.png differ diff --git a/app/src/nightly/res/mipmap-mdpi/ic_launcher_foreground.png b/app/src/nightly/res/mipmap-mdpi/ic_launcher_foreground.png new file mode 100644 index 0000000..7bb6241 Binary files /dev/null and b/app/src/nightly/res/mipmap-mdpi/ic_launcher_foreground.png differ diff --git a/app/src/nightly/res/mipmap-xhdpi/ic_launcher.png b/app/src/nightly/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000..444ba56 Binary files /dev/null and b/app/src/nightly/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/app/src/nightly/res/mipmap-xhdpi/ic_launcher_foreground.png b/app/src/nightly/res/mipmap-xhdpi/ic_launcher_foreground.png new file mode 100644 index 0000000..6b014b8 Binary files /dev/null and b/app/src/nightly/res/mipmap-xhdpi/ic_launcher_foreground.png differ diff --git a/app/src/nightly/res/mipmap-xxhdpi/ic_launcher.png b/app/src/nightly/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000..e0616af Binary files /dev/null and b/app/src/nightly/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/app/src/nightly/res/mipmap-xxhdpi/ic_launcher_foreground.png b/app/src/nightly/res/mipmap-xxhdpi/ic_launcher_foreground.png new file mode 100644 index 0000000..b1caf7a Binary files /dev/null and b/app/src/nightly/res/mipmap-xxhdpi/ic_launcher_foreground.png differ diff --git a/app/src/nightly/res/mipmap-xxxhdpi/ic_launcher.png b/app/src/nightly/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000..80bbc57 Binary files /dev/null and b/app/src/nightly/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/app/src/nightly/res/mipmap-xxxhdpi/ic_launcher_foreground.png b/app/src/nightly/res/mipmap-xxxhdpi/ic_launcher_foreground.png new file mode 100644 index 0000000..5212003 Binary files /dev/null and b/app/src/nightly/res/mipmap-xxxhdpi/ic_launcher_foreground.png differ diff --git a/app/src/nightly/res/values/strings.xml b/app/src/nightly/res/values/strings.xml new file mode 100644 index 0000000..871b5a6 --- /dev/null +++ b/app/src/nightly/res/values/strings.xml @@ -0,0 +1,4 @@ + + + melonDS Nightly + \ No newline at end of file diff --git a/app/src/playStore/java/me/magnum/melonds/playstore/PlayStoreUpdatesRepository.kt b/app/src/playStore/java/me/magnum/melonds/playstore/PlayStoreUpdatesRepository.kt index cf37aad..20436c0 100644 --- a/app/src/playStore/java/me/magnum/melonds/playstore/PlayStoreUpdatesRepository.kt +++ b/app/src/playStore/java/me/magnum/melonds/playstore/PlayStoreUpdatesRepository.kt @@ -1,7 +1,7 @@ package me.magnum.melonds.playstore import io.reactivex.Maybe -import me.magnum.melonds.domain.model.AppUpdate +import me.magnum.melonds.domain.model.appupdate.AppUpdate import me.magnum.melonds.domain.repositories.UpdatesRepository class PlayStoreUpdatesRepository : UpdatesRepository { @@ -12,4 +12,8 @@ class PlayStoreUpdatesRepository : UpdatesRepository { override fun skipUpdate(update: AppUpdate) { // Do nothing. Update checking not supported in the Play Store version } + + override fun notifyUpdateDownloaded(update: AppUpdate) { + // Do nothing + } } \ No newline at end of file diff --git a/app/src/playStore/java/me/magnum/melonds/services/PlayStoreUpdateInstallManager.kt b/app/src/playStore/java/me/magnum/melonds/services/PlayStoreUpdateInstallManager.kt index 5f73daa..f9c27fe 100644 --- a/app/src/playStore/java/me/magnum/melonds/services/PlayStoreUpdateInstallManager.kt +++ b/app/src/playStore/java/me/magnum/melonds/services/PlayStoreUpdateInstallManager.kt @@ -1,8 +1,8 @@ package me.magnum.melonds.services import io.reactivex.Observable -import me.magnum.melonds.domain.model.AppUpdate 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 {