rabbit-android/app/src/main/java/com/img/rabbit/MainActivity.kt

500 lines
23 KiB
Kotlin
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package com.img.rabbit
import android.annotation.SuppressLint
import android.app.Activity
import android.os.Bundle
import android.util.Log
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableFloatStateOf
import androidx.compose.runtime.mutableLongStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.viewmodel.compose.viewModel
import com.google.accompanist.permissions.ExperimentalPermissionsApi
import com.img.rabbit.bean.response.UniVersionEntity
import com.img.rabbit.components.CenterToast
import com.img.rabbit.components.GlobalToast
import com.img.rabbit.pages.LoadingCallback
import com.img.rabbit.pages.LoginScreen
import com.img.rabbit.pages.LoginScreenType
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.TipsUniMpToPageDialog
import com.img.rabbit.pages.dialog.UpdateDialog
import com.img.rabbit.pages.screen.SplashScreenContent
import com.img.rabbit.provider.storage.PreferenceUtil
import com.img.rabbit.utils.AppEventBus
import com.img.rabbit.utils.AppUpdate
import com.img.rabbit.utils.FileUtils
import com.img.rabbit.utils.GlobalEvent
import com.img.rabbit.utils.GlobalEventBus
import com.img.rabbit.utils.LoginBindEvent
import com.img.rabbit.utils.UniAppUtils
import com.img.rabbit.utils.UniAppUtils.distributeUniMp
import com.img.rabbit.utils.UpdateUtils
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 io.dcloud.feature.sdk.DCUniMPSDK
import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import org.json.JSONObject
class MainActivity : ComponentActivity(), LoadingCallback {
private lateinit var generalViewModel: GeneralViewModel
private var isRequestConfigJumpUniMp = true//请求完用户配置后是否跳转小程序页面
@OptIn(DelicateCoroutinesApi::class, ExperimentalPermissionsApi::class)
@SuppressLint("UnrememberedMutableState", "CoroutineCreationDuringComposition",
"SourceLockedOrientationActivity"
)
override fun onCreate(savedInstanceState: Bundle?) {
val splashScreen = installSplashScreen()
super.onCreate(savedInstanceState)
// 启用Edge-to-Edge模式沉浸模式
enableEdgeToEdge()
setContent {
val context = LocalContext.current
val coroutineScope = rememberCoroutineScope()
val loginViewModel: LoginViewModel = viewModel()
val splashViewModel: SplashViewModel = viewModel()
val reportViewModel:ReportViewModel = viewModel()
generalViewModel = ViewModelProvider(this, ViewModelProvider.AndroidViewModelFactory.getInstance(application))[GeneralViewModel::class.java]
var lastClickTime by remember { mutableLongStateOf(0L) }
// UniMp小程序资源下载更新实体
var downloadUniEntity by remember { mutableStateOf<UniVersionEntity?>(null) }
var updateUniEntity by remember { mutableStateOf<UniVersionEntity?>(null) }
// 下载更新进度App、UniMp资源下载更新
val progressForApp = mutableFloatStateOf(0f)
val progressForUniMpDownload = remember { mutableFloatStateOf(0f) }
val progressForUniMpUpdate = mutableFloatStateOf(0f)
// 是否开始下载App标记下载状态
val isDownloadingForApp = mutableStateOf(false)
// 是否开始下载UniApp更新标记下载状态
val isDownloadingForUniUpdate = remember { mutableStateOf(false) }
// 是否全局加载中(Progress加载框)
val isGlobalLoading by generalViewModel.isLoading.collectAsState()
// 登录提示
var showLoginDialog by remember { mutableStateOf(false) }
// 软件更新提示
var showUpdateDialog by remember { mutableStateOf(false) }
// 下载小程序资源(在跳转指定页面时,未下载资源需要提示)
var showUniDownloadDialog by remember { mutableStateOf(false) }
// 小程序资源更新提示
var showUniUpdateDialog by remember { mutableStateOf(false) }
// 是否加载splashScreen完成
val isSplashDone = remember { mutableStateOf(false) }
// 是否启动小程序完成
val isUniMpDone = remember { mutableStateOf(false) }
// 加载UniMp小程序
var isLoadingUniMp by remember { mutableStateOf(false) }
// 是否退出登录
var isLogout by remember { mutableStateOf(false) }
// 加载显示MainScreen
var showMainScreen by remember { mutableStateOf(false) }
// 延迟加载MainScreen通知
var delayLoadingMainScreen by remember { mutableStateOf(false) }
// 设置启动页显示条件
splashScreen.setKeepOnScreenCondition {
splashViewModel.isLoading.value // 当为 true 时,启动页不消失
}
//以下为流事件收集订阅
LaunchedEffect(Unit) {
GlobalEventBus.events.collect { event ->
when (event) {
// 软件更新提示
is GlobalEvent.ShowAppUpdateNotify -> {
val userConfigEntity = PreferenceUtil.getUserConfig()
if(userConfigEntity != null){
showUpdateDialog = true
}
}
// 小程序资源下载提示
is GlobalEvent.ShowUniDownloadNotify -> {
downloadUniEntity = event.entity
showUniDownloadDialog = true
}
// 小程序资源更新提示
is GlobalEvent.ShowUniUpdateNotify -> {
updateUniEntity = event.entity
showUniUpdateDialog = true
}
// 启动小程序
is GlobalEvent.StartupUniMpNotify -> {
downloadUniEntity = PreferenceUtil.getUserConfig()?.config?.uniVersionEntity?.get(2)//添加需要下载的资源
isLoadingUniMp = true//加载启动小程序(如无小程序置于自动下载,需要更新自动更新,已有小程序,直接启动)
}
// 登录提示
is GlobalEvent.ShowLoginNotify -> {
showLoginDialog = true
}
// ... 可以处理其他事件
else -> {}
}
}
}
// 处理全局事件类似与EventBus订阅
LaunchedEffect(Unit) {
AppEventBus.events.collect { event ->
when (event) {
is LoginBindEvent.Login -> {
if(!event.isLogin){
loginViewModel.reset()
isLogout = true
}else{
isLogout = false
}
isRequestConfigJumpUniMp = false
loginViewModel.requestUserConfig()
}
is LoginBindEvent.Bind -> {
isRequestConfigJumpUniMp = false
loginViewModel.requestUserConfig()
}
}
}
}
// UniMp事件监听
LaunchedEffect(Unit) {
DCUniMPSDK.getInstance().setOnUniMPEventCallBack { appid, event, data, callback ->//appid, event, data, callback ->
Log.i("MainScreen", "onUniMPEventCallBack: $event")
if("start_combo_pay" == event){
//拉起微信小程序来支付
val weixinMpOriId = JSONObject(data.toString()).optString("weixinMpOriId")
val outTradeNo = JSONObject(data.toString()).optString("outTradeNo")
UniAppUtils.startUniPay(
api = generalViewModel.api,
weixinMpOriId = weixinMpOriId,
outTradeNo = outTradeNo
)
}else if("unimp_BackPress_goMe" == event){
//这里处理切换到我的页面
coroutineScope.launch {
GlobalEventBus.emit(
GlobalEvent.JumpMineNotify)
}
UniAppUtils.getCurrentUniMp()?.closeUniMP()
}else if("unimp_BackPress_backbutton" == event){
//这里需要处理手势、虚拟返回
val currentTime = System.currentTimeMillis()
if (currentTime - lastClickTime > 2000) {
CenterToast.show("再按一次退出应用")
lastClickTime = currentTime
} else {
(context as? Activity)?.finish()
}
}
}
}
// 同意隐私政策后,获取服务器时间(系统时间同步完成后获取用户配置信息,配置信息同步完成才能启动)
LaunchedEffect(generalViewModel.agreementStatus.value) {
if (generalViewModel.agreementStatus.value){
loginViewModel.setIsPolicyAgreement(true)
//获取服务器时间
generalViewModel.getServerTime()
}
}
// 系统时间同步完成,获取用户配置信息
LaunchedEffect(generalViewModel.serverTime.value) {
if (generalViewModel.serverTime.value != null){
// 获取用户配置
loginViewModel.requestUserConfig(isInitConfig = true)
}
}
// 用户配置信息获取成功,启动应用(获取用户信息)
LaunchedEffect(loginViewModel.userConfigResult.value) {
if (loginViewModel.userConfigResult.value != null){
if(isRequestConfigJumpUniMp){
if( PreferenceUtil.getUserConfig()?.config?.isUniMpOpen == true
&& PreferenceUtil.getUserConfig()?.config?.uniVersionEntity?.find { it.unimp_type == "uniapp" } != null){
downloadUniEntity = PreferenceUtil.getUserConfig()?.config?.uniVersionEntity?.get(2)//添加需要下载的资源
isLoadingUniMp = true//加载启动小程序(如无小程序置于自动下载,需要更新自动更新,已有小程序,直接启动)
}else{
isSplashDone.value = true
}
}
// 用户配置信息获取成功
loginViewModel.requestUserInfo()
loginViewModel.userConfigResult.value = null
}
}
//提示下载小程序资源(在跳转指定页面时,未下载资源需要提示)
LaunchedEffect(showUniDownloadDialog) {
if(showUniDownloadDialog) {
downloadUniEntity?.let{
UniAppUtils.dealUniMpDownloadLaunchForStartUp(
context = context,
scope = coroutineScope,
uniMp = it,
reportViewModel = reportViewModel
){ result, progress ->
when (result) {
-1 -> {
//下载失败
isUniMpDone.value = false
isSplashDone.value = true
}
1 -> {
//下载中
progressForUniMpDownload.floatValue = progress ?: 0f
}
2 -> {
//下载完成(关闭下载框)
showUniDownloadDialog = false
}
3 -> {
//启动成功
isUniMpDone.value = true
isSplashDone.value = true
}
4 -> {
//启动失败
isUniMpDone.value = false
isSplashDone.value = true
}
}
}
}
}
}
//启动小程序(资源存在并且最新则直接启动,否则执行下载后启动)
LaunchedEffect(isLoadingUniMp) {
if(isLoadingUniMp){
downloadUniEntity?.let { uniMp ->
if (UniAppUtils.isDownloadUniMp(uniMp)) {
//启动小程序资源下载
showUniDownloadDialog = true
} else {
// 执行分发逻辑(异步操作)
distributeUniMp(context, uniMp, reportViewModel) {
isUniMpDone.value = it
isSplashDone.value = true
}
}
}
isLoadingUniMp = false
}
}
//启动MainScreen(如果启动小程序则延迟2秒启动否则直接启动主界面)
LaunchedEffect(delayLoadingMainScreen) {
if (delayLoadingMainScreen) {
delay(2000) // 延迟 2 秒
showMainScreen = true
}
}
// 页面显示逻辑
AppTheme {
//显示启动页
SplashScreenContent(splashDone = isSplashDone, generalViewModel = generalViewModel){
if(isUniMpDone.value){
//延迟加载MainScreen
delayLoadingMainScreen = true
}else{
showMainScreen = true
}
}
// 显示主界面因为UniMp小程序启动时要实现SplashScreen图片与小程序的UniMPSplashView无缝切换所以延迟2秒
if (showMainScreen) {
MainScreen(
generalViewModel = generalViewModel,
loginViewModel = loginViewModel,
isUniMpDone = isUniMpDone.value
)
}
// 退出登录后,显示登录页
if(isLogout){
// 同意隐私协议政策,检验是否有一键登录权限
loginViewModel.oneKeyLoginForGeTuiSdk(context as Activity) { isAllowShowOneKeyScreen ->
if (isAllowShowOneKeyScreen) {
loginViewModel.loginScreenType.value = LoginScreenType.LOGIN_ONE_KEY
} else {
// 检验是否有一键登录权限失败,显示验证码登录
loginViewModel.loginScreenType.value = LoginScreenType.LOGIN_CAPTCHA
}
}
// 显示登录页
LoginScreen(generalViewModel = generalViewModel, loginViewModel = loginViewModel, isVisibilityBreak = false)
}
//提示登录
if(showLoginDialog){
TipsDialog(
title = "温馨提示",
content1 = "在使用我们功能之前,需要您先登录!",
content2 = null,
cancel = "取消",
confirm = "立即登录",
data = null,
onStatusChange = { status, isCancel, _ ->
if(!isCancel){
// 隐藏TabBar
generalViewModel.apply {
setNavigationBarVisible(false)
navController.navigate("login?type=${LoginViewModel.JumpLoginType.FROM_ADD.type}")
}
}
showLoginDialog = status
}
)
}
//下载小程序资源直接显示下载进度1、启动时进入小程序未下载资源需要提示2、在跳转指定页面时未下载资源需要提示
if(showUniDownloadDialog){
downloadUniEntity?.let {
TipsUniMpToPageDialog(
title = "下载资源",
content1 = "需要下载完资源才能运行,请稍后...",
content2 = null,
downProgress = progressForUniMpDownload
)
}
}
//提示更新小程序资源
if(showUniUpdateDialog){
updateUniEntity?.let {
TipsUniMpDialog(
title = "资源包更新",
content1 = "是否确定更新资源包",
content2 = null,
cancel = "取消",
confirm = "确定",
scope = coroutineScope,
data = it,
isStartDown = isDownloadingForUniUpdate,
downProgress = progressForUniMpUpdate,
onStatusChange = { isUpdateFinish, isCancel, data ->
if(!isUpdateFinish && !isCancel && data != null){
isDownloadingForUniUpdate.value = true
}else{
showUniUpdateDialog = false
}
}
)
}
}
//App更新提示
if(showUpdateDialog){
val versionEntity = PreferenceUtil.getUserConfig()?.config?.versionEntity
versionEntity?.let{ versionEntity->
UpdateDialog(
title = versionEntity.title,
newVersion = "V${versionEntity.version}",
desc = versionEntity.description,
url = versionEntity.url,
isForce = versionEntity.force,
isStartDown = isDownloadingForApp,
downProgress = progressForApp
){ state, isCancel, url ->
if(isCancel) {
showUpdateDialog = false
}
if(!isCancel){
isDownloadingForApp.value = true
UpdateUtils.download(
scope = coroutineScope,
url = url,
filePath = FileUtils.instance?.cacheDownLoadDir?.absolutePath?:"",
fileName = AppUpdate.getFileNameFromUrl(url),
onProgress = {progress->
progressForApp.floatValue = progress.toFloat()/100f
},
onFinish = {isSuccess, filePath ->
if(isSuccess){
filePath?.let {
UpdateUtils.install(context,it)
}
if(!versionEntity.force) {
showUpdateDialog = state
}
}
}
)
}
}
}
}
//全局加载提示
if(isGlobalLoading){
Box(
modifier = Modifier
.fillMaxSize()
.background(Color.Black.copy(alpha = 0.5f))
) {
CircularProgressIndicator(
color = Color.White,
modifier = Modifier.align(Alignment.Center)
)
}
}
}
// 模拟加载过程500毫秒后关闭启动页
LaunchedEffect(Unit) {
delay(500L)
splashViewModel.setLoading(false)
}
}
}
override fun showLoading() {
lifecycleScope.launch {
generalViewModel.setLoading(true)
}
}
override fun hideLoading() {
lifecycleScope.launch {
delay(2*1000L)
generalViewModel.setLoading(false)
}
}
}
@Composable
private fun AppTheme(content: @Composable () -> Unit) {
// 使用Material3主题
MaterialTheme {
// 界面内容
content()
// 全局居中Toast
GlobalToast()
}
}