Browse Source

fix: 视频更新

dejiAi
高志龙 7 months ago
parent
commit
438d7c214a
  1. 2
      app/build.gradle
  2. 19
      app/src/main/java/qianmu/container/activity/H5/H5Data.java
  3. 31
      app/src/main/java/qianmu/container/activity/H5/WebViewActivity.java
  4. 4
      app/src/main/java/qianmu/container/http/retrofit/BaseService.java
  5. 328
      app/src/main/java/qianmu/container/http/retrofit/BaseService1.java
  6. 235
      app/src/main/java/qianmu/container/util/Base64Util.java
  7. 103
      app/src/main/java/qianmu/container/util/SignUtil.java
  8. BIN
      app/src/main/res/raw/bg1.mp4
  9. BIN
      app/src/main/res/raw/musice.mp3

2
app/build.gradle

@ -10,7 +10,7 @@ android {
minSdkVersion 22 minSdkVersion 22
targetSdkVersion 30 targetSdkVersion 30
versionCode 6 versionCode 6
versionName "V2.0.7.43"
versionName "V2.0.7.44"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
consumerProguardFiles 'consumer-rules.pro' consumerProguardFiles 'consumer-rules.pro'

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

@ -18,6 +18,7 @@ import org.greenrobot.eventbus.EventBus;
import java.io.File; import java.io.File;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -44,6 +45,7 @@ import qianmu.container.util.AesUtil;
import qianmu.container.util.FileUtil; import qianmu.container.util.FileUtil;
import qianmu.container.util.GsonUtil; import qianmu.container.util.GsonUtil;
import qianmu.container.util.LoggerUtil; import qianmu.container.util.LoggerUtil;
import qianmu.container.util.SignUtil;
import qianmu.container.util.StringUtil; import qianmu.container.util.StringUtil;
import retrofit2.Call; import retrofit2.Call;
import retrofit2.Callback; import retrofit2.Callback;
@ -321,7 +323,14 @@ public class H5Data extends BaseData {
public static void queryAiServeList(QueryPreparedListener preparedListener, QueryFinishListener finishListener , Map<String, String> bigFiles ) { public static void queryAiServeList(QueryPreparedListener preparedListener, QueryFinishListener finishListener , Map<String, String> bigFiles ) {
if (preparedListener != null) preparedListener.onPrepared(); if (preparedListener != null) preparedListener.onPrepared();
String mallCode = FloorData.getMallCode(); String mallCode = FloorData.getMallCode();
RetrofitUtil.getBaseService().getAiFileResource(mallCode).enqueue(new Callback<Resp>() {
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 @Override
public void onResponse(Call<Resp> call, Response<Resp> response) { public void onResponse(Call<Resp> call, Response<Resp> response) {
try { try {
@ -375,15 +384,15 @@ public class H5Data extends BaseData {
String deviceCode = DeviceData.getDeviceInfo(DeviceData.HINT_DEVICE_CODE); String deviceCode = DeviceData.getDeviceInfo(DeviceData.HINT_DEVICE_CODE);
url = url +"&"+"code="+deviceCode; url = url +"&"+"code="+deviceCode;
} }
if(!url.startsWith("http")){
url = StringUtil.strSplice(Constant.getBaseAddress(), url);
}
String finalUrl = url; String finalUrl = url;
client.get(url, new AsyncHttpResponseHandler() {
client.get(finalUrl, new AsyncHttpResponseHandler() {
@Override @Override
public void onSuccess(int statusCode, Header[] headers, byte[] responseBody) { public void onSuccess(int statusCode, Header[] headers, byte[] responseBody) {
try { try {
String response = new String(responseBody).replace("\\\\", "/"); String response = new String(responseBody).replace("\\\\", "/");
// Log.d("url", finalUrl);
// Log.d("response", response); // Log.d("response", response);
//获取数据成功时 才解析解析素材url并更新离线文件 //获取数据成功时 才解析解析素材url并更新离线文件
Resp resp = new Gson().fromJson(response, new TypeToken<Resp>() {}.getType()); Resp resp = new Gson().fromJson(response, new TypeToken<Resp>() {}.getType());

31
app/src/main/java/qianmu/container/activity/H5/WebViewActivity.java

@ -8,6 +8,7 @@ import android.content.Intent;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.media.MediaMetadataRetriever; import android.media.MediaMetadataRetriever;
import android.media.MediaPlayer; import android.media.MediaPlayer;
import android.media.SoundPool;
import android.net.Uri; import android.net.Uri;
import android.os.Build; import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
@ -111,6 +112,8 @@ public class WebViewActivity extends BaseActivity {
private VideoView currentVideo; private VideoView currentVideo;
private SocketClient localSocketClient; private SocketClient localSocketClient;
private Map<String, Uri> videoMap = new HashMap<>(); private Map<String, Uri> videoMap = new HashMap<>();
private SoundPool soundPool;
private int soundId;
// 语音听写对象 // 语音听写对象
private ASR mAsr; private ASR mAsr;
private Handler mainHandler; private Handler mainHandler;
@ -202,6 +205,10 @@ public class WebViewActivity extends BaseActivity {
Uri videoUri7 = Uri.parse("android.resource://" + getPackageName() + "/" + R.raw.speek1); Uri videoUri7 = Uri.parse("android.resource://" + getPackageName() + "/" + R.raw.speek1);
videoMap.put("hat_speek1", videoUri7); videoMap.put("hat_speek1", videoUri7);
soundPool = new SoundPool.Builder()
.setMaxStreams(2)
.build();
loadingbgVideo(); loadingbgVideo();
currentVideo = binding.bgVideo; currentVideo = binding.bgVideo;
loadingHelloVideo(); loadingHelloVideo();
@ -221,6 +228,24 @@ public class WebViewActivity extends BaseActivity {
ttsUtil.initTts(); ttsUtil.initTts();
} }
} }
private void playMusic(){
soundId = soundPool.load(this, R.raw.musice, 1);
soundPool.setOnLoadCompleteListener(new SoundPool.OnLoadCompleteListener() {
@Override
public void onLoadComplete(SoundPool soundPool, int sampleId, int status) {
if (status == 0) {
soundPool.play(sampleId, 0.2f, 0.2f, 1, -1, 1.0f);
}
}
});
}
private void stopMusic(){
soundPool.stop(soundId);
soundPool.unload(soundId);
}
private void loadingbgVideo(){ private void loadingbgVideo(){
binding.bgVideo.setVideoURI(videoMap.get("hat_bg")); binding.bgVideo.setVideoURI(videoMap.get("hat_bg"));
binding.bgVideo.setOnPreparedListener(new MediaPlayer.OnPreparedListener() { binding.bgVideo.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
@ -306,7 +331,10 @@ public class WebViewActivity extends BaseActivity {
return true; // 返回true表示已处理,不会弹出系统默认提示 return true; // 返回true表示已处理,不会弹出系统默认提示
} }
}); });
binding.backVideo.setOnCompletionListener( mp -> changeVideo("bg"));
binding.backVideo.setOnCompletionListener( mp -> {
changeVideo("bg");
stopMusic();
});
} }
//彩蛋背景视频 //彩蛋背景视频
private void loadingbgVideo1(){ private void loadingbgVideo1(){
@ -369,6 +397,7 @@ public class WebViewActivity extends BaseActivity {
changeVideo("back"); changeVideo("back");
}else{ //新状态彩蛋 }else{ //新状态彩蛋
changeVideo("to"); changeVideo("to");
playMusic();
} }
} }

4
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.io.File;
import java.util.Map;
import okhttp3.MultipartBody; import okhttp3.MultipartBody;
import okhttp3.RequestBody; import okhttp3.RequestBody;
@ -12,6 +13,7 @@ import retrofit2.http.Body;
import retrofit2.http.Field; import retrofit2.http.Field;
import retrofit2.http.FormUrlEncoded; import retrofit2.http.FormUrlEncoded;
import retrofit2.http.GET; import retrofit2.http.GET;
import retrofit2.http.HeaderMap;
import retrofit2.http.Multipart; import retrofit2.http.Multipart;
import retrofit2.http.POST; import retrofit2.http.POST;
import retrofit2.http.Part; import retrofit2.http.Part;
@ -146,7 +148,7 @@ public interface BaseService {
* @return * @return
*/ */
@GET("api/aimgr/v1/web/file-resources") @GET("api/aimgr/v1/web/file-resources")
Call<Resp> getAiFileResource(@Query("projectCode") String code);
Call<Resp> getAiFileResource(@HeaderMap Map<String, String> headers, @Query("projectCode") String code);
/** /**
* 获取模型资源文件列表(水牌) * 获取模型资源文件列表(水牌)

328
app/src/main/java/qianmu/container/http/retrofit/BaseService1.java

@ -0,0 +1,328 @@
package qianmu.container.http.retrofit;
import java.io.File;
import java.util.Map;
import okhttp3.MultipartBody;
import okhttp3.RequestBody;
import qianmu.container.entity.Resp;
import qianmu.container.entity.RespJXB;
import retrofit2.Call;
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;
import retrofit2.http.Query;
/**
* 接口服务
* Created by Administrator on 2017/11/6.
*/
public interface BaseService {
/**
* 获取应用列表
*
* @return
*/
@POST("/API/Dev/GetDevAppList")
Call<Resp> getDevAppList(@Body RequestBody route);
/**
* 获取主设备ip
*
* @return
*/
@POST("/Api/Dev/GetSyncDev")
Call<Resp> getSyncDev(@Body RequestBody route);
/**
* 新版容器接口
* */
/**
* 获取注册设备信息
*
* @return
*/
@GET("api/device/v1/client/register")
Call<Resp> getRegisterInfo( @Query("code") String code );
/**
* 激活设备
*
* @return
*/
@POST("api/device/v1/client/active")
Call<Resp> activeDevice(@Body RequestBody route);
/**
* 上传截屏
*
* @return
*/
@POST("api/device/v1/client/screenShort")
Call<Resp> upScreenShort(@Body RequestBody route);
/**
* 获取Mqtt密码
*
* @return
*/
@POST("api/device/v1/client/token")
Call<Resp> getTokenPass(@Body RequestBody route);
/**
* 上传日志文件
*
* @return
*/
@POST("api/device/v1/client/log")
Call<Resp> upLogFile(@Body RequestBody body);
/**
* 获取节目列表
*
* @return
*/
@GET("api/msg/v1/web/getScheduleByMachineCode")
Call<Resp> getprogram( @Query("code") String code );
// /**
// * 获取图片视频下载ip地址
// *
// * @return
// */
// @GET("api/info/v1/web/getUploadUrl")
// Call<Resp> getUploadUrl();
/**
* 获取图片视频下载ip地址
*
* @return
*/
@GET("api/data/v1/web/getUploadUrl")
Call<Resp> getUploadUrl();
/**
* 获取滚动字幕
*
* @return
*/
@GET("api/info/v1/web/getSubtitlesByDeviceCode")
Call<Resp> getSubtitles(@Query("code") String code);
/**
* 获取离线接口列表
*
* @return
*/
@GET("api/device/v1/deviceWeb/getInterfaceApi")
Call<Resp> getInterfaceApi(@Query("projectCode") String code);
/**
* 获取资源文件列表
*
* @return
*/
@GET("api/guide/v1/web/getFileResource")
Call<Resp> getFileResourceList(@Query("projectCode") String code);
/**
* 获取资源文件列表(新版)
*
* @return
*/
@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);
/**
* 获取模型资源文件列表(水牌)
*
* @return
*/
@GET("api/guidance/v1/web/getModelFileResource")
Call<Resp> getModelFileResource(@Query("projectCode") String code);
/**
* 获取设备信息
*
* @return
*/
@GET("api/device/v1/client/getMachine")
Call<Resp> getMachine(@Query("code") String code);
/**
* 上传版本信息
*
* @return
*/
@POST("api/device/v1/client/version")
Call<Resp> sendVersion(@Body RequestBody body);
/**
* 获取设备屏保信息默认图跳转时间等
*
* @return
*/
@GET("api/device/v1/client/getProtection")
Call<Resp> getProtection (@Query("projectCode") String projectCode, @Query("code") String code);
/**
* 节目上传记录
*
* @return
*/
@POST("api/analysis/v1/web/deviceUseClickDataUpload")
Call<Resp> programUpload(@Body RequestBody body);
/**
* 获取同屏组主设备
*
* @return
*/
@GET("api/device/v1/client/getPrimaryMachine")
Call<Resp> getPrimaryMachine ( @Query("code") String code);
/**
* 检测网络
*
* @return
*/
@GET("api/mqtt/v1/client/ping")
Call<Resp> pingNet ( @Query("machineCode") String code);
/**
* 获取app版本及应用版本号设备启动时调用
*
* @return
*/
@GET("api/device/v1/deviceWeb/getMachineAppProgramMsg")
Call<Resp> getMachineAppProgramMsg ( @Query("code") String code);
/**
* 定时开关机时间
*
* @return
*/
@GET("api/device/v1/deviceWeb/getMachineOnOffTime")
Call<Resp> getMachineOnOffTime ( @Query("code") String code);
/**
* 天气
*
* @return
*/
@GET("api/data/v1/web/getWeather")
Call<Resp> getWeather(@Query("city") String cityName);
/**
* 获取服务器时间戳
*
* @return
*/
@GET("api/data/v1/util/getCurrentTimestamp")
Call<Resp> getCurrentTimestamp();
/**
* 进入导视数据上报
*
* @return
*/
@POST("api/analysis/v1/web/guideClickDataUpload")
Call<Resp> guideClickDataUpload(@Body RequestBody body);
/**
* 获取播放背景音乐列表流花中心
*
* @return
*/
@GET("api/device/v1/deviceWeb/getSoundInfo")
Call<Resp> getSoundInfo(@Query("projectCode") String projectCode);
/**
* 机械臂重启
*
* @return
*/
@FormUrlEncoded
@POST("/device/reboot")
Call<Resp> jxbReboot(@Field("deviceKey") String deviceKey);
/**
* 机械臂定时开关机
*
* @return
*/
@FormUrlEncoded
@POST("device/setAutoBoot")
Call<Resp> jxbAutoBoot(@Field("deviceKey") String deviceKey,@Field("autoPowerOn") String autoPowerOn,@Field("autoPowerOff") String autoPowerOff);
/**
* 机械臂过渡文字内容
*
* @return
*/
@FormUrlEncoded
@POST("device/setTransText")
Call<Resp> jxbSetTransText(@Field("deviceKey") String deviceKey,@Field("transText") String transText);
/**
* 机械臂开机文字
*
* @return
*/
@FormUrlEncoded
@POST("device/setBootText")
Call<Resp> jxbSetBootText(@Field("deviceKey") String deviceKey,@Field("bootText") String bootText);
/**
* 设置机械臂逻辑零点
*
* @return
*/
@FormUrlEncoded
@POST("device/setLogicZero")
Call<Resp> setLogicZero(@Field("deviceKey") String deviceKey,@Field("logicZero") int logicZero);
/**
* 控制机械臂
*
* @return
*/
@FormUrlEncoded
@POST("program/play")
Call<Resp> jxbPlay(@Field("deviceKey") String deviceKey, @Field("progMode") int progMode, @Field("progText1") String progText1, @Field("progTextColor") int progTextColor,
@Field("progText2") String progText2, @Field("progText2Color") int progText2Color, @Field("progDist") String progDist, @Field("progDistColor") int progDistColor,
@Field("progAngel") int progAngel, @Field("progArmNo") int progArmNo, @Field("progRota") int progRota , @Field("progTrans") int progTrans,
@Field("progDuration") int progDuration , @Field("progMd5") String progMd5, @Field("progStat") int progStat);
/**
* 机械臂开机关机
*
* 0=关机1=开机
* @return
*/
@FormUrlEncoded
@POST("device/turnOnOff")
Call<Resp> jxbTurnOnOff(@Field("deviceKey") String deviceKey,@Field("onOff") int onOff);
}

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

@ -0,0 +1,235 @@
package qianmu.container.util;
/**
* Created by Android Studio.
* User: linzhibin
* Date: 2025/11/26
* Time: 19:22
*/
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;
}
}

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

@ -0,0 +1,103 @@
package qianmu.container.util;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;
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: linzhibin
* Date: 2025/11/26
* Time: 19:16
*/
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);
}
}

BIN
app/src/main/res/raw/bg1.mp4

Binary file not shown.

BIN
app/src/main/res/raw/musice.mp3

Binary file not shown.
Loading…
Cancel
Save