Browse Source

fix: AI资源下载

master
高志龙 3 months ago
parent
commit
7c2c4d6747
  1. 4
      app/build.gradle
  2. 93
      app/src/main/java/qianmu/container/activity/H5/H5Data.java
  3. 2
      app/src/main/java/qianmu/container/activity/H5/UpdateFileActivity.java
  4. 8
      app/src/main/java/qianmu/container/http/retrofit/BaseService.java
  5. 235
      app/src/main/java/qianmu/container/util/Base64Util.java
  6. 102
      app/src/main/java/qianmu/container/util/SignUtil.java

4
app/build.gradle

@ -9,10 +9,10 @@ android {
defaultConfig {
applicationId "qianmu.container"
minSdkVersion 22
minSdkVersion 24
targetSdkVersion 30
versionCode 6
versionName "V2.0.8.16"
versionName "V2.0.8.17"
// 2.0.8.2
// 2.0.8.3 mac修改
// V2.0.8.5

93
app/src/main/java/qianmu/container/activity/H5/H5Data.java

@ -1,48 +1,33 @@
package qianmu.container.activity.H5;
import android.content.Context;
import android.os.Build;
import android.util.Log;
import androidx.annotation.RequiresApi;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.google.gson.reflect.TypeToken;
import com.loopj.android.http.AsyncHttpClient;
import com.loopj.android.http.AsyncHttpResponseHandler;
import com.loopj.android.http.RequestParams;
import cz.msebera.android.httpclient.protocol.HTTP;
import com.lzy.okgo.OkGo;
import com.lzy.okgo.callback.FileCallback;
import com.lzy.okgo.callback.StringCallback;
import org.greenrobot.eventbus.EventBus;
import java.io.File;
import java.security.KeyStore;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import cz.msebera.android.httpclient.Header;
import cz.msebera.android.httpclient.entity.StringEntity;
import qianmu.container.app.Constant;
import qianmu.container.app.MyApplication;
import qianmu.container.data.BaseData;
import qianmu.container.data.DeviceData;
import qianmu.container.data.FloorData;
import qianmu.container.entity.AppVersion;
import qianmu.container.entity.DeviceOption;
import qianmu.container.entity.KdxfBean;
import qianmu.container.entity.MessageEvent;
import qianmu.container.entity.ResourceFileBean;
@ -54,6 +39,7 @@ import qianmu.container.util.AesUtil;
import qianmu.container.util.FileUtil;
import qianmu.container.util.GsonUtil;
import qianmu.container.util.LoggerUtil;
import qianmu.container.util.SignUtil;
import qianmu.container.util.StringUtil;
import retrofit2.Call;
import retrofit2.Callback;
@ -64,6 +50,7 @@ public class H5Data extends BaseData {
public static final String NAME = "H5_app_data";
public static final String H5_APP_VERSION = "H5_version";
public static final String INTERFACE_LIST = "interface_list";
public static final String AI_FILE_RESOURCE_LIST = "aiFileResource_list"; //AI资源文件
public static final String FILE_RESOURCE_LIST = "fileResource_list";//资源文件下载,图片,视频
public static final String MODEL_FILE_RESOURCE_LIST = "modelFileResource_list";//模型资源文件下载,图片,(水牌使用)
public static final String H5_URL = "h5_url"; // 除导视外其他url
@ -114,7 +101,28 @@ public class H5Data extends BaseData {
else dataJson = new Gson().toJson(data);
saveDataJson(NAME, MODEL_FILE_RESOURCE_LIST, dataJson);
}
/**
* 保存AI文件资源列表
* */
public static void saveAIResourceFileList(Object data) {
String dataJson;
if (data instanceof String) dataJson = (String) data;
else dataJson = new Gson().toJson(data);
saveDataJson(NAME, AI_FILE_RESOURCE_LIST, dataJson);
}
/**
* 获取模型文件资源列表
* */
public static List<ResourceFileBean> getAiResourceFileList() {
try {
String dataJson = getDataJson(NAME, AI_FILE_RESOURCE_LIST);
return new Gson().fromJson(dataJson, new TypeToken<List<ResourceFileBean>>() {
}.getType());
} catch (Throwable t) {
LoggerUtil.e("getAIResourceFileList", StringUtil.getThrowableStr(t));
}
return new ArrayList<>();
}
/**
* 获取文件资源列表
* */
@ -301,7 +309,48 @@ public class H5Data extends BaseData {
}
});
}
// 获取AI离线文件
public static void queryAiServeList(QueryPreparedListener preparedListener, QueryFinishListener finishListener , Map<String, String> bigFiles ) {
if (preparedListener != null) preparedListener.onPrepared();
String mallCode = FloorData.getMallCode();
long timeStrap = System.currentTimeMillis();
String regKey = DeviceData.getDeviceInfo(DeviceData.HINT_REG_KEY);
Map<String, String> headers = new HashMap<>();
headers.put("projectcode", mallCode);
headers.put("timeStrap", String.valueOf(timeStrap));
headers.put("sign", SignUtil.buildSk(regKey, mallCode, timeStrap));
RetrofitUtil.getBaseService().getAiFileResource(headers,mallCode).enqueue(new Callback<Resp>() {
@Override
public void onResponse(Call<Resp> call, Response<Resp> response) {
try {
if (StringUtil.respSuccess(response.body())) {
String dataJson = new Gson().toJson(response.body().getData());
if(dataJson.contains("capacity")){
Object data = response.body().getData();
saveAIResourceFileList(data);
}else {
//解密
dataJson= AesUtil.decrypt((String.valueOf(response.body().getData())));
saveAIResourceFileList(dataJson);
}
for(ResourceFileBean fileBean:getAiResourceFileList()){
bigFiles.put(fileBean.getUrl(), StringUtil.strSplice("dist", "/static/offline/", fileBean.getUrl()));
}
}
} catch (Throwable t) {
LoggerUtil.e("getAiFileResource", StringUtil.getThrowableStr(t));
}
if (finishListener != null) finishListener.onFinish();
}
@Override
public void onFailure(Call<Resp> call, Throwable t) {
if (finishListener != null) finishListener.onFinish();
}
});
}
//GET方式获取离线数据
public static void queryOfflineDataByInterfaceToGet(Context context, String url, String jsonData, String fileName, Map<String, String> bigFiles , QueryPreparedListener preparedListener, QueryFinishListener finishListener) {
if (preparedListener != null) preparedListener.onPrepared();
@ -472,7 +521,7 @@ public class H5Data extends BaseData {
* @param name 解压后的文件夹名称
* @param localPath 压缩文件路径
*/
private static void UnzipLocalZipFile(String name, String localPath,String version) {
private static void UnzipLocalZipFile(String name, String localPath, String version) {
//源文件夹
File cssDir = new File(StringUtil.strSplice(Constant.CACHE_PATH, name, "/css"));
File jsDir = new File(StringUtil.strSplice(Constant.CACHE_PATH, name, "/js"));

2
app/src/main/java/qianmu/container/activity/H5/UpdateFileActivity.java

@ -186,6 +186,8 @@ public class UpdateFileActivity extends BaseActivity {
H5Data.queryFileResources( () -> totalQueryCount++, () -> queryResponseCount++ ,bigFiles);
getOfflineDataByInterface();
}
//获取AI资源
H5Data.queryAiServeList( () -> totalQueryCount++, () -> queryResponseCount++ ,bigFiles);
if("指路机".equals(deviceType)){
DeviceData.getBackgroundMusic( () -> totalQueryCount++, () -> queryResponseCount++ ,bigFiles);

8
app/src/main/java/qianmu/container/http/retrofit/BaseService.java

@ -2,6 +2,7 @@ package qianmu.container.http.retrofit;
import java.io.File;
import java.util.Map;
import okhttp3.MultipartBody;
import okhttp3.RequestBody;
@ -12,6 +13,7 @@ import retrofit2.http.Body;
import retrofit2.http.Field;
import retrofit2.http.FormUrlEncoded;
import retrofit2.http.GET;
import retrofit2.http.HeaderMap;
import retrofit2.http.Multipart;
import retrofit2.http.POST;
import retrofit2.http.Part;
@ -140,6 +142,12 @@ public interface BaseService {
*/
@GET("api/data/v1/web/getFileResource")
Call<Resp> getFileResourceList2(@Query("projectCode") String code);
/**
* 获取资源文件(AI服务)
* @return
*/
@GET("api/aimgr/v1/web/file-resources")
Call<Resp> getAiFileResource(@HeaderMap Map<String, String> headers, @Query("projectCode") String code);
/**
* 获取模型资源文件列表(水牌)

235
app/src/main/java/qianmu/container/util/Base64Util.java

@ -0,0 +1,235 @@
package qianmu.container.util;
/**
* Created by Android Studio.
* User: gaozhilong
* Date: 2026/3/13
* Time: 16:02
*/
class Base64Util {
static private final int BASELENGTH = 128;
static private final int LOOKUPLENGTH = 64;
static private final int TWENTYFOURBITGROUP = 24;
static private final int EIGHTBIT = 8;
static private final int SIXTEENBIT = 16;
static private final int FOURBYTE = 4;
static private final int SIGN = -128;
static private final char PAD = '=';
static private final boolean fDebug = false;
static final private byte[] base64Alphabet = new byte[BASELENGTH];
static final private char[] lookUpBase64Alphabet = new char[LOOKUPLENGTH];
static {
for (int i = 0; i < BASELENGTH; ++i) {
base64Alphabet[i] = -1;
}
for (int i = 'Z'; i >= 'A'; i--) {
base64Alphabet[i] = (byte) (i - 'A');
}
for (int i = 'z'; i >= 'a'; i--) {
base64Alphabet[i] = (byte) (i - 'a' + 26);
}
for (int i = '9'; i >= '0'; i--) {
base64Alphabet[i] = (byte) (i - '0' + 52);
}
base64Alphabet['+'] = 62;
base64Alphabet['/'] = 63;
for (int i = 0; i <= 25; i++) {
lookUpBase64Alphabet[i] = (char) ('A' + i);
}
for (int i = 26, j = 0; i <= 51; i++, j++) {
lookUpBase64Alphabet[i] = (char) ('a' + j);
}
for (int i = 52, j = 0; i <= 61; i++, j++) {
lookUpBase64Alphabet[i] = (char) ('0' + j);
}
lookUpBase64Alphabet[62] = '+';
lookUpBase64Alphabet[63] = '/';
}
private static boolean isWhiteSpace(char octect) {
return (octect == 0x20 || octect == 0xd || octect == 0xa || octect == 0x9);
}
private static boolean isPad(char octect) {
return (octect == PAD);
}
private static boolean isData(char octect) {
return (octect < BASELENGTH && base64Alphabet[octect] != -1);
}
/**
* 将十六进制八位字节编码为Base64
*
* @param binaryData 包含二进制数据的数组
* @return 编码Base64字符串
*/
public static String encode(byte[] binaryData) {
if (binaryData == null) {
return null;
}
int lengthDataBits = binaryData.length * EIGHTBIT;
if (lengthDataBits == 0) {
return "";
}
int fewerThan24bits = lengthDataBits % TWENTYFOURBITGROUP;
int numberTriplets = lengthDataBits / TWENTYFOURBITGROUP;
int numberQuartet = fewerThan24bits != 0 ? numberTriplets + 1 : numberTriplets;
char[] encodedData = new char[numberQuartet * 4];
byte k = 0, l = 0, b1 = 0, b2 = 0, b3 = 0;
int encodedIndex = 0;
int dataIndex = 0;
if (fDebug) {
System.out.println("number of triplets = " + numberTriplets);
}
for (int i = 0; i < numberTriplets; i++) {
b1 = binaryData[dataIndex++];
b2 = binaryData[dataIndex++];
b3 = binaryData[dataIndex++];
if (fDebug) {
System.out.println("b1= " + b1 + ", b2= " + b2 + ", b3= " + b3);
}
l = (byte) (b2 & 0x0f);
k = (byte) (b1 & 0x03);
byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0);
byte val2 = ((b2 & SIGN) == 0) ? (byte) (b2 >> 4) : (byte) ((b2) >> 4 ^ 0xf0);
byte val3 = ((b3 & SIGN) == 0) ? (byte) (b3 >> 6) : (byte) ((b3) >> 6 ^ 0xfc);
if (fDebug) {
System.out.println("val2 = " + val2);
System.out.println("k4 = " + (k << 4));
System.out.println("vak = " + (val2 | (k << 4)));
}
encodedData[encodedIndex++] = lookUpBase64Alphabet[val1];
encodedData[encodedIndex++] = lookUpBase64Alphabet[val2 | (k << 4)];
encodedData[encodedIndex++] = lookUpBase64Alphabet[(l << 2) | val3];
encodedData[encodedIndex++] = lookUpBase64Alphabet[b3 & 0x3f];
}
if (fewerThan24bits == EIGHTBIT) {
b1 = binaryData[dataIndex];
k = (byte) (b1 & 0x03);
if (fDebug) {
System.out.println("b1=" + b1);
System.out.println("b1<<2 = " + (b1 >> 2));
}
byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0);
encodedData[encodedIndex++] = lookUpBase64Alphabet[val1];
encodedData[encodedIndex++] = lookUpBase64Alphabet[k << 4];
encodedData[encodedIndex++] = PAD;
encodedData[encodedIndex++] = PAD;
} else if (fewerThan24bits == SIXTEENBIT) {
b1 = binaryData[dataIndex];
b2 = binaryData[dataIndex + 1];
l = (byte) (b2 & 0x0f);
k = (byte) (b1 & 0x03);
byte val1 = ((b1 & SIGN) == 0) ? (byte) (b1 >> 2) : (byte) ((b1) >> 2 ^ 0xc0);
byte val2 = ((b2 & SIGN) == 0) ? (byte) (b2 >> 4) : (byte) ((b2) >> 4 ^ 0xf0);
encodedData[encodedIndex++] = lookUpBase64Alphabet[val1];
encodedData[encodedIndex++] = lookUpBase64Alphabet[val2 | (k << 4)];
encodedData[encodedIndex++] = lookUpBase64Alphabet[l << 2];
encodedData[encodedIndex++] = PAD;
}
return new String(encodedData);
}
/**
* 将Base64数据解码为八位字节
*
* @param encoded 包含Base64数据的字符串
* @return 包含解码数据的数组.
*/
public static byte[] decode(String encoded) {
if (encoded == null) {
return null;
}
char[] base64Data = encoded.toCharArray();
//删除空白
int len = removeWhiteSpace(base64Data);
//应该可以被四整除
if (len % FOURBYTE != 0) {
return null;
}
int numberQuadruple = (len / FOURBYTE);
if (numberQuadruple == 0) {
return new byte[0];
}
byte b1 = 0, b2 = 0, b3 = 0, b4 = 0;
char d1 = 0, d2 = 0, d3 = 0, d4 = 0;
int i = 0;
int encodedIndex = 0;
int dataIndex = 0;
byte[] decodedData = new byte[(numberQuadruple) * 3];
for (; i < numberQuadruple - 1; i++) {
if (!isData((d1 = base64Data[dataIndex++])) || !isData((d2 = base64Data[dataIndex++]))
|| !isData((d3 = base64Data[dataIndex++]))
|| !isData((d4 = base64Data[dataIndex++]))) {
return null;
}//没有数据直接返回null
b1 = base64Alphabet[d1];
b2 = base64Alphabet[d2];
b3 = base64Alphabet[d3];
b4 = base64Alphabet[d4];
decodedData[encodedIndex++] = (byte) (b1 << 2 | b2 >> 4);
decodedData[encodedIndex++] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf));
decodedData[encodedIndex++] = (byte) (b3 << 6 | b4);
}
if (!isData((d1 = base64Data[dataIndex++])) || !isData((d2 = base64Data[dataIndex++]))) {
return null;////没有数据直接返回null
}
b1 = base64Alphabet[d1];
b2 = base64Alphabet[d2];
d3 = base64Data[dataIndex++];
d4 = base64Data[dataIndex++];
if (!isData((d3)) || !isData((d4))) {
//检查是否为填充字符
if (isPad(d3) && isPad(d4)) {
if ((b2 & 0xf) != 0) {//最后四位应为0
return null;
}
byte[] tmp = new byte[i * 3 + 1];
System.arraycopy(decodedData, 0, tmp, 0, i * 3);
tmp[encodedIndex] = (byte) (b1 << 2 | b2 >> 4);
return tmp;
} else if (!isPad(d3) && isPad(d4)) {
b3 = base64Alphabet[d3];
if ((b3 & 0x3) != 0){//最后2位应为零
return null;
}
byte[] tmp = new byte[i * 3 + 2];
System.arraycopy(decodedData, 0, tmp, 0, i * 3);
tmp[encodedIndex++] = (byte) (b1 << 2 | b2 >> 4);
tmp[encodedIndex] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf));
return tmp;
} else {
return null;
}
} else { //No PAD e.g 3cQl
b3 = base64Alphabet[d3];
b4 = base64Alphabet[d4];
decodedData[encodedIndex++] = (byte) (b1 << 2 | b2 >> 4);
decodedData[encodedIndex++] = (byte) (((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf));
decodedData[encodedIndex++] = (byte) (b3 << 6 | b4);
}
return decodedData;
}
/**
* 从包含编码Base64数据的MIME中删除空白
*
* @param data base64数据的字节数组带空白
* @return 新的长度
*/
private static int removeWhiteSpace(char[] data) {
if (data == null) {
return 0;
}
int newSize = 0;
int len = data.length;
for (int i = 0; i < len; i++) {
if (!isWhiteSpace(data[i])) {
data[newSize++] = data[i];
}
}
return newSize;
}
}

102
app/src/main/java/qianmu/container/util/SignUtil.java

@ -0,0 +1,102 @@
package qianmu.container.util;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Locale;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import cn.hutool.core.util.ObjectUtil;
import qianmu.container.data.DeviceData;
import qianmu.container.data.FloorData;
/**
* Created by Android Studio.
* User: gaozhilong
* Date: 2026/3/13
* Time: 16:00
*/
public class SignUtil {
private static final String SignAlgorithm = "HmacSHA256";
public static Boolean checkSign(String activationCode, String projectCode, Long timeStrap, String sk) {
if (ObjectUtil.isEmpty(sk)) {
return false;
}
long thisTime = System.currentTimeMillis();
// 大于/小于5min则过期
long diff = thisTime - timeStrap;
if (diff < -1000 * 60 * 5 || diff > 1000 * 60 * 5) {
return false;
}
String anObject = buildSk(activationCode, projectCode, timeStrap);
boolean equals = sk.equals(anObject);
return equals;
}
/**
* activationCode+timeStrap作为ak,projectCode为盐值
*/
public static String buildSk(String activationCode, String projectCode, Long timeStrap) {
String ak = activationCode + timeStrap;
return sign(projectCode, ak);
}
/**
* 生成签名 哈希后转16进制再sha256加密,最后转base64
* @param secret 秘钥
* @param content 待签名内容
*/
public static String sign(String secret, String content) {
try {
Mac mac = Mac.getInstance(SignAlgorithm);
mac.init(new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), SignAlgorithm));
String data = toHex(hash(content));
byte[] signByte = mac.doFinal(data.getBytes(StandardCharsets.UTF_8));
return Base64Util.encode(toHex(signByte).getBytes());
} catch (NoSuchAlgorithmException | InvalidKeyException ex) {
return null;
}
}
public static byte[] hash(String text) {
try {
MessageDigest md = MessageDigest.getInstance("SHA-256");
md.update(text.getBytes(StandardCharsets.UTF_8));
return md.digest();
} catch (NoSuchAlgorithmException ex) {
return null;
}
}
public static String toHex(byte[] data) {
StringBuilder sb = new StringBuilder(data.length * 2);
for (byte b : data) {
String hex = Integer.toHexString(b);
if (hex.length() == 1) {
sb.append("0");
} else if (hex.length() == 8) {
hex = hex.substring(6);
}
sb.append(hex);
}
return sb.toString().toLowerCase(Locale.getDefault());
}
public static void main(String[] args) {
long timeStrap = System.currentTimeMillis();
String regKey = DeviceData.getDeviceInfo(DeviceData.HINT_REG_KEY);
String mallCode = FloorData.getMallCode();
System.out.println(SignUtil.buildSk(regKey, mallCode, timeStrap));
System.out.println(timeStrap);
}
}
Loading…
Cancel
Save