Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add extra search options to the Find verse dialog #2019

Draft
wants to merge 8 commits into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -99,10 +99,32 @@ class SearchControl @Inject constructor(
"-"
}

fun decorateSearchString(searchString: String, searchType: SearchType, bibleSection: SearchBibleSection, currentBookName: String?): String {
val cleanSearchString = cleanSearchString(searchString)
fun decorateSearchString(searchString: String, searchType: SearchType, bibleSection: SearchBibleSection, currentBookName: String?,
includeAllEndings: Boolean=false, fuzzySearchAccuracy: Double? = null, proximityWords: Int? = null,
strongs: Char? = null): String {
var cleanSearchString = cleanSearchString(searchString)
var decorated: String

if (includeAllEndings || strongs != null || fuzzySearchAccuracy != null) {
var newSearchString =""
val wordArray: List<String> = cleanSearchString.split(" ")
wordArray.forEach {
var decoratedWord = it
if (includeAllEndings) decoratedWord += "* "
if (strongs != null) decoratedWord = "strong:$strongs$decoratedWord "
if (fuzzySearchAccuracy != null) {
val fuzzySearchAccuracyAdjusted = if (fuzzySearchAccuracy.equals(1.0)) 0.99 else fuzzySearchAccuracy
decoratedWord += "~%.2f ".format(fuzzySearchAccuracyAdjusted)
}
newSearchString += decoratedWord
}
cleanSearchString = newSearchString.trim()
}

if (proximityWords != null) {
cleanSearchString = "\"" + cleanSearchString + "\"~" + proximityWords
}

// add search type (all/any/phrase) to search string
decorated = searchType.decorate(cleanSearchString)

Expand Down
211 changes: 189 additions & 22 deletions app/src/main/java/net/bible/android/view/activity/search/Search.kt
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,16 @@ package net.bible.android.view.activity.search
import android.annotation.SuppressLint
import android.content.Intent
import android.os.Bundle
import android.text.Editable
import android.text.TextWatcher
import android.util.Log
import android.view.MenuItem
import android.view.View
import android.view.inputmethod.EditorInfo
import android.widget.LinearLayout
import android.widget.RadioButton
import android.widget.RadioGroup
import android.widget.SeekBar

import net.bible.android.activity.R
import net.bible.android.activity.databinding.SearchBinding
Expand Down Expand Up @@ -63,6 +67,31 @@ class Search : CustomTitlebarActivityBase(R.menu.search_actionbar_menu) {

/** get all, any, phrase query limitation
*/

private var fuzzySearchAccuracySelection: Int = 5
set(value) {
binding.fuzzySearch.text = getString(R.string.search_fuzzy, "(~" + (value * 10).toString() + "%)")
binding.fuzzySearchAccuracy.setProgress(value)
binding.decoratedSearchString.text = decorateSearchString(binding.searchText.text.toString())
field = value
}

// I don't know how to do this properly. I want to update the value of proximityWordNumber whenever it is changed via buttons, text or initialisation
// But part of the update is to set the text value itself. This gets into an infinite loop because the text value change fires the update again.
// So i have broken it into two parts. One updates everything and one does not update the text field.
private var proximitySearchWordsSelection: Int = 10
set(value) {
binding.proximityWordNumber.setText(value.toString())
proximitySearchWordsSelectionFinal = value
binding.decoratedSearchString.text = decorateSearchString(binding.searchText.text.toString())
field = value
}
private var proximitySearchWordsSelectionFinal: Int = 10
set(value) {
binding.proximitySearch.text = getString(R.string.search_proximity, "(${value.toString()} ${getString(R.string.words)})")
field = value
}

private val searchType: SearchType
get() {
return when (wordsRadioSelection) {
Expand All @@ -75,7 +104,6 @@ class Search : CustomTitlebarActivityBase(R.menu.search_actionbar_menu) {
}
}
}

/** get OT, NT, or all query limitation
*
* @return
Expand Down Expand Up @@ -104,11 +132,19 @@ class Search : CustomTitlebarActivityBase(R.menu.search_actionbar_menu) {
CommonUtils.settings.setLong("search-last-used", System.currentTimeMillis())
buildActivityComponent().inject(this)

// set text for current bible book on appropriate radio button
val currentBookRadioButton = findViewById<View>(R.id.searchCurrentBook) as RadioButton

// set current book to default and allow override if saved - implies returning via Back button
currentBookName = searchControl.currentBookName

if (!searchControl.validateIndex(documentToSearch)) {
Dialogs.instance.showErrorMsg(R.string.error_occurred) { finish() }
}

title = getString(R.string.search_in, documentToSearch!!.abbreviation)

// Add all listeners
binding.searchText.setOnEditorActionListener {v, actionId, event ->
return@setOnEditorActionListener when (actionId) {
EditorInfo.IME_ACTION_SEARCH -> {
Expand All @@ -117,17 +153,108 @@ class Search : CustomTitlebarActivityBase(R.menu.search_actionbar_menu) {
}
else -> false
}}
binding.searchText.addTextChangedListener(object : TextWatcher {
override fun afterTextChanged(s: Editable?) {}
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
binding.decoratedSearchString.text = decorateSearchString(binding.searchText.text.toString())
}
})
val sectionRadioGroup = findViewById<View>(R.id.bibleSectionGroup) as RadioGroup
sectionRadioGroup.setOnCheckedChangeListener { group, checkedId ->
sectionRadioSelection = checkedId
CommonUtils.settings.setInt("search_bible_section_group_prompt", checkedId)
enableSearchControls()
}
val wordsRadioGroup = findViewById<View>(R.id.wordsGroup) as RadioGroup
wordsRadioGroup.setOnCheckedChangeListener { group, checkedId ->
wordsRadioSelection = checkedId
CommonUtils.settings.setInt("search_words_group_prompt", checkedId)
enableSearchControls()
}
binding.includeAllEndings.setOnClickListener {
CommonUtils.settings.setBoolean("search_include_all_endings", binding.includeAllEndings.isChecked)
if (binding.includeAllEndings.isChecked) {
binding.proximitySearch.isChecked = false
binding.strongsSearch.isChecked = false
binding.fuzzySearch.isChecked = false
}
enableSearchControls()
}
binding.rememberSearchText.setOnClickListener {
CommonUtils.settings.setBoolean("search_remember_search_text", binding.rememberSearchText.isChecked)
}
binding.fuzzySearch.setOnClickListener {
if (binding.fuzzySearch.isChecked) {
binding.proximitySearch.isChecked = false
binding.strongsSearch.isChecked = false
binding.includeAllEndings.isChecked = false
}
enableSearchControls()
}
binding.fuzzySearchAccuracy.setOnSeekBarChangeListener(object: SeekBar.OnSeekBarChangeListener {
override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) {
fuzzySearchAccuracySelection = progress
}
override fun onStartTrackingTouch(seekBar: SeekBar?) {}
override fun onStopTrackingTouch(seekBar: SeekBar?) {}
})

binding.proximitySearch.setOnClickListener {
if (binding.proximitySearch.isChecked) {
binding.fuzzySearch.isChecked = false
binding.strongsSearch.isChecked = false
binding.includeAllEndings.isChecked = false
}
enableSearchControls()
}
binding.proximityButtonAdd.setOnClickListener {proximitySearchWordsSelection = adjustProximityWordNumber(1)}
binding.proximityButtonSubtract.setOnClickListener {proximitySearchWordsSelection = adjustProximityWordNumber(-1)}
binding.proximityWordNumber.addTextChangedListener(object : TextWatcher {
override fun afterTextChanged(s: Editable?) {}
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {proximitySearchWordsSelectionFinal = if(s.isNullOrEmpty()) 1 else s.toString().toInt()}
})

binding.strongsSearch.setOnClickListener {
if (binding.strongsSearch.isChecked) {
binding.includeAllEndings.isChecked = false
binding.fuzzySearch.isChecked = false
binding.proximitySearch.isChecked = false
}
enableSearchControls()
}
binding.greekOrHebrewGroup.setOnCheckedChangeListener { group, checkedId ->
CommonUtils.settings.setInt("search_greek_or_hebrew", checkedId)
enableSearchControls()
}
binding.decoratedTextShow.setOnCheckedChangeListener { group, checkedId ->
CommonUtils.settings.setBoolean("search_show_decorated_text", checkedId)
enableSearchControls()
}

binding.submit.setOnClickListener { onSearch() }
//searchText.setOnKeyListener(OnKeyListener { v, keyCode, event ->
// // If the event is a key-down event on the "enter" button
// if (event.action == KeyEvent.ACTION_DOWN && keyCode == KeyEvent.KEYCODE_ENTER) {
// // Perform action on key press
// onSearch(null)
// return@OnKeyListener true
// }
// false
//})

// Initialise controls
binding.includeAllEndings.isChecked = CommonUtils.settings.getBoolean("search_include_all_endings", false)
binding.rememberSearchText.isChecked = CommonUtils.settings.getBoolean("search_remember_search_text", false)

binding.proximitySearch.isChecked = CommonUtils.settings.getBoolean("search_proximity",false)
proximitySearchWordsSelection = CommonUtils.settings.getInt("search_proximity_words", 10)

binding.wordsGroup.check(CommonUtils.settings.getInt("search_words_group_prompt", 0))
wordsRadioSelection = binding.wordsGroup.checkedRadioButtonId

binding.bibleSectionGroup.check(CommonUtils.settings.getInt("search_bible_section_group_prompt", 0))
sectionRadioSelection = binding.bibleSectionGroup.checkedRadioButtonId

binding.fuzzySearch.isChecked = CommonUtils.settings.getBoolean("search_fuzzy", false)
fuzzySearchAccuracySelection = CommonUtils.settings.getInt("search_fuzzy_accuracy", 5)

binding.strongsSearch.isChecked = CommonUtils.settings.getBoolean("search_strongs",false)
binding.greekOrHebrewGroup.check(CommonUtils.settings.getInt("search_greek_or_hebrew",0))

binding.decoratedTextShow.isChecked = CommonUtils.settings.getBoolean("search_show_decorated_text",false)

// pre-load search string if passed in
val extras = intent.extras
Expand All @@ -136,31 +263,24 @@ class Search : CustomTitlebarActivityBase(R.menu.search_actionbar_menu) {
if (StringUtils.isNotEmpty(text)) {
binding.searchText.setText(text)
}
} else {
if (binding.rememberSearchText.isChecked) binding.searchText.setText(CommonUtils.settings.getString("search_text", ""))
}

val wordsRadioGroup = findViewById<View>(R.id.wordsGroup) as RadioGroup
wordsRadioGroup.setOnCheckedChangeListener { group, checkedId -> wordsRadioSelection = checkedId }
if (extras != null) {
val wordsSelection = extras.getInt(WORDS_SELECTION_SAVE, -1)
if (wordsSelection != -1) {
wordsRadioGroup.check(wordsSelection)
}
}

val sectionRadioGroup = findViewById<View>(R.id.bibleSectionGroup) as RadioGroup
sectionRadioGroup.setOnCheckedChangeListener { group, checkedId -> sectionRadioSelection = checkedId }
if (extras != null) {
val sectionSelection = extras.getInt(SECTION_SELECTION_SAVE, -1)
if (sectionSelection != -1) {
sectionRadioGroup.check(sectionSelection)
}
}

// set text for current bible book on appropriate radio button
val currentBookRadioButton = findViewById<View>(R.id.searchCurrentBook) as RadioButton

// set current book to default and allow override if saved - implies returning via Back button
currentBookName = searchControl.currentBookName
if (extras != null) {
val currentBibleBookSaved = extras.getString(CURRENT_BIBLE_BOOK_SAVE)
if (currentBibleBookSaved != null) {
Expand All @@ -169,9 +289,46 @@ class Search : CustomTitlebarActivityBase(R.menu.search_actionbar_menu) {
}
currentBookRadioButton.text = currentBookName

binding.textClear.setOnClickListener({binding.searchText.setText("")})
enableSearchControls()

Log.i(TAG, "Finished displaying Search view")
}

private fun adjustProximityWordNumber(changeAmt: Int):Int {
var newProximityWordNumber = binding.proximityWordNumber.text.toString().toInt() + changeAmt
if (newProximityWordNumber < 1) newProximityWordNumber = 1
return newProximityWordNumber
}

fun enableSearchControls() {

// Set control visibility
binding.fuzzySearchDetailsLayout.visibility = if (binding.fuzzySearch.isChecked) View.VISIBLE else View.GONE
binding.proximityDetailsLayout.visibility = if (binding.proximitySearch.isChecked) View.VISIBLE else View.GONE
binding.strongsDetailLayout.visibility = if (binding.strongsSearch.isChecked) View.VISIBLE else View.GONE
binding.decoratedSearchDetailLayout.visibility = if (binding.decoratedTextShow.isChecked) View.VISIBLE else View.GONE

enableLayout(binding.fuzzySearchLayout, binding.allWords.id == wordsRadioSelection)
enableLayout(binding.fuzzySearchDetailsLayout, binding.fuzzySearch.isChecked && binding.fuzzySearch.isEnabled)

enableLayout(binding.strongsDetailLayout, (binding.strongsSearch.isChecked && binding.strongsSearch.isEnabled))

binding.searchOldTestament.isEnabled = !binding.strongsSearch.isChecked || (binding.strongsSearch.isEnabled && binding.strongsSearch.isChecked && binding.searchHebrew.isChecked)
binding.searchNewTestament.isEnabled = !binding.strongsSearch.isChecked || (binding.strongsSearch.isEnabled && binding.strongsSearch.isChecked && binding.searchGreek.isChecked)

binding.decoratedSearchString.text = decorateSearchString(binding.searchText.text.toString())
}

fun enableLayout(layout: LinearLayout, isEnabled: Boolean) {
for (i in 0 until layout.childCount) {
val view: View = layout.getChildAt(i)
if (view is LinearLayout) {enableLayout(view,isEnabled)}
view.isEnabled = isEnabled
}
layout.isEnabled = isEnabled

}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
when(item.itemId) {
R.id.rebuildIndex -> {
Expand All @@ -191,7 +348,6 @@ class Search : CustomTitlebarActivityBase(R.menu.search_actionbar_menu) {
binding.searchText.requestFocus()
}


fun onRebuildIndex(v: View?) {
startActivity(Intent(this, SearchIndex::class.java))
finish()
Expand All @@ -204,6 +360,13 @@ class Search : CustomTitlebarActivityBase(R.menu.search_actionbar_menu) {
var text = binding.searchText.text.toString()
if (!StringUtils.isEmpty(text)) {

CommonUtils.settings.setString("search_text", text)
CommonUtils.settings.setBoolean("search_fuzzy", binding.fuzzySearch.isChecked)
CommonUtils.settings.setInt("search_fuzzy_accuracy", fuzzySearchAccuracySelection)
CommonUtils.settings.setBoolean("search_proximity", binding.proximitySearch.isChecked)
CommonUtils.settings.setInt("search_proximity_words", proximitySearchWordsSelectionFinal)
CommonUtils.settings.setBoolean("search_strongs", binding.strongsSearch.isChecked)

// update current intent so search is restored if we return here via history/back
// the current intent is saved by HistoryManager
intent.putExtra(SEARCH_TEXT_SAVE, text)
Expand All @@ -215,7 +378,7 @@ class Search : CustomTitlebarActivityBase(R.menu.search_actionbar_menu) {
Log.i(TAG, "Search text:$text")

// specify search string and doc in new Intent;
// if doc is not specifed a, possibly invalid, doc may be used when returning to search via history list e.g. search bible, select dict, history list, search results
// if doc is not specifed a possibly invalid doc may be used when returning to search via history list e.g. search bible, select dict, history list, search results
val intent = Intent(this, SearchResults::class.java)
intent.putExtra(SearchControl.SEARCH_TEXT, text)
val currentDocInitials = documentToSearch?.initials
Expand All @@ -229,7 +392,11 @@ class Search : CustomTitlebarActivityBase(R.menu.search_actionbar_menu) {
}

private fun decorateSearchString(searchString: String): String {
return searchControl.decorateSearchString(searchString, searchType, bibleSection, currentBookName)
val fuzzyAccuracy = if (binding.fuzzySearch.isChecked && binding.fuzzySearch.isEnabled) fuzzySearchAccuracySelection.toDouble()/10 else null
val proximityWords = if (binding.proximitySearch.isChecked && binding.proximitySearch.isEnabled) proximitySearchWordsSelectionFinal else null
val strongs = if (binding.strongsSearch.isChecked && binding.strongsSearch.isEnabled) {if (binding.searchGreek.isChecked) 'G' else 'H'} else null

return searchControl.decorateSearchString(searchString, searchType, bibleSection, currentBookName, binding.includeAllEndings.isChecked, fuzzyAccuracy, proximityWords, strongs)
}

companion object {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ class KeyHistoryItem(
val desc = StringBuilder()
try {
val verseDesc = CommonUtils.getKeyDescription(key)
desc.append(verseDesc).append(" ").append(document.abbreviation)
desc.append(verseDesc).append(" ").append(document.abbreviation)
} catch (e: Exception) {
Log.e(TAG, "Error getting description", e)
}
Expand Down
Loading