diff --git a/app/src/main/java/dev/arkbuilders/arkmemo/models/GraphicNote.kt b/app/src/main/java/dev/arkbuilders/arkmemo/models/GraphicNote.kt index 5ab1dc85..e69220d0 100644 --- a/app/src/main/java/dev/arkbuilders/arkmemo/models/GraphicNote.kt +++ b/app/src/main/java/dev/arkbuilders/arkmemo/models/GraphicNote.kt @@ -14,5 +14,6 @@ data class GraphicNote( val svg: SVG? = null, @IgnoredOnParcel override var resource: Resource? = null, - override var pendingForDelete: Boolean = false + override var pendingForDelete: Boolean = false, + override var selected: Boolean = false ) : Note, Parcelable \ No newline at end of file diff --git a/app/src/main/java/dev/arkbuilders/arkmemo/models/Note.kt b/app/src/main/java/dev/arkbuilders/arkmemo/models/Note.kt index 2ad3cfe5..85ecbefc 100644 --- a/app/src/main/java/dev/arkbuilders/arkmemo/models/Note.kt +++ b/app/src/main/java/dev/arkbuilders/arkmemo/models/Note.kt @@ -7,4 +7,5 @@ interface Note { val description: String var resource: Resource? var pendingForDelete: Boolean + var selected: Boolean } diff --git a/app/src/main/java/dev/arkbuilders/arkmemo/models/TextNote.kt b/app/src/main/java/dev/arkbuilders/arkmemo/models/TextNote.kt index 715e946d..3c52fce7 100644 --- a/app/src/main/java/dev/arkbuilders/arkmemo/models/TextNote.kt +++ b/app/src/main/java/dev/arkbuilders/arkmemo/models/TextNote.kt @@ -12,5 +12,6 @@ data class TextNote ( val text: String = "", @IgnoredOnParcel override var resource: Resource? = null, - override var pendingForDelete: Boolean = false + override var pendingForDelete: Boolean = false, + override var selected: Boolean = false ): Note, Parcelable diff --git a/app/src/main/java/dev/arkbuilders/arkmemo/models/VoiceNote.kt b/app/src/main/java/dev/arkbuilders/arkmemo/models/VoiceNote.kt index 2aeafa6f..c2707bc4 100644 --- a/app/src/main/java/dev/arkbuilders/arkmemo/models/VoiceNote.kt +++ b/app/src/main/java/dev/arkbuilders/arkmemo/models/VoiceNote.kt @@ -20,5 +20,6 @@ class VoiceNote( var isPlaying: Boolean = false, var pendingForPlaybackReset: Boolean = false, var currentPlayingPos: Int = 0, - var currentMaxAmplitude: Int = 0 + var currentMaxAmplitude: Int = 0, + override var selected: Boolean = false ): Note, Parcelable \ No newline at end of file diff --git a/app/src/main/java/dev/arkbuilders/arkmemo/repo/NotesRepo.kt b/app/src/main/java/dev/arkbuilders/arkmemo/repo/NotesRepo.kt index 7f12eaa3..03cda8de 100644 --- a/app/src/main/java/dev/arkbuilders/arkmemo/repo/NotesRepo.kt +++ b/app/src/main/java/dev/arkbuilders/arkmemo/repo/NotesRepo.kt @@ -11,4 +11,6 @@ interface NotesRepo { suspend fun read(): List suspend fun delete(note: Note) + + suspend fun delete(notes: List) } \ No newline at end of file diff --git a/app/src/main/java/dev/arkbuilders/arkmemo/repo/NotesRepoHelper.kt b/app/src/main/java/dev/arkbuilders/arkmemo/repo/NotesRepoHelper.kt index 924d6214..eaa92759 100644 --- a/app/src/main/java/dev/arkbuilders/arkmemo/repo/NotesRepoHelper.kt +++ b/app/src/main/java/dev/arkbuilders/arkmemo/repo/NotesRepoHelper.kt @@ -80,6 +80,12 @@ class NotesRepoHelper @Inject constructor( return UserNoteProperties(title, description) } + suspend fun deleteNote(notes: List): Unit = withContext(Dispatchers.IO) { + notes.forEach { note -> + deleteNote(note) + } + } + suspend fun deleteNote(note: Note): Unit = withContext(Dispatchers.IO) { val id = note.resource?.id diff --git a/app/src/main/java/dev/arkbuilders/arkmemo/repo/graphics/GraphicNotesRepo.kt b/app/src/main/java/dev/arkbuilders/arkmemo/repo/graphics/GraphicNotesRepo.kt index f820df82..7d77d225 100644 --- a/app/src/main/java/dev/arkbuilders/arkmemo/repo/graphics/GraphicNotesRepo.kt +++ b/app/src/main/java/dev/arkbuilders/arkmemo/repo/graphics/GraphicNotesRepo.kt @@ -47,6 +47,10 @@ class GraphicNotesRepo @Inject constructor( helper.deleteNote(note) } + override suspend fun delete(notes: List) { + helper.deleteNote(notes) + } + override suspend fun read(): List = withContext(iODispatcher) { readStorage() } diff --git a/app/src/main/java/dev/arkbuilders/arkmemo/repo/text/TextNotesRepo.kt b/app/src/main/java/dev/arkbuilders/arkmemo/repo/text/TextNotesRepo.kt index 12ecba95..c78799a9 100644 --- a/app/src/main/java/dev/arkbuilders/arkmemo/repo/text/TextNotesRepo.kt +++ b/app/src/main/java/dev/arkbuilders/arkmemo/repo/text/TextNotesRepo.kt @@ -42,6 +42,10 @@ class TextNotesRepo @Inject constructor( write(note) { callback(it) } } + override suspend fun delete(notes: List) { + helper.deleteNote(notes) + } + override suspend fun delete(note: TextNote) { helper.deleteNote(note) } diff --git a/app/src/main/java/dev/arkbuilders/arkmemo/repo/voices/VoiceNotesRepo.kt b/app/src/main/java/dev/arkbuilders/arkmemo/repo/voices/VoiceNotesRepo.kt index b324b54b..7fa7bbb5 100644 --- a/app/src/main/java/dev/arkbuilders/arkmemo/repo/voices/VoiceNotesRepo.kt +++ b/app/src/main/java/dev/arkbuilders/arkmemo/repo/voices/VoiceNotesRepo.kt @@ -40,6 +40,10 @@ class VoiceNotesRepo @Inject constructor( readStorage() } + override suspend fun delete(notes: List) { + helper.deleteNote(notes) + } + override suspend fun delete(note: VoiceNote) { helper.deleteNote(note) } diff --git a/app/src/main/java/dev/arkbuilders/arkmemo/ui/adapters/NotesListAdapter.kt b/app/src/main/java/dev/arkbuilders/arkmemo/ui/adapters/NotesListAdapter.kt index 716173f5..08048953 100644 --- a/app/src/main/java/dev/arkbuilders/arkmemo/ui/adapters/NotesListAdapter.kt +++ b/app/src/main/java/dev/arkbuilders/arkmemo/ui/adapters/NotesListAdapter.kt @@ -3,9 +3,11 @@ package dev.arkbuilders.arkmemo.ui.adapters import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import android.widget.CompoundButton import androidx.appcompat.app.AppCompatActivity import androidx.core.content.ContextCompat import androidx.core.content.res.ResourcesCompat +import androidx.lifecycle.MutableLiveData import androidx.recyclerview.widget.RecyclerView import by.kirich1409.viewbindingdelegate.viewBinding import dev.arkbuilders.arkmemo.R @@ -35,6 +37,7 @@ class NotesListAdapter( ): RecyclerView.Adapter() { private lateinit var activity: MainActivity + private var mActionMode = false lateinit var observeItemSideEffect: () -> ArkMediaPlayerSideEffect lateinit var observeItemState: () -> ArkMediaPlayerState @@ -42,6 +45,13 @@ class NotesListAdapter( private var isFromSearch: Boolean = false private var searchKeyWord: String = "" + var onItemLongPressed: ((pos: Int, note: Note) -> Unit)? = null + var onItemClicked: (() -> Unit)? = null + + private val selectedNoteCount by lazy { MutableLiveData() } + val observableSelectedNoteCount by lazy { selectedNoteCount } + val selectedNotedForDelete = mutableListOf() + fun setActivity(activity: AppCompatActivity) { this.activity = activity as MainActivity } @@ -74,13 +84,16 @@ class NotesListAdapter( } holder.btnPlayPause.setOnClickListener { - onPlayPauseClick(note.path.toString(), position) { stopPos -> + onPlayPauseClick(note.path.toString(), holder.bindingAdapterPosition) { stopPos -> + val realPos = + if (holder.bindingAdapterPosition >= 0) holder.bindingAdapterPosition + else position showPlaybackIdleState(holder) - (notes[position] as VoiceNote).isPlaying = false + (notes[realPos] as VoiceNote).isPlaying = false holder.layoutAudioView.animAudioPlaying.resetWave() holder.layoutAudioView.animAudioPlaying.invalidateWave(0) holder.tvPlayingPosition.gone() - notifyItemChanged(position) + notifyItemChanged(realPos) } handleMediaPlayerSideEffect(observeItemSideEffect(), holder) note.isPlaying = !note.isPlaying @@ -111,6 +124,13 @@ class NotesListAdapter( } else { holder.tvDelete.gone() } + + holder.cbDelete.isChecked = note.selected + if (mActionMode) { + holder.cbDelete.visible() + } else { + holder.cbDelete.gone() + } } override fun getItemCount() = notes.size @@ -173,12 +193,33 @@ class NotesListAdapter( return notes } + fun removeNote(noteToRemove: Note) { + notes.remove(noteToRemove) + } + fun setNotes(notes: List) { this.notes = notes.toMutableList() } - fun removeNote(noteToRemove: Note) { - notes.remove(noteToRemove) + fun toggleActionMode() { + mActionMode = !mActionMode + notes.forEach { it.selected = false } + selectedNoteCount.postValue(0) + notifyDataSetChanged() + } + + fun toggleSelectAllItems(selected: Boolean) { + notes.forEach { it.selected = selected } + selectedNotedForDelete.clear() + selectedNoteCount.postValue( + if (selected) { + selectedNotedForDelete.addAll(notes) + notes.size + } else { + 0 + } + ) + notifyDataSetChanged() } inner class NoteViewHolder(itemView: View): RecyclerView.ViewHolder(itemView) { @@ -193,6 +234,8 @@ class NotesListAdapter( val tvPlayingPosition = binding.layoutAudioView.tvPlayingPosition val canvasGraphicThumb = binding.canvasGraphicThumb val tvDelete = binding.tvDelete + val cbDelete = binding.cbDelete + var isSwiping: Boolean = false private val clickNoteToEditListener = View.OnClickListener { @@ -208,11 +251,39 @@ class NotesListAdapter( tag = ArkRecorderFragment.TAG } } + onItemClicked?.invoke() activity.replaceFragment(activity.fragment, tag) } + private val noteCheckedListener = CompoundButton.OnCheckedChangeListener { buttonView, isChecked -> + if (!buttonView.isPressed) return@OnCheckedChangeListener + val selectedNote = notes[bindingAdapterPosition] + selectedNote.selected = isChecked + if (isChecked) { + selectedNoteCount.value?.let { count -> + selectedNoteCount.postValue(count + 1) + } + selectedNotedForDelete.add(selectedNote) + } else { + selectedNoteCount.value?.let { count -> + selectedNoteCount.postValue(count - 1) + } + selectedNotedForDelete.remove(selectedNote) + } + + buttonView.post { + notifyItemChanged(bindingAdapterPosition) + } + } + init { binding.root.setOnClickListener(clickNoteToEditListener) + binding.root.setOnLongClickListener { + onItemLongPressed?.invoke(bindingAdapterPosition, notes[bindingAdapterPosition]) + true + } + binding.cbDelete.setOnCheckedChangeListener(noteCheckedListener) + binding.layoutAudioView.root.setBackgroundResource(R.drawable.bg_audio_view_note_item) } } } \ No newline at end of file diff --git a/app/src/main/java/dev/arkbuilders/arkmemo/ui/dialogs/CommonActionDialog.kt b/app/src/main/java/dev/arkbuilders/arkmemo/ui/dialogs/CommonActionDialog.kt index 1dda9126..e6f352ed 100644 --- a/app/src/main/java/dev/arkbuilders/arkmemo/ui/dialogs/CommonActionDialog.kt +++ b/app/src/main/java/dev/arkbuilders/arkmemo/ui/dialogs/CommonActionDialog.kt @@ -13,8 +13,8 @@ import dev.arkbuilders.arkmemo.databinding.DialogCommonActionBinding * This is a common action dialog that can be used inside app. * It's a basic dialog with customizable title, message, one positive button and one negative button */ -class CommonActionDialog(@StringRes private val title: Int, - @StringRes private val message: Int, +class CommonActionDialog(private val title: String, + private val message: String, @StringRes private val positiveText: Int, @StringRes private val negativeText: Int, private val isAlert: Boolean = false, @@ -46,8 +46,8 @@ class CommonActionDialog(@StringRes private val title: Int, mBinding.tvPositive.setBackgroundResource(R.drawable.bg_red_button) } - mBinding.tvTitle.setText(title) - mBinding.tvMessage.setText(message) + mBinding.tvTitle.text = title + mBinding.tvMessage.text = message mBinding.tvPositive.setText(positiveText) mBinding.tvNegative.setText(negativeText) mBinding.ivClose.setOnClickListener { diff --git a/app/src/main/java/dev/arkbuilders/arkmemo/ui/fragments/ArkRecorderFragment.kt b/app/src/main/java/dev/arkbuilders/arkmemo/ui/fragments/ArkRecorderFragment.kt index b8c0a097..2a98e6f6 100644 --- a/app/src/main/java/dev/arkbuilders/arkmemo/ui/fragments/ArkRecorderFragment.kt +++ b/app/src/main/java/dev/arkbuilders/arkmemo/ui/fragments/ArkRecorderFragment.kt @@ -151,8 +151,8 @@ class ArkRecorderFragment: BaseEditNoteFragment() { && (arkRecorderViewModel.isRecordExisting() || File(getCurrentRecordingPath()).length() > 0L)) { - CommonActionDialog(title = R.string.dialog_replace_recording_title, - message = R.string.dialog_replace_recording_message, + CommonActionDialog(title = getString(R.string.dialog_replace_recording_title), + message = getString(R.string.dialog_replace_recording_message), positiveText = R.string.dialog_replace_recording_positive_text, negativeText = R.string.discard, onPositiveClick = { @@ -200,15 +200,16 @@ class ArkRecorderFragment: BaseEditNoteFragment() { title = title.ifEmpty { defaultNoteTitle }, path = arkRecorderViewModel.getRecordingPath() ) - CommonActionDialog(title = R.string.delete_note, - message = R.string.ark_memo_delete_warn, + CommonActionDialog(title = getString(R.string.delete_note), + message = resources.getQuantityString(R.plurals.delete_batch_note_message, 1), positiveText = R.string.action_delete, negativeText = R.string.ark_memo_cancel, isAlert = true, onPositiveClick = { - notesViewModel.onDeleteConfirmed(note){} - toast(requireContext(), getString(R.string.note_deleted)) - activity.onBackPressedDispatcher.onBackPressed() + notesViewModel.onDeleteConfirmed(listOf(note)) { + toast(requireContext(), getString(R.string.note_deleted)) + activity.onBackPressedDispatcher.onBackPressed() + } }, onNegativeClicked = { }).show(parentFragmentManager, CommonActionDialog.TAG) } diff --git a/app/src/main/java/dev/arkbuilders/arkmemo/ui/fragments/BaseEditNoteFragment.kt b/app/src/main/java/dev/arkbuilders/arkmemo/ui/fragments/BaseEditNoteFragment.kt index 50df0e47..4e7e6fac 100644 --- a/app/src/main/java/dev/arkbuilders/arkmemo/ui/fragments/BaseEditNoteFragment.kt +++ b/app/src/main/java/dev/arkbuilders/arkmemo/ui/fragments/BaseEditNoteFragment.kt @@ -101,8 +101,8 @@ abstract class BaseEditNoteFragment: BaseFragment() { private fun showSaveNoteDialog(needStopRecording: Boolean = false, onDiscard: (needClearResource: Boolean) -> Unit) { val saveNoteDialog = CommonActionDialog( - title = R.string.dialog_save_note_title, - message = R.string.dialog_save_note_message, + title = getString(R.string.dialog_save_note_title), + message = getString(R.string.dialog_save_note_message), positiveText = R.string.save, negativeText = R.string.discard, isAlert = false, @@ -123,15 +123,16 @@ abstract class BaseEditNoteFragment: BaseFragment() { fun showDeleteNoteDialog(note: Note) { CommonActionDialog( - title = R.string.delete_note, - message = R.string.ark_memo_delete_warn , + title = getString(R.string.delete_note), + message = resources.getQuantityString(R.plurals.delete_batch_note_message, 1), positiveText = R.string.action_delete, negativeText = R.string.ark_memo_cancel, isAlert = true, onPositiveClick = { - notesViewModel.onDeleteConfirmed(note){} - hostActivity.onBackPressedDispatcher.onBackPressed() - toast(requireContext(), getString(R.string.note_deleted)) + notesViewModel.onDeleteConfirmed(listOf(note)) { + hostActivity.onBackPressedDispatcher.onBackPressed() + toast(requireContext(), getString(R.string.note_deleted)) + } }, onNegativeClicked = { }).show(parentFragmentManager, CommonActionDialog.TAG) } diff --git a/app/src/main/java/dev/arkbuilders/arkmemo/ui/fragments/NotesFragment.kt b/app/src/main/java/dev/arkbuilders/arkmemo/ui/fragments/NotesFragment.kt index 0a03e983..b7643c67 100644 --- a/app/src/main/java/dev/arkbuilders/arkmemo/ui/fragments/NotesFragment.kt +++ b/app/src/main/java/dev/arkbuilders/arkmemo/ui/fragments/NotesFragment.kt @@ -56,6 +56,9 @@ class NotesFragment: BaseFragment() { private var playingAudioPosition = -1 private var lastNoteItemPosition = 0 + private var mIsActionMode = false + private var selectedCountForDelete = 0 + private val newTextNoteClickListener = View.OnClickListener { onFloatingActionButtonClicked() } @@ -86,25 +89,22 @@ class NotesFragment: BaseFragment() { val noteToDelete = notesAdapter?.getNotes()?.getOrNull(deletePosition)?.apply { pendingForDelete = true } ?: return - val noteViewHolder = viewHolder as? NotesListAdapter.NoteViewHolder noteViewHolder?.isSwiping = true - binding.rvPinnedNotes.adapter?.notifyItemChanged(deletePosition) - CommonActionDialog(title = R.string.delete_note, - message = R.string.ark_memo_delete_warn, + CommonActionDialog(title = getString(R.string.delete_note), + message = resources.getQuantityString(R.plurals.delete_batch_note_message, 1), positiveText = R.string.action_delete, negativeText = R.string.ark_memo_cancel, isAlert = true, onPositiveClick = { noteViewHolder?.isSwiping = false - notesViewModel.onDeleteConfirmed(noteToDelete) { + notesViewModel.onDeleteConfirmed(listOf(noteToDelete)) { notesAdapter?.removeNote(noteToDelete) toast(requireContext(), getString(R.string.note_deleted)) binding.rvPinnedNotes.adapter?.notifyItemRemoved(deletePosition) } - }, onNegativeClicked = { noteViewHolder?.isSwiping = false noteToDelete.pendingForDelete = false @@ -216,6 +216,7 @@ class NotesFragment: BaseFragment() { } ) + observeSelectedNoteForDelete() } else { notesAdapter?.setNotes(notes) @@ -225,6 +226,9 @@ class NotesFragment: BaseFragment() { observePlayerState() observePlayerSideEffect() notesAdapter?.setActivity(activity) + notesAdapter?.onItemLongPressed = {pos, note -> + toggleActionMode() + } binding.rvPinnedNotes.apply { this.layoutManager = layoutManager this.adapter = notesAdapter @@ -235,7 +239,6 @@ class NotesFragment: BaseFragment() { } } } - mItemTouchHelper = ItemTouchHelper(mItemTouchCallback) mItemTouchHelper?.attachToRecyclerView(binding.rvPinnedNotes) @@ -282,7 +285,6 @@ class NotesFragment: BaseFragment() { arkMediaPlayerViewModel.playerSideEffect.collectLatest { sideEffect -> sideEffect ?: return@collectLatest notesAdapter?.observeItemSideEffect = { sideEffect } - if (sideEffect == ArkMediaPlayerSideEffect.StopPlaying) { mItemTouchHelper?.attachToRecyclerView(binding.rvPinnedNotes) } @@ -300,6 +302,10 @@ class NotesFragment: BaseFragment() { notesAdapter?.notifyItemChanged(playingAudioPosition) } } + + if (mIsActionMode) { + toggleActionMode() + } } override fun onPause() { @@ -317,6 +323,9 @@ class NotesFragment: BaseFragment() { activity.fragment = this observeClipboardContent() binding.rvPinnedNotes.layoutManager?.scrollToPosition(lastNoteItemPosition) + if (notesAdapter?.observableSelectedNoteCount?.hasActiveObservers() == false) { + observeSelectedNoteForDelete() + } } private fun createTextNote() { @@ -397,6 +406,94 @@ class NotesFragment: BaseFragment() { } } + private fun toggleActionMode() { + if (mIsActionMode) { + binding.groupActionModeTexts.gone() + binding.layoutBottomControl.visible() + binding.edtSearch.visible() + binding.ivSettings.visible() + } else { + binding.groupActionModeTexts.visible() + updateSelectStateTexts(selectedCountForDelete) + binding.layoutBottomControl.gone() + binding.edtSearch.gone() + binding.ivSettings.gone() + binding.tvActionModeCancel.setOnClickListener { + toggleActionMode() + } + binding.tvActionModeSelectAll.setOnClickListener { + + if (selectedCountForDelete == notesAdapter?.getNotes()?.size) { + notesAdapter?.toggleSelectAllItems(selected = false) + } else { + notesAdapter?.toggleSelectAllItems(selected = true) + } + updateSelectStateTexts(selectedCountForDelete) + } + binding.btnDelete.setOnClickListener { + showBatchDeletionDialog() + } + } + (binding.rvPinnedNotes.adapter as? NotesListAdapter)?.toggleActionMode() + mIsActionMode = !mIsActionMode + } + + private fun showBatchDeletionDialog() { + CommonActionDialog(title = resources.getQuantityString(R.plurals.delete_note_count, + selectedCountForDelete, selectedCountForDelete), + message = resources.getQuantityString(R.plurals.delete_batch_note_message, selectedCountForDelete), + positiveText = R.string.action_delete, + negativeText = R.string.ark_memo_cancel, + isAlert = true, + onPositiveClick = { + binding.pbLoading.visible() + notesViewModel.onDeleteConfirmed(notesAdapter?.selectedNotedForDelete ?: emptyList()) { + binding.pbLoading.gone() + toast(requireContext(), getString(R.string.note_deleted)) + binding.rvPinnedNotes.adapter?.notifyDataSetChanged() + toggleActionMode() + } + }, + onNegativeClicked = {}, + onCloseClicked = {} + ).show(childFragmentManager, CommonActionDialog.TAG) + } + + private fun updateSelectStateTexts(selectedCount: Int) { + binding.tvSelectedNoteCount.text = resources.getQuantityString( + R.plurals.selected_note_count, selectedCount, selectedCount + ) + binding.tvActionModeSelectAll.text = + if (selectedCount == (notesAdapter?.getNotes()?.size ?: 0)) { + getString(R.string.deselect_all) + } else { + getString(R.string.select_all) + } + } + + private fun changeDeleteButtonState(enabled: Boolean) { + if (enabled) { + binding.btnDelete.isClickable = true + binding.btnDelete.alpha = 1f + } else { + binding.btnDelete.isClickable = false + binding.btnDelete.alpha = 0.4f + } + } + + private fun observeSelectedNoteForDelete() { + notesAdapter?.observableSelectedNoteCount?.observe(viewLifecycleOwner) { count -> + selectedCountForDelete = count + updateSelectStateTexts(count) + + if (count > 0) { + changeDeleteButtonState(enabled = true) + } else { + changeDeleteButtonState(enabled = false) + } + } + } + override fun onBackPressed() { if (binding.edtSearch.isFocused) { binding.edtSearch.text.clear() diff --git a/app/src/main/java/dev/arkbuilders/arkmemo/ui/viewmodels/NotesViewModel.kt b/app/src/main/java/dev/arkbuilders/arkmemo/ui/viewmodels/NotesViewModel.kt index ca6b0f4b..c9191b3e 100644 --- a/app/src/main/java/dev/arkbuilders/arkmemo/ui/viewmodels/NotesViewModel.kt +++ b/app/src/main/java/dev/arkbuilders/arkmemo/ui/viewmodels/NotesViewModel.kt @@ -94,7 +94,7 @@ class NotesViewModel @Inject constructor( || result == SaveNoteResult.SUCCESS_UPDATED) { if (result == SaveNoteResult.SUCCESS_NEW) { - parentNote?.let { onDeleteConfirmed(parentNote){} } + parentNote?.let { onDeleteConfirmed(listOf(parentNote)){} } } add(note, noteResId) @@ -124,16 +124,17 @@ class NotesViewModel @Inject constructor( } } - fun onDeleteConfirmed(note: Note, onSuccess: () -> Unit) { + fun onDeleteConfirmed(notes: List, onSuccess: () -> Unit) { viewModelScope.launch(iODispatcher) { - when (note) { - is TextNote -> textNotesRepo.delete(note) - is GraphicNote -> graphicNotesRepo.delete(note) - is VoiceNote -> voiceNotesRepo.delete(note) + notes.forEach { note -> + when (note) { + is TextNote -> textNotesRepo.delete(note) + is GraphicNote -> graphicNotesRepo.delete(note) + is VoiceNote -> voiceNotesRepo.delete(note) + } } - this@NotesViewModel.notes.value = this@NotesViewModel.notes.value.toMutableList() - .apply { remove(note) } + .apply { removeAll(notes) } withContext(Dispatchers.Main) { onSuccess.invoke() } diff --git a/app/src/main/res/drawable/bg_audio_view_note_item.xml b/app/src/main/res/drawable/bg_audio_view_note_item.xml new file mode 100644 index 00000000..739f49e8 --- /dev/null +++ b/app/src/main/res/drawable/bg_audio_view_note_item.xml @@ -0,0 +1,9 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_delete_tag.xml b/app/src/main/res/drawable/bg_delete_tag.xml new file mode 100644 index 00000000..8f74f8aa --- /dev/null +++ b/app/src/main/res/drawable/bg_delete_tag.xml @@ -0,0 +1,9 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/font/font.xml b/app/src/main/res/font/font.xml new file mode 100644 index 00000000..c06f89eb --- /dev/null +++ b/app/src/main/res/font/font.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/adapter_text_note.xml b/app/src/main/res/layout/adapter_text_note.xml index c73d7e13..c79996c2 100644 --- a/app/src/main/res/layout/adapter_text_note.xml +++ b/app/src/main/res/layout/adapter_text_note.xml @@ -4,96 +4,122 @@ android:layout_height="wrap_content" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" - android:background="@drawable/bg_big_radius" android:clickable="true" android:layout_marginBottom="@dimen/note_item_padding" android:foreground="?attr/selectableItemBackgroundBorderless"> - - - + app:layout_constraintBottom_toBottomOf="parent" + android:buttonTint="@color/yellow_700" + android:visibility="gone"/> - + app:layout_constraintTop_toTopOf="parent" + android:background="@drawable/bg_big_radius" + app:layout_constraintBottom_toBottomOf="parent"> - + - + - + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/dialog_common_action.xml b/app/src/main/res/layout/dialog_common_action.xml index 10195b12..045ec13e 100644 --- a/app/src/main/res/layout/dialog_common_action.xml +++ b/app/src/main/res/layout/dialog_common_action.xml @@ -3,6 +3,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools" android:layout_gravity="center" android:fitsSystemWindows="true"> @@ -38,7 +39,7 @@ app:layout_constraintStart_toStartOf="@+id/tv_title" app:layout_constraintTop_toBottomOf="@+id/iv_close" app:layout_constraintEnd_toEndOf="@+id/iv_close" - android:text="@string/ark_memo_delete_warn" + tools:text="Are you sure you want to delete this note? This action cannot be undone." android:id="@+id/tv_message"/> + android:layout_marginStart="@dimen/home_horizontal_margin" /> + + + + + + + + + + + + + + + + + app:layout_constraintTop_toBottomOf="@+id/barrier_top_note_list"/> + android:layout_height="wrap_content" + xmlns:tools="http://schemas.android.com/tools"> + + + + #FFFAEB #FEF0C7 #FEC84B + #DC6803 #B54708 #344054 #B54708 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 14565db5..40c9eab1 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -26,7 +26,6 @@ Nothing to paste OK Cancel - Are you sure you want to delete this note? This action cannot be undone. Note already exists Note saved successfully Title @@ -102,4 +101,19 @@ Invalid recording file Play/Pause + + %d Item selected + %d Items selected + + + Delete %d note + Delete %d notes + + + Are you sure you want to delete this note? This action cannot be undone. + Are you sure you want to delete these notes? This action cannot be undone. + + Select All + Deselect All + \ No newline at end of file diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index e9befa5d..58c44ebc 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -1,5 +1,5 @@ - + + +