chore(release): v0.4.4 (versionCode 70) + deploy.sh self-healing

- Release 0.4.4 zu Play Internal Testing (Android Tamper-Lock-Präzision +
  a11y-Onboarding-Guide), Notes ins CHANGELOG archiviert.
- deploy.sh ensure_native_dir jetzt self-healing: prüft Marker-Datei
  (android/app/build.gradle bzw. ios/Podfile) statt nur ob der Ordner
  existiert. Ein halb-gewischtes native-Verzeichnis (nur build/-Output) wird
  erkannt und via 'expo prebuild --clean' sauber regeneriert, statt mit
  "autolinking.json doesn't exist" zu failen.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
chahinebrini 2026-06-08 07:52:10 +02:00
parent 4a013bc43b
commit fe6a63bd8d
5 changed files with 173 additions and 19 deletions

View File

@ -1,6 +1,148 @@
# Changelog # Changelog
All notable changes to rebreak-native will be documented in this file. All notable changes to rebreak-native will be documented in this file.
## v0.4.4 (Build 90 / versionCode 70) — 2026-06-08\n\n# Next Release
## Improved
- Protection lock (Android) is now surgical: it only blocks Rebreak's *own* sensitive screens — deactivating Rebreak's device administrator, turning off Rebreak's accessibility service, and disconnecting or changing Rebreak's VPN. Managing, force-stopping or uninstalling *other* apps is completely unaffected, and the accessibility-services list, device-admin list and other apps' info pages stay fully navigable. (Previously the lock could over-block entire settings lists, which also risked a Play review rejection.)
- Uninstall protection now relies on the device administrator: the OS itself greys out "Uninstall" and "Force stop" for an active admin, and the only bypass — deactivating the admin — stays locked by the accessibility service. Net effect: Rebreak can't be removed, but you are never blocked from removing or force-stopping any other app.
- Accessibility onboarding guide: while you're in Android's settings, a passive on-screen hint ("Rebreak: …") now appears at the bottom to point you to the right toggle. After you switch the service on, Settings is reset to its home screen (no leftover deep page or search term) and you're routed back to the app after a short moment so it reliably detects the service.\n
## v0.4.4 (Build 90 / versionCode 70) — 2026-06-08\n\n# Next Release
## Improved
- Protection lock (Android) is now surgical: it only blocks Rebreak's *own* sensitive screens — deactivating Rebreak's device administrator, turning off Rebreak's accessibility service, and disconnecting or changing Rebreak's VPN. Managing, force-stopping or uninstalling *other* apps is completely unaffected, and the accessibility-services list, device-admin list and other apps' info pages stay fully navigable. (Previously the lock could over-block entire settings lists, which also risked a Play review rejection.)
- Uninstall protection now relies on the device administrator: the OS itself greys out "Uninstall" and "Force stop" for an active admin, and the only bypass — deactivating the admin — stays locked by the accessibility service. Net effect: Rebreak can't be removed, but you are never blocked from removing or force-stopping any other app.
- Accessibility onboarding guide: while you're in Android's settings, a passive on-screen hint ("Rebreak: …") now appears at the bottom to point you to the right toggle. After you switch the service on, Settings is reset to its home screen (no leftover deep page or search term) and you're routed back to the app after a short moment so it reliably detects the service.\n
## v0.4.1 (Build 87 / versionCode 67) — 2026-06-08\n\n# Next Release
## Improved
- Onboarding accessibility step (Android): now opens a guided sheet first that explains the two parts most people get stuck on — allowing "Display over other apps", then finding ReBreak in the list and flipping the switch — with a screenshot and an indicator showing exactly where to tap. Shown only on the first attempt.
- Lyra now talks you through onboarding automatically (voice auto-enabled while onboarding; mutable any time via the speaker button, and your app-wide voice setting is left untouched afterwards).
- Lyra's avatar no longer freezes on intro animations (e.g. the empathy wave) — it now settles back into a living idle loop instead of stopping on the last frame.
- Onboarding protection step now has a back button.\n
## v0.4.0 (Build 85 / versionCode 66) — 2026-06-07\n\n# Next Release
## New
- Device detail view: tap any device on the Devices page to open a sheet with its connection status, when it was connected, and a protected/unprotected coverage donut (same visual as your profile)
- "New device connected" push notification — when a Mac or Windows computer is bound via Rebreak Magic, your phone(s) get notified
- Devices page now updates in real time the moment a new computer is paired — no manual refresh needed
- Onboarding now sets up the full protection using the exact same guided, gated step flow as the protection screen (single source of truth) — Android: VPN → Device Administrator → Accessibility (strict order: the tamper lock has to come last, otherwise it would block the device-admin screen); iOS: App Lock → Screen Time passcode → content filter. Previously the device-admin / screen-time hardening steps only existed in the protection screen after onboarding.
- Before each system permission dialog in onboarding, a short confirm sheet now explains exactly which button to tap (e.g. on iOS app lock: "tap the bottom button, not the blue one") and requires a deliberate "I understand" checkbox — stops users from blindly clicking past the dialog and tapping the wrong button.
## Changed
- Device list now shows device-specific icons (iPhone / Android / MacBook / PC) instead of generic outlines
- Stationary protection (Mac/Windows) now runs exclusively via Rebreak Magic — the manual offline profile download has been removed. The offline profile would have shipped the removal password in plain text inside the file (bypass risk); with Magic the lock password stays server-side and is never shown to the user.
- Mac DNS profile hardened with `ProhibitDisablement` — the filter can no longer be toggled off in System Settings.
## Fixed
- Android onboarding: if the VPN permission dialog failed to open (e.g. another always-on VPN active, work profile, or certain OEM quirks), the protection step would silently get stuck with no dialog and no error message — especially on Play Store builds, where the underlying error was swallowed. The step now surfaces the real error and offers a retry instead of dead-ending.\n
## v0.3.13 (Build 85 / versionCode 64) — 2026-06-07\n\n# Next Release
## New
- Add Windows PC as a protected device: the "Add device" menu on the Devices page now opens the Rebreak Magic pairing flow for both Mac and Windows (6-digit code, like the Mac app)
- Device overview now shows separate progress bars for mobile (iOS/Android) and computer (Mac/Windows) slots
- New device matrix: Pro = 1 mobile + 1 computer, Legend = 3 mobile + 2 computers (seamless protection on up to 5 devices)
## Improved
- Rebreak Magic sheet: choose between Mac and Windows, platform-specific download links and instructions, desktop slot counter
- Rebreak Magic sheet is now fully localized (DE/EN/FR/AR) — previously German-only
- Settings: Rebreak Magic entry now mentions Mac & Windows protection
## Fixed
- Devices page no longer shows the legacy desktop enrollment flow (replaced by Magic pairing)\n
## v0.3.13 (Build 84 / versionCode 64) — 2026-06-07\n\n# Next Release
## Features
- **iOS protection setup is now a guided 3-step flow (unsupervised devices).** The two
separate toggles (URL filter + app lock) are replaced by a clear, gated sequence
with a progress indicator: Step 1 activate FamilyControls, Step 2 set the Screen
Time passcode (now mandatory, no longer optional), Step 3 activate the VPN / URL
filter. The banner turns fully green only when all three are done. The flow
reflects the real current state on open (no reset). MDM/NEFilter devices and the
Android path are unaffected.
- The Screen Time passcode step no longer shows an "open settings" button. iOS does
not allow third party apps to deep link to Screen Time, so the step now shows clear
written instructions plus the generated code and a confirmation button.
## Fixes
- **iOS: the VPN permission dialog no longer pops up aggressively on every app open.**
It now only appears when protection should be active and the VPN profile was
actually removed (the bypass case). During setup or normal operation it stays quiet.
- **Calls: incoming call no longer auto-rejects after force-quit (iOS).** The
launch-time CallKit zombie cleanup was firing in the same launch as a freshly
reported cold-start incoming call and ending it. The cleanup is now delayed and
conditional (5s grace, only when the call store has no live call), so a real
incoming call is preserved while leftover zombies are still cleared.
- **Dev build no longer crashes on launch** when the CallKit native module is absent.
Calls are simply disabled in builds without it; production builds are unaffected.
## Polish
- **Avatar placeholders are now a neutral grey instead of blue.** Users without a
photo show their initials on a subtle grey circle (iOS contact style) rather than
the dominant blue. Applies to the feed, DMs, header and profile.
- **The optional profile milestone prompt now follows cumulative protected days**
(which never reset) instead of the consecutive streak. Users who relapse and
restart still reach the milestones over time, so the friendly profile nudge reaches
everyone, not only users with a long unbroken run.
## Notes
- Protection-coverage streak (half-donut + progress bar): backend is live, this build
picks up the frontend. Coverage accumulates from the first protection-state report.\n
## v0.3.13 (Build 82 / versionCode 63) — 2026-06-07\n\n# Next Release
## Features
- **iOS protection setup is now a guided 3-step flow (unsupervised devices).** The two
separate toggles (URL filter + app lock) are replaced by a clear, gated sequence
with a progress indicator: Step 1 activate FamilyControls, Step 2 set the Screen
Time passcode (now mandatory, no longer optional), Step 3 activate the VPN / URL
filter. The banner turns fully green only when all three are done. The flow
reflects the real current state on open (no reset). MDM/NEFilter devices and the
Android path are unaffected.
- The Screen Time passcode step no longer shows an "open settings" button. iOS does
not allow third party apps to deep link to Screen Time, so the step now shows clear
written instructions plus the generated code and a confirmation button.
## Fixes
- **iOS: the VPN permission dialog no longer pops up aggressively on every app open.**
It now only appears when protection should be active and the VPN profile was
actually removed (the bypass case). During setup or normal operation it stays quiet.
- **Calls: incoming call no longer auto-rejects after force-quit (iOS).** The
launch-time CallKit zombie cleanup was firing in the same launch as a freshly
reported cold-start incoming call and ending it. The cleanup is now delayed and
conditional (5s grace, only when the call store has no live call), so a real
incoming call is preserved while leftover zombies are still cleared.
- **Dev build no longer crashes on launch** when the CallKit native module is absent.
Calls are simply disabled in builds without it; production builds are unaffected.
## Polish
- **Avatar placeholders are now a neutral grey instead of blue.** Users without a
photo show their initials on a subtle grey circle (iOS contact style) rather than
the dominant blue. Applies to the feed, DMs, header and profile.
- **The optional profile milestone prompt now follows cumulative protected days**
(which never reset) instead of the consecutive streak. Users who relapse and
restart still reach the milestones over time, so the friendly profile nudge reaches
everyone, not only users with a long unbroken run.
## Notes
- Protection-coverage streak (half-donut + progress bar): backend is live, this build
picks up the frontend. Coverage accumulates from the first protection-state report.\n
## v0.3.13 (Build 79 / versionCode 62) — 2026-06-06\n\n# Next Release
## Fixes
- **Calls: fixed phantom/zombie incoming calls (iOS).** After an incoming call ended without the in-app call screen ever mounting (iOS shows the native CallKit banner, not our `/call` screen), the call store stayed stuck in the `ended` state forever. The `ended → idle` reset only lived in the `/call` screen, which never mounts for banner-only incoming calls. A stuck `ended` state then silently blocked every subsequent incoming call (RING + VoIP push were received but ignored by the `status !== 'idle'` guard), so accepting from the banner produced a phantom CallKit call that ticked as "active" with no real connection, and the caller saw a missed call.
- Store now self-heals back to `idle` after a call ends, decoupled from the call screen (fallback timer in `teardown`).
- `receiveIncoming` and the realtime ring handler now treat a stale `ended` state as acceptable (clear + proceed) instead of dropping the new call.
- `onAnswer` now ends the native CallKit call when the store has no `incoming` call, preventing a phantom "active" call.
- `RNCallKeep.endAllCalls()` on app launch clears leftover CallKit zombies from a previous session.
- **DM header: online dot now matches the online text.** The green online dot on the partner avatar used the follow-gated presence (`isOnline` = online AND you follow them), while the "online" text next to it used raw presence. In a DM the dot now uses raw presence too, so it shows whenever the partner is online — consistent with the text, regardless of follow relationship. (Looked like an Android-only bug but was the follow gate + asymmetric follow between the test accounts.)\n
## v0.3.13 (Build 76 / versionCode 59) — 2026-06-04\n\n### Fixes ## v0.3.13 (Build 76 / versionCode 59) — 2026-06-04\n\n### Fixes
- DM image viewer: opening a shared photo no longer jitters — the image now fills the screen smoothly instead of snapping from a square placeholder to its real size on load - DM image viewer: opening a shared photo no longer jitters — the image now fills the screen smoothly instead of snapping from a square placeholder to its real size on load

View File

@ -1,6 +0,0 @@
# Next Release
## Improved
- Protection lock (Android) is now surgical: it only blocks Rebreak's *own* sensitive screens — deactivating Rebreak's device administrator, turning off Rebreak's accessibility service, and disconnecting or changing Rebreak's VPN. Managing, force-stopping or uninstalling *other* apps is completely unaffected, and the accessibility-services list, device-admin list and other apps' info pages stay fully navigable. (Previously the lock could over-block entire settings lists, which also risked a Play review rejection.)
- Uninstall protection now relies on the device administrator: the OS itself greys out "Uninstall" and "Force stop" for an active admin, and the only bypass — deactivating the admin — stays locked by the accessibility service. Net effect: Rebreak can't be removed, but you are never blocked from removing or force-stopping any other app.
- Accessibility onboarding guide: while you're in Android's settings, a passive on-screen hint ("Rebreak: …") now appears at the bottom to point you to the right toggle. After you switch the service on, Settings is reset to its home screen (no leftover deep page or search term) and you're routed back to the app after a short moment so it reliably detects the service.

View File

@ -36,7 +36,7 @@ export default ({ config }: ConfigContext): ExpoConfig => ({
ios: { ios: {
supportsTablet: true, supportsTablet: true,
bundleIdentifier: MAIN_BUNDLE, bundleIdentifier: MAIN_BUNDLE,
buildNumber: "87", buildNumber: "90",
// Apple Sign-In Entitlement — Pflicht für expo-apple-authentication nativen // Apple Sign-In Entitlement — Pflicht für expo-apple-authentication nativen
// signInAsync()-Flow. Ohne flag generiert Expo's prebuild den // signInAsync()-Flow. Ohne flag generiert Expo's prebuild den
// com.apple.developer.applesignin-Entitlement nicht in die .entitlements. // com.apple.developer.applesignin-Entitlement nicht in die .entitlements.
@ -62,7 +62,7 @@ export default ({ config }: ConfigContext): ExpoConfig => ({
android: { android: {
package: "org.rebreak.app", package: "org.rebreak.app",
versionCode: 67, versionCode: 70,
// Firebase / FCM-v1-Credentials: Pflicht ab Expo SDK 53 für Android-Push. // Firebase / FCM-v1-Credentials: Pflicht ab Expo SDK 53 für Android-Push.
// Enthält client-config für beide Packages (org.rebreak.app + .dev) und // Enthält client-config für beide Packages (org.rebreak.app + .dev) und
// ist NICHT geheim (API-Key per Package-Signing-Fingerprint restricted) — // ist NICHT geheim (API-Key per Package-Signing-Fingerprint restricted) —

View File

@ -389,24 +389,42 @@ ASC_API_KEY_PATH="${ASC_API_KEY_PATH:-}"
ASC_API_KEY_ID="${ASC_API_KEY_ID:-}" ASC_API_KEY_ID="${ASC_API_KEY_ID:-}"
ASC_API_KEY_ISSUER="${ASC_API_KEY_ISSUER:-}" ASC_API_KEY_ISSUER="${ASC_API_KEY_ISSUER:-}"
# Stellt sicher dass ios/ oder android/ existiert — sonst Auto-Prebuild. # Stellt sicher dass ios/ oder android/ ein VOLLSTÄNDIGES Prebuild ist — sonst
# Auto-(Re)Prebuild (fehlt komplett → prebuild; halb-gewischt → prebuild --clean).
# Usage: ensure_native_dir ios | ensure_native_dir android # Usage: ensure_native_dir ios | ensure_native_dir android
# Nutzt --platform <p> ohne --clean, damit der jeweils andere Ordner unangetastet bleibt. # Immer --platform <p>, damit der jeweils andere Ordner unangetastet bleibt.
ensure_native_dir() { ensure_native_dir() {
local platform="$1" local platform="$1"
local target_dir local target_dir marker
case "$platform" in case "$platform" in
ios) target_dir="$IOS_DIR" ;; # Marker = eine Datei die NUR ein vollständiges Prebuild hat (nicht der reine
android) target_dir="$ANDROID_DIR" ;; # build/-Output). Damit erkennen wir auch einen halb-gewischten Ordner.
ios) target_dir="$IOS_DIR"; marker="$IOS_DIR/Podfile" ;;
android) target_dir="$ANDROID_DIR"; marker="$ANDROID_DIR/app/build.gradle" ;;
*) die "ensure_native_dir: unbekannte Plattform '$platform'" ;; *) die "ensure_native_dir: unbekannte Plattform '$platform'" ;;
esac esac
if [[ -d "$target_dir" ]]; then
# Self-Healing: NICHT nur prüfen ob der Ordner da ist (-d), sondern ob er ein
# VOLLSTÄNDIGES Prebuild ist. Ein halb-gewischter $platform/ (nur build/-Output,
# keine Gradle-/Podfile-Projektdateien) trickste die alte -d-Prüfung aus → der
# Release-Build failte mit "autolinking.json doesn't exist". Marker-Datei prüfen.
if [[ -d "$target_dir" && -f "$marker" ]]; then
return 0 return 0
fi fi
if [[ -d "$target_dir" && ! -f "$marker" ]]; then
warn "$platform/ ist unvollständig (Marker $(basename "$marker") fehlt) — regeneriere via prebuild --clean"
# --clean wischt + regeneriert NUR $platform/ (die andere Plattform bleibt
# unangetastet) und räumt den kaputten Halb-Zustand sauber auf.
run_quiet "expo prebuild --clean ($platform)" "$LOG_DIR/prebuild-$platform-$TIMESTAMP.log" \
pnpm exec expo prebuild --platform "$platform" --clean --no-install
else
warn "$platform/ fehlt — führe 'expo prebuild --platform $platform' automatisch aus" warn "$platform/ fehlt — führe 'expo prebuild --platform $platform' automatisch aus"
run_quiet "expo prebuild ($platform)" "$LOG_DIR/prebuild-$platform-$TIMESTAMP.log" \ run_quiet "expo prebuild ($platform)" "$LOG_DIR/prebuild-$platform-$TIMESTAMP.log" \
pnpm exec expo prebuild --platform "$platform" --no-install pnpm exec expo prebuild --platform "$platform" --no-install
[[ -d "$target_dir" ]] || die "$platform/ nach prebuild immer noch nicht vorhanden" fi
[[ -f "$marker" ]] || die "$platform/ nach prebuild immer noch unvollständig (kein $(basename "$marker"))"
ok "$platform/ regeneriert" ok "$platform/ regeneriert"
} }

View File

@ -1,6 +1,6 @@
{ {
"name": "rebreak-native", "name": "rebreak-native",
"version": "0.4.1", "version": "0.4.4",
"private": true, "private": true,
"main": "expo-router/entry", "main": "expo-router/entry",
"scripts": { "scripts": {