From c68cd3da12dfb9b6798869bd0d45daed95d0a193 Mon Sep 17 00:00:00 2001 From: Luc LETOFFE Date: Tue, 24 Feb 2026 10:29:54 +0100 Subject: [PATCH 1/3] feat(android): add configurable network access setting Add a Settings screen with a toggle to allow network access to the embedded ActivityWatch server. When enabled, the server binds to 0.0.0.0 instead of 127.0.0.1, allowing other devices on the local network to connect. The setting is off by default. Enabling it shows a security warning dialog explaining the risks (no authentication on the API, data exposed to the network). The change takes effect on app restart. This also wires up the previously-unimplemented Settings action bar button to open the new Settings screen. The aw-server-rust submodule is updated to accept a host parameter in the JNI startServer() call instead of using a hardcoded address. Closes #121, closes #107 --- aw-server-rust | 2 +- mobile/src/main/AndroidManifest.xml | 6 +++ .../activitywatch/android/AWPreferences.kt | 13 +++++ .../net/activitywatch/android/MainActivity.kt | 7 +-- .../activitywatch/android/RustInterface.kt | 10 ++-- .../activitywatch/android/SettingsActivity.kt | 54 +++++++++++++++++++ .../src/main/res/layout/activity_settings.xml | 24 +++++++++ mobile/src/main/res/values/strings.xml | 9 ++++ 8 files changed, 116 insertions(+), 9 deletions(-) create mode 100644 mobile/src/main/java/net/activitywatch/android/SettingsActivity.kt create mode 100644 mobile/src/main/res/layout/activity_settings.xml diff --git a/aw-server-rust b/aw-server-rust index dc70318e..7a5c1484 160000 --- a/aw-server-rust +++ b/aw-server-rust @@ -1 +1 @@ -Subproject commit dc70318e819efc0d0535a5d7bd35a0c7ab8e9106 +Subproject commit 7a5c14841ee7b33b29576d4b2cc2003cacd52ac2 diff --git a/mobile/src/main/AndroidManifest.xml b/mobile/src/main/AndroidManifest.xml index c983de3a..6fe919b2 100644 --- a/mobile/src/main/AndroidManifest.xml +++ b/mobile/src/main/AndroidManifest.xml @@ -48,6 +48,12 @@ android:theme="@style/AppTheme.NoActionBar" android:exported="true"/> + + { - Snackbar.make(binding.coordinatorLayout, "The settings button was clicked, but it's not yet implemented!", Snackbar.LENGTH_LONG) - .setAction("Action", null).show() + val intent = Intent(this, SettingsActivity::class.java) + startActivity(intent) true } else -> super.onOptionsItemSelected(item) diff --git a/mobile/src/main/java/net/activitywatch/android/RustInterface.kt b/mobile/src/main/java/net/activitywatch/android/RustInterface.kt index 01b68c50..1fc1d8bd 100644 --- a/mobile/src/main/java/net/activitywatch/android/RustInterface.kt +++ b/mobile/src/main/java/net/activitywatch/android/RustInterface.kt @@ -40,7 +40,7 @@ class RustInterface constructor(context: Context? = null) { private external fun initialize(): String private external fun greeting(pattern: String): String - private external fun startServer() + private external fun startServer(host: String) private external fun setDataDir(path: String) external fun getBuckets(): String external fun createBucket(bucket: String): String @@ -51,7 +51,7 @@ class RustInterface constructor(context: Context? = null) { return greeting(to) } - fun startServerTask(context: Context) { + fun startServerTask(context: Context, host: String = "127.0.0.1") { if(!serverStarted) { // check if port 5600 is already in use try { @@ -69,8 +69,8 @@ class RustInterface constructor(context: Context? = null) { // will not block the UI thread // Start server - Log.w(TAG, "Starting server...") - startServer() + Log.w(TAG, "Starting server on $host...") + startServer(host) handler.post { // will run on UI thread after the task is done @@ -78,7 +78,7 @@ class RustInterface constructor(context: Context? = null) { serverStarted = false } } - Log.w(TAG, "Server started") + Log.w(TAG, "Server started on $host") } } diff --git a/mobile/src/main/java/net/activitywatch/android/SettingsActivity.kt b/mobile/src/main/java/net/activitywatch/android/SettingsActivity.kt new file mode 100644 index 00000000..6c9dd453 --- /dev/null +++ b/mobile/src/main/java/net/activitywatch/android/SettingsActivity.kt @@ -0,0 +1,54 @@ +package net.activitywatch.android + +import android.os.Bundle +import android.widget.Toast +import androidx.appcompat.app.AlertDialog +import androidx.appcompat.app.AppCompatActivity +import com.google.android.material.switchmaterial.SwitchMaterial + +class SettingsActivity : AppCompatActivity() { + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_settings) + + supportActionBar?.setDisplayHomeAsUpEnabled(true) + supportActionBar?.title = getString(R.string.settings_title) + + val prefs = AWPreferences(this) + val switchNetworkAccess = findViewById(R.id.switchNetworkAccess) + switchNetworkAccess.isChecked = prefs.isNetworkAccessEnabled() + + switchNetworkAccess.setOnCheckedChangeListener { _, isChecked -> + if (isChecked) { + // Show security warning before enabling + AlertDialog.Builder(this) + .setTitle(R.string.network_access_warning_title) + .setMessage(R.string.network_access_warning_message) + .setPositiveButton(R.string.network_access_warning_enable) { _, _ -> + prefs.setNetworkAccessEnabled(true) + showRestartNotice() + } + .setNegativeButton(android.R.string.cancel) { _, _ -> + switchNetworkAccess.isChecked = false + } + .setOnCancelListener { + switchNetworkAccess.isChecked = false + } + .show() + } else { + prefs.setNetworkAccessEnabled(false) + showRestartNotice() + } + } + } + + private fun showRestartNotice() { + Toast.makeText(this, R.string.network_access_restart_notice, Toast.LENGTH_LONG).show() + } + + override fun onSupportNavigateUp(): Boolean { + finish() + return true + } +} diff --git a/mobile/src/main/res/layout/activity_settings.xml b/mobile/src/main/res/layout/activity_settings.xml new file mode 100644 index 00000000..055c9af6 --- /dev/null +++ b/mobile/src/main/res/layout/activity_settings.xml @@ -0,0 +1,24 @@ + + + + + + + + diff --git a/mobile/src/main/res/values/strings.xml b/mobile/src/main/res/values/strings.xml index fd66293d..82d2e656 100644 --- a/mobile/src/main/res/values/strings.xml +++ b/mobile/src/main/res/values/strings.xml @@ -26,4 +26,13 @@ Hello blank fragment + + + Settings + Allow network access + Allow other devices on the local network to connect to the ActivityWatch server. When disabled, only this device can access the server. + Security warning + Enabling network access will allow any device on the same network to access your ActivityWatch data.\n\nThe ActivityWatch API does not have authentication, so anyone on your network will be able to read and modify your activity data.\n\nOnly enable this if you trust your network (e.g. a VPN like Tailscale). + Enable + Restart the app for this change to take effect. From a9a464ff9a214b9c176daf2935cca8a309c659c1 Mon Sep 17 00:00:00 2001 From: Luc LETOFFE Date: Wed, 25 Feb 2026 18:03:51 +0100 Subject: [PATCH 2/3] auto: snapshot feature/configurable-host 2026-02-25 17:03 --- PR-DESCRIPTION.md | 39 +++++++++++++++++++++++++++++++ PROMPT-PR.md | 58 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 97 insertions(+) create mode 100644 PR-DESCRIPTION.md create mode 100644 PROMPT-PR.md diff --git a/PR-DESCRIPTION.md b/PR-DESCRIPTION.md new file mode 100644 index 00000000..0ba8584e --- /dev/null +++ b/PR-DESCRIPTION.md @@ -0,0 +1,39 @@ +## Add configurable network access setting + +I needed to access the ActivityWatch API from other devices on my network (via Tailscale) to sync data to a central server, so I figured I'd contribute a proper implementation rather than just hardcoding `0.0.0.0`. + +### What this does + +Adds a "Allow network access" toggle in a new native Settings screen, accessible from the action bar settings button (which previously showed a "not yet implemented" snackbar). + +- **Off by default** — server binds to `127.0.0.1` (unchanged behavior) +- **When enabled** — server binds to `0.0.0.0`, allowing connections from other devices on the local network +- Enabling shows a **security warning dialog** explaining that the API has no authentication and activity data will be exposed to the network +- Displays a toast noting the change takes effect on app restart + +### Changes + +**aw-android:** +- `AWPreferences.kt` — new `isNetworkAccessEnabled` / `setNetworkAccessEnabled` preference +- `RustInterface.kt` — `startServer()` and `startServerTask()` now accept a `host` parameter +- `MainActivity.kt` — reads the preference and passes the host to the server; wires the Settings action bar button to the new `SettingsActivity` +- `SettingsActivity.kt` (new) — simple settings screen with the network access toggle + confirmation dialog +- `activity_settings.xml` (new) — layout for the settings screen +- `strings.xml` — added setting labels and warning text +- `AndroidManifest.xml` — registered `SettingsActivity` + +**aw-server-rust** (submodule, [see commit](https://github.com/lucletoffe/aw-server-rust/commit/7a5c14841ee7b33b29576d4b2cc2003cacd52ac2)): +- `android/mod.rs` — `startServer` JNI function now accepts a `host` string parameter and passes it to `AWConfig` instead of relying on the default + +### Security considerations + +The toggle is opt-in with a clear warning. The dialog text mentions: +- No authentication on the API +- Anyone on the same network can read/modify data +- Recommendation to only enable on trusted networks (e.g. VPN) + +### Screenshots + +_Not available yet — will add after building the APK._ + +Closes #121, closes #107 diff --git a/PROMPT-PR.md b/PROMPT-PR.md new file mode 100644 index 00000000..fb221190 --- /dev/null +++ b/PROMPT-PR.md @@ -0,0 +1,58 @@ +# PR ActivityWatch Android — Configurable server bind address + +## Contexte + +Je travaille sur un fork de aw-android (ActivityWatch pour Android) : `~/Projects/aw-android` +- Remote origin : github.com/lucletoffe/aw-android +- Remote upstream : github.com/ActivityWatch/aw-android +- Branche actuelle : feature/configurable-host (un commit quick&dirty : bind 0.0.0.0 en dur) +- Le submodule aw-server-rust est dans ./aw-server-rust/ + +## Le probleme upstream + +Issues #121 et #107 : le serveur AW Android ecoute sur 127.0.0.1 uniquement. Impossible d'y acceder depuis un autre device sur le reseau local. Beaucoup d'utilisateurs veulent pouvoir syncer leurs donnees vers un serveur central ou y acceder depuis leur PC. + +## Ce qu'il faut faire + +Creer une PR propre, quality open-source, avec : + +1. **Reset la branche** : partir d'upstream/master propre, pas du commit 0.0.0.0 en dur + +2. **Cote Kotlin (app Android)** : + - Ajouter un setting "Allow network access" (boolean, default false) dans `mobile/src/main/java/net/activitywatch/android/` (probablement AWPreferences.kt ou equivalent) + - UI : toggle dans les Settings de l'app avec un avertissement securite quand on active + - Quand active : passer "0.0.0.0" comme host au serveur. Quand desactive : "127.0.0.1" + +3. **Cote Rust (aw-server-rust submodule)** : + - Dans `aw-server-rust/aw-server/src/android/mod.rs` : accepter le host comme parametre JNI au lieu du hardcode + - Modifier la signature de `startServer()` JNI pour accepter un parametre host (String) + - Fallback sur 127.0.0.1 si pas fourni (backward compatible) + +4. **Documentation** : + - Mettre a jour le README avec la nouvelle option + - Security note : expliquer les risques (exposition reseau local) + +5. **Commit history propre** : + - 1-2 commits max, messages clairs en anglais + - Pas de traces d'IA dans le code, les commits, ou la PR + - Style de code coherent avec le reste du projet + +6. **PR description** : + - Ton humain, concis. "I needed this for my setup, figured I'd contribute it properly." + - Reference issues #121 et #107 + - Screenshot du setting si possible + - Expliquer le choix : off by default, opt-in, avec warning + +## Fichiers cles a lire d'abord + +- `mobile/src/main/java/net/activitywatch/android/` — code Kotlin de l'app +- `aw-server-rust/aw-server/src/android/mod.rs` — bridge JNI Rust +- `mobile/src/main/java/net/activitywatch/android/watcher/` — watchers +- `mobile/src/main/res/xml/` — preferences XML si existant +- Regarder comment les settings existants sont geres pour copier le pattern + +## Contraintes + +- Je n'ai PAS Android Studio installe, je ne peux PAS builder l'APK dans cette session. Focus sur le code, la PR sera testee apres. +- Le submodule aw-server-rust pointe vers un commit specifique. Il faudra peut-etre forker ce repo aussi. +- L'auteur du projet (Erik Bjäreholt) est actif et review les PRs. Qualite > vitesse. From 0973508dfa819e9b9d17ef926931f162f80224d1 Mon Sep 17 00:00:00 2001 From: Luc LETOFFE Date: Mon, 2 Mar 2026 11:45:00 +0100 Subject: [PATCH 3/3] auto: snapshot feature/configurable-host 2026-03-02 10:45 [skip ci] --- PROMPT-PR.md | 58 ---------------------------------------------------- 1 file changed, 58 deletions(-) delete mode 100644 PROMPT-PR.md diff --git a/PROMPT-PR.md b/PROMPT-PR.md deleted file mode 100644 index fb221190..00000000 --- a/PROMPT-PR.md +++ /dev/null @@ -1,58 +0,0 @@ -# PR ActivityWatch Android — Configurable server bind address - -## Contexte - -Je travaille sur un fork de aw-android (ActivityWatch pour Android) : `~/Projects/aw-android` -- Remote origin : github.com/lucletoffe/aw-android -- Remote upstream : github.com/ActivityWatch/aw-android -- Branche actuelle : feature/configurable-host (un commit quick&dirty : bind 0.0.0.0 en dur) -- Le submodule aw-server-rust est dans ./aw-server-rust/ - -## Le probleme upstream - -Issues #121 et #107 : le serveur AW Android ecoute sur 127.0.0.1 uniquement. Impossible d'y acceder depuis un autre device sur le reseau local. Beaucoup d'utilisateurs veulent pouvoir syncer leurs donnees vers un serveur central ou y acceder depuis leur PC. - -## Ce qu'il faut faire - -Creer une PR propre, quality open-source, avec : - -1. **Reset la branche** : partir d'upstream/master propre, pas du commit 0.0.0.0 en dur - -2. **Cote Kotlin (app Android)** : - - Ajouter un setting "Allow network access" (boolean, default false) dans `mobile/src/main/java/net/activitywatch/android/` (probablement AWPreferences.kt ou equivalent) - - UI : toggle dans les Settings de l'app avec un avertissement securite quand on active - - Quand active : passer "0.0.0.0" comme host au serveur. Quand desactive : "127.0.0.1" - -3. **Cote Rust (aw-server-rust submodule)** : - - Dans `aw-server-rust/aw-server/src/android/mod.rs` : accepter le host comme parametre JNI au lieu du hardcode - - Modifier la signature de `startServer()` JNI pour accepter un parametre host (String) - - Fallback sur 127.0.0.1 si pas fourni (backward compatible) - -4. **Documentation** : - - Mettre a jour le README avec la nouvelle option - - Security note : expliquer les risques (exposition reseau local) - -5. **Commit history propre** : - - 1-2 commits max, messages clairs en anglais - - Pas de traces d'IA dans le code, les commits, ou la PR - - Style de code coherent avec le reste du projet - -6. **PR description** : - - Ton humain, concis. "I needed this for my setup, figured I'd contribute it properly." - - Reference issues #121 et #107 - - Screenshot du setting si possible - - Expliquer le choix : off by default, opt-in, avec warning - -## Fichiers cles a lire d'abord - -- `mobile/src/main/java/net/activitywatch/android/` — code Kotlin de l'app -- `aw-server-rust/aw-server/src/android/mod.rs` — bridge JNI Rust -- `mobile/src/main/java/net/activitywatch/android/watcher/` — watchers -- `mobile/src/main/res/xml/` — preferences XML si existant -- Regarder comment les settings existants sont geres pour copier le pattern - -## Contraintes - -- Je n'ai PAS Android Studio installe, je ne peux PAS builder l'APK dans cette session. Focus sur le code, la PR sera testee apres. -- Le submodule aw-server-rust pointe vers un commit specifique. Il faudra peut-etre forker ce repo aussi. -- L'auteur du projet (Erik Bjäreholt) est actif et review les PRs. Qualite > vitesse.