Skip to content

Commit

Permalink
Make nightly builds a separate app
Browse files Browse the repository at this point in the history
This allows users to have nightly builds alongside the production builds
  • Loading branch information
rafaelvcaetano committed Nov 3, 2023
1 parent 439094a commit c03c02f
Show file tree
Hide file tree
Showing 37 changed files with 313 additions and 58 deletions.
8 changes: 4 additions & 4 deletions .github/workflows/main.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
14 changes: 7 additions & 7 deletions .github/workflows/release-playstore.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
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
24 changes: 12 additions & 12 deletions .github/workflows/release.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -52,17 +52,17 @@ 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
uses: actions/upload-artifact@v3
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
Expand Down Expand Up @@ -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
Expand All @@ -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
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
12 changes: 9 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand All @@ -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=<path_to_your_keystore>`
Expand Down
12 changes: 12 additions & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -63,15 +63,27 @@ android {
}

flavorDimensions.add("version")
flavorDimensions.add("build")
productFlavors {
create("playStore") {
dimension = "version"
versionNameSuffix = " PS"
}
create("gitHub") {
dimension = "version"
isDefault = true
versionNameSuffix = " GH"
}

create("prod") {
dimension = "build"
isDefault = true
}
create("nightly") {
dimension = "build"
applicationIdSuffix = ".nightly"
versionNameSuffix = " (NIGHTLY)"
}
}
externalNativeBuild {
cmake {
Expand Down
9 changes: 0 additions & 9 deletions app/src/gitHub/java/me/magnum/melonds/di/GitHubModule.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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 {
Expand Down
3 changes: 3 additions & 0 deletions app/src/gitHub/java/me/magnum/melonds/github/GitHubApi.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,7 @@ import retrofit2.http.GET
interface GitHubApi {
@GET("/repos/rafaelvcaetano/melonDS-android/releases/latest")
fun getLatestRelease(): Single<ReleaseDto>

@GET("/repos/rafaelvcaetano/melonDS-android/releases/tags/nightly-release")
fun getLatestNightlyRelease(): Single<ReleaseDto>
}
Original file line number Diff line number Diff line change
@@ -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"
Original file line number Diff line number Diff line change
Expand Up @@ -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<AssetDto>
)
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
12 changes: 12 additions & 0 deletions app/src/gitHub/res/xml/pref_general_updates.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">

<SwitchPreference
android:key="github_check_for_updates"
android:title="@string/check_for_updates"
android:summary="@string/check_for_updates_summary"
app:iconSpaceReserved="false"
android:defaultValue="true" />
</PreferenceScreen>
Original file line number Diff line number Diff line change
@@ -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)
}
}
Original file line number Diff line number Diff line change
@@ -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<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()
}
}
} 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<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 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
}
}
Loading

0 comments on commit c03c02f

Please sign in to comment.