Skip to content

Commit

Permalink
Handle crash upon app launch when storage folder is removed externally
Browse files Browse the repository at this point in the history
  • Loading branch information
tuancoltech committed Nov 29, 2024
1 parent 8d48ed4 commit 26fc90c
Show file tree
Hide file tree
Showing 9 changed files with 109 additions and 6 deletions.
5 changes: 5 additions & 0 deletions app/src/main/java/dev/arkbuilders/arkmemo/models/LoadError.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package dev.arkbuilders.arkmemo.models

sealed interface LoadError

data class RootNotFound(val rootPath: String) : LoadError
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,6 @@ interface MemoPreferences {
fun storeCrashReportEnabled(enabled: Boolean)

fun getCrashReportEnabled(): Boolean

fun storageNotAvailable(): Boolean
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import dev.arkbuilders.arkmemo.utils.CRASH_REPORT_ENABLE
import java.nio.file.Path
import javax.inject.Inject
import kotlin.io.path.Path
import kotlin.io.path.exists

private const val NAME = "memo_prefs"
private const val CURRENT_NOTES_PATH = "current_notes_path"
Expand All @@ -33,4 +34,8 @@ class MemoPreferencesImpl
}

override fun getCrashReportEnabled(): Boolean = sharedPreferences.getBoolean(CRASH_REPORT_ENABLE, true)

override fun storageNotAvailable(): Boolean {
return getPath().isEmpty() || !getNotesStorage().exists()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,16 @@ import dagger.hilt.android.AndroidEntryPoint
import dev.arkbuilders.arkmemo.R
import dev.arkbuilders.arkmemo.contracts.PermissionContract
import dev.arkbuilders.arkmemo.databinding.ActivityMainBinding
import dev.arkbuilders.arkmemo.models.RootNotFound
import dev.arkbuilders.arkmemo.preferences.MemoPreferences
import dev.arkbuilders.arkmemo.ui.dialogs.CommonActionDialog
import dev.arkbuilders.arkmemo.ui.dialogs.FilePickerDialog
import dev.arkbuilders.arkmemo.ui.fragments.BaseFragment
import dev.arkbuilders.arkmemo.ui.fragments.EditTextNotesFragment
import dev.arkbuilders.arkmemo.ui.fragments.NotesFragment
import dev.arkbuilders.components.filepicker.onArkPathPicked
import javax.inject.Inject
import kotlin.io.path.exists

@AndroidEntryPoint
class MainActivity : AppCompatActivity(R.layout.activity_main) {
Expand Down Expand Up @@ -92,8 +95,13 @@ class MainActivity : AppCompatActivity(R.layout.activity_main) {
}
}

if (memoPreferences.getPath().isEmpty()) {
FilePickerDialog.show(this, supportFragmentManager)
val storageFolderExisting = memoPreferences.getNotesStorage().exists()
if (memoPreferences.storageNotAvailable()) {
if (!storageFolderExisting) {
showNoNoteStorageDialog(RootNotFound(rootPath = memoPreferences.getPath()))
} else {
FilePickerDialog.show(this, supportFragmentManager)
}

supportFragmentManager.onArkPathPicked(this) {
memoPreferences.storePath(it.toString())
Expand All @@ -104,6 +112,27 @@ class MainActivity : AppCompatActivity(R.layout.activity_main) {
}
}

private fun showNoNoteStorageDialog(error: RootNotFound) {
val loadFailDialog =
CommonActionDialog(
title = getString(R.string.error_load_notes_failed_title),
message = getString(R.string.error_load_notes_failed_description, error.rootPath),
positiveText = R.string.error_load_notes_failed_positive_action,
negativeText = R.string.error_load_notes_failed_negative_action,
isAlert = false,
onPositiveClick = {
FilePickerDialog.show(this, supportFragmentManager)
},
onNegativeClicked = {
finish()
},
onCloseClicked = {
finish()
},
)
loadFailDialog.show(supportFragmentManager, CommonActionDialog.TAG)
}

override fun onSaveInstanceState(outState: Bundle) {
outState.putString(CURRENT_FRAGMENT_TAG, fragment.tag)
super.onSaveInstanceState(outState)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import dev.arkbuilders.arkmemo.ui.activities.MainActivity
import dev.arkbuilders.arkmemo.ui.fragments.ArkRecorderFragment
import dev.arkbuilders.arkmemo.ui.fragments.EditGraphicNotesFragment
import dev.arkbuilders.arkmemo.ui.fragments.EditTextNotesFragment
import dev.arkbuilders.arkmemo.ui.fragments.NotesFragment
import dev.arkbuilders.arkmemo.ui.viewmodels.ArkMediaPlayerSideEffect
import dev.arkbuilders.arkmemo.ui.viewmodels.ArkMediaPlayerState
import dev.arkbuilders.arkmemo.utils.getAutoTitle
Expand Down Expand Up @@ -295,6 +296,11 @@ class NotesListAdapter(

private val clickNoteToEditListener =
View.OnClickListener {
val storageFolderExist = (activity.fragment as? NotesFragment)?.checkForStorageExistence() ?: true
if (!storageFolderExist) {
return@OnClickListener
}

if (mActionMode) {
checkedByItemClick = true
binding.cbDelete.toggle()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,20 +24,18 @@ class FilePickerDialog : ArkFilePickerFragment() {

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
if (storageNotAvailable()) {
if (memoPreferences.storageNotAvailable()) {
isCancelable = false
}
}

override fun dismiss() {
super.dismiss()
if (storageNotAvailable()) {
if (memoPreferences.storageNotAvailable()) {
activity?.finish()
}
}

private fun storageNotAvailable(): Boolean = memoPreferences.getPath().isEmpty()

companion object {
private const val TAG = "file_picker"
private lateinit var fragmentManager: FragmentManager
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,12 @@ import dagger.hilt.android.AndroidEntryPoint
import dev.arkbuilders.arkmemo.R
import dev.arkbuilders.arkmemo.databinding.FragmentHomeBinding
import dev.arkbuilders.arkmemo.models.Note
import dev.arkbuilders.arkmemo.models.RootNotFound
import dev.arkbuilders.arkmemo.models.VoiceNote
import dev.arkbuilders.arkmemo.ui.activities.MainActivity
import dev.arkbuilders.arkmemo.ui.adapters.NotesListAdapter
import dev.arkbuilders.arkmemo.ui.dialogs.CommonActionDialog
import dev.arkbuilders.arkmemo.ui.dialogs.FilePickerDialog
import dev.arkbuilders.arkmemo.ui.viewmodels.ArkMediaPlayerSideEffect
import dev.arkbuilders.arkmemo.ui.viewmodels.ArkMediaPlayerViewModel
import dev.arkbuilders.arkmemo.ui.viewmodels.NotesViewModel
Expand Down Expand Up @@ -62,6 +64,10 @@ class NotesFragment : BaseFragment() {

private val pasteNoteClickListener =
View.OnClickListener {
if (!checkForStorageExistence()) {
return@OnClickListener
}

requireContext().getTextFromClipBoard(view) { clipBoardText ->
if (clipBoardText != null) {
activity.fragment = EditTextNotesFragment.newInstance(clipBoardText)
Expand Down Expand Up @@ -151,6 +157,9 @@ class NotesFragment : BaseFragment() {
activity.title = getString(R.string.app_name)
activity.supportActionBar?.setDisplayHomeAsUpEnabled(false)
binding.ivSettings.setOnClickListener {
if (!checkForStorageExistence()) {
return@setOnClickListener
}
activity.fragment = SettingsFragment()
activity.replaceFragment(activity.fragment, SettingsFragment::class.java.name)
}
Expand Down Expand Up @@ -419,6 +428,10 @@ class NotesFragment : BaseFragment() {
binding.groupFabActions.gone()
showingFloatingButtons = false
} else {
if (!checkForStorageExistence()) {
return
}

binding.fabNewAction.extend()
binding.fabNewAction.icon = ContextCompat.getDrawable(activity, R.drawable.ic_close)
binding.fabNewAction.backgroundTintList =
Expand Down Expand Up @@ -539,6 +552,35 @@ class NotesFragment : BaseFragment() {
}
}

private fun showNoNoteStorageDialog(error: RootNotFound) {
val loadFailDialog =
CommonActionDialog(
title = getString(R.string.error_load_notes_failed_title),
message = getString(R.string.error_load_notes_failed_description, error.rootPath),
positiveText = R.string.error_load_notes_failed_positive_action,
negativeText = R.string.error_load_notes_failed_negative_action,
isAlert = false,
onPositiveClick = {
FilePickerDialog.show(activity, fragmentManager = parentFragmentManager)
},
onNegativeClicked = {
activity.finish()
},
onCloseClicked = {
activity.finish()
},
)
loadFailDialog.show(parentFragmentManager, CommonActionDialog.TAG)
}

fun checkForStorageExistence(): Boolean {
if (notesViewModel.storageFolderNotAvailable()) {
showNoNoteStorageDialog(RootNotFound(rootPath = notesViewModel.getStorageFolderPath()))
return false
}
return true
}

companion object {
const val TAG = "NotesFragment"
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import dev.arkbuilders.arkmemo.models.Note
import dev.arkbuilders.arkmemo.models.SaveNoteResult
import dev.arkbuilders.arkmemo.models.TextNote
import dev.arkbuilders.arkmemo.models.VoiceNote
import dev.arkbuilders.arkmemo.preferences.MemoPreferences
import dev.arkbuilders.arkmemo.repo.NotesRepo
import dev.arkbuilders.arkmemo.utils.extractDuration
import kotlinx.coroutines.CoroutineDispatcher
Expand Down Expand Up @@ -39,6 +40,9 @@ class NotesViewModel
private val mSaveNoteResultLiveData = MutableLiveData<SaveNoteResult>()
private var searchJob: Job? = null

@Inject
lateinit var memoPreferences: MemoPreferences

fun init(extraBlock: () -> Unit) {
val initJob =
viewModelScope.launch(iODispatcher) {
Expand Down Expand Up @@ -179,4 +183,12 @@ class NotesViewModel
fun getSaveNoteResultLiveData(): LiveData<SaveNoteResult> {
return mSaveNoteResultLiveData
}

fun storageFolderNotAvailable(): Boolean {
return memoPreferences.storageNotAvailable()
}

fun getStorageFolderPath(): String {
return memoPreferences.getPath()
}
}
4 changes: 4 additions & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -117,5 +117,9 @@
</plurals>
<string name="select_all">Select All</string>
<string name="deselect_all">Deselect All</string>
<string name="error_load_notes_failed_title">Cannot find notes</string>
<string name="error_load_notes_failed_description">The folder %s with notes data cannot be located.\nPlease select a new folder.</string>
<string name="error_load_notes_failed_positive_action">Select</string>
<string name="error_load_notes_failed_negative_action">Leave</string>

</resources>

0 comments on commit 26fc90c

Please sign in to comment.