软件内版本检测更新

This commit is contained in:
2024-10-26 06:02:30 +08:00
parent ed66f15be7
commit e38c36aa2c
4 changed files with 150 additions and 0 deletions

View File

@@ -114,6 +114,8 @@ dependencies {
api ("com.tencent.imsdk:imsdk-plus:8.1.6116") api ("com.tencent.imsdk:imsdk-plus:8.1.6116")
implementation("io.github.rroohit:ImageCropView:3.0.1") implementation("io.github.rroohit:ImageCropView:3.0.1")
implementation("androidx.core:core-splashscreen:1.0.1") // 添加 SplashScreen 依赖 implementation("androidx.core:core-splashscreen:1.0.1") // 添加 SplashScreen 依赖
// 添加 lifecycle-runtime-ktx 依赖
implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.6.2")
} }

View File

@@ -52,6 +52,13 @@
<data android:mimeType="image/*" /> <data android:mimeType="image/*" />
</intent-filter> </intent-filter>
</activity> </activity>
<receiver
android:name=".model.ApkInstallReceiver"
android:exported="false">
<intent-filter>
<action android:name="android.intent.action.DOWNLOAD_COMPLETE" />
</intent-filter>
</receiver>
<service <service
android:name=".MyFirebaseMessagingService" android:name=".MyFirebaseMessagingService"

View File

@@ -1,19 +1,24 @@
package com.aiosman.riderpro package com.aiosman.riderpro
import android.Manifest import android.Manifest
import android.app.AlertDialog
import android.app.DownloadManager
import android.app.NotificationChannel import android.app.NotificationChannel
import android.app.NotificationManager import android.app.NotificationManager
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.IntentFilter
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.net.Uri import android.net.Uri
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.os.Environment
import android.util.Log import android.util.Log
import androidx.activity.ComponentActivity import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge import androidx.activity.enableEdgeToEdge
import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.result.contract.ActivityResultContracts
import androidx.annotation.RequiresApi
import androidx.compose.animation.AnimatedContentScope import androidx.compose.animation.AnimatedContentScope
import androidx.compose.animation.ExperimentalSharedTransitionApi import androidx.compose.animation.ExperimentalSharedTransitionApi
import androidx.compose.animation.SharedTransitionScope import androidx.compose.animation.SharedTransitionScope
@@ -23,6 +28,7 @@ import androidx.compose.ui.graphics.Color
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.view.WindowCompat import androidx.core.view.WindowCompat
import androidx.lifecycle.ProcessLifecycleOwner import androidx.lifecycle.ProcessLifecycleOwner
import androidx.lifecycle.lifecycleScope
import androidx.navigation.NavHostController import androidx.navigation.NavHostController
import cn.jiguang.api.utils.JCollectionAuth import cn.jiguang.api.utils.JCollectionAuth
import cn.jpush.android.api.JPushInterface import cn.jpush.android.api.JPushInterface
@@ -30,21 +36,29 @@ import com.aiosman.riderpro.data.AccountService
import com.aiosman.riderpro.data.AccountServiceImpl import com.aiosman.riderpro.data.AccountServiceImpl
import com.aiosman.riderpro.data.UserService import com.aiosman.riderpro.data.UserService
import com.aiosman.riderpro.data.UserServiceImpl 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.Navigation
import com.aiosman.riderpro.ui.NavigationRoute import com.aiosman.riderpro.ui.NavigationRoute
import com.aiosman.riderpro.ui.navigateToPost import com.aiosman.riderpro.ui.navigateToPost
import com.aiosman.riderpro.ui.post.NewPostViewModel import com.aiosman.riderpro.ui.post.NewPostViewModel
import com.google.accompanist.systemuicontroller.rememberSystemUiController import com.google.accompanist.systemuicontroller.rememberSystemUiController
import com.google.firebase.analytics.FirebaseAnalytics import com.google.firebase.analytics.FirebaseAnalytics
import com.google.gson.Gson
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import okhttp3.OkHttpClient
import okhttp3.Request
class MainActivity : ComponentActivity() { class MainActivity : ComponentActivity() {
// Firebase Analytics // Firebase Analytics
private lateinit var analytics: FirebaseAnalytics private lateinit var analytics: FirebaseAnalytics
private val scope = CoroutineScope(Dispatchers.Main) private val scope = CoroutineScope(Dispatchers.Main)
val context = this
private val apkInstallReceiver = ApkInstallReceiver()
// 请求通知权限 // 请求通知权限
private val requestPermissionLauncher = registerForActivityResult( private val requestPermissionLauncher = registerForActivityResult(
@@ -57,6 +71,76 @@ class MainActivity : ComponentActivity() {
} }
} }
@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("https://rider-pro.aiosman.com/beta_api/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) {
withContext(Dispatchers.Main) {
showUpdateDialog(updateInfo)
}
}
}
} catch (e: Exception) {
// 处理网络请求失败
e.printStackTrace()
}
}
}
private fun showUpdateDialog(updateInfo: UpdateInfo) {
val builder = AlertDialog.Builder(this)
builder.setTitle("发现新版本 v${updateInfo.versionName}")
builder.setMessage(updateInfo.updateContent)
builder.setPositiveButton("立即更新") { dialog, _ ->
downloadApk(updateInfo.downloadUrl,updateInfo.versionName)
dialog.dismiss()
}
if (!updateInfo.forceUpdate) {
builder.setNegativeButton("稍后再说") { 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")
}
/** /**
* 获取账号信息 * 获取账号信息
*/ */
@@ -70,6 +154,7 @@ class MainActivity : ComponentActivity() {
} }
} }
@RequiresApi(Build.VERSION_CODES.P)
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
// 监听应用生命周期 // 监听应用生命周期
@@ -99,6 +184,12 @@ class MainActivity : ComponentActivity() {
} }
enableEdgeToEdge() enableEdgeToEdge()
// 注册广播接收器
val intentFilter = IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE)
registerReceiver(apkInstallReceiver, intentFilter, RECEIVER_NOT_EXPORTED)
checkUpdate()
// 初始化腾讯云通信 SDK // 初始化腾讯云通信 SDK
@@ -176,6 +267,12 @@ class MainActivity : ComponentActivity() {
} }
} }
override fun onDestroy() {
super.onDestroy()
// 取消注册广播接收器
unregisterReceiver(apkInstallReceiver)
}
/** /**
* 请求通知权限 * 请求通知权限
*/ */
@@ -225,3 +322,4 @@ val LocalAnimatedContentScope = compositionLocalOf<AnimatedContentScope> {
} }

View File

@@ -0,0 +1,43 @@
package com.aiosman.riderpro.model
import android.app.DownloadManager
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.net.Uri
import android.util.Log
import androidx.core.net.toUri
import java.io.File
data class UpdateInfo(
val versionCode: Int,
val versionName: String,
val updateContent: String,
val downloadUrl: String,
val forceUpdate: Boolean
)
class ApkInstallReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
Log.d("ApkInstallReceiver", "onReceive() called") // 添加日志输出
if (DownloadManager.ACTION_DOWNLOAD_COMPLETE == intent.action) {
Log.d("ApkInstallReceiver", "Download complete") // 添加日志输出
val downloadId = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1)
val downloadManager = context.getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager
// 方案二:通过 DownloadManager 的 API 获取 Uri
val uri = downloadManager.getUriForDownloadedFile(downloadId)
if (uri != null) {
installApk(context, uri)
}
}
}
private fun installApk(context: Context, uri: Uri) {
Log.d("ApkInstallReceiver", "installApk() called with: context = $context, uri = $uri") // 添加日志输出
val installIntent = Intent(Intent.ACTION_VIEW)
installIntent.setDataAndType(uri, "application/vnd.android.package-archive")
installIntent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_GRANT_READ_URI_PERMISSION
context.startActivity(installIntent)
}
}