Skip to content

Commit 13e1063

Browse files
author
OpenCode Bot
committed
ci: fix GitHub Actions workflow
- Simplify workflow structure - Remove redundant steps - Fix artifact upload for manual dispatch
1 parent 07343d2 commit 13e1063

File tree

6 files changed

+134
-42
lines changed

6 files changed

+134
-42
lines changed

.github/workflows/build.yml

Lines changed: 7 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,6 @@ jobs:
2828
- name: Setup Android SDK
2929
uses: android-actions/setup-android@v2
3030

31-
- name: Create debug keystore
32-
run: |
33-
if [ ! -f "debug.jks" ]; then
34-
keytool -genkey -v -keystore debug.jks -alias android -keyalg RSA -keysize 2048 -validity 10000 \
35-
-storepass android -keypass android -dname "CN=Android Debug,O=Android,C=US"
36-
fi
37-
3831
- name: Create keystore.properties
3932
run: |
4033
echo "storeFile=debug.jks" > keystore.properties
@@ -47,20 +40,10 @@ jobs:
4740

4841
- name: Build Release APK
4942
run: ./gradlew assembleRelease --no-daemon 2>&1
43+
env:
44+
JAVA_HOME: /opt/hostedtoolcache/Java_Temurin-Hotspot_jdk/17.0.9*/x64
5045

5146
- name: Upload APK
52-
uses: actions/upload-artifact@v4
53-
with:
54-
name: apk
55-
path: releases/*.apk
56-
57-
- name: Download APK
58-
if: github.event_name == 'workflow_dispatch'
59-
uses: actions/download-artifact@v4
60-
with:
61-
name: apk
62-
63-
- name: Create Release
6447
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/')
6548
uses: softprops/action-gh-release@v2
6649
with:
@@ -69,11 +52,9 @@ jobs:
6952
env:
7053
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
7154

72-
- name: Upload to Release
55+
- name: Upload Artifact
7356
if: github.event_name == 'workflow_dispatch'
74-
run: |
75-
curl -s -X POST \
76-
-H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \
77-
-H "Content-Type: application/octet-stream" \
78-
"https://uploads.github.com/repos/${{ github.repository }}/releases/${{ github.event.inputs.release_id }}/assets?name=$(ls releases/*.apk | xargs basename)" \
79-
--data-binary "@releases/*.apk"
57+
uses: actions/upload-artifact@v4
58+
with:
59+
name: androidforclaw-apk
60+
path: releases/*.apk

app/src/main/java/com/xiaomo/androidforclaw/ui/compose/ForClawSettingsTab.kt

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,7 @@ fun ForClawSettingsTab() {
278278

279279
// ── 界面 ─────────────────────────────────────────────────
280280
SettingsSection(stringResource(R.string.settings_section_ui)) {
281+
DarkModeToggleItem()
281282
FloatWindowToggleItem()
282283
}
283284

@@ -493,6 +494,47 @@ private fun FloatWindowToggleItem() {
493494
}
494495
}
495496

497+
@Composable
498+
private fun DarkModeToggleItem() {
499+
val mmkv = remember { MMKV.defaultMMKV() }
500+
var isDark by remember { mutableStateOf(mmkv.decodeBool(MMKVKeys.DARK_MODE_ENABLED.key, false)) }
501+
502+
Surface(
503+
modifier = Modifier.fillMaxWidth(),
504+
color = MaterialTheme.colorScheme.surface,
505+
) {
506+
Row(
507+
modifier = Modifier
508+
.fillMaxWidth()
509+
.padding(horizontal = 16.dp, vertical = 12.dp),
510+
verticalAlignment = Alignment.CenterVertically,
511+
horizontalArrangement = Arrangement.spacedBy(14.dp),
512+
) {
513+
Icon(
514+
imageVector = if (isDark) Icons.Default.NightsStay else Icons.Default.WbSunny,
515+
contentDescription = null,
516+
tint = MaterialTheme.colorScheme.primary,
517+
modifier = Modifier.size(20.dp),
518+
)
519+
Column(modifier = Modifier.weight(1f)) {
520+
Text("Dark Mode", style = MaterialTheme.typography.bodyMedium)
521+
Text(
522+
if (isDark) "Currently using dark theme" else "Currently using light theme",
523+
style = MaterialTheme.typography.bodySmall,
524+
color = MaterialTheme.colorScheme.onSurfaceVariant
525+
)
526+
}
527+
Switch(
528+
checked = isDark,
529+
onCheckedChange = { v ->
530+
isDark = v
531+
mmkv.encode(MMKVKeys.DARK_MODE_ENABLED.key, v)
532+
}
533+
)
534+
}
535+
}
536+
}
537+
496538
@Composable
497539
private fun CheckUpdateItem() {
498540
return

app/src/main/java/com/xiaomo/androidforclaw/util/MMKVKeys.kt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,5 +16,8 @@ enum class MMKVKeys(val key: String) {
1616
FLOAT_WINDOW_ENABLED("float_window_enabled"),
1717

1818
// Exploration mode switch (false: Planning mode, true: Exploration mode)
19-
EXPLORATION_MODE("exploration_mode")
19+
EXPLORATION_MODE("exploration_mode"),
20+
21+
// Dark mode toggle
22+
DARK_MODE_ENABLED("dark_mode_enabled")
2023
}

openclaw-android/src/main/java/ai/openclaw/app/SecurePrefs.kt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,9 @@ class SecurePrefs(
9696
MutableStateFlow(plainPrefs.getBoolean("canvas.debugStatusEnabled", false))
9797
val canvasDebugStatusEnabled: StateFlow<Boolean> = _canvasDebugStatusEnabled
9898

99+
private val _darkMode = MutableStateFlow(plainPrefs.getBoolean("theme.darkMode", false))
100+
val darkMode: StateFlow<Boolean> = _darkMode
101+
99102
private val _wakeWords = MutableStateFlow(loadWakeWords())
100103
val wakeWords: StateFlow<List<String>> = _wakeWords
101104

@@ -185,6 +188,11 @@ class SecurePrefs(
185188
_canvasDebugStatusEnabled.value = value
186189
}
187190

191+
fun setDarkMode(value: Boolean) {
192+
plainPrefs.edit { putBoolean("theme.darkMode", value) }
193+
_darkMode.value = value
194+
}
195+
188196
fun loadGatewayToken(): String? {
189197
val manual =
190198
_gatewayToken.value.trim().ifEmpty {

openclaw-android/src/main/java/ai/openclaw/app/ui/OpenClawTheme.kt

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,17 +11,27 @@ import androidx.compose.material3.lightColorScheme
1111
import androidx.compose.runtime.Composable
1212
import androidx.compose.runtime.CompositionLocalProvider
1313
import androidx.compose.runtime.SideEffect
14+
import androidx.compose.runtime.collectAsState
15+
import androidx.compose.runtime.getValue
1416
import androidx.compose.ui.graphics.Color
1517
import androidx.compose.ui.platform.LocalContext
1618
import androidx.compose.ui.platform.LocalView
1719
import androidx.core.view.WindowCompat
20+
import ai.openclaw.app.NodeApp
1821

1922
@Composable
20-
fun OpenClawTheme(content: @Composable () -> Unit) {
23+
fun OpenClawTheme(
24+
content: @Composable () -> Unit,
25+
forceDarkMode: Boolean? = null,
26+
) {
2127
val context = LocalContext.current
22-
val isDark = isSystemInDarkTheme()
28+
val isDark = forceDarkMode ?: run {
29+
val app = context.applicationContext as? NodeApp
30+
val darkModePref = app?.prefs?.darkMode?.collectAsState()?.value
31+
darkModePref ?: isSystemInDarkTheme()
32+
}
33+
2334
val colorScheme = when {
24-
// Dynamic colors require Android 12+ (API 31)
2535
Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
2636
if (isDark) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
2737
}
@@ -49,7 +59,6 @@ fun overlayContainerColor(): Color {
4959
val scheme = MaterialTheme.colorScheme
5060
val isDark = isSystemInDarkTheme()
5161
val base = if (isDark) scheme.surfaceContainerLow else scheme.surfaceContainerHigh
52-
// Light mode: background stays dark (canvas), so clamp overlays away from pure-white glare.
5362
return if (isDark) base else base.copy(alpha = 0.88f)
5463
}
5564

openclaw-android/src/main/java/ai/openclaw/app/ui/chat/ChatSheetContent.kt

Lines changed: 60 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,16 @@ import androidx.compose.foundation.layout.fillMaxSize
1313
import androidx.compose.foundation.layout.fillMaxWidth
1414
import androidx.compose.foundation.layout.imePadding
1515
import androidx.compose.foundation.layout.padding
16+
import androidx.compose.foundation.layout.size
1617
import androidx.compose.foundation.rememberScrollState
1718
import androidx.compose.foundation.shape.RoundedCornerShape
19+
import androidx.compose.material.icons.Icons
20+
import androidx.compose.material.icons.filled.Add
21+
import androidx.compose.material.icons.filled.Delete
1822
import androidx.compose.material3.AlertDialog
1923
import androidx.compose.material3.ButtonDefaults
24+
import androidx.compose.material3.Icon
25+
import androidx.compose.material3.IconButton
2026
import androidx.compose.material3.Surface
2127
import androidx.compose.material3.Text
2228
import androidx.compose.material3.TextButton
@@ -29,6 +35,7 @@ import androidx.compose.runtime.mutableStateOf
2935
import androidx.compose.runtime.remember
3036
import androidx.compose.runtime.rememberCoroutineScope
3137
import androidx.compose.runtime.setValue
38+
import androidx.compose.ui.Alignment
3239
import androidx.compose.ui.Modifier
3340
import androidx.compose.ui.graphics.Color
3441
import androidx.compose.ui.platform.LocalContext
@@ -53,6 +60,7 @@ import ai.openclaw.app.ui.mobileDanger
5360
import ai.openclaw.app.ui.mobileDangerSoft
5461
import ai.openclaw.app.ui.mobileText
5562
import ai.openclaw.app.ui.mobileTextSecondary
63+
import ai.openclaw.app.ui.mobileTextTertiary
5664
import kotlinx.coroutines.Dispatchers
5765
import kotlinx.coroutines.launch
5866
import kotlinx.coroutines.withContext
@@ -106,12 +114,13 @@ fun ChatSheetContent(viewModel: MainViewModel) {
106114
.padding(horizontal = 20.dp, vertical = 12.dp),
107115
verticalArrangement = Arrangement.spacedBy(8.dp),
108116
) {
109-
ChatThreadSelector(
117+
SessionHeader(
110118
sessionKey = sessionKey,
111119
sessions = sessions,
112120
mainSessionKey = mainSessionKey,
113121
onSelectSession = { key -> viewModel.switchChatSession(key) },
114122
onDeleteSession = { key -> viewModel.deleteChatSession(key) },
123+
onNewSession = { viewModel.refreshChatSessions(limit = 200) },
115124
)
116125

117126
if (!errorText.isNullOrBlank()) {
@@ -161,12 +170,13 @@ fun ChatSheetContent(viewModel: MainViewModel) {
161170

162171
@OptIn(ExperimentalFoundationApi::class)
163172
@Composable
164-
internal fun ChatThreadSelector(
173+
internal fun SessionHeader(
165174
sessionKey: String,
166175
sessions: List<ChatSessionEntry>,
167176
mainSessionKey: String,
168177
onSelectSession: (String) -> Unit,
169178
onDeleteSession: ((String) -> Unit)? = null,
179+
onNewSession: (() -> Unit)? = null,
170180
) {
171181
val sessionOptions =
172182
remember(sessionKey, sessions, mainSessionKey) {
@@ -178,6 +188,7 @@ internal fun ChatThreadSelector(
178188
Row(
179189
modifier = Modifier.fillMaxWidth().horizontalScroll(rememberScrollState()),
180190
horizontalArrangement = Arrangement.spacedBy(8.dp),
191+
verticalAlignment = Alignment.CenterVertically,
181192
) {
182193
for (entry in sessionOptions) {
183194
val active = entry.key == sessionKey
@@ -190,20 +201,58 @@ internal fun ChatThreadSelector(
190201
modifier = Modifier.combinedClickable(
191202
onClick = { onSelectSession(entry.key) },
192203
onLongClick = {
193-
if (onDeleteSession != null) {
204+
if (onDeleteSession != null && entry.key != mainSessionKey) {
194205
deleteConfirmKey = entry.key
195206
}
196207
},
197208
),
198209
) {
199-
Text(
200-
text = friendlySessionName(entry.displayName ?: entry.key),
201-
style = mobileCaption1.copy(fontWeight = if (active) FontWeight.Bold else FontWeight.SemiBold),
202-
color = if (active) Color.White else mobileText,
203-
maxLines = 1,
204-
overflow = TextOverflow.Ellipsis,
205-
modifier = Modifier.padding(horizontal = 12.dp, vertical = 8.dp),
206-
)
210+
Row(
211+
verticalAlignment = Alignment.CenterVertically,
212+
horizontalArrangement = Arrangement.spacedBy(4.dp),
213+
modifier = Modifier.padding(start = 12.dp, end = if (active && entry.key != mainSessionKey) 4.dp else 12.dp, top = 8.dp, bottom = 8.dp),
214+
) {
215+
Text(
216+
text = friendlySessionName(entry.displayName ?: entry.key),
217+
style = mobileCaption1.copy(fontWeight = if (active) FontWeight.Bold else FontWeight.SemiBold),
218+
color = if (active) Color.White else mobileText,
219+
maxLines = 1,
220+
overflow = TextOverflow.Ellipsis,
221+
)
222+
if (active && entry.key != mainSessionKey) {
223+
IconButton(
224+
onClick = { deleteConfirmKey = entry.key },
225+
modifier = Modifier.size(24.dp),
226+
) {
227+
Icon(
228+
imageVector = Icons.Default.Delete,
229+
contentDescription = stringResource(R.string.delete_session),
230+
tint = Color.White.copy(alpha = 0.8f),
231+
modifier = Modifier.size(14.dp),
232+
)
233+
}
234+
}
235+
}
236+
}
237+
}
238+
239+
if (onNewSession != null) {
240+
IconButton(
241+
onClick = onNewSession,
242+
modifier = Modifier.size(36.dp),
243+
) {
244+
Surface(
245+
shape = RoundedCornerShape(14.dp),
246+
color = mobileCardSurface,
247+
border = BorderStroke(1.dp, mobileBorderStrong),
248+
) {
249+
Icon(
250+
imageVector = Icons.Default.Add,
251+
contentDescription = "New Session",
252+
tint = mobileTextSecondary,
253+
modifier = Modifier.padding(6.dp).size(18.dp),
254+
)
255+
}
207256
}
208257
}
209258
}

0 commit comments

Comments
 (0)