commit 4a491fc2fc7a8e98776a04a5a43e75e7c0bb559b truex <truex@equwal.com> 2026-09-21 02:25:56 -0700 Shorten the setup reasons and add the monetization comparison The reasons in the allow step are hints of three to seven words. The app says double tap for Power too, so one word names one gesture. docs/MONETIZATION.md compares five models and recommends a free core, a one-time Pro unlock on Google Play, a one-time licence for the full build, and a tip link with no feature behind it.
.../equwal/assistkey/setup/GuidedSetupActivity.kt | 6 +- .../main/java/dev/equwal/assistkey/setup/Route.kt | 16 ++--- .../java/dev/equwal/assistkey/setup/RouteTest.kt | 2 +- docs/MONETIZATION.md | 77 ++++++++++++++++++++++ 4 files changed, 89 insertions(+), 12 deletions(-)
diff --git a/app/src/main/java/dev/equwal/assistkey/setup/GuidedSetupActivity.kt b/app/src/main/java/dev/equwal/assistkey/setup/GuidedSetupActivity.kt index 25383e2..e1c9216 100644 --- a/app/src/main/java/dev/equwal/assistkey/setup/GuidedSetupActivity.kt +++ b/app/src/main/java/dev/equwal/assistkey/setup/GuidedSetupActivity.kt @@ -185,7 +185,7 @@ class GuidedSetupActivity : Activity() { HwKey.POWER in set && set.size > 1 -> col.note("Hold Power, then press the other button.") HwKey.POWER in set && !env.powerIsDirect && !env.shellSupported -> - col.note("A tap cannot reach any app. Hold and double press work.") + col.note("A tap cannot reach any app. Hold and double tap work.") } if (HwKey.SCREEN !in set) { col.row("Advanced", "Every gesture of this button") { @@ -201,7 +201,7 @@ class GuidedSetupActivity : Activity() { HwKey.POWER in t.keys && t.keys.size > 1 -> "Hold Power, then press " + (t.keys - HwKey.POWER).first().label t.type == GestureType.HOLD -> "Press and hold" t.count == 1 -> "Tap" - t.count == 2 -> if (HwKey.POWER in t.keys && !env.powerIsDirect) "Double press" else "Double tap" + t.count == 2 -> "Double tap" t.count == 3 -> "Triple tap" else -> t.count.toString() + " taps" } @@ -313,7 +313,7 @@ class GuidedSetupActivity : Activity() { // Some firmware sends the double press of Power to the wallet app, not to the camera app. val powerDouble = t == Trigger(setOf(HwKey.POWER), GestureType.TAP, 2) && !env.powerIsDirect if (powerDouble && !Channels.isSatisfied(this, Channel.WALLET)) { - col.row("The double press does nothing?", "Take the wallet place too") { + col.row("The double tap does nothing?", "Take the wallet place too") { Channels.setEnabled(this, Channel.WALLET, true) Channels.safeStart(this, Channels.claimIntent(this, Channel.WALLET)) } diff --git a/app/src/main/java/dev/equwal/assistkey/setup/Route.kt b/app/src/main/java/dev/equwal/assistkey/setup/Route.kt index 951137e..00dbabf 100644 --- a/app/src/main/java/dev/equwal/assistkey/setup/Route.kt +++ b/app/src/main/java/dev/equwal/assistkey/setup/Route.kt @@ -19,19 +19,19 @@ object Route { enum class Need(val title: String, val why: String) { KEY_FILTER( "Let Rebind see the buttons", - "Android calls this an accessibility service." + "Turn on Button remapping" ), ASSISTANT( - "Let Rebind take the hold of Power", - "Rebind stands in for the assistant app." + "Let Rebind get the Power hold", + "Choose Rebind as assistant app" ), CAMERA( - "Let Rebind take the double press of Power", - "Rebind stands in for the camera app. Your camera still works." + "Let Rebind get the Power double tap", + "Choose Rebind as camera app" ), SHELL_POWER( "Open the full Power button", - "Shell access makes every Power press work." + "Needs shell access" ) } @@ -64,7 +64,7 @@ object Route { } val power = HwKey.POWER in trigger.keys if (power && trigger.keys.size > 1 && trigger != Trigger.powerThen((trigger.keys - HwKey.POWER).first())) { - return Plan(emptyList(), "With Power, one way works: hold Power, then press the other button.") + return Plan(emptyList(), "Hold Power, then press the other button.") } if (!power || env.powerIsDirect) return Plan(listOf(Need.KEY_FILTER)) @@ -77,7 +77,7 @@ object Route { // No door of Android leads here. Shell access does, where the build has it. else -> return if (env.shellSupported) Plan(listOf(Need.KEY_FILTER, Need.SHELL_POWER)) else Plan( emptyList(), - "Android does not show this Power press to apps. Hold and double press work." + "Android hides this Power press. Hold and double tap work." ) } val second = !alone diff --git a/app/src/test/java/dev/equwal/assistkey/setup/RouteTest.kt b/app/src/test/java/dev/equwal/assistkey/setup/RouteTest.kt index 5a29a88..884d849 100644 --- a/app/src/test/java/dev/equwal/assistkey/setup/RouteTest.kt +++ b/app/src/test/java/dev/equwal/assistkey/setup/RouteTest.kt @@ -38,7 +38,7 @@ class RouteTest { val plan = Route.plan(Trigger(power, GestureType.TAP, n), true, sideDoors) assertFalse(plan.possible) assertTrue(plan.needs.isEmpty()) - assertTrue(plan.blocked!!.contains("Hold and double press")) + assertTrue(plan.blocked!!.contains("Hold and double tap work")) } } diff --git a/docs/MONETIZATION.md b/docs/MONETIZATION.md new file mode 100644 index 0000000..d583f67 --- /dev/null +++ b/docs/MONETIZATION.md @@ -0,0 +1,77 @@ +# Monetization models for Rebind + +## Recommendation + +Keep the free core for everyone. Sell Pro through two one-time unlocks, not a +subscription. On Google Play, sell the `play` build's Pro unlock for **US$4.99** +(already coded). Off Play, sell the `full` build's licence through Lemon +Squeezy for **US$9.99**, for e-ink readers that have no Play Store. Add a +no-gate tip link (Ko-fi or GitHub Sponsors) for people who want to give more. +This combination reaches paying users and non-paying users at the same time, +which matches the goal to help people improve their devices. + +## Model comparison + +| Model | Real example | User pays | Policy risk | Dev effort | Revenue potential | Goodwill / reach | +|---|---|---|---|---|---|---| +| Free + Play one-time Pro IAP | Button Mapper (~US$9.99), MacroDroid (~US$4-5) | Once, in the Play build | Low on Play; no reach off Play | Low, code exists | Modest, small niche | High, free core for all | +| Paid app, free trial off Play | Tasker (Play ~US$3.49, free 7-day trial APK) | Once, after trial | None, direct download | Low, `full` build exists | Modest, reaches non-Play devices | Medium, trial ends | +| Donations / tip jar | KOReader (~€47/week, Liberapay) | Any amount, or none | None, allowed everywhere | Very low, one link | Small, uneven | Very high, nothing locked | +| Open-core, paid extras | Key Mapper (open source, paid convenience) | For extras only | Low; F-Droid lists only the FOSS part | Medium, needs a clean split | Modest | Very high, source stays open | +| Pay-what-you-want / sponsorware | Common on itch.io, Gumroad | Any amount they pick | None | Low-medium, needs a store page | Variable, often low | Very high, ends up free | + +## Generous but paid + +Three ways to help people and still earn money: + +1. **Free forever, no trial timer:** basic remap of two buttons stays free on + every device, always. This covers the most common hardware-hacking need. +2. **Free licence on request:** anyone who cannot pay gets a full Pro licence + by email. Do not ask for proof. Trust the request. +3. **All e-ink profiles are free:** device profiles for Viwoods, Boox, and + other e-ink readers stay free, because these readers are the project's + reason to exist. Pro sells only power features, such as multi-tap, + combinations, menus and the full Power button. Settings export and import + stay free (see the rules below). + +## Risks and rules + +- **Play payments policy:** the Play-distributed APK must use Play Billing + for purchases used inside it. Do not add an external payment link inside + the `play` build. Sell the Lemon Squeezy licence only in the `full` build. +- **No prohibited claims:** do not call the app a "RAM booster" or "battery + saver." These claims are false for a button remapper, and Play bans them. +- **Refunds:** Play handles its own refund window. For the Lemon Squeezy + licence, publish one rule: refund within 14 days, no questions asked. +- **Never lock behind payment:** the control that turns off Power-button + handling, and settings export and import. A user must always undo a bad + remap and keep their own data. + +## Launch sequence and kill criteria + +1. Ship the free public beta now, on both builds. Collect the 12 testers for + 14 days that Play requires. +2. Open Play production: free core, plus the US$4.99 Pro one-time unlock. +3. At the same time, open the Lemon Squeezy US$9.99 licence for the `full` + build. This reaches e-ink readers with no Play Store, the audience the + owner most wants to help. +4. Add a tip link (Ko-fi or GitHub Sponsors) to the README of the public + repository and to the Licence screen of the `full` build, with no feature + gate attached. Do not put it in the `play` build. + +**Kill criteria:** fewer than 25 paid unlocks, counting Play and Lemon +Squeezy together, in the 60 days after Play production. If this happens, drop +the paid Pro tier and keep the free app plus the tip link only. + +## Sources + +- [Button Mapper - Google Play](https://play.google.com/store/apps/details?id=flar2.homebutton) +- [Key Mapper - F-Droid](https://f-droid.org/packages/io.github.sds100.keymapper/) +- [Tasker - trial and pricing](https://tasker.joaoapps.com/) +- [MacroDroid pricing - Hacker News](https://news.ycombinator.com/item?id=42254890) +- [Nova Launcher Prime pricing - XDA Forums](https://xdaforums.com/t/nova-launcher-prime-is-down-to-0-99-down-from-4-99-on-the-play-store-once-again.4379115/) +- [Niagara Pro price](https://help.niagaralauncher.app/article/104-price-of-niagara-pro) +- [KOReader - Liberapay donations](https://en.liberapay.com/KOReader/donate) +- [Shizuku - GitHub](https://github.com/RikkaApps/Shizuku) +- [F-Droid Anti-Features policy](https://f-droid.org/en/docs/Anti-Features/) +- [Google Play App Licensing overview](https://developer.android.com/google/play/licensing/overview)