Skip to content

Commit ef703ce

Browse files
TimeToBuildBobTimeToBuildBob
andauthored
feat(auth): add API key settings screen (#156)
* feat(auth): add API key settings screen and config manager Implements the Android-side UI for API authentication, now that aw-server-rust#608 landed and embedded config loads from app data dir. - ConfigManager.kt: reads/writes [auth].api_key in config.toml stored at context.filesDir; uses UUID-based key generation; pure string TOML manipulation, no extra library dependency - AuthSettingsActivity.kt: shows current API key, copy-to-clipboard, regenerate, and enable/disable toggle; prompts user to restart app after changes (server rereads config at startup) - activity_auth_settings.xml: layout for the settings screen - AndroidManifest.xml: registers AuthSettingsActivity - MainActivity.kt: wires the previously-stub settings button to open AuthSettingsActivity instead of a Snackbar - ConfigManagerTest.kt: 11 JVM unit tests covering parse/write logic including roundtrips and the [auth]-without-key edge case Closes #145 (partial — "Open in browser" URL token passing and server restart API are follow-up work) * fix(auth): address Greptile review findings - ConfigManager.writeApiKey: scope api_key detection to [auth] section only, preventing cross-section corruption when other TOML sections also contain an api_key field - AuthSettingsActivity: show btnCopy immediately after enable via switch or regenerate button (was permanently hidden after toggle) - AuthSettingsActivity: guard switch.isChecked setter with isUpdatingSwitch flag to prevent double-toast when regenerating key - Layout: replace deprecated Switch with SwitchMaterial - ConfigManagerTest: add cross-section api_key isolation regression test * fix(auth): fully scope writeApiKey operations to [auth] section The previous fix scoped the apiKeyLinePresent check but still ran replaceFirst on the full file content, which would still clobber an api_key field in an earlier TOML section. Now: extract the [auth] section, perform all string operations within it, then substitute the modified section back into the full content. Test helper mirrors the fix. * fix(auth): suppress clipboard preview for API key on Android 13+ Set ClipDescription.EXTRA_IS_SENSITIVE so the system does not show a plaintext preview toast of the API key when copying on Android 13+. * fix(auth): use atomic write for config.toml via temp-file rename * fix(ci): fallback versionCode for fork PRs * fix(ci): upgrade artifact actions to v4 * fix(ci): use modern Android SDK tools path * fix(ci): skip signed artifacts on fork PRs * fix(auth): propagate API key write failures * fix(ci): repair Linux emulator startup * fix(ci): handle emulator without kvm acceleration * fix(ci): make E2E screenshot best effort * fix(auth): insert API key in newline-less auth section * ci: re-trigger Test job (stuck pending for 14h) * fix(ci): remove jniLibs dependency from Test job Unit tests (./gradlew test) run on the JVM and don't require native .so files. The jniLibs cache restore was failing because the cache was saved by a ubicloud runner but the Test job runs on a GitHub-hosted runner (different cache backend). Remove NDK setup and jniLibs cache restore from the Test job, and drop the needs:[build-rust] dependency since there are no longer any shared artifacts. * fix(ci): restore NDK in Test job — required at Gradle config time * fix(auth): move ConfigManager I/O off UI thread; fix spurious toast - Add kotlinx-coroutines-android + lifecycle-runtime-ktx deps - Wrap all ConfigManager calls in lifecycleScope.launch { withContext(Dispatchers.IO) } so file reads/writes never block the main thread - Rename refreshUI() → scheduleRefreshUI() (coroutine launcher) + applyAuthToUI(auth) (pure UI update) to make the async boundary explicit - Fix spurious 'Setting saved' toast: only show when the switch ON branch actually wrote a new key; suppress when auth was already enabled --------- Co-authored-by: TimeToBuildBob <bob@gptme.org>
1 parent 00b06c4 commit ef703ce

8 files changed

Lines changed: 583 additions & 44 deletions

File tree

.github/workflows/build.yml

Lines changed: 57 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -99,16 +99,36 @@ jobs:
9999
submodules: 'recursive'
100100

101101

102+
- name: Detect Fastlane secrets
103+
id: fastlane_secrets
104+
env:
105+
KEY_FASTLANE_API: ${{ secrets.KEY_FASTLANE_API }}
106+
run: |
107+
if [ -n "$KEY_FASTLANE_API" ]; then
108+
echo "available=true" >> "$GITHUB_OUTPUT"
109+
else
110+
echo "available=false" >> "$GITHUB_OUTPUT"
111+
fi
112+
113+
- name: Require Fastlane secrets outside pull requests
114+
if: steps.fastlane_secrets.outputs.available != 'true' && github.event_name != 'pull_request'
115+
run: |
116+
echo "::error::KEY_FASTLANE_API is required for non-PR builds"
117+
exit 1
118+
102119
# Ruby & Fastlane
103120
# version set by .ruby-version
104121
- name: Set up Ruby and install fastlane
122+
if: steps.fastlane_secrets.outputs.available == 'true'
105123
uses: ruby/setup-ruby@v1
106124
with:
107125
bundler-cache: true
108126

109127
# Needed for `fastlane update_version`
110128
- uses: adnsio/setup-age-action@v1.2.0
129+
if: steps.fastlane_secrets.outputs.available == 'true'
111130
- name: Load Fastlane secrets
131+
if: steps.fastlane_secrets.outputs.available == 'true'
112132
env:
113133
KEY_FASTLANE_API: ${{ secrets.KEY_FASTLANE_API }}
114134
run: |
@@ -121,12 +141,18 @@ jobs:
121141
# Retry this, in case there are concurrent jobs, which may lead to the error:
122142
# "Google Api Error: Invalid request - This Edit has been deleted."
123143
- name: Update versionCode
144+
if: steps.fastlane_secrets.outputs.available == 'true'
124145
uses: Wandalen/wretry.action@master
125146
with:
126147
command: bundle exec fastlane update_version
127148
attempt_limit: 3
128149
attempt_delay: 20000
129150

151+
- name: Use checked-in versionCode fallback
152+
if: steps.fastlane_secrets.outputs.available != 'true'
153+
run: |
154+
echo "KEY_FASTLANE_API unavailable for pull_request build; using checked-in versionCode."
155+
130156
- name: Output versionCode
131157
id: versionCode
132158
run: |
@@ -136,6 +162,9 @@ jobs:
136162
name: Build ${{ matrix.type }}
137163
runs-on: ubicloud-standard-4
138164
needs: [build-rust, get-versionCode]
165+
# Fork PRs cannot access signing secrets; build/test coverage still runs in
166+
# build-rust, test, and test-e2e.
167+
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository
139168
strategy:
140169
fail-fast: true
141170
matrix:
@@ -222,15 +251,14 @@ jobs:
222251
make dist/aw-android.${{ matrix.type }}
223252
224253
- name: Upload
225-
uses: actions/upload-artifact@v3
254+
uses: actions/upload-artifact@v4
226255
with:
227-
name: aw-android
256+
name: aw-android-${{ matrix.type }}
228257
path: dist/aw-android*.${{ matrix.type }}
229258

230259
test:
231260
name: Test
232261
runs-on: ubuntu-22.04
233-
needs: [build-rust]
234262
env:
235263
SUPPLY_TRACK: production # used by fastlane to determine track to publish to
236264

@@ -239,40 +267,20 @@ jobs:
239267
with:
240268
submodules: 'recursive'
241269

242-
- name: Set RELEASE
243-
run: |
244-
echo "RELEASE=${{ startsWith(github.ref_name, 'v') }}" >> $GITHUB_ENV
245-
246270
- name: Set up JDK
247271
uses: actions/setup-java@v1
248272
with:
249273
java-version: ${{ env.JAVA_VERSION }}
250274

251-
# Android SDK & NDK
275+
# Android SDK & NDK (NDK required at Gradle configuration time even for JVM unit tests)
252276
- name: Set up Android SDK
253277
uses: android-actions/setup-android@v2
254278
- name: Set up Android NDK
255279
run: |
256280
sdkmanager "ndk;${{ env.NDK_VERSION }}"
257281
ANDROID_NDK_HOME="$ANDROID_SDK_ROOT/ndk/${{ env.NDK_VERSION }}"
258-
ls $ANDROID_NDK_HOME
259282
echo "ANDROID_NDK_HOME=$ANDROID_NDK_HOME" >> $GITHUB_ENV
260283
261-
262-
# Restores jniLibs from cache
263-
# `actions/cache/restore` only restores, without saving back in a post-hook
264-
- uses: actions/cache/restore@v3
265-
id: cache-jniLibs
266-
env:
267-
cache-name: jniLibs
268-
with:
269-
path: mobile/src/main/jniLibs/
270-
key: ${{ env.cache-name }}-release-${{ env.RELEASE }}-ndk-${{ env.NDK_VERSION }}-${{ hashFiles('.git/modules/aw-server-rust/HEAD') }}
271-
fail-on-cache-miss: true
272-
- name: Check that jniLibs present
273-
run: |
274-
test -e mobile/src/main/jniLibs/x86_64/libaw_server.so
275-
276284
# Test
277285
- name: Test
278286
run: |
@@ -328,15 +336,18 @@ jobs:
328336
if: runner.os == 'macOS'
329337
run: brew install intel-haxm
330338

339+
- name: Set up Android SDK
340+
uses: android-actions/setup-android@v2
341+
331342
# # # Below code is majorly from https://github.com/actions/runner-images/issues/6152#issuecomment-1243718140
332343
- name: Create Android emulator
333344
run: |
334345
# Install AVD files
335-
echo "y" | $ANDROID_HOME/tools/bin/sdkmanager --install 'system-images;android-'$MATRIX_E_SDK';default;x86_64'
336-
echo "y" | $ANDROID_HOME/tools/bin/sdkmanager --licenses
346+
echo "y" | sdkmanager --install 'system-images;android-'$MATRIX_E_SDK';default;x86_64'
347+
echo "y" | sdkmanager --licenses
337348
338349
# Create emulator
339-
$ANDROID_HOME/tools/bin/avdmanager create avd -n $MATRIX_AVD -d pixel --package 'system-images;android-'$MATRIX_E_SDK';default;x86_64'
350+
avdmanager create avd -n $MATRIX_AVD -d pixel --package 'system-images;android-'$MATRIX_E_SDK';default;x86_64'
340351
$ANDROID_HOME/emulator/emulator -list-avds
341352
if false; then
342353
emulator_config=~/.android/avd/$MATRIX_AVD.avd/config.ini
@@ -369,14 +380,19 @@ jobs:
369380
run: |
370381
echo "Starting emulator and waiting for boot to complete...."
371382
ls -la $ANDROID_HOME/emulator
372-
$ANDROID_HOME/tools/emulator --accel-check # check for hardware acceleration
373-
nohup $ANDROID_HOME/tools/emulator -avd $MATRIX_AVD -gpu host -no-audio -no-boot-anim -camera-back none -camera-front none -qemu -m 2048 2>&1 &
383+
emulator_args=(-avd "$MATRIX_AVD" -gpu swiftshader_indirect -no-window -no-audio -no-boot-anim -camera-back none -camera-front none)
384+
if $ANDROID_HOME/emulator/emulator -accel-check; then
385+
echo "Hardware acceleration available."
386+
else
387+
echo "Hardware acceleration unavailable; using software acceleration."
388+
emulator_args+=(-accel off)
389+
fi
390+
nohup $ANDROID_HOME/emulator/emulator "${emulator_args[@]}" -qemu -m 2048 2>&1 &
374391
$ANDROID_HOME/platform-tools/adb wait-for-device shell 'while [[ -z $(getprop sys.boot_completed | tr -d '\r') ]]; do echo "wait..."; sleep 1; done; input keyevent 82'
375392
echo "Emulator has finished booting"
376393
$ANDROID_HOME/platform-tools/adb devices
377394
sleep 30
378395
mkdir -p screenshots
379-
screencapture screenshots/screenshot-$SUFFIX.jpg
380396
$ANDROID_HOME/platform-tools/adb exec-out screencap -p > screenshots/emulator-$SUFFIX.png
381397
382398
# # # Have to re-setup everything since we need to run emulator for faster performance on masOS ? Other os'es emulator will not startup ?
@@ -422,8 +438,6 @@ jobs:
422438
java-version: ${{ env.JAVA_VERSION }}
423439

424440
# Android SDK & NDK
425-
- name: Set up Android SDK
426-
uses: android-actions/setup-android@v2
427441
- name: Set up Android NDK
428442
run: |
429443
sdkmanager "ndk;${{ env.NDK_VERSION }}"
@@ -449,15 +463,15 @@ jobs:
449463
env:
450464
SUFFIX: ${{ matrix.android_avd }}-eAPI-${{ matrix.android_emu_version }}
451465
run: |
452-
adb shell monkey -p net.activitywatch.android.debug 1
466+
adb shell monkey -p net.activitywatch.android.debug 1 || echo "App launch failed; capturing current emulator screen."
453467
sleep 10
454-
screencapture screenshots/pscreenshot-$SUFFIX.jpg
455-
$ANDROID_HOME/platform-tools/adb exec-out screencap -p > screenshots/pemulator-$SUFFIX.png
468+
mkdir -p screenshots
469+
$ANDROID_HOME/platform-tools/adb exec-out screencap -p > screenshots/pemulator-$SUFFIX.png || echo "Screenshot capture failed."
456470
ls -alh screenshots/
457471
458472
- name: Upload logcat
459473
if: ${{ success() || steps.test.conclusion == 'failure'}}
460-
uses: actions/upload-artifact@v3
474+
uses: actions/upload-artifact@v4
461475
with:
462476
name: logcat
463477
# mobile\build\outputs\connected_android_test_additional_output\debugAndroidTest\connected\Pixel_XL_API_32(AVD) - 12\ScreenshotTest_saveDeviceScreenBitmap.png
@@ -473,7 +487,7 @@ jobs:
473487
# path: ./*.mov # out.mov
474488

475489
- name: Upload screenshots
476-
uses: actions/upload-artifact@v3
490+
uses: actions/upload-artifact@v4
477491
if: ${{ success() || steps.test.conclusion == 'failure'}}
478492
with:
479493
name: screenshots
@@ -500,10 +514,11 @@ jobs:
500514
- uses: actions/checkout@v3
501515

502516
- name: Download APK & AAB
503-
uses: actions/download-artifact@v3
517+
uses: actions/download-artifact@v4
504518
with:
505-
name: aw-android
519+
pattern: aw-android-*
506520
path: dist
521+
merge-multiple: true
507522

508523
- name: Display structure of downloaded files
509524
working-directory: dist
@@ -556,10 +571,11 @@ jobs:
556571

557572
# Will download all artifacts to path
558573
- name: Download release APK & AAB
559-
uses: actions/download-artifact@v3
574+
uses: actions/download-artifact@v4
560575
with:
561-
name: aw-android
576+
pattern: aw-android-*
562577
path: dist
578+
merge-multiple: true
563579

564580
- name: Display structure of downloaded files
565581
working-directory: dist
@@ -581,4 +597,3 @@ jobs:
581597
dist/*.apk
582598
dist/*.aab
583599
# body_path: dist/release_notes/release_notes.md
584-

mobile/build.gradle

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,8 @@ dependencies {
7878
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
7979
implementation 'androidx.recyclerview:recyclerview:1.2.1'
8080
implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
81+
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.6.2'
82+
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3'
8183
implementation 'androidx.annotation:annotation:1.5.0'
8284

8385
implementation 'com.google.android.material:material:1.7.0'

mobile/src/main/AndroidManifest.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,12 @@
4848
android:theme="@style/AppTheme.NoActionBar"
4949
android:exported="true"/>
5050

51+
<activity
52+
android:name=".AuthSettingsActivity"
53+
android:screenOrientation="portrait"
54+
android:configChanges="orientation"
55+
android:exported="false"/>
56+
5157
<receiver
5258
android:name=".watcher.AlarmReceiver"
5359
android:enabled="true"

0 commit comments

Comments
 (0)