1/* 2 * Copyright (c) 2024 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16import web_webview from '@ohos.web.webview'; 17import router from '@ohos.router'; 18import deviceInfo from '@ohos.deviceInfo'; 19import common from '@ohos.app.ability.common'; 20import geoLocationManager from '@ohos.geoLocationManager'; 21import bundleManager from '@ohos.bundle.bundleManager'; 22import abilityAccessCtrl, { Permissions } from '@ohos.abilityAccessCtrl'; 23import connection from '@ohos.net.connection'; 24import request from '@ohos.request'; 25import fs from '@ohos.file.fs'; 26import util from '@ohos.util'; 27import photoAccessHelper from '@ohos.file.photoAccessHelper'; 28import { filePreview } from '@kit.PreviewKit'; 29import fileUri from '@ohos.file.fileuri'; 30import picker from '@ohos.multimedia.cameraPicker'; 31import filePicker from '@ohos.file.picker'; 32import { BusinessError } from '@ohos.base'; 33 34class AsError { 35 public code: number; 36 public message: string; 37 38 constructor(code: number, message: string) { 39 this.code = code; 40 this.message = message; 41 } 42} 43 44class JsApiConfig { 45 public apiName: string; 46 public minVersion: string; 47 public maxVersion: string; 48 public requiredFieldNames?: string[]; 49 50 constructor(apiName: string, minVersion: string, maxVersion: string, requiredFieldNames?: string[]) { 51 this.apiName = apiName; 52 this.minVersion = minVersion; 53 this.maxVersion = maxVersion; 54 this.requiredFieldNames = requiredFieldNames; 55 } 56} 57 58const LOG_ENABLE: boolean = true; 59const LOG_PREFIX: string = '[AtomicServiceWebLog]'; 60const UPLOAD_IMAGE_CACHE_DIR: string = '/cache/'; 61const JAVA_SCRIPT_PROXY_OBJECT_NAME: string = 'atomicServiceProxy'; 62const JAVA_SCRIPT_PROXY_API_NAME_LIST: string[] = ['invokeJsApi']; 63const ATOMIC_SERVICE_JS_API_MAP = new Map<string, JsApiConfig>(); 64const registerJsApi = (apiNameAlias: string, apiName: string, minVersion: string, maxVersion: string, 65 requiredFieldNames: string[]): void => { 66 ATOMIC_SERVICE_JS_API_MAP.set(apiNameAlias, new JsApiConfig(apiName, minVersion, maxVersion, requiredFieldNames)); 67}; 68const MAX_VERSION = '99.99.99'; 69const ATOMIC_SERVICE_JS_SDK_CURRENT_VERSION = '1.0.0'; 70const PERMISSION_APPROXIMATELY_LOCATION: Permissions = 'ohos.permission.APPROXIMATELY_LOCATION'; 71 72const SYSTEM_INTERNAL_ERROR: AsError = new AsError(500, 'System internal error.'); 73const JS_API_INVALID_INVOKE_ERROR: AsError = new AsError(200001, 'Invalid invoke.'); 74const PARAM_REQUIRED_ERROR_CODE: number = 200002; 75const PARAM_NUMBER_POSITIVE_ERROR_CODE: number = 200003; 76const ROUTER_PARAM_MODE_INVALID_ERROR: AsError = new AsError(200004, 'Param mode is invalid.'); 77const BACK_URL_NOT_EXIST_OR_OPENED_ERROR: AsError = new AsError(200005, 'Url is not exist or opened, can not be back.'); 78const NAV_PATH_STACK_NOT_EXIST_ERROR_CODE: number = 200006; 79const POP_PATH_NAME_NOT_EXIST_ERROR: AsError = new AsError(200007, 'Name is not exist or opened, can not be pop.'); 80const POP_PATH_PARAM_INDEX_INVALID_ERROR: AsError = new AsError(200008, 'Param index is invalid.'); 81const POP_PATH_INDEX_OUT_OF_RANGE_ERROR: AsError = new AsError(200009, 'The Index is out of range.'); 82const UPLOAD_IMAGE_FILES_REQUIRED_ERROR: AsError = new AsError(200010, 'Param files is required.'); 83const UPLOAD_IMAGE_FILE_NOT_EXIST_ERROR_CODE: number = 200011; 84const UPLOAD_IMAGE_FILES_URI_REQUIRED_ERROR: AsError = new AsError(200012, 'Param uri of files is required.'); 85const UPLOAD_FILE_ERROR: AsError = new AsError(200013, 'Upload file error.'); 86const IMAGE_CAN_NOT_PREVIEW_ERROR: AsError = new AsError(200014, 'The filePath can not preview.'); 87const NETWORK_NO_ACTIVE_ERROR: AsError = new AsError(200015, 'The network is not active.'); 88const PERMISSION_LOCATION_USER_REFUSED_ERROR: number = 200016; 89 90registerJsApi('router.pushUrl', 'pushUrl', '1.0.0', MAX_VERSION, ['url']); 91registerJsApi('router.replaceUrl', 'replaceUrl', '1.0.0', MAX_VERSION, ['url']); 92registerJsApi('router.back', 'backUrl', '1.0.0', MAX_VERSION, []); 93registerJsApi('router.clear', 'clearUrl', '1.0.0', MAX_VERSION, []); 94registerJsApi('navPathStack.pushPath', 'pushPath', '1.0.0', MAX_VERSION, ['name']); 95registerJsApi('navPathStack.replacePath', 'replacePath', '1.0.0', MAX_VERSION, ['name']); 96registerJsApi('navPathStack.pop', 'popPath', '1.0.0', MAX_VERSION, []); 97registerJsApi('navPathStack.clear', 'clearPath', '1.0.0', MAX_VERSION, []); 98registerJsApi('asWeb.postMessage', 'postMessage', '1.0.0', MAX_VERSION, ['data']); 99registerJsApi('asWeb.getEnv', 'getEnv', '1.0.0', MAX_VERSION, []); 100registerJsApi('asWeb.checkJsApi', 'checkJsApi', '1.0.0', MAX_VERSION, ['jsApiList']); 101registerJsApi('cameraPicker.pick', 'pickCamera', '1.0.0', MAX_VERSION, ['mediaTypes', 'cameraPosition']); 102registerJsApi('photoViewPicker.select', 'selectPhoto', '1.0.0', MAX_VERSION, []); 103registerJsApi('filePreview.openPreview', 'openPreview', '1.0.0', MAX_VERSION, ['uri']); 104registerJsApi('request.uploadFile', 'uploadFile', '1.0.0', MAX_VERSION, ['url', 'files']); 105registerJsApi('request.downloadFile', 'downloadFile', '1.0.0', MAX_VERSION, ['url']); 106registerJsApi('connection.getNetworkType', 'getNetworkType', '1.0.0', MAX_VERSION, []); 107registerJsApi('location.getLocation', 'getLocation', '1.0.0', MAX_VERSION, []); 108 109@Component 110export struct AtomicServiceWeb { 111 public src: ResourceStr | undefined = undefined; 112 public navPathStack?: NavPathStack; 113 @Prop mixedMode?: MixedMode; 114 @Prop darkMode?: WebDarkMode; 115 @Prop forceDarkAccess?: boolean; 116 @ObjectLink controller: AtomicServiceWebController; 117 public onMessage?: Callback<OnMessageEvent> = () => { 118 }; 119 public onErrorReceive?: Callback<OnErrorReceiveEvent> = () => { 120 }; 121 public onHttpErrorReceive?: Callback<OnHttpErrorReceiveEvent> = () => { 122 }; 123 public onPageBegin?: Callback<OnPageBeginEvent> = () => { 124 }; 125 public onPageEnd?: Callback<OnPageEndEvent> = () => { 126 }; 127 public onProgressChange?: Callback<OnProgressChangeEvent> = () => { 128 }; 129 public onControllerAttached?: VoidCallback; 130 public onLoadIntercept?: Callback<OnLoadInterceptEvent, boolean>; 131 private context: common.UIAbilityContext = this.getUIContext().getHostContext() as common.UIAbilityContext; 132 private webViewController: web_webview.WebviewController = new web_webview.WebviewController(); 133 private schemeHandler: web_webview.WebSchemeHandler = new web_webview.WebSchemeHandler(); 134 private atomicService?: AtomicService; 135 private atomicServiceProxy?: AtomicServiceProxy; 136 137 aboutToAppear(): void { 138 if (!this.atomicService) { 139 this.atomicService = new AtomicServiceApi(this.context, this.navPathStack, this.onMessage); 140 this.atomicServiceProxy = new AtomicServiceProxy(this.atomicService); 141 } 142 143 try { 144 let bundleInfo: bundleManager.BundleInfo = bundleManager.getBundleInfoForSelfSync( 145 bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_APPLICATION); 146 if (bundleInfo?.appInfo?.appProvisionType === 'debug') { 147 console.log(`AtomicServiceWeb setWebDebuggingAccess`); 148 web_webview.WebviewController.setWebDebuggingAccess(true); 149 } 150 } catch (err) { 151 console.error(`AtomicServiceWeb set Web Debug Mode failed, code is ${err.code}, message is ${err.message}`); 152 } 153 } 154 155 aboutToDisappear(): void { 156 this.atomicService?.notifyMessage(); 157 } 158 159 build() { 160 Web({ src: this.src, controller: this.webViewController }) 161 .zoomAccess(false) 162 .allowWindowOpenMethod(false) 163 .domStorageAccess(true) 164 .layoutMode(WebLayoutMode.NONE) 165 .mixedMode(this.mixedMode) 166 .darkMode(this.darkMode) 167 .forceDarkAccess(this.forceDarkAccess) 168 .onErrorReceive((event: OnErrorReceiveEvent) => this.onCommonCallBack('onErrorReceive', event, 169 this.onErrorReceive)) 170 .onHttpErrorReceive((event: OnHttpErrorReceiveEvent) => this.onCommonCallBack('onHttpErrorReceive', event, 171 this.onHttpErrorReceive)) 172 .onPageBegin((event: OnPageBeginEvent) => this.onCommonCallBack('onPageBegin', event, this.onPageBegin)) 173 .onPageEnd((event: OnPageEndEvent) => this.onCommonCallBack('onPageEnd', event, this.onPageEnd)) 174 .onProgressChange((event: OnProgressChangeEvent) => this.onCommonCallBack('onProgressChange', event, 175 this.onProgressChange)) 176 .onControllerAttached(() => { 177 this.registerJavaScriptProxy(); 178 this.schemeHandler.onRequestStart((request: web_webview.WebSchemeHandlerRequest) => { 179 return !this.checkUrl(request.getRequestUrl()); 180 }); 181 this.webViewController.setWebSchemeHandler('https', this.schemeHandler); 182 this.initAtomicServiceWebController(); 183 if (this.onControllerAttached) { 184 try { 185 this.onControllerAttached(); 186 } catch (error) { 187 console.error(`AtomicServiceWeb onControllerAttached failed, code is ${error.code}, message is ${error.message}`); 188 } 189 } 190 }) 191 .onOverrideUrlLoading((webResourceRequest: WebResourceRequest) => { 192 return !this.checkUrl(webResourceRequest.getRequestUrl()); 193 }) 194 .onLoadIntercept(event => { 195 let checkResult = !this.checkUrl(event.data.getRequestUrl()); 196 if (!checkResult && this.onLoadIntercept) { 197 try { 198 return this.onLoadIntercept(event); 199 } catch (error) { 200 console.error(`AtomicServiceWeb onLoadIntercept failed, code is ${error.code}, message is ${error.message}`); 201 return true; 202 } 203 } 204 return checkResult 205 }) 206 } 207 208 onCommonCallBack<T>(method: string, event: T, callback?: (event: T) => void): void { 209 try { 210 callback && callback(event); 211 } catch (error) { 212 console.error(`AtomicServiceWeb ${method} failed, code is ${error.code}, message is ${error.message}`); 213 } 214 } 215 216 registerJavaScriptProxy(): void { 217 try { 218 this.webViewController.registerJavaScriptProxy(this.atomicServiceProxy, JAVA_SCRIPT_PROXY_OBJECT_NAME, 219 JAVA_SCRIPT_PROXY_API_NAME_LIST); 220 } catch (error) { 221 let e: BusinessError = error as BusinessError; 222 console.error(`AtomicServiceWeb registerJavaScriptProxy failed, code is ${e.code}, message is ${e.message}`); 223 } 224 } 225 226 initAtomicServiceWebController(): void { 227 if (!this.controller) { 228 return; 229 } 230 this.controller.setWebviewController(this.webViewController); 231 } 232 233 cutUrl(url: string): string { 234 if (url) { 235 let index: number = url.indexOf('?'); 236 if (index > -1) { 237 return url.substring(0, index); 238 } 239 } 240 return url; 241 } 242 243 checkUrl(url: string): boolean { 244 if (!url) { 245 return false; 246 } 247 if (url.startsWith('resource://rawfile')) { 248 return true; 249 } 250 url = this.cutUrl(url); 251 console.log(`AtomicServiceWebLog checkUrl url=${url}`); 252 return true; 253 } 254} 255 256@Observed 257export class AtomicServiceWebController { 258 private webViewController?: web_webview.WebviewController; 259 260 setWebviewController(webViewController: web_webview.WebviewController): void { 261 this.webViewController = webViewController; 262 } 263 264 checkWebviewController(): void { 265 if (!this.webViewController) { 266 const error: BusinessError<string> = { 267 name: '', 268 message: 'Init error. The AtomicServiceWebController must be associated with a AtomicServiceWeb component.', 269 code: 17100001, 270 } 271 throw error as Error; 272 } 273 } 274 275 getUserAgent(): string | undefined { 276 this.checkWebviewController(); 277 return this.webViewController?.getUserAgent(); 278 } 279 280 getCustomUserAgent(): string | undefined { 281 this.checkWebviewController(); 282 return this.webViewController?.getCustomUserAgent(); 283 } 284 285 setCustomUserAgent(userAgent: string): void { 286 this.checkWebviewController(); 287 this.webViewController?.setCustomUserAgent(userAgent); 288 } 289 290 accessForward(): boolean | undefined { 291 this.checkWebviewController(); 292 return this.webViewController?.accessForward(); 293 } 294 295 accessBackward(): boolean | undefined { 296 this.checkWebviewController(); 297 return this.webViewController?.accessBackward(); 298 } 299 300 accessStep(step: number): boolean | undefined { 301 this.checkWebviewController(); 302 return this.webViewController?.accessStep(step); 303 } 304 305 forward(): void { 306 this.checkWebviewController(); 307 this.webViewController?.forward(); 308 } 309 310 backward(): void { 311 this.checkWebviewController(); 312 this.webViewController?.backward(); 313 } 314 315 refresh(): void { 316 this.checkWebviewController(); 317 this.webViewController?.refresh(); 318 } 319 320 loadUrl(url: string | Resource, headers?: Array<WebHeader>): void { 321 this.checkWebviewController(); 322 if (headers) { 323 this.webViewController?.loadUrl(url, headers); 324 } else { 325 this.webViewController?.loadUrl(url); 326 } 327 } 328} 329 330class AtomicServiceProxy { 331 private atomicService: AtomicService; 332 333 constructor(atomicService: AtomicService) { 334 this.atomicService = atomicService; 335 } 336 337 invokeJsApi<T>(apiNameAlias: string, options: BaseOptions<T>): void { 338 try { 339 options = options || {}; 340 if (!apiNameAlias || !ATOMIC_SERVICE_JS_API_MAP.has(apiNameAlias)) { 341 this.atomicService.errorWithCodeAndMsg(JS_API_INVALID_INVOKE_ERROR, options); 342 return; 343 } 344 let jsApiConfig: JsApiConfig | undefined = ATOMIC_SERVICE_JS_API_MAP.get(apiNameAlias); 345 if (!this.atomicService.checkRequiredFieldInOptions(jsApiConfig, options)) { 346 return; 347 } 348 let atomicService: object = this.atomicService; 349 atomicService[jsApiConfig?.apiName as string](options); 350 } catch (err) { 351 this.atomicService.error(err, options); 352 } 353 } 354} 355 356class AtomicService { 357 protected context: common.UIAbilityContext; 358 protected navPathStack?: NavPathStack; 359 protected messageDataList: object[] = []; 360 protected onMessage: (event: OnMessageEvent) => void = () => { 361 }; 362 363 constructor(context: common.UIAbilityContext, navPathStack?: NavPathStack, 364 onMessage?: (event: OnMessageEvent) => void) { 365 this.context = context; 366 this.navPathStack = navPathStack; 367 this.onMessage = onMessage ? onMessage : this.onMessage; 368 } 369 370 success<T>(res: T, options: BaseOptions<T>): void { 371 try { 372 options?.callback && options?.callback(undefined, res); 373 } catch (err) { 374 this.consoleError(`callback error, code is ${err.code}, message is ${err.message}`); 375 } 376 } 377 378 error<T>(err: BusinessError, options: BaseOptions<T>,): void { 379 try { 380 options?.callback && options?.callback(new AsError(err.code ? err.code : SYSTEM_INTERNAL_ERROR.code, 381 err.message ? err.message : SYSTEM_INTERNAL_ERROR.message)); 382 } catch (err) { 383 this.consoleError(`callback error, code is ${err.code}, message is ${err.message}`); 384 } 385 } 386 387 errorWithCodeAndMsg<T>(error: AsError, options: BaseOptions<T>): void { 388 try { 389 options?.callback && options?.callback(error); 390 } catch (err) { 391 this.consoleError(`callback error, code is ${err.code}, message is ${err.message}`); 392 } 393 } 394 395 consoleLog(msg: string): void { 396 if (LOG_ENABLE) { 397 console.log(`${LOG_PREFIX} ${msg}`); 398 } 399 } 400 401 consoleError(msg: string): void { 402 if (LOG_ENABLE) { 403 console.error(`${LOG_PREFIX} ${msg}`); 404 } 405 } 406 407 logOptions<T>(name: string, options: BaseOptions<T>): void { 408 this.consoleLog(`${name} options=${JSON.stringify(options)}`); 409 } 410 411 checkParamRequired<V, T>(paramKey: string, paramValue: V, options: BaseOptions<T>): boolean { 412 if (paramValue === undefined || paramValue === null || paramValue === '') { 413 this.errorWithCodeAndMsg(new AsError(PARAM_REQUIRED_ERROR_CODE, `Param ${paramKey} is required.`), options); 414 return false; 415 } 416 return true; 417 } 418 419 checkNumberParamPositive<T>(paramKey: string, paramValue: number, options: BaseOptions<T>): boolean { 420 if (paramValue <= 0) { 421 this.errorWithCodeAndMsg(new AsError(PARAM_NUMBER_POSITIVE_ERROR_CODE, 422 `Param ${paramKey} must be a positive number.`), options); 423 return false; 424 } 425 return true; 426 } 427 428 checkRequiredFieldInOptions<T>(jsApiConfig: JsApiConfig | undefined, options: BaseOptions<T>): boolean { 429 if (!jsApiConfig) { 430 return false; 431 } 432 if (!jsApiConfig.requiredFieldNames) { 433 return true; 434 } 435 let obj: object = options; 436 for (let i = 0; i < jsApiConfig.requiredFieldNames.length; i++) { 437 let fieldName: string = jsApiConfig.requiredFieldNames[i]; 438 if (!this.checkParamRequired(fieldName, obj[fieldName], options)) { 439 return false; 440 } 441 } 442 return true; 443 } 444 445 checkRouterMode<T>(mode: string | undefined, options: BaseOptions<T>): boolean { 446 if (!mode || mode === 'Single' || mode === 'Standard') { 447 return true; 448 } 449 this.errorWithCodeAndMsg(ROUTER_PARAM_MODE_INVALID_ERROR, options); 450 return false; 451 } 452 453 parseRouterMode(routerMode?: string): router.RouterMode { 454 return routerMode === 'Single' ? router.RouterMode.Single : router.RouterMode.Standard; 455 } 456 457 getRouterIndexByDelta(delta: number): number { 458 let length: number = Number.parseInt(router.getLength()); 459 for (let i = length; i > 0; i--) { 460 let state = router.getStateByIndex(i); 461 if (state?.name && delta-- == 0) { 462 return i; 463 } 464 } 465 return 1; 466 } 467 468 checkBackUrlExists<T>(url: string, options: BaseOptions<T>): boolean { 469 let length: number = Number.parseInt(router.getLength()); 470 for (let i = length; i > 0; i--) { 471 let state = router.getStateByIndex(i); 472 if (state?.name) { 473 let stateUrl: string = state?.path + state?.name; 474 if (stateUrl === url) { 475 return true; 476 } 477 } 478 } 479 this.errorWithCodeAndMsg(BACK_URL_NOT_EXIST_OR_OPENED_ERROR, options); 480 return false; 481 } 482 483 checkNavPathStack<T>(apiName: string, options: BaseOptions<T>): boolean { 484 if (!this.navPathStack) { 485 this.errorWithCodeAndMsg(new AsError(NAV_PATH_STACK_NOT_EXIST_ERROR_CODE, 486 `Current page is not NavDestination, not support ${apiName}().`), options); 487 return false; 488 } 489 return true; 490 } 491 492 getNavPathIndexByDelta(delta: number): number { 493 let pathStack: string[] | undefined = this.navPathStack?.getAllPathName(); 494 if (!pathStack || pathStack.length == 0) { 495 return -1; 496 } 497 return pathStack.length > delta ? (pathStack.length - delta - 1) : -1; 498 } 499 500 onPopHandler(popInfo: PopInfo, onPop?: (event: OnPopEvent) => void): void { 501 if (!popInfo?.info || !onPop) { 502 return; 503 } 504 onPop(new OnPopEvent(popInfo.info.name, popInfo.info.param as object, popInfo.result)); 505 } 506 507 getCurrentNavPathInfo(): NavPathInfo { 508 let navPathStack: Array<string> | undefined = this.navPathStack?.getAllPathName(); 509 let navPathInfo: NavPathInfo = (navPathStack && navPathStack.length > 0) ? 510 new NavPathInfo(navPathStack[navPathStack.length - 1], navPathStack.length - 1) : new NavPathInfo(undefined, -1); 511 if (navPathInfo.index >= 0) { 512 navPathInfo.param = this.navPathStack?.getParamByIndex(navPathInfo.index) as object; 513 } 514 return navPathInfo; 515 } 516 517 notifyMessage(): void { 518 if (this.messageDataList.length <= 0) { 519 return; 520 } 521 try { 522 this.onMessage(new OnMessageEvent(this.messageDataList)); 523 } catch (err) { 524 this.consoleError(`onMessage failed, code is ${err.code}, message is ${err.message}`); 525 } 526 this.messageDataList = []; 527 } 528 529 isJsApiEnable(jsApiConfig?: JsApiConfig): boolean { 530 if (!jsApiConfig) { 531 return false; 532 } 533 if (this.compareVersion(jsApiConfig.minVersion, ATOMIC_SERVICE_JS_SDK_CURRENT_VERSION) && 534 this.compareVersion(ATOMIC_SERVICE_JS_SDK_CURRENT_VERSION, jsApiConfig.maxVersion)) { 535 return true; 536 } 537 return false; 538 } 539 540 compareVersion(lowVersion: string, highVersion: string): boolean { 541 if (!lowVersion || !highVersion) { 542 return false; 543 } 544 let v1 = lowVersion.split('.').map(m => Number.parseInt(m)); 545 let v2 = highVersion.split('.').map(m => Number.parseInt(m)); 546 const maxLength = Math.max(v1.length, v2.length); 547 for (let i = 0; i < maxLength; i++) { 548 if (v1[i] < v2[i]) { 549 return true; 550 } else if (v1[i] > v2[i]) { 551 return false; 552 } 553 } 554 if (v1.length < v2.length) { 555 return true; 556 } 557 if (v1.length > v2.length) { 558 return false; 559 } 560 return true; 561 } 562 563 getUri(uriOrFilePath: string): string { 564 if (!uriOrFilePath || uriOrFilePath.startsWith('file://')) { 565 return uriOrFilePath; 566 } 567 return fileUri.getUriFromPath(uriOrFilePath); 568 } 569 570 async checkUploadFile(options: UploadFileOptions): Promise<CheckUploadFileResult> { 571 if (!options.files || options.files.length <= 0) { 572 this.errorWithCodeAndMsg(UPLOAD_IMAGE_FILES_REQUIRED_ERROR, options); 573 return new CheckUploadFileResult(false); 574 } 575 let uriMap: Map<string, string> = new Map(); 576 for (let i = 0; i < options.files?.length; i++) { 577 let file: UploadFile = options.files[i]; 578 if (!file.uri) { 579 this.errorWithCodeAndMsg(UPLOAD_IMAGE_FILES_URI_REQUIRED_ERROR, options); 580 return new CheckUploadFileResult(false); 581 } 582 if (!file.uri.startsWith('file://') && !fs.accessSync(file.uri, fs.AccessModeType.EXIST)) { 583 this.errorWithCodeAndMsg(new AsError(UPLOAD_IMAGE_FILE_NOT_EXIST_ERROR_CODE, 584 `File uri ${file.uri} is not exist.`), options); 585 return new CheckUploadFileResult(false); 586 } 587 let originUri: string = file.uri; 588 let uploadUri: string = file.uri; 589 if (uploadUri.indexOf(UPLOAD_IMAGE_CACHE_DIR) < 0) { 590 let srcUri: string = uploadUri.startsWith('file://') ? uploadUri : fileUri.getUriFromPath(file.uri); 591 uploadUri = this.context.cacheDir + '/' + uploadUri.substring(uploadUri.lastIndexOf('/') + 1); 592 try { 593 await fs.copy(srcUri, fileUri.getUriFromPath(uploadUri)) 594 } catch (err) { 595 this.errorWithCodeAndMsg(UPLOAD_FILE_ERROR, options); 596 return new CheckUploadFileResult(false); 597 } 598 } 599 file.uri = 'internal://' + uploadUri.substring(uploadUri.indexOf(UPLOAD_IMAGE_CACHE_DIR) + 1); 600 uriMap.set(uploadUri, originUri); 601 } 602 return new CheckUploadFileResult(true, uriMap); 603 } 604 605 convertToRequestData(data?: UploadRequestData[]): request.RequestData[] { 606 let requestData: request.RequestData[] = []; 607 if (data) { 608 data.forEach(item => { 609 if (!item.name || !item.value) { 610 return; 611 } 612 requestData.push({ name: item.name, value: item.value }); 613 }); 614 } 615 return requestData; 616 } 617 618 convertToFile(files?: UploadFile[]): request.File[] { 619 let requestFiles: request.File[] = []; 620 if (files) { 621 files.forEach(item => { 622 requestFiles.push({ 623 filename: item.filename, 624 name: item.name, 625 uri: item.uri, 626 type: item.type 627 }); 628 }); 629 } 630 return requestFiles; 631 } 632 633 handleUploadFileResult(taskStateArray: Array<request.TaskState>, uriMap: Map<string, string>, 634 options: UploadFileOptions): void { 635 let taskStates: UploadFileTaskState[] = []; 636 if (taskStateArray) { 637 taskStateArray.forEach(taskState => { 638 let path: (string | undefined) = taskState.path ? uriMap.get(taskState.path) : taskState.path; 639 taskStates.push(new UploadFileTaskState(path ? path : taskState.path, taskState.responseCode, 640 taskState.message)); 641 }); 642 } 643 this.success(new UploadFileResult(taskStates), options); 644 } 645 646 parseFileNameFromUrl(url?: string): string { 647 if (!url) { 648 return ''; 649 } 650 let http: string = url.split('?')[0]; 651 if (http.indexOf('/') < 0) { 652 return ''; 653 } 654 let index: number = http.lastIndexOf('/'); 655 if (index == (http.length - 1)) { 656 return ''; 657 } 658 return http.substring(index + 1); 659 } 660 661 saveDownloadFile(filePath: string, fileName: string, options: DownloadFileOptions, 662 callback: (uri: string) => void): void { 663 let documentPicker = new filePicker.DocumentViewPicker(); 664 documentPicker.save({ 665 newFileNames: [fileName] 666 }).then(res => { 667 let uri: string = res[0]; 668 fs.copy(fileUri.getUriFromPath(filePath), uri).then(() => { 669 callback && callback(uri); 670 }).catch((err: BusinessError) => { 671 this.error(err, options); 672 }); 673 }).catch((err: BusinessError) => { 674 this.error(err, options); 675 }); 676 } 677 678 checkAccessToken(permissionName: Permissions): Promise<abilityAccessCtrl.GrantStatus> { 679 let bundleInfo: bundleManager.BundleInfo = bundleManager.getBundleInfoForSelfSync( 680 bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_APPLICATION); 681 let tokenId: number = bundleInfo.appInfo.accessTokenId; 682 let atManager: abilityAccessCtrl.AtManager = abilityAccessCtrl.createAtManager(); 683 return atManager.checkAccessToken(tokenId, permissionName); 684 } 685 686 checkPermissions(permissionName: Permissions, grantCallback: (err?: BusinessError) => void): void { 687 this.checkAccessToken(permissionName).then(grantStatus => { 688 if (grantStatus == abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED) { 689 grantCallback(undefined); 690 } else { 691 let atManager: abilityAccessCtrl.AtManager = abilityAccessCtrl.createAtManager(); 692 atManager.requestPermissionsFromUser(this.context, [permissionName]).then(permissionRequestResult => { 693 for (let i = 0; i < permissionRequestResult.authResults.length; i++) { 694 if (permissionRequestResult.authResults[i] != 0) { 695 const error: BusinessError<void> = { 696 name: '', 697 message: `RequestPermissionsFromUser error. authResult: ${permissionRequestResult.authResults[i]}.`, 698 code: PERMISSION_LOCATION_USER_REFUSED_ERROR 699 }; 700 grantCallback(error); 701 return; 702 } 703 } 704 grantCallback(undefined); 705 }).catch((err: BusinessError) => { 706 grantCallback(err); 707 }); 708 } 709 }).catch((err: BusinessError) => { 710 grantCallback(err); 711 }); 712 } 713} 714 715class AtomicServiceApi extends AtomicService { 716 constructor(context: common.UIAbilityContext, navPathStack?: NavPathStack, 717 onMessage?: (event: OnMessageEvent) => void) { 718 super(context, navPathStack, onMessage); 719 } 720 721 pushUrl(options: PushUrlOptions): void { 722 if (!this.checkRouterMode(options.mode, options)) { 723 return; 724 } 725 router.pushUrl({ url: options.url, params: options.params }, this.parseRouterMode(options.mode)).then(() => { 726 this.success(new PushUrlResult(), options); 727 }).catch((err: BusinessError) => { 728 this.error(err, options); 729 }); 730 } 731 732 replaceUrl(options: ReplaceUrlOptions): void { 733 if (!this.checkRouterMode(options.mode, options)) { 734 return; 735 } 736 router.replaceUrl({ url: options.url, params: options.params }, this.parseRouterMode(options.mode)).then(() => { 737 this.success(new ReplaceUrlResult(), options); 738 }).catch((err: BusinessError) => { 739 this.error(err, options); 740 }); 741 } 742 743 backUrl(options: BackUrlOptions): void { 744 if (options.url) { 745 if (!this.checkBackUrlExists(options.url, options)) { 746 return; 747 } 748 router.back({ url: options.url, params: options.params }); 749 this.success(new BackUrlResult(), options); 750 } else if (options.index || options.index === 0) { 751 if (!this.checkNumberParamPositive('index', options.index, options)) { 752 return; 753 } 754 router.back(options.index, options.params); 755 this.success(new BackUrlResult(), options); 756 } else if (options.delta || options.delta === 0) { 757 if (!this.checkNumberParamPositive('delta', options.delta, options)) { 758 return; 759 } 760 router.back(this.getRouterIndexByDelta(options.delta), options.params); 761 this.success(new BackUrlResult(), options); 762 } else { 763 router.back(); 764 this.success(new BackUrlResult(), options); 765 } 766 } 767 768 clearUrl(options: ClearUrlOptions): void { 769 router.clear(); 770 this.success(new ClearUrlResult(), options); 771 } 772 773 pushPath(options: PushPathOptions): void { 774 if (!this.checkNavPathStack('navPathStack.pushPath', options)) { 775 return; 776 } 777 this.navPathStack?.pushPath({ 778 name: options.name, 779 param: options.param, 780 onPop: popInfo => this.onPopHandler(popInfo, options.onPop) 781 }, options.animated); 782 this.success(new PushPathResult(), options); 783 } 784 785 replacePath(options: ReplacePathOptions): void { 786 if (!this.checkNavPathStack('navPathStack.replacePath', options)) { 787 return; 788 } 789 this.navPathStack?.replacePath({ 790 name: options.name, 791 param: options.param, 792 onPop: popInfo => this.onPopHandler(popInfo, options.onPop) 793 }, options.animated); 794 this.success(new ReplacePathResult(), options); 795 } 796 797 popPath(options: PopPathOptions): void { 798 if (!this.checkNavPathStack('navPathStack.pop', options)) { 799 return; 800 } 801 if (options.name) { 802 let index: number | undefined = this.navPathStack?.popToName(options.name, options.result, options.animated); 803 if (index === undefined || index === -1) { 804 this.errorWithCodeAndMsg(POP_PATH_NAME_NOT_EXIST_ERROR, options); 805 return; 806 } 807 } else if (options.index || options.index === 0) { 808 if (options.index < -1) { 809 this.errorWithCodeAndMsg(POP_PATH_PARAM_INDEX_INVALID_ERROR, options); 810 return; 811 } 812 if (options.index > this.getCurrentNavPathInfo().index) { 813 this.errorWithCodeAndMsg(POP_PATH_INDEX_OUT_OF_RANGE_ERROR, options); 814 return; 815 } 816 this.navPathStack?.popToIndex(options.index, options.result, options.animated); 817 } else if (options.delta || options.delta === 0) { 818 if (!this.checkNumberParamPositive('delta', options.delta, options)) { 819 return; 820 } 821 this.navPathStack?.popToIndex(this.getNavPathIndexByDelta(options.delta), options.result, options.animated); 822 } else { 823 this.navPathStack?.pop(options.result, options.animated); 824 } 825 let navPathInfo: NavPathInfo = this.getCurrentNavPathInfo(); 826 this.success(new PopPathResult(navPathInfo.name, navPathInfo.index, navPathInfo.param), options); 827 } 828 829 clearPath(options: ClearPathOptions): void { 830 if (!this.checkNavPathStack('navPathStack.clear', options)) { 831 return; 832 } 833 this.navPathStack?.clear(options.animated); 834 this.success(new ClearPathResult(), options); 835 } 836 837 postMessage(options: PostMessageOptions): void { 838 options.data && this.messageDataList.push(options.data); 839 this.success(new PostMessageResult(), options); 840 } 841 842 getEnv(options: GetEnvOptions): void { 843 let res: GetEnvResult = new GetEnvResult(); 844 res.deviceType = deviceInfo.deviceType; 845 res.brand = deviceInfo.brand; 846 res.productModel = deviceInfo.productModel; 847 res.osFullName = deviceInfo.osFullName; 848 this.success(res, options); 849 } 850 851 checkJsApi(options: CheckJsApiOptions): void { 852 let res: Map<string, boolean> = new Map(); 853 options.jsApiList?.forEach(jsApi => { 854 res[jsApi] = this.isJsApiEnable(ATOMIC_SERVICE_JS_API_MAP.get(jsApi)); 855 }); 856 this.success(new CheckJsApiResult(res), options); 857 } 858 859 pickCamera(options: PickCameraOptions): void { 860 picker.pick(this.context, options.mediaTypes as Array<picker.PickerMediaType>, { 861 cameraPosition: options.cameraPosition, 862 saveUri: options.saveUri, 863 videoDuration: options.videoDuration 864 }).then((pickerResult: picker.PickerResult) => { 865 this.success(new PickCameraResult(pickerResult.resultCode, pickerResult.resultUri, pickerResult.mediaType), 866 options); 867 }).catch((err: BusinessError) => { 868 this.error(err, options); 869 }); 870 } 871 872 selectPhoto(options: SelectPhotoOptions): void { 873 let photoViewPicker = new photoAccessHelper.PhotoViewPicker(); 874 photoViewPicker.select({ 875 MIMEType: options.mimeType as photoAccessHelper.PhotoViewMIMETypes, 876 maxSelectNumber: options.maxSelectNumber, 877 isPhotoTakingSupported: options.isPhotoTakingSupported, 878 isEditSupported: options.isEditSupported, 879 isSearchSupported: options.isSearchSupported, 880 recommendationOptions: { 881 recommendationType: options.recommendationType 882 }, 883 preselectedUris: options.preselectedUris 884 }).then((selectResult: photoAccessHelper.PhotoSelectResult) => { 885 this.success(new SelectPhotoResult(selectResult.photoUris, selectResult.isOriginalPhoto), options); 886 }).catch((err: BusinessError) => { 887 this.error(err, options); 888 }); 889 } 890 891 openPreview(options: OpenPreviewOptions): void { 892 let uri: string = this.getUri(options.uri as string); 893 filePreview.canPreview(this.context, uri).then((res: boolean) => { 894 if (!res) { 895 this.errorWithCodeAndMsg(IMAGE_CAN_NOT_PREVIEW_ERROR, options); 896 return; 897 } 898 filePreview.openPreview(this.context, { 899 uri: uri, 900 mimeType: options.mimeType as string, 901 title: options.title 902 }).then(() => { 903 this.success(new OpenPreviewResult(), options); 904 }).catch((err: BusinessError) => { 905 this.error(err, options); 906 }); 907 }).catch((err: BusinessError) => { 908 this.error(err, options); 909 }); 910 } 911 912 uploadFile(options: UploadFileOptions): void { 913 this.checkUploadFile(options).then(res => { 914 if (!res.checkResult) { 915 return; 916 } 917 let uploadConfig: request.UploadConfig = { 918 url: options.url as string, 919 header: options.header as object, 920 method: options.method as string, 921 files: this.convertToFile(options.files), 922 data: this.convertToRequestData(options.data) 923 }; 924 request.uploadFile(this.context, uploadConfig).then((uploadTask: request.UploadTask) => { 925 uploadTask.on('complete', (taskStateArray: Array<request.TaskState>) => { 926 this.handleUploadFileResult(taskStateArray, res.uriMap as Map<string, string>, options); 927 }); 928 uploadTask.on('fail', (taskStateArray: Array<request.TaskState>) => { 929 this.handleUploadFileResult(taskStateArray, res.uriMap as Map<string, string>, options); 930 }); 931 }).catch((err: BusinessError) => { 932 this.error(err, options); 933 }); 934 }).catch((err: BusinessError) => { 935 this.error(err, options); 936 }); 937 } 938 939 downloadFile(options: DownloadFileOptions): void { 940 let fileName: string = options.fileName ? options.fileName : this.parseFileNameFromUrl(options.url); 941 let cacheFileName: string = `${util.generateRandomUUID().replaceAll('-', '')}`; 942 let filePath: string = `${this.context.cacheDir}/${cacheFileName}`; 943 request.downloadFile(this.context, { 944 url: options.url, 945 header: options.header ? options.header : new Object(), 946 filePath: filePath, 947 enableMetered: options.enableMetered, 948 enableRoaming: options.enableRoaming, 949 networkType: options.networkType, 950 background: false 951 }).then((downloadTask: request.DownloadTask) => { 952 downloadTask.on('complete', () => { 953 this.saveDownloadFile(filePath, fileName, options, uri => { 954 this.success(new DownloadFileResult(uri), options); 955 }); 956 }); 957 downloadTask.on('fail', errCode => { 958 this.errorWithCodeAndMsg(new AsError(errCode, 'File download fail.'), options); 959 }); 960 }).catch((err: BusinessError) => { 961 this.error(err, options); 962 }); 963 } 964 965 getNetworkType(options: GetNetworkTypeOptions): void { 966 connection.getDefaultNet().then(netHandle => { 967 if (!netHandle || netHandle.netId === 0) { 968 this.errorWithCodeAndMsg(NETWORK_NO_ACTIVE_ERROR, options); 969 return; 970 } 971 connection.getNetCapabilities(netHandle).then(netCapabilities => { 972 let res: GetNetworkTypeResult = new GetNetworkTypeResult(netCapabilities.bearerTypes, 973 netCapabilities.networkCap, netCapabilities.linkUpBandwidthKbps, netCapabilities.linkDownBandwidthKbps); 974 this.success(res, options); 975 }).catch((err: BusinessError) => { 976 this.error(err, options); 977 }); 978 }).catch((err: BusinessError) => { 979 this.error(err, options); 980 }); 981 } 982 983 getLocation(options: GetLocationOptions): void { 984 this.checkPermissions(PERMISSION_APPROXIMATELY_LOCATION, err => { 985 if (err) { 986 this.error(err, options); 987 return; 988 } 989 geoLocationManager.getCurrentLocation({ 990 priority: options.priority, 991 scenario: options.scenario, 992 maxAccuracy: options.maxAccuracy, 993 timeoutMs: options.timeoutMs 994 }).then(location => { 995 let res: GetLocationResult = new GetLocationResult(location.latitude, location.longitude, location.altitude, 996 location.accuracy, location.speed, location.timeStamp, location.direction, location.timeSinceBoot, 997 location.additions, location.additionSize); 998 this.success(res, options); 999 }).catch((err: BusinessError) => { 1000 this.error(err, options); 1001 }); 1002 }); 1003 } 1004} 1005 1006class NavPathInfo { 1007 public name: string | undefined; 1008 public index: number; 1009 public param?: object; 1010 1011 constructor(name: string | undefined, index: number) { 1012 this.name = name; 1013 this.index = index; 1014 } 1015} 1016 1017class CheckUploadFileResult { 1018 public checkResult: boolean; 1019 public uriMap?: Map<string, string>; 1020 1021 constructor(checkResult: boolean, uriMap?: Map<string, string>) { 1022 this.checkResult = checkResult; 1023 this.uriMap = uriMap; 1024 } 1025} 1026 1027class BaseOptions<T> { 1028 public callback?: (err: AsError | undefined, res?: T) => void; 1029} 1030 1031class PushUrlOptions extends BaseOptions<PushUrlResult> { 1032 public url?: string; 1033 public params?: object; 1034 public mode?: string; 1035} 1036 1037class PushUrlResult { 1038} 1039 1040class ReplaceUrlOptions extends BaseOptions<ReplaceUrlResult> { 1041 public url?: string; 1042 public params?: object; 1043 public mode?: string; 1044} 1045 1046class ReplaceUrlResult { 1047} 1048 1049class BackUrlOptions extends BaseOptions<BackUrlResult> { 1050 public url?: string; 1051 public index?: number; 1052 public delta?: number; 1053 public params?: object; 1054} 1055 1056class BackUrlResult { 1057} 1058 1059class ClearUrlOptions extends BaseOptions<ClearUrlResult> { 1060} 1061 1062class ClearUrlResult { 1063} 1064 1065class OnPopEvent { 1066 public name?: string; 1067 public param?: object; 1068 public result?: object; 1069 1070 constructor(name?: string, param?: object, result?: object) { 1071 this.name = name; 1072 this.param = param; 1073 this.result = result; 1074 } 1075} 1076 1077class PushPathOptions extends BaseOptions<PushPathResult> { 1078 public name?: string; 1079 public param?: object; 1080 public animated?: boolean; 1081 public onPop?: (event: OnPopEvent) => void; 1082} 1083 1084class PushPathResult { 1085} 1086 1087class ReplacePathOptions extends BaseOptions<ReplacePathResult> { 1088 public name?: string; 1089 public param?: object; 1090 public animated?: boolean; 1091 public onPop?: (event: OnPopEvent) => void; 1092} 1093 1094class ReplacePathResult { 1095} 1096 1097class PopPathOptions extends BaseOptions<PopPathResult> { 1098 public name?: string; 1099 public index?: number; 1100 public delta?: number; 1101 public result?: object; 1102 public animated?: boolean; 1103} 1104 1105class PopPathResult { 1106 public name: string | undefined; 1107 public index: number; 1108 public param?: object; 1109 1110 constructor(name: string | undefined, index: number, param?: object) { 1111 this.name = name; 1112 this.index = index; 1113 this.param = param; 1114 } 1115} 1116 1117class ClearPathOptions extends BaseOptions<ClearPathResult> { 1118 public animated?: boolean; 1119} 1120 1121class ClearPathResult { 1122} 1123 1124class PostMessageOptions extends BaseOptions<PostMessageResult> { 1125 public data?: object; 1126} 1127 1128class PostMessageResult { 1129} 1130 1131export class OnMessageEvent { 1132 public data: object[]; 1133 1134 constructor(data: object[]) { 1135 this.data = data; 1136 } 1137} 1138 1139export class OnErrorReceiveEvent { 1140 public request: WebResourceRequest; 1141 public error: WebResourceError; 1142 1143 constructor(request: WebResourceRequest, error: WebResourceError) { 1144 this.request = request; 1145 this.error = error; 1146 } 1147} 1148 1149export class OnHttpErrorReceiveEvent { 1150 public request: WebResourceRequest; 1151 public response: WebResourceResponse; 1152 1153 constructor(request: WebResourceRequest, response: WebResourceResponse) { 1154 this.request = request; 1155 this.response = response; 1156 } 1157} 1158 1159export class OnPageBeginEvent { 1160 public url: string; 1161 1162 constructor(url: string) { 1163 this.url = url; 1164 } 1165} 1166 1167export class OnPageEndEvent { 1168 public url: string; 1169 1170 constructor(url: string) { 1171 this.url = url; 1172 } 1173} 1174 1175export class WebHeader { 1176 public headerKey: string; 1177 public headerValue: string; 1178 1179 constructor(headerKey: string, headerValue: string) { 1180 this.headerKey = headerKey; 1181 this.headerValue = headerValue; 1182 } 1183} 1184 1185class GetEnvOptions extends BaseOptions<GetEnvResult> { 1186} 1187 1188class GetEnvResult { 1189 public deviceType?: string; 1190 public brand?: string; 1191 public productModel?: string; 1192 public osFullName?: string; 1193} 1194 1195class CheckJsApiOptions extends BaseOptions<CheckJsApiResult> { 1196 public jsApiList?: string[]; 1197} 1198 1199class CheckJsApiResult { 1200 public checkResult?: Map<string, boolean>; 1201 1202 constructor(checkResult?: Map<string, boolean>) { 1203 this.checkResult = checkResult; 1204 } 1205} 1206 1207class PickCameraOptions extends BaseOptions<PickCameraResult> { 1208 public mediaTypes?: string[]; 1209 public cameraPosition?: number; 1210 public saveUri?: string; 1211 public videoDuration?: number; 1212} 1213 1214class PickCameraResult { 1215 public resultCode?: number; 1216 public resultUri?: string; 1217 public mediaType?: string; 1218 1219 constructor(resultCode?: number, resultUri?: string, mediaType?: string) { 1220 this.resultCode = resultCode; 1221 this.resultUri = resultUri; 1222 this.mediaType = mediaType; 1223 } 1224} 1225 1226class SelectPhotoOptions extends BaseOptions<SelectPhotoResult> { 1227 public mimeType?: string; 1228 public maxSelectNumber?: number; 1229 public isPhotoTakingSupported?: boolean; 1230 public isEditSupported?: boolean; 1231 public isSearchSupported?: boolean; 1232 public recommendationType?: number; 1233 public preselectedUris?: string[]; 1234} 1235 1236class SelectPhotoResult { 1237 public photoUris?: string[]; 1238 public isOriginalPhoto?: boolean; 1239 1240 constructor(photoUris?: string[], isOriginalPhoto?: boolean) { 1241 this.photoUris = photoUris; 1242 this.isOriginalPhoto = isOriginalPhoto; 1243 } 1244} 1245 1246class OpenPreviewOptions extends BaseOptions<OpenPreviewResult> { 1247 public title?: string; 1248 public uri?: string; 1249 public mimeType?: string; 1250} 1251 1252class OpenPreviewResult { 1253} 1254 1255class UploadFileOptions extends BaseOptions<UploadFileResult> { 1256 public url?: string; 1257 public header?: object; 1258 public method?: string; 1259 public files?: UploadFile[]; 1260 public data?: UploadRequestData[]; 1261} 1262 1263class UploadFile { 1264 public filename: string; 1265 public name: string; 1266 public uri: string; 1267 public type: string; 1268 1269 constructor(filename: string, name: string, uri: string, type: string) { 1270 this.filename = filename; 1271 this.name = name; 1272 this.uri = uri; 1273 this.type = type; 1274 } 1275} 1276 1277class UploadRequestData { 1278 public name?: string; 1279 public value?: string; 1280} 1281 1282class UploadFileResult { 1283 public taskStates?: UploadFileTaskState[]; 1284 1285 constructor(taskStates?: UploadFileTaskState[]) { 1286 this.taskStates = taskStates; 1287 } 1288} 1289 1290class UploadFileTaskState { 1291 public path?: string; 1292 public responseCode?: number; 1293 public message?: string; 1294 1295 constructor(path?: string, responseCode?: number, message?: string) { 1296 this.path = path; 1297 this.responseCode = responseCode; 1298 this.message = message; 1299 } 1300} 1301 1302class DownloadFileOptions extends BaseOptions<DownloadFileResult> { 1303 public url?: string; 1304 public header?: object; 1305 public fileName?: string; 1306 public enableMetered?: boolean; 1307 public enableRoaming?: boolean; 1308 public networkType?: number; 1309} 1310 1311class DownloadFileResult { 1312 public uri?: string; 1313 1314 constructor(uri?: string) { 1315 this.uri = uri; 1316 } 1317} 1318 1319class GetNetworkTypeOptions extends BaseOptions<GetNetworkTypeResult> { 1320} 1321 1322class GetNetworkTypeResult { 1323 public bearerTypes: number[]; 1324 public networkCap?: number[]; 1325 public linkUpBandwidthKbps?: number; 1326 public linkDownBandwidthKbps?: number; 1327 1328 constructor(bearerTypes: number[], networkCap?: number[], linkUpBandwidthKbps?: number, 1329 linkDownBandwidthKbps?: number) { 1330 this.bearerTypes = bearerTypes; 1331 this.networkCap = networkCap; 1332 this.linkUpBandwidthKbps = linkUpBandwidthKbps; 1333 this.linkDownBandwidthKbps = linkDownBandwidthKbps; 1334 } 1335} 1336 1337class GetLocationOptions extends BaseOptions<GetLocationResult> { 1338 public priority?: number; 1339 public scenario?: number; 1340 public maxAccuracy?: number; 1341 public timeoutMs?: number; 1342} 1343 1344class GetLocationResult { 1345 public latitude: number; 1346 public longitude: number; 1347 public altitude: number; 1348 public accuracy: number; 1349 public speed: number; 1350 public timeStamp: number; 1351 public direction: number; 1352 public timeSinceBoot: number; 1353 public additions?: string[] | undefined; 1354 public additionSize?: number; 1355 1356 constructor(latitude: number, longitude: number, altitude: number, accuracy: number, speed: number, 1357 timeStamp: number, direction: number, timeSinceBoot: number, additions?: string[], additionSize?: number) { 1358 this.latitude = latitude; 1359 this.longitude = longitude; 1360 this.altitude = altitude; 1361 this.accuracy = accuracy; 1362 this.speed = speed; 1363 this.timeStamp = timeStamp; 1364 this.direction = direction; 1365 this.timeSinceBoot = timeSinceBoot; 1366 this.additions = additions; 1367 this.additionSize = additionSize; 1368 } 1369} 1370