Skip to content

Releases: kuafuai/andee

v0.5.0

Choose a tag to compare

@booboosui booboosui released this 16 Oct 11:45

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 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

  • 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, all localOnly: 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; and get_topic — the whole back-and-forth as plain text, cut past a limit with
    truncated saying 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 needs adb forward to reach,
    and says so rather than vanishing.

Fixed

  • A choice card's question and answer now land in the scrollback. ui.ask spoke 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 produces INSTALL_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_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.
  • 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_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 — 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.2 and 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 and HtmlActivity's WebView backdrop are all still dark.
    Carried over from v0.3.1; the app's own default has been light since v0.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 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 token is written in at package time. But a local build that fills it in wou...
Read more

v0.4.1

Choose a tag to compare

@booboosui booboosui released this 15 Oct 09:59

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 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

  • 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_pages and get_page are 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.Entry gained a groupId (which artifact it
    belongs to) and a version (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, and show_html gained an
    optional revise for 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_pages and get_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_pages returns
    every page with its id, title, date, size and whether the file is still there; get_page reads one
    back by id, any revision of it. Both are deliberately outside the screen. 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_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.
  • 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_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 — 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 older MAX_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.2 and 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 and HtmlActivity's WebView backdrop are all still dark.
    Carried over from v0.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 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 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...
Read more

v0.4.0

Choose a tag to compare

@booboosui booboosui released this 14 Oct 10:48

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 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

  • 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 to Accessibilit… with the next card pushed off screen.

What's Changed

Added

  • Google ASR and TTS, as a second voice provider. VoiceConfig.voiceProvider reads huoshan or
    google; the settings card gained a Voice provider picker above the two credential blocks; and
    AsrController / TtsController now 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 exactly google reads 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 with GOOGLE_VOICE_KEY non-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_START and Glass.TINT_END are 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 to Accessibilit… 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 (now Chinese, 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 probed openspeech.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_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.
  • 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_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 — 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 ...

Read more

v0.3.2

Choose a tag to compare

@booboosui booboosui released this 14 Oct 08:19

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, ove...

Read more