Skip to content
Open
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 @@ -400,6 +400,8 @@ class MainActivity : AppCompatActivity() {
inputManager.showSoftInput(binding.EnterSearchKeyword, InputMethodManager.SHOW_IMPLICIT)
} else {
binding.EnterSearchKeyword.visibility = View.GONE
binding.EnterSearchKeyword.setText(String())
model.keyword = String()
inputManager.hideSoftInputFromWindow(binding.EnterSearchKeyword.windowToken, 0)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ class MakeList : NotallyActivity(Type.LIST) {
override fun checkedChanged(position: Int, checked: Boolean) {
model.items[position].checked = checked
}
})
}, searchKeyword)

binding.RecyclerView.adapter = adapter
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import android.os.Bundle
import android.provider.Settings
import android.text.Editable
import android.text.Spannable
import android.text.SpannableString
import android.text.style.BackgroundColorSpan
import android.util.TypedValue
import android.view.KeyEvent
Expand Down Expand Up @@ -69,6 +70,7 @@ abstract class NotallyActivity(private val type: Type) : AppCompatActivity() {

internal lateinit var binding: ActivityNotallyBinding
internal val model: NotallyModel by viewModels()
internal var searchKeyword: String = String()

override fun finish() {
lifecycleScope.launch {
Expand Down Expand Up @@ -96,6 +98,7 @@ abstract class NotallyActivity(private val type: Type) : AppCompatActivity() {

lifecycleScope.launch {
if (model.isFirstInstance) {
searchKeyword = intent.getStringExtra(Constants.SearchKeyword) ?: String()
val persistedId = savedInstanceState?.getLong("id")
val selectedId = intent.getLongExtra(Constants.SelectedBaseNote, 0L)
val id = persistedId ?: selectedId
Expand Down Expand Up @@ -194,7 +197,30 @@ abstract class NotallyActivity(private val type: Type) : AppCompatActivity() {
val formatter = DateFormat.getDateInstance(DateFormat.FULL)
binding.DateCreated.text = formatter.format(model.timestamp)

binding.EnterTitle.setText(model.title)
val title = model.title
if (searchKeyword.isNotEmpty() && title.isNotEmpty()) {
val spannable = SpannableString(title)
highlightText(spannable, searchKeyword)
binding.EnterTitle.setText(spannable)
} else {
binding.EnterTitle.setText(title)
}
}

protected fun highlightText(spannable: Spannable, keyword: String) {
val highlightColor = getColor(R.color.LightBlue100)
var index = 0
while (index < spannable.length) {
val matchIndex = spannable.toString().indexOf(keyword, index, ignoreCase = true)
if (matchIndex == -1) break
spannable.setSpan(
BackgroundColorSpan(highlightColor),
matchIndex,
matchIndex + keyword.length,
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
)
index = matchIndex + keyword.length
}
}


Expand Down Expand Up @@ -554,7 +580,7 @@ abstract class NotallyActivity(private val type: Type) : AppCompatActivity() {
binding.Toolbar.setNavigationOnClickListener { finish() }

val menu = binding.Toolbar.menu
val pin = menu.add(R.string.pin, R.drawable.pin) { item -> pin(item) }
val pin = menu.add(R.string.pin, R.drawable.pin) { item -> bindPinned(item) }
bindPinned(pin)

menu.add(R.string.share, R.drawable.share) { share() }
Expand Down Expand Up @@ -605,7 +631,7 @@ abstract class NotallyActivity(private val type: Type) : AppCompatActivity() {
binding.EnterBody.setTextSize(TypedValue.COMPLEX_UNIT_SP, body)

model.labels.observe(this, Observer { labels ->
Operations.bindLabels(binding.LabelGroup, labels, model.textSize)
Operations.bindLabels(binding.LabelGroup, labels, TextSize.getDisplayBodySize(model.textSize))
})

setupColor()
Expand Down
10 changes: 9 additions & 1 deletion app/src/main/java/com/omgodse/notally/activities/TakeNote.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package com.omgodse.notally.activities
import android.content.Intent
import android.graphics.Typeface
import android.net.Uri
import android.text.SpannableString
import android.text.Spanned
import android.text.style.CharacterStyle
import android.text.style.StrikethroughSpan
Expand Down Expand Up @@ -47,7 +48,14 @@ class TakeNote : NotallyActivity(Type.NOTE) {

override fun setStateFromModel() {
super.setStateFromModel()
binding.EnterBody.text = model.body
val body = model.body
if (searchKeyword.isNotEmpty() && body.isNotEmpty()) {
val spannable = SpannableString(body)
highlightText(spannable, searchKeyword)
binding.EnterBody.setText(spannable)
} else {
binding.EnterBody.text = body
}
}


Expand Down
23 changes: 23 additions & 0 deletions app/src/main/java/com/omgodse/notally/fragments/NotallyFragment.kt
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import com.omgodse.notally.activities.TakeNote
import com.omgodse.notally.databinding.FragmentNotesBinding
import com.omgodse.notally.miscellaneous.Constants
import com.omgodse.notally.recyclerview.ItemListener
import com.omgodse.notally.recyclerview.SwipeSelectionListener
import com.omgodse.notally.recyclerview.adapter.BaseNoteAdapter
import com.omgodse.notally.room.BaseNote
import com.omgodse.notally.room.Item
Expand Down Expand Up @@ -132,12 +133,34 @@ abstract class NotallyFragment : Fragment(), ItemListener {
binding?.RecyclerView?.layoutManager = if (model.preferences.view.value == ViewPref.grid) {
StaggeredGridLayoutManager(2, RecyclerView.VERTICAL)
} else LinearLayoutManager(requireContext())

binding?.RecyclerView?.addOnItemTouchListener(SwipeSelectionListener(binding?.RecyclerView) { position ->
onItemIntercept(position)
})
}

private fun onItemIntercept(position: Int) {
if (position != -1) {
adapter?.currentList?.getOrNull(position)?.let { item ->
if (item is BaseNote) {
if (!model.actionMode.isEnabled()) {
model.actionMode.add(item.id, item)
adapter?.notifyItemChanged(position, 0)
}
handleNoteSelection(item.id, position, item)
}
}
}
}


private fun goToActivity(activity: Class<*>, baseNote: BaseNote) {
val intent = Intent(requireContext(), activity)
intent.putExtra(Constants.SelectedBaseNote, baseNote.id)
val keyword = model.keyword
if (keyword.isNotEmpty()) {
intent.putExtra(Constants.SearchKeyword, keyword)
}
startActivity(intent)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@ package com.omgodse.notally.miscellaneous
object Constants {
const val SelectedLabel = "SelectedLabel"
const val SelectedBaseNote = "SelectedBaseNote"
const val SearchKeyword = "SearchKeyword"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package com.omgodse.notally.recyclerview

import android.annotation.SuppressLint
import android.view.MotionEvent
import android.view.View
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView

class SwipeSelectionListener(
private val recyclerView: RecyclerView?,
private val onItemIntercept: (Int) -> Unit
) : RecyclerView.OnItemTouchListener {

private var isSwipeSelecting = false
private var lastInterceptedPosition = -1

@SuppressLint("ClickableViewAccessibility")
override fun onTouchEvent(rv: RecyclerView, e: MotionEvent) {
when (e.actionMasked) {
MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {
isSwipeSelecting = false
lastInterceptedPosition = -1
}
}
}

override fun onInterceptTouchEvent(rv: RecyclerView, e: MotionEvent): Boolean {
when (e.actionMasked) {
MotionEvent.ACTION_DOWN -> {
isSwipeSelecting = false
lastInterceptedPosition = -1
}
MotionEvent.ACTION_MOVE -> {
if (!isSwipeSelecting) {
val childView = findChildViewUnder(e.x, e.y)
if (childView != null) {
val position = recyclerView?.getChildAdapterPosition(childView) ?: -1
if (position != RecyclerView.NO_POSITION) {
val dx = abs(e.x - (childView.left + childView.width / 2f))
if (dx > childView.width * 0.3f) {
isSwipeSelecting = true
lastInterceptedPosition = position
onItemIntercept(position)
}
}
}
} else {
val childView = findChildViewUnder(e.x, e.y)
if (childView != null) {
val position = recyclerView?.getChildAdapterPosition(childView) ?: -1
if (position != RecyclerView.NO_POSITION && position != lastInterceptedPosition) {
lastInterceptedPosition = position
onItemIntercept(position)
}
}
}
}
}
return false
}

override fun onRequestDisallowInterceptTouchEvent(disallowIntercept: Boolean) {}

private fun findChildViewUnder(x: Float, y: Float): View? {
return recyclerView?.findChildViewUnder(x, y)
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
package com.omgodse.notally.recyclerview.adapter

import android.text.SpannableString
import android.text.Spannable
import android.text.style.BackgroundColorSpan
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.RecyclerView
import com.omgodse.notally.R
import com.omgodse.notally.databinding.RecyclerListItemBinding
import com.omgodse.notally.recyclerview.DragCallback
import com.omgodse.notally.recyclerview.ListItemListener
Expand All @@ -14,7 +18,8 @@ class MakeListAdapter(
private val textSize: String,
elevation: Float,
val list: ArrayList<ListItem>,
private val listener: ListItemListener
private val listener: ListItemListener,
private val searchKeyword: String = String()
) : RecyclerView.Adapter<MakeListVH>() {

private val callback = DragCallback(elevation, this)
Expand All @@ -29,7 +34,7 @@ class MakeListAdapter(

override fun onBindViewHolder(holder: MakeListVH, position: Int) {
val item = list[position]
holder.bind(item)
holder.bind(item, searchKeyword)
}

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MakeListVH {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
package com.omgodse.notally.recyclerview.viewholder

import android.text.Spannable
import android.text.SpannableString
import android.text.style.BackgroundColorSpan
import android.util.TypedValue
import android.view.MotionEvent
import androidx.core.widget.doAfterTextChanged
import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.RecyclerView
import com.omgodse.notally.R
import com.omgodse.notally.databinding.RecyclerListItemBinding
import com.omgodse.notally.miscellaneous.setOnNextAction
import com.omgodse.notally.preferences.TextSize
Expand Down Expand Up @@ -47,9 +51,31 @@ class MakeListVH(
}
}

fun bind(item: ListItem) {
fun bind(item: ListItem, searchKeyword: String = String()) {
binding.root.reset()
binding.EditText.setText(item.body)
if (searchKeyword.isNotEmpty() && item.body.isNotEmpty()) {
val spannable = SpannableString(item.body)
highlightText(spannable, searchKeyword)
binding.EditText.setText(spannable)
} else {
binding.EditText.setText(item.body)
}
binding.CheckBox.isChecked = item.checked
}

private fun highlightText(spannable: Spannable, keyword: String) {
val highlightColor = itemView.context.getColor(R.color.LightBlue100)
var index = 0
while (index < spannable.length) {
val matchIndex = spannable.toString().indexOf(keyword, index, ignoreCase = true)
if (matchIndex == -1) break
spannable.setSpan(
BackgroundColorSpan(highlightColor),
matchIndex,
matchIndex + keyword.length,
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
)
index = matchIndex + keyword.length
}
}
}
25 changes: 18 additions & 7 deletions app/src/main/java/com/omgodse/notally/viewmodels/NotallyModel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import android.net.Uri
import android.text.Editable
import android.text.SpannableStringBuilder
import android.text.Spanned
import android.text.style.BackgroundColorSpan
import android.text.style.CharacterStyle
import android.text.style.StrikethroughSpan
import android.text.style.StyleSpan
Expand Down Expand Up @@ -277,14 +278,9 @@ class NotallyModel(private val app: Application) : AndroidViewModel(app) {
audios.value = baseNote.audios
reminder.value = baseNote.reminder
} else {
createBaseNote()
Toast.makeText(app, R.string.cant_find_note, Toast.LENGTH_LONG).show()
}
} else createBaseNote()
}

private suspend fun createBaseNote() {
id = withContext(Dispatchers.IO) { baseNoteDao.insert(getBaseNote()) }
}
}


Expand All @@ -302,7 +298,20 @@ class NotallyModel(private val app: Application) : AndroidViewModel(app) {
}

suspend fun saveNote(): Long {
return withContext(Dispatchers.IO) { baseNoteDao.insert(getBaseNote()) }
if (isEmpty()) return 0L
return withContext(Dispatchers.IO) {
val savedId = baseNoteDao.insert(getBaseNote())
if (isNewNote) {
id = savedId
isNewNote = false
}
savedId
}
}

private fun isEmpty(): Boolean {
val bodyText = body.trimEnd().toString()
return title.isEmpty() && bodyText.isEmpty() && items.none { it.body.isNotEmpty() }
}

private suspend fun updateImages() {
Expand All @@ -328,6 +337,8 @@ class NotallyModel(private val app: Application) : AndroidViewModel(app) {
private fun getFilteredSpans(spanned: Spanned): ArrayList<SpanRepresentation> {
val representations = LinkedHashSet<SpanRepresentation>()
spanned.getSpans<CharacterStyle>().forEach { span ->
if (span is BackgroundColorSpan) return@forEach

val end = spanned.getSpanEnd(span)
val start = spanned.getSpanStart(span)
val representation = SpanRepresentation(false, false, false, false, false, start, end)
Expand Down