From 27e75b8b8f22683e8080547dcbd9f4529c1ec6c9 Mon Sep 17 00:00:00 2001 From: SkyD666 Date: Thu, 11 Apr 2024 15:54:25 +0800 Subject: [PATCH] [feature|fix] Support statistics of recently shared stickers; fix a database singleton code bug; fix sticker color theme bug --- app/build.gradle.kts | 4 +- .../java/com/skyd/rays/di/DatabaseModule.kt | 9 +++- .../rays/model/bean/StickerShareTimeBean.kt | 41 +++++++++++++++ .../com/skyd/rays/model/db/AppDatabase.kt | 28 ++++++---- .../model/db/dao/cache/StickerShareTimeDao.kt | 51 +++++++++++++++++++ .../rays/model/db/dao/sticker/StickerDao.kt | 38 ++++++++++++-- .../rays/model/db/migration/Migration7To8.kt | 25 +++++++++ .../theme/CustomPrimaryColorPreference.kt | 2 +- .../theme/StickerColorThemePreference.kt | 7 ++- .../preference/theme/ThemeNamePreference.kt | 9 ++-- .../rays/model/respository/DataRepository.kt | 16 ++++-- .../rays/model/respository/HomeRepository.kt | 6 +++ .../skyd/rays/ui/activity/MainViewModel.kt | 3 +- .../rays/ui/component/AnimatedPlaceholder.kt | 2 +- .../ui/screen/home/HomePartialStateChange.kt | 2 + .../skyd/rays/ui/screen/home/HomeScreen.kt | 47 ++++++++++++++--- .../com/skyd/rays/ui/screen/home/HomeState.kt | 1 + .../skyd/rays/ui/screen/home/HomeViewModel.kt | 8 ++- .../rays/ui/screen/settings/data/DataEvent.kt | 4 ++ .../ui/screen/settings/data/DataIntent.kt | 1 + .../settings/data/DataPartialStateChange.kt | 7 +++ .../ui/screen/settings/data/DataScreen.kt | 40 ++++++++++++--- .../ui/screen/settings/data/DataViewModel.kt | 8 +++ .../java/com/skyd/rays/util/StickerUtil.kt | 4 +- app/src/main/res/values-zh-rCN/strings.xml | 5 +- app/src/main/res/values-zh-rTW/strings.xml | 5 +- app/src/main/res/values/strings.xml | 9 ++-- 27 files changed, 331 insertions(+), 51 deletions(-) create mode 100644 app/src/main/java/com/skyd/rays/model/bean/StickerShareTimeBean.kt create mode 100644 app/src/main/java/com/skyd/rays/model/db/dao/cache/StickerShareTimeDao.kt create mode 100644 app/src/main/java/com/skyd/rays/model/db/migration/Migration7To8.kt diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 1038c02..6fc8b48 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -21,8 +21,8 @@ android { applicationId = "com.skyd.rays" minSdk = 24 targetSdk = 34 - versionCode = 62 - versionName = "2.2-beta02" + versionCode = 63 + versionName = "2.2-beta03" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" vectorDrawables { diff --git a/app/src/main/java/com/skyd/rays/di/DatabaseModule.kt b/app/src/main/java/com/skyd/rays/di/DatabaseModule.kt index 971d340..63a26be 100644 --- a/app/src/main/java/com/skyd/rays/di/DatabaseModule.kt +++ b/app/src/main/java/com/skyd/rays/di/DatabaseModule.kt @@ -6,6 +6,7 @@ import com.skyd.rays.model.db.dao.ApiGrantPackageDao import com.skyd.rays.model.db.dao.SearchDomainDao import com.skyd.rays.model.db.dao.TagDao import com.skyd.rays.model.db.dao.UriStringSharePackageDao +import com.skyd.rays.model.db.dao.cache.StickerShareTimeDao import com.skyd.rays.model.db.dao.sticker.MimeTypeDao import com.skyd.rays.model.db.dao.sticker.StickerDao import dagger.Module @@ -48,6 +49,10 @@ object DatabaseModule { @Provides @Singleton - fun provideMimeTypeDao(database: AppDatabase): MimeTypeDao = - database.mimeTypeDao() + fun provideMimeTypeDao(database: AppDatabase): MimeTypeDao = database.mimeTypeDao() + + @Provides + @Singleton + fun provideStickerShareTimeDao(database: AppDatabase): StickerShareTimeDao = + database.stickerShareTimeDao() } diff --git a/app/src/main/java/com/skyd/rays/model/bean/StickerShareTimeBean.kt b/app/src/main/java/com/skyd/rays/model/bean/StickerShareTimeBean.kt new file mode 100644 index 0000000..f6af44d --- /dev/null +++ b/app/src/main/java/com/skyd/rays/model/bean/StickerShareTimeBean.kt @@ -0,0 +1,41 @@ +package com.skyd.rays.model.bean + +import android.os.Parcelable +import androidx.room.ColumnInfo +import androidx.room.Entity +import androidx.room.ForeignKey +import com.skyd.rays.base.BaseBean +import kotlinx.parcelize.Parcelize +import kotlinx.serialization.Serializable + +const val STICKER_SHARE_TIME_TABLE_NAME = "StickerShareTime" + +@Parcelize +@Serializable +@Entity( + tableName = STICKER_SHARE_TIME_TABLE_NAME, + primaryKeys = [ + StickerShareTimeBean.STICKER_UUID_COLUMN, + StickerShareTimeBean.SHARE_TIME_COLUMN + ], + foreignKeys = [ + ForeignKey( + entity = StickerBean::class, + parentColumns = [StickerBean.UUID_COLUMN], + childColumns = [StickerShareTimeBean.STICKER_UUID_COLUMN], + onDelete = ForeignKey.CASCADE + ) + ] +) +data class StickerShareTimeBean( + @ColumnInfo(name = STICKER_UUID_COLUMN) + var stickerUuid: String, + @ColumnInfo(name = SHARE_TIME_COLUMN) + var shareTime: Long, +) : BaseBean, Parcelable { + companion object { + const val STICKER_UUID_COLUMN = "stickerUuid" + const val SHARE_TIME_COLUMN = "shareTime" + } +} + diff --git a/app/src/main/java/com/skyd/rays/model/db/AppDatabase.kt b/app/src/main/java/com/skyd/rays/model/db/AppDatabase.kt index 9347a77..23875eb 100644 --- a/app/src/main/java/com/skyd/rays/model/db/AppDatabase.kt +++ b/app/src/main/java/com/skyd/rays/model/db/AppDatabase.kt @@ -9,12 +9,14 @@ import com.skyd.rays.model.bean.ApiGrantPackageBean import com.skyd.rays.model.bean.MimeTypeBean import com.skyd.rays.model.bean.SearchDomainBean import com.skyd.rays.model.bean.StickerBean +import com.skyd.rays.model.bean.StickerShareTimeBean import com.skyd.rays.model.bean.TagBean import com.skyd.rays.model.bean.UriStringSharePackageBean import com.skyd.rays.model.db.dao.ApiGrantPackageDao import com.skyd.rays.model.db.dao.SearchDomainDao import com.skyd.rays.model.db.dao.TagDao import com.skyd.rays.model.db.dao.UriStringSharePackageDao +import com.skyd.rays.model.db.dao.cache.StickerShareTimeDao import com.skyd.rays.model.db.dao.sticker.MimeTypeDao import com.skyd.rays.model.db.dao.sticker.StickerDao import com.skyd.rays.model.db.migration.Migration1To2 @@ -23,6 +25,7 @@ import com.skyd.rays.model.db.migration.Migration3To4 import com.skyd.rays.model.db.migration.Migration4To5 import com.skyd.rays.model.db.migration.Migration5To6 import com.skyd.rays.model.db.migration.Migration6To7 +import com.skyd.rays.model.db.migration.Migration7To8 const val APP_DATA_BASE_FILE_NAME = "app.db" @@ -34,8 +37,9 @@ const val APP_DATA_BASE_FILE_NAME = "app.db" UriStringSharePackageBean::class, ApiGrantPackageBean::class, MimeTypeBean::class, + StickerShareTimeBean::class, ], - version = 7 + version = 8 ) @TypeConverters( value = [] @@ -48,6 +52,7 @@ abstract class AppDatabase : RoomDatabase() { abstract fun uriStringSharePackageDao(): UriStringSharePackageDao abstract fun apiGrantPackageDao(): ApiGrantPackageDao abstract fun mimeTypeDao(): MimeTypeDao + abstract fun stickerShareTimeDao(): StickerShareTimeDao companion object { @Volatile @@ -55,19 +60,24 @@ abstract class AppDatabase : RoomDatabase() { private val migrations = arrayOf( Migration1To2(), Migration2To3(), Migration3To4(), Migration4To5(), Migration5To6(), - Migration6To7(), + Migration6To7(), Migration7To8(), ) fun getInstance(context: Context): AppDatabase { return if (instance == null) { synchronized(this) { - Room.databaseBuilder( - context.applicationContext, - AppDatabase::class.java, - APP_DATA_BASE_FILE_NAME - ) - .addMigrations(*migrations) - .build() + if (instance == null) { + Room.databaseBuilder( + context.applicationContext, + AppDatabase::class.java, + APP_DATA_BASE_FILE_NAME + ) + .addMigrations(*migrations) + .build() + .apply { instance = this } + } else { + instance as AppDatabase + } } } else { instance as AppDatabase diff --git a/app/src/main/java/com/skyd/rays/model/db/dao/cache/StickerShareTimeDao.kt b/app/src/main/java/com/skyd/rays/model/db/dao/cache/StickerShareTimeDao.kt new file mode 100644 index 0000000..9ea57ff --- /dev/null +++ b/app/src/main/java/com/skyd/rays/model/db/dao/cache/StickerShareTimeDao.kt @@ -0,0 +1,51 @@ +package com.skyd.rays.model.db.dao.cache + +import androidx.room.Dao +import androidx.room.Insert +import androidx.room.OnConflictStrategy +import androidx.room.Query +import androidx.room.Transaction +import com.skyd.rays.model.bean.STICKER_SHARE_TIME_TABLE_NAME +import com.skyd.rays.model.bean.StickerShareTimeBean + +@Dao +interface StickerShareTimeDao { + @Transaction + @Insert(onConflict = OnConflictStrategy.REPLACE) + fun updateShareTime(stickerShareTimeBean: StickerShareTimeBean) + + @Transaction + @Insert(onConflict = OnConflictStrategy.REPLACE) + fun updateShareTime(stickerShareTimeList: List) + + @Transaction + @Query( + """ + SELECT ${StickerShareTimeBean.SHARE_TIME_COLUMN} FROM $STICKER_SHARE_TIME_TABLE_NAME + WHERE ${StickerShareTimeBean.STICKER_UUID_COLUMN} LIKE :uuid + """ + ) + fun getShareTimeByUuid(uuid: String): List + + @Transaction + @Query("DELETE FROM $STICKER_SHARE_TIME_TABLE_NAME") + fun deleteAll(): Int + + @Transaction + @Query( + """ + DELETE FROM $STICKER_SHARE_TIME_TABLE_NAME + WHERE ${StickerShareTimeBean.STICKER_UUID_COLUMN} LIKE :uuid + """ + ) + fun deleteShareTimeByUuid(uuid: String): Int + + @Transaction + @Query( + """ + DELETE FROM $STICKER_SHARE_TIME_TABLE_NAME + WHERE ${StickerShareTimeBean.SHARE_TIME_COLUMN} < :timestamp + """ + ) + fun deleteShareTimeBefore(timestamp: Long): Int +} \ No newline at end of file diff --git a/app/src/main/java/com/skyd/rays/model/db/dao/sticker/StickerDao.kt b/app/src/main/java/com/skyd/rays/model/db/dao/sticker/StickerDao.kt index 7dc9573..8328398 100644 --- a/app/src/main/java/com/skyd/rays/model/db/dao/sticker/StickerDao.kt +++ b/app/src/main/java/com/skyd/rays/model/db/dao/sticker/StickerDao.kt @@ -12,6 +12,7 @@ import com.skyd.rays.appContext import com.skyd.rays.config.STICKER_DIR import com.skyd.rays.ext.dataStore import com.skyd.rays.ext.getOrDefault +import com.skyd.rays.model.bean.STICKER_SHARE_TIME_TABLE_NAME import com.skyd.rays.model.bean.STICKER_TABLE_NAME import com.skyd.rays.model.bean.StickerBean import com.skyd.rays.model.bean.StickerBean.Companion.CLICK_COUNT_COLUMN @@ -20,10 +21,12 @@ import com.skyd.rays.model.bean.StickerBean.Companion.MODIFY_TIME_COLUMN import com.skyd.rays.model.bean.StickerBean.Companion.SHARE_COUNT_COLUMN import com.skyd.rays.model.bean.StickerBean.Companion.STICKER_MD5_COLUMN import com.skyd.rays.model.bean.StickerBean.Companion.UUID_COLUMN +import com.skyd.rays.model.bean.StickerShareTimeBean import com.skyd.rays.model.bean.StickerWithTags import com.skyd.rays.model.bean.StickerWithTagsAndFile import com.skyd.rays.model.bean.TagBean import com.skyd.rays.model.db.dao.TagDao +import com.skyd.rays.model.db.dao.cache.StickerShareTimeDao import com.skyd.rays.model.preference.CurrentStickerUuidPreference import dagger.hilt.EntryPoint import dagger.hilt.InstallIn @@ -41,6 +44,7 @@ interface StickerDao { @InstallIn(SingletonComponent::class) interface StickerDaoEntryPoint { val tagDao: TagDao + val stickerShareTimeDao: StickerShareTimeDao } @Transaction @@ -105,7 +109,8 @@ interface StickerDao { @Transaction @Query( - """SELECT * + """ + SELECT * FROM $STICKER_TABLE_NAME ORDER BY $SHARE_COUNT_COLUMN DESC LIMIT :count @@ -113,6 +118,22 @@ interface StickerDao { ) fun getMostSharedStickersList(count: Int): Flow> + @Transaction + @Query( + """ + SELECT * FROM $STICKER_TABLE_NAME LEFT JOIN ( + SELECT *, MAX(${StickerShareTimeBean.SHARE_TIME_COLUMN}) + FROM $STICKER_SHARE_TIME_TABLE_NAME + GROUP BY ${StickerShareTimeBean.STICKER_UUID_COLUMN} + ORDER BY ${StickerShareTimeBean.SHARE_TIME_COLUMN} DESC + LIMIT :count + ) AS shareTime + WHERE $UUID_COLUMN = shareTime.${StickerShareTimeBean.STICKER_UUID_COLUMN} + ORDER BY ${StickerShareTimeBean.SHARE_TIME_COLUMN} DESC + """ + ) + fun getRecentSharedStickers(count: Int): Flow> + @Transaction @Query("SELECT $UUID_COLUMN FROM $STICKER_TABLE_NAME WHERE $STICKER_MD5_COLUMN LIKE :stickerMd5") fun containsByMd5(stickerMd5: String): String? @@ -137,6 +158,17 @@ interface StickerDao { ) fun addShareCount(uuids: List, count: Int = 1): Int + @Transaction + fun shareStickers(uuids: List, count: Int = 1) { + val hiltEntryPoint = EntryPointAccessors + .fromApplication(appContext, StickerDaoEntryPoint::class.java) + val currentTimeMillis = System.currentTimeMillis() + addShareCount(uuids, count) + hiltEntryPoint.stickerShareTimeDao.updateShareTime(uuids.map { stickerUuid -> + StickerShareTimeBean(stickerUuid, currentTimeMillis) + }) + } + @Transaction @Query("SELECT * FROM $STICKER_TABLE_NAME ORDER BY $SHARE_COUNT_COLUMN DESC LIMIT :count") fun getPopularStickersList(count: Int = 15): Flow> @@ -150,8 +182,8 @@ interface StickerDao { if (updateModifyTime) { stickerWithTags.sticker.modifyTime = System.currentTimeMillis() } - val hiltEntryPoint = - EntryPointAccessors.fromApplication(appContext, StickerDaoEntryPoint::class.java) + val hiltEntryPoint = EntryPointAccessors + .fromApplication(appContext, StickerDaoEntryPoint::class.java) var stickerUuid = stickerWithTags.sticker.uuid runCatching { UUID.fromString(stickerUuid) diff --git a/app/src/main/java/com/skyd/rays/model/db/migration/Migration7To8.kt b/app/src/main/java/com/skyd/rays/model/db/migration/Migration7To8.kt new file mode 100644 index 0000000..3d0bfd4 --- /dev/null +++ b/app/src/main/java/com/skyd/rays/model/db/migration/Migration7To8.kt @@ -0,0 +1,25 @@ +package com.skyd.rays.model.db.migration + +import androidx.room.migration.Migration +import androidx.sqlite.db.SupportSQLiteDatabase +import com.skyd.rays.model.bean.STICKER_SHARE_TIME_TABLE_NAME +import com.skyd.rays.model.bean.STICKER_TABLE_NAME +import com.skyd.rays.model.bean.StickerBean +import com.skyd.rays.model.bean.StickerShareTimeBean + +class Migration7To8 : Migration(7, 8) { + override fun migrate(db: SupportSQLiteDatabase) { + db.execSQL( + """ + CREATE TABLE $STICKER_SHARE_TIME_TABLE_NAME ( + ${StickerShareTimeBean.STICKER_UUID_COLUMN} TEXT NOT NULL, + ${StickerShareTimeBean.SHARE_TIME_COLUMN} INTEGER NOT NULL, + PRIMARY KEY (${StickerShareTimeBean.STICKER_UUID_COLUMN}, ${StickerShareTimeBean.SHARE_TIME_COLUMN}) + FOREIGN KEY (${StickerShareTimeBean.STICKER_UUID_COLUMN}) + REFERENCES $STICKER_TABLE_NAME(${StickerBean.UUID_COLUMN}) + ON DELETE CASCADE + ) + """ + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/skyd/rays/model/preference/theme/CustomPrimaryColorPreference.kt b/app/src/main/java/com/skyd/rays/model/preference/theme/CustomPrimaryColorPreference.kt index bcdcbfe..75c8313 100644 --- a/app/src/main/java/com/skyd/rays/model/preference/theme/CustomPrimaryColorPreference.kt +++ b/app/src/main/java/com/skyd/rays/model/preference/theme/CustomPrimaryColorPreference.kt @@ -11,7 +11,7 @@ import kotlinx.coroutines.launch object CustomPrimaryColorPreference : BasePreference { private const val CUSTOM_PRIMARY_COLOR = "customPrimaryColor" - override val default = "" + override val default = "62539F" val key = stringPreferencesKey(CUSTOM_PRIMARY_COLOR) diff --git a/app/src/main/java/com/skyd/rays/model/preference/theme/StickerColorThemePreference.kt b/app/src/main/java/com/skyd/rays/model/preference/theme/StickerColorThemePreference.kt index c447d00..382b039 100644 --- a/app/src/main/java/com/skyd/rays/model/preference/theme/StickerColorThemePreference.kt +++ b/app/src/main/java/com/skyd/rays/model/preference/theme/StickerColorThemePreference.kt @@ -3,8 +3,8 @@ package com.skyd.rays.model.preference.theme import android.content.Context import androidx.datastore.preferences.core.Preferences import androidx.datastore.preferences.core.booleanPreferencesKey +import androidx.datastore.preferences.core.edit import com.skyd.rays.ext.dataStore -import com.skyd.rays.ext.put import com.skyd.rays.model.preference.BasePreference import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers @@ -18,7 +18,10 @@ object StickerColorThemePreference : BasePreference { fun put(context: Context, scope: CoroutineScope, value: Boolean) { scope.launch(Dispatchers.IO) { - context.dataStore.put(key, value) + context.dataStore.edit { pref -> + pref[key] = value + if (value) pref[ThemeNamePreference.key] = ThemeNamePreference.CUSTOM_THEME_NAME + } } } diff --git a/app/src/main/java/com/skyd/rays/model/preference/theme/ThemeNamePreference.kt b/app/src/main/java/com/skyd/rays/model/preference/theme/ThemeNamePreference.kt index 75d7d3e..321d9a9 100644 --- a/app/src/main/java/com/skyd/rays/model/preference/theme/ThemeNamePreference.kt +++ b/app/src/main/java/com/skyd/rays/model/preference/theme/ThemeNamePreference.kt @@ -3,10 +3,10 @@ package com.skyd.rays.model.preference.theme import android.content.Context import androidx.compose.ui.graphics.Color import androidx.datastore.preferences.core.Preferences +import androidx.datastore.preferences.core.edit import androidx.datastore.preferences.core.stringPreferencesKey import com.skyd.rays.ext.dataStore import com.skyd.rays.ext.getOrDefault -import com.skyd.rays.ext.put import com.skyd.rays.model.preference.BasePreference import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers @@ -19,7 +19,7 @@ object ThemeNamePreference : BasePreference { val values: List = mutableListOf( ThemeItem( name = "Purple", - keyColor = Color(0xFF62539f) + keyColor = Color(0xFF62539F) ), ThemeItem( name = "Blue", @@ -45,7 +45,10 @@ object ThemeNamePreference : BasePreference { fun put(context: Context, scope: CoroutineScope, value: String) { scope.launch(Dispatchers.IO) { - context.dataStore.put(key, value) + context.dataStore.edit { pref -> + pref[key] = value + pref[StickerColorThemePreference.key] = false + } } } diff --git a/app/src/main/java/com/skyd/rays/model/respository/DataRepository.kt b/app/src/main/java/com/skyd/rays/model/respository/DataRepository.kt index 6c808c3..7f96f7a 100644 --- a/app/src/main/java/com/skyd/rays/model/respository/DataRepository.kt +++ b/app/src/main/java/com/skyd/rays/model/respository/DataRepository.kt @@ -2,23 +2,31 @@ 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.cache.StickerShareTimeDao 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() { +class DataRepository @Inject constructor( + private val stickerDao: StickerDao, + private val stickerShareTimeDao: StickerShareTimeDao, +) : BaseRepository() { suspend fun requestDeleteAllData(): Flow { return flowOnIo { emit(measureTimeMillis { stickerDao.deleteAllStickerWithTags() }) } } + suspend fun requestDeleteStickerShareTime(): Flow { + return flowOnIo { + emit(measureTimeMillis { stickerShareTimeDao.deleteAll() }) + } + } + suspend fun requestDeleteDocumentsProviderThumbnails(): Flow { return flowOnIo { - emit(measureTimeMillis { - PROVIDER_THUMBNAIL_DIR.deleteRecursively() - }) + emit(measureTimeMillis { PROVIDER_THUMBNAIL_DIR.deleteRecursively() }) } } } \ No newline at end of file diff --git a/app/src/main/java/com/skyd/rays/model/respository/HomeRepository.kt b/app/src/main/java/com/skyd/rays/model/respository/HomeRepository.kt index 78caa34..baf4116 100644 --- a/app/src/main/java/com/skyd/rays/model/respository/HomeRepository.kt +++ b/app/src/main/java/com/skyd/rays/model/respository/HomeRepository.kt @@ -34,4 +34,10 @@ class HomeRepository @Inject constructor( .distinctUntilChanged() .flowOn(Dispatchers.IO) } + + fun requestRecentSharedStickers(): Flow> { + return stickerDao.getRecentSharedStickers(count = 10) + .distinctUntilChanged() + .flowOn(Dispatchers.IO) + } } \ No newline at end of file diff --git a/app/src/main/java/com/skyd/rays/ui/activity/MainViewModel.kt b/app/src/main/java/com/skyd/rays/ui/activity/MainViewModel.kt index 3c6998d..3bb1728 100644 --- a/app/src/main/java/com/skyd/rays/ui/activity/MainViewModel.kt +++ b/app/src/main/java/com/skyd/rays/ui/activity/MainViewModel.kt @@ -65,8 +65,7 @@ class MainViewModel @Inject constructor() : filterIsInstance().map { intent -> if (intent.stickerUuid.isNotBlank() && - appContext.dataStore.getOrDefault(StickerColorThemePreference) && - ThemeNamePreference.isCustom(appContext) + appContext.dataStore.getOrDefault(StickerColorThemePreference) ) { setPrimaryColor(uuid = intent.stickerUuid) } diff --git a/app/src/main/java/com/skyd/rays/ui/component/AnimatedPlaceholder.kt b/app/src/main/java/com/skyd/rays/ui/component/AnimatedPlaceholder.kt index 663f13d..826ccd5 100644 --- a/app/src/main/java/com/skyd/rays/ui/component/AnimatedPlaceholder.kt +++ b/app/src/main/java/com/skyd/rays/ui/component/AnimatedPlaceholder.kt @@ -37,7 +37,7 @@ fun AnimatedPlaceholder( modifier = Modifier.padding(top = 10.dp), text = tip, textAlign = TextAlign.Center, - style = MaterialTheme.typography.labelLarge + style = MaterialTheme.typography.bodyLarge, ) } } diff --git a/app/src/main/java/com/skyd/rays/ui/screen/home/HomePartialStateChange.kt b/app/src/main/java/com/skyd/rays/ui/screen/home/HomePartialStateChange.kt index cec8145..d11f783 100644 --- a/app/src/main/java/com/skyd/rays/ui/screen/home/HomePartialStateChange.kt +++ b/app/src/main/java/com/skyd/rays/ui/screen/home/HomePartialStateChange.kt @@ -23,6 +23,7 @@ internal sealed interface HomePartialStateChange { randomTagsList = randomTagsList, recentCreatedStickersList = recentCreatedStickersList, mostSharedStickersList = mostSharedStickersList, + recentSharedStickersList = recentSharedStickersList, ).apply { loading = true } ) } @@ -34,6 +35,7 @@ internal sealed interface HomePartialStateChange { val randomTagsList: List, val recentCreatedStickersList: List, val mostSharedStickersList: List, + val recentSharedStickersList: List, ) : HomeList } } diff --git a/app/src/main/java/com/skyd/rays/ui/screen/home/HomeScreen.kt b/app/src/main/java/com/skyd/rays/ui/screen/home/HomeScreen.kt index 62895b5..c3de948 100644 --- a/app/src/main/java/com/skyd/rays/ui/screen/home/HomeScreen.kt +++ b/app/src/main/java/com/skyd/rays/ui/screen/home/HomeScreen.kt @@ -23,11 +23,12 @@ import androidx.compose.foundation.lazy.LazyRow import androidx.compose.foundation.lazy.staggeredgrid.LazyHorizontalStaggeredGrid import androidx.compose.foundation.lazy.staggeredgrid.StaggeredGridCells import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.automirrored.rounded.Reply +import androidx.compose.material.icons.automirrored.outlined.Reply +import androidx.compose.material.icons.automirrored.outlined.ScheduleSend import androidx.compose.material.icons.filled.Add import androidx.compose.material.icons.filled.Search -import androidx.compose.material.icons.rounded.MoreTime -import androidx.compose.material.icons.rounded.Shuffle +import androidx.compose.material.icons.outlined.MoreTime +import androidx.compose.material.icons.outlined.Shuffle import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.ElevatedCard import androidx.compose.material3.FilledTonalButton @@ -142,7 +143,7 @@ fun HomeScreen(viewModel: HomeViewModel = hiltViewModel()) { DisplayStickersRow( title = stringResource(id = R.string.home_screen_most_shared_stickers), count = mostSharedStickersList.size, - emptyIcon = Icons.AutoMirrored.Rounded.Reply, + emptyIcon = Icons.AutoMirrored.Outlined.Reply, key = { mostSharedStickersList[it].sticker.uuid }, itemImage = { mostSharedStickersList[it].sticker.uuid }, itemTitle = { mostSharedStickersList[it].sticker.title }, @@ -167,12 +168,42 @@ fun HomeScreen(viewModel: HomeViewModel = hiltViewModel()) { }, ) } + item { + val recentSharedStickersList = homeUiState.recentSharedStickersList + DisplayStickersRow( + title = stringResource(id = R.string.home_screen_recent_shared_stickers), + count = recentSharedStickersList.size, + emptyIcon = Icons.AutoMirrored.Outlined.ScheduleSend, + key = { recentSharedStickersList[it].sticker.uuid }, + itemImage = { recentSharedStickersList[it].sticker.uuid }, + itemTitle = { recentSharedStickersList[it].sticker.title }, + onItemLongClick = { + context.sendStickerByUuid( + uuid = recentSharedStickersList[it].sticker.uuid, + onSuccess = { recentSharedStickersList[it].sticker.shareCount++ } + ) + }, + onItemClick = { + openDetailScreen( + navController = navController, + stickerUuid = recentSharedStickersList[it].sticker.uuid + ) + }, + itemShouldBlur = { index -> + shouldBlur( + context = context, + c = recentSharedStickersList[index].tags.map { it.tag } + + recentSharedStickersList[index].sticker.title, + ) + }, + ) + } item { val recentCreatedStickersList = homeUiState.recentCreatedStickersList DisplayStickersRow( title = stringResource(id = R.string.home_screen_recent_create_stickers), count = recentCreatedStickersList.size, - emptyIcon = Icons.Rounded.MoreTime, + emptyIcon = Icons.Outlined.MoreTime, key = { recentCreatedStickersList[it].sticker.uuid }, itemImage = { recentCreatedStickersList[it].sticker.uuid }, itemTitle = { recentCreatedStickersList[it].sticker.title }, @@ -220,7 +251,7 @@ fun HomeScreen(viewModel: HomeViewModel = hiltViewModel()) { title = stringResource(id = R.string.home_screen_random_tags), count = randomTagsList.size, key = { randomTagsList[it].tag }, - emptyIcon = Icons.Rounded.Shuffle, + emptyIcon = Icons.Outlined.Shuffle, itemImage = { randomTagsList[it].stickerUuid }, itemTitle = { randomTagsList[it].tag }, onItemClick = { @@ -449,7 +480,7 @@ private fun HomeScreenFloatingActionButton( @Composable fun HomeEmptyPlaceholder() { AnimatedPlaceholder( - resId = R.raw.lottie_genshin_impact_keqing_1, - tip = stringResource(id = R.string.home_screen_empty_tip) + resId = R.raw.lottie_genshin_impact_klee_3, + tip = stringResource(id = R.string.loading) ) } diff --git a/app/src/main/java/com/skyd/rays/ui/screen/home/HomeState.kt b/app/src/main/java/com/skyd/rays/ui/screen/home/HomeState.kt index 6838b6a..337c007 100644 --- a/app/src/main/java/com/skyd/rays/ui/screen/home/HomeState.kt +++ b/app/src/main/java/com/skyd/rays/ui/screen/home/HomeState.kt @@ -25,5 +25,6 @@ sealed class HomeListState { val randomTagsList: List, val recentCreatedStickersList: List, val mostSharedStickersList: List, + val recentSharedStickersList: List, ) : HomeListState() } diff --git a/app/src/main/java/com/skyd/rays/ui/screen/home/HomeViewModel.kt b/app/src/main/java/com/skyd/rays/ui/screen/home/HomeViewModel.kt index e9d8471..c0ffd1b 100644 --- a/app/src/main/java/com/skyd/rays/ui/screen/home/HomeViewModel.kt +++ b/app/src/main/java/com/skyd/rays/ui/screen/home/HomeViewModel.kt @@ -57,12 +57,18 @@ class HomeViewModel @Inject constructor( homeRepo.requestRandomTags(), homeRepo.requestRecentCreateStickers(), homeRepo.requestMostSharedStickers(), - ) { recommendTagsList, randomTagsList, recentCreatedStickersList, mostSharedStickersList -> + homeRepo.requestRecentSharedStickers(), + ) { recommendTagsList, + randomTagsList, + recentCreatedStickersList, + mostSharedStickersList, + recentSharedStickersList -> HomePartialStateChange.HomeList.Success( recommendTagsList = recommendTagsList, randomTagsList = randomTagsList, recentCreatedStickersList = recentCreatedStickersList, mostSharedStickersList = mostSharedStickersList, + recentSharedStickersList = recentSharedStickersList, ) }.startWith(HomePartialStateChange.HomeList.Loading) }, diff --git a/app/src/main/java/com/skyd/rays/ui/screen/settings/data/DataEvent.kt b/app/src/main/java/com/skyd/rays/ui/screen/settings/data/DataEvent.kt index 41b0c5d..db1e482 100644 --- a/app/src/main/java/com/skyd/rays/ui/screen/settings/data/DataEvent.kt +++ b/app/src/main/java/com/skyd/rays/ui/screen/settings/data/DataEvent.kt @@ -6,4 +6,8 @@ sealed interface DataEvent : MviSingleEvent { sealed interface DeleteAllResultEvent : DataEvent { data class Success(val time: Long) : DeleteAllResultEvent } + + sealed interface DeleteStickerShareTimeResultEvent : DataEvent { + data class Success(val time: Long) : DeleteStickerShareTimeResultEvent + } } diff --git a/app/src/main/java/com/skyd/rays/ui/screen/settings/data/DataIntent.kt b/app/src/main/java/com/skyd/rays/ui/screen/settings/data/DataIntent.kt index 04060f4..f0c44d5 100644 --- a/app/src/main/java/com/skyd/rays/ui/screen/settings/data/DataIntent.kt +++ b/app/src/main/java/com/skyd/rays/ui/screen/settings/data/DataIntent.kt @@ -5,4 +5,5 @@ import com.skyd.rays.base.mvi.MviIntent sealed interface DataIntent : MviIntent { data object Init : DataIntent data object DeleteAllData : DataIntent + data object DeleteStickerShareTime : DataIntent } \ No newline at end of file diff --git a/app/src/main/java/com/skyd/rays/ui/screen/settings/data/DataPartialStateChange.kt b/app/src/main/java/com/skyd/rays/ui/screen/settings/data/DataPartialStateChange.kt index edcfe90..427ae1e 100644 --- a/app/src/main/java/com/skyd/rays/ui/screen/settings/data/DataPartialStateChange.kt +++ b/app/src/main/java/com/skyd/rays/ui/screen/settings/data/DataPartialStateChange.kt @@ -17,4 +17,11 @@ internal sealed interface DataPartialStateChange { oldState.copy(loadingDialog = false) } } + + sealed interface DeleteStickerShareTime : DataPartialStateChange { + data class Success(val time: Long) : DeleteStickerShareTime { + override fun reduce(oldState: DataState): DataState = + oldState.copy(loadingDialog = false) + } + } } diff --git a/app/src/main/java/com/skyd/rays/ui/screen/settings/data/DataScreen.kt b/app/src/main/java/com/skyd/rays/ui/screen/settings/data/DataScreen.kt index 0bd089f..43c0535 100644 --- a/app/src/main/java/com/skyd/rays/ui/screen/settings/data/DataScreen.kt +++ b/app/src/main/java/com/skyd/rays/ui/screen/settings/data/DataScreen.kt @@ -47,7 +47,8 @@ fun DataScreen(viewModel: DataViewModel = hiltViewModel()) { val navController = LocalNavController.current val context = LocalContext.current val snackbarHostState = remember { SnackbarHostState() } - var openDeleteWarningDialog by rememberSaveable { mutableStateOf(false) } + var openDeleteAllStickersDialog by rememberSaveable { mutableStateOf(false) } + var openDeleteStickerShareTimeDialog by rememberSaveable { mutableStateOf(false) } val uiState by viewModel.viewState.collectAsStateWithLifecycle() val uiEvent by viewModel.singleEvent.collectAsStateWithLifecycle(initialValue = null) @@ -101,7 +102,15 @@ fun DataScreen(viewModel: DataViewModel = hiltViewModel()) { icon = rememberVectorPainter(image = Icons.Default.Delete), text = stringResource(id = R.string.data_screen_delete_all), descriptionText = stringResource(id = R.string.data_screen_delete_all_description), - onClick = { openDeleteWarningDialog = true } + onClick = { openDeleteAllStickersDialog = true } + ) + } + item { + BaseSettingsItem( + icon = rememberVectorPainter(image = Icons.Default.Delete), + text = stringResource(id = R.string.data_screen_delete_all_sticker_share_time_data), + descriptionText = stringResource(id = R.string.data_screen_delete_all_sticker_share_time_data_description), + onClick = { openDeleteStickerShareTimeDialog = true } ) } } @@ -117,17 +126,36 @@ fun DataScreen(viewModel: DataViewModel = hiltViewModel()) { ) } + is DataEvent.DeleteStickerShareTimeResultEvent.Success -> { + snackbarHostState.showSnackbarWithLaunchedEffect( + message = context.getString( + R.string.data_screen_delete_sticker_share_time_success, + event.time / 1000.0f + ), + key2 = event, + ) + } + null -> Unit } WaitingDialog(visible = uiState.loadingDialog) DeleteWarningDialog( - visible = openDeleteWarningDialog, - onDismissRequest = { openDeleteWarningDialog = false }, - onDismiss = { openDeleteWarningDialog = false }, + visible = openDeleteAllStickersDialog, + onDismissRequest = { openDeleteAllStickersDialog = false }, + onDismiss = { openDeleteAllStickersDialog = false }, onConfirm = { dispatch(DataIntent.DeleteAllData) - openDeleteWarningDialog = false + openDeleteAllStickersDialog = false + } + ) + DeleteWarningDialog( + visible = openDeleteStickerShareTimeDialog, + onDismissRequest = { openDeleteStickerShareTimeDialog = false }, + onDismiss = { openDeleteStickerShareTimeDialog = false }, + onConfirm = { + dispatch(DataIntent.DeleteStickerShareTime) + openDeleteStickerShareTimeDialog = false } ) } diff --git a/app/src/main/java/com/skyd/rays/ui/screen/settings/data/DataViewModel.kt b/app/src/main/java/com/skyd/rays/ui/screen/settings/data/DataViewModel.kt index ad95194..c77acd1 100644 --- a/app/src/main/java/com/skyd/rays/ui/screen/settings/data/DataViewModel.kt +++ b/app/src/main/java/com/skyd/rays/ui/screen/settings/data/DataViewModel.kt @@ -53,6 +53,9 @@ class DataViewModel @Inject constructor(private var dataRepo: DataRepository) : is DataPartialStateChange.DeleteAllData.Success -> DataEvent.DeleteAllResultEvent.Success(change.time) + is DataPartialStateChange.DeleteStickerShareTime.Success -> + DataEvent.DeleteStickerShareTimeResultEvent.Success(change.time) + else -> return@onEach } sendEvent(event) @@ -68,6 +71,11 @@ class DataViewModel @Inject constructor(private var dataRepo: DataRepository) : .map { DataPartialStateChange.DeleteAllData.Success(it) } .startWith(DataPartialStateChange.LoadingDialog) }, + filterIsInstance().flatMapConcat { + dataRepo.requestDeleteStickerShareTime() + .map { DataPartialStateChange.DeleteStickerShareTime.Success(it) } + .startWith(DataPartialStateChange.LoadingDialog) + }, ) } } \ No newline at end of file diff --git a/app/src/main/java/com/skyd/rays/util/StickerUtil.kt b/app/src/main/java/com/skyd/rays/util/StickerUtil.kt index 56d6b6b..5e8b742 100644 --- a/app/src/main/java/com/skyd/rays/util/StickerUtil.kt +++ b/app/src/main/java/com/skyd/rays/util/StickerUtil.kt @@ -56,7 +56,7 @@ fun Context.sendStickerByUuid( stickerFiles = listOf(stickerFile), topActivityFullName = topActivityFullName, onSuccess = { - AppDatabase.getInstance(context).stickerDao().addShareCount(uuids = listOf(uuid)) + AppDatabase.getInstance(context).stickerDao().shareStickers(uuids = listOf(uuid)) onSuccess?.invoke() } ) @@ -75,7 +75,7 @@ fun Context.sendStickersByUuids( stickerFiles = stickerFiles, topActivityFullName = topActivityFullName, onSuccess = { - AppDatabase.getInstance(context).stickerDao().addShareCount(uuids = uuids) + AppDatabase.getInstance(context).stickerDao().shareStickers(uuids = uuids) onSuccess?.invoke() } ) diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index f9a314c..284aecc 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -143,6 +143,7 @@ 删除 Rays 管理的所有本地表情包数据,包括表情包图像、标题和标签等,此操作不可逆 备份、同步、删除表情包数据等 所有表情包数据已清空,耗时 %.2f 秒 + 所有表情包分享数据已清空,耗时 %.2f 秒 交集搜索 使用空格分割多个搜索关键字以实现“并且”条件,即对这些关键字的结果进行交集操作 删除失败 @@ -152,7 +153,6 @@ 导出了 %d 个表情包 还原 - 开始新建表情包吧~ 空空如也呢~ 空空如也呢~ 选择表情包 @@ -301,6 +301,7 @@ 推荐标签 随机标签 最近创建 + 最近分享 最多分享 导出为 Zip 备份文件 表情包列表 @@ -327,4 +328,6 @@ 删除表情包提供者缩略图缓存 已删除缩略图缓存, 花费 %.2f 秒 危险区域 + 删除分享数据 + 删除分享表情包操作的统计数据,例如每次分享的时间 \ No newline at end of file diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index 3ea227a..f247bee 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -128,6 +128,7 @@ 更新本地數據 + 表情包 上次修改時間:%s 所有表情包數據已清除,花費 %.2f 秒 + 所有表情包分享資料已清空,花費 %.2f 秒 文字識別閾值 分享時複製 背景 @@ -208,7 +209,6 @@ 發送表情包 搜尋方案、搜尋領域等 建議允許 Rays 自啟動、關聯啟動和背景運行。\n\nRays 的以下功能需要使用無障礙:\n\n自動分享 - 創建新的表情包吧~ 空~ UUID 遠端回收站為空 @@ -301,6 +301,7 @@ 推薦標籤 隨機標籤 最近創建 + 最近分享 最多分享 匯出為 Zip 備份文件 表情包列表 @@ -327,4 +328,6 @@ 刪除貼紙提供者縮圖快取 所有縮圖均已清除,已花費 %.2f 秒 危險區域 + 刪除分享數據 + 刪除表情包分享統計訊息,例如每次分享的時間 \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 58c6273..3b54d97 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -148,6 +148,7 @@ Delete all local data managed by Rays, including images, titles and labels. This operation is irreversible Backup, synchronize, delete sticker data All sticker data cleared, spent %.2fs + All sticker share statistics cleared, spent %.2fs Intersection search Use spaces to split multiple keywords to achieve the \"and\" condition Delete failed @@ -158,7 +159,6 @@ Export %d stickers Restore - Please create a new sticker~ Empty~ It\'s empty~ Select stickers @@ -312,8 +312,9 @@ More Recommend tags Random tags - Recently created - Most shared + Recent creates + Recent shares + Most shares Export as backup zip Stickers list Export as backup zip @@ -339,4 +340,6 @@ Delete sticker provider thumbnail caches All thumbnails cleared, spent %.2fs Danger area + Delete share data + Delete sticker share statistics, e.g. time of each share \ No newline at end of file