mirror of
				https://github.com/SimpleMobileTools/Simple-Notes.git
				synced 2025-06-05 17:00:23 +02:00 
			
		
		
		
	| @@ -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,6 +915,7 @@ class MainActivity : SimpleActivity() { | ||||
|     } | ||||
|  | ||||
|     fun currentNoteTextChanged(newText: String, showUndo: Boolean, showRedo: Boolean) { | ||||
|         if (searchIsActive.not()) { | ||||
|             var shouldRecreateMenu = false | ||||
|             if (showUndo != showUndoButton) { | ||||
|                 showUndoButton = showUndo | ||||
| @@ -752,6 +938,7 @@ class MainActivity : SimpleActivity() { | ||||
|                 invalidateOptionsMenu() | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private fun checkWhatsNewDialog() { | ||||
|         arrayListOf<Release>().apply { | ||||
|   | ||||
| @@ -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,6 +1,12 @@ | ||||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" | ||||
|     android:layout_width="match_parent" | ||||
|     android:layout_height="match_parent" | ||||
|     android:orientation="vertical"> | ||||
|  | ||||
|     <include layout="@layout/search_item" /> | ||||
|  | ||||
|     <com.simplemobiletools.commons.views.MyViewPager | ||||
|     xmlns:android="http://schemas.android.com/apk/res/android" | ||||
|         android:id="@+id/view_pager" | ||||
|         android:layout_width="match_parent" | ||||
|         android:layout_height="match_parent"> | ||||
| @@ -14,3 +20,4 @@ | ||||
|             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" | ||||
|   | ||||
		Reference in New Issue
	
	Block a user