Consolidate autosave URI persistence management

This commit is contained in:
William Brawner 2021-02-21 18:32:53 -07:00
parent 1522806e62
commit 50d7622172
3 changed files with 50 additions and 61 deletions

View file

@ -1,7 +1,6 @@
package com.wbrawner.simplemarkdown.view.activity
import android.content.Intent
import android.net.Uri
import android.os.Build
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
@ -9,7 +8,6 @@ import androidx.appcompat.app.AppCompatDelegate
import androidx.lifecycle.lifecycleScope
import androidx.preference.PreferenceManager
import com.wbrawner.simplemarkdown.R
import com.wbrawner.simplemarkdown.viewmodel.PREF_KEY_AUTOSAVE_URI
import kotlinx.coroutines.*
import timber.log.Timber
@ -40,20 +38,12 @@ class SplashActivity : AppCompatActivity() {
}
AppCompatDelegate.setDefaultNightMode(darkMode)
val uri = withContext(Dispatchers.IO) {
intent?.data?.let {
Timber.d("Using uri from intent: $it")
it
} ?: PreferenceManager.getDefaultSharedPreferences(this@SplashActivity)
.getString(PREF_KEY_AUTOSAVE_URI, null)
?.let {
Timber.d("Using uri from shared preferences: $it")
Uri.parse(it)
}
}
if (uri == null) {
val uri = intent?.data?.let {
Timber.d("Using uri from intent: $it")
it
} ?: run {
Timber.d("No intent provided to load data from")
null
}
val startIntent = Intent(this@SplashActivity, MainActivity::class.java)

View file

@ -15,7 +15,6 @@ import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import androidx.core.content.edit
import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels
import androidx.lifecycle.lifecycleScope
@ -28,7 +27,6 @@ import com.wbrawner.simplemarkdown.utility.ErrorHandler
import com.wbrawner.simplemarkdown.utility.errorHandlerImpl
import com.wbrawner.simplemarkdown.view.adapter.EditPagerAdapter
import com.wbrawner.simplemarkdown.viewmodel.MarkdownViewModel
import com.wbrawner.simplemarkdown.viewmodel.PREF_KEY_AUTOSAVE_URI
import kotlinx.android.synthetic.main.fragment_main.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
@ -212,21 +210,9 @@ class MainFragment : Fragment(), ActivityCompat.OnRequestPermissionsResultCallba
}
lifecycleScope.launch {
val fileLoaded = context?.let {
viewModel.load(it, data.data)
}
if (fileLoaded == false) {
context?.let {
Toast.makeText(it, R.string.file_load_error, Toast.LENGTH_SHORT)
.show()
}
} else {
Timber.d(
"File load succeeded, updating autosave uri in shared prefs: %s",
data.data.toString()
)
PreferenceManager.getDefaultSharedPreferences(requireContext()).edit {
putString(PREF_KEY_AUTOSAVE_URI, data.data.toString())
context?.let {
if (!viewModel.load(it, data.data)) {
Toast.makeText(it, R.string.file_load_error, Toast.LENGTH_SHORT).show()
}
}
}
@ -253,11 +239,10 @@ class MainFragment : Fragment(), ActivityCompat.OnRequestPermissionsResultCallba
private fun promptSaveOrDiscardChanges() {
if (!viewModel.shouldPromptSave()) {
viewModel.reset("Untitled.md")
Timber.i("Removing autosave uri from shared prefs")
PreferenceManager.getDefaultSharedPreferences(requireContext()).edit {
remove(PREF_KEY_AUTOSAVE_URI)
}
viewModel.reset(
"Untitled.md",
PreferenceManager.getDefaultSharedPreferences(requireContext())
)
return
}
val context = context ?: run {
@ -268,11 +253,11 @@ class MainFragment : Fragment(), ActivityCompat.OnRequestPermissionsResultCallba
.setTitle(R.string.save_changes)
.setMessage(R.string.prompt_save_changes)
.setNegativeButton(R.string.action_discard) { _, _ ->
Timber.d("Discarding changes and deleting autosave uri from shared preferences")
viewModel.reset("Untitled.md")
PreferenceManager.getDefaultSharedPreferences(requireContext()).edit {
remove(PREF_KEY_AUTOSAVE_URI)
}
Timber.d("Discarding changes")
viewModel.reset(
"Untitled.md",
PreferenceManager.getDefaultSharedPreferences(requireContext())
)
}
.setPositiveButton(R.string.action_save) { _, _ ->
Timber.d("Saving changes")

View file

@ -3,8 +3,10 @@ package com.wbrawner.simplemarkdown.viewmodel
import android.content.Context
import android.content.SharedPreferences
import android.net.Uri
import androidx.core.content.edit
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.preference.PreferenceManager
import com.wbrawner.simplemarkdown.utility.getName
import com.wbrawner.simplemarkdown.view.fragment.MainFragment
import kotlinx.coroutines.Dispatchers
@ -32,10 +34,18 @@ class MarkdownViewModel(val timber: Timber.Tree = Timber.asTree()) : ViewModel()
isDirty.set(true)
}
suspend fun load(context: Context, uri: Uri?): Boolean {
suspend fun load(
context: Context,
uri: Uri?,
sharedPrefs: SharedPreferences = PreferenceManager.getDefaultSharedPreferences(context)
): Boolean {
if (uri == null) {
timber.i("Ignoring call to load null uri")
return false
timber.i("No URI provided to load, attempting to load last autosaved file")
sharedPrefs.getString(PREF_KEY_AUTOSAVE_URI, null)
?.let {
Timber.d("Using uri from shared preferences: $it")
return load(context, Uri.parse(it), sharedPrefs)
} ?: return false
}
return withContext(Dispatchers.IO) {
try {
@ -55,6 +65,10 @@ class MarkdownViewModel(val timber: Timber.Tree = Timber.asTree()) : ViewModel()
timber.i("Loaded file $fileName from $fileInput")
timber.v("File contents:\n$content")
isDirty.set(false)
timber.i("Persisting autosave uri in shared prefs: $uri")
sharedPrefs.edit()
.putString(PREF_KEY_AUTOSAVE_URI, uri.toString())
.apply()
true
} ?: run {
timber.w("Open file descriptor returned null for uri: $uri")
@ -67,7 +81,11 @@ class MarkdownViewModel(val timber: Timber.Tree = Timber.asTree()) : ViewModel()
}
}
suspend fun save(context: Context, givenUri: Uri? = null): Boolean = saveMutex.withLock {
suspend fun save(
context: Context,
givenUri: Uri? = null,
sharedPrefs: SharedPreferences = PreferenceManager.getDefaultSharedPreferences(context)
): Boolean = saveMutex.withLock {
val uri = givenUri?.let {
timber.i("Saving file with given uri: $it")
it
@ -94,6 +112,10 @@ class MarkdownViewModel(val timber: Timber.Tree = Timber.asTree()) : ViewModel()
this@MarkdownViewModel.uri.postValue(uri)
isDirty.set(false)
timber.i("Saved file $fileName to uri $uri")
timber.i("Persisting autosave uri in shared prefs: $uri")
sharedPrefs.edit()
.putString(PREF_KEY_AUTOSAVE_URI, uri.toString())
.apply()
true
} catch (e: Exception) {
timber.e(e, "Failed to save file at uri: $uri")
@ -114,37 +136,29 @@ class MarkdownViewModel(val timber: Timber.Tree = Timber.asTree()) : ViewModel()
return
}
val uri = if (save(context)) {
if (save(context)) {
timber.i("Autosave with cached uri succeeded: ${uri.value}")
uri.value
} else {
// The user has left the app, with autosave enabled, and we don't already have a
// Uri for them or for some reason we were unable to save to the original Uri. In
// this case, we need to just save to internal file storage so that we can recover
val fileUri = Uri.fromFile(File(context.filesDir, fileName.value ?: "Untitled.md"))
timber.i("No cached uri for autosave, saving to $fileUri instead")
if (save(context, fileUri)) {
fileUri
} else {
null
}
} ?: run {
timber.w("Unable to perform autosave, uri was null")
return@autosave
save(context, fileUri)
}
timber.i("Persisting autosave uri in shared prefs: $uri")
sharedPrefs.edit()
.putString(PREF_KEY_AUTOSAVE_URI, uri.toString())
.apply()
}
fun reset(untitledFileName: String) {
fun reset(untitledFileName: String, sharedPrefs: SharedPreferences) {
timber.i("Resetting view model to default state")
fileName.postValue(untitledFileName)
uri.postValue(null)
markdownUpdates.postValue("")
editorActions.postValue(EditorAction.Load(""))
isDirty.set(false)
timber.i("Removing autosave uri from shared prefs")
sharedPrefs.edit {
remove(PREF_KEY_AUTOSAVE_URI)
}
}
fun shouldPromptSave() = isDirty.get()