Skip to content

Commit

Permalink
[feature|build] Support clear sticker provider thumbnail caches; upda…
Browse files Browse the repository at this point in the history
…te dependencies
  • Loading branch information
SkyD666 committed Mar 30, 2024
1 parent 6240a0a commit 78d8b78
Show file tree
Hide file tree
Showing 14 changed files with 279 additions and 12 deletions.
10 changes: 5 additions & 5 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ android {
minSdk = 24
targetSdk = 34
versionCode = 61
versionName = "2.2-alpha05"
versionName = "2.2-alpha06"

testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables {
Expand Down Expand Up @@ -146,8 +146,8 @@ dependencies {
implementation("androidx.compose.ui:ui:$composeVersion")
implementation("androidx.compose.material3:material3:$md3Version")
implementation("androidx.compose.material3:material3-window-size-class:$md3Version")
implementation("androidx.compose.material:material:1.6.3")
implementation("androidx.compose.material:material-icons-extended:1.6.3")
implementation("androidx.compose.material:material:1.6.4")
implementation("androidx.compose.material:material-icons-extended:1.6.4")
implementation("androidx.compose.ui:ui-tooling-preview:$md3Version")
implementation("com.google.android.material:material:1.11.0")
implementation("androidx.lifecycle:lifecycle-runtime-compose:2.7.0")
Expand Down Expand Up @@ -190,6 +190,6 @@ dependencies {

implementation("com.github.penfeizhou.android.animation:apng:2.28.0")

debugImplementation("androidx.compose.ui:ui-tooling:1.6.3")
debugImplementation("androidx.compose.ui:ui-test-manifest:1.6.3")
debugImplementation("androidx.compose.ui:ui-tooling:1.6.4")
debugImplementation("androidx.compose.ui:ui-test-manifest:1.6.4")
}
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,7 @@ class StickerProvider : DocumentsProvider() {
signal: CancellationSignal?
): AssetFileDescriptor {
val stickerFile = File(documentId)
// TODO: delete thumbFile
val thumbFile = File(PROVIDER_THUMBNAIL_DIR, stickerFile.name)
if (!thumbFile.exists()) {
thumbFile.parentFile?.mkdirs()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,24 @@
package com.skyd.rays.model.respository

import com.skyd.rays.base.BaseRepository
import com.skyd.rays.config.PROVIDER_THUMBNAIL_DIR
import com.skyd.rays.model.db.dao.sticker.StickerDao
import kotlinx.coroutines.flow.Flow
import javax.inject.Inject
import kotlin.system.measureTimeMillis

class DataRepository @Inject constructor(private val stickerDao: StickerDao) : BaseRepository() {
suspend fun requestDeleteAllData(): Flow<Long> {
return flowOnIo {
val startTime = System.currentTimeMillis()
stickerDao.deleteAllStickerWithTags()
emit(System.currentTimeMillis() - startTime)
emit(measureTimeMillis { stickerDao.deleteAllStickerWithTags() })
}
}

suspend fun requestDeleteDocumentsProviderThumbnails(): Flow<Long> {
return flowOnIo {
emit(measureTimeMillis {
PROVIDER_THUMBNAIL_DIR.deleteRecursively()
})
}
}
}
5 changes: 5 additions & 0 deletions app/src/main/java/com/skyd/rays/ui/activity/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@ import com.skyd.rays.ui.screen.settings.appearance.style.SEARCH_STYLE_SCREEN_ROU
import com.skyd.rays.ui.screen.settings.appearance.style.SearchStyleScreen
import com.skyd.rays.ui.screen.settings.data.DATA_SCREEN_ROUTE
import com.skyd.rays.ui.screen.settings.data.DataScreen
import com.skyd.rays.ui.screen.settings.data.cache.CACHE_SCREEN_ROUTE
import com.skyd.rays.ui.screen.settings.data.cache.CacheScreen
import com.skyd.rays.ui.screen.settings.data.importexport.IMPORT_EXPORT_SCREEN_ROUTE
import com.skyd.rays.ui.screen.settings.data.importexport.ImportExportScreen
import com.skyd.rays.ui.screen.settings.data.importexport.cloud.webdav.WEBDAV_SCREEN_ROUTE
Expand Down Expand Up @@ -304,6 +306,9 @@ class MainActivity : AppCompatActivity() {
composable(route = BLUR_STICKERS_SCREEN_ROUTE) {
BlurStickersScreen()
}
composable(route = CACHE_SCREEN_ROUTE) {
CacheScreen()
}
}

if (openUpdateDialog) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package com.skyd.rays.ui.screen.settings.data
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Cached
import androidx.compose.material.icons.filled.Delete
import androidx.compose.material.icons.filled.ImportExport
import androidx.compose.material.icons.filled.Source
Expand All @@ -28,11 +29,13 @@ import com.skyd.rays.R
import com.skyd.rays.base.mvi.getDispatcher
import com.skyd.rays.ext.showSnackbarWithLaunchedEffect
import com.skyd.rays.ui.component.BaseSettingsItem
import com.skyd.rays.ui.component.CategorySettingsItem
import com.skyd.rays.ui.component.RaysTopBar
import com.skyd.rays.ui.component.RaysTopBarStyle
import com.skyd.rays.ui.component.dialog.DeleteWarningDialog
import com.skyd.rays.ui.component.dialog.WaitingDialog
import com.skyd.rays.ui.local.LocalNavController
import com.skyd.rays.ui.screen.settings.data.cache.CACHE_SCREEN_ROUTE
import com.skyd.rays.ui.screen.settings.data.importexport.IMPORT_EXPORT_SCREEN_ROUTE
import com.skyd.rays.ui.screen.settings.imagesource.IMAGE_SOURCE_SCREEN_ROUTE

Expand Down Expand Up @@ -66,13 +69,32 @@ fun DataScreen(viewModel: DataViewModel = hiltViewModel()) {
.nestedScroll(scrollBehavior.nestedScrollConnection),
contentPadding = paddingValues,
) {
item {
CategorySettingsItem(text = stringResource(id = R.string.data_screen_source_category))
}
item {
BaseSettingsItem(
icon = rememberVectorPainter(Icons.Default.Source),
text = stringResource(id = R.string.image_source_screen_name),
descriptionText = stringResource(id = R.string.setting_screen_image_source_description),
onClick = { navController.navigate(IMAGE_SOURCE_SCREEN_ROUTE) }
)
}
item {
CategorySettingsItem(text = stringResource(id = R.string.data_screen_sync_category))
}
item {
BaseSettingsItem(
icon = rememberVectorPainter(image = Icons.Default.ImportExport),
text = stringResource(id = R.string.import_export_screen_name),
descriptionText = stringResource(id = R.string.data_screen_import_export_description),
onClick = { navController.navigate(IMPORT_EXPORT_SCREEN_ROUTE) }
)
}
item {
CategorySettingsItem(text = stringResource(id = R.string.data_screen_delete_category))
}
item {
BaseSettingsItem(
icon = rememberVectorPainter(image = Icons.Default.Delete),
text = stringResource(id = R.string.data_screen_delete_all),
Expand All @@ -82,10 +104,10 @@ fun DataScreen(viewModel: DataViewModel = hiltViewModel()) {
}
item {
BaseSettingsItem(
icon = rememberVectorPainter(Icons.Default.Source),
text = stringResource(id = R.string.image_source_screen_name),
descriptionText = stringResource(id = R.string.setting_screen_image_source_description),
onClick = { navController.navigate(IMAGE_SOURCE_SCREEN_ROUTE) }
icon = rememberVectorPainter(image = Icons.Default.Cached),
text = stringResource(id = R.string.cache_screen_name),
descriptionText = stringResource(id = R.string.cache_screen_description),
onClick = { navController.navigate(CACHE_SCREEN_ROUTE) }
)
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.skyd.rays.ui.screen.settings.data.cache

import com.skyd.rays.base.mvi.MviSingleEvent

sealed interface CacheEvent : MviSingleEvent {
sealed interface DeleteDocumentsProviderThumbnailsResultEvent : CacheEvent {
data class Success(val time: Long) : DeleteDocumentsProviderThumbnailsResultEvent
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.skyd.rays.ui.screen.settings.data.cache

import com.skyd.rays.base.mvi.MviIntent

sealed interface CacheIntent : MviIntent {
data object Init : CacheIntent
data object DeleteDocumentsProviderThumbnails : CacheIntent
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.skyd.rays.ui.screen.settings.data.cache

internal sealed interface CachePartialStateChange {
fun reduce(oldState: CacheState): CacheState

data object LoadingDialog : CachePartialStateChange {
override fun reduce(oldState: CacheState): CacheState = oldState.copy(loadingDialog = true)
}

data object Init : CachePartialStateChange {
override fun reduce(oldState: CacheState) = oldState
}

sealed interface DeleteDocumentsProviderThumbnails : CachePartialStateChange {
data class Success(val time: Long) : DeleteDocumentsProviderThumbnails {
override fun reduce(oldState: CacheState): CacheState =
oldState.copy(loadingDialog = false)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package com.skyd.rays.ui.screen.settings.data.cache

import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.DeleteOutline
import androidx.compose.material3.Scaffold
import androidx.compose.material3.SnackbarHost
import androidx.compose.material3.SnackbarHostState
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.vector.rememberVectorPainter
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.skyd.rays.R
import com.skyd.rays.base.mvi.getDispatcher
import com.skyd.rays.ext.showSnackbarWithLaunchedEffect
import com.skyd.rays.ui.component.BaseSettingsItem
import com.skyd.rays.ui.component.RaysTopBar
import com.skyd.rays.ui.component.RaysTopBarStyle
import com.skyd.rays.ui.component.dialog.WaitingDialog

const val CACHE_SCREEN_ROUTE = "cacheScreen"

@Composable
fun CacheScreen(viewModel: CacheViewModel = hiltViewModel()) {
val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior()
val context = LocalContext.current
val snackbarHostState = remember { SnackbarHostState() }
val uiState by viewModel.viewState.collectAsStateWithLifecycle()
val uiEvent by viewModel.singleEvent.collectAsStateWithLifecycle(initialValue = null)

val dispatch = viewModel.getDispatcher(startWith = CacheIntent.Init)

Scaffold(
snackbarHost = { SnackbarHost(hostState = snackbarHostState) },
topBar = {
RaysTopBar(
style = RaysTopBarStyle.Large,
scrollBehavior = scrollBehavior,
title = { Text(text = stringResource(R.string.cache_screen_name)) },
)
}
) { paddingValues ->
LazyColumn(
modifier = Modifier
.fillMaxSize()
.nestedScroll(scrollBehavior.nestedScrollConnection),
contentPadding = paddingValues,
) {
item {
BaseSettingsItem(
icon = rememberVectorPainter(image = Icons.Default.DeleteOutline),
text = stringResource(id = R.string.cache_screen_delete_provider_thumbnails),
descriptionText = stringResource(id = R.string.cache_screen_delete_provider_thumbnails_description),
onClick = { dispatch(CacheIntent.DeleteDocumentsProviderThumbnails) }
)
}
}

when (val event = uiEvent) {
is CacheEvent.DeleteDocumentsProviderThumbnailsResultEvent.Success -> {
snackbarHostState.showSnackbarWithLaunchedEffect(
message = context.getString(
R.string.cache_screen_delete_provider_thumbnails_success,
event.time / 1000.0f
),
key2 = event,
)
}

null -> Unit
}

WaitingDialog(visible = uiState.loadingDialog)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.skyd.rays.ui.screen.settings.data.cache

import com.skyd.rays.base.mvi.MviViewState

data class CacheState(
val loadingDialog: Boolean,
) : MviViewState {
companion object {
fun initial() = CacheState(
loadingDialog = false,
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package com.skyd.rays.ui.screen.settings.data.cache

import androidx.lifecycle.viewModelScope
import com.skyd.rays.base.mvi.AbstractMviViewModel
import com.skyd.rays.ext.startWith
import com.skyd.rays.model.respository.DataRepository
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.filterIsInstance
import kotlinx.coroutines.flow.filterNot
import kotlinx.coroutines.flow.flatMapConcat
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.merge
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.scan
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.flow.take
import javax.inject.Inject

@HiltViewModel
class CacheViewModel @Inject constructor(private var dataRepo: DataRepository) :
AbstractMviViewModel<CacheIntent, CacheState, CacheEvent>() {

override val viewState: StateFlow<CacheState>

init {
val initialVS = CacheState.initial()

viewState = merge(
intentSharedFlow.filterIsInstance<CacheIntent.Init>().take(1),
intentSharedFlow.filterNot { it is CacheIntent.Init }
)
.shareWhileSubscribed()
.toPartialStateChangeFlow()
.debugLog("CachePartialStateChange")
.sendSingleEvent()
.scan(initialVS) { vs, change -> change.reduce(vs) }
.debugLog("ViewState")
.stateIn(
viewModelScope,
SharingStarted.Eagerly,
initialVS
)
}


private fun Flow<CachePartialStateChange>.sendSingleEvent(): Flow<CachePartialStateChange> {
return onEach { change ->
val event = when (change) {
is CachePartialStateChange.DeleteDocumentsProviderThumbnails.Success ->
CacheEvent.DeleteDocumentsProviderThumbnailsResultEvent.Success(change.time)

else -> return@onEach
}
sendEvent(event)
}
}

private fun SharedFlow<CacheIntent>.toPartialStateChangeFlow(): Flow<CachePartialStateChange> {
return merge(
filterIsInstance<CacheIntent.Init>().map { CachePartialStateChange.Init },

filterIsInstance<CacheIntent.DeleteDocumentsProviderThumbnails>().flatMapConcat {
dataRepo.requestDeleteDocumentsProviderThumbnails()
.map { CachePartialStateChange.DeleteDocumentsProviderThumbnails.Success(it) }
.startWith(CachePartialStateChange.LoadingDialog)
},
)
}
}
8 changes: 8 additions & 0 deletions app/src/main/res/values-zh-rCN/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -321,4 +321,12 @@
<string name="blur_stickers_screen_radius">模糊半径</string>
<string name="loading">加载中…</string>
<string name="stickers">表情包</string>
<string name="cache_screen_name">缓存</string>
<string name="cache_screen_description">删除缓存数据等</string>
<string name="cache_screen_delete_provider_thumbnails">删除缩略图缓存</string>
<string name="cache_screen_delete_provider_thumbnails_description">删除表情包提供者缩略图缓存</string>
<string name="cache_screen_delete_provider_thumbnails_success">已删除缩略图缓存, 花费 %.2f 秒</string>
<string name="data_screen_sync_category">同步</string>
<string name="data_screen_delete_category">删除</string>
<string name="data_screen_source_category">来源</string>
</resources>
8 changes: 8 additions & 0 deletions app/src/main/res/values-zh-rTW/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -321,4 +321,12 @@
<string name="blur_stickers_screen_radius">模糊半徑</string>
<string name="loading">載入中…</string>
<string name="stickers">表情包</string>
<string name="cache_screen_name">快取</string>
<string name="cache_screen_description">刪除快取資料等</string>
<string name="cache_screen_delete_provider_thumbnails">刪除縮圖快取</string>
<string name="cache_screen_delete_provider_thumbnails_description">刪除貼紙提供者縮圖快取</string>
<string name="cache_screen_delete_provider_thumbnails_success">所有縮圖均已清除,已花費 %.2f 秒</string>
<string name="data_screen_sync_category">同步</string>
<string name="data_screen_delete_category">刪除</string>
<string name="data_screen_source_category">來源</string>
</resources>
Loading

0 comments on commit 78d8b78

Please sign in to comment.