Add update check for GitHub source
This commit is contained in:
parent
3c630e8a5b
commit
0cb60d15b2
|
@ -11,6 +11,7 @@ import kotlinx.coroutines.launch
|
|||
import me.ash.reader.data.module.ApplicationScope
|
||||
import me.ash.reader.data.module.DispatcherDefault
|
||||
import me.ash.reader.data.repository.*
|
||||
import me.ash.reader.data.source.AppNetworkDataSource
|
||||
import me.ash.reader.data.source.OpmlLocalDataSource
|
||||
import me.ash.reader.data.source.ReaderDatabase
|
||||
import me.ash.reader.data.source.RssNetworkDataSource
|
||||
|
@ -30,6 +31,9 @@ class App : Application(), Configuration.Provider {
|
|||
@Inject
|
||||
lateinit var workManager: WorkManager
|
||||
|
||||
@Inject
|
||||
lateinit var appNetworkDataSource: AppNetworkDataSource
|
||||
|
||||
@Inject
|
||||
lateinit var opmlLocalDataSource: OpmlLocalDataSource
|
||||
|
||||
|
@ -39,6 +43,9 @@ class App : Application(), Configuration.Provider {
|
|||
@Inject
|
||||
lateinit var rssHelper: RssHelper
|
||||
|
||||
@Inject
|
||||
lateinit var appRepository: AppRepository
|
||||
|
||||
@Inject
|
||||
lateinit var stringsRepository: StringsRepository
|
||||
|
||||
|
@ -72,6 +79,7 @@ class App : Application(), Configuration.Provider {
|
|||
applicationScope.launch(dispatcherDefault) {
|
||||
accountInit()
|
||||
workerInit()
|
||||
checkUpdate()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -90,6 +98,10 @@ class App : Application(), Configuration.Provider {
|
|||
rssRepository.get().doSync()
|
||||
}
|
||||
|
||||
private suspend fun checkUpdate() {
|
||||
appRepository.checkUpdate()
|
||||
}
|
||||
|
||||
override fun getWorkManagerConfiguration(): Configuration =
|
||||
Configuration.Builder()
|
||||
.setWorkerFactory(workerFactory)
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package me.ash.reader
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Looper
|
||||
import android.util.Log
|
||||
import android.widget.Toast
|
||||
import java.lang.Thread.UncaughtExceptionHandler
|
||||
|
@ -12,7 +13,9 @@ class CrashHandler(private val context: Context) : UncaughtExceptionHandler {
|
|||
}
|
||||
|
||||
override fun uncaughtException(p0: Thread, p1: Throwable) {
|
||||
Looper.prepare()
|
||||
Toast.makeText(context, p1.message, Toast.LENGTH_LONG).show()
|
||||
Looper.loop()
|
||||
p1.printStackTrace()
|
||||
Log.e("RLog", "uncaughtException: ${p1.message}" )
|
||||
android.os.Process.killProcess(android.os.Process.myPid());
|
||||
|
|
23
app/src/main/java/me/ash/reader/data/entity/GitHubRelease.kt
Normal file
23
app/src/main/java/me/ash/reader/data/entity/GitHubRelease.kt
Normal file
|
@ -0,0 +1,23 @@
|
|||
package me.ash.reader.data.entity
|
||||
|
||||
data class GitHubRelease(
|
||||
val html_url: String? = null,
|
||||
val tag_name: String? = null,
|
||||
val name: String? = null,
|
||||
val draft: Boolean? = null,
|
||||
val prerelease: Boolean? = null,
|
||||
val created_at: String? = null,
|
||||
val published_at: String? = null,
|
||||
val assets: List<AssetsItem>? = null,
|
||||
val body: String? = null,
|
||||
)
|
||||
|
||||
data class AssetsItem(
|
||||
val name: String? = null,
|
||||
val content_type: String? = null,
|
||||
val size: Int? = null,
|
||||
val download_count: Int? = null,
|
||||
val created_at: String? = null,
|
||||
val updated_at: String? = null,
|
||||
val browser_download_url: String? = null,
|
||||
)
|
|
@ -4,6 +4,7 @@ import dagger.Module
|
|||
import dagger.Provides
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import me.ash.reader.data.source.AppNetworkDataSource
|
||||
import me.ash.reader.data.source.FeverApiDataSource
|
||||
import me.ash.reader.data.source.GoogleReaderApiDataSource
|
||||
import me.ash.reader.data.source.RssNetworkDataSource
|
||||
|
@ -13,6 +14,11 @@ import javax.inject.Singleton
|
|||
@InstallIn(SingletonComponent::class)
|
||||
object RetrofitModule {
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideAppNetworkDataSource(): AppNetworkDataSource =
|
||||
AppNetworkDataSource.getInstance()
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideRssNetworkDataSource(): RssNetworkDataSource =
|
||||
|
|
|
@ -0,0 +1,78 @@
|
|||
package me.ash.reader.data.repository
|
||||
|
||||
import android.content.Context
|
||||
import android.util.Log
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.withContext
|
||||
import me.ash.reader.data.module.ApplicationScope
|
||||
import me.ash.reader.data.module.DispatcherIO
|
||||
import me.ash.reader.data.source.AppNetworkDataSource
|
||||
import me.ash.reader.ui.ext.DataStoreKeys
|
||||
import me.ash.reader.ui.ext.dataStore
|
||||
import me.ash.reader.ui.ext.put
|
||||
import me.ash.reader.ui.ext.skipVersionNumber
|
||||
import javax.inject.Inject
|
||||
|
||||
class AppRepository @Inject constructor(
|
||||
@ApplicationContext
|
||||
private val context: Context,
|
||||
private val appNetworkDataSource: AppNetworkDataSource,
|
||||
@ApplicationScope
|
||||
private val applicationScope: CoroutineScope,
|
||||
@DispatcherIO
|
||||
private val dispatcherIO: CoroutineDispatcher,
|
||||
) {
|
||||
suspend fun checkUpdate() {
|
||||
withContext(dispatcherIO) {
|
||||
try {
|
||||
val latest = appNetworkDataSource.getReleaseLatest()
|
||||
val latestVersion = latest.tag_name?.formatVersion() ?: listOf()
|
||||
val latestLog = latest.body ?: ""
|
||||
val latestPublishDate = latest.published_at ?: ""
|
||||
val latestSize = latest.assets
|
||||
?.first()
|
||||
?.size
|
||||
?: 0
|
||||
val latestDownloadUrl = latest.assets
|
||||
?.first()
|
||||
?.browser_download_url
|
||||
?: ""
|
||||
val currentVersion = context
|
||||
.packageManager
|
||||
.getPackageInfo(context.packageName, 0)
|
||||
.versionName
|
||||
.formatVersion()
|
||||
|
||||
Log.i("RLog", "current version ${currentVersion.joinToString(".")}")
|
||||
if (latestVersion > context.skipVersionNumber.formatVersion() && latestVersion > currentVersion) {
|
||||
Log.i("RLog", "new version ${latestVersion.joinToString(".")}")
|
||||
context.dataStore.put(
|
||||
DataStoreKeys.NewVersionNumber,
|
||||
latestVersion.joinToString(".")
|
||||
)
|
||||
context.dataStore.put(DataStoreKeys.NewVersionLog, latestLog)
|
||||
context.dataStore.put(DataStoreKeys.NewVersionPublishDate, latestPublishDate)
|
||||
context.dataStore.put(DataStoreKeys.NewVersionSize, latestSize)
|
||||
context.dataStore.put(DataStoreKeys.NewVersionDownloadUrl, latestDownloadUrl)
|
||||
}
|
||||
this
|
||||
} catch (e: Exception) {
|
||||
Log.e("RLog", "checkUpdate: ${e.message}")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun String.formatVersion(): List<String> = this.split(".")
|
||||
|
||||
operator fun List<String>.compareTo(target: List<String>): Int {
|
||||
for (i in 0 until minOf(size, target.size)) {
|
||||
val a = this[i].toIntOrNull() ?: 0
|
||||
val b = target[i].toIntOrNull() ?: 0
|
||||
if (a < b) return -1
|
||||
if (a > b) return 1
|
||||
}
|
||||
return if (size == target.size) 0 else if (size > target.size) 1 else -1
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
package me.ash.reader.data.source
|
||||
|
||||
import me.ash.reader.data.entity.GitHubRelease
|
||||
import retrofit2.Retrofit
|
||||
import retrofit2.converter.gson.GsonConverterFactory
|
||||
import retrofit2.http.GET
|
||||
|
||||
interface AppNetworkDataSource {
|
||||
@GET("https://api.github.com/repos/Ashinch/ReadYou/releases/latest")
|
||||
suspend fun getReleaseLatest(): GitHubRelease
|
||||
|
||||
companion object {
|
||||
private var instance: AppNetworkDataSource? = null
|
||||
|
||||
fun getInstance(): AppNetworkDataSource {
|
||||
return instance ?: synchronized(this) {
|
||||
instance ?: Retrofit.Builder()
|
||||
.baseUrl("https://api.github.com/")
|
||||
.addConverterFactory(GsonConverterFactory.create())
|
||||
.build().create(AppNetworkDataSource::class.java).also {
|
||||
instance = it
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -31,6 +31,7 @@ fun Banner(
|
|||
modifier: Modifier = Modifier,
|
||||
title: String,
|
||||
desc: String? = null,
|
||||
backgroundColor: Color = MaterialTheme.colorScheme.primaryContainer,
|
||||
icon: ImageVector? = null,
|
||||
action: (@Composable () -> Unit)? = null,
|
||||
onClick: () -> Unit = {},
|
||||
|
@ -46,7 +47,7 @@ fun Banner(
|
|||
.fillMaxSize()
|
||||
.padding(horizontal = 16.dp)
|
||||
.clip(RoundedCornerShape(32.dp))
|
||||
.background(MaterialTheme.colorScheme.primaryContainer alwaysLight true)
|
||||
.background(backgroundColor alwaysLight true)
|
||||
.clickable { onClick() }
|
||||
.padding(16.dp, 20.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
|
|
|
@ -2,14 +2,16 @@ package me.ash.reader.ui.component
|
|||
|
||||
import android.view.HapticFeedbackConstants
|
||||
import android.view.SoundEffectConstants
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.LocalContentColor
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.platform.LocalView
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
||||
@Composable
|
||||
fun FeedbackIconButton(
|
||||
|
@ -17,6 +19,7 @@ fun FeedbackIconButton(
|
|||
imageVector: ImageVector,
|
||||
contentDescription: String?,
|
||||
tint: Color = LocalContentColor.current,
|
||||
showBadge: Boolean = false,
|
||||
isHaptic: Boolean? = true,
|
||||
isSound: Boolean? = true,
|
||||
onClick: () -> Unit = {},
|
||||
|
@ -30,6 +33,26 @@ fun FeedbackIconButton(
|
|||
onClick()
|
||||
},
|
||||
) {
|
||||
if (showBadge) {
|
||||
BadgedBox(
|
||||
badge = {
|
||||
Badge(
|
||||
modifier = Modifier
|
||||
.size(8.dp)
|
||||
.clip(CircleShape),
|
||||
containerColor = MaterialTheme.colorScheme.error,
|
||||
contentColor = MaterialTheme.colorScheme.onError,
|
||||
)
|
||||
}
|
||||
) {
|
||||
Icon(
|
||||
modifier = modifier,
|
||||
imageVector = imageVector,
|
||||
contentDescription = contentDescription,
|
||||
tint = tint,
|
||||
)
|
||||
}
|
||||
} else {
|
||||
Icon(
|
||||
modifier = modifier,
|
||||
imageVector = imageVector,
|
||||
|
@ -38,3 +61,4 @@ fun FeedbackIconButton(
|
|||
)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -12,6 +12,19 @@ import kotlinx.coroutines.runBlocking
|
|||
import java.io.IOException
|
||||
|
||||
val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "settings")
|
||||
|
||||
val Context.newVersionPublishDate: String
|
||||
get() = this.dataStore.get(DataStoreKeys.NewVersionPublishDate) ?: ""
|
||||
val Context.newVersionLog: String
|
||||
get() = this.dataStore.get(DataStoreKeys.NewVersionLog) ?: ""
|
||||
val Context.newVersionSize: Int
|
||||
get() = this.dataStore.get(DataStoreKeys.NewVersionSize) ?: 0
|
||||
val Context.newVersionDownloadUrl: String
|
||||
get() = this.dataStore.get(DataStoreKeys.NewVersionDownloadUrl) ?: ""
|
||||
val Context.newVersionNumber: String
|
||||
get() = this.dataStore.get(DataStoreKeys.NewVersionNumber) ?: ""
|
||||
val Context.skipVersionNumber: String
|
||||
get() = this.dataStore.get(DataStoreKeys.SkipVersionNumber) ?: ""
|
||||
val Context.isFirstLaunch: Boolean
|
||||
get() = this.dataStore.get(DataStoreKeys.IsFirstLaunch) ?: true
|
||||
val Context.currentAccountId: Int
|
||||
|
@ -62,6 +75,36 @@ sealed class DataStoreKeys<T> {
|
|||
get() = booleanPreferencesKey("isFirstLaunch")
|
||||
}
|
||||
|
||||
object NewVersionPublishDate : DataStoreKeys<String>() {
|
||||
override val key: Preferences.Key<String>
|
||||
get() = stringPreferencesKey("newVersionPublishDate")
|
||||
}
|
||||
|
||||
object NewVersionLog : DataStoreKeys<String>() {
|
||||
override val key: Preferences.Key<String>
|
||||
get() = stringPreferencesKey("newVersionLog")
|
||||
}
|
||||
|
||||
object NewVersionSize : DataStoreKeys<Int>() {
|
||||
override val key: Preferences.Key<Int>
|
||||
get() = intPreferencesKey("newVersionSize")
|
||||
}
|
||||
|
||||
object NewVersionDownloadUrl : DataStoreKeys<String>() {
|
||||
override val key: Preferences.Key<String>
|
||||
get() = stringPreferencesKey("newVersionDownloadUrl")
|
||||
}
|
||||
|
||||
object NewVersionNumber : DataStoreKeys<String>() {
|
||||
override val key: Preferences.Key<String>
|
||||
get() = stringPreferencesKey("newVersionNumber")
|
||||
}
|
||||
|
||||
object SkipVersionNumber : DataStoreKeys<String>() {
|
||||
override val key: Preferences.Key<String>
|
||||
get() = stringPreferencesKey("skipVersionNumber")
|
||||
}
|
||||
|
||||
object CurrentAccountId : DataStoreKeys<Int>() {
|
||||
override val key: Preferences.Key<Int>
|
||||
get() = intPreferencesKey("currentAccountId")
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package me.ash.reader.ui.page.home.feeds
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.compose.animation.core.*
|
||||
|
@ -26,15 +27,16 @@ import androidx.hilt.navigation.compose.hiltViewModel
|
|||
import androidx.lifecycle.LiveData
|
||||
import androidx.navigation.NavHostController
|
||||
import androidx.work.WorkInfo
|
||||
import kotlinx.coroutines.flow.map
|
||||
import me.ash.reader.R
|
||||
import me.ash.reader.data.repository.SyncWorker.Companion.getIsSyncing
|
||||
import me.ash.reader.data.repository.compareTo
|
||||
import me.ash.reader.data.repository.formatVersion
|
||||
import me.ash.reader.ui.component.Banner
|
||||
import me.ash.reader.ui.component.DisplayText
|
||||
import me.ash.reader.ui.component.FeedbackIconButton
|
||||
import me.ash.reader.ui.component.Subtitle
|
||||
import me.ash.reader.ui.ext.collectAsStateValue
|
||||
import me.ash.reader.ui.ext.getDesc
|
||||
import me.ash.reader.ui.ext.getName
|
||||
import me.ash.reader.ui.ext.*
|
||||
import me.ash.reader.ui.page.common.RouteName
|
||||
import me.ash.reader.ui.page.home.FilterBar
|
||||
import me.ash.reader.ui.page.home.FilterState
|
||||
|
@ -42,6 +44,7 @@ import me.ash.reader.ui.page.home.feeds.subscribe.SubscribeDialog
|
|||
import me.ash.reader.ui.page.home.feeds.subscribe.SubscribeViewAction
|
||||
import me.ash.reader.ui.page.home.feeds.subscribe.SubscribeViewModel
|
||||
|
||||
@SuppressLint("FlowOperatorInvokedInComposition")
|
||||
@OptIn(
|
||||
ExperimentalMaterial3Api::class, com.google.accompanist.pager.ExperimentalPagerApi::class,
|
||||
androidx.compose.foundation.ExperimentalFoundationApi::class
|
||||
|
@ -59,6 +62,19 @@ fun FeedsPage(
|
|||
onScrollToPage: (targetPage: Int) -> Unit = {},
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
val skipVersionNumber =
|
||||
context.dataStore.data
|
||||
.map { it[DataStoreKeys.SkipVersionNumber.key] ?: "" }
|
||||
.collectAsState(initial = "")
|
||||
.value
|
||||
.formatVersion()
|
||||
val newVersionNumber =
|
||||
context.dataStore.data
|
||||
.map { it[DataStoreKeys.NewVersionNumber.key] ?: "" }
|
||||
.collectAsState(initial = "")
|
||||
.value
|
||||
.formatVersion()
|
||||
|
||||
val viewState = feedsViewModel.viewState.collectAsStateValue()
|
||||
|
||||
val owner = LocalLifecycleOwner.current
|
||||
|
@ -116,6 +132,7 @@ fun FeedsPage(
|
|||
imageVector = Icons.Outlined.Settings,
|
||||
contentDescription = stringResource(R.string.settings),
|
||||
tint = MaterialTheme.colorScheme.onSurface,
|
||||
showBadge = newVersionNumber > skipVersionNumber,
|
||||
) {
|
||||
navController.navigate(RouteName.SETTINGS)
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
package me.ash.reader.ui.page.settings
|
||||
|
||||
import android.view.SoundEffectConstants
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Column
|
||||
|
@ -25,6 +26,7 @@ import androidx.compose.ui.Modifier
|
|||
import androidx.compose.ui.draw.alpha
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.platform.LocalView
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
|
||||
|
@ -38,8 +40,15 @@ fun SelectableSettingGroupItem(
|
|||
icon: ImageVector? = null,
|
||||
onClick: () -> Unit,
|
||||
) {
|
||||
val view = LocalView.current
|
||||
|
||||
Surface(
|
||||
modifier = modifier.clickable { onClick() }.alpha(if (enable) 1f else 0.5f),
|
||||
modifier = modifier
|
||||
.clickable {
|
||||
view.playSoundEffect(SoundEffectConstants.CLICK)
|
||||
onClick()
|
||||
}
|
||||
.alpha(if (enable) 1f else 0.5f),
|
||||
color = Color.Unspecified,
|
||||
) {
|
||||
Row(
|
||||
|
|
|
@ -1,33 +1,54 @@
|
|||
package me.ash.reader.ui.page.settings
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.navigationBarsPadding
|
||||
import androidx.compose.foundation.layout.statusBarsPadding
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.outlined.*
|
||||
import androidx.compose.material.icons.rounded.ArrowBack
|
||||
import androidx.compose.material.icons.rounded.Close
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.zIndex
|
||||
import androidx.navigation.NavHostController
|
||||
import kotlinx.coroutines.flow.map
|
||||
import me.ash.reader.R
|
||||
import me.ash.reader.data.repository.compareTo
|
||||
import me.ash.reader.data.repository.formatVersion
|
||||
import me.ash.reader.ui.component.Banner
|
||||
import me.ash.reader.ui.component.DisplayText
|
||||
import me.ash.reader.ui.component.FeedbackIconButton
|
||||
import me.ash.reader.ui.ext.DataStoreKeys
|
||||
import me.ash.reader.ui.ext.dataStore
|
||||
import me.ash.reader.ui.page.common.RouteName
|
||||
import me.ash.reader.ui.theme.palette.onLight
|
||||
|
||||
@SuppressLint("FlowOperatorInvokedInComposition")
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun SettingsPage(
|
||||
navController: NavHostController,
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
var updateDialogVisible by remember { mutableStateOf(false) }
|
||||
val skipVersionNumber =
|
||||
context.dataStore.data
|
||||
.map { it[DataStoreKeys.SkipVersionNumber.key] ?: "" }
|
||||
.collectAsState(initial = "")
|
||||
.value
|
||||
.formatVersion()
|
||||
val newVersionNumber =
|
||||
context.dataStore.data
|
||||
.map { it[DataStoreKeys.NewVersionNumber.key] ?: "" }
|
||||
.collectAsState(initial = "")
|
||||
.value
|
||||
.formatVersion()
|
||||
|
||||
Scaffold(
|
||||
modifier = Modifier
|
||||
.background(MaterialTheme.colorScheme.surface onLight MaterialTheme.colorScheme.inverseOnSurface)
|
||||
|
@ -56,9 +77,15 @@ fun SettingsPage(
|
|||
DisplayText(text = stringResource(R.string.settings), desc = "")
|
||||
}
|
||||
item {
|
||||
Box {
|
||||
if (newVersionNumber > skipVersionNumber) {
|
||||
Banner(
|
||||
title = stringResource(R.string.in_coding),
|
||||
desc = stringResource(R.string.coming_soon),
|
||||
modifier = Modifier.zIndex(1f),
|
||||
title = stringResource(R.string.get_new_updates),
|
||||
desc = stringResource(
|
||||
R.string.get_new_updates_desc,
|
||||
newVersionNumber.joinToString(".")
|
||||
),
|
||||
icon = Icons.Outlined.Lightbulb,
|
||||
action = {
|
||||
Icon(
|
||||
|
@ -66,7 +93,14 @@ fun SettingsPage(
|
|||
contentDescription = stringResource(R.string.close),
|
||||
)
|
||||
},
|
||||
) { updateDialogVisible = true }
|
||||
}
|
||||
Banner(
|
||||
title = stringResource(R.string.in_coding),
|
||||
desc = stringResource(R.string.coming_soon),
|
||||
icon = Icons.Outlined.Lightbulb,
|
||||
)
|
||||
}
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
}
|
||||
item {
|
||||
|
@ -114,4 +148,9 @@ fun SettingsPage(
|
|||
}
|
||||
}
|
||||
)
|
||||
|
||||
UpdateDialog(
|
||||
visible = updateDialogVisible,
|
||||
onDismissRequest = { updateDialogVisible = false },
|
||||
)
|
||||
}
|
||||
|
|
115
app/src/main/java/me/ash/reader/ui/page/settings/UpdateDialog.kt
Normal file
115
app/src/main/java/me/ash/reader/ui/page/settings/UpdateDialog.kt
Normal file
|
@ -0,0 +1,115 @@
|
|||
package me.ash.reader.ui.page.settings
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.heightIn
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.text.selection.SelectionContainer
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.rounded.Update
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.google.accompanist.pager.ExperimentalPagerApi
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.launch
|
||||
import me.ash.reader.R
|
||||
import me.ash.reader.ui.component.Dialog
|
||||
import me.ash.reader.ui.ext.DataStoreKeys
|
||||
import me.ash.reader.ui.ext.dataStore
|
||||
import me.ash.reader.ui.ext.put
|
||||
|
||||
@SuppressLint("FlowOperatorInvokedInComposition")
|
||||
@OptIn(ExperimentalPagerApi::class)
|
||||
@Composable
|
||||
fun UpdateDialog(
|
||||
modifier: Modifier = Modifier,
|
||||
visible: Boolean = false,
|
||||
onDismissRequest: () -> Unit = {},
|
||||
onConfirm: (String) -> Unit = {},
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
val scope = rememberCoroutineScope { Dispatchers.IO }
|
||||
val newVersionNumber = context.dataStore.data
|
||||
.map { it[DataStoreKeys.NewVersionNumber.key] ?: "" }
|
||||
.collectAsState(initial = "")
|
||||
.value
|
||||
val newVersionPublishDate = context.dataStore.data
|
||||
.map { it[DataStoreKeys.NewVersionPublishDate.key] ?: "" }
|
||||
.collectAsState(initial = "")
|
||||
.value
|
||||
val newVersionLog = context.dataStore.data
|
||||
.map { it[DataStoreKeys.NewVersionLog.key] ?: "" }
|
||||
.collectAsState(initial = "")
|
||||
.value
|
||||
val newVersionSize = context.dataStore.data
|
||||
.map { it[DataStoreKeys.NewVersionSize.key] ?: 0 }
|
||||
.map { it / 1024f / 1024f }
|
||||
.collectAsState(initial = 0)
|
||||
.value
|
||||
|
||||
Dialog(
|
||||
modifier = modifier.heightIn(max = 400.dp),
|
||||
visible = visible,
|
||||
onDismissRequest = onDismissRequest,
|
||||
icon = {
|
||||
Icon(
|
||||
imageVector = Icons.Rounded.Update,
|
||||
contentDescription = stringResource(R.string.change_log),
|
||||
)
|
||||
},
|
||||
title = {
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
Text(text = stringResource(R.string.change_log))
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
Text(
|
||||
text = newVersionPublishDate,
|
||||
color = MaterialTheme.colorScheme.outline.copy(alpha = 0.7f),
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
)
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
}
|
||||
},
|
||||
text = {
|
||||
SelectionContainer {
|
||||
Text(
|
||||
modifier = Modifier.verticalScroll(rememberScrollState()),
|
||||
text = newVersionLog,
|
||||
)
|
||||
}
|
||||
},
|
||||
confirmButton = {
|
||||
TextButton(
|
||||
onClick = {
|
||||
}
|
||||
) {
|
||||
Text(text = stringResource(R.string.update, String.format("%.2f", newVersionSize)))
|
||||
}
|
||||
},
|
||||
dismissButton = {
|
||||
TextButton(onClick = {
|
||||
scope.launch {
|
||||
context.dataStore.put(DataStoreKeys.SkipVersionNumber, newVersionNumber)
|
||||
onDismissRequest()
|
||||
}
|
||||
}) {
|
||||
Text(text = stringResource(R.string.skip_this_version))
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
|
@ -73,7 +73,7 @@
|
|||
<string name="seven_days">7天</string>
|
||||
<string name="close">关闭</string>
|
||||
<string name="get_new_updates">获取新的更新</string>
|
||||
<string name="get_new_updates_desc">版本 0.6.1 现已发布</string>
|
||||
<string name="get_new_updates_desc">版本 %1$s 现已发布</string>
|
||||
<string name="in_coding">施工中</string>
|
||||
<string name="coming_soon">正在路上</string>
|
||||
<string name="accounts">账户</string>
|
||||
|
@ -111,4 +111,8 @@
|
|||
<string name="open_source_licenses">开放源代码许可</string>
|
||||
<string name="github_link">https://github.com/Ashinch/ReadYou</string>
|
||||
<string name="telegram_link">https://t.me/ReadYouApp</string>
|
||||
<string name="update_link">https://api.github.com/repos/Ashinch/ReadYou/releases/latest</string>
|
||||
<string name="change_log">更新日志</string>
|
||||
<string name="update">更新 %1$s MB</string>
|
||||
<string name="skip_this_version">跳过这个版本</string>
|
||||
</resources>
|
|
@ -73,7 +73,7 @@
|
|||
<string name="seven_days">7d</string>
|
||||
<string name="close">Close</string>
|
||||
<string name="get_new_updates">Get new updates</string>
|
||||
<string name="get_new_updates_desc">Version 0.6.1 has been released</string>
|
||||
<string name="get_new_updates_desc">Version %1$s has been released</string>
|
||||
<string name="in_coding">In coding</string>
|
||||
<string name="coming_soon">Coming soon</string>
|
||||
<string name="accounts">Accounts</string>
|
||||
|
@ -111,4 +111,8 @@
|
|||
<string name="open_source_licenses">Open Source Licenses</string>
|
||||
<string name="github_link">https://github.com/Ashinch/ReadYou</string>
|
||||
<string name="telegram_link">https://t.me/ReadYouApp</string>
|
||||
<string name="update_link">https://api.github.com/repos/Ashinch/ReadYou/releases/latest</string>
|
||||
<string name="change_log">Change Log</string>
|
||||
<string name="update">Update %1$s MB</string>
|
||||
<string name="skip_this_version">Skip This Version</string>
|
||||
</resources>
|
Loading…
Reference in New Issue
Block a user