更新版本

This commit is contained in:
fengjignqi
2026-08-14 14:04:09 +08:00
parent 969338f3bd
commit a279230669
18 changed files with 214 additions and 44 deletions

Binary file not shown.

View File

@@ -4,7 +4,7 @@
<selectionStates>
<SelectionState runConfigName="app">
<option name="selectionMode" value="DROPDOWN" />
<DropdownSelection timestamp="2026-08-10T02:36:52.136106800Z">
<DropdownSelection timestamp="2026-08-14T02:57:51.212958400Z">
<Target type="DEFAULT_BOOT">
<handle>
<DeviceId pluginId="PhysicalDevice" identifier="serial=D122222104A9" />

View File

@@ -2,6 +2,9 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
<application
android:name=".CKApp"
android:allowBackup="true"
@@ -278,7 +281,7 @@
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="com.ck.ckcollar.app.fileprovider"
android:authorities="${applicationId}.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data

View File

@@ -1,11 +1,10 @@
package com.ck.ckcollar.app.model
class UpdateModel : CkBaseModel(){
//{"androidVersion":1,"content":"","iosVersion":1,"isForce":false}
var androidVersion : Int = 0
var iosVersion : Int = 0
var isForce : Boolean = false
var content : String? = ""
}
var content: String? = ""
var downloadUrl: String? = ""
var hasUpdate: Boolean = false
var isForce: Boolean = false
var versionCode: Int = 0
var versionName: String? = ""
}

View File

@@ -1,44 +1,62 @@
package com.ck.ckcollar.app.ui.act.home
import android.Manifest
import android.content.Intent
import android.graphics.Color
import android.content.pm.PackageManager
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.os.SystemClock
import android.view.MenuItem
import android.provider.Settings
import android.view.View
import android.view.WindowInsets
import android.widget.TextView
import androidx.activity.OnBackPressedCallback
import androidx.viewpager2.widget.ViewPager2
import androidx.viewpager2.widget.ViewPager2.OnPageChangeCallback
import androidx.activity.result.contract.ActivityResultContracts
import androidx.core.content.ContextCompat
import androidx.core.content.FileProvider
import com.ck.ckcollar.app.R
import com.ck.ckcollar.app.databinding.ActivityMainBinding
import com.ck.ckcollar.app.databinding.FragmentHomeBinding
import com.ck.ckcollar.app.model.CustomerStatusModel
import com.ck.ckcollar.app.model.UpdateModel
import com.ck.ckcollar.app.ui.act.home.adapter.MainFragmentAdapter
import com.ck.ckcollar.app.ui.act.home.fragment.HealthFragment
import com.ck.ckcollar.app.ui.act.home.fragment.HomeFragment
import com.ck.ckcollar.app.ui.act.home.fragment.MineFragment
import com.ck.ckcollar.app.ui.act.home.fragment.ShopFragment
import com.ck.ckcollar.app.ui.base.CKBaseActivity
import com.ck.ckcollar.app.ui.base.CKBaseFragment
import com.ck.ckcollar.app.ui.base.CKLHBaseActivity
import com.ck.ckcollar.app.ui.dialog.CKUpdateAlertDialog
import com.ck.ckcollar.app.utils.AppUpdateDownloader
import com.ck.ckcollar.app.utils.networkone.ICallback
import com.ck.ckcollar.app.utils.networkone.TTHttpClient
import com.ck.ckcollar.app.utils.networkone.model.TTRequestParam
import com.cyclone.shadowsocks.ui.frg.TTBaseFragment
import com.google.android.material.navigation.NavigationBarView
import com.google.gson.Gson
import com.tt.kit.base.TTBaseActivity
import com.tt.kit.utils.TTUtils
import java.io.File
class MainActivity : CKLHBaseActivity(),View.OnClickListener {
private var lastBackPressedAt = 0L
private var pendingUpdate: UpdateModel? = null
private var updateDialog: CKUpdateAlertDialog? = null
private var pendingApk: File? = null
private val notificationPermissionLauncher = registerForActivityResult(
ActivityResultContracts.RequestPermission()
) { granted ->
if (granted) {
pendingUpdate?.let(::startUpdateDownload)
} else {
toast(getString(R.string.notification_permission_required))
}
}
private val installPermissionLauncher = registerForActivityResult(
ActivityResultContracts.StartActivityForResult()
) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O || packageManager.canRequestPackageInstalls()) {
pendingApk?.let(::installApk)
} else {
toast(getString(R.string.install_permission_required))
}
}
lateinit var mainBinding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
@@ -156,7 +174,7 @@ class MainActivity : CKLHBaseActivity(),View.OnClickListener {
// }
// }).show()
// checkUpdate()
checkUpdate()
}
@@ -207,15 +225,16 @@ class MainActivity : CKLHBaseActivity(),View.OnClickListener {
TTHttpClient.get(this,"更新", TTRequestParam(),object : ICallback{
override fun onSuccess(data: String) {
var mode = Gson().fromJson<UpdateModel>(data,UpdateModel::class.java)
val model = Gson().fromJson<UpdateModel>(data, UpdateModel::class.java)
if (mode.androidVersion != TTUtils.getVersionCode(this@MainActivity)){
CKUpdateAlertDialog(this@MainActivity,mode
.content!!,mode.isForce,object : CKUpdateAlertDialog.AlertClickListener{
if (model.hasUpdate) {
val updateContent = model.content.orEmpty().replace("\\n", "\n")
updateDialog = CKUpdateAlertDialog(this@MainActivity, updateContent,
model.isForce, object : CKUpdateAlertDialog.AlertClickListener{
override fun onBack(isConfirm: Boolean) {
if (isConfirm) requestUpdateDownload(model)
}
}).show()
}).also { it.show() }
}
}
@@ -226,6 +245,87 @@ class MainActivity : CKLHBaseActivity(),View.OnClickListener {
})
}
private fun requestUpdateDownload(model: UpdateModel) {
if (model.downloadUrl.isNullOrBlank()) {
toast(getString(R.string.update_url_invalid))
return
}
pendingUpdate = model
if (!model.isForce && Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU &&
ContextCompat.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS) !=
PackageManager.PERMISSION_GRANTED
) {
notificationPermissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS)
return
}
startUpdateDownload(model)
}
private fun startUpdateDownload(model: UpdateModel) {
pendingUpdate = null
if (model.isForce) {
updateDialog?.showDownloadProgress()
} else {
updateDialog?.dismiss()
updateDialog = null
}
AppUpdateDownloader(applicationContext).download(
model.downloadUrl.orEmpty(),
showNotification = !model.isForce,
callback = object : AppUpdateDownloader.Callback {
override fun onProgress(percent: Int) {
runOnUiThread { updateDialog?.updateProgress(percent) }
}
override fun onComplete(apkFile: File) {
runOnUiThread {
if (model.isForce) {
updateDialog?.dismiss()
updateDialog = null
}
pendingApk = apkFile
installApk(apkFile)
}
}
override fun onError(message: String) {
runOnUiThread {
updateDialog?.showDownloadError()
toast(getString(R.string.update_download_failed, message))
}
}
}
)
}
private fun installApk(apkFile: File) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O &&
!packageManager.canRequestPackageInstalls()
) {
toast(getString(R.string.install_permission_required))
installPermissionLauncher.launch(
Intent(
Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES,
Uri.parse("package:$packageName")
)
)
return
}
pendingApk = null
val apkUri = FileProvider.getUriForFile(
this,
"$packageName.fileprovider",
apkFile
)
startActivity(
Intent(Intent.ACTION_VIEW).apply {
setDataAndType(apkUri, "application/vnd.android.package-archive")
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
}
)
}
companion object {
const val EXTRA_SHOW_HOME = "extra_show_home"
private const val EXIT_INTERVAL_MS = 2_000L

View File

@@ -357,15 +357,17 @@ class OrderActivity : CKBaseActivity() {
}
override fun onFailed(code: String, msg: String) {
toast(msg)
}
override fun onFailed(code: String, msg: String, data: String) {
onFailed(code, msg)
//原有【不足】的字符串判断条件
if (code.equals("502")){
var a = 0;
// toActivity(
// MyOrderActivity::class.java,
// DataCollection().apply { add(Data("pos", "1")) })
// finish()
if (code == "502") {
val orderId = JSONObject(data).optInt("orderId")
if (orderId > 0) {
model.orderId = orderId
}
}
}
})

View File

@@ -19,10 +19,13 @@ class CKUpdateAlertDialog(
var alertClickListener: AlertClickListener
) : TTBaseDialog(mContext){
private var binding: DialogUpdateAlertBinding? = null
override fun show() {
try {
var dialogAlertBinding =
val dialogAlertBinding =
DialogUpdateAlertBinding.inflate(LayoutInflater.from(mContext))
binding = dialogAlertBinding
dialogAlertBinding.textAlert.text = alertText
if (isForce){
@@ -39,8 +42,6 @@ class CKUpdateAlertDialog(
//dialogView.findViewById(R.id.photo).setVisibility(View.GONE);
dialogAlertBinding.btnConfirm.setOnClickListener(object : View.OnClickListener {
override fun onClick(v: View?) {
// TODO Auto-generated method stub
cancel()
alertClickListener.onBack(true)
}
})
@@ -55,8 +56,36 @@ class CKUpdateAlertDialog(
setCancelable(false)
}
fun showDownloadProgress() {
binding?.apply {
progressContainer.visibility = View.VISIBLE
btnConfirm.visibility = View.GONE
btnCenlel.visibility = View.GONE
textAlert.visibility = View.GONE
title.text = mContext.getString(com.ck.ckcollar.app.R.string.update_downloading, 0)
}
}
fun updateProgress(progress: Int) {
binding?.apply {
updateProgress.progress = progress
progressText.text = "$progress%"
title.text = mContext.getString(com.ck.ckcollar.app.R.string.update_downloading, progress)
}
}
fun showDownloadError() {
binding?.apply {
progressContainer.visibility = View.GONE
btnConfirm.visibility = View.VISIBLE
btnCenlel.visibility = if (isForce) View.GONE else View.VISIBLE
textAlert.visibility = View.VISIBLE
title.text = mContext.getString(com.ck.ckcollar.app.R.string.update_content)
}
}
interface AlertClickListener {
fun onBack(isConfirm : Boolean)
}
}
}

View File

@@ -2,5 +2,8 @@ package com.ck.ckcollar.app.utils.networkone
interface ICallback {
fun onSuccess(data: String)
fun onFailed(code: String, msg: String)
fun onFailed(code: String, msg: String, data: String) {
onFailed(code, msg)
}
fun onComplete() {}
}

View File

@@ -491,7 +491,7 @@ fun postJson(
} else {
activity.runOnUiThread {
activity.hiddenProgeassDialog()
iCallback.onFailed(getCode(model), getMessage(model))
iCallback.onFailed(getCode(model), getMessage(model), model.toString())
}
}
}

View File

@@ -31,9 +31,36 @@
android:layout_marginTop="10dp"
android:text="更新内容"
android:textColor="@color/textColor"
android:textSize="16sp"
android:textSize="14sp"
/>
<LinearLayout
android:id="@+id/progressContainer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
android:orientation="vertical"
android:visibility="gone">
<ProgressBar
android:id="@+id/updateProgress"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="match_parent"
android:layout_height="8dp"
android:max="100"
android:progress="0" />
<TextView
android:id="@+id/progressText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:gravity="center"
android:text="0%"
android:textColor="@color/textSecondColor"
android:textSize="13sp" />
</LinearLayout>
<com.ck.ckcollar.app.widget.CKButton
android:id="@+id/btnConfirm"

View File

@@ -603,4 +603,10 @@
<string name="input_question">请输入问题</string>
<string name="pet_note">宠物常识</string>
<string name="update_downloading">正在下载更新 %1$d%%</string>
<string name="update_download_failed">更新下载失败:%1$s</string>
<string name="update_url_invalid">更新下载地址不可用</string>
<string name="notification_permission_required">需要开启通知权限,以便在通知栏显示下载进度</string>
<string name="install_permission_required">请允许本应用安装更新,授权后返回即可继续安装</string>
<string name="update_content">更新内容</string>
</resources>

View File

@@ -2,6 +2,7 @@
<paths>
<!-- 对应 getExternalFilesDir(Environment.DIRECTORY_PICTURES) -->
<external-files-path name="my_images" path="Pictures" />
<external-files-path name="app_updates" path="updates" />
<!-- 对应 getExternalFilesDir(Environment.DIRECTORY_DCIM) -->
<external-files-path name="dcim" path="DCIM" />
<!-- 对应外部存储根目录 -->