|
|
@@ -1,6 +1,6 @@
|
|
|
/* eslint-disable */
|
|
|
import { v4 } from 'uuid'
|
|
|
-import { SystemInfo, ShareMessage, HttpRequestConfig } from './interface'
|
|
|
+import { SystemInfo, ShareMessage, HttpRequestConfig, Download } from './types'
|
|
|
import { urlScheme } from './constants'
|
|
|
|
|
|
declare global {
|
|
|
@@ -9,6 +9,11 @@ declare global {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+interface AndroidErrorCallback {
|
|
|
+ code: number;
|
|
|
+ message: string;
|
|
|
+}
|
|
|
+
|
|
|
export default new (class {
|
|
|
private readonly plusready = new Promise<void>((resolve) => {
|
|
|
if (this.hasPlus()) {
|
|
|
@@ -24,16 +29,16 @@ export default new (class {
|
|
|
private xhr = new XMLHttpRequest()
|
|
|
|
|
|
/**
|
|
|
- * 当前下载任务
|
|
|
+ * 当前下载进度任务
|
|
|
*/
|
|
|
- private downloadTask = new Map()
|
|
|
+ private progressTask = new Map()
|
|
|
|
|
|
/**
|
|
|
* 系统信息
|
|
|
*/
|
|
|
private systemInfo: SystemInfo = {
|
|
|
os: 'Web', // 客户端操作系统
|
|
|
- version: '1.0', // 客户端版本号
|
|
|
+ version: '1.0.0', // 客户端版本号
|
|
|
versionCode: '100000', // 客户端版本代码
|
|
|
statusBarHeight: 0, // 状态栏高度
|
|
|
}
|
|
|
@@ -53,26 +58,30 @@ export default new (class {
|
|
|
// 监听返回按钮事件
|
|
|
this.onPlusReady((plus) => {
|
|
|
let firstBack = true
|
|
|
- const webview = plus.webview.currentWebview()
|
|
|
-
|
|
|
plus.key.addEventListener('backbutton', () => {
|
|
|
- webview.canBack((e: any) => {
|
|
|
- // 判断能否继续返回
|
|
|
- if (e.canBack) {
|
|
|
- webview.back()
|
|
|
- } else {
|
|
|
- // 1秒内连续两次按返回键退出应用
|
|
|
- if (firstBack) {
|
|
|
- firstBack = false
|
|
|
- plus.nativeUI.toast('再按一次退出应用')
|
|
|
- setTimeout(() => {
|
|
|
- firstBack = true
|
|
|
- }, 1000)
|
|
|
+ const webviews = plus.webview.all() // 所有Webview窗口
|
|
|
+ if (webviews.length > 1) {
|
|
|
+ plus.webview.close(webviews[webviews.length - 1])
|
|
|
+ } else {
|
|
|
+ const webview = plus.webview.currentWebview()
|
|
|
+ webview.canBack((e: any) => {
|
|
|
+ // 判断能否继续返回
|
|
|
+ if (e.canBack) {
|
|
|
+ webview.back()
|
|
|
} else {
|
|
|
- plus.runtime.quit()
|
|
|
+ // 1秒内连续两次按返回键退出应用
|
|
|
+ if (firstBack) {
|
|
|
+ firstBack = false
|
|
|
+ plus.nativeUI.toast('再按一次退出应用')
|
|
|
+ setTimeout(() => {
|
|
|
+ firstBack = true
|
|
|
+ }, 1000)
|
|
|
+ } else {
|
|
|
+ plus.runtime.quit()
|
|
|
+ }
|
|
|
}
|
|
|
- }
|
|
|
- })
|
|
|
+ })
|
|
|
+ }
|
|
|
})
|
|
|
})
|
|
|
}
|
|
|
@@ -214,14 +223,14 @@ export default new (class {
|
|
|
* @param callback
|
|
|
* @returns
|
|
|
*/
|
|
|
- onDownload(callback: (filename: string, progress: number) => void) {
|
|
|
+ onDownloadProgress(callback: (progress: number) => void) {
|
|
|
const uuid = v4()
|
|
|
- this.downloadTask.set(uuid, callback)
|
|
|
+ this.progressTask.set(uuid, callback)
|
|
|
|
|
|
/** 注意离开页面时销毁监听事件,防止事件重复触发 */
|
|
|
return {
|
|
|
uuid,
|
|
|
- cancel: () => this.downloadTask.delete(uuid)
|
|
|
+ cancel: () => this.progressTask.delete(uuid)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -231,39 +240,39 @@ export default new (class {
|
|
|
* @param url
|
|
|
*/
|
|
|
createDownload(url: string) {
|
|
|
- this.onPlusReady((plus) => {
|
|
|
- // plus.downloader.enumerate((downloads: any) => {
|
|
|
- // if (downloads.length) {
|
|
|
- // plus.nativeUI.toast('正在下载')
|
|
|
- // } else {
|
|
|
-
|
|
|
- // }
|
|
|
- // })
|
|
|
- const task = plus.downloader.createDownload(url, {
|
|
|
- filename: '_downloads/', // 非系统 Download 目录
|
|
|
- retry: 1,
|
|
|
- }, (d: any, status: number) => {
|
|
|
- if (status !== 200) {
|
|
|
- plus.nativeUI.toast('下载失败')
|
|
|
- }
|
|
|
- })
|
|
|
- // 监听下载状态
|
|
|
- task.addEventListener('statechanged', (task: any) => {
|
|
|
- switch (task.state) {
|
|
|
- case 3:
|
|
|
- const progress = task.downloadedSize / task.totalSize * 100
|
|
|
- for (const fn of this.downloadTask.values()) {
|
|
|
- fn(task.filename, progress) // 推送下载进度
|
|
|
+ return new Promise<Download>((resolve, reject) => {
|
|
|
+ this.onPlusReady((plus) => {
|
|
|
+ // plus.downloader.enumerate((downloads: any) => {
|
|
|
+ // if (downloads.length) {
|
|
|
+ // plus.nativeUI.toast('正在下载')
|
|
|
+ // } else {
|
|
|
+
|
|
|
+ // }
|
|
|
+ // })
|
|
|
+ const task = plus.downloader.createDownload(url, {
|
|
|
+ filename: '_downloads/', // 非系统 Download 目录
|
|
|
+ retry: 1,
|
|
|
+ }, (download: Download, status: number) => {
|
|
|
+ if (status === 200) {
|
|
|
+ resolve(download)
|
|
|
+ } else {
|
|
|
+ reject('下载失败,请稍后再试')
|
|
|
+ }
|
|
|
+ this.progressTask.clear()
|
|
|
+ })
|
|
|
+ // 监听下载状态
|
|
|
+ task.addEventListener('statechanged', (e: Download) => {
|
|
|
+ //console.log(e.state, e.downloadedSize / e.totalSize * 100)
|
|
|
+ if (e.state === 3) {
|
|
|
+ const progress = e.downloadedSize / e.totalSize * 100
|
|
|
+ for (const fn of this.progressTask.values()) {
|
|
|
+ fn(progress) // 推送下载进度
|
|
|
}
|
|
|
- break
|
|
|
- case 4:
|
|
|
- console.log('下载完成', task.filename)
|
|
|
- this.downloadTask.clear()
|
|
|
- break
|
|
|
- }
|
|
|
+ }
|
|
|
+ })
|
|
|
+ // 开始下载
|
|
|
+ task.start()
|
|
|
})
|
|
|
- // 开始下载
|
|
|
- task.start()
|
|
|
})
|
|
|
}
|
|
|
|
|
|
@@ -334,6 +343,30 @@ export default new (class {
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
+ * https://www.html5plus.org/doc/zh_cn/webview.html#plus.webview.create
|
|
|
+ * @param options
|
|
|
+ */
|
|
|
+ openWebview(options: { url: string; id?: string; titleText?: string; titleColor?: string; backgroundColor?: string; onClose?: () => void; }) {
|
|
|
+ if (this.hasPlus()) {
|
|
|
+ const styles = {
|
|
|
+ titleNView: {
|
|
|
+ backgroundColor: options.backgroundColor,
|
|
|
+ titleText: options.titleText,
|
|
|
+ titleColor: options.titleColor,
|
|
|
+ autoBackButton: true,
|
|
|
+ }
|
|
|
+ }
|
|
|
+ this.onPlusReady((plus) => {
|
|
|
+ const wv = plus.webview.create(options.url, options.id ?? v4(), styles)
|
|
|
+ wv.show()
|
|
|
+ wv.addEventListener('close', () => options.onClose && options.onClose(), false)
|
|
|
+ })
|
|
|
+ } else {
|
|
|
+ this.openURL(options.url)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
* 将本地URL路径转换成平台绝对路径
|
|
|
* https://www.html5plus.org/doc/zh_cn/io.html#plus.io.convertLocalFileSystemURL
|
|
|
* @param url
|
|
|
@@ -454,4 +487,87 @@ export default new (class {
|
|
|
})
|
|
|
})
|
|
|
}
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 检查运行环境的权限
|
|
|
+ * https://www.html5plus.org/doc/zh_cn/navigator.html#plus.navigator.checkPermission
|
|
|
+ */
|
|
|
+ checkPermission() {
|
|
|
+ this.onPlusReady((plus) => {
|
|
|
+ const res = plus.navigator.checkPermission('android.permission.READ_EXTERNAL_STORAGE')
|
|
|
+
|
|
|
+ console.log(res)
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 请求APP权限
|
|
|
+ */
|
|
|
+ requestPermission(options: Partial<{
|
|
|
+ permissionId: string;
|
|
|
+ errorMessage?: string;
|
|
|
+ refuseMessage?: string;
|
|
|
+ onSuccess?: () => void;
|
|
|
+ onError?: (message: string) => void;
|
|
|
+ }>) {
|
|
|
+ const { onSuccess, onError } = options
|
|
|
+ if (this.hasPlus()) {
|
|
|
+ this.onPlusReady((plus) => {
|
|
|
+ plus.android.requestPermissions([options.permissionId], (e: { granted: string[]; deniedPresent: string[]; deniedAlways: string[]; }) => {
|
|
|
+ if (e.deniedAlways.length > 0) {
|
|
|
+ onError && onError(options.refuseMessage ?? '')
|
|
|
+ }
|
|
|
+ if (e.deniedPresent.length > 0) {
|
|
|
+ onError && onError(options.errorMessage ?? '')
|
|
|
+ }
|
|
|
+ if (e.granted.length > 0) {
|
|
|
+ onSuccess && onSuccess()
|
|
|
+ }
|
|
|
+ }, (e: AndroidErrorCallback) => {
|
|
|
+ onError && onError(e.message)
|
|
|
+ })
|
|
|
+ })
|
|
|
+ } else {
|
|
|
+ onSuccess && onSuccess()
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 请求存储权限
|
|
|
+ */
|
|
|
+ requestPermissionReadExternalStorage(options: Partial<{ onSuccess: () => void; onError: (message: string) => void; }> = {}) {
|
|
|
+ this.requestPermission({
|
|
|
+ permissionId: 'android.permission.READ_EXTERNAL_STORAGE',
|
|
|
+ errorMessage: '请打开存储权限',
|
|
|
+ refuseMessage: '访问存储被拒绝',
|
|
|
+ onSuccess: options.onSuccess,
|
|
|
+ onError: options.onError
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 请求摄像头权限
|
|
|
+ */
|
|
|
+ requestPermissionCamera(options: Partial<{ onSuccess: () => void; onError: (message: string) => void; }> = {}) {
|
|
|
+ this.requestPermission({
|
|
|
+ permissionId: 'android.permission.CAMERA',
|
|
|
+ errorMessage: '请打开摄像头权限',
|
|
|
+ refuseMessage: '访问摄像头被拒绝',
|
|
|
+ onSuccess: options.onSuccess,
|
|
|
+ onError: options.onError
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 请求麦克风权限
|
|
|
+ */
|
|
|
+ requestPermissionRecordAudio(options: Partial<{ onSuccess: () => void; onError: (message: string) => void; }> = {}) {
|
|
|
+ this.requestPermission({
|
|
|
+ permissionId: 'android.permission.RECORD_AUDIO',
|
|
|
+ errorMessage: '访问麦克风被拒绝',
|
|
|
+ refuseMessage: '请打开麦克风权限',
|
|
|
+ onSuccess: options.onSuccess,
|
|
|
+ onError: options.onError
|
|
|
+ })
|
|
|
+ }
|
|
|
})
|