Skip to content
Closed
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
c6c8903
feat: port miuix blur to expressive UI
fanziyun Aug 11, 2026
a7e0439
refactor(blur): clean up comments and unused properties
fanziyun Aug 11, 2026
ba22618
Update app/src/main/java/com/abk/kernel/ui/blur/BlurredCardBackdrop.kt
fanziyun Aug 11, 2026
413263b
Update app/src/main/java/com/abk/kernel/ui/components/AppBackgroundHo…
fanziyun Aug 11, 2026
f5f56a8
Update app/src/main/java/com/abk/kernel/ui/blur/BlurredCardBackdrop.kt
fanziyun Aug 11, 2026
b865e99
fix(blur): repair broken Kotlin syntax from PR edits
fanziyun Aug 11, 2026
d640a79
fix(blur): address multi-agent review findings
fanziyun Aug 12, 2026
a849703
fix(blur): restore ContentScale.Crop on the shared background image
fanziyun Aug 12, 2026
63ff616
fix(blur): harden blur gating, cancellation, and toggle semantics
fanziyun Aug 12, 2026
fb7a293
fix(blur): bound the shared background painter's decode to the viewport
fanziyun Aug 12, 2026
8a7161c
fix(blur): pass enableBlur to the SUSFS page top bar
fanziyun Aug 12, 2026
679eeba
fix(blur): stop background/card blur from flashing on IME-driven resizes
fanziyun Aug 12, 2026
23dd907
fix(blur): cache the pre-blurred card background globally and on disk
fanziyun Aug 12, 2026
7e138fb
fix(blur): keep card surfaces translucent from the first frame
fanziyun Aug 12, 2026
6025546
fix(blur): stop IME-driven resizes from re-blurring the backdrop
fanziyun Aug 12, 2026
f8011d6
Update app/src/main/java/com/abk/kernel/ui/blur/BlurredCardBackdrop.kt
fanziyun Aug 12, 2026
52a51c2
fix(blur): key on-disk blur cache by URI and stop deleting it on startup
fanziyun Aug 12, 2026
6f86030
fix(blur): refresh backdrop on real resize and move cache write off m…
fanziyun Aug 12, 2026
318e86e
fix(blur): use SHA-256 cache keys and keep per-URI blur caches across…
fanziyun Aug 12, 2026
f6bacb8
fix(ui): restore adjustResize and harden blur resize + disk-cache beh…
fanziyun Aug 12, 2026
4e2c829
fix(blur): re-blur on real viewport growth while skipping IME shrink
fanziyun Aug 13, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,9 @@ dependencies {
// Preferences
implementation(libs.datastore.preferences)

// Blur / glass
implementation(libs.miuix.blur)

testImplementation(libs.junit)
testImplementation(libs.robolectric)
testImplementation(libs.kotlinx.coroutines.test)
Expand Down
6 changes: 5 additions & 1 deletion app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
Expand All @@ -14,6 +15,9 @@
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />

<!-- Runtime shader blur is guarded in code while the app still supports API 26+. -->
Comment thread
fanziyun marked this conversation as resolved.
Outdated
<uses-sdk tools:overrideLibrary="top.yukonga.miuix.kmp.blur" />

<application
android:name=".AbkApplication"
android:allowBackup="true"
Expand Down
47 changes: 42 additions & 5 deletions app/src/main/java/com/abk/kernel/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ import androidx.compose.runtime.setValue
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.layout.onSizeChanged
import androidx.compose.ui.platform.LocalConfiguration
Expand All @@ -89,6 +90,12 @@ import com.abk.kernel.ui.components.AbkSnackbarHost
import com.abk.kernel.ui.components.animateBottomNavForChildPage
import com.abk.kernel.ui.components.showAbkSnackbar
import com.abk.kernel.extensions.AbkExtensionBootstrapActivity
import com.abk.kernel.ui.blur.LocalBlurState
import com.abk.kernel.ui.blur.blurEffect
import com.abk.kernel.ui.blur.blurSourceBody
import com.abk.kernel.ui.blur.isBlurActive
import com.abk.kernel.ui.blur.rememberBlurBackdrop
import com.abk.kernel.ui.blur.rememberBlurBackgroundPainter
import com.abk.kernel.ui.screens.BuildScreen
import com.abk.kernel.ui.screens.FlashScreen
import com.abk.kernel.ui.screens.InstalledModulesScreen
Expand Down Expand Up @@ -123,6 +130,7 @@ class MainActivity : ComponentActivity() {
setContent {
val vm: MainViewModel = viewModel()
val state by vm.uiState.collectAsState()
val uiSurfaceAlphaPreview by vm.uiSurfaceAlphaPreview.collectAsState(initial = state.uiSurfaceAlpha)
var extensionBootstrapIssued by rememberSaveable { mutableStateOf(false) }

LaunchedEffect(Unit) {
Expand Down Expand Up @@ -161,7 +169,8 @@ class MainActivity : ComponentActivity() {
AppBackgroundHost(
backgroundUri = state.customBackgroundUri,
backgroundEnabled = state.backgroundImageEnabled,
uiSurfaceAlpha = state.uiSurfaceAlpha
uiSurfaceAlpha = uiSurfaceAlphaPreview,
blurBackgroundEnabled = state.blurConfig.wantsBackgroundPainter,
) {
when {
!state.termsLoaded -> Surface(
Expand Down Expand Up @@ -582,6 +591,21 @@ private fun AbkMainScaffold(
}
val navProgress = navProgressAnim.value

// The bottom NavigationBar/NavigationRail live outside every screen's
// BlurScreenScaffold, so they keep their own backdrop as the blur source.
val blurBackdrop = rememberBlurBackdrop(
enableBlur = state.blurConfig.blurEnabled,
surfaceColor = MaterialTheme.colorScheme.surfaceContainer,
backgroundPainter = rememberBlurBackgroundPainter(state.blurConfig),
)

CompositionLocalProvider(
LocalBlurState provides blurBackdrop,
) {
// Gate bar transparency on the frosted effect actually rendering (API >= 33),
// so pre-Android-13 devices fall back to the opaque surface color.
val blurActive = isBlurActive(state.blurEnabled)

Box(
modifier = Modifier
.fillMaxSize()
Expand All @@ -603,8 +627,14 @@ private fun AbkMainScaffold(
contentAlignment = Alignment.Center
) {
NavigationRail(
modifier = Modifier.fillMaxSize(),
containerColor = uiSurfaceColor(MaterialTheme.colorScheme.surfaceContainer)
modifier = Modifier
.fillMaxSize()
.then(if (blurActive) Modifier.blurEffect() else Modifier),
containerColor = if (blurActive) {
Color.Transparent
} else {
uiSurfaceColor(MaterialTheme.colorScheme.surfaceContainer)
}
) {
visibleTabs.forEach { tab ->
NavigationRailItem(
Expand Down Expand Up @@ -650,8 +680,13 @@ private fun AbkMainScaffold(
val hidden = 1f - navProgress
translationY = hidden * bottomBarHeightPx
alpha = 1f - (hidden * 0.15f)
},
containerColor = uiSurfaceColor(MaterialTheme.colorScheme.surfaceContainer),
}
.then(if (blurActive) Modifier.blurEffect() else Modifier),
containerColor = if (blurActive) {
Color.Transparent
} else {
uiSurfaceColor(MaterialTheme.colorScheme.surfaceContainer)
},
tonalElevation = 0.dp
) {
visibleTabs.forEach { tab ->
Expand Down Expand Up @@ -691,6 +726,7 @@ private fun AbkMainScaffold(
modifier = Modifier
.fillMaxSize()
.zIndex(1f)
.blurSourceBody()
) {
Box(
modifier = Modifier
Expand Down Expand Up @@ -789,6 +825,7 @@ private fun AbkMainScaffold(
)
}
}
}

@Composable
private fun AbkTab.displayLabel(rootGranted: Boolean): String = when (this) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import com.abk.kernel.data.model.normalizeAppUpdateStability
import com.abk.kernel.utils.DownloadDirectoryUtils
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.runBlocking
Expand Down Expand Up @@ -53,6 +54,8 @@ class PreferencesRepository(private val context: Context) {
val KEY_CUSTOM_BACKGROUND_URI = stringPreferencesKey("custom_background_uri")
val KEY_BACKGROUND_IMAGE_ENABLED = booleanPreferencesKey("background_image_enabled")
val KEY_UI_SURFACE_ALPHA = floatPreferencesKey("ui_surface_alpha")
val KEY_BLUR_ENABLED = booleanPreferencesKey("blur_enabled")
val KEY_BLUR_BACKGROUND_EXP_ENABLED = booleanPreferencesKey("blur_background_exp_enabled")
val KEY_BUILD_CONFIG = stringPreferencesKey("build_config_json")
val KEY_BUILD_PLANS = stringPreferencesKey("build_plans_json")
val KEY_BUILD_QUEUE = stringPreferencesKey("build_queue_json")
Expand Down Expand Up @@ -105,9 +108,16 @@ class PreferencesRepository(private val context: Context) {
val dynamicColorEnabled: Flow<Boolean> = context.dataStore.data.map { it[KEY_DYNAMIC_COLOR_ENABLED] ?: true }
val customThemeColorArgb: Flow<Int?> = context.dataStore.data.map { it[KEY_CUSTOM_THEME_COLOR] }
val customAccentColorArgb: Flow<Int?> = context.dataStore.data.map { it[KEY_CUSTOM_ACCENT_COLOR] }
val customBackgroundUri: Flow<String?> = context.dataStore.data.map { it[KEY_CUSTOM_BACKGROUND_URI] }
val backgroundImageEnabled: Flow<Boolean> = context.dataStore.data.map { it[KEY_BACKGROUND_IMAGE_ENABLED] ?: false }
val uiSurfaceAlpha: Flow<Float> = context.dataStore.data.map { it[KEY_UI_SURFACE_ALPHA] ?: 1f }
val customBackgroundUri: Flow<String?> =
context.dataStore.data.map { it[KEY_CUSTOM_BACKGROUND_URI] }.distinctUntilChanged()
val backgroundImageEnabled: Flow<Boolean> =
context.dataStore.data.map { it[KEY_BACKGROUND_IMAGE_ENABLED] ?: false }.distinctUntilChanged()
val uiSurfaceAlpha: Flow<Float> =
context.dataStore.data.map { it[KEY_UI_SURFACE_ALPHA] ?: 1f }.distinctUntilChanged()
val blurEnabled: Flow<Boolean> =
context.dataStore.data.map { it[KEY_BLUR_ENABLED] ?: true }.distinctUntilChanged()
val blurBackgroundExpEnabled: Flow<Boolean> =
context.dataStore.data.map { it[KEY_BLUR_BACKGROUND_EXP_ENABLED] ?: false }.distinctUntilChanged()
val buildConfigJson: Flow<String?> = context.dataStore.data.map { it[KEY_BUILD_CONFIG] }
val buildPlansJson: Flow<String?> = context.dataStore.data.map { it[KEY_BUILD_PLANS] }
val buildQueueJson: Flow<String?> = context.dataStore.data.map { it[KEY_BUILD_QUEUE] }
Expand Down Expand Up @@ -247,6 +257,13 @@ class PreferencesRepository(private val context: Context) {
suspend fun setUiSurfaceAlpha(alpha: Float) = context.dataStore.edit {
it[KEY_UI_SURFACE_ALPHA] = alpha.coerceIn(0f, 1f)
}
suspend fun setBlurEnabled(v: Boolean) = context.dataStore.edit {
it[KEY_BLUR_ENABLED] = v
if (!v) it[KEY_BLUR_BACKGROUND_EXP_ENABLED] = false
}
suspend fun setBlurBackgroundExpEnabled(v: Boolean) = context.dataStore.edit {
it[KEY_BLUR_BACKGROUND_EXP_ENABLED] = v
}
suspend fun saveBuildConfigJson(json: String) = context.dataStore.edit { it[KEY_BUILD_CONFIG] = json }
suspend fun saveBuildPlansJson(json: String) = context.dataStore.edit { it[KEY_BUILD_PLANS] = json }
suspend fun saveBuildQueueJson(json: String) = context.dataStore.edit { it[KEY_BUILD_QUEUE] = json }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
Expand All @@ -51,12 +50,15 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleEventObserver
import androidx.lifecycle.compose.LocalLifecycleOwner
import com.abk.kernel.R
import com.abk.kernel.data.repository.PreferencesRepository
import com.abk.kernel.ui.blur.BlurConfig
import com.abk.kernel.ui.blur.BlurScreenScaffold
import com.abk.kernel.ui.components.AbkCenteredLoadingTransition
import com.abk.kernel.ui.components.AbkScreenHorizontalPadding
import com.abk.kernel.ui.components.AppBackgroundHost
Expand Down Expand Up @@ -91,6 +93,8 @@ class AbkExtensionManagerActivity : ComponentActivity() {
val customBackgroundUri by prefs.customBackgroundUri.collectAsState(initial = null)
val backgroundImageEnabled by prefs.backgroundImageEnabled.collectAsState(initial = false)
val uiSurfaceAlpha by prefs.uiSurfaceAlpha.collectAsState(initial = 1f)
val blurEnabled by prefs.blurEnabled.collectAsState(initial = true)
val blurBackgroundExpEnabled by prefs.blurBackgroundExpEnabled.collectAsState(initial = false)

AbkTheme(
themeMode = themeMode,
Expand All @@ -101,13 +105,21 @@ class AbkExtensionManagerActivity : ComponentActivity() {
AppBackgroundHost(
backgroundUri = customBackgroundUri,
backgroundEnabled = backgroundImageEnabled,
uiSurfaceAlpha = uiSurfaceAlpha
uiSurfaceAlpha = uiSurfaceAlpha,
blurBackgroundEnabled = blurEnabled &&
blurBackgroundExpEnabled &&
backgroundImageEnabled &&
!customBackgroundUri.isNullOrBlank(),
) {
AbkExtensionManagerScreen(
focusExtensionId = focusExtensionId,
bootstrapMode = bootstrapMode,
onBack = ::finish,
onExternalFlowLaunched = ::finish,
blurEnabled = blurEnabled,
blurBackgroundExpEnabled = blurBackgroundExpEnabled,
backgroundUri = customBackgroundUri,
backgroundImageEnabled = backgroundImageEnabled,
)
}
}
Expand All @@ -122,6 +134,10 @@ fun AbkExtensionManagerScreen(
onBack: () -> Unit,
modifier: Modifier = Modifier,
containerColor: Color = Color.Transparent,
blurEnabled: Boolean = false,
blurBackgroundExpEnabled: Boolean = false,
backgroundUri: String? = null,
backgroundImageEnabled: Boolean = false,
onExternalFlowLaunched: () -> Unit = {},
) {
val context = LocalContext.current
Expand Down Expand Up @@ -269,7 +285,13 @@ fun AbkExtensionManagerScreen(
return
}

Scaffold(
BlurScreenScaffold(
blurConfig = BlurConfig(
blurEnabled = blurEnabled,
backgroundExpEnabled = blurBackgroundExpEnabled,
backgroundUri = backgroundUri,
backgroundImageEnabled = backgroundImageEnabled,
),
modifier = modifier,
containerColor = containerColor,
topBar = {
Expand All @@ -293,28 +315,32 @@ fun AbkExtensionManagerScreen(
}
}
},
enableBlur = blurEnabled,
actions = {
IconButton(onClick = ::requestRefresh) {
Icon(Icons.Default.Refresh, contentDescription = stringResource(R.string.refresh))
}
}
)
}
) { padding ->
) { topBarHeight ->
if (loading) {
AbkCenteredLoadingTransition(
text = stringResource(R.string.loading),
modifier = Modifier.padding(padding)
modifier = Modifier
.fillMaxSize()
.padding(top = topBarHeight + 16.dp)
)
return@Scaffold
return@BlurScreenScaffold
}

if (extensions.isEmpty()) {
Column(
modifier = Modifier
.fillMaxSize()
.padding(padding)
.padding(horizontal = AbkScreenHorizontalPadding, vertical = 16.dp),
.padding(top = topBarHeight + 16.dp)
.padding(horizontal = AbkScreenHorizontalPadding)
.padding(bottom = 16.dp),
verticalArrangement = Arrangement.Top
) {
ExpressiveSectionCard(
Expand All @@ -324,16 +350,14 @@ fun AbkExtensionManagerScreen(
containerColor = MaterialTheme.colorScheme.surfaceContainerHigh,
) {}
}
return@Scaffold
return@BlurScreenScaffold
}

LazyColumn(
modifier = Modifier
.fillMaxSize()
.padding(padding),
modifier = Modifier.fillMaxSize(),
contentPadding = PaddingValues(
start = AbkScreenHorizontalPadding,
top = 16.dp,
top = topBarHeight + 16.dp,
end = AbkScreenHorizontalPadding,
bottom = 80.dp
),
Expand Down
Loading
Loading