Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 3 additions & 4 deletions .husky/pre-commit
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"

yarn lint && yarn typescript
swiftformat ios/
npm test
4 changes: 4 additions & 0 deletions android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -128,5 +128,9 @@ dependencies {
api 'com.facebook.react:react-native:+'
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
api "androidx.browser:browser:1.4.0"
// coroutine
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.5.2'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.2'
implementation 'androidx.core:core-ktx:1.0.0'

}
Binary file modified android/gradle/wrapper/gradle-wrapper.jar
Binary file not shown.
Empty file modified android/gradlew
100755 → 100644
Empty file.
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package com.reactnativewebbrowser
import android.app.Activity
import com.facebook.react.bridge.ReactContext

class InternalActicityProvider(val reactContext: ReactContext) :
class InternalActivityProvider(private val reactContext: ReactContext) :
ActivityProvider {
override fun getCurrentActivity(): Activity? {
return reactContext.currentActivity
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,99 +8,116 @@ import android.net.Uri
import androidx.browser.customtabs.CustomTabsClient
import androidx.browser.customtabs.CustomTabsIntent
import androidx.browser.customtabs.CustomTabsService
import com.reactnativewebbrowser.error.CurrentActivityNotFoundException
import com.reactnativewebbrowser.error.PackageManagerNotFoundException
import java.util.ArrayList
import java.util.Collections
import java.util.LinkedHashSet

private const val DUMMY_URL = "https://web3auth.io"

import androidx.browser.customtabs.CustomTabsService.ACTION_CUSTOM_TABS_CONNECTION
import com.reactnativewebbrowser.error.CurrentActivityNotFoundException
import com.reactnativewebbrowser.error.PackageManagerNotFoundException
class InternalCustomTabsActivitiesHelper(
private val activityProvider: ActivityProvider?
) {

class InternalCustomTabsActivitiesHelper(val activityProvider: ActivityProvider) :
CustomTabsActivitiesHelper {
// region Actual custom tabs activities helper methods

override val customTabsResolvingActivities: ArrayList<String>
get() = mapCollectionToDistinctArrayList(
getResolvingActivities(
createDefaultCustomTabsIntent()
)
) { resolveInfo -> resolveInfo.activityInfo.packageName }
/**
* @throws CurrentActivityNotFoundException
* @throws PackageManagerNotFoundException
*/
fun canResolveIntent(intent: Intent): Boolean = getResolvingActivities(intent).isNotEmpty()

override val customTabsResolvingServices: ArrayList<String>
get() {
return mapCollectionToDistinctArrayList(
packageManager.queryIntentServices(
createDefaultCustomTabsServiceIntent(),
0
)
) { resolveInfo -> resolveInfo.serviceInfo.packageName }
}
/**
* @throws PackageManagerNotFoundException
* @throws CurrentActivityNotFoundException
*/
val customTabsResolvingActivities: ArrayList<String>
get() = getResolvingActivities(createDefaultCustomTabsIntent())
.mapToDistinctArrayList { resolveInfo: ResolveInfo ->
resolveInfo.activityInfo.packageName
}

/**
* @throws PackageManagerNotFoundException
* @throws CurrentActivityNotFoundException
*/
val customTabsResolvingServices: ArrayList<String>
get() = packageManager.queryIntentServices(createDefaultCustomTabsServiceIntent(), 0)
.mapToDistinctArrayList { resolveInfo: ResolveInfo ->
resolveInfo.serviceInfo.packageName
}

override fun getPreferredCustomTabsResolvingActivity(packages: List<String>?): String? {
var packages: List<String>? = packages
if (packages == null) packages = customTabsResolvingActivities
return CustomTabsClient.getPackageName(currentActivity, packages)
/**
* @throws PackageManagerNotFoundException
* @throws CurrentActivityNotFoundException
*/
fun getPreferredCustomTabsResolvingActivity(packages: List<String?>?): String? {
val resolvedPackages = packages ?: customTabsResolvingActivities
return CustomTabsClient.getPackageName(currentActivity, resolvedPackages)
}

override val defaultCustomTabsResolvingActivity: String?
/**
* @throws PackageManagerNotFoundException
* @throws CurrentActivityNotFoundException
*/
val defaultCustomTabsResolvingActivity: String?
get() {
val info: ResolveInfo? =
packageManager.resolveActivity(createDefaultCustomTabsIntent(), 0)
val info = packageManager.resolveActivity(createDefaultCustomTabsIntent(), 0)
return info?.activityInfo?.packageName
}

override fun canResolveIntent(intent: Intent): Boolean {
return getResolvingActivities(intent).isNotEmpty()
}

override fun startCustomTabs(intent: Intent) {
/**
* @throws CurrentActivityNotFoundException
*/
fun startCustomTabs(intent: Intent) {
currentActivity.startActivity(intent)
}

// endregion

// region Private helpers

/**
* @throws CurrentActivityNotFoundException
* @throws PackageManagerNotFoundException
*/
private fun getResolvingActivities(intent: Intent): List<ResolveInfo> {
return packageManager.queryIntentActivities(intent, 0)
}

/**
* @throws CurrentActivityNotFoundException
* @throws PackageManagerNotFoundException
*/
private val packageManager: PackageManager
get() {
val pm: PackageManager? = currentActivity.packageManager
if (pm == null) throw PackageManagerNotFoundException() else return pm
}
get() = currentActivity.packageManager ?: throw PackageManagerNotFoundException()

/**
* @throws CurrentActivityNotFoundException
*/
private val currentActivity: Activity
get() {
return activityProvider.getCurrentActivity()
?: throw CurrentActivityNotFoundException()
return activityProvider?.getCurrentActivity() ?: throw CurrentActivityNotFoundException()
}

private fun createDefaultCustomTabsIntent(): Intent {
val builder: CustomTabsIntent.Builder = CustomTabsIntent.Builder()
val customTabsIntent: CustomTabsIntent = builder.build()
val intent: Intent = customTabsIntent.intent
intent.data = Uri.parse(DUMMY_URL)
return intent
}
// endregion
}

private fun createDefaultCustomTabsServiceIntent(): Intent {
val serviceIntent: Intent = Intent()
serviceIntent.action = CustomTabsService.ACTION_CUSTOM_TABS_CONNECTION
return serviceIntent
private inline fun <T, R> Collection<T>.mapToDistinctArrayList(mapper: (T) -> R): ArrayList<R> {
val resultSet = LinkedHashSet<R>()
for (element in this) {
resultSet.add(mapper.invoke(element))
}
return ArrayList(resultSet)
}

fun onDestroy() {}

companion object {
private val DUMMY_URL: String = "https://expo.io"
fun <T, R> mapCollectionToDistinctArrayList(
toMap: Collection<T>,
mapper: (T) -> R
): ArrayList<R> {
val resultSet: LinkedHashSet<R> = LinkedHashSet()
for (element: T in toMap) {
resultSet.add(mapper(element))
}
return ArrayList(resultSet)
}
private fun createDefaultCustomTabsIntent(): Intent {
val customTabsIntent = CustomTabsIntent.Builder().build()
return customTabsIntent.intent.apply {
data = Uri.parse(DUMMY_URL)
}
}

private fun createDefaultCustomTabsServiceIntent() = Intent().apply {
action = CustomTabsService.ACTION_CUSTOM_TABS_CONNECTION
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,108 +6,92 @@ import android.net.Uri
import androidx.browser.customtabs.CustomTabsClient
import androidx.browser.customtabs.CustomTabsServiceConnection
import androidx.browser.customtabs.CustomTabsSession
import com.facebook.react.bridge.LifecycleEventListener

class InternalCustomTabsConnectionHelper internal constructor(private val context: Context) :
CustomTabsServiceConnection(), LifecycleEventListener,
CustomTabsConnectionHelper {
private var mPackageName: String? = null
private val clientActions: DeferredClientActionsQueue<CustomTabsClient> =
DeferredClientActionsQueue()
private val sessionActions: DeferredClientActionsQueue<CustomTabsSession> =
DeferredClientActionsQueue()
class InternalCustomTabsConnectionHelper(
private val context: Context
) : CustomTabsServiceConnection() {
private var currentPackageName: String? = null
private val clientActions = DeferredClientActionsQueue<CustomTabsClient>()
private val sessionActions = DeferredClientActionsQueue<CustomTabsSession?>()

override fun warmUp(packageName: String) {
clientActions.executeOrQueueAction { client -> client.warmup(0) }
// region lifecycle methods
fun destroy() = clearConnection()
// endregion

// region Actual connection helper methods
fun warmUp(packageName: String) {
clientActions.executeOrQueueAction { client: CustomTabsClient -> client.warmup(0) }
ensureConnection(packageName)
}

override fun mayInitWithUrl(packageName: String, uri: Uri) {
sessionActions.executeOrQueueAction { session ->
session.mayLaunchUrl(
uri,
null,
null
)
fun mayInitWithUrl(packageName: String, uri: Uri) {
sessionActions.executeOrQueueAction { session: CustomTabsSession? ->
session?.mayLaunchUrl(uri, null, null)
}
ensureConnection(packageName)
ensureSession()
}

private fun ensureSession() {
if (!sessionActions.hasClient()) {
clientActions.executeOrQueueAction { client ->
sessionActions.setClient(
client.newSession(null)
)
}
}
}

override fun coolDown(packageName: String): Boolean {
if ((packageName == mPackageName)) {
unbindService()
fun coolDown(packageName: String): Boolean {
if (isConnectionStarted(packageName)) {
clearConnection()
return true
}
return false
}
// endregion

private fun ensureConnection(packageName: String) {
if (mPackageName != null && !(mPackageName == packageName)) {
clearConnection()
}
if (!connectionStarted(packageName)) {
CustomTabsClient.bindCustomTabsService(context, packageName, this)
mPackageName = packageName
}
}

private fun connectionStarted(packageName: String): Boolean {
return (packageName == mPackageName)
}

// region CustomTabsServiceConnection implementation
override fun onBindingDied(componentName: ComponentName) {
if ((componentName.packageName == mPackageName)) {
if (isConnectionStarted(componentName.packageName)) {
clearConnection()
}
}

override fun onCustomTabsServiceConnected(
componentName: ComponentName,
client: CustomTabsClient
) {
if ((componentName.packageName == mPackageName)) {
override fun onCustomTabsServiceConnected(componentName: ComponentName, client: CustomTabsClient) {
if (isConnectionStarted(componentName.packageName)) {
clientActions.setClient(client)
}
}

override fun onServiceDisconnected(componentName: ComponentName) {
if ((componentName.packageName == mPackageName)) {
if (isConnectionStarted(componentName.packageName)) {
clearConnection()
}
}
// endregion

override fun onHostResume() {
// do nothing
}
private fun ensureSession() {
if (sessionActions.hasClient()) {
return
}

override fun onHostPause() {
// do nothing
clientActions.executeOrQueueAction { client: CustomTabsClient ->
sessionActions.setClient(client.newSession(null))
}
}

override fun onHostDestroy() {
unbindService()
private fun ensureConnection(packageName: String) {
if (currentPackageName != null && currentPackageName != packageName) {
clearConnection()
}
if (!isConnectionStarted(packageName)) {
CustomTabsClient.bindCustomTabsService(context, packageName, this)
currentPackageName = packageName
}
}

private fun unbindService() {
context.unbindService(this)
clearConnection()
private fun isConnectionStarted(packageName: String): Boolean {
return packageName == currentPackageName
}

private fun clearConnection() {
mPackageName = null
if (currentPackageName != null) {
context.unbindService(this)
}

currentPackageName = null
clientActions.clear()
sessionActions.clear()
}

}
Loading