Repository navigation
Releases: kuafuai/andee
Release list
v0.5.0
v0.5.0
A conversation that has finished is now put away rather than carried forever, and it can be read
back when you ask about a detail. That is the new capability this release is numbered for. Two
smaller changes ride along: a choice card's question and answer now land in the scrollback, and the
README explains what signing actually protects.
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
- Finished topics get put away, not deleted. A stretch of conversation that has ended was the most
expensive thing in every later request and the least likely to be needed again. It is now folded into
a titled summary with a one-line marker left in its place, and the verbatim conversation is archived
beside it. In the scrollback those messages become a card offering Continue, Transcript and
Made here — so you can pick the thread back up, read what was actually said, or reopen what was
produced in it. - It reads a detail back instead of guessing. Ask about something a summary left out and it opens
the archived conversation rather than answering from the one-liner or telling you it never happened. - A choice card's question and answer stay in the conversation. The card used to come and go, so
the scrollback showed the assistant acting on a choice nobody could see being made. - The README says what signing is protecting — and the tool count in it is no longer a number that
can go stale.
What's Changed
Added
- Topics: a finished stretch of conversation is folded away, and can be picked back up. The nightly
review marks where topics begin and end among the user's numbered turns (fold_topic), the device
validates the range and swaps it for a one-line marker after the turn it names, and the verbatim
conversation is archived so it can be read back.
Three tools, alllocalOnly:fold_topic— review-only, and an ordinary turn that calls it
is refused, because folding is irreversible from the model's side and a request is not the moment to
spend it;list_topics— each folded topic's id, title, when it ended and how many messages it
covers; andget_topic— the whole back-and-forth as plain text, cut past a limit with
truncatedsaying so.
The device refuses a fold with a reason rather than a retry: a turn number not in the list, a
range ending at the latest turn (nothing has started after it, so nothing says it ended), something
inside still waiting on an answer, an overlap with a topic already put away, too short, or a scene
active. At most three topics per review, and they are put away when the review ends rather than
when the tool is called. The summary's fourth line is deliberately what the model left out,
because a summary that reads complete is one whose details are lost.
In the scrollback the folded topic is a card standing where its messages stood. Continue inlines
the archived conversation into the next turn; Transcript opens the full record, and says so
honestly when the text has been cleared and only the summary is left; Made here reopens the pages
produced inside that topic, each with its version. debug.sweep, for testing the review on demand. Loopback only — it needsadb forwardto reach,
and says so rather than vanishing.
Fixed
- A choice card's question and answer now land in the scrollback.
ui.askspoke the question and
handed the chosen option straight to the brain, so the card came and went and the conversation showed
the brain acting on a choice nobody could see being made. The question is an assistant row now and
the answer — the button, or the words said instead — is a row of your own. A timeout writes nothing,
because nothing was chosen.
Changed
- The README says what signing is for. The signature is the only thing stopping a fake "update" from
installing over a user's copy and inheriting the accessibility service, the secure-settings grant and
everything in the vault. That is now stated where signing is already discussed, along with why a
per-machine debug key is what producesINSTALL_FAILED_UPDATE_INCOMPATIBLE. - The tool count in the READMEs and the DISCLAIMER is no longer a number. Both said "sixty-odd
tools", which was accurate when written and had drifted as tools were added. The sentences now
describe what the tools do and say "a few dozen" — a count written into prose is a count that goes
stale, which is a lesson this project's own versioning policy had already learned once. - Dark is still one tap away, and this release changes nothing about it. Noted here only because
the previous release changed the default; nothing about appearance moved in this one.
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.5.0-debug-arm64-v8a.apk |
anything bought in the last several years — this is the one for almost everyone |
andee-0.5.0-debug-armeabi-v7a.apk |
an older 32-bit device |
andee-0.5.0-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.- The Google voice settings are empty too, so this build comes up on Volcengine and has no
provider key of any kind baked in. 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 — speech recognition and synthesis. Pick the provider first (Voice provider →
Volcengine or Google), then fill in that provider's key: a Volcengine Speech key, or a Google Vertex
Express key plus a GCP project number and a location. 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
- Folding is only ever done by the nightly review. There is no way for you to fold a topic by hand,
and a very long conversation therefore keeps costing context until the review next runs.debug.sweep
can trigger it, but it needs a cable. - A folded topic's transcript can be cleared with nothing but the summary left — the card says so
rather than opening an empty page, but the conversation is gone at that point. - The wake threshold's margin is thin, carried from
v0.3.2and unchanged here: 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. There
is still no quality gate on enrolment takes. - 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; the app's own default has been light sincev0.4.1, so the contrast
between the two is more visible than it was. - 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-user token is written in at package time. But a local build that fills it in wou...
v0.4.1
v0.4.1
Pages became versions, the brain learned to see its own work, and the default look is now the light
one. Three fixes ride along: the settings card no longer throws the conversation away, every face
got its finishing touches, and a page can be revised without growing a second page beside it.
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
- A revision is a new version of a page, not a second page. Ask for a change and the panel keeps
one row for it with the old revisions behind it — Versions opens the list, and any of them can
be made current again, as a new version rather than by rewriting history. - The brain can see the pages it made.
list_pagesandget_pageare two new read-only tools,
and the newest few also ride along on every turn — so "change the table from yesterday" reads
yesterday's table back instead of producing a lookalike or denying it exists. - A fresh install comes up light. A device that has chosen a theme keeps it; the dark palette is
one tap away in Settings → Appearance. - Changing the model no longer wipes the conversation. Flipping the thinking switch used to reset
the history mid-task, and the model would then say it had no record of a page it had just built.
What's Changed
Added
- Page revisions, kept as versions.
ChatHistory.Entrygained agroupId(which artifact it
belongs to) and aversion(which revision — only ever increasing). The Made for you panel
collapses to one row per artifact with its version number (v3); older revisions stay on disk, and
Go back to this makes an old one current as a new version, so the history stays append-only
and a rollback can itself be rolled back. One artifact keeps at most 20 revisions.
Which artifact a revision belongs to is decided on the device, never asked of the model — reading
a page back arms the target, the panel's revise button hands it over, andshow_htmlgained an
optionalrevisefor naming one explicitly. The target is consumed rather than merely read:
found by hand on a device, where a target left standing folded a second, unrelated page in as a
later version of the first. list_pagesandget_page. A page drawn yesterday existed for the user (it is on the shelf)
and not for the model that drew it, so a request to revise earlier work could come back as a fresh
table that merely resembled the old one, or as a flat "I have not made that".list_pagesreturns
every page with its id, title, date, size and whether the file is still there;get_pagereads one
back by id, any revision of it. Both are deliberately outside thescreen.prefix, so neither
folds the card nor stops at the lock screen — reading a file reaches neither. Alongside them the
newest few ride on the per-turn user message as a short index, the same place and for the same
reason as the notebook index: a tool only answers a question the model thought to ask.
Changed
- The default appearance is light, not dark. A fresh install now comes up on the light palette.
A device that has touched the setting keeps whatever it chose — the rule reads only an explicit
night, so nothing here can overrule a user who picked dark. Written against the non-default
value on purpose: it makes changing the default later a one-line edit rather than a migration, and
the dark palette stays one tap away in Settings → Appearance.
Dark is not the older palette and nothing about it is being corrected — it is the look this app
was designed on, and light was derived from it as the inversion. The frame drawn before the saved
setting is read moves with the default, so a fresh device does not flip palettes one frame in. - Every face got its props, its idle life and its finishing touches. Andee's mouth dropped twice
(−0.185 → −0.235 → −0.285) so there is a finger of sphere between its eyes and its smile instead
of the two crowding together. Sleep stopped being a flattening pose: the squash that melted the
resting and sleeping ball into a puddle is gone, and the floppy horns, the ⌣ eyes and (while
sleeping) the bubble carry it instead.
Fixed
- Changing the model, the thinking switch or the reasoning effort no longer wipes the
conversation. Closing the settings card rebuilt the local brain on any config difference, and a
rebuild is the loss of the whole conversation — flipping the thinking switch reset the history
mid-task, and the model then said it had no record of a page it had just built. Only a change of
who answers (mode, key, endpoint) rebuilds now; model, thinking and reasoning effort are request
parameters and are retuned in place, taking effect on the next request.
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.4.1-debug-arm64-v8a.apk |
anything bought in the last several years — this is the one for almost everyone |
andee-0.4.1-debug-armeabi-v7a.apk |
an older 32-bit device |
andee-0.4.1-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.- The Google voice settings are empty too, so this build comes up on Volcengine and has no
provider key of any kind baked in. 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 — speech recognition and synthesis. Pick the provider first (Voice provider →
Volcengine or Google), then fill in that provider's key: a Volcengine Speech key, or a Google Vertex
Express key plus a GCP project number and a location. 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
- Twenty revisions is a per-artifact cap, and a global one sits behind it.
MAX_VERSIONS(20)
trims the oldest revisions of one artifact; the olderMAX_PAGES(100) still governs how many
page-bearing entries exist at all — and since a revision is an entry, that global number now counts
versions rather than pages. Neither is surfaced anywhere in the UI. - The wake threshold's margin is thin, carried from
v0.3.2and unchanged here: 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. There
is still no quality gate on enrolment takes. - 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; worth reading again in this release, because the app's own default is
now light while those four surfaces are not. - 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-user token is written in at package time. But a local build that fills it in would
be a key in a tracked file, and n...
v0.4.0
v0.4.0
A second voice provider, a launcher icon that is the character rather than a description of it, and
three fixes to how the interface is drawn. The voice work came in as
PR #8, authored by a teammate rather than by the
maintainer, and it is the largest change in the release.
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
- You can choose who hears and who speaks. Before this release there was one speech provider and
it was not a choice; now there are two, picked in ⚙ → Backend → Voice provider. Volcengine stays
the default and the fallback. - The self-check stopped naming a provider the device might not be using. Its key and reachability
rows now ask about the provider actually selected, and they name neither — a green tick earned by one
provider's key while the device is talking to the other is the lie the checklist exists to prevent. - The launcher icon is the character's thinking pose, on white — the same character as before, in
the pose the app is in when it is working. - The primary button is one flat blue fill instead of a gradient, and it now answers to the theme
like everything else. Type is sized against the device, so a phone no longer renders a tablet's
title clipped toAccessibilit…with the next card pushed off screen.
What's Changed
Added
- Google ASR and TTS, as a second voice provider.
VoiceConfig.voiceProviderreadshuoshanor
google; the settings card gained a Voice provider picker above the two credential blocks; and
AsrController/TtsControllernow build one of two engines behind a shared interface
(AsrEngine,TtsEngine). The interface exists because both backends are streaming and reduce to
the same five calls — nothing above them had to change. Volcengine remains the fallback: anything
that is not exactlygooglereads as Volcengine, and it is the leftmost option, because for every
device that predates this row it is what has been running.
Google's two halves are different protocols behind one key: recognition runs over the Gemini Live
API (a WebSocket, so it needs the project number and a location) and synthesis is plain HTTP. Its
defaults are real values rather than blanks —asia-northeast1,
gemini-3.5-transcribe-live-preview,gemini-3.1-flash-tts-preview— and every hint on its rows is
what a blank row resolves to, because a card that advertises a model but silently calls another is
lying in the one place the user goes to decide. The key's value is never printed; the project
number is not a secret, and showing it is how the row can be checked at a glance.
A build withGOOGLE_VOICE_KEYnon-empty comes up on Google, and that is deliberate: a build is
packaged for whoever it is packaged for. This repository's own copy of that setting is empty, so a
checkout builds exactly what it built before Google existed.
(Contributed by @kuafu-limanxiang in #8.) - The launcher icon is now the thinking pose, on white. It was built from a sheet with a glow
painted around the whole head and composited over that glow; it is now built from the cut-out of the
character with a hand on its chin, on a plain#ffffff. Same character either way, so nothing about
the identity changes. The background is literally white because these illustrations were drawn on
white and the warm light on the ball's edge is painted on the ball itself — no colour is inferred
from anything. The inferred-glow treatment is not deleted, just one flag away (--bg glow). The icon
set also came out smaller: 405.4 → 258.3 KiB.
Changed
- The primary button is one flat fill instead of a blue-to-violet gradient. A gradient inside a
pill makes the button carry its own light source — the look of a landing page's call to action — and
it was the only colour in the whole palette that did not answer to the theme, so on a light card it
sat there like a sticker off another design. It is now one theme-aware fill, a step deeper than each
theme's accent, which keeps the button the loudest thing on a card for the reason that survives going
flat: it is the only saturated fill on it. Violet went with the gradient — the interface has exactly
one hue and it is blue.Glass.TINT_STARTandGlass.TINT_ENDare gone.
The label moved with the fill, and that is measurable rather than a matter of taste: the old one was
near-black by day and white at night, which works on a saturated ground at night and measures about
4:1 by day — under the line for body text. It is white in both themes now, because both fills
were picked deep enough to carry it. Seven call sites, one token each; no other surface changes. - Type is sized against the device now, not just against the tablet. The six rungs were drawn on a
711dp tablet where a card is 500dp wide with air around it. A phone is 392dp and the same card fills
it edge to edge, so the same 26sp title read as a heading on one and a shout on the other — on a
phone it showed up as a row title clipped toAccessibilit…with the next card pushed off screen
entirely. Nothing about the type had grown: the box framing it lost a third of its width while
padding, radii and line spacing stayed in dp, and that mismatch is what reads as clumsy rather than as
large. Six rungs now carry a second column about three quarters of the first, chosen once from the
device's short side against Android's own 600dp line (sw600dp) — the same line, not a second one.
The tablet column is the original design and is untouched; a phone gets 20 / 15.5 / 13.5 / 12 / 10.5 /
9.5. The smallest rung moves least, on purpose: it is already the floor of what can be read, and a
floor that moves with the screen is not a floor. - Every English-facing markdown file is now free of Chinese characters. The README had to be; the
other fourteen did not, and they had accumulated UI labels, language badges (nowChinese, with the
link target unchanged) and one piece of shorthand. What the rule deliberately excludes is worth
knowing: code identifiers, code comments and the internal notes written for AI assistants are out of
scope.
Fixed
- The voice self-check named a provider the device might not be using. Its key row asked for the
Volcengine key and its reachability row probedopenspeech.bytedance.com, unconditionally —
correct while there was one provider, and a wrong answer the moment there were two. Both follow the
selected provider now.
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.4.0-debug-arm64-v8a.apk |
anything bought in the last several years — this is the one for almost everyone |
andee-0.4.0-debug-armeabi-v7a.apk |
an older 32-bit device |
andee-0.4.0-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.- The Google voice settings are empty too, so this build comes up on Volcengine and has no
provider key of any kind baked in. Being bare is what keeps the claim above true: a build packaged
with a Google key would come up on Google instead. 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 — speech recognition and synthesis. Pick the provider first (Voice provider →
Volcengine or Google), then fill in that provider's key: a Volcengine Speech key, or a Google Vertex
Express key plus a GCP project number and a location. 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 ...
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, ove...