diff --git a/CHANGELOG.md b/CHANGELOG.md index 6721c9ea4..0799e1fb3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +### v3.2.1 + +- Optimize APK file size + ### v3.2.0 (2024-12-03) - Add Android 16 preview diff --git a/CHANGELOG_zh.md b/CHANGELOG_zh.md index 6a9d7cec8..06965fee8 100644 --- a/CHANGELOG_zh.md +++ b/CHANGELOG_zh.md @@ -1,5 +1,9 @@ # 更新日志 +### v3.2.1 + +- 优化 APK 文件大小 + ### v3.2.0 (2024-12-03) - 添加 Android 16 预览 diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 2d8cb676b..9e5f2aac9 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -7,8 +7,8 @@ android { defaultConfig { applicationId = "com.dede.android_eggs" - versionCode = 55 - versionName = "3.2.0" + versionCode = 56 + versionName = "3.2.1" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" resourceConfigurations += listOf( @@ -95,8 +95,10 @@ android { "META-INF/*.version", "META-INF/NOTICE.*", "META-INF/LICENSE", + "META-INF/**/LICENSE.txt", "kotlin/**.kotlin_builtins", "DebugProbesKt.bin", + "*.properties" ) } @@ -115,7 +117,6 @@ dependencies { implementation(libs.androidx.lifecycle) implementation(libs.androidx.viewmodel) implementation(libs.androidx.livedata) - implementation(libs.google.material) implementation(libs.androidx.startup) implementation(libs.androidx.compose.activity) diff --git a/app/src/androidTest/java/com/dede/android_eggs/fake_test/ClockAnalogSimpleDialUtil.kt b/app/src/androidTest/java/com/dede/android_eggs/fake_test/ClockAnalogSimpleDialUtil.kt index 05e384e11..6c69aff3e 100644 --- a/app/src/androidTest/java/com/dede/android_eggs/fake_test/ClockAnalogSimpleDialUtil.kt +++ b/app/src/androidTest/java/com/dede/android_eggs/fake_test/ClockAnalogSimpleDialUtil.kt @@ -3,7 +3,6 @@ package com.dede.android_eggs.fake_test import androidx.core.graphics.drawable.toBitmap import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.platform.app.InstrumentationRegistry -import com.dede.android_eggs.R import com.dede.android_eggs.fake_test.utils.EasterEggsServer import com.dede.android_eggs.fake_test.utils.ResponseUtils.toResponse import com.dede.basic.createThemeWrapperContext @@ -26,8 +25,9 @@ class ClockAnalogSimpleDialUtil { @Test fun generate() { val context = InstrumentationRegistry.getInstrumentation().targetContext + val drawableRes = com.dede.android_eggs.views.widget.R.drawable.clock_analog_simple_dial val wrapperContext = context.createThemeWrapperContext() - val drawable = wrapperContext.requireDrawable(R.drawable.clock_analog_simple_dial) + val drawable = wrapperContext.requireDrawable(drawableRes) val bitmap = drawable.toBitmap() //bitmap = bitmap.scale(IMAGE_SIZE, IMAGE_SIZE) diff --git a/app/src/foss/kotlin/com/dede/android_eggs/FlavorFeaturesImpl.kt b/app/src/foss/kotlin/com/dede/android_eggs/FlavorFeaturesImpl.kt index ea33de95c..f0e9faffe 100644 --- a/app/src/foss/kotlin/com/dede/android_eggs/FlavorFeaturesImpl.kt +++ b/app/src/foss/kotlin/com/dede/android_eggs/FlavorFeaturesImpl.kt @@ -1,9 +1,9 @@ package com.dede.android_eggs -import androidx.fragment.app.FragmentActivity +import androidx.activity.ComponentActivity import com.dede.android_eggs.inject.FlavorFeatures class FlavorFeaturesImpl : FlavorFeatures { - override fun call(activity: FragmentActivity) { + override fun call(activity: ComponentActivity) { } } \ No newline at end of file diff --git a/app/src/main/java/com/dede/android_eggs/inject/FlavorFeatures.kt b/app/src/main/java/com/dede/android_eggs/inject/FlavorFeatures.kt index 6fe950eb6..4c11e7f7b 100644 --- a/app/src/main/java/com/dede/android_eggs/inject/FlavorFeatures.kt +++ b/app/src/main/java/com/dede/android_eggs/inject/FlavorFeatures.kt @@ -1,6 +1,6 @@ package com.dede.android_eggs.inject -import androidx.fragment.app.FragmentActivity +import androidx.activity.ComponentActivity import com.dede.android_eggs.FlavorFeaturesImpl interface FlavorFeatures { @@ -14,5 +14,5 @@ interface FlavorFeatures { } } - fun call(activity: FragmentActivity) + fun call(activity: ComponentActivity) } diff --git a/app/src/main/java/com/dede/android_eggs/startup/ApplicationInitializer.kt b/app/src/main/java/com/dede/android_eggs/startup/ApplicationInitializer.kt index 164ff83a9..c93994522 100644 --- a/app/src/main/java/com/dede/android_eggs/startup/ApplicationInitializer.kt +++ b/app/src/main/java/com/dede/android_eggs/startup/ApplicationInitializer.kt @@ -4,17 +4,12 @@ import android.app.Application import android.content.Context import androidx.startup.Initializer import com.dede.android_eggs.util.ActivityActionDispatcher -import com.dede.android_eggs.views.settings.compose.prefs.DynamicColorPrefUtil -import com.dede.android_eggs.views.settings.compose.prefs.ThemePrefUtil import com.dede.basic.GlobalContext class ApplicationInitializer : Initializer { override fun create(context: Context) { val application = context.applicationContext as Application - // apply compat style - DynamicColorPrefUtil.apply(application) - ThemePrefUtil.apply(application) ActivityActionDispatcher.register(application) } diff --git a/app/src/main/java/com/dede/android_eggs/ui/Icons.kt b/app/src/main/java/com/dede/android_eggs/ui/Icons.kt deleted file mode 100644 index ea4ceb484..000000000 --- a/app/src/main/java/com/dede/android_eggs/ui/Icons.kt +++ /dev/null @@ -1,14 +0,0 @@ -package com.dede.android_eggs.ui - -/** Generated automatically via **subset_icons_font.py**, do not modify this file. */ -// 1711432340 -object Icons { - - object Rounded { - - /** tips_and_updates */ - const val tips_and_updates = "\uE79A" - - } - -} diff --git a/app/src/main/java/com/dede/android_eggs/ui/composes/ThemedHashImageBitmap.kt b/app/src/main/java/com/dede/android_eggs/ui/composes/ThemedHashImageBitmap.kt index 99cdf2fb0..64dfd8403 100644 --- a/app/src/main/java/com/dede/android_eggs/ui/composes/ThemedHashImageBitmap.kt +++ b/app/src/main/java/com/dede/android_eggs/ui/composes/ThemedHashImageBitmap.kt @@ -21,11 +21,11 @@ fun rememberThemedHashImageBitmap( height: Int = 32 ): ImageBitmap { val context = LocalContext.current - return remember(hash, ThemeUtils.isSystemNightMode(context)) { + return remember(hash, ThemeUtils.isDarkMode(context.resources)) { var bitmap = checkNotNull(BlurHashDecoder.decode(hash, width, height)) { "BlurHash decode error! hash: ".format(hash) } - if (ThemeUtils.isSystemNightMode(context)) { + if (ThemeUtils.isDarkMode(context.resources)) { val nightMode = Bitmap.createBitmap(bitmap.width, bitmap.height, Bitmap.Config.ARGB_8888) val paint = Paint(Paint.ANTI_ALIAS_FLAG) diff --git a/app/src/main/java/com/dede/android_eggs/ui/drawables/FontIconsDrawable.kt b/app/src/main/java/com/dede/android_eggs/ui/drawables/FontIconsDrawable.kt deleted file mode 100644 index 6d188db79..000000000 --- a/app/src/main/java/com/dede/android_eggs/ui/drawables/FontIconsDrawable.kt +++ /dev/null @@ -1,225 +0,0 @@ -package com.dede.android_eggs.ui.drawables - -import android.content.Context -import android.content.res.ColorStateList -import android.graphics.Canvas -import android.graphics.Color -import android.graphics.ColorFilter -import android.graphics.Paint -import android.graphics.Paint.FontMetrics -import android.graphics.PixelFormat -import android.graphics.Rect -import android.graphics.Typeface -import android.graphics.drawable.Drawable -import android.text.TextPaint -import android.util.LayoutDirection -import androidx.annotation.AttrRes -import androidx.annotation.Dimension -import androidx.annotation.FloatRange -import androidx.annotation.Keep -import androidx.core.content.res.ResourcesCompat -import androidx.core.graphics.drawable.DrawableCompat -import androidx.core.graphics.withSave -import com.dede.android_eggs.R -import com.dede.basic.dp -import com.dede.basic.globalContext -import com.google.android.material.color.MaterialColors -import kotlin.math.min -import com.google.android.material.R as M3R - -/** - * Material Icons. - * - * @author shhu - * @since 2023/1/17 - */ -class FontIconsDrawable( - context: Context, - private val unicode: String, - @Dimension(unit = Dimension.DP) size: Float = -1f, -) : Drawable() { - - companion object { - val typeface: Typeface by lazy { - checkNotNull(ResourcesCompat.getFont(globalContext, R.font.icons)) - } - } - - constructor( - context: Context, - unicode: String, - @AttrRes colorAttributeResId: Int, - @Dimension(unit = Dimension.DP) size: Float = -1f, - ) : this(context, unicode, size) { - val color = MaterialColors.getColor(context, colorAttributeResId, Color.WHITE) - setColor(color) - } - - constructor( - context: Context, - unicode: String, - color: ColorStateList?, - @Dimension(unit = Dimension.DP) size: Float = -1f, - ) : this(context, unicode, size) { - setColorStateList(color) - } - - private val paint = TextPaint(Paint.ANTI_ALIAS_FLAG) - private val metrics = FontMetrics() - private val tempBounds = Rect() - - private var dimension: Int = -1 - private var colorStateList: ColorStateList? = null - private var degree: Float = 0f - private var autoMirrored = false - - init { - DrawableCompat.setLayoutDirection(this, context.resources.configuration.layoutDirection) - paint.typeface = typeface - paint.textAlign = Paint.Align.CENTER - val color = MaterialColors.getColor(context, M3R.attr.colorControlNormal, Color.WHITE) - paint.color = color - if (size > 0) { - dimension = size.dp - val rect = Rect(0, 0, dimension, dimension) - bounds = rect - computeIconSize(rect) - } - } - - @Keep - fun setRotate(@FloatRange(from = -360.0, to = 360.0) degree: Float) { - val newDegree = degree % 360f - if (newDegree != this.degree) { - this.degree = newDegree - invalidateSelf() - } - } - - @Keep - fun getRotate(): Float = this.degree - - fun setTypeface(typeface: Typeface) { - paint.typeface = typeface - invalidateSelf() - } - - fun setColor(color: Int) { - if (color != paint.color) { - paint.color = color - invalidateSelf() - } - } - - fun setColorStateList(colorStateList: ColorStateList?) { - if (colorStateList != this.colorStateList) { - this.colorStateList = colorStateList - onStateChange(state) - } - } - - override fun onStateChange(state: IntArray): Boolean { - val colorStateList = this.colorStateList - if (colorStateList != null) { - setColor(colorStateList.getColorForState(state, colorStateList.defaultColor)) - return true - } - return super.onStateChange(state) - } - - override fun getIntrinsicHeight(): Int { - return if (dimension > 0) dimension else -1 - } - - override fun getIntrinsicWidth(): Int { - return if (dimension > 0) dimension else -1 - } - - private fun updateBounds(bounds: Rect) { - if (dimension > 0) { - tempBounds.set( - bounds.left, - bounds.top, - bounds.left + dimension, - bounds.top + dimension - ) - } else { - tempBounds.set(bounds) - } - } - - private fun computeIconSize(bounds: Rect) { - updateBounds(bounds) - - val size = min(tempBounds.width(), tempBounds.height()) - if (size <= 0) return - - paint.textSize = size.toFloat() - paint.getFontMetrics(metrics) - } - - override fun onBoundsChange(bounds: Rect) { - if (dimension > 0) { - updateBounds(bounds) - return - } - computeIconSize(bounds) - } - - override fun draw(canvas: Canvas) { - if (unicode.isEmpty()) return - - val cx = tempBounds.exactCenterX() - val cy = tempBounds.exactCenterY() - canvas.withSave { - canvas.translate(tempBounds.left.toFloat(), tempBounds.top.toFloat()) - rotate(degree, cx, cy) - if (needAutoMirrored()) { - scale(-1f, 1f, cx, cy) - } - val y = (metrics.descent - metrics.ascent) / 2 - metrics.ascent / 2 - canvas.drawText(unicode, cx, y, paint) - } - } - - override fun onLayoutDirectionChanged(layoutDirection: Int): Boolean { - invalidateSelf() - return true - } - - private fun needAutoMirrored(): Boolean { - return isAutoMirrored && DrawableCompat.getLayoutDirection(this) == LayoutDirection.RTL - } - - override fun setAutoMirrored(mirrored: Boolean) { - if (autoMirrored != mirrored) { - autoMirrored = mirrored - invalidateSelf() - } - } - - override fun isAutoMirrored(): Boolean { - return autoMirrored; - } - - override fun getAlpha(): Int { - return paint.alpha - } - - override fun setAlpha(alpha: Int) { - paint.alpha = alpha - invalidateSelf() - } - - override fun setColorFilter(colorFilter: ColorFilter?) { - paint.colorFilter = colorFilter - invalidateSelf() - } - - @Deprecated("Deprecated in Java", - ReplaceWith("PixelFormat.TRANSLUCENT", "android.graphics.PixelFormat") - ) - override fun getOpacity(): Int { - return PixelFormat.TRANSLUCENT - } -} \ No newline at end of file diff --git a/app/src/main/java/com/dede/android_eggs/util/ActivityActionDispatcher.kt b/app/src/main/java/com/dede/android_eggs/util/ActivityActionDispatcher.kt index fff3fcd04..0406bba12 100644 --- a/app/src/main/java/com/dede/android_eggs/util/ActivityActionDispatcher.kt +++ b/app/src/main/java/com/dede/android_eggs/util/ActivityActionDispatcher.kt @@ -30,10 +30,10 @@ class ActivityActionDispatcher : Application.ActivityLifecycleCallbacks { } } - private val actions = arrayListOf( - PermissionRequestAction(), + private val actions: List = arrayListOf( + PlatLogoActivityAction(), WarningDialogAction(), - PlatLogoActivityAction() + PermissionRequestAction(), ) override fun onActivityPreCreated(activity: Activity, savedInstanceState: Bundle?) { diff --git a/app/src/main/java/com/dede/android_eggs/util/ColorsExt.kt b/app/src/main/java/com/dede/android_eggs/util/ColorsExt.kt deleted file mode 100644 index 2d566c20d..000000000 --- a/app/src/main/java/com/dede/android_eggs/util/ColorsExt.kt +++ /dev/null @@ -1,67 +0,0 @@ -@file:JvmName("ColorsExt") - -package com.dede.android_eggs.util - -import android.annotation.SuppressLint -import android.content.Context -import android.content.res.ColorStateList -import android.graphics.Color -import androidx.annotation.AttrRes -import androidx.annotation.ColorRes -import androidx.appcompat.widget.TintTypedArray -import androidx.core.content.ContextCompat -import androidx.core.content.withStyledAttributes -import com.google.android.material.color.MaterialColors -import com.google.android.material.resources.MaterialAttributes - - -fun Context.color(@ColorRes colorRes: Int): Int { - return ContextCompat.getColor(this, colorRes) -} - -fun Context.resolveColor(@AttrRes colorAttr: Int): Int { - return MaterialColors.getColor(this, colorAttr, "") -} - -fun Context.resolveColorStateList(@AttrRes colorAttr: Int): ColorStateList { - return MaterialColors.getColorStateList( - this, colorAttr, ColorStateList.valueOf(Color.TRANSPARENT) - ) -} - -@SuppressLint("RestrictedApi") -fun Context.resolveColorStateList( - @AttrRes styleAttr: Int, - @AttrRes colorAttr: Int, - useTint: Boolean = false, - @ColorRes defaultId: Int = -1, -): ColorStateList? { - val value = MaterialAttributes.resolve(this, styleAttr) - var colorStateList: ColorStateList? = null - if (value != null) { - val resourceId = value.resourceId - val attrs = intArrayOf(colorAttr) - if (useTint) { - val array = TintTypedArray.obtainStyledAttributes(this, resourceId, attrs) - colorStateList = array.getColorStateList(0) - array.recycle() - } else { - withStyledAttributes(resourceId, attrs) { - colorStateList = getColorStateList(0) - } - } - } - if (colorStateList == null && defaultId != -1) { - colorStateList = ContextCompat.getColorStateList(this, defaultId) - } - return colorStateList -} - -fun Int.revertColor(): Int { - return Color.argb( - Color.alpha(this), - 255 - Color.red(this), - 255 - Color.green(this), - 255 - Color.blue(this) - ) -} diff --git a/app/src/main/java/com/dede/android_eggs/util/DrawableExt.kt b/app/src/main/java/com/dede/android_eggs/util/DrawableExt.kt index 56901ca21..041f291d8 100644 --- a/app/src/main/java/com/dede/android_eggs/util/DrawableExt.kt +++ b/app/src/main/java/com/dede/android_eggs/util/DrawableExt.kt @@ -4,20 +4,16 @@ import android.content.Context import android.graphics.Shader import android.graphics.drawable.BitmapDrawable import android.graphics.drawable.Drawable -import androidx.annotation.AttrRes import androidx.annotation.DrawableRes import androidx.core.graphics.drawable.toBitmap import com.dede.basic.requireDrawable -import com.google.android.material.R fun createRepeatWavyDrawable( context: Context, @DrawableRes wavyRes: Int, - @AttrRes tintAttr: Int = R.attr.colorSecondary, ): Drawable { val bitmap = context.requireDrawable(wavyRes).toBitmap() return BitmapDrawable(context.resources, bitmap).apply { tileModeX = Shader.TileMode.REPEAT - setTint(context.resolveColor(tintAttr)) } } diff --git a/app/src/main/java/com/dede/android_eggs/util/actions/WarningDialogAction.kt b/app/src/main/java/com/dede/android_eggs/util/actions/WarningDialogAction.kt index d71b0f7c7..e7f9fe1e3 100644 --- a/app/src/main/java/com/dede/android_eggs/util/actions/WarningDialogAction.kt +++ b/app/src/main/java/com/dede/android_eggs/util/actions/WarningDialogAction.kt @@ -1,28 +1,49 @@ package com.dede.android_eggs.util.actions import android.app.Activity -import android.graphics.Color +import android.view.ViewGroup import android.widget.TextView import androidx.annotation.StringRes +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.rounded.TipsAndUpdates +import androidx.compose.material3.AlertDialog +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.material3.TextButton +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.toArgb +import androidx.compose.ui.platform.ComposeView +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import androidx.compose.ui.viewinterop.AndroidView +import androidx.compose.ui.window.DialogProperties import androidx.core.text.HtmlCompat +import androidx.lifecycle.setViewTreeLifecycleOwner +import androidx.savedstate.setViewTreeSavedStateRegistryOwner import com.dede.android_eggs.R -import com.dede.android_eggs.ui.Icons -import com.dede.android_eggs.ui.drawables.FontIconsDrawable import com.dede.android_eggs.util.ActivityActionDispatcher -import com.dede.android_eggs.util.ThemeUtils -import com.dede.android_eggs.util.updateCompoundDrawablesRelative -import com.dede.basic.createThemeWrapperContext -import com.dede.basic.dp +import com.dede.android_eggs.views.theme.AppTheme +import com.dede.basic.androidLifecycleOwner +import com.dede.basic.androidSavedStateOwner import com.dede.basic.getBoolean import com.dede.basic.putBoolean -import com.google.android.material.color.MaterialColors -import com.google.android.material.dialog.MaterialAlertDialogBuilder -import com.google.android.material.R as M3R - class WarningDialogAction : ActivityActionDispatcher.ActivityAction { - private class ActionInfo( + private class WarningInfo( val key: String, @StringRes val title: Int, @StringRes val message: Int, @@ -30,12 +51,12 @@ class WarningDialogAction : ActivityActionDispatcher.ActivityAction { companion object { private val target = mapOf( - com.android_t.egg.PlatLogoActivity::class to ActionInfo( + com.android_t.egg.PlatLogoActivity::class to WarningInfo( "key_t_trypophobia_warning", android.R.string.dialog_alert_title, R.string.message_trypophobia_warning ), - com.android_s.egg.PlatLogoActivity::class to ActionInfo( + com.android_s.egg.PlatLogoActivity::class to WarningInfo( "key_s_trypophobia_warning", android.R.string.dialog_alert_title, R.string.message_trypophobia_warning @@ -48,32 +69,111 @@ class WarningDialogAction : ActivityActionDispatcher.ActivityAction { val agreed = activity.getBoolean(info.key, false) if (agreed) return - val wrapperContext = activity.createThemeWrapperContext() - ThemeUtils.tryApplyOLEDTheme(wrapperContext) - val spanned = HtmlCompat.fromHtml( - wrapperContext.getString(info.message), - HtmlCompat.FROM_HTML_MODE_COMPACT - ) - val icon = FontIconsDrawable(wrapperContext, Icons.Rounded.tips_and_updates, 24f) - val color = - MaterialColors.getColor(wrapperContext, M3R.attr.colorControlNormal, Color.BLACK) - icon.setColor(color) - MaterialAlertDialogBuilder(wrapperContext) - .setTitle(info.title) - .setMessage(spanned) - .setCancelable(false) - .setPositiveButton(android.R.string.ok) { _, _ -> - activity.putBoolean(info.key, true) - } - .setNegativeButton(android.R.string.cancel) { _, _ -> - activity.finish() - } - .show() - .apply { - val titleView = findViewById(androidx.appcompat.R.id.alertTitle) - titleView?.compoundDrawablePadding = 6.dp - titleView?.updateCompoundDrawablesRelative(start = icon) + val composeView = ComposeView(activity) + composeView.setViewTreeLifecycleOwner(activity.androidLifecycleOwner) + // composeView.setViewTreeViewModelStoreOwner() + composeView.setViewTreeSavedStateRegistryOwner(activity.androidSavedStateOwner) + activity.window.decorView.post { + activity.addContentView( + composeView, + ViewGroup.LayoutParams( + ViewGroup.LayoutParams.WRAP_CONTENT, + ViewGroup.LayoutParams.WRAP_CONTENT + ) + ) + } + composeView.setContent { + AppTheme { + val context = LocalContext.current + WarningDialog( + info.title, + info.message, + onConfirm = { + context.putBoolean(info.key, true) + }, + onCancel = { + @Suppress("DEPRECATION") + activity.onBackPressed() + }, + ) } + } } -} \ No newline at end of file +} + +@Composable +private fun WarningDialog( + @StringRes title: Int, + @StringRes message: Int, + onConfirm: () -> Unit, + onCancel: () -> Unit +) { + var visible by remember { mutableStateOf(true) } + if (!visible) { + return + } + AlertDialog( + onDismissRequest = { + }, + properties = remember { + DialogProperties( + dismissOnBackPress = false, + dismissOnClickOutside = false + ) + }, + title = { + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.Center + ) { + Icon( + imageVector = Icons.Rounded.TipsAndUpdates, + contentDescription = stringResource(id = title), + modifier = Modifier.size(28.dp) + ) + Text( + text = stringResource(id = title), + modifier = Modifier.padding(start = 10.dp) + ) + } + }, + text = { + val messageText = stringResource(message) + val messageSpanned = remember(messageText) { + HtmlCompat.fromHtml(messageText, HtmlCompat.FROM_HTML_MODE_COMPACT) + } + val textColor = MaterialTheme.colorScheme.onPrimaryContainer.toArgb() + AndroidView( + modifier = Modifier.fillMaxWidth(), + factory = { + TextView(it) + }, + update = { + it.text = messageSpanned + it.setTextColor(textColor) + } + ) + }, + confirmButton = { + TextButton( + onClick = { + visible = false + onConfirm() + } + ) { + Text(text = stringResource(android.R.string.ok)) + } + }, + dismissButton = { + TextButton( + onClick = { + visible = false + onCancel() + } + ) { + Text(text = stringResource(android.R.string.cancel)) + } + } + ) +} diff --git a/app/src/main/java/com/dede/android_eggs/views/main/EasterEggsActivity.kt b/app/src/main/java/com/dede/android_eggs/views/main/EasterEggsActivity.kt index 4cf07efa4..bb595790d 100644 --- a/app/src/main/java/com/dede/android_eggs/views/main/EasterEggsActivity.kt +++ b/app/src/main/java/com/dede/android_eggs/views/main/EasterEggsActivity.kt @@ -7,7 +7,6 @@ import android.content.Intent import android.os.Build import android.os.Bundle import androidx.activity.compose.setContent -import androidx.activity.enableEdgeToEdge import androidx.appcompat.app.AppCompatActivity import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.WindowInsets @@ -41,7 +40,6 @@ import com.dede.android_eggs.views.main.compose.BottomSearchBar import com.dede.android_eggs.views.main.compose.EasterEggScreen import com.dede.android_eggs.views.main.compose.Konfetti import com.dede.android_eggs.views.main.compose.LocalEasterEggLogoSensor -import com.dede.android_eggs.views.main.compose.LocalFragmentManager import com.dede.android_eggs.views.main.compose.LocalKonfettiState import com.dede.android_eggs.views.main.compose.MainTitleBar import com.dede.android_eggs.views.main.compose.Welcome @@ -80,14 +78,12 @@ class EasterEggsActivity : AppCompatActivity() { lateinit var sensor: EasterEggLogoSensorMatrixConvert override fun onCreate(savedInstanceState: Bundle?) { - ThemeUtils.tryApplyOLEDTheme(this) - enableEdgeToEdge() + ThemeUtils.enableEdgeToEdge(this) super.onCreate(savedInstanceState) setContent { val konfettiState = rememberKonfettiState() CompositionLocalProvider( - LocalFragmentManager provides supportFragmentManager, LocalEasterEggLogoSensor provides sensor, LocalKonfettiState provides konfettiState ) { diff --git a/app/src/main/java/com/dede/android_eggs/views/main/compose/LocalProvider.kt b/app/src/main/java/com/dede/android_eggs/views/main/compose/LocalProvider.kt index 113859830..a747ca95a 100644 --- a/app/src/main/java/com/dede/android_eggs/views/main/compose/LocalProvider.kt +++ b/app/src/main/java/com/dede/android_eggs/views/main/compose/LocalProvider.kt @@ -9,7 +9,6 @@ import androidx.compose.runtime.ReadOnlyComposable import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.staticCompositionLocalOf import androidx.compose.ui.platform.LocalInspectionMode -import androidx.fragment.app.FragmentManager import com.dede.android_eggs.views.main.util.EasterEggLogoSensorMatrixConvert private inline fun noLocalProvidedFor(name: String): Nothing { @@ -27,10 +26,6 @@ val ProvidableCompositionLocal.currentOutInspectionMode: T? @Composable get() = if (LocalInspectionMode.current) null else current -val LocalFragmentManager = staticCompositionLocalOf { - noLocalProvidedFor("LocalFragmentManager") -} - val LocalEasterEggLogoSensor = staticCompositionLocalOf { noLocalProvidedFor("LocalEasterEggLogoSensor") } diff --git a/app/src/main/java/com/dede/android_eggs/views/main/compose/Wavy.kt b/app/src/main/java/com/dede/android_eggs/views/main/compose/Wavy.kt index d8293fdd3..a7bd9e1b4 100644 --- a/app/src/main/java/com/dede/android_eggs/views/main/compose/Wavy.kt +++ b/app/src/main/java/com/dede/android_eggs/views/main/compose/Wavy.kt @@ -1,8 +1,10 @@ package com.dede.android_eggs.views.main.compose +import androidx.annotation.DrawableRes import androidx.compose.foundation.Image import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding +import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.runtime.remember import androidx.compose.ui.Modifier @@ -16,13 +18,13 @@ import androidx.compose.ui.unit.dp import com.dede.android_eggs.R import com.dede.android_eggs.util.createRepeatWavyDrawable -@Preview(showBackground = true) +@Preview @Composable fun PreviewWavyRepeat() { Wavy(R.drawable.ic_wavy_line_1, true) } -@Preview(showBackground = true) +@Preview @Composable fun PreviewWavy() { Wavy(R.drawable.ic_wavy_line) @@ -30,7 +32,11 @@ fun PreviewWavy() { @Composable -fun Wavy(res: Int, repeat: Boolean = false, tint: Color? = null) { +fun Wavy( + @DrawableRes res: Int, + repeat: Boolean = false, + tint: Color = MaterialTheme.colorScheme.secondary +) { val modifier = Modifier .fillMaxWidth() .padding(vertical = 26.dp) @@ -38,9 +44,7 @@ fun Wavy(res: Int, repeat: Boolean = false, tint: Color? = null) { val context = LocalContext.current val drawable = remember(res, context.theme) { createRepeatWavyDrawable(context, res).apply { - if (tint != null) { - setTint(tint.toArgb()) - } + setTint(tint.toArgb()) } } DrawableImage( diff --git a/app/src/main/java/com/dede/android_eggs/views/settings/compose/prefs/ThemePref.kt b/app/src/main/java/com/dede/android_eggs/views/settings/compose/prefs/ThemePref.kt index ea75fd38a..3ddb21be9 100644 --- a/app/src/main/java/com/dede/android_eggs/views/settings/compose/prefs/ThemePref.kt +++ b/app/src/main/java/com/dede/android_eggs/views/settings/compose/prefs/ThemePref.kt @@ -1,6 +1,5 @@ package com.dede.android_eggs.views.settings.compose.prefs -import androidx.appcompat.app.AppCompatDelegate import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.height import androidx.compose.material.icons.Icons @@ -13,13 +12,11 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import com.dede.android_eggs.R import com.dede.android_eggs.util.LocalEvent -import com.dede.android_eggs.util.ThemeUtils import com.dede.android_eggs.views.settings.compose.basic.ExpandOptionsPref import com.dede.android_eggs.views.settings.compose.basic.OptionShapes import com.dede.android_eggs.views.settings.compose.basic.ValueOption @@ -38,23 +35,10 @@ import com.dede.android_eggs.views.theme.themeMode @Preview @Composable fun ThemePref() { - val context = LocalContext.current var themeModeValue by rememberPrefIntState(KEY_NIGHT_MODE, FOLLOW_SYSTEM) val onOptionClick = click@{ mode: Int -> themeModeValue = mode themeMode = themeModeValue - var appCompatMode = mode - if (appCompatMode == AMOLED) { - appCompatMode = DARK - } - if (appCompatMode == AppCompatDelegate.getDefaultNightMode()) { - if ((mode == AMOLED) != ThemeUtils.isOLEDTheme(context)) { - ThemeUtils.recreateActivityIfPossible(context) - LocalEvent.poster().post(ACTION_NIGHT_MODE_CHANGED) - } - return@click - } - AppCompatDelegate.setDefaultNightMode(appCompatMode) LocalEvent.poster().post(ACTION_NIGHT_MODE_CHANGED) } diff --git a/app/src/main/res/drawable-anydpi/ic_wavy_line.xml b/app/src/main/res/drawable-anydpi/ic_wavy_line.xml index f3feab7a2..15823552f 100644 --- a/app/src/main/res/drawable-anydpi/ic_wavy_line.xml +++ b/app/src/main/res/drawable-anydpi/ic_wavy_line.xml @@ -1,7 +1,6 @@ 600 - @style/ThemeOverlay.MaterialAlertDialog.BlurBehind - - - - - - - \ No newline at end of file diff --git a/app/src/market/kotlin/com/dede/android_eggs/FlavorFeaturesImpl.kt b/app/src/market/kotlin/com/dede/android_eggs/FlavorFeaturesImpl.kt index 7dfe99871..6240bf2e5 100644 --- a/app/src/market/kotlin/com/dede/android_eggs/FlavorFeaturesImpl.kt +++ b/app/src/market/kotlin/com/dede/android_eggs/FlavorFeaturesImpl.kt @@ -1,10 +1,10 @@ package com.dede.android_eggs -import androidx.fragment.app.FragmentActivity +import androidx.activity.ComponentActivity import com.dede.android_eggs.inject.FlavorFeatures class FlavorFeaturesImpl : FlavorFeatures { - override fun call(activity: FragmentActivity) { + override fun call(activity: ComponentActivity) { GooglePlayCore.launchReview(activity) } } \ No newline at end of file diff --git a/app/src/market/kotlin/com/dede/android_eggs/GooglePlayCore.kt b/app/src/market/kotlin/com/dede/android_eggs/GooglePlayCore.kt index fc26633b0..6a50fd81b 100644 --- a/app/src/market/kotlin/com/dede/android_eggs/GooglePlayCore.kt +++ b/app/src/market/kotlin/com/dede/android_eggs/GooglePlayCore.kt @@ -2,8 +2,7 @@ package com.dede.android_eggs import android.content.Context import android.util.Log -import androidx.annotation.Keep -import androidx.fragment.app.FragmentActivity +import androidx.activity.ComponentActivity import androidx.lifecycle.lifecycleScope import com.dede.android_eggs.util.launchCatchable import com.dede.android_eggs.util.pref @@ -38,7 +37,7 @@ object GooglePlayCore { } @JvmStatic - fun launchReview(activity: FragmentActivity) { + fun launchReview(activity: ComponentActivity) { if (!isAgreedPrivacyPolicy(activity) || !isGooglePlayServicesAvailable(activity) || !isLaunchReviewTiming(activity) diff --git a/basic/build.gradle.kts b/basic/build.gradle.kts index d148f61ef..f7258aed8 100644 --- a/basic/build.gradle.kts +++ b/basic/build.gradle.kts @@ -15,5 +15,4 @@ dependencies { implementation(libs.androidx.viewmodel) implementation(libs.androidx.startup) implementation(libs.androidx.activity) - implementation(libs.androidx.localbroadcastmanager) } \ No newline at end of file diff --git a/basic/src/main/java/com/dede/basic/ContextExt.kt b/basic/src/main/java/com/dede/basic/ContextExt.kt index 5ab47edae..514676913 100644 --- a/basic/src/main/java/com/dede/basic/ContextExt.kt +++ b/basic/src/main/java/com/dede/basic/ContextExt.kt @@ -3,9 +3,11 @@ package com.dede.basic import android.annotation.SuppressLint +import android.app.Activity import android.content.ClipData import android.content.ClipboardManager import android.content.Context +import android.content.ContextWrapper import android.content.Intent import android.content.res.Configuration import android.widget.Toast @@ -22,17 +24,12 @@ import androidx.core.os.LocaleListCompat val globalContext: Context get() = GlobalContext.globalContext -val globalThemeContext: Context - get() = GlobalContext.globalThemeContext - @SuppressLint("StaticFieldLeak") object GlobalContext { internal lateinit var globalContext: Context private set - internal val globalThemeContext by lazy { globalContext.createThemeWrapperContext() } - class Initializer : androidx.startup.Initializer { override fun create(context: Context) { globalContext = context @@ -42,6 +39,16 @@ object GlobalContext { } } +fun Context.getActivity(): Activity? { + var context = this + while (context is ContextWrapper) { + if (context is Activity) { + return context + } + context = context.baseContext + } + return null +} fun Context.toast(@StringRes resId: Int, duration: Int = Toast.LENGTH_SHORT) { Toast.makeText(this, resId, duration).show() diff --git a/basic/src/main/java/com/dede/basic/LifecycleExt.kt b/basic/src/main/java/com/dede/basic/LifecycleExt.kt index d7d0a5a0a..adb4ac271 100644 --- a/basic/src/main/java/com/dede/basic/LifecycleExt.kt +++ b/basic/src/main/java/com/dede/basic/LifecycleExt.kt @@ -13,6 +13,9 @@ import androidx.lifecycle.Lifecycle import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.LifecycleRegistry import androidx.lifecycle.coroutineScope +import androidx.savedstate.SavedStateRegistry +import androidx.savedstate.SavedStateRegistryController +import androidx.savedstate.SavedStateRegistryOwner import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineStart import kotlinx.coroutines.launch @@ -32,25 +35,28 @@ private val handler = Handler(Looper.getMainLooper()) { msg -> true } -val Activity.androidLifecycle: Lifecycle +val Activity.androidLifecycleOwner: LifecycleOwner get() { if (this is ComponentActivity) { - return lifecycle + return this } + return LifecycleFragment.injectIfNeededIn(this) + } - // com.bumptech.glide.manager.RequestManagerRetriever#get(android.app.Activity) - val fm = fragmentManager - var current = fm.findFragmentByTag(TAG_FRAGMENT) as? LifecycleFragment - if (current == null) { - current = pendingLifecycleFragments[fm] - if (current == null) { - current = LifecycleFragment() - pendingLifecycleFragments[fm] = current - fm.beginTransaction().add(current, TAG_FRAGMENT).commitAllowingStateLoss() - handler.obtainMessage(ID_REMOVE, fm).sendToTarget() - } +val Activity.androidSavedStateOwner: SavedStateRegistryOwner + get() { + if (this is ComponentActivity) { + return this + } + return LifecycleFragment.injectIfNeededIn(this) + } + +val Activity.androidLifecycle: Lifecycle + get() { + if (this is ComponentActivity) { + return lifecycle } - return current.lifecycle + return LifecycleFragment.injectIfNeededIn(this).lifecycle } fun Lifecycle.launch( @@ -61,11 +67,35 @@ fun Lifecycle.launch( @Suppress("OVERRIDE_DEPRECATION") @SuppressLint("ValidFragment") -internal class LifecycleFragment : android.app.Fragment(), LifecycleOwner { +internal class LifecycleFragment : android.app.Fragment(), LifecycleOwner, SavedStateRegistryOwner { + + companion object { + fun injectIfNeededIn(activity: Activity): LifecycleFragment { + // com.bumptech.glide.manager.RequestManagerRetriever#get(android.app.Activity) + val fm = activity.fragmentManager + var current = fm.findFragmentByTag(TAG_FRAGMENT) as? LifecycleFragment + if (current == null) { + current = pendingLifecycleFragments[fm] + if (current == null) { + current = LifecycleFragment() + pendingLifecycleFragments[fm] = current + fm.beginTransaction().add(current, TAG_FRAGMENT).commitAllowingStateLoss() + handler.obtainMessage(ID_REMOVE, fm).sendToTarget() + } + } + return current + } + } private val lifecycleRegistry = LifecycleRegistry(this) + private val savedStateRegistryController = SavedStateRegistryController.create(this) + + init { + savedStateRegistryController.performAttach() + } override fun onActivityCreated(savedInstanceState: Bundle?) { + savedStateRegistryController.performRestore(savedInstanceState) super.onActivityCreated(savedInstanceState) lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_CREATE) } @@ -95,6 +125,15 @@ internal class LifecycleFragment : android.app.Fragment(), LifecycleOwner { super.onDestroy() } + override fun onSaveInstanceState(outState: Bundle) { + lifecycleRegistry.currentState = Lifecycle.State.CREATED + super.onSaveInstanceState(outState) + savedStateRegistryController.performSave(outState) + } + override val lifecycle: Lifecycle get() = lifecycleRegistry + + override val savedStateRegistry: SavedStateRegistry + get() = savedStateRegistryController.savedStateRegistry } \ No newline at end of file diff --git a/eggs/AndroidNext/src/main/java/com/android_next/egg/AndroidNextEasterEgg.kt b/eggs/AndroidNext/src/main/java/com/android_next/egg/AndroidNextEasterEgg.kt index 9f0cbd445..fb6cb08f5 100644 --- a/eggs/AndroidNext/src/main/java/com/android_next/egg/AndroidNextEasterEgg.kt +++ b/eggs/AndroidNext/src/main/java/com/android_next/egg/AndroidNextEasterEgg.kt @@ -5,8 +5,6 @@ import android.view.View import android.widget.ImageView import androidx.annotation.DrawableRes import androidx.annotation.StringRes -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.toArgb import androidx.core.view.setPadding import com.dede.basic.dp import com.dede.basic.provider.BaseEasterEgg @@ -66,7 +64,7 @@ object AndroidNextEasterEgg : EasterEggProvider { return ImageView(context).apply { setImageDrawable(context.requireDrawable(PLATLOGO_RES)) setPadding(12.dp) - setBackgroundColor(Color(0xFF_202124).toArgb()) + setBackgroundColor(0xFF_202124.toInt()) } } diff --git a/fastlane/metadata/android/en-US/changelogs/56.txt b/fastlane/metadata/android/en-US/changelogs/56.txt new file mode 100644 index 000000000..4a3cc5c54 --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/56.txt @@ -0,0 +1 @@ +- Optimize APK file size \ No newline at end of file diff --git a/fastlane/metadata/android/zh-CN/changelogs/56.txt b/fastlane/metadata/android/zh-CN/changelogs/56.txt new file mode 100644 index 000000000..5fe6bc91d --- /dev/null +++ b/fastlane/metadata/android/zh-CN/changelogs/56.txt @@ -0,0 +1 @@ +- 优化 APK 文件大小 \ No newline at end of file diff --git a/feature/crash/src/main/java/com/dede/android_eggs/crash/CrashActivity.kt b/feature/crash/src/main/java/com/dede/android_eggs/crash/CrashActivity.kt index b1e1ecf0d..15e895b7c 100644 --- a/feature/crash/src/main/java/com/dede/android_eggs/crash/CrashActivity.kt +++ b/feature/crash/src/main/java/com/dede/android_eggs/crash/CrashActivity.kt @@ -7,7 +7,6 @@ import android.os.Build import android.os.Bundle import android.util.Log import androidx.activity.compose.setContent -import androidx.activity.enableEdgeToEdge import androidx.appcompat.app.AppCompatActivity import androidx.compose.animation.animateContentSize import androidx.compose.foundation.layout.Arrangement @@ -71,8 +70,7 @@ import kotlin.system.exitProcess class CrashActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { - ThemeUtils.tryApplyOLEDTheme(this) - enableEdgeToEdge() + ThemeUtils.enableEdgeToEdge(this) super.onCreate(savedInstanceState) val tr: Throwable? = GlobalExceptionHandler.getUncaughtException(intent) diff --git a/feature/custom-tab-browser/build.gradle.kts b/feature/custom-tab-browser/build.gradle.kts index 3d678f5c1..c894b79d2 100644 --- a/feature/custom-tab-browser/build.gradle.kts +++ b/feature/custom-tab-browser/build.gradle.kts @@ -9,8 +9,8 @@ android { } dependencies { + implementation(project(":theme")) + implementation(libs.androidx.core) - implementation(libs.androidx.appcompat) - implementation(libs.google.material) implementation(libs.androidx.browser) } diff --git a/feature/custom-tab-browser/src/main/java/com/dede/android_eggs/util/CustomTabsBrowser.kt b/feature/custom-tab-browser/src/main/java/com/dede/android_eggs/util/CustomTabsBrowser.kt index 291417702..39ab7f465 100644 --- a/feature/custom-tab-browser/src/main/java/com/dede/android_eggs/util/CustomTabsBrowser.kt +++ b/feature/custom-tab-browser/src/main/java/com/dede/android_eggs/util/CustomTabsBrowser.kt @@ -3,20 +3,16 @@ package com.dede.android_eggs.util import android.content.ActivityNotFoundException import android.content.Context import android.content.Intent -import android.graphics.Color import android.net.Uri import android.provider.Browser import androidx.annotation.StringRes -import androidx.appcompat.app.AppCompatDelegate import androidx.browser.customtabs.CustomTabColorSchemeParams import androidx.browser.customtabs.CustomTabsIntent import androidx.core.net.toUri import androidx.core.os.bundleOf +import com.dede.android_eggs.views.settings.compose.prefs.ThemePrefUtil import com.dede.basic.createChooser import com.dede.basic.getConfigurationLocales -import com.google.android.material.color.DynamicColors -import com.google.android.material.color.MaterialColors -import com.google.android.material.R as M3R /** * CustomTabs Help @@ -33,16 +29,14 @@ object CustomTabsBrowser { @JvmStatic fun launchUrl(context: Context, uri: Uri) { - val colorScheme = when (AppCompatDelegate.getDefaultNightMode()) { - AppCompatDelegate.MODE_NIGHT_YES -> CustomTabsIntent.COLOR_SCHEME_DARK - AppCompatDelegate.MODE_NIGHT_NO -> CustomTabsIntent.COLOR_SCHEME_LIGHT + val colorScheme = when (ThemePrefUtil.getThemeModeValue(context)) { + ThemePrefUtil.DARK, ThemePrefUtil.AMOLED -> CustomTabsIntent.COLOR_SCHEME_DARK + ThemePrefUtil.LIGHT -> CustomTabsIntent.COLOR_SCHEME_LIGHT else -> CustomTabsIntent.COLOR_SCHEME_SYSTEM } - val dynamicContext = DynamicColors.wrapContextIfAvailable(context) - val color = MaterialColors.getColor(dynamicContext, M3R.attr.colorSurface, Color.WHITE) val params = CustomTabColorSchemeParams.Builder() - .setToolbarColor(color) + .setToolbarColor(ThemeUtils.getThemedSurfaceColor(context)) .build() val builder = CustomTabsIntent.Builder() diff --git a/feature/embedding-splits/build.gradle.kts b/feature/embedding-splits/build.gradle.kts index 3880253fd..c84519e8e 100644 --- a/feature/embedding-splits/build.gradle.kts +++ b/feature/embedding-splits/build.gradle.kts @@ -10,9 +10,8 @@ dependencies { implementation(project(":theme")) implementation(libs.androidx.core) - implementation(libs.androidx.appcompat) + implementation(libs.androidx.activity) implementation(libs.androidx.window) - implementation(libs.google.material) implementation(libs.androidx.startup) implementation(libs.androidx.shapes) implementation(libs.androidx.compose.activity) diff --git a/feature/embedding-splits/src/main/java/com/dede/android_eggs/embedding_splits/PlaceholderActivity.kt b/feature/embedding-splits/src/main/java/com/dede/android_eggs/embedding_splits/PlaceholderActivity.kt index 05b12c1d9..7274637f8 100644 --- a/feature/embedding-splits/src/main/java/com/dede/android_eggs/embedding_splits/PlaceholderActivity.kt +++ b/feature/embedding-splits/src/main/java/com/dede/android_eggs/embedding_splits/PlaceholderActivity.kt @@ -1,9 +1,8 @@ package com.dede.android_eggs.embedding_splits import android.os.Bundle +import androidx.activity.ComponentActivity import androidx.activity.compose.setContent -import androidx.activity.enableEdgeToEdge -import androidx.appcompat.app.AppCompatActivity import androidx.compose.animation.core.Animatable import androidx.compose.animation.core.AnimationVector1D import androidx.compose.animation.core.spring @@ -39,10 +38,7 @@ import androidx.graphics.shapes.pillStar import androidx.graphics.shapes.rectangle import androidx.graphics.shapes.star import androidx.graphics.shapes.toPath -import com.dede.android_eggs.util.LocalEvent import com.dede.android_eggs.util.ThemeUtils -import com.dede.android_eggs.views.settings.compose.prefs.DynamicColorPrefUtil -import com.dede.android_eggs.views.settings.compose.prefs.ThemePrefUtil import com.dede.android_eggs.views.theme.AppTheme import kotlinx.coroutines.launch import kotlin.math.max @@ -51,11 +47,10 @@ import kotlin.random.Random /** * Placeholder for embedding splits */ -class PlaceholderActivity : AppCompatActivity() { +class PlaceholderActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { - ThemeUtils.tryApplyOLEDTheme(this) - enableEdgeToEdge() + ThemeUtils.enableEdgeToEdge(this) super.onCreate(savedInstanceState) setContent { @@ -66,16 +61,6 @@ class PlaceholderActivity : AppCompatActivity() { } } } - - with(LocalEvent.receiver(this)) { - register(ThemePrefUtil.ACTION_NIGHT_MODE_CHANGED) { - recreate() - } - register(DynamicColorPrefUtil.ACTION_DYNAMIC_COLOR_CHANGED) { - recreate() - } - } - } init { diff --git a/feature/embedding-splits/src/main/java/com/dede/android_eggs/util/SplitUtils.kt b/feature/embedding-splits/src/main/java/com/dede/android_eggs/util/SplitUtils.kt index fa657f808..39f395154 100644 --- a/feature/embedding-splits/src/main/java/com/dede/android_eggs/util/SplitUtils.kt +++ b/feature/embedding-splits/src/main/java/com/dede/android_eggs/util/SplitUtils.kt @@ -4,7 +4,7 @@ import android.annotation.SuppressLint import android.content.Context import androidx.window.embedding.ActivityEmbeddingController import androidx.window.embedding.SplitController -import com.google.android.material.internal.ContextUtils +import com.dede.basic.getActivity /** * SplitController API. @@ -16,7 +16,7 @@ object SplitUtils { @SuppressLint("RestrictedApi") fun isActivityEmbedded(context: Context): Boolean { - val activity = ContextUtils.getActivity(context) ?: return false + val activity = context.getActivity() ?: return false return ActivityEmbeddingController.getInstance(context).isActivityEmbedded(activity) } diff --git a/feature/widget/build.gradle.kts b/feature/widget/build.gradle.kts index b51dd37cb..53ebd8430 100644 --- a/feature/widget/build.gradle.kts +++ b/feature/widget/build.gradle.kts @@ -10,6 +10,5 @@ android { dependencies { implementation(libs.androidx.core) - implementation(libs.androidx.appcompat) implementation(libs.google.material) } diff --git a/feature/widget/src/main/res/values-night/colors.xml b/feature/widget/src/main/res/values-night/colors.xml new file mode 100644 index 000000000..61bc3ea9a --- /dev/null +++ b/feature/widget/src/main/res/values-night/colors.xml @@ -0,0 +1,4 @@ + + + #414942 + diff --git a/feature/widget/src/main/res/values/colors.xml b/feature/widget/src/main/res/values/colors.xml index 0f36b5053..907dc1ba7 100644 --- a/feature/widget/src/main/res/values/colors.xml +++ b/feature/widget/src/main/res/values/colors.xml @@ -1,4 +1,4 @@ - ?attr/colorSurfaceVariant + #DDE5DB diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 58d68622f..e08250895 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -36,7 +36,6 @@ androidx-browser = "androidx.browser:browser:1.8.0" androidx-window = "androidx.window:window:1.3.0" google-material = "com.google.android.material:material:1.12.0" androidx-startup = "androidx.startup:startup-runtime:1.2.0" -androidx-localbroadcastmanager = "androidx.localbroadcastmanager:localbroadcastmanager:1.1.0" androidx-shapes = "androidx.graphics:graphics-shapes:1.0.1" androidx-compose-activity = { module = "androidx.activity:activity-compose", version.ref = "activity" } androidx-compose-lifecycle = { module = "androidx.lifecycle:lifecycle-runtime-compose", version.ref = "lifecycle" } diff --git a/script/icons/unicodes.json b/script/icons/unicodes.json index 5f6236ffc..74974d32d 100644 --- a/script/icons/unicodes.json +++ b/script/icons/unicodes.json @@ -1,7 +1,5 @@ { "outlined": [], - "rounded": [ - "tips_and_updates" - ], + "rounded": [], "filled": [] } \ No newline at end of file diff --git a/theme/build.gradle.kts b/theme/build.gradle.kts index 6e7bdf75a..f0e5fef7c 100644 --- a/theme/build.gradle.kts +++ b/theme/build.gradle.kts @@ -9,7 +9,6 @@ android { dependencies { implementation(libs.androidx.appcompat) implementation(libs.androidx.core) - implementation(libs.google.material) implementation(platform(libs.androidx.compose.bom)) implementation(libs.androidx.compose.ui) diff --git a/theme/src/main/java/com/dede/android_eggs/util/ThemeUtils.kt b/theme/src/main/java/com/dede/android_eggs/util/ThemeUtils.kt index 917268774..852cb6931 100644 --- a/theme/src/main/java/com/dede/android_eggs/util/ThemeUtils.kt +++ b/theme/src/main/java/com/dede/android_eggs/util/ThemeUtils.kt @@ -1,48 +1,79 @@ package com.dede.android_eggs.util -import android.annotation.SuppressLint import android.content.Context import android.content.res.Configuration -import androidx.annotation.StyleRes +import android.content.res.Resources +import android.graphics.Color +import androidx.activity.ComponentActivity +import androidx.activity.SystemBarStyle +import androidx.activity.enableEdgeToEdge +import androidx.annotation.ColorInt +import androidx.compose.material3.dynamicDarkColorScheme +import androidx.compose.material3.dynamicLightColorScheme +import androidx.compose.ui.graphics.toArgb +import com.dede.android_eggs.views.settings.compose.prefs.DynamicColorPrefUtil import com.dede.android_eggs.views.settings.compose.prefs.ThemePrefUtil -import com.dede.android_eggs.views.theme.R -import com.google.android.material.color.ThemeUtils -import com.google.android.material.internal.ContextUtils -import com.google.android.material.resources.MaterialAttributes -import com.google.android.material.R as M3R +import com.dede.android_eggs.views.theme.isDynamicColorEnable +import com.dede.android_eggs.views.theme.surfaceDark +import com.dede.android_eggs.views.theme.surfaceLight +import com.dede.android_eggs.views.theme.themeMode object ThemeUtils { - fun isSystemNightMode(context: Context): Boolean { - return (context.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK) == + private fun isSystemNightMode(resources: Resources): Boolean { + return (resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES } - @SuppressLint("RestrictedApi") - fun applyThemeOverlay(context: Context, @StyleRes theme: Int) { - ThemeUtils.applyThemeOverlay(context, theme) + fun isDarkMode(resources: Resources): Boolean { + val currentThemeMode = themeMode + return currentThemeMode == ThemePrefUtil.DARK || + currentThemeMode == ThemePrefUtil.AMOLED || + (currentThemeMode == ThemePrefUtil.FOLLOW_SYSTEM && isSystemNightMode(resources)) } - @SuppressLint("RestrictedApi") - fun isOLEDTheme(context: Context): Boolean { - return MaterialAttributes.resolveBoolean(context, R.attr.isOLEDTheme, false) - } + fun enableEdgeToEdge(activity: ComponentActivity) { + activity.enableEdgeToEdge( + statusBarStyle = SystemBarStyle.auto( + Color.TRANSPARENT, Color.TRANSPARENT + ) { resources -> isDarkMode(resources) }, + ) - @SuppressLint("RestrictedApi", "PrivateResource") - fun isMaterial3Theme(context: Context): Boolean { - return MaterialAttributes.resolveBoolean(context, M3R.attr.isMaterial3Theme, false) + LocalEvent.receiver(activity).register(ThemePrefUtil.ACTION_NIGHT_MODE_CHANGED) { + enableEdgeToEdge(activity) + } } - fun tryApplyOLEDTheme(context: Context) { - if (ThemePrefUtil.isAmoledMode(context)) { - applyThemeOverlay(context, R.style.ThemeOverlay_OLED) + @ColorInt + fun getThemedSurfaceColor(context: Context): Int { + val themeModeValue = ThemePrefUtil.getThemeModeValue(context) + if (DynamicColorPrefUtil.isSupported() && isDynamicColorEnable) { + return when (themeModeValue) { + ThemePrefUtil.LIGHT -> dynamicLightColorScheme(context).surface.toArgb() + ThemePrefUtil.DARK -> dynamicDarkColorScheme(context).surface.toArgb() + ThemePrefUtil.FOLLOW_SYSTEM -> { + if (isSystemNightMode(context.resources)) { + dynamicDarkColorScheme(context).surface.toArgb() + } else { + dynamicLightColorScheme(context).surface.toArgb() + } + } + else -> Color.WHITE + } } - } - @SuppressLint("RestrictedApi") - fun recreateActivityIfPossible(context: Context) { - val activity = ContextUtils.getActivity(context) - activity?.recreate() + return when (themeModeValue) { + ThemePrefUtil.LIGHT -> surfaceLight.toArgb() + ThemePrefUtil.DARK -> surfaceDark.toArgb() + ThemePrefUtil.FOLLOW_SYSTEM -> { + if (isSystemNightMode(context.resources)) { + surfaceDark.toArgb() + } else { + surfaceLight.toArgb() + } + } + else -> Color.WHITE + } } -} \ No newline at end of file +} diff --git a/theme/src/main/java/com/dede/android_eggs/views/settings/compose/prefs/DynamicColorPrefUtil.kt b/theme/src/main/java/com/dede/android_eggs/views/settings/compose/prefs/DynamicColorPrefUtil.kt index d6b72abe9..0849dec6b 100644 --- a/theme/src/main/java/com/dede/android_eggs/views/settings/compose/prefs/DynamicColorPrefUtil.kt +++ b/theme/src/main/java/com/dede/android_eggs/views/settings/compose/prefs/DynamicColorPrefUtil.kt @@ -1,53 +1,24 @@ package com.dede.android_eggs.views.settings.compose.prefs -import android.app.Activity -import android.app.Application import android.content.Context -import com.dede.android_eggs.util.ThemeUtils +import android.os.Build +import androidx.annotation.ChecksSdkIntAtLeast import com.dede.android_eggs.views.settings.compose.basic.SettingPrefUtil -import com.google.android.material.color.DynamicColors -import com.google.android.material.color.DynamicColorsOptions -import com.google.android.material.color.HarmonizedColors -import com.google.android.material.color.HarmonizedColorsOptions object DynamicColorPrefUtil { - val DEFAULT = - if (DynamicColors.isDynamicColorAvailable()) SettingPrefUtil.ON else SettingPrefUtil.OFF + val DEFAULT = if (isSupported()) SettingPrefUtil.ON else SettingPrefUtil.OFF const val KEY_DYNAMIC_COLOR = "pref_key_dynamic_color" - const val ACTION_DYNAMIC_COLOR_CHANGED = "ACTION_DYNAMIC_COLOR_CHANGED" + @ChecksSdkIntAtLeast(api = Build.VERSION_CODES.S) fun isSupported(): Boolean { - return DynamicColors.isDynamicColorAvailable() + return Build.VERSION.SDK_INT >= Build.VERSION_CODES.S } + @ChecksSdkIntAtLeast(api = Build.VERSION_CODES.S) fun isDynamicColorEnable(context: Context): Boolean { - return SettingPrefUtil.getValue(context, KEY_DYNAMIC_COLOR, DEFAULT) == SettingPrefUtil.ON + return isSupported() && + SettingPrefUtil.getValue(context, KEY_DYNAMIC_COLOR, DEFAULT) == SettingPrefUtil.ON } - fun apply(context: Context) { - if (!isSupported()) return - - val callback = Callback() - DynamicColors.applyToActivitiesIfAvailable( - context.applicationContext as Application, - DynamicColorsOptions.Builder() - .setPrecondition(callback) - .setOnAppliedCallback(callback) - .build() - ) - } - - private class Callback : DynamicColors.Precondition, DynamicColors.OnAppliedCallback { - override fun shouldApplyDynamicColors(activity: Activity, theme: Int): Boolean { - return ThemeUtils.isMaterial3Theme(activity) && isDynamicColorEnable(activity) - } - - override fun onApplied(activity: Activity) { - HarmonizedColors.applyToContextIfAvailable( - activity, HarmonizedColorsOptions.createMaterialDefaults() - ) - } - - } } \ No newline at end of file diff --git a/theme/src/main/java/com/dede/android_eggs/views/settings/compose/prefs/ThemePrefUtil.kt b/theme/src/main/java/com/dede/android_eggs/views/settings/compose/prefs/ThemePrefUtil.kt index c3fea8e9c..e83a25606 100644 --- a/theme/src/main/java/com/dede/android_eggs/views/settings/compose/prefs/ThemePrefUtil.kt +++ b/theme/src/main/java/com/dede/android_eggs/views/settings/compose/prefs/ThemePrefUtil.kt @@ -1,34 +1,22 @@ package com.dede.android_eggs.views.settings.compose.prefs import android.content.Context -import androidx.appcompat.app.AppCompatDelegate import com.dede.android_eggs.util.pref object ThemePrefUtil { const val AMOLED = -2 - const val LIGHT = AppCompatDelegate.MODE_NIGHT_NO - const val DARK = AppCompatDelegate.MODE_NIGHT_YES - const val FOLLOW_SYSTEM = AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM + const val LIGHT = 1 //AppCompatDelegate.MODE_NIGHT_NO + const val DARK = 2 //AppCompatDelegate.MODE_NIGHT_YES + const val FOLLOW_SYSTEM = -1//AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM const val KEY_NIGHT_MODE = "pref_key_night_mode" const val ACTION_NIGHT_MODE_CHANGED = "action_night_mode_changed" - fun isAmoledMode(context: Context): Boolean { - return context.pref.getInt(KEY_NIGHT_MODE, FOLLOW_SYSTEM) == AMOLED - } - fun getThemeModeValue(context: Context): Int { return context.pref.getInt(KEY_NIGHT_MODE, FOLLOW_SYSTEM) } - fun apply(context: Context) { - var mode = getThemeModeValue(context) - if (mode == AMOLED) { - mode = DARK - } - AppCompatDelegate.setDefaultNightMode(mode) - } } diff --git a/theme/src/main/res/values-night/colors.xml b/theme/src/main/res/values-night/colors.xml deleted file mode 100644 index da24a4a73..000000000 --- a/theme/src/main/res/values-night/colors.xml +++ /dev/null @@ -1,143 +0,0 @@ - - #95D5A7 - #00391D - #10512E - #B1F1C2 - #B6CCB9 - #223527 - #384B3D - #D2E8D4 - #A2CDDA - #023640 - #214C57 - #BEEAF7 - #FFB4AB - #690005 - #93000A - #FFDAD6 - #101510 - #DFE4DD - #101510 - #DFE4DD - #414942 - #C0C9BF - #8B938A - #414942 - #000000 - #DFE4DD - #2C322D - #2D6A44 - #B1F1C2 - #00210E - #95D5A7 - #10512E - #D2E8D4 - #0D1F13 - #B6CCB9 - #384B3D - #BEEAF7 - #001F26 - #A2CDDA - #214C57 - #101510 - #353A36 - #0A0F0B - #181D18 - #1C211C - #262B27 - #313631 - #99D9AB - #001B0B - #619E74 - #000000 - #BAD0BD - #071A0E - #819684 - #000000 - #A7D2DE - #00191F - #6D97A3 - #000000 - #FFBAB1 - #370001 - #FF5449 - #000000 - #101510 - #DFE4DD - #101510 - #F7FCF5 - #414942 - #C5CDC4 - #9DA59C - #7D857D - #000000 - #DFE4DD - #262B27 - #125230 - #B1F1C2 - #001507 - #95D5A7 - #003F21 - #D2E8D4 - #031509 - #B6CCB9 - #273A2D - #BEEAF7 - #001419 - #A2CDDA - #0B3C46 - #101510 - #353A36 - #0A0F0B - #181D18 - #1C211C - #262B27 - #313631 - #EFFFEF - #000000 - #99D9AB - #000000 - #EFFFEF - #000000 - #BAD0BD - #000000 - #F4FCFF - #000000 - #A7D2DE - #000000 - #FFF9F9 - #000000 - #FFBAB1 - #000000 - #101510 - #DFE4DD - #101510 - #FFFFFF - #414942 - #F5FDF3 - #C5CDC4 - #C5CDC4 - #000000 - #DFE4DD - #000000 - #003218 - #B5F6C6 - #000000 - #99D9AB - #001B0B - #D6EDD8 - #000000 - #BAD0BD - #071A0E - #C2EEFB - #000000 - #A7D2DE - #00191F - #101510 - #353A36 - #0A0F0B - #181D18 - #1C211C - #262B27 - #313631 - diff --git a/theme/src/main/res/values-night/themes.xml b/theme/src/main/res/values-night/themes.xml deleted file mode 100644 index 21946d0c2..000000000 --- a/theme/src/main/res/values-night/themes.xml +++ /dev/null @@ -1,53 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/theme/src/main/res/values/attrs.xml b/theme/src/main/res/values/attrs.xml deleted file mode 100644 index 9287a3a6b..000000000 --- a/theme/src/main/res/values/attrs.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/theme/src/main/res/values/colors.xml b/theme/src/main/res/values/colors.xml deleted file mode 100644 index 78ddd9b3a..000000000 --- a/theme/src/main/res/values/colors.xml +++ /dev/null @@ -1,143 +0,0 @@ - - #2D6A44 - #FFFFFF - #B1F1C2 - #00210E - #4F6353 - #FFFFFF - #D2E8D4 - #0D1F13 - #3A646F - #FFFFFF - #BEEAF7 - #001F26 - #BA1A1A - #FFFFFF - #FFDAD6 - #410002 - #F6FBF3 - #181D18 - #F6FBF3 - #181D18 - #DDE5DB - #414942 - #717971 - #C0C9BF - #000000 - #2C322D - #EDF2EB - #95D5A7 - #B1F1C2 - #00210E - #95D5A7 - #10512E - #D2E8D4 - #0D1F13 - #B6CCB9 - #384B3D - #BEEAF7 - #001F26 - #A2CDDA - #214C57 - #D6DBD4 - #F6FBF3 - #FFFFFF - #F0F5ED - #EAEFE8 - #E5EAE2 - #DFE4DD - #0A4D2B - #FFFFFF - #448159 - #FFFFFF - #344739 - #FFFFFF - #657A69 - #FFFFFF - #1C4853 - #FFFFFF - #517B86 - #FFFFFF - #8C0009 - #FFFFFF - #DA342E - #FFFFFF - #F6FBF3 - #181D18 - #F6FBF3 - #181D18 - #DDE5DB - #3D453E - #59615A - #757D75 - #000000 - #2C322D - #EDF2EB - #95D5A7 - #448159 - #FFFFFF - #2B6742 - #FFFFFF - #657A69 - #FFFFFF - #4D6151 - #FFFFFF - #517B86 - #FFFFFF - #38626D - #FFFFFF - #D6DBD4 - #F6FBF3 - #FFFFFF - #F0F5ED - #EAEFE8 - #E5EAE2 - #DFE4DD - #002913 - #FFFFFF - #0A4D2B - #FFFFFF - #142619 - #FFFFFF - #344739 - #FFFFFF - #00262E - #FFFFFF - #1C4853 - #FFFFFF - #4E0002 - #FFFFFF - #8C0009 - #FFFFFF - #F6FBF3 - #181D18 - #F6FBF3 - #000000 - #DDE5DB - #1E2620 - #3D453E - #3D453E - #000000 - #2C322D - #FFFFFF - #BAFBCB - #0A4D2B - #FFFFFF - #00351A - #FFFFFF - #344739 - #FFFFFF - #1E3123 - #FFFFFF - #1C4853 - #FFFFFF - #00323C - #FFFFFF - #D6DBD4 - #F6FBF3 - #FFFFFF - #F0F5ED - #EAEFE8 - #E5EAE2 - #DFE4DD - diff --git a/theme/src/main/res/values/themes.xml b/theme/src/main/res/values/themes.xml index 76e462403..149ab5014 100644 --- a/theme/src/main/res/values/themes.xml +++ b/theme/src/main/res/values/themes.xml @@ -1,92 +1,5 @@ - - - - +