Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
59 commits
Select commit Hold shift + click to select a range
aea8bba
fix: Improve ADB connection stability
kangrio Aug 17, 2026
abf02e9
chore: bump version 1.0.3
kangrio Aug 18, 2026
7e54da3
ui: Update description for "Play Sound" setting in SettingsActivity
kangrio Aug 20, 2026
8795e1f
chore: Update .gitignore and remove tracked build artifacts
kangrio Aug 20, 2026
21a2c67
feat: Make microphone permission optional and improve hotword state t…
kangrio Aug 21, 2026
3b0d2f3
feat: Handle microphone permission for wake word toggle
kangrio Aug 21, 2026
0b2302f
beta: fix DiLink device detection logic
kangrio Aug 21, 2026
9103b39
refactor(voice): improve hotword detection restart logic
kangrio Aug 22, 2026
8064ef9
feat: Integrate Audx VAD for efficient wake word detection
kangrio Aug 22, 2026
00336a2
fix(service): use processed audio for detection and improve thread sh…
kangrio Aug 22, 2026
479ee6f
chore: add comment explaining StartActivity redirection in Utils.kt
kangrio Aug 22, 2026
25b3062
chore(ui): make notification permission required in onboarding
kangrio Aug 22, 2026
d61f904
docs(util): add KDoc comments to Utils.kt
kangrio Aug 22, 2026
fefcabc
fix(service): implement detection cooldown and optimize pause/resume …
kangrio Aug 22, 2026
a00be8a
beta: remove x86_64 from abiFilters
kangrio Aug 22, 2026
6bea828
fix: Prevent activity recreation on orientation change
kangrio Aug 22, 2026
81b22fa
fix(settings): use VOICE_RECOGNITION as audio source for sample recor…
kangrio Aug 22, 2026
99b7b67
feat: conditionally require WRITE_SECURE_SETTINGS based on device type
kangrio Aug 22, 2026
2bb02e4
chore: format snowboy sensitivity to 2 decimal places
kangrio Aug 22, 2026
67bf127
feat: Add new Snowboy hotword models and update native libraries
kangrio Aug 23, 2026
76ccfba
chore: format snowboy sensitivity to 1 decimal place
kangrio Aug 23, 2026
950921d
docs: add credits
kangrio Aug 23, 2026
027b545
feat(util): add toast notification for ADB connection status in adbShell
kangrio Aug 23, 2026
d8f769a
fix(util): fix ADB failure toast condition and ensure it runs on main…
kangrio Aug 23, 2026
11ef07a
improve(wakeword): optimize audio processing and align chunk size wit…
kangrio Aug 23, 2026
845ee0d
debug: add log for wake word detection
kangrio Aug 23, 2026
b60a634
Merge branch 'main' into dev
kangrio Aug 23, 2026
1e43f95
debug: add Logcat viewer activity
kangrio Aug 23, 2026
450b4ab
perf(wakeword): reuse output buffer in detection loop to reduce alloc…
kangrio Aug 23, 2026
01d8b62
feat: add configurable audio source for wake-word detection
kangrio Aug 23, 2026
ebe2880
chore: try to reduce audio gain by 50% for solving VAD
kangrio Aug 23, 2026
7007849
chore: increase hotword detection restart delay to 1s
kangrio Aug 23, 2026
700a50f
feat: change default audio source to VOICE_COMMUNICATION and optimize…
kangrio Aug 23, 2026
3518a65
feat: change default audio source to DEFAULT and remove gain reduction
kangrio Aug 23, 2026
27da512
feat(settings): add VOICE_RECOGNITION to audio source options
kangrio Aug 23, 2026
4da66bc
perf(logcat): optimize UI updates with log line batching and reduced …
kangrio Aug 23, 2026
f867845
perf(logcat): refactor log flushing logic and ensure final batch proc…
kangrio Aug 23, 2026
b50c9ee
feat(settings): mark voice detection as experimental and update descr…
kangrio Aug 23, 2026
ccf58ad
refactor(onboarding): remove automatic voice assistant start in finis…
kangrio Aug 23, 2026
867598a
fix(settings): toggle model selection dropdown and refresh list on ex…
kangrio Aug 23, 2026
9b14bbc
feat: migrate wake-word detection engine from Snowboy to openWakeWord
kangrio Aug 23, 2026
ee6675f
feaat: migrate wake-word detection library from openwakeword to Andro…
kangrio Aug 23, 2026
c333dc4
feaat: migrate wake-word detection library from openwakeword to Andro…
kangrio Aug 24, 2026
6f67705
chore: remove Snowboy legacy files and update credits in README
kangrio Aug 24, 2026
9dc3051
feat: copy from assets to app data and load wake-word models from app…
kangrio Aug 24, 2026
f264332
feat: add support for downloading and importing online wake-word models
kangrio Aug 24, 2026
c0f826a
docs: add wakewords-collection to credits in README.md
kangrio Aug 24, 2026
65ae8ff
fix: Set StartActivity launchMode to singleTask
kangrio Aug 24, 2026
52b849f
feat(settings): disable wake word configuration when voice detection …
kangrio Aug 24, 2026
3842d47
build: downgrade onnxruntime-android to 1.23.2
kangrio Aug 24, 2026
3352fd6
feat(settings): add long-press to delete custom wake word models
kangrio Aug 24, 2026
9a4dde6
feat(settings): make dropdown menus scrollable and add height limits
kangrio Aug 24, 2026
b5f491a
debug(wakeword): add classifier score logging in WakeWordDetector
kangrio Aug 24, 2026
469e2c2
debug(logcat): filter out noisy UI tags from logcat output
kangrio Aug 24, 2026
a7b9b4b
fix(service): pause wake-word detection during assistant activation w…
kangrio Aug 24, 2026
db9d3f6
fix(wakeword): implement fallback to default model when specified mod…
kangrio Aug 24, 2026
f697649
feat(wakeword): validate model integrity during import using ONNX Run…
kangrio Aug 24, 2026
b793b90
build: add onnxruntime-android as compileOnly dependency
kangrio Aug 24, 2026
4fbb71a
fix(settings): prevent activity finish when launching file picker for…
kangrio Aug 24, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,8 @@ fabric.properties

# End of https://www.toptal.com/developers/gitignore/api/android,androidstudio

/*/release/
app/release/*
app/beta/*

signing.properties
*.keystore
33 changes: 23 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -176,16 +176,29 @@ Artifacts will be generated in `app/build/outputs/apk/<variant>/` following the

## Permissions Overview

| Permission | Purpose |
| :--- | :--- |
| `RECORD_AUDIO` | Required for wake-word listening and voice sample recording. |
| `FOREGROUND_SERVICE` / `FOREGROUND_SERVICE_MICROPHONE` | Keeps the hotword detector running reliably in the background. |
| `SYSTEM_ALERT_WINDOW` | Enables launching assistant interfaces over active apps. |
| `WRITE_SECURE_SETTINGS` | Configures system default assistant and voice interaction services. |
| `RECEIVE_BOOT_COMPLETED` | Automatically restarts hotword service and secures settings on device boot. |
| `REQUEST_IGNORE_BATTERY_OPTIMIZATIONS` | Prevents Android OS from killing background services in standby. |
| `POST_NOTIFICATIONS` | Displays the foreground service notification on Android 13+. |
| `INTERNET` | Used for local ADB loopback socket communication and wake-word model training. |
| Permission | Purpose |
|:-------------------------------------------------------|:-------------------------------------------------------------------------------|
| `RECORD_AUDIO` | Required for wake-word listening and voice sample recording. |
| `FOREGROUND_SERVICE` / `FOREGROUND_SERVICE_MICROPHONE` | Keeps the hotword detector running reliably in the background. |
| `SYSTEM_ALERT_WINDOW` | Enables launching assistant interfaces over active apps. |
| `WRITE_SECURE_SETTINGS` | Configures system default assistant and voice interaction services. |
| `RECEIVE_BOOT_COMPLETED` | Automatically restarts hotword service and secures settings on device boot. |
| `REQUEST_IGNORE_BATTERY_OPTIMIZATIONS` | Prevents Android OS from killing background services in standby. |
| `POST_NOTIFICATIONS` | Displays the foreground service notification on Android 13+. |
| `INTERNET` | Used for local ADB loopback socket communication and wake-word model training. |

---

## Credits

This project builds upon the following open-source libraries and projects:

| Project | Author / Org | Description |
|:----------------------------------------------------------------------------------------|:-------------|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| [AndroidWakeWord](https://github.com/Suxsem/AndroidWakeWord) | Suxsem | Android library implementing openWakeWord detection using ONNX Runtime and SpeexDSP. |
| [dadb](https://github.com/mobile-dev-inc/dadb) | mobile.dev | A Kotlin library for communicating with Android devices over ADB directly from the device itself. Used here to automatically grant `WRITE_SECURE_SETTINGS` without needing a PC. |
| [wakewords-collection](https://github.com/fwartner/home-assistant-wakewords-collection) | fwartner | Community Collection of Wake-Words for Home Assistant |


---

Expand Down
1 change: 1 addition & 0 deletions androidwakeword/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/build
37 changes: 37 additions & 0 deletions androidwakeword/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
plugins {
alias(libs.plugins.android.library)
}

android {
namespace = "com.suxsem.androidwakeword"
compileSdk {
version = release(37)
}

defaultConfig {
minSdk = 24
ndk {
abiFilters += listOf("arm64-v8a")
}

testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
externalNativeBuild {
cmake {
path("src/main/cpp/CMakeLists.txt")
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
}

dependencies {
implementation(libs.androidx.core.ktx)
implementation(libs.onnxruntime.android)
}
4 changes: 4 additions & 0 deletions androidwakeword/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.RECORD_AUDIO" />
</manifest>
Binary file not shown.
Binary file not shown.
Binary file not shown.
42 changes: 42 additions & 0 deletions androidwakeword/src/main/cpp/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
cmake_minimum_required(VERSION 3.14)
project("speex-wrapper" LANGUAGES C CXX)

# Elenco dei file essenziali per il pre-processing e resample
set(SPEEX_SOURCES
speex/libspeexdsp/preprocess.c
speex/libspeexdsp/resample.c
speex/libspeexdsp/filterbank.c
speex/libspeexdsp/fftwrap.c
speex/libspeexdsp/mdf.c
speex/libspeexdsp/kiss_fft.c
speex/libspeexdsp/kiss_fftr.c
speex/libspeexdsp/buffer.c
speex/libspeexdsp/jitter.c
speex/libspeexdsp/scal.c
speex/libspeexdsp/smallft.c
)
add_library(speex-lib SHARED speex-wrapper.cpp ${SPEEX_SOURCES})

# Qui puntiamo alle cartelle che contengono gli header
target_include_directories(speex-lib PRIVATE
speex/include
speex/include/speex
speex/libspeexdsp
)

target_compile_definitions(speex-lib PRIVATE
FIXED_POINT=1
EXPORT=
USE_KISS_FFT=1
HAVE_STDINT_H=1
)

find_library(log-lib log)
target_link_libraries(speex-lib ${log-lib})

target_compile_options(speex-lib PRIVATE -UHAVE_CONFIG_H)

# Forza l'allineamento a 16KB per la compatibilità con Android 15+
set_target_properties(speex-lib PROPERTIES
LINK_FLAGS "-Wl,-z,max-page-size=16384"
)
85 changes: 85 additions & 0 deletions androidwakeword/src/main/cpp/speex-wrapper.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
#include <jni.h>
#include <string>
#include <vector>
#include "speex/include/speex/speex_preprocess.h"

// Puntatore globale allo stato del preprocessore
// In un'app reale, gestiscilo in modo più sicuro (es. in una classe C++)
static SpeexPreprocessState *st = nullptr;
static int frame_size = 256;
static int sample_rate = 16000;

extern "C" {

// Inizializzazione del preprocessore
JNIEXPORT void JNICALL
Java_com_suxsem_androidwakeword_SpeexWrapper_initSpeex(JNIEnv *env, jobject instance) {
if (st == nullptr) {
st = speex_preprocess_state_init(frame_size, sample_rate);

// 1. Denoise (Riduzione rumore)
int denoise = 1;
int noise_suppress = -25; // Più spinto di -25. Oltre -60 inizi a sentire artefatti metallici.
speex_preprocess_ctl(st, SPEEX_PREPROCESS_SET_DENOISE, &denoise);
speex_preprocess_ctl(st, SPEEX_PREPROCESS_SET_NOISE_SUPPRESS, &noise_suppress);

// 2. AGC (Guadagno automatico)
int agc = 1;
float agc_level = 24000; // Valore target alto (range tipico 0-32767)
int agc_max_gain = 25; // Limita il guadagno massimo per non esagerare con il rumore di fondo
speex_preprocess_ctl(st, SPEEX_PREPROCESS_SET_AGC, &agc);
speex_preprocess_ctl(st, SPEEX_PREPROCESS_SET_AGC_LEVEL, &agc_level);
speex_preprocess_ctl(st, SPEEX_PREPROCESS_SET_AGC_MAX_GAIN, &agc_max_gain);

// 3. Dereverb (Soppressione dell'eco ambientale)
int dereverb = 1;
float dereverb_decay = 0.2f;
float dereverb_level = 0.3f;
speex_preprocess_ctl(st, SPEEX_PREPROCESS_SET_DEREVERB, &dereverb);
speex_preprocess_ctl(st, SPEEX_PREPROCESS_SET_DEREVERB_DECAY, &dereverb_decay);
speex_preprocess_ctl(st, SPEEX_PREPROCESS_SET_DEREVERB_LEVEL, &dereverb_level);

// 4. VAD
int vad = 1;
int prob_start = 95; // Percentuale di probabilità per far scattare il VAD (es. 80%)

speex_preprocess_ctl(st, SPEEX_PREPROCESS_SET_VAD, &vad);
speex_preprocess_ctl(st, SPEEX_PREPROCESS_SET_PROB_START, &prob_start);

}
}

// Elaborazione del buffer audio con ritorno Booleano (VAD)
JNIEXPORT jboolean JNICALL
Java_com_suxsem_androidwakeword_SpeexWrapper_processAudio(JNIEnv *env, jobject instance, jshortArray audioData) {
if (st == nullptr) return JNI_FALSE;

// Otteniamo il puntatore ai dati grezzi dell'array JNI
jshort *buffer = env->GetShortArrayElements(audioData, nullptr);
jsize len = env->GetArrayLength(audioData);

int is_speech = 0;

// Elaborazione (speex_preprocess_run lavora in-place e restituisce lo stato VAD)
if (len >= frame_size) {
// La firma restituisce 1 se c'è parlato, 0 se è silenzio/rumore
is_speech = speex_preprocess_run(st, buffer);
}

// Rilasciamo l'array comunicando al sistema che abbiamo finito
env->ReleaseShortArrayElements(audioData, buffer, 0);

// Convertiamo l'int di Speex nel jboolean di JNI
return (is_speech == 1) ? JNI_TRUE : JNI_FALSE;
}

// Distruzione dello stato (Chiamalo quando chiudi l'app)
JNIEXPORT void JNICALL
Java_com_suxsem_androidwakeword_SpeexWrapper_destroySpeex(JNIEnv *env, jobject instance) {
if (st != nullptr) {
speex_preprocess_state_destroy(st);
st = nullptr;
}
}

} // extern "C"
2 changes: 2 additions & 0 deletions androidwakeword/src/main/cpp/speex/include/Makefile.am
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@

SUBDIRS = speex
18 changes: 18 additions & 0 deletions androidwakeword/src/main/cpp/speex/include/speex/Makefile.am
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Disable automatic dependency tracking if using other tools than gcc and gmake
#AUTOMAKE_OPTIONS = no-dependencies

# Historically, these headers were part of the speex package, so in order to
# keep compatibility with existing software we need to keep installing them
# in the same place as they were found then. If new functionality, with new
# headers, are added later, some thought should be given as to whether they
# should instead go somewhere other than $prefix/include/speex.

speexincludedir = $(includedir)/speex

nodist_speexinclude_HEADERS = speexdsp_config_types.h

speexinclude_HEADERS = speex_echo.h speex_jitter.h speex_preprocess.h speex_resampler.h \
speexdsp_types.h

noinst_HEADERS=speex_buffer.h

68 changes: 68 additions & 0 deletions androidwakeword/src/main/cpp/speex/include/speex/speex_buffer.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/* Copyright (C) 2007 Jean-Marc Valin

File: speex_buffer.h
This is a very simple ring buffer implementation. It is not thread-safe
so you need to do your own locking.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:

1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.

2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.

3. The name of the author may not be used to endorse or promote products
derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/

#ifndef SPEEX_BUFFER_H
#define SPEEX_BUFFER_H

#include "speexdsp_types.h"

#ifdef __cplusplus
extern "C" {
#endif

struct SpeexBuffer_;
typedef struct SpeexBuffer_ SpeexBuffer;

SpeexBuffer *speex_buffer_init(int size);

void speex_buffer_destroy(SpeexBuffer *st);

int speex_buffer_write(SpeexBuffer *st, void *data, int len);

int speex_buffer_writezeros(SpeexBuffer *st, int len);

int speex_buffer_read(SpeexBuffer *st, void *data, int len);

int speex_buffer_get_available(SpeexBuffer *st);

int speex_buffer_resize(SpeexBuffer *st, int len);

#ifdef __cplusplus
}
#endif

#endif




Loading