新增选择图片创建主题颜色功能

This commit is contained in:
HapeLee committed 2025-10-19 00:40:46 +08:00
1 parent 0d671df389
commit 45c953024f
9 files changed
+218 -43

No files matched your search

+31 -3
View File
@@ -7,7 +7,9 @@ import android.app.NotificationManager
import android.content.Context import android.content.Context
import android.content.pm.ApplicationInfo import android.content.pm.ApplicationInfo
import android.content.res.Configuration import android.content.res.Configuration
import android.graphics.BitmapFactory
import android.os.Build import android.os.Build
import androidx.core.graphics.scale
import com.github.liuyueyi.quick.transfer.constants.TransType import com.github.liuyueyi.quick.transfer.constants.TransType
import com.google.android.material.color.DynamicColors import com.google.android.material.color.DynamicColors
import com.google.android.material.color.DynamicColorsOptions import com.google.android.material.color.DynamicColorsOptions
@@ -59,6 +61,7 @@ import kotlinx.coroutines.launch
import org.chromium.base.ThreadUtils import org.chromium.base.ThreadUtils
import splitties.init.appCtx import splitties.init.appCtx
import splitties.systemservices.notificationManager import splitties.systemservices.notificationManager
import java.io.File
import java.net.URL import java.net.URL
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
import java.util.logging.Level import java.util.logging.Level
@@ -72,9 +75,34 @@ class App : Application() {
if (getPrefString("app_theme", "0") == "11") { if (getPrefString("app_theme", "0") == "11") {
if (AppConfig.customMode == "accent") if (AppConfig.customMode == "accent")
setTheme(R.style.ThemeOverlay_WhiteBackground) setTheme(R.style.ThemeOverlay_WhiteBackground)
DynamicColors.applyToActivitiesIfAvailable(this, DynamicColorsOptions.Builder()
.setContentBasedSource(this.primaryColor) val colorImagePath = getPrefString(PreferKey.colorImage)
.build()) if (!colorImagePath.isNullOrBlank()) {
val file = File(colorImagePath)
if (file.exists()) {
val bitmap = BitmapFactory.decodeFile(file.absolutePath)
if (bitmap != null) {
val colorAccuracy = true
val targetWidth = if (colorAccuracy) (bitmap.width / 4).coerceAtMost(256) else 16
val targetHeight = if (colorAccuracy) (bitmap.height / 4).coerceAtMost(256) else 16
val scaledBitmap = bitmap.scale(targetWidth, targetHeight, false)
val options = DynamicColorsOptions.Builder()
.setContentBasedSource(scaledBitmap)
.build()
DynamicColors.applyToActivitiesIfAvailable(this, options)
bitmap.recycle()
}
}
}else{
DynamicColors.applyToActivitiesIfAvailable(
this,
DynamicColorsOptions.Builder()
.setContentBasedSource(this.primaryColor)
.build()
)
}
} }
super.onCreate() super.onCreate()
@@ -3,6 +3,7 @@ package io.legato.kazusa.base
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.content.Context import android.content.Context
import android.content.res.Configuration import android.content.res.Configuration
import android.graphics.BitmapFactory
import android.graphics.Color import android.graphics.Color
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
@@ -14,6 +15,7 @@ import android.view.View
import androidx.activity.enableEdgeToEdge import androidx.activity.enableEdgeToEdge
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.core.graphics.drawable.toDrawable import androidx.core.graphics.drawable.toDrawable
import androidx.core.graphics.scale
import androidx.core.view.WindowCompat import androidx.core.view.WindowCompat
import androidx.viewbinding.ViewBinding import androidx.viewbinding.ViewBinding
import com.google.android.material.appbar.AppBarLayout import com.google.android.material.appbar.AppBarLayout
@@ -22,6 +24,7 @@ import com.google.android.material.color.DynamicColorsOptions
import io.legato.kazusa.R import io.legato.kazusa.R
import io.legato.kazusa.constant.AppLog import io.legato.kazusa.constant.AppLog
import io.legato.kazusa.constant.EventBus import io.legato.kazusa.constant.EventBus
import io.legato.kazusa.constant.PreferKey
import io.legato.kazusa.constant.Theme import io.legato.kazusa.constant.Theme
import io.legato.kazusa.help.config.AppConfig import io.legato.kazusa.help.config.AppConfig
import io.legato.kazusa.help.config.ThemeConfig import io.legato.kazusa.help.config.ThemeConfig
@@ -38,6 +41,7 @@ import io.legato.kazusa.utils.fullScreen
import io.legato.kazusa.utils.setNavigationBarColorAuto import io.legato.kazusa.utils.setNavigationBarColorAuto
import io.legato.kazusa.utils.setStatusBarColorAuto import io.legato.kazusa.utils.setStatusBarColorAuto
import io.legato.kazusa.utils.themeColor import io.legato.kazusa.utils.themeColor
import java.io.File
abstract class BaseActivity<VB : ViewBinding>( abstract class BaseActivity<VB : ViewBinding>(
@@ -169,11 +173,36 @@ abstract class BaseActivity<VB : ViewBinding>(
"11" -> { "11" -> {
if (AppConfig.customMode == "accent") if (AppConfig.customMode == "accent")
setTheme(R.style.ThemeOverlay_WhiteBackground) setTheme(R.style.ThemeOverlay_WhiteBackground)
DynamicColors.applyToActivitiesIfAvailable(application, DynamicColorsOptions.Builder()
.setContentBasedSource(application.primaryColor)
.build())
val colorImagePath = getPrefString(PreferKey.colorImage)
if (!colorImagePath.isNullOrBlank()) {
val file = File(colorImagePath)
if (file.exists()) {
val bitmap = BitmapFactory.decodeFile(file.absolutePath)
if (bitmap != null) {
val colorAccuracy = true
val targetWidth = if (colorAccuracy) (bitmap.width / 4).coerceAtMost(256) else 16
val targetHeight = if (colorAccuracy) (bitmap.height / 4).coerceAtMost(256) else 16
val scaledBitmap = bitmap.scale(targetWidth, targetHeight, false)
val options = DynamicColorsOptions.Builder()
.setContentBasedSource(scaledBitmap)
.build()
DynamicColors.applyToActivitiesIfAvailable(application, options)
bitmap.recycle()
}
}
}else{
DynamicColors.applyToActivitiesIfAvailable(
application,
DynamicColorsOptions.Builder()
.setContentBasedSource(application.primaryColor)
.build()
)
}
} }
"12" -> setTheme(R.style.AppTheme_Transparent) "12" -> setTheme(R.style.AppTheme_Transparent)
} }
@@ -205,4 +205,6 @@ object PreferKey {
const val pureBlack = "pure_black" const val pureBlack = "pure_black"
const val labelVisibilityMode = "labelVisibilityMode" const val labelVisibilityMode = "labelVisibilityMode"
const val menuAlpha = "menuAlpha" const val menuAlpha = "menuAlpha"
const val colorImage = "colorImage"
} }
@@ -0,0 +1,53 @@
package io.legato.kazusa.lib.prefs
import android.content.Context
import android.graphics.BitmapFactory
import android.net.Uri
import android.util.AttributeSet
import android.view.View
import android.widget.ImageView
import androidx.preference.PreferenceViewHolder
import io.legato.kazusa.R
import java.io.File
class ImagePreviewPreference(context: Context, attrs: AttributeSet) : Preference(context, attrs) {
private var preview: ImageView? = null
init {
layoutResource = R.layout.view_preference
widgetLayoutResource = R.layout.view_icon
}
override fun onBindViewHolder(holder: PreferenceViewHolder) {
super.onBindViewHolder(holder)
val v = Preference.bindView<ImageView>(
context, holder, icon, title, summary,
widgetLayoutResource,
R.id.preview,
50,
50
)
preview = v
updatePreview()
}
fun updatePreview() {
val path = sharedPreferences?.getString(key, null)
val view = preview ?: return
if (!path.isNullOrBlank()) {
val file = File(path)
if (file.exists()) {
val bitmap = BitmapFactory.decodeFile(path)
view.setImageBitmap(bitmap)
view.visibility = View.VISIBLE
return
}
}
view.setImageDrawable(null)
view.visibility = View.GONE
}
}
@@ -58,10 +58,12 @@ class ThemeConfigFragment : PreferenceFragmentCompat(),
SharedPreferences.OnSharedPreferenceChangeListener SharedPreferences.OnSharedPreferenceChangeListener
//MenuProvider //MenuProvider
{ {
private val requestCodeBgLight = 121
private val requestCodeBgDark = 122
private val requestCodeBgLight = 121 private val requestCodeColorImage = 123
private val requestCodeBgDark = 122
private val selectImage = registerForActivityResult(SelectImageContract()) { private val selectImage = registerForActivityResult(SelectImageContract()) {
it.uri?.let { uri -> it.uri?.let { uri ->
when (it.requestCode) { when (it.requestCode) {
requestCodeBgLight -> setBgFromUri(uri, PreferKey.bgImage) { requestCodeBgLight -> setBgFromUri(uri, PreferKey.bgImage) {
@@ -71,6 +73,10 @@ class ThemeConfigFragment : PreferenceFragmentCompat(),
requestCodeBgDark -> setBgFromUri(uri, PreferKey.bgImageN) { requestCodeBgDark -> setBgFromUri(uri, PreferKey.bgImageN) {
upTheme(true) upTheme(true)
} }
requestCodeColorImage -> setBgFromUri(uri, PreferKey.colorImage) {
upPreferenceSummary(PreferKey.colorImage, getPrefString(PreferKey.colorImage))
}
} }
} }
} }
@@ -78,8 +84,6 @@ class ThemeConfigFragment : PreferenceFragmentCompat(),
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
addPreferencesFromResource(R.xml.pref_config_theme) addPreferencesFromResource(R.xml.pref_config_theme)
upPreferenceSummary(PreferKey.bgImage, getPrefString(PreferKey.bgImage))
upPreferenceSummary(PreferKey.bgImageN, getPrefString(PreferKey.bgImageN))
upPreferenceSummary(PreferKey.fontScale) upPreferenceSummary(PreferKey.fontScale)
findPreference<ColorPreference>(PreferKey.cBackground)?.let { findPreference<ColorPreference>(PreferKey.cBackground)?.let {
@@ -129,6 +133,18 @@ class ThemeConfigFragment : PreferenceFragmentCompat(),
customMode?.isVisible = newValue == "11" customMode?.isVisible = newValue == "11"
true true
} }
val hasColorImage = !getPrefString(PreferKey.colorImage).isNullOrBlank()
colorPrimary?.isEnabled = !hasColorImage
if (hasColorImage)
{
upPreferenceSummary("colorPrimary", getString(R.string.seed_photo_alart))
upPreferenceSummary(PreferKey.colorImage, getString(R.string.click_to_delete))
}
if (!getPrefString(PreferKey.bgImage).isNullOrBlank())
upPreferenceSummary(PreferKey.bgImage, getString(R.string.click_to_delete))
if (!getPrefString(PreferKey.bgImageN).isNullOrBlank())
upPreferenceSummary(PreferKey.bgImageN, getString(R.string.click_to_delete))
} }
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
@@ -200,6 +216,11 @@ class ThemeConfigFragment : PreferenceFragmentCompat(),
//recreateActivities() //recreateActivities()
} }
PreferKey.colorImage ->
Handler(Looper.getMainLooper()).postDelayed({
requireContext().restart()
}, 100)
PreferKey.bgImage, PreferKey.bgImage,
PreferKey.bgImageN -> { PreferKey.bgImageN -> {
upPreferenceSummary(key, getPrefString(key)) upPreferenceSummary(key, getPrefString(key))
@@ -232,6 +253,8 @@ class ThemeConfigFragment : PreferenceFragmentCompat(),
PreferKey.bgImage -> selectBgAction(false) PreferKey.bgImage -> selectBgAction(false)
PreferKey.bgImageN -> selectBgAction(true) PreferKey.bgImageN -> selectBgAction(true)
"colorImage" -> selectBgAction(null)
"themeList" -> ThemeListDialog().show(childFragmentManager, "themeList") "themeList" -> ThemeListDialog().show(childFragmentManager, "themeList")
"saveDayTheme", "saveDayTheme",
"saveNightTheme" -> alertSaveTheme(key) "saveNightTheme" -> alertSaveTheme(key)
@@ -267,39 +290,60 @@ class ThemeConfigFragment : PreferenceFragmentCompat(),
} }
} }
private fun selectBgAction(isNight: Boolean) { private fun selectBgAction(isNight: Boolean?) {
val bgKey = if (isNight) PreferKey.bgImageN else PreferKey.bgImage val bgKey = when (isNight) {
val blurringKey = if (isNight) PreferKey.bgImageNBlurring else PreferKey.bgImageBlurring true -> PreferKey.bgImageN
val actions = arrayListOf( false -> PreferKey.bgImage
getString(R.string.background_image_blurring), null -> PreferKey.colorImage
getString(R.string.select_image) }
) val blurringKey = when (isNight) {
if (!getPrefString(bgKey).isNullOrEmpty()) { true -> PreferKey.bgImageNBlurring
actions.add(getString(R.string.delete)) false -> PreferKey.bgImageBlurring
} else -> null
context?.selector(items = actions) { _, i -> }
when (i) {
0 -> alertImageBlurring(blurringKey) {
upTheme(isNight)
}
1 -> { val actions = mutableListOf<String>()
if (isNight) {
selectImage.launch(requestCodeBgDark) if (isNight != null) {
} else { actions.add(getString(R.string.background_image_blurring))
selectImage.launch(requestCodeBgLight) }
actions.add(getString(R.string.select_image))
if (!getPrefString(bgKey).isNullOrEmpty()) {
actions.add(getString(R.string.delete))
}
context?.selector(items = actions) { _, i ->
when {
isNight != null && i == 0 -> {
alertImageBlurring(blurringKey!!) {
upTheme(isNight)
}
} }
}
2 -> { (isNight == null && i == 0) || (isNight != null && i == 1) -> {
removePref(bgKey) when (isNight) {
upTheme(isNight) true -> selectImage.launch(requestCodeBgDark)
false -> selectImage.launch(requestCodeBgLight)
null -> selectImage.launch(requestCodeColorImage)
}
}
(isNight == null && i == 1) || (isNight != null && i == 2) -> {
removePref(bgKey)
if (isNight != null) {
upTheme(isNight)
} else {
upPreferenceSummary(PreferKey.colorImage, getPrefString(PreferKey.colorImage))
}
}
} }
} }
} }
}
private fun alertImageBlurring(preferKey: String, success: () -> Unit) {
private fun alertImageBlurring(preferKey: String, success: () -> Unit) {
alert(R.string.background_image_blurring) { alert(R.string.background_image_blurring) {
val alertBinding = DialogImageBlurringBinding.inflate(layoutInflater).apply { val alertBinding = DialogImageBlurringBinding.inflate(layoutInflater).apply {
getPrefInt(preferKey, 0).let { getPrefInt(preferKey, 0).let {
+11 -3
View File
@@ -1,5 +1,13 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<ImageView android:id="@+id/preview" <com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="48dp" android:layout_width="48dp"
android:layout_height="48dp" /> android:layout_height="48dp"
xmlns:app="http://schemas.android.com/apk/res-auto"
app:cardBackgroundColor="@android:color/transparent"
app:strokeWidth="0dp">
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/preview"
android:scaleType="centerCrop"/>
</com.google.android.material.card.MaterialCardView>
+4 -1
View File
@@ -1270,7 +1270,7 @@
<string name="dark_mode">深色</string> <string name="dark_mode">深色</string>
<string name="pure_black">纯黑深色模式</string> <string name="pure_black">纯黑深色模式</string>
<string name="transparent_theme_alarm">透明主题请于设置背景图片后设置</string> <string name="transparent_theme_alarm">透明主题请于设置背景图片后设置</string>
<string name="seed_color_summary">使用种子颜色创建主题</string> <string name="seed_color_summary">使用种子颜色生成主题颜色</string>
<string name="seed_color">种子色</string> <string name="seed_color">种子色</string>
<string name="custom_mode">自定义模式</string> <string name="custom_mode">自定义模式</string>
<string name="be_swiped">仍可滑动切换页面</string> <string name="be_swiped">仍可滑动切换页面</string>
@@ -1293,4 +1293,7 @@
<string name="add_as_new_book">新增书籍到书架</string> <string name="add_as_new_book">新增书籍到书架</string>
<string name="quick_add_to_shelf">快速添加到书架</string> <string name="quick_add_to_shelf">快速添加到书架</string>
<string name="book_added_to_shelf">书籍已添加到书架</string> <string name="book_added_to_shelf">书籍已添加到书架</string>
<string name="seed_photo_summary">使用图片生成主题颜色</string>
<string name="seed_photo_alart">若需使用种子颜色,请取消种子图片选择</string>
<string name="click_to_delete">点击以删除或更改</string>
</resources> </resources>
+3
View File
@@ -1294,4 +1294,7 @@
<string name="add_as_new_book">新增书籍到书架</string> <string name="add_as_new_book">新增书籍到书架</string>
<string name="quick_add_to_shelf">快速添加到书架</string> <string name="quick_add_to_shelf">快速添加到书架</string>
<string name="book_added_to_shelf">书籍已添加到书架</string> <string name="book_added_to_shelf">书籍已添加到书架</string>
<string name="seed_photo_summary">使用图片生成主题颜色</string>
<string name="seed_photo_alart">若需使用种子颜色,请取消种子图片选择</string>
<string name="click_to_delete">Click to delete or change</string>
</resources> </resources>
+7 -2
View File
@@ -37,6 +37,11 @@
app:cpv_dialogType="preset" app:cpv_dialogType="preset"
app:iconSpaceReserved="false" /> app:iconSpaceReserved="false" />
<io.legato.kazusa.lib.prefs.ImagePreviewPreference
android:key="colorImage"
android:title="种子图片"
android:summary="@string/seed_photo_summary"/>
<io.legato.kazusa.lib.prefs.NameListPreference <io.legato.kazusa.lib.prefs.NameListPreference
android:defaultValue="global" android:defaultValue="global"
android:key="customMode" android:key="customMode"
@@ -131,11 +136,11 @@
app:iconSpaceReserved="false" app:iconSpaceReserved="false"
app:layout="@layout/view_preference_category"> app:layout="@layout/view_preference_category">
<io.legato.kazusa.lib.prefs.Preference <io.legato.kazusa.lib.prefs.ImagePreviewPreference
android:key="backgroundImage" android:key="backgroundImage"
android:title="@string/light_mode" /> android:title="@string/light_mode" />
<io.legato.kazusa.lib.prefs.Preference <io.legato.kazusa.lib.prefs.ImagePreviewPreference
android:key="backgroundImageNight" android:key="backgroundImageNight"
android:title="@string/dark_mode" /> android:title="@string/dark_mode" />