We have an Android application opening Chrome Custom Tabs to perform user authentication. (A stripped version of the fragment code is listed below.)
When the mobile network has a proxy address:port configured, the CCT browser displays a popup to gather credentials. This has 3 problems:
- The browser fails to authenticate and crashes
- The popup is crude and doesn't match our app's dialog style
- The app usually has the credentials already so no dialog is necessary--a method should allow them to be inserted without user input
Our old implementation used a WebViewClient which had the onReceivedHttpAuthRequest()
callback. This gave control back to the app to present it's own dialog or supply the credentials directly.
Is there no way to do same with CCTs?
open class CustomTabsFragment {
...
private var cctServiceConnection: CustomTabsServiceConnection? = null
private var cctClient: CustomTabsClient? = null
private var cctSession: CustomTabsSession? = null
private var cctBuilder = CustomTabsIntent.Builder()
private lateinit var chromePackageName: String
lateinit var url: String
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
...
// get Bundle arguments
url = requiredArguments().getString("url","")
chromePackageName = requiredArguments().getString("package","")
...
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val uri = Uri.parse(url)
cctServiceConnection = object : CustomTabsServiceConnection() {
override fun onCustomTabsServiceConnected(name: ComponentName, mClient: CustomTabsClient) {
cctClient = mClient
cctClient?.let {
it.warmup(0L)
cctSession = it.newSession(chromeCustomTabsCallback)
}
cctSession?.let {
it.mayLaunchUrl(uri, null, null)
cctBuilder.setSession(it)
val customTabsIntent = cctBuilder
.setShowTitle(true)
.setDefaultShareMenuItemEnabled(false)
.setUrlBarHidingEnabled(true)
.setStartAnimations(requireActivity(), android.R.anim.fade_in, android.R.anim.fade_out)
.setExitAnimations(requireActivity(), android.R.anim.fade_in, android.R.anim.fade_out)
.build()
customTabsIntent.intent.data = uri
startActivityForResult(customTabsIntent.intent, 1000)
}
}
override fun onServiceDisconnected(name: ComponentName?) {
cctClient = null
cctSession = null
}
}
// Binds to the service.
cctServiceConnection?.let {
CustomTabsClient.bindCustomTabsService(requireContext(), chromePackageName, it)
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
val loginActivity = activity as? LoginActivity
loginActivity?.let {
if (requestCode == 1000 && !it.isAuthenticated) {
// Handle close or back pressed when opening web page via chrome custom tabs
findNavController().navigate(R.id.destinationFragment)
}
}
super.onActivityResult(requestCode, resultCode, data)
}
private val chromeCustomTabsCallback = object : CustomTabsCallback() {
override fun onNavigationEvent(navigationEvent: Int, extras: Bundle?) {
super.onNavigationEvent(navigationEvent, extras)
when (navigationEvent) {
NAVIGATION_STARTED -> Logger("Chrome Custom Tabs Navigation started - the tab has started loading a page.")
NAVIGATION_FINISHED -> Logger("Chrome Custom Tabs Navigation finished - the tab has finished loading a page.")
NAVIGATION_FAILED -> Logger("Chrome Custom Tabs Navigation failed - the tab couldn't finish loading due to a failure.")
NAVIGATION_ABORTED -> Logger("Chrome Custom Tabs Navigation aborted - loading was aborted by a user action before it finishes like clicking on a link or refreshing the page.")
TAB_SHOWN -> Logger("Chrome Custom Tabs Navigation tab shown")
TAB_HIDDEN -> Logger("Chrome Custom Tabs Navigation tab hidden")
else -> Logger("Chrome Custom Tabs Navigation Unknown")
}
}
}
...
}