Consolidate autosave URI persistence management
This commit is contained in:
parent
1522806e62
commit
50d7622172
3 changed files with 50 additions and 61 deletions
|
@ -1,7 +1,6 @@
|
||||||
package com.wbrawner.simplemarkdown.view.activity
|
package com.wbrawner.simplemarkdown.view.activity
|
||||||
|
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.net.Uri
|
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
@ -9,7 +8,6 @@ import androidx.appcompat.app.AppCompatDelegate
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.preference.PreferenceManager
|
import androidx.preference.PreferenceManager
|
||||||
import com.wbrawner.simplemarkdown.R
|
import com.wbrawner.simplemarkdown.R
|
||||||
import com.wbrawner.simplemarkdown.viewmodel.PREF_KEY_AUTOSAVE_URI
|
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
|
||||||
|
@ -40,20 +38,12 @@ class SplashActivity : AppCompatActivity() {
|
||||||
}
|
}
|
||||||
|
|
||||||
AppCompatDelegate.setDefaultNightMode(darkMode)
|
AppCompatDelegate.setDefaultNightMode(darkMode)
|
||||||
val uri = withContext(Dispatchers.IO) {
|
val uri = intent?.data?.let {
|
||||||
intent?.data?.let {
|
Timber.d("Using uri from intent: $it")
|
||||||
Timber.d("Using uri from intent: $it")
|
it
|
||||||
it
|
} ?: run {
|
||||||
} ?: 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) {
|
|
||||||
Timber.d("No intent provided to load data from")
|
Timber.d("No intent provided to load data from")
|
||||||
|
null
|
||||||
}
|
}
|
||||||
|
|
||||||
val startIntent = Intent(this@SplashActivity, MainActivity::class.java)
|
val startIntent = Intent(this@SplashActivity, MainActivity::class.java)
|
||||||
|
|
|
@ -15,7 +15,6 @@ import androidx.appcompat.app.AlertDialog
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.core.app.ActivityCompat
|
import androidx.core.app.ActivityCompat
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.core.content.edit
|
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.fragment.app.viewModels
|
import androidx.fragment.app.viewModels
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
|
@ -28,7 +27,6 @@ import com.wbrawner.simplemarkdown.utility.ErrorHandler
|
||||||
import com.wbrawner.simplemarkdown.utility.errorHandlerImpl
|
import com.wbrawner.simplemarkdown.utility.errorHandlerImpl
|
||||||
import com.wbrawner.simplemarkdown.view.adapter.EditPagerAdapter
|
import com.wbrawner.simplemarkdown.view.adapter.EditPagerAdapter
|
||||||
import com.wbrawner.simplemarkdown.viewmodel.MarkdownViewModel
|
import com.wbrawner.simplemarkdown.viewmodel.MarkdownViewModel
|
||||||
import com.wbrawner.simplemarkdown.viewmodel.PREF_KEY_AUTOSAVE_URI
|
|
||||||
import kotlinx.android.synthetic.main.fragment_main.*
|
import kotlinx.android.synthetic.main.fragment_main.*
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
@ -212,21 +210,9 @@ class MainFragment : Fragment(), ActivityCompat.OnRequestPermissionsResultCallba
|
||||||
}
|
}
|
||||||
|
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
val fileLoaded = context?.let {
|
context?.let {
|
||||||
viewModel.load(it, data.data)
|
if (!viewModel.load(it, data.data)) {
|
||||||
}
|
Toast.makeText(it, R.string.file_load_error, Toast.LENGTH_SHORT).show()
|
||||||
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())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -253,11 +239,10 @@ class MainFragment : Fragment(), ActivityCompat.OnRequestPermissionsResultCallba
|
||||||
|
|
||||||
private fun promptSaveOrDiscardChanges() {
|
private fun promptSaveOrDiscardChanges() {
|
||||||
if (!viewModel.shouldPromptSave()) {
|
if (!viewModel.shouldPromptSave()) {
|
||||||
viewModel.reset("Untitled.md")
|
viewModel.reset(
|
||||||
Timber.i("Removing autosave uri from shared prefs")
|
"Untitled.md",
|
||||||
PreferenceManager.getDefaultSharedPreferences(requireContext()).edit {
|
PreferenceManager.getDefaultSharedPreferences(requireContext())
|
||||||
remove(PREF_KEY_AUTOSAVE_URI)
|
)
|
||||||
}
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
val context = context ?: run {
|
val context = context ?: run {
|
||||||
|
@ -268,11 +253,11 @@ class MainFragment : Fragment(), ActivityCompat.OnRequestPermissionsResultCallba
|
||||||
.setTitle(R.string.save_changes)
|
.setTitle(R.string.save_changes)
|
||||||
.setMessage(R.string.prompt_save_changes)
|
.setMessage(R.string.prompt_save_changes)
|
||||||
.setNegativeButton(R.string.action_discard) { _, _ ->
|
.setNegativeButton(R.string.action_discard) { _, _ ->
|
||||||
Timber.d("Discarding changes and deleting autosave uri from shared preferences")
|
Timber.d("Discarding changes")
|
||||||
viewModel.reset("Untitled.md")
|
viewModel.reset(
|
||||||
PreferenceManager.getDefaultSharedPreferences(requireContext()).edit {
|
"Untitled.md",
|
||||||
remove(PREF_KEY_AUTOSAVE_URI)
|
PreferenceManager.getDefaultSharedPreferences(requireContext())
|
||||||
}
|
)
|
||||||
}
|
}
|
||||||
.setPositiveButton(R.string.action_save) { _, _ ->
|
.setPositiveButton(R.string.action_save) { _, _ ->
|
||||||
Timber.d("Saving changes")
|
Timber.d("Saving changes")
|
||||||
|
|
|
@ -3,8 +3,10 @@ package com.wbrawner.simplemarkdown.viewmodel
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.SharedPreferences
|
import android.content.SharedPreferences
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
|
import androidx.core.content.edit
|
||||||
import androidx.lifecycle.MutableLiveData
|
import androidx.lifecycle.MutableLiveData
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
|
import androidx.preference.PreferenceManager
|
||||||
import com.wbrawner.simplemarkdown.utility.getName
|
import com.wbrawner.simplemarkdown.utility.getName
|
||||||
import com.wbrawner.simplemarkdown.view.fragment.MainFragment
|
import com.wbrawner.simplemarkdown.view.fragment.MainFragment
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
@ -32,10 +34,18 @@ class MarkdownViewModel(val timber: Timber.Tree = Timber.asTree()) : ViewModel()
|
||||||
isDirty.set(true)
|
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) {
|
if (uri == null) {
|
||||||
timber.i("Ignoring call to load null uri")
|
timber.i("No URI provided to load, attempting to load last autosaved file")
|
||||||
return false
|
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) {
|
return withContext(Dispatchers.IO) {
|
||||||
try {
|
try {
|
||||||
|
@ -55,6 +65,10 @@ class MarkdownViewModel(val timber: Timber.Tree = Timber.asTree()) : ViewModel()
|
||||||
timber.i("Loaded file $fileName from $fileInput")
|
timber.i("Loaded file $fileName from $fileInput")
|
||||||
timber.v("File contents:\n$content")
|
timber.v("File contents:\n$content")
|
||||||
isDirty.set(false)
|
isDirty.set(false)
|
||||||
|
timber.i("Persisting autosave uri in shared prefs: $uri")
|
||||||
|
sharedPrefs.edit()
|
||||||
|
.putString(PREF_KEY_AUTOSAVE_URI, uri.toString())
|
||||||
|
.apply()
|
||||||
true
|
true
|
||||||
} ?: run {
|
} ?: run {
|
||||||
timber.w("Open file descriptor returned null for uri: $uri")
|
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 {
|
val uri = givenUri?.let {
|
||||||
timber.i("Saving file with given uri: $it")
|
timber.i("Saving file with given uri: $it")
|
||||||
it
|
it
|
||||||
|
@ -94,6 +112,10 @@ class MarkdownViewModel(val timber: Timber.Tree = Timber.asTree()) : ViewModel()
|
||||||
this@MarkdownViewModel.uri.postValue(uri)
|
this@MarkdownViewModel.uri.postValue(uri)
|
||||||
isDirty.set(false)
|
isDirty.set(false)
|
||||||
timber.i("Saved file $fileName to uri $uri")
|
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
|
true
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
timber.e(e, "Failed to save file at uri: $uri")
|
timber.e(e, "Failed to save file at uri: $uri")
|
||||||
|
@ -114,37 +136,29 @@ class MarkdownViewModel(val timber: Timber.Tree = Timber.asTree()) : ViewModel()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
val uri = if (save(context)) {
|
if (save(context)) {
|
||||||
timber.i("Autosave with cached uri succeeded: ${uri.value}")
|
timber.i("Autosave with cached uri succeeded: ${uri.value}")
|
||||||
uri.value
|
|
||||||
} else {
|
} else {
|
||||||
// The user has left the app, with autosave enabled, and we don't already have a
|
// 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
|
// 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
|
// 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"))
|
val fileUri = Uri.fromFile(File(context.filesDir, fileName.value ?: "Untitled.md"))
|
||||||
timber.i("No cached uri for autosave, saving to $fileUri instead")
|
timber.i("No cached uri for autosave, saving to $fileUri instead")
|
||||||
if (save(context, fileUri)) {
|
save(context, fileUri)
|
||||||
fileUri
|
|
||||||
} else {
|
|
||||||
null
|
|
||||||
}
|
|
||||||
} ?: run {
|
|
||||||
timber.w("Unable to perform autosave, uri was null")
|
|
||||||
return@autosave
|
|
||||||
}
|
}
|
||||||
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")
|
timber.i("Resetting view model to default state")
|
||||||
fileName.postValue(untitledFileName)
|
fileName.postValue(untitledFileName)
|
||||||
uri.postValue(null)
|
uri.postValue(null)
|
||||||
markdownUpdates.postValue("")
|
markdownUpdates.postValue("")
|
||||||
editorActions.postValue(EditorAction.Load(""))
|
editorActions.postValue(EditorAction.Load(""))
|
||||||
isDirty.set(false)
|
isDirty.set(false)
|
||||||
|
timber.i("Removing autosave uri from shared prefs")
|
||||||
|
sharedPrefs.edit {
|
||||||
|
remove(PREF_KEY_AUTOSAVE_URI)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun shouldPromptSave() = isDirty.get()
|
fun shouldPromptSave() = isDirty.get()
|
||||||
|
|
Loading…
Reference in a new issue