feat: 应用内更新 & 崩溃报告
This commit is contained in:
parent
b6c4299398
commit
a4046ea98d
|
|
@ -153,4 +153,5 @@ dependencies {
|
|||
def appCenterSdkVersion = '4.3.1'
|
||||
implementation "com.microsoft.appcenter:appcenter-analytics:${appCenterSdkVersion}"
|
||||
implementation "com.microsoft.appcenter:appcenter-crashes:${appCenterSdkVersion}"
|
||||
implementation "com.microsoft.appcenter:appcenter-distribute:${appCenterSdkVersion}"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,6 +37,13 @@ import com.huanchengfly.tieba.post.utils.QuickPreviewUtil.getForumName
|
|||
import com.huanchengfly.tieba.post.utils.QuickPreviewUtil.getPreviewInfo
|
||||
import com.huanchengfly.tieba.post.utils.QuickPreviewUtil.isForumUrl
|
||||
import com.huanchengfly.tieba.post.utils.QuickPreviewUtil.isThreadUrl
|
||||
import com.microsoft.appcenter.AppCenter
|
||||
import com.microsoft.appcenter.analytics.Analytics
|
||||
import com.microsoft.appcenter.crashes.Crashes
|
||||
import com.microsoft.appcenter.distribute.Distribute
|
||||
import com.microsoft.appcenter.distribute.DistributeListener
|
||||
import com.microsoft.appcenter.distribute.ReleaseDetails
|
||||
import com.microsoft.appcenter.distribute.UpdateAction
|
||||
import org.intellij.lang.annotations.RegExp
|
||||
import org.litepal.LitePal
|
||||
import java.util.*
|
||||
|
|
@ -72,6 +79,11 @@ class BaseApplication : Application(), IApp {
|
|||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
||||
setWebViewPath(this)
|
||||
}
|
||||
Distribute.setListener(MyDistributeListener())
|
||||
AppCenter.start(
|
||||
this, "b56debcc-264b-4368-a2cd-8c20213f6433",
|
||||
Analytics::class.java, Crashes::class.java, Distribute::class.java
|
||||
)
|
||||
ThemeUtils.init(ThemeDelegate)
|
||||
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM)
|
||||
LitePal.initialize(this)
|
||||
|
|
@ -268,6 +280,35 @@ class BaseApplication : Application(), IApp {
|
|||
var DENSITY = 0f
|
||||
}
|
||||
|
||||
class MyDistributeListener : DistributeListener {
|
||||
override fun onReleaseAvailable(
|
||||
activity: Activity,
|
||||
releaseDetails: ReleaseDetails
|
||||
): Boolean {
|
||||
val versionName = releaseDetails.shortVersion
|
||||
val releaseNotes = releaseDetails.releaseNotes
|
||||
if (activity is BaseActivity) {
|
||||
activity.showDialog {
|
||||
setTitle(activity.getString(R.string.title_dialog_update, versionName))
|
||||
setMessage(releaseNotes)
|
||||
setCancelable(!releaseDetails.isMandatoryUpdate)
|
||||
setPositiveButton(R.string.appcenter_distribute_update_dialog_download) { _, _ ->
|
||||
Distribute.notifyUpdateAction(UpdateAction.UPDATE)
|
||||
}
|
||||
if (!releaseDetails.isMandatoryUpdate) {
|
||||
setNeutralButton(R.string.appcenter_distribute_update_dialog_postpone) { _, _ ->
|
||||
Distribute.notifyUpdateAction(UpdateAction.POSTPONE)
|
||||
}
|
||||
setNegativeButton(R.string.button_next_time, null)
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onNoReleaseAvailable(activity: Activity) {}
|
||||
}
|
||||
|
||||
companion object {
|
||||
val TAG = BaseApplication::class.java.simpleName
|
||||
|
||||
|
|
|
|||
|
|
@ -23,7 +23,10 @@ import com.google.android.material.snackbar.Snackbar
|
|||
import com.huanchengfly.tieba.post.*
|
||||
import com.huanchengfly.tieba.post.adapters.ViewPagerAdapter
|
||||
import com.huanchengfly.tieba.post.api.Error
|
||||
import com.huanchengfly.tieba.post.api.LiteApi
|
||||
import com.huanchengfly.tieba.post.api.interfaces.CommonCallback
|
||||
import com.huanchengfly.tieba.post.api.retrofit.doIfFailure
|
||||
import com.huanchengfly.tieba.post.api.retrofit.doIfSuccess
|
||||
import com.huanchengfly.tieba.post.fragments.MainForumListFragment
|
||||
import com.huanchengfly.tieba.post.fragments.MessageFragment
|
||||
import com.huanchengfly.tieba.post.fragments.MyInfoFragment
|
||||
|
|
@ -33,9 +36,12 @@ import com.huanchengfly.tieba.post.models.MyInfoBean
|
|||
import com.huanchengfly.tieba.post.services.NotifyJobService
|
||||
import com.huanchengfly.tieba.post.utils.*
|
||||
import com.huanchengfly.tieba.post.widgets.MyViewPager
|
||||
import com.microsoft.appcenter.AppCenter
|
||||
import com.microsoft.appcenter.analytics.Analytics
|
||||
import com.microsoft.appcenter.crashes.Crashes
|
||||
import com.microsoft.appcenter.distribute.Distribute
|
||||
import kotlinx.coroutines.Dispatchers.IO
|
||||
import kotlinx.coroutines.launch
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
|
||||
open class MainActivity : BaseActivity(), BottomNavigationView.OnNavigationItemSelectedListener, OnNavigationItemReselectedListener {
|
||||
var mAdapter: ViewPagerAdapter = ViewPagerAdapter(supportFragmentManager)
|
||||
|
|
@ -147,6 +153,13 @@ open class MainActivity : BaseActivity(), BottomNavigationView.OnNavigationItemS
|
|||
|
||||
override fun getLayoutId(): Int = R.layout.activity_main
|
||||
|
||||
private fun formatDateTime(
|
||||
pattern: String,
|
||||
timestamp: Long = System.currentTimeMillis()
|
||||
): String {
|
||||
return SimpleDateFormat(pattern, Locale.getDefault()).format(Date(timestamp))
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setSwipeBackEnable(false)
|
||||
|
|
@ -154,20 +167,53 @@ open class MainActivity : BaseActivity(), BottomNavigationView.OnNavigationItemS
|
|||
findView()
|
||||
initView()
|
||||
initListener()
|
||||
AppCenter.start(
|
||||
getApplication(), "b56debcc-264b-4368-a2cd-8c20213f6433",
|
||||
Analytics::class.java, Crashes::class.java
|
||||
)
|
||||
Distribute.checkForUpdate()
|
||||
Crashes.hasCrashedInLastSession().thenAccept { hasCrashed ->
|
||||
if (hasCrashed) {
|
||||
Crashes.getLastSessionCrashReport().thenAccept {
|
||||
val device = it.device
|
||||
showDialog {
|
||||
setTitle(R.string.title_dialog_crash)
|
||||
setMessage(R.string.message_dialog_crash)
|
||||
setPositiveButton(R.string.button_copy_crash_link) { _, _ ->
|
||||
launch(IO + job) {
|
||||
LiteApi.instance.pastebinAsync(
|
||||
"崩溃报告 ${
|
||||
formatDateTime(
|
||||
"yyyy-MM-dd HH:mm:ss",
|
||||
it.appErrorTime.time
|
||||
)
|
||||
}",
|
||||
"""
|
||||
App 版本:${device.appVersion}
|
||||
系统版本:${device.osVersion}
|
||||
机型:${device.oemName} ${device.model}
|
||||
|
||||
崩溃:
|
||||
${it.stackTrace}
|
||||
""".trimIndent()
|
||||
).doIfSuccess {
|
||||
TiebaUtil.copyText(this@MainActivity, it)
|
||||
}.doIfFailure {
|
||||
toastShort(R.string.toast_get_link_failed)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!SharedPreferencesUtil.get(SharedPreferencesUtil.SP_APP_DATA)
|
||||
.getBoolean("notice_dialog", false)
|
||||
) {
|
||||
showDialog(DialogUtil.build(this)
|
||||
.setTitle(R.string.title_dialog_notice)
|
||||
.setMessage(R.string.message_dialog_notice)
|
||||
.setPositiveButton(R.string.button_sure_default) { _, _ ->
|
||||
SharedPreferencesUtil.put(
|
||||
this,
|
||||
SharedPreferencesUtil.SP_APP_DATA,
|
||||
showDialog(
|
||||
DialogUtil.build(this)
|
||||
.setTitle(R.string.title_dialog_notice)
|
||||
.setMessage(R.string.message_dialog_notice)
|
||||
.setPositiveButton(R.string.button_sure_default) { _, _ ->
|
||||
SharedPreferencesUtil.put(
|
||||
this,
|
||||
SharedPreferencesUtil.SP_APP_DATA,
|
||||
"notice_dialog",
|
||||
true
|
||||
)
|
||||
|
|
|
|||
|
|
@ -2,9 +2,23 @@ package com.huanchengfly.tieba.post.api.retrofit.interfaces
|
|||
|
||||
import com.huanchengfly.tieba.post.api.retrofit.ApiResult
|
||||
import kotlinx.coroutines.Deferred
|
||||
import retrofit2.http.Field
|
||||
import retrofit2.http.FormUrlEncoded
|
||||
import retrofit2.http.GET
|
||||
import retrofit2.http.POST
|
||||
|
||||
interface LiteApiInterface {
|
||||
@GET("https://huancheng65.github.io/TiebaLite/wallpapers.json")
|
||||
fun wallpapersAsync(): Deferred<ApiResult<List<String>>>
|
||||
|
||||
@POST("https://pastebin.com/api/api_post.php")
|
||||
@FormUrlEncoded
|
||||
fun pastebinAsync(
|
||||
@Field("api_paste_name") name: String,
|
||||
@Field("api_paste_code") content: String,
|
||||
@Field("api_dev_key") apiDevKey: String = "CB4NlNwnukUaLURHqIbL-IQgdCJTkB7l",
|
||||
@Field("api_paste_private") private: String = "1",
|
||||
@Field("api_paste_expire_date") expireDate: String = "1W",
|
||||
@Field("api_paste_format") format: String = "markdown",
|
||||
): Deferred<ApiResult<String>>
|
||||
}
|
||||
|
|
@ -16,9 +16,8 @@ import com.huanchengfly.tieba.post.interfaces.BackHandledInterface
|
|||
import com.huanchengfly.tieba.post.interfaces.Refreshable
|
||||
import com.huanchengfly.tieba.post.utils.AppPreferencesUtils
|
||||
import com.huanchengfly.tieba.post.utils.HandleBackUtil
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.Dispatchers.IO
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
|
||||
/**
|
||||
|
|
@ -192,6 +191,13 @@ abstract class BaseFragment : Fragment(), BackHandledInterface, CoroutineScope {
|
|||
return false
|
||||
}
|
||||
|
||||
fun launchIO(
|
||||
start: CoroutineStart = CoroutineStart.DEFAULT,
|
||||
block: suspend CoroutineScope.() -> Unit
|
||||
): Job {
|
||||
return launch(IO + job, start, block)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val TAG = BaseFragment::class.java.simpleName
|
||||
}
|
||||
|
|
|
|||
|
|
@ -480,4 +480,8 @@
|
|||
<string name="text_size_large">大</string>
|
||||
<string name="text_size_very_large">极大</string>
|
||||
<string name="toast_after_change_will_restart">修改已保存,即将重启 App 以应用设置</string>
|
||||
<string name="title_dialog_crash">崩溃了!</string>
|
||||
<string name="message_dialog_crash">非常抱歉,应用在使用中发生了崩溃。\n您可以复制崩溃报告链接来向开发者反馈。</string>
|
||||
<string name="button_copy_crash_link">复制报告链接</string>
|
||||
<string name="toast_get_link_failed">获取链接失败</string>
|
||||
</resources>
|
||||
|
|
|
|||
Loading…
Reference in New Issue