1、模拟器-接入小程序
2、优化加载释放资源
This commit is contained in:
shenzuqiang 2026-03-05 09:16:34 +08:00
parent 7b00bda629
commit cfc1767f0e
39 changed files with 1412 additions and 80 deletions

View File

@ -4,10 +4,10 @@
<selectionStates> <selectionStates>
<SelectionState runConfigName="app"> <SelectionState runConfigName="app">
<option name="selectionMode" value="DROPDOWN" /> <option name="selectionMode" value="DROPDOWN" />
<DropdownSelection timestamp="2026-03-02T03:06:17.024211200Z"> <DropdownSelection timestamp="2026-03-04T10:36:17.441189700Z">
<Target type="DEFAULT_BOOT"> <Target type="DEFAULT_BOOT">
<handle> <handle>
<DeviceId pluginId="PhysicalDevice" identifier="serial=VSJNQ4NFBYJB5PZD" /> <DeviceId pluginId="PhysicalDevice" identifier="serial=Y5DELZR46DZTCI9D" />
</handle> </handle>
</Target> </Target>
</DropdownSelection> </DropdownSelection>

6
.idea/studiobot.xml Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="StudioBotProjectSettings">
<option name="shareContext" value="OptedIn" />
</component>
</project>

View File

@ -1,3 +1,5 @@
import org.gradle.kotlin.dsl.api
plugins { plugins {
id("com.android.application") id("com.android.application")
alias(libs.plugins.compose.compiler) alias(libs.plugins.compose.compiler)
@ -13,7 +15,18 @@ android {
buildConfig = true buildConfig = true
viewBinding = true viewBinding = true
} }
aaptOptions {
additionalParameters("--auto-add-overlay")
}
androidResources {
ignoreAssetsPattern = "!.svn:!.git:.*:!CVS:!thumbs.db:!picasa.ini:!*.scc:*~"
}
packagingOptions {
jniLibs {
useLegacyPackaging = true
}
}
composeOptions { composeOptions {
kotlinCompilerExtensionVersion = "1.4.8" kotlinCompilerExtensionVersion = "1.4.8"
@ -25,6 +38,7 @@ android {
targetSdk = 36 targetSdk = 36
versionCode = 1 versionCode = 1
versionName = "1.0.0" versionName = "1.0.0"
multiDexEnabled = true
setManifestPlaceholders(mapOf( setManifestPlaceholders(mapOf(
@ -60,6 +74,7 @@ android {
manifestPlaceholders.putAll(mapOf( manifestPlaceholders.putAll(mapOf(
"GETUI_APPID" to (project.findProperty("GETUI_APPID") as? String ?: ""), "GETUI_APPID" to (project.findProperty("GETUI_APPID") as? String ?: ""),
"GT_INSTALL_CHANNEL" to "general", "GT_INSTALL_CHANNEL" to "general",
"apk.applicationId" to "com.img.rabbit"
)) ))
} }
@ -98,7 +113,15 @@ android {
} }
} }
configurations {
all {
exclude(group = "com.android.support")
}
}
dependencies { dependencies {
api(fileTree(mapOf("dir" to "libs", "include" to listOf("*.jar","*.aar"))))
// 基础依赖 // 基础依赖
implementation(libs.androidx.core.ktx) implementation(libs.androidx.core.ktx)
implementation(libs.androidx.appcompat) implementation(libs.androidx.appcompat)
@ -106,6 +129,7 @@ dependencies {
implementation(libs.androidx.activity) implementation(libs.androidx.activity)
implementation(libs.androidx.constraintlayout) implementation(libs.androidx.constraintlayout)
implementation(libs.androidx.core.splashscreen) implementation(libs.androidx.core.splashscreen)
implementation(libs.multidex)
// Compose 依赖 // Compose 依赖
implementation(platform(libs.androidx.compose.bom)) implementation(platform(libs.androidx.compose.bom))
@ -172,8 +196,21 @@ dependencies {
implementation (files("libs/channelsdk-0.2.2.aar")) //快手分包 implementation (files("libs/channelsdk-0.2.2.aar")) //快手分包
implementation (files("libs/humesdk-1.0.0.aar")) //巨量分包 implementation (files("libs/humesdk-1.0.0.aar")) //巨量分包
//工具类相关依赖
implementation(libs.android.cn.oaid) //获取手机设备id implementation(libs.android.cn.oaid) //获取手机设备id
implementation(libs.fastaes) //解密 implementation(libs.fastaes) //解密
implementation("com.google.accompanist:accompanist-permissions:0.32.0") implementation(libs.accompanist.permissions)
//Uni小程序相关依赖
implementation (files("libs/sqlite-release.aar")) //sqlite数据库
implementation(libs.androidx.recyclerview) //必须集成android 自带recyclerview支持
implementation(libs.androidx.legacy.support.v4) //必须集成androidx support支持
// implementation(libs.appcompat)
implementation(libs.alibaba.fastjson) //必须集成fastjson功能需要
implementation(libs.facebook.fresco) //必须集成,图片加载需要
implementation(libs.facebook.animated.gif) //必须集成,图片加载需要
implementation(libs.bumptech.glide) //必须集成,图片加载需要
implementation(libs.androidx.webkit) //4.45版本之后 必须集成,用来支持暗黑模式
implementation("androidx.legacy:legacy-support-v4:1.0.0")
} }

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
app/libs/sqlite-release.aar Normal file

Binary file not shown.

Binary file not shown.

View File

@ -34,17 +34,10 @@
android:name="android.permission.INSTALL_PACKAGES" android:name="android.permission.INSTALL_PACKAGES"
tools:ignore="ProtectedPermissions" /> tools:ignore="ProtectedPermissions" />
<queries> <queries>
<package android:name="com.eg.android.AlipayGphone" /> <!-- 支付宝 --> <package android:name="com.eg.android.AlipayGphone" /> <!-- 支付宝 -->
<package android:name="hk.alipay.wallet" /> <!-- AlipayHK --> <package android:name="hk.alipay.wallet" /> <!-- AlipayHK -->
</queries>
<queries>
<package android:name="com.tencent.mm" /> <package android:name="com.tencent.mm" />
</queries>
<queries>
<intent> <intent>
<action android:name="com.getui.sdk.action" /> <action android:name="com.getui.sdk.action" />
</intent> </intent>
@ -53,7 +46,9 @@
<application <application
android:name=".BaseApplication" android:name=".BaseApplication"
android:allowBackup="true" android:allowBackup="true"
android:allowNativeHeapPointerTagging="false"
android:dataExtractionRules="@xml/data_extraction_rules" android:dataExtractionRules="@xml/data_extraction_rules"
android:extractNativeLibs="true"
android:fullBackupContent="@xml/backup_rules" android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher_logo" android:icon="@mipmap/ic_launcher_logo"
android:label="@string/app_name" android:label="@string/app_name"
@ -63,7 +58,6 @@
android:enableOnBackInvokedCallback="true" android:enableOnBackInvokedCallback="true"
android:hardwareAccelerated="true" android:hardwareAccelerated="true"
android:requestLegacyExternalStorage="true" android:requestLegacyExternalStorage="true"
android:resizeableActivity="true"
android:usesCleartextTraffic="true" android:usesCleartextTraffic="true"
android:networkSecurityConfig="@xml/network_security_config" android:networkSecurityConfig="@xml/network_security_config"
tools:replace="android:allowBackup,android:supportsRtl" tools:replace="android:allowBackup,android:supportsRtl"
@ -73,7 +67,8 @@
android:theme="@style/SplashTheme" android:theme="@style/SplashTheme"
android:exported="true" android:exported="true"
android:launchMode="singleTask"> android:launchMode="singleTask">
<intent-filter> <intent-filter tools:ignore="AppLinkUrlError">
<action android:name="android.intent.action.VIEW" />
<action android:name="android.intent.action.MAIN" /> <action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" /> <category android:name="android.intent.category.LAUNCHER" />
@ -82,13 +77,14 @@
<activity <activity
android:name=".WebViewActivity" android:name=".WebViewActivity"
android:excludeFromRecents="true"
android:exported="true" android:exported="true"
android:taskAffinity="com.img.rabbit"
android:theme="@style/Theme.AppCompat.Light.NoActionBar" /> android:theme="@style/Theme.AppCompat.Light.NoActionBar" />
<activity <activity
android:name=".wxapi.WXEntryActivity" android:name=".wxapi.WXEntryActivity"
android:excludeFromRecents="true"
android:configChanges="keyboardHidden|orientation|screenSize" android:configChanges="keyboardHidden|orientation|screenSize"
android:exported="true" android:exported="true"
android:screenOrientation="portrait" android:screenOrientation="portrait"
@ -96,6 +92,40 @@
tools:ignore="DiscouragedApi,LockedOrientationActivity" /> tools:ignore="DiscouragedApi,LockedOrientationActivity" />
<activity
android:name=".wxapi.WXPayEntryActivity"
android:excludeFromRecents="true"
android:exported="true"
android:launchMode="singleTask"
android:theme="@android:style/Theme.Translucent.NoTitleBar" />
<!--如果是小程序模式,还需要添加这行配置-->
<activity
android:name="io.dcloud.feature.payment.weixin.WXPayProcessMeadiatorActivity"
android:excludeFromRecents="true"
android:exported="false"
android:theme="@style/ProjectDialogTheme" />
<!-- 小程序入口 -->
<!-- <activity-->
<!-- android:name="io.dcloud.feature.sdk.multi.DCUniMPEntry0"-->
<!-- android:excludeFromRecents="true"-->
<!-- android:taskAffinity="com.img.rabbit"-->
<!-- android:theme="@style/Theme.rabbit"-->
<!-- android:launchMode="singleTop"-->
<!-- tools:replace="android:launchMode,android:taskAffinity,android:theme"/>-->
<!-- 小程序运行页 -->
<!-- <activity-->
<!-- android:name="io.dcloud.feature.sdk.multi.DCUniMPActivity0"-->
<!-- android:excludeFromRecents="true"-->
<!-- android:taskAffinity="com.img.rabbit"-->
<!-- android:launchMode="singleTask"-->
<!-- android:configChanges="orientation|keyboardHidden|screenSize"-->
<!-- tools:replace="android:taskAffinity,android:launchMode,android:configChanges" />-->
<provider <provider
android:name="androidx.core.content.FileProvider" android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.fileProvider" android:authorities="${applicationId}.fileProvider"
@ -106,6 +136,16 @@
android:resource="@xml/filepath_data" /> android:resource="@xml/filepath_data" />
</provider> </provider>
<provider
android:name="io.dcloud.common.util.DCloud_FileProvider"
android:authorities="${apk.applicationId}.dc.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/dcloud_file_provider" />
</provider>
</application> </application>
</manifest> </manifest>

View File

@ -0,0 +1,6 @@
<hbuilder version="1.9.9.80110" debug="false" syncDebug ="false">
<apps>
<app appid="unimp" appver="1.0"/>
</apps>
</hbuilder>

View File

@ -0,0 +1,92 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<meta name="viewport" content="initial-scale=1.0, maximum-scale=1.0, user-scalable=no"/>
<meta name="HandheldFriendly" content="true"/>
<meta name="MobileOptimized" content="320"/>
<title>Error</title>
<script type="text/javascript">
// H5 plus事件处理
var ws=null;
function plusReady(){
// Android处理返回键
plus.key.addEventListener('backbutton',function(){
(history.length==1)&&ws.close();
var c=setTimeout(function(){
ws.close();
},1000);
window.onbeforeunload=function(){
clearTimeout(c);
}
history.go(-2);
},false);
ws=plus.webview.currentWebview();
}
if(window.plus){
plusReady();
}else{
document.addEventListener('plusready',plusReady,false);
}
document.addEventListener('touchstart',function(){
return false;
},true);
// 禁止选择
document.oncontextmenu=function(){
return false;
};
// 获取错误信息
document.addEventListener("error",function(e){
info.innerText="请求的页面("+e.url+")无法打开";
console.log("请求的页面无法打开:"+e.href);
},false);
</script>
<style>
*{
-webkit-user-select: none;
}
html,body{
margin: 0px;
padding: 0px;
width: 100%;
height: 100%;
text-align: center;
word-break: break-all;
-webkit-touch-callout:none;
-webkit-tap-highlight-color:rgba(0,0,0,0);
}
.button{
width: 50%;
font-size: 18px;
font-weight: normal;
text-decoration: none;
text-align: center;
padding: .5em 0em;
margin: .5em auto;
color: #333333;
background-color: #EEEEEE;
border: 1px solid #CCCCCC;
-webkit-border-radius: 5px;
border-radius: 5px;
}
.button:active{
background-color: #CCCCCC;
}
</style>
</head>
<body>
<div style="width:100%;height:20%;"></div>
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 512 512" style="height:20%;width:30%">
<g id="icomoon-ignore">
<line stroke-width="1" x1="" y1="" x2="" y2="" stroke="#449FDB" opacity=""></line>
</g>
<path d="M256 0c-141.385 0-256 114.615-256 256s114.615 256 256 256 256-114.615 256-256-114.615-256-256-256zM352 128c17.673 0 32 14.327 32 32s-14.327 32-32 32-32-14.327-32-32 14.327-32 32-32zM160 128c17.673 0 32 14.327 32 32s-14.327 32-32 32-32-14.327-32-32 14.327-32 32-32zM352.049 390.37c-19.587-32.574-55.272-54.37-96.049-54.37s-76.462 21.796-96.049 54.37l-41.164-24.698c27.98-46.535 78.958-77.672 137.213-77.672s109.232 31.137 137.213 77.672l-41.164 24.698z" fill="#666666"></path>
</svg>
<p style="font-size:18px;font-weight:bolder;">We're sorry ...</p>
<p id="info" style="font-size:12px;"></p>
<!--<div class="button" onclick="history.back()">Retry</div>-->
<div class="button" onclick="if(history.length == 1){ws.close();}else{ws.back();ws.back();}">Back</div>
<div class="button" onclick="ws.close()">Close</div>
<div class="button" onclick="plus.runtime.restart()">Restart</div>
</body>
</html>

View File

@ -0,0 +1,73 @@
<properties>
<features>
<feature name="sqlite" value="io.dcloud.feature.sqlite.DataBaseFeature"/>
<feature name="Barcode" value="io.dcloud.feature.barcode2.BarcodeFeatureImpl"/>
<feature name="Speech" value="io.dcloud.feature.speech.SpeechFeatureImpl">
<module name="iFly" value="io.dcloud.feature.speech.IflySpeechEngine"/>
<module name="baidu" value="io.dcloud.feature.speech.BaiduSpeechEngine"/>
</feature>
<feature name="Maps" value="io.dcloud.js.map.amap.JsMapPluginImpl"/>
<!--<feature name="Maps" value="io.dcloud.js.map.JsMapPluginImpl"/>-->
<feature name="Contacts" value="io.dcloud.feature.contacts.ContactsFeatureImpl"/>
<feature name="Messaging" value="io.dcloud.adapter.messaging.MessagingPluginImpl"/>
<feature name="Camera" value="io.dcloud.js.camera.CameraFeatureImpl"/>
<feature name="Console" value="io.dcloud.feature.pdr.LoggerFeatureImpl"/>
<feature name="Device" value="io.dcloud.feature.device.DeviceFeatureImpl"/>
<feature name="File" value="io.dcloud.js.file.FileFeatureImpl"/>
<feature name="Proximity" value="io.dcloud.feature.sensor.ProximityFeatureImpl"/>
<feature name="Storage" value="io.dcloud.feature.pdr.NStorageFeatureImpl"/>
<feature name="Cache" value="io.dcloud.feature.pdr.CoreCacheFeatureImpl"/>
<feature name="Invocation" value="io.dcloud.invocation.Invocation"/>
<feature name="Navigator" value="io.dcloud.feature.ui.navigator.NavigatorUIFeatureImpl"/>
<feature name="NativeUI" value="io.dcloud.feature.ui.nativeui.NativeUIFeatureImpl"/>
<feature name="UI" value="io.dcloud.feature.ui.UIFeatureImpl">
<module name="Navigator" value="io.dcloud.feature.ui.NavView"/>
</feature>
<feature name="Gallery" value="io.dcloud.js.gallery.GalleryFeatureImpl"/>
<feature name="Downloader" value="io.dcloud.net.DownloaderFeatureImpl"/>
<feature name="Uploader" value="io.dcloud.net.UploadFeature"/>
<feature name="Push" value="io.dcloud.feature.aps.APSFeatureImpl">
<module name="igexin" value="io.dcloud.feature.apsGt.GTPushService"/>
<!-- mkeypush -->
</feature>
<feature name="Zip" value="io.dcloud.feature.pdr.ZipFeature"/>
<feature name="Audio" value="io.dcloud.feature.audio.AudioFeatureImpl"/>
<feature name="Runtime" value="io.dcloud.feature.pdr.RuntimeFeatureImpl"/>
<feature name="VideoPlayer" value="io.dcloud.media.MediaFeatureImpl"/>
<feature name="LivePusher" value="io.dcloud.media.live.LiveMediaFeatureImpl"/>
<feature name="XMLHttpRequest" value="io.dcloud.net.XMLHttpRequestFeature"/>
<feature name="Statistic" value="io.dcloud.feature.statistics.StatisticsFeatureImpl"/>
<feature name="Accelerometer" value="io.dcloud.feature.sensor.AccelerometerFeatureImpl"/>
<feature name="Orientation" value="io.dcloud.feature.sensor.OrientationFeatureImpl"/>
<feature name="NativeObj" value="io.dcloud.feature.nativeObj.FeatureImpl"/>
<feature name="Geolocation" value="io.dcloud.js.geolocation.GeolocationFeatureImpl"/>
<feature name="Payment" value="io.dcloud.feature.payment.PaymentFeatureImpl">
<module name="AliPay" value="io.dcloud.feature.payment.alipay.AliPay"/>
<module name="Payment-Weixin" value="io.dcloud.feature.payment.weixin.WeiXinPay"/>
</feature>
<feature name="Share" value="io.dcloud.share.ShareFeatureImpl">
<module name="Sina" value="io.dcloud.share.sina.SinaWeiboApiManager"/>
<module name="Tencent" value="io.dcloud.share.tencent.TencentWeiboApiManager"/>
<module name="Weixin" value="io.dcloud.share.mm.WeiXinApiManager"/>
<module name="QQ" value="io.dcloud.share.qq.QQApiManager"/>
</feature>
<feature name="OAuth" value="io.dcloud.feature.oauth.OAuthFeatureImpl">
<module name="OAuth-Weixin" value="io.dcloud.feature.oauth.weixin.WeiXinOAuthService"/>
<module name="OAuth-QQ" value="io.dcloud.feature.oauth.qq.QQOAuthService"/>
<module name="OAuth-Sina" value="io.dcloud.feature.oauth.sina.SinaOAuthService"/>
<module name="OAuth-Qihoo" value="io.dcloud.oauth.qihoo.QihooOAuthService"/>
<module name="OAuth-MiUi" value="io.dcloud.feature.oauth.miui.MiUiOAuthService"/>
</feature>
<feature name="Stream" value="io.dcloud.appstream.js.StreamAppFeatureImpl"/>
<feature name="Fingerprint" value="io.dcloud.feature.fingerprint.FingerPrintsImpl"/>
<feature name="iBeacon" value="io.dcloud.feature.iBeacon.WxBluetoothFeatureImpl"/>
<feature name="Bluetooth" value="io.dcloud.feature.bluetooth.BluetoothFeature"/>
</features>
<services>
<service name="push" value="io.dcloud.feature.aps.APSFeatureImpl"/>
<service name="Statistic" value="io.dcloud.feature.statistics.StatisticsBootImpl"/>
<service name="Downloader" value="io.dcloud.net.DownloaderBootImpl"/>
<service name="Maps" value="io.dcloud.js.map.MapInitImpl"/>
</services>
</properties>

View File

@ -1,14 +1,35 @@
package com.img.rabbit package com.img.rabbit
import android.app.Activity
import android.app.ActivityManager
import android.app.Application import android.app.Application
import android.content.Intent
import android.graphics.BitmapFactory
import android.os.Build
import android.os.Bundle
import android.util.Log import android.util.Log
import android.view.KeyEvent
import android.webkit.WebView
import androidx.activity.ComponentActivity
import androidx.activity.OnBackPressedCallback
import androidx.multidex.MultiDex
import com.bumptech.glide.Glide
import com.img.rabbit.utils.NetworkMonitor import com.img.rabbit.utils.NetworkMonitor
import com.g.gysdk.GYManager import com.g.gysdk.GYManager
import com.getui.gtc.base.util.CommonUtil.isMainProcess
import com.github.gzuliyujiang.oaid.DeviceID
import com.github.gzuliyujiang.oaid.IGetter
import com.img.rabbit.config.Constants import com.img.rabbit.config.Constants
import com.img.rabbit.provider.storage.PreferenceUtil
import com.tencent.mmkv.MMKV import com.tencent.mmkv.MMKV
import com.umeng.analytics.MobclickAgent import com.umeng.analytics.MobclickAgent
import com.umeng.commonsdk.UMConfigure import com.umeng.commonsdk.UMConfigure
import com.umeng.socialize.PlatformConfig import com.umeng.socialize.PlatformConfig
import io.dcloud.common.util.RuningAcitvityUtil
import io.dcloud.feature.sdk.DCSDKInitConfig
import io.dcloud.feature.sdk.DCUniMPSDK
import io.dcloud.feature.sdk.MenuActionSheetItem
import java.lang.Exception
class BaseApplication : Application() { class BaseApplication : Application() {
@ -16,14 +37,32 @@ class BaseApplication : Application() {
override fun onCreate() { override fun onCreate() {
super.onCreate() super.onCreate()
// 非小程序进程使用unimp 关键字 可以根据宿主的具体情况进行调整)
if (!RuningAcitvityUtil.getAppName(baseContext).contains("unimp")) {
// 初始化网络状态监控 // 初始化网络状态监控
NetworkMonitor.initialize(this) NetworkMonitor.initialize(this)
//初始化Glide
Glide.get(this)
// 初始化MMKV // 初始化MMKV
initMMKV() initMMKV()
// 初始化个推SDK // 初始化个推SDK
initGeTuiOneKeyLogin() initGeTuiOneKeyLogin()
// 初始化友盟 // 初始化友盟
initUM() initUM()
// 初始化OAID和uni-app SDK
initOAIDAndUniAPPSdk()
//需要在隐私框架下才能处理的逻辑
if(PreferenceUtil.getAgreement()){
//用户已同意隐私政策
}
}
}
override fun attachBaseContext(base: android.content.Context?) {
MultiDex.install(base)
super.attachBaseContext(base)
} }
/** /**
@ -48,4 +87,47 @@ class BaseApplication : Application() {
Log.i(TAG, "MMKV root dir: $rootDir") Log.i(TAG, "MMKV root dir: $rootDir")
} }
private fun initOAIDAndUniAPPSdk() {
if(PreferenceUtil.getOAID().isNullOrEmpty()){
DeviceID.getOAID(this, object : IGetter{
override fun onOAIDGetComplete(result: String?) {
Log.i(TAG, "获取OAID成功OAID: $result 开始记录OAID并初始化Uni-app SDK----->")
PreferenceUtil.saveOAID(result)
initUniAPPSdk()
}
override fun onOAIDGetError(error: Exception?) {
Log.i(TAG, "获取OAID失败开始初始化Uni-app SDK----->")
initUniAPPSdk()
}
})
}else{
Log.i(TAG, "已识别到OAID开始初始化Uni-app SDK----->")
initUniAPPSdk()
}
}
//初始化uni-app SDK
private fun initUniAPPSdk() {
if (isMainProcess(this)) {
val item = MenuActionSheetItem("关于", "gy")
val item1 = MenuActionSheetItem("获取当前页面url", "hqdqym")
val item2 = MenuActionSheetItem("跳转到宿主原生测试页面", "gotoTestPage")
val sheetItems: MutableList<MenuActionSheetItem?> = ArrayList()
sheetItems.add(item)
sheetItems.add(item1)
sheetItems.add(item2)
val config: DCSDKInitConfig? = DCSDKInitConfig.Builder()
.setCapsule(true)
.setMenuDefFontSize("16px")
.setMenuDefFontColor("#ff00ff")
.setMenuDefFontWeight("normal")
.setMenuActionSheetItems(sheetItems)
.setCustomOAID(PreferenceUtil.getOAID())
.setEnableBackground(false)
.build()
DCUniMPSDK.getInstance().initialize(this, config)
}
}
} }

View File

@ -1,9 +1,7 @@
package com.img.rabbit package com.img.rabbit
import android.Manifest
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.app.Activity import android.app.Activity
import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.util.Log import android.util.Log
import androidx.activity.ComponentActivity import androidx.activity.ComponentActivity
@ -26,6 +24,8 @@ import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.foundation.layout.wrapContentWidth import androidx.compose.foundation.layout.wrapContentWidth
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.ClickableText import androidx.compose.foundation.text.ClickableText
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text 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
@ -52,19 +52,28 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.viewmodel.compose.viewModel import androidx.lifecycle.viewmodel.compose.viewModel
import com.google.accompanist.permissions.ExperimentalPermissionsApi import com.google.accompanist.permissions.ExperimentalPermissionsApi
import com.img.rabbit.bean.response.UserInfoEntity
import com.img.rabbit.config.Constants import com.img.rabbit.config.Constants
import com.img.rabbit.config.Constants.agreementUrl import com.img.rabbit.config.Constants.agreementUrl
import com.img.rabbit.config.Constants.privacyUrl import com.img.rabbit.config.Constants.privacyUrl
import com.img.rabbit.pages.LoadingCallback
import com.img.rabbit.pages.LoginScreen import com.img.rabbit.pages.LoginScreen
import com.img.rabbit.pages.LoginScreenType import com.img.rabbit.pages.LoginScreenType
import com.img.rabbit.pages.MainScreen import com.img.rabbit.pages.MainScreen
import com.img.rabbit.pages.dialog.TipsDialog
import com.img.rabbit.pages.dialog.TipsUniMpDialog
import com.img.rabbit.pages.dialog.UpdateDialog import com.img.rabbit.pages.dialog.UpdateDialog
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
import com.img.rabbit.provider.storage.PreferenceUtil.saveBDVID import com.img.rabbit.provider.storage.PreferenceUtil.saveBDVID
import com.img.rabbit.utils.AppUpdate
import com.img.rabbit.utils.ChannelUtils import com.img.rabbit.utils.ChannelUtils
import com.img.rabbit.utils.FileUtils
import com.img.rabbit.utils.UniAppUtils
import com.img.rabbit.utils.UpdateUtils 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
@ -78,7 +87,7 @@ import kotlinx.coroutines.delay
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlin.system.exitProcess import kotlin.system.exitProcess
class MainActivity : ComponentActivity() { class MainActivity : ComponentActivity(), LoadingCallback {
@OptIn(DelicateCoroutinesApi::class, ExperimentalPermissionsApi::class) @OptIn(DelicateCoroutinesApi::class, ExperimentalPermissionsApi::class)
@SuppressLint("UnrememberedMutableState", "CoroutineCreationDuringComposition") @SuppressLint("UnrememberedMutableState", "CoroutineCreationDuringComposition")
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
@ -97,11 +106,15 @@ class MainActivity : ComponentActivity() {
var loginViewModel: LoginViewModel = viewModel() var loginViewModel: LoginViewModel = viewModel()
val context = LocalContext.current val context = LocalContext.current
var showSplash by remember { mutableStateOf(false) } var showSplash by remember { mutableStateOf(false) }
var globalLogin by mutableStateOf(GlobalStateManager(context).globalLoginNotifyFlow().collectAsState(initial = false)) var globalLogin by mutableStateOf(GlobalStateManager(context).globalLoginNotifyFlow().collectAsState(initial = false))
var globalLogout by mutableStateOf(GlobalStateManager(context).globalLogoutNotifyFlow().collectAsState(initial = false)) var globalLogout by mutableStateOf(GlobalStateManager(context).globalLogoutNotifyFlow().collectAsState(initial = false))
var globalBind by mutableStateOf(GlobalStateManager(context).globalBindNotifyFlow().collectAsState(initial = false)) var globalBind by mutableStateOf(GlobalStateManager(context).globalBindNotifyFlow().collectAsState(initial = false))
var globalUnBind by mutableStateOf(GlobalStateManager(context).globalUnBindNotifyFlow().collectAsState(initial = false)) var globalUnBind by mutableStateOf(GlobalStateManager(context).globalUnBindNotifyFlow().collectAsState(initial = false))
var globalUpdate by mutableStateOf(GlobalStateManager(context).globalUpdateNotifyFlow().collectAsState(initial = false)) var globalUpdate by mutableStateOf(GlobalStateManager(context).globalUpdateNotifyFlow().collectAsState(initial = false))
val globalUniUpdate by GlobalStateManager(context).globalUniUpdateNotifyFlow().collectAsState(initial = false)
val globalLoading by GlobalStateManager(context).isGlobalLoadingFlow().collectAsState(initial = false)
LaunchedEffect(generalViewModel.agreementStatus.value) { LaunchedEffect(generalViewModel.agreementStatus.value) {
if (generalViewModel.agreementStatus.value == true){ if (generalViewModel.agreementStatus.value == true){
@ -176,6 +189,7 @@ class MainActivity : ComponentActivity() {
) { isAllowPrivacyPolicy -> ) { isAllowPrivacyPolicy ->
if (isAllowPrivacyPolicy) { if (isAllowPrivacyPolicy) {
generalViewModel.setIsAgreement(true) generalViewModel.setIsAgreement(true)
loginViewModel.setIsPolicyAgreement(true)
showSplash = true showSplash = true
} else { } else {
// 不同意隐私协议政策,直接退出应用 // 不同意隐私协议政策,直接退出应用
@ -217,6 +231,33 @@ class MainActivity : ComponentActivity() {
} }
} }
//UniApp更新提示
val isDownloadingWGT = mutableStateOf(false)
val progressWGTState = mutableFloatStateOf(0f)
if(globalUniUpdate == true){
generalViewModel.currentUpdateUniMp?.let {
TipsUniMpDialog(
title = "资源包更新",
content1 = "是否确定更新资源包",
content2 = null,
cancel = "取消",
confirm = "确定",
scope = scope,
data = it,
isStartDown = isDownloadingWGT,
downProgress = progressWGTState,
onStatusChange = { isUpdateFinish, isCancel, data ->
if(!isUpdateFinish && !isCancel && data != null){
isDownloadingWGT.value = true
}else{
scope.launch { GlobalStateManager(context).storeGlobalUniUpdateNotify(false) }
}
}
)
}
}
//App更新提示
val isStartDownload = mutableStateOf(false) val isStartDownload = mutableStateOf(false)
val progressState = mutableFloatStateOf(0f) val progressState = mutableFloatStateOf(0f)
if(globalUpdate.value == true){ if(globalUpdate.value == true){
@ -239,6 +280,8 @@ class MainActivity : ComponentActivity() {
UpdateUtils.download( UpdateUtils.download(
scope = scope, scope = scope,
url = url, url = url,
filePath = FileUtils.getInstance().cacheDownLoadDir.absolutePath,
fileName = AppUpdate.getFileNameFromUrl(url),
onProgress = {progress-> onProgress = {progress->
progressState.floatValue = progress.toFloat()/100f progressState.floatValue = progress.toFloat()/100f
}, },
@ -256,6 +299,21 @@ class MainActivity : ComponentActivity() {
} }
} }
} }
//全局加载提示
if (globalLoading == true) {
Log.i("HomeScreen","isStartOn--->${System.currentTimeMillis()}")
Box(
modifier = Modifier
.fillMaxSize()
.background(Color.Black.copy(alpha = 0.5f))
) {
CircularProgressIndicator(
color = Color.White,
modifier = Modifier.align(Alignment.Center)
)
}
}
} }
} }
@ -267,8 +325,6 @@ class MainActivity : ComponentActivity() {
} }
} }
/** /**
* 初始化友盟 * 初始化友盟
*/ */
@ -277,13 +333,29 @@ class MainActivity : ComponentActivity() {
UMConfigure.init(this, Constants.UmengAppkey, ChannelUtils.getChannel(applicationContext), UMConfigure.DEVICE_TYPE_PHONE, "") UMConfigure.init(this, Constants.UmengAppkey, ChannelUtils.getChannel(applicationContext), UMConfigure.DEVICE_TYPE_PHONE, "")
MobclickAgent.setPageCollectionMode(MobclickAgent.PageMode.AUTO) MobclickAgent.setPageCollectionMode(MobclickAgent.PageMode.AUTO)
} }
override fun showLoading() {
Log.i("HomeScreen","isStartOn--->${System.currentTimeMillis()}")
lifecycleScope.launch {
GlobalStateManager(this@MainActivity).storeGlobalLoading(true)
}
}
override fun hideLoading() {
Log.i("HomeScreen","isStartOff--->${System.currentTimeMillis()}")
lifecycleScope.launch {
delay(3000L)
GlobalStateManager(this@MainActivity).storeGlobalLoading(false)
}
}
} }
@Composable @Composable
fun AppTheme(content: @Composable () -> Unit) { fun AppTheme(content: @Composable () -> Unit) {
// 使用Material3主题 // 使用Material3主题
androidx.compose.material3.MaterialTheme { MaterialTheme {
content() content()
} }
} }
@ -487,7 +559,6 @@ private fun PrivacyPolicyScreen(viewModel: LoginViewModel, onAgreementChange: (B
interactionSource = remember { MutableInteractionSource() } interactionSource = remember { MutableInteractionSource() }
) { ) {
onAgreementChange(true) onAgreementChange(true)
viewModel.setIsPolicyAgreement(true)
} }
) { ) {
Text( Text(

View File

@ -10,7 +10,7 @@ object Constants {
//const val getuiAppId = "40qbPjPkYs7TnVAYCX0Ig6"//个推appid (gradle.properties) //const val getuiAppId = "40qbPjPkYs7TnVAYCX0Ig6"//个推appid (gradle.properties)
const val WechatAppId = "wx7d1a7d1507482cef"// 微信APPID const val WechatAppId = "wx7d1a7d1507482cef"// 微信APPID
const val WechatAppSecret = ""//微信secret const val WechatAppSecret = ""//微信secret
const val UmengAppkey = ""//TODO 友盟appKey const val UmengAppkey = "69a641119a7f3764887cd287"// 友盟appKey
const val AppId = ""//appid const val AppId = ""//appid

View File

@ -0,0 +1,6 @@
package com.img.rabbit.pages
interface LoadingCallback {
fun showLoading()
fun hideLoading()
}

View File

@ -1,28 +1,12 @@
package com.img.rabbit.pages package com.img.rabbit.pages
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.app.Activity
import android.util.Log import android.util.Log
import android.widget.Toast
import androidx.activity.compose.BackHandler
import androidx.compose.foundation.Image
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.foundation.layout.wrapContentWidth
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.navigation.compose.NavHost import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController import androidx.navigation.compose.rememberNavController
@ -37,22 +21,16 @@ import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text 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.MutableState
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.runtime.mutableLongStateOf
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.navigation.NavType import androidx.navigation.NavType
import androidx.navigation.compose.currentBackStackEntryAsState import androidx.navigation.compose.currentBackStackEntryAsState
import androidx.navigation.navArgument import androidx.navigation.navArgument
@ -68,7 +46,6 @@ import com.img.rabbit.pages.screen.mine.setting.AccountBindScreen
import com.img.rabbit.pages.screen.mine.setting.AccountManagerScreen import com.img.rabbit.pages.screen.mine.setting.AccountManagerScreen
import com.img.rabbit.pages.screen.mine.setting.BindScreen import com.img.rabbit.pages.screen.mine.setting.BindScreen
import com.img.rabbit.pages.screen.other.CameraGuideScreen import com.img.rabbit.pages.screen.other.CameraGuideScreen
import com.img.rabbit.provider.storage.PreferenceUtil
import com.img.rabbit.route.ScreenRoute import com.img.rabbit.route.ScreenRoute
import com.img.rabbit.viewmodel.BindViewModel import com.img.rabbit.viewmodel.BindViewModel
import com.img.rabbit.viewmodel.GeneralViewModel import com.img.rabbit.viewmodel.GeneralViewModel
@ -168,7 +145,8 @@ fun MainScreen(generalViewModel: GeneralViewModel, loginViewModel: LoginViewMode
composable(ScreenRoute.Home.route) { composable(ScreenRoute.Home.route) {
HomeScreen( HomeScreen(
navController = navController, navController = navController,
generalViewModel = generalViewModel generalViewModel = generalViewModel,
loadingCallback = LocalContext.current as? LoadingCallback
) )
} }
composable(ScreenRoute.Mine.route) { composable(ScreenRoute.Mine.route) {

View File

@ -0,0 +1,340 @@
package com.img.rabbit.pages.dialog
import android.util.Log
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.foundation.layout.wrapContentWidth
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.LinearProgressIndicator
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.img.rabbit.R
import com.img.rabbit.bean.response.UniVersionEntity
import com.img.rabbit.utils.UniAppUtils
import com.img.rabbit.utils.UniMpUpdate
import kotlinx.coroutines.CoroutineScope
@Composable
fun TipsUniMpDialog(
title: String?,
content1: String?,
content2: String?,
cancel: String="取消",
confirm: String="确定",
scope: CoroutineScope,
data: UniVersionEntity,
isStartDown: MutableState<Boolean>,
downProgress: MutableState<Float>,
onStatusChange: (state: Boolean, isCancel:Boolean, data: Any?) -> Unit
){
val context = LocalContext.current
Column(
modifier = Modifier
.fillMaxSize()
.background(Color(0x80000000))
.clickable(
indication = null,
interactionSource = remember { MutableInteractionSource() }
) {
onStatusChange(false, true, data)
},
verticalArrangement = Arrangement.Center
){
Box(
modifier = Modifier
.fillMaxWidth()
.wrapContentHeight()
.padding(horizontal = 36.dp)
.background(Color.White, shape = RoundedCornerShape(26.dp))
.align(Alignment.CenterHorizontally)
.clickable(
indication = null,
interactionSource = remember { MutableInteractionSource() }
) {
//什么都不用做,只是解决点击穿透问题
}
) {
Image(
painter = painterResource(id = R.mipmap.ic_dialog_top_mask1),
contentDescription = null,
contentScale = ContentScale.FillWidth,
modifier = Modifier
.fillMaxWidth()
)
Column(
modifier = Modifier
.fillMaxWidth()
.wrapContentHeight()
.padding(top = 46.dp)
) {
if(!title.isNullOrEmpty()){
Text(
text = title,
fontSize = 18.sp,
fontWeight = FontWeight.Bold,
color = Color(0xFF1A1A1A),
modifier = Modifier
.wrapContentSize()
.align(Alignment.CenterHorizontally)
)
}
Box(
modifier = Modifier
.fillMaxWidth()
.height(14.dp)
)
if(!content1.isNullOrEmpty()){
Text(
text = content1,
fontSize = 12.sp,
fontWeight = FontWeight.Normal,
color = Color(0xFF767676),
modifier = Modifier
.wrapContentSize()
.align(Alignment.CenterHorizontally)
)
}
if(!content2.isNullOrEmpty()){
Text(
text = content2,
fontSize = 12.sp,
fontWeight = FontWeight.Normal,
color = Color(0xFF767676),
modifier = Modifier
.wrapContentSize()
.align(Alignment.CenterHorizontally)
)
}
Box(
modifier = Modifier
.fillMaxWidth()
.height(16.dp)
)
Row(
modifier = Modifier
.fillMaxWidth()
.padding(start = 18.dp, end = 18.dp, bottom = 20.dp)
) {
if (isStartDown.value) {
Column(
modifier = Modifier.fillMaxWidth().padding(horizontal = 16.dp)
) {
// 使用 LinearProgressIndicator 显示确定性进度
LinearProgressIndicator(
progress = { downProgress.value }, // 使用 Lambda 更新进度0~1
modifier = Modifier.fillMaxWidth().height(8.dp)
)
Text(text = "下载进度: ${(downProgress.value * 100).toInt()}%")
}
} else {
Box(
modifier = Modifier
.fillMaxWidth()
.wrapContentHeight()
.weight(1f)
.background(
Color(0x00000000),
shape = RoundedCornerShape(359.dp),
)
.border(
width = 1.dp,
color = Color(0xFF000000),
shape = RoundedCornerShape(359.dp)
)
.clickable(
indication = null,
interactionSource = remember { MutableInteractionSource() }
) {
onStatusChange(false, true, data)
}
) {
Text(
cancel,
color = Color(0xFF1A1A1A),
fontSize = 16.sp,
fontWeight = FontWeight.Bold,
modifier = Modifier
.wrapContentWidth()
.wrapContentHeight()
.padding(vertical = 12.dp)
.align(Alignment.Center)
)
}
Box(
modifier = Modifier
.width(11.dp)
)
//确定
Box(
modifier = Modifier
.fillMaxWidth()
.wrapContentHeight()
.weight(1f)
.background(
Color(0xFF252525),
shape = RoundedCornerShape(359.dp),
)
.clickable(
indication = null,
interactionSource = remember { MutableInteractionSource() }
) {
onStatusChange(false, false, data)
UniAppUtils.downloadWGT(context, scope, data){ uniState, filePath, progress ->
when (uniState) {
UniMpUpdate.DOWNLOAD_START -> {
//资源开始下载
downProgress.value = 0f
Log.i("HomeScreen","DOWNLOAD_START")
}
UniMpUpdate.DOWNLOAD_FINISH -> {
//资源下载完成
downProgress.value = 1f
onStatusChange(true, false, data)
Log.i("HomeScreen","DOWNLOAD_FINISH")
}
UniMpUpdate.DOWNLOAD_FAIL -> {
//资源下载失败
downProgress.value = -1f
Log.i("HomeScreen","DOWNLOAD_FAIL")
}
else -> {
//资源下载进度
if(progress != null){
downProgress.value = progress
Log.i("HomeScreen","DOWNLOAD_PROGRESS:$progress")
}
}
}
}
}
) {
Text(
confirm,
color = Color(0xFFC2FF43),
fontSize = 16.sp,
fontWeight = FontWeight.Bold,
modifier = Modifier
.wrapContentWidth()
.wrapContentHeight()
.padding(vertical = 12.dp)
.align(Alignment.Center)
)
}
}
}
//取消
// Row(
// modifier = Modifier
// .fillMaxWidth()
// .padding(start = 18.dp, end = 18.dp, bottom = 20.dp)
// ) {
// Box(
// modifier = Modifier
// .fillMaxWidth()
// .wrapContentHeight()
// .weight(1f)
// .background(
// Color(0x00000000),
// shape = RoundedCornerShape(359.dp),
// )
// .border(
// width = 1.dp,
// color = Color(0xFF000000),
// shape = RoundedCornerShape(359.dp)
// )
// .clickable(
// indication = null,
// interactionSource = remember { MutableInteractionSource() }
// ) {
// onStatusChange(false, true, data)
// }
// ) {
// Text(
// cancel,
// color = Color(0xFF1A1A1A),
// fontSize = 16.sp,
// fontWeight = FontWeight.Bold,
// modifier = Modifier
// .wrapContentWidth()
// .wrapContentHeight()
// .padding(vertical = 12.dp)
// .align(Alignment.Center)
// )
// }
//
// Box(
// modifier = Modifier
// .width(11.dp)
// )
//
// //确定
// Box(
// modifier = Modifier
// .fillMaxWidth()
// .wrapContentHeight()
// .weight(1f)
// .background(
// Color(0xFF252525),
// shape = RoundedCornerShape(359.dp),
// )
// .clickable(
// indication = null,
// interactionSource = remember { MutableInteractionSource() }
// ) {
// onStatusChange(false, false, data)
// }
// ) {
// Text(
// confirm,
// color = Color(0xFFC2FF43),
// fontSize = 16.sp,
// fontWeight = FontWeight.Bold,
// modifier = Modifier
// .wrapContentWidth()
// .wrapContentHeight()
// .padding(vertical = 12.dp)
// .align(Alignment.Center)
// )
// }
// }
}
}
}
}

View File

@ -1,6 +1,8 @@
package com.img.rabbit.pages.screen package com.img.rabbit.pages.screen
import android.annotation.SuppressLint
import android.app.Activity import android.app.Activity
import android.util.Log
import android.widget.Toast import android.widget.Toast
import androidx.activity.compose.BackHandler import androidx.activity.compose.BackHandler
import androidx.compose.foundation.Image import androidx.compose.foundation.Image
@ -23,12 +25,17 @@ import androidx.compose.foundation.layout.wrapContentWidth
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.LinearProgressIndicator
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableLongStateOf import androidx.compose.runtime.mutableLongStateOf
import androidx.compose.runtime.mutableStateMapOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
@ -37,6 +44,7 @@ import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
@ -46,13 +54,28 @@ import androidx.navigation.compose.currentBackStackEntryAsState
import androidx.navigation.compose.rememberNavController import androidx.navigation.compose.rememberNavController
import coil3.compose.AsyncImage import coil3.compose.AsyncImage
import com.img.rabbit.R import com.img.rabbit.R
import com.img.rabbit.pages.LoadingCallback
import com.img.rabbit.provider.storage.GlobalStateManager
import com.img.rabbit.provider.storage.PreferenceUtil import com.img.rabbit.provider.storage.PreferenceUtil
import com.img.rabbit.route.ScreenRoute import com.img.rabbit.route.ScreenRoute
import com.img.rabbit.utils.MMKVUtils.put
import com.img.rabbit.utils.UniAppUtils
import com.img.rabbit.utils.UniMpUpdate
import com.img.rabbit.utils.UniState
import com.img.rabbit.viewmodel.GeneralViewModel import com.img.rabbit.viewmodel.GeneralViewModel
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
@SuppressLint("UnrememberedMutableState", "MutableCollectionMutableState")
@Composable @Composable
fun HomeScreen(navController: NavHostController,generalViewModel: GeneralViewModel) { fun HomeScreen(
navController: NavHostController,
generalViewModel: GeneralViewModel,
loadingCallback: LoadingCallback?
) {
val context = LocalContext.current val context = LocalContext.current
val scope: CoroutineScope = rememberCoroutineScope()
val progressPair = mutableStateMapOf<String, Float>()
// 获取当前路由状态 // 获取当前路由状态
val navBackStackEntry by navController.currentBackStackEntryAsState() val navBackStackEntry by navController.currentBackStackEntryAsState()
@ -64,6 +87,7 @@ fun HomeScreen(navController: NavHostController,generalViewModel: GeneralViewMod
val currentTime = System.currentTimeMillis() val currentTime = System.currentTimeMillis()
if (currentTime - lastClickTime > 2000) { if (currentTime - lastClickTime > 2000) {
Toast.makeText(context, "再按一次退出应用", Toast.LENGTH_SHORT).show() Toast.makeText(context, "再按一次退出应用", Toast.LENGTH_SHORT).show()
Log.i("BackHandler", "HomeScreen----->BackHandler")
lastClickTime = currentTime lastClickTime = currentTime
} else { } else {
(context as? Activity)?.finish() (context as? Activity)?.finish()
@ -132,12 +156,58 @@ fun HomeScreen(navController: NavHostController,generalViewModel: GeneralViewMod
indication = null, indication = null,
interactionSource = remember { MutableInteractionSource() } interactionSource = remember { MutableInteractionSource() }
) { ) {
// 处理点击事件 val uniMp = uniVersionConfig[0]
Toast.makeText(context, "微信模拟器", Toast.LENGTH_SHORT).show() val uniMpId = uniMp.unimp_id
// 处理点击事件,微信模拟器
if(UniAppUtils.isDownloadUniMp(uniMp)){
//强制更新(更新释放新版本并启动)
UniAppUtils.downloadWGT(context, scope, uniMp){ uniState, filePath, progress ->
progressPair[uniMpId]?: mutableMapOf<String, Float>().apply{put(uniMpId, 0f)}
when (uniState) {
UniMpUpdate.DOWNLOAD_START -> {
//资源开始下载
progressPair.apply { put(uniMpId, 0f) }
Log.i("HomeScreen","DOWNLOAD_START")
}
UniMpUpdate.DOWNLOAD_FINISH -> {
//资源下载完成
progressPair.apply { put(uniMpId, 1f) }
Log.i("HomeScreen","DOWNLOAD_FINISH")
}
UniMpUpdate.DOWNLOAD_FAIL -> {
//资源下载失败
progressPair.apply { put(uniMpId, -1f) }
Log.i("HomeScreen","DOWNLOAD_FAIL")
}
else -> {
//资源下载进度
if(progress != null){
progressPair.apply { put(uniMpId, progress) }
Log.i("HomeScreen","DOWNLOAD_PROGRESS:$progress")
}
}
}
}
}else if(UniAppUtils.isUpdate(uniMp)){
// 提示更新1、更新释放新版本并启动2、直接启动现有版本
generalViewModel.currentUpdateUniMp = uniMp
scope.launch {
GlobalStateManager(context).storeGlobalUniUpdateNotify(true)
}
}else {
loadingCallback?.showLoading()
//启动uni小程序1、直接启动2、释放并启动
UniAppUtils.distributeUniMp(context, uniMp){
loadingCallback?.hideLoading()
}
}
} }
) { ) {
val uniMpId = uniVersionConfig[0].unimp_id
val uniIcon = uniVersionConfig[0].icon
AsyncImage( AsyncImage(
model = uniVersionConfig[0].icon, model = uniIcon,
contentDescription = "微信模拟器图标", contentDescription = "微信模拟器图标",
contentScale = ContentScale.FillWidth, contentScale = ContentScale.FillWidth,
modifier = Modifier.fillMaxWidth().aspectRatio(168/96f), modifier = Modifier.fillMaxWidth().aspectRatio(168/96f),
@ -145,6 +215,32 @@ fun HomeScreen(navController: NavHostController,generalViewModel: GeneralViewMod
error = painterResource(id = R.mipmap.ic_wx_mock) error = painterResource(id = R.mipmap.ic_wx_mock)
) )
//下载完成前显示
if((progressPair[uniMpId]?:0f) > 0f && (progressPair[uniMpId]?:0f) < 1f){
Box(
modifier = Modifier.fillMaxWidth().aspectRatio(168/96f).background(Color(
0x66000000
), RoundedCornerShape(18.dp)),
){
Column(
modifier = Modifier.fillMaxWidth().align(Alignment.Center).padding(horizontal = 12.dp)
) {
// 使用 LinearProgressIndicator 显示确定性进度
LinearProgressIndicator(
progress = { progressPair[uniMpId]?:0f }, // 使用 Lambda 更新进度0~1
modifier = Modifier.fillMaxWidth().height(8.dp)
)
Text(
text = "下载${((progressPair[uniMpId]?:0f) * 100).toInt()}%",
color = Color.White,
fontSize = 12.sp,
modifier = Modifier.align(Alignment.CenterHorizontally),
textAlign = TextAlign.Center,
)
}
}
}
} }
Box(modifier = Modifier.width(7.dp)) Box(modifier = Modifier.width(7.dp))
Box( Box(
@ -155,19 +251,91 @@ fun HomeScreen(navController: NavHostController,generalViewModel: GeneralViewMod
indication = null, indication = null,
interactionSource = remember { MutableInteractionSource() } interactionSource = remember { MutableInteractionSource() }
) { ) {
// 处理点击事件 val uniMp = uniVersionConfig[1]
Toast.makeText(context, "支付宝模拟器", Toast.LENGTH_SHORT).show() val uniMpId = uniMp.unimp_id
// 处理点击事件,微信模拟器
if(UniAppUtils.isDownloadUniMp(uniMp)){
//强制更新(更新释放新版本并启动)
UniAppUtils.downloadWGT(context, scope, uniMp){ uniState, filePath, progress ->
progressPair[uniMpId]?: mutableMapOf<String, Float>().apply{put(uniMpId, 0f)}
when (uniState) {
UniMpUpdate.DOWNLOAD_START -> {
//资源开始下载
progressPair.apply { put(uniMpId, 0f) }
Log.i("HomeScreen","DOWNLOAD_START")
}
UniMpUpdate.DOWNLOAD_FINISH -> {
//资源下载完成
progressPair.apply { put(uniMpId, 1f) }
Log.i("HomeScreen","DOWNLOAD_FINISH")
}
UniMpUpdate.DOWNLOAD_FAIL -> {
//资源下载失败
progressPair.apply { put(uniMpId, -1f) }
Log.i("HomeScreen","DOWNLOAD_FAIL")
}
else -> {
//资源下载进度
if(progress != null){
progressPair.apply { put(uniMpId, progress) }
Log.i("HomeScreen","DOWNLOAD_PROGRESS:$progress")
}
}
}
}
}else if(UniAppUtils.isUpdate(uniMp)){
// 提示更新1、更新释放新版本并启动2、直接启动现有版本
generalViewModel.currentUpdateUniMp = uniMp
scope.launch {
GlobalStateManager(context).storeGlobalUniUpdateNotify(true)
}
}else {
loadingCallback?.showLoading()
//启动uni小程序1、直接启动2、释放并启动
UniAppUtils.distributeUniMp(context, uniMp){
loadingCallback?.hideLoading()
}
}
} }
) { ) {
val uniMpId = uniVersionConfig[1].unimp_id
val uniIcon = uniVersionConfig[1].icon
if(uniVersionSize>1){ if(uniVersionSize>1){
AsyncImage( AsyncImage(
model = uniVersionConfig[1].icon, model = uniIcon,
contentDescription = "支付宝模拟器", contentDescription = "支付宝模拟器",
contentScale = ContentScale.FillWidth, contentScale = ContentScale.FillWidth,
modifier = Modifier.fillMaxWidth().aspectRatio(168/96f), modifier = Modifier.fillMaxWidth().aspectRatio(168/96f),
fallback = painterResource(id = R.mipmap.ic_alipay_mock), fallback = painterResource(id = R.mipmap.ic_alipay_mock),
error = painterResource(id = R.mipmap.ic_alipay_mock) error = painterResource(id = R.mipmap.ic_alipay_mock)
) )
//下载完成前显示
if((progressPair[uniMpId]?:0f) > 0f && (progressPair[uniMpId]?:0f) < 1f){
Box(
modifier = Modifier.fillMaxWidth().aspectRatio(168/96f).background(Color(
0x66000000
), RoundedCornerShape(18.dp)),
){
Column(
modifier = Modifier.fillMaxWidth().align(Alignment.Center).padding(horizontal = 12.dp)
) {
// 使用 LinearProgressIndicator 显示确定性进度
LinearProgressIndicator(
progress = { progressPair[uniMpId]?:0f }, // 使用 Lambda 更新进度0~1
modifier = Modifier.fillMaxWidth().height(8.dp)
)
Text(
text = "下载${((progressPair[uniMpId]?:0f) * 100).toInt()}%",
color = Color.White,
fontSize = 12.sp,
modifier = Modifier.align(Alignment.CenterHorizontally),
textAlign = TextAlign.Center,
)
}
}
}
} }
} }
} }
@ -1059,5 +1227,13 @@ fun HomeScreen(navController: NavHostController,generalViewModel: GeneralViewMod
@Preview(showBackground = true) @Preview(showBackground = true)
@Composable @Composable
private fun PreviewHomeScreen() { private fun PreviewHomeScreen() {
HomeScreen(navController = rememberNavController(),generalViewModel = viewModel()) HomeScreen(navController = rememberNavController(), generalViewModel = viewModel(),object: LoadingCallback{
override fun showLoading() {
}
override fun hideLoading() {
}
})
} }

View File

@ -89,6 +89,7 @@ fun MineScreen(
val currentTime = System.currentTimeMillis() val currentTime = System.currentTimeMillis()
if (currentTime - lastClickTime > 2000) { if (currentTime - lastClickTime > 2000) {
Toast.makeText(context, "再按一次退出应用", Toast.LENGTH_SHORT).show() Toast.makeText(context, "再按一次退出应用", Toast.LENGTH_SHORT).show()
Log.i("BackHandler", "MineScreen----->BackHandler")
lastClickTime = currentTime lastClickTime = currentTime
} else { } else {
(context as? Activity)?.finish() (context as? Activity)?.finish()

View File

@ -9,6 +9,8 @@ import androidx.datastore.preferences.core.intPreferencesKey
import androidx.datastore.preferences.core.stringPreferencesKey import androidx.datastore.preferences.core.stringPreferencesKey
import androidx.datastore.preferences.preferencesDataStore import androidx.datastore.preferences.preferencesDataStore
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
private val Context.storeData: DataStore<Preferences> by preferencesDataStore(name = "global_state") private val Context.storeData: DataStore<Preferences> by preferencesDataStore(name = "global_state")
@ -24,6 +26,7 @@ class GlobalStateManager(
private val GLOBAL_BIND_NOTIFY = booleanPreferencesKey("global_bind_notify") private val GLOBAL_BIND_NOTIFY = booleanPreferencesKey("global_bind_notify")
private val GLOBAL_UNBIND_NOTIFY = booleanPreferencesKey("global_unbind_notify") private val GLOBAL_UNBIND_NOTIFY = booleanPreferencesKey("global_unbind_notify")
private val GLOBAL_UPDATE_NOTIFY = booleanPreferencesKey("global_update_notify") private val GLOBAL_UPDATE_NOTIFY = booleanPreferencesKey("global_update_notify")
private val GLOBAL_UNI_UPDATE_NOTIFY = booleanPreferencesKey("global_uni_update_notify")
} }
suspend fun storeGlobalLoading(value: Boolean) { suspend fun storeGlobalLoading(value: Boolean) {
@ -39,7 +42,6 @@ class GlobalStateManager(
} }
suspend fun storeGlobalWxAuthorization(value: String) { suspend fun storeGlobalWxAuthorization(value: String) {
context.storeData.edit { preferences -> context.storeData.edit { preferences ->
preferences[GLOBAL_WX_AUTHORIZATION] = value preferences[GLOBAL_WX_AUTHORIZATION] = value
@ -116,4 +118,16 @@ class GlobalStateManager(
} }
} }
suspend fun storeGlobalUniUpdateNotify(value: Boolean) {
context.storeData.edit { preferences ->
preferences[GLOBAL_UNI_UPDATE_NOTIFY] = value
}
}
fun globalUniUpdateNotifyFlow(): Flow<Boolean?> {
return context.storeData.data.map {
preferences ->
preferences[GLOBAL_UNI_UPDATE_NOTIFY]
}
}
} }

View File

@ -18,11 +18,14 @@ import org.json.JSONObject
object PreferenceUtil { object PreferenceUtil {
private const val KEY_ACCESS_TOKEN = "access_token" private const val KEY_ACCESS_TOKEN = "access_token"
private const val KEY_X_TOKEN = "x_token" private const val KEY_X_TOKEN = "x_token"
private const val KEY_OAID = "android_oaid"
private const val KEY_LOGIN_INFO = "login_info" private const val KEY_LOGIN_INFO = "login_info"
private const val KEY_DB_VID = "bd_vid" private const val KEY_DB_VID = "bd_vid"
private const val KEY_USER_CONFIG = "user_config" private const val KEY_USER_CONFIG = "user_config"
private const val KEY_WX_CODE = "wx_code" private const val KEY_WX_CODE = "wx_code"
private const val KEY_USER_INFO = "user_info" private const val KEY_USER_INFO = "user_info"
private const val KEY_AGREEMENT = "agreement"
private const val KEY_WGT_VERSION = "uni_wgt_version"
@ -58,6 +61,25 @@ object PreferenceUtil {
return mmkv.decodeString(KEY_X_TOKEN, null) return mmkv.decodeString(KEY_X_TOKEN, null)
} }
fun saveOAID(oaid: String?) {
mmkv.encode(KEY_OAID, oaid)
}
fun getOAID(): String? {
return mmkv.decodeString(KEY_OAID, null)
}
fun saveAgreement(agreement: Boolean) {
mmkv.putBoolean(KEY_AGREEMENT, agreement)
}
fun getAgreement(): Boolean {
return mmkv.decodeBool(KEY_AGREEMENT, false)
}
fun saveUserConfig(config: UserConfigEntity) { fun saveUserConfig(config: UserConfigEntity) {
val resultJson = Gson().toJson(config) val resultJson = Gson().toJson(config)
mmkv.encode(KEY_USER_CONFIG, resultJson) mmkv.encode(KEY_USER_CONFIG, resultJson)
@ -112,6 +134,14 @@ object PreferenceUtil {
return mmkv.decodeString(KEY_WX_CODE, null) return mmkv.decodeString(KEY_WX_CODE, null)
} }
fun saveWgtVersion(wgtVersion: String) {
mmkv.encode(KEY_WGT_VERSION, wgtVersion)
}
fun getWgtVersion(): String? {
return mmkv.decodeString(KEY_WGT_VERSION, null)
}
//真实的服务器时间 //真实的服务器时间
fun serverTimeMillis(): Long { fun serverTimeMillis(): Long {
return System.currentTimeMillis() + timeDiff * 1000 return System.currentTimeMillis() + timeDiff * 1000
@ -152,9 +182,9 @@ object PreferenceUtil {
*/ */
fun clearLogin(){ fun clearLogin(){
mmkv.remove(KEY_ACCESS_TOKEN) mmkv.remove(KEY_ACCESS_TOKEN)
mmkv.remove(KEY_X_TOKEN)
mmkv.remove(KEY_LOGIN_INFO) mmkv.remove(KEY_LOGIN_INFO)
mmkv.remove(KEY_USER_INFO) mmkv.remove(KEY_USER_INFO)
mmkv.remove(KEY_X_TOKEN)
mmkv.remove(KEY_WX_CODE) mmkv.remove(KEY_WX_CODE)
} }

View File

@ -0,0 +1,32 @@
package com.img.rabbit.uni;
import android.content.Context;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.ImageView;
import com.img.rabbit.R;
import io.dcloud.feature.sdk.Interface.IDCUniMPAppSplashView;
public class UniMPSplashView implements IDCUniMPAppSplashView {
FrameLayout splashView;
@Override
public View getSplashView(Context context, String appid, String s1, String s2) {
splashView = new FrameLayout(context);
ImageView imageView = new ImageView(context);
imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
imageView.setImageResource(R.mipmap.ic_launch_wx_unimp);
splashView.addView(imageView, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
return splashView;
}
@Override
public void onCloseSplash(ViewGroup rootView) {
if(rootView != null)
rootView.removeView(splashView);
}
}

View File

@ -19,7 +19,7 @@ import java.util.Objects;
public class FileUtils { public class FileUtils {
private static FileUtils instance; private static FileUtils instance;
private static String packageName = "devcon"; private static String packageName = "dev";
// 文件缓存路径 // 文件缓存路径
private String CACHE_DIR; private String CACHE_DIR;
@ -36,6 +36,8 @@ public class FileUtils {
private File cacheEditImageDir; private File cacheEditImageDir;
private File cachePuzzleImageDir; private File cachePuzzleImageDir;
// 下载Uni小程序WGTD文件存放目录
private File uniWGTDir;
public static FileUtils getInstance() { public static FileUtils getInstance() {
if (instance == null) { if (instance == null) {
@ -76,6 +78,10 @@ public class FileUtils {
downloadDir = new File(cacheDir, "/download/"); downloadDir = new File(cacheDir, "/download/");
if (!downloadDir.exists()) if (!downloadDir.exists())
downloadDir.mkdirs(); downloadDir.mkdirs();
uniWGTDir = new File(cacheDir, "/uniApp/");
if (!uniWGTDir.exists())
uniWGTDir.mkdirs();
} }
public String getCACHE_DIR() { public String getCACHE_DIR() {
@ -92,10 +98,17 @@ public class FileUtils {
/** /**
* 获取下载目录 * 获取下载目录
*/ */
public File getCacheDownLoderDir() { public File getCacheDownLoadDir() {
return downloadDir; return downloadDir;
} }
/**
* 获取UniApp目录
*/
public File getCacheUniAppDir() {
return uniWGTDir;
}
/** /**
* 获取缓存图片目录 * 获取缓存图片目录
*/ */
@ -269,6 +282,17 @@ public class FileUtils {
return fileSizeString; return fileSizeString;
} }
//判断文件是否存在
public boolean fileIsExists(File strFile) {
try {
if (!strFile.exists()) {
return false;
}
} catch (Exception e) {
return false;
}
return true;
}
//判断文件是否存在 //判断文件是否存在
public boolean fileIsExists(String strFile) { public boolean fileIsExists(String strFile) {
try { try {

View File

@ -0,0 +1,280 @@
package com.img.rabbit.utils
import android.content.Context
import android.content.Intent
import android.util.Log
import android.widget.Toast
import androidx.compose.runtime.snapshots.SnapshotStateMap
import com.bumptech.glide.Glide
import com.github.gzuliyujiang.oaid.DeviceIdentifier
import com.img.rabbit.BuildConfig
import com.img.rabbit.bean.response.UniVersionEntity
import com.img.rabbit.config.Constants
import com.img.rabbit.pages.LoadingCallback
import com.img.rabbit.provider.storage.GlobalStateManager
import com.img.rabbit.provider.storage.PreferenceUtil
import com.img.rabbit.provider.storage.PreferenceUtil.getBDVID
import com.img.rabbit.provider.utils.HeadParamUtils.applicationContext
import com.img.rabbit.provider.utils.HeadParamUtils.getAppVersionName
import com.img.rabbit.uni.UniMPSplashView
import com.img.rabbit.viewmodel.GeneralViewModel
import io.dcloud.feature.sdk.DCUniMPSDK
import io.dcloud.feature.sdk.Interface.IUniMP
import io.dcloud.feature.unimp.config.UniMPOpenConfiguration
import io.dcloud.feature.unimp.config.UniMPReleaseConfiguration
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
import java.io.File
import kotlin.jvm.java
object UniAppUtils {
val uniMpPair = mutableMapOf<String?, IUniMP?>()
// private var wechatIUniMP: IUniMP? = null
// fun getWechatIUniMP(): IUniMP?{
// return wechatIUniMP
// }
/**
* 是否存在更新
*/
fun isUpdate(uniVersion: UniVersionEntity): Boolean{
return checkUpdate(uniVersion, PreferenceUtil.getWgtVersion()?:"0.0.0")
}
/**
* 是否强制更新
*/
private fun isUpdateForce(uniVersion: UniVersionEntity): Boolean{
return checkUpdate(uniVersion, PreferenceUtil.getWgtVersion()?:"0.0.0") && uniVersion.force
}
/**
* 是否需要下载强制更新或者不存在wgt文件
*/
fun isDownloadUniMp(uniVersion: UniVersionEntity): Boolean{
val wgtName = String.format("%s.wgt", uniVersion.unimp_id)
val wgtFile = File(FileUtils.getInstance().cacheUniAppDir.absolutePath, wgtName)
return isUpdateForce(uniVersion) || !(FileUtils.getInstance().fileIsExists(wgtFile) && FileUtils.getFileSize(wgtFile) > 0)
}
/**
* 检查更新
*/
private fun checkUpdate(uniVersion: UniVersionEntity, currentVersion: String): Boolean {
val version = uniVersion.version
if(version.isEmpty()){
return false
}
val newVersions = version.split("\\.")
val originVersions = currentVersion.split("\\.")
if(newVersions.size != 3){
return false
}
if(originVersions.size != 3){
return false
}
for (i in 0..2) {
if (Integer.parseInt(newVersions[i]) > Integer.parseInt(originVersions[i])) {
return true
} else if (Integer.parseInt(newVersions[i]) < Integer.parseInt(originVersions[i])) {
return false
}
}
return false
}
/**
* 分发uniMP下载更新与启动
*/
fun distributeUniMp(context: Context, uniVersion: UniVersionEntity,onResult:(loading: Boolean) -> Unit){
val isExists = DCUniMPSDK.getInstance().isExistsApp(uniVersion.unimp_id)
if(isExists){
//资源已释放,直接启动
startUniMp(context, uniVersion, onResult)
}else{
//资源未释放,先释放后启动
releaseWgt(uniVersion){ isSuccess, versionEntity ->
if(isSuccess){
startUniMp(context, versionEntity, onResult)
}else{
onResult(false)
}
}
}
}
private fun startUniMp(context: Context, uniVersion: UniVersionEntity, onResult:(loading: Boolean) -> Unit){
val uniMp = uniMpPair[uniVersion.unimp_id]
if(uniMp?.isRuning == true){
Log.i("UniAppUtils", "startUniMp: 运行中...")
uniMp.showUniMP()
}else{
Log.i("UniAppUtils", "startUniMp: 重新加载...")
val configuration = getUniMPOpenConfiguration()
if(uniVersion.unimp_type == "wx"){
configuration.splashClass = UniMPSplashView::class.java
}
uniMpPair[uniVersion.unimp_id] = DCUniMPSDK.getInstance().openUniMP(context, uniVersion.unimp_id, configuration)
}
onResult(false)
}
//释放资源
private fun releaseWgt(versionEntity: UniVersionEntity, onReleaseWgt: (isSuccess: Boolean, versionEntity: UniVersionEntity) -> Unit) {
val wgtName = String.format("%s.wgt", versionEntity.unimp_id)
val wgtFile = File(FileUtils.getInstance().cacheUniAppDir.absolutePath, wgtName)
val uniMPReleaseConfiguration = UniMPReleaseConfiguration().apply {
wgtPath = wgtFile.path
password = "6462"////没有密码可以不写
}
DCUniMPSDK.getInstance().releaseWgtToRunPath(versionEntity.unimp_id, uniMPReleaseConfiguration
) { code, _ ->
if (code == 1) {
//释放wgt完成
onReleaseWgt(true, versionEntity)
} else {
//释放wgt失败
Toast.makeText(applicationContext, "小程序加载失败,请清除缓存后重试!", Toast.LENGTH_SHORT).show()
onReleaseWgt(false, versionEntity)
}
}
}
/**
* 下载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)
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){
distributeUniMp(context, uniVersion) { loading ->}
}
}
/*
if (wechatIUniMP?.isRuning == true) {
Log.i("UniAppUtils", "startUniMp: 运行中...")
onResult(UniState.START_OFF, runFile.path, 1f)
return
}
if(isUpdateUniMP && DCUniMPSDK.getInstance().isExistsApp(uniMpID)){
//资源已释放,直接运行
loadUniMp(uniMpID, uniVersion.unimp_type){ loading ->
onResult(if(loading) UniState.START_OFF else UniState.START_FAIL, runFile.path, if(loading)1f else 0f)
}
return
}
if (FileUtils.getInstance().fileIsExists(wgtFile) && FileUtils.getFileSize(wgtFile) > 0 && !uniVersion.force) {
if (!isUpdateUniMP) {
//文件存在,且不是强制更新,直接启动
onResult(UniState.START_ON, wgtFile.path, 1f)
startUniMp(uniMpID, uniVersion.unimp_type, wgtFile){loading->
onResult(if(loading) UniState.START_OFF else UniState.START_FAIL, wgtFile.path, if(loading)1f else 0f)
}
} else {
//文件存在,有更新,提示用户更新
scope.launch { applicationContext?.let { GlobalStateManager(it).storeGlobalUniUpdateNotify(true) } }
}
return
}
//直接更新
downloadUniMp(scope, uniVersion){uniState, filePath, progress ->
if(uniState == UniState.FINISH_DOWNLOAD){
PreferenceUtil.saveWgtVersion(uniVersion.version)
startUniMp(uniVersion.unimp_id, uniVersion.unimp_type, wgtFile){loading->
onResult(if(loading) UniState.START_OFF else UniState.START_FAIL, wgtFile.path, if(loading)1f else 0f)
}
}else{
onResult(uniState, filePath, progress)
}
}
*/
}
fun isStartOn(uniVersion: UniVersionEntity): Boolean{
val uniMpID = uniVersion.unimp_id
val wgtName = String.format("%s.wgt", uniMpID)
val isUpdateUniMP: Boolean = checkUpdate(uniVersion, PreferenceUtil.getWgtVersion()?:"0.0.0")
val file = File(FileUtils.getInstance().cacheUniAppDir.absolutePath, wgtName)
return FileUtils.getInstance().fileIsExists(file) && FileUtils.getFileSize(file) > 0 && !isUpdateUniMP
}
//下载资源
private fun downloadUniMp(
scope: CoroutineScope,
uniVersion: UniVersionEntity,
onProgress:(state:UniMpUpdate,filePath: String?, progress: Float) -> Unit
) {
val uniMpID = uniVersion.unimp_id
val wgtName = String.format("%s.wgt", uniMpID)
val path = FileUtils.getInstance().cacheUniAppDir.absolutePath
UpdateUtils.download(
scope = scope,
url = uniVersion.url,
filePath = path,
fileName = wgtName,
onProgress = {progress->
onProgress(UniMpUpdate.DOWNLOAD_LOADING, null, progress.toFloat()/100f)
},
onFinish = {isSuccess, filePath ->
if(isSuccess){
Log.i("UniAppUtils", "下载完成---->updateUniMp: $filePath")
onProgress(UniMpUpdate.DOWNLOAD_FINISH, filePath, 1f)
}else{
Log.i("UniAppUtils", "下载失败---->updateUniMp: $filePath")
onProgress(UniMpUpdate.DOWNLOAD_FAIL, filePath, -1f)
}
}
)
}
private fun getUniMPOpenConfiguration(): UniMPOpenConfiguration{
return UniMPOpenConfiguration().apply {
extraData.put("x-token", PreferenceUtil.getXToken()?:"")
extraData.put("x-version", getAppVersionName()?:"")
extraData.put("x-platform", "android")
extraData.put("x-device-id", DeviceIdentifier.getAndroidID(applicationContext))
extraData.put("x-mobile-brand", android.os.Build.BRAND)
extraData.put("x-mobile-model", android.os.Build.MODEL)
extraData.put("x-channel", "rabbit_${ChannelUtils.getChannel(applicationContext)}")
extraData.put("x-package", BuildConfig.APPLICATION_ID)
extraData.put("x-click-id",getBDVID())
extraData.put("host", Constants.RELEASE_BASE_URL)
extraData.put("decrypt", Constants.AESDecrypt)
extraData.put("encrypt", Constants.Signature)
}
}
}
enum class UniMpUpdate{
DOWNLOAD_START,
DOWNLOAD_LOADING,
DOWNLOAD_FINISH,
DOWNLOAD_FAIL,
UPDATE_SHOW,
UPDATE_HIDE
}
enum class UniState{
START_DOWNLOAD,
DOWNLOAD_LOADING,
FINISH_DOWNLOAD,
FAIL_DOWNLOAD,
START_UPDATE,
UPDATE_LOADING,
FINISH_UPDATE,
FAIL_UPDATE,
START_ON,
START_OFF,
START_FAIL,
}

View File

@ -11,15 +11,15 @@ import java.io.File
object UpdateUtils { object UpdateUtils {
@SuppressLint("SetTextI18n") @SuppressLint("SetTextI18n")
fun download(scope: CoroutineScope, url: 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) {
var totalProgress = 0L var totalProgress = 0L
DownLoadUtils.getInstance() DownLoadUtils.getInstance()
.setReadTImeOut(10L) .setReadTImeOut(10L)
.setDeleteWhenException(false) .setDeleteWhenException(false)
.initUrl(url, null) .initUrl(url, null)
.setFilePath(FileUtils.getInstance().cacheDownLoderDir.absolutePath) .setFilePath(filePath)
.setFileName(AppUpdate.getFileNameFromUrl(url)) .setFileName(fileName)
.setActionCallBack( .setActionCallBack(
{ totalProgress = it }, { totalProgress = it },
{ {
@ -37,7 +37,7 @@ object UpdateUtils {
fun install(context: Context, apkFilePath: String) { fun install(context: Context, apkFilePath: String) {
try { try {
val apkFile = File(FileUtils.getInstance().cacheDownLoderDir, AppUpdate.getFileNameFromUrl(apkFilePath)) val apkFile = File(FileUtils.getInstance().cacheDownLoadDir, AppUpdate.getFileNameFromUrl(apkFilePath))
if (!apkFile.exists()) { if (!apkFile.exists()) {
return return
} }

View File

@ -13,14 +13,21 @@ import androidx.compose.runtime.mutableStateOf
import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope
import com.img.rabbit.bean.response.UniVersionEntity
import com.img.rabbit.bean.response.VersionEntity
import com.img.rabbit.config.Constants import com.img.rabbit.config.Constants
import com.img.rabbit.provider.api.ApiManager import com.img.rabbit.provider.api.ApiManager
import com.img.rabbit.provider.storage.GlobalStateManager
import com.img.rabbit.provider.storage.PreferenceUtil import com.img.rabbit.provider.storage.PreferenceUtil
import com.img.rabbit.utils.MMKVUtils.mmkv import com.img.rabbit.utils.MMKVUtils.mmkv
import com.tencent.mm.opensdk.openapi.IWXAPI import com.tencent.mm.opensdk.openapi.IWXAPI
import com.tencent.mm.opensdk.openapi.WXAPIFactory import com.tencent.mm.opensdk.openapi.WXAPIFactory
import kotlinx.coroutines.DelicateCoroutinesApi import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@SuppressLint("ObsoleteSdkInt") @SuppressLint("ObsoleteSdkInt")
@ -53,9 +60,9 @@ class GeneralViewModel(application: Application) : AndroidViewModel(application)
private val _agreementStatus = MutableLiveData<Boolean>() private val _agreementStatus = MutableLiveData<Boolean>()
val agreementStatus: LiveData<Boolean> = _agreementStatus val agreementStatus: LiveData<Boolean> = _agreementStatus
private fun getIsAgreement(): Boolean{
return mmkv.getBoolean("isAgreement", false) var currentUpdateUniMp: UniVersionEntity? = null
}
init { init {
// 注册网络监听 // 注册网络监听
@ -71,7 +78,7 @@ class GeneralViewModel(application: Application) : AndroidViewModel(application)
// 初始化状态 // 初始化状态
_networkStatus.value = isNetworkAvailable() _networkStatus.value = isNetworkAvailable()
// 初始化隐私政策状态 // 初始化隐私政策状态
_agreementStatus.value = getIsAgreement() _agreementStatus.value = PreferenceUtil.getAgreement()
// 初始化微信API // 初始化微信API
initWXApi(application) initWXApi(application)
@ -94,7 +101,7 @@ class GeneralViewModel(application: Application) : AndroidViewModel(application)
} }
fun setIsAgreement(agreement: Boolean){ fun setIsAgreement(agreement: Boolean){
mmkv.putBoolean("isAgreement", agreement) PreferenceUtil.saveAgreement(agreement)
_agreementStatus.value = agreement _agreementStatus.value = agreement
} }

View File

@ -15,12 +15,10 @@ import com.g.gysdk.GYManager
import com.g.gysdk.GYResponse import com.g.gysdk.GYResponse
import com.g.gysdk.GyCallBack import com.g.gysdk.GyCallBack
import com.g.gysdk.GyConfig import com.g.gysdk.GyConfig
import com.github.gzuliyujiang.oaid.DeviceIdentifier
import com.google.gson.JsonObject import com.google.gson.JsonObject
import com.img.rabbit.bean.local.ErrorBean import com.img.rabbit.bean.local.ErrorBean
import com.img.rabbit.bean.local.OnekeyPreLogin import com.img.rabbit.bean.local.OnekeyPreLogin
import com.img.rabbit.bean.response.LoginInfoEntity import com.img.rabbit.bean.response.LoginInfoEntity
import com.img.rabbit.config.Constants
import com.img.rabbit.provider.api.ApiManager import com.img.rabbit.provider.api.ApiManager
import com.img.rabbit.provider.api.ResultVo import com.img.rabbit.provider.api.ResultVo
import com.img.rabbit.provider.storage.GlobalStateManager import com.img.rabbit.provider.storage.GlobalStateManager
@ -28,7 +26,6 @@ import com.img.rabbit.provider.storage.PreferenceUtil
import com.img.rabbit.utils.MMKVUtils import com.img.rabbit.utils.MMKVUtils
import com.tencent.mm.opensdk.modelmsg.SendAuth import com.tencent.mm.opensdk.modelmsg.SendAuth
import com.tencent.mm.opensdk.openapi.IWXAPI import com.tencent.mm.opensdk.openapi.IWXAPI
import com.tencent.mm.opensdk.openapi.WXAPIFactory
import kotlinx.coroutines.DelicateCoroutinesApi import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.GlobalScope
@ -36,7 +33,6 @@ import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import okhttp3.RequestBody.Companion.toRequestBody import okhttp3.RequestBody.Companion.toRequestBody
import okhttp3.internal.platform.PlatformRegistry.applicationContext
class LoginViewModel : BaseViewModel() { class LoginViewModel : BaseViewModel() {
private val TAG = "LoginViewModel" private val TAG = "LoginViewModel"

View File

@ -13,6 +13,7 @@ import kotlinx.coroutines.launch
class WXEntryActivity : WXCallbackActivity() { class WXEntryActivity : WXCallbackActivity() {
override fun onResp(resp: BaseResp?) { override fun onResp(resp: BaseResp?) {
if (resp?.type == ConstantsAPI.COMMAND_SENDAUTH) { if (resp?.type == ConstantsAPI.COMMAND_SENDAUTH) {
when (resp.errCode) { when (resp.errCode) {

View File

@ -0,0 +1,12 @@
package com.img.rabbit.wxapi;
import io.dcloud.feature.payment.weixin.AbsWXPayCallbackActivity;
public class WXPayEntryActivity extends AbsWXPayCallbackActivity{
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

View File

@ -24,6 +24,9 @@ android.nonTransitiveRClass=true
android.injected.testOnly=false android.injected.testOnly=false
# ???????Support?
android.enableJetifier=true
# ???? # ????
GETUI_APPID=40qbPjPkYs7TnVAYCX0Ig6 GETUI_APPID=40qbPjPkYs7TnVAYCX0Ig6
GT_INSTALL_CHANNEL=general GT_INSTALL_CHANNEL=general

View File

@ -13,6 +13,7 @@ constraintlayout = "2.1.4"
splashscreen = "1.0.1" splashscreen = "1.0.1"
datastoreCore = "1.2.0" datastoreCore = "1.2.0"
datastorePreferences = "1.1.1" datastorePreferences = "1.1.1"
multidex = "2.0.1"
# Compose Version # Compose Version
composeBom = "2024.05.00" composeBom = "2024.05.00"
@ -52,10 +53,19 @@ umengUmsdkAsms = "1.8.7.2"
umengUmsdkApm = "2.0.6" umengUmsdkApm = "2.0.6"
umengUmsdkShareCore = "7.3.7" umengUmsdkShareCore = "7.3.7"
tencentHelper = "3.0.6" tencentHelper = "3.0.6"
#oaid #oaid version
android_cn_oaid = "4.2.12" 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"
# Uni小程序相关依赖 version
recyclerview = "1.0.0"
legacySupportV4 = "1.0.0"
appcompat1 = "1.0.0"
fastJson = "1.2.83"
fresco = "2.5.0"
glide = "4.9.0"
webkit = "1.5.0"
[libraries] [libraries]
@ -70,6 +80,7 @@ kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serializa
androidx-navigation-compose = { group = "androidx.navigation", name = "navigation-compose", version.ref = "navigationCompose" } androidx-navigation-compose = { group = "androidx.navigation", name = "navigation-compose", version.ref = "navigationCompose" }
androidx-datastore-core = { group = "androidx.datastore", name = "datastore-core", version.ref = "datastoreCore" } androidx-datastore-core = { group = "androidx.datastore", name = "datastore-core", version.ref = "datastoreCore" }
androidx-datastore-preferences = { module = "androidx.datastore:datastore-preferences", version.ref = "datastorePreferences" } androidx-datastore-preferences = { module = "androidx.datastore:datastore-preferences", version.ref = "datastorePreferences" }
multidex = { module = "androidx.multidex:multidex", version.ref = "multidex" }
# Compose dependencies # Compose dependencies
androidx-compose-bom = { module = "androidx.compose:compose-bom", version.ref = "composeBom" } androidx-compose-bom = { module = "androidx.compose:compose-bom", version.ref = "composeBom" }
@ -124,8 +135,22 @@ tencent-helper = { module = "com.tencent.vasdolly:helper", version.ref = "tencen
android_cn_oaid = { module = "com.github.gzu-liyujiang:Android_CN_OAID", version.ref = "android_cn_oaid" } android_cn_oaid = { module = "com.github.gzu-liyujiang:Android_CN_OAID", version.ref = "android_cn_oaid" }
#Decrypt #Decrypt
fastaes = { module = "io.github.billywei01:fastaes", version.ref = "fastaes" } fastaes = { module = "io.github.billywei01:fastaes", version.ref = "fastaes" }
#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" }
# Uni小程序相关依赖
androidx-recyclerview = { module = "androidx.recyclerview:recyclerview", version.ref = "recyclerview" }
androidx-legacy-support-v4 = { module = "androidx.legacy:legacy-support-v4", version.ref = "legacySupportV4" }
#appcompat = { module = "androidx.appcompat:appcompat", version.ref = "appcompat1" }
alibaba-fastjson = { module = "com.alibaba:fastjson", version.ref = "fastJson" }
facebook-fresco = { module = "com.facebook.fresco:fresco", version.ref = "fresco" }
#noinspection Aligned16KB
facebook-animated-gif = { module = "com.facebook.fresco:animated-gif", version.ref = "fresco" }
bumptech-glide = { module = "com.github.bumptech.glide:glide", version.ref = "glide" }
androidx-webkit = { module = "androidx.webkit:webkit", version.ref = "webkit" }
[plugins] [plugins]
android-application = { id = "com.android.application", version.ref = "agp" } android-application = { id = "com.android.application", version.ref = "agp" }
org-jetbrains-kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" } org-jetbrains-kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }