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 {
getByName("debug") {
isMinifyEnabled = false
isShrinkResources = false
signingConfig = signingConfigs.getByName("config")
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
}
getByName("release") {
// 建议开启混淆以减少启动耗时和体积
isMinifyEnabled = false
isShrinkResources = false
isMinifyEnabled = true
isShrinkResources = true
signingConfig = signingConfigs.getByName("config")
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
}
@ -326,6 +328,7 @@ dependencies {
implementation(libs.android.cn.oaid) //获取手机设备id
implementation(libs.fastaes) //解密
implementation(libs.accompanist.permissions)
// implementation(libs.zip4j)
//Uni小程序相关依赖
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.
#-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.**{*;}
@ -32,3 +412,6 @@
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?) {
// MultiDex.install(base)
//MultiDex.install(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.viewmodel.GeneralViewModel
import com.img.rabbit.viewmodel.LoginViewModel
import com.img.rabbit.viewmodel.ReportViewModel
import com.img.rabbit.viewmodel.SplashViewModel
import com.umeng.analytics.MobclickAgent
import com.umeng.commonsdk.UMConfigure
@ -108,6 +109,7 @@ class MainActivity : ComponentActivity(), LoadingCallback {
val context = LocalContext.current
val loginViewModel: LoginViewModel = viewModel()
val splashViewModel: SplashViewModel = viewModel()
val reportViewModel:ReportViewModel = viewModel()
generalViewModel = ViewModelProvider(this, ViewModelProvider.AndroidViewModelFactory.getInstance(application))[GeneralViewModel::class.java]
var updateAppNotify by mutableStateOf(GlobalStateManager(context).globalUpdateNotifyFlow().collectAsState(initial = false))
@ -230,29 +232,36 @@ class MainActivity : ComponentActivity(), LoadingCallback {
UniMpUpdate.DOWNLOAD_START -> {
//资源开始下载
progressWGTToPageState.floatValue = 0f
Log.i("HomeScreen","DOWNLOAD_START")
Log.i("HomeScreen", "DOWNLOAD_START")
}
UniMpUpdate.DOWNLOAD_FINISH -> {
//资源下载完成
progressWGTToPageState.floatValue = 1f
Log.i("HomeScreen","DOWNLOAD_FINISH")
Log.i("HomeScreen", "DOWNLOAD_FINISH")
coroutineScope.launch {
GlobalStateManager(context).storeGlobalUniDownloadNotify(false)
GlobalStateManager(context).storeGlobalUniDownloadNotify(
false
)
}
}
UniMpUpdate.DOWNLOAD_FAIL -> {
//资源下载失败
progressWGTToPageState.floatValue = -1f
Log.i("HomeScreen","DOWNLOAD_FAIL")
Log.i("HomeScreen", "DOWNLOAD_FAIL")
coroutineScope.launch {
GlobalStateManager(context).storeGlobalUniDownloadNotify(false)
GlobalStateManager(context).storeGlobalUniDownloadNotify(
false
)
}
}
else -> {
//资源下载进度
if(progress != null){
if (progress != null) {
progressWGTToPageState.floatValue = progress
Log.i("HomeScreen","DOWNLOAD_PROGRESS:$progress")
Log.i("HomeScreen", "DOWNLOAD_PROGRESS:$progress")
}
}
@ -261,10 +270,17 @@ class MainActivity : ComponentActivity(), LoadingCallback {
onRelease = {
//资源下载完成后,启动小程序到指定位置
val uniMpEntity = UniAppUtils.currentDownloadUniMp
if(uniMpEntity!=null){
UniAppUtils.startUniMpPage(context = context, uniMpId = uniMpEntity.unimp_id, uniMpType = uniMpEntity.unimp_type, pagePath = UniAppUtils.currentUniMpJumpPatch?:"")
}
if (uniMpEntity != null) {
UniAppUtils.startUniMpPage(
context = context,
uniMpId = uniMpEntity.unimp_id,
uniMpType = uniMpEntity.unimp_type,
pagePath = UniAppUtils.currentUniMpJumpPatch ?: "",
reportViewModel = reportViewModel
)
}
},
reportViewModel = reportViewModel
)
TipsUniMpToPageDialog(
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 {
@SerializedName("client.version.upgrade") //版本更新
var versionEntity: VersionEntity? = null
@SerializedName("client.uni.version.upgrade") //小程序模拟器
var uniVersionEntity: List<UniVersionEntity>? = emptyList()
@SerializedName("client.icon.uni.home") //uni首页icon
var homeIconEntity: List<UniIconEntity>? = emptyList()
@SerializedName("client.wgt.password") //WGT解压密码
var wgtPassword: String? = null
@SerializedName("client.popup.display") //显示开关控制

View File

@ -9,7 +9,6 @@ import android.util.Log
import android.view.LayoutInflater
import android.widget.CheckBox
import android.widget.TextView
import android.widget.Toast
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.border
@ -38,7 +37,6 @@ import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
@ -88,6 +86,7 @@ import org.json.JSONObject
@SuppressLint("UnrememberedMutableState")
@Composable
fun LoginScreen(navController: NavHostController? = null, generalViewModel: GeneralViewModel, loginViewModel: LoginViewModel, isVisibilityBreak: Boolean) {
val TAG = "Rabbit_LoginScreen"
val context = LocalContext.current
//关于登录的事件监听
@ -116,11 +115,19 @@ fun LoginScreen(navController: NavHostController? = null, generalViewModel: Gene
}
loginViewModel.loginResult.value = null //清理loginState
}else if(loginViewModel.loginResult.value != null && loginViewModel.loginResult.value?.data?.token == null){
Log.w("LoginScreen","登录失败,无有效的Token")
Log.w(TAG,"登录失败,无有效的Token")
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{
Box(
modifier = Modifier.fillMaxSize().navigationBarsPadding()
@ -413,7 +420,6 @@ private fun CaptchaLoginScreen(context: Context, viewModel: LoginViewModel) {
.clickable(enabled = !isCaptchaCountdown) {
// 点击获取验证码
if (validatePhoneEmpty(
context = context,
viewModel = viewModel,
showToast = true
)
@ -468,7 +474,6 @@ private fun CaptchaLoginScreen(context: Context, viewModel: LoginViewModel) {
) {
// 点击登录
if (validateCaptchaLoginEmpty(
context = context,
viewModel = viewModel,
showToast = true
)
@ -588,7 +593,7 @@ private fun CaptchaLoginScreen(context: Context, viewModel: LoginViewModel) {
@SuppressLint("UseKtx", "InflateParams", "SetTextI18n")
@Composable
private fun OneKeyLoginScreen(context: Context, viewModel: LoginViewModel) {
val TAG = "Rabbit_LoginPage_OneKeyLoginScreen"
val TAG = "Rabbit_LoginScreen_OneKeyLoginScreen"
val preLoginResult = GYManager.getInstance().preLoginResult
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()) {
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(viewModel.userName.value.isEmpty()){
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.UniMpUpdate
import com.img.rabbit.viewmodel.GeneralViewModel
import com.img.rabbit.viewmodel.ReportViewModel
import io.dcloud.feature.sdk.DCUniMPSDK
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
@ -79,13 +80,14 @@ fun HomeScreen(
val scope: CoroutineScope = rememberCoroutineScope()
val progressPair = mutableStateMapOf<String, Float>()
val reportViewModel = viewModel<ReportViewModel>()
// 获取当前路由状态
val navBackStackEntry by navController.currentBackStackEntryAsState()
val currentRoute = navBackStackEntry?.destination?.route
// 只有当当前路由处于首页 Tab 之一时,才激活 BackHandler
var lastClickTime by remember { mutableLongStateOf(0L) }
val updateUserConfigNotify by GlobalStateManager(context).globalUserConfigNotifyFlow().collectAsState(initial = false)
BackHandler(enabled = (currentRoute == ScreenRoute.Home.route)) {
@ -259,7 +261,7 @@ fun HomeScreen(
} else {
loadingCallback?.showLoading()
//启动uni小程序1、直接启动2、释放并启动
UniAppUtils.distributeUniMp(context, uniMp) {
UniAppUtils.distributeUniMp(context, uniMp, reportViewModel) {
loadingCallback?.hideLoading()
}
}
@ -389,7 +391,7 @@ fun HomeScreen(
} else {
loadingCallback?.showLoading()
//启动uni小程序1、直接启动2、释放并启动
UniAppUtils.distributeUniMp(context, uniMp) {
UniAppUtils.distributeUniMp(context, uniMp, reportViewModel) {
loadingCallback?.hideLoading()
}
}
@ -484,7 +486,7 @@ fun HomeScreen(
GlobalStateManager(context).storeGlobalUniDownloadNotify(true)
}
}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.compose.rememberNavController
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.provider.storage.GlobalStateManager
import com.img.rabbit.provider.storage.PreferenceUtil
@ -57,6 +59,7 @@ import com.img.rabbit.viewmodel.LoginViewModel
fun SettingScreen(navController: NavHostController, loginViewModel: LoginViewModel) {
val context = LocalContext.current
var cacheDataSize by remember { mutableStateOf("正在计算...") }
var showConfirmClearCache by remember { mutableStateOf(false) }
LaunchedEffect(Unit) {
AppDataStoreUtils.getAppStorageStats(context){ _, _, _, totalDataCacheSize ->
@ -98,9 +101,7 @@ fun SettingScreen(navController: NavHostController, loginViewModel: LoginViewMod
indication = null,
interactionSource = remember { MutableInteractionSource() }
) {
// 跳转页面
//navController.navigate("")
AppDataStoreUtils.openAppSettings(context)
showConfirmClearCache = true
},
verticalAlignment = Alignment.CenterVertically
) {
@ -364,7 +365,7 @@ fun SettingScreen(navController: NavHostController, loginViewModel: LoginViewMod
interactionSource = remember { MutableInteractionSource() }
) {
// 退出登录
loginViewModel.requestLogout(context)
loginViewModel.requestLogout()
}
) {
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),
contentDescription = null,
contentScale = ContentScale.FillWidth,
modifier = Modifier.size(86.dp).align(Alignment.CenterHorizontally)
modifier = Modifier.size(60.dp).align(Alignment.CenterHorizontally)
)
Text(
@ -279,7 +279,7 @@ fun DeleteAccountScreen(navController: NavHostController,viewModel: DeleteAccoun
.padding(start = 16.dp, end = 16.dp, top = 16.dp)
) {
Text(
text = "4.账号注销后账号不可登录,不可恢复",
text = "4.账号注销后账号不可登录,不可恢复",
fontSize = 15.sp,
fontWeight = FontWeight.Bold,
modifier = Modifier
@ -317,7 +317,7 @@ fun DeleteAccountScreen(navController: NavHostController,viewModel: DeleteAccoun
)
}
Text(
text = "同意请打勾",
text = "再次登录后会生成新账号,原账号被删除,同意请打勾",
fontSize = 13.sp,
fontWeight = FontWeight.Normal,
modifier = Modifier

View File

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

View File

@ -3,13 +3,16 @@ package com.img.rabbit.utils
import android.annotation.SuppressLint
import android.content.Context
import android.content.Intent
import android.util.Log
import androidx.core.content.FileProvider
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import net.lingala.zip4j.ZipFile
import java.io.File
object UpdateUtils {
private const val TAG = "UpdateUtils"
@SuppressLint("SetTextI18n")
fun download(scope: CoroutineScope, url: String, filePath: String, fileName: String, onProgress:(progress:Int)-> Unit, onFinish:(isSuccess: Boolean, filePath: String?)-> Unit) {
scope.launch(Dispatchers.IO) {
@ -51,4 +54,25 @@ object UpdateUtils {
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)
fun requestLogout(context: Context) {
fun requestLogout() {
isLoading.value = true // 开始加载
mLaunch {
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")
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"
foundationVersion = "1.10.3"
accompanistPermissions = "0.32.0"
zip4j = "2.11.6"
# Uni小程序相关依赖 version
recyclerview = "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" }
#noinspection SimilarGradleDependency
foundation = { group = "androidx.compose.foundation", name = "foundation", version.ref = "foundationVersion" }
#权限申请
accompanist-permissions = { module = "com.google.accompanist:accompanist-permissions", version.ref = "accompanistPermissions" }
#压缩解压
zip4j = { group = "net.lingala.zip4j", name = "zip4j", version.ref = "zip4j" }
# Uni小程序相关依赖
androidx-recyclerview = { module = "androidx.recyclerview:recyclerview", version.ref = "recyclerview" }