mirror of
https://github.com/SimpleMobileTools/Simple-Notes.git
synced 2025-03-23 03:50:07 +01:00
commit
8bdac0e761
@ -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'
|
||||
}
|
||||
|
@ -3,13 +3,20 @@ package com.simplemobiletools.notes.pro.activities
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.text.Editable
|
||||
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.view.inputmethod.EditorInfo
|
||||
import android.widget.TextView
|
||||
import androidx.core.graphics.ColorUtils
|
||||
import com.simplemobiletools.commons.dialogs.ConfirmationAdvancedDialog
|
||||
import com.simplemobiletools.commons.dialogs.FilePickerDialog
|
||||
import com.simplemobiletools.commons.dialogs.RadioGroupDialog
|
||||
@ -26,9 +33,14 @@ 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.fragments.TextFragment
|
||||
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 +59,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 +80,164 @@ class MainActivity : SimpleActivity() {
|
||||
}
|
||||
|
||||
wasInit = true
|
||||
|
||||
checkAppOnSDCard()
|
||||
searchListeners()
|
||||
}
|
||||
|
||||
private fun searchListeners() {
|
||||
search_query.onTextChangeListener { query ->
|
||||
currentNotesView()?.let { noteView ->
|
||||
currentTextFragment?.removeTextWatcher()
|
||||
searchClearSpans(noteView.text)
|
||||
|
||||
if (query.isNotBlank() && query.length > 1) {
|
||||
searchMatches = searchMatches(query, noteView.value)
|
||||
searchHighLightText(noteView, query)
|
||||
}
|
||||
|
||||
currentTextFragment?.setTextWatcher()
|
||||
|
||||
if (searchMatches.isNotEmpty()) {
|
||||
noteView.requestFocus()
|
||||
noteView.setSelection(searchMatches.getOrNull(searchIndex) ?: 0)
|
||||
}
|
||||
|
||||
search_query.postDelayed({
|
||||
search_query.requestFocus()
|
||||
}, 50)
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
searchHide()
|
||||
}
|
||||
|
||||
view_pager.onPageChangeListener {
|
||||
currentTextFragment?.removeTextWatcher()
|
||||
currentNotesView()?.let { noteView ->
|
||||
searchClearSpans(noteView.text)
|
||||
}
|
||||
|
||||
searchHide()
|
||||
currentTextFragment?.setTextWatcher()
|
||||
}
|
||||
|
||||
search_query.setOnEditorActionListener(TextView.OnEditorActionListener { _, actionId, _ ->
|
||||
if (actionId == EditorInfo.IME_ACTION_SEARCH) {
|
||||
search_next.performClick()
|
||||
return@OnEditorActionListener true
|
||||
}
|
||||
|
||||
false
|
||||
})
|
||||
}
|
||||
|
||||
private val currentTextFragment: TextFragment? get() = mAdapter?.textFragment(view_pager.currentItem)
|
||||
|
||||
private fun searchClearSpans(editable: Editable) {
|
||||
val spans = editable.getSpans(0, editable.length, Any::class.java)
|
||||
for (span in spans) {
|
||||
if (span is BackgroundColorSpan) {
|
||||
editable.removeSpan(span)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun selectMatch(noteView: MyEditText) {
|
||||
if (searchMatches.isNotEmpty()) {
|
||||
noteView.requestFocus()
|
||||
noteView.setSelection(searchMatches.getOrNull(searchIndex) ?: 0)
|
||||
} else
|
||||
hideKeyboard()
|
||||
}
|
||||
|
||||
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, highlightText: String) {
|
||||
val content = view.text.toString()
|
||||
var indexOf = content.indexOf(highlightText, 0, true)
|
||||
val wordToSpan = SpannableString(view.text)
|
||||
|
||||
var offset = 0
|
||||
while (offset < content.length && indexOf != -1) {
|
||||
indexOf = content.indexOf(highlightText, offset, true)
|
||||
|
||||
if (indexOf == -1) {
|
||||
break
|
||||
} else {
|
||||
val spanBgColor = BackgroundColorSpan(ColorUtils.setAlphaComponent(config.primaryColor, 128))
|
||||
val spanFlag = Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
|
||||
wordToSpan.setSpan(spanBgColor, indexOf, indexOf + highlightText.length, spanFlag)
|
||||
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() {
|
||||
search_query.text?.clear()
|
||||
searchIsActive = false
|
||||
search_root.beGone()
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
@ -82,6 +254,12 @@ class MainActivity : SimpleActivity() {
|
||||
setTextColor(config.textColor)
|
||||
}
|
||||
updateTextColors(view_pager)
|
||||
|
||||
val contrastColor = config.primaryColor.getContrastColor()
|
||||
search_root.setBackgroundColor(config.primaryColor)
|
||||
search_previous.applyColorFilter(contrastColor)
|
||||
search_next.applyColorFilter(contrastColor)
|
||||
search_clear.applyColorFilter(contrastColor)
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
@ -114,6 +292,7 @@ class MainActivity : SimpleActivity() {
|
||||
findItem(R.id.open_note).isVisible = shouldBeVisible
|
||||
findItem(R.id.delete_note).isVisible = shouldBeVisible
|
||||
findItem(R.id.export_all_notes).isVisible = shouldBeVisible
|
||||
findItem(R.id.open_search).isVisible = currentItemIsCheckList.not()
|
||||
|
||||
saveNoteButton = findItem(R.id.save_note)
|
||||
saveNoteButton!!.isVisible = !config.autosaveNotes && showSaveButton && mCurrentNote.type == NoteType.TYPE_TEXT.value
|
||||
@ -129,6 +308,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()
|
||||
@ -176,6 +356,8 @@ class MainActivity : SimpleActivity() {
|
||||
}
|
||||
super.onBackPressed()
|
||||
}
|
||||
} else if (searchIsActive) {
|
||||
searchHide()
|
||||
} else {
|
||||
super.onBackPressed()
|
||||
}
|
||||
@ -188,6 +370,8 @@ class MainActivity : SimpleActivity() {
|
||||
checkIntents(intent)
|
||||
}
|
||||
|
||||
private val currentItemIsCheckList get() = mAdapter?.isChecklistFragment(view_pager.currentItem) ?: false
|
||||
|
||||
private fun checkIntents(intent: Intent) {
|
||||
intent.apply {
|
||||
if (action == Intent.ACTION_SEND && type == MIME_TEXT_PLAIN) {
|
||||
@ -627,7 +811,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 +915,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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -45,6 +45,10 @@ class NotesPagerAdapter(fm: FragmentManager, val notes: List<Note>, val activity
|
||||
}
|
||||
}
|
||||
|
||||
fun isChecklistFragment(position: Int): Boolean = (fragments[position] is ChecklistFragment)
|
||||
|
||||
fun textFragment(position: Int): TextFragment? = (fragments[position] as? TextFragment)
|
||||
|
||||
fun getCurrentNotesView(position: Int) = (fragments[position] as? TextFragment)?.getNotesView()
|
||||
|
||||
fun getCurrentNoteViewText(position: Int) = (fragments[position] as? TextFragment)?.getCurrentNoteViewText()
|
||||
|
@ -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)
|
||||
|
@ -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!!
|
||||
|
||||
|
@ -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,12 @@ 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)
|
||||
}
|
||||
|
||||
|
@ -77,7 +77,8 @@ class TextFragment : NoteFragment() {
|
||||
if (config!!.autosaveNotes) {
|
||||
saveText(false)
|
||||
}
|
||||
view.text_note_view.removeTextChangedListener(textWatcher)
|
||||
|
||||
removeTextWatcher()
|
||||
}
|
||||
|
||||
override fun setMenuVisibility(menuVisible: Boolean) {
|
||||
@ -154,9 +155,12 @@ class TextFragment : NoteFragment() {
|
||||
view.notes_counter.beGone()
|
||||
}
|
||||
|
||||
view.text_note_view.addTextChangedListener(textWatcher)
|
||||
setTextWatcher()
|
||||
}
|
||||
|
||||
fun setTextWatcher() = view.text_note_view.addTextChangedListener(textWatcher)
|
||||
fun removeTextWatcher() = view.text_note_view.removeTextChangedListener(textWatcher)
|
||||
|
||||
fun updateNoteValue(value: String) {
|
||||
note?.value = value
|
||||
}
|
||||
|
@ -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>
|
||||
|
69
app/src/main/res/layout/search_item.xml
Normal file
69
app/src/main/res/layout/search_item.xml
Normal file
@ -0,0 +1,69 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<FrameLayout 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="vertical"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal"
|
||||
android:paddingTop="@dimen/medium_margin"
|
||||
android:paddingBottom="@dimen/medium_margin">
|
||||
|
||||
<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:background="@null"
|
||||
android:hint="@string/search"
|
||||
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
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:layout_gravity="bottom"
|
||||
android:alpha="0.1"
|
||||
android:background="@color/md_grey_white" />
|
||||
</FrameLayout>
|
@ -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"
|
||||
|
Loading…
x
Reference in New Issue
Block a user