Skip to content

v0.3.2

Choose a tag to compare

@booboosui booboosui released this 14 Oct 08:19
· 44 commits to main since this release

v0.3.2

Wake word plus polish — and the first release that ships an APK with the release rather than
telling the reader to build one.

The attached APK is the debug build, as a stopgap. The release build type has no
signingConfig and there is no keystore in this repository, so assembleRelease emits
app-<abi>-release-unsigned.apk, which will not install until you sign it. Rather than ship nothing
while that decision is still open, the attached APK is the debug variant: signed with the standard
Android debug certificate, and it installs as-is. It is debuggable, it is larger than a signed
release build, and it is bare — no keys, English, CodeFlying off. See Download
for the two keys you will have to supply yourself before it can do anything.

Highlights

  • The wake word works with the screen off now, which is the state a phone is in when it is in your
    pocket. ⚙ → wake word → the "listen while locked" row controls it and ships on; the
    microphone stays open while
    it is, and the system's recording indicator shows it.
  • It will not pretend it can act while you are locked out. Woken with the keyguard up it can hear
    and answer, but screen and app-launch tools refuse outright with a sentence saying why — instead of
    reporting "completed" for a gesture the system silently dropped.
  • Enrolment stopped needing three tries. The segmenter was starting on the microphone rather than
    on the voice, so the first take came out long and dirty and was dropped in silence. Across three
    enrolments the spread between takes fell from 0.42 to 0.12.
  • The cover shows the character instead of describing it — five of its own gesture faces along the
    bottom of both the README cover and the social card.

What's Changed

Added

  • Wake word with the screen off, gated by wakeListeningAllowed() — the screen is on, or the
    user left the "listen while locked" row on
    , defaulting to on. The old rule (screen on only) had a real reason behind it
    — a dark tablet on a bedside table should not listen all night — but it also meant the assistant
    could not be called from the one place a person keeps their phone. The cost is stated on the row
    that flips it rather than left for the status bar to mention.
  • A locked device refuses the tools that cannot work there. A gesture aimed at a locked device is
    dropped by the input pipeline and reports nothing; the lock screen's own FLAG_SECURE means there
    were never any logs to search; screen.ui_tree returns the keyguard's empty placeholder;
    device.launch opens an Activity nobody sees. dispatch() now refuses them with a sentence saying
    why. screen. is blocked by prefix so a new tool in that namespace inherits the guard, and the
    names outside it (device.launch, open_url, scan, look, permissions.request, ui.ask,
    ui.alert, ui.show_html) go with it. device.battery, device.volume, tts.speak and the whole
    note. namespace stay open, because they work perfectly well locked. Read live rather than mirrored
    into a flag: locking the phone mid-answer is a normal thing to do, not a state transition to track.
  • Five gesture faces along the bottom of both cover images, one word under each — Listen, Look,
    Think, Act, Speak.
    They are the app's own illustrations, and specifically the gesture moods the web renderer cannot
    draw.

Changed

  • The release now carries an APK. Every release up to 0.3.1 said "no binary, on purpose", and
    the reason has not changed; what changed is the trade. A reader who cannot install anything has no
    way to try the app short of provisioning an Android SDK. The build is deliberately bare and the
    sizes, the keys and the debug certificate are all discussed under Download below.
  • The prose about the wake word moved with the default. wake_done, check_detail_wake_on and
    settings_wake_enrolled all said it works "while the screen is on" — in both languages — and both
    READMEs said screen-off hands the microphone back. Every sentence describing the old default would
    have shipped stale.

Fixed

  • The voice segmenter started on the microphone, not on the voice. START_FRAMES 3 → 8 (80 ms) so
    a transient cannot open a segment, and PREROLL_FRAMES 15 → 30 (300 ms) so the onset is still in
    the ring when the decision lands. A segment that starts early comes out long and, past MAX_FRAMES,
    was dropped in silence — the card simply sat on "listening…", which is what "it takes three tries" was.
    Delaying a decision is free; discarding the audio is not, which is why there is deliberately no
    warm-up that drops the first 200 ms.
  • The enrolment prompt appeared before the microphone existed. The "now speak" prompt was set on the tapper's
    thread; it now waits for mic.start() to return, so the card reads "listening…" while the recorder comes
    up and invites you to speak only once something is listening.
  • The wake threshold was anchored on the worst take rather than the closest. One dirty take used
    to set the door width for the whole system — 2.2755 and 2.1083 against non-wake speech measured at
    2.21–2.59, with sixteen of sixty judgements within 0.06 of the line. MAX_THRESHOLD 2.8 → 1.8,
    which had been picked as "the widest thing still callable a threshold" and never measured.
  • VoiceSegmenter gained a pure-Kotlin Stats with no Android dependency, keeping the DSP half of the
    wake pipeline JVM-testable; WakeEnrollUi and WakeMic log under one Wake tag.

Download

Android 11+ (minSdk 30). The APK is per-ABI (splits in app/build.gradle), so pick the file
that matches your device:

File Take it if
andee-0.3.2-debug-arm64-v8a.apk anything bought in the last several years — this is the one for almost everyone
andee-0.3.2-debug-armeabi-v7a.apk an older 32-bit device
andee-0.3.2-debug-x86_64.apk an emulator

This build is deliberately bare

It is built from source with nothing configured — verified against the generated BuildConfig, not
assumed
:

  • CODEFLYING_ENABLED is false, and both CODEFLYING_KEY and CODEFLYING_DOMAIN are empty, so
    CodeFlying is not available in this build and nothing is pre-wired to a hosted backend.
  • DEFAULT_LANG is en: a device that has never picked a language comes up in English.
  • The thinking default is off.

The two keys you have to supply yourself

The assistant cannot do anything until you give it two keys. Open ⚙ → Backend → On-device and
fill in:

  1. The model key (LLM) — an OpenAI-compatible endpoint plus a model name. On an English UI these
    default to https://api.openai.com/v1 and gpt-4o-mini; a Chinese UI defaults to
    https://api.deepseek.com and deepseek-flash.
  2. The voice key (Volcengine) — speech recognition and synthesis. Optional: without it the
    assistant is silent but still works on screen.

Then, in this order: grant Overlay → enable Accessibility → tap the ball. Until the keys are
in, the card the assistant puts up will name what is missing — that is the self-check working, not a
fault.

Would you rather not configure keys at all? Then this APK is the wrong door. Use the one-click
page instead — codeflying.app/andee, or
codeflying.net/andee in mainland China. Describe what you
want, tap the build button, and you get a package with the tokens already configured plus a QR
code to scan. Starting is free and the free token allowance is finite — once it runs out, tokens
must be bought.
It is a separate service with no direct relationship to this project, with its own
terms, pricing and data handling.

Why a debug build, when the README says not to hand one out

Because the alternative, today, is no binary at all. The README's advice still stands for anything you
distribute: a debug APK is debuggable, and its certificate is the shared Android debug key —
so it cannot install over a release-signed build, which means moving from this to a signed release
takes an uninstall, and an uninstall takes the notebook and the vault with it. Use this to try the
thing, not to hand it to someone who will depend on it.

Known issues

  • The wake threshold's margin is thin. After the fix, the eight measured judgements sit in
    1.47–2.05 with the line landing in the gap between 1.69 and 1.77 — 0.08 wide, which is the honest
    state of it. There is also no quality gate on enrolment takes, so a future enrolment can still
    contribute a bad one.
  • A light generated page still fades in over a dark stage. TextPage, TracePage,
    Theme.Body.Fullscreen's window background and HtmlActivity's WebView backdrop are all still dark.
    Carried over from v0.3.1; not addressed here.
  • A build with CodeFlying compiled in but no API key can still raise the wrong complaint. The
    availability test looks at CODEFLYING_ENABLED and the domain rather than the key. Carried from
    v0.1.1 onward; not fixed by this release.
  • assets/andee_key.txt is tracked by git and is not in .gitignore. It ships empty, which is
    correct — the per-user kft_ token is written in at package time. But a local build that fills it in
    would be a key in a tracked file, and nothing currently stops that being committed.
  • The two issues v0.1.0 listed are unchanged and untouched in this release:
    tools/verify_faces_lab.py still reports 7/16 assertions passing against the committed
    reports/ball-faces-lab.html, and the debug WebSocket server (net/BodyWsServer) still binds
    0.0.0.0:9008 with no authentication (documented in SECURITY.md).

Before you install

Read DISCLAIMER.md and
docs/acceptable-use.md first. This app asks for accessibility, overlay,
notification access, SMS and call log, and it can complete a payment by tapping. The only guardrail on
that path is Android's FLAG_SECURE; the prompt-level checks on risky actions are instructions to the
model, not enforcement.

Install from source

If you would rather build it yourself — or need a signed release build, which is what you should
actually distribute:

git clone https://github.com/kuafuai/andee
cd andee
git checkout v0.3.2
./gradlew :app:assembleDebug      # installs as-is, signed with the debug certificate

To reproduce the attached build exactly, leave local.properties with no CODEFLYING_* keys and
DEFAULT_LANG=en; that is the whole configuration, and nothing is passed on the command line.

assembleRelease needs your own keystore — the repo has none, so it produces an unsigned APK that
will not install until you sign it. Full from-scratch setup, including the JDK traps, is in the
README's Provisioning section.

Upgrading from v0.3.1: versionCode moves 5 → 6, so this installs over it normally. The wake
word now keeps listening with the screen off
— if you have enrolled one, that is a new behaviour and
a new battery cost, and ⚙ → wake word → the "listen while locked" row turns it off.

Full Changelog: v0.3.1...v0.3.2