@@ -236,6 +236,7 @@ class MainActivity : AppCompatActivity(), CoroutineScope by MainScope() {
236236
237237 buttonContainer.visibility = View .VISIBLE
238238 buttonContainer.addView(primaryButton(R .string.disconnect_button, ::disconnect))
239+ buttonContainer.addView(secondaryButton(R .string.test_button, ::testInterception))
239240 }
240241 MainState .DISCONNECTING -> {
241242 statusText.setText(R .string.disconnecting_status)
@@ -394,6 +395,21 @@ class MainActivity : AppCompatActivity(), CoroutineScope by MainScope() {
394395 startActivity(browserIntent)
395396 }
396397
398+ private fun testInterception () {
399+ app.trackEvent(" Button" , " test-interception" )
400+
401+ val browserIntent = Intent (Intent .ACTION_VIEW ,
402+ Uri .parse(" https://amiusing.httptoolkit.tech" )
403+ )
404+
405+ // If there's a supported browser available, make sure we use it. This prioritises
406+ // the default browser, and only returns null if no known supported browser is installed.
407+ val testBrowserPackage = getTestBrowserPackage(this )
408+ if (testBrowserPackage != null ) browserIntent.setPackage(testBrowserPackage)
409+
410+ startActivity(browserIntent)
411+ }
412+
397413 override fun onActivityResult (requestCode : Int , resultCode : Int , data : Intent ? ) {
398414 super .onActivityResult(requestCode, resultCode, data)
399415
@@ -505,9 +521,42 @@ class MainActivity : AppCompatActivity(), CoroutineScope by MainScope() {
505521 }
506522}
507523
508- private fun isStoreAvailable (context : Context ): Boolean = try {
509- context.packageManager.getPackageInfo(GooglePlayServicesUtil . GOOGLE_PLAY_STORE_PACKAGE , 0 )
524+ private fun isPackageAvailable (context : Context , packageName : String ) = try {
525+ context.packageManager.getPackageInfo(packageName , 0 )
510526 true
511527} catch (e: PackageManager .NameNotFoundException ) {
512528 false
513- }
529+ }
530+
531+ private fun getDefaultBrowserPackage (context : Context ): String {
532+ val browserIntent = Intent (" android.intent.action.VIEW" , Uri .parse(" http://example.com" ))
533+ val resolveInfo = context.packageManager.resolveActivity(browserIntent, PackageManager .MATCH_DEFAULT_ONLY )
534+ return resolveInfo.activityInfo.packageName
535+ }
536+
537+ private fun getTestBrowserPackage (context : Context ): String? {
538+ // A list of browsers that trust the user store by default, and so
539+ // will work OOTB even if only the user cert is trusted.
540+ val supportedBrowsers = listOf (
541+ " com.android.chrome" , // Modern Android
542+ " com.android.browser" , // <= Android 2.3
543+ " com.google.android.browser" , // > 2.3, < 4.0.2
544+ " com.brave.browser" , // Brave
545+ " com.microsoft.emmx" // Edge
546+ // FF, Opera & others don't trust user CAs by default, so we avoid them for testing
547+ )
548+
549+ // If the default browser is supported, just use that, easy
550+ val defaultBrowser = getDefaultBrowserPackage(context)
551+ Log .i(" tech.httptoolkit" , " Default browser is $defaultBrowser " )
552+ if (supportedBrowsers.contains(defaultBrowser)) {
553+ return defaultBrowser
554+ }
555+
556+ return supportedBrowsers.firstOrNull { packageName ->
557+ isPackageAvailable(context, packageName)
558+ }
559+ }
560+
561+ private fun isStoreAvailable (context : Context ): Boolean =
562+ isPackageAvailable(context, GooglePlayServicesUtil .GOOGLE_PLAY_STORE_PACKAGE )
0 commit comments