package com.aiosman.riderpro import android.Manifest import android.app.AlertDialog import android.app.DownloadManager import android.app.NotificationChannel import android.app.NotificationManager import android.content.Context import android.content.Intent import android.content.IntentFilter import android.content.pm.PackageManager import android.net.Uri import android.os.Build import android.os.Bundle import android.os.Environment import android.util.Log import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.activity.enableEdgeToEdge import androidx.activity.result.contract.ActivityResultContracts import androidx.annotation.RequiresApi import androidx.compose.animation.AnimatedContentScope import androidx.compose.animation.ExperimentalSharedTransitionApi import androidx.compose.animation.SharedTransitionScope import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.padding import androidx.compose.material3.AlertDialog import androidx.compose.material3.BasicAlertDialog import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.compositionLocalOf import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import androidx.core.content.ContextCompat import androidx.core.view.WindowCompat import androidx.lifecycle.ProcessLifecycleOwner import androidx.lifecycle.lifecycleScope import androidx.navigation.NavHostController import cn.jiguang.api.utils.JCollectionAuth import cn.jpush.android.api.JPushInterface import com.aiosman.riderpro.data.AccountService import com.aiosman.riderpro.data.AccountServiceImpl import com.aiosman.riderpro.data.UserService import com.aiosman.riderpro.data.UserServiceImpl import com.aiosman.riderpro.model.ApkInstallReceiver import com.aiosman.riderpro.model.UpdateInfo import com.aiosman.riderpro.ui.Navigation import com.aiosman.riderpro.ui.NavigationRoute import com.aiosman.riderpro.ui.dialogs.CheckUpdateDialog import com.aiosman.riderpro.ui.navigateToPost import com.aiosman.riderpro.ui.post.NewPostViewModel import com.google.accompanist.systemuicontroller.rememberSystemUiController import com.google.firebase.analytics.FirebaseAnalytics import com.google.gson.Gson import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import okhttp3.OkHttpClient import okhttp3.Request class MainActivity : ComponentActivity() { // Firebase Analytics private lateinit var analytics: FirebaseAnalytics private val scope = CoroutineScope(Dispatchers.Main) val context = this private val apkInstallReceiver = ApkInstallReceiver() // 请求通知权限 private val requestPermissionLauncher = registerForActivityResult( ActivityResultContracts.RequestPermission(), ) { isGranted: Boolean -> if (isGranted) { // FCM SDK (and your app) can post notifications. } else { } } @RequiresApi(Build.VERSION_CODES.P) fun getVersionCode(context: Context): Int { val packageManager = context.packageManager val packageInfo = packageManager.getPackageInfo(context.packageName,0) return packageInfo.longVersionCode.toInt() } @RequiresApi(Build.VERSION_CODES.P) private fun checkUpdate() { lifecycleScope.launch(Dispatchers.IO) { try { val client = OkHttpClient() val request = Request.Builder() .url("${ConstVars.BASE_SERVER}/static/update/beta/version.json") .build() val response = client.newCall(request).execute() if (response.isSuccessful) { val responseBody = response.body?.string() val updateInfo = Gson().fromJson(responseBody, UpdateInfo::class.java) val versionCode = getVersionCode(context) // 检查是否有新版本 Log.d("MainActivity", "Current version code: $versionCode") Log.d("MainActivity", "Server version code: ${updateInfo.versionCode}") if (updateInfo.versionCode > versionCode || true) { withContext(Dispatchers.Main) { showUpdateDialog(updateInfo) } } } } catch (e: Exception) { // 处理网络请求失败 e.printStackTrace() } } } private fun showUpdateDialog(updateInfo: UpdateInfo) { val builder = AlertDialog.Builder(this) builder.setTitle("🔴 ${getString(R.string.update_find_new_version)} v${updateInfo.versionName}") builder.setMessage(updateInfo.updateContent) builder.setPositiveButton(R.string.update_update_now) { dialog, _ -> downloadApk(updateInfo.downloadUrl,updateInfo.versionName) dialog.dismiss() } if (!updateInfo.forceUpdate) { builder.setNegativeButton(R.string.update_later) { dialog, _ -> dialog.dismiss() } } else { builder.setCancelable(false) } builder.show() } private fun downloadApk(downloadUrl: String,versionName: String) { // 使用 DownloadManager 下载 APK 文件 val downloadManager = getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager val request = DownloadManager.Request(Uri.parse(downloadUrl)) val apkFileName = "RiderPro_v${versionName.replace(".","_")}.apk" request.setMimeType("application/vnd.android.package-archive") // 添加这行代码 request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, apkFileName) request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED) val downloadId = downloadManager.enqueue(request) Log.d("MainActivity", "Download enqueued with ID: $downloadId") } /** * 获取账号信息 */ private suspend fun getAccount(): Boolean { val accountService: AccountService = AccountServiceImpl() try { val resp = accountService.getMyAccount() return true } catch (e: Exception) { return false } } @OptIn(ExperimentalMaterial3Api::class) @RequiresApi(Build.VERSION_CODES.P) override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // 监听应用生命周期 ProcessLifecycleOwner.get().lifecycle.addObserver(MainActivityLifecycleObserver()) // 创建通知渠道 createNotificationChannel() // 沉浸式状态栏 WindowCompat.setDecorFitsSystemWindows(window, false) // 初始化 Places SDK // 初始化 Firebase Analytics // analytics = Firebase.analytics // 请求通知权限 askNotificationPermission() // 加载一些本地化的配置 AppStore.init(this) JPushInterface.setDebugMode(true); // 调整点一:初始化代码前增加setAuth调用 JCollectionAuth.setAuth(this, true) JPushInterface.init(this) if (AppState.darkMode) { window.decorView.setBackgroundColor(android.graphics.Color.BLACK) } enableEdgeToEdge() // // 注册广播接收器 // val intentFilter = IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE) // registerReceiver(apkInstallReceiver, intentFilter, RECEIVER_NOT_EXPORTED) // // checkUpdate() // 初始化腾讯云通信 SDK scope.launch { // 检查是否有登录态 val isAccountValidate = getAccount() var startDestination = NavigationRoute.Login.route // 如果有登录态,且记住登录状态,且账号有效,则初始化 FCM,下一步进入首页 if (AppStore.token != null && AppStore.rememberMe && isAccountValidate) { AppState.initWithAccount(scope, this@MainActivity) startDestination = NavigationRoute.Index.route } setContent { CheckUpdateDialog() Navigation(startDestination) { navController -> // 处理带有 postId 的通知点击 val postId = intent.getStringExtra("POST_ID") var commentId = intent.getStringExtra("COMMENT_ID") val action = intent.getStringExtra("ACTION") if (action == "newFollow") { navController.navigate(NavigationRoute.Followers.route) return@Navigation } if (action == "followCount") { navController.navigate(NavigationRoute.Followers.route) return@Navigation } if (action == "TRTC_NEW_MESSAGE") { val userService:UserService = UserServiceImpl() val sender = intent.getStringExtra("SENDER") sender?.let { scope.launch { try { val profile = userService.getUserProfileByTrtcUserId(it) navController.navigate(NavigationRoute.Chat.route.replace( "{id}", profile.id.toString() )) }catch (e:Exception){ e.printStackTrace() } } } return@Navigation } if (commentId == null) { commentId = "0" } if (postId != null) { Log.d("MainActivity", "Navigation to Post$postId") navController.navigateToPost( id = postId.toInt(), highlightCommentId = commentId.toInt(), initImagePagerIndex = 0 ) } // 处理分享过来的图片 if (intent?.action == Intent.ACTION_SEND || intent?.action == Intent.ACTION_SEND_MULTIPLE) { val imageUris: List? = if (intent.action == Intent.ACTION_SEND) { listOf(intent.getParcelableExtra(Intent.EXTRA_STREAM)!!) } else { intent.getParcelableArrayListExtra(Intent.EXTRA_STREAM) } NewPostViewModel.asNewPostWithImageUris(imageUris!!.map { it.toString() }) navController.navigate(NavigationRoute.NewPost.route) } } } } } override fun onDestroy() { super.onDestroy() // 取消注册广播接收器 unregisterReceiver(apkInstallReceiver) } /** * 请求通知权限 */ private fun askNotificationPermission() { // This is only necessary for API level >= 33 (TIRAMISU) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { if (ContextCompat.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS) == PackageManager.PERMISSION_GRANTED ) { // FCM SDK (and your app) can post notifications. } else if (shouldShowRequestPermissionRationale(android.Manifest.permission.POST_NOTIFICATIONS)) { } else { // Directly ask for the permission requestPermissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS) } } } /** * 创建通知渠道 */ private fun createNotificationChannel() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { val channelId = ConstVars.MOMENT_LIKE_CHANNEL_ID val channelName = ConstVars.MOMENT_LIKE_CHANNEL_NAME val channel = NotificationChannel(channelId, channelName, NotificationManager.IMPORTANCE_DEFAULT) val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager notificationManager.createNotificationChannel(channel) } } } val LocalNavController = compositionLocalOf { error("NavController not provided") } @OptIn(ExperimentalSharedTransitionApi::class) val LocalSharedTransitionScope = compositionLocalOf { error("SharedTransitionScope not provided") } val LocalAnimatedContentScope = compositionLocalOf { error("AnimatedContentScope not provided") }