Merge branch 'master' into opengl_renderer
23
.github/changelog/gitHub.md
vendored
@ -1,16 +1,11 @@
|
||||
**IMPORTANT**: This update brings the changes from melonDS 0.9.5, which has incompatible saves-states with the previous version. If you rely on save-states to keep your
|
||||
progress, be aware that you will lose your progress!
|
||||
|
||||
**Changelog:**
|
||||
* Update to melonDS 0.9.5
|
||||
* Implement the DSiWare Manager. This allows you to install DSiWare titles to the NAND directly from the emulator
|
||||
* Allow DS and DSi firmwares to be launched from home screen shortcuts
|
||||
* Improve search performance when there are a lot of ROMs
|
||||
* Add option to quickly view enabled cheats
|
||||
* Fix selected ROM icon filtering not being applied until the app was restarted
|
||||
* Add monochrome icon
|
||||
* Add Bahasa Indonesia translation (thanks @NTHGiT)
|
||||
* Add French translation (thanks @SombrAbsol)
|
||||
* Add Spanish translation (thanks @BackpackXl)
|
||||
* Add Portuguese (Brazil) translation (thanks @Bardock88)
|
||||
* Add support for RetroAchievements (leaderboards are not yet supported)
|
||||
* Add support for DSi camera
|
||||
* Add proper support for Wi-Fi connectivity (huge thanks to @JesseTG from the melonDS team!)
|
||||
* Fix DS firmware not being able to boot under certain scenarios
|
||||
* Fix some crashes when JIT was enabled
|
||||
* Fix some issues on startup when save files could not be created
|
||||
* Updated French translation (thanks @SombrAbsol)
|
||||
* Updated Russian translation (thanks @6lackmag3)
|
||||
* Updated Spanish translation (thanks @BackpackXl)
|
||||
* Other minor fixes and improvements
|
17
.github/changelog/playStore/whatsnew-en-GB
vendored
@ -1,9 +1,8 @@
|
||||
<b>IMPORTANT</b>: This update brings the changes from melonDS 0.9.5, which has incompatible saves-states with the previous version. If you rely on save-states to keep your progress, be aware that you will lose it!
|
||||
|
||||
• Update to melonDS 0.9.5
|
||||
• You can now install DSiWare titles to the NAND
|
||||
• Add home shortcuts for DS and DSi firmwares
|
||||
• Improve search performance
|
||||
• Add option to quickly view enabled cheats
|
||||
• Monochrome icon
|
||||
• Add Indonesian, French, Spanish and Portuguese (Brazil) translations
|
||||
• Add support for RetroAchievements (leaderboards are not yet supported)
|
||||
• Add support for DSi camera
|
||||
• Add proper support for Wi-Fi connectivity (huge thanks to Jesse from the melonDS team!)
|
||||
• Fix DS firmware not being able to boot under certain scenarios
|
||||
• Fix some crashes when JIT was enabled
|
||||
• Fix some issues on startup when save files could not be created
|
||||
• Updated French, Russian and Spanish translations
|
||||
• Other minor fixes and improvements
|
8
.github/workflows/main.yaml
vendored
@ -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
|
||||
|
17
.github/workflows/release-playstore.yaml
vendored
@ -35,32 +35,31 @@ 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
|
||||
run: echo VERSION=$(grep -oP 'versionName = "\K(.*?)(?=")' buildSrc/src/main/kotlin/AppConfig.kt) >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Create Play Store Release
|
||||
uses: r0adkll/upload-google-play@v1.0.19
|
||||
uses: r0adkll/upload-google-play@v1.1.2
|
||||
with:
|
||||
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
|
||||
changesNotSentForReview: true
|
||||
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
|
30
.github/workflows/release.yaml
vendored
@ -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,21 +60,21 @@ 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
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- uses: actions/setup-java@v3
|
||||
with:
|
||||
java-version: '11'
|
||||
java-version: '17'
|
||||
distribution: zulu
|
||||
|
||||
- name: Setup Android SDK
|
||||
@ -93,31 +93,31 @@ 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
|
||||
run: echo VERSION=$(grep -oP 'versionName = "\K(.*?)(?=")' buildSrc/src/main/kotlin/AppConfig.kt) >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Create Play Store Release
|
||||
uses: r0adkll/upload-google-play@v1.0.19
|
||||
uses: r0adkll/upload-google-play@v1.1.2
|
||||
with:
|
||||
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
|
||||
changesNotSentForReview: true
|
||||
whatsNewDirectory: ./.github/changelog/playStore
|
||||
mappingFile: app/build/outputs/mapping/playStoreRelease/mapping.txt
|
||||
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
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=<path_to_your_keystore>`
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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>
|
||||
}
|
@ -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"
|
@ -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>
|
||||
)
|
@ -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
|
||||
|
12
app/src/gitHub/res/xml/pref_general_updates.xml
Normal file
@ -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>
|
@ -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)
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
@ -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<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)
|
@ -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
|
||||
)
|
@ -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,
|
||||
}
|
||||
}
|
@ -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<AppUpdate>
|
||||
fun skipUpdate(update: AppUpdate)
|
||||
fun notifyUpdateDownloaded(update: AppUpdate)
|
||||
}
|
@ -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 {
|
||||
|
@ -561,7 +561,7 @@ class SharedPreferencesSettingsRepository(
|
||||
return preferenceFlow.map { mapper() }
|
||||
}
|
||||
|
||||
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String) {
|
||||
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String?) {
|
||||
val subject = preferenceObservers[key]
|
||||
subject?.onNext(Any())
|
||||
|
||||
|
@ -56,8 +56,8 @@ class EmulatorSession {
|
||||
return areRetroAchievementsEnabled && isRetroAchievementsIntegrationEnabled
|
||||
}
|
||||
|
||||
fun areSaveStatesAllowed(): Boolean {
|
||||
// Cannot use save-states when RA hardcore is enabled
|
||||
fun areSaveStateLoadsAllowed(): Boolean {
|
||||
// Cannot load save-states when RA hardcore is enabled
|
||||
return !isRetroAchievementsHardcoreModeEnabled || !areRetroAchievementsEnabled
|
||||
}
|
||||
|
||||
|
@ -79,10 +79,12 @@ import me.magnum.melonds.impl.emulator.LifecycleOwnerProvider
|
||||
import me.magnum.melonds.parcelables.RomInfoParcelable
|
||||
import me.magnum.melonds.parcelables.RomParcelable
|
||||
import me.magnum.melonds.ui.cheats.CheatsActivity
|
||||
import me.magnum.melonds.ui.emulator.component.EmulatorOverlayTracker
|
||||
import me.magnum.melonds.ui.emulator.input.FrontendInputHandler
|
||||
import me.magnum.melonds.ui.emulator.input.INativeInputListener
|
||||
import me.magnum.melonds.ui.emulator.input.InputProcessor
|
||||
import me.magnum.melonds.ui.emulator.input.MelonTouchHandler
|
||||
import me.magnum.melonds.ui.emulator.model.EmulatorOverlay
|
||||
import me.magnum.melonds.ui.emulator.model.EmulatorState
|
||||
import me.magnum.melonds.ui.emulator.model.EmulatorUiEvent
|
||||
import me.magnum.melonds.ui.emulator.model.PauseMenu
|
||||
@ -210,9 +212,17 @@ class EmulatorActivity : AppCompatActivity() {
|
||||
closeRewindWindow()
|
||||
}
|
||||
private val showAchievementList = mutableStateOf(false)
|
||||
private var resumeEmulatorOnActivityResume = true
|
||||
private var emulatorReady = false
|
||||
|
||||
private val activeOverlays = EmulatorOverlayTracker(
|
||||
onOverlaysCleared = {
|
||||
disableScreenTimeOut()
|
||||
},
|
||||
onOverlaysPresent = {
|
||||
enableScreenTimeOut()
|
||||
}
|
||||
)
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
lifecycleOwnerProvider.setCurrentLifecycleOwner(this)
|
||||
@ -512,24 +522,25 @@ class EmulatorActivity : AppCompatActivity() {
|
||||
|
||||
if (viewModel.emulatorState.value.isRunning()) {
|
||||
window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
|
||||
resumeEmulatorOnActivityResume = false
|
||||
viewModel.pauseEmulator(false)
|
||||
backPressedCallback.isEnabled = false
|
||||
|
||||
activeOverlays.addActiveOverlay(EmulatorOverlay.SWITCH_NEW_ROM_DIALOG)
|
||||
AlertDialog.Builder(this)
|
||||
.setTitle(getString(R.string.title_emulator_running))
|
||||
.setMessage(getString(R.string.message_stop_emulation))
|
||||
.setPositiveButton(R.string.ok) { _, _ ->
|
||||
viewModel.stopEmulator()
|
||||
resumeEmulatorOnActivityResume = true
|
||||
setIntent(intent)
|
||||
launchEmulator()
|
||||
}
|
||||
.setNegativeButton(R.string.no) { dialog, _ ->
|
||||
dialog.cancel()
|
||||
}
|
||||
.setOnDismissListener {
|
||||
activeOverlays.removeActiveOverlay(EmulatorOverlay.SWITCH_NEW_ROM_DIALOG)
|
||||
}
|
||||
.setOnCancelListener {
|
||||
resumeEmulatorOnActivityResume = true
|
||||
backPressedCallback.isEnabled = true
|
||||
viewModel.resumeEmulator()
|
||||
}
|
||||
@ -541,7 +552,7 @@ class EmulatorActivity : AppCompatActivity() {
|
||||
super.onResume()
|
||||
binding.surfaceMain.onResume()
|
||||
|
||||
if (resumeEmulatorOnActivityResume) {
|
||||
if (!activeOverlays.hasActiveOverlays()) {
|
||||
disableScreenTimeOut()
|
||||
viewModel.resumeEmulator()
|
||||
}
|
||||
@ -678,19 +689,18 @@ class EmulatorActivity : AppCompatActivity() {
|
||||
getString(pauseMenu.options[it].textResource)
|
||||
}
|
||||
|
||||
resumeEmulatorOnActivityResume = false
|
||||
enableScreenTimeOut()
|
||||
activeOverlays.addActiveOverlay(EmulatorOverlay.PAUSE_MENU)
|
||||
AlertDialog.Builder(this)
|
||||
.setTitle(R.string.pause)
|
||||
.setItems(options) { _, which ->
|
||||
val selectedOption = pauseMenu.options[which]
|
||||
viewModel.onPauseMenuOptionSelected(selectedOption)
|
||||
disableScreenTimeOut()
|
||||
}
|
||||
.setOnDismissListener {
|
||||
activeOverlays.removeActiveOverlay(EmulatorOverlay.PAUSE_MENU)
|
||||
}
|
||||
.setOnCancelListener {
|
||||
viewModel.resumeEmulator()
|
||||
resumeEmulatorOnActivityResume = true
|
||||
disableScreenTimeOut()
|
||||
}
|
||||
.show()
|
||||
}
|
||||
@ -721,7 +731,6 @@ class EmulatorActivity : AppCompatActivity() {
|
||||
var adapter: SaveStateListAdapter? = null
|
||||
|
||||
adapter = SaveStateListAdapter(slots, picasso, dateFormatter, timeFormatter, {
|
||||
disableScreenTimeOut()
|
||||
dialog?.cancel()
|
||||
onSlotPicked(it)
|
||||
}) {
|
||||
@ -730,7 +739,7 @@ class EmulatorActivity : AppCompatActivity() {
|
||||
}
|
||||
}
|
||||
|
||||
enableScreenTimeOut()
|
||||
activeOverlays.addActiveOverlay(EmulatorOverlay.SAVE_STATES_DIALOG)
|
||||
dialog = AlertDialog.Builder(this)
|
||||
.setTitle(getString(R.string.save_slot))
|
||||
.setAdapter(adapter) { _, _ ->
|
||||
@ -738,15 +747,17 @@ class EmulatorActivity : AppCompatActivity() {
|
||||
.setNegativeButton(R.string.cancel) { _dialog, _ ->
|
||||
_dialog.cancel()
|
||||
}
|
||||
.setOnDismissListener {
|
||||
activeOverlays.removeActiveOverlay(EmulatorOverlay.SAVE_STATES_DIALOG)
|
||||
}
|
||||
.setOnCancelListener {
|
||||
viewModel.resumeEmulator()
|
||||
disableScreenTimeOut()
|
||||
}
|
||||
.show()
|
||||
}
|
||||
|
||||
private fun showRomLoadErrorDialog() {
|
||||
enableScreenTimeOut()
|
||||
activeOverlays.addActiveOverlay(EmulatorOverlay.ROM_LOAD_ERROR_DIALOG)
|
||||
AlertDialog.Builder(this)
|
||||
.setCancelable(false)
|
||||
.setTitle(R.string.error_load_rom)
|
||||
@ -759,7 +770,7 @@ class EmulatorActivity : AppCompatActivity() {
|
||||
}
|
||||
|
||||
private fun showRomNotFoundDialog(romPath: String) {
|
||||
enableScreenTimeOut()
|
||||
activeOverlays.addActiveOverlay(EmulatorOverlay.ROM_NOT_FOUND_DIALOG)
|
||||
AlertDialog.Builder(this)
|
||||
.setTitle(R.string.error_rom_not_found)
|
||||
.setMessage(getString(R.string.error_rom_not_found_info, romPath))
|
||||
@ -773,7 +784,7 @@ class EmulatorActivity : AppCompatActivity() {
|
||||
}
|
||||
|
||||
private fun showFirmwareLoadErrorDialog(error: EmulatorState.FirmwareLoadError) {
|
||||
enableScreenTimeOut()
|
||||
activeOverlays.addActiveOverlay(EmulatorOverlay.FIRMWARE_LOAD_ERROR_DIALOG)
|
||||
AlertDialog.Builder(this)
|
||||
.setCancelable(false)
|
||||
.setTitle(R.string.error_load_firmware)
|
||||
@ -786,13 +797,13 @@ class EmulatorActivity : AppCompatActivity() {
|
||||
}
|
||||
|
||||
private fun showRewindWindow(rewindWindow: RewindWindow) {
|
||||
enableScreenTimeOut()
|
||||
activeOverlays.addActiveOverlay(EmulatorOverlay.REWIND_WINDOW)
|
||||
binding.root.transitionToState(R.id.rewind_visible)
|
||||
rewindSaveStateAdapter.setRewindWindow(rewindWindow)
|
||||
}
|
||||
|
||||
private fun closeRewindWindow() {
|
||||
disableScreenTimeOut()
|
||||
activeOverlays.removeActiveOverlay(EmulatorOverlay.REWIND_WINDOW)
|
||||
binding.root.transitionToState(R.id.rewind_hidden)
|
||||
viewModel.resumeEmulator()
|
||||
}
|
||||
|
@ -346,7 +346,7 @@ class EmulatorViewModel @Inject constructor(
|
||||
return
|
||||
}
|
||||
|
||||
if (!emulatorSession.areSaveStatesAllowed()) {
|
||||
if (!emulatorSession.areSaveStateLoadsAllowed()) {
|
||||
_toastEvent.tryEmit(ToastEvent.RewindNotAvailableWhileRAHardcoreModeEnabled)
|
||||
return
|
||||
}
|
||||
@ -394,17 +394,13 @@ class EmulatorViewModel @Inject constructor(
|
||||
val currentState = _emulatorState.value
|
||||
when (currentState) {
|
||||
is EmulatorState.RunningRom -> {
|
||||
if (emulatorSession.areSaveStatesAllowed()) {
|
||||
sessionCoroutineScope.launch {
|
||||
emulatorManager.pauseEmulator()
|
||||
val quickSlot = saveStatesRepository.getRomQuickSaveStateSlot(currentState.rom)
|
||||
if (saveRomState(currentState.rom, quickSlot)) {
|
||||
_toastEvent.emit(ToastEvent.QuickSaveSuccessful)
|
||||
}
|
||||
emulatorManager.resumeEmulator()
|
||||
sessionCoroutineScope.launch {
|
||||
emulatorManager.pauseEmulator()
|
||||
val quickSlot = saveStatesRepository.getRomQuickSaveStateSlot(currentState.rom)
|
||||
if (saveRomState(currentState.rom, quickSlot)) {
|
||||
_toastEvent.emit(ToastEvent.QuickSaveSuccessful)
|
||||
}
|
||||
} else {
|
||||
_toastEvent.tryEmit(ToastEvent.CannotUseSaveStatesWhenRAHardcoreIsEnabled)
|
||||
emulatorManager.resumeEmulator()
|
||||
}
|
||||
}
|
||||
is EmulatorState.RunningFirmware -> {
|
||||
@ -420,7 +416,7 @@ class EmulatorViewModel @Inject constructor(
|
||||
val currentState = _emulatorState.value
|
||||
when (currentState) {
|
||||
is EmulatorState.RunningRom -> {
|
||||
if (emulatorSession.areSaveStatesAllowed()) {
|
||||
if (emulatorSession.areSaveStateLoadsAllowed()) {
|
||||
sessionCoroutineScope.launch {
|
||||
emulatorManager.pauseEmulator()
|
||||
val quickSlot = saveStatesRepository.getRomQuickSaveStateSlot(currentState.rom)
|
||||
@ -733,9 +729,8 @@ class EmulatorViewModel @Inject constructor(
|
||||
|
||||
private fun filterRomPauseMenuOption(option: RomPauseMenuOption): Boolean {
|
||||
return when (option) {
|
||||
RomPauseMenuOption.REWIND -> settingsRepository.isRewindEnabled() && emulatorSession.areSaveStatesAllowed()
|
||||
RomPauseMenuOption.SAVE_STATE -> emulatorSession.areSaveStatesAllowed()
|
||||
RomPauseMenuOption.LOAD_STATE -> emulatorSession.areSaveStatesAllowed()
|
||||
RomPauseMenuOption.REWIND -> settingsRepository.isRewindEnabled() && emulatorSession.areSaveStateLoadsAllowed()
|
||||
RomPauseMenuOption.LOAD_STATE -> emulatorSession.areSaveStateLoadsAllowed()
|
||||
RomPauseMenuOption.CHEATS -> emulatorSession.areCheatsEnabled()
|
||||
RomPauseMenuOption.VIEW_ACHIEVEMENTS -> emulatorSession.areRetroAchievementsEnabled()
|
||||
else -> true
|
||||
|
@ -0,0 +1,29 @@
|
||||
package me.magnum.melonds.ui.emulator.component
|
||||
|
||||
import me.magnum.melonds.ui.emulator.model.EmulatorOverlay
|
||||
|
||||
class EmulatorOverlayTracker(
|
||||
private val onOverlaysCleared: () -> Unit,
|
||||
private val onOverlaysPresent: () -> Unit,
|
||||
) {
|
||||
|
||||
private val activeOverlays = mutableListOf<EmulatorOverlay>()
|
||||
|
||||
fun addActiveOverlay(overlay: EmulatorOverlay) {
|
||||
activeOverlays.add(overlay)
|
||||
if (activeOverlays.size == 1) {
|
||||
onOverlaysPresent()
|
||||
}
|
||||
}
|
||||
|
||||
fun removeActiveOverlay(overlay: EmulatorOverlay) {
|
||||
activeOverlays.remove(overlay)
|
||||
if (activeOverlays.isEmpty()) {
|
||||
onOverlaysCleared()
|
||||
}
|
||||
}
|
||||
|
||||
fun hasActiveOverlays(): Boolean {
|
||||
return activeOverlays.isNotEmpty()
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
package me.magnum.melonds.ui.emulator.model
|
||||
|
||||
enum class EmulatorOverlay {
|
||||
PAUSE_MENU,
|
||||
REWIND_WINDOW,
|
||||
SAVE_STATES_DIALOG,
|
||||
ROM_LOAD_ERROR_DIALOG,
|
||||
FIRMWARE_LOAD_ERROR_DIALOG,
|
||||
ROM_NOT_FOUND_DIALOG,
|
||||
SWITCH_NEW_ROM_DIALOG,
|
||||
}
|
@ -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)
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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<SwitchPreference>("enable_sustained_performance")!!
|
||||
|
@ -37,6 +37,8 @@ object RomProcessor {
|
||||
stream.read(header)
|
||||
save(KEY_HEADER, header)
|
||||
|
||||
val gameCode = String(header, 0x0C, 4)
|
||||
|
||||
val arm9Offset = byteArrayToInt(header, 0x20)
|
||||
val arm9Size = byteArrayToInt(header, 0x2C)
|
||||
|
||||
@ -78,22 +80,30 @@ object RomProcessor {
|
||||
save(KEY_DEVELOPER_NAME, developer)
|
||||
}
|
||||
|
||||
val cartCategory = gameCode[0]
|
||||
if (cartCategory == 'H' || cartCategory == 'K') {
|
||||
// This is probably a DSi Ware game. But confirm in a later value processor
|
||||
register(
|
||||
RomStreamDataProcessor.SectionProcessor.SectionValueProcessor(
|
||||
streamOffset = 0x234
|
||||
) { stream, save ->
|
||||
val categoryData = ByteArray(4)
|
||||
stream.read(categoryData)
|
||||
val categoryId = byteArrayToInt(categoryData)
|
||||
save(KEY_ROM_IS_DSIWARE_TITLE, categoryId.toUInt() == DSIWARE_CATEGORY)
|
||||
}
|
||||
)
|
||||
} else {
|
||||
save(KEY_ROM_IS_DSIWARE_TITLE, false)
|
||||
}
|
||||
|
||||
register(arm9Processor)
|
||||
register(arm7Processor)
|
||||
register(bannerProcessor)
|
||||
}
|
||||
)
|
||||
)
|
||||
registerProcessor(
|
||||
RomStreamDataProcessor.SectionProcessor.SectionValueProcessor(
|
||||
streamOffset = 0x234
|
||||
) { stream, save ->
|
||||
val categoryData = ByteArray(4)
|
||||
stream.read(categoryData)
|
||||
val categoryId = byteArrayToInt(categoryData)
|
||||
save(KEY_ROM_IS_DSIWARE_TITLE, categoryId.toUInt() == DSIWARE_CATEGORY)
|
||||
}
|
||||
)
|
||||
|
||||
process(inputStream)
|
||||
}
|
||||
|
||||
|
@ -16,6 +16,8 @@
|
||||
<string name="undo">Deshacer</string>
|
||||
<string name="update">Actualizar</string>
|
||||
<string name="close">Cerrar</string>
|
||||
<string name="play">Jugar</string>
|
||||
<string name="ellipsis">…</string>
|
||||
|
||||
<string name="version_alpha">Alfa</string>
|
||||
<string name="version_beta">Beta</string>
|
||||
@ -31,10 +33,13 @@
|
||||
<string name="size_mb">MB</string>
|
||||
<string name="size_gb">GB</string>
|
||||
|
||||
<string name="error_rom_not_found">No se pudo encontrar ROM</string>
|
||||
<string name="error_rom_not_found_info">No se ha encontrado la ROM seleccionada. Asegúrese de que la ROM seleccionada se encuentre en el directorio de búsqueda de la ROM actual.\n%1$s</string>
|
||||
<string name="error_rom_not_found">No se pudo encontrar la ROM</string>
|
||||
<string name="error_rom_not_found_info">No se ha encontrado la ROM seleccionada. Asegúrate de que la ROM seleccionada se encuentre en el directorio de búsqueda de la ROM actual.\n%1$s</string>
|
||||
<string name="error_load_rom">No se pudo cargar la ROM</string>
|
||||
<string name="error_load_rom_report_issue">Se generó un registro de errores que puede ayudar a solucionar este problema. ¿Quieres abrir un problema en GitHub con este registro? Necesitarás una cuenta de GitHub.</string>
|
||||
<string name="error_load_rom_message">Hubo un problema cargando la ROM. Asegúrate de que no esté dañada e inténtalo de nuevo.</string>
|
||||
<string name="error_load_firmware">No se pudo cargar el firmware.</string>
|
||||
<string name="error_load_firmware_message">Hubo un problema al cargar el firmware (%1$s). Asegúrate de que la BIOS y el archivo de firmware no estén dañados, de que el firmware sea arrancable e inténtalo de nuevo.</string>
|
||||
<string name="error_load_rom_report_issue">Se generó un registro de errores que puede ayudar a solucionar este problema. ¿Deseas abrir un problema en GitHub con este registro? Necesitarás una cuenta de GitHub.</string>
|
||||
<string name="title_emulator_running">Emulador ejecutándose</string>
|
||||
<string name="message_stop_emulation">¿Detener la emulación y cargar una nueva ROM?</string>
|
||||
<string name="error_load_gba_rom">No se pudo cargar la ROM de GBA</string>
|
||||
@ -63,9 +68,10 @@
|
||||
<string name="dsiware_manager_import_title_error_not_dsiware_title">No es un título de DSiWare</string>
|
||||
<string name="dsiware_manager_import_title_error_title_already_imported">Título de DSiWare ya importado</string>
|
||||
<string name="dsiware_manager_import_title_error_insatll_failed">Error al instalar el título</string>
|
||||
<string name="dsiware_manager_import_title_error_metadat_fetch_failed">No se pudieron descargar los metadatos del título. Verifica tu conexión a Internet.</string>
|
||||
<string name="dsiware_manager_import_title_error_unknown">Se ha producido un error desconocido</string>
|
||||
<string name="dsiware_import_from_file">Desde el archivo</string>
|
||||
<string name="dsiware_import_from_rom_list">De la lista de ROM</string>
|
||||
<string name="dsiware_import_from_rom_list">Desde la lista de ROM</string>
|
||||
|
||||
<string name="select_dsiware_title">Seleccione el título de DSiWare</string>
|
||||
<string name="no_dsiware_roms_found">Seleccione el título de DSiWare</string>
|
||||
@ -76,11 +82,15 @@
|
||||
<string name="failed_save_state">No se pudo guardar</string>
|
||||
<string name="failed_load_state">No se pudo cargar</string>
|
||||
<string name="cant_load_empty_slot">No se puede cargar un espacio vacío</string>
|
||||
<string name="failed_reset_emulation">No se pudo restablecer</string>
|
||||
<string name="failed_reset_emulation">No se pudo restablecer la emulación</string>
|
||||
<string name="save_states_not_supported">Los estados de guardado no son compatibles mientras se ejecuta el firmware</string>
|
||||
<string name="rewind_time_seconds">%1$ss</string> <!-- Ex: 43.56s -->
|
||||
<string name="rewind_time_minutes_seconds">%1$dm%2$ss</string> <!-- Ex: 2m37.93s -->
|
||||
<string name="save_states_unavailable_ra_hardcore_enabled">Guardados no disponibles en modo Hardcore</string>
|
||||
<string name="rewind_time_seconds">%1$s s</string> <!-- Ej: 43,56 s -->
|
||||
<string name="rewind_time_minutes_seconds">%1$d min %2$s s</string> <!-- Ej: 2 min 37,93 s -->
|
||||
<string name="rewind_not_enabled">El rebobinado no está habilitado</string>
|
||||
<string name="rewind_unavailable_ra_hardcore_enabled">El rebobinado no disponible en modo Hardcore</string>
|
||||
<string name="no_image_selected">Ninguna imagen seleccionada</string>
|
||||
<string name="failed_to_load_image">Error al cargar la imagen</string>
|
||||
|
||||
<string name="action_sort_alphabetically">Alfabéticamente</string>
|
||||
<string name="action_sort_recently_played">Recientemente</string>
|
||||
@ -109,7 +119,8 @@
|
||||
<string name="choose_component">Elegir componente</string>
|
||||
|
||||
<string name="pause">Pausar</string>
|
||||
<string name="reset">Resetear</string>
|
||||
<string name="achievements">Logros</string>
|
||||
<string name="reset">Reestablecer</string>
|
||||
<string name="exit">Salir</string>
|
||||
|
||||
<string name="settings">Configuración</string>
|
||||
@ -163,9 +174,11 @@
|
||||
<string name="threaded_rendering">Renderizar en subproceso</string>
|
||||
<string name="fps_counter_position">Posición del contador de FPS</string>
|
||||
<string name="threaded_rendering_summary">Mejora el rendimiento de los juegos en 3D cuando está habilitado, pero causará fallas gráficas.</string>
|
||||
<string name="category_video">Video</string>
|
||||
<string name="category_video_summary">Filtro, Renderizado de subprocesos, contador de FPS</string>
|
||||
<string name="category_video">Vídeo</string>
|
||||
<string name="category_video_summary">Filtro, renderizado de subprocesos, contador de FPS</string>
|
||||
<string name="filter">Filtro</string>
|
||||
<string name="dsi_camera_source">Fuente de cámara DSi</string>
|
||||
<string name="dsi_camera_image">Imagen de cámara DSi</string>
|
||||
<string name="category_audio">Audio</string>
|
||||
<string name="category_audio_summary">Volumen, latencia, fuente de micrófono</string>
|
||||
<string name="enable_sound">Activar sonido</string>
|
||||
@ -189,13 +202,20 @@
|
||||
<string name="vibrate_on_touch">Vibrar al tacto</string>
|
||||
<string name="vibration_strength">Fuerza de vibración</string>
|
||||
<string name="soft_input_opacity">Opacidad de entrada suave</string>
|
||||
<string name="retroachievements">RetroAchievements</string> <!-- Mantén las palabras unidas porque este es el nombre oficial -->
|
||||
<string name="retroachievements_summary">Inicio de sesión & Gestionar la conexión de RetroAchievements</string> <!-- Mantén las palabras unidas porque este es el nombre oficial -->
|
||||
<string name="enable_rich_presence">Activar la opción para mostrar lo que estás haciendo</string>
|
||||
<string name="rich_presence_summary">Permite que otros usuarios de RetroAchievement vean lo que estás jugando</string>
|
||||
<string name="hardcore_mode">Modo Hardcore</string>
|
||||
<string name="hardcore_mode_summary">Gana el doble de puntos, pero no puedes usar estados de guardado, trucos ni cámara lenta mientras juegas</string>
|
||||
|
||||
<string name="cheats">Trucos</string>
|
||||
<string name="cheats_summary">Habilitar e importar códigos de trucos</string>
|
||||
<string name="cheats_summary">Habilitar e importar trucos de & trucos</string>
|
||||
<string name="enable_cheats">Habilitar trucos</string>
|
||||
<string name="cheats_database">Base de datos de trucos (formato XML)</string>
|
||||
<string name="import_cheats">Importar trucos</string>
|
||||
<string name="import_cheats_summary">Importar trucos de archivos base disponibles. Por ahora, solo se admite XML. Se eliminará cualquier truco importado anteriormente</string>
|
||||
<string name="rom_shortcut">Acceso directo de ROM</string>
|
||||
<string name="import_cheats_summary">Importar trucos desde archivos de base de datos para tenerlos disponibles. Por ahora, solo se admiten bases de datos en formato XML. Cualquier truco previamente importado se eliminará</string>
|
||||
<string name="rom_shortcut">Acceso directo de la ROM</string>
|
||||
|
||||
<string name="firmware_nickname">Apodo</string>
|
||||
<string name="firmware_message">Mensaje</string>
|
||||
@ -218,9 +238,11 @@
|
||||
|
||||
<string name="rom_settings">Configuración de Rom</string> <!-- Cadena de accesibilidad. No debe usar acrónimos -->
|
||||
<string name="label_rom_config_console">Sistema de arranque</string>
|
||||
<string name="label_rom_config_load_gba_rom">Cargar ROM GBA</string>
|
||||
<string name="label_rom_config_gba_rom_path">Ruta de acceso ROM GBA</string>
|
||||
<string name="label_rom_config_load_gba_rom">Cargar ROM de GBA</string>
|
||||
<string name="label_rom_config_gba_rom_path">Ruta de acceso de la ROM de GBA</string>
|
||||
<string name="label_rom_config_gba_save_path">Ruta de guardado de GBA</string>
|
||||
<string name="rom_details_configuration_tab">Configuración</string>
|
||||
<string name="retro_achievements_tab">Retro Achievements</string> <!-- Mantén las palabras separadas para ayudar con el ajuste de palabras -->
|
||||
|
||||
<string name="theme_option_light">Claro</string>
|
||||
<string name="theme_option_dark">Oscuro</string>
|
||||
@ -285,11 +307,42 @@
|
||||
<string name="background_name">Nombre de fondo</string>
|
||||
<string name="background_deleted">Fondo eliminado</string>
|
||||
<string name="background_add_wrong_orientation">Se agregó fondo pero no es visible para la orientación seleccionada.</string>
|
||||
<string name="background_add_processing_failed">Error al procesar la imagen de fondo seleccionada. Asegúrese de que no esté dañado.</string>
|
||||
|
||||
<string-array name="fast_forward_speed_multiplier_options">
|
||||
<string name="background_add_processing_failed">Error al procesar la imagen de fondo seleccionada. Asegúrate de que no esté dañada.</string>
|
||||
|
||||
<string name="retro_achievements_login_description">¡Inicia sesión con RetroAchievements para ver los logros que puedes desbloquear mientras juegas este juego!</string>
|
||||
<string name="login_with_retro_achievements">Inicia sesión con RetroAchievements</string>
|
||||
<string name="login">Iniciar sesión</string>
|
||||
<string name="retroachievements_login_summary">¡Inicia sesión con RetroAchievements para desbloquear logros mientras juegas!</string>
|
||||
<string name="retroachievements_login_status">Sesión iniciada como %1$s</string>
|
||||
<string name="retroachievements_logout">Cerrar sesión</string>
|
||||
<string name="retroachievements_logout_confirmation">¿Estás seguro de que quieres cerrar sesión en RetroAchievements? No podrás desbloquear logros mientras juegas.</string>
|
||||
<string name="retro_achievements_login_error">Hubo un problema al intentar iniciar sesión. Asegúrate de que tu nombre de usuario y contraseña sean correctos y de que estés conectado a Internet</string>
|
||||
<string name="retro_achievements_login_error_short">Se produjo un problema al intentar iniciar sesión</string>
|
||||
<string name="retro_achievements_load_error">Se produjo un problema al intentar cargar los logros de este juego. Asegúrate de estar conectado a Internet e intenta de nuevo más tarde</string>
|
||||
<string name="retro_achievements_no_achievements">No se encontraron RetroAchievements para este juego</string>
|
||||
<string name="retro_achievements_relaunch_to_apply_settings">Reinicia la ROM para aplicar la nueva configuración de RetroAchievements</string>
|
||||
<string name="username">Nombre de usuario</string>
|
||||
<string name="password">Contraseña</string>
|
||||
<string name="retry">Reintentar</string>
|
||||
<string name="points_abbreviated">PTS</string>
|
||||
<string name="points">Puntos</string>
|
||||
<string name="completed">Completado</string>
|
||||
<string name="ra_mode_hardcore">Hardcore</string>
|
||||
<string name="ra_mode_softcore">Softcore</string>
|
||||
<string name="view_achievement">Ver logro</string>
|
||||
<string name="completed_achievements"><b>%1$d</b> de <b>%2$d</b> (%3$d% %)</string>
|
||||
<string name="submitting_achievements">Enviando RetroAchievements…</string>
|
||||
<string name="achievements_loaded">Logros cargados</string>
|
||||
<string name="achievements_unlocked_compact">Desbloqueados: %1$d/%2$d</string>
|
||||
<string name="achievements_failed_load">¡Error al cargar los logros!</string>
|
||||
<string name="achievements_failed_load_tip">Asegúrate de estar en línea</string>
|
||||
<string name="achievement_unlocked">¡Logro desbloqueado!</string>
|
||||
<string name="achievement_missable">Logro perdible</string>
|
||||
<string name="achievement_missable_description">Este logro se puede perder durante una partida</string>
|
||||
|
||||
<string-array name="fast_forward_speed_multiplier_options">
|
||||
<item>Ilimitado</item>
|
||||
<item>1.5x</item>
|
||||
<item>1,5x</item>
|
||||
<item>2x</item>
|
||||
<item>3x</item>
|
||||
<item>4x</item>
|
||||
@ -297,12 +350,12 @@
|
||||
</string-array>
|
||||
|
||||
<string-array name="rom_icon_filtering_options">
|
||||
<item>Ninguno (pixelado)</item>
|
||||
<item>Lineal (suave)</item>
|
||||
<item>Ninguno (Pixelado)</item>
|
||||
<item>Lineal (Suave)</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="game_runtime_console_type_options">
|
||||
<item>Predeterminado</item>
|
||||
<item>Por defecto</item>
|
||||
<item>@string/console_ds</item>
|
||||
<item>@string/console_dsi</item>
|
||||
</string-array>
|
||||
@ -317,7 +370,7 @@
|
||||
</string-array>
|
||||
|
||||
<string-array name="game_runtime_mic_source_options">
|
||||
<item>Predeterminado</item>
|
||||
<item>Por defecto</item>
|
||||
<item>Ninguno</item>
|
||||
<item>Soplar</item>
|
||||
<item>Micrófono del dispositivo</item>
|
||||
@ -337,7 +390,7 @@
|
||||
<item>Inferior-central</item>
|
||||
<item>Abajo-derecha</item>
|
||||
</string-array>
|
||||
|
||||
|
||||
<string-array name="video_filtering_options">
|
||||
<item>Ninguno</item>
|
||||
<item>Lineal</item>
|
||||
@ -348,6 +401,12 @@
|
||||
<item>LCD</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="dsi_camera_source_options">
|
||||
<item>Pantalla negra</item>
|
||||
<item>Cámaras físicas</item>
|
||||
<item>Imagen estática</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="audio_interpolation_options">
|
||||
<item>Ninguno</item>
|
||||
<item>Lineal</item>
|
||||
@ -386,16 +445,16 @@
|
||||
</string-array>
|
||||
|
||||
<string-array name="background_portrait_mode_options">
|
||||
<item>Elasticidad</item>
|
||||
<item>Ajuste al centro</item>
|
||||
<item>Ajuste superior</item>
|
||||
<item>Ajuste inferior</item>
|
||||
<item>Estirar</item>
|
||||
<item>Ajustar al centro</item>
|
||||
<item>Ajustar a la parte superior</item>
|
||||
<item>Ajustar a la parte inferior</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="background_landscape_mode_options">
|
||||
<item>Elasticidad</item>
|
||||
<item>Centro de ajuste</item>
|
||||
<item>Ajuste a la izquierda</item>
|
||||
<item>Ajuste a la derecha</item>
|
||||
<item>Estirar</item>
|
||||
<item>Ajustar al centro</item>
|
||||
<item>Ajustar a la izquierda</item>
|
||||
<item>Ajustar a la derecha</item>
|
||||
</string-array>
|
||||
</resources>
|
||||
|
@ -17,6 +17,8 @@
|
||||
<string name="undo">Annuler</string>
|
||||
<string name="update">Mettre à jour</string>
|
||||
<string name="close">Fermer</string>
|
||||
<string name="play">Jouer</string>
|
||||
<string name="ellipsis">…</string>
|
||||
|
||||
<string name="version_alpha">Alpha</string>
|
||||
<string name="version_beta">Bêta</string>
|
||||
@ -35,6 +37,9 @@
|
||||
<string name="error_rom_not_found">Impossible de trouver la ROM</string>
|
||||
<string name="error_rom_not_found_info">La ROM que vous avez sélectionnée n\'a pas été trouvée. Assurez-vous que la ROM sélectionnée peut être trouvée dans le répertoire de recherche de ROMs actuel.\n%1$s</string>
|
||||
<string name="error_load_rom">Impossible de charger la ROM</string>
|
||||
<string name="error_load_rom_message">Un problème est survenu lors du chargement de la ROM. Veuillez vérifier qu\'elle n\'est pas corrompue et réessayez.</string>
|
||||
<string name="error_load_firmware">Impossible de charger le firmware</string>
|
||||
<string name="error_load_firmware_message">Un problème est survenu lors du chargement du firmware (%1$s). Veuillez vérifier que les dumps du BIOS et du firmware ne sont pas corrompus, que le firmare peut être démarré, et réessayez.</string>
|
||||
<string name="error_load_rom_report_issue">Un journal d\'erreur qui peut aider à résoudre ce problème a été généré. Voulez-vous ouvrir un problème sur GitHub avec ce journal ? Vous aurez besoin d\'un compte GitHub.</string>
|
||||
<string name="title_emulator_running">Émulateur en cours d\'exécution</string>
|
||||
<string name="message_stop_emulation">Arrêter l\'émulation et charger une nouvelle ROM ?</string>
|
||||
@ -58,12 +63,13 @@
|
||||
<string name="import_dsiware_title">Importer le titre DSiWare</string>
|
||||
<string name="dsiware_manager_setup">Configuration</string>
|
||||
<string name="dsiware_manager_fix_setup">Réparer la configuration</string>
|
||||
<string name="dsiware_manager_load_error">Il y a eu un problème avec la liste de vos titres DSiWare. Veuillez vérifier votre configuration DSi</string>
|
||||
<string name="dsiware_manager_load_error">Un problème est survenu lors du listage de vos titres DSiWare. Veuillez vérifier votre configuration DSi</string>
|
||||
<string name="dsiware_manager_import_title_error_open_nand_failed">La NAND n\'a pas pu être ouverte</string>
|
||||
<string name="dsiware_manager_import_title_error_open_file_failed">Erreur lors de l\'ouverture du fichier sélectionné</string>
|
||||
<string name="dsiware_manager_import_title_error_not_dsiware_title">Il ne s\'agit pas d\'un titre DSiWare</string>
|
||||
<string name="dsiware_manager_import_title_error_title_already_imported">Ce titre DSiWare a déjà été importé</string>
|
||||
<string name="dsiware_manager_import_title_error_insatll_failed">Impossible d\'installer le titre</string>
|
||||
<string name="dsiware_manager_import_title_error_metadat_fetch_failed">Impossible de télécharger les métadonnées du titre. Veuillez vérifier votre connexion Internet</string>
|
||||
<string name="dsiware_manager_import_title_error_unknown">Une erreur inconnue est survenue</string>
|
||||
<string name="dsiware_import_from_file">Depuis le fichier</string>
|
||||
<string name="dsiware_import_from_rom_list">Depuis la liste des ROMs</string>
|
||||
@ -79,9 +85,13 @@
|
||||
<string name="cant_load_empty_slot">Impossible de charger un emplacement de sauvegarde d\'état vide</string>
|
||||
<string name="failed_reset_emulation">Impossible de réinitialiser l\'émulation</string>
|
||||
<string name="save_states_not_supported">Les sauvegardes d\'état ne sont pas prises en charge pendant l\'exécution du firmware</string>
|
||||
<string name="save_states_unavailable_ra_hardcore_enabled">Les sauvegardes d\'état sont indisponibles lorsque le mode Hardcore est activé</string>
|
||||
<string name="rewind_time_seconds">%1$s s</string>
|
||||
<string name="rewind_time_minutes_seconds">%1$d min%2$s s</string>
|
||||
<string name="rewind_not_enabled">Le rembobinage n\'est pas activé</string>
|
||||
<string name="rewind_unavailable_ra_hardcore_enabled">Le rembobinage n\'est pas disponible lorsque le mode Hardcore est activé</string>
|
||||
<string name="no_image_selected">Aucune image sélectionnée</string>
|
||||
<string name="failed_to_load_image">Impossible de charger l\'image</string>
|
||||
|
||||
<string name="action_sort_alphabetically">Ordre alphabétique</string>
|
||||
<string name="action_sort_recently_played">Joué récemment</string>
|
||||
@ -110,6 +120,7 @@
|
||||
<string name="choose_component">Choisissez le composant</string>
|
||||
|
||||
<string name="pause">Pause</string>
|
||||
<string name="achievements">Succès</string>
|
||||
<string name="reset">Redémarrer</string>
|
||||
<string name="exit">Quitter</string>
|
||||
|
||||
@ -167,6 +178,8 @@
|
||||
<string name="category_video">Vidéo</string>
|
||||
<string name="category_video_summary">Filtre, rendu fileté, compteur FPS</string>
|
||||
<string name="filter">Filtre</string>
|
||||
<string name="dsi_camera_source">Source appareil photo DSi</string>
|
||||
<string name="dsi_camera_image">Image appareil photo DSi</string>
|
||||
<string name="category_audio">Audio</string>
|
||||
<string name="category_audio_summary">Volume, latence, source du microphone</string>
|
||||
<string name="enable_sound">Activer le son</string>
|
||||
@ -190,6 +203,13 @@
|
||||
<string name="vibrate_on_touch">Vibrer au toucher</string>
|
||||
<string name="vibration_strength">Force de vibration</string>
|
||||
<string name="soft_input_opacity">Opacité des entrées logicielles</string>
|
||||
<string name="retroachievements">RetroAchievements</string> <!-- Garder les deux mots collés car il s\'agit du nom officiel -->
|
||||
<string name="retroachievements_summary">Se connecter et gérer l\'intégration de RetroAchievements</string> <!-- Garder les deux mots collés car il s\'agit du nom officiel -->
|
||||
<string name="enable_rich_presence">Activer Rich Presence</string>
|
||||
<string name="rich_presence_summary">Permet aux autres utilisateurs de RetroAchievement de voir ce à quoi vous jouez</string>
|
||||
<string name="hardcore_mode">Mode Hardcore</string>
|
||||
<string name="hardcore_mode_summary">Gagnez le double de points, mais vous ne pouvez pas utiliser les sauvegardes d\'état, ni les triches, ni le ralenti en jeu</string>
|
||||
|
||||
<string name="cheats">Triches</string>
|
||||
<string name="cheats_summary">Activer et importer des codes de triche</string>
|
||||
<string name="enable_cheats">Activer les triches</string>
|
||||
@ -222,6 +242,8 @@
|
||||
<string name="label_rom_config_load_gba_rom">Charger la ROM GBA</string>
|
||||
<string name="label_rom_config_gba_rom_path">Chemin de la ROM GBA</string>
|
||||
<string name="label_rom_config_gba_save_path">Chemin de la sauvegarde GBA</string>
|
||||
<string name="rom_details_configuration_tab">Configuration</string>
|
||||
<string name="retro_achievements_tab">Retro Achievements</string>
|
||||
|
||||
<string name="theme_option_light">Clair</string>
|
||||
<string name="theme_option_dark">Sombre</string>
|
||||
@ -286,7 +308,38 @@
|
||||
<string name="background_name">Nom de l\'arrière-plan</string>
|
||||
<string name="background_deleted">Arrière-plan supprimé</string>
|
||||
<string name="background_add_wrong_orientation">L\'arrière-plan a été ajouté mais n\'est pas visible pour l\'orientation sélectionnée.</string>
|
||||
<string name="background_add_processing_failed">Le traitement de l\'image d\'arrière-plan sélectionnée a échoué. Vérifiez qu\'elle n\'est pas corrompue.</string>
|
||||
<string name="background_add_processing_failed">Le traitement de l\'image d\'arrière-plan sélectionnée a échoué. Veuillez vérifier qu\'elle n\'est pas corrompue.</string>
|
||||
|
||||
<string name="retro_achievements_login_description">Connectez-vous à RetroAchievements pour consulter les succès que vous pouvez déverouiller en jouant à ce jeu !</string>
|
||||
<string name="login_with_retro_achievements">Connexion à RetroAchievements</string>
|
||||
<string name="login">Connexion</string>
|
||||
<string name="retroachievements_login_summary">Connectez-vous avec RetroAchievements pour débloquer des succès tout en jouant !</string>
|
||||
<string name="retroachievements_login_status">Connecté en tant que %1$s</string>
|
||||
<string name="retroachievements_logout">Déconnexion</string>
|
||||
<string name="retroachievements_logout_confirmation">Are you sure you want to logout from RetroAchievements? You won\'t be able to unlock achievements while playing.</string>
|
||||
<string name="retro_achievements_login_error">Un problème est survenu lors de la tentative de connexion. Assurez-vous que votre nom d\'utilisateur et votre mot de passe sont corrects et que vous êtes connecté à Internet</string>
|
||||
<string name="retro_achievements_login_error_short">Un problème est survenu lors de la connexion</string>
|
||||
<string name="retro_achievements_load_error">Un problème est survenu lors du chargement des succès de ce jeu. Assurez-vous que vous êtes connecté à Internet et réessayez plus tard</string>
|
||||
<string name="retro_achievements_no_achievements">Aucun RetroAchievements n\'a été trouvé pour ce jeu</string>
|
||||
<string name="retro_achievements_relaunch_to_apply_settings">Redémarrez la ROM pour appliquer les nouveaux paramètres de RetroAchievements</string>
|
||||
<string name="username">Nom d\'utilisateur</string>
|
||||
<string name="password">Mot de passe</string>
|
||||
<string name="retry">Réessayer</string>
|
||||
<string name="points_abbreviated">PTS</string>
|
||||
<string name="points">Points</string>
|
||||
<string name="completed">Terminé</string>
|
||||
<string name="ra_mode_hardcore">Hardcore</string>
|
||||
<string name="ra_mode_softcore">Softcore</string>
|
||||
<string name="view_achievement">Consulter le succès</string>
|
||||
<string name="completed_achievements"><b>%1$d</b> sur <b>%2$d</b> (%3$d %%)</string>
|
||||
<string name="submitting_achievements">Envoi des RetroAchievements…</string>
|
||||
<string name="achievements_loaded">Succès chargés</string>
|
||||
<string name="achievements_unlocked_compact">Déverouillé : %1$d/%2$d</string>
|
||||
<string name="achievements_failed_load">Impossible de charger les succès !</string>
|
||||
<string name="achievements_failed_load_tip">Veuillez vérifier que vous êtes en ligne</string>
|
||||
<string name="achievement_unlocked">Succès déverouillé !</string>
|
||||
<string name="achievement_missable">Manquable</string>
|
||||
<string name="achievement_missable_description">Ce succès peut être manqué pendant une session de jeu.</string>
|
||||
|
||||
<string-array name="fast_forward_speed_multiplier_options">
|
||||
<item>Illimitée</item>
|
||||
@ -349,6 +402,12 @@
|
||||
<item>LCD</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="dsi_camera_source_options">
|
||||
<item>Écran noir</item>
|
||||
<item>Caméras physiques</item>
|
||||
<item>Image statique</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="audio_interpolation_options">
|
||||
<item>Aucune</item>
|
||||
<item>Linéaire</item>
|
||||
|
@ -15,20 +15,34 @@
|
||||
<string name="preview">Предпросмотр</string>
|
||||
<string name="undo">Отмена</string>
|
||||
<string name="update">Обновить</string>
|
||||
<string name="close">Закрыть</string>
|
||||
<string name="play">Запуск</string>
|
||||
<string name="ellipsis">…</string>
|
||||
|
||||
<string name="version_alpha">Альфа</string>
|
||||
<string name="version_beta">Бета</string>
|
||||
|
||||
<string name="console_ds">DS</string>
|
||||
<string name="console_dsi">DSi (экспериментально)</string>
|
||||
|
||||
<string name="console_ds_full">Nintendo DS</string>
|
||||
<string name="console_dsi_full">Nintendo DSi</string>
|
||||
|
||||
<string name="size_bytes">Б</string>
|
||||
<string name="size_kb">КБ</string>
|
||||
<string name="size_mb">МБ</string>
|
||||
<string name="size_gb">ГБ</string>
|
||||
|
||||
<string name="error_rom_not_found">Игра не найдена</string>
|
||||
<string name="error_rom_not_found_info">Выбранный ROM не найден. Убедитесь, что файл присутствует в текущем каталоге поиска.\n%1$s</string>
|
||||
<string name="error_rom_not_found_info">Выбранный образ игры не найден. Убедитесь, что файл присутствует в текущем каталоге поиска.\n%1$s</string>
|
||||
<string name="error_load_rom">Не удалось загрузить игру</string>
|
||||
<string name="error_load_rom_message">Ошибка при загрузке игры. Убедитесь, что образ не повреждён и повторите попытку.</string>
|
||||
<string name="error_load_firmware">Не удалось загрузить прошивку</string>
|
||||
<string name="error_load_firmware_message">Ошибка при загрузке прошивки (%1$s). Убедитесь, что образы прошивки и BIOS не повреждены, прошивка является загрузочной и повторите попытку.</string>
|
||||
<string name="error_load_rom_report_issue">Сгенерирован лог об ошибке, который может помочь устранить проблему. Создать на GitHub обращение с содержимым лога? Требуется аккаунт GitHub.</string>
|
||||
<string name="title_emulator_running">Эмулятор запущен</string>
|
||||
<string name="message_stop_emulation">Остановить эмуляцию и загрузить новый файл?</string>
|
||||
<string name="error_load_gba_rom">Не удалось загрузить файл GBA</string>
|
||||
<string name="error_load_gba_rom">Не удалось загрузить образ GBA</string>
|
||||
<string name="action_search_roms">Поиск игр</string>
|
||||
<string name="action_sort_roms">Сортировка</string>
|
||||
<string name="action_boot_firmware">Загрузить прошивку</string>
|
||||
@ -41,6 +55,27 @@
|
||||
<string name="download_progress_sizes">%1$.1fMB/%2$.1fMB</string>
|
||||
<string name="update_download_failed">Не удалось загрузить обновление</string>
|
||||
|
||||
<string name="dsiware_manager">Менеджер DSiWare</string>
|
||||
<string name="no_dsiware_titles_installed">Нет установленных игр DSiWare</string>
|
||||
<string name="dsiware_manager_no_dsi_setup">Система DSi не настроена</string>
|
||||
<string name="dsiware_manager_invalid_dsi_setup">Неправильные установки системы DSi</string>
|
||||
<string name="import_dsiware_title">Импорт игры DSiWare</string>
|
||||
<string name="dsiware_manager_setup">Настройка</string>
|
||||
<string name="dsiware_manager_fix_setup">Исправить настройку</string>
|
||||
<string name="dsiware_manager_load_error">Ошибка при создании списка игр DSiWare. Проверьте установки DSi</string>
|
||||
<string name="dsiware_manager_import_title_error_open_nand_failed">Не удалось открыть NAND</string>
|
||||
<string name="dsiware_manager_import_title_error_open_file_failed">Ошибка запуска выбранного файла</string>
|
||||
<string name="dsiware_manager_import_title_error_not_dsiware_title">Не является игрой DSiWare</string>
|
||||
<string name="dsiware_manager_import_title_error_title_already_imported">Данная игра DSiWare уже импортирована</string>
|
||||
<string name="dsiware_manager_import_title_error_insatll_failed">Не удалось установить игру</string>
|
||||
<string name="dsiware_manager_import_title_error_metadat_fetch_failed">Не удалось загрузить метаданные игры. Проверьте соединение с Интернет</string>
|
||||
<string name="dsiware_manager_import_title_error_unknown">Произошла неизвестная ошибка</string>
|
||||
<string name="dsiware_import_from_file">Из файла</string>
|
||||
<string name="dsiware_import_from_rom_list">Из списка игр</string>
|
||||
|
||||
<string name="select_dsiware_title">Выберите игру DSiWare</string>
|
||||
<string name="no_dsiware_roms_found">Не найдены игры DSiWare</string>
|
||||
|
||||
<string name="info_fps">FPS: %1$d</string>
|
||||
<string name="info_loading">ЗАГРУЗКА…</string>
|
||||
<string name="save_state_slot">%1$s.</string>
|
||||
@ -49,9 +84,13 @@
|
||||
<string name="cant_load_empty_slot">Невозможно загрузить пустой слот сохранения</string>
|
||||
<string name="failed_reset_emulation">Не удалось перезапустить эмуляцию</string>
|
||||
<string name="save_states_not_supported">Сохранения недоступны во время запуска прошивки</string>
|
||||
<string name="save_states_unavailable_ra_hardcore_enabled">Сохранения недоступны пока включен режим хардкора</string>
|
||||
<string name="rewind_time_seconds">%1$ss</string>
|
||||
<string name="rewind_time_minutes_seconds">%1$dm%2$ss</string>
|
||||
<string name="rewind_not_enabled">Перемотка назад не включена</string>
|
||||
<string name="rewind_not_enabled">Обратная перемотка не включена</string>
|
||||
<string name="rewind_unavailable_ra_hardcore_enabled">Обратная перемотка недоступна пока включен режим хардкора</string>
|
||||
<string name="no_image_selected">Образ не выбран</string>
|
||||
<string name="failed_to_load_image">Не удалось загрузить образ</string>
|
||||
|
||||
<string name="action_sort_alphabetically">По алфавиту</string>
|
||||
<string name="action_sort_recently_played">Недавние</string>
|
||||
@ -72,68 +111,74 @@
|
||||
<string name="error_invalid_directory">Неправильный каталог</string>
|
||||
<string name="error_invalid_directory_description">Невозможно произвести запись в выбранный каталог. Пожалуйста, укажите другой. Если выбран архив, попробуйте извлечь его содержимое.</string>
|
||||
<string name="no_roms_found">Игры не найдены</string>
|
||||
<string name="dsiware_title_cannot_be_launched_directly_title">Запуск игр DSiWare невозможен</string>
|
||||
<string name="dsiware_title_cannot_be_launched_directly_message">Игры DSiWare нельзя запускать напрямую. Они должны быть установлены в NAND (с помощью менеджера DSiWare) и запускаться через программное обеспечение DSi.</string>
|
||||
|
||||
<string name="press_any_button">Нажмите любую кнопку…</string>
|
||||
|
||||
<string name="choose_component">Выберите компонент</string>
|
||||
|
||||
<string name="pause">Пауза</string>
|
||||
<string name="achievements">Достижения</string>
|
||||
<string name="reset">Сброс</string>
|
||||
<string name="exit">Выход</string>
|
||||
|
||||
<string name="settings">Настройки</string>
|
||||
<string name="save_state">Сохранение</string>
|
||||
<string name="load_state">Загрузка</string>
|
||||
<string name="rewind">Перемотка</string>
|
||||
<string name="rewind">Обратная перемотка</string>
|
||||
<string name="save_slot">Слот сохранения</string>
|
||||
<string name="empty_slot">%1$s. <Пусто></string>
|
||||
<string name="quick_slot">Быстрый слот</string>
|
||||
<string name="saved">Сохранено</string>
|
||||
<string name="loaded">Загружено</string>
|
||||
<string name="title_activity_settings">Настройки</string>
|
||||
<string name="category_general">Основные</string>
|
||||
<string name="category_general">Общие</string>
|
||||
<string name="category_general_summary">Тема, общие настройки</string>
|
||||
<string name="category_roms">Образы игр</string>
|
||||
<string name="category_roms_summary">Каталог поиска, сглаживание иконок, настройки кэширования</string>
|
||||
<string name="category_system">Система</string>
|
||||
<string name="category_system_summary">Настройки прошивки, выбор BIOS, JIT</string>
|
||||
<string name="theme">Тема</string>
|
||||
<string name="fast_forward_max_speed">Макс. ускорение</string>
|
||||
<string name="rewind_description">Включение активирует автоматическое сохранение через заданный период времени. При необходимости отмотать прогресс откройте меню перемотки и выберите сохранение, с которого хотите продолжить.\n\nУчитывайте, что включение данной функции может привести к появлению подтормаживаний, если ваше устройство недостаточно мощное. Также, потребуется значительный объём памяти в зависимости от длительности хранения и частоты создания сохранений.</string>
|
||||
<string name="rewind_save_period">Частота сохранения</string>
|
||||
<string name="fast_forward_max_speed">Максимальное ускорение</string>
|
||||
<string name="rewind_description">При включении активирует автоматическое сохранение через заданный период времени. Для того, чтобы отмотать прогресс откройте меню обратной перемотки и выберите сохранение, с которого хотите продолжить.\n\nПри включении обратной перемотки возможно падение скорости, если устройство недостаточно мощное. В зависимости от выбранных параметров может потребоваться значительный объём свободной памяти.</string>
|
||||
<string name="rewind_save_period">Частота сохранений</string>
|
||||
<string name="rewind_length">Буфер перемотки</string>
|
||||
<string name="rewind_max_memory_usage">Макс. объём памяти: %1$s</string>
|
||||
<string name="rewind_max_memory_usage">Максимальный объём памяти: %1$s</string>
|
||||
<string name="rewind_memory_usage_above_recommended_limit">Превышен рекомендуемый объём памяти</string>
|
||||
<string name="sustained_performance_mode">Режим устойчивой производительности</string>
|
||||
<string name="sustained_performance_mode_summary">При включении снижает пиковую производительность и уменьшает троттлинг.</string>
|
||||
<string name="rom_icon_filtering">Сглаживание иконок</string>
|
||||
<string name="max_rom_cache_size">Макс. размер кэша игр</string>
|
||||
<string name="clear_extracted_rom_cache">Удалить кэш извлечённых игр</string>
|
||||
<string name="max_rom_cache_size">Максимальный размер кэша игр</string>
|
||||
<string name="clear_extracted_rom_cache">Очистить кэш извлечённых игр</string>
|
||||
<string name="cache_size">Размер кэша: %s</string>
|
||||
<string name="cache_size_calculating">Размер кэша: вычисление…</string>
|
||||
<string name="rom_search_directory">Каталог поиска</string>
|
||||
<string name="use_custom_bios">Исп. внешние BIOS и прошивку</string>
|
||||
<string name="console_type">Система по умолчанию</string>
|
||||
<string name="internal_firmware_settings">Настройки прошивки</string>
|
||||
<string name="internal_firmware_settings_summary">Параметры встроенного программного обеспечения.</string>
|
||||
<string name="custom_bios_firmware">Ручная установка BIOS и прошивки</string>
|
||||
<string name="custom_bios_firmware_description">Использование внешних BIOS и прошивки позволяет запускать и настраивать прошивку DS без загрузки игры, как на реальной системе</string>
|
||||
<string name="internal_firmware_settings_summary">Установки встроенного программного обеспечения.</string>
|
||||
<string name="custom_bios_firmware">Ручной выбор BIOS и прошивки</string>
|
||||
<string name="custom_bios_firmware_description">Использование внешних BIOS и прошивки позволяет запускать и настраивать программное обеспечение DS без запуска игр, как на реальной консоли.</string>
|
||||
<string name="bios_directory">Каталог DS BIOS</string>
|
||||
<string name="dsi_bios_directory">Каталог DSi BIOS</string>
|
||||
<string name="show_boot_screen">Показывать экран загрузки</string>
|
||||
<string name="mac_address">MAC-адрес</string>
|
||||
<string name="randomize_mac_address">Случайный MAC-адрес</string>
|
||||
<string name="randomize_mac_address_summary">Если включено, при каждой загрузке будет использоваться случайный MAC-адрес. Это может привести к тому, что некоторые игры будут определять, что они запущены на другой системе и к сбоям событий с привязкой ко времени.</string>
|
||||
<string name="randomize_mac_address_summary">Если включено, при каждой загрузке будет сгенерирован случайный MAC-адрес. Это может привести к тому, что некоторые игры будут определять, что они запущены на другой системе и к сбоям событий с привязкой ко времени.</string>
|
||||
<string name="generate_new_mac_address">Сгенерировать новый</string>
|
||||
<string name="generate_new_mac_address_description">Создание нового MAC-адреса может привести к тому, что игры будут определять, что они запущены на другой системе.</string>
|
||||
<string name="generate_new_mac_address_description">Создание нового MAC-адреса может привести к тому, что игры будут определять, что они запущены на другой системе.</string>
|
||||
<string name="enable_jit">Включить JIT</string>
|
||||
<string name="enable_jit_summary">Улучшает производительность, но может вызывать подвисания и вылеты.</string>
|
||||
<string name="jit_not_supported">JIT доступен для 64-битных устройств.</string>
|
||||
<string name="jit_not_supported">JIT доступен только для 64-битных устройств.</string>
|
||||
<string name="threaded_rendering">Рендеринг в отдельном потоке</string>
|
||||
<string name="fps_counter_position">Положение счётчика FPS</string>
|
||||
<string name="threaded_rendering_summary">Повышает производительность в 3D-играх, но может приводить к ошибкам графики.</string>
|
||||
<string name="fps_counter_position">Отображение счётчика FPS</string>
|
||||
<string name="threaded_rendering_summary">Повышает производительность в 3D-играх, но может вызывать проблемы с графикой.</string>
|
||||
<string name="category_video">Видео</string>
|
||||
<string name="category_video_summary">Сглаживание, рендеринг в отдельном потоке, счётчик FPS</string>
|
||||
<string name="filter">Сглаживание</string>
|
||||
<string name="dsi_camera_source">Источник камеры DSi</string>
|
||||
<string name="dsi_camera_image">Образ камеры DSi</string>
|
||||
<string name="category_audio">Аудио</string>
|
||||
<string name="category_audio_summary">Громкость, задержка, источник микрофона</string>
|
||||
<string name="enable_sound">Включить звук</string>
|
||||
@ -155,14 +200,21 @@
|
||||
<string name="key_mapping_summary">Назначить кнопки контроллера</string>
|
||||
<string name="show_soft_input">Экранные кнопки</string>
|
||||
<string name="vibrate_on_touch">Вибрация при касании</string>
|
||||
<string name="vibration_strength">Сила вибрации</string>
|
||||
<string name="vibration_strength">Интенсивность вибрации</string>
|
||||
<string name="soft_input_opacity">Видимость экранных кнопок</string>
|
||||
<string name="retroachievements">RetroAchievements</string> <!-- Keep words together because this is the official name -->
|
||||
<string name="retroachievements_summary">Вход и управление интеграцией с RetroAchievements</string> <!-- Keep words together because this is the official name -->
|
||||
<string name="enable_rich_presence">Включить статус активности</string>
|
||||
<string name="rich_presence_summary">Позволяет другим пользователям RetroAchievements видеть запущенную вами игру.</string>
|
||||
<string name="hardcore_mode">Режим хардкора</string>
|
||||
<string name="hardcore_mode_summary">Удваивает количество очков, но отключает быстрые сохранения, читы и замедление во время игры</string>
|
||||
|
||||
<string name="cheats">Чит-коды</string>
|
||||
<string name="cheats_summary">Активация и импорт чит-кодов</string>
|
||||
<string name="cheats_summary">Включение и импорт чит-кодов</string>
|
||||
<string name="enable_cheats">Включить чит-коды</string>
|
||||
<string name="cheats_database">База чит-кодов (в формате XML)</string>
|
||||
<string name="import_cheats">Импорт чит-кодов</string>
|
||||
<string name="import_cheats_summary">Для доступа к чит-кодам импортируйте их из внешней базы. Поддерживаются только файлы в формате XML. Все ранее добавленные коды будут удалены.</string>
|
||||
<string name="import_cheats_summary">Для доступа к чит-кодам импортируйте их из внешней базы. Поддерживаются файлы в формате XML. Все ранее добавленные коды будут удалены.</string>
|
||||
<string name="rom_shortcut">Ярлык для игры</string>
|
||||
|
||||
<string name="firmware_nickname">Никнейм</string>
|
||||
@ -176,7 +228,9 @@
|
||||
<string name="starting">Запуск…</string>
|
||||
<string name="move_to_background">Переместить на задний план</string>
|
||||
<string name="failed_save_cheat_changes">Не удалось сохранить изменения чит-кода</string>
|
||||
<string name="no_cheats_found">Не найдены чит-коды для текущей игры. Попробуйте импортировать другую базу.</string>
|
||||
<string name="no_cheats_found">Не найдены чит-коды для данной игры. Попробуйте импортировать другую базу.</string>
|
||||
<string name="enabled_cheats">Активные чит-коды</string>
|
||||
<string name="no_enabled_cheats_for_rom">Нет активных чит-кодов для данной игры</string>
|
||||
<string name="day">День</string>
|
||||
<string name="month">Месяц</string>
|
||||
<string name="symbol_increase">+</string>
|
||||
@ -184,9 +238,11 @@
|
||||
|
||||
<string name="rom_settings">Настройки игры</string> <!-- Accessibility string. Should not use acronyms -->
|
||||
<string name="label_rom_config_console">Система загрузки</string>
|
||||
<string name="label_rom_config_load_gba_rom">Загрузить файл GBA</string>
|
||||
<string name="label_rom_config_gba_rom_path">Путь к файлу GBA</string>
|
||||
<string name="label_rom_config_load_gba_rom">Загрузить образ GBA</string>
|
||||
<string name="label_rom_config_gba_rom_path">Путь к образу GBA</string>
|
||||
<string name="label_rom_config_gba_save_path">Путь к сохранению GBA</string>
|
||||
<string name="rom_details_configuration_tab">Конфигурация</string>
|
||||
<string name="retro_achievements_tab">Retro Achievements</string> <!-- Keep words separated to help with word wrapping -->
|
||||
|
||||
<string name="theme_option_light">Светлая</string>
|
||||
<string name="theme_option_dark">Тёмная</string>
|
||||
@ -215,7 +271,7 @@
|
||||
<string name="input_reset">@string/reset</string>
|
||||
<string name="input_dpad">DPAD</string>
|
||||
<string name="input_abxy_buttons">Кнопки ABXY</string>
|
||||
<string name="input_swap_screens">Поменять экраны</string>
|
||||
<string name="input_swap_screens">Переключить экраны</string>
|
||||
<string name="input_quick_save">Быстрое сохранение</string>
|
||||
<string name="input_quick_load">Быстрая загрузка</string>
|
||||
|
||||
@ -252,6 +308,37 @@
|
||||
<string name="background_deleted">Фон удалён</string>
|
||||
<string name="background_add_wrong_orientation">Фон добавлен, но не отображается в выбранной ориентации.</string>
|
||||
<string name="background_add_processing_failed">Не удалось обработать выбранное изображение. Убедитесь, что файл не повреждён.</string>
|
||||
|
||||
<string name="retro_achievements_login_description">Выполните вход в RetroAchievements для просмотра достижений, доступных для данной игры.</string>
|
||||
<string name="login_with_retro_achievements">Войти в RetroAchievements</string>
|
||||
<string name="login">Вход</string>
|
||||
<string name="retroachievements_login_summary">Выполните вход в RetroAchievements для получения достижений во время игры!</string>
|
||||
<string name="retroachievements_login_status">Выполнен вход для %1$s</string>
|
||||
<string name="retroachievements_logout">Выход</string>
|
||||
<string name="retroachievements_logout_confirmation">Вы подтверждаете выход из RetroAchievements? Получение достижений во время игры будет недоступно.</string>
|
||||
<string name="retro_achievements_login_error">Ошибка при попытке входа. Проверьте соединение с Интернет и убедитесь, что имя пользователя и пароль введены верно.</string>
|
||||
<string name="retro_achievements_login_error_short">Ошибка при попытке входа.</string>
|
||||
<string name="retro_achievements_load_error">Ошибка при загрузке достижений для данной игры. Проверьте соединение с Интернет и повторите попытку.</string>
|
||||
<string name="retro_achievements_no_achievements">Не найдены доступные достижения для данной игры.</string>
|
||||
<string name="retro_achievements_relaunch_to_apply_settings">Перезапустите игру, чтобы применить новые настройки RetroAchievements</string>
|
||||
<string name="username">Имя пользователя</string>
|
||||
<string name="password">Пароль</string>
|
||||
<string name="retry">Повторить</string>
|
||||
<string name="points_abbreviated">ОЧК</string>
|
||||
<string name="points">Очки</string>
|
||||
<string name="completed">Завершено</string>
|
||||
<string name="ra_mode_hardcore">Хардкор</string>
|
||||
<string name="ra_mode_softcore">Софткор</string>
|
||||
<string name="view_achievement">Просмотр достижения</string>
|
||||
<string name="completed_achievements"><b>%1$d</b> из <b>%2$d</b> (%3$d%%)</string>
|
||||
<string name="submitting_achievements">Отправка RetroAchievements…</string>
|
||||
<string name="achievements_loaded">Достижения загружены</string>
|
||||
<string name="achievements_unlocked_compact">Открыто: %1$d/%2$d</string>
|
||||
<string name="achievements_failed_load">Не удалось загрузить достижения!</string>
|
||||
<string name="achievements_failed_load_tip">Убедитесь, что вы онлайн</string>
|
||||
<string name="achievement_unlocked">Открыто достижение!</string>
|
||||
<string name="achievement_missable">Безвозвратное</string>
|
||||
<string name="achievement_missable_description">Данное достижение может быть упущено при прохождении.</string>
|
||||
|
||||
<string-array name="fast_forward_speed_multiplier_options">
|
||||
<item>Без ограничения</item>
|
||||
@ -314,6 +401,12 @@
|
||||
<item>LCD</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="dsi_camera_source_options">
|
||||
<item>Чёрный экран</item>
|
||||
<item>Камеры устройства</item>
|
||||
<item>Статичное изображение</item>
|
||||
</string-array>
|
||||
|
||||
<string-array name="audio_interpolation_options">
|
||||
<item>Нет</item>
|
||||
<item>Линейная</item>
|
||||
|
@ -49,7 +49,10 @@
|
||||
<string name="action_refresh_rom_list">Refresh ROM list</string>
|
||||
<string name="hint_search_roms">Search ROMs…</string>
|
||||
<string name="update_available">Update available: %1$s</string>
|
||||
<string name="nightly_update_available">Nightly update available</string>
|
||||
<string name="nightly_update_available_message">Do you want to download it now?</string>
|
||||
<string name="skip_update">Skip</string>
|
||||
<string name="remind_later_update">Later</string>
|
||||
<string name="downloading_update">Downloading update…</string>
|
||||
<string name="starting_download">Starting download…</string>
|
||||
<string name="download_progress_sizes">%1$.1fMB/%2$.1fMB</string>
|
||||
@ -148,6 +151,8 @@
|
||||
<string name="rewind_memory_usage_above_recommended_limit">Memory usage above recommended limit</string>
|
||||
<string name="sustained_performance_mode">Sustained performance mode</string>
|
||||
<string name="sustained_performance_mode_summary">When enabled, peak performance will be lower but thermal throttling will be less pronounced.</string>
|
||||
<string name="check_for_updates">Check for updates</string>
|
||||
<string name="check_for_updates_summary">When enabled, the app will periodically check for available updates</string>
|
||||
<string name="rom_icon_filtering">Icon filtering</string>
|
||||
<string name="max_rom_cache_size">Max. ROM cache size</string>
|
||||
<string name="clear_extracted_rom_cache">Clear extracted ROM cache</string>
|
||||
|
4
app/src/main/res/xml/pref_general_updates.xml
Normal file
@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<PreferenceScreen>
|
||||
<!-- Empty on purpose since not all builds have update options -->
|
||||
</PreferenceScreen>
|
BIN
app/src/nightly/res/mipmap-hdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 5.8 KiB |
BIN
app/src/nightly/res/mipmap-hdpi/ic_launcher_foreground.png
Normal file
After Width: | Height: | Size: 5.4 KiB |
BIN
app/src/nightly/res/mipmap-mdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 3.3 KiB |
BIN
app/src/nightly/res/mipmap-mdpi/ic_launcher_foreground.png
Normal file
After Width: | Height: | Size: 3.0 KiB |
BIN
app/src/nightly/res/mipmap-xhdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 8.4 KiB |
BIN
app/src/nightly/res/mipmap-xhdpi/ic_launcher_foreground.png
Normal file
After Width: | Height: | Size: 8.1 KiB |
BIN
app/src/nightly/res/mipmap-xxhdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 14 KiB |
BIN
app/src/nightly/res/mipmap-xxhdpi/ic_launcher_foreground.png
Normal file
After Width: | Height: | Size: 15 KiB |
BIN
app/src/nightly/res/mipmap-xxxhdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 21 KiB |
BIN
app/src/nightly/res/mipmap-xxxhdpi/ic_launcher_foreground.png
Normal file
After Width: | Height: | Size: 23 KiB |
4
app/src/nightly/res/values/strings.xml
Normal file
@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="app_name">melonDS Nightly</string>
|
||||
</resources>
|
@ -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
|
||||
}
|
||||
}
|
@ -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 {
|
||||
|
@ -1,9 +1,9 @@
|
||||
object AppConfig {
|
||||
const val compileSdkVersion = 33
|
||||
const val compileSdkVersion = 34
|
||||
const val targetSdkVersion = compileSdkVersion
|
||||
const val minSdkVersion = 24
|
||||
const val ndkVersion = "25.1.8937393"
|
||||
|
||||
const val versionCode = 26
|
||||
const val versionName = "Beta 1.8.0"
|
||||
const val versionCode = 27
|
||||
const val versionName = "Beta 1.9.0"
|
||||
}
|
@ -3,11 +3,11 @@ apply plugin: 'kotlin-android'
|
||||
|
||||
android {
|
||||
namespace "com.smp.masterswitchpreference"
|
||||
compileSdk 33
|
||||
compileSdk 34
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion 19
|
||||
targetSdkVersion 33
|
||||
targetSdkVersion 34
|
||||
versionCode 6
|
||||
versionName "0.9.4"
|
||||
|
||||
|