Skip to content

Commit

Permalink
feat: SubsSheet
Browse files Browse the repository at this point in the history
  • Loading branch information
lisonge committed Dec 31, 2024
1 parent d403da0 commit 1ba6ecd
Show file tree
Hide file tree
Showing 17 changed files with 676 additions and 321 deletions.
1 change: 1 addition & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,7 @@ android {
"-opt-in=androidx.compose.material3.ExperimentalMaterial3Api",
"-opt-in=androidx.compose.foundation.ExperimentalFoundationApi",
"-opt-in=androidx.compose.animation.graphics.ExperimentalAnimationGraphicsApi",
"-opt-in=androidx.compose.ui.ExperimentalComposeUiApi",
)
}
dependenciesInfo.includeInApk = false
Expand Down
5 changes: 5 additions & 0 deletions app/src/main/kotlin/li/songe/gkd/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ import li.songe.gkd.service.ManageService
import li.songe.gkd.service.fixRestartService
import li.songe.gkd.service.updateLauncherAppId
import li.songe.gkd.ui.component.BuildDialog
import li.songe.gkd.ui.component.ShareDataDialog
import li.songe.gkd.ui.component.SubsSheet
import li.songe.gkd.ui.theme.AppTheme
import li.songe.gkd.util.EditGithubCookieDlg
import li.songe.gkd.util.LocalNavController
Expand Down Expand Up @@ -108,6 +110,9 @@ class MainActivity : ComponentActivity() {
if (META.updateEnabled) {
UpgradeDialog(mainVm.updateStatus)
}
SubsSheet(mainVm, mainVm.sheetSubsIdFlow)
ShareDataDialog(mainVm, mainVm.showShareDataIdsFlow)
mainVm.inputSubsLinkOption.ContentDialog()
}
}
}
Expand Down
67 changes: 67 additions & 0 deletions app/src/main/kotlin/li/songe/gkd/MainViewModel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package li.songe.gkd
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.blankj.utilcode.util.LogUtils
import io.ktor.client.request.get
import io.ktor.client.statement.bodyAsText
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
Expand All @@ -15,14 +17,19 @@ import li.songe.gkd.data.SubsItem
import li.songe.gkd.db.DbSet
import li.songe.gkd.permission.AuthReason
import li.songe.gkd.ui.component.AlertDialogOptions
import li.songe.gkd.ui.component.InputSubsLinkOption
import li.songe.gkd.ui.component.UploadOptions
import li.songe.gkd.util.LOCAL_SUBS_ID
import li.songe.gkd.util.UpdateStatus
import li.songe.gkd.util.checkUpdate
import li.songe.gkd.util.clearCache
import li.songe.gkd.util.client
import li.songe.gkd.util.launchTry
import li.songe.gkd.util.map
import li.songe.gkd.util.storeFlow
import li.songe.gkd.util.subsItemsFlow
import li.songe.gkd.util.toast
import li.songe.gkd.util.updateSubsMutex
import li.songe.gkd.util.updateSubscription

class MainViewModel : ViewModel() {
Expand All @@ -48,6 +55,66 @@ class MainViewModel : ViewModel() {

val showEditCookieDlgFlow = MutableStateFlow(false)

val inputSubsLinkOption = InputSubsLinkOption()

val sheetSubsIdFlow = MutableStateFlow<Long?>(null)

val showShareDataIdsFlow = MutableStateFlow<Set<Long>?>(null)

fun addOrModifySubs(
url: String,
oldItem: SubsItem? = null,
) = viewModelScope.launchTry(Dispatchers.IO) {
if (updateSubsMutex.mutex.isLocked) return@launchTry
updateSubsMutex.withLock {
val subItems = subsItemsFlow.value
val text = try {
client.get(url).bodyAsText()
} catch (e: Exception) {
e.printStackTrace()
LogUtils.d(e)
toast("下载订阅文件失败")
return@launchTry
}
val newSubsRaw = try {
RawSubscription.parse(text)
} catch (e: Exception) {
e.printStackTrace()
LogUtils.d(e)
toast("解析订阅文件失败")
return@launchTry
}
if (oldItem == null) {
if (subItems.any { it.id == newSubsRaw.id }) {
toast("订阅已存在")
return@launchTry
}
} else {
if (oldItem.id != newSubsRaw.id) {
toast("订阅id不对应")
return@launchTry
}
}
if (newSubsRaw.id < 0) {
toast("订阅id不可为${newSubsRaw.id}\n负数id为内部使用")
return@launchTry
}
val newItem = oldItem?.copy(updateUrl = url) ?: SubsItem(
id = newSubsRaw.id,
updateUrl = url,
order = if (subItems.isEmpty()) 1 else (subItems.maxBy { it.order }.order + 1)
)
updateSubscription(newSubsRaw)
if (oldItem == null) {
DbSet.subsItemDao.insert(newItem)
toast("成功添加订阅")
} else {
DbSet.subsItemDao.update(newItem)
toast("成功修改订阅")
}
}
}

init {
viewModelScope.launchTry(Dispatchers.IO) {
val subsItems = DbSet.subsItemDao.queryAll()
Expand Down
2 changes: 1 addition & 1 deletion app/src/main/kotlin/li/songe/gkd/data/RawSubscription.kt
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ data class RawSubscription(
map
}

private val appGroups by lazy {
val appGroups by lazy {
apps.flatMap { a -> a.groups }
}

Expand Down
3 changes: 3 additions & 0 deletions app/src/main/kotlin/li/songe/gkd/data/SubsItem.kt
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import kotlinx.serialization.Serializable
import li.songe.gkd.appScope
import li.songe.gkd.db.DbSet
import li.songe.gkd.util.LOCAL_SUBS_IDS
import li.songe.gkd.util.format
import li.songe.gkd.util.launchTry
import li.songe.gkd.util.subsFolder
import li.songe.gkd.util.subsIdToRawFlow
Expand All @@ -39,6 +40,8 @@ data class SubsItem(
val isLocal: Boolean
get() = LOCAL_SUBS_IDS.contains(id)

val mtimeStr by lazy { mtime.format("yyyy-MM-dd HH:mm:ss") }

@Dao
interface SubsItemDao {
@Update
Expand Down
14 changes: 13 additions & 1 deletion app/src/main/kotlin/li/songe/gkd/ui/ActionLogPage.kt
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ import li.songe.gkd.util.ProfileTransitions
import li.songe.gkd.util.appInfoCacheFlow
import li.songe.gkd.util.launchAsFn
import li.songe.gkd.util.subsIdToRawFlow
import li.songe.gkd.util.subsItemsFlow
import li.songe.gkd.util.throttle
import li.songe.gkd.util.toast

Expand Down Expand Up @@ -143,6 +144,7 @@ fun ActionLogPage() {
Icon(
imageVector = Icons.Outlined.Delete,
contentDescription = null,
tint = MaterialTheme.colorScheme.error
)
}
}
Expand Down Expand Up @@ -322,6 +324,7 @@ private fun ActionLogCard(
lastItem: Tuple3<ActionLog, RawSubscription.RawGroupProps?, RawSubscription.RawRuleProps?>?,
onClick: () -> Unit
) {
val context = LocalContext.current as MainActivity
val (actionLog, group, rule) = item
val lastActionLog = lastItem?.t0
val isDiffApp = actionLog.appId != lastActionLog?.appId
Expand Down Expand Up @@ -375,7 +378,16 @@ private fun ActionLogCard(
color = LocalContentColor.current.copy(alpha = 0.5f),
)
}
Text(text = subscription?.name ?: actionLog.subsId.toString())
Text(
text = subscription?.name ?: "id=${actionLog.subsId}",
modifier = Modifier.clickable(onClick = throttle {
if (subsItemsFlow.value.any { it.id == actionLog.subsId }) {
context.mainVm.sheetSubsIdFlow.value = actionLog.subsId
} else {
toast("订阅不存在")
}
})
)
Row(
modifier = Modifier.fillMaxWidth()
) {
Expand Down
1 change: 1 addition & 0 deletions app/src/main/kotlin/li/songe/gkd/ui/ActivityLogPage.kt
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ fun ActivityLogPage() {
Icon(
imageVector = Icons.Outlined.Delete,
contentDescription = null,
tint = MaterialTheme.colorScheme.error
)
}
}
Expand Down
6 changes: 3 additions & 3 deletions app/src/main/kotlin/li/songe/gkd/ui/AdvancedPage.kt
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material.icons.filled.Edit
import androidx.compose.material.icons.outlined.Edit
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
Expand Down Expand Up @@ -272,7 +272,7 @@ fun AdvancedPage() {
SettingItem(
title = "服务端口",
subtitle = store.httpServerPort.toString(),
imageVector = Icons.Default.Edit,
imageVector = Icons.Outlined.Edit,
onClick = {
showEditPortDlg = true
}
Expand Down Expand Up @@ -397,7 +397,7 @@ fun AdvancedPage() {
onSuffixClick = {
openUri("https://gkd.li?r=1")
},
imageVector = Icons.Default.Edit,
imageVector = Icons.Outlined.Edit,
onClick = {
context.mainVm.showEditCookieDlgFlow.value = true
}
Expand Down
4 changes: 2 additions & 2 deletions app/src/main/kotlin/li/songe/gkd/ui/AppConfigPage.kt
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material.icons.automirrored.filled.Sort
import androidx.compose.material.icons.filled.Edit
import androidx.compose.material.icons.outlined.Edit
import androidx.compose.material3.DropdownMenu
import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.FloatingActionButton
Expand Down Expand Up @@ -171,7 +171,7 @@ fun AppConfigPage(appId: String) {
},
content = {
Icon(
imageVector = Icons.Default.Edit,
imageVector = Icons.Outlined.Edit,
contentDescription = null,
)
}
Expand Down
1 change: 1 addition & 0 deletions app/src/main/kotlin/li/songe/gkd/ui/SnapshotPage.kt
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ fun SnapshotPage() {
Icon(
imageVector = Icons.Outlined.Delete,
contentDescription = null,
tint = MaterialTheme.colorScheme.error
)
}
}
Expand Down
70 changes: 70 additions & 0 deletions app/src/main/kotlin/li/songe/gkd/ui/component/ShareDataDialog.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package li.songe.gkd.ui.component

import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Card
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Dialog
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import li.songe.gkd.MainActivity
import li.songe.gkd.data.exportData
import li.songe.gkd.util.launchTry
import li.songe.gkd.util.saveFileToDownloads
import li.songe.gkd.util.shareFile
import li.songe.gkd.util.throttle

@Composable
fun ShareDataDialog(
vm: ViewModel,
showShareDataIdsFlow: MutableStateFlow<Set<Long>?>,
) {
val showShareDataIds = showShareDataIdsFlow.collectAsState().value
if (showShareDataIds != null) {
val context = LocalContext.current as MainActivity
Dialog(onDismissRequest = { showShareDataIdsFlow.value = null }) {
Card(
modifier = Modifier
.fillMaxWidth()
.padding(16.dp),
shape = RoundedCornerShape(16.dp),
) {
val modifier = Modifier
.fillMaxWidth()
.padding(16.dp)
Text(
text = "分享到其他应用", modifier = Modifier
.clickable(onClick = throttle {
showShareDataIdsFlow.value = null
vm.viewModelScope.launchTry(Dispatchers.IO) {
val file = exportData(showShareDataIds)
context.shareFile(file, "分享数据文件")
}
})
.then(modifier)
)
Text(
text = "保存到下载",
modifier = Modifier
.clickable(onClick = throttle {
showShareDataIdsFlow.value = null
vm.viewModelScope.launchTry(Dispatchers.IO) {
val file = exportData(showShareDataIds)
context.saveFileToDownloads(file)
}
})
.then(modifier)
)
}
}
}
}
Loading

0 comments on commit 1ba6ecd

Please sign in to comment.