Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,4 @@ Android-APP/gradlew.bat
# Generated server bundle (esbuild output) — CI/local builds regenerate it via
# "bun run build:android-bundle" + copy; see Android-APP/AGENTS.md BUILD section.
Android-APP/app/src/main/assets/server_bundle/server.cjs
/Android-APP/design/previews
1 change: 1 addition & 0 deletions Android-APP/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

<application
android:label="ZCode Proxy"
android:icon="@mipmap/ic_launcher"
android:allowBackup="true"
android:supportsRtl="true"
android:usesCleartextTraffic="true"
Expand Down
1,300 changes: 996 additions & 304 deletions Android-APP/app/src/main/java/com/zcode/proxy/MainActivity.kt

Large diffs are not rendered by default.

56 changes: 56 additions & 0 deletions Android-APP/app/src/main/java/com/zcode/proxy/ui/theme/Color.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package com.zcode.proxy.ui.theme

import androidx.compose.ui.graphics.Color

/*
* 中国传统色 token(只取色相,按 WCAG 微调明度;无国风符号)。
* 语义:靛青/月白蓝=primary,松绿=成功,胭脂=错误与登出,秋香/缃=429 等待。
*/

// 亮色:霜白/月白 + 靛青
val LightSurface = Color(0xFFF5F9FA) // 霜白
val LightSurfaceContainerLow = Color(0xFFE8F0F3) // 月白(卡片)
val LightSurfaceContainer = Color(0xFFE0EAEE)
val LightSurfaceContainerHigh = Color(0xFFD8E3E8)
val LightSurfaceContainerHighest = Color(0xFFCFDCE2)
val LightSurfaceContainerLowest = Color(0xFFFFFFFF)
val LightPrimary = Color(0xFF177CB0) // 靛青
val LightOnPrimary = Color(0xFFFFFFFF)
val LightPrimaryContainer = Color(0xFFD8EAEF) // 月白(英雄卡)
val LightOnPrimaryContainer = Color(0xFF123B4E) // 绀青
val LightSecondary = Color(0xFF14587D)
val LightSecondaryContainer = Color(0xFFD8EAEF)
val LightOnSecondaryContainer = Color(0xFF14587D)
val LightTertiary = Color(0xFF21A675) // 松绿(在线/200)
val LightOnTertiary = Color(0xFFFFFFFF)
val LightError = Color(0xFF9D2933) // 胭脂(错误/登出)
val LightOnError = Color(0xFFFFFFFF)
val LightOnSurface = Color(0xFF1F2A30) // 鸦青
val LightOnSurfaceVariant = Color(0xFF5A6B74) // 黛灰
val LightOutline = Color(0xFF9FB4BE)
val LightOutlineVariant = Color(0xFFCBD9DE)
val LightDim = Color(0xFF7C8B92)

// 暗色:漆黑/鸦青 + 月白蓝
val DarkSurface = Color(0xFF12181D) // 漆黑
val DarkSurfaceContainerLow = Color(0xFF1C2429) // 鸦青黑(卡片)
val DarkSurfaceContainer = Color(0xFF222B31)
val DarkSurfaceContainerHigh = Color(0xFF2A343A)
val DarkSurfaceContainerHighest = Color(0xFF323E45)
val DarkSurfaceContainerLowest = Color(0xFF0D1216)
val DarkPrimary = Color(0xFF7EC3DF) // 月白蓝
val DarkOnPrimary = Color(0xFF0C2A38)
val DarkPrimaryContainer = Color(0xFF17475E) // 绀青(英雄卡)
val DarkOnPrimaryContainer = Color(0xFFD9EEF6) // 月白
val DarkSecondary = Color(0xFF6FB9D8)
val DarkSecondaryContainer = Color(0xFF1D5A77)
val DarkOnSecondaryContainer = Color(0xFFC4E3EF)
val DarkTertiary = Color(0xFF5BC89B) // 松绿亮
val DarkOnTertiary = Color(0xFF0A2E1F)
val DarkError = Color(0xFFE06B76) // 胭脂亮
val DarkOnError = Color(0xFF3A0A10)
val DarkOnSurface = Color(0xFFDEE7EA)
val DarkOnSurfaceVariant = Color(0xFF9DAEB6)
val DarkOutline = Color(0xFF5F707A)
val DarkOutlineVariant = Color(0xFF2E3A40)
val DarkDim = Color(0xFF76878F)
174 changes: 174 additions & 0 deletions Android-APP/app/src/main/java/com/zcode/proxy/ui/theme/Theme.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
package com.zcode.proxy.ui.theme

import android.app.Activity
import android.content.Context
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Shapes
import androidx.compose.material3.darkColorScheme
import androidx.compose.material3.lightColorScheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.SideEffect
import androidx.compose.runtime.staticCompositionLocalOf
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.unit.Density
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalView
import androidx.compose.ui.unit.dp
import androidx.core.view.WindowCompat

/** 外观三档:跟随系统(默认)/ 亮色 / 暗色。偏好存 SharedPreferences。 */
enum class ThemeMode {
FOLLOW_SYSTEM,
LIGHT,
DARK,
}

/** 当前生效的暗色状态(含手动覆盖后),组件据此做暗色专属处理(如图标垫边)。 */
val LocalDarkTheme = staticCompositionLocalOf { false }

@Composable
fun isDarkTheme(): Boolean = LocalDarkTheme.current

object ThemePrefs {
private const val PREFS = "ui_prefs"
private const val KEY_THEME_MODE = "theme_mode"

fun load(context: Context): ThemeMode {
val ordinal = context.getSharedPreferences(PREFS, Context.MODE_PRIVATE)
.getInt(KEY_THEME_MODE, ThemeMode.FOLLOW_SYSTEM.ordinal)
return ThemeMode.entries.getOrElse(ordinal) { ThemeMode.FOLLOW_SYSTEM }
}

fun save(context: Context, mode: ThemeMode) {
context.getSharedPreferences(PREFS, Context.MODE_PRIVATE)
.edit()
.putInt(KEY_THEME_MODE, mode.ordinal)
.apply()
}
}

private val LightColors = lightColorScheme(
primary = LightPrimary,
onPrimary = LightOnPrimary,
primaryContainer = LightPrimaryContainer,
onPrimaryContainer = LightOnPrimaryContainer,
secondary = LightSecondary,
onSecondary = Color(0xFFFFFFFF),
secondaryContainer = LightSecondaryContainer,
onSecondaryContainer = LightOnSecondaryContainer,
tertiary = LightTertiary,
onTertiary = LightOnTertiary,
error = LightError,
onError = LightOnError,
background = LightSurface,
onBackground = LightOnSurface,
surface = LightSurface,
onSurface = LightOnSurface,
surfaceVariant = LightSurfaceContainerLow,
onSurfaceVariant = LightOnSurfaceVariant,
outline = LightOutline,
outlineVariant = LightOutlineVariant,
surfaceContainerLowest = LightSurfaceContainerLowest,
surfaceContainerLow = LightSurfaceContainerLow,
surfaceContainer = LightSurfaceContainer,
surfaceContainerHigh = LightSurfaceContainerHigh,
surfaceContainerHighest = LightSurfaceContainerHighest,
)

private val DarkColors = darkColorScheme(
primary = DarkPrimary,
onPrimary = DarkOnPrimary,
primaryContainer = DarkPrimaryContainer,
onPrimaryContainer = DarkOnPrimaryContainer,
secondary = DarkSecondary,
onSecondary = Color(0xFF07161F),
secondaryContainer = DarkSecondaryContainer,
onSecondaryContainer = DarkOnSecondaryContainer,
tertiary = DarkTertiary,
onTertiary = DarkOnTertiary,
error = DarkError,
onError = DarkOnError,
background = DarkSurface,
onBackground = DarkOnSurface,
surface = DarkSurface,
onSurface = DarkOnSurface,
surfaceVariant = DarkSurfaceContainerLow,
onSurfaceVariant = DarkOnSurfaceVariant,
outline = DarkOutline,
outlineVariant = DarkOutlineVariant,
surfaceContainerLowest = DarkSurfaceContainerLowest,
surfaceContainerLow = DarkSurfaceContainerLow,
surfaceContainer = DarkSurfaceContainer,
surfaceContainerHigh = DarkSurfaceContainerHigh,
surfaceContainerHighest = DarkSurfaceContainerHighest,
)

private val AppShapes = Shapes(
extraSmall = RoundedCornerShape(6.dp),
small = RoundedCornerShape(10.dp),
medium = RoundedCornerShape(20.dp),
large = RoundedCornerShape(24.dp),
extraLarge = RoundedCornerShape(28.dp),
)

/** 全局视觉缩放:整体缩小 5%(dp+sp 同缩),窄屏不易挤压。 */
private const val UiScale = 0.95f

/**
* 跟随系统同步链路:系统深色开关 → Configuration.uiMode 变化 →
* MainActivity 未声明 configChanges,Activity 重建 → isSystemInDarkTheme()
* 取新值重组。手动三档覆盖由调用方持久化后经 [themeMode] 传入。
*/
@Composable
fun ZcodeTheme(
themeMode: ThemeMode = ThemeMode.FOLLOW_SYSTEM,
content: @Composable () -> Unit,
) {
val dark = when (themeMode) {
ThemeMode.FOLLOW_SYSTEM -> isSystemInDarkTheme()
ThemeMode.LIGHT -> false
ThemeMode.DARK -> true
}
val colorScheme = if (dark) DarkColors else LightColors
val view = LocalView.current
if (!view.isInEditMode) {
SideEffect {
val window = (view.context as Activity).window
WindowCompat.getInsetsController(window, view).apply {
isAppearanceLightStatusBars = !dark
isAppearanceLightNavigationBars = !dark
}
}
}
val baseDensity = LocalDensity.current
CompositionLocalProvider(
LocalDensity provides Density(baseDensity.density * UiScale, baseDensity.fontScale * UiScale),
LocalDarkTheme provides dark,
) {
MaterialTheme(
colorScheme = colorScheme,
typography = AppTypography,
shapes = AppShapes,
content = content,
)
}
}

/** 语义色取值捷径:在线/成功=松绿,429/等待=秋香·缃。 */
val lightSuccess = Color(0xFF21A675)
val darkSuccess = Color(0xFF5BC89B)
val lightWarning = Color(0xFFA9760B)
val darkWarning = Color(0xFFE0BC5E)

@Composable
fun successColor(): Color = if (isSystemInDarkTheme()) darkSuccess else lightSuccess

@Composable
fun warningColor(): Color = if (isSystemInDarkTheme()) darkWarning else lightWarning

/** 卡片内的弱化文字(日志 dim / 提示)。 */
@Composable
fun dimColor(): Color = if (isSystemInDarkTheme()) DarkDim else LightDim
20 changes: 20 additions & 0 deletions Android-APP/app/src/main/java/com/zcode/proxy/ui/theme/Type.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.zcode.proxy.ui.theme

import androidx.compose.material3.Typography
import androidx.compose.ui.text.font.Font
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontWeight
import com.zcode.proxy.R

/**
* 数据声部(地址 / 数值 / 日志)专用等宽字体:JetBrains Mono(SIL OFL 1.1,
* 许可证全文见 Android-APP/fonts-OFL.txt)。CJK 字形回落系统字体。
*/
val Mono = FontFamily(
Font(R.font.jetbrainsmono_regular, FontWeight.Normal),
Font(R.font.jetbrainsmono_medium, FontWeight.Medium),
Font(R.font.jetbrainsmono_semibold, FontWeight.SemiBold),
Font(R.font.jetbrainsmono_bold, FontWeight.Bold),
)

val AppTypography = Typography()
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 5 additions & 0 deletions Android-APP/app/src/main/res/values-night/themes.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- 深色模式下启动瞬间的窗口底色(android:Theme.Material 即深色主题) -->
<style name="Theme.ZcodeProxy" parent="android:Theme.Material.NoActionBar" />
</resources>
2 changes: 1 addition & 1 deletion Android-APP/app/src/main/res/values/themes.xml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="Theme.ZcodeProxy" parent="android:Theme.Material.NoActionBar" />
<style name="Theme.ZcodeProxy" parent="android:Theme.Material.Light.NoActionBar" />
</resources>
Loading