* Add missing Android node command families (parity gap)
- Implement Notifications, System, Photos, Contacts, Calendar, and Motion command families.
- Add OpenClawNotificationListenerService and NotificationManager for notification support.
- Implement robust runtime permission checks and structured error handling in all handlers.
- Wire new handlers into NodeRuntime, InvokeDispatcher, and ConnectionManager.
- Conditionally advertise capabilities to the gateway based on granted permissions.
- Add unit tests for command routing and handler logic.
- Update AndroidManifest.xml with necessary permissions and service declarations.
Co-authored-by: yuga-hashimoto <74749461+yuga-hashimoto@users.noreply.github.com>
* Refine Android node command families based on PR feedback
- Implement actual Notification actions (dismiss, open) using NotificationListenerService.
- Correct permission gating for Notifications (use Listener Service check instead of POST_NOTIFICATIONS).
- Add runtime permission check for system.notify on Android 13+.
- Improve Calendar event insertion by querying for a writable calendar instead of hardcoding ID 1.
- Update capability advertisement in ConnectionManager to reflect true availability.
- Enhance PhotosHandler and MotionHandler with more complete implementations.
- Update unit tests to match refined permission and action logic.
Co-authored-by: yuga-hashimoto <74749461+yuga-hashimoto@users.noreply.github.com>
* Finalize Android node command families with refined notification support
- Implement dismiss/open notification actions using NotificationListenerService.
- Return NOT_IMPLEMENTED for reply notification action as requested.
- Fix permission gating for notifications (use listener service status).
- Add runtime permission check for system notifications on Android 13+.
- Improve calendar event creation with dynamic writable calendar selection.
- Refine capability advertising in ConnectionManager.
- Clean up unused imports and duplicated logic.
- Update unit tests to match refined behavior.
Co-authored-by: yuga-hashimoto <74749461+yuga-hashimoto@users.noreply.github.com>
* fix: address review issues in node command families PR
- MotionHandler: add close() to unregister SensorEventListener on disconnect
- NodeRuntime: call motionHandler.close() in disconnect() for proper cleanup
- OpenClawNotificationListenerService: add @Volatile to companion manager field
- SystemHandlerTest: replace unconditional SDK_INT branch with asserting tests
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat: add on-demand permission requests to new node command handlers
Contacts, Calendar, Photos, and Motion handlers now use PermissionRequester
to show the system permission dialog when a command is first invoked,
matching the existing SMS/Camera handler pattern.
- ContactsHandler, CalendarHandler, PhotosHandler, MotionHandler:
add attachPermissionRequester() + ensureXPermission() suspend helpers,
convert handle methods to suspend fun
- PermissionRequester: add human-readable labels for new permissions
- NodeRuntime: expose attachPermissionRequester() delegating to all handlers
- MainActivity: wire permissionRequester to new handlers on startup
- InvokeDispatcherTest: update every -> coEvery for suspend handler mocks
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat: guide user to notification listener settings via dialog
When notifications.list or notifications.actions is invoked without
notification access, show an AlertDialog explaining the requirement
and open ACTION_NOTIFICATION_LISTENER_SETTINGS on confirmation.
- PermissionRequester: add requestNotificationAccess() suspend fun
- NotificationsHandler: add PermissionRequester wiring, make handle
methods suspend, call requestNotificationAccess() before error return
- NodeRuntime.attachPermissionRequester: include notificationsHandler
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* add funding
---------
Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(voice): support Gateway connection in voice session (#165)
- Update SettingsRepository.isConfigured() to return true when
connectionType is GATEWAY, so voice session no longer shows
"Please configure HTTP connection" for Gateway-only users
- Add sendViaGateway() to OpenClawSession that routes through
NodeRuntime.sendChat() and waits for the assistant response
via pendingRunCount + chatMessages flow
- Route sendToOpenClaw() based on connectionType (Gateway vs HTTP)
- Add error_gateway_not_connected string resource (EN/JA)
- Update error_config_required to be connection-type agnostic
Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)
Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
* feat(http): support self-signed HTTPS certificates (#166)
- Add httpIgnoreSslErrors setting to SettingsRepository
- Configure OpenClawClient to accept an ignoreSslErrors parameter;
when true, uses a trust-all X509TrustManager and hostname verifier
to allow self-signed/invalid HTTPS certificates
- Add "Ignore SSL Certificate Errors" toggle in HTTP settings UI,
shown in the HTTP tab of the connection settings section
- Persist the setting at save time and when test connection succeeds
- Apply ignoreSslErrors in ChatViewModel and OpenClawSession so
all HTTP chat traffic respects the setting
- Add string resources for the new toggle (EN/JA)
Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)
Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
* fix(voice): address review feedback on Gateway session (#165)
- Add fail-fast Gateway health check in onShow() so users see
error_gateway_not_connected immediately at session open, consistent
with HTTP mode behavior
- Replace combine(pendingRunCount, chatMessages) with chatMessages.first{}
to detect the completed assistant response; chatMessages only receives
committed messages (not streaming partials), eliminating the
premature pendingRunCount==0 firing between tool calls and the
TOCTOU race with assistantCountBefore
- Remove unused combine import
Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)
Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
* feat(voice): add wakeword connection type selector and strengthen SSL warning
- Add `wakewordConnectionType` setting (Gateway/HTTP) in SettingsRepository
- Voice session (long-press Home) now uses `wakewordConnectionType` instead of
the global `connectionType`, allowing independent configuration
- Add voice session connection type selector (FilterChip) in Wake Word settings
- Show prominent error card with MITM risk details when SSL verification is disabled
Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)
Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
* fix(ci): use shared debug keystore to prevent conflicting app signature
Without a fixed keystore each runner generates a fresh debug.keystore,
so APKs from different PRs have different signatures and cannot be
installed over each other.
Solution: decode a shared keystore from `DEBUG_KEYSTORE_B64` secret into
~/.android/debug.keystore (the default path Android Gradle Plugin uses
for debug signing). Fork PRs fall back to a freshly generated keystore.
Setup (one-time, see PR description):
keytool -genkey ... -keystore debug.keystore
base64 debug.keystore → store as GitHub secret DEBUG_KEYSTORE_B64
Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)
Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
* fix(voice): skip local DB in Gateway wakeword session
Gateway mode voice sessions were incorrectly creating local DB chat
sessions and saving messages, causing duplicate entries to appear in
the chat list as HTTP sessions.
- onShow(): in Gateway mode, skip chatRepository session create/resume.
Instead switch to a new gateway session when resumeLatestSession=false,
or keep the current active gateway session when resumeLatestSession=true.
- sendToOpenClaw(): only write user message to local DB in HTTP mode.
- handleResponseReceived(): only write assistant reply to local DB in HTTP mode.
Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)
Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
* perf(ci): parallelize lint and build jobs to cut wall-clock time
Before: lint (all variants) → build standard → build full ≈ 7 min sequential
After: lint (lintStandardDebug) ║ build (--parallel) ≈ ~3 min
Changes:
- Split into two parallel jobs: `lint` and `build`
- Lint runs only `lintStandardDebug` instead of `lint` (all variants)
which avoids re-running the same checks on every flavor × build-type
- Build uses `--parallel` so Standard and Full flavors compile concurrently
- Lint job uses fetch-depth: 1 (no git history needed for static analysis)
Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)
Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
* fix(voice): name new gateway session with label instead of raw UUID
New gateway sessions created by the wakeword voice session now get a
human-readable label matching the existing HTTP session format:
"Voice Session HH:mm" / "音声セッション HH:mm"
Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)
Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
---------
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Happy <yesreply@happy.engineering>
* feat: add multi-provider TTS support and build flavor split
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(tts): code review fixes - i18n, bugs, copyright compliance
- Move all hardcoded Japanese UI strings to strings.xml (~35 entries)
for proper i18n across TTS providers, CreditsScreen, SettingsActivity,
and VoiceVoxModelManager
- Fix TTSManager.reinitialize(): add missing BuildConfig.FLAVOR guard
so VoiceVoxProvider is only instantiated in 'full' flavor builds
- Fix VoiceVoxProvider: remove duplicate VVM_FILE_MAPPING; delegate to
VoiceVoxCharacterData as single source of truth
- Fix VoiceVoxCharacterData: align style IDs with SettingsActivity
CHARACTERS (official VOICEVOX style IDs) and add missing characters
(玄野武宏, 白上虎太郎, 青山龍星) for correct copyright display
- Fix VoiceVoxProvider.speak(): call synthesizer.metas() as method
- Fix OpenAIProvider: use prepareAsync() instead of blocking prepare()
- Fix ElevenLabsProvider, OpenAIProvider: delete temp files in speak()
(speakWithProgress already did cleanup; speak() did not)
- Fix CreditsScreen: wrap all startActivity() calls in
ActivityNotFoundException handler; add character credit fallback
when styleId is unregistered (copyright compliance)
- Add ProGuard rules for new TTS provider interface and classes
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(tts): address code review findings
- VoiceVoxProvider: remove debug metas() enumeration loop that ran on
every speak() call (overhead + unnecessary logging in production)
- ElevenLabsProvider, OpenAIProvider: replace hardcoded English fallback
string "Not configured" in speakWithProgress() with string resource
- .gitignore: add *.vvm to prevent accidental commit of large model
files (50-75 MB each, downloaded at runtime from GitHub releases)
- Remove erroneously committed placeholder app/src/full/assets/0.vvm
(contained "Invalid username or password." — not a real VVM model)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(voicevox): implement actual VVM download and add setup code to settings
- Fix VoiceVoxSetupDialog: replace fake progress simulation with real
downloads via VoiceVoxModelManager.copyDictionaryFromAssets() and
downloadVvmModel(). Dialog now moves inside VoiceVoxSettingsCard so
refreshStatus() is called immediately on completion.
- Fix VoiceVoxModelManager stub (standard flavor): align sealed class
API (CopyProgress/DownloadProgress) with full flavor so SettingsActivity
compiles against the same interface on both flavors.
- Add setup code input to Gateway tab in SettingsActivity: paste a code
from `openclaw qr --setup-code-only` and tap Apply to auto-fill host,
port, TLS, and token — matching the SetupGuideScreen behaviour.
- Remove spurious debug metas() logging from VoiceVoxProvider.speak().
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(voicevox): correct official style IDs and VVM file mappings
- VoiceVoxCharacterData: rewrite all style IDs to match official VOICEVOX
Core API (voicevox_vvm 0.16.x) per README
- SettingsActivity.VoiceVoxCharacters: sync CHARACTERS list and
VVM_FILE_MAPPING with corrected IDs
- VoiceVoxModelManager: drop unused 1.vvm/2.vvm, add 9.vvm/10.vvm/15.vvm
for 白上虎太郎, 玄野武宏 extra styles, 青山龍星
Key corrections (old → new):
ずんだもん ヒソヒソ: ID 11/0.vvm → 38/5.vvm
ずんだもん ノーマル: ID 7 → 3
白上虎太郎 ふつう: ID 20/3.vvm → 12/9.vvm
青山龍星 ノーマル: ID 27/4.vvm → 13/15.vvm
玄野武宏 ノーマル: ID 16/2.vvm → 11/4.vvm
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat(credits): add Credits navigation from Settings TopAppBar
- SettingsActivity: toggle between SettingsScreen and CreditsScreen
using showCredits state
- SettingsScreen: add onCredits callback parameter
- TopAppBar: add Info icon button that opens CreditsScreen
CreditsScreen already shows VOICEVOX credit notation
(e.g. "VOICEVOX:白上虎太郎") but had no entry point in the UI.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* TTS再生タイミング修正、ウェイクワード後の連続会話改善、VOICEVOX設定画面にクレジット追加
* CI: standardは元の名前を維持、fullのビルド名に-in-VOICEVOXを付加
* fix
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix: disable minification for debug builds
* fix: fix debug APK crash and add fork PR support in CI
- Rename workflow from "CI" to "Build & Check PR"
- Use DEBUG_GOOGLE_SERVICES_JSON secret for own-repo PRs (real Firebase)
- Set FIREBASE_ENABLED=false for fork PRs (no secret available)
- Disable FirebaseInitProvider auto-init in debug builds via manifest merge
- Initialize Firebase manually in OpenClawApplication based on FIREBASE_ENABLED
- Add FIREBASE_ENABLED BuildConfig flag to debug/release build types
Fixes crash: FirebaseInitProvider threw IllegalArgumentException on mock-api-key
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix: use printf to write google-services.json to avoid echo escape issues
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix: use python3 to write google-services.json to avoid shell escape issues
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix: use python heredoc to safely write google-services.json with JSON validation
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix: decode base64-encoded DEBUG_GOOGLE_SERVICES_JSON secret
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* ci: run lint before build for earlier feedback
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
The workflow was failing when triggered by push events (e.g., when
GitHub validates workflow files). Added explicit github.event_name
check to ensure the job only runs for issues events.
Co-authored-by: Kimi Claw <kimi@openclaw.ai>
PR builds from forks cannot access repository secrets like RELEASE_KEYSTORE_BASE64.
This change switches the AAB build from release to debug variant,
which uses the default Android debug keystore instead of requiring
a release keystore.
Changes:
- Build PR Test AAB: assembleRelease → bundleDebug
- Upload path: bundle/release → bundle/debug
This allows PRs from external contributors to pass CI without
needing access to signing secrets.
Co-authored-by: Yuga Hashimoto <yuga.hashimoto@example.com>
Output filename is customized in build.gradle.kts (openclaw-<version>-release.apk),
so fixed hardcoded app-release.apk/aab references to use wildcards.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Output filename is customized in build.gradle.kts, so fixed path
pattern from hardcoded app-debug.apk to *.apk / *.aab.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* ci: use PR test APK variant with stable version and distinct app identity
* ci: generate dummy google-services for pr variant package
* ci: include release/debug/test package clients in dummy google-services
* ui(pr): rename app to Debug-OpenClawAssistant and use red launcher color
* build(pr): base test variant on debug for launch reliability
* build: switch PR artifact to debug variant to prevent startup crash
* fix(android): use fully-qualified component names to avoid variant package resolution crash
* feat(debug): use purple icon for debug builds and clean up pr source set
- Add debug-specific adaptive icon overrides using ic_launcher_purple_foreground
- Add purple launcher icon assets (PNG + adaptive icon XMLs)
- Move app_name override from resValue to debug strings.xml for reliability
- Remove unused pr/ source set (leftover from earlier approach)
- Fix CI comment to reflect debug variant and remove stale .test client
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* revert(ci): restore release build and google-services decode
Revert CI to build assembleRelease with real google-services.json
from secrets. Debug build type with purple icon is for local dev only.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* ci: build debug APK with real Firebase config for PR testing
Switch CI back to assembleDebug for PR test builds. The
GOOGLE_SERVICES_JSON secret should include both release and debug
client entries so Firebase initializes correctly.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* ci: build both debug and release APKs
Build and upload both variants so PR testing gets the debug APK
(with .debug suffix and purple icon) while also verifying the
release build succeeds.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* ci: keep only debug build in PR workflow
Release builds are handled by build-release.yml, so ci.yml only
needs to build the debug APK for PR testing.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* ci: use separate DEBUG_GOOGLE_SERVICES_JSON secret for debug builds
Keep GOOGLE_SERVICES_JSON for release (build-release.yml) and use
a dedicated DEBUG_GOOGLE_SERVICES_JSON secret for debug builds.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* fix: correct gateway connect params and reduce Crashlytics noise
- Fix invalid connect params (closes#48): change client.id to
"openclaw-android" and client.mode to "cli" to match the gateway
server's schema (GATEWAY_CLIENT_IDS/GATEWAY_CLIENT_MODES)
- Stop reporting transient network errors (SocketTimeout, SocketException,
ConnectException, ProtocolException, ErrnoException, etc.) to
Crashlytics in both GatewayClient and OpenClawClient
- Handle Vosk UnsatisfiedLinkError gracefully (related to #38): persist a
"vosk_unsupported" flag so incompatible devices skip Vosk init on
subsequent launches instead of repeatedly crashing and reporting
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(tts): increase timeout and optimize chunk size for long Japanese text
* feat(build): enable automated versioning based on git tags and commit count
* ci: upload debug APK as artifact for pull requests
* ci: fetch full git history for correct versioning
* ci: sign PR builds with release key for update compatibility
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* feat: Add Telnyx phone calling integration
- Add TelnyxClient.kt for Telnyx Call Control API
- Make outbound phone calls
- Hangup, transfer, and DTMF support
- Text-to-speech synthesis via Telnyx AI API
- Add PhoneCallService.kt for call management
- Call state management
- Notification support for active calls
- Integration with OpenClaw session
- Update SettingsRepository with Telnyx credentials
- telnyx_api_key
- telnyx_connection_id
- telnyx_caller_id
- Add phone/call icons for notifications
- Update README with phone calling documentation
This enables the OpenClaw Assistant to make real phone calls
via Telnyx, integrating with ClawdTalk for AI conversations.
Environment variables (not stored in code):
- TELNYX_API_KEY: API key for authentication
- TELNYX_CONNECTION_ID: Call Control connection ID
- TELNYX_CALLER_ID: Default caller ID for outbound calls
* feat: Automate app versioning based on git tags
---------
Co-authored-by: Abhishek <abhishek@telnyx.com>
* fix: enable chat text selection and fix TTS stopping on long responses
- Wrap chat message bubbles in SelectionContainer for text selection/copy
- Split long TTS text into chunks (max 3900 chars) to avoid Android's
4000 char limit which caused speech to silently stop mid-response
- Add try-catch around TTS speak flow to ensure wake word detection
always resumes even when TTS fails unexpectedly
- Scale TTS timeout dynamically based on text length instead of fixed 90s
Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)
Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
* fix(ci): fix heredoc indentation in google-services.json fallback
The EOT delimiter was indented, causing bash to fail with
"unexpected end of file". Move delimiter to baseline indent level.
Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)
Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
* fix: address Copilot review feedback
- Fix TTSManager pendingSpeak to queue all chunks (not just first)
when TTS initialization is still in progress
- Clarify timeout calculation comments (15ms per char ~15s per 1000)
Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)
Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
---------
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Happy <yesreply@happy.engineering>
- Rethrow CancellationException in OpenClawClient, GatewayClient, and
HotwordService to prevent swallowing coroutine cancellation and
polluting Crashlytics with expected cancellations
- Add guard for missing GOOGLE_SERVICES_JSON secret in CI workflow
and use printf instead of echo for robustness
Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)
Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
The google-services.json file is excluded from version control via
.gitignore. Decode it from the GOOGLE_SERVICES_JSON secret in CI
so the build can succeed.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Use ActivityResultContracts for mic permission in ChatActivity (replacing deprecated requestPermissions)
- Show settings dialog when mic permission is permanently denied
- Fix hotword toggle to properly await permission result before starting service
- Explicitly select a real speech recognition service, skipping the app's own stub RecognitionService
- Change release workflow from draft to published
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>