diff --git a/INTERFACE.md b/INTERFACE.md index 46047db..8030209 100644 --- a/INTERFACE.md +++ b/INTERFACE.md @@ -201,17 +201,47 @@ adb -s shell am start -n /.ClipActivity --es text_b64 shell am start -n /.InfoActivity \ --es name "A08" --es ip "192.168.20.100" --es serial "192.168.20.100:5555" \ - --es server "http://192.168.20.220:18050" + --es server "http://192.168.20.220:18050" \ + --es accounts_b64 "" # 可选,见 §5.2.1 # 关闭:--es close 1 ``` 要求:全屏大字、再次调用可覆盖内容、`close` 可关闭、不驻留前台。 +#### 5.2.1 本机账号台账(`accounts_b64`,可选 extras,已实现 v1.5) + +平台在**打开**身份页时顺带推这台设备在「账号」页登记的抖音账号(`close` 时不带)。 +**标准 base64(不是 URL_SAFE)+ UTF-8**: + +```json +{"v":1,"total":4,"shown":4,"truncated":false, + "note":"设备『A03』登记 4 个号", + "accounts":[ + {"name":"AA建材王总","douyin_id":"66057500463","device_name":"A03","device_no":"A03", + "sim_in_device":true,"can_post_video":true}]} +``` + +| 字段 | 说明 | +|------|------| +| `v` | 块格式版本(当前 **1**) | +| `total` / `shown` / `truncated` | 台账里的总数 / 本次几条 / 是否被截断 | +| `accounts[].name` / `douyin_id` | 账号名称 / **抖音号(纯号)** | +| `accounts[].sim_in_device` / `can_post_video` | 卡在机内 / 可发视频 | +| `accounts[].phone` | **默认不推**(大字页是公开屏幕);平台侧可传 `include_phone:true` 要 | + +设备端三种情况要分清(InfoActivity 已按此实现): + +| 情况 | 表现 | +|---|---| +| 没有该 extra | 账号块**整块不显示**(= 这台设备在台账里没登记账号,旧平台也走这条) | +| 有但解不开 | 显示一行**解析失败**提示(不要静默,否则看着像"这台没账号") | +| 有且能解开 | 逐条显示;`truncated` 时补"…还有 N 个,见平台「账号」页" | + ### 5.3 一次性配置下发(建议设备端实现) ```bash diff --git a/app/build.gradle b/app/build.gradle index 61f59f3..9391012 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -11,8 +11,8 @@ android { applicationId "com.example.deviceagent" minSdk 29 // Android 10;剪贴板限制就是从这一版开始的 targetSdk 34 - versionCode 5 - versionName "1.4" + versionCode 6 + versionName "1.5" // 1.5:身份页显示本机账号台账(accounts_b64) } buildTypes { diff --git a/app/src/main/java/com/example/deviceagent/InfoActivity.kt b/app/src/main/java/com/example/deviceagent/InfoActivity.kt index 93bcc0a..2c69c32 100644 --- a/app/src/main/java/com/example/deviceagent/InfoActivity.kt +++ b/app/src/main/java/com/example/deviceagent/InfoActivity.kt @@ -4,11 +4,13 @@ import android.app.Activity import android.content.Intent import android.graphics.Color import android.os.Bundle +import android.util.Base64 import android.util.TypedValue import android.view.Gravity import android.view.WindowManager import android.widget.LinearLayout import android.widget.TextView +import org.json.JSONObject import java.text.SimpleDateFormat import java.util.Date import java.util.Locale @@ -18,16 +20,23 @@ import java.util.Locale * * adb shell am start -n com.example.deviceagent/.InfoActivity \ * --es name A08 --es ip 192.168.20.100 --es serial 192.168.20.100:5555 \ - * --es server http://192.168.20.220:18050 + * --es server http://192.168.20.220:18050 \ + * --es accounts_b64 # 可选:本机账号台账 * # 关闭:--es close 1 * - * 用途:人站在机架前,一眼看出"手头这台是平台上的哪一台";排查断联时不用连电脑。 + * 用途:人站在机架前,一眼看出"手头这台是平台上的哪一台"、**登着哪几个号**; + * 对号/排查断联时不用连电脑。 * 再次调用会覆盖内容(launchMode=singleTop + onNewIntent)。 + * + * `accounts_b64` 是可选的加字段(契约见平台 doc/DEVICE_AGENT.md §5.2.1 与本仓库 INTERFACE.md): + * **没有这个 extra = 这台设备在台账里没有账号**(不是"平台忘了给"),此时账号块整块不显示。 + * 解码用**标准 base64**(不是 URL_SAFE)+ UTF-8。 */ class InfoActivity : Activity() { private lateinit var tvName: TextView private lateinit var tvLines: TextView + private lateinit var tvAccounts: TextView private lateinit var tvTime: TextView override fun onCreate(savedInstanceState: Bundle?) { @@ -61,9 +70,19 @@ class InfoActivity : Activity() { } tvLines = TextView(this).apply { setTextColor(Color.parseColor("#FFD54F")) - setTextSize(TypedValue.COMPLEX_UNIT_SP, 26f) + setTextSize(TypedValue.COMPLEX_UNIT_SP, 24f) gravity = Gravity.CENTER - setPadding(0, 40, 0, 40) + setPadding(0, 24, 0, 20) + } + // 本机账号(台账推过来的):站在机器旁就能看出这台登着哪几个号 + // 字号/行距刻意比上面的设备信息小一档:一屏要塞下"设备信息 + 最多 4 个号", + // 26sp 时第 3 个号就被切掉了(实测 720x1650 上溢出) + tvAccounts = TextView(this).apply { + setTextColor(Color.parseColor("#80DEEA")) + setTextSize(TypedValue.COMPLEX_UNIT_SP, 22f) + gravity = Gravity.START + setPadding(0, 8, 0, 16) + visibility = android.view.View.GONE // 没有 accounts_b64 就不占位 } tvTime = TextView(this).apply { setTextColor(Color.parseColor("#9E9E9E")) @@ -72,6 +91,7 @@ class InfoActivity : Activity() { } root.addView(tvName) root.addView(tvLines) + root.addView(tvAccounts) root.addView(tvTime) setContentView(root) } @@ -90,5 +110,59 @@ class InfoActivity : Activity() { "Agent v${Prefs.agentVersion(this)}" ).filter { it.substringAfter(" ").trim().isNotEmpty() }.joinToString("\n") tvTime.text = "更新于 " + SimpleDateFormat("HH:mm:ss", Locale.CHINA).format(Date()) + renderAccounts(intent?.getStringExtra("accounts_b64").orEmpty()) + } + + /** + * 本机账号块。契约(平台 doc/DEVICE_AGENT.md §5.2.1): + * `{"v":1,"total":4,"shown":4,"truncated":false,"accounts":[{"name","douyin_id",…}]}` + * + * 三种情况要分清,别混成一种: + * · 没有 extra → 这台设备在台账里没登记账号 → 整块不显示(旧平台/旧行为) + * · 有但解不开 → 格式变了或串被截断 → 显示一行提示(**不要静默**,否则像"没账号") + * · 有且能解开 → 逐条显示;`truncated` 时补一行"还有 N 个" + */ + private fun renderAccounts(b64: String) { + if (b64.isEmpty()) { + tvAccounts.visibility = android.view.View.GONE + return + } + tvAccounts.visibility = android.view.View.VISIBLE + val obj = try { + JSONObject(String(Base64.decode(b64, Base64.DEFAULT), Charsets.UTF_8)) + } catch (e: Exception) { + // 解不开要说出来:不然跟"这台没账号"长得一样,平台侧还以为推成功了 + tvAccounts.setTextColor(Color.parseColor("#FF8A65")) + tvAccounts.text = "账号信息解析失败(v?):${e.javaClass.simpleName}" + return + } + tvAccounts.setTextColor(Color.parseColor("#80DEEA")) + val total = obj.optInt("total", 0) + val arr = obj.optJSONArray("accounts") ?: org.json.JSONArray() + if (arr.length() == 0) { + // 有块但 0 条:明确写出来,免得人以为"这页没渲染完" + tvAccounts.text = "本机账号:暂无(平台「账号」页还没登记这台设备)" + return + } + // 一屏放不下太多:**只显示前 4 个**,其余用"还有 N 个"交代 + // (人在机器旁要的是"这台是谁",不是把台账全量投到屏上) + val maxShow = 4 + val n = minOf(arr.length(), maxShow) + val sb = StringBuilder("本机账号 $total 个") + for (i in 0 until n) { + val a = arr.optJSONObject(i) ?: continue + val name = a.optString("name").ifEmpty { "(未命名)" } + val id = a.optString("douyin_id") + val marks = buildList { + if (a.optBoolean("sim_in_device")) add("卡") + if (a.optBoolean("can_post_video")) add("发") + } + sb.append("\n").append(i + 1).append(". ").append(name) + if (id.isNotEmpty()) sb.append("\n ").append(id) + if (marks.isNotEmpty()) sb.append(" ").append(marks.joinToString("·")) + } + val rest = total - n + if (rest > 0) sb.append("\n…还有 ").append(rest).append(" 个,见平台「账号」页") + tvAccounts.text = sb.toString() } }