diff --git a/app/build.gradle b/app/build.gradle index 88cf1a46..dffa1ffa 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -84,13 +84,8 @@ dependencies { implementation 'androidx.preference:preference-ktx:1.2.0' implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.1' - //implementation 'com.github.ARK-Builders:ark-filepicker:b2bfa01ea7' - - implementation 'com.github.ark-builders:ark-filepicker:b2bfa01ea7' - implementation 'space.taran:arklib:0.1.0-SNAPSHOT-7df9a4e581' - - implementation 'ch.acra:acra-http:5.9.5' - implementation 'ch.acra:acra-dialog:5.9.5' + implementation 'dev.arkbuilders:arkfilepicker:0.1.1' + implementation 'dev.arkbuilders:arklib:0.3.3' implementation 'androidx.preference:preference:1.2.0' implementation "com.google.dagger:hilt-android:2.42" diff --git a/app/src/main/java/space/taran/arkmemo/App.kt b/app/src/main/java/space/taran/arkmemo/App.kt index 83839790..2083b325 100644 --- a/app/src/main/java/space/taran/arkmemo/App.kt +++ b/app/src/main/java/space/taran/arkmemo/App.kt @@ -3,6 +3,7 @@ package space.taran.arkmemo import android.app.Application import android.content.Context import dagger.hilt.android.HiltAndroidApp +import dev.arkbuilders.arklib.initArkLib import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch @@ -11,13 +12,17 @@ import org.acra.config.httpSender import org.acra.data.StringFormat import org.acra.ktx.initAcra import org.acra.sender.HttpSender +import dev.arkbuilders.arkfilepicker.folders.FoldersRepo import space.taran.arkmemo.space.taran.arkmemo.utils.Config @HiltAndroidApp class App: Application() { - override fun attachBaseContext(base: Context?) { - super.attachBaseContext(base) + override fun onCreate() { + super.onCreate() + System.loadLibrary("arklib") + initArkLib() + FoldersRepo.init(this) initAcra() } @@ -41,4 +46,4 @@ class App: Application() { } } } -} \ No newline at end of file +} diff --git a/app/src/main/java/space/taran/arkmemo/data/ResourceMeta.kt b/app/src/main/java/space/taran/arkmemo/data/ResourceMeta.kt index d32d62bb..d165bd32 100644 --- a/app/src/main/java/space/taran/arkmemo/data/ResourceMeta.kt +++ b/app/src/main/java/space/taran/arkmemo/data/ResourceMeta.kt @@ -1,9 +1,10 @@ package space.taran.arkmemo.data +import dev.arkbuilders.arklib.ResourceId import java.nio.file.attribute.FileTime data class ResourceMeta( - val id: Long, + val id: ResourceId, val name: String, val extension: String, val modified: FileTime, diff --git a/app/src/main/java/space/taran/arkmemo/data/repositories/TextNotesRepo.kt b/app/src/main/java/space/taran/arkmemo/data/repositories/TextNotesRepo.kt new file mode 100644 index 00000000..10b9e947 --- /dev/null +++ b/app/src/main/java/space/taran/arkmemo/data/repositories/TextNotesRepo.kt @@ -0,0 +1,138 @@ +package space.taran.arkmemo.data.repositories + +import android.util.Log +import dev.arkbuilders.arklib.ResourceId +import dev.arkbuilders.arklib.computeId +import dev.arkbuilders.arklib.data.index.RootIndex +import dev.arkbuilders.arklib.user.properties.Properties +import dev.arkbuilders.arklib.user.properties.PropertiesStorage +import dev.arkbuilders.arklib.user.properties.PropertiesStorageRepo +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.job +import kotlinx.coroutines.withContext +import space.taran.arkmemo.data.ResourceMeta +import space.taran.arkmemo.models.TextNote +import java.nio.file.Files +import java.nio.file.Path +import javax.inject.Inject +import javax.inject.Singleton +import kotlin.io.path.deleteIfExists +import kotlin.io.path.exists +import kotlin.io.path.extension +import kotlin.io.path.fileSize +import kotlin.io.path.forEachLine +import kotlin.io.path.getLastModifiedTime +import kotlin.io.path.moveTo +import kotlin.io.path.name +import kotlin.io.path.writeLines + +@Singleton +class TextNotesRepo @Inject constructor() { + + private val iODispatcher = Dispatchers.IO + + private lateinit var propertiesStorage: PropertiesStorage + private lateinit var propertiesStorageRepo: PropertiesStorageRepo + + private lateinit var root: Path + + suspend fun init(root: Path, scope: CoroutineScope) { + this.root = root + propertiesStorageRepo = PropertiesStorageRepo(scope) + propertiesStorage = propertiesStorageRepo.provide(RootIndex.provide(root)) + } + + suspend fun save(note: TextNote) { + write(note) + } + + suspend fun delete(note: TextNote) = withContext(iODispatcher) { + val path = root.resolve("${note.meta?.name}") + delete(path) + propertiesStorage.remove(note.meta?.id!!) + propertiesStorage.persist() + Log.d("text-repo", "${note.meta?.name!!} has been deleted") + } + + suspend fun read(): List = withContext(Dispatchers.IO) { + val notes = mutableListOf() + Files.list(root).forEach { path -> + if (path.fileName.extension == NOTE_EXT) { + val data = StringBuilder() + path.forEachLine { + data.appendLine(it) + } + val size = path.fileSize() + val id = computeId(size, path) + val meta = ResourceMeta( + id, + path.fileName.name, + path.extension, + path.getLastModifiedTime(), + size + ) + val titles = propertiesStorage.getProperties(id).titles + val content = TextNote.Content(titles.elementAt(0), data.toString()) + val note = TextNote(content, meta) + notes.add(note) + } + } + Log.d("text-repo", "${notes.size} text note resources found") + notes + } + + private suspend fun write(note: TextNote) = withContext(Dispatchers.IO) { + val tempPath = kotlin.io.path.createTempFile() + val lines = note.content.data.split('\n') + tempPath.writeLines(lines) + val size = tempPath.fileSize() + val id = computeId(size, tempPath) + Log.d("text-repo", "initial resource name ${tempPath.name}") + persistNoteProperties(resourceId = id, noteTitle = note.content.title) + + val resourcePath = root.resolve("$id.$NOTE_EXT") + renameResourceWithNewResourceMeta( + note = note, + tempPath = tempPath, + resourcePath = resourcePath, + resourceId = id, + size = size + ) + } + + private suspend fun persistNoteProperties(resourceId: ResourceId, noteTitle: String) { + with(propertiesStorage) { + val properties = Properties(setOf(noteTitle), setOf()) + setProperties(resourceId, properties) + persist() + } + } + + private fun renameResourceWithNewResourceMeta( + note: TextNote, + tempPath: Path, + resourcePath: Path, + resourceId: ResourceId, + size: Long + ) { + tempPath.moveTo(resourcePath) + note.meta = ResourceMeta( + id = resourceId, + name = resourcePath.fileName.name, + extension = resourcePath.extension, + modified = resourcePath.getLastModifiedTime(), + size = size + ) + Log.d("notes-repo", "resource renamed to ${resourcePath.name} successfully") + } + + private fun delete(path: Path) { + path.deleteIfExists() + } +} + +private const val NOTE_EXT = "note" + diff --git a/app/src/main/java/space/taran/arkmemo/data/repositories/TextNotesRepository.kt b/app/src/main/java/space/taran/arkmemo/data/repositories/TextNotesRepository.kt deleted file mode 100644 index 264025f3..00000000 --- a/app/src/main/java/space/taran/arkmemo/data/repositories/TextNotesRepository.kt +++ /dev/null @@ -1,143 +0,0 @@ -package space.taran.arkmemo.data.repositories - -import android.content.Context -import android.util.Log -import dagger.hilt.android.qualifiers.ApplicationContext -import space.taran.arklib.computeId -import space.taran.arkmemo.data.ResourceMeta -import space.taran.arkmemo.files.parsers.JsonParser -import space.taran.arkmemo.models.TextNote -import space.taran.arkmemo.preferences.MemoPreferences -import java.io.* -import java.nio.file.Files -import java.nio.file.Path -import javax.inject.Inject -import kotlin.io.path.extension -import kotlin.io.path.getLastModifiedTime - -class TextNotesRepository @Inject constructor() { - - @Inject @ApplicationContext lateinit var context: Context - - fun saveNote(note: TextNote?) { - if (note != null) { - val path = getPath() - if (path != null) { - Files.list(path) - createTextNoteFile( - path, - JsonParser.parseNoteToJson(note.content), - ) - } - } - } - - fun deleteNote(note: TextNote) { - val filePath = getPath()?.resolve("${note.meta?.name}") - removeFileFromMemory(filePath) - Log.d("Deleted", note.meta?.name!!) - } - - fun getAllNotes(): List { - val notes = mutableListOf() - val path = getPath() - var number = 0 - if (path != null) { - Files.list(path).forEach { filePath -> - if (filePath.fileName.extension == NOTE_EXT) { - number += 1 - try { - val jsonFile = filePath.toFile() - val fileReader = FileReader(jsonFile) - val bufferedReader = BufferedReader(fileReader) - val jsonTextNote = StringBuilder() - with(bufferedReader) { - forEachLine { - jsonTextNote.append(it) - } - val content = JsonParser.parseNoteFromJson(jsonTextNote.toString()) - val size = Files.size(filePath) - val id = computeId(size, filePath) - val meta = ResourceMeta( - id, - filePath.fileName.toString(), - filePath.extension, - filePath.getLastModifiedTime(), - size - ) - - val note = TextNote(content, meta) - notes.add(note) - close() - } - } catch (e: Exception) { - e.printStackTrace() - } - } - } - } - return notes - } - - private fun createTextNoteFile(path: Path?, noteString: String?) { - fun writeToFile(bufferedWriter: BufferedWriter) { - with(bufferedWriter) { - write(noteString) - close() - } - } - if (path != null) { - val file = path.toFile() - val noteFile = File(file, "${DUMMY_FILENAME}.${NOTE_EXT}") - if (!noteFile.exists()) { - try { - val fileWriter = FileWriter(noteFile) - val bufferedWriter = BufferedWriter(fileWriter) - writeToFile(bufferedWriter) - - val id = computeId(Files.size(noteFile.toPath()), noteFile.toPath()) - - Log.d("Filename", noteFile.name) - - with(noteFile){ - val newFile = File(file, "$id.${NOTE_EXT}") - - if(!newFile.exists()) - if (renameTo(newFile)) - Log.d("New filename", newFile.name) - else - removeFileFromMemory(noteFile.toPath()) - - } - - } catch (e: Exception) { - e.printStackTrace() - } - } - } - } - - private fun removeFileFromMemory(path: Path?) { - if (path != null) - Files.deleteIfExists(path) - } - - private fun getPath(): Path? { - val prefs = MemoPreferences.getInstance(context) - val pathString = prefs.getPath() - var path: Path? = null - try { - val file = File(pathString!!) - file.mkdir() - path = file.toPath() - } catch (e: Exception) { - e.printStackTrace() - } - return path - } - - companion object { - private const val NOTE_EXT = "note" - private const val DUMMY_FILENAME = "Note" - } -} \ No newline at end of file diff --git a/app/src/main/java/space/taran/arkmemo/data/viewmodels/EditTextNotesViewModel.kt b/app/src/main/java/space/taran/arkmemo/data/viewmodels/EditTextNotesViewModel.kt deleted file mode 100644 index e1d74424..00000000 --- a/app/src/main/java/space/taran/arkmemo/data/viewmodels/EditTextNotesViewModel.kt +++ /dev/null @@ -1,25 +0,0 @@ -package space.taran.arkmemo.data.viewmodels - -import android.content.Context -import androidx.lifecycle.ViewModel -import androidx.lifecycle.viewModelScope -import dagger.hilt.android.lifecycle.HiltViewModel -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch -import space.taran.arkmemo.data.repositories.TextNotesRepository -import space.taran.arkmemo.models.TextNote -import javax.inject.Inject - -@HiltViewModel -class EditTextNotesViewModel @Inject constructor(): ViewModel() { - - private val iODispatcher = Dispatchers.IO - - @Inject lateinit var repo: TextNotesRepository - - fun saveNote(note: TextNote){ - viewModelScope.launch(iODispatcher) { - repo.saveNote(note) - } - } -} \ No newline at end of file diff --git a/app/src/main/java/space/taran/arkmemo/data/viewmodels/TextNotesViewModel.kt b/app/src/main/java/space/taran/arkmemo/data/viewmodels/TextNotesViewModel.kt deleted file mode 100644 index cfc2492f..00000000 --- a/app/src/main/java/space/taran/arkmemo/data/viewmodels/TextNotesViewModel.kt +++ /dev/null @@ -1,38 +0,0 @@ -package space.taran.arkmemo.data.viewmodels - -import android.content.Context -import androidx.lifecycle.ViewModel -import androidx.lifecycle.viewModelScope -import dagger.hilt.android.lifecycle.HiltViewModel -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext -import space.taran.arkmemo.data.repositories.TextNotesRepository -import space.taran.arkmemo.models.TextNote -import javax.inject.Inject - -@HiltViewModel -class TextNotesViewModel @Inject constructor(): ViewModel() { - - @Inject lateinit var textNotesRepo: TextNotesRepository - private val iODispatcher = Dispatchers.IO - private val textNotes: MutableStateFlow> by lazy{ - MutableStateFlow(listOf()) - } - - fun deleteNote(note: TextNote){ - viewModelScope.launch(iODispatcher) { - textNotesRepo.deleteNote(note) - textNotes.value = textNotesRepo.getAllNotes() - } - } - - fun getAllNotes(): StateFlow>{ - viewModelScope.launch(iODispatcher) { - textNotes.value = textNotesRepo.getAllNotes() - } - return textNotes - } -} \ No newline at end of file diff --git a/app/src/main/java/space/taran/arkmemo/files/FilePicker.kt b/app/src/main/java/space/taran/arkmemo/files/FilePicker.kt index 6681f172..e498e45b 100644 --- a/app/src/main/java/space/taran/arkmemo/files/FilePicker.kt +++ b/app/src/main/java/space/taran/arkmemo/files/FilePicker.kt @@ -10,9 +10,9 @@ import androidx.activity.result.ActivityResultLauncher import androidx.appcompat.app.AppCompatActivity import androidx.core.content.ContextCompat import androidx.fragment.app.FragmentManager -import space.taran.arkfilepicker.ArkFilePickerConfig -import space.taran.arkfilepicker.ArkFilePickerFragment -import space.taran.arkfilepicker.ArkFilePickerMode +import dev.arkbuilders.arkfilepicker.ArkFilePickerConfig +import dev.arkbuilders.arkfilepicker.presentation.filepicker.ArkFilePickerFragment +import dev.arkbuilders.arkfilepicker.presentation.filepicker.ArkFilePickerMode import space.taran.arkmemo.BuildConfig import space.taran.arkmemo.R @@ -65,4 +65,4 @@ class FilePicker private constructor(){ pickButtonStringId = R.string.select ) } -} \ No newline at end of file +} diff --git a/app/src/main/java/space/taran/arkmemo/files/parsers/JsonParser.kt b/app/src/main/java/space/taran/arkmemo/files/parsers/JsonParser.kt index 60e6668d..ab1e5282 100644 --- a/app/src/main/java/space/taran/arkmemo/files/parsers/JsonParser.kt +++ b/app/src/main/java/space/taran/arkmemo/files/parsers/JsonParser.kt @@ -1,5 +1,6 @@ package space.taran.arkmemo.files.parsers +import android.graphics.Path import com.google.gson.Gson import space.taran.arkmemo.models.TextNote @@ -14,4 +15,4 @@ class JsonParser { gson.fromJson(json, TextNote.Content::class.java) } -} \ No newline at end of file +} diff --git a/app/src/main/java/space/taran/arkmemo/models/TextNote.kt b/app/src/main/java/space/taran/arkmemo/models/TextNote.kt index 9b007f5b..b0e3f726 100644 --- a/app/src/main/java/space/taran/arkmemo/models/TextNote.kt +++ b/app/src/main/java/space/taran/arkmemo/models/TextNote.kt @@ -1,6 +1,5 @@ package space.taran.arkmemo.models -import android.os.Parcel import android.os.Parcelable import kotlinx.parcelize.IgnoredOnParcel import space.taran.arkmemo.data.ResourceMeta @@ -10,7 +9,7 @@ import kotlinx.parcelize.Parcelize data class TextNote ( val content: Content, @IgnoredOnParcel - val meta: ResourceMeta? = null + var meta: ResourceMeta? = null ): Parcelable { @Parcelize diff --git a/app/src/main/java/space/taran/arkmemo/preferences/MemoPreferences.kt b/app/src/main/java/space/taran/arkmemo/preferences/MemoPreferences.kt index fb5ccde6..1e7b5448 100644 --- a/app/src/main/java/space/taran/arkmemo/preferences/MemoPreferences.kt +++ b/app/src/main/java/space/taran/arkmemo/preferences/MemoPreferences.kt @@ -2,6 +2,8 @@ package space.taran.arkmemo.preferences import android.content.Context import android.content.Context.MODE_PRIVATE +import java.io.File +import java.nio.file.Path class MemoPreferences private constructor(context: Context) { private val sharedPreferences = context.getSharedPreferences(NAME, MODE_PRIVATE) @@ -14,7 +16,20 @@ class MemoPreferences private constructor(context: Context) { } } - fun getPath() = sharedPreferences.getString(CURRENT_NOTES_PATH, null) + fun getPathString() = sharedPreferences.getString(CURRENT_NOTES_PATH, null) + + fun getPath(): Path? { + val pathString = getPathString() + var path: Path? = null + try { + val file = File(pathString!!) + file.mkdir() + path = file.toPath() + } catch (e: Exception) { + e.printStackTrace() + } + return path + } companion object{ private const val NAME = "memo_prefs" @@ -26,5 +41,7 @@ class MemoPreferences private constructor(context: Context) { preferences = MemoPreferences(context) return preferences!! } + + fun getInstance() = preferences!! } } \ No newline at end of file diff --git a/app/src/main/java/space/taran/arkmemo/ui/activities/MainActivity.kt b/app/src/main/java/space/taran/arkmemo/ui/activities/MainActivity.kt index 03b0f77c..dba5cdcc 100644 --- a/app/src/main/java/space/taran/arkmemo/ui/activities/MainActivity.kt +++ b/app/src/main/java/space/taran/arkmemo/ui/activities/MainActivity.kt @@ -10,10 +10,11 @@ import android.view.MenuItem import androidx.activity.result.contract.ActivityResultContracts import androidx.annotation.IdRes import androidx.appcompat.app.AppCompatActivity +import androidx.core.view.isVisible import androidx.fragment.app.Fragment import by.kirich1409.viewbindingdelegate.viewBinding import dagger.hilt.android.AndroidEntryPoint -import space.taran.arkfilepicker.onArkPathPicked +import dev.arkbuilders.arkfilepicker.presentation.onArkPathPicked import space.taran.arkmemo.R import space.taran.arkmemo.contracts.PermissionContract import space.taran.arkmemo.databinding.ActivityMainBinding @@ -51,15 +52,13 @@ class MainActivity : AppCompatActivity(R.layout.activity_main) { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - System.loadLibrary("arklib") - setContentView(binding.root) setSupportActionBar(binding.toolbar) binding.toolbar.setNavigationOnClickListener { - onBackPressed() + onBackPressedDispatcher.onBackPressed() } - fun showFragment(){ + fun showFragment() { val textDataFromIntent = intent?.getStringExtra(Intent.EXTRA_TEXT) if (textDataFromIntent != null) { fragment = EditTextNotesFragment.newInstance(textDataFromIntent) @@ -85,7 +84,7 @@ class MainActivity : AppCompatActivity(R.layout.activity_main) { } - if (MemoPreferences.getInstance(this).getPath() == null) { + if (MemoPreferences.getInstance(this).getPathString() == null) { FilePicker.show(this, supportFragmentManager) supportFragmentManager.onArkPathPicked(this) { @@ -126,10 +125,9 @@ class MainActivity : AppCompatActivity(R.layout.activity_main) { } } - fun showFragment(){ - + fun showProgressBar(show: Boolean) { + binding.progressBar.isVisible = show } - companion object{ private const val CURRENT_FRAGMENT_TAG = "current fragment tag" } diff --git a/app/src/main/java/space/taran/arkmemo/ui/adapters/TextNotesListAdapter.kt b/app/src/main/java/space/taran/arkmemo/ui/adapters/TextNotesListAdapter.kt index 1e52eb62..c51c5119 100644 --- a/app/src/main/java/space/taran/arkmemo/ui/adapters/TextNotesListAdapter.kt +++ b/app/src/main/java/space/taran/arkmemo/ui/adapters/TextNotesListAdapter.kt @@ -35,7 +35,7 @@ class TextNotesListAdapter(private val notes: List): RecyclerView.Adap override fun onBindViewHolder(holder: NoteViewHolder, position: Int) { holder.title.text = notes[position].content.title - holder.date.text = notes[position].meta?.modified.toString() + holder.date.text = notes[position].meta?.modified?.toString() ?: "Just now" } override fun getItemCount() = notes.size diff --git a/app/src/main/java/space/taran/arkmemo/ui/dialogs/NoteDeleteDialogFragment.kt b/app/src/main/java/space/taran/arkmemo/ui/dialogs/NoteDeleteDialogFragment.kt index fe9c0c4d..248585d4 100644 --- a/app/src/main/java/space/taran/arkmemo/ui/dialogs/NoteDeleteDialogFragment.kt +++ b/app/src/main/java/space/taran/arkmemo/ui/dialogs/NoteDeleteDialogFragment.kt @@ -7,7 +7,7 @@ import androidx.appcompat.app.AlertDialog import androidx.fragment.app.DialogFragment import space.taran.arkmemo.R import space.taran.arkmemo.models.TextNote -import space.taran.arkmemo.ui.fragments.deleteTextNote +import space.taran.arkmemo.ui.fragments.deleteNote class NoteDeleteDialogFragment: DialogFragment() { @@ -27,7 +27,7 @@ class NoteDeleteDialogFragment: DialogFragment() { } .setPositiveButton(R.string.ark_memo_ok){ dialog, _ -> if(note != null) { - parentFragment?.deleteTextNote(note!!) + parentFragment?.deleteNote(note!!) Toast.makeText( requireContext(), getString(R.string.note_deleted), Toast.LENGTH_SHORT diff --git a/app/src/main/java/space/taran/arkmemo/ui/fragments/EditTextNotesFragment.kt b/app/src/main/java/space/taran/arkmemo/ui/fragments/EditTextNotesFragment.kt index 70ca2c7b..2abd0067 100644 --- a/app/src/main/java/space/taran/arkmemo/ui/fragments/EditTextNotesFragment.kt +++ b/app/src/main/java/space/taran/arkmemo/ui/fragments/EditTextNotesFragment.kt @@ -3,17 +3,14 @@ package space.taran.arkmemo.ui.fragments import android.os.Bundle import android.text.Editable import android.text.TextWatcher -import android.util.Log import android.view.View import android.widget.Toast import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels -import androidx.fragment.app.viewModels import by.kirich1409.viewbindingdelegate.viewBinding import dagger.hilt.android.AndroidEntryPoint import space.taran.arkmemo.R -import space.taran.arkmemo.data.viewmodels.EditTextNotesViewModel -import space.taran.arkmemo.data.viewmodels.TextNotesViewModel +import space.taran.arkmemo.ui.viewmodels.NotesViewModel import space.taran.arkmemo.databinding.FragmentEditTextNotesBinding import space.taran.arkmemo.models.TextNote import space.taran.arkmemo.ui.activities.MainActivity @@ -25,16 +22,21 @@ class EditTextNotesFragment: Fragment(R.layout.fragment_edit_text_notes) { requireActivity() as MainActivity } - private val editViewModel: EditTextNotesViewModel by viewModels() + private val notesViewModel: NotesViewModel by activityViewModels() private val binding by viewBinding(FragmentEditTextNotesBinding::bind) - private var note: TextNote? = null + private var note = TextNote(TextNote.Content("", "")) private var noteStr: String? = null + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + notesViewModel.init() + } + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - var note:TextNote? = null + var note = this.note val editTextListener = object: TextWatcher{ override fun afterTextChanged(s: Editable?) = Unit @@ -42,9 +44,9 @@ class EditTextNotesFragment: Fragment(R.layout.fragment_edit_text_notes) { override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) { val noteString = s?.toString() - var title = "" + var title = this@EditTextNotesFragment.note.content.title if(noteString != null){ - for(char in noteString){ + if (title == "") for(char in noteString){ if(char != '\n'){ title += char } @@ -64,8 +66,9 @@ class EditTextNotesFragment: Fragment(R.layout.fragment_edit_text_notes) { val saveNoteButton = binding.saveNote if(arguments != null) { - this.note = requireArguments().getParcelable(NOTE_KEY) - //Log.d("Note", "${this.note?.content}") + requireArguments().getParcelable(NOTE_KEY)?.let { + this.note = it + } noteStr = requireArguments().getString(NOTE_STRING_KEY) } @@ -75,21 +78,19 @@ class EditTextNotesFragment: Fragment(R.layout.fragment_edit_text_notes) { editNote.requestFocus() editNote.addTextChangedListener(editTextListener) - - if(this.note != null) - editNote.setText(this.note?.content?.data!!) + editNote.setText(this.note.content.data) if(noteStr != null) editNote.setText(noteStr) saveNoteButton.setOnClickListener { - if(note != null) { - with(editViewModel){ - saveNote(note!!) + notesViewModel.onSaveClick(note) { show -> + activity.showProgressBar(show) + if (!show) { Toast.makeText(requireContext(), getString(R.string.ark_memo_note_saved), Toast.LENGTH_SHORT) .show() - activity.onBackPressed() + activity.onBackPressedDispatcher.onBackPressed() } } } diff --git a/app/src/main/java/space/taran/arkmemo/ui/fragments/TextNotesFragment.kt b/app/src/main/java/space/taran/arkmemo/ui/fragments/NotesFragment.kt similarity index 72% rename from app/src/main/java/space/taran/arkmemo/ui/fragments/TextNotesFragment.kt rename to app/src/main/java/space/taran/arkmemo/ui/fragments/NotesFragment.kt index f7e49a7b..fcb4cfcb 100644 --- a/app/src/main/java/space/taran/arkmemo/ui/fragments/TextNotesFragment.kt +++ b/app/src/main/java/space/taran/arkmemo/ui/fragments/NotesFragment.kt @@ -5,7 +5,7 @@ import android.view.View import android.widget.Button import android.widget.Toast import androidx.fragment.app.Fragment -import androidx.fragment.app.viewModels +import androidx.fragment.app.activityViewModels import androidx.lifecycle.Lifecycle import androidx.lifecycle.lifecycleScope import androidx.lifecycle.repeatOnLifecycle @@ -16,7 +16,7 @@ import com.google.android.material.floatingactionbutton.FloatingActionButton import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.launch import space.taran.arkmemo.R -import space.taran.arkmemo.data.viewmodels.TextNotesViewModel +import space.taran.arkmemo.ui.viewmodels.NotesViewModel import space.taran.arkmemo.databinding.FragmentTextNotesBinding import space.taran.arkmemo.models.TextNote import space.taran.arkmemo.ui.activities.MainActivity @@ -32,14 +32,15 @@ class TextNotesFragment: Fragment(R.layout.fragment_text_notes) { private val activity: MainActivity by lazy { requireActivity() as MainActivity } - private val textNotesViewModel: TextNotesViewModel by viewModels() + + private val textNotesViewModel: NotesViewModel by activityViewModels() private lateinit var newNoteButton: FloatingActionButton private lateinit var pasteNoteButton: Button private lateinit var recyclerView: RecyclerView - private val newNoteClickListener = View.OnClickListener{ + private val newNoteClickListener = View.OnClickListener { activity.fragment = EditTextNotesFragment() activity.replaceFragment(activity.fragment, EditTextNotesFragment.TAG) } @@ -53,6 +54,11 @@ class TextNotesFragment: Fragment(R.layout.fragment_text_notes) { else Toast.makeText(requireContext(), getString(R.string.nothing_to_paste), Toast.LENGTH_SHORT).show() } + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + textNotesViewModel.init() + } + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) recyclerView = binding.include.recyclerView @@ -63,17 +69,15 @@ class TextNotesFragment: Fragment(R.layout.fragment_text_notes) { newNoteButton.setOnClickListener(newNoteClickListener) pasteNoteButton.setOnClickListener(pasteNoteClickListener) lifecycleScope.launch { - viewLifecycleOwner.apply{ - repeatOnLifecycle(Lifecycle.State.STARTED) { - textNotesViewModel.getAllNotes().collect { - val adapter = TextNotesListAdapter(it) - val layoutManager = LinearLayoutManager(requireContext()) - adapter.setActivity(activity) - adapter.setFragmentManager(childFragmentManager) - recyclerView.apply { - this.layoutManager = layoutManager - this.adapter = adapter - } + viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) { + textNotesViewModel.getTextNotes { + val adapter = TextNotesListAdapter(it) + val layoutManager = LinearLayoutManager(requireContext()) + adapter.setActivity(activity) + adapter.setFragmentManager(childFragmentManager) + recyclerView.apply { + this.layoutManager = layoutManager + this.adapter = adapter } } } @@ -85,12 +89,12 @@ class TextNotesFragment: Fragment(R.layout.fragment_text_notes) { activity.fragment = this } - companion object{ + companion object { const val TAG = "Text Notes Fragment" } } -fun Fragment.deleteTextNote(note: TextNote){ - val viewModel: TextNotesViewModel by viewModels() - viewModel.deleteNote(note) +fun Fragment.deleteNote(note: TextNote){ + val viewModel: NotesViewModel by activityViewModels() + viewModel.onDelete(note) } \ No newline at end of file diff --git a/app/src/main/java/space/taran/arkmemo/ui/fragments/SettingsFragment.kt b/app/src/main/java/space/taran/arkmemo/ui/fragments/SettingsFragment.kt index 3f2441ae..ea6f2727 100644 --- a/app/src/main/java/space/taran/arkmemo/ui/fragments/SettingsFragment.kt +++ b/app/src/main/java/space/taran/arkmemo/ui/fragments/SettingsFragment.kt @@ -3,7 +3,7 @@ package space.taran.arkmemo.ui.fragments import android.os.Bundle import android.view.View import androidx.preference.PreferenceFragmentCompat -import space.taran.arkfilepicker.onArkPathPicked +import dev.arkbuilders.arkfilepicker.presentation.onArkPathPicked import space.taran.arkmemo.R import space.taran.arkmemo.files.FilePicker import space.taran.arkmemo.preferences.MemoPreferences @@ -48,4 +48,4 @@ class SettingsFragment : PreferenceFragmentCompat() { companion object{ const val TAG = "Settings" } -} \ No newline at end of file +} diff --git a/app/src/main/java/space/taran/arkmemo/ui/viewmodels/NotesViewModel.kt b/app/src/main/java/space/taran/arkmemo/ui/viewmodels/NotesViewModel.kt new file mode 100644 index 00000000..89649dd1 --- /dev/null +++ b/app/src/main/java/space/taran/arkmemo/ui/viewmodels/NotesViewModel.kt @@ -0,0 +1,76 @@ +package space.taran.arkmemo.ui.viewmodels + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext +import space.taran.arkmemo.data.repositories.TextNotesRepo +import space.taran.arkmemo.models.TextNote +import space.taran.arkmemo.preferences.MemoPreferences +import javax.inject.Inject + +@HiltViewModel +class NotesViewModel @Inject constructor(): ViewModel() { + + @Inject lateinit var textNotesRepo: TextNotesRepo + + private val iODispatcher = Dispatchers.IO + + private val notes = MutableStateFlow(listOf()) + + fun init() { + viewModelScope.launch(iODispatcher) { + textNotesRepo.init( + MemoPreferences.getInstance().getPath()!!, + viewModelScope + ) + notes.value = textNotesRepo.read() + } + } + + fun onSaveClick(note: TextNote, showProgress: (Boolean) -> Unit) { + viewModelScope.launch(iODispatcher) { + withContext(Dispatchers.Main) { + showProgress(true) + + } + textNotesRepo.save(note) + add(note) + withContext(Dispatchers.Main) { + showProgress(false) + } + } + } + + fun onDelete(note: TextNote) { + viewModelScope.launch(iODispatcher) { + remove(note) + textNotesRepo.delete(note) + } + } + + fun getTextNotes(emit: (List) -> Unit) { + viewModelScope.launch(iODispatcher) { + notes.collect { + withContext(Dispatchers.Main) { + emit(it) + } + } + } + } + + private fun add(note: TextNote) { + val notes = this.notes.value.toMutableList() + notes.add(note) + this.notes.value = notes + } + + private fun remove(note: TextNote) { + val notes = this.notes.value.toMutableList() + notes.remove(note) + this.notes.value = notes + } +} \ No newline at end of file diff --git a/app/src/main/java/space/taran/arkmemo/ui/views/PathPreference.kt b/app/src/main/java/space/taran/arkmemo/ui/views/PathPreference.kt index 8aad0d23..f4421907 100644 --- a/app/src/main/java/space/taran/arkmemo/ui/views/PathPreference.kt +++ b/app/src/main/java/space/taran/arkmemo/ui/views/PathPreference.kt @@ -26,6 +26,6 @@ class PathPreference(context: Context, attrs: AttributeSet): Preference(context, super.onBindViewHolder(holder) title = holder.findViewById(R.id.title) as TextView path = holder.findViewById(R.id.pathValue) as TextView - setPath(MemoPreferences.getInstance(context).getPath()) + setPath(MemoPreferences.getInstance(context).getPathString()) } } \ No newline at end of file diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index cb588eee..cc811d7d 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -21,5 +21,14 @@ android:layout_width="match_parent" android:layout_height="match_parent" app:layout_behavior="@string/appbar_scrolling_view_behavior"> + \ No newline at end of file diff --git a/app/src/main/res/values-night/themes.xml b/app/src/main/res/values-night/themes.xml deleted file mode 100644 index 2f82d9cf..00000000 --- a/app/src/main/res/values-night/themes.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - \ No newline at end of file diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml index 5a494a66..f2c16aea 100644 --- a/app/src/main/res/values/themes.xml +++ b/app/src/main/res/values/themes.xml @@ -1,6 +1,6 @@ -