THIS IS EXPERIMENTAL AND MAY NEED TO BE REVERTED.
- Replace background_fetch (^1.7.0) and flutter_foreground_task (^9.2.2)
with workmanager (^0.9.0+3)
- Single callbackDispatcher replaces both the old headless task and the
foreground service TaskHandler — simplifies to one code path
- WorkManager constraints enforce WiFi-only and charging-only at the OS
level instead of checking in Dart after waking the app
- Fix: enableBackgroundUpdates toggle now properly cancels/resumes
WorkManager tasks (previously the toggle was UI-only and didn't
actually stop background checks)
- Add "Run background check now" button to the bottom of the Updates
settings card for manual testing
- Detailed logging at every stage: WorkManager init, task registration,
callback invocation, completion, and crashes
- Removed: foreground service declaration, FOREGROUND_SERVICE_SPECIAL_USE
permission, useFGService setting, showBatteryOptimizationPrompt setting,
and 4 orphaned translation keys
Known risk: workmanager was previously tried and abandoned in Nov 2022
(PR #97) due to fluttercommunity/flutter_workmanager#415 and #363 (tasks
stopping after app closed / after 3-5 days). These issues remain open
but the package has had 4 years of updates since then.
Add a cancel button for in-progress downloads (in the app list tile, the
detail page, and the download notification) and surface download sizes:
the expected size before starting (best-effort probe) and live
received/total progress in the UI and notification.
- Route notification Cancel taps via the FLN background isolate to the main
isolate (IsolateNameServer/SendPort); gated off for background-isolate
downloads whose cancel token isn't reachable.
- Share per-app download state (progress + bytes) across AppInMemory copies
so UI listeners keep updating after saveApps replaces the map entry.
- Treat cancellations silently and keep the .part file for later resume.
Introduce a lib/installers/ strategy layer (Installer + InstallResult with
stock, Shizuku, and external implementations) and route install decisions
through it instead of the inline useShizuku branching.
Add an external-installer mode that hands a downloaded APK/bundle to a
user-chosen installer app: discovery and a FileProvider content URI come from
two tiny native helpers, while intent dispatch, foreground tracking, and
install confirmation (via lastUpdateTime, robust to pseudo-versions) live in
Dart. Replace the useShizuku toggle with an install-method selector and
migrate the old setting.
HIGH severity:
- Fix Completer double-completion masking CheckUpdatesException as StateError
- Delete orphaned APK files on install-already-pending status code
- Stop transient source-load errors from permanently deleting user app entries
- Await saveApps in setCategories to avoid data inconsistency
- Guard packageManager.openApp double-tap behind installed check
- Fix hashCode.abs() overflow on min int value in DownloadNotification
MEDIUM severity:
- Wrap checkETagHeader client.send in try/finally to prevent resource leak
- Remove unused loadingCompleter dead code in loadApps
- Pass additionalSettings (not {}) in apkcombo sourceRequest call
- Remove dead getRequestHeaders call in coolapk
- Add missing == true on boolean setting checks in fdroidrepo
- Zero-pad month/day in itchio date version string
- Deep-copy form items by value in izzyondroid and codeberg constructors
- Remove redundant double-reverse for sort=none in github release sorting
- Clamp negative gradient stop to valid range in app_list_tile
- Show WebView error state when main frame resource fails to load
- Defer WebView appId changes until page load completes
- Defer category pruning side effect out of build into post-frame callback
- Expand URL regex in import_export to match whitespace-delimited URLs
- Remove future-update-in-build side effect for source notes in add_app
- Add items hash to form reinit check alongside widget key
LOW severity:
- Add 'name' field to all 11 AppSource subclasses that lacked it
- Remove unused source_provider imports from app_detail_widgets, category_editor
- Rename copy-pasted bool show param to bool value on non-show setter pairs
- Delete dead proguard-rules.pro (Gson is not a dependency)
- Remove npm package files from git tracking in assets/translations
- Fix DEVELOPER_GUIDE test directory reference
The release crash was a native SIGSEGV in libflutter.so caused by an
ABI mismatch. Commit f85309b9 was the last one that produced a working
release APK. All files outside lib/ that changed since then are reverted
to that commit: android/ build config, pubspec, build.sh, sign.sh,
docker, .gitignore, analysis_options.yaml, and the .flutter submodule.
The Dart source code in lib/ is deliberately kept at its current state.
Also adds a minimal proguard-rules.pro (2 lines) with -dontwarn for
androidx.window.extensions/sidecar, which R8 needs to complete the build.
The Flutter Gradle plugin's shouldShrinkResources() checks the "shrink"
project property before forcing isMinifyEnabled=true on release builds.
Setting it to false in gradle.properties prevents R8 from running entirely,
eliminating the need for any proguard-rules.pro (no -dontwarn, no -keep,
no maintenance burden). Dart-side tree-shaking is unaffected.
The Flutter docs state R8 is always enabled on release (--no-shrink has no
effect). The SDK's own flutter_proguard_rules.pro ships a best-effort
-if..-keep rule, but it only covers classes directly implementing
FlutterPlugin - not the internal/model/companion classes that plugins also
access via reflection at startup. Stripping those causes immediate launch
crashes (NoClassDefFoundError).
This file adds the -dontwarn for androidx.window (compile-time-only OEM
stubs pulled transitively by the newer Flutter embedder) and blanket -keep
rules for every native plugin package R8 was observed to remove (sourced
from usage.txt of a prior release build).
The Flutter Gradle plugin (FlutterPlugin.kt:218-225) already programmatically
applies the SDK's own flutter_proguard_rules.pro (which ships -dontwarn
android.** and an -if..-keep for FlutterPlugin implementations) plus
proguard-android-optimize.txt. An explicit proguardFiles(...) block in
build.gradle.kts REPLACES that list, silently losing the SDK's rules.
Now build.gradle.kts leaves the proguardFiles list alone (the plugin manages
it) and the only thing proguard-rules.pro adds is -dontwarn for
androidx.window.extensions/sidecar — the one package the SDK's built-in
-dontwarn android.** doesn't match (it's "androidx", not "android").
The Flutter embedder (io.flutter:flutter_embedding_release) transitively
pulls androidx.window:window-java:1.2.0, whose ExtensionsUtil references
the compile-time-only androidx.window.extensions / sidecar modules. R8
(rightly) rejects the missing classes. The -dontwarn suppresses those
errors *and* 5ad3cb1 already needed, while the -keep rules prevent R8
from stripping plugin classes accessed reflectively at launch (the
Flutter Gradle plugin forces isMinifyEnabled=true on release regardless
of build.gradle.kts).
The Gson boilerplate present in the old proguard-rules.pro was removed;
none of those rules are relevant to this app (the Gson classes are
transitive noise from a plugin dependency, and the example.com.gson
package literally doesn't exist).
Commit 5ad3cb1 added isMinifyEnabled/isShrinkResources/proguardFiles to
the release buildType, enabling R8 on the Android-native side. Without
comprehensive keep rules, R8 stripped plugin classes accessed reflectively
at launch (flutter_foreground_task, android_package_installer, shizuku,
etc. — 267 classes in usage.txt), causing an immediate
NoClassDefFoundError crash in production. Reverting to standard Flutter
defaults (no Android-native minification) to restore working release
builds.
This Flutter SDK's Gradle plugin enables R8 release minification by default (FlutterPluginUtils.shouldShrinkResources() returns true), so release builds run minifyReleaseWithR8. R8 then fails on the optional androidx.window.extensions/sidecar classes - compile-time-only stubs referenced by the transitive androidx.window dependency and provided by the OEM at runtime.
- proguard-rules.pro: -dontwarn androidx.window.extensions/sidecar (per R8's own missing_rules.txt) - build.gradle.kts: make isMinifyEnabled/isShrinkResources explicit and add proguardFiles so proguard-rules.pro is actually applied (it was never referenced before, leaving its rules dormant)
Bugs:
- Indonesian locale 'in' → 'id' so translations are found (id.json)
- sign.sh: keystore password via env: (not command line); apksigner
found via find|sort -V instead of fragile ls|tail -1
- font sizes 11/12 now use Theme.of(context).textTheme tokens
- textTheme.labelSmall! null assertion replaced with safe ?.
- download progress indicator wrapped in Semantics for a11y
Architecture / robustness:
- Side effects (_manageServices, _handleFirstRun, checkLaunchByNotif)
moved out of build() into initState + SettingsProvider listener
- createHttpClient sets connectionTimeout: 30s
- Error-swallowing catch blocks in source matching now log to LogsProvider
- HttpClient lifecycle: httpClientResponseStreamToFinalResponse now
closes client in finally block
- JSON migration failure sets compatVersion to prevent re-trigger
- Icon cache cleanup on removeApps
- FG/BG state sync: static timestamp tells foreground to reload when
background task saves to disk
- Concurrent download polling capped at ~5 minutes (43 iterations)
Performance:
- _pipelineSignature: 6 fields/app instead of 17
- .asBroadcastStream() removed from download stream
- DeviceInfoPlugin() cached in apkpure.dart (was called twice per update)
Config / i18n:
- New config_keys.dart: 30 typed AppConfigKey constants replacing
raw string literals in AppSource form item definitions
- easy_localization implementation imports justified with comment
HTTP extraction:
- HttpClientMixin extracted from AppSource for getRequestHeaders
Build / deps:
- Gradle: deprecated allprojects {} replaced with
dependencyResolutionManagement (PREFER_PROJECT mode)
- Android build dirs added to .gitignore
- Dockerfile: removed pre-installed NDK 26.3 (AGP auto-downloads 28.2),
consolidated SDK RUN layers into one
- Third-party git deps pinned to commit SHAs
The following case is now properly being handled:
```
Execution failed for task ':app:packageRelease'.
> A failure occurred while executing com.android.build.gradle.tasks.PackageAndroidArtifact$IncrementalSplitterRunnable
> SigningConfig "release" is missing required property "storeFile".
```
Renamed the flavor dimension to align with Flutter's recommendations. Changed keystore properties to casts, and allowed the store file to be nullable. Introduced handling of an absent key.properties file during release builds.
- Bumped Dart SDK to 3.10.1
- Bumped Flutter SDK to 3.38.0
- Bumped NDK to 28.2.13676358
- Bumped com.android.application to 8.11.1
- Bumped org.jetbrains.kotlin.android to 2.2.20
- Bumped Java to 17
- Changed formatting in Gradle configuration files to align with a fresh Flutter project's