Skip to content

[Android] Add mayLaunchUrl and warmup methods #341

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
May 20, 2022
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
27 changes: 27 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,33 @@ import { InAppBrowser } from 'react-native-inappbrowser-reborn'
...
```

### Android Optimizations

On Android, you can warmup the in app browser client to make it launch siginificantly faster. To do so, add the following to your `MainActivity`

```java
import com.proyecto26.inappbrowser.RNInAppBrowserModule;

public class MainActivity extends ReactActivity {

@Override
protected void onStart() {
super.onStart();
RNInAppBrowserModule.onStart(this);
}

}
```

You can further optimize performance and pre-render pages [by providing the urls that the user is likely to open](https://developer.chrome.com/docs/android/custom-tabs/best-practices/#pre-render-content).

```javascript
// Do not call this every time the component render
useEffect(() => {
InAppBrowser.mayLaunchUrl("Url user has high chance to open", ["Other urls that user might open ordered by priority"]);
}, []);
```

### Authentication Flow using Deep Linking

In order to redirect back to your application from a web browser, you must specify a unique URI to your app. To do this,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,36 +1,45 @@
package com.proyecto26.inappbrowser;

import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.text.TextUtils;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ResolveInfo;
import android.graphics.Color;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.net.Uri;
import android.os.Bundle;
import android.provider.Browser;
import android.text.TextUtils;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.browser.customtabs.CustomTabsCallback;
import androidx.browser.customtabs.CustomTabsClient;
import androidx.browser.customtabs.CustomTabsIntent;
import androidx.browser.customtabs.CustomTabsService;
import androidx.browser.customtabs.CustomTabsServiceConnection;
import androidx.browser.customtabs.CustomTabsSession;
import androidx.core.graphics.ColorUtils;

import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.JSApplicationIllegalArgumentException;
import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.ReadableType;
import com.facebook.react.bridge.ReadableMapKeySetIterator;
import com.facebook.react.bridge.JSApplicationIllegalArgumentException;
import com.facebook.react.bridge.ReadableType;
import com.facebook.react.bridge.WritableMap;

import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.regex.Pattern;
import java.util.List;
import java.util.regex.Pattern;

public class RNInAppBrowser {
private final static String ERROR_CODE = "InAppBrowser";
private static final String KEY_TOOLBAR_COLOR = "toolbarColor";
Expand Down Expand Up @@ -63,6 +72,18 @@ public class RNInAppBrowser {
private Activity currentActivity;
private static final Pattern animationIdentifierPattern = Pattern.compile("^.+:.+/");

@Nullable
private CustomTabsClient customTabsClient;

private static RNInAppBrowser _inAppBrowser;

public static RNInAppBrowser getInstance() {
if (_inAppBrowser == null) {
_inAppBrowser = new RNInAppBrowser();
}
return _inAppBrowser;
}

public Integer setColor(CustomTabsIntent.Builder builder, final ReadableMap options, String key, String method, String colorName) {
String colorString = null;
Integer color = null;
Expand Down Expand Up @@ -309,4 +330,57 @@ private String getDefaultBrowser(Context context) {
}
return packageName;
}

public void onStart(Activity activity) {
Context applicationContext = activity.getApplicationContext();
CustomTabsServiceConnection connection = new CustomTabsServiceConnection() {
@Override
public void onCustomTabsServiceConnected(@NonNull ComponentName name, @NonNull CustomTabsClient client) {
customTabsClient = client;
if (!customTabsClient.warmup(0L)) {
System.err.println("Couldn't warmup custom tabs client");
}
applicationContext.unbindService(this);
}

@Override
public void onServiceDisconnected(ComponentName name) {
customTabsClient = null;
}
};

final String packageName = getDefaultBrowser(applicationContext);
if (packageName != null) {
CustomTabsClient.bindCustomTabsService(applicationContext, packageName, connection);
} else {
System.err.println("No browser supported to bind custom tab service");
}
}

public void warmup(final Promise promise) {
if (customTabsClient != null) {
promise.resolve(customTabsClient.warmup(0L));
}
promise.resolve(false);
}

public void mayLaunchUrl(String mostLikelyUrl, ReadableArray otherUrls) {
if (customTabsClient != null) {
final CustomTabsSession customTabsSession = customTabsClient.newSession(new CustomTabsCallback());
if (customTabsSession != null) {
final ArrayList<Bundle> otherUrlBundles = new ArrayList<>(otherUrls.size());

for (int index = 0; index < otherUrls.size(); index++) {
String link = otherUrls.getString(index);
if (link != null) {
final Bundle bundle = new Bundle();
bundle.putParcelable(CustomTabsService.KEY_URL, Uri.parse(link));
otherUrlBundles.add(bundle);
}
}

customTabsSession.mayLaunchUrl(Uri.parse(mostLikelyUrl), null, otherUrlBundles);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,19 @@
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.module.annotations.ReactModule;

@ReactModule(name = RNInAppBrowserModule.NAME)
public class RNInAppBrowserModule extends ReactContextBaseJavaModule {
public final static String NAME = "RNInAppBrowser";

private final RNInAppBrowser inAppBrowser;
private final ReactApplicationContext reactContext;

public RNInAppBrowserModule(ReactApplicationContext reactContext) {
super(reactContext);
this.reactContext = reactContext;
this.inAppBrowser = new RNInAppBrowser();
}

@Override
Expand All @@ -30,16 +29,31 @@ public String getName() {
@ReactMethod
public void open(final ReadableMap options, final Promise promise) {
final Activity activity = getCurrentActivity();
inAppBrowser.open(this.reactContext, options, promise, activity);
RNInAppBrowser.getInstance().open(this.reactContext, options, promise, activity);
}

@ReactMethod
public void close() {
inAppBrowser.close();
RNInAppBrowser.getInstance().close();
}

@ReactMethod
public void isAvailable(final Promise promise) {
inAppBrowser.isAvailable(this.reactContext, promise);
RNInAppBrowser.getInstance().isAvailable(this.reactContext, promise);
}

public static void onStart(final Activity activity) {
RNInAppBrowser.getInstance().onStart(activity);
}

@ReactMethod
public void warmup(final Promise promise) {
RNInAppBrowser.getInstance().warmup(promise);
}

@ReactMethod
public void mayLaunchUrl(final String mostLikelyUrl, final ReadableArray otherUrls) {
RNInAppBrowser.getInstance().mayLaunchUrl(mostLikelyUrl, otherUrls);
}

}
7 changes: 6 additions & 1 deletion example/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
* @flow strict-local
*/

import React, {useCallback, useState} from 'react';
import React, {useEffect, useCallback, useState} from 'react';
import {
Platform,
StatusBar,
Expand All @@ -17,6 +17,7 @@ import {
Button,
} from 'react-native';
import {openLink, tryDeepLinking} from './utils';
import {InAppBrowser} from 'react-native-inappbrowser-reborn';

const instructions = Platform.select({
ios: 'Press Cmd+R to reload,\n' + 'Cmd+D or shake for dev menu',
Expand All @@ -29,6 +30,10 @@ const App = () => {
const [url, setUrl] = useState('https://reactnative.dev');
const [statusBarStyle] = useState('dark-content');

useEffect(() => {
InAppBrowser.mayLaunchUrl('https://reactnative.dev', []);
}, []);

const onOpenLink = useCallback(async () => {
await openLink(url, statusBarStyle);
}, [url, statusBarStyle]);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.example;

import com.facebook.react.ReactActivity;
import com.proyecto26.inappbrowser.RNInAppBrowserModule;

public class MainActivity extends ReactActivity {

Expand All @@ -12,4 +13,10 @@ public class MainActivity extends ReactActivity {
protected String getMainComponentName() {
return "example";
}

@Override
protected void onStart() {
super.onStart();
RNInAppBrowserModule.onStart(this);
}
}
2 changes: 2 additions & 0 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,8 @@ declare module 'react-native-inappbrowser-reborn' {
options?: InAppBrowserOptions,
) => Promise<BrowserResult>;
close: () => void;
warmup: () => Promise<boolean>;
mayLaunchUrl: (mostLikelyUrl: string, otherUrls: Array<string>) => void;
openAuth: (
url: string,
redirectUrl: string,
Expand Down
19 changes: 19 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import type {
InAppBrowserOptions,
} from './types';
import {
isAndroid,
RNInAppBrowser,
openBrowserAsync,
openAuthSessionAsync,
Expand Down Expand Up @@ -43,6 +44,22 @@ function close(): void {
RNInAppBrowser.close();
}

function warmup(): Promise<boolean> {
if (isAndroid) {
return RNInAppBrowser.warmup();
}
return Promise.resolve(false);
}

function mayLaunchUrl(
mostLikelyUrl: string,
otherUrls: Array<string> = []
): void {
if (isAndroid) {
RNInAppBrowser.mayLaunchUrl(mostLikelyUrl, otherUrls);
}
}

function closeAuth(): void {
closeAuthSessionPolyfillAsync();
if (authSessionIsNativelySupported()) {
Expand All @@ -62,6 +79,8 @@ export const InAppBrowser = {
close,
closeAuth,
isAvailable,
warmup,
mayLaunchUrl,
};

export default InAppBrowser;
2 changes: 2 additions & 0 deletions utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -177,3 +177,5 @@ export function authSessionIsNativelySupported(): boolean {
const versionNumber = parseInt(Platform.Version, 10);
return versionNumber >= 11;
}

export const isAndroid = Platform.OS === 'android';