import { RequestOptions, RequestSuccess, RequestTask, UploadFileOptions, UploadFile, UploadTask, OnProgressUpdateResult, UploadFileSuccess, UploadFileProgressUpdateCallback, DownloadFile, DownloadTask, DownloadFileOptions, OnProgressDownloadResult, DownloadFileProgressUpdateCallback, DownloadFileSuccess } from '../interface' import { RequestFailImpl, UploadFileFailImpl, DownloadFileFailImpl, getErrcode } from '../unierror'; import { NetworkManager, NetworkRequestListener, NetworkUploadFileListener, NetworkDownloadFileListener } from './network/NetworkManager.uts' import Pattern from 'java.util.regex.Pattern'; import Locale from 'java.util.Locale'; import TextUtils from 'android.text.TextUtils'; import { StatusCode } from './network/StatusCode.uts' import Key from 'kotlinx.coroutines.CoroutineExceptionHandler.Key'; import JSONObject from 'com.alibaba.fastjson.JSONObject'; import ArrayList from 'java.util.ArrayList'; import ProgressListener from 'android.os.RecoverySystem.ProgressListener'; import Handler from 'android.os.Handler'; import Looper from 'android.os.Looper'; import Class from 'java.lang.Class'; import Type from 'java.lang.reflect.Type'; let charsetPattern = Pattern.compile('charset=([a-z0-9-]+)') class RunnableTask extends Runnable { private callback : () => void | null; private looper : Looper | null = null; constructor(looper : Looper | null, callback : () => void) { super(); this.looper = looper; this.callback = callback } override run() { this.callback?.() } public execute() { if (this.looper == null) { this.run(); } else { new Handler(this.looper!!).post(this); } } } class RequestNetworkListener extends NetworkRequestListener { private param : RequestOptions | null = null; private headers : UTSJSONObject = {}; private looper : Looper | null = null; private type : Type | null = null; private clzName : string | null = null; constructor(param : RequestOptions, type : Type, clzName : string) { super(); this.param = param; this.type = type; this.clzName = clzName; this.looper = Looper.myLooper(); } override onStart() : void { } override onHeadersReceived(statusCode : number, headers : MutableMap>) : void { let simpleHeaders = {}; if (headers != null) { let it = headers.iterator(); while (it.hasNext()) { let entry = it.next(); let key = entry.key; let value = entry.value; let tmpKey = '_'; if (key == null) { key = tmpKey; } if (value.size == 0) { continue; } else if (value.size == 1) { simpleHeaders[key] = value.get(0); } else { simpleHeaders[key] = value.toString(); } } } this.headers = simpleHeaders; } override onProgress(progress : number) : void { } override onComplete(option : UTSJSONObject) : void { let kParam = this.param; let result = {}; if (kParam != null) { if (option == null || '-1' == option['statusCode']) { if (this.headers != null) { result['header'] = this.headers; } let exception = option['cause']! as Exception; let cause = exception.cause.toString(); let errMsg = option['errorMsg']! as string; let errCode = (option['errorCode']! as string).toInt(); if (errMsg.indexOf("timeout") != -1) { errCode = 5; errMsg = "time out"; } else if (cause.contains("Connection refused")) { errCode = 1000; errMsg = "server system error"; } else if (cause.contains("Network is unreachable")) { errCode = 600003; errMsg = "network interrupted error"; } else if (cause.contains("invalid URL")) { errCode = 600009; errMsg = "invalid URL"; } else { errCode = 602001; errMsg = "request system error"; } let failResult = new RequestFailImpl(getErrcode(errCode)); failResult.cause = new SourceError(cause); new RunnableTask(this.looper, () => { if (kParam != null) { let fail = kParam.fail; if (fail != null) { fail(failResult); } let complete = kParam.complete; if (complete != null) { complete(failResult); } } }).execute(); } else { result['statusCode'] = option['statusCode']; if (option['originalData'] == null) { if ("java.lang.Object".equals(this.clzName, true)) { let errMsg = option['errorMsg']; if (errMsg != null) { let errMsgJson = JSON.parse((option['errorMsg']! as string)); if (errMsgJson != null) { result['data'] = errMsgJson; } else { result['data'] = errMsg; } } else { result['data'] = "error"; } } else { let errMsg = option['errorMsg']; if (errMsg != null) { let errMsgJson = JSON.parse(errMsg as string, this.type); if (errMsgJson != null) { result['data'] = errMsgJson; } else { let failResult = new RequestFailImpl(getErrcode(100002)); new RunnableTask(this.looper, () => { if (kParam != null) { let fail = kParam.fail; if (fail != null) { fail(failResult); } let complete = kParam.complete; if (complete != null) { complete(failResult); } } }).execute(); return; } } } } else { let charset = ""; let headers = this.headers.toJSONObject() as JSONObject; if (headers != null) { for (key in headers.keys) { if (key.equals("Content-Type", true)) { charset = headers[key] as string; } } } let strData = this.readAsString(option['originalData'] as ByteArray, charset); let type = kParam.responseType != null ? kParam.responseType : kParam.dataType; if (type == null) { type = charset; } if (kParam.method == "HEAD") { type = ""; } const data = this.parseData(strData, type); if (data == null) { let failResult = new RequestFailImpl(getErrcode(100001)); new RunnableTask(this.looper, () => { if (kParam != null) { let fail = kParam.fail; if (fail != null) { fail(failResult); } let complete = kParam.complete; if (complete != null) { complete(failResult); } } }).execute(); return; } result['data'] = data; } result['statusText'] = StatusCode.getStatus(option['statusCode'] as string); if (this.headers != null) { result['header'] = this.headers; } let tmp : RequestSuccess = { data: result['data'] as T, statusCode: (result['statusCode'] as string).toInt(), header: result['header']!, cookies: this.parseCookie(this.headers) }; new RunnableTask(this.looper, () => { if (kParam != null) { let success = kParam.success; if (success != null) { success(tmp); } let complete = kParam.complete; if (complete != null) { complete(tmp); } } }).execute(); } } } private readAsString(byteArray : ByteArray, type : string) : string { let charsetType = "utf-8"; if (type != null) { let matcher = charsetPattern.matcher(type.toLowerCase(Locale.ENGLISH)); if (matcher.find()) { charsetType = matcher.group(1); } } try { return new String(byteArray, charset(charsetType)); } catch (e : Exception) { return new String(byteArray); } } private parseData(data : string, type : string) : any | null { if ("java.lang.Object".equals(this.clzName, true)) { if (type.indexOf("json") != -1) { return JSON.parse(data); } else if (type == 'jsonp') { if (TextUtils.isEmpty(data)) { return {}; } let start = data.indexOf('(') + 1; let end = data.indexOf(')'); if (start == 0 || start >= end) { return {}; } let tmp = data.substring(start, end); return JSON.parse(tmp); } else { return data; } } else { return JSON.parse(data, this.type); } } private parseCookie(header : UTSJSONObject | null) : string[] { if (header == null) { return [] } let cookiesStr = header.getString('Set-Cookie') if (cookiesStr == null) { cookiesStr = header.getString('set-cookie') } if (cookiesStr == null) { return [] } let cookiesArr = new Array() if (cookiesStr.charAt(0) == "[" && cookiesStr.charAt(cookiesStr.length - 1) == "]") { cookiesStr = cookiesStr.slice(1, -1) } const handleCookiesArr = cookiesStr.split(';') for (let i = 0; i < handleCookiesArr.length; i++) { if (handleCookiesArr[i].indexOf('Expires=') != -1 || handleCookiesArr[i].indexOf('expires=') != -1) { cookiesArr.push(handleCookiesArr[i].replace(',', '')) } else { cookiesArr.push(handleCookiesArr[i]) } } cookiesArr = cookiesArr.join(';').split(',') return cookiesArr } } class UploadNetworkListener implements NetworkUploadFileListener { private param : UploadFileOptions | null = null; public progressListeners = new ArrayList(); private looper : Looper | null = null; constructor(param : UploadFileOptions) { this.param = param; this.looper = Looper.myLooper(); } onProgress(progressUpdate : OnProgressUpdateResult) { if (this.progressListeners.size != 0) { new RunnableTask(this.looper, () => { for (let i : Int = 0; i < this.progressListeners.size; i++) { let listener = this.progressListeners.get(i); listener(progressUpdate); } }).execute(); } } onComplete(option : UTSJSONObject) { let kParam = this.param; if (kParam != null) { const errorMsg = option["errorMsg"]; let errCode = (option['statusCode']! as string).toInt(); if (errorMsg != null) { let failResult = new UploadFileFailImpl(getErrcode(errCode)); let cause = option['cause']; if (cause != null) { failResult.cause = cause as SourceError; } new RunnableTask(this.looper, () => { if (kParam != null) { let fail = kParam.fail; if (fail != null) { fail(failResult); } let complete = kParam.complete; if (complete != null) { complete(failResult); } } }).execute(); } else { let kData = option["data"]; let data = ""; if (kData != null) { data = kData as string; } let successResult : UploadFileSuccess = { data: data, statusCode: errCode } new RunnableTask(this.looper, () => { if (kParam != null) { let success = kParam.success; if (success != null) { success(successResult); } let complete = kParam.complete; if (complete != null) { complete(successResult); } } }).execute(); } } } } class DownloadNetworkListener implements NetworkDownloadFileListener { private param : DownloadFileOptions | null = null; public progressListeners = new ArrayList(); private looper : Looper | null = null; constructor(param : DownloadFileOptions) { this.param = param; this.looper = Looper.myLooper(); } onProgress(progressUpdate : OnProgressDownloadResult) { if (this.progressListeners.size != 0) { new RunnableTask(this.looper, () => { for (let i : Int = 0; i < this.progressListeners.size; i++) { let listener = this.progressListeners.get(i); listener(progressUpdate); } }).execute(); } } onComplete(option : UTSJSONObject) { let kParam = this.param; if (kParam != null) { let statusCode = (option['statusCode']! as string).toInt(); let errMsg = option['errorMsg']; if (errMsg != null) { let errCode = (option['errorCode']! as string).toInt(); let failResult = new DownloadFileFailImpl(getErrcode(errCode)); failResult.errMsg = errMsg as string; let cause = option['cause']; if (cause != null) { failResult.cause = cause as SourceError; } new RunnableTask(this.looper, () => { if (kParam != null) { let fail = kParam.fail; if (fail != null) { fail(failResult); } let complete = kParam.complete; if (complete != null) { complete(failResult); } } }).execute(); } else { let kTempFilePath = option["tempFilePath"]; let tempFilePath = ""; if (kTempFilePath != null) { tempFilePath = kTempFilePath as string; } let successResult : DownloadFileSuccess = { tempFilePath: tempFilePath, statusCode: statusCode } new RunnableTask(this.looper, () => { if (kParam != null) { let success = kParam.success; if (success != null) { success(successResult); } let complete = kParam.complete; if (complete != null) { complete(successResult); } } }).execute(); } } } } @UTSAndroid.keyword("inline") @UTSAndroid.keyword('reified') export function request(options : RequestOptions) : RequestTask { const type = UTSAndroid.getGenericType(); const clzName = UTSAndroid.getGenericClassName(); return NetworkManager.getInstance().request(options, new RequestNetworkListener(options, type, clzName)); } export const uploadFile : UploadFile = (options : UploadFileOptions) : UploadTask => { return NetworkManager.getInstance().uploadFile(options, new UploadNetworkListener(options)); } export const downloadFile : DownloadFile = (options : DownloadFileOptions) : DownloadTask => { return NetworkManager.getInstance().downloadFile(options, new DownloadNetworkListener(options)); }