Add GET_CONTENT picker support, fix account/login bugs, restyle selection UI
Build APK / build (push) Successful in 7m40s
Build APK / build (push) Successful in 7m40s
- Let Noo be picked as a source (Files + Photos) by other apps' native file/photo pickers via Android's GET_CONTENT intent, mirroring the existing "Share to Noo" plumbing: MainActivity.kt/PickIntentService/ PickRequest, ServerProvider.pickRequest/confirmPick/cancelPick, and pick-mode tap handling in FilesView/PhotosView. While picking, the bottom nav and MoreTabsButton are restricted to Files/Photos only. - Fix two account bugs: testConnection() failures no longer delete a still-valid stored app password (only an actual 401 does), and switchAccount() now retries a nominally-active-but-logged-out account instead of no-op'ing - both were silently locking users out of "Continue as" with no way back in except removing and re-adding the account (now exposed via login_view.dart's inline remove button). - Fix the login WebView: clear cookies before every load so "Add Account" can't silently reuse an existing session, and push it as a non-animated route to avoid a known WebView-as-PlatformView black-screen- on-pop issue under Impeller. - Redesign the Files/Photos selection toolbar to take over the top app bar entirely (SyncedHeaderScaffold.selectionBar) instead of sitting as a second bar under the controls row, with a horizontally-scrollable, uniformly-colored actions row and the same one-shot scroll-hint nudge the controls row uses. - Redesign ShareUploadView to share the same SyncedHeaderScaffold chrome as every tab, with the uploading-file summary (marqueed via the new shared MarqueeTitle widget) and upload button grouped into one rounded/elevated bottom sheet. - Restyle ShareSheet to open on the same DetailsHeader every other per-item sheet uses, and fix a couple of small design-language drifts (hardcoded text style, a non-rounded icon). - Settings' account card: icon buttons with tooltips instead of text buttons for refresh/logout/remove, divider removed.
This commit is contained in:
@@ -26,6 +26,15 @@ via [`LoginFlowService`](../../lib/services/login_flow_service.dart):
|
||||
Chrome-autofill (Android's own system Autofill framework, e.g. a
|
||||
password manager, may still work in the WebView; Chrome's own
|
||||
saved-password autofill specifically cannot, since that's Chrome-only).
|
||||
`LoginWebViewView` also clears cookies (`WebViewCookieManager().
|
||||
clearCookies()`) before every load, not just once - Android's WebView
|
||||
`CookieManager` is a single store shared/persisted across every WebView
|
||||
instance in the app process, not scoped per-controller, so without this
|
||||
a second/"Add Account" login silently reuses whichever account's
|
||||
Nextcloud session cookie is already there instead of prompting for
|
||||
credentials (the same class of bug the Custom-Tab-reuse issue above
|
||||
was, just recurring one layer down once everything moved to the owned
|
||||
WebView).
|
||||
3. `ServerProvider` polls `LoginFlowService.poll(pollEndpoint, token)` every
|
||||
2 seconds (`Timer.periodic`, see `_pollTimer`/`_pollTimeoutTimer` in
|
||||
`server_provider.dart`) until it gets a 200 with `server`/`loginName`/
|
||||
@@ -119,6 +128,49 @@ rather than needing a rewrite for multi-account support.
|
||||
`AtomicBoolean` the copy/upload loops poll) - there's no plumbing back to
|
||||
the Dart UI, by design, since the app may not even be running.
|
||||
|
||||
## Being picked by other apps (photo/file picker)
|
||||
|
||||
Noo can also be launched *by* another app as a `GET_CONTENT` picker (e.g.
|
||||
Google Drive/Instagram's "choose a file" flow), the reverse direction of
|
||||
"Share to Noo" above - hand-rolled the same way, not a plugin.
|
||||
|
||||
- `MainActivity.kt` matches `ACTION_GET_CONTENT` (`OPENABLE`, any
|
||||
mimeType - a single `*/*` filter, since Android matches it against
|
||||
whatever the caller actually requested) alongside its existing
|
||||
`ACTION_SEND`/`SEND_MULTIPLE` filters, and exposes the caller's requested
|
||||
mimeType/multi-select flag/app label via the
|
||||
`dev.ayushya.noo/pick_intent` method+event channel pair
|
||||
([`PickIntentService`](../../lib/services/pick_intent_service.dart)/
|
||||
[`PickRequest`](../../lib/models/pick_request.dart)) - same
|
||||
cold-start-vs-already-running split as the share-intent channels.
|
||||
- `ServerProvider.pickRequest`/`isPicking` drive picking mode app-wide once
|
||||
`MainShellView` learns about a request at startup or via
|
||||
`onNewPickRequest`. While picking, `MainShellView` restricts the visible
|
||||
bottom-nav tabs to just Files and Photos (see `architecture.md`) -
|
||||
`FilesView`/`PhotosView` route taps through
|
||||
`itemMatchesPickFilter`/`confirmPick` instead of their normal
|
||||
open/select behavior (folders still navigate; a mime-mismatched file is
|
||||
rejected with a snackbar; matching files toggle-select or immediately
|
||||
confirm depending on `PickRequest.allowMultiple`).
|
||||
- `ServerProvider.confirmPick` downloads the selected item(s) to a
|
||||
`picker/` scratch subfolder in the app's cache dir (`downloadToFile`,
|
||||
same as any other download) - each item into its own `picker/<item.id>/`
|
||||
subfolder, keeping the on-disk filename as plain `item.name` rather than
|
||||
prefixing it with the id to dodge collisions between same-named items;
|
||||
the caller reads that on-disk name back as the display name, so
|
||||
prefixing it there was a real bug (Drive showing e.g. `163332_photo.jpg`
|
||||
instead of `photo.jpg`) - then hands the local paths to
|
||||
`PickIntentService.finishPick`, which calls back into
|
||||
`MainActivity.kt.finishPick`: it wraps each file in a `content://` Uri
|
||||
via this app's own `FileProvider` (`${applicationId}.picker.fileprovider`,
|
||||
scoped to just that cache subfolder - see `android/app/src/main/res/xml/
|
||||
file_paths.xml`) and returns it to the caller via `setResult`. Single
|
||||
file uses `setDataAndType` (never `.data =`/`.type =` as two separate
|
||||
calls - each one silently nulls out the other field on a plain
|
||||
`Intent`); multiple files use `ClipData`. `cancelPick` mirrors this for
|
||||
backing out (system back while picking, or a picked-item mismatch) with
|
||||
`RESULT_CANCELED` instead.
|
||||
|
||||
## Multi-account storage & session persistence
|
||||
|
||||
[`AccountStore`](../../lib/services/account_store.dart) owns everything
|
||||
|
||||
Reference in New Issue
Block a user