| Age | Commit message (Collapse) | Author | Files | Lines |
|
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
Every screen moves from XML layouts, Views and Fragments to Jetpack
Compose with Material 3, in one MainActivity with a NavHost: the list
with breadcrumbs, search, pull to sync and multi-select; the entry view
with OTP countdown; the editor with generator sheet and folder picker;
setup (welcome, git server and clone, SSH key, PGP keys); settings, git
config, commit log and proxy; and the autofill picker, publisher
warning and decryption prompts. LaunchActivity stays the only exported
activity. Fixes found on the way reach master with it, among them
symlink-safe deletes, the proxy user name, screen lock gated switches,
the Android 14 app lock and screen lock prompts, and the autofill
passphrase cache rule. The View-era dependencies and view binding are
gone; Material Components (Views) stays for the git dialogs.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
The detached HEAD error sent users to "Settings > Git utils", a screen
the Compose settings no longer have. Every locale now names Settings >
Repository > Local Git config & utilities, and the English quotes are
escaped so they show. The git configuration screen's title reuses that
translated row label instead of an untranslatable English string.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
AGENTS.md drops the compose branch exception, describes the Compose UI
and its activities, updates the emulator notes (serial pinning, prefixed
helpers, Compose nodes, which windows are FLAG_SECURE, waiting for user
storage after boot, FAB content-desc, text field clearing, run-as
quoting) and the last device runs, and records why Material Components
(Views) and fragment-ktx stay. The spec is marked complete with what was
removed and kept, and says the merge waits for the maintainer's phone
test; CONTRIBUTING.md no longer asks for view binding. The cleanup
plan's emulator pass gets the harness fixes found while running it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
The success dialog showed a literal conflicting-<branch>-... instead of
the branch BreakOutOfDetached pushed. It now reads the newest local
conflicting-<branch>-<millis> ref, which is the one just created.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
0.1.4 renamed the app in the English and Italian autofill and
biometric texts only; German, French, Galician, Korean, Polish,
Brazilian Portuguese, Russian and Chinese still said Password Store in
22 strings. 20 now name HardenedPass; the two German messages where it
meant the store itself say Repository, as the rest of that translation
does.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
The prompt was ConstraintLayout's last user: a vertical LinearLayout
holds the same field and checkbox, so the dependency goes. Ids, input
type and the remember box's default are unchanged.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
Nothing uses preference, recyclerview, recyclerview-selection,
fastscroll or modernandroidpreferences since the Compose screens
replaced the Views, nor view binding. fragment-ktx stays as the pin
that keeps androidx.fragment at 1.8.4. The preference widget override,
the multiselect attribute, the toolbar and bottom sheet styles and the
lint ignores for deleted layouts go. Lint checks unused ids again.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
AutofillDecryptActivity was its last subclass: it now sets FLAG_SECURE
and injects the settings, crypto repository and dispatchers itself. The
path and clipboard helpers move to CryptoExtensions.kt as top-level
functions (a test pins the path helpers), and the FILE_PATH extra key
moves, unchanged, to LaunchActivity. An invalid or short .gpg-id ID is
reported with a toast: the snackbar never showed in the autofill
decryption, which closes its window at once.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
The Compose screens use no Fragments or view binding, so
FragmentExtensions (with its four slide animators) and
FragmentViewBindingDelegate have no users, nor do resolveAttribute,
isPermissionGranted, launchActivity and the unused Modifier.conditional.
SearchableRepositoryViewModel keeps one navigateTo: the Compose list owns
the back stack, so the RecyclerView-era navigation stack, stableId,
isFiltered and the unused DirectoriesOnly mode go. PasswordItem loses
the parent overloads, @JvmStatic and the unread NAME extra, and the
leftover sentry.properties goes too.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
hasEnabledAutofillServices() still answers true right after
disableAutofillServices(), so the switch stayed on and the next tap
turned autofill off again instead of opening the instructions dialog.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
The picker decides which entry fills the requesting app, so it drops
touches under another app's overlay, as the publisher warning does.
The entry view now uses cachesPassphrase too, so the cache rule lives in
one place, and the publisher warning's app name gets its closing quote.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
The autofill activities cannot join MainActivity's graph, so they keep
opening the PGP key import through the setup intent; the spec and the
ponytail note no longer promise otherwise. The spec gains an Autofill
section.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
Autofill re-cached the passphrase it had just read from the cache, and
cached an empty one for keys without a passphrase. Like the entry view,
it now caches only what the user typed into the passphrase dialog.
An unreadable .gpg-id left the transparent window up with a snackbar no
one could see; the activity now finishes and fills nothing.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
AutofillDecryptActivity shows the no keys dialog and the entry view's
passphrase dialog in Compose over its transparent FLAG_SECURE window.
The flow starts once, on the first window focus, instead of on every
onStart: Android 14 evicts a screen lock prompt shown before the
activity is on top, and a restart showed a second passphrase dialog.
After a key import it restarts on the next focus. Dismissing the no
keys dialog now finishes instead of leaving an invisible window.
Rotation and resizing no longer recreate the activity; other
recreations cancel the fill.
The passphrase is now cached only after a successful screen lock
prompt, as the entry view does: a passphrase typed after a cancelled
or unavailable prompt was cached whenever the cache feature was on.
PasswordDialog, its layout, dimens.xml and requireKeysExist lose their
last user and go.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
The Re-enable button replaced Advanced information in place, so a quick
double tap re-enabled autofill for an app with a changed publisher
(upstream had the same flaw). The package and certificate details now
come first and push Re-enable below them. The title is marked as a
heading and the footer's ponytail note sits on the footer.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
- Run the first search during composition with the saved query and
strict switch, so a recreated picker no longer flashes the results
for the initial query under the restored text.
- Scroll the result list back to the top on every new result, as the
old picker did.
- Fall back to the file name when a row has neither identifier nor
account part, instead of an empty label. DirectoryStructure never
returns both null today, so no test can reach it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
AutofillPublisherChangedActivity draws the warning with the shared
theme. Keep Autofill disabled is now the primary button, and the
Possible phishing attempt title replaces the logo. Re-enabling still
takes two taps (Advanced information first) and still clears the
app's matches before returning the prepared fill response; every other
way out fills nothing. Touches that reach the warning through another
app's window are now dropped (filterTouchesWhenObscured on the decor
view), so an overlay cannot trick the user into re-enabling.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
AutofillFilterView draws the picker with the shared theme: origin
header, search field, entry rows in a grouped surface and the three
switches, over the same SearchableRepositoryViewModel search. Rows
name the origin identifier and show the account and folders below it.
The first tap wins, so a double tap no longer starts two decryptions.
PasswordViewHolder, the RecyclerView adapter, the two layouts, their
drawables, the multiselect color and their lint baseline entries go.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
autofillRows turns the picker's search results into list rows: the
origin identifier, the part that must match the site or app, becomes
the row's name, and the account part and the folders above it the
subtitle, as the old picker's bold title and subtitle showed them.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
LaunchActivity showed the screen lock prompt from onCreate, before the
activity was on top. Android 14's SystemUI evicts a prompt whose app is
not the top activity ("Evicting client due to top activity is not ..."),
so the prompt closed at once with ERROR_CANCELED and the app quit. This
is upstream issue 2802, which upstream worked around by disabling the
setting on Android 14. The prompt now opens on the first window focus.
LaunchActivity also used a translucent theme, so after unlocking the
home screen showed through until MainActivity drew. It now uses the
app's opaque theme.
Verified on an Android 14 tablet with a PIN-only screen lock: unlock
opens the app without a flash of the home screen, cancel closes it, and
turning the lock off asks for the PIN.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
- A host with an empty port was saved, but ProxySelector treats port -1
as no proxy, so git silently connected directly. The port is now
required while a host is set.
- Patterns.WEB_URL accepted "http://proxy.example.org:3128/" as a host,
which then broke every HTTPS operation. The host must be a numeric
address or a plain domain name, without scheme, port or path.
- setDefaultProxy copied the proxy password into the process-wide
http.proxyPassword system property. The Authenticator already supplies
the credentials, so the properties are only cleared now.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
The Compose settings, git config, commit log and proxy screens replace
SettingsActivity with its six preference providers, GitConfigActivity
with TextInputDialog, GitLogActivity with its adapter and GitLogModel,
and ProxySelectorActivity, with their layouts and manifest entries.
The dead OpenKeystore key ID row goes with its string, the Android 14
platform bug summary and normal_margin lose their last users, and the
setup intent loses the two starts only Settings used.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
The list and the welcome screen open the Compose settings; their rows
open the git server, SSH key, key generation, PGP keys, proxy and git
config screens within the graph. Deleting the repository returns to the
welcome screen.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
executeAfterAuthentication waited for another result whenever the
prompt reported Failure, on the assumption that Failure meant a failed
attempt with the prompt still open. Failure comes from error callbacks
(lockout, timeout, unable to process), which always close the prompt,
so the operation hung forever. Only Retry keeps the prompt open; every
other result now ends the wait, and Failure and CanceledBySystem fail
the operation with the generic screen lock error, like the other
non-success results.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
BiometricAuthenticator mapped onAuthenticationError(ERROR_UNABLE_TO_PROCESS)
to Retry, but an error callback always closes the prompt. The settings
screen keeps its one-prompt-at-a-time guard set on Retry, so the gated
switches stayed locked until the section was left. The error now maps to
Failure; Retry comes only from onAuthenticationFailed, while the prompt is
still open.
Once the device screen lock is removed, the biometric app lock could not
be turned off: the prompt returns HardwareUnavailableOrDisabled and nothing
was written. Turning it off without a usable screen lock now writes the
value directly, as the passphrase cache row already does.
The settings category rows and PreferenceAction rows get Role.Button for
accessibility services.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
Author name and email, HEAD status, commit log, abort rebase, hard
reset and gc. The hard reset now warns and prefills the current branch;
a finished reset or gc stays on the screen with a snackbar, and a
failed operation keeps the screen too. The commit log is read off the
main thread. BreakOutOfDetached's not-rebasing dialog no longer
finishes the calling activity.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
Host, port, user name and password. An invalid host or port blocks the
save, an emptied port is cleared, the proxy applies at once, and the
screen is FLAG_SECURE while the saved password is on it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
The categories and one screen per category, with the old rows over the
same keys. Screen lock gated switches store their value only after a
successful prompt. Clearing a saved HTTPS password or SSH passphrase,
which never removed anything, now asks and removes it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
Switch, single choice, text and action rows over the existing
preference keys, and a ViewModel that reads every displayed value,
follows preference changes, deletes the store without following
symlinks, and clears saved credentials and the passphrase cache.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
The Compose list shows folders and entries in separate groups, so the
files first and type independent sort orders did nothing visible. They
are removed; a stored one migrates to folders first, and any unknown
value reads as folders first instead of throwing. The OpenKeychain SSH
key ID, which nothing reads any more, is removed once.
GitSettings wrote the proxy user name to the proxy preferences but read
it from the main settings, so ProxyUtils never authenticated.
Adds the pure parts of the settings port: proxy port parsing, HEAD
status and the commit log.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
- A store a cancelled attempt already made for the chosen key (its
.gpg-id names exactly that key) no longer counts as occupied:
choosing the key again commits and moves on instead of refusing.
- SSH key import reads at most 100 kB whatever the provider reports,
like the PGP import, instead of reading the stream whole.
- PGPKeyManager replaces a key through a temporary file moved over it
atomically, so a failed write keeps the old key; listings only read
.key files, so a stray temporary file is never taken for a key.
- "View generated public SSH key" goes through the settings result
launcher, so the preference visibility refreshes afterwards.
- A plain start from LaunchActivity replaces the task: an open
MainActivity (e.g. one whose store vanished) is no longer left
below the new one, and one back leaves the app.
- The PGP keys screen's import FAB carries its content description
itself; M3 cleared the icon's.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
The Compose welcome, git server, SSH key, key generation and PGP keys
screens replace OnboardingActivity with its fragments,
GitServerConfigActivity with BasicBottomSheet, SshKeyImportActivity,
SshKeyGenActivity, ShowSshKeyFragment, PGPKeyImportActivity and
PGPKeyListActivity with KeyList and PGPKeyListViewModel, their layouts
and manifest entries, and ui:compose's APSAppBar. Resources only they
used are removed too.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
- createStore refuses a non-empty store folder (only .git allowed), so a
restored, cloned store keeps its .gpg-id
- failed store creation cleans up with deleteWithoutFollowingLinks, which
also removes a fresh .git
- PGP keys screen: Later goes through the guarded leave path
- Import SSH key row launches via the result launcher so the public key
row refreshes
- GitServerViewModel.changeUrl always refits the auth mode, also for an
empty URL
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
The welcome screen starts the app without a store, the list sends a
vanished store back to it, and its clone prompt opens the git server
screen. A clone asks for the store's PGP key when none matches it.
Settings, the entry view, autofill decryption and git operations open
the setup screens through MainActivity.setupIntent. The No SSH key
dialog's Cancel and the unusable screen lock prompt no longer finish
the calling activity, which is now the app's main screen.
KeysMode is a route argument, so it is kept from minification (lint
MissingKeepAnnotation).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
One screen for managing keys, choosing the key of a new local store
and importing the key a store needs. Rows show user ID and key ID;
the import keeps the old success, replace and failure dialogs.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
The SSH key screen shows the key's state and, for a generated key, the
public key with copy and share, and imports a key file after the old
replace confirmation. The generation screen ports the old one; the
key is generated in the ViewModel after the screen lock prompt.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
The welcome screen merges the old welcome and repository type screens.
The git server screen serves clone and edit: the old URL checks as
dialogs, authentication mode buttons, the SSH key's state with Manage,
the host key reset, and a clone that keeps the screen on failure.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
- PGP key import reads at most 1 MiB (bounded read, buffer zeroed) and
refuses larger files with a new message.
- SshKeyViewModel catches any Exception from import and generation
(CancellationException rethrown) instead of a short list, so an
unexpected provider or sshj error no longer crashes the app.
- Pending PGP key bytes are zeroed in a finally block, also on
cancellation during addKey after a replace, and an earlier pending
pick is zeroed before a new one takes its place.
- PGPKeyManager never replaces a stored secret key with a public-only
key of the same ID, with or without replace (KeyDowngradeException);
the setup ViewModel shows a localized message for it. Unit test added.
- storeRecipients strips '#' comments and a trailing '!' as pass reads
.gpg-id; tests added.
- GitServerViewModel.clearStore returns the failure when the store
folder cannot be emptied, so the clone does not start; the plan's
Task 3 call site reports it.
- readGpgId opens the file with NOFOLLOW_LINKS and reads at most
64 KiB + 1 bytes, refusing more.
- SshKey.import writes the new private key to a temp file and moves it
over atomically before dropping the old key's Keystore entry, public
key and passphrase, so an I/O error keeps the old key.
- KeyUtils.isSecretKey notes (ponytail) that GnuPG stub and smartcard
keyrings count as secret.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
Git server (URL, mode, clone state, the after-clone key check), SSH
key (import and generation off the main thread, outcome kept across
recreation) and PGP keys (rows with key IDs, import with the replace
question, delete by key ID, creation of a local store).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
URL checks and fixes, authentication modes per URL, the clone target,
the store's recipients, a size- and symlink-checked .gpg-id read and
the .gpg-id text for a new store (uppercase long key IDs), as pure
functions for the Compose setup screens. The SSH key import now also
requires a format sshj recognizes (an OpenPGP private key block
matched the old marker check and was imported as the SSH key) and no
longer crashes on an empty size cursor. KeyUtils tells secret keys
apart and no longer throws on a key without user ID.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
Recent Conscrypt (Android 14 mainline updates) registers a KeyFactory
for Ed25519. With sshj leaving provider selection to the JCA, its
Ed25519KeyFactory now gets Conscrypt's key class, whose algorithm name
sshj does not map to ssh-ed25519: KeyType.fromKey returns UNKNOWN and
every Ed25519 host key fails with "Don't know how to encode key", so
clone and sync over SSH break. 0.1.4 is affected since the sshj 0.41.2
update.
Register a provider at position 1 that serves only KeyFactory.Ed25519
from Bouncy Castle (kept by the existing R8 rule). Everything else,
including signatures with Android Keystore keys, still goes through
normal JCA provider selection.
Verified on an Android 14 tablet: clone over SSH failed before and
works with the fix.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit ad3c63f665e6264ed1db42d6ae2dfc0947e33393)
|
|
Recent Conscrypt (Android 14 mainline updates) registers a KeyFactory
for Ed25519. With sshj leaving provider selection to the JCA, its
Ed25519KeyFactory now gets Conscrypt's key class, whose algorithm name
sshj does not map to ssh-ed25519: KeyType.fromKey returns UNKNOWN and
every Ed25519 host key fails with "Don't know how to encode key", so
clone and sync over SSH break. 0.1.4 is affected since the sshj 0.41.2
update.
Register a provider at position 1 that serves only KeyFactory.Ed25519
from Bouncy Castle (kept by the existing R8 rule). Everything else,
including signatures with Android Keystore keys, still goes through
normal JCA provider selection.
Verified on an Android 14 tablet: clone over SSH failed before and
works with the fix.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|