-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcallApp.js
186 lines (172 loc) · 7.13 KB
/
callApp.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
(function (root, factory) {
if (typeof define === 'function' && define.amd) {
// AMD
define([], factory);
} else if (typeof exports === 'object') {
// Node, CommonJS-like
module.exports = factory();
} else {
root.CallApp = factory();
}
}(window, function () {
// 检测浏览器
const UA = navigator.userAgent;
const IOS_UA_ARR = (navigator.userAgent).match(/OS (\d+)_(\d+)_?(\d+)?/);
const ANDROID_UA_ARR = (navigator.userAgent).match(/Android (\d+)\.(\d+)?/i);
const browser = {
isAndroid: !!UA.match(/android/ig),
isAndroidGte6: ANDROID_UA_ARR && parseInt(ANDROID_UA_ARR[1], 10) > 5 ? true : false, // 安卓版本大于等于6
isIos: !!UA.match(/iphone|ipod/ig),
isIosGte9: IOS_UA_ARR && parseInt(IOS_UA_ARR[1], 10) > 8 ? true : false, // IOS版本是否大于等于9
isWeixin: !!UA.match(/MicroMessenger/ig),
isChrome: !!UA.match(/chrome\/(\d+\.\d+)/ig)
}
// 基础配置
const BASE_CONFIG = {
scheme: '', // 应该必填,考虑低版本IOS和安卓
androidScheme: null, // 安卓用Scheme,以防万一安卓与IOS定义的不一致的时候可用,:D
iosScheme: null, // IOS 用scheme
params: null, // 参数,url里的查询字符串
applink: null, //安卓
universalLink: null, // IOS
// androidIntent: null, // Android Intent 方式唤起,尽量不用,该方式能用,scheme方式都能用
timeout: 1600, // 超时时间
autoCall: false, // 是否自动唤起
success: function () {},
error: function () {}
}
class CallApp {
constructor(config = {}) {
// 考虑改用深拷贝?
this._config = Object.assign(BASE_CONFIG, config);
// scheme
let scheme = ''
if (typeof config === 'string') {
this._url = config
} else if (config.androidScheme && browser.isAndroid) {
this._url = config.androidScheme
} else if (config.iosScheme && browser.isIos) {
this._url = config.iosScheme
} else {
this._url = config.scheme
}
// 如果采用deep link
// 安卓针对6.0+
// IOS针对9.0+
if (browser.isAndroid && browser.isAndroidGte6 && this._config.applink) {
this._url = this._config.applink
} else if (browser.isIos && browser.isIosGte9 && this._config.universalLink) {
this._url = this._config.universalLink
}
// 如果有查询参数
if (this._config.params) {
this._url += '?' + this._buildQueryString(this._config.params);
}
// 如果需要自动唤起
if (this._config.autoCall) {
this.call()
}
}
/**检测是否唤起成功
* 基于时间差来判断是否唤起成功,不太准确
* 假定超时时间设为1600ms,判断基准为800ms
* 如果没有唤起成功,停留在当前页,则当计数完毕后,时间差应该不会超过太多
* 相反,如果唤起成功,则浏览器进入后台工作,setInterval会被延迟执行,时间差会被拉大,
* 一些国产浏览器或者safari会在唤起的时候弹窗,导致此方法无效,考虑以下解决方案:
* 1、对于Android国产浏览器,如果因为弹窗超时,则判定为失败,自动跳转下载页,但是由于弹窗还存在,如果用户点击唤起,依然可以唤起APP
* 2、对于IOS,IOS7、8依旧采用scheme,IOS9+可以考虑使用universalLink!!
*/
_checkApp(cb) {
const timeout = this._config.timeout;
const totalCount = Math.ceil(timeout / 20);
const acceptTime = timeout + 800;
const _callTime = +new Date(); // 开始调用时间
let _count = 0, // 计数,20ms记一次
timer = null; //定时器
timer = setInterval(function () {
_count++;
const elsTime = +(new Date()) - _callTime
if (_count >= totalCount || elsTime > acceptTime) {
clearInterval(timer);
if (elsTime > acceptTime || document.hidden || document.webkitHidden) {
cb && cb(true);
} else {
cb && cb(false);
}
}
}, 20)
}
// 唤起app
call(cb) {
const successfn = this._config.success
const errorfn = this._config.error
// 检测唤起状态
this._checkApp(flag => {
cb && cb(flag);
if (flag) {
successfn && successfn();
} else {
errorfn && errorfn();
}
})
// 根据不同的浏览器来使用不同的唤起方式
if (browser.isAndroid) {
if (browser.isChrome) {
this._callAlink();
return;
}
this._callIframe();
return;
} else if (browser.isIos) {
this._callAlink()
}
}
// a.href方式
_callAlink() {
const alink = document.createElement('a');
alink.href = this._url;
alink.click();
}
// iframe.src 方式
_callIframe() {
const iframe = document.createElement('iframe');
iframe.style.display = 'none';
iframe.src = this._url;
document.body.appendChild(iframe);
}
// location 方式
_callLocation() {
location.href = this._url;
}
// Android intent方式,仅限Android,暂时未启用
// 一般可以用Android intent 方式唤起的都可以用scheme方式唤起
_callIntent() {
const androidIndent = this._config.androidIntent
const indentURL = `intent://
${androidIndent.host}
${androidIndent.path}
#Intent;
scheme=${androidIndent.scheme};
package=${androidIndent.package};
${androidIndent.action && 'action=' + androidIndent.action + ';'}
${androidIndent.category && 'category=' + androidIndent.category + ';'}
${androidIndent.component && 'component=' + androidIndent.component + ';'}
${androidIndent.browser_fallback_url && 'S.browser_fallback_url=' + androidIndent.browser_fallback_url + ';'}
end`;
location.href = indentURL
}
// 构建查询字符串
_buildQueryString(obj = {}) {
const keys = Object.keys(obj);
let queryArr = keys.map(k => {
let v = obj[k];
if (!Array.isArray(v) && typeof v === 'object') {
v = JSON.stringify(v);
}
return k + '=' + v;
})
return queryArr.join('&');
}
}
return CallApp
}))