add launcher update check

This commit is contained in:
qimiko committed 2024-01-23 03:15:39 -07:00
1 parent 199b6f142b
commit 7f64236e89
8 files changed
+139 -11

No files matched your search

@@ -28,6 +28,7 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalUriHandler
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.pluralStringResource
import androidx.compose.ui.res.stringResource
@@ -94,6 +95,43 @@ class MainActivity : ComponentActivity() {
}
}
@Composable
fun LauncherUpdateIndicator(modifier: Modifier = Modifier, openTo: String, onDismiss: () -> Unit) {
val uriHandler = LocalUriHandler.current
ElevatedCard(modifier) {
Column(
// buttons add enough padding already, lower to compensate
modifier = Modifier.padding(
top = 20.dp,
start = 12.dp,
end = 12.dp,
bottom = 8.dp
),
verticalArrangement = Arrangement.spacedBy(12.dp),
) {
Text(
stringResource(R.string.launcher_update_available),
modifier = Modifier.padding(horizontal = 10.dp)
)
Row(modifier = Modifier.align(Alignment.End)) {
TextButton(onClick = onDismiss) {
Text(stringResource(R.string.launcher_update_dismiss))
}
Spacer(Modifier.size(4.dp))
TextButton(onClick = { uriHandler.openUri(openTo) },) {
Text(stringResource(R.string.launcher_download))
}
}
}
}
}
@Composable
fun UpdateProgressIndicator(
message: String,
@@ -322,6 +360,22 @@ fun UpdateCard(releaseViewModel: ReleaseViewModel, modifier: Modifier = Modifier
}
}
}
val nextUpdate by releaseViewModel.nextLauncherUpdate.collectAsState()
val nextUpdateValue = nextUpdate
if (!releaseViewModel.isInUpdate && nextUpdateValue != null) {
val updateUrl = nextUpdateValue.getLauncherDownload()?.browserDownloadUrl
?: nextUpdateValue.htmlUrl
LauncherUpdateIndicator(
modifier = modifier,
openTo = updateUrl,
onDismiss = {
releaseViewModel.dismissLauncherUpdate()
}
)
}
}
fun onLaunch(context: Context) {
@@ -474,7 +528,8 @@ fun MainScreen(
UpdateCard(
releaseViewModel,
modifier = Modifier.padding(12.dp)
modifier = Modifier
.padding(12.dp)
)
}
@@ -27,10 +27,10 @@ class LogViewModel: ViewModel() {
private val _lineState = mutableStateListOf<LogLine>()
val lineState = _lineState
private var _isLoading = MutableStateFlow(false)
var isLoading = _isLoading.asStateFlow()
private val _isLoading = MutableStateFlow(false)
val isLoading = _isLoading.asStateFlow()
var logJob: Job? = null
private var logJob: Job? = null
var filterCrashes = false
private set
@@ -23,7 +23,8 @@ class Release(
val tagName: String,
val createdAt: Instant,
val publishedAt: Instant,
val assets: List<Asset>
val assets: List<Asset>,
val htmlUrl: String
) {
fun getDescription(): String {
if (tagName == "nightly") {
@@ -42,7 +43,7 @@ class Release(
return createdAt.epochSeconds
}
fun getAndroidDownload(): Asset? {
fun getGeodeDownload(): Asset? {
// try to find an asset that matches the architecture first
val platform = LaunchUtils.platformName
@@ -51,4 +52,22 @@ class Release(
it.name.endsWith(releaseSuffix)
}
}
fun getLauncherDownload(): Asset? {
val platform = LaunchUtils.platformName
val use32BitPlatform = platform == "android32"
return assets.find { asset ->
/* you know it's good when you pull out the truth table
* u32bp | contains | found
* 1 | 1 | 1
* 0 | 1 | 0
* 1 | 0 | 0
* 0 | 0 | 1
* surprise! it's an xnor
*/
(asset.name.contains("android32")) == use32BitPlatform
}
}
}
@@ -2,6 +2,8 @@ package com.geode.launcher.updater
import android.content.Context
import android.util.Log
import androidx.compose.runtime.mutableStateOf
import com.geode.launcher.BuildConfig
import com.geode.launcher.utils.DownloadUtils
import com.geode.launcher.utils.LaunchUtils
import com.geode.launcher.utils.PreferenceUtils
@@ -77,6 +79,9 @@ class ReleaseManager private constructor(
val isInUpdate: Boolean
get() = _uiState.value !is ReleaseManagerState.Failure && _uiState.value !is ReleaseManagerState.Finished
private val _availableLauncherUpdate = MutableStateFlow<Release?>(null)
val availableLauncherUpdate = _availableLauncherUpdate.asStateFlow()
private fun sendError(e: Exception) {
_uiState.value = ReleaseManagerState.Failure(e)
@@ -87,6 +92,15 @@ class ReleaseManager private constructor(
}
}
fun dismissLauncherUpdate() {
val update = availableLauncherUpdate.value ?: return
val sharedPreferences = PreferenceUtils.get(applicationContext)
sharedPreferences.setString(PreferenceUtils.Key.LAST_DISMISSED_UPDATE, update.tagName)
_availableLauncherUpdate.value = null
}
private suspend fun getLatestRelease(): Release? {
val sharedPreferences = PreferenceUtils.get(applicationContext)
val useNightly = sharedPreferences.getBoolean(PreferenceUtils.Key.RELEASE_CHANNEL)
@@ -95,7 +109,7 @@ class ReleaseManager private constructor(
}
private suspend fun performUpdate(release: Release) {
val releaseAsset = release.getAndroidDownload()
val releaseAsset = release.getGeodeDownload()
if (releaseAsset == null) {
val noAssetException = Exception("missing Android download")
_uiState.value = ReleaseManagerState.Failure(noAssetException)
@@ -153,6 +167,24 @@ class ReleaseManager private constructor(
return originalFileHash != currentFileHash
}
private fun checkLauncherUpdate(launcherUpdate: Release) {
if (BuildConfig.DEBUG) {
println("ignoring launcher update check - in debug mode!")
return
}
val sharedPreferences = PreferenceUtils.get(applicationContext)
val lastDismissedVersion = sharedPreferences.getString(PreferenceUtils.Key.LAST_DISMISSED_UPDATE)
if (!lastDismissedVersion.isNullOrEmpty() && launcherUpdate.tagName == lastDismissedVersion) {
return
}
if (launcherUpdate.tagName != BuildConfig.VERSION_NAME) {
_availableLauncherUpdate.value = launcherUpdate
}
}
private suspend fun checkForNewRelease(allowOverwriting: Boolean = false) {
val release = try {
getLatestRelease()
@@ -161,6 +193,17 @@ class ReleaseManager private constructor(
return
}
val launcherRelease = try {
releaseRepository.getLatestLauncherRelease()
} catch (e: Exception) {
sendError(e)
return
}
if (launcherRelease != null) {
checkLauncherUpdate(launcherRelease)
}
if (release == null) {
_uiState.value = ReleaseManagerState.Finished()
return
@@ -43,8 +43,6 @@ class ReleaseRepository(private val httpClient: OkHttpClient) {
.addHeader(GITHUB_API_HEADER, GITHUB_API_VERSION)
.build()
println("fetching url $url")
val call = httpClient.newCall(request)
val response = call.executeCoroutine()
@@ -7,6 +7,7 @@ import androidx.lifecycle.ViewModelProvider.AndroidViewModelFactory.Companion.AP
import androidx.lifecycle.viewModelScope
import androidx.lifecycle.viewmodel.initializer
import androidx.lifecycle.viewmodel.viewModelFactory
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
@@ -52,6 +53,8 @@ class ReleaseViewModel(private val application: Application): ViewModel() {
val isInUpdate
get() = ReleaseManager.get(application).isInUpdate
val nextLauncherUpdate = ReleaseManager.get(application).availableLauncherUpdate
var hasPerformedCheck = false
private set
@@ -77,7 +80,7 @@ class ReleaseViewModel(private val application: Application): ViewModel() {
fun useGlobalCheckState() {
hasPerformedCheck = true
viewModelScope.launch {
viewModelScope.launch(Dispatchers.IO) {
val releaseFlow = ReleaseManager.get(application)
.uiState
@@ -88,11 +91,17 @@ class ReleaseViewModel(private val application: Application): ViewModel() {
fun runReleaseCheck(isManual: Boolean = false) {
hasPerformedCheck = true
viewModelScope.launch {
viewModelScope.launch(Dispatchers.IO) {
val releaseFlow = ReleaseManager.get(application)
.checkForUpdates(isManual)
syncUiState(releaseFlow)
}
}
fun dismissLauncherUpdate() {
viewModelScope.launch(Dispatchers.IO) {
ReleaseManager.get(application).dismissLauncherUpdate()
}
}
}
@@ -99,6 +99,7 @@ class PreferenceUtils(private val sharedPreferences: SharedPreferences) {
THEME,
BLACK_BACKGROUND,
CURRENT_RELEASE_MODIFIED,
LAST_DISMISSED_UPDATE
}
private fun defaultValueForBooleanKey(key: Key): Boolean {
@@ -120,6 +121,7 @@ class PreferenceUtils(private val sharedPreferences: SharedPreferences) {
Key.THEME -> "PreferenceTheme"
Key.BLACK_BACKGROUND -> "PreferenceBlackBackground"
Key.CURRENT_RELEASE_MODIFIED -> "PreferenceReleaseModifiedHash"
Key.LAST_DISMISSED_UPDATE -> "PreferenceLastDismissedUpdate"
}
}
+2
View File
@@ -25,6 +25,8 @@
<string name="launcher_unsupported_version_description">
The current version of Geometry Dash is not compatible with the version supported by the launcher (%2$s). A launcher update is required to fix this.\nLaunch will continue, but crashes may occur!
</string>
<string name="launcher_update_available">A new launcher update is available!</string>
<string name="launcher_update_dismiss">Dismiss</string>
<!-- Updater strings -->
<string name="release_fetch_failed">Update failed.\n%1$s</string>