diff --git a/app/src/main/kotlin/com/simplemobiletools/notes/pro/activities/MainActivity.kt b/app/src/main/kotlin/com/simplemobiletools/notes/pro/activities/MainActivity.kt index 101a18db..b58faddb 100644 --- a/app/src/main/kotlin/com/simplemobiletools/notes/pro/activities/MainActivity.kt +++ b/app/src/main/kotlin/com/simplemobiletools/notes/pro/activities/MainActivity.kt @@ -904,17 +904,24 @@ class MainActivity : SimpleActivity() { } } - private fun exportNotesTo(outputStream: OutputStream?) { - toast(R.string.exporting) - ensureBackgroundThread { - val notesExporter = NotesExporter(this) - notesExporter.exportNotes(outputStream) { - val toastId = when (it) { - NotesExporter.ExportResult.EXPORT_OK -> R.string.exporting_successful - else -> R.string.exporting_failed - } + private fun requestUnlockNotes(callback: (List<Long>) -> Unit) { + val lockedNotes = mNotes.filter { it.isLocked() } + UnlockNotesDialog(this, lockedNotes, callback) + } - toast(toastId) + private fun exportNotesTo(outputStream: OutputStream?) { + requestUnlockNotes { unlockedIds -> + toast(R.string.exporting) + ensureBackgroundThread { + val notesExporter = NotesExporter(this) + notesExporter.exportNotes(outputStream, unlockedIds) { + val toastId = when (it) { + NotesExporter.ExportResult.EXPORT_OK -> R.string.exporting_successful + else -> R.string.exporting_failed + } + + toast(toastId) + } } } } @@ -1011,49 +1018,51 @@ class MainActivity : SimpleActivity() { } private fun exportAllNotesBelowQ() { - ExportFilesDialog(this, mNotes) { parent, extension -> - val items = arrayListOf( - RadioItem(EXPORT_FILE_SYNC, getString(R.string.update_file_at_note)), - RadioItem(EXPORT_FILE_NO_SYNC, getString(R.string.only_export_file_content)) - ) + requestUnlockNotes { unlockedIds -> + ExportFilesDialog(this, mNotes) { parent, extension -> + val items = arrayListOf( + RadioItem(EXPORT_FILE_SYNC, getString(R.string.update_file_at_note)), + RadioItem(EXPORT_FILE_NO_SYNC, getString(R.string.only_export_file_content)) + ) - RadioGroupDialog(this, items) { - val syncFile = it as Int == EXPORT_FILE_SYNC - var failCount = 0 - NotesHelper(this).getNotes { - mNotes = it - mNotes.filter { !it.isLocked() }.forEachIndexed { index, note -> - val filename = if (extension.isEmpty()) note.title else "${note.title}.$extension" - val file = File(parent, filename) - if (!filename.isAValidFilename()) { - toast(String.format(getString(R.string.filename_invalid_characters_placeholder, filename))) - } else { - val noteStoredValue = note.getNoteStoredValue(this) ?: "" - tryExportNoteValueToFile(file.absolutePath, mCurrentNote.title, note.value, false) { exportedSuccessfully -> - if (exportedSuccessfully) { - if (syncFile) { - note.path = file.absolutePath - note.value = "" - } else { - note.path = "" - note.value = noteStoredValue + RadioGroupDialog(this, items) { any -> + val syncFile = any as Int == EXPORT_FILE_SYNC + var failCount = 0 + NotesHelper(this).getNotes { notes -> + mNotes = notes + mNotes.filter { !it.isLocked() || it.id in unlockedIds }.forEachIndexed { index, note -> + val filename = if (extension.isEmpty()) note.title else "${note.title}.$extension" + val file = File(parent, filename) + if (!filename.isAValidFilename()) { + toast(String.format(getString(R.string.filename_invalid_characters_placeholder, filename))) + } else { + val noteStoredValue = note.getNoteStoredValue(this) ?: "" + tryExportNoteValueToFile(file.absolutePath, mCurrentNote.title, note.value, false) { exportedSuccessfully -> + if (exportedSuccessfully) { + if (syncFile) { + note.path = file.absolutePath + note.value = "" + } else { + note.path = "" + note.value = noteStoredValue + } + + NotesHelper(this).insertOrUpdateNote(note) } - NotesHelper(this).insertOrUpdateNote(note) - } + if (mCurrentNote.id == note.id) { + mCurrentNote.value = note.value + mCurrentNote.path = note.path + getPagerAdapter().updateCurrentNoteData(view_pager.currentItem, mCurrentNote.path, mCurrentNote.value) + } - if (mCurrentNote.id == note.id) { - mCurrentNote.value = note.value - mCurrentNote.path = note.path - getPagerAdapter().updateCurrentNoteData(view_pager.currentItem, mCurrentNote.path, mCurrentNote.value) - } + if (!exportedSuccessfully) { + failCount++ + } - if (!exportedSuccessfully) { - failCount++ - } - - if (index == mNotes.size - 1) { - toast(if (failCount == 0) R.string.exporting_successful else R.string.exporting_some_entries_failed) + if (index == mNotes.size - 1) { + toast(if (failCount == 0) R.string.exporting_successful else R.string.exporting_some_entries_failed) + } } } } diff --git a/app/src/main/kotlin/com/simplemobiletools/notes/pro/dialogs/NewNoteDialog.kt b/app/src/main/kotlin/com/simplemobiletools/notes/pro/dialogs/NewNoteDialog.kt index 90ccaba9..9ef06225 100644 --- a/app/src/main/kotlin/com/simplemobiletools/notes/pro/dialogs/NewNoteDialog.kt +++ b/app/src/main/kotlin/com/simplemobiletools/notes/pro/dialogs/NewNoteDialog.kt @@ -24,16 +24,16 @@ class NewNoteDialog(val activity: Activity, title: String? = null, val setCheckl new_note_type.check(defaultType) } - view.note_title.setText(title) + view.locked_note_title.setText(title) activity.getAlertDialogBuilder() .setPositiveButton(R.string.ok, null) .setNegativeButton(R.string.cancel, null) .apply { activity.setupDialogStuff(view, this, R.string.new_note) { alertDialog -> - alertDialog.showKeyboard(view.note_title) + alertDialog.showKeyboard(view.locked_note_title) alertDialog.getButton(BUTTON_POSITIVE).setOnClickListener { - val newTitle = view.note_title.value + val newTitle = view.locked_note_title.value ensureBackgroundThread { when { newTitle.isEmpty() -> activity.toast(R.string.no_title) diff --git a/app/src/main/kotlin/com/simplemobiletools/notes/pro/dialogs/RenameNoteDialog.kt b/app/src/main/kotlin/com/simplemobiletools/notes/pro/dialogs/RenameNoteDialog.kt index e9d0dd0a..e85d7631 100644 --- a/app/src/main/kotlin/com/simplemobiletools/notes/pro/dialogs/RenameNoteDialog.kt +++ b/app/src/main/kotlin/com/simplemobiletools/notes/pro/dialogs/RenameNoteDialog.kt @@ -18,16 +18,16 @@ class RenameNoteDialog(val activity: SimpleActivity, val note: Note, val current init { val view = activity.layoutInflater.inflate(R.layout.dialog_rename_note, null) - view.note_title.setText(note.title) + view.locked_note_title.setText(note.title) activity.getAlertDialogBuilder() .setPositiveButton(R.string.ok, null) .setNegativeButton(R.string.cancel, null) .apply { activity.setupDialogStuff(view, this, R.string.rename_note) { alertDialog -> - alertDialog.showKeyboard(view.note_title) + alertDialog.showKeyboard(view.locked_note_title) alertDialog.getButton(BUTTON_POSITIVE).setOnClickListener { - val title = view.note_title.value + val title = view.locked_note_title.value ensureBackgroundThread { newTitleConfirmed(title, alertDialog) } diff --git a/app/src/main/kotlin/com/simplemobiletools/notes/pro/dialogs/UnlockNotesDialog.kt b/app/src/main/kotlin/com/simplemobiletools/notes/pro/dialogs/UnlockNotesDialog.kt new file mode 100644 index 00000000..f42cbd88 --- /dev/null +++ b/app/src/main/kotlin/com/simplemobiletools/notes/pro/dialogs/UnlockNotesDialog.kt @@ -0,0 +1,64 @@ +package com.simplemobiletools.notes.pro.dialogs + +import android.content.DialogInterface +import android.view.ViewGroup +import com.simplemobiletools.commons.activities.BaseSimpleActivity +import com.simplemobiletools.commons.extensions.* +import com.simplemobiletools.notes.pro.R +import com.simplemobiletools.notes.pro.models.Note +import kotlinx.android.synthetic.main.dialog_unlock_notes.view.* +import kotlinx.android.synthetic.main.item_locked_note.view.* + +class UnlockNotesDialog(val activity: BaseSimpleActivity, notes: List<Note>, callback: (unlockedNoteIds: List<Long>) -> Unit) { + private val view = activity.layoutInflater.inflate(R.layout.dialog_unlock_notes, null) as ViewGroup + private val redColor = activity.getColor(R.color.md_red) + private val greenColor = activity.getColor(R.color.md_green) + private val unlockedNoteIds = mutableListOf<Long>() + + init { + for (note in notes) { + addLockedNoteView(note) + } + activity.getAlertDialogBuilder() + .setPositiveButton(R.string.ok, null) + .setNegativeButton(R.string.cancel, null) + .apply { + activity.setupDialogStuff(view, this, R.string.unlock_notes, cancelOnTouchOutside = false) { alertDialog -> + alertDialog.getButton(DialogInterface.BUTTON_POSITIVE).setOnClickListener { + callback(unlockedNoteIds) + alertDialog.dismiss() + } + } + } + } + + private fun addLockedNoteView(note: Note) { + activity.layoutInflater.inflate(R.layout.item_locked_note, null).apply { + view.notes_holder.addView(this) + activity.updateTextColors(view.notes_holder) + locked_note_title.text = note.title + locked_unlocked_image.applyColorFilter(redColor) + locked_note_holder.setOnClickListener { + if (note.id !in unlockedNoteIds) { + activity.performSecurityCheck( + protectionType = note.protectionType, + requiredHash = note.protectionHash, + successCallback = { _, _ -> + unlockedNoteIds.add(note.id!!) + locked_unlocked_image.apply { + setImageResource(R.drawable.ic_lock_open_vector) + applyColorFilter(greenColor) + } + } + ) + } else { + unlockedNoteIds.remove(note.id) + locked_unlocked_image.apply { + setImageResource(R.drawable.ic_lock_vector) + applyColorFilter(redColor) + } + } + } + } + } +} diff --git a/app/src/main/kotlin/com/simplemobiletools/notes/pro/helpers/NotesExporter.kt b/app/src/main/kotlin/com/simplemobiletools/notes/pro/helpers/NotesExporter.kt index 74c5346b..7d0e69c3 100644 --- a/app/src/main/kotlin/com/simplemobiletools/notes/pro/helpers/NotesExporter.kt +++ b/app/src/main/kotlin/com/simplemobiletools/notes/pro/helpers/NotesExporter.kt @@ -16,7 +16,7 @@ class NotesExporter(private val context: Context) { private val gson = Gson() - fun exportNotes(outputStream: OutputStream?, callback: (result: ExportResult) -> Unit) { + fun exportNotes(outputStream: OutputStream?, unlockedNoteIds: List<Long>, callback: (result: ExportResult) -> Unit) { ensureBackgroundThread { if (outputStream == null) { callback.invoke(ExportResult.EXPORT_FAIL) @@ -29,7 +29,7 @@ class NotesExporter(private val context: Context) { writer.beginArray() val notes = context.notesDB.getNotes() as ArrayList<Note> for (note in notes) { - if (note.protectionType == PROTECTION_NONE) { + if (!note.isLocked() || note.id in unlockedNoteIds) { val noteToSave = getNoteToExport(note) writer.jsonValue(gson.toJson(noteToSave)) written++ diff --git a/app/src/main/res/drawable/ic_lock_open_vector.xml b/app/src/main/res/drawable/ic_lock_open_vector.xml new file mode 100644 index 00000000..542b9037 --- /dev/null +++ b/app/src/main/res/drawable/ic_lock_open_vector.xml @@ -0,0 +1,3 @@ +<vector android:height="24dp" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android"> + <path android:fillColor="@android:color/white" android:pathData="M12 17c1.1 0 2-0.9 2-2s-0.9-2-2-2-2 0.9-2 2 0.9 2 2 2zm6-9h-1V6c0-2.76-2.24-5-5-5S7 3.24 7 6h1.9c0-1.71 1.39-3.1 3.1-3.1 1.71 0 3.1 1.39 3.1 3.1v2H6c-1.1 0-2 0.9-2 2v10c0 1.1 0.9 2 2 2h12c1.1 0 2-0.9 2-2V10c0-1.1-0.9-2-2-2zm0 12H6V10h12v10z"/> +</vector> diff --git a/app/src/main/res/layout/dialog_new_note.xml b/app/src/main/res/layout/dialog_new_note.xml index 26464308..dad5583a 100644 --- a/app/src/main/res/layout/dialog_new_note.xml +++ b/app/src/main/res/layout/dialog_new_note.xml @@ -17,7 +17,7 @@ android:hint="@string/label"> <com.google.android.material.textfield.TextInputEditText - android:id="@+id/note_title" + android:id="@+id/locked_note_title" android:layout_width="match_parent" android:layout_height="wrap_content" android:inputType="textCapSentences" diff --git a/app/src/main/res/layout/dialog_rename_note.xml b/app/src/main/res/layout/dialog_rename_note.xml index b8d1a37c..7e9bb395 100644 --- a/app/src/main/res/layout/dialog_rename_note.xml +++ b/app/src/main/res/layout/dialog_rename_note.xml @@ -14,7 +14,7 @@ android:hint="@string/title"> <com.google.android.material.textfield.TextInputEditText - android:id="@+id/note_title" + android:id="@+id/locked_note_title" android:layout_width="match_parent" android:layout_height="wrap_content" android:inputType="textCapSentences" diff --git a/app/src/main/res/layout/dialog_unlock_notes.xml b/app/src/main/res/layout/dialog_unlock_notes.xml new file mode 100644 index 00000000..c0ac82de --- /dev/null +++ b/app/src/main/res/layout/dialog_unlock_notes.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="utf-8"?> +<ScrollView xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/dialog_holder" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:fillViewport="true" + android:paddingTop="@dimen/activity_margin"> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical"> + + <com.simplemobiletools.commons.views.MyTextView + android:id="@+id/found_locked_notes_info" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginHorizontal="@dimen/big_margin" + android:layout_marginTop="@dimen/small_margin" + android:layout_marginBottom="@dimen/activity_margin" + android:text="@string/found_locked_notes_info" /> + + <LinearLayout + android:id="@+id/notes_holder" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical" /> + + </LinearLayout> +</ScrollView> diff --git a/app/src/main/res/layout/item_locked_note.xml b/app/src/main/res/layout/item_locked_note.xml new file mode 100644 index 00000000..e77ae12b --- /dev/null +++ b/app/src/main/res/layout/item_locked_note.xml @@ -0,0 +1,35 @@ +<?xml version="1.0" encoding="utf-8"?> +<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools" + android:id="@+id/locked_note_holder" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:background="@drawable/ripple_background" + android:clickable="true" + android:focusable="true" + android:paddingHorizontal="@dimen/normal_margin"> + + <com.simplemobiletools.commons.views.MyTextView + android:id="@+id/locked_note_title" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:paddingStart="@dimen/activity_margin" + android:textSize="@dimen/bigger_text_size" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toStartOf="@+id/locked_unlocked_image" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" + tools:text="Passwords" /> + + <ImageView + android:id="@+id/locked_unlocked_image" + android:layout_width="@dimen/fab_size" + android:layout_height="@dimen/fab_size" + android:paddingVertical="@dimen/activity_margin" + android:src="@drawable/ic_lock_vector" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintTop_toTopOf="parent" /> + +</androidx.constraintlayout.widget.ConstraintLayout>