diff --git a/app/build.gradle b/app/build.gradle index 802645a5..dc680426 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -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}" } diff --git a/app/src/main/java/com/huanchengfly/tieba/post/BaseApplication.kt b/app/src/main/java/com/huanchengfly/tieba/post/BaseApplication.kt index 50805b01..f3dccdc3 100644 --- a/app/src/main/java/com/huanchengfly/tieba/post/BaseApplication.kt +++ b/app/src/main/java/com/huanchengfly/tieba/post/BaseApplication.kt @@ -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 diff --git a/app/src/main/java/com/huanchengfly/tieba/post/activities/MainActivity.kt b/app/src/main/java/com/huanchengfly/tieba/post/activities/MainActivity.kt index 0996521b..cd6bfec5 100644 --- a/app/src/main/java/com/huanchengfly/tieba/post/activities/MainActivity.kt +++ b/app/src/main/java/com/huanchengfly/tieba/post/activities/MainActivity.kt @@ -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 ) diff --git a/app/src/main/java/com/huanchengfly/tieba/post/api/retrofit/interfaces/LiteApiInterface.kt b/app/src/main/java/com/huanchengfly/tieba/post/api/retrofit/interfaces/LiteApiInterface.kt index 8992df63..38e79ebf 100644 --- a/app/src/main/java/com/huanchengfly/tieba/post/api/retrofit/interfaces/LiteApiInterface.kt +++ b/app/src/main/java/com/huanchengfly/tieba/post/api/retrofit/interfaces/LiteApiInterface.kt @@ -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>> + + @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> } \ No newline at end of file diff --git a/app/src/main/java/com/huanchengfly/tieba/post/fragments/BaseFragment.kt b/app/src/main/java/com/huanchengfly/tieba/post/fragments/BaseFragment.kt index 2df9e1b9..0bf702df 100644 --- a/app/src/main/java/com/huanchengfly/tieba/post/fragments/BaseFragment.kt +++ b/app/src/main/java/com/huanchengfly/tieba/post/fragments/BaseFragment.kt @@ -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 } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index a7a87ba6..47bf3452 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -480,4 +480,8 @@ 极大 修改已保存,即将重启 App 以应用设置 + 崩溃了! + 非常抱歉,应用在使用中发生了崩溃。\n您可以复制崩溃报告链接来向开发者反馈。 + 复制报告链接 + 获取链接失败