WIP - searching for strings in a given note, then highlighting and looping through the string occurrences with some arrows.

This commit is contained in:
Pavol Franek 2020-03-22 16:36:50 +01:00
parent 12604ac41b
commit 716953ec9d
8 changed files with 248 additions and 35 deletions

View File

@ -57,10 +57,10 @@ android {
}
dependencies {
implementation 'com.simplemobiletools:commons:5.22.19'
implementation 'androidx.constraintlayout:constraintlayout:2.0.0-beta2'
implementation 'com.simplemobiletools:commons:5.23.7'
implementation 'androidx.constraintlayout:constraintlayout:2.0.0-beta4'
kapt 'androidx.room:room-compiler:2.2.2'
implementation 'androidx.room:room-runtime:2.2.2'
annotationProcessor 'androidx.room:room-compiler:2.2.2'
kapt 'androidx.room:room-compiler:2.2.4'
implementation 'androidx.room:room-runtime:2.2.4'
annotationProcessor 'androidx.room:room-compiler:2.2.4'
}

View File

@ -3,13 +3,17 @@ package com.simplemobiletools.notes.pro.activities
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import android.text.Spannable
import android.text.SpannableString
import android.text.method.ArrowKeyMovementMethod
import android.text.method.LinkMovementMethod
import android.text.style.BackgroundColorSpan
import android.util.TypedValue
import android.view.ActionMode
import android.view.Gravity
import android.view.Menu
import android.view.MenuItem
import android.widget.TextView
import com.simplemobiletools.commons.dialogs.ConfirmationAdvancedDialog
import com.simplemobiletools.commons.dialogs.FilePickerDialog
import com.simplemobiletools.commons.dialogs.RadioGroupDialog
@ -26,9 +30,13 @@ import com.simplemobiletools.notes.pro.adapters.NotesPagerAdapter
import com.simplemobiletools.notes.pro.databases.NotesDatabase
import com.simplemobiletools.notes.pro.dialogs.*
import com.simplemobiletools.notes.pro.extensions.*
import com.simplemobiletools.notes.pro.helpers.*
import com.simplemobiletools.notes.pro.helpers.MIME_TEXT_PLAIN
import com.simplemobiletools.notes.pro.helpers.NoteType
import com.simplemobiletools.notes.pro.helpers.NotesHelper
import com.simplemobiletools.notes.pro.helpers.OPEN_NOTE_ID
import com.simplemobiletools.notes.pro.models.Note
import kotlinx.android.synthetic.main.activity_main.*
import kotlinx.android.synthetic.main.search_item.*
import java.io.File
import java.nio.charset.Charset
@ -47,6 +55,9 @@ class MainActivity : SimpleActivity() {
private var showSaveButton = false
private var showUndoButton = false
private var showRedoButton = false
private var searchIndex = 0
private var searchMatches = emptyList<Int>()
private var searchIsActive = false
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@ -65,7 +76,127 @@ class MainActivity : SimpleActivity() {
}
wasInit = true
checkAppOnSDCard()
searchListeners()
}
private fun searchListeners() {
search_query.onTextChangeListener { query ->
currentNotesView()?.let { noteView ->
noteView.setText(noteView.value)
if (query.isNotBlank() && query.length > 1) {
searchMatches = searchMatches(query, noteView.value)
searchHighLightText(noteView, query)
}
}
}
search_previous.setOnClickListener {
currentNotesView()?.let { noteView ->
if (searchIndex > 0) {
searchIndex--
}
else {
searchIndex = searchMatches.lastIndex
}
selectMatch(noteView)
}
}
search_next.setOnClickListener {
currentNotesView()?.let { noteView ->
if (searchIndex < searchMatches.lastIndex) {
searchIndex++
}
else {
searchIndex = 0
}
selectMatch(noteView)
}
}
search_clear.setOnClickListener {
if (search_query.value.isNotBlank())
search_query.text?.clear()
else
searchHide()
}
view_pager.onPageChangeListener {
if (searchIsActive) searchHide()
}
}
private fun selectMatch(noteView: MyEditText) {
if (searchMatches.isNotEmpty()) {
noteView.requestFocus()
noteView.setSelection(searchMatches.getOrNull(searchIndex) ?: 0)
} else {
hideKeyboard()
//toast("No matches")//TODO
}
}
private fun searchMatches(textToHighlight: String, content: String): ArrayList<Int> {
val indexes = arrayListOf<Int>()
var indexOf = content.indexOf(textToHighlight, 0, ignoreCase = true)
var offset = 0
while (offset < content.length && indexOf != -1) {
indexOf = content.indexOf(textToHighlight, offset, ignoreCase = true)
if (indexOf == -1)
break
else
indexes.add(indexOf)
offset = indexOf + 1
}
return indexes
}
private fun searchHighLightText(view: MyEditText, textToHighlight: String) {
val content = view.text.toString()
var indexOf = content.indexOf(textToHighlight, 0, true)
val wordToSpan = SpannableString(view.text)
var offset = 0
while (offset < content.length && indexOf != -1) {
indexOf = content.indexOf(textToHighlight, offset, true)
if (indexOf == -1) break
else {
wordToSpan.setSpan(BackgroundColorSpan(color(R.color.color_accent)), indexOf, indexOf + textToHighlight.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
view.setText(wordToSpan, TextView.BufferType.SPANNABLE)
}
offset = indexOf + 1
}
}
private fun searchShow() {
searchIsActive = true
search_root.beVisible()
showKeyboard(search_query)
currentNotesView()?.let { noteView ->
noteView.requestFocus()
noteView.setSelection(0)
}
search_query.postDelayed({
search_query.requestFocus()
}, 250)
}
private fun searchHide() {
searchIsActive = false
search_root.beGone()
}
override fun onResume() {
@ -129,6 +260,7 @@ class MainActivity : SimpleActivity() {
}
when (item.itemId) {
R.id.open_search -> searchShow()
R.id.open_note -> displayOpenNoteDialog()
R.id.save_note -> saveNote()
R.id.undo -> undo()
@ -627,7 +759,8 @@ class MainActivity : SimpleActivity() {
private fun saveCurrentNote(force: Boolean) {
getPagerAdapter().saveCurrentNote(view_pager.currentItem, force)
if (mCurrentNote.type == NoteType.TYPE_CHECKLIST.value) {
mCurrentNote.value = getPagerAdapter().getNoteChecklistItems(view_pager.currentItem) ?: ""
mCurrentNote.value = getPagerAdapter().getNoteChecklistItems(view_pager.currentItem)
?: ""
}
}
@ -730,26 +863,28 @@ class MainActivity : SimpleActivity() {
}
fun currentNoteTextChanged(newText: String, showUndo: Boolean, showRedo: Boolean) {
var shouldRecreateMenu = false
if (showUndo != showUndoButton) {
showUndoButton = showUndo
shouldRecreateMenu = true
}
if (showRedo != showRedoButton) {
showRedoButton = showRedo
shouldRecreateMenu = true
}
if (!config.autosaveNotes) {
showSaveButton = newText != mCurrentNote.value
if (showSaveButton != saveNoteButton?.isVisible) {
if (searchIsActive.not()) {
var shouldRecreateMenu = false
if (showUndo != showUndoButton) {
showUndoButton = showUndo
shouldRecreateMenu = true
}
}
if (shouldRecreateMenu) {
invalidateOptionsMenu()
if (showRedo != showRedoButton) {
showRedoButton = showRedo
shouldRecreateMenu = true
}
if (!config.autosaveNotes) {
showSaveButton = newText != mCurrentNote.value
if (showSaveButton != saveNoteButton?.isVisible) {
shouldRecreateMenu = true
}
}
if (shouldRecreateMenu) {
invalidateOptionsMenu()
}
}
}

View File

@ -4,6 +4,7 @@ import android.appwidget.AppWidgetManager
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import androidx.core.content.ContextCompat
import com.simplemobiletools.notes.pro.databases.NotesDatabase
import com.simplemobiletools.notes.pro.helpers.Config
import com.simplemobiletools.notes.pro.helpers.MyWidgetProvider
@ -26,3 +27,5 @@ fun Context.updateWidgets() {
}
}
}
fun Context.color(id: Int) = ContextCompat.getColor(this, id)

View File

@ -7,3 +7,4 @@ import com.simplemobiletools.notes.pro.helpers.Config
val Fragment.config: Config? get() = if (context != null) Config.newInstance(context!!) else null
val Fragment.requiredActivity: FragmentActivity get() = this.activity!!

View File

@ -1,7 +1,12 @@
package com.simplemobiletools.notes.pro.extensions
import android.os.Build
import android.text.Html
import androidx.annotation.RequiresApi
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
import com.simplemobiletools.commons.helpers.isMarshmallowPlus
import com.simplemobiletools.commons.helpers.isNougatPlus
import com.simplemobiletools.notes.pro.models.ChecklistItem
fun String.parseChecklistItems(): ArrayList<ChecklistItem>? {
@ -14,3 +19,7 @@ fun String.parseChecklistItems(): ArrayList<ChecklistItem>? {
}
return null
}
@RequiresApi(Build.VERSION_CODES.N)
fun String.toHtml() = if (isNougatPlus()) Html.fromHtml(this, Html.FROM_HTML_MODE_LEGACY) else Html.fromHtml(this)

View File

@ -1,16 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<com.simplemobiletools.commons.views.MyViewPager
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/view_pager"
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
android:layout_height="match_parent"
android:orientation="vertical">
<androidx.viewpager.widget.PagerTitleStrip
android:id="@+id/pager_title_strip"
<include layout="@layout/search_item" />
<com.simplemobiletools.commons.views.MyViewPager
android:id="@+id/view_pager"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="top"
android:paddingLeft="@dimen/activity_margin"
android:paddingRight="@dimen/activity_margin"/>
android:layout_height="match_parent">
</com.simplemobiletools.commons.views.MyViewPager>
<androidx.viewpager.widget.PagerTitleStrip
android:id="@+id/pager_title_strip"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="top"
android:paddingLeft="@dimen/activity_margin"
android:paddingRight="@dimen/activity_margin" />
</com.simplemobiletools.commons.views.MyViewPager>
</LinearLayout>

View File

@ -0,0 +1,53 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/search_root"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/color_primary"
android:gravity="center_vertical"
android:orientation="horizontal"
android:paddingTop="@dimen/small_margin"
android:paddingBottom="@dimen/small_margin"
android:visibility="gone"
tools:visibility="visible">
<com.simplemobiletools.commons.views.MyEditText
android:id="@+id/search_query"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/normal_margin"
android:layout_marginEnd="@dimen/normal_margin"
android:layout_weight="1"
android:maxLength="50"
android:maxLines="1"
android:singleLine="true"
android:textCursorDrawable="@null" />
<ImageView
android:id="@+id/search_previous"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/smaller_margin"
android:background="?selectableItemBackgroundBorderless"
android:padding="@dimen/small_margin"
android:src="@drawable/ic_chevron_left_vector" />
<ImageView
android:id="@+id/search_next"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/smaller_margin"
android:background="?selectableItemBackgroundBorderless"
android:padding="@dimen/small_margin"
android:src="@drawable/ic_chevron_right_vector" />
<ImageView
android:id="@+id/search_clear"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/medium_margin"
android:background="?selectableItemBackgroundBorderless"
android:padding="@dimen/small_margin"
android:src="@drawable/ic_cross_vector" />
</LinearLayout>

View File

@ -16,6 +16,11 @@
android:icon="@drawable/ic_redo_vector"
android:title="@string/redo"
app:showAsAction="ifRoom"/>
<item
android:id="@+id/open_search"
android:icon="@drawable/ic_search_vector"
android:title="@string/search"
app:showAsAction="ifRoom"/>
<item
android:id="@+id/open_note"
android:icon="@drawable/ic_folder_open_vector"