Ver Fonte

feat 使用bugly实现远程升级

RobinTan1024 há 4 anos atrás
pai
commit
268068721a

+ 2 - 0
.idea/dictionaries/11017950.xml

@@ -1,6 +1,7 @@
 <component name="ProjectDictionaryState">
   <dictionary name="11017950">
     <words>
+      <w>bugly</w>
       <w>currentbu</w>
       <w>doverfuelingsolutions</w>
       <w>errcd</w>
@@ -9,6 +10,7 @@
       <w>isspt</w>
       <w>noperm</w>
       <w>snackbar</w>
+      <w>tencent</w>
     </words>
   </dictionary>
 </component>

+ 8 - 0
app/build.gradle

@@ -17,6 +17,11 @@ android {
         versionName "1.0.0"
 
         testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+
+        ndk {
+            // 设置支持的SO库架构
+            abiFilters 'armeabi', 'armeabi-v7a' //, 'x86', 'armeabi-v7a', 'x86_64', 'arm64-v8a'
+        }
     }
 
     buildFeatures {
@@ -82,4 +87,7 @@ dependencies {
     implementation 'com.hjq:toast:8.8'
 
     implementation 'com.moos:Material-ProgressView:1.0.6'
+
+    implementation 'com.tencent.bugly:crashreport_upgrade:1.5.0'
+    implementation 'com.tencent.bugly:nativecrashreport:3.7.700'
 }

+ 10 - 2
app/src/main/AndroidManifest.xml

@@ -3,11 +3,19 @@
     xmlns:tools="http://schemas.android.com/tools"
     package="com.doverfuelingsolutions.issp">
 
+    <uses-permission android:name="android.permission.INTERNET" />
     <uses-permission android:name="android.permission.INTERNET" />
     <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
     <uses-permission
         android:name="android.permission.WRITE_EXTERNAL_STORAGE"
         tools:ignore="ScopedStorage" />
+    <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
+    <uses-permission
+        android:name="android.permission.READ_LOGS"
+        tools:ignore="ProtectedPermissions" />
+    <uses-permission android:name="android.permission.READ_PHONE_STATE" />
+    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
+    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
 
     <application
         android:name=".DFSApplication"
@@ -29,7 +37,7 @@
             android:name=".view.PreferenceActivity"
             android:theme="@style/ActivityTransparent" />
         <activity
-            android:name=".view.UpdateActivity"
+            android:name=".view.UpgradeActivity"
             android:theme="@style/ActivityTransparent" />
 
         <provider
@@ -43,7 +51,7 @@
         </provider>
         <provider
             android:name="androidx.core.content.FileProvider"
-            android:authorities="${applicationId}.provider"
+            android:authorities="${applicationId}.fileProvider"
             android:exported="false"
             android:grantUriPermissions="true">
             <meta-data

+ 33 - 1
app/src/main/java/com/doverfuelingsolutions/issp/DFSApplication.kt

@@ -1,6 +1,7 @@
 package com.doverfuelingsolutions.issp
 
 import android.app.Application
+import android.content.Intent
 import android.graphics.Color
 import android.view.Gravity
 import androidx.core.app.ActivityCompat
@@ -8,10 +9,16 @@ import androidx.core.content.ContextCompat
 import androidx.core.graphics.ColorUtils
 import com.doverfuelingsolutions.issp.utils.DFSToastUtil
 import com.doverfuelingsolutions.issp.utils.DFSUtilsInitializer
+import com.doverfuelingsolutions.issp.view.UpgradeActivity
 import com.hjq.toast.IToastStyle
 import com.hjq.toast.ToastUtils
+import com.tencent.bugly.Bugly
+import com.tencent.bugly.beta.Beta
+import com.tencent.bugly.beta.UpgradeInfo
+import com.tencent.bugly.beta.upgrade.UpgradeListener
+import com.tencent.bugly.beta.upgrade.UpgradeStateListener
 
-class DFSApplication : Application() {
+class DFSApplication : Application(), UpgradeListener, UpgradeStateListener {
 
     companion object {
 
@@ -23,5 +30,30 @@ class DFSApplication : Application() {
         instance = this
 
         DFSToastUtil.init(this)
+
+        Beta.autoInstallApk = true
+        Beta.autoInit = true
+        Beta.autoCheckUpgrade = true
+        Beta.initDelay = 1000
+        Beta.upgradeListener = this
+        Bugly.init(applicationContext, "789ca54e1f", BuildConfig.DEBUG)
     }
+
+    override fun onUpgrade(ret: Int, upgradeInfo: UpgradeInfo?, isManual: Boolean, isSilence: Boolean) {
+        if (upgradeInfo == null) {
+            DFSToastUtil.info(R.string.no_update_version)
+            return
+        }
+
+        Intent(applicationContext, UpgradeActivity::class.java).let {
+            it.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+            startActivity(it)
+        }
+    }
+
+    override fun onUpgradeNoVersion(isManual: Boolean) = DFSToastUtil.info(R.string.current_version_is_latest)
+    override fun onUpgradeFailed(isManual: Boolean) = DFSToastUtil.info(R.string.download_fail)
+    override fun onUpgrading(isManual: Boolean) {}
+    override fun onDownloadCompleted(isManual: Boolean) {}
+    override fun onUpgradeSuccess(isManual: Boolean) {}
 }

+ 4 - 2
app/src/main/java/com/doverfuelingsolutions/issp/view/PreferenceActivity.kt

@@ -22,6 +22,7 @@ import com.doverfuelingsolutions.issp.utils.log.DFSLog
 import com.doverfuelingsolutions.issp.utils.sp.SPKeys
 import com.doverfuelingsolutions.issp.utils.sp.SPUtil
 import com.doverfuelingsolutions.issp.view.fragment.FragmentPreference
+import com.tencent.bugly.beta.Beta
 import kotlinx.coroutines.launch
 
 class PreferenceActivity : AppCompatActivity(),
@@ -105,7 +106,8 @@ class PreferenceActivity : AppCompatActivity(),
     override fun onClick(v: View?) {
         when (v) {
             binding.checkUpdate -> {
-                processUpdate()
+                Beta.checkUpgrade()
+                // processUpdate()
             }
             binding.buttonClose -> {
                 onBackPressed()
@@ -158,7 +160,7 @@ class PreferenceActivity : AppCompatActivity(),
                 return@launch
             }
 
-            UpdateActivity.start(this@PreferenceActivity, remoteVersion.version, remoteVersion.name, remoteVersion.info)
+            UpgradeActivity.start(this@PreferenceActivity, remoteVersion.version, remoteVersion.name, remoteVersion.info)
         }
     }
 

+ 153 - 152
app/src/main/java/com/doverfuelingsolutions/issp/view/UpdateActivity.kt

@@ -25,159 +25,160 @@ import com.doverfuelingsolutions.issp.utils.download.DownloadAction
 import kotlinx.coroutines.launch
 import java.io.File
 
+/* DIY 版本的远程升级 */
 class UpdateActivity : AppCompatActivity(),
-    View.OnClickListener{
-
-    companion object {
-
-        fun start(context: Context, version: String, remoteFileName: String, description: String) {
-            Intent(context, UpdateActivity::class.java).let {
-                it.putExtra("version", version)
-                it.putExtra("remoteFileName", remoteFileName)
-                it.putExtra("description", description)
-
-                context.startActivity(it)
-            }
-        }
-    }
-
-    private val binding: ActivityUpdateBinding by lazy {
-        DataBindingUtil.setContentView(this, R.layout.activity_update)
-    }
-    private val viewModel: UpdateViewModel by viewModels()
-
-    private var remoteFileName = ""
-    private var localFileName = ""
-    private var downloadAction: DownloadAction? = null
-
-    override fun onCreate(savedInstanceState: Bundle?) {
-        super.onCreate(savedInstanceState)
-        supportActionBar?.hide()
-
-        binding.lifecycleOwner = this
-        binding.viewModel = viewModel
-        binding.handler = this
-
-        viewModel.version.value = intent.getStringExtra("version") ?: ""
-        viewModel.versionDescription.value = intent.getStringExtra("description") ?: ""
-        intent.getStringExtra("remoteFileName").let {
-            if (it.isNullOrBlank()) {
-                DFSToastUtil.fail(R.string.invalid_version_file)
-                finish()
-                return
-            }
-
-            remoteFileName = it
-            download()
-        }
-
-        viewModel.progress.observe(this, {
-            binding.progressView.progress = it
-        })
-    }
-
-    override fun onResume() {
-        super.onResume()
-
-        WindowUtil.setFullscreen(this)
-    }
-
+    View.OnClickListener {
+
+//    companion object {
+//
+//        fun start(context: Context, version: String, remoteFileName: String, description: String) {
+//            Intent(context, UpdateActivity::class.java).let {
+//                it.putExtra("version", version)
+//                it.putExtra("remoteFileName", remoteFileName)
+//                it.putExtra("description", description)
+//
+//                context.startActivity(it)
+//            }
+//        }
+//    }
+//
+//    private val binding: ActivityUpdateBinding by lazy {
+//        DataBindingUtil.setContentView(this, R.layout.activity_update)
+//    }
+//    private val viewModel: UpdateViewModel by viewModels()
+//
+//    private var remoteFileName = ""
+//    private var localFileName = ""
+//    private var downloadAction: DownloadAction? = null
+//
+//    override fun onCreate(savedInstanceState: Bundle?) {
+//        super.onCreate(savedInstanceState)
+//        supportActionBar?.hide()
+//
+//        binding.lifecycleOwner = this
+//        binding.viewModel = viewModel
+//        binding.handler = this
+//
+//        viewModel.version.value = intent.getStringExtra("version") ?: ""
+//        viewModel.versionDescription.value = intent.getStringExtra("description") ?: ""
+//        intent.getStringExtra("remoteFileName").let {
+//            if (it.isNullOrBlank()) {
+//                DFSToastUtil.fail(R.string.invalid_version_file)
+//                finish()
+//                return
+//            }
+//
+//            remoteFileName = it
+//            download()
+//        }
+//
+//        viewModel.progress.observe(this, {
+//            binding.progressView.progress = it
+//        })
+//    }
+//
+//    override fun onResume() {
+//        super.onResume()
+//
+//        WindowUtil.setFullscreen(this)
+//    }
+//
     override fun onClick(v: View?) {
-        when (v) {
-            binding.buttonRetry -> {
-                downloadAction?.stop()
-                download()
-            }
-            binding.reInstall -> {
-                installApk()
-            }
-            binding.buttonCancel -> {
-                downloadAction?.stop()
-                finish()
-            }
-        }
-    }
-
-    private fun download() {
-        viewModel.isCancel.value = false
-        viewModel.isRetry.value = false
-        viewModel.isRetry.value = false
-
-        localFileName = "update/$remoteFileName"
-        downloadAction = DownloadAction(
-            this,
-            Uri.parse("${WayneApiConfig.HOST_ASSETS}/isspt/$remoteFileName"),
-            localFileName)
-        downloadAction?.start { status, size, total ->
-            lifecycleScope.launch {
-                when (status) {
-                    DownloadManager.STATUS_RUNNING -> {
-                        viewModel.errorText.value = ""
-                        viewModel.progress.value = size.toFloat() * 100 / total
-                    }
-                    DownloadManager.STATUS_SUCCESSFUL -> {
-                        viewModel.errorText.value = ""
-                        viewModel.progress.value = 100f
-                        viewModel.isCancel.value = true
-                        viewModel.isDownloaded.value = true
-
-                        installApk()
-                    }
-                    DownloadManager.STATUS_FAILED -> {
-                        viewModel.isCancel.value = true
-                        viewModel.isRetry.value = true
-                        viewModel.errorText.value = StringUtil.get(R.string.download_fail)
-                        downloadAction?.stop()
-                    }
-                    DownloadManager.STATUS_PAUSED -> {
-                        viewModel.isCancel.value = true
-                        if (NetworkUtil.isAvailable(this@UpdateActivity)) {
-                            viewModel.errorText.value = StringUtil.get(R.string.download_pause_no_network)
-                        } else {
-                            viewModel.errorText.value = StringUtil.get(R.string.download_pause)
-                        }
-                    }
-                }
-            }
-        }
-    }
-
-    private fun installApk() {
-        getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS)?.let { folder ->
-            val file = File("${folder.absolutePath}/$localFileName")
-            if (!file.isFile) {
-                DFSToastUtil.fail(R.string.cant_find_local_file)
-                finish()
-                return@let
-            }
-
-            // 先删掉其他APK
-            file.parentFile?.list()?.forEach {
-                File(it).let { f ->
-                    if (f.absolutePath != file.absolutePath && f.isFile && f.name.endsWith("apk")) {
-                        f.delete()
-                    }
-                }
-            }
-
-            // 安装
-            val fileUri = FileProvider.getUriForFile(this, "$packageName.provider", file)
-            Intent(Intent.ACTION_VIEW).let { intent ->
-                intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
-                intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
-                intent.setDataAndType(fileUri, "application/vnd.android.package-archive")
-                this.startActivity(intent)
-            }
-        }
-    }
-
-    class UpdateViewModel : ViewModel() {
-        val version = MutableLiveData("1.0.0")
-        val versionDescription = MutableLiveData("")
-        val progress = MutableLiveData(0f)
-        val isDownloaded = MutableLiveData(false)
-        val isCancel = MutableLiveData(false)
-        val isRetry = MutableLiveData(false)
-        val errorText = MutableLiveData("")
+//        when (v) {
+//            binding.buttonRetry -> {
+//                downloadAction?.stop()
+//                download()
+//            }
+//            binding.reInstall -> {
+//                installApk()
+//            }
+//            binding.buttonCancel -> {
+//                downloadAction?.stop()
+//                finish()
+//            }
+//        }
     }
+//
+//    private fun download() {
+//        viewModel.isCancel.value = false
+//        viewModel.isRetry.value = false
+//        viewModel.isRetry.value = false
+//
+//        localFileName = "update/$remoteFileName"
+//        downloadAction = DownloadAction(
+//            this,
+//            Uri.parse("${WayneApiConfig.HOST_ASSETS}/isspt/$remoteFileName"),
+//            localFileName)
+//        downloadAction?.start { status, size, total ->
+//            lifecycleScope.launch {
+//                when (status) {
+//                    DownloadManager.STATUS_RUNNING -> {
+//                        viewModel.errorText.value = ""
+//                        viewModel.progress.value = size.toFloat() * 100 / total
+//                    }
+//                    DownloadManager.STATUS_SUCCESSFUL -> {
+//                        viewModel.errorText.value = ""
+//                        viewModel.progress.value = 100f
+//                        viewModel.isCancel.value = true
+//                        viewModel.isDownloaded.value = true
+//
+//                        installApk()
+//                    }
+//                    DownloadManager.STATUS_FAILED -> {
+//                        viewModel.isCancel.value = true
+//                        viewModel.isRetry.value = true
+//                        viewModel.errorText.value = StringUtil.get(R.string.download_fail)
+//                        downloadAction?.stop()
+//                    }
+//                    DownloadManager.STATUS_PAUSED -> {
+//                        viewModel.isCancel.value = true
+//                        if (NetworkUtil.isAvailable(this@UpdateActivity)) {
+//                            viewModel.errorText.value = StringUtil.get(R.string.download_pause_no_network)
+//                        } else {
+//                            viewModel.errorText.value = StringUtil.get(R.string.download_pause)
+//                        }
+//                    }
+//                }
+//            }
+//        }
+//    }
+//
+//    private fun installApk() {
+//        getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS)?.let { folder ->
+//            val file = File("${folder.absolutePath}/$localFileName")
+//            if (!file.isFile) {
+//                DFSToastUtil.fail(R.string.cant_find_local_file)
+//                finish()
+//                return@let
+//            }
+//
+//            // 先删掉其他APK
+//            file.parentFile?.list()?.forEach {
+//                File(it).let { f ->
+//                    if (f.absolutePath != file.absolutePath && f.isFile && f.name.endsWith("apk")) {
+//                        f.delete()
+//                    }
+//                }
+//            }
+//
+//            // 安装
+//            val fileUri = FileProvider.getUriForFile(this, "$packageName.provider", file)
+//            Intent(Intent.ACTION_VIEW).let { intent ->
+//                intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
+//                intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
+//                intent.setDataAndType(fileUri, "application/vnd.android.package-archive")
+//                this.startActivity(intent)
+//            }
+//        }
+//    }
+//
+//    class UpdateViewModel : ViewModel() {
+//        val version = MutableLiveData("1.0.0")
+//        val versionDescription = MutableLiveData("")
+//        val progress = MutableLiveData(0f)
+//        val isDownloaded = MutableLiveData(false)
+//        val isCancel = MutableLiveData(false)
+//        val isRetry = MutableLiveData(false)
+//        val errorText = MutableLiveData("")
+//    }
 }

+ 265 - 0
app/src/main/java/com/doverfuelingsolutions/issp/view/UpgradeActivity.kt

@@ -0,0 +1,265 @@
+package com.doverfuelingsolutions.issp.view
+
+import android.content.Context
+import android.content.Intent
+import android.os.Bundle
+import android.os.Environment
+import android.view.View
+import androidx.activity.viewModels
+import androidx.appcompat.app.AppCompatActivity
+import androidx.core.content.FileProvider
+import androidx.databinding.DataBindingUtil
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.ViewModel
+import com.doverfuelingsolutions.issp.R
+import com.doverfuelingsolutions.issp.databinding.ActivityUpdateBinding
+import com.doverfuelingsolutions.issp.utils.*
+import com.doverfuelingsolutions.issp.utils.download.DownloadAction
+import com.tencent.bugly.beta.Beta
+import com.tencent.bugly.beta.UpgradeInfo
+import com.tencent.bugly.beta.download.DownloadListener
+import com.tencent.bugly.beta.download.DownloadTask
+import java.io.File
+
+class UpgradeActivity : AppCompatActivity(),
+    View.OnClickListener,
+    DownloadListener {
+
+    companion object {
+
+        fun start(context: Context, version: String, remoteFileName: String, description: String) {
+            Intent(context, UpgradeActivity::class.java).let {
+                it.putExtra("version", version)
+                it.putExtra("remoteFileName", remoteFileName)
+                it.putExtra("description", description)
+
+                context.startActivity(it)
+            }
+        }
+    }
+
+    private val binding: ActivityUpdateBinding by lazy {
+        DataBindingUtil.setContentView(this, R.layout.activity_update)
+    }
+    private val viewModel: UpdateViewModel by viewModels()
+
+    /* DIY */
+    private var remoteFileName = ""
+    private var localFileName = ""
+    private var downloadAction: DownloadAction? = null
+
+    private var upgradeInfo: UpgradeInfo? = null
+    private var downloadTask: DownloadTask? = null
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        supportActionBar?.hide()
+
+        binding.lifecycleOwner = this
+        binding.viewModel = viewModel
+        binding.handler = this
+
+        /* bugly */
+        Beta.registerDownloadListener(this)
+        upgradeInfo = Beta.getUpgradeInfo()
+        viewModel.version.value = upgradeInfo!!.versionName
+        viewModel.versionFeature.value = upgradeInfo!!.newFeature
+        viewModel.versionPublishTime.value = TimeUtil.dateFormat(upgradeInfo!!.publishTime)
+
+        // view control
+        viewModel.progress.observe(this, {
+            binding.progressView.progress = it
+        })
+        viewModel.taskStatus.observe(this, {
+            viewModel.buttonVisible.value = true
+            when (it) {
+                DownloadTask.INIT -> {
+                    viewModel.buttonText.value = StringUtil.get(R.string.start_upgrade)
+                }
+                DownloadTask.PAUSED -> {
+                    viewModel.buttonText.value = StringUtil.get(R.string.continue_upgrade)
+                }
+                DownloadTask.DELETED,
+                DownloadTask.FAILED -> {
+                    viewModel.buttonText.value = StringUtil.get(R.string.retry)
+                }
+                DownloadTask.COMPLETE -> {
+                    viewModel.buttonText.value = StringUtil.get(R.string.install_app)
+                }
+                DownloadTask.DOWNLOADING -> {
+                    viewModel.buttonVisible.value = false
+                }
+            }
+        })
+    }
+
+    override fun onResume() {
+        super.onResume()
+
+        WindowUtil.setFullscreen(this)
+    }
+
+    override fun onDestroy() {
+        super.onDestroy()
+
+        Beta.unregisterDownloadListener()
+    }
+
+    override fun onClick(v: View?) {
+        when (v) {
+            binding.buttonAction -> {
+                when (viewModel.taskStatus.value) {
+                    DownloadTask.INIT -> {
+                        downloadTask = Beta.startDownload()
+                        viewModel.taskStatus.value = downloadTask!!.status
+                    }
+                    DownloadTask.DELETED,
+                    DownloadTask.PAUSED,
+                    DownloadTask.FAILED -> {
+                        if (!NetworkUtil.isAvailable(this)) {
+                            DFSToastUtil.fail(R.string.network_none)
+                        } else if (downloadTask?.status != DownloadTask.COMPLETE && DownloadTask.DOWNLOADING != downloadTask?.status) {
+                            downloadTask?.download()
+                        }
+                    }
+                    DownloadTask.COMPLETE -> {
+                        Beta.installApk(downloadTask?.saveFile)
+                    }
+                }
+            }
+            binding.buttonCancel -> {
+                downloadTask?.stop()
+                Beta.cancelDownload()
+                finish()
+            }
+        }
+    }
+
+    /* bugly */
+    override fun onReceive(task: DownloadTask) {
+        if (task.status != viewModel.taskStatus.value) {
+            viewModel.taskStatus.value = task.status
+        }
+
+        viewModel.progress.value = if (task.savedLength > 0 && task.totalLength > 0) {
+            task.savedLength.toFloat() * 100 / task.totalLength.toFloat()
+        } else {
+            0f
+        }
+    }
+
+    override fun onCompleted(task: DownloadTask) {
+        viewModel.taskStatus.value = task.status
+    }
+
+    override fun onFailed(task: DownloadTask, code: Int, msg: String) {
+        viewModel.taskStatus.value = task.status
+        viewModel.errorText.value = if (!NetworkUtil.isAvailable(this)) {
+            StringUtil.get(R.string.cant_download_no_net)
+        } else {
+            msg
+        }
+    }
+
+    /* DIY */
+    private fun check() {
+        viewModel.version.value = intent.getStringExtra("version") ?: ""
+        viewModel.versionFeature.value = intent.getStringExtra("description") ?: ""
+        intent.getStringExtra("remoteFileName").let {
+            if (it.isNullOrBlank()) {
+                DFSToastUtil.fail(R.string.invalid_version_file)
+                finish()
+                return
+            }
+
+            remoteFileName = it
+            download()
+        }
+    }
+
+    private fun download() {
+//        viewModel.isCancel.value = false
+//        viewModel.isContinue.value = false
+//        viewModel.isContinue.value = false
+//
+//        localFileName = "update/$remoteFileName"
+//        downloadAction = DownloadAction(
+//            this,
+//            Uri.parse("${WayneApiConfig.HOST_ASSETS}/isspt/$remoteFileName"),
+//            localFileName)
+//        downloadAction?.start { status, size, total ->
+//            lifecycleScope.launch {
+//                when (status) {
+//                    DownloadManager.STATUS_RUNNING -> {
+//                        viewModel.errorText.value = ""
+//                        viewModel.progress.value = size.toFloat() * 100 / total
+//                    }
+//                    DownloadManager.STATUS_SUCCESSFUL -> {
+//                        viewModel.errorText.value = ""
+//                        viewModel.progress.value = 100f
+//                        viewModel.isCancel.value = true
+//                        viewModel.isDownloaded.value = true
+//
+//                        installApk()
+//                    }
+//                    DownloadManager.STATUS_FAILED -> {
+//                        viewModel.isCancel.value = true
+//                        viewModel.isContinue.value = true
+//                        viewModel.errorText.value = StringUtil.get(R.string.download_fail)
+//                        downloadAction?.stop()
+//                    }
+//                    DownloadManager.STATUS_PAUSED -> {
+//                        viewModel.isCancel.value = true
+//                        if (NetworkUtil.isAvailable(this@UpgradeActivity)) {
+//                            viewModel.errorText.value = StringUtil.get(R.string.download_pause_no_network)
+//                        } else {
+//                            viewModel.errorText.value = StringUtil.get(R.string.download_pause)
+//                        }
+//                    }
+//                }
+//            }
+//        }
+    }
+
+    private fun installApk() {
+        getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS)?.let { folder ->
+            val file = File("${folder.absolutePath}/$localFileName")
+            if (!file.isFile) {
+                DFSToastUtil.fail(R.string.cant_find_local_file)
+                finish()
+                return@let
+            }
+
+            // 先删掉其他APK
+            file.parentFile?.list()?.forEach {
+                File(it).let { f ->
+                    if (f.absolutePath != file.absolutePath && f.isFile && f.name.endsWith("apk")) {
+                        f.delete()
+                    }
+                }
+            }
+
+            // 安装
+            val fileUri = FileProvider.getUriForFile(this, "$packageName.fileProvider", file)
+            Intent(Intent.ACTION_VIEW).let { intent ->
+                intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
+                intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
+                intent.setDataAndType(fileUri, "application/vnd.android.package-archive")
+                this.startActivity(intent)
+            }
+        }
+    }
+
+    class UpdateViewModel : ViewModel() {
+        val version = MutableLiveData("1.0.0")
+        val versionFeature = MutableLiveData("")
+        val versionPublishTime = MutableLiveData("")
+        val progress = MutableLiveData(0f)
+
+        val taskStatus = MutableLiveData(DownloadTask.INIT)
+        val buttonText = MutableLiveData(StringUtil.get(R.string.start_upgrade))
+        val buttonVisible = MutableLiveData(true)
+
+        val errorText = MutableLiveData("")
+    }
+}

+ 30 - 23
app/src/main/res/layout/activity_update.xml

@@ -6,11 +6,11 @@
 
         <variable
             name="viewModel"
-            type="com.doverfuelingsolutions.issp.view.UpdateActivity.UpdateViewModel" />
+            type="com.doverfuelingsolutions.issp.view.UpgradeActivity.UpdateViewModel" />
 
         <variable
             name="handler"
-            type="com.doverfuelingsolutions.issp.view.UpdateActivity" />
+            type="com.doverfuelingsolutions.issp.view.UpgradeActivity" />
 
         <import type="android.view.View" />
     </data>
@@ -21,7 +21,7 @@
         android:background="@drawable/border_gray"
         android:elevation="60dp"
         android:gravity="center_horizontal"
-        android:minWidth="500dp"
+        android:minWidth="600dp"
         android:orientation="vertical"
         android:paddingHorizontal="40dp"
         android:paddingVertical="80dp">
@@ -50,8 +50,11 @@
         <TextView
             android:id="@+id/errorText"
             style="@style/wrap"
-            android:text="@{viewModel.errorText, default=@string/download_fail}"
             android:layout_marginTop="40dp"
+            android:lineSpacingExtra="2dp"
+            android:maxWidth="500dp"
+            android:singleLine="false"
+            android:text="@{viewModel.errorText, default=@string/download_fail}"
             android:textColor="@color/colorRed"
             android:textSize="20sp" />
 
@@ -65,14 +68,29 @@
             android:textSize="24sp" />
 
         <TextView
-            android:id="@+id/versionInfo"
             style="@style/wrap"
             android:layout_gravity="start"
-            android:layout_marginTop="20dp"
-            android:text="@{viewModel.versionDescription, default=@string/new_version}"
-            android:textColor="@color/color666"
+            android:layout_marginTop="14dp"
+            android:text="@{@string/publish_at(viewModel.versionPublishTime), default=@string/publish_at}"
+            android:textColor="@color/color555"
             android:textSize="18sp" />
 
+        <TextView
+            style="@style/wrap"
+            android:layout_gravity="start"
+            android:layout_marginTop="14dp"
+            android:text="@string/version_info_is"
+            android:textColor="@color/color555"
+            android:textSize="18sp" />
+
+        <TextView
+            style="@style/wrap"
+            android:layout_gravity="start"
+            android:layout_marginTop="8dp"
+            android:text="@{viewModel.versionFeature, default=@string/current_version_is_latest}"
+            android:textColor="@color/color777"
+            android:textSize="16sp" />
+
         <LinearLayout
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
@@ -80,24 +98,14 @@
             android:orientation="horizontal">
 
             <Button
-                android:id="@+id/buttonRetry"
-                style="@style/Widget.MaterialComponents.Button"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:layout_marginHorizontal="12dp"
-                android:onClick="@{handler}"
-                android:text="@string/retry"
-                android:visibility="@{viewModel.isRetry ? View.VISIBLE : View.GONE}" />
-
-            <Button
-                android:id="@+id/reInstall"
+                android:id="@+id/buttonAction"
                 style="@style/Widget.MaterialComponents.Button"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
                 android:layout_marginHorizontal="12dp"
                 android:onClick="@{handler}"
-                android:text="@string/reinstall"
-                android:visibility="@{viewModel.isDownloaded ? View.VISIBLE : View.GONE}" />
+                android:text="@{viewModel.buttonText, default=@string/install_app}"
+                android:visibility="@{viewModel.buttonVisible ? View.VISIBLE : View.GONE}" />
 
             <Button
                 android:id="@+id/buttonCancel"
@@ -106,8 +114,7 @@
                 android:layout_height="wrap_content"
                 android:layout_marginHorizontal="12dp"
                 android:onClick="@{handler}"
-                android:text="@string/cancel_update"
-                android:visibility="@{viewModel.isCancel ? View.VISIBLE : View.GONE}" />
+                android:text="@string/cancel_update" />
         </LinearLayout>
     </LinearLayout>
 </layout>

+ 7 - 2
app/src/main/res/values/strings.xml

@@ -143,7 +143,9 @@
     <!-- 版本迭代 -->
     <string name="check_update">检查更新</string>
     <string name="find_new_version">发现新版本</string>
-    <string name="new_version">新版本 %1$s 信息</string>
+    <string name="new_version">新版本 %1$s</string>
+    <string name="publish_at">发布时间:%1$s</string>
+    <string name="version_info_is">版本信息:</string>
     <string name="in_check_update">检查更新中</string>
     <string name="no_update_version">无可更新版本</string>
     <string name="current_version_is_latest">当前版本已是最新版本</string>
@@ -156,9 +158,12 @@
     <string name="click_close">点击关闭</string>
     <string name="cancel_update">取消升级</string>
     <string name="cant_find_local_file">未找到本地文件</string>
-    <string name="reinstall">重新安装</string>
+    <string name="install_app">安装应用</string>
     <string name="retry">重新尝试</string>
+    <string name="start_upgrade">开始升级</string>
+    <string name="continue_upgrade">继续升级</string>
     <string name="invalid_version_file">错误的远程版本文件</string>
+    <string name="cant_download_no_net">未检测到可用网络,更新下载已暂停,请您在网络畅通的情况下重试。</string>
 
     <!-- Fusion -->
     <string name="fusion_in_connect">正在连接 Fusion…</string>

+ 9 - 0
app/src/main/res/xml/provider_paths.xml

@@ -3,4 +3,13 @@
     <external-files-path
         name="download"
         path="." />
+
+    <!-- /storage/emulated/0/Download/${applicationId}/.beta/apk-->
+    <external-path
+        name="beta_external_path"
+        path="Download/" />
+    <!--/storage/emulated/0/Android/data/${applicationId}/files/apk/-->
+    <external-path
+        name="beta_external_files_path"
+        path="Android/data/" />
 </paths>