Skip to content

Commit

Permalink
Merge pull request #13 from droibit/feature/renewal
Browse files Browse the repository at this point in the history
Improve integration with `CustomTabsIntent`
  • Loading branch information
droibit authored Sep 13, 2023
2 parents faa1070 + b07b3c0 commit 993b6b0
Show file tree
Hide file tree
Showing 9 changed files with 199 additions and 115 deletions.
88 changes: 47 additions & 41 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,21 @@
# CustomTabsLauncher
[![Android CI](https://github.com/droibit/CustomTabsLauncher/workflows/Android%20CI/badge.svg)](https://github.com/droibit/CustomTabsLauncher/actions?query=workflow%3A%22Android+CI%22) [![JitPack.io](https://jitpack.io/v/droibit/customtabslauncher.svg)](https://jitpack.io/#droibit/customtabslauncher) [![Software License](https://img.shields.io/badge/license-Apache%202.0-brightgreen.svg)](https://github.com/droibit/prefbinding/blob/develop/LICENSE)

This library to launch the [Chrome Custom Tabs](https://developer.chrome.com/multidevice/android/customtabs) directly.
This library to launch the [Custom Tabs](https://developer.chrome.com/docs/android/custom-tabs/) directly.
Custom Tabs does not launch directly in the following user environment.

* Multiple browser app is installed.
* Default browser other than Chrome.
* Default browser is non-Chrome.

Custom Tabs can be displayed as one screen of the app to customize the look & feel. For this reason, I have created a library for launching direct.

### Browser Priorities

1. [Chrome](https://play.google.com/store/apps/details?id=com.android.chrome).
2. [Chrome Beta](https://play.google.com/store/apps/details?id=com.chrome.beta).
3. [Chrome Dev](https://play.google.com/store/apps/details?id=com.chrome.dev).
4. Local(com.google.android.apps.chrome).
5. (Optional) Browsers provided by `CustomTabsPackageFallback`.

## Download

Add it in your root build.gradle at the end of repositories:
Expand All @@ -29,53 +36,52 @@ implementation 'com.github.droibit:customtabslauncher:LATEST_VERSION'

## Usage

```kotlin
val tabsIntent = createCustomTabsIntent();
CustomTabsLauncher.launch(
context,
customTabsIntent,
Uri.parse("https://www.google.com"),
) { context, uri, customTabsIntent ->
// Fallback is optional.
// Launch WebView, display a toast, etc.
}

// or
#### Basic usage

createCustomTabsIntent().launch(context, url = "https://www.google.com") { context, url, customTabsIntent ->
// Fallback is optional.
// Launch WebView, display a toast, etc.
```kotlin
try {
val customTabsIntent = buildCustomTabsIntent()
.ensureCustomTabsPackage(context)
customTabsIntent.launchUrl(context, Uri.parse("https://example.com"))
} catch (e: ActivityNotFoundException) {
// Launch WebView, display a toast, etc.
}
```

#### Priority of Chrome
##### Present the custom tab as bottom sheet

1. [Chrome](https://play.google.com/store/apps/details?id=com.android.chrome)
2. [Chrome Beta](https://play.google.com/store/apps/details?id=com.chrome.beta)
3. [Chrome Dev](https://play.google.com/store/apps/details?id=com.chrome.dev)
4. Local(com.google.android.apps.chrome)
```kotlin
val activityLauncher = registerForActivityResult(StartActivityForResult()) {
// Do something.
}

#### Launch non-Chrome browsers as Fallback
try {
val customTabsIntent = build().apply {
ensureCustomTabsPackage(context)
intent.data = Uri.parse("https://example.com")
}
activityLauncher.launch(customTabsIntent.intent)
} catch (e: ActivityNotFoundException) {
// Launch WebView, display a toast, etc.
}
```

This library officially supports Chrome,
but provides `LaunchNonChromeCustomTabsFallback` as a helper class for directly launching other browsers that support CustomTabs.
#### How to use `CustomTabsPackageFallback`

```kotlin
import com.droibit.android.customtabs.launcher.CustomTabsLauncher.LaunchNonChromeCustomTabs;

val exampleNonChromePackages = listOf(
"org.mozilla.firefox",
"com.microsoft.emmx"
)

CustomTabsLauncher.launch(
context,
customTabsIntent,
Uri.parse("https://www.google.com"),
LaunchNonChromeCustomTabs(exampleNonChromePackages)
// or Specify browser apps that supports Custom Tabs that is not Chrome installed on a device.
// LaunchNonChromeCustomTabs(context)
)
buildCustomTabsIntent()
.ensureCustomTabsPackage(
context,
// Launch a specific browser that supports Custom Tabs.
NonChromeCustomTabs(
listOf(
"org.mozilla.firefox",
"com.microsoft.emmx"
)
// or launch a browser that supports Custom Tabs (not Chrome).
// NonChromeCustomTabs(context)
)
)
```

## License
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,22 @@ package com.droibit.android.customtabs.launcher.example

import android.content.ActivityNotFoundException
import android.net.Uri
import android.util.Log
import android.view.View
import android.widget.Toast
import androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult
import androidx.appcompat.app.AppCompatActivity
import androidx.browser.customtabs.CustomTabColorSchemeParams
import androidx.browser.customtabs.CustomTabsIntent
import androidx.core.content.ContextCompat
import com.droibit.android.customtabs.launcher.CustomTabsLauncher
import com.droibit.android.customtabs.launcher.CustomTabsLauncher.LaunchNonChromeCustomTabs
import com.droibit.android.customtabs.launcher.launch
import com.droibit.android.customtabs.launcher.NonChromeCustomTabs
import com.droibit.android.customtabs.launcher.ensureCustomTabsPackage

@Suppress("UNUSED_PARAMETER")
class MainActivity : AppCompatActivity(R.layout.activity_main) {
private val activityLauncher = registerForActivityResult(StartActivityForResult()) { result ->
Log.d("DEBUG", "result: $result")
}

fun launchDefaultCustomTabs(v: View) {
try {
Expand All @@ -26,30 +30,44 @@ class MainActivity : AppCompatActivity(R.layout.activity_main) {

fun launchFromLauncher(v: View) {
try {
val customTabsIntent = customTabsBuilder().build()
CustomTabsLauncher.launch(this, customTabsIntent, URI_GOOGLE)
val customTabsIntent = customTabsBuilder()
.build().also {
it.ensureCustomTabsPackage(this)
}
customTabsIntent.launchUrl(this, URI_GOOGLE)
} catch (e: ActivityNotFoundException) {
showErrorToast()
}
}

fun launchFromKotlinLauncher(v: View) {
fun fallbacks(v: View) {
try {
customTabsBuilder().build()
.launch(this, URI_GOOGLE)
val customTabsIntent = customTabsBuilder()
.build().also {
it.ensureCustomTabsPackage(this, NonChromeCustomTabs(this))
}
customTabsIntent.launchUrl(this, URI_GOOGLE)
} catch (e: ActivityNotFoundException) {
showErrorToast()
}
}

fun fallbacks(v: View) {
val customTabsIntent = customTabsBuilder().build()
CustomTabsLauncher.launch(
this,
customTabsIntent,
URI_GOOGLE,
LaunchNonChromeCustomTabs(this)
)
fun startActivityWithCustomTabs(v: View) {
val customTabsIntent = CustomTabsIntent.Builder()
.setShowTitle(true)
.setDefaultColorSchemeParams(
CustomTabColorSchemeParams.Builder()
.setToolbarColor(
ContextCompat.getColor(this, R.color.colorPrimary)
)
.build()
)
.setInitialActivityHeightPx(400)
.build().apply {
ensureCustomTabsPackage(this@MainActivity)
intent.data = URI_GOOGLE
}
activityLauncher.launch(customTabsIntent.intent)
}

private fun customTabsBuilder(): CustomTabsIntent.Builder {
Expand Down
12 changes: 6 additions & 6 deletions example/src/main/res/layout/activity_main.xml
Original file line number Diff line number Diff line change
Expand Up @@ -22,17 +22,17 @@
android:onClick="launchFromLauncher" />

<Button
android:id="@+id/launcherKotlinButton"
android:text="Launch from Launcher(Kotlin ext)"
android:id="@+id/fallbacksButton"
android:text="FallBacks"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="launchFromKotlinLauncher" />
android:onClick="fallbacks" />

<Button
android:id="@+id/fallbacksButton"
android:text="FallBacks"
android:id="@+id/startActivityButton"
android:text="Start Activity"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="fallbacks" />
android:onClick="startActivityWithCustomTabs" />

</LinearLayout>

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
@file:JvmName("CustomTabsIntentHelper")

package com.droibit.android.customtabs.launcher

import android.content.Context
import androidx.browser.customtabs.CustomTabsClient
import androidx.browser.customtabs.CustomTabsIntent
import com.droibit.android.customtabs.launcher.internal.CustomTabsPackage.CHROME_PACKAGES

/**
* Sets the package name of the [CustomTabsIntent] to directly launch a browser that supports Custom Tabs.
*
* Chrome is prioritized as the launch target, but if it is not installed,
* other browsers can be provided as the target using the [CustomTabsPackageFallback].
* (If no browser that supports Custom Tabs is installed on the device, the CustomTabsIntent is not affected.)
*
* * Basic usage:
* ```
* val customTabsIntent = build()
* customTabsIntent.ensureCustomTabsPackage(context)
* customTabsIntent.launchUrl(context, Uri)
* ```
*
* * Present the custom tab as bottom sheet
* ```
* val activityLauncher = registerForActivityResult(StartActivityForResult()) {
* // Do something.
* }
*
* val customTabsIntent = build().apply {
* ensureCustomTabsPackage(context)
* intent.data = Uri
* }
* activityLauncher.launch(customTabsIntent.intent)
* ```
*
* @param context The source Context
* @param fallback A [CustomTabsPackageFallback] to be used if Chrome Custom Tabs is not available.
*/
@JvmOverloads
fun CustomTabsIntent.ensureCustomTabsPackage(
context: Context,
fallback: CustomTabsPackageFallback? = null,
): CustomTabsIntent {
setCustomTabsPackage(context, CHROME_PACKAGES, fallback)
return this
}

internal fun CustomTabsIntent.setCustomTabsPackage(
context: Context,
customTabsPackages: List<String>,
fallback: CustomTabsPackageFallback? = null,
) {
val customTabsPackage =
CustomTabsClient.getPackageName(context, customTabsPackages, true)
if (customTabsPackage == null && fallback != null) {
with(fallback) {
setCustomTabsPackage(context)
}
return
}
intent.setPackage(customTabsPackage)
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ object CustomTabsLauncher {
* @param uri the Uri to be opened.
* @param fallback a [CustomTabsFallback] to be used if Custom Tabs is not available.
*/
@Deprecated("Migrate to 'CustomTabsIntent.ensureCustomTabsPackage' and 'CustomTabsIntent.launchUrl'.")
@JvmStatic
@JvmOverloads
fun launch(
Expand All @@ -53,6 +54,7 @@ object CustomTabsLauncher {
*
* @param customTabsPackages Package list of non-Chrome browsers supporting Custom Tabs. The top of the list is used with the highest priority.
*/
@Deprecated("Migrate to 'NonChromeCustomTabs'.")
class LaunchNonChromeCustomTabs(
private val customTabsPackages: List<String>
) : CustomTabsFallback {
Expand All @@ -73,4 +75,17 @@ object CustomTabsLauncher {
}
}
}
}

/**
* To be used as a fallback to open the Uri when Custom Tabs is not available.
*/
@Deprecated("Migrate to 'CustomTabsPackageFallback'.")
fun interface CustomTabsFallback {
/**
* @param context The source Context
* @param uri The Uri to load in the Custom Tab
* @param customTabsIntent a source CustomTabsIntent
*/
fun openUrl(context: Context, uri: Uri, customTabsIntent: CustomTabsIntent)
}

This file was deleted.

Loading

0 comments on commit 993b6b0

Please sign in to comment.