< Back to all hacks

#41 APK v2: Escape Hatches (Post-Brick Fix)

Launcher
Problem
APK v1 was a brick trap: fullscreen hid ADB auth popup, back disabled, no escape to Settings.
Solution
5 protections: visible status bar, triple-tap Settings, double-back chooser, emergency button, ADB kill switch.
Lesson
NEVER make a launcher without escape hatches. One battery death + ADB auth loss = factory reset.

Context

The first PocketClaw launcher (Hack #39) was a fullscreen WebView with no escape route. This created a brick trap: if the battery dies, Android invalidates ADB RSA keys. On reboot, the ADB authorization dialog appears — but it renders BEHIND the fullscreen launcher, invisible and untouchable. Without ADB, there's no way to install a different launcher or access settings. Factory reset becomes the only option.

This scenario actually happened and nearly bricked the phone. The fix was to add 5 layers of escape hatches to ensure the user can always reach Settings or a launcher chooser.

Implementation

Five protections were added to the launcher:

// 1. Status bar visible (not truly fullscreen)
// Use Theme.NoTitleBar instead of Theme.NoTitleBar.Fullscreen
// in AndroidManifest.xml:
// android:theme="@android:style/Theme.NoTitleBar"
// This keeps the status bar visible, including ADB auth notifications

// 2. Triple-tap title opens Settings
private int tapCount = 0;
private long lastTap = 0;
titleView.setOnClickListener(v -> {
    long now = System.currentTimeMillis();
    if (now - lastTap > 1000) tapCount = 0;
    tapCount++;
    lastTap = now;
    if (tapCount >= 3) {
        startActivity(new Intent(Settings.ACTION_SETTINGS));
        tapCount = 0;
    }
});

// 3. Double-back shows launcher chooser
private long lastBack = 0;
public void onBackPressed() {
    long now = System.currentTimeMillis();
    if (now - lastBack < 500) {
        // Show launcher chooser
        Intent i = new Intent(Intent.ACTION_MAIN);
        i.addCategory(Intent.CATEGORY_HOME);
        i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        startActivity(Intent.createChooser(i, "Choose launcher"));
    }
    lastBack = now;
}

// 4. Emergency Settings button after 5 min without gateway
// If HTTP fetch to localhost:9000 fails for 5 minutes,
// show a red "OPEN SETTINGS" button on screen

// 5. ADB broadcast kill switch
// Registered in manifest:
// <receiver android:name=".ExitReceiver">
//   <intent-filter>
//     <action android:name="com.pocketclaw.EXIT" />
//   </intent-filter>
// </receiver>

The ADB kill switch works from any ADB connection:

# Kill the launcher from ADB:
adb shell am broadcast -a com.pocketclaw.EXIT
# Launcher exits, falls back to system launcher chooser

Verification

# Test triple-tap (on device):
# Tap "POCKETCLAW" title 3 times quickly -> Settings opens

# Test double-back (on device):
# Press Back twice quickly -> launcher chooser appears

# Test ADB kill switch:
adb shell am broadcast -a com.pocketclaw.EXIT
# Expected: launcher exits, chooser appears

# Test emergency button (disconnect gateway):
# Wait 5 minutes with gateway stopped
# Red "OPEN SETTINGS" button should appear on screen

Gotchas

  • Theme.NoTitleBar (not Fullscreen) is critical. The status bar must remain visible so ADB authorization dialogs and notifications are accessible
  • The triple-tap timeout (1 second) prevents accidental triggers but is short enough for intentional use
  • The emergency button timer (5 minutes) is deliberately long to avoid false positives during gateway restarts
  • On the native APK (Hack #40), these same 5 protections were re-implemented in Java

Result

MetricBeforeAfter
Escape routes05
Brick riskHigh (near-brick happened)Minimal
ADB auth recoveryFactory resetKill switch broadcast
Settings accessImpossibleTriple-tap or emergency