Adds MailProvider.AOL as a guided app-password provider (after iCloud, before
Other), closing the coupled pair of #156 (app-password help content) and #154
(IMAP/SMTP presets):
- appPasswordHelpUrl points at AOL's "Create and manage 3rd-party app
passwords" article. No twoFactorHelpUrl: verified against both AOL's
app-password article and its separate two-step-verification article that
neither treats 2FA as a prerequisite for generating an app password (AOL
mirrors Yahoo here, not Gmail/iCloud).
- IMAP imap.aol.com:993 (SSL/TLS); SMTP smtp.aol.com:465 (SSL/TLS) — AOL's
official docs and the Thunderbird ISPDB autoconfig only document implicit
TLS on 465 for submission, with no STARTTLS/587 alternative, so this
follows Yahoo's rationale rather than Gmail/iCloud's STARTTLS preset.
AppPasswordSetupScreen's two exhaustive `when` blocks (providerIntro,
twoFactorHelpLabel) gain an AOL branch; AccountPickerScreen already lists
providers generically via MailProvider.entries. Test coverage mirrors the
Yahoo/iCloud assertions: presets, help URLs, no twoFactorHelpUrl, fromKey,
forImapHost, and brandFor resolving a manually-configured imap.aol.com
account to the AOL brand.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Yahoo's guided app-password setup pointed appPasswordHelpUrl at the
generic account-security sign-in page, which assumes the user already
knows to hunt for "Create app password" once there. Point it instead at
Yahoo's own step-by-step "Generate and manage 3rd-party app passwords"
article, mirroring what #153 did for iCloud.
Yahoo does NOT gate app-password creation behind two-step verification
(verified against Yahoo's live help docs, which never list it as a
prerequisite), so — unlike Gmail and iCloud — it keeps twoFactorHelpUrl
null and shows no 2FA button. AppPasswordSetupScreen already renders the
help buttons generically from these provider fields, so no screen change
is needed; the existing PR #152 ordering (2FA link before the
app-password link) is preserved for the providers that have both.
Add a MailProviderTest assertion pinning Yahoo's new app-password URL
(mirroring the iCloud test) and extend the onboarding
yahooSetup_hasNoTwoFactorHelpLink coverage note for #155.
Closes#155
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The rich-text engine already fully supported RichStyle.Strikethrough
(HTML serialization, parsing, and rendering); only the toolbar control
was missing. Adds an "S" FormatButton next to Bold/Italic/Underline,
wired the same way (onToggleStyle + isStyled toggle state), plus the
format_strikethrough string resource for its onClickLabel.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Adds friction to the "Report a Problem" form: a required email field (basic
local-part@domain.tld validation), a required consent notice about being
contacted at that address, and a 200-character minimum on the comment field
with a live "x/200" counter that turns red (with the field outline) until the
threshold is met. Submit stays disabled until both the comment and email are
valid, mirroring and extending the existing SUBMITTING gate. The email rides
along on DebugReport (userEmail) so it round-trips through the storage JSON
and the exact payload that's previewed, copied, saved, and POSTed. The new
ViewModel-level guard on submit() also fully integrates with the #161
success-confirmation dialog: invalid attempts never reach SUBMITTING/SUCCEEDED,
so the dialog flow is unaffected.
Closes#159
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
MainActivity.onCreate only parsed the incoming intent (pendingCompose /
pendingOpenMessageId) when savedInstanceState == null, on the assumption
that a non-null value always means a config-change recreation (e.g.
rotation), where Android redelivers the same, already-handled intent and
re-parsing would just navigate to a duplicate destination.
But Android also passes a restored, non-null savedInstanceState when it
recreates the activity after the process was killed in the background and
is then relaunched by tapping a notification. There, intent is the new
tap, not a replay, but the guard swallowed it exactly like a rotation, so
pendingOpenMessageId was never set and the tap silently landed wherever
the restored back stack was (typically the mailbox) instead of the
message. That's the #157 regression from the original fix in a6ec00d
(#56). pendingCompose (mailto:/share intents) went through the identical
guard and had the same latent bug.
Replace the savedInstanceState check with IntentHandledMarker, which
marks the Intent instance itself once parsed. A config-change recreation
redelivers that same marked instance, so it's correctly skipped; a
genuinely new intent -- warm via onNewIntent or cold via onCreate after a
process-death relaunch -- is never marked yet, so it's always parsed.
This dedupes on the intent's own identity instead of an unreliable proxy
for it, so it can't confuse the two recreation paths.
Adds IntentHandledMarkerTest covering the marking contract: unhandled on
first look, recognized as handled on a redelivered instance, and still
unhandled on a freshly constructed (but content-equal) instance -- the
process-death case.
Closes#157
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Replace the small inline "Report sent. Thank you!" text with an AlertDialog
carrying the fuller thank-you/no-guarantee message, and gate the screen's
auto-navigate-on-delete LaunchedEffect so it no longer fires while a submit
is in flight or has just succeeded — the dialog's acknowledgement is what
calls onDone() for that path instead. This closes the race where
ReportUploadWorker deletes the report row (and thus flips state.exists to
false) moments after SubmitUiState.SUCCEEDED, which could previously
navigate the user away before the confirmation was ever visible. Discard
and the other non-success paths (FAILED/UNAVAILABLE) are unaffected and
still auto-navigate immediately.
Closes#161
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>