Commit Graph

1304 Commits

Author SHA1 Message Date
Rahul Patel
9f813d965b fix linting issue & add new icon 2026-05-30 18:41:31 +05:30
Rahul Patel
60a6df58c5 Organise notification channels into system-settings groups
Group the five channels under two headings in the system notification
settings so they're easier to navigate: "Downloads & updates" (progress,
installed, updates, export) and "Alerts" (errors and action-required).

Purely organisational; channel importances and behaviour are unchanged.
2026-05-30 18:27:25 +05:30
Rahul Patel
a660983438 Add a Notifications settings screen with a progress toggle
There was no in-app control over notifications. Add a Notifications
preference screen under Settings offering:

- "Show download progress": when off, downloads/updates no longer post
  per-tick progress; a single quiet foreground notification keeps the
  work running in the background (honouring the mandatory FGS rule).
- Deep links into each notification category's system settings (O+) so
  users can tune sound and importance per channel.
- A shortcut to enable notifications when they're turned off at the
  system level, so the screen degrades gracefully.

The download worker reads the progress preference live, keeping its
foreground notification minimal and skipping progress refreshes when
the user has opted out. POST_NOTIFICATIONS is already handled by the
onboarding permission flow, so no duplicate prompt is added here.
2026-05-30 18:17:52 +05:30
Rahul Patel
aa5d3fcc4b Group install and failure notifications under summaries
A bulk update produced one standalone notification per app for both
successes and failures, flooding the shade. Bundle these terminal
notifications into two groups, each with a collapsible summary that
lists the apps via InboxStyle:

- "N apps installed" on the (LOW, silent) installed channel, expiring
  on its own since it's informational.
- "N apps failed" on the (HIGH) alerts channel, persisting until
  handled and carrying a Retry action per app.

Summaries are rebuilt from the live posted notifications rather than
tracked state, so they stay correct across retries, dismissals and
process restarts, and are only posted on Android N+ where the system
actually renders groups.

Retry re-queues the download via a new DownloadRetryReceiver; the
worker resumes from verified files on disk, so retrying an install
failure re-installs without re-downloading.
2026-05-30 18:07:28 +05:30
Rahul Patel
9bd5e3f8e7 Stop per-app download notification chatter during installs
The download worker posted a separate per-app notification for every
internal phase transition, keyed by package hash and distinct from the
ongoing foreground progress notification. In practice this meant:

- a blank, non-dismissable notification while "purchasing" (no branch
  rendered it), and
- a transient "download complete" notice for every app that was then
  immediately replaced by the install-success notification.

During a bulk update this produced a burst of redundant notifications.

Suppress the purchasing/verifying phases (the ongoing progress
notification already conveys activity) and only surface completion when
the user must act on it (apps that can't be installed silently get a
tap-to-install notice). Silently-installable apps now go straight to a
single "installed" notification.

Also clear any stale per-app notification on these phases and on
cancellation, fixing a leak where a cancelled download left its last
notification behind, and refresh the foreground notification to an
"Installing" state so the user sees a clean downloading -> installing
progression instead of a download bar stuck at 100%.
2026-05-30 17:57:50 +05:30
Rahul Patel
012f4dcc26 Restructure notification channels by intent and importance
Notifications were too intrusive: install-success and export sat on
IMPORTANT_HIGH channels and produced heads-up alerts for every app,
while genuine download failures were buried in the IMPORTANCE_MIN
downloads channel where they were easily missed.

Restructure the channels around user intent:

- Download progress -> MIN, silent, no badge
- Installed apps    -> LOW, silent (was HIGH)
- Updates available -> DEFAULT, silent
- App export        -> LOW, silent (was HIGH)
- Errors & alerts   -> HIGH (new, the only heads-up channel)

Failed downloads, installer-status errors and the unarchive auth prompt
now route to the high-importance alerts channel so they aren't lost,
while successful installs/exports stay quiet.

Android ignores importance edits on an already-created channel, so the
install/export channels get new IDs and the retired IDs (including the
folded-in account channel) are deleted on next launch.
2026-05-30 17:54:59 +05:30
Rahul Patel
a68d1b276d Add optional app lock behind biometric or device credential
Implements a full app lock (issue #1313) gating Aurora Store behind the
device biometric or, where unavailable, the screen-lock credential
(PIN/pattern/password) so it also works on TVs and older phones.

- AppLockManager: process-scoped, in-memory lock state so a cold start is
  always locked, with a short background grace timeout to avoid
  re-prompting during the install dialog or the biometric sheet itself.
- AppLockAuthenticator: BiometricPrompt wrapper using
  BIOMETRIC_STRONG | DEVICE_CREDENTIAL on API 30+ and
  setDeviceCredentialAllowed on older releases, plus an enrollment check.
- ComposeActivity (now a FragmentActivity) gates content via a three-state
  machine (AUTHENTICATING/LOCKED/UNLOCKED); the authenticating state shows
  a blank surface behind the prompt so dismissing it never flashes the
  lock card. Re-locks on return past the timeout and sets FLAG_SECURE
  while locked.
- Toggle lives on a dedicated Security preference screen, reached from
  Settings like the other preference entries.
2026-05-30 15:25:27 +05:30
Rahul Patel
db9a7723a8 Fall back to session installer when chosen installer is unavailable
Address #1386

The Shizuku branch of getPreferredInstaller() already fell back to the
session installer, but the guard hasShizukuPerm() called
Shizuku.checkSelfPermission(), which throws when the binder is not alive
(Shizuku disabled). The exception escaped before the fallback could run,
so installs failed instead of falling back.

- Guard hasShizukuPerm() on Shizuku.pingBinder() and swallow failures so
  it returns false (instead of throwing) when Shizuku is unavailable,
  letting the existing fallback run. This also fixes canInstallSilently().
- Detect when any chosen installer resolves to the default and optionally
  inform the user via a toast, gated by notifyOnFallback so cancel/remove
  callers stay silent while real install paths surface it.
2026-05-30 05:58:53 +05:30
Rahul Patel
25d0944e37 Improve app export: completeness, reliability and progress
Address several gaps in the app export feature (#1132):

- Bundle shared-library APKs and OBB/patch files, preserving their
  relative layout (libraries/<pkg>/ and Android/obb/<pkg>/).
- Fall back to the installed app's on-device APKs when the cached
  download was cleared (e.g. auto-deleted after install), guarded by an
  exact version match so a different installed version is never exported.
- Guard against the download row being replaced by a manual download of
  another version between enqueue and execution.
- Fail (and delete the partial/empty document) instead of reporting
  success when there is nothing to bundle or an error occurs.
- Store entries uncompressed and use larger buffers, so multi-GB exports
  are no longer bottlenecked on pointless re-compression.
- Report progress through the foreground notification (alert once, then
  silent) and dedupe concurrent exports for the same app via unique work.
2026-05-30 05:40:53 +05:30
Rahul Patel
7d87a14c89 Harden download routine: storage checks, single verification, URL expiry
- Check free space before downloading (only the not-yet-fetched bytes) and, on
  Android O+, use StorageManager.getAllocatableBytes/allocateBytes so the system
  can evict its cache; fail fast with a clear message instead of dying mid-write.
  SessionInstaller sets a SessionParams size hint so staging can reserve space
  too.
- Verify each APK at most once: files already verified during the download pass
  are skipped in the final verification gate. Prefer SHA-256 and log SHA-1
  fallback.
- An expired download URL (403/410) now clears the stored file lists and retries,
  re-purchasing fresh URLs instead of repeatedly failing on the dead one.
- Cancel promptly mid-file rather than only between files, and cancel copyTo's
  progress timer in a finally to avoid leaking the timer thread.
- Download.canInstall now requires a real .apk on disk; DownloadStatus
  finished/running are Sets.
2026-05-30 00:49:04 +05:30
Rahul Patel
03638ca84c Track the install phase with INSTALLING/INSTALLED download states
A download row previously stopped at COMPLETED and never reflected whether the
app actually installed. Add INSTALLING/INSTALLED states driven by a central
installer-event observer in DownloadHelper:
- COMPLETED -> INSTALLING on the installer's first progress event
- -> INSTALLED on success (row kept so the APK can still be exported)
- a failed install reverts INSTALLING -> COMPLETED so it can be re-installed
  without re-downloading

Consumers that branched on COMPLETED are updated (App Details state, MicroG
status mapping, Downloads list icon). downloadStatus is stored as TEXT so no
schema migration is needed.
2026-05-30 00:37:05 +05:30
Rahul Patel
7e8746dbcb Abandon staged install session when a download is cancelled
Cancelling a download cancelled the worker and marked the row CANCELLED but
never abandoned a PackageInstaller session that had already been staged for
install, leaking it. Add IInstaller.cancelInstall (no-op default, implemented by
SessionInstaller) and call it from DownloadHelper.cancelDownload.

Cross-process session persistence/reconciliation (committing a session staged
before a restart) is left as a follow-up; the startup session cleanup remains,
and is now cheap to recover from since CacheWorker keeps the downloaded files.
2026-05-30 00:29:49 +05:30
Rahul Patel
ab9b66eb94 Serialize downloads and make cache cleanup install-aware
- CacheWorker no longer purges the files of a download that is still in-flight
  or downloaded and awaiting install, so missing the system install prompt for
  longer than the cache window no longer forces a re-download.
- observeDownloads now starts the next queued download only once nothing else
  is purchasing/downloading/verifying, instead of merely checking for a
  DOWNLOADING row. This serializes downloads so concurrent workers can't clobber
  the shared foreground/progress notification.
2026-05-30 00:27:51 +05:30
Rahul Patel
ada5a1f899 Harden download routine against redownloads and flaky networks
- enqueue() no longer resets an active/verifying download back to QUEUED, so
  the periodic update check and repeated taps can't trigger needless
  re-downloads; when files are already downloaded & verified it installs
  directly instead of re-running the pipeline.
- DownloadWorker only purges partial files on a genuine user cancellation;
  system-initiated stops (lost connectivity, quota) keep partials and retry,
  fixing downloads appearing to restart on a flaky network.
- Add NetworkType.CONNECTED constraint + exponential backoff and return
  Result.retry() for transient/network failures (capped) instead of always
  succeeding.
- Validate HTTP 206 before resuming a .tmp (overwrite on 200) and drop corrupt
  files on verification failure so retries restart clean.
2026-05-30 00:19:33 +05:30
Rahul Patel
d13d50e442 Surface self-updates through the regular updates list
Fold Aurora Store's self-update back into the existing update pipeline
instead of a dedicated sheet/viewmodel/helper. The feed entry is mapped
to a regular App and added to the update list, reusing the standard
download + install path; it is never auto-installed silently.

- Add a dedicated "Self-update" section in the Updates tab with the
  app-details navigation disabled (it's served from the Aurora OSS feed).
- Gate eligibility via PackageUtil.isSelfUpdateSupported(): vanilla/preload
  flavors, not debug, not F-Droid (huawei excluded by flavor).
- Add a build-aware Settings toggle as the opt-out, removing the row
  immediately when disabled.
- Exempt nightly self-updates from deleteInvalidUpdates (static version
  code) and drop any stale self-update row when none is offered, so a
  phantom update doesn't linger after a self-install.
2026-05-29 23:28:09 +05:30
Rahul Patel
71f6538d46 vm: let paging errors surface so retry() re-runs the load
Stream/ExpandedStream/Review/Search paging VMs were swallowing exceptions
inside manualPager and returning emptyList, so the pager never entered
LoadState.Error and the error-placeholder retry buttons were dead. Let
non-Auth exceptions propagate; keep the AuthException → SessionExpired
branch so 401 still hands off to Splash.

Also drop supervisorScope wrappers from CategoryStream/DevProfile/TopChart
VMs that launch no children — the surrounding try/catch already handles
everything.
2026-05-29 01:31:48 +05:30
Rahul Patel
0bcb087be2 use a SessionExpired event instead of a retry loop 2026-05-29 01:31:48 +05:30
Rahul Patel
bae9233b27 Update viewmodel to reflect gplayapi changes 2026-05-29 01:31:48 +05:30
Rahul Patel
eada663bfb compose: use ic_refresh icon on retryable error placeholders 2026-05-29 01:31:48 +05:30
Rahul Patel
272870ddd2 auth: refresh anonymous session on activity resume
Long-backgrounded sessions (especially on aggressive vendors like Huawei)
could leave the Play token invalid and OkHttp's connection pool full of
dead sockets, producing a perpetual loading spinner with no error.

- ComposeActivity#onStart probes the saved token via isSavedAuthDataValid;
  on failure evicts the OkHttp pool and refreshes anonymous auth.
- AuthProvider gains authReady StateFlow, awaitReady(), and a mutex-guarded
  refreshAnonymousAuth() so concurrent callers don't double-fetch.
- Play-backed ViewModels (AppDetails, DevProfile, MoreApps, Reviews, Search,
  ExpandedStreamBrowse, Category) await refresh completion before issuing
  helper calls, closing the race between resume-time refresh and screen
  LaunchedEffect fetches.
- AppDetailsScreen rendered Error as Loading when app was null; reorder so
  Error wins and wire a Retry button through Placeholder.
- Stream/Review/Search/DevProfile/CategoryBrowse error placeholders gain
  Retry actions wired to PagingItems.retry() or the VM's fetch.
- CategoryViewModel was logging-only on failure; now emits ViewState.Error
  with a matching Retry path in CategoriesPage.
2026-05-29 01:31:48 +05:30
Rahul Patel
ada4e8de2a fix: google account login - 2 2026-05-29 01:31:48 +05:30
Rahul Patel
1607529bbd fix: google account login 2026-05-29 01:31:48 +05:30
Rahul Patel
ab888fab75 rootinstaller: fix apps being installed on main profile from work profile
Resolves: #1382
2026-05-29 01:31:48 +05:30
Rahul Patel
7bb2728b57 favourites: add a check icon to indicate app is installed
resolves: #1153
2026-05-29 01:31:47 +05:30
Rahul Patel
0493075783 installed/blacklist: add sort & filter sheet + dedicated list item [1/2]
Share a sort/filter sheet between InstalledScreen and BlacklistScreen via
new commons module (SortFilterState + SortFilterSheet + applyFilter/applySort
on InstalledAppMeta), persisted per-screen. InstalledScreen renders a new
InstalledAppListItem that surfaces size, last-update, and installer instead
of Play-listing extras. Blacklisted rows now desaturate their icons.
2026-05-20 03:43:36 +05:30
Rahul Patel
572387d7b5 downloads: minor syntax change 2026-05-20 01:30:33 +05:30
Rahul Patel
b84f3a3fa9 updates: fix stale UI states & cancel-all coverage
- AppUpdateItem now shows a disabled "Installing" button while the
  download is COMPLETED and awaiting install, instead of briefly
  reverting to "Update" between download-done and InstallerEvent.Installed.
- Section header replaces all-or-nothing "Update all" toggle with an
  any-active check, so "Cancel all" appears as soon as a single update
  is in flight. Same treatment for the ownership-approval section.
- Empty placeholder no longer hides the ignored-updates list when there
  are no live updates.
- DownloadHelper.cancelAll also flips COMPLETED -> CANCELLED so the
  Cancel-all button gives immediate feedback for pending-install rows.
- DownloadListItem: surface the failed icon for FAILED downloads too.
2026-05-20 01:21:19 +05:30
Rahul Patel
91fc4d714a network: hoist status into CompositionLocal and promote sheet to screen
Collect NetworkProvider.status once at the activity root and expose it via
LocalNetworkStatus so any screen can read it without injecting the provider.
Drop the redundant networkProvider field from MainViewModel and AuthViewModel.
NetworkSheet becomes a full-screen NetworkScreen with the same content.
2026-05-20 00:53:03 +05:30
Rahul Patel
0391adf82f appdetails: add back error sheet
- I intend to use this sheet to offer possible workarounds/solutions later.
- Just future proofing
2026-05-19 23:32:13 +05:30
Rahul Patel
fa0e3c53d1 screenshots: filter out dupes & used stable id 2026-05-19 22:49:38 +05:30
Rahul Patel
8c51175312 downloads: improve ui 2026-05-19 22:37:26 +05:30
Rahul Patel
5c6bcd6361 compose: housekeeping 2026-05-19 16:40:37 +05:30
Rahul Patel
c4bc76e2a4 extention: remove unused duplicate function 2026-05-19 14:39:29 +05:30
Rahul Patel
8f0bf55714 updates: granular installer filters + advanced-section move
- New SourceFiltersScreen with All toggle + add/remove of specific
  installer package names. Backed by PREFERENCE_FILTER_INSTALLERS
  (Set<String>); PREFERENCE_FILTER_AURORA_ONLY preserved as the All
  switch for backward compat.
- UpdateWorker reads installer via getInstallSourceInfo (R+) / falls
  back to deprecated getInstallerPackageName below.
- UpdatesPreferenceScreen: "Filter F-Droid apps" moved to Advanced;
  "Filter apps from other sources" becomes a navigation row to the
  new sub-screen with a state summary.
2026-05-19 12:14:59 +05:30
Rahul Patel
106779737c updates: switch frequency slider to discrete preset chooser
Replace the 1-24h slider with a SingleChoiceDialog listing 7 presets
(3h, 6h, 12h, 24h, 3d, 7d, 15d). Storage stays as an Int hour count
under the same preference key, so old values keep working; off-preset
values display as the nearest preset but are only overwritten when the
user picks something new.
2026-05-19 12:14:59 +05:30
Rahul Patel
1e191e13c8 updates: revamp updates UX
- Group updates into main / approval-required / incompatible / ignored
- New ignored_update table (v8) with per-version skip support
- isIncompatible flag on pristine system apps for HyperOS / GrapheneOS (v7)
- requiresOwnershipTransfer() runtime check on Android 14+
- Rename AppMenuSheet -> AppUpdateSheet; drop MinimalApp, nav carries Update
- Trim AppUpdateItem (no changelog dropdown / long-click)
- Fix DownloadHelper.downloadsList recomposition flicker (stable StateFlow)
- ExportWorker is now download-only
2026-05-19 12:14:59 +05:30
Rahul Patel
f5ef5e71e9 appdetails: add back app suggestions 2026-05-19 12:14:59 +05:30
Rahul Patel
6dc1cf2a8c legacy: remove fragment-based screens and sheets
Drops every Fragment, DialogFragment and BottomSheet in view/ui plus
the flavoured SplashFragment variants. All call sites now route
through compose/ui equivalents and ComposeActivity.
2026-05-19 12:14:03 +05:30
Rahul Patel
fef75cb5b3 legacy: remove Epoxy views and custom view layouts
Drops the Epoxy carousel controllers, view models, shimmer placeholders,
custom layouts (ActionHeader, StateButton, UpdateButton) and bespoke
preference helpers, all now superseded by the Compose composables.
2026-05-19 12:13:42 +05:30
Rahul Patel
0270dc6f39 compose: migrate remaining screens and fix category browse
CategoryBrowseScreen and ExpandedStreamBrowseScreen are added; the
details / dev / dispenser / downloads / favourite / installed /
search / spoof / blacklist / accounts / about screens, StreamBrowse,
the onboarding MicroG page, the AppDetailsScreen plus the affected
viewmodels (AuthViewModel, ExpandedStreamBrowse, CategoryStream,
TopChart) are reworked to feed the new Compose entry points.

Also fixes CategoryBrowse pagination and the cluster-header
navigation regressions surfaced during migration.
2026-05-19 12:13:20 +05:30
Rahul Patel
f2474cfb91 compose: overhaul SplashScreen and add GoogleLoginScreen
SplashScreen reacts to NetworkProvider and ditches the separate
SplashButton class for inline buttons. Google sign-in moves into the
dedicated GoogleLoginScreen reachable from the splash flow.
2026-05-19 12:12:42 +05:30
Rahul Patel
0c60df635f compose: add NetworkSheet, MoreSheet and AppMenuSheet
NetworkSheet replaces the legacy NetworkDialogSheet with a persistent
modal that reacts to NetworkProvider status. MoreSheet hosts the
account / settings / about entry points. AppMenuSheet provides the
per-app long-click menu (blacklist, uninstall, export, info).
2026-05-19 12:12:23 +05:30
Rahul Patel
9e0a4ffee7 compose: migrate preferences screens
Settings, UI, Installation, Network and Updates preference panes are
now native Compose. Replaces the old PreferenceFragment XML hierarchy
with explicit ListItem / Switch / dialog rows.
2026-05-19 12:12:01 +05:30
Rahul Patel
9a1191ecf8 compose: add UpdatesScreen with pull-to-refresh
Renders the cached update list, exposes per-row update / cancel
actions, and supports the bulk Update-All header. Pull-to-refresh
triggers an expedited UpdateWorker check.
2026-05-19 12:11:41 +05:30
Rahul Patel
8d3eb1176e compose: build MainScreen and the apps/games tabs
MainScreen hosts the bottom-nav between Apps / Games / Updates with a
HorizontalPager. The Apps/Games host (AppsGamesScreen) carries the
For You / Top Charts / Categories sub-tabs implemented as separate
pages.
2026-05-19 12:11:23 +05:30
Rahul Patel
baf593bd51 compose: rewire navigation as the single entry point
ComposeActivity becomes the sole launch target; the legacy MainActivity
is removed. Destination / Screen pair drives navigation3, NavDisplay
handles deep links plus microG-aware resume rechecks, and Theme.kt
is tightened for edge-to-edge.
2026-05-19 12:10:59 +05:30
Rahul Patel
7617a9b953 compose: add StateFlow/LiveData collectForced helpers
Bridges Flows whose values have broken equals() (the gplayapi
StreamBundle/StreamCluster id-only-equals quirk) into Compose state by
forcing recomposition via neverEqualPolicy.
2026-05-19 12:10:06 +05:30
Rahul Patel
2137710012 compose: add stream and app item composables
StreamCarousel for cluster-based home/category lists, CategoryItem grid
tile, and AppUpdateItem for the updates list. AnimatedAppIcon plus the
existing AppListItem and LargeAppListItem are adjusted to fit the new
call sites.
2026-05-19 12:09:51 +05:30
Rahul Patel
d12cd77e17 compose: add shared composable foundations
EmptyState, SectionHeader, Shimmer placeholders; TopAppBar and
ScrollHint refinements to support the migrated screens.
2026-05-19 12:09:33 +05:30
Rahul Patel
ff69759634 microg: improve installer state detection 2026-05-12 20:51:21 +05:30