Files
zapstore/spec/work/WORK-010-unmanaged-apps.md
T

3.3 KiB

WORK-010: Unmanaged Apps

Goal

Allow users to mark installed apps as "Unmanaged" so they are excluded from the Updates screen and the "Update All" batch operation.

Approach

Store the unmanaged set as an encrypted AppStack (kind 30267) with identifier zapstore-unmanaged-apps, using the same pattern as the bookmarks stack. This gives reactivity (SQLite + query), NIP-44 encryption, and cross-device sync.

Tasks

  • Add kUnmanagedAppsIdentifier constant
  • Update appStackEventFilter to reject the unmanaged-apps stack by d tag
  • Add flutter_slidable dependency
  • Create unmanagedAppsProvider (mirrors bookmarksProvider)
  • Create unmanaged_apps_service.dart with toggleUnmanagedApp write helper
  • Filter unmanaged apps out of all lists in categorizedUpdatesProvider
  • Add unmanagedApps: List to CategorizedUpdates
  • Wrap app cards in Slidable on updates screen (swipe-left -> Unmanage)
  • Add "Unmanaged Apps" section at bottom with swipe-left -> "Manage" action
  • Preserve catalog metadata for cataloged unmanaged apps in the Unmanaged Apps section
  • Extend native package scan with Android installer-source metadata
  • Default apps installed by known third-party app stores to unmanaged
  • Keep browser/manual/package-installer installs managed by default
  • Add explicit override state so user Manage/Unmanage choices win over installer-source defaults
  • Cover defaulting, override, restart, and unavailable-installer-source cases in tests

Decisions

  • Encrypted stack (not CustomData) reuses existing infrastructure and encryption
  • Unmanaged app IDs stored as bare package IDs since uncataloged apps have no kind:pubkey prefix
  • For cataloged apps the app.identifier (package ID) is used consistently
  • Graceful degradation: unsigned-out users see no swipe actions
  • Unmanaged apps are excluded from automaticUpdates, manualUpdates, upToDateApps, and uncatalogedApps
  • Installer-source detection should be conservative: unknown or ambiguous source means managed
  • Third-party app stores should default unmanaged; Zapstore, package installer, file manager, browser, shell, and unknown/manual flows should default managed
  • Automatic defaults need a persistent "user has overridden this package/install" signal; otherwise tapping Manage would be undone by the next package scan
  • Cataloged unmanaged apps should render from App metadata when local catalog data exists, and fall back to PackageInfo only when uncataloged

Implementation Notes

  • Android can expose source through PackageManager.getInstallSourceInfo(packageName) on API 30+ and getInstallerPackageName(packageName) on older APIs.
  • Add installer-source fields to PackageInfo, AndroidPackageManager.syncInstalledPackages, BackgroundPackageManager.syncInstalledPackages, and InstalledPackagesSnapshot.
  • Store policy separately from the unmanaged AppStack or extend the private data model carefully: the unmanaged stack alone cannot represent both "auto-unmanaged by source" and "user explicitly managed this package".
  • The updates categorizer currently removes unmanaged app IDs before querying App metadata. To preserve metadata, it needs a separate local query for unmanaged cataloged IDs with latestAsset / latestRelease.latestMetadata, then render those with AppCard and a Manage action.