From 2c3a6937a7b31386f789db1ac082c4381fbfb3dc Mon Sep 17 00:00:00 2001 From: tibbi Date: Tue, 17 Jul 2018 21:09:53 +0200 Subject: [PATCH] adding the undo/redo functionality --- .../notes/activities/MainActivity.kt | 14 +++- .../notes/adapters/NotesPagerAdapter.kt | 4 ++ .../notes/fragments/NoteFragment.kt | 72 ++++++++++++++++++- .../notes/models/TextHistory.kt | 35 +++++++++ .../notes/models/TextHistoryItem.kt | 3 + 5 files changed, 123 insertions(+), 5 deletions(-) create mode 100644 app/src/main/kotlin/com/simplemobiletools/notes/models/TextHistory.kt create mode 100644 app/src/main/kotlin/com/simplemobiletools/notes/models/TextHistoryItem.kt diff --git a/app/src/main/kotlin/com/simplemobiletools/notes/activities/MainActivity.kt b/app/src/main/kotlin/com/simplemobiletools/notes/activities/MainActivity.kt index 540aadb6..f59dfef4 100644 --- a/app/src/main/kotlin/com/simplemobiletools/notes/activities/MainActivity.kt +++ b/app/src/main/kotlin/com/simplemobiletools/notes/activities/MainActivity.kt @@ -132,6 +132,8 @@ class MainActivity : SimpleActivity(), ViewPager.OnPageChangeListener { when (item.itemId) { R.id.open_note -> displayOpenNoteDialog() R.id.save_note -> saveNote() + R.id.undo -> undo() + R.id.redo -> redo() R.id.new_note -> displayNewNoteDialog() R.id.rename_note -> displayRenameDialog() R.id.share -> shareText() @@ -287,11 +289,11 @@ class MainActivity : SimpleActivity(), ViewPager.OnPageChangeListener { private fun openFile() { FilePickerDialog(this) { - openFile(it, true, { + openFile(it, true) { OpenFileDialog(this, it.path) { addNewNote(it) } - }) + } } } @@ -524,6 +526,14 @@ class MainActivity : SimpleActivity(), ViewPager.OnPageChangeListener { invalidateOptionsMenu() } + private fun undo() { + mAdapter?.undo(view_pager.currentItem) + } + + private fun redo() { + mAdapter?.redo(view_pager.currentItem) + } + private fun getNoteIndexWithId(id: Int): Int { for (i in 0 until mNotes.count()) { if (mNotes[i].id == id) { diff --git a/app/src/main/kotlin/com/simplemobiletools/notes/adapters/NotesPagerAdapter.kt b/app/src/main/kotlin/com/simplemobiletools/notes/adapters/NotesPagerAdapter.kt index 928fd865..aca6e523 100644 --- a/app/src/main/kotlin/com/simplemobiletools/notes/adapters/NotesPagerAdapter.kt +++ b/app/src/main/kotlin/com/simplemobiletools/notes/adapters/NotesPagerAdapter.kt @@ -42,6 +42,10 @@ class NotesPagerAdapter(fm: FragmentManager, val notes: List, val activity fun focusEditText(position: Int) = fragments[position]?.focusEditText() + fun undo(position: Int) = fragments[position]?.undo() + + fun redo(position: Int) = fragments[position]?.redo() + override fun finishUpdate(container: ViewGroup) { try { super.finishUpdate(container) diff --git a/app/src/main/kotlin/com/simplemobiletools/notes/fragments/NoteFragment.kt b/app/src/main/kotlin/com/simplemobiletools/notes/fragments/NoteFragment.kt index 5241b892..08ec932d 100644 --- a/app/src/main/kotlin/com/simplemobiletools/notes/fragments/NoteFragment.kt +++ b/app/src/main/kotlin/com/simplemobiletools/notes/fragments/NoteFragment.kt @@ -4,7 +4,9 @@ import android.graphics.Typeface import android.os.Bundle import android.support.v4.app.Fragment import android.text.Editable +import android.text.Selection import android.text.TextWatcher +import android.text.style.UnderlineSpan import android.text.util.Linkify import android.util.TypedValue import android.view.Gravity @@ -22,12 +24,18 @@ import com.simplemobiletools.notes.extensions.getTextSize import com.simplemobiletools.notes.extensions.updateWidget import com.simplemobiletools.notes.helpers.* import com.simplemobiletools.notes.models.Note +import com.simplemobiletools.notes.models.TextHistory +import com.simplemobiletools.notes.models.TextHistoryItem import kotlinx.android.synthetic.main.fragment_note.* import kotlinx.android.synthetic.main.fragment_note.view.* import kotlinx.android.synthetic.main.note_view_horiz_scrollable.view.* import java.io.File +// text history handling taken from https://gist.github.com/zeleven/0cfa738c1e8b65b23ff7df1fc30c9f7e class NoteFragment : Fragment() { + private var textHistory = TextHistory() + + private var isUndoOrRedo = false private var noteId = 0 lateinit var note: Note lateinit var view: ViewGroup @@ -161,11 +169,69 @@ class NoteFragment : Fragment() { notes_counter.text = words.count { it.isNotEmpty() }.toString() } - private var textWatcher: TextWatcher = object : TextWatcher { - override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) { + fun undo() { + val edit = textHistory.getPrevious() ?: return + + val text = view.notes_view.editableText + val start = edit.start + val end = start + if (edit.after != null) edit.after.length else 0 + + isUndoOrRedo = true + text.replace(start, end, edit.before) + isUndoOrRedo = false + + for (span in text.getSpans(0, text.length, UnderlineSpan::class.java)) { + text.removeSpan(span) } - override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) { + Selection.setSelection(text, if (edit.before == null) { + start + } else { + start + edit.before.length + }) + } + + fun redo() { + val edit = textHistory.getNext() ?: return + + val text = view.notes_view.editableText + val start = edit.start + val end = start + if (edit.before != null) edit.before.length else 0 + + isUndoOrRedo = true + text.replace(start, end, edit.after) + isUndoOrRedo = false + + for (o in text.getSpans(0, text.length, UnderlineSpan::class.java)) { + text.removeSpan(o) + } + + Selection.setSelection(text, if (edit.after == null) { + start + } else { + start + edit.after.length + }) + } + + fun isUndoAvailable() = textHistory.position > 0 + + fun isRedoAvailable() = textHistory.position < textHistory.history.size + + private var textWatcher: TextWatcher = object : TextWatcher { + private var beforeChange: CharSequence? = null + private var afterChange: CharSequence? = null + + override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) { + if (!isUndoOrRedo) { + beforeChange = s.subSequence(start, start + count) + } + } + + override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) { + if (!isUndoOrRedo) { + afterChange = s.subSequence(start, start + count) + textHistory.add(TextHistoryItem(start, beforeChange!!, afterChange!!)) + } } override fun afterTextChanged(editable: Editable) { diff --git a/app/src/main/kotlin/com/simplemobiletools/notes/models/TextHistory.kt b/app/src/main/kotlin/com/simplemobiletools/notes/models/TextHistory.kt new file mode 100644 index 00000000..e3d76165 --- /dev/null +++ b/app/src/main/kotlin/com/simplemobiletools/notes/models/TextHistory.kt @@ -0,0 +1,35 @@ +package com.simplemobiletools.notes.models + +import java.util.* + +class TextHistory { + var position = 0 + val history = LinkedList() + + fun getPrevious(): TextHistoryItem? { + if (position == 0) { + return null + } + position-- + return history[position] + } + + fun getNext(): TextHistoryItem? { + if (position >= history.size) { + return null + } + + val item = history[position] + position++ + return item + } + + fun add(item: TextHistoryItem) { + while (history.size > position) { + history.removeLast() + } + + history.add(item) + position++ + } +} diff --git a/app/src/main/kotlin/com/simplemobiletools/notes/models/TextHistoryItem.kt b/app/src/main/kotlin/com/simplemobiletools/notes/models/TextHistoryItem.kt new file mode 100644 index 00000000..e96c3cb1 --- /dev/null +++ b/app/src/main/kotlin/com/simplemobiletools/notes/models/TextHistoryItem.kt @@ -0,0 +1,3 @@ +package com.simplemobiletools.notes.models + +data class TextHistoryItem(val start: Int, val before: CharSequence?, val after: CharSequence?)