Creating a category whose name already exists replaced its color with a new
random one; main no-oped on duplicates. Guard the create path and inform the
user instead.
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
HIGH SEVERITY:
- Check HTTP status code before streaming download body to avoid wasted bandwidth
- Only clean up .part files in old-download cleanup, not legitimate APKs
- Fix getDefaultValuesFromFormItems crash on empty form item lists (reduce -> fold)
- Guard hosts list index access with firstOrNull / .first to prevent RangeError
- Clean up extracted APK directory on disk when package info is null
- Add 5-minute timeout to foreground busy-wait loop
- Fix isEnglish() false positive by falling back to false instead of tr('and')=='and'
- Use dart-define APP_VERSION from build.sh instead of hardcoded kPackageVersion
MEDIUM SEVERITY:
- Fix GitHub /latest endpoint URL reconstruction (split/join corrupting query params)
- Add try-catch for corrupt JSON in categories getter
- Check arch-filtered APK list emptiness and throw NoAPKError if empty
- Cache combinedAppSpecificSettingFormItems per AppSource instance
- Replace fragile didUpdateWidget condition with setEquals
- Replace putIfAbsent with direct assignment in _resetEntrySelections
- Remove double notify() call in import path (saveApps already calls notify)
- Added unawaited wrapper for async log calls in rethrowOrWrapError
LOW SEVERITY:
- Remove unused Logger? field from AppsProvider
- Remove redundant _obtainium prefix from private constants
- Remove redundant recursive:true on File.deleteSync
- Remove redundant .toList() copy on Uint8List write
- Make sourceUrl final (use const obtainiumUrl)
- Remove duplicated _updateSourceNote inline in add_app.dart build()
- Fix logs clear message showing literal 'null'
- Remove duplicate // ignore: implementation_imports comment
* architecture: consolidate and simplify
- Collapse 14 directories into 4 (app_sources, components, pages, providers)
- Replace DI container with inline construction in main.dart
- Fold page controllers back into their pages (one file per screen)
- Merge small files into their natural homes (removing 40 files)
- Standardize error handling across all 28 app sources (rethrowOrWrapError)
- Make App immutable with copyWith() replacing deepCopy()
- Replace globalNavigatorKey with go_router
- Add error boundaries, source health monitor, typed settings
- Eliminate all silent catch blocks
- Add tests and CI pipeline
- Extend lint rules
97 files → 56 files. 0 errors, 4/4 tests passing.
* fix: remove unused go_router, restore internal navigation
- Drop go_router (ShellRoute broke tab switching and disabled the
tablet/TV two-pane layout); revert to MaterialApp + navigatorKey
- Move appNavigatorKey into main.dart; delete router.dart
- Remove HomePage.child branch so the internal switcher drives the UI
- Fix dead 'Restart' button in the error fallback screen
- Remove TypedSettings []= mutation backdoor
- Persist clamped preferredApkIndex to the in-memory app map
- Use settings.getBool('trackOnly') instead of raw map access
* fix: remove dead rethrowOrWrapError, add missing import, update stale comments and docs
* fix: satisfy lint rules, wrap fire-and-forget futures, drop unused CI
- Fix use_build_context_synchronously via mounted/context.mounted guards
- Wrap deliberate fire-and-forget futures in unawaited()
- Document static app-lifetime event bus for close_sinks
- Apply dart format and dart fix across the codebase
- Drop avoid_catches_without_on_clauses (noisy in source scrapers)
- Remove test.yml CI workflow
- Rename test/services to test/providers
* fix: show real HTTP error messages, remove source health monitor and dead event-bus code
- Bypass deferred localization for HTTP_ERROR so the real reason phrase/status is shown instead of a generic 'unexpected error'
- Remove SourceHealthMonitor entirely (per-instance state made it ineffective for background checks)
- Simplify save-notification bus to StreamController<void>; drop unused AppRepositoryEvent/AppRepositoryEventType and events getter
- Scrub stale docs (SourceHealthMonitor, event type, deleted test.yml workflow)
* fix: preserve installedVersion on import, correct stale theme doc reference
* fix: type logger as Logger?, simplify ErrorWidget builder, document _sentinel, resolve TextDirection conflict
- Change dynamic logger to Logger? in AppsProvider for type safety (the
field was unused but the contract should be explicit).
- Replace MaterialApp wrapper in ErrorWidget.builder with a minimal
Directionality + Scaffold; MaterialApp is wasteful for a one-shot
error screen.
- Add a doc comment explaining the _sentinel pattern used by
App.copyWith to distinguish unset from explicitly-null nullable fields.
- Hide TextDirection from easy_localization (which re-exports intl's
conflicting TextDirection enum that lacks ltr/rtl) so the Flutter
dart:ui TextDirection.ltr resolves correctly.
* fix: register SourceForge, restore Palestine banner, drop dead code and tests
- Register the implemented SourceForge source in _buildSources() so the
README's supported-sources list is accurate.
- Restore the Support Palestine banner in README.md (unintentionally removed).
- Remove unused ConfigProvider, CredentialHealth, getCredentialHealth, and
the never-injected _configProvider path from settings_provider.
- Remove unit tests and their test-only dev dependencies.
App sources:
- Harden sources against malformed/changed responses (null, parse, index
and empty-version guards): rustore, tencent, apkmirror, gitlab, aptoide,
fdroid(repo), sourcehut, huaweiappgallery, itchio, izzyondroid,
telegramapp, uptodown, vivoappstore, etc.
- APKCombo: fix and enable (issue #341) - unwrap "/r2?u=" links to the
signed Cloudflare R2 URL, drop the Host header that 403'd the R2
download, take one build per variant, and parse the release date;
register the source.
- Remove orphaned Mullvad source (never registered).
- F-Droid version-regex filter selects the newest match; fix Jenkins
changelog link; GitLab reuses one GitHub helper instance; GitHubStars
reuses one GitHub instance.
UI / behaviour:
- Fix download progress bar never updating on the app detail page and the
apps list (provider select on an in-place-mutated AppInMemory never
fired a rebuild).
- Fix single-category gradient crash, dead "installing" tile state, silent
swipe-install errors, deep-link dedup, mounted/context guards in
import/export and add-app, and ExportSection future caching.
- generated_form: GeneratedFormSwitch.disabled is now applied; clone()
preserves disabled and autoCompleteOptions.
Performance:
- Cache SettingsProvider.categories (was rebuilding a Map + re-parsing JSON
on every access, over-rebuilding category selectors).
- Memoize the flattened combined form items used by the per-app JSON
migration at load.
Tooling / config:
- build.sh: set -euo pipefail; make the --build-id=none workaround
idempotent and glob-safe.
- pubspec: branch-track git dependencies.
- Remove unused config_keys.dart (consolidate on raw setting keys).
- Fix GitHub rate limit time calculation (was ignoring current time)
- Fix HTTP client leaks on redirect exhaustion and early download returns
- Drain StreamedResponse before closing client in checkETagHeader
- Fix null assertion crash in checkUpdate when app ID not found
- Remove duplicate null check in getObtainiumHttpError
- Replace empty catch blocks with typed exceptions and comments
- Fix potential null dereference in AppFilePicker and CategorySelector
- Fix category sort comparator transitivity violation for null values
- Call LogsProvider.close() on app dispose (was never called)
- Replace print() with debugPrint() for consistency
- Replace sync I/O with async variants (readAsString, writeAsString, rename, list, stat)
- Convert installedAppsData list scan to O(1) map lookup in loadApps
- Pre-group apps by category to avoid O(n*m) per-category scans
- Use Set for alreadyAddedUrls to enable O(1) contains lookups
- Add sourceType cache to AppInMemory to avoid re-detecting source on filter
- Use context.select instead of context.watch to reduce unnecessary rebuilds
- Extract interval slider to dedicated widget to avoid full settings page rebuilds
- Extract _FormSwitchRow to prevent switch rebuilds on text field changes
- Add cacheWidth/cacheHeight to AppIcon image decoding
- Batch URL import with concurrency limit of 4 instead of sequential
- Guard auto-export timer callback with _disposed check
- Defer _lastBackgroundSave clear until loadApps completes
- Simplify findExistingUpdates complex nested logic
- Refactor 7 app sources to use standardizeUrlWithRegex helper
- Replace 25+ magic numbers with named constants
- Clean up late keyword on immutable fields in App and APKDetails
- apps_provider_install: stream-decompress gzip tarballs >64MB to temp file
instead of loading into memory; async readAsBytes for APK sharing
- html.dart: null-guard res.request.url fallback; cap intermediate link
depth at 10
- github.dart: null-check regex matches before .start/.end assertions
- home.dart: use context.select for isTV to avoid full-shell rebuilds on
every settings change
- app_list_tile.dart: replace MediaQuery.sizeOf with LayoutBuilder;
guard category gradient division-by-zero for single-category apps
- add_app.dart: cache FutureBuilder future so it does not re-fetch on
every rebuild
- import_export.dart: add didUpdateWidget to SelectionModal to reset
stale entrySelections when parent rebuilds with new entries
- generated_form.dart: dispose old TextEditingControllers before re-init
- logs_provider.dart: make db connection static (shared across instances);
add close() method
- fdroid.dart: guard changelog substring against UTF-16 surrogate splits
- ui_widgets.dart: await Clipboard.setData; change showConfirmDialog
content from Object? to Widget?; use consistent TextButton type in
HighlightableButton; add semanticLabel to EmptyState icon
- app.dart: await _getUpdate in RefreshIndicator; show loading spinner
in WebView instead of empty Container
- analysis_options.yaml: enable stricter lint rules
- Settings: show a loading spinner instead of a blank screen while prefs load
- APK-origin warning dialog: add a "don't show again" checkbox (sets
hideAPKOriginWarning) so the choice isn't buried in settings
- Accessibility: announce LinkText as a link; exclude decorative AppIcon images
from semantics; mark category color swatches as selectable buttons with their
selected state; expose the category long-press-to-edit via a tooltip and a
semantic long-press action
- Settings: extract the ~85-line update-interval slider tile into
_buildIntervalSliderTile to shrink the build() method
- downloadFile: break out of the partial-download wait loop when the temp file
disappears (a concurrent download finished it), instead of polling forever
- downloadFile: propagate allowInsecure to the actual APK download (previously
only the header request honored it, so insecure downloads failed)
- checkPartialDownloadHash: close the HttpClient (was leaked on every call)
- bgUpdateCheck: actually wait retryAfterXSeconds before retrying so rate-limited
hosts aren't hammered
- native_provider: await FontLoader.load() before marking the system font loaded
- Guard fragile source parsing against crashes (LiteAPKs empty array, APKMirror
missing ' by ', GitHub null/non-string tag_name)
- Add mounted guards before setState across async gaps (category editor, logs)
- GeneratedForm: dispose TextEditingControllers (were never disposed)
- NotificationsProvider: guard against double initialize()
- main: run the launch-by-notification check once instead of on every build
(was re-showing the same dialog)
- Extract the god build() methods in the apps/app/add_app pages into focused, named helper methods
- Memoize AppSource instances and SettingsProvider platform calls; drop a duplicate source-config lookup
- Batch loadApps notifications, replace the busy-wait with a Completer lock, and debounce auto-export
- Add a compatVersion stamp so already-migrated apps skip one-time legacy migrations
- Only rebuild on integer download-progress changes; harden list/detail cache signatures with Object.hashAll
- Fix dead select-all branches, add mounted guards, and clone source-owned form items to avoid mutation leaks
- Expose swipe install/remove and icon long-press to screen readers; add a list progress bar and URL copy tooltip
- Move flutter_lints to dev_dependencies and apply dart format across the tree