@@ -2,12 +2,12 @@ package ai.muse.gadgeteverywhere
22
33import android.app.Activity
44import android.content.pm.PackageManager
5+ import android.graphics.Typeface
56import android.os.Bundle
67import android.view.Gravity
78import android.view.View
89import android.widget.Button
910import android.widget.LinearLayout
10- import android.widget.ScrollView
1111import android.widget.TextView
1212import com.chaquo.python.Python
1313import java.io.File
@@ -18,44 +18,103 @@ class PairActivity : Activity() {
1818 private lateinit var status: TextView
1919 private lateinit var startButton: Button
2020 private lateinit var cancelButton: Button
21+ private lateinit var tokenValue: TextView
22+ private lateinit var tokenDot: View
2123 private var transport: BleTransport ? = null
2224 @Volatile private var pairing = false
2325
2426 override fun onCreate (savedInstanceState : Bundle ? ) {
2527 super .onCreate(savedInstanceState)
28+ val (scroll, content) = Ui .screenFrame(this )
2629
27- status = TextView (this ).apply {
28- textSize = 20f
29- setPadding(48 , 48 , 48 , 48 )
30+ content.addView(
31+ TextView (this ).apply {
32+ text = getString(R .string.pair_title)
33+ textSize = 28f
34+ typeface = Typeface .create(Typeface .DEFAULT , Typeface .BOLD )
35+ setTextColor(Ui .run { brand(R .color.ink) })
36+ setPadding(0 , 0 , 0 , Ui .run { this @PairActivity.dp(16 ) })
37+ },
38+ )
39+
40+ // Token state card: users see BEFORE tapping whether the import
41+ // file is staged, instead of learning it from an error.
42+ val tokenCard = Ui .card(this )
43+ val (tokenRow, valueView) = Ui .statusRow(
44+ this , getString(R .string.pair_token_label),
45+ getString(R .string.pair_token_missing),
46+ Ui .run { brand(R .color.warn) },
47+ )
48+ tokenDot = tokenRow.getChildAt(0 )
49+ tokenValue = valueView
50+ tokenCard.addView(tokenRow)
51+ content.addView(tokenCard)
52+ updateTokenRow()
53+
54+ val statusCard = Ui .card(this ).apply {
55+ layoutParams = LinearLayout .LayoutParams (
56+ LinearLayout .LayoutParams .MATCH_PARENT ,
57+ LinearLayout .LayoutParams .WRAP_CONTENT ,
58+ ).apply { topMargin = Ui .run { this @PairActivity.dp(12 ) } }
59+ }
60+ status = Ui .bodyText(this ).apply {
3061 text = getString(R .string.pair_ready)
3162 }
32- startButton = Button (this ).apply {
33- text = getString(R .string.pair_open_setup)
63+ statusCard.addView(status)
64+ content.addView(statusCard)
65+
66+ startButton = Ui .primaryButton(this , getString(R .string.pair_open_setup)).apply {
3467 setOnClickListener { startPairing() }
3568 }
36- cancelButton = Button (this ).apply {
37- text = getString(R .string.pair_cancel)
69+ cancelButton = Ui .secondaryButton(this , getString(R .string.pair_cancel)).apply {
3870 setOnClickListener { finish() }
3971 }
40- val buttons = LinearLayout (this ).apply {
41- orientation = LinearLayout .HORIZONTAL
42- gravity = Gravity .CENTER
43- addView(startButton)
44- addView(cancelButton)
45- }
46- setContentView(
72+ content.addView(
4773 LinearLayout (this ).apply {
48- orientation = LinearLayout .VERTICAL
49- // See ProbeActivity: stay below the status bar on 35+.
50- fitsSystemWindows = true
51- addView(buttons)
52- addView(ScrollView (this @PairActivity).apply { addView(status) })
74+ orientation = LinearLayout .HORIZONTAL
75+ gravity = Gravity .CENTER
76+ layoutParams = LinearLayout .LayoutParams (
77+ LinearLayout .LayoutParams .MATCH_PARENT ,
78+ LinearLayout .LayoutParams .WRAP_CONTENT ,
79+ ).apply { topMargin = Ui .run { this @PairActivity.dp(18 ) } }
80+ val gap = Ui .run { this @PairActivity.dp(10 ) }
81+ listOf (startButton, cancelButton).forEach {
82+ it.layoutParams = LinearLayout .LayoutParams (
83+ LinearLayout .LayoutParams .WRAP_CONTENT ,
84+ LinearLayout .LayoutParams .WRAP_CONTENT ,
85+ ).apply { marginEnd = gap }
86+ addView(it)
87+ }
5388 },
5489 )
90+
91+ setContentView(scroll)
5592 startButton.requestFocus()
5693 ensurePermissions()
5794 }
5895
96+ /* * Refresh the token row from the import file. Cheap; call on show
97+ * and after any import attempt (users stage the file between taps). */
98+ private fun updateTokenRow () {
99+ val staged = try {
100+ val dir = getExternalFilesDir(" import" )
101+ dir != null && File (dir, " muse_token.txt" ).let { it.exists() && it.readText().isNotBlank() }
102+ } catch (_: Exception ) {
103+ false
104+ }
105+ tokenValue.text = getString(
106+ if (staged) R .string.pair_token_staged else R .string.pair_token_missing,
107+ )
108+ tokenDot.background = android.graphics.drawable.GradientDrawable ().apply {
109+ shape = android.graphics.drawable.GradientDrawable .OVAL
110+ setColor(
111+ Ui .run {
112+ brand(if (staged) R .color.ok else R .color.warn)
113+ },
114+ )
115+ }
116+ }
117+
59118 private fun ensurePermissions () {
60119 val missing = Startup .missingBlePermissions(this )
61120 if (missing.isNotEmpty()) {
@@ -139,6 +198,7 @@ class PairActivity : Activity() {
139198 setStatus(getString(R .string.pair_token_saved))
140199 }
141200 }
201+ runOnUiThread { updateTokenRow() }
142202 if (sdkToken == null ) {
143203 setStatus(getString(R .string.pair_no_token, importFile.absolutePath))
144204 pairing = false
0 commit comments