Skip to content

Commit 3b3b67b

Browse files
committed
🚑
[Add] - Recovery mode.
1 parent ddf9e68 commit 3b3b67b

7 files changed

Lines changed: 268 additions & 17 deletions

File tree

app/build.gradle.kts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ android {
1515
applicationId = "me.rosuh.easywatermark"
1616
minSdk = (Apps.minSdk)
1717
targetSdk = (Apps.targetSdk)
18-
versionCode = 20700
19-
versionName = "2.7.0"
18+
versionCode = 20701
19+
versionName = "2.7.1"
2020
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
2121
setProperty("archivesBaseName", "$applicationId-v$versionName($versionCode)")
2222
}

app/src/main/java/me/rosuh/easywatermark/MyApp.kt

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,30 +21,53 @@ class MyApp : Application() {
2121
@Inject
2222
lateinit var waterMarkRepo: WaterMarkRepository
2323

24+
private val sp by lazy { getSharedPreferences(SP_NAME, Context.MODE_PRIVATE) }
25+
26+
override fun attachBaseContext(base: Context?) {
27+
super.attachBaseContext(base)
28+
instance = this
29+
catchException()
30+
}
31+
2432
override fun onCreate() {
2533
super.onCreate()
26-
CMonet.init(this, true )
27-
instance = this
28-
applicationScope.launch {
29-
waterMarkRepo.resetModeToText()
34+
val crashCount = sp.getInt(SP_KEY_CRASH_COUNT, 0)
35+
if (crashCount >= 2) {
36+
recoveryMode = true
37+
return
38+
} else {
39+
applicationScope.launch {
40+
waterMarkRepo.resetModeToText()
41+
}
42+
CMonet.init(this, true)
3043
}
31-
catchException()
3244
}
3345

3446
override fun onConfigurationChanged(newConfig: Configuration) {
3547
ThemeManager.onConfigurationChanged(newConfig)
3648
super.onConfigurationChanged(newConfig)
3749
}
3850

51+
fun launchSuccess() {
52+
recoveryMode = false
53+
val sp = getSharedPreferences(SP_NAME, Context.MODE_PRIVATE)
54+
sp.edit {
55+
putInt(SP_KEY_CRASH_COUNT, 0)
56+
}
57+
}
58+
3959
private fun catchException() {
4060
Thread.setDefaultUncaughtExceptionHandler { t, e ->
4161
// Because intent limit data to 1mb, so that we should limit the stack track by magic number below
62+
Log.e("MyApp", "uncaughtException")
4263
val maxStringLength = 1024 * 1024 / 2 / 10 // the 10 is a magic number ;)
4364
var fullStackTrace = Log.getStackTraceString(e)
4465
if (fullStackTrace.length > maxStringLength) {
4566
fullStackTrace = fullStackTrace.substring(IntRange(0, maxStringLength))
4667
}
47-
getSharedPreferences(SP_NAME, MODE_PRIVATE).edit(true) {
68+
Log.e("MyApp", "uncaughtException: $fullStackTrace")
69+
sp.edit(true) {
70+
putInt(SP_KEY_CRASH_COUNT, sp.getInt(SP_KEY_CRASH_COUNT, 0) + 1)
4871
putBoolean(KEY_IS_CRASH, true)
4972
putString(
5073
KEY_STACK_TRACE,
@@ -72,9 +95,13 @@ class MyApp : Application() {
7295
lateinit var instance: Context
7396
private set
7497

98+
var recoveryMode = false
99+
private set
100+
75101
const val SP_NAME = "sp_water_mark_crash_info"
76102

77103
const val KEY_IS_CRASH = SP_NAME + "_key_is_crash"
78104
const val KEY_STACK_TRACE = SP_NAME + "_key_stack_trace"
105+
const val SP_KEY_CRASH_COUNT = SP_NAME + "_key_crash_count"
79106
}
80107
}

app/src/main/java/me/rosuh/easywatermark/ui/MainActivity.kt

Lines changed: 103 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,14 @@ package me.rosuh.easywatermark.ui
22

33
import android.animation.ObjectAnimator
44
import android.annotation.SuppressLint
5+
import android.content.ClipData
6+
import android.content.ClipboardManager
57
import android.content.Context
68
import android.content.Intent
79
import android.content.Intent.ACTION_SEND
10+
import android.content.Intent.ACTION_VIEW
811
import android.content.pm.PackageManager
912
import android.content.res.ColorStateList
10-
import android.content.res.Configuration
1113
import android.graphics.Color
1214
import android.graphics.drawable.ColorDrawable
1315
import android.net.Uri
@@ -16,18 +18,26 @@ import android.os.Bundle
1618
import android.util.Log
1719
import android.view.*
1820
import android.view.View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
21+
import android.widget.Button
22+
import android.widget.TextView
1923
import android.widget.Toast
2024
import androidx.activity.result.ActivityResultLauncher
2125
import androidx.activity.viewModels
2226
import androidx.appcompat.app.AppCompatActivity
2327
import androidx.core.content.ContextCompat
2428
import androidx.core.content.edit
25-
import androidx.core.view.*
29+
import androidx.core.view.WindowCompat
30+
import androidx.core.view.WindowInsetsCompat
31+
import androidx.core.view.WindowInsetsControllerCompat
32+
import androidx.core.view.forEach
2633
import androidx.fragment.app.commit
34+
import androidx.lifecycle.lifecycleScope
2735
import androidx.recyclerview.widget.RecyclerView
2836
import com.google.android.material.dialog.MaterialAlertDialogBuilder
2937
import com.google.android.material.tabs.TabLayout
3038
import dagger.hilt.android.AndroidEntryPoint
39+
import kotlinx.coroutines.delay
40+
import kotlinx.coroutines.launch
3141
import me.rosuh.easywatermark.MyApp
3242
import me.rosuh.easywatermark.R
3343
import me.rosuh.easywatermark.data.model.FuncTitleModel
@@ -37,14 +47,18 @@ import me.rosuh.easywatermark.data.repo.WaterMarkRepository
3747
import me.rosuh.easywatermark.ui.about.AboutActivity
3848
import me.rosuh.easywatermark.ui.adapter.FuncPanelAdapter
3949
import me.rosuh.easywatermark.ui.adapter.PhotoListPreviewAdapter
40-
import me.rosuh.easywatermark.ui.dialog.*
50+
import me.rosuh.easywatermark.ui.dialog.CompressImageDialogFragment
51+
import me.rosuh.easywatermark.ui.dialog.EditTextBSDialogFragment
52+
import me.rosuh.easywatermark.ui.dialog.GalleryFragment
53+
import me.rosuh.easywatermark.ui.dialog.SaveImageBSDialogFragment
4154
import me.rosuh.easywatermark.ui.panel.*
4255
import me.rosuh.easywatermark.ui.widget.CenterLayoutManager
4356
import me.rosuh.easywatermark.ui.widget.LaunchView
44-
import me.rosuh.easywatermark.utils.*
57+
import me.rosuh.easywatermark.utils.FileUtils
58+
import me.rosuh.easywatermark.utils.PickImageContract
59+
import me.rosuh.easywatermark.utils.VibrateHelper
4560
import me.rosuh.easywatermark.utils.ktx.*
46-
import java.util.*
47-
import kotlin.collections.ArrayList
61+
import me.rosuh.easywatermark.utils.onItemClick
4862

4963

5064
@AndroidEntryPoint
@@ -129,6 +143,11 @@ class MainActivity : AppCompatActivity() {
129143

130144
override fun onCreate(savedInstanceState: Bundle?) {
131145
super.onCreate(savedInstanceState)
146+
if (MyApp.recoveryMode) {
147+
setContentView(R.layout.activity_recovery)
148+
initRecoveryView()
149+
return
150+
}
132151
launchView = LaunchView(this)
133152
setContentView(launchView)
134153
if (savedInstanceState == null) {
@@ -144,8 +163,67 @@ class MainActivity : AppCompatActivity() {
144163
SaveImageBSDialogFragment.safetyHide(this@MainActivity.supportFragmentManager)
145164
}
146165

166+
private fun initRecoveryView() {
167+
val tvCrashInfo = findViewById<TextView>(R.id.tv_crash_info).apply {
168+
with(getSharedPreferences(MyApp.SP_NAME, MODE_PRIVATE)) {
169+
val crashInfo = getString(MyApp.KEY_STACK_TRACE, "")
170+
text = crashInfo
171+
}
172+
}
173+
val btnCopy = findViewById<Button>(R.id.btn_copy).apply {
174+
setOnClickListener {
175+
try {
176+
val clipboard = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
177+
val clip = ClipData.newPlainText(tvCrashInfo.text, tvCrashInfo.text)
178+
clipboard.setPrimaryClip(clip)
179+
Toast.makeText(this@MainActivity, R.string.copy_success, Toast.LENGTH_SHORT)
180+
.show()
181+
} catch (e: Exception) {
182+
Toast.makeText(this@MainActivity, R.string.copy_failed, Toast.LENGTH_SHORT)
183+
.show()
184+
}
185+
}
186+
}
187+
val btnSendEmail = findViewById<Button>(R.id.btn_email).apply {
188+
setOnClickListener {
189+
viewModel.extraCrashInfo(this@MainActivity, tvCrashInfo.text.toString())
190+
}
191+
}
192+
val btnTelegram = findViewById<Button>(R.id.btn_telegram).apply {
193+
setOnClickListener {
194+
openLink("https://t.me/rosuh")
195+
}
196+
}
197+
val btnStore = findViewById<Button>(R.id.btn_store).apply {
198+
setOnClickListener {
199+
try {
200+
startActivity(
201+
Intent(
202+
Intent.ACTION_VIEW,
203+
Uri.parse("market://details?id=me.rosuh.easywatermark")
204+
)
205+
)
206+
} catch (e: Exception) {
207+
Toast.makeText(this@MainActivity, R.string.store_not_found, Toast.LENGTH_SHORT)
208+
.show()
209+
}
210+
}
211+
}
212+
213+
findViewById<Button>(R.id.btn_close_recovery_mode).apply {
214+
setOnClickListener {
215+
(MyApp.instance as MyApp).launchSuccess()
216+
Toast.makeText(this@MainActivity, R.string.recovery_mode_closed, Toast.LENGTH_SHORT)
217+
.show()
218+
}
219+
}
220+
}
221+
147222
override fun onWindowFocusChanged(hasFocus: Boolean) {
148223
super.onWindowFocusChanged(hasFocus)
224+
if (MyApp.recoveryMode) {
225+
return
226+
}
149227
if (hasFocus) {
150228
hideSystemUI()
151229
}
@@ -196,6 +274,20 @@ class MainActivity : AppCompatActivity() {
196274
}
197275
}
198276

277+
override fun onResume() {
278+
super.onResume()
279+
if (MyApp.recoveryMode) {
280+
return
281+
}
282+
lifecycleScope.launch {
283+
delay(1000)
284+
if (this@MainActivity.isFinishing) {
285+
return@launch
286+
}
287+
(MyApp.instance as? MyApp?)?.launchSuccess()
288+
}
289+
}
290+
199291
override fun onDestroy() {
200292
bgTransformAnimator?.cancel()
201293
super.onDestroy()
@@ -541,8 +633,10 @@ class MainActivity : AppCompatActivity() {
541633
} else {
542634
0
543635
}
544-
window.insetsController?.setSystemBarsAppearance(systemUiAppearance,
545-
WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS)
636+
window.insetsController?.setSystemBarsAppearance(
637+
systemUiAppearance,
638+
WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS
639+
)
546640
} else {
547641
val systemUiVisibilityFlags = if (!isInEditMode && !this.isNight()) {
548642
window.decorView.systemUiVisibility or SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
@@ -720,7 +814,7 @@ class MainActivity : AppCompatActivity() {
720814
hideDetailPanel()
721815
}
722816

723-
private fun setActivityBackground(color: Int){
817+
private fun setActivityBackground(color: Int) {
724818
(launchView.parent as? View?)?.setBackgroundColor(color)
725819
}
726820

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<shape xmlns:android="http://schemas.android.com/apk/res/android"
3+
android:shape="rectangle">
4+
5+
6+
<solid android:color="?attr/colorErrorContainer" />
7+
8+
<corners android:radius="20dp" />
9+
</shape>
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
3+
xmlns:app="http://schemas.android.com/apk/res-auto"
4+
xmlns:tools="http://schemas.android.com/tools"
5+
android:layout_width="match_parent"
6+
android:layout_height="match_parent">
7+
8+
9+
<com.google.android.material.textview.MaterialTextView
10+
android:id="@+id/tv_title"
11+
style="@style/TextAppearance.Material3.TitleLarge"
12+
android:layout_width="wrap_content"
13+
android:layout_height="wrap_content"
14+
android:layout_marginTop="16dp"
15+
android:text="@string/recovery_title"
16+
app:layout_constraintEnd_toEndOf="parent"
17+
app:layout_constraintStart_toStartOf="parent"
18+
app:layout_constraintTop_toTopOf="parent" />
19+
20+
<com.google.android.material.textview.MaterialTextView
21+
android:id="@+id/tv_info_title"
22+
style="@style/TextAppearance.Material3.TitleMedium"
23+
android:layout_width="wrap_content"
24+
android:layout_height="wrap_content"
25+
android:layout_marginStart="16dp"
26+
android:layout_marginTop="16dp"
27+
android:paddingStart="16dp"
28+
android:paddingEnd="16dp"
29+
android:text="糟糕,您似乎遇到了多次崩溃。现在已经自动进入恢复模式。以下是您的设备信息以及崩溃信息,辛苦您将其复制以后通过下列方式联系我们,将对解决问题非常有帮助。谢谢您!"
30+
app:layout_constraintEnd_toEndOf="parent"
31+
app:layout_constraintStart_toStartOf="parent"
32+
app:layout_constraintTop_toBottomOf="@+id/tv_title" />
33+
34+
35+
<ScrollView
36+
android:id="@+id/sv_info"
37+
android:layout_width="match_parent"
38+
android:layout_height="0dp"
39+
android:layout_margin="16dp"
40+
android:background="@drawable/bg_recovery_mode_info"
41+
app:layout_constraintBottom_toTopOf="@id/btn_copy"
42+
app:layout_constraintEnd_toEndOf="parent"
43+
app:layout_constraintStart_toStartOf="parent"
44+
app:layout_constraintTop_toBottomOf="@id/tv_info_title">
45+
46+
<com.google.android.material.textview.MaterialTextView
47+
android:id="@+id/tv_crash_info"
48+
android:layout_width="match_parent"
49+
android:layout_height="wrap_content"
50+
android:padding="16dp"
51+
android:textColor="?attr/colorOnErrorContainer"
52+
android:textIsSelectable="true" />
53+
</ScrollView>
54+
55+
<com.google.android.material.button.MaterialButton
56+
android:id="@+id/btn_copy"
57+
style="@style/ThemeOverlay.Material3.Button.ElevatedButton"
58+
android:layout_width="wrap_content"
59+
android:layout_height="wrap_content"
60+
android:layout_marginBottom="16dp"
61+
android:text="copy"
62+
app:layout_constraintBottom_toTopOf="@id/btn_email"
63+
app:layout_constraintEnd_toEndOf="parent"
64+
app:layout_constraintStart_toStartOf="parent" />
65+
66+
<com.google.android.material.button.MaterialButton
67+
android:id="@+id/btn_email"
68+
android:layout_width="wrap_content"
69+
android:layout_height="wrap_content"
70+
android:layout_marginTop="16dp"
71+
android:layout_marginBottom="16dp"
72+
android:text="Send email"
73+
app:layout_constraintBottom_toTopOf="@id/btn_telegram"
74+
app:layout_constraintEnd_toEndOf="parent"
75+
app:layout_constraintStart_toStartOf="parent" />
76+
77+
<com.google.android.material.button.MaterialButton
78+
android:id="@+id/btn_telegram"
79+
android:layout_width="wrap_content"
80+
android:layout_height="wrap_content"
81+
android:layout_marginTop="16dp"
82+
android:layout_marginBottom="16dp"
83+
android:text="Send Telegram"
84+
app:layout_constraintBottom_toTopOf="@id/btn_store"
85+
app:layout_constraintEnd_toEndOf="parent"
86+
app:layout_constraintStart_toStartOf="parent" />
87+
88+
<com.google.android.material.button.MaterialButton
89+
android:id="@+id/btn_store"
90+
android:layout_width="wrap_content"
91+
android:layout_height="wrap_content"
92+
android:layout_marginTop="16dp"
93+
android:layout_marginBottom="16dp"
94+
android:text="Jump to Store"
95+
app:layout_constraintBottom_toTopOf="@+id/btn_close_recovery_mode"
96+
app:layout_constraintEnd_toEndOf="parent"
97+
app:layout_constraintStart_toStartOf="parent" />
98+
99+
<com.google.android.material.button.MaterialButton
100+
android:id="@+id/btn_close_recovery_mode"
101+
android:layout_width="wrap_content"
102+
android:layout_height="wrap_content"
103+
android:layout_marginTop="16dp"
104+
android:layout_marginBottom="48dp"
105+
android:text="关闭恢复模式"
106+
app:layout_constraintBottom_toBottomOf="parent"
107+
app:layout_constraintEnd_toEndOf="parent"
108+
app:layout_constraintStart_toStartOf="parent" />
109+
</androidx.constraintlayout.widget.ConstraintLayout>

0 commit comments

Comments
 (0)