Skip to content
Merged
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 @@ -7,13 +7,16 @@ import android.content.ClipData
import android.content.ClipboardManager
import android.content.ContentResolver
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.graphics.Typeface
import android.hardware.biometrics.BiometricManager
import android.hardware.biometrics.BiometricManager.Authenticators.BIOMETRIC_STRONG
import android.hardware.biometrics.BiometricManager.Authenticators.DEVICE_CREDENTIAL
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.os.Parcelable
import android.provider.OpenableColumns
import android.text.Editable
import android.text.InputType
Expand Down Expand Up @@ -486,3 +489,41 @@ fun MaterialAlertDialogBuilder.showAndFocus(view: View): AlertDialog {
fun Context.getQuantityString(id: Int, quantity: Int, vararg formatArgs: Any): String {
return resources.getQuantityString(id, quantity, quantity, *formatArgs)
}

fun <T : Parcelable> Bundle?.getParcelableCompat(key: String, clazz: Class<T>): T? {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
this?.getParcelable(key, clazz)
} else {
this?.getParcelable(key)
}
}

fun <T : Parcelable> Bundle?.getParcelableArrayListCompat(
key: String,
clazz: Class<T>,
): ArrayList<T>? {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
this?.getParcelableArrayList(key, clazz)
} else {
this?.getParcelableArrayList(key)
}
}

fun <T : Parcelable> Intent?.getParcelableArrayListExtraCompat(
key: String,
clazz: Class<T>,
): ArrayList<T>? {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
this?.getParcelableArrayListExtra(key, clazz)
} else {
this?.getParcelableArrayListExtra(key)
}
}

fun <T : Parcelable> Intent?.getParcelableExtraCompat(key: String, clazz: Class<T>): T? {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
this?.getParcelableExtra(key, clazz)
} else {
this?.getParcelableExtra(key)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import android.os.Build
import android.os.Bundle
import android.view.View.INVISIBLE
import android.view.View.VISIBLE
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity
import androidx.viewbinding.ViewBinding
import com.philkes.notallyx.NotallyXApplication
Expand All @@ -18,6 +20,8 @@ import com.philkes.notallyx.utils.security.showBiometricOrPinPrompt
abstract class LockedActivity<T : ViewBinding> : AppCompatActivity() {

private lateinit var notallyXApplication: NotallyXApplication
private lateinit var biometricAuthenticationActivityResultLauncher:
ActivityResultLauncher<Intent>

protected lateinit var binding: T
protected lateinit var preferences: Preferences
Expand All @@ -26,6 +30,16 @@ abstract class LockedActivity<T : ViewBinding> : AppCompatActivity() {
super.onCreate(savedInstanceState)
notallyXApplication = (application as NotallyXApplication)
preferences = Preferences.getInstance(application)

biometricAuthenticationActivityResultLauncher =
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
if (result.resultCode == Activity.RESULT_OK) {
notallyXApplication.isLocked = false
show()
} else {
finish()
}
}
}

override fun onResume() {
Expand All @@ -47,23 +61,11 @@ abstract class LockedActivity<T : ViewBinding> : AppCompatActivity() {
}
}

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == REQUEST_BIOMETRIC_AUTHENTICATION) {
if (resultCode == Activity.RESULT_OK) {
notallyXApplication.isLocked = false
show()
} else {
finish()
}
}
}

open fun showLockScreen() {
showBiometricOrPinPrompt(
true,
preferences.iv!!,
REQUEST_BIOMETRIC_AUTHENTICATION,
biometricAuthenticationActivityResultLauncher,
R.string.unlock,
onSuccess = {
notallyXApplication.isLocked = false
Expand Down Expand Up @@ -91,8 +93,4 @@ abstract class LockedActivity<T : ViewBinding> : AppCompatActivity() {
false
}
}

companion object {
private const val REQUEST_BIOMETRIC_AUTHENTICATION = 11
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package com.philkes.notallyx.presentation.activity.main

import android.app.Activity
import android.content.Intent
import android.content.pm.PackageManager
import android.net.Uri
Expand All @@ -12,6 +11,9 @@ import android.view.MenuItem
import android.view.View
import android.view.inputmethod.InputMethodManager
import android.widget.Toast
import androidx.activity.OnBackPressedCallback
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts
import androidx.activity.viewModels
import androidx.core.content.FileProvider
import androidx.core.view.GravityCompat
Expand Down Expand Up @@ -55,14 +57,15 @@ class MainActivity : LockedActivity<ActivityMainBinding>() {

private lateinit var navController: NavController
private lateinit var configuration: AppBarConfiguration
private lateinit var exportFileActivityResultLauncher: ActivityResultLauncher<Intent>

private val model: BaseNoteModel by viewModels()

override fun onBackPressed() {
if (model.actionMode.enabled.value) {
model.actionMode.close(true)
} else super.onBackPressed()
}
private val actionModeCancelCallback =
object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
model.actionMode.close(true)
}
}

override fun onSupportNavigateUp(): Boolean {
return navController.navigateUp(configuration)
Expand All @@ -78,13 +81,9 @@ class MainActivity : LockedActivity<ActivityMainBinding>() {
setupActionMode()
setupNavigation()
setupSearch()
}

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == REQUEST_EXPORT_FILE && resultCode == Activity.RESULT_OK) {
data?.data?.let { uri -> model.writeCurrentFileToUri(uri) }
}
setupActivityResultLaunchers()
onBackPressedDispatcher.addCallback(this, actionModeCancelCallback)
}

private fun setupFAB() {
Expand Down Expand Up @@ -141,6 +140,7 @@ class MainActivity : LockedActivity<ActivityMainBinding>() {
binding.ActionMode.visibility = View.GONE
binding.DrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNDEFINED)
}
actionModeCancelCallback.isEnabled = enabled
}

val menu = binding.ActionMode.menu
Expand Down Expand Up @@ -387,9 +387,8 @@ class MainActivity : LockedActivity<ActivityMainBinding>() {
addCategory(Intent.CATEGORY_OPENABLE)
putExtra(Intent.EXTRA_TITLE, file.nameWithoutExtension)
}

model.currentFile = file
startActivityForResult(intent, REQUEST_EXPORT_FILE)
exportFileActivityResultLauncher.launch(intent)
}

private fun setupNavigation() {
Expand Down Expand Up @@ -472,7 +471,12 @@ class MainActivity : LockedActivity<ActivityMainBinding>() {
}
}

companion object {
private const val REQUEST_EXPORT_FILE = 10
private fun setupActivityResultLaunchers() {
exportFileActivityResultLauncher =
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
if (result.resultCode == RESULT_OK) {
result.data?.data?.let { uri -> model.writeCurrentFileToUri(uri) }
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts
import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
Expand Down Expand Up @@ -36,6 +38,8 @@ import com.philkes.notallyx.presentation.viewmodel.BaseNoteModel
abstract class NotallyFragment : Fragment(), ListItemListener {

private var notesAdapter: BaseNoteAdapter? = null
private lateinit var openNoteActivityResultLauncher: ActivityResultLauncher<Intent>

internal var binding: FragmentNotesBinding? = null

internal val model: BaseNoteModel by activityViewModels()
Expand All @@ -52,6 +56,35 @@ abstract class NotallyFragment : Fragment(), ListItemListener {
setupAdapter()
setupRecyclerView()
setupObserver()

setupActivityResultLaunchers()
}

private fun setupActivityResultLaunchers() {
openNoteActivityResultLauncher =
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
if (result.resultCode == RESULT_OK) {
// If a note has been moved inside of EditActivity
// present snackbar to undo it
val data = result.data
val id = data?.getLongExtra(NOTE_ID, -1)
if (id != null) {
val folderFrom = Folder.valueOf(data.getStringExtra(FOLDER_FROM)!!)
val folderTo = Folder.valueOf(data.getStringExtra(FOLDER_TO)!!)
Snackbar.make(
binding!!.root,
requireContext().getQuantityString(folderTo.movedToResId(), 1),
Snackbar.LENGTH_SHORT,
)
.apply {
setAction(R.string.undo) {
model.moveBaseNotes(longArrayOf(id), folderFrom)
}
}
.show()
}
}
}
}

override fun onCreateView(
Expand Down Expand Up @@ -92,32 +125,6 @@ abstract class NotallyFragment : Fragment(), ListItemListener {
}
}

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (resultCode == RESULT_OK) {
if (requestCode == REQUEST_NOTE_EDIT) {
// If a note has been moved inside of EditActivity
// present snackbar to undo it
val id = data?.getLongExtra(NOTE_ID, -1)
if (id != null) {
val folderFrom = Folder.valueOf(data.getStringExtra(FOLDER_FROM)!!)
val folderTo = Folder.valueOf(data.getStringExtra(FOLDER_TO)!!)
Snackbar.make(
binding!!.root,
requireContext().getQuantityString(folderTo.movedToResId(), 1),
Snackbar.LENGTH_SHORT,
)
.apply {
setAction(R.string.undo) {
model.moveBaseNotes(longArrayOf(id), folderFrom)
}
}
.show()
}
}
}
}

private fun handleNoteSelection(id: Long, position: Int, baseNote: BaseNote) {
if (model.actionMode.selectedNotes.contains(id)) {
model.actionMode.remove(id)
Expand Down Expand Up @@ -188,14 +195,10 @@ abstract class NotallyFragment : Fragment(), ListItemListener {
private fun goToActivity(activity: Class<*>, baseNote: BaseNote) {
val intent = Intent(requireContext(), activity)
intent.putExtra(Constants.SelectedBaseNote, baseNote.id)
startActivityForResult(intent, REQUEST_NOTE_EDIT)
openNoteActivityResultLauncher.launch(intent)
}

abstract fun getBackground(): Int

abstract fun getObservable(): LiveData<List<Item>>

companion object {
private const val REQUEST_NOTE_EDIT = 11
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ class SearchFragment : NotallyFragment() {
}

binding?.ChipGroup?.apply {
setOnCheckedChangeListener { _, checkedId ->
when (checkedId) {
setOnCheckedStateChangeListener { _, checkedId ->
when (checkedId.first()) {
R.id.Notes -> model.folder = Folder.NOTES
R.id.Deleted -> model.folder = Folder.DELETED
R.id.Archived -> model.folder = Folder.ARCHIVED
Expand Down
Loading