Why won't CustomTabsClient.bindCustomTabsService work?

Viewed 1870

I've recently been working on adding some Chrome Custom Tabs to my Android app and for some reason, I can't bind to the CustomTabsService.

  • I have an updated version of Chrome
  • Android Gradle Plugin Version 4.0.0
  • Gradle Version 6.1.1

I've added Log calls to see if my connection was successful but it never is. I'm trying to take action based on the user's navigation within the CustomTabs by using the CustomTabsCallback but I need to connect to the service first. Any help is appreciated!

Dependencies

// Custom Tabs
    implementation 'androidx.browser:browser:1.3.0-alpha01'
    implementation 'com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava'

I've also tried with this (current versions are here):

implementation 'androidx.browser:browser:1.2.0'

CustomTabsFragment

package com.mullr.rabbithole.ui.dig

import android.app.Service
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.graphics.Color
import android.net.Uri
import android.os.Bundle
import android.util.Log
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.content.res.AppCompatResources
import androidx.browser.customtabs.*
import androidx.core.content.ContextCompat
import androidx.core.graphics.drawable.DrawableCompat
import androidx.core.graphics.drawable.toBitmap
import com.mullr.rabbithole.R
import com.mullr.rabbithole.main.MainActivity
import kotlinx.android.synthetic.main.dig_fragment.*

private lateinit var main: MainActivity

class DigTabs : Fragment() {

    // Custom Tabs
    lateinit var client: CustomTabsClient
    lateinit var session: CustomTabsSession
    lateinit var serviceConnection: CustomTabsServiceConnection
    var builder = CustomTabsIntent.Builder()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        main = requireActivity() as MainActivity

       serviceConnection = object : CustomTabsServiceConnection() {
            override fun onCustomTabsServiceConnected(name: ComponentName, mClient: CustomTabsClient) {
                Log.d("Service", "Connected")
                client = mClient
                client.warmup(0L)
                val callback = RabbitCallback()
                session = mClient.newSession(callback)!!
                builder = CustomTabsIntent.Builder(session);
            }

            override fun onServiceDisconnected(name: ComponentName?) {

            }
        }

        Log.d("start", "attempt")
        // Connect to service
        var ok =  CustomTabsClient.bindCustomTabsService(main, main.packageName, serviceConnection)
        if (ok) {
            Log.d("start", "connected")
        } else {
            Log.d("start", ok.toString())
        }
    }

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.fragment_dig_tabs, container, false)
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        wiki_button.setOnClickListener {
            var url = "https://www.wikipedia.org/"
            loadCustomTab(url)
        }

        google_button.setOnClickListener {
            var url = "https://www.google.com"
            loadCustomTab(url)
        }
    }

    fun loadCustomTab(url: String) {
        builder.setSecondaryToolbarColor(ContextCompat.getColor(main, R.color.colorAccent))
        builder.setToolbarColor(
            ContextCompat.getColor(
                main,
                R.color.black
            )
        ) // Change tab toolbar color
        builder.setShowTitle(true)
        builder.addDefaultShareMenuItem()
        builder.enableUrlBarHiding()
        AppCompatResources.getDrawable(main, R.drawable.close_icon)?.let {
            DrawableCompat.setTint(it, Color.WHITE)
            builder.setCloseButtonIcon(it.toBitmap())
        }
        val customTabsIntent: CustomTabsIntent = builder.build()
        customTabsIntent.launchUrl(main, Uri.parse(url))
    }

    override fun onDestroy() {
        super.onDestroy()
        main.unbindService(serviceConnection)
    }

    class RabbitCallback : CustomTabsCallback() {
        override fun onNavigationEvent(navigationEvent: Int, extras: Bundle?) {
            super.onNavigationEvent(navigationEvent, extras)
            Log.d("Nav", "test")
            when (navigationEvent) {
                1 -> Log.d("Navigation", "Start")
                NAVIGATION_FINISHED -> Log.d("Navigation", "Finished")
                NAVIGATION_FAILED -> Log.d("Navigation", "Failed")
                NAVIGATION_ABORTED -> Log.d("Navigation", "Aborted")
                TAB_SHOWN -> Log.d("Navigation", "Tab Shown")
                TAB_HIDDEN -> Log.d("Navigation", "Tab Hidden")
                else -> Log.d("Navigation", "Else")
            }
        }
    }
}
2 Answers

This turned out to be a silly mistake. The CustomTabsClient.bindCustomTabsService method needs the package name of the Custom Tabs service and not the package of the calling activity. So this...

var ok =  CustomTabsClient.bindCustomTabsService(main, main.packageName, serviceConnection)

Should be...

var ok =  CustomTabsClient.bindCustomTabsService(main, "com.android.chrome", serviceConnection)

Possible package names can be found here. They include:

  • com.android.chrome
  • com.chrome.beta
  • com.chrome.dev

Android 11 has introduced package visibility changes. If your Android app is targeting API level 30 or above, adding a queries section to AndroidManifest.xml is needed, otherwise the code snippet above won't return results:

<queries>
    <intent>
        <action android:name=
            "android.support.customtabs.action.CustomTabsService" />
    </intent>
</queries>
Related