@agentprojectcontext/apx 1.81.0 → 1.89.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +9 -8
- package/skills/apc-context/SKILL.md +14 -7
- package/skills/apx/SKILL.md +62 -2
- package/skills/apx-mcp/SKILL.md +42 -15
- package/src/core/agent/a2a/peers.js +161 -0
- package/src/core/agent/a2a/reply.js +379 -27
- package/src/core/agent/abort.js +25 -0
- package/src/core/agent/agent-model.js +58 -0
- package/src/core/agent/agent-tools.js +101 -0
- package/src/core/agent/author-line.js +110 -0
- package/src/core/agent/build-agent-system.js +52 -18
- package/src/core/agent/channels/voice-context.js +17 -2
- package/src/core/agent/closing-floor.js +117 -0
- package/src/core/agent/constants.js +119 -11
- package/src/core/agent/engine-call.js +56 -0
- package/src/core/agent/group/run-group-turn.js +218 -0
- package/src/core/agent/group/turn-resolver.js +165 -0
- package/src/core/agent/judge.js +138 -11
- package/src/core/agent/loop/side-effects.js +106 -3
- package/src/core/agent/memory.js +31 -18
- package/src/core/agent/model-capabilities.js +47 -0
- package/src/core/agent/model-router.js +11 -40
- package/src/core/agent/prompt-builder.js +47 -0
- package/src/core/agent/prompts/channels/a2a.md +12 -0
- package/src/core/agent/prompts/channels/code.md +3 -1
- package/src/core/agent/prompts/channels/telegram.md +2 -0
- package/src/core/agent/prompts/channels/web_code.md +1 -1
- package/src/core/agent/prompts/channels/whatsapp.md +37 -0
- package/src/core/agent/prompts/core/agent-base.md +2 -0
- package/src/core/agent/prompts/discipline/action.md +1 -0
- package/src/core/agent/prompts/discipline/two-segment.md +2 -1
- package/src/core/agent/prompts/modes/mobility.md +36 -0
- package/src/core/agent/retry.js +42 -0
- package/src/core/agent/run-agent.js +322 -25
- package/src/core/agent/run-turn.js +253 -0
- package/src/core/agent/self-memory.js +18 -52
- package/src/core/agent/skills/agent-skills.js +118 -0
- package/src/core/agent/skills/catalog.js +1 -1
- package/src/core/agent/skills/declared.js +13 -0
- package/src/core/agent/skills/index-store.js +8 -1
- package/src/core/agent/skills/inspector.js +20 -3
- package/src/core/agent/skills/loader.js +18 -3
- package/src/core/agent/skills/media.js +121 -0
- package/src/core/agent/skills/origin.js +61 -0
- package/src/core/agent/skills/sections.js +136 -0
- package/src/core/agent/super-agent.js +54 -14
- package/src/core/agent/tools/handlers/_calendar.js +50 -0
- package/src/core/agent/tools/handlers/add-mcp.js +103 -0
- package/src/core/agent/tools/handlers/asana-create-task.js +3 -1
- package/src/core/agent/tools/handlers/attach-media.js +56 -0
- package/src/core/agent/tools/handlers/calendar-create-event.js +38 -0
- package/src/core/agent/tools/handlers/calendar-find-slot.js +39 -0
- package/src/core/agent/tools/handlers/calendar-list-events.js +39 -0
- package/src/core/agent/tools/handlers/calendar-update-event.js +35 -0
- package/src/core/agent/tools/handlers/call-agent.js +14 -6
- package/src/core/agent/tools/handlers/call-runtime.js +27 -49
- package/src/core/agent/tools/handlers/comment-task.js +78 -0
- package/src/core/agent/tools/handlers/complete-task.js +60 -0
- package/src/core/agent/tools/handlers/configure-agent.js +70 -0
- package/src/core/agent/tools/handlers/create-agent.js +68 -0
- package/src/core/agent/tools/handlers/create-task.js +41 -3
- package/src/core/agent/tools/handlers/import-agent.js +5 -5
- package/src/core/agent/tools/handlers/list-mcp-tools.js +83 -0
- package/src/core/agent/tools/handlers/list-routines.js +46 -0
- package/src/core/agent/tools/handlers/list-tasks.js +8 -1
- package/src/core/agent/tools/handlers/mark-commitment.js +57 -0
- package/src/core/agent/tools/handlers/read-skill.js +64 -0
- package/src/core/agent/tools/handlers/remember-routine.js +2 -8
- package/src/core/agent/tools/handlers/remember.js +82 -6
- package/src/core/agent/tools/handlers/remove-agent.js +42 -0
- package/src/core/agent/tools/handlers/run-routine.js +94 -0
- package/src/core/agent/tools/handlers/run-shell.js +44 -2
- package/src/core/agent/tools/handlers/send-telegram.js +41 -19
- package/src/core/agent/tools/handlers/set-agent-prompt.js +44 -0
- package/src/core/agent/tools/handlers/view-media.js +69 -0
- package/src/core/agent/tools/handlers/write-agent-memory.js +52 -0
- package/src/core/agent/tools/helpers.js +36 -4
- package/src/core/agent/tools/names.js +64 -0
- package/src/core/agent/tools/registry-bridge.js +24 -1
- package/src/core/agent/tools/registry.js +79 -0
- package/src/core/agent/tools/tool-call-parser.js +136 -13
- package/src/core/agent/vision-bridge.js +69 -0
- package/src/core/apc/agent-identity.js +74 -0
- package/src/core/apc/agent-rename-refs.js +187 -0
- package/src/core/apc/agent-write.js +312 -0
- package/src/core/apc/agents-vault.js +1 -1
- package/src/core/apc/blob-keys.js +42 -0
- package/src/core/apc/frontmatter.js +166 -10
- package/src/core/apc/parser.js +33 -83
- package/src/core/apc/paths.js +4 -6
- package/src/core/apc/project-memory.js +44 -0
- package/src/core/apc/projects-helpers.js +33 -0
- package/src/core/apc/scaffold.js +36 -16
- package/src/core/channels/telegram/ask-callbacks.js +103 -0
- package/src/core/channels/telegram/dispatch.js +154 -30
- package/src/core/channels/telegram/helpers.js +59 -7
- package/src/core/channels/telegram/inbound/audio.js +26 -30
- package/src/core/channels/telegram/inbound/file.js +22 -32
- package/src/core/channels/telegram/inbound/photo.js +24 -28
- package/src/core/channels/telegram/progress-gate.js +76 -0
- package/src/core/channels/telegram/reply.js +151 -43
- package/src/core/channels/telegram/voice-note.js +150 -0
- package/src/core/config/index.js +53 -54
- package/src/core/config/paths.js +117 -31
- package/src/core/config/redact.js +89 -4
- package/src/core/config/secret-values.js +2 -4
- package/src/core/constants/actors.js +5 -0
- package/src/core/constants/channels.js +3 -1
- package/src/core/constants/task-categories.js +73 -0
- package/src/core/constants/task-fields.js +28 -0
- package/src/core/engines/_globs.js +33 -0
- package/src/core/engines/anthropic.js +19 -1
- package/src/core/engines/catalog.js +20 -0
- package/src/core/engines/gemini.js +84 -16
- package/src/core/engines/index.js +19 -3
- package/src/core/engines/mock.js +145 -1
- package/src/core/engines/openai-compatible.js +150 -15
- package/src/core/engines/presets.js +36 -9
- package/src/core/engines/zen.js +131 -0
- package/src/core/events/bus.js +140 -0
- package/src/core/events/mascot-notify.js +156 -0
- package/src/core/http-tools/browser.js +366 -36
- package/src/core/http-tools/catalog.js +206 -22
- package/src/core/http-tools/grep.js +62 -7
- package/src/core/http-tools/registry.js +3 -1
- package/src/core/i18n/en.js +34 -3
- package/src/core/i18n/es.js +36 -4
- package/src/core/i18n/index.js +5 -5
- package/src/core/i18n/pt.js +32 -3
- package/src/core/identity/self.js +14 -1
- package/src/core/images/engines/a1111.js +219 -0
- package/src/core/images/engines/index.js +187 -0
- package/src/core/images/engines/mock.js +74 -0
- package/src/core/images/engines/openai.js +130 -0
- package/src/core/images/engines/sdcpp.js +204 -0
- package/src/core/images/engines/shared.js +218 -0
- package/src/core/images/generate.js +260 -0
- package/src/core/integrations/catalog.js +8 -3
- package/src/core/integrations/plugins/_google-oauth.js +179 -0
- package/src/core/integrations/plugins/asana.js +7 -3
- package/src/core/integrations/plugins/calendar.js +404 -0
- package/src/core/integrations/store.js +2 -0
- package/src/core/memory/broker.js +16 -1
- package/src/core/memory/compactor.js +19 -3
- package/src/core/memory/consolidate.js +2 -2
- package/src/core/memory/dated-log.js +66 -0
- package/src/core/memory/embed-engines/custom.js +59 -0
- package/src/core/memory/embed-engines/gemini.js +9 -3
- package/src/core/memory/embed-engines/index.js +87 -15
- package/src/core/memory/embeddings.js +52 -24
- package/src/core/memory/index.js +13 -3
- package/src/core/memory/indexer.js +83 -27
- package/src/core/memory/prune.js +182 -0
- package/src/core/memory/store.js +22 -0
- package/src/core/memory/summarizer.js +72 -8
- package/src/core/mobility/answer.js +80 -0
- package/src/core/mobility/geofence.js +667 -0
- package/src/core/mobility/osm-route.js +282 -0
- package/src/core/mobility/preferences.js +91 -0
- package/src/core/mobility/state.js +245 -0
- package/src/core/mobility/trip-event.js +384 -0
- package/src/core/net/tailscale.js +168 -0
- package/src/core/profiles/bundled/secretary/PROFILE.md +32 -42
- package/src/core/profiles/bundled/secretary/config.schema.es.json +68 -0
- package/src/core/profiles/bundled/secretary/config.schema.json +30 -2
- package/src/core/profiles/bundled/secretary/profile.json +3 -1
- package/src/core/profiles/bundled/secretary/routines/a2a-sweep.json +20 -0
- package/src/core/profiles/bundled/secretary/routines/day-close.json +4 -1
- package/src/core/profiles/bundled/secretary/routines/day-open.json +7 -1
- package/src/core/profiles/bundled/secretary/routines/watch.json +13 -2
- package/src/core/profiles/lifecycle.js +243 -19
- package/src/core/profiles/paths.js +12 -0
- package/src/core/profiles/store.js +47 -0
- package/src/core/routines/active-runs.js +213 -0
- package/src/core/routines/delivery.js +612 -0
- package/src/core/routines/header.js +88 -0
- package/src/core/routines/index.js +26 -0
- package/src/core/routines/run-log.js +78 -0
- package/src/core/routines/runner.js +629 -66
- package/src/core/routines/signals.js +75 -0
- package/src/core/runtime-skills/apc-context/SKILL.md +14 -7
- package/src/core/runtime-skills/apx/SKILL.md +93 -1
- package/src/core/runtime-skills/apx-agency-agents/SKILL.md +2 -2
- package/src/core/runtime-skills/apx-agent/SKILL.md +134 -19
- package/src/core/runtime-skills/apx-commitment/SKILL.md +21 -11
- package/src/core/runtime-skills/apx-image/SKILL.md +60 -0
- package/src/core/runtime-skills/apx-integrations/SKILL.md +82 -0
- package/src/core/runtime-skills/apx-mcp/SKILL.md +45 -14
- package/src/core/runtime-skills/apx-mcp-builder/SKILL.md +4 -4
- package/src/core/runtime-skills/apx-profile/SKILL.md +27 -2
- package/src/core/runtime-skills/apx-project/SKILL.md +23 -2
- package/src/core/runtime-skills/apx-routine/SKILL.md +40 -17
- package/src/core/runtime-skills/apx-runtime/SKILL.md +124 -14
- package/src/core/runtime-skills/apx-sessions/SKILL.md +7 -6
- package/src/core/runtime-skills/apx-skill-builder/SKILL.md +15 -13
- package/src/core/runtime-skills/apx-task/SKILL.md +79 -26
- package/src/core/runtime-skills/apx-telegram/SKILL.md +12 -12
- package/src/core/runtime-skills/apx-transcribe/SKILL.md +56 -0
- package/src/core/runtime-skills/apx-voice/SKILL.md +27 -8
- package/src/core/runtime-skills/claude-code/SKILL.md +8 -0
- package/src/core/runtime-skills/openrouter/SKILL.md +8 -2
- package/src/core/runtimes/_spawn.js +9 -1
- package/src/core/runtimes/claude-code.js +11 -2
- package/src/core/runtimes/codex.js +44 -10
- package/src/core/runtimes/index.js +17 -2
- package/src/core/runtimes/opencode.js +88 -9
- package/src/core/runtimes/outcome.js +119 -0
- package/src/core/sessions/index.js +193 -1
- package/src/core/stores/agent-inbox.js +109 -23
- package/src/core/stores/code-sessions.js +21 -0
- package/src/core/stores/commitments.js +27 -3
- package/src/core/stores/conversations.js +220 -20
- package/src/core/stores/deliveries.js +177 -0
- package/src/core/stores/media-archive.js +228 -0
- package/src/core/stores/messages.js +1096 -112
- package/src/core/stores/organization.js +34 -0
- package/src/core/stores/project-memory.js +71 -0
- package/src/core/stores/routine-memory.js +16 -8
- package/src/core/stores/routines.js +27 -49
- package/src/core/stores/sessions.js +4 -8
- package/src/core/stores/tasks.js +212 -18
- package/src/core/stores/turn-record.js +209 -0
- package/src/core/tasks/columns.js +136 -0
- package/src/core/tasks/comment-turn.js +258 -0
- package/src/core/util/time.js +13 -0
- package/src/core/voice/engines/index.js +54 -1
- package/src/core/voice/engines/openai.js +50 -4
- package/src/core/voice/opus.js +67 -0
- package/src/core/voice/pronounceable.js +43 -0
- package/src/core/voice/transcription.js +4 -0
- package/src/core/voice/tts.js +114 -24
- package/src/host/daemon/active-turns.js +188 -0
- package/src/host/daemon/api/admin-config.js +9 -0
- package/src/host/daemon/api/agents.js +99 -90
- package/src/host/daemon/api/artifact-preview.js +7 -6
- package/src/host/daemon/api/artifacts.js +3 -2
- package/src/host/daemon/api/code.js +55 -9
- package/src/host/daemon/api/commitments.js +11 -0
- package/src/host/daemon/api/confirm.js +3 -2
- package/src/host/daemon/api/conversations.js +543 -35
- package/src/host/daemon/api/deck.js +9 -8
- package/src/host/daemon/api/deliveries.js +33 -0
- package/src/host/daemon/api/desktop.js +5 -4
- package/src/host/daemon/api/embeddings.js +15 -6
- package/src/host/daemon/api/engines.js +59 -8
- package/src/host/daemon/api/exec.js +437 -121
- package/src/host/daemon/api/groups.js +215 -0
- package/src/host/daemon/api/images.js +107 -0
- package/src/host/daemon/api/inbox.js +123 -2
- package/src/host/daemon/api/integrations.js +64 -2
- package/src/host/daemon/api/mcps.js +7 -6
- package/src/host/daemon/api/media.js +283 -0
- package/src/host/daemon/api/mobility.js +177 -0
- package/src/host/daemon/api/net.js +182 -0
- package/src/host/daemon/api/pairing.js +38 -5
- package/src/host/daemon/api/profiles.js +30 -2
- package/src/host/daemon/api/routines.js +36 -4
- package/src/host/daemon/api/runtimes.js +74 -39
- package/src/host/daemon/api/sessions-search.js +7 -2
- package/src/host/daemon/api/sessions.js +44 -2
- package/src/host/daemon/api/shared.js +127 -20
- package/src/host/daemon/api/skills.js +11 -4
- package/src/host/daemon/api/super-agent.js +384 -53
- package/src/host/daemon/api/tasks.js +180 -10
- package/src/host/daemon/api/telegram.js +11 -10
- package/src/host/daemon/api/thread-faces.js +159 -0
- package/src/host/daemon/api/top-level.js +3 -3
- package/src/host/daemon/api/transcribe.js +11 -10
- package/src/host/daemon/api/tts.js +97 -4
- package/src/host/daemon/api/turn-abort.js +23 -0
- package/src/host/daemon/api/turns.js +49 -0
- package/src/host/daemon/api/voice.js +5 -4
- package/src/host/daemon/api/web.js +98 -0
- package/src/host/daemon/api.js +39 -0
- package/src/host/daemon/delivery-sweep.js +58 -0
- package/src/host/daemon/desktop-ws.js +5 -23
- package/src/host/daemon/events-ws.js +273 -0
- package/src/host/daemon/index.js +59 -15
- package/src/host/daemon/plugins/desktop/index.js +17 -4
- package/src/host/daemon/plugins/telegram/index.js +78 -40
- package/src/host/daemon/pty-bridge.py +99 -0
- package/src/host/daemon/routines-scheduler.js +1 -0
- package/src/host/daemon/smoke.js +1 -2
- package/src/host/daemon/terminal-ws.js +132 -0
- package/src/host/daemon/tts-keepwarm.js +87 -0
- package/src/host/daemon/wakeup.js +11 -6
- package/src/host/daemon/whisper-server.js +44 -0
- package/src/host/daemon/whisper-server.py +70 -13
- package/src/host/daemon/ws-auth.js +28 -0
- package/src/interfaces/android/app/build.gradle +42 -0
- package/src/interfaces/android/app/proguard-rules.pro +4 -0
- package/src/interfaces/android/app/src/main/AndroidManifest.xml +139 -0
- package/src/interfaces/android/app/src/main/java/dev/agentprojectcontext/apx/AlertScreen.java +139 -0
- package/src/interfaces/android/app/src/main/java/dev/agentprojectcontext/apx/AndroidAutoDetector.java +106 -0
- package/src/interfaces/android/app/src/main/java/dev/agentprojectcontext/apx/ApxCarAppService.java +57 -0
- package/src/interfaces/android/app/src/main/java/dev/agentprojectcontext/apx/ApxPreferences.java +165 -0
- package/src/interfaces/android/app/src/main/java/dev/agentprojectcontext/apx/CarAlertStore.java +84 -0
- package/src/interfaces/android/app/src/main/java/dev/agentprojectcontext/apx/CarMessageActionReceiver.java +47 -0
- package/src/interfaces/android/app/src/main/java/dev/agentprojectcontext/apx/CarMessageNotification.java +152 -0
- package/src/interfaces/android/app/src/main/java/dev/agentprojectcontext/apx/DaemonAddress.java +35 -0
- package/src/interfaces/android/app/src/main/java/dev/agentprojectcontext/apx/DaemonClient.java +339 -0
- package/src/interfaces/android/app/src/main/java/dev/agentprojectcontext/apx/DeviceLocation.java +38 -0
- package/src/interfaces/android/app/src/main/java/dev/agentprojectcontext/apx/ErrandScreen.java +127 -0
- package/src/interfaces/android/app/src/main/java/dev/agentprojectcontext/apx/MainActivity.java +872 -0
- package/src/interfaces/android/app/src/main/java/dev/agentprojectcontext/apx/MapsNavigationDetector.java +63 -0
- package/src/interfaces/android/app/src/main/java/dev/agentprojectcontext/apx/MapsNavigationListenerService.java +453 -0
- package/src/interfaces/android/app/src/main/java/dev/agentprojectcontext/apx/MapsShareIntentParser.java +93 -0
- package/src/interfaces/android/app/src/main/java/dev/agentprojectcontext/apx/MascotBlobCatalog.java +66 -0
- package/src/interfaces/android/app/src/main/java/dev/agentprojectcontext/apx/MascotOverlayService.java +448 -0
- package/src/interfaces/android/app/src/main/java/dev/agentprojectcontext/apx/MascotView.java +249 -0
- package/src/interfaces/android/app/src/main/java/dev/agentprojectcontext/apx/MessageFrameParser.java +240 -0
- package/src/interfaces/android/app/src/main/java/dev/agentprojectcontext/apx/NotifyChannels.java +69 -0
- package/src/interfaces/android/app/src/main/java/dev/agentprojectcontext/apx/ProximityActionReceiver.java +78 -0
- package/src/interfaces/android/app/src/main/java/dev/agentprojectcontext/apx/ProximityNotification.java +161 -0
- package/src/interfaces/android/app/src/main/java/dev/agentprojectcontext/apx/StartupReceiver.java +57 -0
- package/src/interfaces/android/app/src/main/java/dev/agentprojectcontext/apx/TravelEventGate.java +62 -0
- package/src/interfaces/android/app/src/main/java/dev/agentprojectcontext/apx/TravelStatusBanner.java +116 -0
- package/src/interfaces/android/app/src/main/java/dev/agentprojectcontext/apx/TripLocationService.java +272 -0
- package/src/interfaces/android/app/src/main/java/dev/agentprojectcontext/apx/TripPlacesSheet.java +203 -0
- package/src/interfaces/android/app/src/main/java/dev/agentprojectcontext/apx/TripScreen.java +182 -0
- package/src/interfaces/android/app/src/main/res/drawable/app_background.xml +12 -0
- package/src/interfaces/android/app/src/main/res/drawable/ic_apx_app.xml +10 -0
- package/src/interfaces/android/app/src/main/res/drawable/ic_apx_launcher_foreground.xml +12 -0
- package/src/interfaces/android/app/src/main/res/drawable/ic_apx_notification.xml +22 -0
- package/src/interfaces/android/app/src/main/res/drawable/ic_trip_car.xml +37 -0
- package/src/interfaces/android/app/src/main/res/drawable-nodpi/apx_logo.png +0 -0
- package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_amatista.png +0 -0
- package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_aqua.png +0 -0
- package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_brasa.png +0 -0
- package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_campana.png +0 -0
- package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_caracol.png +0 -0
- package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_carbon.png +0 -0
- package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_cobalto.png +0 -0
- package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_cometa.png +0 -0
- package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_coral.png +0 -0
- package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_cubi.png +0 -0
- package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_eclipse.png +0 -0
- package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_faro.png +0 -0
- package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_gajo.png +0 -0
- package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_iris.png +0 -0
- package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_kiwi.png +0 -0
- package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_laguna.png +0 -0
- package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_lila.png +0 -0
- package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_menta.png +0 -0
- package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_nimbo.png +0 -0
- package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_noche.png +0 -0
- package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_onyx.png +0 -0
- package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_orbita.png +0 -0
- package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_ovni.png +0 -0
- package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_papa.png +0 -0
- package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_parche.png +0 -0
- package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_peon.png +0 -0
- package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_perla.png +0 -0
- package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_pino.png +0 -0
- package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_quad.png +0 -0
- package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_rubi.png +0 -0
- package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_saturno.png +0 -0
- package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_trebol.png +0 -0
- package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_trino.png +0 -0
- package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_volcan.png +0 -0
- package/src/interfaces/android/app/src/main/res/drawable-nodpi/mascot_zafiro.png +0 -0
- package/src/interfaces/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml +5 -0
- package/src/interfaces/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml +5 -0
- package/src/interfaces/android/app/src/main/res/raw/apx_notification.mp3 +0 -0
- package/src/interfaces/android/app/src/main/res/values/colors.xml +4 -0
- package/src/interfaces/android/app/src/main/res/values/strings.xml +25 -0
- package/src/interfaces/android/app/src/main/res/values/styles.xml +10 -0
- package/src/interfaces/android/app/src/main/res/values-v31/styles.xml +14 -0
- package/src/interfaces/android/app/src/main/res/xml/automotive_app_desc.xml +9 -0
- package/src/interfaces/android/app/src/main/res/xml/network_security_config.xml +10 -0
- package/src/interfaces/android/app/src/test/java/dev/agentprojectcontext/apx/AndroidAutoDetectorTest.java +85 -0
- package/src/interfaces/android/app/src/test/java/dev/agentprojectcontext/apx/CarAlertStoreTest.java +104 -0
- package/src/interfaces/android/app/src/test/java/dev/agentprojectcontext/apx/DaemonAddressTest.java +23 -0
- package/src/interfaces/android/app/src/test/java/dev/agentprojectcontext/apx/MapsNavigationDetectorTest.java +69 -0
- package/src/interfaces/android/app/src/test/java/dev/agentprojectcontext/apx/MapsShareIntentParserTest.java +31 -0
- package/src/interfaces/android/app/src/test/java/dev/agentprojectcontext/apx/MessageFrameParserTest.java +180 -0
- package/src/interfaces/android/app/src/test/java/dev/agentprojectcontext/apx/TravelEventGateTest.java +64 -0
- package/src/interfaces/android/app/src/test/java/dev/agentprojectcontext/apx/TripPlacesSheetTest.java +66 -0
- package/src/interfaces/android/build.gradle +3 -0
- package/src/interfaces/android/gradle/wrapper/gradle-wrapper.jar +0 -0
- package/src/interfaces/android/gradle/wrapper/gradle-wrapper.properties +9 -0
- package/src/interfaces/android/gradle.properties +3 -0
- package/src/interfaces/android/gradlew +248 -0
- package/src/interfaces/android/gradlew.bat +82 -0
- package/src/interfaces/android/settings.gradle +18 -0
- package/src/interfaces/cli/commands/a2a.js +145 -4
- package/src/interfaces/cli/commands/agent.js +247 -22
- package/src/interfaces/cli/commands/code.js +1 -1
- package/src/interfaces/cli/commands/commitment.js +5 -0
- package/src/interfaces/cli/commands/config.js +85 -6
- package/src/interfaces/cli/commands/exec.js +90 -1
- package/src/interfaces/cli/commands/image.js +314 -0
- package/src/interfaces/cli/commands/mcp.js +87 -23
- package/src/interfaces/cli/commands/memory.js +35 -22
- package/src/interfaces/cli/commands/panel.js +94 -0
- package/src/interfaces/cli/commands/profile.js +14 -0
- package/src/interfaces/cli/commands/project.js +29 -0
- package/src/interfaces/cli/commands/routine.js +45 -10
- package/src/interfaces/cli/commands/runtime.js +30 -7
- package/src/interfaces/cli/commands/session.js +90 -38
- package/src/interfaces/cli/commands/sessions.js +35 -2
- package/src/interfaces/cli/commands/skills.js +15 -9
- package/src/interfaces/cli/commands/status.js +2 -3
- package/src/interfaces/cli/commands/task.js +54 -5
- package/src/interfaces/cli/commands/telegram.js +12 -4
- package/src/interfaces/cli/commands/transcribe.js +177 -0
- package/src/interfaces/cli/help/index.js +311 -72
- package/src/interfaces/cli/http.js +61 -13
- package/src/interfaces/cli/index.js +1 -1
- package/src/interfaces/cli/routes/agent.js +5 -4
- package/src/interfaces/cli/routes/commitment.js +3 -2
- package/src/interfaces/cli/routes/config.js +7 -2
- package/src/interfaces/cli/routes/image.js +11 -0
- package/src/interfaces/cli/routes/index.js +2 -0
- package/src/interfaces/cli/routes/log.js +1 -1
- package/src/interfaces/cli/routes/memory.js +3 -2
- package/src/interfaces/cli/routes/panel.js +15 -2
- package/src/interfaces/cli/routes/profile.js +3 -2
- package/src/interfaces/cli/routes/transcribe.js +10 -0
- package/src/interfaces/cli/stdin.js +29 -0
- package/src/interfaces/desktop/assets/blobs/amatista.png +0 -0
- package/src/interfaces/desktop/assets/blobs/aqua.png +0 -0
- package/src/interfaces/desktop/assets/blobs/brasa.png +0 -0
- package/src/interfaces/desktop/assets/blobs/campana.png +0 -0
- package/src/interfaces/desktop/assets/blobs/caracol.png +0 -0
- package/src/interfaces/desktop/assets/blobs/carbon.png +0 -0
- package/src/interfaces/desktop/assets/blobs/cobalto.png +0 -0
- package/src/interfaces/desktop/assets/blobs/cometa.png +0 -0
- package/src/interfaces/desktop/assets/blobs/coral.png +0 -0
- package/src/interfaces/desktop/assets/blobs/cubi.png +0 -0
- package/src/interfaces/desktop/assets/blobs/eclipse.png +0 -0
- package/src/interfaces/desktop/assets/blobs/faro.png +0 -0
- package/src/interfaces/desktop/assets/blobs/gajo.png +0 -0
- package/src/interfaces/desktop/assets/blobs/iris.png +0 -0
- package/src/interfaces/desktop/assets/blobs/kiwi.png +0 -0
- package/src/interfaces/desktop/assets/blobs/laguna.png +0 -0
- package/src/interfaces/desktop/assets/blobs/lila.png +0 -0
- package/src/interfaces/desktop/assets/blobs/menta.png +0 -0
- package/src/interfaces/desktop/assets/blobs/nimbo.png +0 -0
- package/src/interfaces/desktop/assets/blobs/noche.png +0 -0
- package/src/interfaces/desktop/assets/blobs/onyx.png +0 -0
- package/src/interfaces/desktop/assets/blobs/orbita.png +0 -0
- package/src/interfaces/desktop/assets/blobs/ovni.png +0 -0
- package/src/interfaces/desktop/assets/blobs/papa.png +0 -0
- package/src/interfaces/desktop/assets/blobs/parche.png +0 -0
- package/src/interfaces/desktop/assets/blobs/peon.png +0 -0
- package/src/interfaces/desktop/assets/blobs/perla.png +0 -0
- package/src/interfaces/desktop/assets/blobs/pino.png +0 -0
- package/src/interfaces/desktop/assets/blobs/quad.png +0 -0
- package/src/interfaces/desktop/assets/blobs/rubi.png +0 -0
- package/src/interfaces/desktop/assets/blobs/saturno.png +0 -0
- package/src/interfaces/desktop/assets/blobs/trebol.png +0 -0
- package/src/interfaces/desktop/assets/blobs/trino.png +0 -0
- package/src/interfaces/desktop/assets/blobs/volcan.png +0 -0
- package/src/interfaces/desktop/assets/blobs/zafiro.png +0 -0
- package/src/interfaces/desktop/assets/notification.mp3 +0 -0
- package/src/interfaces/desktop/main.js +473 -29
- package/src/interfaces/desktop/mascot-blobs.js +41 -0
- package/src/interfaces/desktop/mascot-preload.js +32 -0
- package/src/interfaces/desktop/mascot.css +186 -0
- package/src/interfaces/desktop/mascot.html +40 -0
- package/src/interfaces/desktop/mascot.js +163 -0
- package/src/interfaces/desktop/preload.js +10 -0
- package/src/interfaces/desktop/renderer.js +308 -48
- package/src/interfaces/desktop/style.css +34 -0
- package/src/interfaces/mcp-server/index.js +87 -0
- package/src/interfaces/tui/_shims/util-locale.ts +0 -2
- package/src/interfaces/tui/app.tsx +0 -6
- package/src/interfaces/tui/attach.ts +0 -1
- package/src/interfaces/tui/context/sdk-apx.tsx +0 -4
- package/src/interfaces/tui/plugin/api.tsx +0 -3
- package/src/interfaces/tui/plugin/runtime.ts +0 -3
- package/src/interfaces/tui/thread.ts +0 -16
- package/src/interfaces/web/README.md +22 -14
- package/src/interfaces/web/dist/assets/claude-XtpLmGtF.webp +0 -0
- package/src/interfaces/web/dist/assets/codex-DExPUgvh.webp +0 -0
- package/src/interfaces/web/dist/assets/index-BEoRgIqd.css +32 -0
- package/src/interfaces/web/dist/assets/index-BEoRgIqd.css.br +0 -0
- package/src/interfaces/web/dist/assets/index-BEoRgIqd.css.gz +0 -0
- package/src/interfaces/web/dist/assets/index-C6fXHc1p.js +1074 -0
- package/src/interfaces/web/dist/assets/index-C6fXHc1p.js.br +0 -0
- package/src/interfaces/web/dist/assets/index-C6fXHc1p.js.gz +0 -0
- package/src/interfaces/web/dist/assets/index-C6fXHc1p.js.map +1 -0
- package/src/interfaces/web/dist/assets/index-C6fXHc1p.js.map.br +0 -0
- package/src/interfaces/web/dist/assets/index-C6fXHc1p.js.map.gz +0 -0
- package/src/interfaces/web/dist/favicon/dark/site.webmanifest +12 -6
- package/src/interfaces/web/dist/favicon/white/site.webmanifest +12 -6
- package/src/interfaces/web/dist/index.html +22 -6
- package/src/interfaces/web/dist/index.html.br +0 -0
- package/src/interfaces/web/dist/index.html.gz +0 -0
- package/src/interfaces/web/dist/manifest.webmanifest +25 -0
- package/src/interfaces/web/dist/modules/blobs/amatista.png +0 -0
- package/src/interfaces/web/dist/modules/blobs/aqua.png +0 -0
- package/src/interfaces/web/dist/modules/blobs/brasa.png +0 -0
- package/src/interfaces/web/dist/modules/blobs/campana.png +0 -0
- package/src/interfaces/web/dist/modules/blobs/caracol.png +0 -0
- package/src/interfaces/web/dist/modules/blobs/carbon.png +0 -0
- package/src/interfaces/web/dist/modules/blobs/cobalto.png +0 -0
- package/src/interfaces/web/dist/modules/blobs/cometa.png +0 -0
- package/src/interfaces/web/dist/modules/blobs/coral.png +0 -0
- package/src/interfaces/web/dist/modules/blobs/cubi.png +0 -0
- package/src/interfaces/web/dist/modules/blobs/eclipse.png +0 -0
- package/src/interfaces/web/dist/modules/blobs/faro.png +0 -0
- package/src/interfaces/web/dist/modules/blobs/gajo.png +0 -0
- package/src/interfaces/web/dist/modules/blobs/iris.png +0 -0
- package/src/interfaces/web/dist/modules/blobs/kiwi.png +0 -0
- package/src/interfaces/web/dist/modules/blobs/laguna.png +0 -0
- package/src/interfaces/web/dist/modules/blobs/lila.png +0 -0
- package/src/interfaces/web/dist/modules/blobs/menta.png +0 -0
- package/src/interfaces/web/dist/modules/blobs/nimbo.png +0 -0
- package/src/interfaces/web/dist/modules/blobs/noche.png +0 -0
- package/src/interfaces/web/dist/modules/blobs/onyx.png +0 -0
- package/src/interfaces/web/dist/modules/blobs/orbita.png +0 -0
- package/src/interfaces/web/dist/modules/blobs/ovni.png +0 -0
- package/src/interfaces/web/dist/modules/blobs/papa.png +0 -0
- package/src/interfaces/web/dist/modules/blobs/parche.png +0 -0
- package/src/interfaces/web/dist/modules/blobs/peon.png +0 -0
- package/src/interfaces/web/dist/modules/blobs/perla.png +0 -0
- package/src/interfaces/web/dist/modules/blobs/pino.png +0 -0
- package/src/interfaces/web/dist/modules/blobs/quad.png +0 -0
- package/src/interfaces/web/dist/modules/blobs/rubi.png +0 -0
- package/src/interfaces/web/dist/modules/blobs/saturno.png +0 -0
- package/src/interfaces/web/dist/modules/blobs/trebol.png +0 -0
- package/src/interfaces/web/dist/modules/blobs/trino.png +0 -0
- package/src/interfaces/web/dist/modules/blobs/volcan.png +0 -0
- package/src/interfaces/web/dist/modules/blobs/zafiro.png +0 -0
- package/src/interfaces/web/dist/sw.js +106 -0
- package/src/interfaces/web/dist/sw.js.br +0 -0
- package/src/interfaces/web/dist/sw.js.gz +0 -0
- package/src/interfaces/web/eslint.config.js +83 -0
- package/src/interfaces/web/index.html +20 -4
- package/src/interfaces/web/package-lock.json +1651 -1280
- package/src/interfaces/web/package.json +9 -10
- package/src/interfaces/web/pnpm-lock.yaml +867 -869
- package/src/interfaces/web/public/favicon/dark/site.webmanifest +12 -6
- package/src/interfaces/web/public/favicon/white/site.webmanifest +12 -6
- package/src/interfaces/web/public/manifest.webmanifest +25 -0
- package/src/interfaces/web/public/modules/blobs/amatista.png +0 -0
- package/src/interfaces/web/public/modules/blobs/aqua.png +0 -0
- package/src/interfaces/web/public/modules/blobs/brasa.png +0 -0
- package/src/interfaces/web/public/modules/blobs/campana.png +0 -0
- package/src/interfaces/web/public/modules/blobs/caracol.png +0 -0
- package/src/interfaces/web/public/modules/blobs/carbon.png +0 -0
- package/src/interfaces/web/public/modules/blobs/cobalto.png +0 -0
- package/src/interfaces/web/public/modules/blobs/cometa.png +0 -0
- package/src/interfaces/web/public/modules/blobs/coral.png +0 -0
- package/src/interfaces/web/public/modules/blobs/cubi.png +0 -0
- package/src/interfaces/web/public/modules/blobs/eclipse.png +0 -0
- package/src/interfaces/web/public/modules/blobs/faro.png +0 -0
- package/src/interfaces/web/public/modules/blobs/gajo.png +0 -0
- package/src/interfaces/web/public/modules/blobs/iris.png +0 -0
- package/src/interfaces/web/public/modules/blobs/kiwi.png +0 -0
- package/src/interfaces/web/public/modules/blobs/laguna.png +0 -0
- package/src/interfaces/web/public/modules/blobs/lila.png +0 -0
- package/src/interfaces/web/public/modules/blobs/menta.png +0 -0
- package/src/interfaces/web/public/modules/blobs/nimbo.png +0 -0
- package/src/interfaces/web/public/modules/blobs/noche.png +0 -0
- package/src/interfaces/web/public/modules/blobs/onyx.png +0 -0
- package/src/interfaces/web/public/modules/blobs/orbita.png +0 -0
- package/src/interfaces/web/public/modules/blobs/ovni.png +0 -0
- package/src/interfaces/web/public/modules/blobs/papa.png +0 -0
- package/src/interfaces/web/public/modules/blobs/parche.png +0 -0
- package/src/interfaces/web/public/modules/blobs/peon.png +0 -0
- package/src/interfaces/web/public/modules/blobs/perla.png +0 -0
- package/src/interfaces/web/public/modules/blobs/pino.png +0 -0
- package/src/interfaces/web/public/modules/blobs/quad.png +0 -0
- package/src/interfaces/web/public/modules/blobs/rubi.png +0 -0
- package/src/interfaces/web/public/modules/blobs/saturno.png +0 -0
- package/src/interfaces/web/public/modules/blobs/trebol.png +0 -0
- package/src/interfaces/web/public/modules/blobs/trino.png +0 -0
- package/src/interfaces/web/public/modules/blobs/volcan.png +0 -0
- package/src/interfaces/web/public/modules/blobs/zafiro.png +0 -0
- package/src/interfaces/web/public/sw.js +106 -0
- package/src/interfaces/web/src/App.tsx +106 -10
- package/src/interfaces/web/src/assets/cli/claude.webp +0 -0
- package/src/interfaces/web/src/assets/cli/codex.webp +0 -0
- package/src/interfaces/web/src/assets/cli/cursor.svg +1 -0
- package/src/interfaces/web/src/assets/cli/opencode.png +0 -0
- package/src/interfaces/web/src/components/Combobox.tsx +201 -0
- package/src/interfaces/web/src/components/MobileHint.tsx +95 -0
- package/src/interfaces/web/src/components/MobileLinkDialog.tsx +156 -0
- package/src/interfaces/web/src/components/ModelCombobox.tsx +21 -113
- package/src/interfaces/web/src/components/{routines/ReadOnlyBlock.tsx → ReadOnlyBlock.tsx} +13 -4
- package/src/interfaces/web/src/components/Roby.tsx +5 -3
- package/src/interfaces/web/src/components/RobyBubble.tsx +23 -5
- package/src/interfaces/web/src/components/RowMenu.tsx +46 -0
- package/src/interfaces/web/src/components/TimezoneSelect.tsx +4 -1
- package/src/interfaces/web/src/components/Toast.tsx +50 -14
- package/src/interfaces/web/src/components/UiSelect.tsx +12 -5
- package/src/interfaces/web/src/components/agents/AgentAvatar.tsx +171 -0
- package/src/interfaces/web/src/components/agents/AgentFormFields.tsx +42 -11
- package/src/interfaces/web/src/components/agents/AgentModelBadge.tsx +157 -0
- package/src/interfaces/web/src/components/agents/AgentModelSelect.tsx +57 -0
- package/src/interfaces/web/src/components/agents/AgentSkillsPicker.tsx +115 -0
- package/src/interfaces/web/src/components/agents/BlobAvatar.tsx +50 -0
- package/src/interfaces/web/src/components/agents/blobPresets.ts +50 -0
- package/src/interfaces/web/src/components/agents/modelCatalog.ts +66 -0
- package/src/interfaces/web/src/components/chat/ActionGroup.tsx +162 -0
- package/src/interfaces/web/src/components/chat/AskAnswersCard.tsx +4 -3
- package/src/interfaces/web/src/components/chat/AskQuestionsCard.tsx +1 -1
- package/src/interfaces/web/src/components/chat/Attachment.tsx +220 -0
- package/src/interfaces/web/src/components/chat/ChatList.tsx +226 -51
- package/src/interfaces/web/src/components/chat/ChatRowActivity.tsx +45 -0
- package/src/interfaces/web/src/components/chat/Composer.tsx +672 -23
- package/src/interfaces/web/src/components/chat/ComposerCommands.tsx +175 -0
- package/src/interfaces/web/src/components/chat/ComposerSuggest.tsx +215 -0
- package/src/interfaces/web/src/components/chat/ContextBar.tsx +133 -65
- package/src/interfaces/web/src/components/chat/InlineAskPanel.tsx +107 -47
- package/src/interfaces/web/src/components/chat/MessageBubble.tsx +460 -119
- package/src/interfaces/web/src/components/chat/MessageList.tsx +165 -18
- package/src/interfaces/web/src/components/chat/ReasoningBlock.tsx +58 -0
- package/src/interfaces/web/src/components/chat/SessionPicker.tsx +182 -0
- package/src/interfaces/web/src/components/chat/SkillTrace.tsx +183 -0
- package/src/interfaces/web/src/components/chat/ToolCall.tsx +11 -11
- package/src/interfaces/web/src/components/code/CodeContextTab.tsx +87 -53
- package/src/interfaces/web/src/components/code/CodeFileTree.tsx +11 -52
- package/src/interfaces/web/src/components/code/CodeFileViewer.tsx +5 -4
- package/src/interfaces/web/src/components/code/CodeProjectPicker.tsx +31 -7
- package/src/interfaces/web/src/components/code/CodeSessionList.tsx +32 -11
- package/src/interfaces/web/src/components/code/CodeSidePanel.tsx +33 -3
- package/src/interfaces/web/src/components/code/CodeTerminal.tsx +5 -4
- package/src/interfaces/web/src/components/code/NewCodeSessionDialog.tsx +164 -0
- package/src/interfaces/web/src/components/commitments/CommitmentDetail.tsx +162 -0
- package/src/interfaces/web/src/components/commitments/CommitmentFormDialog.tsx +158 -0
- package/src/interfaces/web/src/components/commitments/CommitmentList.tsx +194 -0
- package/src/interfaces/web/src/components/commitments/commitmentState.tsx +46 -0
- package/src/interfaces/web/src/components/common/BulkActionBar.tsx +60 -0
- package/src/interfaces/web/src/components/common/ConfirmDialog.tsx +4 -1
- package/src/interfaces/web/src/components/common/SelectCheckbox.tsx +40 -0
- package/src/interfaces/web/src/components/common/TabNav.tsx +1 -1
- package/src/interfaces/web/src/components/cron/CronPicker.tsx +1 -1
- package/src/interfaces/web/src/components/files/FileBrowser.tsx +1 -1
- package/src/interfaces/web/src/components/files/FileTree.tsx +6 -4
- package/src/interfaces/web/src/components/files/FileViewer.tsx +9 -5
- package/src/interfaces/web/src/components/files/MarkdownPreview.tsx +45 -9
- package/src/interfaces/web/src/components/images/ImageDefaultsCard.tsx +93 -0
- package/src/interfaces/web/src/components/images/ImageProviderList.tsx +142 -0
- package/src/interfaces/web/src/components/images/ImageProviderModal.tsx +263 -0
- package/src/interfaces/web/src/components/images/ImageTestCard.tsx +142 -0
- package/src/interfaces/web/src/components/inbox/ChannelFilter.tsx +178 -0
- package/src/interfaces/web/src/components/inbox/InboxList.tsx +77 -81
- package/src/interfaces/web/src/components/inbox/InboxRowItem.tsx +140 -0
- package/src/interfaces/web/src/components/integrations/ComingSoonPlugin.tsx +2 -2
- package/src/interfaces/web/src/components/integrations/PluginConnect.tsx +208 -30
- package/src/interfaces/web/src/components/integrations/PluginToolsSection.tsx +1 -1
- package/src/interfaces/web/src/components/layout/ProjectAvatar.tsx +39 -24
- package/src/interfaces/web/src/components/layout/ProjectSidebar.tsx +253 -58
- package/src/interfaces/web/src/components/memory/MemoryBrowser.tsx +94 -47
- package/src/interfaces/web/src/components/routines/ExecutionsList.tsx +202 -90
- package/src/interfaces/web/src/components/routines/RoutineDetail.tsx +28 -6
- package/src/interfaces/web/src/components/routines/RoutineEditor.tsx +16 -5
- package/src/interfaces/web/src/components/routines/RoutineList.tsx +49 -9
- package/src/interfaces/web/src/components/routines/VarTextarea.tsx +1 -1
- package/src/interfaces/web/src/components/routines/runChat.ts +121 -0
- package/src/interfaces/web/src/components/sessions/SessionDetail.tsx +148 -0
- package/src/interfaces/web/src/components/sessions/SessionTerminal.tsx +98 -0
- package/src/interfaces/web/src/components/settings/AccessPanel.tsx +205 -0
- package/src/interfaces/web/src/components/settings/DefaultRouterCard.tsx +246 -101
- package/src/interfaces/web/src/components/settings/DesktopSettingsPanel.tsx +36 -2
- package/src/interfaces/web/src/components/settings/DevicesPanel.tsx +19 -6
- package/src/interfaces/web/src/components/settings/EmbedProviderList.tsx +274 -0
- package/src/interfaces/web/src/components/settings/EnginesPanel.tsx +26 -6
- package/src/interfaces/web/src/components/settings/MemoryPanel.tsx +118 -119
- package/src/interfaces/web/src/components/settings/NudgePanel.tsx +4 -3
- package/src/interfaces/web/src/components/settings/PairDeviceDialog.tsx +15 -4
- package/src/interfaces/web/src/components/settings/PanelPrefs.tsx +374 -0
- package/src/interfaces/web/src/components/settings/ProfilePanel.tsx +70 -2
- package/src/interfaces/web/src/components/settings/RoutingPanel.tsx +2 -1
- package/src/interfaces/web/src/components/settings/SkillsInspectorPanel.tsx +102 -43
- package/src/interfaces/web/src/components/settings/SkillsManager.tsx +76 -21
- package/src/interfaces/web/src/components/settings/SkillsSettings.tsx +10 -3
- package/src/interfaces/web/src/components/settings/SuperAgentPanel.tsx +8 -0
- package/src/interfaces/web/src/components/settings/TelegramChannelsPanel.tsx +11 -2
- package/src/interfaces/web/src/components/settings/TelegramContactsPanel.tsx +19 -8
- package/src/interfaces/web/src/components/settings/TelegramRolesPanel.tsx +12 -2
- package/src/interfaces/web/src/components/settings/WebPanel.tsx +22 -31
- package/src/interfaces/web/src/components/settings/providers/ProviderCard.tsx +15 -3
- package/src/interfaces/web/src/components/settings/providers/ProviderModal.tsx +97 -66
- package/src/interfaces/web/src/components/settings/providers/ProviderTestDialog.tsx +150 -0
- package/src/interfaces/web/src/components/settings/providers/typeStyles.ts +67 -25
- package/src/interfaces/web/src/components/settings/providers/types.ts +3 -0
- package/src/interfaces/web/src/components/tasks/TaskBoard.tsx +201 -0
- package/src/interfaces/web/src/components/tasks/TaskColumnsDialog.tsx +243 -0
- package/src/interfaces/web/src/components/tasks/TaskComments.tsx +167 -0
- package/src/interfaces/web/src/components/tasks/TaskDetail.tsx +335 -0
- package/src/interfaces/web/src/components/tasks/TaskFormDialog.tsx +406 -0
- package/src/interfaces/web/src/components/tasks/TaskList.tsx +258 -0
- package/src/interfaces/web/src/components/tasks/TaskSubtasks.tsx +126 -0
- package/src/interfaces/web/src/components/tasks/columns.ts +51 -0
- package/src/interfaces/web/src/components/tasks/taskStatus.tsx +159 -34
- package/src/interfaces/web/src/components/tasks/taskViewPrefs.ts +73 -0
- package/src/interfaces/web/src/components/tasks/useTaskColumns.ts +31 -0
- package/src/interfaces/web/src/components/ui/chat-input.tsx +136 -12
- package/src/interfaces/web/src/components/ui/context-menu.tsx +15 -198
- package/src/interfaces/web/src/components/ui/dialog.tsx +3 -2
- package/src/interfaces/web/src/components/ui/filter-chips.tsx +4 -0
- package/src/interfaces/web/src/components/ui/popover.tsx +14 -49
- package/src/interfaces/web/src/components/ui/sheet.tsx +2 -1
- package/src/interfaces/web/src/components/ui/spinner.tsx +2 -1
- package/src/interfaces/web/src/components/ui.tsx +62 -23
- package/src/interfaces/web/src/components/voice/VoiceProviderList.tsx +2 -1
- package/src/interfaces/web/src/components/voice/VoiceProviderModal.tsx +66 -8
- package/src/interfaces/web/src/components/voice/VoiceQvoxInstallCard.tsx +63 -0
- package/src/interfaces/web/src/components/voice/VoiceQvoxStatusCard.tsx +51 -0
- package/src/interfaces/web/src/components/voice/VoiceSttCard.tsx +4 -3
- package/src/interfaces/web/src/components/voice/VoiceTestCard.tsx +38 -4
- package/src/interfaces/web/src/constants/index.ts +1 -0
- package/src/interfaces/web/src/hooks/useChannelPrefs.ts +39 -0
- package/src/interfaces/web/src/hooks/useChat.ts +1092 -85
- package/src/interfaces/web/src/hooks/useChatActivity.ts +24 -0
- package/src/interfaces/web/src/hooks/useInbox.ts +26 -4
- package/src/interfaces/web/src/hooks/useLiveMessages.ts +16 -0
- package/src/interfaces/web/src/hooks/useOllamaModels.ts +79 -0
- package/src/interfaces/web/src/hooks/useRecorder.ts +198 -0
- package/src/interfaces/web/src/hooks/useTokenBootstrap.ts +23 -6
- package/src/interfaces/web/src/i18n/en.ts +832 -175
- package/src/interfaces/web/src/i18n/es.ts +841 -188
- package/src/interfaces/web/src/lib/api/agents.ts +23 -4
- package/src/interfaces/web/src/lib/api/code.ts +17 -0
- package/src/interfaces/web/src/lib/api/commitments.ts +7 -1
- package/src/interfaces/web/src/lib/api/conversations.ts +38 -11
- package/src/interfaces/web/src/lib/api/embeddings.ts +3 -0
- package/src/interfaces/web/src/lib/api/engines.ts +14 -0
- package/src/interfaces/web/src/lib/api/groups.ts +93 -0
- package/src/interfaces/web/src/lib/api/images.ts +193 -0
- package/src/interfaces/web/src/lib/api/inbox.ts +29 -5
- package/src/interfaces/web/src/lib/api/integrations.ts +9 -0
- package/src/interfaces/web/src/lib/api/media.ts +126 -0
- package/src/interfaces/web/src/lib/api/net.ts +44 -0
- package/src/interfaces/web/src/lib/api/profiles.ts +12 -0
- package/src/interfaces/web/src/lib/api/projects.ts +9 -1
- package/src/interfaces/web/src/lib/api/routines.ts +9 -1
- package/src/interfaces/web/src/lib/api/sessions.ts +18 -0
- package/src/interfaces/web/src/lib/api/skills.ts +8 -0
- package/src/interfaces/web/src/lib/api/super_agent.ts +3 -0
- package/src/interfaces/web/src/lib/api/tasks.ts +46 -6
- package/src/interfaces/web/src/lib/api/tools.ts +2 -0
- package/src/interfaces/web/src/lib/api/transcribe.ts +31 -0
- package/src/interfaces/web/src/lib/api/turns.ts +20 -0
- package/src/interfaces/web/src/lib/api/voice.ts +72 -2
- package/src/interfaces/web/src/lib/api.ts +3 -0
- package/src/interfaces/web/src/lib/channels.ts +214 -0
- package/src/interfaces/web/src/lib/chat-activity.ts +175 -0
- package/src/interfaces/web/src/lib/chat-prefs.ts +48 -0
- package/src/interfaces/web/src/lib/code-context.ts +0 -2
- package/src/interfaces/web/src/lib/http.ts +51 -17
- package/src/interfaces/web/src/lib/inbox-selection.ts +29 -0
- package/src/interfaces/web/src/lib/live.ts +252 -0
- package/src/interfaces/web/src/lib/net.ts +211 -0
- package/src/interfaces/web/src/lib/notify.ts +331 -0
- package/src/interfaces/web/src/lib/projectNav.ts +40 -0
- package/src/interfaces/web/src/lib/pwa.ts +115 -0
- package/src/interfaces/web/src/lib/qvox.ts +24 -0
- package/src/interfaces/web/src/lib/slug.ts +3 -0
- package/src/interfaces/web/src/lib/tone.ts +150 -0
- package/src/interfaces/web/src/main.tsx +15 -4
- package/src/interfaces/web/src/screens/ApxAdminScreen.tsx +33 -11
- package/src/interfaces/web/src/screens/InboxScreen.tsx +126 -71
- package/src/interfaces/web/src/screens/ProjectScreen.tsx +8 -4
- package/src/interfaces/web/src/screens/SettingsScreen.tsx +7 -4
- package/src/interfaces/web/src/screens/base/AgentDefaultsTab.tsx +30 -9
- package/src/interfaces/web/src/screens/base/CommitmentsTab.tsx +150 -168
- package/src/interfaces/web/src/screens/base/LogsTab.tsx +8 -7
- package/src/interfaces/web/src/screens/base/SessionsTab.tsx +99 -37
- package/src/interfaces/web/src/screens/base/WorkspacesTab.tsx +1 -1
- package/src/interfaces/web/src/screens/mobile/MobileChat.tsx +85 -0
- package/src/interfaces/web/src/screens/mobile/MobileChatList.tsx +244 -0
- package/src/interfaces/web/src/screens/mobile/MobileCommitments.tsx +350 -0
- package/src/interfaces/web/src/screens/mobile/MobileScreen.tsx +138 -0
- package/src/interfaces/web/src/screens/mobile/MobileTabBar.tsx +105 -0
- package/src/interfaces/web/src/screens/mobile/MobileTasks.tsx +407 -0
- package/src/interfaces/web/src/screens/mobile/NewChatSheet.tsx +313 -0
- package/src/interfaces/web/src/screens/mobile/mobileList.tsx +194 -0
- package/src/interfaces/web/src/screens/mobile/routes.ts +239 -0
- package/src/interfaces/web/src/screens/modules/CodeScreen.tsx +135 -64
- package/src/interfaces/web/src/screens/modules/DeckScreen.tsx +0 -1
- package/src/interfaces/web/src/screens/modules/ImagesScreen.tsx +172 -0
- package/src/interfaces/web/src/screens/modules/VoiceScreen.tsx +36 -4
- package/src/interfaces/web/src/screens/project/AgentBrainGraph.tsx +117 -30
- package/src/interfaces/web/src/screens/project/AgentDetailScreen.tsx +537 -92
- package/src/interfaces/web/src/screens/project/AgentsTab.tsx +344 -93
- package/src/interfaces/web/src/screens/project/ArtifactsTab.tsx +1 -1
- package/src/interfaces/web/src/screens/project/ChatTab.tsx +1160 -94
- package/src/interfaces/web/src/screens/project/IntegrationsTab.tsx +3 -2
- package/src/interfaces/web/src/screens/project/McpsTab.tsx +62 -14
- package/src/interfaces/web/src/screens/project/Overview.tsx +36 -15
- package/src/interfaces/web/src/screens/project/RoutinesTab.tsx +93 -8
- package/src/interfaces/web/src/screens/project/SkillsTab.tsx +4 -1
- package/src/interfaces/web/src/screens/project/StructureTab.tsx +5 -4
- package/src/interfaces/web/src/screens/project/TasksTab.tsx +351 -129
- package/src/interfaces/web/src/screens/project/VarsTab.tsx +17 -5
- package/src/interfaces/web/src/styles.css +106 -31
- package/src/interfaces/web/src/types/daemon.ts +246 -2
- package/src/interfaces/web/src/vite-env.d.ts +20 -0
- package/src/interfaces/tui/plugin/command-shim.ts +0 -109
- package/src/interfaces/web/components.json +0 -25
- package/src/interfaces/web/dist/assets/index-CvEoGtTf.js +0 -849
- package/src/interfaces/web/dist/assets/index-CvEoGtTf.js.map +0 -1
- package/src/interfaces/web/dist/assets/index-DzBBXFaO.css +0 -1
- package/src/interfaces/web/src/components/tasks/TaskDetailPanel.tsx +0 -142
- package/src/interfaces/web/src/components/ui/accordion.tsx +0 -72
- package/src/interfaces/web/src/components/ui/alert-dialog.tsx +0 -187
- package/src/interfaces/web/src/components/ui/alert.tsx +0 -76
- package/src/interfaces/web/src/components/ui/aspect-ratio.tsx +0 -22
- package/src/interfaces/web/src/components/ui/avatar.tsx +0 -107
- package/src/interfaces/web/src/components/ui/breadcrumb.tsx +0 -125
- package/src/interfaces/web/src/components/ui/button-group.tsx +0 -87
- package/src/interfaces/web/src/components/ui/calendar.tsx +0 -221
- package/src/interfaces/web/src/components/ui/card.tsx +0 -103
- package/src/interfaces/web/src/components/ui/carousel.tsx +0 -242
- package/src/interfaces/web/src/components/ui/chart.tsx +0 -371
- package/src/interfaces/web/src/components/ui/checkbox.tsx +0 -29
- package/src/interfaces/web/src/components/ui/collapsible.tsx +0 -19
- package/src/interfaces/web/src/components/ui/combobox.tsx +0 -295
- package/src/interfaces/web/src/components/ui/command.tsx +0 -196
- package/src/interfaces/web/src/components/ui/direction.tsx +0 -4
- package/src/interfaces/web/src/components/ui/drawer.tsx +0 -134
- package/src/interfaces/web/src/components/ui/empty.tsx +0 -104
- package/src/interfaces/web/src/components/ui/field.tsx +0 -236
- package/src/interfaces/web/src/components/ui/hover-card.tsx +0 -51
- package/src/interfaces/web/src/components/ui/input-otp.tsx +0 -85
- package/src/interfaces/web/src/components/ui/item.tsx +0 -201
- package/src/interfaces/web/src/components/ui/menubar.tsx +0 -280
- package/src/interfaces/web/src/components/ui/native-select.tsx +0 -61
- package/src/interfaces/web/src/components/ui/navigation-menu.tsx +0 -168
- package/src/interfaces/web/src/components/ui/pagination.tsx +0 -130
- package/src/interfaces/web/src/components/ui/progress.tsx +0 -83
- package/src/interfaces/web/src/components/ui/radio-group.tsx +0 -36
- package/src/interfaces/web/src/components/ui/resizable.tsx +0 -50
- package/src/interfaces/web/src/components/ui/scroll-area.tsx +0 -53
- package/src/interfaces/web/src/components/ui/sidebar.tsx +0 -726
- package/src/interfaces/web/src/components/ui/slider.tsx +0 -52
- package/src/interfaces/web/src/components/ui/sonner.tsx +0 -49
- package/src/interfaces/web/src/components/ui/table.tsx +0 -116
- package/src/interfaces/web/src/components/ui/toggle-group.tsx +0 -87
- package/src/interfaces/web/src/components/ui/toggle.tsx +0 -45
- package/src/interfaces/web/src/hooks/use-mobile.ts +0 -19
- package/src/interfaces/web/src/screens/base/GlobalTasksTab.tsx +0 -161
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
package dev.agentprojectcontext.apx;
|
|
2
|
+
|
|
3
|
+
import android.app.Notification;
|
|
4
|
+
import android.app.NotificationChannel;
|
|
5
|
+
import android.app.NotificationManager;
|
|
6
|
+
import android.app.PendingIntent;
|
|
7
|
+
import android.content.Context;
|
|
8
|
+
import android.content.Intent;
|
|
9
|
+
|
|
10
|
+
import androidx.core.app.NotificationCompat;
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* The proximity card on the PHONE: "you are 1.4 km from Farmacia del Puente,
|
|
14
|
+
* Av. San Martín 1234 — Comprar ibuprofeno", and four buttons.
|
|
15
|
+
*
|
|
16
|
+
* WHY THIS IS NOT A CarMessageNotification. That one is a MessagingStyle
|
|
17
|
+
* conversation, and Android Auto only ever renders two controls on those —
|
|
18
|
+
* reply and mark-as-read. Four answers do not fit in a shape built for a chat.
|
|
19
|
+
* On the head unit the same alert is drawn by ApxCarAppService, which uses the
|
|
20
|
+
* Car App Library and can show all four; this class is the phone's copy.
|
|
21
|
+
*
|
|
22
|
+
* NOTHING HERE ADDS A GLYPH. The daemon sends the text already stripped
|
|
23
|
+
* (core/mobility/geofence.js) because Assistant reads it out loud, and
|
|
24
|
+
* decorating it here would put the emoji straight back into the sentence.
|
|
25
|
+
*
|
|
26
|
+
* ONE CARD PER ALERT, keyed by the alert id: two errands can come into range
|
|
27
|
+
* on the same GPS sample, and collapsing them into one notification would lose
|
|
28
|
+
* one of the two answers.
|
|
29
|
+
*/
|
|
30
|
+
final class ProximityNotification {
|
|
31
|
+
/**
|
|
32
|
+
* v2 because a channel is IMMUTABLE once created: createNotificationChannel
|
|
33
|
+
* on an existing id updates the name and description and silently ignores
|
|
34
|
+
* importance, sound and vibration. The v1 channel shipped soundless and
|
|
35
|
+
* therefore non-alerting, so fixing it in place would have changed nothing
|
|
36
|
+
* on any phone that had already run the app.
|
|
37
|
+
*/
|
|
38
|
+
static final String CHANNEL = "apx_proximity_v2";
|
|
39
|
+
static final String ACTION_ANSWER = "dev.agentprojectcontext.apx.PROXIMITY_ANSWER";
|
|
40
|
+
static final String EXTRA_ALERT_ID = "alert_id";
|
|
41
|
+
static final String EXTRA_ACTION_ID = "action_id";
|
|
42
|
+
static final String EXTRA_URL = "url";
|
|
43
|
+
static final String EXTRA_NOTIFICATION_ID = "notification_id";
|
|
44
|
+
|
|
45
|
+
private ProximityNotification() {}
|
|
46
|
+
|
|
47
|
+
/** Stable per alert, so a re-sent card replaces itself instead of stacking. */
|
|
48
|
+
static int notificationId(String alertId) {
|
|
49
|
+
return 7200 + Math.abs(alertId.hashCode() % 700);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
static void show(Context context, MessageFrameParser.MobilityAlert alert) {
|
|
53
|
+
ensureChannel(context);
|
|
54
|
+
int id = notificationId(alert.id());
|
|
55
|
+
|
|
56
|
+
NotificationCompat.Builder builder = new NotificationCompat.Builder(context, CHANNEL)
|
|
57
|
+
.setSmallIcon(R.drawable.ic_apx_notification)
|
|
58
|
+
.setContentTitle(alert.title())
|
|
59
|
+
.setContentText(alert.body())
|
|
60
|
+
.setStyle(new NotificationCompat.BigTextStyle().bigText(alert.body()))
|
|
61
|
+
// NAVIGATION, not MESSAGE: this is a place and a route, and the
|
|
62
|
+
// category is what tells the system (and the car) which it is.
|
|
63
|
+
.setCategory(NotificationCompat.CATEGORY_NAVIGATION)
|
|
64
|
+
.setPriority(NotificationCompat.PRIORITY_HIGH)
|
|
65
|
+
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
|
|
66
|
+
// NOT onlyAlertOnce: each alert is its own place and its own
|
|
67
|
+
// decision, and the flag is what let the second one of a drive
|
|
68
|
+
// arrive without announcing itself.
|
|
69
|
+
.setAutoCancel(true)
|
|
70
|
+
// Its own bundle, so One UI does not fold it into the app's
|
|
71
|
+
// aggregated section where the actions are a tap away instead of
|
|
72
|
+
// on screen.
|
|
73
|
+
.setGroup("apx_proximity")
|
|
74
|
+
.setDefaults(NotificationCompat.DEFAULT_VIBRATE)
|
|
75
|
+
.setContentIntent(openApp(context, id));
|
|
76
|
+
|
|
77
|
+
// The daemon decides the four and their order — it owns the wording and
|
|
78
|
+
// the translations. Android draws what it is given rather than keeping
|
|
79
|
+
// its own copy of the list, so adding a fifth never needs a new APK.
|
|
80
|
+
int requestCode = 0;
|
|
81
|
+
for (MessageFrameParser.Action action : alert.actions()) {
|
|
82
|
+
String url = switch (action.id()) {
|
|
83
|
+
case "navigate" -> alert.navigateUrl();
|
|
84
|
+
case "add_stop" -> alert.addStopUrl();
|
|
85
|
+
default -> null;
|
|
86
|
+
};
|
|
87
|
+
builder.addAction(new NotificationCompat.Action.Builder(
|
|
88
|
+
R.drawable.ic_apx_notification,
|
|
89
|
+
action.label(),
|
|
90
|
+
answerIntent(context, alert.id(), action.id(), url, id, requestCode++)
|
|
91
|
+
).build());
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
context.getSystemService(NotificationManager.class).notify(id, builder.build());
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
static void cancel(Context context, int id) {
|
|
98
|
+
context.getSystemService(NotificationManager.class).cancel(id);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
private static PendingIntent answerIntent(
|
|
102
|
+
Context context, String alertId, String actionId, String url, int notificationId, int requestCode
|
|
103
|
+
) {
|
|
104
|
+
Intent intent = new Intent(context, ProximityActionReceiver.class)
|
|
105
|
+
.setAction(ACTION_ANSWER)
|
|
106
|
+
.putExtra(EXTRA_ALERT_ID, alertId)
|
|
107
|
+
.putExtra(EXTRA_ACTION_ID, actionId)
|
|
108
|
+
.putExtra(EXTRA_NOTIFICATION_ID, notificationId);
|
|
109
|
+
if (url != null) intent.putExtra(EXTRA_URL, url);
|
|
110
|
+
return PendingIntent.getBroadcast(
|
|
111
|
+
context,
|
|
112
|
+
// The alert id is part of the request code or two open cards would
|
|
113
|
+
// share one PendingIntent and the second would answer the first.
|
|
114
|
+
notificationId * 10 + requestCode,
|
|
115
|
+
intent,
|
|
116
|
+
PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
private static PendingIntent openApp(Context context, int notificationId) {
|
|
121
|
+
Intent open = new Intent(context, MainActivity.class)
|
|
122
|
+
.putExtra(MainActivity.EXTRA_PATH, "/mobile")
|
|
123
|
+
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP);
|
|
124
|
+
return PendingIntent.getActivity(
|
|
125
|
+
context, notificationId, open,
|
|
126
|
+
PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/** The soundless v1. Left behind by an upgrade, and dead. */
|
|
131
|
+
private static final String LEGACY_CHANNEL = "apx_proximity";
|
|
132
|
+
|
|
133
|
+
private static void ensureChannel(Context context) {
|
|
134
|
+
NotificationManager manager = context.getSystemService(NotificationManager.class);
|
|
135
|
+
// An abandoned channel does not disappear on its own: it stays listed
|
|
136
|
+
// in the app's notification settings forever, with the same name as the
|
|
137
|
+
// live one, and whatever the owner toggles on it does nothing.
|
|
138
|
+
manager.deleteNotificationChannel(LEGACY_CHANNEL);
|
|
139
|
+
NotificationChannel channel = new NotificationChannel(
|
|
140
|
+
CHANNEL,
|
|
141
|
+
context.getString(R.string.proximity_channel),
|
|
142
|
+
NotificationManager.IMPORTANCE_HIGH
|
|
143
|
+
);
|
|
144
|
+
channel.setDescription("Avisos de lugares cerca tuyo durante un viaje.");
|
|
145
|
+
// Silent, because MascotOverlayService already plays the one APX sound
|
|
146
|
+
// for the event and a channel tone would ring the same alert twice.
|
|
147
|
+
channel.setSound(null, null);
|
|
148
|
+
// BUT VIBRATING, and that pairing is the whole point. A channel with no
|
|
149
|
+
// sound AND no vibration is treated as non-alerting by One UI and
|
|
150
|
+
// MagicOS: the card was posted with its four actions, landed folded
|
|
151
|
+
// into the aggregated section, and never peeked — so the driver saw a
|
|
152
|
+
// line of text and no buttons, which is exactly the bug this is. The
|
|
153
|
+
// vibration keeps it a heads-up without a second sound.
|
|
154
|
+
channel.enableVibration(true);
|
|
155
|
+
channel.setVibrationPattern(new long[] { 0, 250 });
|
|
156
|
+
// A proximity alert is time-boxed by the car moving; it has to be
|
|
157
|
+
// readable on the lock screen while driving.
|
|
158
|
+
channel.setLockscreenVisibility(android.app.Notification.VISIBILITY_PUBLIC);
|
|
159
|
+
manager.createNotificationChannel(channel);
|
|
160
|
+
}
|
|
161
|
+
}
|
package/src/interfaces/android/app/src/main/java/dev/agentprojectcontext/apx/StartupReceiver.java
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
package dev.agentprojectcontext.apx;
|
|
2
|
+
|
|
3
|
+
import android.content.BroadcastReceiver;
|
|
4
|
+
import android.content.Context;
|
|
5
|
+
import android.content.Intent;
|
|
6
|
+
import android.os.Build;
|
|
7
|
+
import android.util.Log;
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Brings the socket back after the two moments that silently kill it.
|
|
11
|
+
*
|
|
12
|
+
* MascotOverlayService is what holds the connection to the daemon and raises
|
|
13
|
+
* every bubble, sound and Android Auto card. Nothing but MainActivity used to
|
|
14
|
+
* start it, so the phone went quiet in two cases where the owner has no reason
|
|
15
|
+
* to suspect anything is wrong:
|
|
16
|
+
*
|
|
17
|
+
* - a REBOOT: nothing runs until an app is opened, and APX was never opened;
|
|
18
|
+
* - an UPDATE: installing an APK force-stops the package, and the service does
|
|
19
|
+
* not come back on its own. Measured on both phones — zero ServiceRecords
|
|
20
|
+
* right after `adb install -r`, back the instant MainActivity ran.
|
|
21
|
+
*
|
|
22
|
+
* "Notifications stopped working" and "I have not opened APX since Tuesday" are
|
|
23
|
+
* the same sentence, and only one of them is visible from the outside.
|
|
24
|
+
*/
|
|
25
|
+
public final class StartupReceiver extends BroadcastReceiver {
|
|
26
|
+
private static final String TAG = "APX";
|
|
27
|
+
|
|
28
|
+
@Override
|
|
29
|
+
public void onReceive(Context context, Intent intent) {
|
|
30
|
+
String action = intent == null ? null : intent.getAction();
|
|
31
|
+
if (!Intent.ACTION_BOOT_COMPLETED.equals(action)
|
|
32
|
+
&& !Intent.ACTION_MY_PACKAGE_REPLACED.equals(action)) {
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
// An unpaired install has nothing to connect to. The service would
|
|
36
|
+
// stopSelf() the moment it looked, so the cheaper answer is not to
|
|
37
|
+
// start it — and a phone that never finished pairing costs nothing.
|
|
38
|
+
if (!new ApxPreferences(context).paired()) return;
|
|
39
|
+
|
|
40
|
+
Intent service = new Intent(context, MascotOverlayService.class);
|
|
41
|
+
try {
|
|
42
|
+
if (Build.VERSION.SDK_INT >= 26) {
|
|
43
|
+
context.startForegroundService(service);
|
|
44
|
+
} else {
|
|
45
|
+
context.startService(service);
|
|
46
|
+
}
|
|
47
|
+
} catch (RuntimeException refused) {
|
|
48
|
+
// Same shape as TripLocationService: SecurityException (the app is
|
|
49
|
+
// not in an eligible state) and ForegroundServiceStartNotAllowedException
|
|
50
|
+
// are both RuntimeException, and a boot broadcast is exactly where
|
|
51
|
+
// Android is entitled to say no. Losing the notifications until the
|
|
52
|
+
// owner next opens APX is the old behaviour; taking the phone's boot
|
|
53
|
+
// down with an uncaught crash is not.
|
|
54
|
+
Log.w(TAG, "Android refused the startup service: " + refused.getMessage());
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
package/src/interfaces/android/app/src/main/java/dev/agentprojectcontext/apx/TravelEventGate.java
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
package dev.agentprojectcontext.apx;
|
|
2
|
+
|
|
3
|
+
import java.util.Locale;
|
|
4
|
+
|
|
5
|
+
final class TravelEventGate {
|
|
6
|
+
static final long KNOWN_DESTINATION_SETTLE_MS = 45_000L;
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* How long to wait before telling the daemon about a trip whose destination
|
|
10
|
+
* Maps has not published.
|
|
11
|
+
*
|
|
12
|
+
* WAS TEN MINUTES, and that was protecting the wrong thing. The long wait
|
|
13
|
+
* was there so a destination arriving late would be used instead of firing
|
|
14
|
+
* a "where are you going?" on a trip that was about to name itself — but
|
|
15
|
+
* MapsNavigationListenerService already handles that case directly: when
|
|
16
|
+
* the destination resolves it calls scheduleDaemonNotification(..., true),
|
|
17
|
+
* which REPLACES the pending notification with a 45 s known-destination
|
|
18
|
+
* one. The long settle never had to catch it.
|
|
19
|
+
*
|
|
20
|
+
* So on the case it actually governs — a drive with no route set, which is
|
|
21
|
+
* most short local ones — ten minutes was pure delay, and the trip-start
|
|
22
|
+
* message landed when the drive was half over. Three minutes still absorbs
|
|
23
|
+
* Maps posting and removing its notification while a route is being set up
|
|
24
|
+
* (the removal debounce covers the rest), and leaves the message useful.
|
|
25
|
+
*
|
|
26
|
+
* It does NOT gate proximity alerts. The state-only event and the GPS
|
|
27
|
+
* service start the moment navigation is detected — see the class comment
|
|
28
|
+
* in MapsNavigationListenerService and the rule in rules/android.md.
|
|
29
|
+
*/
|
|
30
|
+
static final long UNKNOWN_DESTINATION_SETTLE_MS = 3 * 60_000L;
|
|
31
|
+
static final long DIFFERENT_DESTINATION_COOLDOWN_MS = 5 * 60_000L;
|
|
32
|
+
static final long SAME_DESTINATION_COOLDOWN_MS = 30 * 60_000L;
|
|
33
|
+
|
|
34
|
+
private TravelEventGate() {}
|
|
35
|
+
|
|
36
|
+
static long delayMs(long now, String destination, long lastSentAt, String lastDestination) {
|
|
37
|
+
String normalized = normalize(destination);
|
|
38
|
+
long settle = normalized.isBlank()
|
|
39
|
+
? UNKNOWN_DESTINATION_SETTLE_MS
|
|
40
|
+
: KNOWN_DESTINATION_SETTLE_MS;
|
|
41
|
+
if (lastSentAt <= 0L) return settle;
|
|
42
|
+
|
|
43
|
+
long cooldown = normalized.equals(normalize(lastDestination))
|
|
44
|
+
? SAME_DESTINATION_COOLDOWN_MS
|
|
45
|
+
: DIFFERENT_DESTINATION_COOLDOWN_MS;
|
|
46
|
+
long remaining = Math.max(0L, lastSentAt + cooldown - now);
|
|
47
|
+
return Math.max(settle, remaining);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
static boolean coolingDown(long now, String destination, long lastSentAt, String lastDestination) {
|
|
51
|
+
if (lastSentAt <= 0L) return false;
|
|
52
|
+
String normalized = normalize(destination);
|
|
53
|
+
long cooldown = normalized.equals(normalize(lastDestination))
|
|
54
|
+
? SAME_DESTINATION_COOLDOWN_MS
|
|
55
|
+
: DIFFERENT_DESTINATION_COOLDOWN_MS;
|
|
56
|
+
return now < lastSentAt + cooldown;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
private static String normalize(String value) {
|
|
60
|
+
return value == null ? "" : value.trim().toLowerCase(Locale.ROOT);
|
|
61
|
+
}
|
|
62
|
+
}
|
package/src/interfaces/android/app/src/main/java/dev/agentprojectcontext/apx/TravelStatusBanner.java
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
package dev.agentprojectcontext.apx;
|
|
2
|
+
|
|
3
|
+
import android.content.Context;
|
|
4
|
+
import android.graphics.Color;
|
|
5
|
+
import android.graphics.Typeface;
|
|
6
|
+
import android.text.TextUtils;
|
|
7
|
+
import android.view.Gravity;
|
|
8
|
+
import android.view.View;
|
|
9
|
+
import android.widget.ImageView;
|
|
10
|
+
import android.widget.LinearLayout;
|
|
11
|
+
import android.widget.TextView;
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* The strip above `/mobile` that says a trip is in progress.
|
|
15
|
+
*
|
|
16
|
+
* It used to offer "Abrir Maps", which was the wrong destination: Maps is
|
|
17
|
+
* already open — it is what APX is reading the trip from. What the owner has
|
|
18
|
+
* no way to see is what APX is watching for on THIS drive, so the whole strip
|
|
19
|
+
* now opens that list instead (MainActivity.showTripPlaces).
|
|
20
|
+
*/
|
|
21
|
+
final class TravelStatusBanner extends LinearLayout {
|
|
22
|
+
private final TextView title;
|
|
23
|
+
private final TextView detail;
|
|
24
|
+
private final TextView affordance;
|
|
25
|
+
|
|
26
|
+
TravelStatusBanner(Context context) {
|
|
27
|
+
super(context);
|
|
28
|
+
setOrientation(HORIZONTAL);
|
|
29
|
+
setGravity(Gravity.CENTER_VERTICAL);
|
|
30
|
+
setPadding(dp(16), dp(11), dp(16), dp(11));
|
|
31
|
+
setBackgroundColor(Color.rgb(21, 48, 40));
|
|
32
|
+
setClickable(true);
|
|
33
|
+
setFocusable(true);
|
|
34
|
+
|
|
35
|
+
ImageView icon = new ImageView(context);
|
|
36
|
+
icon.setImageResource(R.drawable.ic_trip_car);
|
|
37
|
+
LayoutParams iconParams = new LayoutParams(dp(30), dp(30));
|
|
38
|
+
iconParams.rightMargin = dp(12);
|
|
39
|
+
addView(icon, iconParams);
|
|
40
|
+
|
|
41
|
+
LinearLayout copy = new LinearLayout(context);
|
|
42
|
+
copy.setOrientation(VERTICAL);
|
|
43
|
+
// One line each, ellipsized: the strip sits above the whole app and a
|
|
44
|
+
// subtitle that wraps pushes `/mobile` down and collides with the
|
|
45
|
+
// affordance on the right.
|
|
46
|
+
title = new TextView(context);
|
|
47
|
+
title.setTextColor(Color.WHITE);
|
|
48
|
+
title.setTextSize(15);
|
|
49
|
+
title.setTypeface(null, Typeface.BOLD);
|
|
50
|
+
title.setMaxLines(1);
|
|
51
|
+
title.setEllipsize(TextUtils.TruncateAt.END);
|
|
52
|
+
detail = new TextView(context);
|
|
53
|
+
detail.setTextColor(Color.rgb(166, 220, 202));
|
|
54
|
+
detail.setTextSize(12);
|
|
55
|
+
detail.setMaxLines(1);
|
|
56
|
+
detail.setEllipsize(TextUtils.TruncateAt.END);
|
|
57
|
+
copy.addView(title, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
|
|
58
|
+
copy.addView(detail, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
|
|
59
|
+
addView(copy, new LayoutParams(0, LayoutParams.WRAP_CONTENT, 1f));
|
|
60
|
+
|
|
61
|
+
affordance = new TextView(context);
|
|
62
|
+
affordance.setTextColor(Color.rgb(58, 231, 176));
|
|
63
|
+
affordance.setTextSize(12);
|
|
64
|
+
affordance.setTypeface(null, Typeface.BOLD);
|
|
65
|
+
affordance.setMaxLines(1);
|
|
66
|
+
LayoutParams affordanceParams = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
|
|
67
|
+
affordanceParams.leftMargin = dp(10);
|
|
68
|
+
addView(affordance, affordanceParams);
|
|
69
|
+
setVisibility(View.GONE);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
void update(boolean active, String destination, String source) {
|
|
73
|
+
update(active, destination, source, -1);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* `source` decides the sub-line, because "no destination yet" means two
|
|
78
|
+
* different things: Maps has not published one, or there is no route at all
|
|
79
|
+
* and APX only knows the phone is plugged into the car.
|
|
80
|
+
*
|
|
81
|
+
* `places` is how many errands APX is watching on this trip, or -1 while
|
|
82
|
+
* that is still unknown — the count comes from the daemon, so the banner
|
|
83
|
+
* has to render correctly before the first answer arrives.
|
|
84
|
+
*/
|
|
85
|
+
void update(boolean active, String destination, String source, int places) {
|
|
86
|
+
if (!active) {
|
|
87
|
+
setVisibility(View.GONE);
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
boolean known = destination != null && !destination.isBlank();
|
|
91
|
+
boolean sharedLink = known && (destination.startsWith("https://maps.app.goo.gl/")
|
|
92
|
+
|| destination.startsWith("https://goo.gl/maps/"));
|
|
93
|
+
boolean projecting = ApxPreferences.SOURCE_ANDROID_AUTO.equals(source);
|
|
94
|
+
title.setText(known
|
|
95
|
+
? "Estás en camino"
|
|
96
|
+
: projecting ? "Estás en el auto" : "Estás navegando por una ruta");
|
|
97
|
+
detail.setText(sharedLink
|
|
98
|
+
? "Viaje compartido desde Google Maps"
|
|
99
|
+
: known ? "Destino: " + destination
|
|
100
|
+
: projecting ? "Android Auto conectado · sin ruta cargada"
|
|
101
|
+
: "Google Maps activo · destino todavía no disponible");
|
|
102
|
+
affordance.setText(placesLabel(places));
|
|
103
|
+
setVisibility(View.VISIBLE);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/** Kept short on purpose: it shares one line with the trip's own status. */
|
|
107
|
+
private String placesLabel(int places) {
|
|
108
|
+
if (places < 0) return "Pendientes ›";
|
|
109
|
+
if (places == 0) return "Nada cerca ›";
|
|
110
|
+
return places + " cerca ›";
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
private int dp(int value) {
|
|
114
|
+
return Math.round(value * getResources().getDisplayMetrics().density);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
package dev.agentprojectcontext.apx;
|
|
2
|
+
|
|
3
|
+
import android.Manifest;
|
|
4
|
+
import android.app.Notification;
|
|
5
|
+
import android.app.NotificationChannel;
|
|
6
|
+
import android.app.NotificationManager;
|
|
7
|
+
import android.app.PendingIntent;
|
|
8
|
+
import android.app.Service;
|
|
9
|
+
import android.content.Context;
|
|
10
|
+
import android.content.Intent;
|
|
11
|
+
import android.content.pm.PackageManager;
|
|
12
|
+
import android.content.pm.ServiceInfo;
|
|
13
|
+
import android.location.Location;
|
|
14
|
+
import android.location.LocationListener;
|
|
15
|
+
import android.location.LocationManager;
|
|
16
|
+
import android.os.Build;
|
|
17
|
+
import android.os.IBinder;
|
|
18
|
+
import android.os.SystemClock;
|
|
19
|
+
import android.util.Log;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Streams the phone's position to the paired daemon while a trip is running.
|
|
23
|
+
*
|
|
24
|
+
* A foreground service, and not a timer inside the notification listener, for
|
|
25
|
+
* one reason: from Android 10 on, an app that is not in the foreground gets no
|
|
26
|
+
* location at all without ACCESS_BACKGROUND_LOCATION — and a foreground
|
|
27
|
+
* service typed `location` grants exactly the while-in-use access this needs
|
|
28
|
+
* without asking the owner for the far broader always-on permission. The
|
|
29
|
+
* ongoing notification it is obliged to show is a feature here, not a cost:
|
|
30
|
+
* the phone says out loud that APX is following the trip, and tapping it opens
|
|
31
|
+
* the app to stop.
|
|
32
|
+
*
|
|
33
|
+
* Nothing about a route is uploaded. Each sample is a point plus its accuracy,
|
|
34
|
+
* the daemon answers it with at most a proximity reminder, and no location
|
|
35
|
+
* history is written anywhere — see rules/android.md.
|
|
36
|
+
*/
|
|
37
|
+
public final class TripLocationService extends Service {
|
|
38
|
+
private static final String TAG = "APXTravel";
|
|
39
|
+
private static final String CHANNEL = "apx_trip_location";
|
|
40
|
+
private static final int NOTIFICATION_ID = 7300;
|
|
41
|
+
static final String EXTRA_TRIP_ID = "trip_id";
|
|
42
|
+
|
|
43
|
+
// 30 s / 150 m. Fast enough that a 2 km geofence cannot be crossed and
|
|
44
|
+
// left between two samples at city speed, slow enough that a whole drive
|
|
45
|
+
// costs a couple of hundred requests rather than a couple of thousand.
|
|
46
|
+
private static final long MIN_INTERVAL_MS = 30_000L;
|
|
47
|
+
private static final float MIN_DISTANCE_M = 150f;
|
|
48
|
+
// The provider can hand us a fix more often than we asked; this is the
|
|
49
|
+
// floor on what actually leaves the phone.
|
|
50
|
+
private static final long MIN_UPLOAD_INTERVAL_MS = 25_000L;
|
|
51
|
+
|
|
52
|
+
private ApxPreferences preferences;
|
|
53
|
+
private LocationManager locations;
|
|
54
|
+
private String tripId = "";
|
|
55
|
+
private long lastUploadElapsedMs;
|
|
56
|
+
|
|
57
|
+
private final LocationListener listener = new LocationListener() {
|
|
58
|
+
@Override
|
|
59
|
+
public void onLocationChanged(Location location) {
|
|
60
|
+
publish(location);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Required on API < 30; harmless above it.
|
|
64
|
+
@Override
|
|
65
|
+
public void onProviderEnabled(String provider) {}
|
|
66
|
+
|
|
67
|
+
@Override
|
|
68
|
+
public void onProviderDisabled(String provider) {}
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Begin reporting for `tripId`. Safe to call again for the same trip — the
|
|
73
|
+
* service just re-reads the id. Returns false when Android refused the
|
|
74
|
+
* start (a background foreground-service start is not always allowed), so
|
|
75
|
+
* the caller can log it rather than assume tracking is live.
|
|
76
|
+
*/
|
|
77
|
+
static boolean start(Context context, String tripId) {
|
|
78
|
+
Intent intent = new Intent(context, TripLocationService.class).putExtra(EXTRA_TRIP_ID, tripId);
|
|
79
|
+
try {
|
|
80
|
+
context.startForegroundService(intent);
|
|
81
|
+
return true;
|
|
82
|
+
} catch (RuntimeException refused) {
|
|
83
|
+
Log.w(TAG, "Could not start trip location tracking", refused);
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
static void stop(Context context) {
|
|
89
|
+
try {
|
|
90
|
+
context.stopService(new Intent(context, TripLocationService.class));
|
|
91
|
+
} catch (RuntimeException ignored) {
|
|
92
|
+
// Already gone; nothing to stop.
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/** Do we have the permission this service exists to use? */
|
|
97
|
+
static boolean canTrack(Context context) {
|
|
98
|
+
return context.checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED
|
|
99
|
+
|| context.checkSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
@Override
|
|
103
|
+
public void onCreate() {
|
|
104
|
+
super.onCreate();
|
|
105
|
+
preferences = new ApxPreferences(this);
|
|
106
|
+
locations = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
|
|
107
|
+
ensureChannel();
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
@Override
|
|
111
|
+
public int onStartCommand(Intent intent, int flags, int startId) {
|
|
112
|
+
String requested = intent == null ? "" : String.valueOf(intent.getStringExtra(EXTRA_TRIP_ID));
|
|
113
|
+
if (requested != null && !requested.isBlank() && !"null".equals(requested)) tripId = requested;
|
|
114
|
+
// Permission first. startForeground() with the `location` type THROWS
|
|
115
|
+
// when the app may not use location, and this used to run before the
|
|
116
|
+
// check — so the whole app died with a SecurityException at the exact
|
|
117
|
+
// moment a trip began.
|
|
118
|
+
if (!canTrack(this)) {
|
|
119
|
+
Log.w(TAG, "Trip tracking asked for without location permission");
|
|
120
|
+
stopSelf();
|
|
121
|
+
return START_NOT_STICKY;
|
|
122
|
+
}
|
|
123
|
+
boolean foreground = startForegroundCompat();
|
|
124
|
+
// Announced either way: a trip we cannot FOLLOW is still a trip, and
|
|
125
|
+
// the daemon already has an origin to reason about.
|
|
126
|
+
announceTrip();
|
|
127
|
+
if (!foreground) {
|
|
128
|
+
stopSelf();
|
|
129
|
+
return START_NOT_STICKY;
|
|
130
|
+
}
|
|
131
|
+
requestUpdates();
|
|
132
|
+
// Send the fix we already have so the daemon can evaluate proximity
|
|
133
|
+
// immediately instead of waiting up to 30 s for the first update.
|
|
134
|
+
publish(newestFix());
|
|
135
|
+
// START_STICKY: a trip outlives a low-memory kill, and the listener
|
|
136
|
+
// service is what ends it — not the system.
|
|
137
|
+
return START_STICKY;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
@Override
|
|
141
|
+
public void onDestroy() {
|
|
142
|
+
try {
|
|
143
|
+
locations.removeUpdates(listener);
|
|
144
|
+
} catch (RuntimeException ignored) {
|
|
145
|
+
// Never registered (missing permission) — nothing to remove.
|
|
146
|
+
}
|
|
147
|
+
super.onDestroy();
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
@Override
|
|
151
|
+
public IBinder onBind(Intent intent) {
|
|
152
|
+
return null;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
private void requestUpdates() {
|
|
156
|
+
for (String provider : new String[] { LocationManager.GPS_PROVIDER, LocationManager.NETWORK_PROVIDER }) {
|
|
157
|
+
try {
|
|
158
|
+
if (!locations.isProviderEnabled(provider)) continue;
|
|
159
|
+
locations.requestLocationUpdates(provider, MIN_INTERVAL_MS, MIN_DISTANCE_M, listener);
|
|
160
|
+
} catch (SecurityException | IllegalArgumentException unavailable) {
|
|
161
|
+
Log.w(TAG, "Provider " + provider + " unavailable: " + unavailable.getMessage());
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
private Location newestFix() {
|
|
167
|
+
try {
|
|
168
|
+
Location newest = null;
|
|
169
|
+
for (String provider : locations.getProviders(true)) {
|
|
170
|
+
Location candidate = locations.getLastKnownLocation(provider);
|
|
171
|
+
if (candidate == null) continue;
|
|
172
|
+
if (newest == null || candidate.getTime() > newest.getTime()) newest = candidate;
|
|
173
|
+
}
|
|
174
|
+
return newest;
|
|
175
|
+
} catch (SecurityException denied) {
|
|
176
|
+
return null;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
private void announceTrip() {
|
|
181
|
+
String trip = tripId.isBlank() ? preferences.travelTripId() : tripId;
|
|
182
|
+
if (trip.isBlank()) return;
|
|
183
|
+
DaemonClient.notifyTripContext(
|
|
184
|
+
preferences.daemonUrl(),
|
|
185
|
+
preferences.token(),
|
|
186
|
+
trip,
|
|
187
|
+
preferences.travelDestination(),
|
|
188
|
+
DeviceLocation.latest(this)
|
|
189
|
+
);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
private void publish(Location location) {
|
|
193
|
+
if (location == null) return;
|
|
194
|
+
if (!preferences.travelActive()) {
|
|
195
|
+
// The trip ended between two fixes. Stop rather than report a
|
|
196
|
+
// position for a journey the daemon has already closed.
|
|
197
|
+
stopSelf();
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
long now = SystemClock.elapsedRealtime();
|
|
201
|
+
if (lastUploadElapsedMs != 0L && now - lastUploadElapsedMs < MIN_UPLOAD_INTERVAL_MS) return;
|
|
202
|
+
lastUploadElapsedMs = now;
|
|
203
|
+
String trip = tripId.isBlank() ? preferences.travelTripId() : tripId;
|
|
204
|
+
if (trip.isBlank()) return;
|
|
205
|
+
DaemonClient.reportPosition(preferences.daemonUrl(), preferences.token(), trip, location);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Go foreground, or say why not.
|
|
210
|
+
*
|
|
211
|
+
* Holding ACCESS_FINE_LOCATION is not sufficient on its own: it is a
|
|
212
|
+
* while-in-use permission, so Android also requires the app to be in an
|
|
213
|
+
* "eligible state" to start a `location`-typed foreground service — and it
|
|
214
|
+
* refuses with a SecurityException when it is not. The refusal is routine
|
|
215
|
+
* rather than exceptional: the notification listener that detects a trip
|
|
216
|
+
* runs in the background, which is exactly the ineligible case, and this
|
|
217
|
+
* threw straight through onStartCommand and killed the app every time a
|
|
218
|
+
* drive began with APX not on screen.
|
|
219
|
+
*
|
|
220
|
+
* Losing the GPS stream is a real cost — no proximity reminders on this
|
|
221
|
+
* leg — but it is recoverable and a crash is not: MainActivity retries the
|
|
222
|
+
* start on resume, when the app IS eligible, and "APX menu → Remove battery
|
|
223
|
+
* restriction" makes the background start succeed on its own.
|
|
224
|
+
*/
|
|
225
|
+
private boolean startForegroundCompat() {
|
|
226
|
+
Notification notice = buildNotification();
|
|
227
|
+
try {
|
|
228
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
|
|
229
|
+
startForeground(NOTIFICATION_ID, notice, ServiceInfo.FOREGROUND_SERVICE_TYPE_LOCATION);
|
|
230
|
+
} else {
|
|
231
|
+
startForeground(NOTIFICATION_ID, notice);
|
|
232
|
+
}
|
|
233
|
+
return true;
|
|
234
|
+
} catch (RuntimeException refused) {
|
|
235
|
+
// SecurityException (not eligible) and
|
|
236
|
+
// ForegroundServiceStartNotAllowedException are both RuntimeException.
|
|
237
|
+
Log.w(TAG, "Android refused the trip tracking service: " + refused.getMessage());
|
|
238
|
+
return false;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
private Notification buildNotification() {
|
|
243
|
+
Intent open = new Intent(this, MainActivity.class).putExtra(MainActivity.EXTRA_SETTINGS, true);
|
|
244
|
+
PendingIntent pending = PendingIntent.getActivity(
|
|
245
|
+
this,
|
|
246
|
+
8,
|
|
247
|
+
open,
|
|
248
|
+
PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE
|
|
249
|
+
);
|
|
250
|
+
String destination = preferences.travelDestination();
|
|
251
|
+
return new Notification.Builder(this, CHANNEL)
|
|
252
|
+
.setSmallIcon(R.drawable.ic_apx_notification)
|
|
253
|
+
.setContentTitle(getString(R.string.trip_tracking_title))
|
|
254
|
+
.setContentText(destination.isBlank()
|
|
255
|
+
? getString(R.string.trip_tracking_text)
|
|
256
|
+
: getString(R.string.trip_tracking_text_destination, destination))
|
|
257
|
+
.setContentIntent(pending)
|
|
258
|
+
.setOngoing(true)
|
|
259
|
+
.build();
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
private void ensureChannel() {
|
|
263
|
+
NotificationManager manager = getSystemService(NotificationManager.class);
|
|
264
|
+
NotificationChannel channel = new NotificationChannel(
|
|
265
|
+
CHANNEL,
|
|
266
|
+
getString(R.string.trip_tracking_channel),
|
|
267
|
+
NotificationManager.IMPORTANCE_LOW
|
|
268
|
+
);
|
|
269
|
+
channel.setDescription(getString(R.string.trip_tracking_channel_description));
|
|
270
|
+
manager.createNotificationChannel(channel);
|
|
271
|
+
}
|
|
272
|
+
}
|