Repository navigation
v0.3.2
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
debugbuild, as a stopgap. The release build type has no
signingConfigand there is no keystore in this repository, soassembleReleaseemits
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 isdebuggable, 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 ownFLAG_SECUREmeans there
were never any logs to search;screen.ui_treereturns the keyguard's empty placeholder;
device.launchopens 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.speakand 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.1said "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_onand
settings_wake_enrolledall 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_FRAMES3 → 8 (80 ms) so
a transient cannot open a segment, andPREROLL_FRAMES15 → 30 (300 ms) so the onset is still in
the ring when the decision lands. A segment that starts early comes out long and, pastMAX_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 formic.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_THRESHOLD2.8 → 1.8,
which had been picked as "the widest thing still callable a threshold" and never measured. VoiceSegmentergained a pure-KotlinStatswith no Android dependency, keeping the DSP half of the
wake pipeline JVM-testable;WakeEnrollUiandWakeMiclog under oneWaketag.
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_ENABLEDisfalse, and bothCODEFLYING_KEYandCODEFLYING_DOMAINare empty, so
CodeFlying is not available in this build and nothing is pre-wired to a hosted backend.DEFAULT_LANGisen: 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:
- The model key (LLM) — an OpenAI-compatible endpoint plus a model name. On an English UI these
default tohttps://api.openai.com/v1andgpt-4o-mini; a Chinese UI defaults to
https://api.deepseek.comanddeepseek-flash. - 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 andHtmlActivity's WebView backdrop are all still dark.
Carried over fromv0.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 atCODEFLYING_ENABLEDand the domain rather than the key. Carried from
v0.1.1onward; not fixed by this release. assets/andee_key.txtis tracked by git and is not in.gitignore. It ships empty, which is
correct — the per-userkft_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.0listed are unchanged and untouched in this release:
tools/verify_faces_lab.pystill 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:9008with no authentication (documented inSECURITY.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 certificateTo 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