Files
im/uni_modules/network-manage/utssdk/app-android/index.uts
T
2025-12-27 07:08:30 +08:00

495 lines
14 KiB
Plaintext

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';
import Gson from "io.dcloud.uts.gson.Gson";
import ByteBuffer from 'java.nio.ByteBuffer';
import { NetworkUtil } from './network/utils/NetworkUtil.uts';
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<T> extends NetworkRequestListener {
private param : RequestOptions<T> | null = null;
private headers : UTSJSONObject = {};
private looper : Looper | null = null;
private type : Type | null = null;
private clzName : string | null = null;
constructor(param : RequestOptions<T>, 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<String, MutableList<String>>) : 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;
const originalErrMsg = option['errorMsg']! as string;
let cause = exception.cause.toString();
if(exception.cause == null) {
cause = originalErrMsg
}
let errMsg = originalErrMsg
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;
} else if (cause.contains("Network is unreachable")) {
errCode = 600003;
} else if (cause.contains("invalid URL")) {
errCode = 600009;
}
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 if("java.lang.String".equals(this.clzName, true)){
result['data'] = option['errorMsg'] ?? "error";
} else if("io.dcloud.uts.ArrayBuffer".equals(this.clzName, true)){
let textDecoder : TextEncoder = new TextEncoder()
let error :string= (option['errorMsg'] ?? "error") as string
let uint8Array = textDecoder.encode(error)
result['data'] = uint8Array.buffer
}else {
let errMsg = option['errorMsg'];
if (errMsg != null) {
let errMsgJson = JSON.parse<T>(errMsg as string, this.type);
if (errMsgJson != null) {
result['data'] = errMsgJson;
} else {
let failResult = new RequestFailImpl(getErrcode(100002));
failResult.data = errMsg
failResult.cause = new SourceError("Error message parsing failed")
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 data :any | null;
if("io.dcloud.uts.ArrayBuffer".equals(this.clzName, true)){
let by= option['originalData'] as ByteArray
data =ArrayBuffer.fromByteBuffer(ByteBuffer.wrap(by))
}else{
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 = "";
}
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<T> = {
data: result['data'] as T,
statusCode: (result['statusCode'] as string).toInt(),
header: result['header']!,
cookies: NetworkUtil.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.lowercase(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 {
let jsonData: any | null = null;
try{
const gson = new Gson();
jsonData = gson.fromJson<any>(data, this.type);
}catch(e){
}
return jsonData ?? data;
}
} else if ("java.lang.String".equals(this.clzName, true)){
return data;
} else {
return JSON.parse<T>(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<string>()
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<UploadFileProgressUpdateCallback>();
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"];
if (errorMsg != null) {
let errCode = (option['errorCode']! as string).toInt();
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 statusCode = (option['statusCode']! as string).toInt();
let successResult : UploadFileSuccess = {
data: data,
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();
}
}
}
}
class DownloadNetworkListener implements NetworkDownloadFileListener {
private param : DownloadFileOptions | null = null;
public progressListeners = new ArrayList<DownloadFileProgressUpdateCallback>();
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 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 statusCode = (option['statusCode']! as string).toInt();
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<T>(options : RequestOptions<T>) : RequestTask {
const type = UTSAndroid.getGenericType<T>();
const clzName = UTSAndroid.getGenericClassName<T>();
return NetworkManager.getInstance().request<T>(options, new RequestNetworkListener<T>(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));
}