Skip to content

Commit d90c146

Browse files
committed
Add hasBackButton, browserPackage and showInRecents options for Android
1 parent 8e9b20c commit d90c146

21 files changed

+444
-365
lines changed

README.md

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
<h4 align="center"><a href="https://developer.chrome.com/multidevice/android/customtabs#whatarethey">Chrome Custom Tabs</a> for Android & <a href="https://developer.apple.com/documentation/safariservices">SafariServices</a>/<a href="https://developer.apple.com/documentation/authenticationservices">AuthenticationServices</a> for iOS.</h4>
3434

3535
<p align="center">
36-
<img width="400px" src="img/inappbrowser.png">
36+
<img width="400px" src="https://github.com/proyecto26/nativescript-inappbrowser/blob/develop/img/inappbrowser.png?raw=true">
3737
</p>
3838

3939
## Getting started
@@ -80,13 +80,16 @@ Property | Description
8080
`animations` (Object) | Sets the start and exit animations. [`{ startEnter, startExit, endEnter, endExit }`]
8181
`headers` (Object) | The data are key/value pairs, they will be sent in the HTTP request headers for the provided url. [`{ 'Authorization': 'Bearer ...' }`]
8282
`forceCloseOnRedirection` (Boolean) | Open Custom Tab in a new task to avoid issues redirecting back to app scheme. [`true`/`false`]
83+
`hasBackButton` (Boolean) | Sets a back arrow instead of the default `X` icon to close the custom tab. [`true`/`false`]
84+
`browserPackage` (String) | Package name of a browser to be used to handle Custom Tabs.
85+
`showInRecents` (Boolean) | Determining whether browsed website should be shown as separate entry in Android recents/multitasking view. [`true`/`false`]
8386

8487
### Demo
8588

8689
```javascript
8790
import { openUrl } from 'tns-core-modules/utils/utils'
8891
import { alert } from 'tns-core-modules/ui/dialogs'
89-
import InAppBrowser from 'nativescript-inappbrowser'
92+
import { InAppBrowser } from 'nativescript-inappbrowser'
9093

9194
...
9295
openLink = async () => {
@@ -101,7 +104,7 @@ import InAppBrowser from 'nativescript-inappbrowser'
101104
readerMode: false,
102105
animated: true,
103106
modalPresentationStyle: 'fullScreen',
104-
modalTransitionStyle: 'partialCurl',
107+
modalTransitionStyle: 'coverVertical',
105108
modalEnabled: true,
106109
enableBarCollapsing: false,
107110
// Android Properties
@@ -155,7 +158,7 @@ define your app scheme and replace `my-scheme` and `my-host` with your info.
155158
<action android:name="android.intent.action.VIEW" />
156159
<category android:name="android.intent.category.DEFAULT" />
157160
<category android:name="android.intent.category.BROWSABLE" />
158-
<data android:scheme="my-scheme" android:host="my-host" android:pathPrefix="" />
161+
<data android:scheme="my-scheme" android:host="my-host" />
159162
</intent-filter>
160163
```
161164

@@ -189,7 +192,7 @@ export const getDeepLink = (path = "") => {
189192
- home-page.ts
190193
```javascript
191194
import { openUrl } from 'tns-core-modules/utils/utils';
192-
import InAppBrowser from 'nativescript-inappbrowser';
195+
import { InAppBrowser } from 'nativescript-inappbrowser';
193196
import { getDeepLink } from './utilities';
194197
...
195198
async onLogin() {

demo/app/App_Resources/Android/src/main/AndroidManifest.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@
4242
<action android:name="android.intent.action.VIEW" />
4343
<category android:name="android.intent.category.DEFAULT" />
4444
<category android:name="android.intent.category.BROWSABLE" />
45-
<data android:scheme="my-demo" android:host="demo" android:pathPrefix="" />
45+
<data android:scheme="my-demo" android:host="demo" />
4646
</intent-filter>
4747
</activity>
4848
<activity android:name="com.tns.ErrorReportActivity"/>

demo/app/home/home-view-model.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ export class HelloWorldModel extends Observable {
3535
readerMode: false,
3636
animated: true,
3737
modalPresentationStyle: 'fullScreen',
38-
modalTransitionStyle: 'partialCurl',
38+
modalTransitionStyle: 'coverVertical',
3939
modalEnabled: true,
4040
enableBarCollapsing: false,
4141
// Android Properties

src/ChromeTabsManagerActivity.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1-
import { Observable } from '@nativescript/core';
2-
31
import Context = android.content.Context;
42
import Intent = android.content.Intent;
53
import Bundle = android.os.Bundle;
64

5+
import { Observable } from '@nativescript/core';
6+
import { BROWSER_TYPES } from './InAppBrowser.common';
7+
78

89
export class ChromeTabsEvent extends Observable {
910
public message: String;
@@ -59,15 +60,15 @@ export class ChromeTabsManagerActivity extends android.app.Activity {
5960
if (!this.mOpened) {
6061
this.mOpened = true;
6162
} else {
62-
this.resultType = 'cancel';
63+
this.resultType = BROWSER_TYPES.CANCEL;
6364
this.finish();
6465
}
6566
}
6667

6768
onDestroy(): void {
6869
if (this.resultType) {
6970
switch (this.resultType) {
70-
case 'cancel':
71+
case BROWSER_TYPES.CANCEL:
7172
BROWSER_ACTIVITY_EVENTS.set('message', 'chrome tabs activity closed');
7273
BROWSER_ACTIVITY_EVENTS.set('resultType', this.resultType);
7374
break;

src/InAppBrowser.android.ts

Lines changed: 74 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,62 +1,47 @@
1-
import { Color, Utils, Application, EventData } from '@nativescript/core';
21
import Uri = android.net.Uri;
32
import Bundle = android.os.Bundle;
3+
import TextUtils = android.text.TextUtils;
44
import Intent = android.content.Intent;
55
import Context = android.content.Context;
6+
import Color = android.graphics.Color;
7+
import BitmapFactory = android.graphics.BitmapFactory;
68
import Browser = android.provider.Browser;
79
import Pattern = java.util.regex.Pattern;
810
import AssertionError = java.lang.AssertionError;
911

12+
import { Utils, Application, EventData } from '@nativescript/core';
1013
import {
1114
ChromeTabsEvent,
1215
BROWSER_ACTIVITY_EVENTS,
1316
createStartIntent,
1417
createDismissIntent
1518
} from './ChromeTabsManagerActivity';
16-
1719
import {
20+
Animations,
1821
BrowserResult,
19-
AuthSessionResult,
20-
getDefaultOptions
22+
getDefaultOptions,
23+
InAppBrowserOptions,
24+
InAppBrowserClassMethods
2125
} from './InAppBrowser.common';
22-
2326
import {
27+
Builder,
28+
getDrawableId,
29+
toolbarIsLight,
30+
CustomTabsIntent,
31+
ARROW_BACK_WHITE,
32+
ARROW_BACK_BLACK,
33+
getPreferredPackages,
2434
openAuthSessionPolyfillAsync,
25-
closeAuthSessionPolyfillAsync
35+
closeAuthSessionPolyfillAsync,
2636
} from './utils.android';
27-
import { InAppBrowserClassMethods } from '.';
2837

2938
declare let global: any;
3039

31-
type Builder = androidx.browser.customtabs.CustomTabsIntent.Builder;
32-
const CustomTabsIntent = (useAndroidX() ? androidx.browser : android.support).customtabs.CustomTabsIntent;
33-
function useAndroidX() {
34-
return global.androidx && global.androidx.browser;
35-
}
36-
37-
type Animations = {
38-
startEnter: string,
39-
startExit: string,
40-
endEnter: string,
41-
endExit: string
42-
};
43-
44-
type InAppBrowserOptions = {
45-
showTitle?: boolean,
46-
toolbarColor?: string,
47-
secondaryToolbarColor?: string,
48-
enableUrlBarHiding?: boolean,
49-
enableDefaultShare?: boolean,
50-
forceCloseOnRedirection?: boolean,
51-
animations?: Animations,
52-
headers?: { [key: string]: string }
53-
};
54-
5540
let InAppBrowserModuleInstance: InAppBrowserClassMethods;
5641

5742
function setup() {
5843
@NativeClass()
59-
class InAppBrowserModule extends java.lang.Object {
44+
class InAppBrowserModule extends java.lang.Object implements InAppBrowserClassMethods {
6045
private static ERROR_CODE = "InAppBrowser";
6146
private static KEY_TOOLBAR_COLOR = "toolbarColor";
6247
private static KEY_SECONDARY_TOOLBAR_COLOR = "secondaryToolbarColor";
@@ -70,9 +55,13 @@ function setup() {
7055
private static KEY_ANIMATION_START_EXIT = "startExit";
7156
private static KEY_ANIMATION_END_ENTER = "endEnter";
7257
private static KEY_ANIMATION_END_EXIT = "endExit";
58+
private static KEY_HAS_BACK_BUTTON = "hasBackButton";
59+
private static KEY_BROWSER_PACKAGE = "browserPackage";
60+
private static KEY_SHOW_IN_RECENTS = "showInRecents";
7361

7462
private static redirectResolve: any;
7563
private static redirectReject: any;
64+
private isLightTheme: Boolean;
7665
private currentActivity: any;
7766
private animationIdentifierPattern = Pattern.compile("^.+:.+/");
7867

@@ -81,13 +70,15 @@ function setup() {
8170
return global.__native(this);
8271
}
8372

84-
isAvailable(): Promise<boolean> {
85-
return Promise.resolve(true);
73+
isAvailable() {
74+
const context = Utils.android.getApplicationContext();
75+
const resolveInfos = getPreferredPackages(context);
76+
return Promise.resolve(!(resolveInfos === null || resolveInfos.isEmpty()));
8677
}
8778

8879
open(
8980
url: string,
90-
options: InAppBrowserOptions = {}
81+
options?: InAppBrowserOptions,
9182
): Promise<BrowserResult> {
9283
const mOpenBrowserPromise = InAppBrowserModule.redirectResolve;
9384
if (mOpenBrowserPromise) {
@@ -103,13 +94,14 @@ function setup() {
10394
return Promise.reject(new Error(InAppBrowserModule.ERROR_CODE));
10495
}
10596

106-
const inAppBrowserOptions: InAppBrowserOptions = getDefaultOptions(url, options);
97+
const inAppBrowserOptions = getDefaultOptions(url, options);
10798

10899
const builder = new CustomTabsIntent.Builder();
109100
if (inAppBrowserOptions[InAppBrowserModule.KEY_TOOLBAR_COLOR]) {
110101
const colorString = inAppBrowserOptions[InAppBrowserModule.KEY_TOOLBAR_COLOR];
111102
try {
112-
builder.setToolbarColor(new Color(colorString).android);
103+
builder.setToolbarColor(Color.parseColor(colorString));
104+
this.isLightTheme = toolbarIsLight(colorString);
113105
} catch (error) {
114106
throw new Error(
115107
"Invalid toolbar color '" + colorString + "': " + error.message);
@@ -118,25 +110,32 @@ function setup() {
118110
if (inAppBrowserOptions[InAppBrowserModule.KEY_SECONDARY_TOOLBAR_COLOR]) {
119111
const colorString = inAppBrowserOptions[InAppBrowserModule.KEY_SECONDARY_TOOLBAR_COLOR];
120112
try {
121-
builder.setSecondaryToolbarColor(new Color(colorString).android);
113+
builder.setSecondaryToolbarColor(Color.parseColor(colorString));
122114
} catch (error) {
123115
throw new Error(
124116
"Invalid secondary toolbar color '" + colorString + "': " + error.message);
125117
}
126118
}
127-
if (inAppBrowserOptions[InAppBrowserModule.KEY_ENABLE_URL_BAR_HIDING]) {
128-
builder.enableUrlBarHiding();
129-
}
119+
130120
if (inAppBrowserOptions[InAppBrowserModule.KEY_DEFAULT_SHARE_MENU_ITEM]) {
131121
builder.addDefaultShareMenuItem();
132122
}
133-
const context = Utils.android.getApplicationContext();
123+
const context = Utils.android.getApplicationContext() as Context;
134124
if (inAppBrowserOptions[InAppBrowserModule.KEY_ANIMATIONS]) {
135125
const animations = inAppBrowserOptions[InAppBrowserModule.KEY_ANIMATIONS];
136126
this.applyAnimation(context, builder, animations);
137127
}
128+
if (inAppBrowserOptions[InAppBrowserModule.KEY_HAS_BACK_BUTTON]) {
129+
builder.setCloseButtonIcon(BitmapFactory.decodeResource(
130+
context.getResources(),
131+
this.isLightTheme
132+
? getDrawableId(ARROW_BACK_BLACK)
133+
: getDrawableId(ARROW_BACK_WHITE)
134+
));
135+
}
138136

139137
const customTabsIntent = builder.build();
138+
const intent = customTabsIntent.intent;
140139

141140
const keyHeaders = inAppBrowserOptions[InAppBrowserModule.KEY_HEADERS];
142141
if (keyHeaders) {
@@ -146,16 +145,36 @@ function setup() {
146145
headers.putString(key, keyHeaders[key]);
147146
}
148147
}
149-
customTabsIntent.intent.putExtra(Browser.EXTRA_HEADERS, headers);
148+
intent.putExtra(Browser.EXTRA_HEADERS, headers);
150149
}
151150

152151
if (inAppBrowserOptions[InAppBrowserModule.KEY_FORCE_CLOSE_ON_REDIRECTION]) {
153-
customTabsIntent.intent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
154-
customTabsIntent.intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
155-
customTabsIntent.intent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
152+
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
156153
}
157-
158-
const intent = customTabsIntent.intent;
154+
155+
if (!inAppBrowserOptions[InAppBrowserModule.KEY_SHOW_IN_RECENTS]) {
156+
intent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
157+
intent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
158+
}
159+
160+
intent.putExtra(
161+
CustomTabsIntent.EXTRA_ENABLE_URLBAR_HIDING,
162+
!!inAppBrowserOptions[InAppBrowserModule.KEY_ENABLE_URL_BAR_HIDING]
163+
);
164+
try {
165+
if (inAppBrowserOptions[InAppBrowserModule.KEY_BROWSER_PACKAGE] !== undefined) {
166+
const packageName = inAppBrowserOptions[InAppBrowserModule.KEY_BROWSER_PACKAGE];
167+
if (!TextUtils.isEmpty(packageName)) {
168+
intent.setPackage(packageName);
169+
}
170+
} else {
171+
const packageName = inAppBrowserOptions[InAppBrowserModule.KEY_BROWSER_PACKAGE];
172+
intent.setPackage(packageName);
173+
}
174+
} catch (error) {
175+
if (error.printStackTrace) error.printStackTrace();
176+
}
177+
159178
intent.setData(Uri.parse(url));
160179
if (inAppBrowserOptions[InAppBrowserModule.KEY_SHOW_PAGE_TITLE]) {
161180
builder.setShowTitle(!!inAppBrowserOptions[InAppBrowserModule.KEY_SHOW_PAGE_TITLE]);
@@ -177,7 +196,7 @@ function setup() {
177196
});
178197
}
179198

180-
public close(): void {
199+
public close() {
181200
if (!InAppBrowserModule.redirectResolve) {
182201
return;
183202
}
@@ -204,17 +223,18 @@ function setup() {
204223
async openAuth(
205224
url: string,
206225
redirectUrl: string,
207-
options: InAppBrowserOptions = {}
208-
): Promise<AuthSessionResult> {
209-
const inAppBrowserOptions = getDefaultOptions(url, options);
226+
options?: InAppBrowserOptions,
227+
) {
228+
let response = null;
210229
try {
211-
return await openAuthSessionPolyfillAsync(
212-
url, redirectUrl, inAppBrowserOptions, (startUrl, opt) => this.open(startUrl, opt)
230+
response = await openAuthSessionPolyfillAsync(
231+
(startUrl, opt) => this.open(startUrl, opt), url, redirectUrl, options
213232
);
214233
} finally {
215234
closeAuthSessionPolyfillAsync();
216235
this.close();
217236
}
237+
return response;
218238
}
219239

220240
public closeAuth(): void {

0 commit comments

Comments
 (0)