From 4f12db0ee4fbc1de9a6d858730633878819bf2f1 Mon Sep 17 00:00:00 2001 From: SkyD666 Date: Mon, 20 Nov 2023 00:00:21 +0800 Subject: [PATCH] [feature|optimize] Support HEIF/HEIC format images; optimize code --- app/build.gradle | 4 +- .../java/com/skyd/rays/ext/PreferenceExt.kt | 4 +- .../skyd/rays/model/preference/Settings.kt | 8 +-- ...tickerToClipboardWhenSharingPreference.kt} | 6 +- .../java/com/skyd/rays/ui/local/LocalValue.kt | 4 +- .../settings/appearance/AppearanceScreen.kt | 11 +++- .../settings/shareconfig/ShareConfigScreen.kt | 8 +-- .../java/com/skyd/rays/util/StickerUtil.kt | 5 +- .../rays/util/image/format/FormatStandard.kt | 59 ++++++++++++++++++- .../rays/util/image/format/ImageFormat.kt | 4 ++ app/src/main/res/values-zh-rCN/strings.xml | 4 ++ app/src/main/res/values-zh-rTW/strings.xml | 4 ++ app/src/main/res/values/strings.xml | 2 +- build.gradle | 4 +- 14 files changed, 102 insertions(+), 25 deletions(-) rename app/src/main/java/com/skyd/rays/model/preference/share/{CopyStickerToClipboardPreference.kt => CopyStickerToClipboardWhenSharingPreference.kt} (74%) diff --git a/app/build.gradle b/app/build.gradle index 008ab57..0a55d85 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -21,8 +21,8 @@ android { applicationId "com.skyd.rays" minSdk 24 targetSdk 34 - versionCode 44 - versionName "1.6-rc03" + versionCode 45 + versionName "1.6-rc04" flavorDimensions = ["versionName"] testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" diff --git a/app/src/main/java/com/skyd/rays/ext/PreferenceExt.kt b/app/src/main/java/com/skyd/rays/ext/PreferenceExt.kt index dd3e3c1..ef849c1 100644 --- a/app/src/main/java/com/skyd/rays/ext/PreferenceExt.kt +++ b/app/src/main/java/com/skyd/rays/ext/PreferenceExt.kt @@ -20,7 +20,7 @@ import com.skyd.rays.model.preference.search.QueryPreference import com.skyd.rays.model.preference.search.SearchResultReversePreference import com.skyd.rays.model.preference.search.SearchResultSortPreference import com.skyd.rays.model.preference.search.UseRegexSearchPreference -import com.skyd.rays.model.preference.share.CopyStickerToClipboardPreference +import com.skyd.rays.model.preference.share.CopyStickerToClipboardWhenSharingPreference import com.skyd.rays.model.preference.share.StickerExtNamePreference import com.skyd.rays.model.preference.share.UriStringSharePreference import com.skyd.rays.model.preference.theme.CustomPrimaryColorPreference @@ -64,7 +64,7 @@ fun Preferences.toSettings(): Settings { // Share uriStringShare = UriStringSharePreference.fromPreferences(this), stickerExtName = StickerExtNamePreference.fromPreferences(this), - copyStickerToClipboard = CopyStickerToClipboardPreference.fromPreferences(this), + copyStickerToClipboardWhenSharing = CopyStickerToClipboardWhenSharingPreference.fromPreferences(this), autoShareIgnoreStrategy = AutoShareIgnoreStrategyPreference.fromPreferences(this), // Api diff --git a/app/src/main/java/com/skyd/rays/model/preference/Settings.kt b/app/src/main/java/com/skyd/rays/model/preference/Settings.kt index fef8de6..ff4cedb 100644 --- a/app/src/main/java/com/skyd/rays/model/preference/Settings.kt +++ b/app/src/main/java/com/skyd/rays/model/preference/Settings.kt @@ -16,7 +16,7 @@ import com.skyd.rays.model.preference.search.QueryPreference import com.skyd.rays.model.preference.search.SearchResultReversePreference import com.skyd.rays.model.preference.search.SearchResultSortPreference import com.skyd.rays.model.preference.search.UseRegexSearchPreference -import com.skyd.rays.model.preference.share.CopyStickerToClipboardPreference +import com.skyd.rays.model.preference.share.CopyStickerToClipboardWhenSharingPreference import com.skyd.rays.model.preference.share.StickerExtNamePreference import com.skyd.rays.model.preference.share.UriStringSharePreference import com.skyd.rays.model.preference.theme.CustomPrimaryColorPreference @@ -26,7 +26,7 @@ import com.skyd.rays.model.preference.theme.ThemeNamePreference import com.skyd.rays.ui.local.LocalApiGrant import com.skyd.rays.ui.local.LocalAutoShareIgnoreStrategy import com.skyd.rays.ui.local.LocalClassificationThreshold -import com.skyd.rays.ui.local.LocalCopyStickerToClipboard +import com.skyd.rays.ui.local.LocalCopyStickerToClipboardWhenSharing import com.skyd.rays.ui.local.LocalCurrentStickerUuid import com.skyd.rays.ui.local.LocalCustomPrimaryColor import com.skyd.rays.ui.local.LocalDarkMode @@ -79,7 +79,7 @@ data class Settings( // Share val uriStringShare: Boolean = UriStringSharePreference.default, val stickerExtName: Boolean = StickerExtNamePreference.default, - val copyStickerToClipboard: Boolean = CopyStickerToClipboardPreference.default, + val copyStickerToClipboardWhenSharing: Boolean = CopyStickerToClipboardWhenSharingPreference.default, val autoShareIgnoreStrategy: String = AutoShareIgnoreStrategyPreference.default, // Api val apiGrant: Boolean = ApiGrantPreference.default, @@ -126,7 +126,7 @@ fun SettingsProvider( // Share LocalUriStringShare provides settings.uriStringShare, LocalStickerExtName provides settings.stickerExtName, - LocalCopyStickerToClipboard provides settings.copyStickerToClipboard, + LocalCopyStickerToClipboardWhenSharing provides settings.copyStickerToClipboardWhenSharing, LocalAutoShareIgnoreStrategy provides settings.autoShareIgnoreStrategy, // Api LocalApiGrant provides settings.apiGrant, diff --git a/app/src/main/java/com/skyd/rays/model/preference/share/CopyStickerToClipboardPreference.kt b/app/src/main/java/com/skyd/rays/model/preference/share/CopyStickerToClipboardWhenSharingPreference.kt similarity index 74% rename from app/src/main/java/com/skyd/rays/model/preference/share/CopyStickerToClipboardPreference.kt rename to app/src/main/java/com/skyd/rays/model/preference/share/CopyStickerToClipboardWhenSharingPreference.kt index 4b6674f..f8fbe82 100644 --- a/app/src/main/java/com/skyd/rays/model/preference/share/CopyStickerToClipboardPreference.kt +++ b/app/src/main/java/com/skyd/rays/model/preference/share/CopyStickerToClipboardWhenSharingPreference.kt @@ -10,11 +10,11 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch -object CopyStickerToClipboardPreference : BasePreference { - private const val COPY_STICKER_TO_CLIPBOARD = "copyStickerToClipboard" +object CopyStickerToClipboardWhenSharingPreference : BasePreference { + private const val COPY_STICKER_TO_CLIPBOARD_WHEN_SHARING = "copyStickerToClipboardWhenSharing" override val default = true - val key = booleanPreferencesKey(COPY_STICKER_TO_CLIPBOARD) + val key = booleanPreferencesKey(COPY_STICKER_TO_CLIPBOARD_WHEN_SHARING) fun put(context: Context, scope: CoroutineScope, value: Boolean) { scope.launch(Dispatchers.IO) { diff --git a/app/src/main/java/com/skyd/rays/ui/local/LocalValue.kt b/app/src/main/java/com/skyd/rays/ui/local/LocalValue.kt index 288b3ec..64054c4 100644 --- a/app/src/main/java/com/skyd/rays/ui/local/LocalValue.kt +++ b/app/src/main/java/com/skyd/rays/ui/local/LocalValue.kt @@ -21,7 +21,7 @@ import com.skyd.rays.model.preference.search.QueryPreference import com.skyd.rays.model.preference.search.SearchResultReversePreference import com.skyd.rays.model.preference.search.SearchResultSortPreference import com.skyd.rays.model.preference.search.UseRegexSearchPreference -import com.skyd.rays.model.preference.share.CopyStickerToClipboardPreference +import com.skyd.rays.model.preference.share.CopyStickerToClipboardWhenSharingPreference import com.skyd.rays.model.preference.share.StickerExtNamePreference import com.skyd.rays.model.preference.share.UriStringSharePreference import com.skyd.rays.model.preference.theme.CustomPrimaryColorPreference @@ -73,7 +73,7 @@ val LocalHomeShareButtonAlignment = // Share val LocalUriStringShare = compositionLocalOf { UriStringSharePreference.default } val LocalStickerExtName = compositionLocalOf { StickerExtNamePreference.default } -val LocalCopyStickerToClipboard = compositionLocalOf { CopyStickerToClipboardPreference.default } +val LocalCopyStickerToClipboardWhenSharing = compositionLocalOf { CopyStickerToClipboardWhenSharingPreference.default } val LocalAutoShareIgnoreStrategy = compositionLocalOf { AutoShareIgnoreStrategyPreference.default } // Api diff --git a/app/src/main/java/com/skyd/rays/ui/screen/settings/appearance/AppearanceScreen.kt b/app/src/main/java/com/skyd/rays/ui/screen/settings/appearance/AppearanceScreen.kt index 6a1d386..3df7232 100644 --- a/app/src/main/java/com/skyd/rays/ui/screen/settings/appearance/AppearanceScreen.kt +++ b/app/src/main/java/com/skyd/rays/ui/screen/settings/appearance/AppearanceScreen.kt @@ -8,9 +8,11 @@ import androidx.compose.animation.fadeIn import androidx.compose.animation.fadeOut import androidx.compose.animation.scaleIn import androidx.compose.animation.scaleOut +import androidx.compose.foundation.LocalIndication import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.horizontalScroll +import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column @@ -48,6 +50,7 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue @@ -206,9 +209,8 @@ private fun DarkModeSheet(onDismissRequest: () -> Unit) { .selectableGroup() ) { DarkModePreference.values.forEach { - Card( - colors = CardDefaults.cardColors(containerColor = Color.Transparent) - ) { + Card(colors = CardDefaults.cardColors(containerColor = Color.Transparent)) { + val interactionSource = remember { MutableInteractionSource() } Row( Modifier .fillMaxWidth() @@ -221,6 +223,8 @@ private fun DarkModeSheet(onDismissRequest: () -> Unit) { value = it ) }, + interactionSource = interactionSource, + indication = LocalIndication.current, role = Role.RadioButton ) .padding(horizontal = 16.dp), @@ -228,6 +232,7 @@ private fun DarkModeSheet(onDismissRequest: () -> Unit) { ) { RadioButton( selected = (it == darkMode), + interactionSource = interactionSource, onClick = null // null recommended for accessibility with screen readers ) Text( diff --git a/app/src/main/java/com/skyd/rays/ui/screen/settings/shareconfig/ShareConfigScreen.kt b/app/src/main/java/com/skyd/rays/ui/screen/settings/shareconfig/ShareConfigScreen.kt index 1a47855..0611bb4 100644 --- a/app/src/main/java/com/skyd/rays/ui/screen/settings/shareconfig/ShareConfigScreen.kt +++ b/app/src/main/java/com/skyd/rays/ui/screen/settings/shareconfig/ShareConfigScreen.kt @@ -18,13 +18,13 @@ import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import com.skyd.rays.R -import com.skyd.rays.model.preference.share.CopyStickerToClipboardPreference +import com.skyd.rays.model.preference.share.CopyStickerToClipboardWhenSharingPreference import com.skyd.rays.model.preference.share.StickerExtNamePreference 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.SwitchSettingsItem -import com.skyd.rays.ui.local.LocalCopyStickerToClipboard +import com.skyd.rays.ui.local.LocalCopyStickerToClipboardWhenSharing import com.skyd.rays.ui.local.LocalNavController import com.skyd.rays.ui.local.LocalStickerExtName import com.skyd.rays.ui.screen.settings.shareconfig.autoshare.AUTO_SHARE_SCREEN_ROUTE @@ -74,12 +74,12 @@ fun ShareConfigScreen() { item { SwitchSettingsItem( icon = Icons.Default.FileCopy, - checked = LocalCopyStickerToClipboard.current, + checked = LocalCopyStickerToClipboardWhenSharing.current, text = stringResource(R.string.share_config_screen_copy_sticker_to_clipboard), description = stringResource(R.string.share_config_screen_copy_sticker_to_clipboard_description), onCheckedChange = { scope.launch { - CopyStickerToClipboardPreference.put( + CopyStickerToClipboardWhenSharingPreference.put( scope = scope, context = context, value = it 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 7902644..400f815 100644 --- a/app/src/main/java/com/skyd/rays/util/StickerUtil.kt +++ b/app/src/main/java/com/skyd/rays/util/StickerUtil.kt @@ -19,6 +19,7 @@ import com.skyd.rays.ext.dataStore import com.skyd.rays.ext.getOrDefault import com.skyd.rays.model.bean.StickerWithTags import com.skyd.rays.model.db.AppDatabase +import com.skyd.rays.model.preference.share.CopyStickerToClipboardWhenSharingPreference import com.skyd.rays.model.preference.share.StickerExtNamePreference import com.skyd.rays.model.preference.share.UriStringSharePreference import com.skyd.rays.ui.service.RaysAccessibilityService @@ -107,7 +108,9 @@ fun Context.sendStickersByFiles( ) } - copyStickerToClipboard(*contentUris.toTypedArray()) + if (dataStore.getOrDefault(CopyStickerToClipboardWhenSharingPreference)) { + copyStickerToClipboard(*contentUris.toTypedArray()) + } with(AppDatabase.getInstance(this@sendStickersByFiles)) { if (dataStore.getOrDefault(UriStringSharePreference)) { diff --git a/app/src/main/java/com/skyd/rays/util/image/format/FormatStandard.kt b/app/src/main/java/com/skyd/rays/util/image/format/FormatStandard.kt index a25a38c..f6f5421 100644 --- a/app/src/main/java/com/skyd/rays/util/image/format/FormatStandard.kt +++ b/app/src/main/java/com/skyd/rays/util/image/format/FormatStandard.kt @@ -10,7 +10,7 @@ sealed class FormatStandard( ) { companion object { val formatStandards by lazy { - arrayOf(PngFormat, JpgFormat, GifFormat, BmpFormat, WebpFormat) + arrayOf(PngFormat, JpgFormat, GifFormat, BmpFormat, WebpFormat, HeifFormat, HeicFormat) } } @@ -179,6 +179,63 @@ sealed class FormatStandard( return true } } + + /** + * https://github.com/nokiatech/heif/issues/74 + * https://zh.wikipedia.org/wiki/%E9%AB%98%E6%95%88%E7%8E%87%E5%9B%BE%E5%83%8F%E6%96%87%E4%BB%B6%E6%A0%BC%E5%BC%8F# + */ + data object HeifFormat : FormatStandard( + format = ImageFormat.HEIF, + requiredByteArraySize = 12, + ) { + private val ftyp = byteArrayOf(0x66.toByte(), 0x74.toByte(), 0x79.toByte(), 0x70.toByte()) + private val mif1 = byteArrayOf(0x6d.toByte(), 0x69.toByte(), 0x66.toByte(), 0x31.toByte()) + private val msf1 = byteArrayOf(0x6d.toByte(), 0x73.toByte(), 0x66.toByte(), 0x31.toByte()) + private val m = arrayOf(mif1, msf1) + + override fun check(tested: ByteArray): Boolean { + for (i in 4..7) { + if (tested[i] != ftyp[i - 4]) return false + } + + m.forEach { + if (baseCheck(it, byteArrayOf(tested[8], tested[9], tested[10], tested[11]))) { + return true + } + } + return false + } + } + + data object HeicFormat : FormatStandard( + format = ImageFormat.HEIC, + requiredByteArraySize = 12, + ) { + private val ftyp = byteArrayOf(0x66.toByte(), 0x74.toByte(), 0x79.toByte(), 0x70.toByte()) + private val he = byteArrayOf(0x68.toByte(), 0x65.toByte()) + private val icIxIsVcVx = arrayOf( + byteArrayOf(0x69.toByte(), 0x63.toByte()), + byteArrayOf(0x69.toByte(), 0x78.toByte()), + byteArrayOf(0x69.toByte(), 0x73.toByte()), + byteArrayOf(0x76.toByte(), 0x63.toByte()), + byteArrayOf(0x76.toByte(), 0x78.toByte()), + ) + + override fun check(tested: ByteArray): Boolean { + for (i in 4..7) { + if (tested[i] != ftyp[i - 4]) return false + } + for (i in 8..9) { + if (tested[i] != he[i - 8]) return false + } + icIxIsVcVx.forEach { + if (baseCheck(it, byteArrayOf(tested[10], tested[11]))) { + return true + } + } + return false + } + } } object FormatStandardUtil { diff --git a/app/src/main/java/com/skyd/rays/util/image/format/ImageFormat.kt b/app/src/main/java/com/skyd/rays/util/image/format/ImageFormat.kt index e066ef3..f791e2a 100644 --- a/app/src/main/java/com/skyd/rays/util/image/format/ImageFormat.kt +++ b/app/src/main/java/com/skyd/rays/util/image/format/ImageFormat.kt @@ -6,6 +6,8 @@ enum class ImageFormat { GIF, WEBP, BMP, + HEIF, + HEIC, UNDEFINED; override fun toString(): String { @@ -15,6 +17,8 @@ enum class ImageFormat { GIF -> ".gif" WEBP -> ".webp" BMP -> ".bmp" + HEIF -> ".heif" + HEIC -> ".heic" UNDEFINED -> "" } } diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index 54c19e0..11ef69a 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -289,4 +289,8 @@ 禁止在应用内截图 展开 收起 + 复制 + 保存 + 已保存到“Pictures/Rays” + 保存失败 \ 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 96f8dd6..3afda4d 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -289,4 +289,8 @@ 同步表情包數據 填滿寬度 表情包分類 + 複製 + 儲存 + 已儲存到「Pictures/Rays」 + 保存失敗 \ 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 fc50031..e6e2b2b 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -302,6 +302,6 @@ Collapse Copy Save - Saved to Pictures/Rays + Saved to \"Pictures/Rays\" Save failed \ No newline at end of file diff --git a/build.gradle b/build.gradle index d1b80d4..8c39131 100644 --- a/build.gradle +++ b/build.gradle @@ -11,8 +11,8 @@ buildscript { } }// Top-level build file where you can add configuration options common to all sub-projects/modules. plugins { - id 'com.android.application' version '8.1.3' apply false - id 'com.android.library' version '8.1.3' apply false + id 'com.android.application' version '8.1.4' apply false + id 'com.android.library' version '8.1.4' apply false id 'org.jetbrains.kotlin.android' version '1.9.20' apply false id 'com.google.dagger.hilt.android' version '2.48.1' apply false id 'com.google.devtools.ksp' version '1.9.20-1.0.14' apply false