Skip to content

Repository files navigation

Photos for Proton

Photos for Proton

End-to-end encrypted photo backup and browsing for your Proton Drive Photos library.
Unofficial open-source Android client built against the publicly documented Drive API by Akoos. Not affiliated with Proton AG.

Release License Min SDK Website

Buy me a coffee

Features

Area What you get
End-to-end encrypted Photos, videos, albums and names encrypted with ProtonCore + GoOpenPGP.
Backup Per-folder or back-up-everything (with a per-folder exclude list), Wi-Fi-only, continuous (uploads start seconds after capture and resume after a reboot), parallel uploads. Optional lighter uploads that compress photos and videos to a quality you pick, leaving the on-device originals untouched. Auto re-pairs your library after a reinstall; optional delete-after-backup that routes removed copies to the system trash so they stay recoverable, plus one-tap or automatic scheduled free-up of already-backed-up copies. An Activity screen shows uploads, downloads and offline saves in progress, the photos still waiting to upload, and a history of finished transfers.
Browse & find Pinch-zoom timeline that regroups by day, month or year, a year-jump scrubber, a calendar view (a hero photo and editable place per day, type-to-jump search), a photo map of your located shots with offline place names, per-place pages, search (type, date and sync filters, accent-insensitive), and a memories page (On this day, seasons).
Albums & sharing Cloud-native encrypted albums with custom covers; share by email with viewer or editor roles, manage members and invites, and save albums shared with you into your own library; mirror device folders to matching Drive albums. When adding photos, see which albums already hold each one and filter the picker to photos not yet in any album.
Organize Multi-select by long-press or by dragging across the grid, in the timeline, albums and device folders, with bulk back-up, download, add-to-album, delete, hide and strip; a duplicate finder for exact and visually similar photos, on device and in the cloud, with a review step before removing extras; per-folder browsing; a display-only timeline filter; share to any other app.
Edit Photo editor (eight adjustments, filter, redact, rotate, free-form crop, undo and redo) and video editor (trim, crop, rotate, music overlay, strips location metadata); RAW and DNG previews; open a photo from any app straight into the viewer or editor.
Privacy & lock Hide any photo, or a whole cloud album, and restore it from the Hidden screen: photos only on this device move into a locked area behind biometric or PIN with a blur overlay, while backed-up and cloud-only photos are hidden from your timeline with the Drive copy never moved or deleted. Per-field metadata stripping (GPS, camera, timestamp, software) on upload or in bulk, including formats such as HEIC that can't be edited in place. Mirror-to-local so your on-device copy matches the cloud, in-app cloud trash, and a configurable app lock with timeout.
Hardened TLS cert pinning on every Proton call, a strict host allowlist with cleartext blocked, no app state carried in cloud auto-backup or device transfer, and logs stripped from release builds.
Comfort Make any cloud photo available offline, pinned at full quality with an offline badge, a Search filter and a storage readout; a full-screen viewer with slideshow, motion-photo playback and frame-by-frame video scrubbing with a filmstrip preview; an optional edge-to-edge grid for a denser, gapless timeline; an opt-in screenshot bar to edit, share, upload or link a new screenshot; a background thumbnail warm-up for smooth scrolling, a home-screen widget (including a Cloud Photos mode served from the encrypted cache), an in-app FAQ, an in-app updater, an adaptive app icon, 11 languages, light, dark and system themes with a pure-black AMOLED option, and 6 colour palettes.

Install

Download the latest APK from the releases page and tap to install.

Architecture

Clean architecture in a single module, with strict one-way layer dependencies:

Layer Holds Depends on
Presentation Jetpack Compose screens, ViewModels, navigation, theme Domain
Domain Entities, repository interfaces, use cases, pure Kotlin, no Android imports none
Data Drive API client, crypto wrapper, Room database, DataStore, repository implementations, WorkManager workers Domain

Hilt binds the Data implementations to the Domain interfaces; every Proton Drive feature lives under data/repository/drive/ (detailed below).

Project structure

app/src/main/kotlin/eu/akoos/photos/
├── presentation/     UI: Composables + ViewModels
│   ├── auth/         Sign-in
│   ├── onboarding/   First-launch wizard (about, backup mode, folder pick, privacy, metadata mirror, lock, appearance, permissions)
│   ├── gallery/      Photos tab + multi-select
│   ├── albums/       Albums tab + detail + sharing
│   ├── shared/       Shared-with-me albums tab
│   ├── folders/      Per-device-folder detail + multi-select
│   ├── memories/     On this day + seasons memories
│   ├── calendar/     Calendar view + per-day metadata editor
│   ├── map/          Photo map (osmdroid thumbnail pins, place drawer, city search)
│   ├── location/     Per-place detail page
│   ├── viewer/       Full-screen photo / video viewer
│   ├── editor/       Photo + video editor
│   ├── search/       Search with filename + content filters
│   ├── duplicates/   Duplicate finder (exact + visually similar)
│   ├── offline/      Make photos available offline
│   ├── hidden/       Hidden vault (PIN / biometric)
│   ├── settings/     Settings, About, Privacy, Permissions, Activity, Language, Theme, Palette, Trash, FAQ
│   ├── lock/         App lock
│   ├── updater/      In-app update orchestration
│   ├── whatsnew/     Post-update highlights
│   ├── common/       Shared composables (ErrorPopup, ConfirmDialog, EmptyState, ThemedSnackbar)
│   ├── util/         Presentation-layer helpers (formatters, focus helpers)
│   └── theme/        Colour tokens + palette factories
├── domain/           Pure domain layer
│   ├── entity/
│   ├── repository/   Interfaces
│   └── usecase/
├── data/             Implementations + IO
│   ├── api/          Retrofit Drive API + DTOs
│   ├── crypto/       DriveCryptoHelper (ProtonCore wrapper)
│   ├── db/           Room DAOs / entities / migrations
│   ├── repository/   incl. GpsBackfillScheduler + CloudGpsBackfillScheduler
│   │   │             (geotag extraction for on-device + cloud photos)
│   │   └── drive/    Drive backend split per concern (Upload, Download,
│   │                 Stream, Album, AlbumSharing, AlbumCryptoChain,
│   │                 CloudTrash, ThumbnailDecryptScheduler, …)
│   ├── preferences/  DataStore
│   ├── updater/      APK download + install
│   ├── hidden/       Hidden-vault storage
│   ├── offline/      Offline pinned-photo blobs
│   ├── transfer/     Live transfer state + history log
│   └── upload/       Upload-side photo + video compression, mirror + album-add helpers
├── di/               Hilt modules (Core, Network, Database, Repository,
│                     WorkManager, Updater, Stub)
├── navigation/       Single NavGraph
├── util/             Cross-cutting helpers (Exif, NetworkObserver, ErrorMessageSanitizer,
│                     OfflineGeocoder for offline reverse-geocoding from a bundled GeoNames dataset)
├── worker/           WorkManager workers
├── service/          Background-sync service + boot receiver + screenshot quick-action overlay
├── widget/           Home-screen widget (4 modes incl. Cloud Photos from encrypted cache)
└── App.kt + MainActivity.kt

Tech stack

  • Kotlin · Jetpack Compose · Material 3
  • Hilt (DI) · Room (DB) · Coil (images) · OkHttp + Retrofit (network) · Media3/ExoPlayer (video) · osmdroid (map tiles)
  • Offline place names from the GeoNames cities15000 dataset (CC BY 4.0), bundled under app/src/main/assets
  • ProtonCore Android (GPL-3.0) + GoOpenPGP for auth, keys, crypto
  • Drive API reference: ProtonDriveApps/android-drive
  • Min SDK 26 (Android 8) · Target SDK 35

What's reused vs. what's built here

This project uses the published me.proton.core:* libraries as-is (no forks, no modifications). All Drive feature code is written from scratch against the publicly documented Drive API. Requests carry a client-specific app-version / User-Agent so Proton's servers see this independent client for what it is, rather than impersonating the official app.

ProtonCore modules consumed (all me.proton.core:* v36.6.0, GPL-3.0, via Maven Central; see gradle/libs.versions.toml for the exact list):

  • Auth & account: auth, account, account-manager, user, human-verification
  • Crypto & keys: crypto, crypto-android, key
  • Network: network, network-data
  • UI plumbing: presentation, presentation-compose, account-manager-presentation-compose, human-verification-presentation, payment-presentation
  • Features & misc: plan, feature-flag, data-room, util-*, country/push/challenge/notification helpers, *-dagger bind modules for Hilt
  • Recovery: account-recovery, user-recovery

Telemetry / observability modules are pulled in (transitively required by some dagger modules) but the app does not emit any telemetry events; there is no analytics call site anywhere in the codebase.

What this app implements on top (app/src/main/kotlin/eu/akoos/photos/data/repository/drive/):

File Responsibility
PhotoUploadService Per-file streaming upload pipeline: 4 MB blocks → encrypt + sign → CDN PUT in parallel (bounded). Manifest = sorted block-hash concat + detached signature. Block spill files in cacheDir.
PhotoDownloadService Cloud → device. Stream + decrypt + apply DATE_TAKEN from captureTime + zone offset. Optional album subfolder.
PhotoStreamService Incremental cloud-state sync (mutation journal). Owns createOrGetPhotosVolume lazy bootstrap.
AlbumService Album CRUD: create / rename / set-cover, add/remove photos (batched). Mirrors selected device folders into matching Drive albums.
AlbumCryptoChain Single source of truth for album-share key selection: album NodeKey decrypt, photo parent-key resolution, and the share-context bundle that downstream code reuses.
AlbumSharingService Public link mint, email invite (PKESK encrypt to invitee + sign) with viewer / editor roles, member + invitation management (revoke / remove), leave or stop sharing, shared-with-me (primary + v2 backup endpoints), accept / decline, and server-side "Save to my library" copy. The multi-step share popup (email chips, role picker, optional message) is wired through this service.
PhotosShareService Per-user key cache (volumeId, shareId, rootLinkId, rootLinkKeyBytes, rootNodeHashKey) + shared API semaphore. Wiped on sign-out.
PhotosVolumeBootstrap First-run Photos volume + share + root-link creation. Handles ALREADY_EXISTS race via getVolumes() fallback.
CloudTrashService Cloud trash listing, restore (moveTrashLinks), permanent delete; surfaced as the in-app Trash screen.
ThumbnailDecryptScheduler On-demand + background thumbnail decryption: resolves visible cells first (priority-ordered with prefetch), warms the rest of the library in the background, and keeps the decrypted-thumbnail cache within a size budget by evicting the least-recently-used.
RecentUploadsTracker Short-TTL (90 s) recently-uploaded-linkIds map; stops stale rows from blocking cloud-delete propagation.
LinkDetailHelpers, ThumbnailHelpers, PhotoEntityBuilder Shared helpers (batch link metadata, JPEG thumbnail ≤512 px, DTO → Room entity mapping).

Plus the layers around them:

  • data/api/: Retrofit interface + DTOs for /drive/photos/*, /drive/v2/*, block uploads, sharing endpoints.
  • data/crypto/DriveCryptoHelper.kt: thin wrapper over me.proton.core:crypto* (no custom crypto primitives; PGP delegated entirely to upstream).
  • data/db/: Room schema for the local mutation journal (PhotoListingEntity, SyncStateEntity) with exported schemas + migrations.
  • domain/usecase/: UploadPendingUseCase, ReconcileSyncStateUseCase, DownloadPhotosUseCase, FreeUpSpaceUseCase, DeletePhotoUseCase, GetGalleryItemsUseCase, CategorizeItem.
  • worker/: SyncWorker, AlbumDownloadWorker, FreeUpSpaceWorker, CachePruneWorker (WorkManager + foreground service notifications, battery-aware constraints).
  • presentation/: Jetpack Compose UI (gallery, viewer, editor, albums, settings, …) and ViewModels.

No private endpoints, no undocumented APIs, no obfuscation (-dontobfuscate in proguard-rules.pro so the released APK can be byte-compared to a fresh build from this source).

Disclaimer

Not affiliated with Proton AG. Proton, Proton Drive, and Proton Photos are trademarks of Proton AG. Built against the publicly documented Drive API with no proprietary code from Proton.

Author

Akoos: creator and maintainer.

License

GPL-3.0: matches the upstream me.proton.core:* libraries this app links against.

About

Unofficial open-source Proton Drive Photos client for Android. End-to-end encrypted backup, albums, hidden vault.

Topics

Resources

Stars

94 stars

Watchers

1 watching

Forks

Releases

Sponsor this project

Contributors

Languages