Skip to content

Commit 70aa86a

Browse files
committed
Improve UX of manual cert install on Android 11+
This does a few small things: * Improves list styling to make the text clearer * Bolds the key button names you need * Updates the text to exactly match for Android 12 & 13 * Explicitly says where the cert is stored, in case users's devices don't show it in 'recent' or go to downloads by default (not sure why, but appears this can happen) * Reshow the prompt if you don't complete it * Disable accidental dismissal but add an explicit 'cancel' button
1 parent 1787c28 commit 70aa86a

File tree

1 file changed

+41
-9
lines changed

1 file changed

+41
-9
lines changed

app/src/main/java/tech/httptoolkit/android/MainActivity.kt

Lines changed: 41 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import android.provider.Settings
1414
import android.security.KeyChain
1515
import android.security.KeyChain.EXTRA_CERTIFICATE
1616
import android.security.KeyChain.EXTRA_NAME
17+
import android.text.Html
1718
import android.util.Log
1819
import android.view.View
1920
import android.widget.Button
@@ -552,6 +553,16 @@ class MainActivity : AppCompatActivity(), CoroutineScope by MainScope() {
552553
// Then go back to the disconnected state:
553554
mainState = MainState.DISCONNECTED
554555
updateUi()
556+
} else if (
557+
requestCode == INSTALL_CERT_REQUEST &&
558+
Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q // Required for promptToManuallyInstallCert
559+
) {
560+
// Certificate install failed. Could be manual (failed to follow instructions) or automated
561+
// via prompt. We redo the manual step regardless: either (on modern Android) manual is
562+
// required so this is just reshowing the instructions, or it was automated but that's not
563+
// working for some reason, in which case manual setup is a best-effort fallback.
564+
app.trackEvent("Setup", "cert-install-failed")
565+
launch { promptToManuallyInstallCert(currentProxyConfig!!.certificate) }
555566
} else {
556567
Sentry.capture("Non-OK result $resultCode for requestCode $requestCode")
557568
mainState = MainState.FAILED
@@ -679,19 +690,40 @@ class MainActivity : AppCompatActivity(), CoroutineScope by MainScope() {
679690
.setTitle("Manual setup required")
680691
.setIcon(R.drawable.ic_exclamation_triangle)
681692
.setMessage(
693+
Html.fromHtml(
682694
"""
683-
Android ${Build.VERSION.RELEASE} doesn't allow automatic certificate setup.
684-
685-
To allow HTTP Toolkit to intercept HTTPS traffic:
686-
687-
- Open "Encryption & Credentials" in your security settings
688-
- Select "Install a certificate", and then "CA Certificate"
689-
- Select the HTTP Toolkit certificate
690-
""".trimIndent()
695+
<p>
696+
Android ${Build.VERSION.RELEASE} doesn't allow automatic certificate setup.
697+
</p>
698+
<p>
699+
To allow HTTP Toolkit to intercept HTTPS traffic:
700+
</p>
701+
<ul>
702+
${if (Build.VERSION.SDK_INT > Build.VERSION_CODES.R) // Android 12+
703+
"""
704+
<li>&nbsp; Open "<b>${
705+
// Slightly different UI for Android 12 and 13:
706+
if (Build.VERSION.SDK_INT == 31) "Advanced Settings" else "More security settings"
707+
}</b>" in your security settings</li>
708+
<li>&nbsp; Open "<b>Encryption & Credentials</b>"</li>
709+
"""
710+
else
711+
"""
712+
<li>&nbsp; Open "<b>Encryption & Credentials</b>" in your security settings</li>
713+
"""
714+
}
715+
<li>&nbsp; Select "<b>Install a certificate</b>", then "<b>CA Certificate</b>"</li>
716+
<li>&nbsp; <b>Select the HTTP Toolkit certificate in your Downloads folder</b></li>
717+
</ul>
718+
""",0)
691719
)
692-
.setPositiveButton("Open security settings now") { _, _ ->
720+
.setPositiveButton("Open security settings") { _, _ ->
693721
startActivityForResult(Intent(Settings.ACTION_SECURITY_SETTINGS), INSTALL_CERT_REQUEST)
694722
}
723+
.setNegativeButton("Cancel") { _, _ ->
724+
disconnect()
725+
}
726+
.setCancelable(false)
695727
.show()
696728
}
697729
}

0 commit comments

Comments
 (0)