Files
sing-android/REDESIGN.md
Sing Dev 1106f61cf0 Add CommandReceiver for adb control, fix tailscale routing
- CommandReceiver: exported broadcast receiver for adb CLI control
  - start/stop/status/set_config commands
  - Usage: adb shell am broadcast -a com.sing.vpn.CMD -n com.sing.vpn/.CommandReceiver --es cmd start
- SingConfig: add controlplane.tailscale.com → direct route rule
  to prevent tailnet control traffic from going through proxy
2026-04-02 15:31:15 +08:00

15 KiB
Raw Blame History

Android UI Redesign — Linear Style

Design Language

参考 Linear.app 的设计语言:极简、高信息密度、深色主题、无多余装饰。 告别"VPN 大圆按钮"的传统模板,走向工具型产品的质感。

核心原则

  1. 信息密度优先 — 一屏展示最多有效信息,不浪费空间在装饰元素上
  2. 深色主题 — Linear 标志性的深灰背景 + 高对比度文字
  3. 微妙的层次 — 通过 subtle 的背景色差区分区域,不用卡片阴影
  4. Monospace 数据 — IP、延迟、流量等技术数据用等宽字体
  5. 无圆角按钮 — 操作通过 inline 文字按钮或 icon 触发
  6. 状态用颜色 — 绿色=在线/连接,黄色=警告,红色=断开/错误,紫色=品牌色

色板

Background:       #0D0D0D   (接近纯黑)
Surface:          #1A1A1A   (卡片/列表背景)
Surface Hover:    #222222   (hover/pressed 状态)
Border:           #2A2A2A   (subtle 分割线)
Text Primary:     #EDEDEF   (主要文字,略带暖色)
Text Secondary:   #7C7C82   (次要文字)
Text Tertiary:    #4E4E52   (最弱文字)
Accent Purple:    #8B5CF6   (品牌色,交互高亮)
Green:            #22C55E   (在线/成功)
Yellow:           #EAB308   (警告/中等延迟)
Red:              #EF4444   (断开/错误/高延迟)
Blue:             #3B82F6   (信息/链接)

字体

Sans:       Inter / system sans-serif (UI 文字)
Mono:       JetBrains Mono / system monospace (数据)
Size:       12sp 默认11sp 次要14sp 标题20sp 页面标题
Weight:     400 normal500 medium (标题/强调)

Navigation

保持底部 3 tab但视觉重新设计

┌────────────────────────────────────┐
│ (content area)                     │
├────────────────────────────────────┤
│  ◉ Overview    ◎ Nodes    ◎ Config │  ← 底部导航icon + label
└────────────────────────────────────┘
  • Tab 名称: Overview / Nodes / Config (更专业)
  • Icon: 线性风格1.5dp stroke
  • Active: accent purple icon + white label
  • Inactive: text_tertiary

Screen 1: Overview (原 Home)

不再是大圆按钮。改为信息面板 + 顶部状态栏。

┌────────────────────────────────────┐
│ Sing                     ● Online  │  ← 顶栏: 品牌名 + 状态 dot + 文字
├────────────────────────────────────┤
│                                    │
│ ┌────────────────────────────────┐ │
│ │ CONNECTION                     │ │  ← section label (text_tertiary, 11sp, uppercase)
│ │                                │ │
│ │ Node     Tokyo-01              │ │  ← key-value pairs
│ │ Server   1.2.3.4:443          │ │
│ │ Protocol VLESS + Reality       │ │
│ │ Uptime   02:34:17             │ │
│ └────────────────────────────────┘ │
│                                    │
│ ┌────────────────────────────────┐ │
│ │ TRAFFIC                        │ │
│ │                                │ │
│ │    ↑ 1.2 GB        ↓ 8.7 GB   │ │  ← 大字号流量统计
│ │    ↑ 2.4 MB/s      ↓ 12 MB/s  │ │  ← 实时速率
│ │                                │ │
│ │ Connections  47 active         │ │
│ └────────────────────────────────┘ │
│                                    │
│ ┌────────────────────────────────┐ │
│ │ RECENT CONNECTIONS             │ │
│ │                                │ │
│ │ youtube.com        proxy  12ms │ │  ← 最近连接列表 (内嵌)
│ │ api.github.com     proxy  45ms │ │
│ │ baidu.com          direct  3ms │ │
│ │ ads.doubleclick    block   --  │ │
│ │                                │ │
│ │ View all →                     │ │
│ └────────────────────────────────┘ │
│                                    │
│         ┌──────────────┐           │
│         │  Disconnect  │           │  ← 底部操作按钮 (text button, red when connected)
│         └──────────────┘           │
│                                    │
├────────────────────────────────────┤
│  ◉ Overview    ◎ Nodes    ◎ Config │
└────────────────────────────────────┘

断开状态:

┌────────────────────────────────────┐
│ Sing                    ○ Offline  │
├────────────────────────────────────┤
│                                    │
│                                    │
│     Not connected                  │  ← 居中灰色文字
│     Tokyo-01 selected              │  ← 当前选中节点
│                                    │
│         ┌──────────────┐           │
│         │   Connect    │           │  ← accent purple 按钮
│         └──────────────┘           │
│                                    │
├────────────────────────────────────┤
│  ◉ Overview    ◎ Nodes    ◎ Config │
└────────────────────────────────────┘

Screen 2: Nodes (节点列表)

Linear 风格的列表 — 高密度、无卡片、hover 高亮。

┌────────────────────────────────────┐
│ Nodes                    3 nodes   │  ← 标题 + 计数
│ ┌────────┐ ┌──────────┐   ⟳  +   │  ← tab pills: Proxy | Tailscale | 测速 | 添加
│ │ Proxy  │ │Tailscale │           │
│ └────────┘ └──────────┘           │
├────────────────────────────────────┤
│                                    │
│ ● Tokyo-01                  12ms  │  ← active dot(green) + name + delay(green)
│   vless | 1.2.3.4:443             │  ← subtitle (text_secondary, 11sp)
│────────────────────────────────────│  ← subtle border, not divider
│ ○ Singapore-02             156ms  │  ← inactive dot(border only) + delay(yellow)
│   trojan | 5.6.7.8:443            │
│────────────────────────────────────│
│ ○ HK-Premium              328ms  │  ← delay(red)
│   ss | 9.10.11.12:8388            │
│────────────────────────────────────│
│                                    │
│ Tailscale tab:                     │
│ ● station            100.64.0.1  │  ← online dot + hostname + IP
│   linux | online                   │
│────────────────────────────────────│
│ ○ phone              100.64.0.2  │  ← offline dot
│   android | 3h ago                 │
│                                    │
├────────────────────────────────────┤
│  ◎ Overview    ◉ Nodes    ◎ Config │
└────────────────────────────────────┘

Node 操作 — 长按弹出 bottom sheet (不是 expand):

┌────────────────────────────────────┐
│ Tokyo-01                           │
│ vless + reality | 1.2.3.4:443      │
├────────────────────────────────────┤
│ ● Set Active                       │
│ ⟳ Test Latency                     │
│ ✎ Edit                             │
│ 🗑 Delete                   (red)  │
└────────────────────────────────────┘

添加节点 — 底部 sheet:

┌────────────────────────────────────┐
│ Import Nodes                       │
├────────────────────────────────────┤
│ 📋 Paste URI                       │  ← vless://, vmess://, ss://, trojan://
│ 🔗 Subscription URL                │
│ {} JSON Import                     │
│ ✎  Manual                          │
└────────────────────────────────────┘

Screen 3: Config (原 Settings)

分组列表,每组有 subtle header。不用 Material TextInputLayout用更紧凑的 inline 样式。

┌────────────────────────────────────┐
│ Config                             │
├────────────────────────────────────┤
│                                    │
│ ROUTING                            │  ← section header (11sp, text_tertiary)
│ Mode          Rule-based     ▾    │  ← inline dropdown, not spinner
│                                    │
│ DNS                                │
│ Remote        tls://8.8.8.8  ✎    │  ← value + edit icon
│ Local         223.5.5.5      ✎    │
│                                    │
│ GENERAL                            │
│ Auto-connect               ──●    │  ← toggle, accent purple when on
│ IPv6                       ●──    │
│ Block Ads                  ──●    │
│                                    │
│ RULES                              │
│ Proxy domains         3 rules    │  ← tap → detail screen
│ Direct domains        2 rules    │
│ Block domains         0 rules    │
│                                    │
│ TAILSCALE                          │
│ Enabled                    ──●    │
│ Auth key         ••••••••••  ✎    │
│ Hostname         my-phone    ✎    │
│ Accept routes              ●──    │
│                                    │
│ TOOLS                              │
│ Active Connections               │
│ Logs                             │
│                                    │
│ ABOUT                              │
│ Version                    0.5    │
│ Engine              mini-sing     │
│                                    │
├────────────────────────────────────┤
│  ◎ Overview    ◎ Nodes    ◉ Config │
└────────────────────────────────────┘

Implementation Plan

Phase 1: 基础框架 (迁移到 Compose)

XML 布局要实现 Linear 风格太费劲,建议迁移到 Jetpack Compose。理由

  • 深色主题 + 自定义组件在 Compose 里实现更自然
  • 动画/过渡效果更容易
  • 代码量更少Compose 比 XML + Fragment 减少约 40%
  • 现在是重写 UI 的最佳时机

改动:

  1. build.gradle.kts: 添加 Compose 依赖 + BOM
  2. Theme.kt: 定义 Linear 风格色板 + 字体
  3. MainActivity.kt: 改为 setContent {} + Compose Navigation
  4. 删除所有 XML layouts

Phase 2: 三个主屏

  1. OverviewScreen.kt — 状态面板 + 流量统计 + 最近连接
  2. NodesScreen.kt — 节点列表 + tab + 底部 sheet 操作
  3. ConfigScreen.kt — 分组配置列表

Phase 3: 二级页面

  1. ConnectionsScreen.kt — 全部活跃连接
  2. LogsScreen.kt — 实时日志
  3. EditNodeSheet.kt — 编辑/添加节点的 bottom sheet
  4. EditRulesScreen.kt — 编辑自定义规则

Phase 4: 细节打磨

  1. 过渡动画(页面切换、连接状态变化)
  2. 实时数据更新(流量速率曲线?)
  3. 触感反馈haptic on connect/disconnect
  4. 图标替换为自定义 linear-style icon set

Component Library

复用组件抽取:

// 基础组件
@Composable fun SectionHeader(title: String)           // "ROUTING", "DNS" etc.
@Composable fun KeyValueRow(key: String, value: String) // inline key-value
@Composable fun ToggleRow(label: String, checked: Boolean, onToggle: (Boolean) -> Unit)
@Composable fun NavigationRow(label: String, detail: String, onClick: () -> Unit)
@Composable fun StatusDot(color: Color, size: Dp = 8.dp)

// 节点相关
@Composable fun NodeRow(node: Node, isActive: Boolean, delay: Int?, onClick: () -> Unit)
@Composable fun PeerRow(peer: TailscalePeer, onClick: () -> Unit)

// 连接相关
@Composable fun ConnectionRow(conn: Connection)
@Composable fun TrafficCard(upload: Long, download: Long, uploadRate: Long, downloadRate: Long)

文件变更预估

操作 文件
新建 ui/theme/Theme.kt, ui/theme/Color.kt, ui/theme/Type.kt
新建 ui/components/*.kt (6-8 个基础组件)
新建 ui/screens/OverviewScreen.kt
新建 ui/screens/NodesScreen.kt
新建 ui/screens/ConfigScreen.kt
新建 ui/screens/ConnectionsScreen.kt
新建 ui/screens/LogsScreen.kt
新建 ui/sheets/NodeActionSheet.kt
新建 ui/sheets/ImportSheet.kt
重写 MainActivity.kt (Compose entry)
保留 所有非 UI 代码 (VPN service, config, JNI, etc.)
删除 所有 res/layout/*.xml
删除 所有 Fragment 类 (HomeFragment.kt 等)
更新 build.gradle.kts (Compose deps)
更新 colors.xml → 仅保留 splash 需要的
更新 themes.xml → 仅保留 splash + Compose bridge