152 Commits

Author SHA1 Message Date
Imran Remtulla
e68087b558 Reduce padding/spacing for ToggleTile and GeneratedForm in modal dialogs
- Add optional  parameter to ToggleTile (default false)
- Add optional  parameter to GeneratedForm (default false)
- GeneratedFormModal now passes  to its inner GeneratedForm
- Set  on direct AlertDialog + GeneratedForm in import_export.dart
- Reduce non-tile mode row spacing from 24 to 8
2026-07-19 23:26:01 +01:00
Imran Remtulla
dbf9a1047c Tweak category component 2026-07-19 17:58:15 +01:00
Imran Remtulla
444312af78 Revert GeneratedFormModal tileMode default to false, fixing changelog dialog regression (#2039) 2026-07-19 17:30:01 +01:00
Imran Remtulla
9be6ba932e Slightly increase version/changelog area text size (#2039) 2026-07-19 17:25:15 +01:00
Imran Remtulla
9e2719a8c0 Restrict highlight setting to version/changelog label only; use custom _VersionLabel widget instead of HighlightableButton; revert HighlightableButton to original behaviour; fix 'Download release assets' button with plain TextButton (#2039) 2026-07-19 17:23:06 +01:00
Imran Remtulla
9cc782f459 Sub-form entries as individual connected cards with positional radii, inline add/remove buttons (#3107) 2026-07-19 15:36:36 +01:00
Imran Remtulla
89e2fc247f Add trailingKey support to GeneratedFormTextField, fix add/search button state with Row layout, include _urlValid in trailingKey hash (#3107) 2026-07-19 15:23:51 +01:00
Imran Remtulla
b74e1bf966 Unify ToggleTile: strip inner CardTile, replace _FormSwitchRow, zero tileMode padding, simplify _fieldTile to ConnectedCard (#3107) 2026-07-17 21:43:53 +01:00
Imran Remtulla
05dd135665 Unify form input styling: global borderless filled theme, rename Settings* components to generic names, merge settings_widgets.dart into ui_widgets.dart, convert SegmentedButtons to DropdownMenus, add per-app checkbox bulk update dialog, compact SliverAppBars, inline trailing icon buttons, newline-terminated error messages for breathing room, ConnectedCard defaults to zero padding (#3107, #3100) 2026-07-17 21:33:26 +01:00
Imran Remtulla
78cc9042be Replace bulky SliverAppBar.large headers with compact SliverAppBar; add 3-way action banner setting (All/Updates/None); skip confirmation dialog for update-only banner clicks; replace all-or-nothing obtain dialog toggles with per-app checkbox list (#3107, #3100) 2026-07-17 12:16:02 +01:00
Imran Remtulla
3228a2a42c Fix keyboard closing on add-app additional settings fields
_generateFormState._computeItemsHash included item.value for
switches and dropdowns (only text fields were excluded). On the
add-app page, _buildSourceSpecificForm mutates item.value from
additionalSettings on every rebuild, so any value change via a
related onValueChanges callback would alter the hash, trigger
_initFormData, dispose all TextEditingControllers, and kill the
keyboard.

Hash now uses only key + runtime type for all item kinds -- form
re-initialization is driven by widget key changes (source
switch), not value fluctuations.
2026-07-15 18:25:16 +01:00
Imran Remtulla
a74bf9a145 Fix multiple tags gradient rendering where rightmost tag color incorrectly stretched across entire bubble (#3086) 2026-07-15 09:23:24 +01:00
Imran Remtulla
4de395454f - Removed tabbed UI (#3059, #3069)
- Improved update check UI performance (#3043)
- Improved app list loading UI (#2666)
- Other minor UI tweaks
- Improve install result detection for external installers in foreground
2026-07-15 00:10:39 +01:00
Imran Remtulla
dafecfe66e Fix download progress not updating on app list tiles because tileChild was computed outside ValueListenableBuilder 2026-07-14 09:37:10 +01:00
Imran Remtulla
6ae36c41ff Add disable-swipe setting and always-use-phone-layout option (#3039)(#3045). Pin app-detail actions bar to bottom of scroll (#3044). Fix settings scroll on wide/trackpad devices (#3046). Fix TV navigation regressions across all dialogs, text fields, search bar, radio picker, and app list checkboxes (#3053). Deduplicate TV text-field focus pattern into public TvTextFieldFocus widget. 2026-07-12 04:21:36 +01:00
Imran Remtulla
68a7a21746 Add disable-swipe setting and always-use-phone-layout option (#3039)(#3045). Pin app-detail actions bar to bottom (#3044). Fix settings scroll on wide/trackpad devices (#3046). 2026-07-11 19:02:53 +01:00
Imran Remtulla
579e77233d Apply German translations from issue, add lowerCaseUnlessLang helper (#3030) 2026-07-09 19:37:43 +01:00
Imran Remtulla
b485ad59ab perf: memoize app list pipeline, preload icons, fix form validation
- apps.dart: cache filtered/sorted/grouped app list results keyed by
  pipeline signature, avoiding O(n) recomputation on every rebuild
  when only progress ticks or selection changes fire

- add_app.dart: skip parent rebuild when URL text changes without
  switching the detected source; track _urlValid separately so the
  Add button correctly enables/disables; _prevValid prevents early
  return from skipping setState when validity flips

- generated_form_renderer.dart: replace broken validateTextField
  which couldn't find TextFormField inside TypeAheadField wrappers
  with a _fieldKeys list for direct FormFieldState.isValid checks

- apps_provider_lifecycle.dart: preload app icons from disk cache
  after initial loadApps() so scrolling never triggers disk I/O
2026-07-08 19:57:39 +01:00
Imran Remtulla
d420699c5e Fix subform add/delete buttons not rebuilding on app detail page
notifyFormChange() did not call setState(), so subform add/delete button
presses on the app detail (edit) page mutated values internally but never
triggered a rebuild of _buildSubForm(). The add_app page worked because
its parent onValueChanges callback called setState() externally.
2026-07-08 10:30:21 +01:00
Imran Remtulla
b3fc9f32e4 Use standard 16px spacing below subform headers 2026-07-08 09:16:50 +01:00
Imran Remtulla
07421696b1 Increase subform header spacing to 12px 2026-07-08 09:16:26 +01:00
Imran Remtulla
5ba6099178 Add spacing between subform entry header and its text field
Insert SizedBox(height: 8) between the per-link header (e.g. "Intermediate
link (1)") and the GeneratedForm containing the text fields below it.
2026-07-08 09:15:52 +01:00
Imran Remtulla
05aac42eff Fix subform add/delete buttons and settings page layout
- add_app.dart: populate form item values from additionalSettings before
  passing to GeneratedForm. Without this, the items hash changes on every
  rebuild (fresh clones have default values, not user data), causing
  _initFormData to reset user entries. This is why add/delete buttons
  on dynamic form items (Intermediate Link, Request Header, etc.)
  appeared to do nothing.
- settings_widgets.dart: replace Material.shape (RenderPhysicalShape)
  with ClipPath + custom _TileClipper + plain Material. RenderPhysicalShape
  does not support intrinsic sizing, causing tiles in CustomScrollView to
  render at zero height. ClipPath + Material uses RenderDecoratedBox which
  properly measures content.
2026-07-08 09:09:31 +01:00
Imran Remtulla
dd4f6ac19f Fix unsafe cast in _initSubForm: as List<dynamic> -> type check
Replace unchecked as List<dynamic> with an is! List guard and .cast()
to prevent crashes if formItem.value is not a List.
2026-07-08 09:03:42 +01:00
Imran Remtulla
875d8c44d8 Fix remaining List<Widget> cast in _initDropdown
The _initDropdown method still had formItem.belowWidgets as List<Widget>
after the _buildHelpSuffixIcon refactor. The helper now takes List<dynamic>
and casts internally via .cast<Widget>().
2026-07-08 08:56:58 +01:00
Imran Remtulla
308cd02445 Fix runtime crash: type cast error in _buildHelpSuffixIcon
The model stores belowWidgets as List<dynamic> but the helper parameter
was typed List<Widget>. Changed _buildHelpSuffixIcon parameter to
List<dynamic> and use .cast<Widget>() when passing to showHelpDialog.
2026-07-08 08:54:24 +01:00
Imran Remtulla
45f0d4c1bd Extract shared form helpers and deduplicate form patterns
- _fieldDecoration(): shared InputDecoration builder that handles tileMode
  border/fill config, used by both text fields and dropdowns
- _buildHelpSuffixIcon(): shared help URL / help-dialog icon builder
- add_app.dart: extract _buildSourceSpecificForm() from immediately
  invoked closure pattern in _getAdditionalOptsCol
2026-07-08 08:51:12 +01:00
Imran Remtulla
0f251fd64b Fix bugs, inconsistencies, dead code, and unsafe patterns in form components
- textInputType: dynamic -> TextInputType? (type safety, was crash-prone)
- GeneratedFormDropdown: add required and helpUrl fields, render help suffix
  icon and required indicator matching text field behavior
- Dropdown null fallback: preserve current selection instead of silently
  picking first option
- add_app.dart: remove redundant cloneFormItems (getter already returns clones)
- Remove unused _formKey and Form wrapper (validation done manually)
- validateTextField: add type-check guard before unsafe cast
- add_app.dart: remove dead resetUserInputAfter parameter and code
- _buildSubForm: fix variable shadowing (values -> subValues)
2026-07-08 02:52:39 +01:00
Imran Remtulla
52080469af Add vertical padding around SubForm column
Wrap the subform entries Column in Padding(vertical: 8) so panel content
has breathing room inside its SettingsTile, matching the BG check button
and add-app page button spacing.
2026-07-08 02:32:23 +01:00
Imran Remtulla
4af6e95e96 Fix BG check button and subform panel vertical padding
- Settings BG check button: add vertical padding to SettingsTile (16px),
  matching add-app page button spacing
- SubForm entries: add 8px spacing between inner form and delete button
- SubForm add button: increase top padding from 8px to 12px
2026-07-08 02:29:18 +01:00
Imran Remtulla
be71f33123 Extract shared code between settings and add-app form pages
A. Tile wrapper: Replace inline Material in tileMode form loop with
   SettingsTile — eliminates duplicate shape/clip/color wiring.

B. Haptic helper: Extract hapticSwitchOnChanged() shared between
   _FormSwitchRow and SettingsToggleRow.

C. Tile joining: Use shapeSettingsTiles() in tileMode form loop instead
   of inline isFirst/isLast/positionalTileShape logic.

D. ConnectedCard -> SettingsTile delegation: ConnectedCard now delegates
   to SettingsTile internally, unifying the tile rendering path (switches
   from standard rounded corners to RoundedSuperellipseBorder).

Dependency clean-up:
- Move showHelpDialog from ui_widgets to settings_widgets to break
  circular import (ui_widgets now imports settings_widgets for D).
- Remove unused theme.dart and ui_widgets imports from form renderer.
2026-07-08 02:22:51 +01:00
Imran Remtulla
e5d67e74ed Align settings page with add app page styling
- SettingsToggleRow contentPadding: 16 -> 20 (matches add app tileMode)
- SettingsTile: borderRadius -> RoundedSuperellipseBorder shape (matches add app)
- SettingsToggleRow: RoundedRectangleBorder -> RoundedSuperellipseBorder
- Add app _FormSwitchRow: add haptic feedback via selectionClick()
  (respects tactileFeedbackEnabled pref, matches settings page)
2026-07-08 02:09:23 +01:00
Imran Remtulla
3f2876f37f Increase settings page horizontal padding from 12 to 16
SettingsTile default padding and SettingsToggleRow contentPadding now use
16px horizontal, matching the InputDecoration contentPadding used by field
tiles (dropdowns, text fields) on the same settings page.
2026-07-08 02:02:57 +01:00
Imran Remtulla
63b0151f9c Fix settings toggle horizontal padding and ConnectedCard vertical padding
- SettingsToggleRow: contentPadding horizontal 8 -> 12, matching default
  SettingsTile padding so toggle labels align with other settings text
- ConnectedCard: default padding EdgeInsets.all(16) ->
  EdgeInsets.symmetric(horizontal: 16, vertical: 20) for more breathing room
  around text fields and buttons inside cards
2026-07-08 02:00:22 +01:00
Imran Remtulla
7d776639bc Unify form item padding, spacing, and label text style
TileMode:
  - All tiles now use surfaceContainerLow background (was mixed)
  - Non-field tiles: vertical padding unified at 8px (was 4px for switches,
    8px for subforms, 12px horizontal for subforms -> 16px)
  - Removed redundant switch/subform type check in padding logic

Non-tileMode:
  - Uniform 24px gap between all rows (was 16px after switches, 28px after
    other types)

Text styles:
  - Toggle label: use textTheme.bodyLarge (was default Text style)
  - SubForm header: use textTheme.bodyLarge.copyWith(bold) (was bare
    TextStyle with no textTheme token)
2026-07-08 01:46:47 +01:00
Imran Remtulla
f14a11acf7 Prevent stale translations by converting form-item fields and names to getters
All form-item list fields (additionalSourceAppSpecificSettingFormItems,
sourceConfigSettingFormItems, searchQuerySettingFormItems) and
_commonAppSettingFormItems now use getters or computed keys instead of
frozen tr() at construction time, so labels pick up the current locale
on every access via the renderer's tr() wrapping.

- _commonAppSettingFormItems: final field -> getter
- additionalSourceAppSpecificSettingFormItems: mutable field -> getter (12 sources)
- sourceConfigSettingFormItems: mutable field -> getter (GitHub, GitLab)
- searchQuerySettingFormItems: mutable field -> getter (GitHub, Codeberg)
- HTML's intermediateFormItems/commonFormItems/finalStepFormitems: var -> getters
- FDroidRepo's runOnAddAppInputChange: list mutation -> _appIdFromUrl bool
- Source names: tr('fdroid') etc. -> raw key string; display sites wrap with tr()
- completeInstallationNotification: module-level final -> getter
- GitHubStars.name/requiredArgs: fields -> getters; MassAppUrlSource -> abstract getters
- Removed memoization caches (_combinedItemsCache, _hasAppSpecificSettingsCache,
  _flatCombinedFormItemsCache) now superseded by getter rebuilding
- Renderer: tr() wrapping on dropdown option values
2026-07-07 23:50:50 +01:00
Imran Remtulla
4ca365fa36 fix: consistent form input styling and translation at render time
- SettingsToggleRow: remove full-row tap ink, unify both paths to
  non-tappable ListTile + Switch in trailing. Matches _FormSwitchRow
  in generated forms — only the Switch itself is interactive.
- ActionListTile: add optional borderRadius param for ListTile.shape
  to match container card corners for proper ink clipping.
- LinkText: add InkWell borderRadius (circular 4) to contain splash.
- Replace raw Material in add_app source note card with ConnectedCard.
- Replace raw Card in ExportSection with ConnectedCard.
- Add ListTile.shape (connectedTileBigRadius) to color picker and
  external installer tiles for matching ink clipping.
- Standardize SettingsTile paddings: remove horizontal 8px overrides
  on install method, external installer, group-by, and manual BG check
  tiles — use default horizontal 12px. Theme/sort order keep 16px.
- GeneratedForm labels now always call tr() at render time (7 locations)
  preventing stale/raw translation keys when the item cache is built
  before locale initialization or persists across locale changes.
- Remove unused SwitchListTile import (now unused in SettingsToggleRow).
2026-07-07 23:10:20 +01:00
Imran Remtulla
d75f82516e fix(install): use the app-wide navigator context for install dialogs
Install/update was launched with the local widget context, so if it unmounted
during the flow (a list tile rebuilt on re-sort, backing out of the detail
page, later apps in a mass install) the APK picker and origin-warning dialogs
were silently skipped - installing the default APK without asking. Pass
appNavigatorKey.currentContext (the root navigator, valid app-wide) like main.
2026-07-04 16:52:01 +01:00
Imran Remtulla
0c2226a4b2 fix(categories): don't overwrite an existing category's color on create
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.
2026-07-04 14:46:14 +01:00
Imran Remtulla
995b8b1fae fix(forms): restore isBuilding flag so initial form callbacks aren't user edits
notifyFormChange hardcoded isBuilding=false, so every '!isBuilding' guard
(settings sinks, add-app handlers, export filter) ran on form mount - e.g.
opening Settings wrote source-config values back to prefs. Pass isBuilding=true
on the initial/post-reinit callback and propagate it through sub-forms.
GeneratedFormModal now always setState (safe in the post-frame callback), so its
OK-button validity stays correct.
2026-07-04 04:45:10 +01:00
Imran Remtulla
325dc74a2a fix(forms): keep keyboard focus while typing in generated form fields
didUpdateWidget reinitialized the form (recreating controllers and focus
nodes) whenever the items hash changed, and that hash folded in each field's
live value. Since the parent mirrors the typed value back into the field's
value, every keystroke changed the hash and dropped keyboard focus. Exclude
text-field values from the hash; structural resets still reinitialize via a
changed widget key.
2026-07-03 20:39:15 +01:00
Imran Remtulla
51ad867261 feat(apps): modernize update button and refine list tile trailing
- Redesign the update button as an M3-expressive filled icon button
  with a superellipse shape
- Remove the cancel button from list entries during download
- Widen the version/changelog/progress trailing section
2026-07-03 19:31:23 +01:00
Imran Remtulla
7a8082cbe1 fix: remove tarball size limit, fix docs, clean up code quality issues
- Remove all tarball size limits (500MB cap deleted)
- Remove redundant 'late' keywords from AppsFilter fields that have constructor defaults
- Add logging to previously-silent catch in download size probe
- Fix DEVELOPER_GUIDE.md: remove nonexistent AppListCategorySection, fix showGeneratedFormModal reference, correct tarball docs, add installers/ and external_install_bridge.dart to directory tree, fix double backtick fence
- Update pubspec.yaml TODO comments for all git-fork dependencies to be more specific
2026-07-03 14:52:21 +01:00
Imran Remtulla
c911cba670 fix: resolve 20 bugs, safety issues, and code quality problems
Critical fixes:
- Remove setTrustedCertificatesBytes which replaced the entire TLS trust store
- Fix late final async init race causing LateInitializationError crashes
- Fix infinite recursion in ObtainiumError.message for MULTI_ERROR code

High fixes:
- Fix identical() always returning false on badge icon (update count never shown)
- Clone form items before mutation to prevent cross-contamination between sources
- Log transient errors before removing apps on load failure
- Await uninstallApp calls instead of fire-and-forget
- Revert optimistic install on background workaround failure
- Log SAF errors before silently disabling export directory

Medium fixes:
- Remove dead GeneratedForm.fromDefinitions constructor and unused fields
- Replace Shizuku-specific param with generic installOptions map in Installer interface
- Add importable-keys allowlist to prevent arbitrary SharedPreferences writes
- Guard against stale Shizuku permission check callbacks with sequence counter
- Prevent ReceivePort leak in listenForDownloadCancelFromMain
- Add 500MB tarball size guard to prevent OOM on large archives
- Simplify ReDoS-vulnerable URL regex in html.dart
- Fix TOCTOU race between existsSync and length calls
- Log rename failures instead of empty catch blocks
- Use safe null-aware defaults in setFormValuesFromMap
2026-07-03 13:44:04 +01:00
Imran Remtulla
10b3af22cc fix: squash 18 bugs, dead code, and code quality issues across the codebase
- Fix rethrowOrWrapError to preserve original stack traces and populate
  null stacks on unexpected ObtainiumError instances
- Fix MultiAppMultiError.message returning empty string (use toString())
- Fix getDefaultValuesFromFormItems returning '' for bool-typed switches
  and subforms (use ?? false and ?? [] respectively)
- Fix GeneratedFormSubForm.ensureType returning val unchanged
- Fix main_fdroid.dart async-without-await (unhandled Future errors)
- Fix InstallResult conflating null/'already installed' (code 3) with
  'cancelled' — add InstallOutcome.alreadyInstalled
- Fix external installer ignoring split APKs (only used first path)
- Replace direct LogsProvider() instantiation in settings.dart with
  context.read<LogsProvider>()
- Replace 7 ad-hoc SourceProvider() instances across pages with
  context.read<SourceProvider>() or constructor injection
- Remove ExportSchema.credentials dead field (never populated)
- Remove redundant double .abs() in DownloadNotification ID calculation
- Remove redundant overrideName double-check in AppInMemory.name getter
- Remove unnecessary '!= true' anti-pattern in auto-export check
- Replace 4 throwaway UpdateNotification([]).id calls with named constant
  updateNotificationId
- Add notification ID named constants (updateNotificationId, etc.)
- Deduplicate _showImportError / showImportError in import_export.dart
- Fix _readFileBytes dangling ByteData (use ByteData.sublistView
  instead of ByteData.view)
- Fix apps.dart missing setState after pruning stale selectedAppIds
2026-07-03 09:19:27 +01:00
Imran Remtulla
538953e4f2 feat: open the app by tapping its icon on the detail page 2026-07-02 23:16:50 +01:00
Imran Remtulla
12db58050d feat: group apps by source, plus grouped-list and FAB polish
- Replace the 'Group by category' toggle with a 'Group by' control
  (None/Category/Source); sources are listed alphabetically. Migrates the
  old groupByCategory setting.
- Give grouped entries small gaps so they read as distinct positional
  tiles, and make the multi-select/pinned highlight follow each tile's
  positional (or card) corners instead of the default radius.
- Make the multi-select 'more' FAB extended to match the Add FAB.
- Expose FAB shadow via floatingActionButtonTheme elevation in theme.dart.
2026-07-02 22:44:21 +01:00
Imran Remtulla
7d6ad09910 feat: expressive Add FAB, broader haptics, and progress-spinner consistency
- Use an extended (icon + label) FAB for the primary Add action in the
  bottom layout; keep a compact FAB in the navigation rail so it fits.
- Add tactile feedback (respecting the existing setting) to interactions
  that lacked it: download cancel buttons, Add/more/view FABs, list-tile
  update and swipe install/remove, settings segmented buttons, and all
  settings toggles (centralized in SettingsToggleRow).
- Drop manual strokeWidth overrides so inline spinners inherit the themed
  Material 3 look.
2026-07-02 21:41:34 +01:00
Imran Remtulla
c2aac8885a feat: per-download cancel button and download size display
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.
2026-07-02 21:03:58 +01:00
Imran Remtulla
e15abd627c fix: remove dead code in _resolveAppId, rename generateRandomNumber, use sourceIdentifier over runtimeType, and fix slider tile positional radii in settings 2026-07-02 16:15:21 +01:00