1、添加事件上报
2、Uni小程序资源压缩解压优化
This commit is contained in:
shenzuqiang 2026-03-11 09:28:42 +08:00
parent c97842e9ab
commit 5f0a7b3a22
16 changed files with 588 additions and 61 deletions

View File

@ -110,12 +110,14 @@ android {
buildTypes { buildTypes {
getByName("debug") { getByName("debug") {
isMinifyEnabled = false isMinifyEnabled = false
isShrinkResources = false
signingConfig = signingConfigs.getByName("config") signingConfig = signingConfigs.getByName("config")
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
} }
getByName("release") { getByName("release") {
// 建议开启混淆以减少启动耗时和体积 // 建议开启混淆以减少启动耗时和体积
isMinifyEnabled = false isMinifyEnabled = true
isShrinkResources = false isShrinkResources = true
signingConfig = signingConfigs.getByName("config") signingConfig = signingConfigs.getByName("config")
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro") proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
} }
@ -326,6 +328,7 @@ dependencies {
implementation(libs.android.cn.oaid) //获取手机设备id implementation(libs.android.cn.oaid) //获取手机设备id
implementation(libs.fastaes) //解密 implementation(libs.fastaes) //解密
implementation(libs.accompanist.permissions) implementation(libs.accompanist.permissions)
// implementation(libs.zip4j)
//Uni小程序相关依赖 //Uni小程序相关依赖
implementation (files("libs/sqlite-release.aar")) //sqlite数据库 implementation (files("libs/sqlite-release.aar")) //sqlite数据库

383
app/proguard-rules.pro vendored
View File

@ -20,6 +20,386 @@
# hide the original source file name. # hide the original source file name.
#-renamesourcefileattribute SourceFile #-renamesourcefileattribute SourceFile
-dontwarn
-optimizationpasses 5
-dontusemixedcaseclassnames
-dontskipnonpubliclibraryclasses
-dontskipnonpubliclibraryclassmembers
-dontpreverify
-verbose
#-optimizations !code/simplification/arithmetic,!field/*,!class/merging/*
-dontoptimize
-keep public class * extends android.app.Activity
-keep public class * extends android.app.Application
-keep public class * extends android.app.Service
-keep public class * extends android.content.BroadcastReceiver
-keep public class * extends android.content.ContentProvider
-keep public class * extends android.app.backup.BackupAgentHelper
-keep public class * extends android.preference.Preference
-keep public class * extends io.dcloud.common.DHInterface.IPlugin
-keep public class * extends io.dcloud.common.DHInterface.IFeature
-keep public class * extends io.dcloud.common.DHInterface.IBoot
-keep public class * extends io.dcloud.common.DHInterface.IReflectAble
-keep class io.dcloud.** {*;}
-dontwarn io.dcloud.**
-keep class vi.com.gdi.** {*;}
-keep class android.support.v4.** {*;}
-keepclasseswithmembers class io.dcloud.appstream.StreamAppManager {
public protected <methods>;
}
-keep public class * extends io.dcloud.common.DHInterface.IReflectAble{
public protected <methods>;
public protected *;
}
-keep class **.R
-keep class **.R$* {
public static <fields>;
}
-keep public class * extends io.dcloud.common.DHInterface.IJsInterface{
public protected <methods>;
public protected *;
}
-keepclasseswithmembers class io.dcloud.EntryProxy {
<methods>;
}
-keep class * implements android.os.IInterface {
<methods>;
}
-keepclasseswithmembers class *{
public static java.lang.String getJsContent();
}
-keepclasseswithmembers class *{
public static io.dcloud.share.AbsWebviewClient getWebviewClient(io.dcloud.share.ShareAuthorizeView);
}
-keepattributes Exceptions,InnerClasses,Signature,Deprecated, SourceFile,LineNumberTable,*Annotation*,EnclosingMethod
-keepclasseswithmembers class * {
public <init>(android.content.Context, android.util.AttributeSet);
}
-keepclasseswithmembers class * {
public <init>(android.content.Context, android.util.AttributeSet, int);
}
-keep public class * extends android.app.Application{
public static <methods>;
public *;
}
-keepclassmembers class * extends android.app.Activity {
public void *(android.view.View);
public static <methods>;
}
-keepclassmembers enum * {
public static **[] values();
public static ** valueOf(java.lang.String);
}
-keep class * implements android.os.Parcelable {
public static final android.os.Parcelable$Creator *;
}
-keep class dc.** {*;}
-keep class okio.**{*;}
-keep class org.apache.** {*;}
-keep class org.json.** {*;}
-keep class net.ossrs.** {*;}
-keep class android.** {*;}
-keep class com.facebook.**{*;}
-keep class com.bumptech.glide.**{*;}
-keep class com.alibaba.fastjson.**{*;}
-keep class com.sina.**{*;}
-keep class com.weibo.ssosdk.**{*;}
-keep class com.asus.**{*;}
-keep class com.bun.**{*;}
-keep class com.heytap.**{*;}
-keep class com.huawei.**{*;}
-keep class com.netease.**{*;}
-keep class com.meizu.**{*;}
-keep class com.samsung.**{*;}
-keep class com.zui.**{*;}
-keep class com.amap.**{*;}
-keep class com.autonavi.**{*;}
-keep class pl.droidsonroids.gif.**{*;}
-keep class com.tencent.**{*;}
-keep class com.baidu.**{*;}
-keep class com.iflytek.**{*;}
-keep class com.umeng.**{*;}
-keep class tv.**{*;}
-keep class master.**{*;}
-keep class uk.co.**{*;}
-keep class com.dmcbig.**{*;}
-keep class org.mozilla.**{*;}
-keep class androidtranscoder.**{*;}
-keep class XI.**{*;}
-dontwarn android.**
-dontwarn com.tencent.**
-keep class * implements com.taobao.weex.IWXObject{*;}
-keep public class * extends com.taobao.weex.common.WXModule{*;}
-keepattributes Signature
-dontwarn org.codehaus.mojo.**
-dontwarn org.apache.commons.**
-dontwarn com.amap.**
-dontwarn com.sina.weibo.sdk.**
-dontwarn com.alipay.**
-dontwarn com.lucan.ajtools.**
-dontwarn pl.droidsonroids.gif.**
-keep class com.taobao.weex.** { *; }
-keep class com.taobao.gcanvas.**{*;}
-dontwarn com.taobao.weex.**
-dontwarn com.taobao.gcanvas.**
#个推
-dontwarn com.igexin.**
-keep class com.igexin.** { *; }
-keep class org.json.** { *; }
-dontwarn com.getui.**
-keep class com.getui.** { *; }
-keep class android.support.v4.app.NotificationCompat { *; }
-keep class android.support.v4.app.NotificationCompat$Builder { *; }
#魅族
-keep class com.meizu.** { *; }
-dontwarn com.meizu.**
#小米
-keep class com.xiaomi.** { *; }
-dontwarn com.xiaomi.push.**
-keep class org.apache.thrift.** { *; }
#华为
-dontwarn com.huawei.hms.**
-keep class com.huawei.hms.** { *; }
-keep class com.huawei.android.** { *; }
-dontwarn com.huawei.android.**
-keep class com.hianalytics.android.** { *; }
-dontwarn com.hianalytics.android.**
-keep class com.huawei.updatesdk.** { *; }
-dontwarn com.huawei.updatesdk.**
#OPPO
-keep class com.coloros.mcssdk.** { *; }
-dontwarn com.coloros.mcssdk.**
#360聚合广告核心包
-keep class com.ak.** {*;}
-dontwarn com.ak.**
-keep class android.support.v4.** {
public *;
}
#广点通SDK
-keep class com.qq.e.** {
public protected *;
}
-keep class android.support.v7.**{
public *;
}
#穿山甲SDK
-keep class com.bytedance.sdk.openadsdk.** { *; }
-dontwarn com.bytedance.sdk.openadsdk.**
-keep class com.androidquery.callback.** {*;}
-keep public interface com.bytedance.sdk.openadsdk.downloadnew.** {*;}
-keep class com.ss.sys.ces.* {*;}
#快手
-keep class org.chromium.** {*;}
-keep class org.chromium.** {*;}
-keep class aegon.chrome.** {*;}
-keep class com.kwai.**{*;}
-keep class com.kwad.**{*;}
-keepclasseswithmembernames class * {
native <methods>;
}
-dontwarn com.kwai.**
-dontwarn com.kwad.**
-dontwarn com.ksad.**
-dontwarn aegon.chrome.**
#一键登录
-dontwarn com.g.gysdk.**
-keep class com.g.gysdk.**{*;}
-dontwarn com.g.elogin.**
-keep class com.g.elogin.**{*;}
-dontwarn com.cmic.sso.sdk.**
-keep class com.cmic.sso.sdk.** {*;}
-dontwarn com.geetest.onelogin.**
-keep class com.geetest.onelogin.** {*;}
-dontwarn com.geetest.onepassv2.**
-keep class com.geetest.onepassv2.** {*;}
-dontwarn com.unicom.xiaowo.login.**
-keep class com.unicom.xiaowo.login.** {*;}
-dontwarn cn.com.chinatelecom.account.api.**
-keep class cn.com.chinatelecom.account.api.** {*;}
#腾讯X5--------------start-----------------------
-dontwarn dalvik.**
-dontwarn com.tencent.smtt.**
#-overloadaggressively
# ------------------ Keep LineNumbers and properties ---------------- #
-keepattributes Exceptions,InnerClasses,Signature,Deprecated,SourceFile,LineNumberTable,*Annotation*,EnclosingMethod
# --------------------------------------------------------------------------
# Addidional for x5.sdk classes for apps
-keep class com.tencent.smtt.export.external.**{*;}
-keep class com.tencent.tbs.video.interfaces.IUserStateChangedListener {*;}
-keep class com.tencent.smtt.sdk.CacheManager {public *;}
-keep class com.tencent.smtt.sdk.CookieManager {public *;}
-keep class com.tencent.smtt.sdk.WebHistoryItem {public *;}
-keep class com.tencent.smtt.sdk.WebViewDatabase {public *;}
-keep class com.tencent.smtt.sdk.WebBackForwardList {public *;}
-keep public class com.tencent.smtt.sdk.WebView {public <fields>;public <methods>;}
-keep public class com.tencent.smtt.sdk.WebView$HitTestResult {public static final <fields>;public java.lang.String getExtra();public int getType();}
-keep public class com.tencent.smtt.sdk.WebView$WebViewTransport {public <methods>;}
-keep public class com.tencent.smtt.sdk.WebView$PictureListener {public <fields>;public <methods>;}
-keepattributes InnerClasses
-keep public enum com.tencent.smtt.sdk.WebSettings$** {*;}
-keep public enum com.tencent.smtt.sdk.QbSdk$** {*;}
-keep public class com.tencent.smtt.sdk.WebSettings {public *;}
-keepattributes Signature
-keep public class com.tencent.smtt.sdk.ValueCallback {public <fields>;public <methods>;}
-keep public class com.tencent.smtt.sdk.WebViewClient {public <fields>;public <methods>;}
-keep public class com.tencent.smtt.sdk.DownloadListener {public <fields>;public <methods>;}
-keep public class com.tencent.smtt.sdk.WebChromeClient {public <fields>;public <methods>;}
-keep public class com.tencent.smtt.sdk.WebChromeClient$FileChooserParams {public <fields>;public <methods>;}
-keep class com.tencent.smtt.sdk.SystemWebChromeClient{public *;}
# 1. extension interfaces should be apparent
-keep public class com.tencent.smtt.export.external.extension.interfaces.* {public protected *;}
# 2. interfaces should be apparent
-keep public class com.tencent.smtt.export.external.interfaces.* {public protected *;}
-keep public class com.tencent.smtt.sdk.WebViewCallbackClient {public protected *;}
-keep public class com.tencent.smtt.sdk.WebStorage$QuotaUpdater {public <fields>;public <methods>;}
-keep public class com.tencent.smtt.sdk.WebIconDatabase {public <fields>;public <methods>;}
-keep public class com.tencent.smtt.sdk.WebStorage {public <fields>;public <methods>;}
-keep public class com.tencent.smtt.sdk.DownloadListener {public <fields>;public <methods>;}
-keep public class com.tencent.smtt.sdk.QbSdk {public <fields>;public <methods>;}
-keep public class com.tencent.smtt.sdk.QbSdk$PreInitCallback {public <fields>;public <methods>;}
-keep public class com.tencent.smtt.sdk.CookieSyncManager {public <fields>;public <methods>;}
-keep public class com.tencent.smtt.sdk.Tbs* {public <fields>;public <methods>;}
-keep public class com.tencent.smtt.utils.LogFileUtils {public <fields>;public <methods>;}
-keep public class com.tencent.smtt.utils.TbsLog {public <fields>;public <methods>;}
-keep public class com.tencent.smtt.utils.TbsLogClient {public <fields>;public <methods>;}
-keep public class com.tencent.smtt.sdk.CookieSyncManager {public <fields>;public <methods>;}
# Added for game demos
-keep public class com.tencent.smtt.sdk.TBSGamePlayer {public <fields>;public <methods>;}
-keep public class com.tencent.smtt.sdk.TBSGamePlayerClient* {public <fields>;public <methods>;}
-keep public class com.tencent.smtt.sdk.TBSGamePlayerClientExtension {public <fields>;public <methods>;}
-keep public class com.tencent.smtt.sdk.TBSGamePlayerService* {public <fields>;public <methods>;}
-keep public class com.tencent.smtt.utils.Apn {public <fields>;public <methods>;}
-keep class com.tencent.smtt.** {*;}
# end
-keep public class com.tencent.smtt.export.external.extension.proxy.ProxyWebViewClientExtension {public <fields>;public <methods>;}
-keep class MTT.ThirdAppInfoNew {*;}
-keep class com.tencent.mtt.MttTraceEvent {*;}
# Game related
-keep public class com.tencent.smtt.gamesdk.* {public protected *;}
-keep public class com.tencent.smtt.sdk.TBSGameBooter {public <fields>;public <methods>;}
-keep public class com.tencent.smtt.sdk.TBSGameBaseActivity {public protected *;}
-keep public class com.tencent.smtt.sdk.TBSGameBaseActivityProxy {public protected *;}
-keep public class com.tencent.smtt.gamesdk.internal.TBSGameServiceClient {public *;}
#腾讯X5--------------end-----------------------
# 代码混淆压缩比,在 0~7 之间,默认为 5,一般不做修改
-optimizationpasses 5
# 混合时不使用大小写混合,混合后的类名为小写
-dontusemixedcaseclassnames
# 指定不去忽略非公共库的类
-dontskipnonpubliclibraryclasses
# 这句话能够使我们在查看崩溃报告时能对应到明文的代码
-keepattributes SourceFile,LineNumberTable
# 保持泛型
-keepattributes Signature
# 保持注解
-keepattributes *Annotation*
# 关键正式环境移除日志打印 (需配合 build.gradle isMinifyEnabled true 使用)
-assumenosideeffects class android.util.Log {
public static int v(...);
public static int d(...);
public static int i(...);
public static int w(...);
public static int e(...);
}
# --- Retrofit & OkHttp ---
-keepattributes Signature, InnerClasses
-keep class retrofit2.** { *; }
-keep class okhttp3.** { *; }
-dontwarn retrofit2.**
-dontwarn okhttp3.**
-keep class com.squareup.okhttp3.** { *; }
-dontwarn com.squareup.okhttp3.**
-dontwarn okio.**
# --- Gson (必须保持用于解析的 Bean 类不被混淆) ---
-keep class com.google.gson.** { *; }
# 假设你的数据模型包名是 com.img.rabbit.model请务必根据实际修改
-keep class com.img.rabbit.model.** { *; }
# --- Glide & Coil ---
-keep public class * implements com.bumptech.glide.module.GlideModule
-keep class com.bumptech.glide.** { *; }
-keep class io.coilkt.** { *; }
# --- MMKV ---
-keep class com.tencent.mmkv.** { *; }
# --- 友盟 (Umeng) ---
-keep class com.umeng.** { *; }
-keep class com.uc.** { *; }
-dontwarn com.umeng.**
-dontwarn com.uc.**
# --- 个推 (Getui) ---
-keep class com.getui.** { *; }
-dontwarn com.getui.**
# --- 微信 SDK ---
-keep class com.tencent.mm.opensdk.** { *; }
-keep class com.tencent.wxop.** { *; }
-keep class com.tencent.mm.sdk.** { *; }
# --- MLKit (人脸检测/分割) ---
-keep class com.google.mlkit.** { *; }
-keep class com.google.android.gms.** { *; }
# --- Uni小程序 & Fresco & FastJson ---
-keep class com.alibaba.fastjson.** { *; }
-keep class com.facebook.fresco.** { *; }
-keep class com.facebook.imagepipeline.** { *; }
-keep class com.facebook.animated.gif.** { *; }
-keep class pl.droidsonroids.gif.** { *; }
# Uni小程序特定类保持
-keep class io.dcloud.** { *; }
# --- Android_CN_OAID ---
-keep class com.github.gzu_liyujiang.oaid.** { *; }
# 友盟号码认证 # 友盟号码认证
-keep class com.umeng.**{*;} -keep class com.umeng.**{*;}
@ -32,3 +412,6 @@
public static ** valueOf(java.lang.String); public static ** valueOf(java.lang.String);
} }
# 友盟号码认证 结束 # 友盟号码认证 结束
-keep class com.img.rabbit.** { *; }
-keep class com.img.rabbit.data.bean.** { *; }

View File

@ -50,7 +50,7 @@ class BaseApplication : Application() {
} }
override fun attachBaseContext(base: android.content.Context?) { override fun attachBaseContext(base: android.content.Context?) {
// MultiDex.install(base) //MultiDex.install(base)
super.attachBaseContext(base) super.attachBaseContext(base)
} }

View File

@ -81,6 +81,7 @@ import com.img.rabbit.utils.UpdateUtils
import com.img.rabbit.utils.UrlLinkUtils.openAgreement import com.img.rabbit.utils.UrlLinkUtils.openAgreement
import com.img.rabbit.viewmodel.GeneralViewModel import com.img.rabbit.viewmodel.GeneralViewModel
import com.img.rabbit.viewmodel.LoginViewModel import com.img.rabbit.viewmodel.LoginViewModel
import com.img.rabbit.viewmodel.ReportViewModel
import com.img.rabbit.viewmodel.SplashViewModel import com.img.rabbit.viewmodel.SplashViewModel
import com.umeng.analytics.MobclickAgent import com.umeng.analytics.MobclickAgent
import com.umeng.commonsdk.UMConfigure import com.umeng.commonsdk.UMConfigure
@ -108,6 +109,7 @@ class MainActivity : ComponentActivity(), LoadingCallback {
val context = LocalContext.current val context = LocalContext.current
val loginViewModel: LoginViewModel = viewModel() val loginViewModel: LoginViewModel = viewModel()
val splashViewModel: SplashViewModel = viewModel() val splashViewModel: SplashViewModel = viewModel()
val reportViewModel:ReportViewModel = viewModel()
generalViewModel = ViewModelProvider(this, ViewModelProvider.AndroidViewModelFactory.getInstance(application))[GeneralViewModel::class.java] generalViewModel = ViewModelProvider(this, ViewModelProvider.AndroidViewModelFactory.getInstance(application))[GeneralViewModel::class.java]
var updateAppNotify by mutableStateOf(GlobalStateManager(context).globalUpdateNotifyFlow().collectAsState(initial = false)) var updateAppNotify by mutableStateOf(GlobalStateManager(context).globalUpdateNotifyFlow().collectAsState(initial = false))
@ -230,29 +232,36 @@ class MainActivity : ComponentActivity(), LoadingCallback {
UniMpUpdate.DOWNLOAD_START -> { UniMpUpdate.DOWNLOAD_START -> {
//资源开始下载 //资源开始下载
progressWGTToPageState.floatValue = 0f progressWGTToPageState.floatValue = 0f
Log.i("HomeScreen","DOWNLOAD_START") Log.i("HomeScreen", "DOWNLOAD_START")
} }
UniMpUpdate.DOWNLOAD_FINISH -> { UniMpUpdate.DOWNLOAD_FINISH -> {
//资源下载完成 //资源下载完成
progressWGTToPageState.floatValue = 1f progressWGTToPageState.floatValue = 1f
Log.i("HomeScreen","DOWNLOAD_FINISH") Log.i("HomeScreen", "DOWNLOAD_FINISH")
coroutineScope.launch { coroutineScope.launch {
GlobalStateManager(context).storeGlobalUniDownloadNotify(false) GlobalStateManager(context).storeGlobalUniDownloadNotify(
false
)
} }
} }
UniMpUpdate.DOWNLOAD_FAIL -> { UniMpUpdate.DOWNLOAD_FAIL -> {
//资源下载失败 //资源下载失败
progressWGTToPageState.floatValue = -1f progressWGTToPageState.floatValue = -1f
Log.i("HomeScreen","DOWNLOAD_FAIL") Log.i("HomeScreen", "DOWNLOAD_FAIL")
coroutineScope.launch { coroutineScope.launch {
GlobalStateManager(context).storeGlobalUniDownloadNotify(false) GlobalStateManager(context).storeGlobalUniDownloadNotify(
false
)
} }
} }
else -> { else -> {
//资源下载进度 //资源下载进度
if(progress != null){ if (progress != null) {
progressWGTToPageState.floatValue = progress progressWGTToPageState.floatValue = progress
Log.i("HomeScreen","DOWNLOAD_PROGRESS:$progress") Log.i("HomeScreen", "DOWNLOAD_PROGRESS:$progress")
} }
} }
@ -261,10 +270,17 @@ class MainActivity : ComponentActivity(), LoadingCallback {
onRelease = { onRelease = {
//资源下载完成后,启动小程序到指定位置 //资源下载完成后,启动小程序到指定位置
val uniMpEntity = UniAppUtils.currentDownloadUniMp val uniMpEntity = UniAppUtils.currentDownloadUniMp
if(uniMpEntity!=null){ if (uniMpEntity != null) {
UniAppUtils.startUniMpPage(context = context, uniMpId = uniMpEntity.unimp_id, uniMpType = uniMpEntity.unimp_type, pagePath = UniAppUtils.currentUniMpJumpPatch?:"") UniAppUtils.startUniMpPage(
context = context,
uniMpId = uniMpEntity.unimp_id,
uniMpType = uniMpEntity.unimp_type,
pagePath = UniAppUtils.currentUniMpJumpPatch ?: "",
reportViewModel = reportViewModel
)
} }
} },
reportViewModel = reportViewModel
) )
TipsUniMpToPageDialog( TipsUniMpToPageDialog(
title = "下载资源", title = "下载资源",

View File

@ -0,0 +1,22 @@
package com.img.rabbit.bean.request
import kotlinx.serialization.Serializable
@Serializable
data class ReportRequest(
private val source: String = "android",
private val type: String,//事件类型:click error
private val key: String,
private val value: String,
private val extra: String? = null
)
object ReportType {
const val CLICK = "click"
const val ERROR = "error"
}
object ReportKey {
const val EVENT_CLIENT_UNI_RELEASE_WGT: String = "client.uni.release.wgt" //释放资源
}

View File

@ -7,12 +7,12 @@ import kotlinx.serialization.Serializable
class ConfigEntity { class ConfigEntity {
@SerializedName("client.version.upgrade") //版本更新 @SerializedName("client.version.upgrade") //版本更新
var versionEntity: VersionEntity? = null var versionEntity: VersionEntity? = null
@SerializedName("client.uni.version.upgrade") //小程序模拟器 @SerializedName("client.uni.version.upgrade") //小程序模拟器
var uniVersionEntity: List<UniVersionEntity>? = emptyList() var uniVersionEntity: List<UniVersionEntity>? = emptyList()
@SerializedName("client.icon.uni.home") //uni首页icon @SerializedName("client.icon.uni.home") //uni首页icon
var homeIconEntity: List<UniIconEntity>? = emptyList() var homeIconEntity: List<UniIconEntity>? = emptyList()
@SerializedName("client.wgt.password") //WGT解压密码
var wgtPassword: String? = null
@SerializedName("client.popup.display") //显示开关控制 @SerializedName("client.popup.display") //显示开关控制

View File

@ -9,7 +9,6 @@ import android.util.Log
import android.view.LayoutInflater import android.view.LayoutInflater
import android.widget.CheckBox import android.widget.CheckBox
import android.widget.TextView import android.widget.TextView
import android.widget.Toast
import androidx.compose.foundation.Image import androidx.compose.foundation.Image
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.border import androidx.compose.foundation.border
@ -38,7 +37,6 @@ import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
@ -88,6 +86,7 @@ import org.json.JSONObject
@SuppressLint("UnrememberedMutableState") @SuppressLint("UnrememberedMutableState")
@Composable @Composable
fun LoginScreen(navController: NavHostController? = null, generalViewModel: GeneralViewModel, loginViewModel: LoginViewModel, isVisibilityBreak: Boolean) { fun LoginScreen(navController: NavHostController? = null, generalViewModel: GeneralViewModel, loginViewModel: LoginViewModel, isVisibilityBreak: Boolean) {
val TAG = "Rabbit_LoginScreen"
val context = LocalContext.current val context = LocalContext.current
//关于登录的事件监听 //关于登录的事件监听
@ -116,11 +115,19 @@ fun LoginScreen(navController: NavHostController? = null, generalViewModel: Gene
} }
loginViewModel.loginResult.value = null //清理loginState loginViewModel.loginResult.value = null //清理loginState
}else if(loginViewModel.loginResult.value != null && loginViewModel.loginResult.value?.data?.token == null){ }else if(loginViewModel.loginResult.value != null && loginViewModel.loginResult.value?.data?.token == null){
Log.w("LoginScreen","登录失败,无有效的Token") Log.w(TAG,"登录失败,无有效的Token")
CenterToast.show("登录失败,请重新登录") CenterToast.show("登录失败,请重新登录")
} }
} }
LaunchedEffect(loginViewModel.errorState.value) {
if(loginViewModel.errorState.value != null){
Log.w(TAG,loginViewModel.errorState.value?.message?:"登录失败")
CenterToast.show("登录失败")
}
loginViewModel.errorState.value = null
}
Scaffold{ Scaffold{
Box( Box(
modifier = Modifier.fillMaxSize().navigationBarsPadding() modifier = Modifier.fillMaxSize().navigationBarsPadding()
@ -413,7 +420,6 @@ private fun CaptchaLoginScreen(context: Context, viewModel: LoginViewModel) {
.clickable(enabled = !isCaptchaCountdown) { .clickable(enabled = !isCaptchaCountdown) {
// 点击获取验证码 // 点击获取验证码
if (validatePhoneEmpty( if (validatePhoneEmpty(
context = context,
viewModel = viewModel, viewModel = viewModel,
showToast = true showToast = true
) )
@ -468,7 +474,6 @@ private fun CaptchaLoginScreen(context: Context, viewModel: LoginViewModel) {
) { ) {
// 点击登录 // 点击登录
if (validateCaptchaLoginEmpty( if (validateCaptchaLoginEmpty(
context = context,
viewModel = viewModel, viewModel = viewModel,
showToast = true showToast = true
) )
@ -588,7 +593,7 @@ private fun CaptchaLoginScreen(context: Context, viewModel: LoginViewModel) {
@SuppressLint("UseKtx", "InflateParams", "SetTextI18n") @SuppressLint("UseKtx", "InflateParams", "SetTextI18n")
@Composable @Composable
private fun OneKeyLoginScreen(context: Context, viewModel: LoginViewModel) { private fun OneKeyLoginScreen(context: Context, viewModel: LoginViewModel) {
val TAG = "Rabbit_LoginPage_OneKeyLoginScreen" val TAG = "Rabbit_LoginScreen_OneKeyLoginScreen"
val preLoginResult = GYManager.getInstance().preLoginResult val preLoginResult = GYManager.getInstance().preLoginResult
val phoneNumber = viewModel.oneKeyPreLogin?.number ?: "" val phoneNumber = viewModel.oneKeyPreLogin?.number ?: ""
@ -1144,7 +1149,7 @@ private fun OtherLoginBar(viewModel: LoginViewModel) {
/** /**
* 验证手机号是否为空 * 验证手机号是否为空
*/ */
private fun validatePhoneEmpty(context: Context, viewModel: LoginViewModel, showToast: Boolean = false): Boolean { private fun validatePhoneEmpty(viewModel: LoginViewModel, showToast: Boolean = false): Boolean {
if (showToast && viewModel.userName.value.isEmpty()) { if (showToast && viewModel.userName.value.isEmpty()) {
CenterToast.show("请输入手机号") CenterToast.show("请输入手机号")
} }
@ -1154,7 +1159,7 @@ private fun validatePhoneEmpty(context: Context, viewModel: LoginViewModel, show
/** /**
* 验证验证码登录是否为空 * 验证验证码登录是否为空
*/ */
private fun validateCaptchaLoginEmpty(context: Context, viewModel: LoginViewModel, showToast: Boolean = false): Boolean { private fun validateCaptchaLoginEmpty(viewModel: LoginViewModel, showToast: Boolean = false): Boolean {
if (showToast) { if (showToast) {
if(viewModel.userName.value.isEmpty()){ if(viewModel.userName.value.isEmpty()){
CenterToast.show("请输入手机号") CenterToast.show("请输入手机号")

View File

@ -62,6 +62,7 @@ import com.img.rabbit.route.ScreenRoute
import com.img.rabbit.utils.UniAppUtils import com.img.rabbit.utils.UniAppUtils
import com.img.rabbit.utils.UniMpUpdate import com.img.rabbit.utils.UniMpUpdate
import com.img.rabbit.viewmodel.GeneralViewModel import com.img.rabbit.viewmodel.GeneralViewModel
import com.img.rabbit.viewmodel.ReportViewModel
import io.dcloud.feature.sdk.DCUniMPSDK import io.dcloud.feature.sdk.DCUniMPSDK
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@ -79,13 +80,14 @@ fun HomeScreen(
val scope: CoroutineScope = rememberCoroutineScope() val scope: CoroutineScope = rememberCoroutineScope()
val progressPair = mutableStateMapOf<String, Float>() val progressPair = mutableStateMapOf<String, Float>()
val reportViewModel = viewModel<ReportViewModel>()
// 获取当前路由状态 // 获取当前路由状态
val navBackStackEntry by navController.currentBackStackEntryAsState() val navBackStackEntry by navController.currentBackStackEntryAsState()
val currentRoute = navBackStackEntry?.destination?.route val currentRoute = navBackStackEntry?.destination?.route
// 只有当当前路由处于首页 Tab 之一时,才激活 BackHandler // 只有当当前路由处于首页 Tab 之一时,才激活 BackHandler
var lastClickTime by remember { mutableLongStateOf(0L) } var lastClickTime by remember { mutableLongStateOf(0L) }
val updateUserConfigNotify by GlobalStateManager(context).globalUserConfigNotifyFlow().collectAsState(initial = false) val updateUserConfigNotify by GlobalStateManager(context).globalUserConfigNotifyFlow().collectAsState(initial = false)
BackHandler(enabled = (currentRoute == ScreenRoute.Home.route)) { BackHandler(enabled = (currentRoute == ScreenRoute.Home.route)) {
@ -259,7 +261,7 @@ fun HomeScreen(
} else { } else {
loadingCallback?.showLoading() loadingCallback?.showLoading()
//启动uni小程序1、直接启动2、释放并启动 //启动uni小程序1、直接启动2、释放并启动
UniAppUtils.distributeUniMp(context, uniMp) { UniAppUtils.distributeUniMp(context, uniMp, reportViewModel) {
loadingCallback?.hideLoading() loadingCallback?.hideLoading()
} }
} }
@ -389,7 +391,7 @@ fun HomeScreen(
} else { } else {
loadingCallback?.showLoading() loadingCallback?.showLoading()
//启动uni小程序1、直接启动2、释放并启动 //启动uni小程序1、直接启动2、释放并启动
UniAppUtils.distributeUniMp(context, uniMp) { UniAppUtils.distributeUniMp(context, uniMp, reportViewModel) {
loadingCallback?.hideLoading() loadingCallback?.hideLoading()
} }
} }
@ -484,7 +486,7 @@ fun HomeScreen(
GlobalStateManager(context).storeGlobalUniDownloadNotify(true) GlobalStateManager(context).storeGlobalUniDownloadNotify(true)
} }
}else { }else {
UniAppUtils.startUniMpPage(context = context, uniMpId = uniMpId, uniMpType = item.type, pagePath = item.url) UniAppUtils.startUniMpPage(context = context, uniMpId = uniMpId, uniMpType = item.type, pagePath = item.url, reportViewModel = reportViewModel)
} }
} }
) { ) {

View File

@ -44,6 +44,8 @@ import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavHostController import androidx.navigation.NavHostController
import androidx.navigation.compose.rememberNavController import androidx.navigation.compose.rememberNavController
import com.img.rabbit.R import com.img.rabbit.R
import com.img.rabbit.bean.response.UserInfoEntity
import com.img.rabbit.pages.dialog.TipsDialog
import com.img.rabbit.pages.toolbar.TitleBar import com.img.rabbit.pages.toolbar.TitleBar
import com.img.rabbit.provider.storage.GlobalStateManager import com.img.rabbit.provider.storage.GlobalStateManager
import com.img.rabbit.provider.storage.PreferenceUtil import com.img.rabbit.provider.storage.PreferenceUtil
@ -57,6 +59,7 @@ import com.img.rabbit.viewmodel.LoginViewModel
fun SettingScreen(navController: NavHostController, loginViewModel: LoginViewModel) { fun SettingScreen(navController: NavHostController, loginViewModel: LoginViewModel) {
val context = LocalContext.current val context = LocalContext.current
var cacheDataSize by remember { mutableStateOf("正在计算...") } var cacheDataSize by remember { mutableStateOf("正在计算...") }
var showConfirmClearCache by remember { mutableStateOf(false) }
LaunchedEffect(Unit) { LaunchedEffect(Unit) {
AppDataStoreUtils.getAppStorageStats(context){ _, _, _, totalDataCacheSize -> AppDataStoreUtils.getAppStorageStats(context){ _, _, _, totalDataCacheSize ->
@ -98,9 +101,7 @@ fun SettingScreen(navController: NavHostController, loginViewModel: LoginViewMod
indication = null, indication = null,
interactionSource = remember { MutableInteractionSource() } interactionSource = remember { MutableInteractionSource() }
) { ) {
// 跳转页面 showConfirmClearCache = true
//navController.navigate("")
AppDataStoreUtils.openAppSettings(context)
}, },
verticalAlignment = Alignment.CenterVertically verticalAlignment = Alignment.CenterVertically
) { ) {
@ -364,7 +365,7 @@ fun SettingScreen(navController: NavHostController, loginViewModel: LoginViewMod
interactionSource = remember { MutableInteractionSource() } interactionSource = remember { MutableInteractionSource() }
) { ) {
// 退出登录 // 退出登录
loginViewModel.requestLogout(context) loginViewModel.requestLogout()
} }
) { ) {
Text( Text(
@ -383,6 +384,25 @@ fun SettingScreen(navController: NavHostController, loginViewModel: LoginViewMod
} }
} }
} }
if(showConfirmClearCache) {
TipsDialog(
title = "清除缓存",
content1 = "清除缓存后可能会丢失聊天数据",
content2 = "",
cancel = "取消",
confirm = "确定",
data = null,
onStatusChange = { status, isCancel, _ ->
if (!isCancel) {
// 跳转应用设置页面(清除缓存)
AppDataStoreUtils.openAppSettings(context)
}
showConfirmClearCache = status
}
)
}
} }
} }

View File

@ -91,7 +91,7 @@ fun DeleteAccountScreen(navController: NavHostController,viewModel: DeleteAccoun
painter = painterResource(id = R.mipmap.ic_warning), painter = painterResource(id = R.mipmap.ic_warning),
contentDescription = null, contentDescription = null,
contentScale = ContentScale.FillWidth, contentScale = ContentScale.FillWidth,
modifier = Modifier.size(86.dp).align(Alignment.CenterHorizontally) modifier = Modifier.size(60.dp).align(Alignment.CenterHorizontally)
) )
Text( Text(
@ -279,7 +279,7 @@ fun DeleteAccountScreen(navController: NavHostController,viewModel: DeleteAccoun
.padding(start = 16.dp, end = 16.dp, top = 16.dp) .padding(start = 16.dp, end = 16.dp, top = 16.dp)
) { ) {
Text( Text(
text = "4.账号注销后账号不可登录,不可恢复", text = "4.账号注销后账号不可登录,不可恢复",
fontSize = 15.sp, fontSize = 15.sp,
fontWeight = FontWeight.Bold, fontWeight = FontWeight.Bold,
modifier = Modifier modifier = Modifier
@ -317,7 +317,7 @@ fun DeleteAccountScreen(navController: NavHostController,viewModel: DeleteAccoun
) )
} }
Text( Text(
text = "同意请打勾", text = "再次登录后会生成新账号,原账号被删除,同意请打勾",
fontSize = 13.sp, fontSize = 13.sp,
fontWeight = FontWeight.Normal, fontWeight = FontWeight.Normal,
modifier = Modifier modifier = Modifier

View File

@ -2,8 +2,12 @@ package com.img.rabbit.utils
import android.content.Context import android.content.Context
import android.util.Log import android.util.Log
import android.webkit.MimeTypeMap
import com.github.gzuliyujiang.oaid.DeviceIdentifier import com.github.gzuliyujiang.oaid.DeviceIdentifier
import com.img.rabbit.BuildConfig import com.img.rabbit.BuildConfig
import com.img.rabbit.bean.request.ReportKey
import com.img.rabbit.bean.request.ReportRequest
import com.img.rabbit.bean.request.ReportType
import com.img.rabbit.bean.response.UniVersionEntity import com.img.rabbit.bean.response.UniVersionEntity
import com.img.rabbit.components.CenterToast import com.img.rabbit.components.CenterToast
import com.img.rabbit.config.Constants import com.img.rabbit.config.Constants
@ -13,6 +17,7 @@ import com.img.rabbit.provider.utils.HeadParamUtils.applicationContext
import com.img.rabbit.provider.utils.HeadParamUtils.getAppVersionName import com.img.rabbit.provider.utils.HeadParamUtils.getAppVersionName
import com.img.rabbit.uni.UniMPAlipaySplashView import com.img.rabbit.uni.UniMPAlipaySplashView
import com.img.rabbit.uni.UniMPWxSplashView import com.img.rabbit.uni.UniMPWxSplashView
import com.img.rabbit.viewmodel.ReportViewModel
import com.tencent.mm.opensdk.modelbiz.WXLaunchMiniProgram import com.tencent.mm.opensdk.modelbiz.WXLaunchMiniProgram
import com.tencent.mm.opensdk.openapi.IWXAPI import com.tencent.mm.opensdk.openapi.IWXAPI
import io.dcloud.feature.sdk.DCUniMPSDK import io.dcloud.feature.sdk.DCUniMPSDK
@ -55,7 +60,10 @@ object UniAppUtils {
//仅当跳转指定小程序时,才需要跳转指定位置 //仅当跳转指定小程序时,才需要跳转指定位置
var currentUniMpJumpPatch: String? = null var currentUniMpJumpPatch: String? = null
private fun getWgtName(uniMpId: String): String{
return String.format("%s.wgt", uniMpId)
//return String.format("%s.zip", uniMpId)
}
/** /**
*获取当前前台运行的UniMp小程序实体 *获取当前前台运行的UniMp小程序实体
*/ */
@ -81,13 +89,13 @@ object UniAppUtils {
* 是否需要下载强制更新或者不存在wgt文件 * 是否需要下载强制更新或者不存在wgt文件
*/ */
fun isDownloadUniMp(uniVersion: UniVersionEntity): Boolean{ fun isDownloadUniMp(uniVersion: UniVersionEntity): Boolean{
val wgtName = String.format("%s.wgt", uniVersion.unimp_id) val wgtName = getWgtName(uniVersion.unimp_id)
val wgtFile = File(FileUtils.getInstance().cacheUniAppDir.absolutePath, wgtName) val wgtFile = File(FileUtils.getInstance().cacheUniAppDir.absolutePath, wgtName)
return isUpdateForce(uniVersion) || !(FileUtils.getInstance().fileIsExists(wgtFile) && FileUtils.getFileSize(wgtFile) > 0) return isUpdateForce(uniVersion) || !(FileUtils.getInstance().fileIsExists(wgtFile) && FileUtils.getFileSize(wgtFile) > 0)
} }
fun wgtIsExists(uniMpId: String): Boolean{ fun wgtIsExists(uniMpId: String): Boolean{
val wgtName = String.format("%s.wgt", uniMpId) val wgtName = getWgtName(uniMpId)
val wgtFile = File(FileUtils.getInstance().cacheUniAppDir.absolutePath, wgtName) val wgtFile = File(FileUtils.getInstance().cacheUniAppDir.absolutePath, wgtName)
//判断wgt是否下载 //判断wgt是否下载
return FileUtils.getInstance().fileIsExists(wgtFile) && FileUtils.getFileSize(wgtFile) > 0 return FileUtils.getInstance().fileIsExists(wgtFile) && FileUtils.getFileSize(wgtFile) > 0
@ -127,14 +135,14 @@ object UniAppUtils {
/** /**
* 分发uniMP下载更新与启动 * 分发uniMP下载更新与启动
*/ */
fun distributeUniMp(context: Context, uniVersion: UniVersionEntity, onResult:(loading: Boolean) -> Unit){ fun distributeUniMp(context: Context, uniVersion: UniVersionEntity,reportViewModel: ReportViewModel, onResult:(loading: Boolean) -> Unit){
val isExists = DCUniMPSDK.getInstance().isExistsApp(uniVersion.unimp_id) val isExists = DCUniMPSDK.getInstance().isExistsApp(uniVersion.unimp_id)
if(isExists){ if(isExists){
//资源已释放,直接启动 //资源已释放,直接启动
startUniMp(context, uniVersion, onResult) startUniMp(context, uniVersion, onResult)
}else{ }else{
//资源未释放,先释放后启动 //资源未释放,先释放后启动
releaseWgt(uniVersion){ isSuccess, versionEntity -> releaseWgt(uniVersion,reportViewModel){ isSuccess, versionEntity ->
if(isSuccess){ if(isSuccess){
startUniMp(context, versionEntity, onResult) startUniMp(context, versionEntity, onResult)
}else{ }else{
@ -165,9 +173,9 @@ object UniAppUtils {
/** /**
* 启动小程序并直达指定页面 * 启动小程序并直达指定页面
*/ */
fun startUniMpPage(context: Context, uniMpId: String, uniMpType: String, pagePath: String){ fun startUniMpPage(context: Context, uniMpId: String, uniMpType: String, pagePath: String, reportViewModel: ReportViewModel){
if(isRelease(uniMpId)){ if(isRelease(uniMpId)){
releaseWgt(uniMpId){ releaseWgt(uniMpId,reportViewModel){
// 启动直达页面 // 启动直达页面
startUniMpToPage(context, uniMpId, uniMpType, pagePath) startUniMpToPage(context, uniMpId, uniMpType, pagePath)
@ -196,8 +204,8 @@ object UniAppUtils {
updateUniMp(uniMpId, DCUniMPSDK.getInstance().openUniMP(context, uniMpId, configuration)) updateUniMp(uniMpId, DCUniMPSDK.getInstance().openUniMP(context, uniMpId, configuration))
} }
private fun releaseWgt(versionEntity: UniVersionEntity, onReleaseWgt: (isSuccess: Boolean, versionEntity: UniVersionEntity) -> Unit) { private fun releaseWgt(versionEntity: UniVersionEntity, reportViewModel: ReportViewModel, onReleaseWgt: (isSuccess: Boolean, versionEntity: UniVersionEntity) -> Unit) {
releaseWgt(versionEntity.unimp_id){ isSuccess -> releaseWgt(versionEntity.unimp_id,reportViewModel){ isSuccess ->
if(isSuccess){ if(isSuccess){
onReleaseWgt(true, versionEntity) onReleaseWgt(true, versionEntity)
}else{ }else{
@ -207,15 +215,15 @@ object UniAppUtils {
} }
//释放资源 //释放资源
private fun releaseWgt(uniMpId: String, onReleaseWgt: (isSuccess: Boolean) -> Unit) { private fun releaseWgt(uniMpId: String, reportViewModel: ReportViewModel, onReleaseWgt: (isSuccess: Boolean) -> Unit) {
val appBasePath = DCUniMPSDK.getInstance().getAppBasePath(applicationContext) val appBasePath = DCUniMPSDK.getInstance().getAppBasePath(applicationContext)
val deleteSuccess = File(appBasePath, uniMpId).deleteRecursively() val deleteSuccess = File(appBasePath, uniMpId).deleteRecursively()
if(deleteSuccess){ if(deleteSuccess){
val wgtName = String.format("%s.wgt", uniMpId) val wgtName = getWgtName(uniMpId)
val wgtFile = File(FileUtils.getInstance().cacheUniAppDir.absolutePath, wgtName) val wgtFile = File(FileUtils.getInstance().cacheUniAppDir.absolutePath, wgtName)
val uniMPReleaseConfiguration = UniMPReleaseConfiguration().apply { val uniMPReleaseConfiguration = UniMPReleaseConfiguration().apply {
wgtPath = wgtFile.path wgtPath = wgtFile.path
password = "6462"////没有密码可以不写 password = PreferenceUtil.getUserConfig()?.config?.wgtPassword//"6462"////没有密码可以不写
} }
DCUniMPSDK.getInstance().releaseWgtToRunPath(uniMpId, uniMPReleaseConfiguration) { code, _ -> DCUniMPSDK.getInstance().releaseWgtToRunPath(uniMpId, uniMPReleaseConfiguration) { code, _ ->
if (code == 1) { if (code == 1) {
@ -223,12 +231,22 @@ object UniAppUtils {
onReleaseWgt(true) onReleaseWgt(true)
} else { } else {
//释放wgt失败 //释放wgt失败
CenterToast.show("小程序加载失败,请清除缓存后重试!") CenterToast.show("加载失败,请重试或联系客服")
onReleaseWgt(false) onReleaseWgt(false)
File(appBasePath, uniMpId).deleteRecursively()
//事件提交
reportViewModel.requestReport(
ReportRequest(
ReportType.ERROR,
ReportKey.EVENT_CLIENT_UNI_RELEASE_WGT,
uniMpId,
"释放资源失败"
)
)
} }
} }
}else{ }else{
CenterToast.show("资源释放失败,请手动删除小程序运行文件!") CenterToast.show("加载失败,请重试或联系客服")
} }
} }
@ -236,17 +254,16 @@ object UniAppUtils {
/** /**
* 下载wgt文件 * 下载wgt文件
*/ */
fun downloadWGT(context: Context,scope: CoroutineScope, uniVersion: UniVersionEntity, onProgress:(state: UniMpUpdate, filePath: String?, progress: Float?) -> Unit) { fun downloadWGT(context: Context,scope: CoroutineScope, uniVersion: UniVersionEntity, reportViewModel: ReportViewModel = ReportViewModel(), onProgress:(state: UniMpUpdate, filePath: String?, progress: Float?) -> Unit) {
val uniMpID = uniVersion.unimp_id val uniMpId = uniVersion.unimp_id
val wgtName = String.format("%s.wgt", uniMpID) val wgtName = getWgtName(uniMpId)
val wgtFile = File(FileUtils.getInstance().cacheUniAppDir.absolutePath, wgtName) val wgtFile = File(FileUtils.getInstance().cacheUniAppDir.absolutePath, wgtName)
onProgress(UniMpUpdate.DOWNLOAD_START, wgtFile.path, 0f) onProgress(UniMpUpdate.DOWNLOAD_START, wgtFile.path, 0f)
downloadUniMp(scope, uniVersion){uniState, filePath, progress -> downloadUniMp(scope, uniVersion){uniState, filePath, progress ->
onProgress(uniState, filePath, progress) onProgress(uniState, filePath, progress)
if(uniState == UniMpUpdate.DOWNLOAD_FINISH){ if(uniState == UniMpUpdate.DOWNLOAD_FINISH){
PreferenceUtil.saveWgtVersion(uniVersion.unimp_id, uniVersion.version) distributeUniMp(context, uniVersion,reportViewModel) { _ ->}
distributeUniMp(context, uniVersion) { _ ->}
} }
} }
} }
@ -254,15 +271,14 @@ object UniAppUtils {
/** /**
* 下载并释放资源但不会启动 * 下载并释放资源但不会启动
*/ */
fun downloadReleaseWgt(scope: CoroutineScope, uniVersion: UniVersionEntity, onProgress:(state: UniMpUpdate, progress: Float?) -> Unit,onRelease:(isSuccess: Boolean) -> Unit){ fun downloadReleaseWgt(scope: CoroutineScope, uniVersion: UniVersionEntity,reportViewModel: ReportViewModel, onProgress:(state: UniMpUpdate, progress: Float?) -> Unit,onRelease:(isSuccess: Boolean) -> Unit){
val uniMpId = uniVersion.unimp_id val uniMpId = uniVersion.unimp_id
onProgress(UniMpUpdate.DOWNLOAD_START, 0f) onProgress(UniMpUpdate.DOWNLOAD_START, 0f)
downloadUniMp(scope, uniVersion){uniState, _, progress -> downloadUniMp(scope, uniVersion){uniState, _, progress ->
onProgress(UniMpUpdate.DOWNLOAD_LOADING, progress) onProgress(UniMpUpdate.DOWNLOAD_LOADING, progress)
if(uniState == UniMpUpdate.DOWNLOAD_FINISH){ if(uniState == UniMpUpdate.DOWNLOAD_FINISH){
PreferenceUtil.saveWgtVersion(uniMpId, uniVersion.version)
onProgress(UniMpUpdate.DOWNLOAD_FINISH, 1f) onProgress(UniMpUpdate.DOWNLOAD_FINISH, 1f)
releaseWgt(uniMpId){ isSuccess -> releaseWgt(uniMpId,reportViewModel){ isSuccess ->
if(isSuccess){ if(isSuccess){
onRelease(true) onRelease(true)
}else{ }else{
@ -283,12 +299,20 @@ object UniAppUtils {
uniVersion: UniVersionEntity, uniVersion: UniVersionEntity,
onProgress:(state:UniMpUpdate,filePath: String?, progress: Float) -> Unit onProgress:(state:UniMpUpdate,filePath: String?, progress: Float) -> Unit
) { ) {
val uniMpID = uniVersion.unimp_id val extension = MimeTypeMap.getFileExtensionFromUrl(uniVersion.url)
val wgtName = String.format("%s.wgt", uniMpID) if(extension != "zip" && extension != "wgt"){
CenterToast.show("资源文件格式不被支持...")
onProgress(UniMpUpdate.DOWNLOAD_FAIL, null, -1f)
return
}
val uniMpId = uniVersion.unimp_id
val wgtName = getWgtName(uniMpId)
val path = FileUtils.getInstance().cacheUniAppDir.absolutePath val path = FileUtils.getInstance().cacheUniAppDir.absolutePath
//先删除旧文件 //先删除旧文件
val oldFile = File(path, wgtName) val oldFile = File(path, wgtName)
if(oldFile.exists()){ if(oldFile.exists()){
oldFile.delete() oldFile.delete()
} }
onProgress(UniMpUpdate.DOWNLOAD_LOADING, null, 0.01f) onProgress(UniMpUpdate.DOWNLOAD_LOADING, null, 0.01f)
@ -296,7 +320,7 @@ object UniAppUtils {
scope.launch { scope.launch {
val isAvailable = isFileDownloadable(uniVersion.url) val isAvailable = isFileDownloadable(uniVersion.url)
if(!isAvailable){ if(!isAvailable){
Log.i(TAG, "下载失败,无效地址") Log.i(TAG, "下载失败,无效地址 ------>${uniVersion.url}")
onProgress(UniMpUpdate.DOWNLOAD_FAIL, null, -1f) onProgress(UniMpUpdate.DOWNLOAD_FAIL, null, -1f)
CenterToast.show("下载失败...") CenterToast.show("下载失败...")
}else{ }else{
@ -317,6 +341,7 @@ object UniAppUtils {
}, },
onFinish = {isSuccess, filePath -> onFinish = {isSuccess, filePath ->
if(isSuccess){ if(isSuccess){
PreferenceUtil.saveWgtVersion(uniMpId, uniVersion.version)
Log.i(TAG, "下载完成---->updateUniMp: $filePath") Log.i(TAG, "下载完成---->updateUniMp: $filePath")
onProgress(UniMpUpdate.DOWNLOAD_FINISH, filePath, 1f) onProgress(UniMpUpdate.DOWNLOAD_FINISH, filePath, 1f)
}else{ }else{

View File

@ -3,13 +3,16 @@ package com.img.rabbit.utils
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.util.Log
import androidx.core.content.FileProvider import androidx.core.content.FileProvider
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import net.lingala.zip4j.ZipFile
import java.io.File import java.io.File
object UpdateUtils { object UpdateUtils {
private const val TAG = "UpdateUtils"
@SuppressLint("SetTextI18n") @SuppressLint("SetTextI18n")
fun download(scope: CoroutineScope, url: String, filePath: String, fileName: String, onProgress:(progress:Int)-> Unit, onFinish:(isSuccess: Boolean, filePath: String?)-> Unit) { fun download(scope: CoroutineScope, url: String, filePath: String, fileName: String, onProgress:(progress:Int)-> Unit, onFinish:(isSuccess: Boolean, filePath: String?)-> Unit) {
scope.launch(Dispatchers.IO) { scope.launch(Dispatchers.IO) {
@ -51,4 +54,25 @@ object UpdateUtils {
e.printStackTrace() e.printStackTrace()
} }
} }
/**
* @param zipFilePath 压缩包路径 (例如: context.filesDir.path + "/test.zip")
* @param destPath 解压目标目录
* @param password 密码如果没有密码传 null
*/
fun unpackZip(zipFilePath: String, destPath: String, password: String? = null) {
try {
val zipFile = ZipFile(zipFilePath)
// 如果有密码,设置密码
if (zipFile.isEncrypted && password != null) {
zipFile.setPassword(password.toCharArray())
}
// 核心一行代码:解压全部文件
zipFile.extractAll(destPath)
Log.i(TAG, "unpackZip: 解压成功到: $destPath")
} catch (e: Exception) {
e.printStackTrace()
Log.i(TAG, "unpackZip: 解压失败: ${e.message}")
}
}
} }

View File

@ -431,7 +431,7 @@ class LoginViewModel : BaseViewModel() {
* 请求退出登录 * 请求退出登录
*/ */
@OptIn(DelicateCoroutinesApi::class) @OptIn(DelicateCoroutinesApi::class)
fun requestLogout(context: Context) { fun requestLogout() {
isLoading.value = true // 开始加载 isLoading.value = true // 开始加载
mLaunch { mLaunch {
val response = ApiManager.serviceVo.logout() val response = ApiManager.serviceVo.logout()

View File

@ -0,0 +1,20 @@
package com.img.rabbit.viewmodel
import android.util.Log
import com.img.rabbit.bean.request.ReportRequest
import com.img.rabbit.provider.api.ApiManager
import kotlinx.serialization.json.Json
import okhttp3.RequestBody.Companion.toRequestBody
class ReportViewModel : BaseViewModel() {
private val TAG = "ReportViewModel"
//请求客服连接
fun requestReport(requst: ReportRequest){
mLaunch {
val requstJson = Json.encodeToString(requst)
val response = ApiManager.serviceVo.report(requstJson.toRequestBody())
Log.i(TAG, "requestReport: ${response.data}")
}
}
}

View File

@ -111,4 +111,8 @@ interface ServiceVo {
*/ */
@GET("/api/weixin/service") @GET("/api/weixin/service")
suspend fun wxService(): ResultVo<ServiceWxLinkEntity> suspend fun wxService(): ResultVo<ServiceWxLinkEntity>
//事件上报
@POST("/api/user/event")
suspend fun report(@Body requestBody: RequestBody): ResultVo<Any>
} }

View File

@ -58,6 +58,7 @@ android_cn_oaid = "4.2.12"
fastaes = "1.1.5" fastaes = "1.1.5"
foundationVersion = "1.10.3" foundationVersion = "1.10.3"
accompanistPermissions = "0.32.0" accompanistPermissions = "0.32.0"
zip4j = "2.11.6"
# Uni小程序相关依赖 version # Uni小程序相关依赖 version
recyclerview = "1.0.0" recyclerview = "1.0.0"
legacySupportV4 = "1.0.0" legacySupportV4 = "1.0.0"
@ -137,8 +138,10 @@ android_cn_oaid = { module = "com.github.gzu-liyujiang:Android_CN_OAID", version
fastaes = { module = "io.github.billywei01:fastaes", version.ref = "fastaes" } fastaes = { module = "io.github.billywei01:fastaes", version.ref = "fastaes" }
#noinspection SimilarGradleDependency #noinspection SimilarGradleDependency
foundation = { group = "androidx.compose.foundation", name = "foundation", version.ref = "foundationVersion" } foundation = { group = "androidx.compose.foundation", name = "foundation", version.ref = "foundationVersion" }
#权限申请
accompanist-permissions = { module = "com.google.accompanist:accompanist-permissions", version.ref = "accompanistPermissions" } accompanist-permissions = { module = "com.google.accompanist:accompanist-permissions", version.ref = "accompanistPermissions" }
#压缩解压
zip4j = { group = "net.lingala.zip4j", name = "zip4j", version.ref = "zip4j" }
# Uni小程序相关依赖 # Uni小程序相关依赖
androidx-recyclerview = { module = "androidx.recyclerview:recyclerview", version.ref = "recyclerview" } androidx-recyclerview = { module = "androidx.recyclerview:recyclerview", version.ref = "recyclerview" }