mirror of
https://github.com/PhilKes/NotallyX.git
synced 2025-06-29 04:39:54 +00:00
Refactor Preferences from objects to Enums
This commit is contained in:
parent
87279902ca
commit
e38e14bdd4
43 changed files with 925 additions and 927 deletions
|
@ -105,6 +105,7 @@ dependencies {
|
||||||
implementation 'net.lingala.zip4j:zip4j:2.11.5'
|
implementation 'net.lingala.zip4j:zip4j:2.11.5'
|
||||||
|
|
||||||
implementation "androidx.work:work-runtime:2.9.1"
|
implementation "androidx.work:work-runtime:2.9.1"
|
||||||
|
implementation "androidx.preference:preference-ktx:1.2.1"
|
||||||
|
|
||||||
//noinspection GradleDependency
|
//noinspection GradleDependency
|
||||||
implementation "androidx.navigation:navigation-ui-ktx:$navVersion"
|
implementation "androidx.navigation:navigation-ui-ktx:$navVersion"
|
||||||
|
|
|
@ -5,32 +5,33 @@ import android.content.Intent
|
||||||
import android.content.IntentFilter
|
import android.content.IntentFilter
|
||||||
import androidx.appcompat.app.AppCompatDelegate
|
import androidx.appcompat.app.AppCompatDelegate
|
||||||
import androidx.lifecycle.Observer
|
import androidx.lifecycle.Observer
|
||||||
import com.philkes.notallyx.presentation.view.misc.BetterLiveData
|
import com.philkes.notallyx.presentation.view.misc.NotNullLiveData
|
||||||
import com.philkes.notallyx.presentation.view.misc.BiometricLock.enabled
|
import com.philkes.notallyx.presentation.viewmodel.preference.BiometricLock
|
||||||
import com.philkes.notallyx.presentation.view.misc.Theme
|
import com.philkes.notallyx.presentation.viewmodel.preference.NotallyXPreferences
|
||||||
|
import com.philkes.notallyx.presentation.viewmodel.preference.Theme
|
||||||
import com.philkes.notallyx.presentation.widget.WidgetProvider
|
import com.philkes.notallyx.presentation.widget.WidgetProvider
|
||||||
import com.philkes.notallyx.utils.backup.Export.scheduleAutoBackup
|
import com.philkes.notallyx.utils.backup.Export.scheduleAutoBackup
|
||||||
import com.philkes.notallyx.utils.security.UnlockReceiver
|
import com.philkes.notallyx.utils.security.UnlockReceiver
|
||||||
|
|
||||||
class NotallyXApplication : Application() {
|
class NotallyXApplication : Application() {
|
||||||
|
|
||||||
private lateinit var biometricLockObserver: Observer<String>
|
private lateinit var biometricLockObserver: Observer<BiometricLock>
|
||||||
private lateinit var preferences: Preferences
|
private lateinit var preferences: NotallyXPreferences
|
||||||
private var unlockReceiver: UnlockReceiver? = null
|
private var unlockReceiver: UnlockReceiver? = null
|
||||||
|
|
||||||
val locked = BetterLiveData(true)
|
val locked = NotNullLiveData(true)
|
||||||
|
|
||||||
override fun onCreate() {
|
override fun onCreate() {
|
||||||
super.onCreate()
|
super.onCreate()
|
||||||
|
|
||||||
preferences = Preferences.getInstance(this)
|
preferences = NotallyXPreferences.getInstance(this)
|
||||||
preferences.theme.observeForever { theme ->
|
preferences.theme.observeForever { theme ->
|
||||||
when (theme) {
|
when (theme) {
|
||||||
Theme.dark ->
|
Theme.DARK ->
|
||||||
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES)
|
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES)
|
||||||
Theme.light ->
|
Theme.LIGHT ->
|
||||||
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO)
|
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO)
|
||||||
Theme.followSystem ->
|
Theme.FOLLOW_SYSTEM ->
|
||||||
AppCompatDelegate.setDefaultNightMode(
|
AppCompatDelegate.setDefaultNightMode(
|
||||||
AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM
|
AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM
|
||||||
)
|
)
|
||||||
|
@ -41,7 +42,7 @@ class NotallyXApplication : Application() {
|
||||||
|
|
||||||
val filter = IntentFilter().apply { addAction(Intent.ACTION_SCREEN_OFF) }
|
val filter = IntentFilter().apply { addAction(Intent.ACTION_SCREEN_OFF) }
|
||||||
biometricLockObserver = Observer {
|
biometricLockObserver = Observer {
|
||||||
if (it == enabled) {
|
if (it == BiometricLock.ENABLED) {
|
||||||
unlockReceiver = UnlockReceiver(this)
|
unlockReceiver = UnlockReceiver(this)
|
||||||
registerReceiver(unlockReceiver, filter)
|
registerReceiver(unlockReceiver, filter)
|
||||||
} else if (unlockReceiver != null) {
|
} else if (unlockReceiver != null) {
|
||||||
|
|
|
@ -1,228 +0,0 @@
|
||||||
package com.philkes.notallyx
|
|
||||||
|
|
||||||
import android.app.Application
|
|
||||||
import android.os.Build
|
|
||||||
import android.preference.PreferenceManager
|
|
||||||
import androidx.security.crypto.EncryptedSharedPreferences
|
|
||||||
import androidx.security.crypto.MasterKey
|
|
||||||
import com.philkes.notallyx.data.model.Type
|
|
||||||
import com.philkes.notallyx.data.model.toPreservedByteArray
|
|
||||||
import com.philkes.notallyx.data.model.toPreservedString
|
|
||||||
import com.philkes.notallyx.presentation.view.misc.AutoBackup
|
|
||||||
import com.philkes.notallyx.presentation.view.misc.AutoBackupMax
|
|
||||||
import com.philkes.notallyx.presentation.view.misc.AutoBackupPeriodDays
|
|
||||||
import com.philkes.notallyx.presentation.view.misc.BackupPassword
|
|
||||||
import com.philkes.notallyx.presentation.view.misc.BetterLiveData
|
|
||||||
import com.philkes.notallyx.presentation.view.misc.BiometricLock
|
|
||||||
import com.philkes.notallyx.presentation.view.misc.DateFormat
|
|
||||||
import com.philkes.notallyx.presentation.view.misc.ListInfo
|
|
||||||
import com.philkes.notallyx.presentation.view.misc.ListItemSorting
|
|
||||||
import com.philkes.notallyx.presentation.view.misc.MaxItems
|
|
||||||
import com.philkes.notallyx.presentation.view.misc.MaxLines
|
|
||||||
import com.philkes.notallyx.presentation.view.misc.MaxTitle
|
|
||||||
import com.philkes.notallyx.presentation.view.misc.NotesSorting
|
|
||||||
import com.philkes.notallyx.presentation.view.misc.SeekbarInfo
|
|
||||||
import com.philkes.notallyx.presentation.view.misc.SortDirection
|
|
||||||
import com.philkes.notallyx.presentation.view.misc.TextInfo
|
|
||||||
import com.philkes.notallyx.presentation.view.misc.TextSize
|
|
||||||
import com.philkes.notallyx.presentation.view.misc.Theme
|
|
||||||
import com.philkes.notallyx.presentation.view.misc.View
|
|
||||||
import java.security.SecureRandom
|
|
||||||
import javax.crypto.Cipher
|
|
||||||
|
|
||||||
private const val DATABASE_ENCRYPTION_KEY = "database_encryption_key"
|
|
||||||
|
|
||||||
private const val ENCRYPTION_IV = "encryption_iv"
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Custom implementation of androidx.preference library Way faster, simpler and smaller, logic of
|
|
||||||
* storing preferences has been decoupled from their UI. It is backed by SharedPreferences but it
|
|
||||||
* should be trivial to shift to another source if needed.
|
|
||||||
*/
|
|
||||||
class Preferences private constructor(app: Application) {
|
|
||||||
|
|
||||||
private val preferences = PreferenceManager.getDefaultSharedPreferences(app)
|
|
||||||
private val editor = preferences.edit()
|
|
||||||
|
|
||||||
private val encryptedPreferences by lazy {
|
|
||||||
EncryptedSharedPreferences.create(
|
|
||||||
app,
|
|
||||||
"secret_shared_prefs",
|
|
||||||
MasterKey.Builder(app).setKeyScheme(MasterKey.KeyScheme.AES256_GCM).build(),
|
|
||||||
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
|
|
||||||
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Main thread (unfortunately)
|
|
||||||
val view = BetterLiveData(getListPref(View))
|
|
||||||
val theme = BetterLiveData(getListPref(Theme))
|
|
||||||
val dateFormat = BetterLiveData(getListPref(DateFormat))
|
|
||||||
|
|
||||||
val notesSorting = BetterLiveData(getNotesSorting(NotesSorting))
|
|
||||||
|
|
||||||
val textSize = BetterLiveData(getListPref(TextSize))
|
|
||||||
val listItemSorting = BetterLiveData(getListPref(ListItemSorting))
|
|
||||||
var maxItems = getSeekbarPref(MaxItems)
|
|
||||||
var maxLines = getSeekbarPref(MaxLines)
|
|
||||||
var maxTitle = getSeekbarPref(MaxTitle)
|
|
||||||
|
|
||||||
val autoBackupPath = BetterLiveData(getTextPref(AutoBackup))
|
|
||||||
var autoBackupPeriodDays = BetterLiveData(getSeekbarPref(AutoBackupPeriodDays))
|
|
||||||
var autoBackupMax = getSeekbarPref(AutoBackupMax)
|
|
||||||
val backupPassword by lazy { BetterLiveData(getEncryptedTextPref(BackupPassword)) }
|
|
||||||
|
|
||||||
val biometricLock = BetterLiveData(getListPref(BiometricLock))
|
|
||||||
var iv: ByteArray?
|
|
||||||
get() = preferences.getString(ENCRYPTION_IV, null)?.toPreservedByteArray
|
|
||||||
set(value) {
|
|
||||||
editor.putString(ENCRYPTION_IV, value?.toPreservedString)
|
|
||||||
editor.commit()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getDatabasePassphrase(): ByteArray {
|
|
||||||
val string = preferences.getString(DATABASE_ENCRYPTION_KEY, "")!!
|
|
||||||
return string.toPreservedByteArray
|
|
||||||
}
|
|
||||||
|
|
||||||
fun generatePassphrase(cipher: Cipher): ByteArray {
|
|
||||||
val random =
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
|
||||||
SecureRandom.getInstanceStrong()
|
|
||||||
} else {
|
|
||||||
SecureRandom()
|
|
||||||
}
|
|
||||||
val result = ByteArray(64)
|
|
||||||
|
|
||||||
random.nextBytes(result)
|
|
||||||
|
|
||||||
// filter out zero byte values, as SQLCipher does not like them
|
|
||||||
while (result.contains(0)) {
|
|
||||||
random.nextBytes(result)
|
|
||||||
}
|
|
||||||
|
|
||||||
val encryptedPassphrase = cipher.doFinal(result)
|
|
||||||
editor.putString(DATABASE_ENCRYPTION_KEY, encryptedPassphrase.toPreservedString)
|
|
||||||
editor.commit()
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getListPref(info: ListInfo) =
|
|
||||||
requireNotNull(preferences.getString(info.key, info.defaultValue))
|
|
||||||
|
|
||||||
private fun getNotesSorting(info: NotesSorting): Pair<String, SortDirection> {
|
|
||||||
val sortBy = requireNotNull(preferences.getString(info.key, info.defaultValue))
|
|
||||||
val sortDirection =
|
|
||||||
requireNotNull(preferences.getString(info.directionKey, info.defaultValueDirection))
|
|
||||||
return Pair(sortBy, SortDirection.valueOf(sortDirection))
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getTextPref(info: TextInfo) =
|
|
||||||
requireNotNull(preferences.getString(info.key, info.defaultValue))
|
|
||||||
|
|
||||||
private fun getEncryptedTextPref(info: TextInfo) =
|
|
||||||
requireNotNull(encryptedPreferences!!.getString(info.key, info.defaultValue))
|
|
||||||
|
|
||||||
private fun getSeekbarPref(info: SeekbarInfo) =
|
|
||||||
requireNotNull(preferences.getInt(info.key, info.defaultValue))
|
|
||||||
|
|
||||||
fun getWidgetData(id: Int) = preferences.getLong("widget:$id", 0)
|
|
||||||
|
|
||||||
fun getWidgetNoteType(id: Int) =
|
|
||||||
preferences.getString("widgetNoteType:$id", null)?.let { Type.valueOf(it) }
|
|
||||||
|
|
||||||
fun deleteWidget(id: Int) {
|
|
||||||
editor.remove("widget:$id")
|
|
||||||
editor.remove("widgetNoteType:$id")
|
|
||||||
editor.commit()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun updateWidget(id: Int, noteId: Long, noteType: Type) {
|
|
||||||
editor.putLong("widget:$id", noteId)
|
|
||||||
editor.putString("widgetNoteType:$id", noteType.name)
|
|
||||||
editor.commit()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getUpdatableWidgets(noteIds: LongArray? = null): List<Pair<Int, Long>> {
|
|
||||||
val updatableWidgets = ArrayList<Pair<Int, Long>>()
|
|
||||||
val pairs = preferences.all
|
|
||||||
pairs.keys.forEach { key ->
|
|
||||||
val token = "widget:"
|
|
||||||
if (key.startsWith(token)) {
|
|
||||||
val end = key.substringAfter(token)
|
|
||||||
val id = end.toIntOrNull()
|
|
||||||
if (id != null) {
|
|
||||||
val value = pairs[key] as? Long
|
|
||||||
if (value != null) {
|
|
||||||
if (noteIds == null || noteIds.contains(value)) {
|
|
||||||
updatableWidgets.add(Pair(id, value))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return updatableWidgets
|
|
||||||
}
|
|
||||||
|
|
||||||
fun savePreference(info: SeekbarInfo, value: Int) {
|
|
||||||
editor.putInt(info.key, value)
|
|
||||||
editor.commit()
|
|
||||||
when (info) {
|
|
||||||
MaxItems -> maxItems = getSeekbarPref(MaxItems)
|
|
||||||
MaxLines -> maxLines = getSeekbarPref(MaxLines)
|
|
||||||
MaxTitle -> maxTitle = getSeekbarPref(MaxTitle)
|
|
||||||
AutoBackupMax -> autoBackupMax = getSeekbarPref(AutoBackupMax)
|
|
||||||
AutoBackupPeriodDays ->
|
|
||||||
autoBackupPeriodDays.postValue(getSeekbarPref(AutoBackupPeriodDays))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun savePreference(info: NotesSorting, sortBy: String, sortDirection: SortDirection) {
|
|
||||||
editor.putString(info.key, sortBy)
|
|
||||||
editor.putString(info.directionKey, sortDirection.name)
|
|
||||||
editor.commit()
|
|
||||||
notesSorting.postValue(getNotesSorting(info))
|
|
||||||
}
|
|
||||||
|
|
||||||
fun savePreference(info: ListInfo, value: String) {
|
|
||||||
editor.putString(info.key, value)
|
|
||||||
editor.commit()
|
|
||||||
when (info) {
|
|
||||||
View -> view.postValue(getListPref(info))
|
|
||||||
Theme -> theme.postValue(getListPref(info))
|
|
||||||
DateFormat -> dateFormat.postValue(getListPref(info))
|
|
||||||
TextSize -> textSize.postValue(getListPref(info))
|
|
||||||
ListItemSorting -> listItemSorting.postValue(getListPref(info))
|
|
||||||
BiometricLock -> biometricLock.postValue(getListPref(info))
|
|
||||||
else -> return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun savePreference(info: TextInfo, value: String) {
|
|
||||||
val editor = if (info is BackupPassword) encryptedPreferences!!.edit() else this.editor
|
|
||||||
editor.putString(info.key, value)
|
|
||||||
editor.commit()
|
|
||||||
when (info) {
|
|
||||||
AutoBackup -> autoBackupPath.postValue(getTextPref(info))
|
|
||||||
BackupPassword -> backupPassword.postValue(getEncryptedTextPref(info))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun showDateCreated(): Boolean {
|
|
||||||
return dateFormat.value != DateFormat.none
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
|
|
||||||
@Volatile private var instance: Preferences? = null
|
|
||||||
|
|
||||||
fun getInstance(app: Application): Preferences {
|
|
||||||
return instance
|
|
||||||
?: synchronized(this) {
|
|
||||||
val instance = Preferences(app)
|
|
||||||
Companion.instance = instance
|
|
||||||
return instance
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -10,16 +10,16 @@ import androidx.room.TypeConverters
|
||||||
import androidx.room.migration.Migration
|
import androidx.room.migration.Migration
|
||||||
import androidx.sqlite.db.SimpleSQLiteQuery
|
import androidx.sqlite.db.SimpleSQLiteQuery
|
||||||
import androidx.sqlite.db.SupportSQLiteDatabase
|
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||||
import com.philkes.notallyx.Preferences
|
|
||||||
import com.philkes.notallyx.data.dao.BaseNoteDao
|
import com.philkes.notallyx.data.dao.BaseNoteDao
|
||||||
import com.philkes.notallyx.data.dao.CommonDao
|
import com.philkes.notallyx.data.dao.CommonDao
|
||||||
import com.philkes.notallyx.data.dao.LabelDao
|
import com.philkes.notallyx.data.dao.LabelDao
|
||||||
import com.philkes.notallyx.data.model.BaseNote
|
import com.philkes.notallyx.data.model.BaseNote
|
||||||
import com.philkes.notallyx.data.model.Converters
|
import com.philkes.notallyx.data.model.Converters
|
||||||
import com.philkes.notallyx.data.model.Label
|
import com.philkes.notallyx.data.model.Label
|
||||||
import com.philkes.notallyx.presentation.observeForeverSkipFirst
|
import com.philkes.notallyx.presentation.view.misc.NotNullLiveData
|
||||||
import com.philkes.notallyx.presentation.view.misc.BetterLiveData
|
import com.philkes.notallyx.presentation.viewmodel.preference.BiometricLock
|
||||||
import com.philkes.notallyx.presentation.view.misc.BiometricLock.enabled
|
import com.philkes.notallyx.presentation.viewmodel.preference.NotallyXPreferences
|
||||||
|
import com.philkes.notallyx.presentation.viewmodel.preference.observeForeverSkipFirst
|
||||||
import com.philkes.notallyx.utils.security.getInitializedCipherForDecryption
|
import com.philkes.notallyx.utils.security.getInitializedCipherForDecryption
|
||||||
import net.sqlcipher.database.SupportFactory
|
import net.sqlcipher.database.SupportFactory
|
||||||
|
|
||||||
|
@ -37,20 +37,20 @@ abstract class NotallyDatabase : RoomDatabase() {
|
||||||
getBaseNoteDao().query(SimpleSQLiteQuery("pragma wal_checkpoint(FULL)"))
|
getBaseNoteDao().query(SimpleSQLiteQuery("pragma wal_checkpoint(FULL)"))
|
||||||
}
|
}
|
||||||
|
|
||||||
private var observer: Observer<String>? = null
|
private var biometricLockObserver: Observer<BiometricLock>? = null
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
const val DatabaseName = "NotallyDatabase"
|
const val DatabaseName = "NotallyDatabase"
|
||||||
|
|
||||||
@Volatile private var instance: BetterLiveData<NotallyDatabase>? = null
|
@Volatile private var instance: NotNullLiveData<NotallyDatabase>? = null
|
||||||
|
|
||||||
fun getDatabase(app: Application): BetterLiveData<NotallyDatabase> {
|
fun getDatabase(app: Application): NotNullLiveData<NotallyDatabase> {
|
||||||
return instance
|
return instance
|
||||||
?: synchronized(this) {
|
?: synchronized(this) {
|
||||||
val preferences = Preferences.getInstance(app)
|
val preferences = NotallyXPreferences.getInstance(app)
|
||||||
this.instance =
|
this.instance =
|
||||||
BetterLiveData(
|
NotNullLiveData(
|
||||||
createInstance(app, preferences, preferences.biometricLock.value)
|
createInstance(app, preferences, preferences.biometricLock.value)
|
||||||
)
|
)
|
||||||
return this.instance!!
|
return this.instance!!
|
||||||
|
@ -59,31 +59,33 @@ abstract class NotallyDatabase : RoomDatabase() {
|
||||||
|
|
||||||
private fun createInstance(
|
private fun createInstance(
|
||||||
app: Application,
|
app: Application,
|
||||||
preferences: Preferences,
|
preferences: NotallyXPreferences,
|
||||||
biometrickLock: String,
|
biometrickLock: BiometricLock,
|
||||||
): NotallyDatabase {
|
): NotallyDatabase {
|
||||||
val instanceBuilder =
|
val instanceBuilder =
|
||||||
Room.databaseBuilder(app, NotallyDatabase::class.java, DatabaseName)
|
Room.databaseBuilder(app, NotallyDatabase::class.java, DatabaseName)
|
||||||
.addMigrations(Migration2, Migration3, Migration4, Migration5, Migration6)
|
.addMigrations(Migration2, Migration3, Migration4, Migration5, Migration6)
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||||
if (biometrickLock == enabled) {
|
if (biometrickLock == BiometricLock.ENABLED) {
|
||||||
val initializationVector = preferences.iv!!
|
val initializationVector = preferences.iv.value!!
|
||||||
val cipher = getInitializedCipherForDecryption(iv = initializationVector)
|
val cipher = getInitializedCipherForDecryption(iv = initializationVector)
|
||||||
val encryptedPassphrase = preferences.getDatabasePassphrase()
|
val encryptedPassphrase = preferences.databaseEncryptionKey.value
|
||||||
val passphrase = cipher.doFinal(encryptedPassphrase)
|
val passphrase = cipher.doFinal(encryptedPassphrase)
|
||||||
val factory = SupportFactory(passphrase)
|
val factory = SupportFactory(passphrase)
|
||||||
instanceBuilder.openHelperFactory(factory)
|
instanceBuilder.openHelperFactory(factory)
|
||||||
}
|
}
|
||||||
val instance = instanceBuilder.build()
|
val instance = instanceBuilder.build()
|
||||||
instance.observer = Observer { newBiometrickLock ->
|
instance.biometricLockObserver = Observer { newBiometrickLock ->
|
||||||
NotallyDatabase.instance?.value?.observer?.let {
|
NotallyDatabase.instance?.value?.biometricLockObserver?.let {
|
||||||
preferences.biometricLock.removeObserver(it)
|
preferences.biometricLock.removeObserver(it)
|
||||||
}
|
}
|
||||||
val newInstance = createInstance(app, preferences, newBiometrickLock)
|
val newInstance = createInstance(app, preferences, newBiometrickLock)
|
||||||
NotallyDatabase.instance?.postValue(newInstance)
|
NotallyDatabase.instance?.postValue(newInstance)
|
||||||
preferences.biometricLock.observeForeverSkipFirst(newInstance.observer!!)
|
preferences.biometricLock.observeForeverSkipFirst(
|
||||||
|
newInstance.biometricLockObserver!!
|
||||||
|
)
|
||||||
}
|
}
|
||||||
preferences.biometricLock.observeForeverSkipFirst(instance.observer!!)
|
preferences.biometricLock.observeForeverSkipFirst(instance.biometricLockObserver!!)
|
||||||
return instance
|
return instance
|
||||||
}
|
}
|
||||||
return instanceBuilder.build()
|
return instanceBuilder.build()
|
||||||
|
|
|
@ -46,9 +46,7 @@ import androidx.appcompat.app.AppCompatActivity.INPUT_METHOD_SERVICE
|
||||||
import androidx.appcompat.app.AppCompatActivity.KEYGUARD_SERVICE
|
import androidx.appcompat.app.AppCompatActivity.KEYGUARD_SERVICE
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.lifecycle.LifecycleOwner
|
import androidx.lifecycle.LifecycleOwner
|
||||||
import androidx.lifecycle.LiveData
|
|
||||||
import androidx.lifecycle.MutableLiveData
|
import androidx.lifecycle.MutableLiveData
|
||||||
import androidx.lifecycle.Observer
|
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
import com.philkes.notallyx.R
|
import com.philkes.notallyx.R
|
||||||
import com.philkes.notallyx.data.imports.ImportProgress
|
import com.philkes.notallyx.data.imports.ImportProgress
|
||||||
|
@ -57,10 +55,10 @@ import com.philkes.notallyx.data.model.Folder
|
||||||
import com.philkes.notallyx.data.model.SpanRepresentation
|
import com.philkes.notallyx.data.model.SpanRepresentation
|
||||||
import com.philkes.notallyx.data.model.getUrl
|
import com.philkes.notallyx.data.model.getUrl
|
||||||
import com.philkes.notallyx.databinding.DialogProgressBinding
|
import com.philkes.notallyx.databinding.DialogProgressBinding
|
||||||
import com.philkes.notallyx.presentation.view.misc.DateFormat
|
|
||||||
import com.philkes.notallyx.presentation.view.misc.EditTextWithHistory
|
import com.philkes.notallyx.presentation.view.misc.EditTextWithHistory
|
||||||
import com.philkes.notallyx.presentation.view.misc.Progress
|
import com.philkes.notallyx.presentation.view.misc.Progress
|
||||||
import com.philkes.notallyx.presentation.view.note.listitem.ListManager
|
import com.philkes.notallyx.presentation.view.note.listitem.ListManager
|
||||||
|
import com.philkes.notallyx.presentation.viewmodel.preference.DateFormat
|
||||||
import com.philkes.notallyx.utils.changehistory.ChangeHistory
|
import com.philkes.notallyx.utils.changehistory.ChangeHistory
|
||||||
import com.philkes.notallyx.utils.changehistory.EditTextWithHistoryChange
|
import com.philkes.notallyx.utils.changehistory.EditTextWithHistoryChange
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
@ -198,10 +196,10 @@ fun Menu.add(
|
||||||
|
|
||||||
fun TextView.displayFormattedTimestamp(
|
fun TextView.displayFormattedTimestamp(
|
||||||
timestamp: Long?,
|
timestamp: Long?,
|
||||||
dateFormat: String,
|
dateFormat: DateFormat,
|
||||||
prefixResId: Int? = null,
|
prefixResId: Int? = null,
|
||||||
) {
|
) {
|
||||||
if (dateFormat != DateFormat.none && timestamp != null) {
|
if (dateFormat != DateFormat.NONE && timestamp != null) {
|
||||||
visibility = View.VISIBLE
|
visibility = View.VISIBLE
|
||||||
text =
|
text =
|
||||||
"${prefixResId?.let { getString(it) } ?: ""} ${formatTimestamp(timestamp, dateFormat)}"
|
"${prefixResId?.let { getString(it) } ?: ""} ${formatTimestamp(timestamp, dateFormat)}"
|
||||||
|
@ -324,22 +322,6 @@ fun Context.canAuthenticateWithBiometrics(): Int {
|
||||||
return BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE
|
return BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE
|
||||||
}
|
}
|
||||||
|
|
||||||
fun <T> LiveData<T>.observeForeverSkipFirst(observer: Observer<T>) {
|
|
||||||
var isFirstEvent = true
|
|
||||||
this.observeForever { value ->
|
|
||||||
if (isFirstEvent) {
|
|
||||||
isFirstEvent = false
|
|
||||||
} else {
|
|
||||||
observer.onChanged(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun String.fileNameWithoutExtension(): String {
|
|
||||||
return this.substringAfterLast("/") // Remove the path
|
|
||||||
.substringBeforeLast(".") // Remove the extension
|
|
||||||
}
|
|
||||||
|
|
||||||
fun Context.getFileName(uri: Uri): String? =
|
fun Context.getFileName(uri: Uri): String? =
|
||||||
when (uri.scheme) {
|
when (uri.scheme) {
|
||||||
ContentResolver.SCHEME_CONTENT -> getContentFileName(uri)
|
ContentResolver.SCHEME_CONTENT -> getContentFileName(uri)
|
||||||
|
@ -458,10 +440,10 @@ fun Activity.checkNotificationPermission(
|
||||||
} else onSuccess()
|
} else onSuccess()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun formatTimestamp(timestamp: Long, dateFormat: String): String {
|
private fun formatTimestamp(timestamp: Long, dateFormat: DateFormat): String {
|
||||||
val date = Date(timestamp)
|
val date = Date(timestamp)
|
||||||
return when (dateFormat) {
|
return when (dateFormat) {
|
||||||
DateFormat.relative -> PrettyTime().format(date)
|
DateFormat.RELATIVE -> PrettyTime().format(date)
|
||||||
else -> java.text.DateFormat.getDateInstance(java.text.DateFormat.FULL).format(date)
|
else -> java.text.DateFormat.getDateInstance(java.text.DateFormat.FULL).format(date)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,9 +3,9 @@ package com.philkes.notallyx.presentation.activity
|
||||||
import android.appwidget.AppWidgetManager
|
import android.appwidget.AppWidgetManager
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import com.philkes.notallyx.Preferences
|
|
||||||
import com.philkes.notallyx.data.model.BaseNote
|
import com.philkes.notallyx.data.model.BaseNote
|
||||||
import com.philkes.notallyx.presentation.activity.note.PickNoteActivity
|
import com.philkes.notallyx.presentation.activity.note.PickNoteActivity
|
||||||
|
import com.philkes.notallyx.presentation.viewmodel.preference.NotallyXPreferences
|
||||||
import com.philkes.notallyx.presentation.widget.WidgetProvider
|
import com.philkes.notallyx.presentation.widget.WidgetProvider
|
||||||
|
|
||||||
class ConfigureWidgetActivity : PickNoteActivity() {
|
class ConfigureWidgetActivity : PickNoteActivity() {
|
||||||
|
@ -27,7 +27,7 @@ class ConfigureWidgetActivity : PickNoteActivity() {
|
||||||
|
|
||||||
override fun onClick(position: Int) {
|
override fun onClick(position: Int) {
|
||||||
if (position != -1) {
|
if (position != -1) {
|
||||||
val preferences = Preferences.getInstance(application)
|
val preferences = NotallyXPreferences.getInstance(application)
|
||||||
val baseNote = adapter.getItem(position) as BaseNote
|
val baseNote = adapter.getItem(position) as BaseNote
|
||||||
preferences.updateWidget(id, baseNote.id, baseNote.type)
|
preferences.updateWidget(id, baseNote.id, baseNote.type)
|
||||||
|
|
||||||
|
|
|
@ -12,9 +12,9 @@ import androidx.activity.result.contract.ActivityResultContracts
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.viewbinding.ViewBinding
|
import androidx.viewbinding.ViewBinding
|
||||||
import com.philkes.notallyx.NotallyXApplication
|
import com.philkes.notallyx.NotallyXApplication
|
||||||
import com.philkes.notallyx.Preferences
|
|
||||||
import com.philkes.notallyx.R
|
import com.philkes.notallyx.R
|
||||||
import com.philkes.notallyx.presentation.view.misc.BiometricLock.enabled
|
import com.philkes.notallyx.presentation.viewmodel.preference.BiometricLock
|
||||||
|
import com.philkes.notallyx.presentation.viewmodel.preference.NotallyXPreferences
|
||||||
import com.philkes.notallyx.utils.security.showBiometricOrPinPrompt
|
import com.philkes.notallyx.utils.security.showBiometricOrPinPrompt
|
||||||
|
|
||||||
abstract class LockedActivity<T : ViewBinding> : AppCompatActivity() {
|
abstract class LockedActivity<T : ViewBinding> : AppCompatActivity() {
|
||||||
|
@ -24,12 +24,12 @@ abstract class LockedActivity<T : ViewBinding> : AppCompatActivity() {
|
||||||
ActivityResultLauncher<Intent>
|
ActivityResultLauncher<Intent>
|
||||||
|
|
||||||
protected lateinit var binding: T
|
protected lateinit var binding: T
|
||||||
protected lateinit var preferences: Preferences
|
protected lateinit var preferences: NotallyXPreferences
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
notallyXApplication = (application as NotallyXApplication)
|
notallyXApplication = (application as NotallyXApplication)
|
||||||
preferences = Preferences.getInstance(application)
|
preferences = NotallyXPreferences.getInstance(application)
|
||||||
|
|
||||||
biometricAuthenticationActivityResultLauncher =
|
biometricAuthenticationActivityResultLauncher =
|
||||||
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
|
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
|
||||||
|
@ -42,7 +42,7 @@ abstract class LockedActivity<T : ViewBinding> : AppCompatActivity() {
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onResume() {
|
override fun onResume() {
|
||||||
if (preferences.biometricLock.value == enabled) {
|
if (preferences.biometricLock.value == BiometricLock.ENABLED) {
|
||||||
if (hasToAuthenticateWithBiometric()) {
|
if (hasToAuthenticateWithBiometric()) {
|
||||||
hide()
|
hide()
|
||||||
showLockScreen()
|
showLockScreen()
|
||||||
|
@ -55,7 +55,7 @@ abstract class LockedActivity<T : ViewBinding> : AppCompatActivity() {
|
||||||
|
|
||||||
override fun onPause() {
|
override fun onPause() {
|
||||||
super.onPause()
|
super.onPause()
|
||||||
if (preferences.biometricLock.value == enabled) {
|
if (preferences.biometricLock.value == BiometricLock.ENABLED) {
|
||||||
hide()
|
hide()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -63,7 +63,7 @@ abstract class LockedActivity<T : ViewBinding> : AppCompatActivity() {
|
||||||
open fun showLockScreen() {
|
open fun showLockScreen() {
|
||||||
showBiometricOrPinPrompt(
|
showBiometricOrPinPrompt(
|
||||||
true,
|
true,
|
||||||
preferences.iv!!,
|
preferences.iv.value!!,
|
||||||
biometricAuthenticationActivityResultLauncher,
|
biometricAuthenticationActivityResultLauncher,
|
||||||
R.string.unlock,
|
R.string.unlock,
|
||||||
onSuccess = { unlock() },
|
onSuccess = { unlock() },
|
||||||
|
|
|
@ -31,9 +31,9 @@ import com.philkes.notallyx.presentation.getQuantityString
|
||||||
import com.philkes.notallyx.presentation.movedToResId
|
import com.philkes.notallyx.presentation.movedToResId
|
||||||
import com.philkes.notallyx.presentation.view.Constants
|
import com.philkes.notallyx.presentation.view.Constants
|
||||||
import com.philkes.notallyx.presentation.view.main.BaseNoteAdapter
|
import com.philkes.notallyx.presentation.view.main.BaseNoteAdapter
|
||||||
import com.philkes.notallyx.presentation.view.misc.View as ViewPref
|
|
||||||
import com.philkes.notallyx.presentation.view.note.listitem.ListItemListener
|
import com.philkes.notallyx.presentation.view.note.listitem.ListItemListener
|
||||||
import com.philkes.notallyx.presentation.viewmodel.BaseNoteModel
|
import com.philkes.notallyx.presentation.viewmodel.BaseNoteModel
|
||||||
|
import com.philkes.notallyx.presentation.viewmodel.preference.NotesView
|
||||||
|
|
||||||
abstract class NotallyFragment : Fragment(), ListItemListener {
|
abstract class NotallyFragment : Fragment(), ListItemListener {
|
||||||
|
|
||||||
|
@ -139,11 +139,11 @@ abstract class NotallyFragment : Fragment(), ListItemListener {
|
||||||
BaseNoteAdapter(
|
BaseNoteAdapter(
|
||||||
model.actionMode.selectedIds,
|
model.actionMode.selectedIds,
|
||||||
dateFormat.value,
|
dateFormat.value,
|
||||||
notesSorting.value.first,
|
notesSorting.value.sortedBy,
|
||||||
textSize.value,
|
textSize.value,
|
||||||
maxItems,
|
maxItems.value,
|
||||||
maxLines,
|
maxLines.value,
|
||||||
maxTitle,
|
maxTitle.value,
|
||||||
model.imageRoot,
|
model.imageRoot,
|
||||||
this@NotallyFragment,
|
this@NotallyFragment,
|
||||||
)
|
)
|
||||||
|
@ -170,8 +170,8 @@ abstract class NotallyFragment : Fragment(), ListItemListener {
|
||||||
binding?.ImageView?.isVisible = list.isEmpty()
|
binding?.ImageView?.isVisible = list.isEmpty()
|
||||||
}
|
}
|
||||||
|
|
||||||
model.preferences.notesSorting.observe(viewLifecycleOwner) { (sortBy, sortDirection) ->
|
model.preferences.notesSorting.observe(viewLifecycleOwner) { notesSort ->
|
||||||
notesAdapter?.setSorting(sortBy, sortDirection)
|
notesAdapter?.setSorting(notesSort)
|
||||||
}
|
}
|
||||||
|
|
||||||
model.actionMode.closeListener.observe(viewLifecycleOwner) { event ->
|
model.actionMode.closeListener.observe(viewLifecycleOwner) { event ->
|
||||||
|
@ -187,7 +187,7 @@ abstract class NotallyFragment : Fragment(), ListItemListener {
|
||||||
|
|
||||||
private fun setupRecyclerView() {
|
private fun setupRecyclerView() {
|
||||||
binding?.RecyclerView?.layoutManager =
|
binding?.RecyclerView?.layoutManager =
|
||||||
if (model.preferences.view.value == ViewPref.grid) {
|
if (model.preferences.notesView.value == NotesView.GRID) {
|
||||||
StaggeredGridLayoutManager(2, RecyclerView.VERTICAL)
|
StaggeredGridLayoutManager(2, RecyclerView.VERTICAL)
|
||||||
} else LinearLayoutManager(requireContext())
|
} else LinearLayoutManager(requireContext())
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,27 +35,20 @@ import com.philkes.notallyx.presentation.canAuthenticateWithBiometrics
|
||||||
import com.philkes.notallyx.presentation.checkedTag
|
import com.philkes.notallyx.presentation.checkedTag
|
||||||
import com.philkes.notallyx.presentation.setupImportProgressDialog
|
import com.philkes.notallyx.presentation.setupImportProgressDialog
|
||||||
import com.philkes.notallyx.presentation.setupProgressDialog
|
import com.philkes.notallyx.presentation.setupProgressDialog
|
||||||
import com.philkes.notallyx.presentation.view.misc.AutoBackup
|
|
||||||
import com.philkes.notallyx.presentation.view.misc.AutoBackupMax
|
|
||||||
import com.philkes.notallyx.presentation.view.misc.AutoBackupPeriodDays
|
|
||||||
import com.philkes.notallyx.presentation.view.misc.BackupPassword
|
|
||||||
import com.philkes.notallyx.presentation.view.misc.BackupPassword.emptyPassword
|
|
||||||
import com.philkes.notallyx.presentation.view.misc.BiometricLock
|
|
||||||
import com.philkes.notallyx.presentation.view.misc.BiometricLock.disabled
|
|
||||||
import com.philkes.notallyx.presentation.view.misc.BiometricLock.enabled
|
|
||||||
import com.philkes.notallyx.presentation.view.misc.DateFormat
|
|
||||||
import com.philkes.notallyx.presentation.view.misc.ListInfo
|
|
||||||
import com.philkes.notallyx.presentation.view.misc.MaxItems
|
|
||||||
import com.philkes.notallyx.presentation.view.misc.MaxLines
|
|
||||||
import com.philkes.notallyx.presentation.view.misc.MaxTitle
|
|
||||||
import com.philkes.notallyx.presentation.view.misc.MenuDialog
|
import com.philkes.notallyx.presentation.view.misc.MenuDialog
|
||||||
import com.philkes.notallyx.presentation.view.misc.NotesSorting
|
|
||||||
import com.philkes.notallyx.presentation.view.misc.SeekbarInfo
|
|
||||||
import com.philkes.notallyx.presentation.view.misc.SortDirection
|
|
||||||
import com.philkes.notallyx.presentation.view.misc.TextSize
|
|
||||||
import com.philkes.notallyx.presentation.view.misc.TextWithIconAdapter
|
import com.philkes.notallyx.presentation.view.misc.TextWithIconAdapter
|
||||||
import com.philkes.notallyx.presentation.view.misc.Theme
|
|
||||||
import com.philkes.notallyx.presentation.viewmodel.BaseNoteModel
|
import com.philkes.notallyx.presentation.viewmodel.BaseNoteModel
|
||||||
|
import com.philkes.notallyx.presentation.viewmodel.preference.BiometricLock
|
||||||
|
import com.philkes.notallyx.presentation.viewmodel.preference.Constants.BACKUP_PATH_EMPTY
|
||||||
|
import com.philkes.notallyx.presentation.viewmodel.preference.Constants.PASSWORD_EMPTY
|
||||||
|
import com.philkes.notallyx.presentation.viewmodel.preference.EnumPreference
|
||||||
|
import com.philkes.notallyx.presentation.viewmodel.preference.IntPreference
|
||||||
|
import com.philkes.notallyx.presentation.viewmodel.preference.NotesSort
|
||||||
|
import com.philkes.notallyx.presentation.viewmodel.preference.NotesSortBy
|
||||||
|
import com.philkes.notallyx.presentation.viewmodel.preference.NotesSortPreference
|
||||||
|
import com.philkes.notallyx.presentation.viewmodel.preference.SortDirection
|
||||||
|
import com.philkes.notallyx.presentation.viewmodel.preference.StringPreference
|
||||||
|
import com.philkes.notallyx.presentation.viewmodel.preference.TextProvider
|
||||||
import com.philkes.notallyx.utils.Operations
|
import com.philkes.notallyx.utils.Operations
|
||||||
import com.philkes.notallyx.utils.backup.Export.scheduleAutoBackup
|
import com.philkes.notallyx.utils.backup.Export.scheduleAutoBackup
|
||||||
import com.philkes.notallyx.utils.security.decryptDatabase
|
import com.philkes.notallyx.utils.security.decryptDatabase
|
||||||
|
@ -76,22 +69,20 @@ class SettingsFragment : Fragment() {
|
||||||
|
|
||||||
private fun setupBinding(binding: FragmentSettingsBinding) {
|
private fun setupBinding(binding: FragmentSettingsBinding) {
|
||||||
model.preferences.apply {
|
model.preferences.apply {
|
||||||
view.observe(viewLifecycleOwner) { value ->
|
notesView.observe(viewLifecycleOwner) { value -> binding.View.setup(notesView, value) }
|
||||||
binding.View.setup(com.philkes.notallyx.presentation.view.misc.View, value)
|
|
||||||
}
|
|
||||||
|
|
||||||
theme.observe(viewLifecycleOwner) { value -> binding.Theme.setup(Theme, value) }
|
theme.observe(viewLifecycleOwner) { value -> binding.Theme.setup(theme, value) }
|
||||||
|
|
||||||
dateFormat.observe(viewLifecycleOwner) { value ->
|
dateFormat.observe(viewLifecycleOwner) { value ->
|
||||||
binding.DateFormat.setup(DateFormat, value)
|
binding.DateFormat.setup(dateFormat, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
textSize.observe(viewLifecycleOwner) { value ->
|
textSize.observe(viewLifecycleOwner) { value ->
|
||||||
binding.TextSize.setup(TextSize, value)
|
binding.TextSize.setup(textSize, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
notesSorting.observe(viewLifecycleOwner) { (sortBy, sortDirection) ->
|
notesSorting.observe(viewLifecycleOwner) { notesSort ->
|
||||||
binding.NotesSortOrder.setup(NotesSorting, sortBy, sortDirection)
|
binding.NotesSortOrder.setup(notesSorting, notesSort)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Hide for now until checked auto-sort is working reliably
|
// TODO: Hide for now until checked auto-sort is working reliably
|
||||||
|
@ -99,29 +90,29 @@ class SettingsFragment : Fragment() {
|
||||||
// binding.CheckedListItemSorting.setup(ListItemSorting, value)
|
// binding.CheckedListItemSorting.setup(ListItemSorting, value)
|
||||||
// }
|
// }
|
||||||
|
|
||||||
binding.MaxItems.setup(MaxItems, maxItems)
|
binding.MaxItems.setup(maxItems)
|
||||||
|
|
||||||
binding.MaxLines.setup(MaxLines, maxLines)
|
binding.MaxLines.setup(maxLines)
|
||||||
|
|
||||||
binding.MaxTitle.setup(MaxTitle, maxTitle)
|
binding.MaxTitle.setup(maxTitle)
|
||||||
|
|
||||||
binding.AutoBackupMax.setup(AutoBackupMax, autoBackupMax)
|
binding.AutoBackupMax.setup(autoBackupMax)
|
||||||
|
|
||||||
autoBackupPath.observe(viewLifecycleOwner) { value ->
|
autoBackupPath.observe(viewLifecycleOwner) { value ->
|
||||||
binding.AutoBackup.setup(AutoBackup, value)
|
binding.AutoBackup.setupAutoBackup(autoBackupPath, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
autoBackupPeriodDays.observe(viewLifecycleOwner) { value ->
|
autoBackupPeriodDays.observe(viewLifecycleOwner) { value ->
|
||||||
binding.AutoBackupPeriodDays.setup(AutoBackupPeriodDays, value)
|
binding.AutoBackupPeriodDays.setup(autoBackupPeriodDays, value)
|
||||||
scheduleAutoBackup(value.toLong(), requireContext())
|
scheduleAutoBackup(value.toLong(), requireContext())
|
||||||
}
|
}
|
||||||
|
|
||||||
backupPassword.observe(viewLifecycleOwner) { value ->
|
backupPassword.observe(viewLifecycleOwner) { value ->
|
||||||
binding.BackupPassword.setup(BackupPassword, value)
|
binding.BackupPassword.setupBackupPassword(backupPassword, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
biometricLock.observe(viewLifecycleOwner) { value ->
|
biometricLock.observe(viewLifecycleOwner) { value ->
|
||||||
binding.BiometricLock.setup(BiometricLock, value)
|
binding.BiometricLock.setup(biometricLock, value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -209,7 +200,7 @@ class SettingsFragment : Fragment() {
|
||||||
val layout = TextInputDialogBinding.inflate(layoutInflater, null, false)
|
val layout = TextInputDialogBinding.inflate(layoutInflater, null, false)
|
||||||
val password = model.preferences.backupPassword.value
|
val password = model.preferences.backupPassword.value
|
||||||
layout.InputText.apply {
|
layout.InputText.apply {
|
||||||
if (password != emptyPassword) {
|
if (password != PASSWORD_EMPTY) {
|
||||||
setText(password)
|
setText(password)
|
||||||
}
|
}
|
||||||
transformationMethod = PasswordTransformationMethod.getInstance()
|
transformationMethod = PasswordTransformationMethod.getInstance()
|
||||||
|
@ -384,24 +375,23 @@ class SettingsFragment : Fragment() {
|
||||||
.show()
|
.show()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun PreferenceBinding.setup(info: ListInfo, value: String) {
|
private inline fun <reified T> PreferenceBinding.setup(
|
||||||
Title.setText(info.title)
|
enumPreference: EnumPreference<T>,
|
||||||
|
value: T,
|
||||||
val entries = info.getEntries(requireContext())
|
) where T : Enum<T>, T : TextProvider {
|
||||||
val entryValues = info.getEntryValues()
|
Title.setText(enumPreference.titleResId!!)
|
||||||
|
val context = requireContext()
|
||||||
val checked = entryValues.indexOf(value)
|
Value.text = value.getText(context)
|
||||||
val displayValue = entries[checked]
|
val enumEntries = T::class.java.enumConstants!!.toList()
|
||||||
|
val entries = enumEntries.map { it.getText(requireContext()) }.toTypedArray()
|
||||||
Value.text = displayValue
|
val checked = enumEntries.indexOfFirst { it == value }
|
||||||
|
|
||||||
root.setOnClickListener {
|
root.setOnClickListener {
|
||||||
MaterialAlertDialogBuilder(requireContext())
|
MaterialAlertDialogBuilder(context)
|
||||||
.setTitle(info.title)
|
.setTitle(enumPreference.titleResId)
|
||||||
.setSingleChoiceItems(entries, checked) { dialog, which ->
|
.setSingleChoiceItems(entries, checked) { dialog, which ->
|
||||||
dialog.cancel()
|
dialog.cancel()
|
||||||
val newValue = entryValues[which]
|
val newValue = enumEntries[which]
|
||||||
model.savePreference(info, newValue)
|
model.savePreference(enumPreference, newValue)
|
||||||
}
|
}
|
||||||
.setNegativeButton(R.string.cancel, null)
|
.setNegativeButton(R.string.cancel, null)
|
||||||
.show()
|
.show()
|
||||||
|
@ -409,35 +399,73 @@ class SettingsFragment : Fragment() {
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun PreferenceBinding.setup(
|
private fun PreferenceBinding.setup(
|
||||||
info: NotesSorting,
|
preference: EnumPreference<BiometricLock>,
|
||||||
sortBy: String,
|
value: BiometricLock,
|
||||||
sortDirection: SortDirection,
|
|
||||||
) {
|
) {
|
||||||
Title.setText(info.title)
|
Title.setText(preference.titleResId!!)
|
||||||
|
|
||||||
val entries = info.getEntries(requireContext())
|
val context = requireContext()
|
||||||
val entryValues = info.getEntryValues()
|
Value.text = value.getText(context)
|
||||||
|
val enumEntries = BiometricLock.entries
|
||||||
|
val entries = enumEntries.map { context.getString(it.textResId) }.toTypedArray()
|
||||||
|
val checked = enumEntries.indexOfFirst { it == value }
|
||||||
|
|
||||||
val checked = entryValues.indexOf(sortBy)
|
root.setOnClickListener {
|
||||||
val displayValue = entries[checked]
|
MaterialAlertDialogBuilder(requireContext())
|
||||||
|
.setTitle(preference.titleResId)
|
||||||
|
.setSingleChoiceItems(entries, checked) { dialog, which ->
|
||||||
|
dialog.cancel()
|
||||||
|
val newValue = enumEntries[which]
|
||||||
|
if (newValue == BiometricLock.ENABLED) {
|
||||||
|
when (requireContext().canAuthenticateWithBiometrics()) {
|
||||||
|
BiometricManager.BIOMETRIC_SUCCESS -> showEnableBiometricLock()
|
||||||
|
BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE ->
|
||||||
|
showNoBiometricsSupportToast()
|
||||||
|
|
||||||
Value.text = "$displayValue (${requireContext().getString(sortDirection.textResId)})"
|
BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED ->
|
||||||
|
showBiometricsNotSetupDialog()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
when (requireContext().canAuthenticateWithBiometrics()) {
|
||||||
|
BiometricManager.BIOMETRIC_SUCCESS -> showDisableBiometricLock()
|
||||||
|
BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE -> {
|
||||||
|
showNoBiometricsSupportToast()
|
||||||
|
model.savePreference(
|
||||||
|
model.preferences.biometricLock,
|
||||||
|
BiometricLock.DISABLED,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED -> {
|
||||||
|
showBiometricsNotSetupDialog()
|
||||||
|
model.savePreference(
|
||||||
|
model.preferences.biometricLock,
|
||||||
|
BiometricLock.DISABLED,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.setNegativeButton(R.string.cancel, null)
|
||||||
|
.show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun PreferenceBinding.setup(preference: NotesSortPreference, value: NotesSort) {
|
||||||
|
Title.setText(preference.titleResId!!)
|
||||||
|
|
||||||
|
Value.text = value.getText(requireContext())
|
||||||
|
|
||||||
root.setOnClickListener {
|
root.setOnClickListener {
|
||||||
val layout = NotesSortDialogBinding.inflate(layoutInflater, null, false)
|
val layout = NotesSortDialogBinding.inflate(layoutInflater, null, false)
|
||||||
entries.zip(entryValues).forEachIndexed { idx, (choiceText, sortByValue) ->
|
NotesSortBy.entries.forEachIndexed { idx, notesSortBy ->
|
||||||
ChoiceItemBinding.inflate(layoutInflater).root.apply {
|
ChoiceItemBinding.inflate(layoutInflater).root.apply {
|
||||||
id = idx
|
id = idx
|
||||||
text = choiceText
|
text = requireContext().getString(notesSortBy.textResId)
|
||||||
tag = sortByValue
|
tag = notesSortBy
|
||||||
layout.NotesSortByRadioGroup.addView(this)
|
layout.NotesSortByRadioGroup.addView(this)
|
||||||
setCompoundDrawablesRelativeWithIntrinsicBounds(
|
setCompoundDrawablesRelativeWithIntrinsicBounds(notesSortBy.iconResId, 0, 0, 0)
|
||||||
NotesSorting.getSortIconResId(sortByValue),
|
if (notesSortBy == value.sortedBy) {
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
)
|
|
||||||
if (sortByValue == sortBy) {
|
|
||||||
layout.NotesSortByRadioGroup.check(this.id)
|
layout.NotesSortByRadioGroup.check(this.id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -450,37 +478,43 @@ class SettingsFragment : Fragment() {
|
||||||
tag = sortDir
|
tag = sortDir
|
||||||
setCompoundDrawablesRelativeWithIntrinsicBounds(sortDir.iconResId, 0, 0, 0)
|
setCompoundDrawablesRelativeWithIntrinsicBounds(sortDir.iconResId, 0, 0, 0)
|
||||||
layout.NotesSortDirectionRadioGroup.addView(this)
|
layout.NotesSortDirectionRadioGroup.addView(this)
|
||||||
if (sortDir == sortDirection) {
|
if (sortDir == value.sortDirection) {
|
||||||
layout.NotesSortDirectionRadioGroup.check(this.id)
|
layout.NotesSortDirectionRadioGroup.check(this.id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MaterialAlertDialogBuilder(requireContext())
|
MaterialAlertDialogBuilder(requireContext())
|
||||||
.setTitle(info.title)
|
.setTitle(preference.titleResId)
|
||||||
.setView(layout.root)
|
.setView(layout.root)
|
||||||
.setPositiveButton(R.string.save) { dialog, _ ->
|
.setPositiveButton(R.string.save) { dialog, _ ->
|
||||||
dialog.cancel()
|
dialog.cancel()
|
||||||
val newSortBy = layout.NotesSortByRadioGroup.checkedTag() as String
|
val newSortBy = layout.NotesSortByRadioGroup.checkedTag() as NotesSortBy
|
||||||
val newSortDirection =
|
val newSortDirection =
|
||||||
layout.NotesSortDirectionRadioGroup.checkedTag() as SortDirection
|
layout.NotesSortDirectionRadioGroup.checkedTag() as SortDirection
|
||||||
model.preferences.savePreference(info, newSortBy, newSortDirection)
|
model.savePreference(
|
||||||
|
model.preferences.notesSorting,
|
||||||
|
NotesSort(newSortBy, newSortDirection),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
.setNegativeButton(R.string.cancel, null)
|
.setNegativeButton(R.string.cancel, null)
|
||||||
.show()
|
.show()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun PreferenceBinding.setup(info: BackupPassword, password: String) {
|
private fun PreferenceBinding.setupBackupPassword(
|
||||||
Title.setText(info.title)
|
preference: StringPreference,
|
||||||
|
password: String,
|
||||||
|
) {
|
||||||
|
Title.setText(preference.titleResId!!)
|
||||||
|
|
||||||
Value.transformationMethod =
|
Value.transformationMethod =
|
||||||
if (password != emptyPassword) PasswordTransformationMethod.getInstance() else null
|
if (password != PASSWORD_EMPTY) PasswordTransformationMethod.getInstance() else null
|
||||||
Value.text = if (password != emptyPassword) password else getText(R.string.tap_to_set_up)
|
Value.text = if (password != PASSWORD_EMPTY) password else getText(R.string.tap_to_set_up)
|
||||||
root.setOnClickListener {
|
root.setOnClickListener {
|
||||||
val layout = TextInputDialogBinding.inflate(layoutInflater, null, false)
|
val layout = TextInputDialogBinding.inflate(layoutInflater, null, false)
|
||||||
layout.InputText.apply {
|
layout.InputText.apply {
|
||||||
if (password != emptyPassword) {
|
if (password != PASSWORD_EMPTY) {
|
||||||
setText(password)
|
setText(password)
|
||||||
}
|
}
|
||||||
transformationMethod = PasswordTransformationMethod.getInstance()
|
transformationMethod = PasswordTransformationMethod.getInstance()
|
||||||
|
@ -491,66 +525,22 @@ class SettingsFragment : Fragment() {
|
||||||
visibility = View.VISIBLE
|
visibility = View.VISIBLE
|
||||||
}
|
}
|
||||||
MaterialAlertDialogBuilder(requireContext())
|
MaterialAlertDialogBuilder(requireContext())
|
||||||
.setTitle(info.title)
|
.setTitle(preference.titleResId)
|
||||||
.setView(layout.root)
|
.setView(layout.root)
|
||||||
.setPositiveButton(R.string.save) { dialog, _ ->
|
.setPositiveButton(R.string.save) { dialog, _ ->
|
||||||
dialog.cancel()
|
dialog.cancel()
|
||||||
val updatedPassword = layout.InputText.text.toString()
|
val updatedPassword = layout.InputText.text.toString()
|
||||||
model.preferences.savePreference(info, updatedPassword)
|
model.savePreference(preference, updatedPassword)
|
||||||
}
|
}
|
||||||
.setNegativeButton(R.string.cancel, null)
|
.setNegativeButton(R.string.cancel, null)
|
||||||
.setNeutralButton(R.string.clear) { dialog, _ ->
|
.setNeutralButton(R.string.clear) { dialog, _ ->
|
||||||
dialog.cancel()
|
dialog.cancel()
|
||||||
model.preferences.savePreference(info, emptyPassword)
|
model.savePreference(preference, PASSWORD_EMPTY)
|
||||||
}
|
}
|
||||||
.show()
|
.show()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun PreferenceBinding.setup(info: BiometricLock, value: String) {
|
|
||||||
Title.setText(info.title)
|
|
||||||
|
|
||||||
val entries = info.getEntries(requireContext())
|
|
||||||
val entryValues = info.getEntryValues()
|
|
||||||
|
|
||||||
val checked = entryValues.indexOf(value)
|
|
||||||
val displayValue = entries[checked]
|
|
||||||
|
|
||||||
Value.text = displayValue
|
|
||||||
|
|
||||||
root.setOnClickListener {
|
|
||||||
MaterialAlertDialogBuilder(requireContext())
|
|
||||||
.setTitle(info.title)
|
|
||||||
.setSingleChoiceItems(entries, checked) { dialog, which ->
|
|
||||||
dialog.cancel()
|
|
||||||
val newValue = entryValues[which]
|
|
||||||
if (newValue == enabled) {
|
|
||||||
when (requireContext().canAuthenticateWithBiometrics()) {
|
|
||||||
BiometricManager.BIOMETRIC_SUCCESS -> showEnableBiometricLock()
|
|
||||||
BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE ->
|
|
||||||
showNoBiometricsSupportToast()
|
|
||||||
BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED ->
|
|
||||||
showBiometricsNotSetupDialog()
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
when (requireContext().canAuthenticateWithBiometrics()) {
|
|
||||||
BiometricManager.BIOMETRIC_SUCCESS -> showDisableBiometricLock()
|
|
||||||
BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE -> {
|
|
||||||
showNoBiometricsSupportToast()
|
|
||||||
model.preferences.biometricLock.value = disabled
|
|
||||||
}
|
|
||||||
BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED -> {
|
|
||||||
showBiometricsNotSetupDialog()
|
|
||||||
model.preferences.biometricLock.value = disabled
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.setNegativeButton(R.string.cancel, null)
|
|
||||||
.show()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun showEnableBiometricLock() {
|
private fun showEnableBiometricLock() {
|
||||||
showBiometricOrPinPrompt(
|
showBiometricOrPinPrompt(
|
||||||
false,
|
false,
|
||||||
|
@ -559,10 +549,10 @@ class SettingsFragment : Fragment() {
|
||||||
R.string.enable_lock_description,
|
R.string.enable_lock_description,
|
||||||
onSuccess = { cipher ->
|
onSuccess = { cipher ->
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||||
model.preferences.iv = cipher.iv
|
model.savePreference(model.preferences.iv, cipher.iv)
|
||||||
val passphrase = model.preferences.generatePassphrase(cipher)
|
val passphrase = model.preferences.databaseEncryptionKey.init(cipher)
|
||||||
encryptDatabase(requireContext(), passphrase)
|
encryptDatabase(requireContext(), passphrase)
|
||||||
model.savePreference(BiometricLock, enabled)
|
model.savePreference(model.preferences.biometricLock, BiometricLock.ENABLED)
|
||||||
}
|
}
|
||||||
val app = (activity?.application as NotallyXApplication)
|
val app = (activity?.application as NotallyXApplication)
|
||||||
app.locked.value = false
|
app.locked.value = false
|
||||||
|
@ -579,14 +569,14 @@ class SettingsFragment : Fragment() {
|
||||||
disableLockActivityResultLauncher,
|
disableLockActivityResultLauncher,
|
||||||
R.string.disable_lock_title,
|
R.string.disable_lock_title,
|
||||||
R.string.disable_lock_description,
|
R.string.disable_lock_description,
|
||||||
model.preferences.iv!!,
|
model.preferences.iv.value!!,
|
||||||
onSuccess = { cipher ->
|
onSuccess = { cipher ->
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||||
val encryptedPassphrase = model.preferences.getDatabasePassphrase()
|
val encryptedPassphrase = model.preferences.databaseEncryptionKey.value
|
||||||
val passphrase = cipher.doFinal(encryptedPassphrase)
|
val passphrase = cipher.doFinal(encryptedPassphrase)
|
||||||
model.closeDatabase()
|
model.closeDatabase()
|
||||||
decryptDatabase(requireContext(), passphrase)
|
decryptDatabase(requireContext(), passphrase)
|
||||||
model.savePreference(BiometricLock, disabled)
|
model.savePreference(model.preferences.biometricLock, BiometricLock.DISABLED)
|
||||||
}
|
}
|
||||||
showBiometricsDisabledToast()
|
showBiometricsDisabledToast()
|
||||||
},
|
},
|
||||||
|
@ -632,10 +622,10 @@ class SettingsFragment : Fragment() {
|
||||||
.show()
|
.show()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun PreferenceBinding.setup(info: AutoBackup, value: String) {
|
private fun PreferenceBinding.setupAutoBackup(preference: StringPreference, value: String) {
|
||||||
Title.setText(info.title)
|
Title.setText(preference.titleResId!!)
|
||||||
|
|
||||||
if (value == info.emptyPath) {
|
if (value == BACKUP_PATH_EMPTY) {
|
||||||
Value.setText(R.string.tap_to_set_up)
|
Value.setText(R.string.tap_to_set_up)
|
||||||
|
|
||||||
root.setOnClickListener { displayChooseFolderDialog() }
|
root.setOnClickListener { displayChooseFolderDialog() }
|
||||||
|
@ -655,14 +645,17 @@ class SettingsFragment : Fragment() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun PreferenceSeekbarBinding.setup(info: SeekbarInfo, initialValue: Int) {
|
private fun PreferenceSeekbarBinding.setup(
|
||||||
Title.setText(info.title)
|
preference: IntPreference,
|
||||||
|
value: Int = preference.value,
|
||||||
|
) {
|
||||||
|
Title.setText(preference.titleResId!!)
|
||||||
|
|
||||||
Slider.apply {
|
Slider.apply {
|
||||||
valueTo = info.max.toFloat()
|
valueTo = preference.max.toFloat()
|
||||||
valueFrom = info.min.toFloat()
|
valueFrom = preference.min.toFloat()
|
||||||
value = initialValue.toFloat()
|
this@apply.value = value.toFloat()
|
||||||
addOnChangeListener { _, value, _ -> model.savePreference(info, value.toInt()) }
|
addOnChangeListener { _, value, _ -> model.savePreference(preference, value.toInt()) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -40,14 +40,12 @@ import com.philkes.notallyx.presentation.getParcelableExtraCompat
|
||||||
import com.philkes.notallyx.presentation.getQuantityString
|
import com.philkes.notallyx.presentation.getQuantityString
|
||||||
import com.philkes.notallyx.presentation.setupProgressDialog
|
import com.philkes.notallyx.presentation.setupProgressDialog
|
||||||
import com.philkes.notallyx.presentation.view.Constants
|
import com.philkes.notallyx.presentation.view.Constants
|
||||||
import com.philkes.notallyx.presentation.view.misc.NotesSorting.autoSortByCreationDate
|
|
||||||
import com.philkes.notallyx.presentation.view.misc.NotesSorting.autoSortByModifiedDate
|
|
||||||
import com.philkes.notallyx.presentation.view.misc.TextSize
|
|
||||||
import com.philkes.notallyx.presentation.view.note.ErrorAdapter
|
import com.philkes.notallyx.presentation.view.note.ErrorAdapter
|
||||||
import com.philkes.notallyx.presentation.view.note.audio.AudioAdapter
|
import com.philkes.notallyx.presentation.view.note.audio.AudioAdapter
|
||||||
import com.philkes.notallyx.presentation.view.note.preview.PreviewFileAdapter
|
import com.philkes.notallyx.presentation.view.note.preview.PreviewFileAdapter
|
||||||
import com.philkes.notallyx.presentation.view.note.preview.PreviewImageAdapter
|
import com.philkes.notallyx.presentation.view.note.preview.PreviewImageAdapter
|
||||||
import com.philkes.notallyx.presentation.viewmodel.NotallyModel
|
import com.philkes.notallyx.presentation.viewmodel.NotallyModel
|
||||||
|
import com.philkes.notallyx.presentation.viewmodel.preference.NotesSortBy
|
||||||
import com.philkes.notallyx.presentation.widget.WidgetProvider
|
import com.philkes.notallyx.presentation.widget.WidgetProvider
|
||||||
import com.philkes.notallyx.utils.FileError
|
import com.philkes.notallyx.utils.FileError
|
||||||
import com.philkes.notallyx.utils.Operations
|
import com.philkes.notallyx.utils.Operations
|
||||||
|
@ -282,13 +280,12 @@ abstract class EditActivity(private val type: Type) : LockedActivity<ActivityEdi
|
||||||
|
|
||||||
open fun setStateFromModel() {
|
open fun setStateFromModel() {
|
||||||
val (date, datePrefixResId) =
|
val (date, datePrefixResId) =
|
||||||
when (preferences.notesSorting.value.first) {
|
when (preferences.notesSorting.value.sortedBy) {
|
||||||
autoSortByCreationDate -> Pair(model.timestamp, R.string.creation_date)
|
NotesSortBy.CREATION_DATE -> Pair(model.timestamp, R.string.creation_date)
|
||||||
autoSortByModifiedDate -> Pair(model.modifiedTimestamp, R.string.modified_date)
|
NotesSortBy.MODIFIED_DATE -> Pair(model.modifiedTimestamp, R.string.modified_date)
|
||||||
else -> Pair(null, null)
|
else -> Pair(null, null)
|
||||||
}
|
}
|
||||||
binding.Date.displayFormattedTimestamp(date, preferences.dateFormat.value, datePrefixResId)
|
binding.Date.displayFormattedTimestamp(date, preferences.dateFormat.value, datePrefixResId)
|
||||||
|
|
||||||
binding.EnterTitle.setText(model.title)
|
binding.EnterTitle.setText(model.title)
|
||||||
Operations.bindLabels(binding.LabelGroup, model.labels, model.textSize)
|
Operations.bindLabels(binding.LabelGroup, model.labels, model.textSize)
|
||||||
|
|
||||||
|
@ -602,9 +599,9 @@ abstract class EditActivity(private val type: Type) : LockedActivity<ActivityEdi
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val title = TextSize.getEditTitleSize(model.textSize)
|
val title = model.textSize.editTitleSize
|
||||||
val date = TextSize.getDisplayBodySize(model.textSize)
|
val date = model.textSize.displayBodySize
|
||||||
val body = TextSize.getEditBodySize(model.textSize)
|
val body = model.textSize.editBodySize
|
||||||
|
|
||||||
binding.EnterTitle.setTextSize(TypedValue.COMPLEX_UNIT_SP, title)
|
binding.EnterTitle.setTextSize(TypedValue.COMPLEX_UNIT_SP, title)
|
||||||
binding.Date.setTextSize(TypedValue.COMPLEX_UNIT_SP, date)
|
binding.Date.setTextSize(TypedValue.COMPLEX_UNIT_SP, date)
|
||||||
|
|
|
@ -4,18 +4,18 @@ import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.MenuItem
|
import android.view.MenuItem
|
||||||
import android.view.inputmethod.InputMethodManager
|
import android.view.inputmethod.InputMethodManager
|
||||||
import com.philkes.notallyx.Preferences
|
|
||||||
import com.philkes.notallyx.R
|
import com.philkes.notallyx.R
|
||||||
import com.philkes.notallyx.data.model.Type
|
import com.philkes.notallyx.data.model.Type
|
||||||
import com.philkes.notallyx.presentation.add
|
import com.philkes.notallyx.presentation.add
|
||||||
import com.philkes.notallyx.presentation.setOnNextAction
|
import com.philkes.notallyx.presentation.setOnNextAction
|
||||||
import com.philkes.notallyx.presentation.view.misc.ListItemSorting
|
|
||||||
import com.philkes.notallyx.presentation.view.note.listitem.ListItemAdapter
|
import com.philkes.notallyx.presentation.view.note.listitem.ListItemAdapter
|
||||||
import com.philkes.notallyx.presentation.view.note.listitem.ListManager
|
import com.philkes.notallyx.presentation.view.note.listitem.ListManager
|
||||||
import com.philkes.notallyx.presentation.view.note.listitem.sorting.ListItemNoSortCallback
|
import com.philkes.notallyx.presentation.view.note.listitem.sorting.ListItemNoSortCallback
|
||||||
import com.philkes.notallyx.presentation.view.note.listitem.sorting.ListItemSortedByCheckedCallback
|
import com.philkes.notallyx.presentation.view.note.listitem.sorting.ListItemSortedByCheckedCallback
|
||||||
import com.philkes.notallyx.presentation.view.note.listitem.sorting.ListItemSortedList
|
import com.philkes.notallyx.presentation.view.note.listitem.sorting.ListItemSortedList
|
||||||
import com.philkes.notallyx.presentation.view.note.listitem.sorting.toMutableList
|
import com.philkes.notallyx.presentation.view.note.listitem.sorting.toMutableList
|
||||||
|
import com.philkes.notallyx.presentation.viewmodel.preference.ListItemSort
|
||||||
|
import com.philkes.notallyx.presentation.viewmodel.preference.NotallyXPreferences
|
||||||
import com.philkes.notallyx.utils.changehistory.ChangeHistory
|
import com.philkes.notallyx.utils.changehistory.ChangeHistory
|
||||||
|
|
||||||
class EditListActivity : EditActivity(Type.LIST) {
|
class EditListActivity : EditActivity(Type.LIST) {
|
||||||
|
@ -102,12 +102,12 @@ class EditListActivity : EditActivity(Type.LIST) {
|
||||||
ListItemAdapter(
|
ListItemAdapter(
|
||||||
model.textSize,
|
model.textSize,
|
||||||
elevation,
|
elevation,
|
||||||
Preferences.getInstance(application),
|
NotallyXPreferences.getInstance(application),
|
||||||
listManager,
|
listManager,
|
||||||
)
|
)
|
||||||
val sortCallback =
|
val sortCallback =
|
||||||
when (preferences.listItemSorting.value) {
|
when (preferences.listItemSorting.value) {
|
||||||
ListItemSorting.autoSortByChecked -> ListItemSortedByCheckedCallback(adapter)
|
ListItemSort.AUTO_SORT_BY_CHECKED -> ListItemSortedByCheckedCallback(adapter)
|
||||||
else -> ListItemNoSortCallback(adapter)
|
else -> ListItemNoSortCallback(adapter)
|
||||||
}
|
}
|
||||||
items = ListItemSortedList(sortCallback)
|
items = ListItemSortedList(sortCallback)
|
||||||
|
|
|
@ -6,7 +6,6 @@ import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import androidx.recyclerview.widget.StaggeredGridLayoutManager
|
import androidx.recyclerview.widget.StaggeredGridLayoutManager
|
||||||
import com.philkes.notallyx.Preferences
|
|
||||||
import com.philkes.notallyx.R
|
import com.philkes.notallyx.R
|
||||||
import com.philkes.notallyx.data.NotallyDatabase
|
import com.philkes.notallyx.data.NotallyDatabase
|
||||||
import com.philkes.notallyx.data.model.BaseNote
|
import com.philkes.notallyx.data.model.BaseNote
|
||||||
|
@ -14,9 +13,10 @@ import com.philkes.notallyx.data.model.Header
|
||||||
import com.philkes.notallyx.databinding.ActivityPickNoteBinding
|
import com.philkes.notallyx.databinding.ActivityPickNoteBinding
|
||||||
import com.philkes.notallyx.presentation.activity.LockedActivity
|
import com.philkes.notallyx.presentation.activity.LockedActivity
|
||||||
import com.philkes.notallyx.presentation.view.main.BaseNoteAdapter
|
import com.philkes.notallyx.presentation.view.main.BaseNoteAdapter
|
||||||
import com.philkes.notallyx.presentation.view.misc.View
|
|
||||||
import com.philkes.notallyx.presentation.view.note.listitem.ListItemListener
|
import com.philkes.notallyx.presentation.view.note.listitem.ListItemListener
|
||||||
import com.philkes.notallyx.presentation.viewmodel.BaseNoteModel
|
import com.philkes.notallyx.presentation.viewmodel.BaseNoteModel
|
||||||
|
import com.philkes.notallyx.presentation.viewmodel.preference.NotallyXPreferences
|
||||||
|
import com.philkes.notallyx.presentation.viewmodel.preference.NotesView
|
||||||
import com.philkes.notallyx.utils.IO.getExternalImagesDirectory
|
import com.philkes.notallyx.utils.IO.getExternalImagesDirectory
|
||||||
import java.util.Collections
|
import java.util.Collections
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
@ -37,18 +37,18 @@ open class PickNoteActivity : LockedActivity<ActivityPickNoteBinding>(), ListIte
|
||||||
val result = Intent()
|
val result = Intent()
|
||||||
setResult(RESULT_CANCELED, result)
|
setResult(RESULT_CANCELED, result)
|
||||||
|
|
||||||
val preferences = Preferences.getInstance(application)
|
val preferences = NotallyXPreferences.getInstance(application)
|
||||||
|
|
||||||
adapter =
|
adapter =
|
||||||
with(preferences) {
|
with(preferences) {
|
||||||
BaseNoteAdapter(
|
BaseNoteAdapter(
|
||||||
Collections.emptySet(),
|
Collections.emptySet(),
|
||||||
dateFormat.value,
|
dateFormat.value,
|
||||||
notesSorting.value.first,
|
notesSorting.value.sortedBy,
|
||||||
textSize.value,
|
textSize.value,
|
||||||
maxItems,
|
maxItems.value,
|
||||||
maxLines,
|
maxLines.value,
|
||||||
maxTitle,
|
maxTitle.value,
|
||||||
application.getExternalImagesDirectory(),
|
application.getExternalImagesDirectory(),
|
||||||
this@PickNoteActivity,
|
this@PickNoteActivity,
|
||||||
)
|
)
|
||||||
|
@ -58,7 +58,7 @@ open class PickNoteActivity : LockedActivity<ActivityPickNoteBinding>(), ListIte
|
||||||
adapter = this@PickNoteActivity.adapter
|
adapter = this@PickNoteActivity.adapter
|
||||||
setHasFixedSize(true)
|
setHasFixedSize(true)
|
||||||
layoutManager =
|
layoutManager =
|
||||||
if (preferences.view.value == View.grid) {
|
if (preferences.notesView.value == NotesView.GRID) {
|
||||||
StaggeredGridLayoutManager(2, RecyclerView.VERTICAL)
|
StaggeredGridLayoutManager(2, RecyclerView.VERTICAL)
|
||||||
} else LinearLayoutManager(this@PickNoteActivity)
|
} else LinearLayoutManager(this@PickNoteActivity)
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,17 +13,19 @@ import com.philkes.notallyx.databinding.RecyclerHeaderBinding
|
||||||
import com.philkes.notallyx.presentation.view.main.sorting.BaseNoteCreationDateSort
|
import com.philkes.notallyx.presentation.view.main.sorting.BaseNoteCreationDateSort
|
||||||
import com.philkes.notallyx.presentation.view.main.sorting.BaseNoteModifiedDateSort
|
import com.philkes.notallyx.presentation.view.main.sorting.BaseNoteModifiedDateSort
|
||||||
import com.philkes.notallyx.presentation.view.main.sorting.BaseNoteTitleSort
|
import com.philkes.notallyx.presentation.view.main.sorting.BaseNoteTitleSort
|
||||||
import com.philkes.notallyx.presentation.view.misc.NotesSorting.autoSortByModifiedDate
|
|
||||||
import com.philkes.notallyx.presentation.view.misc.NotesSorting.autoSortByTitle
|
|
||||||
import com.philkes.notallyx.presentation.view.misc.SortDirection
|
|
||||||
import com.philkes.notallyx.presentation.view.note.listitem.ListItemListener
|
import com.philkes.notallyx.presentation.view.note.listitem.ListItemListener
|
||||||
|
import com.philkes.notallyx.presentation.viewmodel.preference.DateFormat
|
||||||
|
import com.philkes.notallyx.presentation.viewmodel.preference.NotesSort
|
||||||
|
import com.philkes.notallyx.presentation.viewmodel.preference.NotesSortBy
|
||||||
|
import com.philkes.notallyx.presentation.viewmodel.preference.SortDirection
|
||||||
|
import com.philkes.notallyx.presentation.viewmodel.preference.TextSize
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
class BaseNoteAdapter(
|
class BaseNoteAdapter(
|
||||||
private val selectedIds: Set<Long>,
|
private val selectedIds: Set<Long>,
|
||||||
private val dateFormat: String,
|
private val dateFormat: DateFormat,
|
||||||
private val sortedBy: String,
|
private val sortedBy: NotesSortBy,
|
||||||
private val textSize: String,
|
private val textSize: TextSize,
|
||||||
private val maxItems: Int,
|
private val maxItems: Int,
|
||||||
private val maxLines: Int,
|
private val maxLines: Int,
|
||||||
private val maxTitle: Int,
|
private val maxTitle: Int,
|
||||||
|
@ -82,12 +84,12 @@ class BaseNoteAdapter(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setSorting(sortBy: String, sortDirection: SortDirection) {
|
fun setSorting(notesSort: NotesSort) {
|
||||||
val sortCallback =
|
val sortCallback =
|
||||||
when (sortBy) {
|
when (notesSort.sortedBy) {
|
||||||
autoSortByTitle -> BaseNoteTitleSort(this, sortDirection)
|
NotesSortBy.TITLE -> BaseNoteTitleSort(this, notesSort.sortDirection)
|
||||||
autoSortByModifiedDate -> BaseNoteModifiedDateSort(this, sortDirection)
|
NotesSortBy.MODIFIED_DATE -> BaseNoteModifiedDateSort(this, notesSort.sortDirection)
|
||||||
else -> BaseNoteCreationDateSort(this, sortDirection)
|
else -> BaseNoteCreationDateSort(this, notesSort.sortDirection)
|
||||||
}
|
}
|
||||||
replaceSorting(sortCallback)
|
replaceSorting(sortCallback)
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,17 +28,17 @@ import com.philkes.notallyx.presentation.applySpans
|
||||||
import com.philkes.notallyx.presentation.displayFormattedTimestamp
|
import com.philkes.notallyx.presentation.displayFormattedTimestamp
|
||||||
import com.philkes.notallyx.presentation.dp
|
import com.philkes.notallyx.presentation.dp
|
||||||
import com.philkes.notallyx.presentation.getQuantityString
|
import com.philkes.notallyx.presentation.getQuantityString
|
||||||
import com.philkes.notallyx.presentation.view.misc.NotesSorting.autoSortByCreationDate
|
|
||||||
import com.philkes.notallyx.presentation.view.misc.NotesSorting.autoSortByModifiedDate
|
|
||||||
import com.philkes.notallyx.presentation.view.misc.TextSize
|
|
||||||
import com.philkes.notallyx.presentation.view.note.listitem.ListItemListener
|
import com.philkes.notallyx.presentation.view.note.listitem.ListItemListener
|
||||||
|
import com.philkes.notallyx.presentation.viewmodel.preference.DateFormat
|
||||||
|
import com.philkes.notallyx.presentation.viewmodel.preference.NotesSortBy
|
||||||
|
import com.philkes.notallyx.presentation.viewmodel.preference.TextSize
|
||||||
import com.philkes.notallyx.utils.Operations
|
import com.philkes.notallyx.utils.Operations
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
class BaseNoteVH(
|
class BaseNoteVH(
|
||||||
private val binding: RecyclerBaseNoteBinding,
|
private val binding: RecyclerBaseNoteBinding,
|
||||||
private val dateFormat: String,
|
private val dateFormat: DateFormat,
|
||||||
private val textSize: String,
|
private val textSize: TextSize,
|
||||||
private val maxItems: Int,
|
private val maxItems: Int,
|
||||||
maxLines: Int,
|
maxLines: Int,
|
||||||
maxTitle: Int,
|
maxTitle: Int,
|
||||||
|
@ -46,8 +46,8 @@ class BaseNoteVH(
|
||||||
) : RecyclerView.ViewHolder(binding.root) {
|
) : RecyclerView.ViewHolder(binding.root) {
|
||||||
|
|
||||||
init {
|
init {
|
||||||
val title = TextSize.getDisplayTitleSize(textSize)
|
val title = textSize.displayTitleSize
|
||||||
val body = TextSize.getDisplayBodySize(textSize)
|
val body = textSize.displayBodySize
|
||||||
|
|
||||||
binding.apply {
|
binding.apply {
|
||||||
Title.setTextSize(TypedValue.COMPLEX_UNIT_SP, title)
|
Title.setTextSize(TypedValue.COMPLEX_UNIT_SP, title)
|
||||||
|
@ -75,7 +75,7 @@ class BaseNoteVH(
|
||||||
binding.root.isChecked = checked
|
binding.root.isChecked = checked
|
||||||
}
|
}
|
||||||
|
|
||||||
fun bind(baseNote: BaseNote, imageRoot: File?, checked: Boolean, sortBy: String) {
|
fun bind(baseNote: BaseNote, imageRoot: File?, checked: Boolean, sortBy: NotesSortBy) {
|
||||||
updateCheck(checked)
|
updateCheck(checked)
|
||||||
|
|
||||||
when (baseNote.type) {
|
when (baseNote.type) {
|
||||||
|
@ -84,8 +84,9 @@ class BaseNoteVH(
|
||||||
}
|
}
|
||||||
val (date, datePrefixResId) =
|
val (date, datePrefixResId) =
|
||||||
when (sortBy) {
|
when (sortBy) {
|
||||||
autoSortByCreationDate -> Pair(baseNote.timestamp, R.string.creation_date)
|
NotesSortBy.CREATION_DATE -> Pair(baseNote.timestamp, R.string.creation_date)
|
||||||
autoSortByModifiedDate -> Pair(baseNote.modifiedTimestamp, R.string.modified_date)
|
NotesSortBy.MODIFIED_DATE ->
|
||||||
|
Pair(baseNote.modifiedTimestamp, R.string.modified_date)
|
||||||
else -> Pair(null, null)
|
else -> Pair(null, null)
|
||||||
}
|
}
|
||||||
binding.Date.displayFormattedTimestamp(date, dateFormat, datePrefixResId)
|
binding.Date.displayFormattedTimestamp(date, dateFormat, datePrefixResId)
|
||||||
|
|
|
@ -2,7 +2,7 @@ package com.philkes.notallyx.presentation.view.main.sorting
|
||||||
|
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import com.philkes.notallyx.data.model.BaseNote
|
import com.philkes.notallyx.data.model.BaseNote
|
||||||
import com.philkes.notallyx.presentation.view.misc.SortDirection
|
import com.philkes.notallyx.presentation.viewmodel.preference.SortDirection
|
||||||
|
|
||||||
class BaseNoteCreationDateSort(adapter: RecyclerView.Adapter<*>?, sortDirection: SortDirection) :
|
class BaseNoteCreationDateSort(adapter: RecyclerView.Adapter<*>?, sortDirection: SortDirection) :
|
||||||
BaseNoteSort(adapter, sortDirection) {
|
BaseNoteSort(adapter, sortDirection) {
|
||||||
|
|
|
@ -2,7 +2,7 @@ package com.philkes.notallyx.presentation.view.main.sorting
|
||||||
|
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import com.philkes.notallyx.data.model.BaseNote
|
import com.philkes.notallyx.data.model.BaseNote
|
||||||
import com.philkes.notallyx.presentation.view.misc.SortDirection
|
import com.philkes.notallyx.presentation.viewmodel.preference.SortDirection
|
||||||
|
|
||||||
class BaseNoteModifiedDateSort(adapter: RecyclerView.Adapter<*>?, sortDirection: SortDirection) :
|
class BaseNoteModifiedDateSort(adapter: RecyclerView.Adapter<*>?, sortDirection: SortDirection) :
|
||||||
BaseNoteSort(adapter, sortDirection) {
|
BaseNoteSort(adapter, sortDirection) {
|
||||||
|
|
|
@ -5,7 +5,7 @@ import androidx.recyclerview.widget.SortedListAdapterCallback
|
||||||
import com.philkes.notallyx.data.model.BaseNote
|
import com.philkes.notallyx.data.model.BaseNote
|
||||||
import com.philkes.notallyx.data.model.Header
|
import com.philkes.notallyx.data.model.Header
|
||||||
import com.philkes.notallyx.data.model.Item
|
import com.philkes.notallyx.data.model.Item
|
||||||
import com.philkes.notallyx.presentation.view.misc.SortDirection
|
import com.philkes.notallyx.presentation.viewmodel.preference.SortDirection
|
||||||
|
|
||||||
abstract class BaseNoteSort(
|
abstract class BaseNoteSort(
|
||||||
adapter: RecyclerView.Adapter<*>?,
|
adapter: RecyclerView.Adapter<*>?,
|
||||||
|
|
|
@ -2,7 +2,7 @@ package com.philkes.notallyx.presentation.view.main.sorting
|
||||||
|
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import com.philkes.notallyx.data.model.BaseNote
|
import com.philkes.notallyx.data.model.BaseNote
|
||||||
import com.philkes.notallyx.presentation.view.misc.SortDirection
|
import com.philkes.notallyx.presentation.viewmodel.preference.SortDirection
|
||||||
|
|
||||||
class BaseNoteTitleSort(adapter: RecyclerView.Adapter<*>?, sortDirection: SortDirection) :
|
class BaseNoteTitleSort(adapter: RecyclerView.Adapter<*>?, sortDirection: SortDirection) :
|
||||||
BaseNoteSort(adapter, sortDirection) {
|
BaseNoteSort(adapter, sortDirection) {
|
||||||
|
|
|
@ -1,197 +0,0 @@
|
||||||
package com.philkes.notallyx.presentation.view.misc
|
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import com.philkes.notallyx.R
|
|
||||||
import java.text.DateFormat
|
|
||||||
import java.util.Date
|
|
||||||
import org.ocpsoft.prettytime.PrettyTime
|
|
||||||
|
|
||||||
sealed interface ListInfo {
|
|
||||||
|
|
||||||
val title: Int
|
|
||||||
|
|
||||||
val key: String
|
|
||||||
val defaultValue: String
|
|
||||||
|
|
||||||
fun getEntryValues(): Array<String>
|
|
||||||
|
|
||||||
fun getEntries(context: Context): Array<String>
|
|
||||||
|
|
||||||
fun convertToValues(ids: Array<Int>, context: Context): Array<String> {
|
|
||||||
return Array(ids.size) { index ->
|
|
||||||
val id = ids[index]
|
|
||||||
context.getString(id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
object View : ListInfo {
|
|
||||||
const val list = "list"
|
|
||||||
const val grid = "grid"
|
|
||||||
|
|
||||||
override val title = R.string.view
|
|
||||||
override val key = "view"
|
|
||||||
override val defaultValue = list
|
|
||||||
|
|
||||||
override fun getEntryValues() = arrayOf(list, grid)
|
|
||||||
|
|
||||||
override fun getEntries(context: Context): Array<String> {
|
|
||||||
val ids = arrayOf(R.string.list, R.string.grid)
|
|
||||||
return convertToValues(ids, context)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
object Theme : ListInfo {
|
|
||||||
const val dark = "dark"
|
|
||||||
const val light = "light"
|
|
||||||
const val followSystem = "followSystem"
|
|
||||||
|
|
||||||
override val title = R.string.theme
|
|
||||||
override val key = "theme"
|
|
||||||
override val defaultValue = followSystem
|
|
||||||
|
|
||||||
override fun getEntryValues() = arrayOf(dark, light, followSystem)
|
|
||||||
|
|
||||||
override fun getEntries(context: Context): Array<String> {
|
|
||||||
val ids = arrayOf(R.string.dark, R.string.light, R.string.follow_system)
|
|
||||||
return convertToValues(ids, context)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
object DateFormat : ListInfo {
|
|
||||||
const val none = "none"
|
|
||||||
const val relative = "relative"
|
|
||||||
const val absolute = "absolute"
|
|
||||||
|
|
||||||
override val title = R.string.date_format
|
|
||||||
override val key = "dateFormat"
|
|
||||||
override val defaultValue = relative
|
|
||||||
|
|
||||||
override fun getEntryValues() = arrayOf(none, relative, absolute)
|
|
||||||
|
|
||||||
override fun getEntries(context: Context): Array<String> {
|
|
||||||
val none = context.getString(R.string.none)
|
|
||||||
val date = Date(System.currentTimeMillis() - 86400000)
|
|
||||||
val relative = PrettyTime().format(date)
|
|
||||||
val absolute = DateFormat.getDateInstance(DateFormat.FULL).format(date)
|
|
||||||
return arrayOf(none, relative, absolute)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
object TextSize : ListInfo {
|
|
||||||
const val small = "small"
|
|
||||||
const val medium = "medium"
|
|
||||||
const val large = "large"
|
|
||||||
|
|
||||||
override val title = R.string.text_size
|
|
||||||
override val key = "textSize"
|
|
||||||
override val defaultValue = medium
|
|
||||||
|
|
||||||
override fun getEntryValues() = arrayOf(small, medium, large)
|
|
||||||
|
|
||||||
override fun getEntries(context: Context): Array<String> {
|
|
||||||
val ids = arrayOf(R.string.small, R.string.medium, R.string.large)
|
|
||||||
return convertToValues(ids, context)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getEditBodySize(textSize: String): Float {
|
|
||||||
return when (textSize) {
|
|
||||||
small -> 14f
|
|
||||||
medium -> 16f
|
|
||||||
large -> 18f
|
|
||||||
else -> throw IllegalArgumentException("Invalid : $textSize")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getEditTitleSize(textSize: String): Float {
|
|
||||||
return when (textSize) {
|
|
||||||
small -> 18f
|
|
||||||
medium -> 20f
|
|
||||||
large -> 22f
|
|
||||||
else -> throw IllegalArgumentException("Invalid : $textSize")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getDisplayBodySize(textSize: String): Float {
|
|
||||||
return when (textSize) {
|
|
||||||
small -> 12f
|
|
||||||
medium -> 14f
|
|
||||||
large -> 16f
|
|
||||||
else -> throw IllegalArgumentException("Invalid : $textSize")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getDisplayTitleSize(textSize: String): Float {
|
|
||||||
return when (textSize) {
|
|
||||||
small -> 14f
|
|
||||||
medium -> 16f
|
|
||||||
large -> 18f
|
|
||||||
else -> throw IllegalArgumentException("Invalid : $textSize")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
object ListItemSorting : ListInfo {
|
|
||||||
const val noAutoSort = "noAutoSort"
|
|
||||||
const val autoSortByChecked = "autoSortByChecked"
|
|
||||||
|
|
||||||
override val title = R.string.checked_list_item_sorting
|
|
||||||
override val key = "checkedListItemSorting"
|
|
||||||
override val defaultValue = noAutoSort
|
|
||||||
|
|
||||||
override fun getEntryValues() = arrayOf(noAutoSort, autoSortByChecked)
|
|
||||||
|
|
||||||
override fun getEntries(context: Context): Array<String> {
|
|
||||||
val ids = arrayOf(R.string.no_auto_sort, R.string.auto_sort_by_checked)
|
|
||||||
return convertToValues(ids, context)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
object NotesSorting : ListInfo {
|
|
||||||
const val autoSortByCreationDate = "autoSortByCreationDate"
|
|
||||||
const val autoSortByModifiedDate = "autoSortByModifiedDate"
|
|
||||||
const val autoSortByTitle = "autoSortByTitle"
|
|
||||||
|
|
||||||
override val title = R.string.notes_sorted_by
|
|
||||||
override val key = "notesSorting"
|
|
||||||
const val directionKey = "notesSortingDirection"
|
|
||||||
override val defaultValue = autoSortByCreationDate
|
|
||||||
val defaultValueDirection = SortDirection.DESC.name
|
|
||||||
|
|
||||||
override fun getEntryValues() =
|
|
||||||
arrayOf(autoSortByCreationDate, autoSortByModifiedDate, autoSortByTitle)
|
|
||||||
|
|
||||||
override fun getEntries(context: Context): Array<String> {
|
|
||||||
val ids = arrayOf(R.string.creation_date, R.string.modified_date, R.string.title)
|
|
||||||
return convertToValues(ids, context)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getSortIconResId(sortBy: String): Int {
|
|
||||||
return when (sortBy) {
|
|
||||||
autoSortByModifiedDate -> R.drawable.edit_calendar
|
|
||||||
autoSortByTitle -> R.drawable.sort_by_alpha
|
|
||||||
else -> R.drawable.calendar_add_on
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
enum class SortDirection(val textResId: Int, val iconResId: Int) {
|
|
||||||
ASC(R.string.ascending, R.drawable.arrow_upward),
|
|
||||||
DESC(R.string.descending, R.drawable.arrow_downward),
|
|
||||||
}
|
|
||||||
|
|
||||||
object BiometricLock : ListInfo {
|
|
||||||
const val enabled = "enabled"
|
|
||||||
const val disabled = "disabled"
|
|
||||||
|
|
||||||
override val title = R.string.biometric_lock
|
|
||||||
override val key = "biometricLock"
|
|
||||||
override val defaultValue = disabled
|
|
||||||
|
|
||||||
override fun getEntryValues() = arrayOf(enabled, disabled)
|
|
||||||
|
|
||||||
override fun getEntries(context: Context): Array<String> {
|
|
||||||
val ids = arrayOf(R.string.enabled, R.string.disabled)
|
|
||||||
return convertToValues(ids, context)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -3,7 +3,7 @@ package com.philkes.notallyx.presentation.view.misc
|
||||||
import androidx.lifecycle.MutableLiveData
|
import androidx.lifecycle.MutableLiveData
|
||||||
|
|
||||||
// LiveData that doesn't accept null values
|
// LiveData that doesn't accept null values
|
||||||
class BetterLiveData<T>(value: T) : MutableLiveData<T>(value) {
|
open class NotNullLiveData<T>(value: T) : MutableLiveData<T>(value) {
|
||||||
|
|
||||||
override fun getValue(): T {
|
override fun getValue(): T {
|
||||||
return requireNotNull(super.getValue())
|
return requireNotNull(super.getValue())
|
|
@ -1,69 +0,0 @@
|
||||||
package com.philkes.notallyx.presentation.view.misc
|
|
||||||
|
|
||||||
import com.philkes.notallyx.R
|
|
||||||
|
|
||||||
sealed interface SeekbarInfo {
|
|
||||||
|
|
||||||
val title: Int
|
|
||||||
|
|
||||||
val key: String
|
|
||||||
val defaultValue: Int
|
|
||||||
|
|
||||||
val min: Int
|
|
||||||
val max: Int
|
|
||||||
}
|
|
||||||
|
|
||||||
object MaxItems : SeekbarInfo {
|
|
||||||
|
|
||||||
override val title = R.string.max_items_to_display
|
|
||||||
|
|
||||||
override val key = "maxItemsToDisplayInList.v1"
|
|
||||||
override val defaultValue = 4
|
|
||||||
|
|
||||||
override val min = 1
|
|
||||||
override val max = 10
|
|
||||||
}
|
|
||||||
|
|
||||||
object MaxLines : SeekbarInfo {
|
|
||||||
|
|
||||||
override val title = R.string.max_lines_to_display
|
|
||||||
|
|
||||||
override val key = "maxLinesToDisplayInNote.v1"
|
|
||||||
override val defaultValue = 8
|
|
||||||
|
|
||||||
override val min = 1
|
|
||||||
override val max = 10
|
|
||||||
}
|
|
||||||
|
|
||||||
object MaxTitle : SeekbarInfo {
|
|
||||||
|
|
||||||
override val title = R.string.max_lines_to_display_title
|
|
||||||
|
|
||||||
override val key = "maxLinesToDisplayInTitle"
|
|
||||||
override val defaultValue = 1
|
|
||||||
|
|
||||||
override val min = 1
|
|
||||||
override val max = 10
|
|
||||||
}
|
|
||||||
|
|
||||||
object AutoBackupMax : SeekbarInfo {
|
|
||||||
|
|
||||||
override val title = R.string.max_backups
|
|
||||||
|
|
||||||
override val key = "maxBackups"
|
|
||||||
override val defaultValue = 3
|
|
||||||
|
|
||||||
override val min = 1
|
|
||||||
override val max = 10
|
|
||||||
}
|
|
||||||
|
|
||||||
object AutoBackupPeriodDays : SeekbarInfo {
|
|
||||||
|
|
||||||
override val title = R.string.backup_period_days
|
|
||||||
|
|
||||||
override val key = "autoBackupPeriodDays"
|
|
||||||
override val defaultValue = 1
|
|
||||||
|
|
||||||
override val min = 1
|
|
||||||
override val max = 31
|
|
||||||
}
|
|
|
@ -1,29 +0,0 @@
|
||||||
package com.philkes.notallyx.presentation.view.misc
|
|
||||||
|
|
||||||
import com.philkes.notallyx.R
|
|
||||||
|
|
||||||
sealed interface TextInfo {
|
|
||||||
|
|
||||||
val title: Int
|
|
||||||
|
|
||||||
val key: String
|
|
||||||
val defaultValue: String
|
|
||||||
}
|
|
||||||
|
|
||||||
object AutoBackup : TextInfo {
|
|
||||||
const val emptyPath = "emptyPath"
|
|
||||||
|
|
||||||
override val title = R.string.auto_backup
|
|
||||||
|
|
||||||
override val key = "autoBackup"
|
|
||||||
override val defaultValue = emptyPath
|
|
||||||
}
|
|
||||||
|
|
||||||
object BackupPassword : TextInfo {
|
|
||||||
const val emptyPassword = "None"
|
|
||||||
|
|
||||||
override val title = R.string.backup_password
|
|
||||||
|
|
||||||
override val key = "backupPassword"
|
|
||||||
override val defaultValue = emptyPassword
|
|
||||||
}
|
|
|
@ -4,14 +4,15 @@ import android.view.LayoutInflater
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import androidx.recyclerview.widget.ItemTouchHelper
|
import androidx.recyclerview.widget.ItemTouchHelper
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import com.philkes.notallyx.Preferences
|
|
||||||
import com.philkes.notallyx.databinding.RecyclerListItemBinding
|
import com.philkes.notallyx.databinding.RecyclerListItemBinding
|
||||||
import com.philkes.notallyx.presentation.view.note.listitem.sorting.ListItemSortedList
|
import com.philkes.notallyx.presentation.view.note.listitem.sorting.ListItemSortedList
|
||||||
|
import com.philkes.notallyx.presentation.viewmodel.preference.NotallyXPreferences
|
||||||
|
import com.philkes.notallyx.presentation.viewmodel.preference.TextSize
|
||||||
|
|
||||||
class ListItemAdapter(
|
class ListItemAdapter(
|
||||||
private val textSize: String,
|
private val textSize: TextSize,
|
||||||
elevation: Float,
|
elevation: Float,
|
||||||
private val preferences: Preferences,
|
private val preferences: NotallyXPreferences,
|
||||||
private val listManager: ListManager,
|
private val listManager: ListManager,
|
||||||
) : RecyclerView.Adapter<ListItemVH>() {
|
) : RecyclerView.Adapter<ListItemVH>() {
|
||||||
|
|
||||||
|
|
|
@ -14,22 +14,22 @@ import com.philkes.notallyx.data.model.ListItem
|
||||||
import com.philkes.notallyx.databinding.RecyclerListItemBinding
|
import com.philkes.notallyx.databinding.RecyclerListItemBinding
|
||||||
import com.philkes.notallyx.presentation.createListTextWatcherWithHistory
|
import com.philkes.notallyx.presentation.createListTextWatcherWithHistory
|
||||||
import com.philkes.notallyx.presentation.setOnNextAction
|
import com.philkes.notallyx.presentation.setOnNextAction
|
||||||
import com.philkes.notallyx.presentation.view.misc.ListItemSorting
|
|
||||||
import com.philkes.notallyx.presentation.view.misc.SwipeLayout.SwipeActionsListener
|
import com.philkes.notallyx.presentation.view.misc.SwipeLayout.SwipeActionsListener
|
||||||
import com.philkes.notallyx.presentation.view.misc.TextSize
|
import com.philkes.notallyx.presentation.viewmodel.preference.ListItemSort
|
||||||
|
import com.philkes.notallyx.presentation.viewmodel.preference.TextSize
|
||||||
|
|
||||||
class ListItemVH(
|
class ListItemVH(
|
||||||
val binding: RecyclerListItemBinding,
|
val binding: RecyclerListItemBinding,
|
||||||
val listManager: ListManager,
|
val listManager: ListManager,
|
||||||
touchHelper: ItemTouchHelper,
|
touchHelper: ItemTouchHelper,
|
||||||
textSize: String,
|
textSize: TextSize,
|
||||||
) : RecyclerView.ViewHolder(binding.root) {
|
) : RecyclerView.ViewHolder(binding.root) {
|
||||||
|
|
||||||
private var editTextWatcher: TextWatcher
|
private var editTextWatcher: TextWatcher
|
||||||
private var dragHandleInitialY: Float = 0f
|
private var dragHandleInitialY: Float = 0f
|
||||||
|
|
||||||
init {
|
init {
|
||||||
val body = TextSize.getEditBodySize(textSize)
|
val body = textSize.editBodySize
|
||||||
binding.EditText.apply {
|
binding.EditText.apply {
|
||||||
setTextSize(TypedValue.COMPLEX_UNIT_SP, body)
|
setTextSize(TypedValue.COMPLEX_UNIT_SP, body)
|
||||||
|
|
||||||
|
@ -62,7 +62,7 @@ class ListItemVH(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun bind(item: ListItem, firstItem: Boolean, autoSort: String) {
|
fun bind(item: ListItem, firstItem: Boolean, autoSort: ListItemSort) {
|
||||||
updateEditText(item)
|
updateEditText(item)
|
||||||
|
|
||||||
updateCheckBox(item)
|
updateCheckBox(item)
|
||||||
|
@ -70,7 +70,7 @@ class ListItemVH(
|
||||||
updateDeleteButton(item)
|
updateDeleteButton(item)
|
||||||
|
|
||||||
updateSwipe(item.isChild, !firstItem && !item.checked)
|
updateSwipe(item.isChild, !firstItem && !item.checked)
|
||||||
if (item.checked && autoSort == ListItemSorting.autoSortByChecked) {
|
if (item.checked && autoSort == ListItemSort.AUTO_SORT_BY_CHECKED) {
|
||||||
binding.DragHandle.visibility = INVISIBLE
|
binding.DragHandle.visibility = INVISIBLE
|
||||||
} else {
|
} else {
|
||||||
binding.DragHandle.visibility = VISIBLE
|
binding.DragHandle.visibility = VISIBLE
|
||||||
|
|
|
@ -5,11 +5,9 @@ import android.util.Log
|
||||||
import android.view.inputmethod.InputMethodManager
|
import android.view.inputmethod.InputMethodManager
|
||||||
import android.widget.EditText
|
import android.widget.EditText
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import com.philkes.notallyx.Preferences
|
|
||||||
import com.philkes.notallyx.data.model.ListItem
|
import com.philkes.notallyx.data.model.ListItem
|
||||||
import com.philkes.notallyx.data.model.areAllChecked
|
import com.philkes.notallyx.data.model.areAllChecked
|
||||||
import com.philkes.notallyx.data.model.plus
|
import com.philkes.notallyx.data.model.plus
|
||||||
import com.philkes.notallyx.presentation.view.misc.ListItemSorting
|
|
||||||
import com.philkes.notallyx.presentation.view.note.listitem.sorting.ListItemSortedList
|
import com.philkes.notallyx.presentation.view.note.listitem.sorting.ListItemSortedList
|
||||||
import com.philkes.notallyx.presentation.view.note.listitem.sorting.deleteItem
|
import com.philkes.notallyx.presentation.view.note.listitem.sorting.deleteItem
|
||||||
import com.philkes.notallyx.presentation.view.note.listitem.sorting.filter
|
import com.philkes.notallyx.presentation.view.note.listitem.sorting.filter
|
||||||
|
@ -24,6 +22,8 @@ import com.philkes.notallyx.presentation.view.note.listitem.sorting.setCheckedWi
|
||||||
import com.philkes.notallyx.presentation.view.note.listitem.sorting.setIsChild
|
import com.philkes.notallyx.presentation.view.note.listitem.sorting.setIsChild
|
||||||
import com.philkes.notallyx.presentation.view.note.listitem.sorting.shiftItemOrders
|
import com.philkes.notallyx.presentation.view.note.listitem.sorting.shiftItemOrders
|
||||||
import com.philkes.notallyx.presentation.view.note.listitem.sorting.toReadableString
|
import com.philkes.notallyx.presentation.view.note.listitem.sorting.toReadableString
|
||||||
|
import com.philkes.notallyx.presentation.viewmodel.preference.ListItemSort
|
||||||
|
import com.philkes.notallyx.presentation.viewmodel.preference.NotallyXPreferences
|
||||||
import com.philkes.notallyx.utils.changehistory.ChangeCheckedForAllChange
|
import com.philkes.notallyx.utils.changehistory.ChangeCheckedForAllChange
|
||||||
import com.philkes.notallyx.utils.changehistory.ChangeHistory
|
import com.philkes.notallyx.utils.changehistory.ChangeHistory
|
||||||
import com.philkes.notallyx.utils.changehistory.DeleteCheckedChange
|
import com.philkes.notallyx.utils.changehistory.DeleteCheckedChange
|
||||||
|
@ -41,7 +41,7 @@ import com.philkes.notallyx.utils.changehistory.ListMoveChange
|
||||||
class ListManager(
|
class ListManager(
|
||||||
private val recyclerView: RecyclerView,
|
private val recyclerView: RecyclerView,
|
||||||
private val changeHistory: ChangeHistory,
|
private val changeHistory: ChangeHistory,
|
||||||
private val preferences: Preferences,
|
private val preferences: NotallyXPreferences,
|
||||||
private val inputMethodManager: InputMethodManager,
|
private val inputMethodManager: InputMethodManager,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
|
@ -396,7 +396,7 @@ class ListManager(
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun isAutoSortByCheckedEnabled() =
|
private fun isAutoSortByCheckedEnabled() =
|
||||||
preferences.listItemSorting.value == ListItemSorting.autoSortByChecked
|
preferences.listItemSorting.value == ListItemSort.AUTO_SORT_BY_CHECKED
|
||||||
|
|
||||||
private val Int.isBeforeChildItemOfOtherParent: Boolean
|
private val Int.isBeforeChildItemOfOtherParent: Boolean
|
||||||
get() {
|
get() {
|
||||||
|
|
|
@ -13,7 +13,6 @@ import androidx.lifecycle.MutableLiveData
|
||||||
import androidx.lifecycle.Observer
|
import androidx.lifecycle.Observer
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import androidx.room.withTransaction
|
import androidx.room.withTransaction
|
||||||
import com.philkes.notallyx.Preferences
|
|
||||||
import com.philkes.notallyx.R
|
import com.philkes.notallyx.R
|
||||||
import com.philkes.notallyx.data.NotallyDatabase
|
import com.philkes.notallyx.data.NotallyDatabase
|
||||||
import com.philkes.notallyx.data.dao.BaseNoteDao
|
import com.philkes.notallyx.data.dao.BaseNoteDao
|
||||||
|
@ -38,10 +37,10 @@ import com.philkes.notallyx.data.model.SearchResult
|
||||||
import com.philkes.notallyx.data.model.Type
|
import com.philkes.notallyx.data.model.Type
|
||||||
import com.philkes.notallyx.presentation.applySpans
|
import com.philkes.notallyx.presentation.applySpans
|
||||||
import com.philkes.notallyx.presentation.getQuantityString
|
import com.philkes.notallyx.presentation.getQuantityString
|
||||||
import com.philkes.notallyx.presentation.view.misc.AutoBackup
|
|
||||||
import com.philkes.notallyx.presentation.view.misc.ListInfo
|
|
||||||
import com.philkes.notallyx.presentation.view.misc.Progress
|
import com.philkes.notallyx.presentation.view.misc.Progress
|
||||||
import com.philkes.notallyx.presentation.view.misc.SeekbarInfo
|
import com.philkes.notallyx.presentation.viewmodel.preference.BasePreference
|
||||||
|
import com.philkes.notallyx.presentation.viewmodel.preference.Constants.BACKUP_PATH_EMPTY
|
||||||
|
import com.philkes.notallyx.presentation.viewmodel.preference.NotallyXPreferences
|
||||||
import com.philkes.notallyx.utils.ActionMode
|
import com.philkes.notallyx.utils.ActionMode
|
||||||
import com.philkes.notallyx.utils.Cache
|
import com.philkes.notallyx.utils.Cache
|
||||||
import com.philkes.notallyx.utils.IO.deleteAttachments
|
import com.philkes.notallyx.utils.IO.deleteAttachments
|
||||||
|
@ -105,7 +104,7 @@ class BaseNoteModel(private val app: Application) : AndroidViewModel(app) {
|
||||||
private val pinned = Header(app.getString(R.string.pinned))
|
private val pinned = Header(app.getString(R.string.pinned))
|
||||||
private val others = Header(app.getString(R.string.others))
|
private val others = Header(app.getString(R.string.others))
|
||||||
|
|
||||||
val preferences = Preferences.getInstance(app)
|
val preferences = NotallyXPreferences.getInstance(app)
|
||||||
|
|
||||||
val imageRoot = app.getExternalImagesDirectory()
|
val imageRoot = app.getExternalImagesDirectory()
|
||||||
val fileRoot = app.getExternalFilesDirectory()
|
val fileRoot = app.getExternalFilesDirectory()
|
||||||
|
@ -181,24 +180,20 @@ class BaseNoteModel(private val app: Application) : AndroidViewModel(app) {
|
||||||
|
|
||||||
private fun transform(list: List<BaseNote>) = transform(list, pinned, others)
|
private fun transform(list: List<BaseNote>) = transform(list, pinned, others)
|
||||||
|
|
||||||
fun savePreference(info: SeekbarInfo, value: Int) = executeAsync {
|
|
||||||
preferences.savePreference(info, value)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun savePreference(info: ListInfo, value: String) = executeAsync {
|
|
||||||
preferences.savePreference(info, value)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun disableAutoBackup() {
|
fun disableAutoBackup() {
|
||||||
clearPersistedUriPermissions()
|
clearPersistedUriPermissions()
|
||||||
executeAsync { preferences.savePreference(AutoBackup, AutoBackup.emptyPath) }
|
savePreference(preferences.autoBackupPath, BACKUP_PATH_EMPTY)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setAutoBackupPath(uri: Uri) {
|
fun setAutoBackupPath(uri: Uri) {
|
||||||
clearPersistedUriPermissions()
|
clearPersistedUriPermissions()
|
||||||
val flags = Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION
|
val flags = Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION
|
||||||
app.contentResolver.takePersistableUriPermission(uri, flags)
|
app.contentResolver.takePersistableUriPermission(uri, flags)
|
||||||
executeAsync { preferences.savePreference(AutoBackup, uri.toString()) }
|
savePreference(preferences.autoBackupPath, uri.toString())
|
||||||
|
}
|
||||||
|
|
||||||
|
fun <T> savePreference(preference: BasePreference<T>, value: T) {
|
||||||
|
executeAsync { preference.save(value) }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -16,7 +16,6 @@ import androidx.core.text.getSpans
|
||||||
import androidx.lifecycle.AndroidViewModel
|
import androidx.lifecycle.AndroidViewModel
|
||||||
import androidx.lifecycle.MutableLiveData
|
import androidx.lifecycle.MutableLiveData
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import com.philkes.notallyx.Preferences
|
|
||||||
import com.philkes.notallyx.R
|
import com.philkes.notallyx.R
|
||||||
import com.philkes.notallyx.data.DataUtil
|
import com.philkes.notallyx.data.DataUtil
|
||||||
import com.philkes.notallyx.data.NotallyDatabase
|
import com.philkes.notallyx.data.NotallyDatabase
|
||||||
|
@ -30,8 +29,9 @@ import com.philkes.notallyx.data.model.ListItem
|
||||||
import com.philkes.notallyx.data.model.SpanRepresentation
|
import com.philkes.notallyx.data.model.SpanRepresentation
|
||||||
import com.philkes.notallyx.data.model.Type
|
import com.philkes.notallyx.data.model.Type
|
||||||
import com.philkes.notallyx.presentation.applySpans
|
import com.philkes.notallyx.presentation.applySpans
|
||||||
import com.philkes.notallyx.presentation.view.misc.BetterLiveData
|
import com.philkes.notallyx.presentation.view.misc.NotNullLiveData
|
||||||
import com.philkes.notallyx.presentation.view.misc.Progress
|
import com.philkes.notallyx.presentation.view.misc.Progress
|
||||||
|
import com.philkes.notallyx.presentation.viewmodel.preference.NotallyXPreferences
|
||||||
import com.philkes.notallyx.presentation.widget.WidgetProvider
|
import com.philkes.notallyx.presentation.widget.WidgetProvider
|
||||||
import com.philkes.notallyx.utils.Cache
|
import com.philkes.notallyx.utils.Cache
|
||||||
import com.philkes.notallyx.utils.Event
|
import com.philkes.notallyx.utils.Event
|
||||||
|
@ -51,7 +51,7 @@ class NotallyModel(private val app: Application) : AndroidViewModel(app) {
|
||||||
private val database = NotallyDatabase.getDatabase(app)
|
private val database = NotallyDatabase.getDatabase(app)
|
||||||
private lateinit var baseNoteDao: BaseNoteDao
|
private lateinit var baseNoteDao: BaseNoteDao
|
||||||
|
|
||||||
val textSize = Preferences.getInstance(app).textSize.value
|
val textSize = NotallyXPreferences.getInstance(app).textSize.value
|
||||||
|
|
||||||
var isNewNote = true
|
var isNewNote = true
|
||||||
|
|
||||||
|
@ -71,9 +71,9 @@ class NotallyModel(private val app: Application) : AndroidViewModel(app) {
|
||||||
var body: Editable = SpannableStringBuilder()
|
var body: Editable = SpannableStringBuilder()
|
||||||
|
|
||||||
val items = ArrayList<ListItem>()
|
val items = ArrayList<ListItem>()
|
||||||
val images = BetterLiveData<List<FileAttachment>>(emptyList())
|
val images = NotNullLiveData<List<FileAttachment>>(emptyList())
|
||||||
val files = BetterLiveData<List<FileAttachment>>(emptyList())
|
val files = NotNullLiveData<List<FileAttachment>>(emptyList())
|
||||||
val audios = BetterLiveData<List<Audio>>(emptyList())
|
val audios = NotNullLiveData<List<Audio>>(emptyList())
|
||||||
|
|
||||||
val addingFiles = MutableLiveData<Progress>()
|
val addingFiles = MutableLiveData<Progress>()
|
||||||
val eventBus = MutableLiveData<Event<List<FileError>>>()
|
val eventBus = MutableLiveData<Event<List<FileError>>>()
|
||||||
|
|
|
@ -0,0 +1,157 @@
|
||||||
|
package com.philkes.notallyx.presentation.viewmodel.preference
|
||||||
|
|
||||||
|
import android.app.Application
|
||||||
|
import androidx.core.content.edit
|
||||||
|
import androidx.preference.PreferenceManager
|
||||||
|
import androidx.security.crypto.EncryptedSharedPreferences
|
||||||
|
import androidx.security.crypto.MasterKey
|
||||||
|
import com.philkes.notallyx.R
|
||||||
|
import com.philkes.notallyx.data.model.Type
|
||||||
|
import com.philkes.notallyx.presentation.viewmodel.preference.Constants.BACKUP_PATH_EMPTY
|
||||||
|
import com.philkes.notallyx.presentation.viewmodel.preference.Constants.PASSWORD_EMPTY
|
||||||
|
|
||||||
|
class NotallyXPreferences private constructor(app: Application) {
|
||||||
|
|
||||||
|
private val preferences = PreferenceManager.getDefaultSharedPreferences(app)
|
||||||
|
|
||||||
|
private val encryptedPreferences by lazy {
|
||||||
|
EncryptedSharedPreferences.create(
|
||||||
|
app,
|
||||||
|
"secret_shared_prefs",
|
||||||
|
MasterKey.Builder(app).setKeyScheme(MasterKey.KeyScheme.AES256_GCM).build(),
|
||||||
|
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
|
||||||
|
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Main thread (unfortunately)
|
||||||
|
val theme = createEnumPreference(preferences, "theme", Theme.FOLLOW_SYSTEM, R.string.theme)
|
||||||
|
val textSize =
|
||||||
|
createEnumPreference(preferences, "textSize", TextSize.MEDIUM, R.string.text_size)
|
||||||
|
val dateFormat =
|
||||||
|
createEnumPreference(preferences, "dateFormat", DateFormat.RELATIVE, R.string.date_format)
|
||||||
|
|
||||||
|
val notesView = createEnumPreference(preferences, "view", NotesView.LIST, R.string.view)
|
||||||
|
val notesSorting = NotesSortPreference(preferences)
|
||||||
|
val listItemSorting =
|
||||||
|
createEnumPreference(
|
||||||
|
preferences,
|
||||||
|
"checkedListItemSorting",
|
||||||
|
ListItemSort.NO_AUTO_SORT,
|
||||||
|
R.string.checked_list_item_sorting,
|
||||||
|
)
|
||||||
|
|
||||||
|
val maxItems =
|
||||||
|
IntPreference(
|
||||||
|
"maxItemsToDisplayInList.v1",
|
||||||
|
preferences,
|
||||||
|
4,
|
||||||
|
1,
|
||||||
|
10,
|
||||||
|
R.string.max_items_to_display,
|
||||||
|
)
|
||||||
|
val maxLines =
|
||||||
|
IntPreference(
|
||||||
|
"maxLinesToDisplayInNote.v1",
|
||||||
|
preferences,
|
||||||
|
8,
|
||||||
|
1,
|
||||||
|
10,
|
||||||
|
R.string.max_lines_to_display,
|
||||||
|
)
|
||||||
|
val maxTitle =
|
||||||
|
IntPreference(
|
||||||
|
"maxLinesToDisplayInTitle",
|
||||||
|
preferences,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
10,
|
||||||
|
R.string.max_lines_to_display_title,
|
||||||
|
)
|
||||||
|
|
||||||
|
val autoBackupPath =
|
||||||
|
StringPreference("autoBackup", preferences, BACKUP_PATH_EMPTY, R.string.auto_backup)
|
||||||
|
val autoBackupPeriodDays =
|
||||||
|
IntPreference("autoBackupPeriodDays", preferences, 1, 1, 31, R.string.backup_period_days)
|
||||||
|
val autoBackupMax = IntPreference("maxBackups", preferences, 3, 1, 10, R.string.max_backups)
|
||||||
|
|
||||||
|
val backupPassword by lazy {
|
||||||
|
StringPreference(
|
||||||
|
"backupPassword",
|
||||||
|
encryptedPreferences,
|
||||||
|
PASSWORD_EMPTY,
|
||||||
|
R.string.backup_password,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
val biometricLock =
|
||||||
|
createEnumPreference(
|
||||||
|
preferences,
|
||||||
|
"biometricLock",
|
||||||
|
BiometricLock.DISABLED,
|
||||||
|
R.string.biometric_lock,
|
||||||
|
)
|
||||||
|
|
||||||
|
val iv = ByteArrayPreference("encryption_iv", preferences, null)
|
||||||
|
val databaseEncryptionKey =
|
||||||
|
EncryptedPassphrasePreference("database_encryption_key", preferences, ByteArray(0))
|
||||||
|
|
||||||
|
fun getWidgetData(id: Int) = preferences.getLong("widget:$id", 0)
|
||||||
|
|
||||||
|
fun getWidgetNoteType(id: Int) =
|
||||||
|
preferences.getString("widgetNoteType:$id", null)?.let { Type.valueOf(it) }
|
||||||
|
|
||||||
|
fun deleteWidget(id: Int) {
|
||||||
|
preferences.edit(true) {
|
||||||
|
remove("widget:$id")
|
||||||
|
remove("widgetNoteType:$id")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun updateWidget(id: Int, noteId: Long, noteType: Type) {
|
||||||
|
preferences.edit(true) {
|
||||||
|
putLong("widget:$id", noteId)
|
||||||
|
putString("widgetNoteType:$id", noteType.name)
|
||||||
|
commit()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getUpdatableWidgets(noteIds: LongArray? = null): List<Pair<Int, Long>> {
|
||||||
|
val updatableWidgets = ArrayList<Pair<Int, Long>>()
|
||||||
|
val pairs = preferences.all
|
||||||
|
pairs.keys.forEach { key ->
|
||||||
|
val token = "widget:"
|
||||||
|
if (key.startsWith(token)) {
|
||||||
|
val end = key.substringAfter(token)
|
||||||
|
val id = end.toIntOrNull()
|
||||||
|
if (id != null) {
|
||||||
|
val value = pairs[key] as? Long
|
||||||
|
if (value != null) {
|
||||||
|
if (noteIds == null || noteIds.contains(value)) {
|
||||||
|
updatableWidgets.add(Pair(id, value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return updatableWidgets
|
||||||
|
}
|
||||||
|
|
||||||
|
fun showDateCreated(): Boolean {
|
||||||
|
return dateFormat.value != DateFormat.NONE
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
|
||||||
|
@Volatile private var instance: NotallyXPreferences? = null
|
||||||
|
|
||||||
|
fun getInstance(app: Application): NotallyXPreferences {
|
||||||
|
return instance
|
||||||
|
?: synchronized(this) {
|
||||||
|
val instance = NotallyXPreferences(app)
|
||||||
|
Companion.instance = instance
|
||||||
|
return instance
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,68 @@
|
||||||
|
package com.philkes.notallyx.presentation.viewmodel.preference
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.SharedPreferences
|
||||||
|
import android.content.SharedPreferences.Editor
|
||||||
|
import com.philkes.notallyx.R
|
||||||
|
|
||||||
|
class NotesSortPreference(sharedPreferences: SharedPreferences) :
|
||||||
|
BasePreference<NotesSort>(sharedPreferences, NotesSort(), R.string.notes_sorted_by) {
|
||||||
|
|
||||||
|
override fun getValue(sharedPreferences: SharedPreferences): NotesSort {
|
||||||
|
val sortedByName = sharedPreferences.getString(SORTED_BY_KEY, null)
|
||||||
|
val sortedBy =
|
||||||
|
sortedByName?.let {
|
||||||
|
try {
|
||||||
|
NotesSortBy.fromValue(sortedByName)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
defaultValue.sortedBy
|
||||||
|
}
|
||||||
|
} ?: defaultValue.sortedBy
|
||||||
|
val sortDirectionName = sharedPreferences.getString(SORT_DIRECTION_KEY, null)
|
||||||
|
val sortDirection =
|
||||||
|
sortDirectionName?.let {
|
||||||
|
try {
|
||||||
|
SortDirection.valueOf(sortDirectionName)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
defaultValue.sortDirection
|
||||||
|
}
|
||||||
|
} ?: defaultValue.sortDirection
|
||||||
|
return NotesSort(sortedBy, sortDirection)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun Editor.put(value: NotesSort) {
|
||||||
|
putString(SORTED_BY_KEY, value.sortedBy.value)
|
||||||
|
putString(SORT_DIRECTION_KEY, value.sortDirection.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val SORTED_BY_KEY = "notesSorting"
|
||||||
|
const val SORT_DIRECTION_KEY = "notesSortingDirection"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class SortDirection(val textResId: Int, val iconResId: Int) {
|
||||||
|
ASC(R.string.ascending, R.drawable.arrow_upward),
|
||||||
|
DESC(R.string.descending, R.drawable.arrow_downward),
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class NotesSortBy(val textResId: Int, val iconResId: Int, val value: String) {
|
||||||
|
TITLE(R.string.title, R.drawable.sort_by_alpha, "autoSortByTitle"),
|
||||||
|
CREATION_DATE(R.string.creation_date, R.drawable.calendar_add_on, "autoSortByCreationDate"),
|
||||||
|
MODIFIED_DATE(R.string.modified_date, R.drawable.edit_calendar, "autoSortByModifiedDate");
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun fromValue(value: String): NotesSortBy? {
|
||||||
|
return entries.find { it.value == value }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data class NotesSort(
|
||||||
|
val sortedBy: NotesSortBy = NotesSortBy.CREATION_DATE,
|
||||||
|
val sortDirection: SortDirection = SortDirection.DESC,
|
||||||
|
) : TextProvider {
|
||||||
|
override fun getText(context: Context): String {
|
||||||
|
return "${context.getString(sortedBy.textResId)} (${context.getString(sortDirection.textResId)})"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,322 @@
|
||||||
|
package com.philkes.notallyx.presentation.viewmodel.preference
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.SharedPreferences
|
||||||
|
import android.os.Build
|
||||||
|
import androidx.core.content.edit
|
||||||
|
import androidx.lifecycle.LifecycleOwner
|
||||||
|
import androidx.lifecycle.Observer
|
||||||
|
import com.philkes.notallyx.R
|
||||||
|
import com.philkes.notallyx.data.model.toPreservedByteArray
|
||||||
|
import com.philkes.notallyx.data.model.toPreservedString
|
||||||
|
import com.philkes.notallyx.presentation.view.misc.NotNullLiveData
|
||||||
|
import java.security.SecureRandom
|
||||||
|
import java.util.Date
|
||||||
|
import java.util.Locale
|
||||||
|
import javax.crypto.Cipher
|
||||||
|
import org.ocpsoft.prettytime.PrettyTime
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Every Preference can be observed like a [NotNullLiveData].
|
||||||
|
*
|
||||||
|
* @param titleResId Optional string resource id, if preference can be set via the UI.
|
||||||
|
*/
|
||||||
|
abstract class BasePreference<T>(
|
||||||
|
private val sharedPreferences: SharedPreferences,
|
||||||
|
protected val defaultValue: T,
|
||||||
|
val titleResId: Int? = null,
|
||||||
|
) {
|
||||||
|
private var data: NotNullLiveData<T>? = null
|
||||||
|
private var cachedValue: T? = null
|
||||||
|
|
||||||
|
val value: T
|
||||||
|
get() {
|
||||||
|
if (cachedValue == null) {
|
||||||
|
cachedValue = getValue(sharedPreferences)
|
||||||
|
}
|
||||||
|
return cachedValue!!
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract fun getValue(sharedPreferences: SharedPreferences): T
|
||||||
|
|
||||||
|
private fun getData(): NotNullLiveData<T> {
|
||||||
|
if (data == null) {
|
||||||
|
data = NotNullLiveData(value)
|
||||||
|
}
|
||||||
|
return data as NotNullLiveData<T>
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun save(value: T) {
|
||||||
|
sharedPreferences.edit(true) { put(value) }
|
||||||
|
cachedValue = value
|
||||||
|
getData().postValue(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract fun SharedPreferences.Editor.put(value: T)
|
||||||
|
|
||||||
|
fun observe(lifecycleOwner: LifecycleOwner, observer: Observer<T>) {
|
||||||
|
getData().observe(lifecycleOwner, observer)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun observeForever(observer: Observer<T>) {
|
||||||
|
getData().observeForever(observer)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun removeObserver(observer: Observer<T>) {
|
||||||
|
getData().removeObserver(observer)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun removeObservers(lifecycleOwner: LifecycleOwner) {
|
||||||
|
getData().removeObservers(lifecycleOwner)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun <T> BasePreference<T>.observeForeverSkipFirst(observer: Observer<T>) {
|
||||||
|
var isFirstEvent = true
|
||||||
|
this.observeForever { value ->
|
||||||
|
if (isFirstEvent) {
|
||||||
|
isFirstEvent = false
|
||||||
|
} else {
|
||||||
|
observer.onChanged(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface TextProvider {
|
||||||
|
fun getText(context: Context): String
|
||||||
|
}
|
||||||
|
|
||||||
|
interface StaticTextProvider : TextProvider {
|
||||||
|
val textResId: Int
|
||||||
|
|
||||||
|
override fun getText(context: Context): String {
|
||||||
|
return context.getString(textResId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class EnumPreference<T>(
|
||||||
|
sharedPreferences: SharedPreferences,
|
||||||
|
private val key: String,
|
||||||
|
defaultValue: T,
|
||||||
|
private val enumClass: Class<T>,
|
||||||
|
titleResId: Int? = null,
|
||||||
|
) : BasePreference<T>(sharedPreferences, defaultValue, titleResId) where
|
||||||
|
T : Enum<T>,
|
||||||
|
T : TextProvider {
|
||||||
|
|
||||||
|
override fun getValue(sharedPreferences: SharedPreferences): T {
|
||||||
|
val storedValue = sharedPreferences.getString(key, null)
|
||||||
|
return try {
|
||||||
|
storedValue?.let { java.lang.Enum.valueOf(enumClass, it.fromCamelCaseToEnumName()) }
|
||||||
|
?: defaultValue
|
||||||
|
} catch (e: IllegalArgumentException) {
|
||||||
|
defaultValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun SharedPreferences.Editor.put(value: T) {
|
||||||
|
putString(key, value.name.toCamelCase())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fun <reified T> createEnumPreference(
|
||||||
|
sharedPreferences: SharedPreferences,
|
||||||
|
key: String,
|
||||||
|
defaultValue: T,
|
||||||
|
titleResId: Int? = null,
|
||||||
|
): EnumPreference<T> where T : Enum<T>, T : TextProvider {
|
||||||
|
return EnumPreference(sharedPreferences, key, defaultValue, T::class.java, titleResId)
|
||||||
|
}
|
||||||
|
|
||||||
|
class IntPreference(
|
||||||
|
private val key: String,
|
||||||
|
sharedPreferences: SharedPreferences,
|
||||||
|
defaultValue: Int,
|
||||||
|
val min: Int,
|
||||||
|
val max: Int,
|
||||||
|
titleResId: Int? = null,
|
||||||
|
) : BasePreference<Int>(sharedPreferences, defaultValue, titleResId) {
|
||||||
|
|
||||||
|
override fun getValue(sharedPreferences: SharedPreferences): Int {
|
||||||
|
return sharedPreferences.getInt(key, defaultValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun SharedPreferences.Editor.put(value: Int) {
|
||||||
|
putInt(key, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class StringPreference(
|
||||||
|
private val key: String,
|
||||||
|
sharedPreferences: SharedPreferences,
|
||||||
|
defaultValue: String,
|
||||||
|
titleResId: Int? = null,
|
||||||
|
) : BasePreference<String>(sharedPreferences, defaultValue, titleResId) {
|
||||||
|
|
||||||
|
override fun getValue(sharedPreferences: SharedPreferences): String {
|
||||||
|
return sharedPreferences.getString(key, defaultValue)!!
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun SharedPreferences.Editor.put(value: String) {
|
||||||
|
putString(key, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ByteArrayPreference(
|
||||||
|
private val key: String,
|
||||||
|
sharedPreferences: SharedPreferences,
|
||||||
|
defaultValue: ByteArray?,
|
||||||
|
titleResId: Int? = null,
|
||||||
|
) : BasePreference<ByteArray?>(sharedPreferences, defaultValue, titleResId) {
|
||||||
|
|
||||||
|
override fun getValue(sharedPreferences: SharedPreferences): ByteArray? {
|
||||||
|
return sharedPreferences.getString(key, null)?.toPreservedByteArray ?: defaultValue
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun SharedPreferences.Editor.put(value: ByteArray?) {
|
||||||
|
putString(key, value?.toPreservedString)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class EncryptedPassphrasePreference(
|
||||||
|
private val key: String,
|
||||||
|
sharedPreferences: SharedPreferences,
|
||||||
|
defaultValue: ByteArray,
|
||||||
|
titleResId: Int? = null,
|
||||||
|
) : BasePreference<ByteArray>(sharedPreferences, defaultValue, titleResId) {
|
||||||
|
|
||||||
|
override fun getValue(sharedPreferences: SharedPreferences): ByteArray {
|
||||||
|
return sharedPreferences.getString(key, null)?.toPreservedByteArray ?: defaultValue
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun SharedPreferences.Editor.put(value: ByteArray) {
|
||||||
|
putString(key, value.toPreservedString)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun init(cipher: Cipher): ByteArray {
|
||||||
|
val random =
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
|
SecureRandom.getInstanceStrong()
|
||||||
|
} else {
|
||||||
|
SecureRandom()
|
||||||
|
}
|
||||||
|
val result = ByteArray(64)
|
||||||
|
|
||||||
|
random.nextBytes(result)
|
||||||
|
|
||||||
|
// filter out zero byte values, as SQLCipher does not like them
|
||||||
|
while (result.contains(0)) {
|
||||||
|
random.nextBytes(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
val encryptedPassphrase = cipher.doFinal(result)
|
||||||
|
save(encryptedPassphrase)
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class NotesView(override val textResId: Int) : StaticTextProvider {
|
||||||
|
LIST(R.string.list),
|
||||||
|
GRID(R.string.grid),
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class Theme(override val textResId: Int) : StaticTextProvider {
|
||||||
|
DARK(R.string.dark),
|
||||||
|
LIGHT(R.string.light),
|
||||||
|
FOLLOW_SYSTEM(R.string.follow_system),
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class DateFormat : TextProvider {
|
||||||
|
NONE,
|
||||||
|
RELATIVE,
|
||||||
|
ABSOLUTE;
|
||||||
|
|
||||||
|
override fun getText(context: Context): String {
|
||||||
|
val date = Date(System.currentTimeMillis() - 86400000)
|
||||||
|
return when (this) {
|
||||||
|
NONE -> context.getString(R.string.none)
|
||||||
|
RELATIVE -> PrettyTime().format(date)
|
||||||
|
ABSOLUTE -> java.text.DateFormat.getDateInstance(java.text.DateFormat.FULL).format(date)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class TextSize(override val textResId: Int) : StaticTextProvider {
|
||||||
|
SMALL(R.string.small),
|
||||||
|
MEDIUM(R.string.medium),
|
||||||
|
LARGE(R.string.large);
|
||||||
|
|
||||||
|
val editBodySize: Float
|
||||||
|
get() {
|
||||||
|
return when (this) {
|
||||||
|
SMALL -> 14f
|
||||||
|
MEDIUM -> 16f
|
||||||
|
LARGE -> 18f
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val editTitleSize: Float
|
||||||
|
get() {
|
||||||
|
return when (this) {
|
||||||
|
SMALL -> 18f
|
||||||
|
MEDIUM -> 20f
|
||||||
|
LARGE -> 22f
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val displayBodySize: Float
|
||||||
|
get() {
|
||||||
|
return when (this) {
|
||||||
|
SMALL -> 12f
|
||||||
|
MEDIUM -> 14f
|
||||||
|
LARGE -> 16f
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val displayTitleSize: Float
|
||||||
|
get() {
|
||||||
|
return when (this) {
|
||||||
|
SMALL -> 14f
|
||||||
|
MEDIUM -> 16f
|
||||||
|
LARGE -> 18f
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class ListItemSort(override val textResId: Int) : StaticTextProvider {
|
||||||
|
NO_AUTO_SORT(R.string.no_auto_sort),
|
||||||
|
AUTO_SORT_BY_CHECKED(R.string.auto_sort_by_checked),
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class BiometricLock(override val textResId: Int) : StaticTextProvider {
|
||||||
|
ENABLED(R.string.enabled),
|
||||||
|
DISABLED(R.string.disabled),
|
||||||
|
}
|
||||||
|
|
||||||
|
object Constants {
|
||||||
|
const val BACKUP_PATH_EMPTY = "emptyPath"
|
||||||
|
const val PASSWORD_EMPTY = "None"
|
||||||
|
}
|
||||||
|
|
||||||
|
fun String.toCamelCase(): String {
|
||||||
|
return this.lowercase()
|
||||||
|
.split("_")
|
||||||
|
.mapIndexed { index, word ->
|
||||||
|
if (index == 0) word
|
||||||
|
else
|
||||||
|
word.replaceFirstChar {
|
||||||
|
if (it.isLowerCase()) it.titlecase(Locale.ROOT) else it.toString()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.joinToString("")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun String.fromCamelCaseToEnumName(): String {
|
||||||
|
return this.fold(StringBuilder()) { acc, char ->
|
||||||
|
if (char.isUpperCase() && acc.isNotEmpty()) {
|
||||||
|
acc.append("_")
|
||||||
|
}
|
||||||
|
acc.append(char.uppercase())
|
||||||
|
}
|
||||||
|
.toString()
|
||||||
|
}
|
|
@ -8,12 +8,11 @@ import android.view.View
|
||||||
import android.widget.RemoteViews
|
import android.widget.RemoteViews
|
||||||
import android.widget.RemoteViewsService
|
import android.widget.RemoteViewsService
|
||||||
import com.philkes.notallyx.NotallyXApplication
|
import com.philkes.notallyx.NotallyXApplication
|
||||||
import com.philkes.notallyx.Preferences
|
|
||||||
import com.philkes.notallyx.R
|
import com.philkes.notallyx.R
|
||||||
import com.philkes.notallyx.data.NotallyDatabase
|
import com.philkes.notallyx.data.NotallyDatabase
|
||||||
import com.philkes.notallyx.data.model.BaseNote
|
import com.philkes.notallyx.data.model.BaseNote
|
||||||
import com.philkes.notallyx.data.model.Type
|
import com.philkes.notallyx.data.model.Type
|
||||||
import com.philkes.notallyx.presentation.view.misc.TextSize
|
import com.philkes.notallyx.presentation.viewmodel.preference.NotallyXPreferences
|
||||||
import com.philkes.notallyx.presentation.widget.WidgetProvider.Companion.getSelectNoteIntent
|
import com.philkes.notallyx.presentation.widget.WidgetProvider.Companion.getSelectNoteIntent
|
||||||
|
|
||||||
class WidgetFactory(
|
class WidgetFactory(
|
||||||
|
@ -24,7 +23,7 @@ class WidgetFactory(
|
||||||
|
|
||||||
private var baseNote: BaseNote? = null
|
private var baseNote: BaseNote? = null
|
||||||
private lateinit var database: NotallyDatabase
|
private lateinit var database: NotallyDatabase
|
||||||
private val preferences = Preferences.getInstance(app)
|
private val preferences = NotallyXPreferences.getInstance(app)
|
||||||
|
|
||||||
init {
|
init {
|
||||||
NotallyDatabase.getDatabase(app).observeForever { database = it }
|
NotallyDatabase.getDatabase(app).observeForever { database = it }
|
||||||
|
@ -64,14 +63,11 @@ class WidgetFactory(
|
||||||
|
|
||||||
private fun getNoteView(note: BaseNote): RemoteViews {
|
private fun getNoteView(note: BaseNote): RemoteViews {
|
||||||
return RemoteViews(app.packageName, R.layout.widget_note).apply {
|
return RemoteViews(app.packageName, R.layout.widget_note).apply {
|
||||||
setTextViewTextSize(
|
val textSize = preferences.textSize.value
|
||||||
R.id.Title,
|
setTextViewTextSize(R.id.Title, TypedValue.COMPLEX_UNIT_SP, textSize.displayTitleSize)
|
||||||
TypedValue.COMPLEX_UNIT_SP,
|
|
||||||
TextSize.getDisplayTitleSize(preferences.textSize.value),
|
|
||||||
)
|
|
||||||
setTextViewText(R.id.Title, note.title)
|
setTextViewText(R.id.Title, note.title)
|
||||||
|
|
||||||
val bodyTextSize = TextSize.getDisplayBodySize(preferences.textSize.value)
|
val bodyTextSize = textSize.displayBodySize
|
||||||
|
|
||||||
setTextViewTextSize(R.id.Note, TypedValue.COMPLEX_UNIT_SP, bodyTextSize)
|
setTextViewTextSize(R.id.Note, TypedValue.COMPLEX_UNIT_SP, bodyTextSize)
|
||||||
if (note.body.isNotEmpty()) {
|
if (note.body.isNotEmpty()) {
|
||||||
|
@ -91,7 +87,7 @@ class WidgetFactory(
|
||||||
setTextViewTextSize(
|
setTextViewTextSize(
|
||||||
R.id.Title,
|
R.id.Title,
|
||||||
TypedValue.COMPLEX_UNIT_SP,
|
TypedValue.COMPLEX_UNIT_SP,
|
||||||
TextSize.getDisplayTitleSize(preferences.textSize.value),
|
preferences.textSize.value.displayTitleSize,
|
||||||
)
|
)
|
||||||
setTextViewText(R.id.Title, list.title)
|
setTextViewText(R.id.Title, list.title)
|
||||||
|
|
||||||
|
@ -116,7 +112,7 @@ class WidgetFactory(
|
||||||
setTextViewTextSize(
|
setTextViewTextSize(
|
||||||
R.id.CheckBox,
|
R.id.CheckBox,
|
||||||
TypedValue.COMPLEX_UNIT_SP,
|
TypedValue.COMPLEX_UNIT_SP,
|
||||||
TextSize.getDisplayBodySize(preferences.textSize.value),
|
preferences.textSize.value.displayBodySize,
|
||||||
)
|
)
|
||||||
setTextViewText(R.id.CheckBox, item.body)
|
setTextViewText(R.id.CheckBox, item.body)
|
||||||
setInt(
|
setInt(
|
||||||
|
|
|
@ -9,7 +9,6 @@ import android.content.Intent
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.widget.RemoteViews
|
import android.widget.RemoteViews
|
||||||
import com.philkes.notallyx.Preferences
|
|
||||||
import com.philkes.notallyx.R
|
import com.philkes.notallyx.R
|
||||||
import com.philkes.notallyx.data.NotallyDatabase
|
import com.philkes.notallyx.data.NotallyDatabase
|
||||||
import com.philkes.notallyx.data.dao.BaseNoteDao
|
import com.philkes.notallyx.data.dao.BaseNoteDao
|
||||||
|
@ -21,6 +20,7 @@ import com.philkes.notallyx.presentation.activity.ConfigureWidgetActivity
|
||||||
import com.philkes.notallyx.presentation.activity.note.EditListActivity
|
import com.philkes.notallyx.presentation.activity.note.EditListActivity
|
||||||
import com.philkes.notallyx.presentation.activity.note.EditNoteActivity
|
import com.philkes.notallyx.presentation.activity.note.EditNoteActivity
|
||||||
import com.philkes.notallyx.presentation.view.Constants
|
import com.philkes.notallyx.presentation.view.Constants
|
||||||
|
import com.philkes.notallyx.presentation.viewmodel.preference.NotallyXPreferences
|
||||||
import kotlinx.coroutines.DelicateCoroutinesApi
|
import kotlinx.coroutines.DelicateCoroutinesApi
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.GlobalScope
|
import kotlinx.coroutines.GlobalScope
|
||||||
|
@ -110,7 +110,7 @@ class WidgetProvider : AppWidgetProvider() {
|
||||||
|
|
||||||
override fun onDeleted(context: Context, appWidgetIds: IntArray) {
|
override fun onDeleted(context: Context, appWidgetIds: IntArray) {
|
||||||
val app = context.applicationContext as Application
|
val app = context.applicationContext as Application
|
||||||
val preferences = Preferences.getInstance(app)
|
val preferences = NotallyXPreferences.getInstance(app)
|
||||||
|
|
||||||
appWidgetIds.forEach { id -> preferences.deleteWidget(id) }
|
appWidgetIds.forEach { id -> preferences.deleteWidget(id) }
|
||||||
}
|
}
|
||||||
|
@ -121,7 +121,7 @@ class WidgetProvider : AppWidgetProvider() {
|
||||||
appWidgetIds: IntArray,
|
appWidgetIds: IntArray,
|
||||||
) {
|
) {
|
||||||
val app = context.applicationContext as Application
|
val app = context.applicationContext as Application
|
||||||
val preferences = Preferences.getInstance(app)
|
val preferences = NotallyXPreferences.getInstance(app)
|
||||||
|
|
||||||
appWidgetIds.forEach { id ->
|
appWidgetIds.forEach { id ->
|
||||||
val noteId = preferences.getWidgetData(id)
|
val noteId = preferences.getWidgetData(id)
|
||||||
|
@ -134,7 +134,7 @@ class WidgetProvider : AppWidgetProvider() {
|
||||||
|
|
||||||
fun updateWidgets(context: Context, noteIds: LongArray? = null, locked: Boolean = false) {
|
fun updateWidgets(context: Context, noteIds: LongArray? = null, locked: Boolean = false) {
|
||||||
val app = context.applicationContext as Application
|
val app = context.applicationContext as Application
|
||||||
val preferences = Preferences.getInstance(app)
|
val preferences = NotallyXPreferences.getInstance(app)
|
||||||
|
|
||||||
val manager = AppWidgetManager.getInstance(context)
|
val manager = AppWidgetManager.getInstance(context)
|
||||||
val updatableWidgets = preferences.getUpdatableWidgets(noteIds)
|
val updatableWidgets = preferences.getUpdatableWidgets(noteIds)
|
||||||
|
|
|
@ -2,12 +2,12 @@ package com.philkes.notallyx.utils
|
||||||
|
|
||||||
import androidx.lifecycle.MutableLiveData
|
import androidx.lifecycle.MutableLiveData
|
||||||
import com.philkes.notallyx.data.model.BaseNote
|
import com.philkes.notallyx.data.model.BaseNote
|
||||||
import com.philkes.notallyx.presentation.view.misc.BetterLiveData
|
import com.philkes.notallyx.presentation.view.misc.NotNullLiveData
|
||||||
|
|
||||||
class ActionMode {
|
class ActionMode {
|
||||||
|
|
||||||
val enabled = BetterLiveData(false)
|
val enabled = NotNullLiveData(false)
|
||||||
val count = BetterLiveData(0)
|
val count = NotNullLiveData(0)
|
||||||
val selectedNotes = HashMap<Long, BaseNote>()
|
val selectedNotes = HashMap<Long, BaseNote>()
|
||||||
val selectedIds = selectedNotes.keys
|
val selectedIds = selectedNotes.keys
|
||||||
val closeListener = MutableLiveData<Event<Set<Long>>>()
|
val closeListener = MutableLiveData<Event<Set<Long>>>()
|
||||||
|
|
|
@ -19,7 +19,7 @@ import com.philkes.notallyx.R
|
||||||
import com.philkes.notallyx.data.model.Color
|
import com.philkes.notallyx.data.model.Color
|
||||||
import com.philkes.notallyx.data.model.ListItem
|
import com.philkes.notallyx.data.model.ListItem
|
||||||
import com.philkes.notallyx.databinding.LabelBinding
|
import com.philkes.notallyx.databinding.LabelBinding
|
||||||
import com.philkes.notallyx.presentation.view.misc.TextSize
|
import com.philkes.notallyx.presentation.viewmodel.preference.TextSize
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.FileOutputStream
|
import java.io.FileOutputStream
|
||||||
import java.io.OutputStreamWriter
|
import java.io.OutputStreamWriter
|
||||||
|
@ -99,7 +99,7 @@ object Operations {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun bindLabels(group: ChipGroup, labels: List<String>, textSize: String) {
|
fun bindLabels(group: ChipGroup, labels: List<String>, textSize: TextSize) {
|
||||||
if (labels.isEmpty()) {
|
if (labels.isEmpty()) {
|
||||||
group.visibility = View.GONE
|
group.visibility = View.GONE
|
||||||
} else {
|
} else {
|
||||||
|
@ -107,7 +107,7 @@ object Operations {
|
||||||
group.removeAllViews()
|
group.removeAllViews()
|
||||||
|
|
||||||
val inflater = LayoutInflater.from(group.context)
|
val inflater = LayoutInflater.from(group.context)
|
||||||
val labelSize = TextSize.getDisplayBodySize(textSize)
|
val labelSize = textSize.displayBodySize
|
||||||
|
|
||||||
for (label in labels) {
|
for (label in labels) {
|
||||||
val view = LabelBinding.inflate(inflater, group, true).root
|
val view = LabelBinding.inflate(inflater, group, true).root
|
||||||
|
|
|
@ -6,7 +6,7 @@ import android.media.MediaRecorder
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.SystemClock
|
import android.os.SystemClock
|
||||||
import androidx.annotation.RequiresApi
|
import androidx.annotation.RequiresApi
|
||||||
import com.philkes.notallyx.presentation.view.misc.BetterLiveData
|
import com.philkes.notallyx.presentation.view.misc.NotNullLiveData
|
||||||
import com.philkes.notallyx.utils.IO.getTempAudioFile
|
import com.philkes.notallyx.utils.IO.getTempAudioFile
|
||||||
import com.philkes.notallyx.utils.audio.Status.PAUSED
|
import com.philkes.notallyx.utils.audio.Status.PAUSED
|
||||||
import com.philkes.notallyx.utils.audio.Status.READY
|
import com.philkes.notallyx.utils.audio.Status.READY
|
||||||
|
@ -15,7 +15,7 @@ import com.philkes.notallyx.utils.audio.Status.RECORDING
|
||||||
@RequiresApi(24)
|
@RequiresApi(24)
|
||||||
class AudioRecordService : Service() {
|
class AudioRecordService : Service() {
|
||||||
|
|
||||||
var status = BetterLiveData(READY)
|
var status = NotNullLiveData(READY)
|
||||||
private var lastStart = 0L
|
private var lastStart = 0L
|
||||||
private var audioDuration = 0L
|
private var audioDuration = 0L
|
||||||
|
|
||||||
|
|
|
@ -6,8 +6,8 @@ import android.net.Uri
|
||||||
import androidx.documentfile.provider.DocumentFile
|
import androidx.documentfile.provider.DocumentFile
|
||||||
import androidx.work.Worker
|
import androidx.work.Worker
|
||||||
import androidx.work.WorkerParameters
|
import androidx.work.WorkerParameters
|
||||||
import com.philkes.notallyx.Preferences
|
import com.philkes.notallyx.presentation.viewmodel.preference.Constants.BACKUP_PATH_EMPTY
|
||||||
import com.philkes.notallyx.presentation.view.misc.AutoBackup
|
import com.philkes.notallyx.presentation.viewmodel.preference.NotallyXPreferences
|
||||||
import com.philkes.notallyx.utils.backup.Export.exportAsZip
|
import com.philkes.notallyx.utils.backup.Export.exportAsZip
|
||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
|
@ -17,11 +17,11 @@ class AutoBackupWorker(private val context: Context, params: WorkerParameters) :
|
||||||
|
|
||||||
override fun doWork(): Result {
|
override fun doWork(): Result {
|
||||||
val app = context.applicationContext as Application
|
val app = context.applicationContext as Application
|
||||||
val preferences = Preferences.getInstance(app)
|
val preferences = NotallyXPreferences.getInstance(app)
|
||||||
val backupPath = preferences.autoBackupPath.value
|
val backupPath = preferences.autoBackupPath.value
|
||||||
val maxBackups = preferences.autoBackupMax
|
val maxBackups = preferences.autoBackupMax.value
|
||||||
|
|
||||||
if (backupPath != AutoBackup.emptyPath) {
|
if (backupPath != BACKUP_PATH_EMPTY) {
|
||||||
val uri = Uri.parse(backupPath)
|
val uri = Uri.parse(backupPath)
|
||||||
val folder = requireNotNull(DocumentFile.fromTreeUri(app, uri))
|
val folder = requireNotNull(DocumentFile.fromTreeUri(app, uri))
|
||||||
|
|
||||||
|
|
|
@ -8,13 +8,13 @@ import androidx.lifecycle.MutableLiveData
|
||||||
import androidx.work.ExistingPeriodicWorkPolicy
|
import androidx.work.ExistingPeriodicWorkPolicy
|
||||||
import androidx.work.PeriodicWorkRequest
|
import androidx.work.PeriodicWorkRequest
|
||||||
import androidx.work.WorkManager
|
import androidx.work.WorkManager
|
||||||
import com.philkes.notallyx.Preferences
|
|
||||||
import com.philkes.notallyx.data.NotallyDatabase
|
import com.philkes.notallyx.data.NotallyDatabase
|
||||||
import com.philkes.notallyx.data.model.Converters
|
import com.philkes.notallyx.data.model.Converters
|
||||||
import com.philkes.notallyx.data.model.FileAttachment
|
import com.philkes.notallyx.data.model.FileAttachment
|
||||||
import com.philkes.notallyx.presentation.view.misc.BackupPassword.emptyPassword
|
|
||||||
import com.philkes.notallyx.presentation.view.misc.BiometricLock.enabled
|
|
||||||
import com.philkes.notallyx.presentation.view.misc.Progress
|
import com.philkes.notallyx.presentation.view.misc.Progress
|
||||||
|
import com.philkes.notallyx.presentation.viewmodel.preference.BiometricLock
|
||||||
|
import com.philkes.notallyx.presentation.viewmodel.preference.Constants.PASSWORD_EMPTY
|
||||||
|
import com.philkes.notallyx.presentation.viewmodel.preference.NotallyXPreferences
|
||||||
import com.philkes.notallyx.utils.IO.SUBFOLDER_AUDIOS
|
import com.philkes.notallyx.utils.IO.SUBFOLDER_AUDIOS
|
||||||
import com.philkes.notallyx.utils.IO.SUBFOLDER_FILES
|
import com.philkes.notallyx.utils.IO.SUBFOLDER_FILES
|
||||||
import com.philkes.notallyx.utils.IO.SUBFOLDER_IMAGES
|
import com.philkes.notallyx.utils.IO.SUBFOLDER_IMAGES
|
||||||
|
@ -43,28 +43,28 @@ object Export {
|
||||||
backupProgress?.postValue(Progress(indeterminate = true))
|
backupProgress?.postValue(Progress(indeterminate = true))
|
||||||
val database = NotallyDatabase.getDatabase(app).value
|
val database = NotallyDatabase.getDatabase(app).value
|
||||||
database.checkpoint()
|
database.checkpoint()
|
||||||
val preferences = Preferences.getInstance(app)
|
val preferences = NotallyXPreferences.getInstance(app)
|
||||||
val backupPassword = preferences.backupPassword.value
|
val backupPassword = preferences.backupPassword.value
|
||||||
|
|
||||||
val tempFile = File.createTempFile("export", "tmp", app.cacheDir)
|
val tempFile = File.createTempFile("export", "tmp", app.cacheDir)
|
||||||
val zipFile =
|
val zipFile =
|
||||||
ZipFile(
|
ZipFile(
|
||||||
tempFile,
|
tempFile,
|
||||||
if (backupPassword != emptyPassword) backupPassword.toCharArray() else null,
|
if (backupPassword != PASSWORD_EMPTY) backupPassword.toCharArray() else null,
|
||||||
)
|
)
|
||||||
val zipParameters =
|
val zipParameters =
|
||||||
ZipParameters().apply {
|
ZipParameters().apply {
|
||||||
isEncryptFiles = backupPassword != emptyPassword
|
isEncryptFiles = backupPassword != PASSWORD_EMPTY
|
||||||
compressionLevel = CompressionLevel.NO_COMPRESSION
|
compressionLevel = CompressionLevel.NO_COMPRESSION
|
||||||
encryptionMethod = EncryptionMethod.AES
|
encryptionMethod = EncryptionMethod.AES
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
preferences.biometricLock.value == enabled &&
|
preferences.biometricLock.value == BiometricLock.ENABLED &&
|
||||||
Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
|
Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
|
||||||
) {
|
) {
|
||||||
val cipher = getInitializedCipherForDecryption(iv = preferences.iv!!)
|
val cipher = getInitializedCipherForDecryption(iv = preferences.iv.value!!)
|
||||||
val passphrase = cipher.doFinal(preferences.getDatabasePassphrase())
|
val passphrase = cipher.doFinal(preferences.databaseEncryptionKey.value)
|
||||||
val decryptedFile = File.createTempFile("decrypted", "tmp", app.cacheDir)
|
val decryptedFile = File.createTempFile("decrypted", "tmp", app.cacheDir)
|
||||||
decryptDatabase(app, passphrase, decryptedFile)
|
decryptDatabase(app, passphrase, decryptedFile)
|
||||||
zipFile.addFile(decryptedFile, zipParameters.copy(NotallyDatabase.DatabaseName))
|
zipFile.addFile(decryptedFile, zipParameters.copy(NotallyDatabase.DatabaseName))
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
package com.philkes.notallyx.recyclerview.listmanager
|
package com.philkes.notallyx.recyclerview.listmanager
|
||||||
|
|
||||||
import com.philkes.notallyx.data.model.ListItem
|
import com.philkes.notallyx.data.model.ListItem
|
||||||
import com.philkes.notallyx.presentation.view.misc.ListItemSorting
|
import com.philkes.notallyx.presentation.viewmodel.preference.ListItemSort
|
||||||
import com.philkes.notallyx.test.assert
|
import com.philkes.notallyx.test.assert
|
||||||
import com.philkes.notallyx.test.assertChildren
|
import com.philkes.notallyx.test.assertChildren
|
||||||
import com.philkes.notallyx.test.assertOrder
|
import com.philkes.notallyx.test.assertOrder
|
||||||
|
@ -21,7 +21,7 @@ class ListManagerAddDeleteTest : ListManagerTestBase() {
|
||||||
// region add
|
// region add
|
||||||
@Test
|
@Test
|
||||||
fun `add item at default position`() {
|
fun `add item at default position`() {
|
||||||
setSorting(ListItemSorting.noAutoSort)
|
setSorting(ListItemSort.NO_AUTO_SORT)
|
||||||
val item = createListItem("Test")
|
val item = createListItem("Test")
|
||||||
val newItem = item.clone() as ListItem
|
val newItem = item.clone() as ListItem
|
||||||
listManager.add(item = item)
|
listManager.add(item = item)
|
||||||
|
@ -33,7 +33,7 @@ class ListManagerAddDeleteTest : ListManagerTestBase() {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `add default item before child item`() {
|
fun `add default item before child item`() {
|
||||||
setSorting(ListItemSorting.noAutoSort)
|
setSorting(ListItemSort.NO_AUTO_SORT)
|
||||||
listManager.changeIsChild(1, true)
|
listManager.changeIsChild(1, true)
|
||||||
listManager.add(1)
|
listManager.add(1)
|
||||||
|
|
||||||
|
@ -45,7 +45,7 @@ class ListManagerAddDeleteTest : ListManagerTestBase() {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `add default item after child item`() {
|
fun `add default item after child item`() {
|
||||||
setSorting(ListItemSorting.noAutoSort)
|
setSorting(ListItemSort.NO_AUTO_SORT)
|
||||||
listManager.changeIsChild(1, true)
|
listManager.changeIsChild(1, true)
|
||||||
listManager.add(2)
|
listManager.add(2)
|
||||||
|
|
||||||
|
@ -57,7 +57,7 @@ class ListManagerAddDeleteTest : ListManagerTestBase() {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `add checked item with correct order`() {
|
fun `add checked item with correct order`() {
|
||||||
setSorting(ListItemSorting.noAutoSort)
|
setSorting(ListItemSort.NO_AUTO_SORT)
|
||||||
val itemToAdd = ListItem("Test", true, false, null, mutableListOf())
|
val itemToAdd = ListItem("Test", true, false, null, mutableListOf())
|
||||||
val newItem = itemToAdd.clone() as ListItem
|
val newItem = itemToAdd.clone() as ListItem
|
||||||
listManager.add(0, item = itemToAdd)
|
listManager.add(0, item = itemToAdd)
|
||||||
|
@ -71,7 +71,7 @@ class ListManagerAddDeleteTest : ListManagerTestBase() {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `add checked item with correct order when auto-sort enabled`() {
|
fun `add checked item with correct order when auto-sort enabled`() {
|
||||||
setSorting(ListItemSorting.autoSortByChecked)
|
setSorting(ListItemSort.AUTO_SORT_BY_CHECKED)
|
||||||
val itemToAdd = ListItem("Test", true, false, null, mutableListOf())
|
val itemToAdd = ListItem("Test", true, false, null, mutableListOf())
|
||||||
val newItem = itemToAdd.clone() as ListItem
|
val newItem = itemToAdd.clone() as ListItem
|
||||||
listManager.add(position = 0, item = itemToAdd)
|
listManager.add(position = 0, item = itemToAdd)
|
||||||
|
@ -85,7 +85,7 @@ class ListManagerAddDeleteTest : ListManagerTestBase() {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `add item with children`() {
|
fun `add item with children`() {
|
||||||
setSorting(ListItemSorting.noAutoSort)
|
setSorting(ListItemSort.NO_AUTO_SORT)
|
||||||
val childItem1 = ListItem("Child1", false, true, null, mutableListOf())
|
val childItem1 = ListItem("Child1", false, true, null, mutableListOf())
|
||||||
val childItem2 = ListItem("Child2", false, true, null, mutableListOf())
|
val childItem2 = ListItem("Child2", false, true, null, mutableListOf())
|
||||||
val parentItem =
|
val parentItem =
|
||||||
|
@ -114,7 +114,7 @@ class ListManagerAddDeleteTest : ListManagerTestBase() {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `delete first item from list unforced does not delete`() {
|
fun `delete first item from list unforced does not delete`() {
|
||||||
setSorting(ListItemSorting.noAutoSort)
|
setSorting(ListItemSort.NO_AUTO_SORT)
|
||||||
val deletedItem = listManager.delete(position = 0, force = false, allowFocusChange = false)
|
val deletedItem = listManager.delete(position = 0, force = false, allowFocusChange = false)
|
||||||
|
|
||||||
assertNull(deletedItem)
|
assertNull(deletedItem)
|
||||||
|
@ -124,7 +124,7 @@ class ListManagerAddDeleteTest : ListManagerTestBase() {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `delete first item from list forced`() {
|
fun `delete first item from list forced`() {
|
||||||
setSorting(ListItemSorting.noAutoSort)
|
setSorting(ListItemSort.NO_AUTO_SORT)
|
||||||
val deletedItem = listManager.delete(position = 0, force = true)
|
val deletedItem = listManager.delete(position = 0, force = true)
|
||||||
|
|
||||||
assertEquals("A", deletedItem!!.body)
|
assertEquals("A", deletedItem!!.body)
|
||||||
|
@ -134,7 +134,7 @@ class ListManagerAddDeleteTest : ListManagerTestBase() {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `delete item with children from list`() {
|
fun `delete item with children from list`() {
|
||||||
setSorting(ListItemSorting.noAutoSort)
|
setSorting(ListItemSort.NO_AUTO_SORT)
|
||||||
listManager.changeIsChild(1, true)
|
listManager.changeIsChild(1, true)
|
||||||
|
|
||||||
val deletedItem = listManager.delete(position = 0, force = true)!!
|
val deletedItem = listManager.delete(position = 0, force = true)!!
|
||||||
|
@ -147,7 +147,7 @@ class ListManagerAddDeleteTest : ListManagerTestBase() {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `delete at invalid position`() {
|
fun `delete at invalid position`() {
|
||||||
setSorting(ListItemSorting.noAutoSort)
|
setSorting(ListItemSort.NO_AUTO_SORT)
|
||||||
val deletedItem = listManager.delete(10)
|
val deletedItem = listManager.delete(10)
|
||||||
|
|
||||||
assertNull(deletedItem)
|
assertNull(deletedItem)
|
||||||
|
@ -160,7 +160,7 @@ class ListManagerAddDeleteTest : ListManagerTestBase() {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `delete checked items`() {
|
fun `delete checked items`() {
|
||||||
setSorting(ListItemSorting.noAutoSort)
|
setSorting(ListItemSort.NO_AUTO_SORT)
|
||||||
listManager.changeIsChild(3, true)
|
listManager.changeIsChild(3, true)
|
||||||
listManager.changeChecked(2, true)
|
listManager.changeChecked(2, true)
|
||||||
listManager.changeChecked(0, true)
|
listManager.changeChecked(0, true)
|
||||||
|
@ -174,7 +174,7 @@ class ListManagerAddDeleteTest : ListManagerTestBase() {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `delete checked items with auto-sort enabled`() {
|
fun `delete checked items with auto-sort enabled`() {
|
||||||
setSorting(ListItemSorting.autoSortByChecked)
|
setSorting(ListItemSort.AUTO_SORT_BY_CHECKED)
|
||||||
listManager.changeIsChild(3, true)
|
listManager.changeIsChild(3, true)
|
||||||
listManager.changeChecked(2, true)
|
listManager.changeChecked(2, true)
|
||||||
listManager.changeChecked(0, true)
|
listManager.changeChecked(0, true)
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
package com.philkes.notallyx.recyclerview.listmanager
|
package com.philkes.notallyx.recyclerview.listmanager
|
||||||
|
|
||||||
import com.philkes.notallyx.presentation.view.misc.ListItemSorting
|
import com.philkes.notallyx.presentation.viewmodel.preference.ListItemSort
|
||||||
import com.philkes.notallyx.test.assert
|
import com.philkes.notallyx.test.assert
|
||||||
import com.philkes.notallyx.test.assertChecked
|
import com.philkes.notallyx.test.assertChecked
|
||||||
import com.philkes.notallyx.test.assertOrder
|
import com.philkes.notallyx.test.assertOrder
|
||||||
|
@ -14,7 +14,7 @@ class ListManagerCheckedTest : ListManagerTestBase() {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `changeChecked checks item`() {
|
fun `changeChecked checks item`() {
|
||||||
setSorting(ListItemSorting.noAutoSort)
|
setSorting(ListItemSort.NO_AUTO_SORT)
|
||||||
listManager.changeChecked(0, checked = true, pushChange = true)
|
listManager.changeChecked(0, checked = true, pushChange = true)
|
||||||
|
|
||||||
"A".assertIsChecked()
|
"A".assertIsChecked()
|
||||||
|
@ -24,7 +24,7 @@ class ListManagerCheckedTest : ListManagerTestBase() {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `changeChecked unchecks item and updates order`() {
|
fun `changeChecked unchecks item and updates order`() {
|
||||||
setSorting(ListItemSorting.noAutoSort)
|
setSorting(ListItemSort.NO_AUTO_SORT)
|
||||||
listManager.changeChecked(0, true)
|
listManager.changeChecked(0, true)
|
||||||
listManager.changeChecked(0, checked = false, pushChange = true)
|
listManager.changeChecked(0, checked = false, pushChange = true)
|
||||||
|
|
||||||
|
@ -35,7 +35,7 @@ class ListManagerCheckedTest : ListManagerTestBase() {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `changeChecked does nothing when state is unchanged`() {
|
fun `changeChecked does nothing when state is unchanged`() {
|
||||||
setSorting(ListItemSorting.noAutoSort)
|
setSorting(ListItemSort.NO_AUTO_SORT)
|
||||||
listManager.changeChecked(0, true)
|
listManager.changeChecked(0, true)
|
||||||
|
|
||||||
listManager.changeChecked(0, checked = true, pushChange = true)
|
listManager.changeChecked(0, checked = true, pushChange = true)
|
||||||
|
@ -46,7 +46,7 @@ class ListManagerCheckedTest : ListManagerTestBase() {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `changeChecked on child item`() {
|
fun `changeChecked on child item`() {
|
||||||
setSorting(ListItemSorting.noAutoSort)
|
setSorting(ListItemSort.NO_AUTO_SORT)
|
||||||
listManager.changeIsChild(1, true)
|
listManager.changeIsChild(1, true)
|
||||||
|
|
||||||
listManager.changeChecked(1, checked = true, pushChange = true)
|
listManager.changeChecked(1, checked = true, pushChange = true)
|
||||||
|
@ -61,7 +61,7 @@ class ListManagerCheckedTest : ListManagerTestBase() {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `changeChecked on parent item checks all children`() {
|
fun `changeChecked on parent item checks all children`() {
|
||||||
setSorting(ListItemSorting.noAutoSort)
|
setSorting(ListItemSort.NO_AUTO_SORT)
|
||||||
listManager.changeIsChild(1, true)
|
listManager.changeIsChild(1, true)
|
||||||
listManager.changeIsChild(2, true)
|
listManager.changeIsChild(2, true)
|
||||||
|
|
||||||
|
@ -79,7 +79,7 @@ class ListManagerCheckedTest : ListManagerTestBase() {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `changeChecked on child item and does not move when auto-sort is enabled`() {
|
fun `changeChecked on child item and does not move when auto-sort is enabled`() {
|
||||||
setSorting(ListItemSorting.autoSortByChecked)
|
setSorting(ListItemSort.AUTO_SORT_BY_CHECKED)
|
||||||
listManager.changeIsChild(1, true)
|
listManager.changeIsChild(1, true)
|
||||||
|
|
||||||
listManager.changeChecked(1, checked = true, pushChange = true)
|
listManager.changeChecked(1, checked = true, pushChange = true)
|
||||||
|
@ -94,7 +94,7 @@ class ListManagerCheckedTest : ListManagerTestBase() {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `changeChecked true on parent item checks all children and moves when auto-sort is enabled`() {
|
fun `changeChecked true on parent item checks all children and moves when auto-sort is enabled`() {
|
||||||
setSorting(ListItemSorting.autoSortByChecked)
|
setSorting(ListItemSort.AUTO_SORT_BY_CHECKED)
|
||||||
listManager.changeIsChild(1, true)
|
listManager.changeIsChild(1, true)
|
||||||
listManager.changeIsChild(2, true)
|
listManager.changeIsChild(2, true)
|
||||||
|
|
||||||
|
@ -115,7 +115,7 @@ class ListManagerCheckedTest : ListManagerTestBase() {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `changeChecked false on parent item unchecks all children and moves when auto-sort is enabled`() {
|
fun `changeChecked false on parent item unchecks all children and moves when auto-sort is enabled`() {
|
||||||
setSorting(ListItemSorting.autoSortByChecked)
|
setSorting(ListItemSort.AUTO_SORT_BY_CHECKED)
|
||||||
listManager.changeIsChild(1, true)
|
listManager.changeIsChild(1, true)
|
||||||
listManager.changeIsChild(2, true)
|
listManager.changeIsChild(2, true)
|
||||||
listManager.changeChecked(0, checked = true, pushChange = true)
|
listManager.changeChecked(0, checked = true, pushChange = true)
|
||||||
|
@ -137,7 +137,7 @@ class ListManagerCheckedTest : ListManagerTestBase() {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `changeChecked false on parent item after add another item when auto-sort is enabled`() {
|
fun `changeChecked false on parent item after add another item when auto-sort is enabled`() {
|
||||||
setSorting(ListItemSorting.autoSortByChecked)
|
setSorting(ListItemSort.AUTO_SORT_BY_CHECKED)
|
||||||
listManager.changeIsChild(1, true)
|
listManager.changeIsChild(1, true)
|
||||||
listManager.changeIsChild(2, true)
|
listManager.changeIsChild(2, true)
|
||||||
listManager.changeChecked(0, checked = true, pushChange = true)
|
listManager.changeChecked(0, checked = true, pushChange = true)
|
||||||
|
@ -155,7 +155,7 @@ class ListManagerCheckedTest : ListManagerTestBase() {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `changeChecked false on child item also unchecks parent`() {
|
fun `changeChecked false on child item also unchecks parent`() {
|
||||||
setSorting(ListItemSorting.autoSortByChecked)
|
setSorting(ListItemSort.AUTO_SORT_BY_CHECKED)
|
||||||
listManager.changeIsChild(2, true)
|
listManager.changeIsChild(2, true)
|
||||||
listManager.changeIsChild(3, true)
|
listManager.changeIsChild(3, true)
|
||||||
listManager.changeChecked(1, checked = true, pushChange = true)
|
listManager.changeChecked(1, checked = true, pushChange = true)
|
||||||
|
@ -176,7 +176,7 @@ class ListManagerCheckedTest : ListManagerTestBase() {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `changeCheckedForAll true checks all unchecked items`() {
|
fun `changeCheckedForAll true checks all unchecked items`() {
|
||||||
setSorting(ListItemSorting.noAutoSort)
|
setSorting(ListItemSort.NO_AUTO_SORT)
|
||||||
listManager.changeChecked(1, true)
|
listManager.changeChecked(1, true)
|
||||||
listManager.changeChecked(3, true)
|
listManager.changeChecked(3, true)
|
||||||
|
|
||||||
|
@ -189,7 +189,7 @@ class ListManagerCheckedTest : ListManagerTestBase() {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `changeCheckedForAll false unchecks all unchecked items`() {
|
fun `changeCheckedForAll false unchecks all unchecked items`() {
|
||||||
setSorting(ListItemSorting.noAutoSort)
|
setSorting(ListItemSort.NO_AUTO_SORT)
|
||||||
listManager.changeIsChild(2, true)
|
listManager.changeIsChild(2, true)
|
||||||
listManager.changeChecked(1, true)
|
listManager.changeChecked(1, true)
|
||||||
listManager.changeChecked(3, true)
|
listManager.changeChecked(3, true)
|
||||||
|
@ -203,7 +203,7 @@ class ListManagerCheckedTest : ListManagerTestBase() {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `changeCheckedForAll true correct order with auto-sort enabled`() {
|
fun `changeCheckedForAll true correct order with auto-sort enabled`() {
|
||||||
setSorting(ListItemSorting.autoSortByChecked)
|
setSorting(ListItemSort.AUTO_SORT_BY_CHECKED)
|
||||||
listManager.changeIsChild(3, true)
|
listManager.changeIsChild(3, true)
|
||||||
listManager.changeChecked(2, true)
|
listManager.changeChecked(2, true)
|
||||||
listManager.changeChecked(0, true)
|
listManager.changeChecked(0, true)
|
||||||
|
@ -217,7 +217,7 @@ class ListManagerCheckedTest : ListManagerTestBase() {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `changeCheckedForAll false correct order with auto-sort enabled`() {
|
fun `changeCheckedForAll false correct order with auto-sort enabled`() {
|
||||||
setSorting(ListItemSorting.autoSortByChecked)
|
setSorting(ListItemSort.AUTO_SORT_BY_CHECKED)
|
||||||
listManager.changeIsChild(3, true)
|
listManager.changeIsChild(3, true)
|
||||||
listManager.changeChecked(2, true)
|
listManager.changeChecked(2, true)
|
||||||
listManager.changeChecked(0, true)
|
listManager.changeChecked(0, true)
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
package com.philkes.notallyx.recyclerview.listmanager
|
package com.philkes.notallyx.recyclerview.listmanager
|
||||||
|
|
||||||
import com.philkes.notallyx.presentation.view.misc.ListItemSorting
|
import com.philkes.notallyx.presentation.viewmodel.preference.ListItemSort
|
||||||
import com.philkes.notallyx.test.assert
|
import com.philkes.notallyx.test.assert
|
||||||
import com.philkes.notallyx.test.assertOrder
|
import com.philkes.notallyx.test.assertOrder
|
||||||
import com.philkes.notallyx.utils.changehistory.ListIsChildChange
|
import com.philkes.notallyx.utils.changehistory.ListIsChildChange
|
||||||
|
@ -12,7 +12,7 @@ class ListManagerIsChildTest : ListManagerTestBase() {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `changeIsChild changes parent to child and pushes change`() {
|
fun `changeIsChild changes parent to child and pushes change`() {
|
||||||
setSorting(ListItemSorting.noAutoSort)
|
setSorting(ListItemSort.NO_AUTO_SORT)
|
||||||
listManager.changeIsChild(1, isChild = true)
|
listManager.changeIsChild(1, isChild = true)
|
||||||
|
|
||||||
items.assertOrder("A", "B")
|
items.assertOrder("A", "B")
|
||||||
|
@ -22,7 +22,7 @@ class ListManagerIsChildTest : ListManagerTestBase() {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `changeIsChild changes child to parent`() {
|
fun `changeIsChild changes child to parent`() {
|
||||||
setSorting(ListItemSorting.noAutoSort)
|
setSorting(ListItemSort.NO_AUTO_SORT)
|
||||||
listManager.changeIsChild(1, isChild = true)
|
listManager.changeIsChild(1, isChild = true)
|
||||||
|
|
||||||
listManager.changeIsChild(1, isChild = false)
|
listManager.changeIsChild(1, isChild = false)
|
||||||
|
@ -34,7 +34,7 @@ class ListManagerIsChildTest : ListManagerTestBase() {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `changeIsChild adds all child items when item becomes a parent`() {
|
fun `changeIsChild adds all child items when item becomes a parent`() {
|
||||||
setSorting(ListItemSorting.noAutoSort)
|
setSorting(ListItemSort.NO_AUTO_SORT)
|
||||||
listManager.changeIsChild(1, true)
|
listManager.changeIsChild(1, true)
|
||||||
listManager.changeIsChild(2, true)
|
listManager.changeIsChild(2, true)
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
package com.philkes.notallyx.recyclerview.listmanager
|
package com.philkes.notallyx.recyclerview.listmanager
|
||||||
|
|
||||||
import com.philkes.notallyx.presentation.view.misc.ListItemSorting
|
|
||||||
import com.philkes.notallyx.presentation.view.note.listitem.sorting.lastIndex
|
import com.philkes.notallyx.presentation.view.note.listitem.sorting.lastIndex
|
||||||
|
import com.philkes.notallyx.presentation.viewmodel.preference.ListItemSort
|
||||||
import com.philkes.notallyx.test.assert
|
import com.philkes.notallyx.test.assert
|
||||||
import com.philkes.notallyx.test.assertOrder
|
import com.philkes.notallyx.test.assertOrder
|
||||||
import com.philkes.notallyx.test.createListItem
|
import com.philkes.notallyx.test.createListItem
|
||||||
|
@ -18,7 +18,7 @@ class ListManagerMoveTest : ListManagerTestBase() {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `move parent without children`() {
|
fun `move parent without children`() {
|
||||||
setSorting(ListItemSorting.noAutoSort)
|
setSorting(ListItemSort.NO_AUTO_SORT)
|
||||||
val newPosition = listManager.move(3, 1)
|
val newPosition = listManager.move(3, 1)
|
||||||
|
|
||||||
items.assertOrder("A", "D", "B")
|
items.assertOrder("A", "D", "B")
|
||||||
|
@ -28,7 +28,7 @@ class ListManagerMoveTest : ListManagerTestBase() {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `move parent with children into other parent`() {
|
fun `move parent with children into other parent`() {
|
||||||
setSorting(ListItemSorting.noAutoSort)
|
setSorting(ListItemSort.NO_AUTO_SORT)
|
||||||
listManager.changeIsChild(1, true, false)
|
listManager.changeIsChild(1, true, false)
|
||||||
listManager.changeIsChild(2, true, false)
|
listManager.changeIsChild(2, true, false)
|
||||||
listManager.changeIsChild(4, true, false)
|
listManager.changeIsChild(4, true, false)
|
||||||
|
@ -46,7 +46,7 @@ class ListManagerMoveTest : ListManagerTestBase() {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `move parent with children to bottom`() {
|
fun `move parent with children to bottom`() {
|
||||||
setSorting(ListItemSorting.noAutoSort)
|
setSorting(ListItemSort.NO_AUTO_SORT)
|
||||||
listManager.changeIsChild(1, true, false)
|
listManager.changeIsChild(1, true, false)
|
||||||
listManager.changeIsChild(2, true, false)
|
listManager.changeIsChild(2, true, false)
|
||||||
items.printList("Before")
|
items.printList("Before")
|
||||||
|
@ -63,7 +63,7 @@ class ListManagerMoveTest : ListManagerTestBase() {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `move parent with children to top`() {
|
fun `move parent with children to top`() {
|
||||||
setSorting(ListItemSorting.noAutoSort)
|
setSorting(ListItemSort.NO_AUTO_SORT)
|
||||||
listManager.changeIsChild(1, true, false)
|
listManager.changeIsChild(1, true, false)
|
||||||
listManager.changeIsChild(4, true, false)
|
listManager.changeIsChild(4, true, false)
|
||||||
listManager.add(0, createListItem("G"))
|
listManager.add(0, createListItem("G"))
|
||||||
|
@ -81,7 +81,7 @@ class ListManagerMoveTest : ListManagerTestBase() {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `move child to other parent`() {
|
fun `move child to other parent`() {
|
||||||
setSorting(ListItemSorting.noAutoSort)
|
setSorting(ListItemSort.NO_AUTO_SORT)
|
||||||
listManager.changeIsChild(3, true, false)
|
listManager.changeIsChild(3, true, false)
|
||||||
|
|
||||||
val newPosition = listManager.move(3, 1)
|
val newPosition = listManager.move(3, 1)
|
||||||
|
@ -94,7 +94,7 @@ class ListManagerMoveTest : ListManagerTestBase() {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `move child above other child`() {
|
fun `move child above other child`() {
|
||||||
setSorting(ListItemSorting.noAutoSort)
|
setSorting(ListItemSort.NO_AUTO_SORT)
|
||||||
listManager.changeIsChild(3, true, false)
|
listManager.changeIsChild(3, true, false)
|
||||||
listManager.changeIsChild(4, true, false)
|
listManager.changeIsChild(4, true, false)
|
||||||
listManager.changeIsChild(5, true, false)
|
listManager.changeIsChild(5, true, false)
|
||||||
|
@ -109,7 +109,7 @@ class ListManagerMoveTest : ListManagerTestBase() {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `move child to top`() {
|
fun `move child to top`() {
|
||||||
setSorting(ListItemSorting.noAutoSort)
|
setSorting(ListItemSort.NO_AUTO_SORT)
|
||||||
listManager.changeIsChild(3, true, false)
|
listManager.changeIsChild(3, true, false)
|
||||||
|
|
||||||
val newPosition = listManager.move(3, 0)
|
val newPosition = listManager.move(3, 0)
|
||||||
|
@ -122,7 +122,7 @@ class ListManagerMoveTest : ListManagerTestBase() {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `dont move parent into own children`() {
|
fun `dont move parent into own children`() {
|
||||||
setSorting(ListItemSorting.noAutoSort)
|
setSorting(ListItemSort.NO_AUTO_SORT)
|
||||||
listManager.changeIsChild(3, true)
|
listManager.changeIsChild(3, true)
|
||||||
listManager.changeIsChild(4, true)
|
listManager.changeIsChild(4, true)
|
||||||
|
|
||||||
|
@ -135,7 +135,7 @@ class ListManagerMoveTest : ListManagerTestBase() {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `dont move parent under checked item if auto-sort enabled`() {
|
fun `dont move parent under checked item if auto-sort enabled`() {
|
||||||
setSorting(ListItemSorting.autoSortByChecked)
|
setSorting(ListItemSort.AUTO_SORT_BY_CHECKED)
|
||||||
listManager.changeChecked(5, true)
|
listManager.changeChecked(5, true)
|
||||||
|
|
||||||
val newPosition = listManager.move(2, 5)
|
val newPosition = listManager.move(2, 5)
|
||||||
|
@ -150,7 +150,7 @@ class ListManagerMoveTest : ListManagerTestBase() {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `undoMove parent without children`() {
|
fun `undoMove parent without children`() {
|
||||||
setSorting(ListItemSorting.noAutoSort)
|
setSorting(ListItemSort.NO_AUTO_SORT)
|
||||||
val newPosition = listManager.move(1, 4)!!
|
val newPosition = listManager.move(1, 4)!!
|
||||||
val itemBeforeMove = (changeHistory.lookUp() as ListMoveChange).itemBeforeMove
|
val itemBeforeMove = (changeHistory.lookUp() as ListMoveChange).itemBeforeMove
|
||||||
|
|
||||||
|
@ -161,7 +161,7 @@ class ListManagerMoveTest : ListManagerTestBase() {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `undoMove parent with children into other parent`() {
|
fun `undoMove parent with children into other parent`() {
|
||||||
setSorting(ListItemSorting.noAutoSort)
|
setSorting(ListItemSort.NO_AUTO_SORT)
|
||||||
listManager.changeIsChild(2, true, false)
|
listManager.changeIsChild(2, true, false)
|
||||||
val newPosition = listManager.move(1, 3)!!
|
val newPosition = listManager.move(1, 3)!!
|
||||||
val itemBeforeMove = (changeHistory.lookUp() as ListMoveChange).itemBeforeMove
|
val itemBeforeMove = (changeHistory.lookUp() as ListMoveChange).itemBeforeMove
|
||||||
|
@ -175,7 +175,7 @@ class ListManagerMoveTest : ListManagerTestBase() {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `undoMove move parent with children to bottom`() {
|
fun `undoMove move parent with children to bottom`() {
|
||||||
setSorting(ListItemSorting.noAutoSort)
|
setSorting(ListItemSort.NO_AUTO_SORT)
|
||||||
listManager.changeIsChild(1, true, false)
|
listManager.changeIsChild(1, true, false)
|
||||||
listManager.changeIsChild(2, true, false)
|
listManager.changeIsChild(2, true, false)
|
||||||
val newPosition = listManager.move(0, items.lastIndex)!!
|
val newPosition = listManager.move(0, items.lastIndex)!!
|
||||||
|
@ -191,7 +191,7 @@ class ListManagerMoveTest : ListManagerTestBase() {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `undoMove parent with children to top`() {
|
fun `undoMove parent with children to top`() {
|
||||||
setSorting(ListItemSorting.noAutoSort)
|
setSorting(ListItemSort.NO_AUTO_SORT)
|
||||||
listManager.changeIsChild(1, true, false)
|
listManager.changeIsChild(1, true, false)
|
||||||
listManager.changeIsChild(4, true, false)
|
listManager.changeIsChild(4, true, false)
|
||||||
items.printList("Before")
|
items.printList("Before")
|
||||||
|
@ -208,7 +208,7 @@ class ListManagerMoveTest : ListManagerTestBase() {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `undoMove child to other parent`() {
|
fun `undoMove child to other parent`() {
|
||||||
setSorting(ListItemSorting.noAutoSort)
|
setSorting(ListItemSort.NO_AUTO_SORT)
|
||||||
listManager.changeIsChild(3, true, false)
|
listManager.changeIsChild(3, true, false)
|
||||||
val newPosition = listManager.move(3, 1)!!
|
val newPosition = listManager.move(3, 1)!!
|
||||||
val itemBeforeMove = (changeHistory.lookUp() as ListMoveChange).itemBeforeMove
|
val itemBeforeMove = (changeHistory.lookUp() as ListMoveChange).itemBeforeMove
|
||||||
|
@ -222,7 +222,7 @@ class ListManagerMoveTest : ListManagerTestBase() {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `undoMove child above other child`() {
|
fun `undoMove child above other child`() {
|
||||||
setSorting(ListItemSorting.noAutoSort)
|
setSorting(ListItemSort.NO_AUTO_SORT)
|
||||||
listManager.changeIsChild(3, true, false)
|
listManager.changeIsChild(3, true, false)
|
||||||
listManager.changeIsChild(4, true, false)
|
listManager.changeIsChild(4, true, false)
|
||||||
listManager.changeIsChild(5, true, false)
|
listManager.changeIsChild(5, true, false)
|
||||||
|
@ -239,7 +239,7 @@ class ListManagerMoveTest : ListManagerTestBase() {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `undoMove child to top`() {
|
fun `undoMove child to top`() {
|
||||||
setSorting(ListItemSorting.noAutoSort)
|
setSorting(ListItemSort.NO_AUTO_SORT)
|
||||||
listManager.changeIsChild(3, true, false)
|
listManager.changeIsChild(3, true, false)
|
||||||
val newPosition = listManager.move(3, 0)!!
|
val newPosition = listManager.move(3, 0)!!
|
||||||
val itemBeforeMove = (changeHistory.lookUp() as ListMoveChange).itemBeforeMove
|
val itemBeforeMove = (changeHistory.lookUp() as ListMoveChange).itemBeforeMove
|
||||||
|
@ -256,7 +256,7 @@ class ListManagerMoveTest : ListManagerTestBase() {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `endDrag parent without children`() {
|
fun `endDrag parent without children`() {
|
||||||
setSorting(ListItemSorting.noAutoSort)
|
setSorting(ListItemSort.NO_AUTO_SORT)
|
||||||
listItemDragCallback.simulateDrag(3, 1, "D".itemCount)
|
listItemDragCallback.simulateDrag(3, 1, "D".itemCount)
|
||||||
|
|
||||||
items.assertOrder("A", "D", "B")
|
items.assertOrder("A", "D", "B")
|
||||||
|
@ -265,7 +265,7 @@ class ListManagerMoveTest : ListManagerTestBase() {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `endDrag parent with children into other parent`() {
|
fun `endDrag parent with children into other parent`() {
|
||||||
setSorting(ListItemSorting.noAutoSort)
|
setSorting(ListItemSort.NO_AUTO_SORT)
|
||||||
listManager.changeIsChild(1, true, false)
|
listManager.changeIsChild(1, true, false)
|
||||||
listManager.changeIsChild(2, true, false)
|
listManager.changeIsChild(2, true, false)
|
||||||
listManager.changeIsChild(5, true, false)
|
listManager.changeIsChild(5, true, false)
|
||||||
|
@ -300,7 +300,7 @@ class ListManagerMoveTest : ListManagerTestBase() {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `endDrag parent with children to bottom`() {
|
fun `endDrag parent with children to bottom`() {
|
||||||
setSorting(ListItemSorting.noAutoSort)
|
setSorting(ListItemSort.NO_AUTO_SORT)
|
||||||
listManager.changeIsChild(1, true, false)
|
listManager.changeIsChild(1, true, false)
|
||||||
listManager.changeIsChild(2, true, false)
|
listManager.changeIsChild(2, true, false)
|
||||||
items.printList("Before")
|
items.printList("Before")
|
||||||
|
@ -316,7 +316,7 @@ class ListManagerMoveTest : ListManagerTestBase() {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `endDrag parent with children to top`() {
|
fun `endDrag parent with children to top`() {
|
||||||
setSorting(ListItemSorting.noAutoSort)
|
setSorting(ListItemSort.NO_AUTO_SORT)
|
||||||
listManager.changeIsChild(1, true, false)
|
listManager.changeIsChild(1, true, false)
|
||||||
listManager.changeIsChild(4, true, false)
|
listManager.changeIsChild(4, true, false)
|
||||||
items.printList("Before")
|
items.printList("Before")
|
||||||
|
@ -332,7 +332,7 @@ class ListManagerMoveTest : ListManagerTestBase() {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `endDrag child to other parent`() {
|
fun `endDrag child to other parent`() {
|
||||||
setSorting(ListItemSorting.noAutoSort)
|
setSorting(ListItemSort.NO_AUTO_SORT)
|
||||||
listManager.changeIsChild(3, true, false)
|
listManager.changeIsChild(3, true, false)
|
||||||
|
|
||||||
listItemDragCallback.simulateDrag(3, 1, "D".itemCount)
|
listItemDragCallback.simulateDrag(3, 1, "D".itemCount)
|
||||||
|
@ -344,7 +344,7 @@ class ListManagerMoveTest : ListManagerTestBase() {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `endDrag child above other child`() {
|
fun `endDrag child above other child`() {
|
||||||
setSorting(ListItemSorting.noAutoSort)
|
setSorting(ListItemSort.NO_AUTO_SORT)
|
||||||
listManager.changeIsChild(3, true, false)
|
listManager.changeIsChild(3, true, false)
|
||||||
listManager.changeIsChild(4, true, false)
|
listManager.changeIsChild(4, true, false)
|
||||||
listManager.changeIsChild(5, true, false)
|
listManager.changeIsChild(5, true, false)
|
||||||
|
@ -358,7 +358,7 @@ class ListManagerMoveTest : ListManagerTestBase() {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `endDrag child to top`() {
|
fun `endDrag child to top`() {
|
||||||
setSorting(ListItemSorting.noAutoSort)
|
setSorting(ListItemSort.NO_AUTO_SORT)
|
||||||
listManager.changeIsChild(3, true, false)
|
listManager.changeIsChild(3, true, false)
|
||||||
|
|
||||||
listItemDragCallback.simulateDrag(3, 0, "D".itemCount)
|
listItemDragCallback.simulateDrag(3, 0, "D".itemCount)
|
||||||
|
|
|
@ -3,10 +3,7 @@ package com.philkes.notallyx.recyclerview.listmanager
|
||||||
import android.view.inputmethod.InputMethodManager
|
import android.view.inputmethod.InputMethodManager
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import androidx.recyclerview.widget.SortedList
|
import androidx.recyclerview.widget.SortedList
|
||||||
import com.philkes.notallyx.Preferences
|
|
||||||
import com.philkes.notallyx.data.model.ListItem
|
import com.philkes.notallyx.data.model.ListItem
|
||||||
import com.philkes.notallyx.presentation.view.misc.BetterLiveData
|
|
||||||
import com.philkes.notallyx.presentation.view.misc.ListItemSorting
|
|
||||||
import com.philkes.notallyx.presentation.view.note.listitem.ListItemDragCallback
|
import com.philkes.notallyx.presentation.view.note.listitem.ListItemDragCallback
|
||||||
import com.philkes.notallyx.presentation.view.note.listitem.ListItemVH
|
import com.philkes.notallyx.presentation.view.note.listitem.ListItemVH
|
||||||
import com.philkes.notallyx.presentation.view.note.listitem.ListManager
|
import com.philkes.notallyx.presentation.view.note.listitem.ListManager
|
||||||
|
@ -15,6 +12,9 @@ import com.philkes.notallyx.presentation.view.note.listitem.sorting.ListItemSort
|
||||||
import com.philkes.notallyx.presentation.view.note.listitem.sorting.ListItemSortedList
|
import com.philkes.notallyx.presentation.view.note.listitem.sorting.ListItemSortedList
|
||||||
import com.philkes.notallyx.presentation.view.note.listitem.sorting.find
|
import com.philkes.notallyx.presentation.view.note.listitem.sorting.find
|
||||||
import com.philkes.notallyx.presentation.view.note.listitem.sorting.indexOfFirst
|
import com.philkes.notallyx.presentation.view.note.listitem.sorting.indexOfFirst
|
||||||
|
import com.philkes.notallyx.presentation.viewmodel.preference.EnumPreference
|
||||||
|
import com.philkes.notallyx.presentation.viewmodel.preference.ListItemSort
|
||||||
|
import com.philkes.notallyx.presentation.viewmodel.preference.NotallyXPreferences
|
||||||
import com.philkes.notallyx.test.assertChildren
|
import com.philkes.notallyx.test.assertChildren
|
||||||
import com.philkes.notallyx.test.createListItem
|
import com.philkes.notallyx.test.createListItem
|
||||||
import com.philkes.notallyx.test.mockAndroidLog
|
import com.philkes.notallyx.test.mockAndroidLog
|
||||||
|
@ -34,7 +34,7 @@ open class ListManagerTestBase {
|
||||||
protected lateinit var inputMethodManager: InputMethodManager
|
protected lateinit var inputMethodManager: InputMethodManager
|
||||||
protected lateinit var changeHistory: ChangeHistory
|
protected lateinit var changeHistory: ChangeHistory
|
||||||
protected lateinit var listItemVH: ListItemVH
|
protected lateinit var listItemVH: ListItemVH
|
||||||
protected lateinit var preferences: Preferences
|
protected lateinit var preferences: NotallyXPreferences
|
||||||
protected lateinit var listItemDragCallback: ListItemDragCallback
|
protected lateinit var listItemDragCallback: ListItemDragCallback
|
||||||
|
|
||||||
protected lateinit var items: ListItemSortedList
|
protected lateinit var items: ListItemSortedList
|
||||||
|
@ -49,17 +49,17 @@ open class ListManagerTestBase {
|
||||||
inputMethodManager = mock(InputMethodManager::class.java)
|
inputMethodManager = mock(InputMethodManager::class.java)
|
||||||
changeHistory = ChangeHistory() {}
|
changeHistory = ChangeHistory() {}
|
||||||
listItemVH = mock(ListItemVH::class.java)
|
listItemVH = mock(ListItemVH::class.java)
|
||||||
preferences = mock(Preferences::class.java)
|
preferences = mock(NotallyXPreferences::class.java)
|
||||||
listManager = ListManager(recyclerView, changeHistory, preferences, inputMethodManager)
|
listManager = ListManager(recyclerView, changeHistory, preferences, inputMethodManager)
|
||||||
listManager.adapter = adapter as RecyclerView.Adapter<ListItemVH>
|
listManager.adapter = adapter as RecyclerView.Adapter<ListItemVH>
|
||||||
// Prepare view holder
|
// Prepare view holder
|
||||||
`when`(recyclerView.findViewHolderForAdapterPosition(anyInt())).thenReturn(listItemVH)
|
`when`(recyclerView.findViewHolderForAdapterPosition(anyInt())).thenReturn(listItemVH)
|
||||||
}
|
}
|
||||||
|
|
||||||
protected fun setSorting(sorting: String) {
|
protected fun setSorting(sorting: ListItemSort) {
|
||||||
val sortCallback =
|
val sortCallback =
|
||||||
when (sorting) {
|
when (sorting) {
|
||||||
ListItemSorting.autoSortByChecked -> ListItemSortedByCheckedCallback(adapter)
|
ListItemSort.AUTO_SORT_BY_CHECKED -> ListItemSortedByCheckedCallback(adapter)
|
||||||
else -> ListItemNoSortCallback(adapter)
|
else -> ListItemNoSortCallback(adapter)
|
||||||
}
|
}
|
||||||
items = ListItemSortedList(sortCallback)
|
items = ListItemSortedList(sortCallback)
|
||||||
|
@ -76,7 +76,10 @@ open class ListManagerTestBase {
|
||||||
)
|
)
|
||||||
listManager.initList(items)
|
listManager.initList(items)
|
||||||
listItemDragCallback = ListItemDragCallback(1.0f, listManager)
|
listItemDragCallback = ListItemDragCallback(1.0f, listManager)
|
||||||
`when`(preferences.listItemSorting).thenReturn(BetterLiveData(sorting))
|
val listItemSortingPreference = mock(EnumPreference::class.java)
|
||||||
|
`when`(listItemSortingPreference.value).thenReturn(sorting)
|
||||||
|
`when`(preferences.listItemSorting)
|
||||||
|
.thenReturn(listItemSortingPreference as EnumPreference<ListItemSort>)
|
||||||
}
|
}
|
||||||
|
|
||||||
protected operator fun List<ListItem>.get(body: String): ListItem {
|
protected operator fun List<ListItem>.get(body: String): ListItem {
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
package com.philkes.notallyx.recyclerview.listmanager
|
package com.philkes.notallyx.recyclerview.listmanager
|
||||||
|
|
||||||
import com.philkes.notallyx.presentation.view.misc.ListItemSorting
|
|
||||||
import com.philkes.notallyx.presentation.view.note.listitem.sorting.lastIndex
|
import com.philkes.notallyx.presentation.view.note.listitem.sorting.lastIndex
|
||||||
import com.philkes.notallyx.presentation.view.note.listitem.sorting.map
|
import com.philkes.notallyx.presentation.view.note.listitem.sorting.map
|
||||||
|
import com.philkes.notallyx.presentation.viewmodel.preference.ListItemSort
|
||||||
import com.philkes.notallyx.test.assertChecked
|
import com.philkes.notallyx.test.assertChecked
|
||||||
import com.philkes.notallyx.test.assertIds
|
import com.philkes.notallyx.test.assertIds
|
||||||
import com.philkes.notallyx.test.assertOrder
|
import com.philkes.notallyx.test.assertOrder
|
||||||
|
@ -12,7 +12,7 @@ class ListManagerWithChangeHistoryTest : ListManagerTestBase() {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `undo and redo moves`() {
|
fun `undo and redo moves`() {
|
||||||
setSorting(ListItemSorting.noAutoSort)
|
setSorting(ListItemSort.NO_AUTO_SORT)
|
||||||
listManager.move(0, 4)
|
listManager.move(0, 4)
|
||||||
listManager.move(2, 3)
|
listManager.move(2, 3)
|
||||||
listManager.move(4, 1)
|
listManager.move(4, 1)
|
||||||
|
@ -34,7 +34,7 @@ class ListManagerWithChangeHistoryTest : ListManagerTestBase() {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `undo and redo changeChecked`() {
|
fun `undo and redo changeChecked`() {
|
||||||
setSorting(ListItemSorting.noAutoSort)
|
setSorting(ListItemSort.NO_AUTO_SORT)
|
||||||
listManager.changeChecked(0, true)
|
listManager.changeChecked(0, true)
|
||||||
listManager.changeChecked(3, true)
|
listManager.changeChecked(3, true)
|
||||||
listManager.changeChecked(0, false)
|
listManager.changeChecked(0, false)
|
||||||
|
@ -55,7 +55,7 @@ class ListManagerWithChangeHistoryTest : ListManagerTestBase() {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `undo and redo changeChecked if auto-sort enabled`() {
|
fun `undo and redo changeChecked if auto-sort enabled`() {
|
||||||
setSorting(ListItemSorting.autoSortByChecked)
|
setSorting(ListItemSort.AUTO_SORT_BY_CHECKED)
|
||||||
listManager.changeChecked(0, true)
|
listManager.changeChecked(0, true)
|
||||||
listManager.changeChecked(3, true)
|
listManager.changeChecked(3, true)
|
||||||
listManager.changeChecked(0, false)
|
listManager.changeChecked(0, false)
|
||||||
|
@ -79,7 +79,7 @@ class ListManagerWithChangeHistoryTest : ListManagerTestBase() {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `undo and redo changeChecked false on child item`() {
|
fun `undo and redo changeChecked false on child item`() {
|
||||||
setSorting(ListItemSorting.autoSortByChecked)
|
setSorting(ListItemSort.AUTO_SORT_BY_CHECKED)
|
||||||
listManager.changeIsChild(2, true)
|
listManager.changeIsChild(2, true)
|
||||||
listManager.changeIsChild(3, true)
|
listManager.changeIsChild(3, true)
|
||||||
listManager.changeChecked(1, checked = true, pushChange = true)
|
listManager.changeChecked(1, checked = true, pushChange = true)
|
||||||
|
@ -96,7 +96,7 @@ class ListManagerWithChangeHistoryTest : ListManagerTestBase() {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `undo and redo changeIsChild`() {
|
fun `undo and redo changeIsChild`() {
|
||||||
setSorting(ListItemSorting.noAutoSort)
|
setSorting(ListItemSort.NO_AUTO_SORT)
|
||||||
listManager.changeIsChild(1, true)
|
listManager.changeIsChild(1, true)
|
||||||
listManager.changeIsChild(2, true)
|
listManager.changeIsChild(2, true)
|
||||||
listManager.changeIsChild(4, true)
|
listManager.changeIsChild(4, true)
|
||||||
|
@ -120,7 +120,7 @@ class ListManagerWithChangeHistoryTest : ListManagerTestBase() {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `undo and redo add parents with children`() {
|
fun `undo and redo add parents with children`() {
|
||||||
setSorting(ListItemSorting.noAutoSort)
|
setSorting(ListItemSort.NO_AUTO_SORT)
|
||||||
listManager.addWithChildren(0, "Parent1", "Child1")
|
listManager.addWithChildren(0, "Parent1", "Child1")
|
||||||
listManager.addWithChildren(4, "Parent2")
|
listManager.addWithChildren(4, "Parent2")
|
||||||
listManager.addWithChildren(0, "Parent3")
|
listManager.addWithChildren(0, "Parent3")
|
||||||
|
@ -150,7 +150,7 @@ class ListManagerWithChangeHistoryTest : ListManagerTestBase() {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `undo and redo delete parents with children`() {
|
fun `undo and redo delete parents with children`() {
|
||||||
setSorting(ListItemSorting.noAutoSort)
|
setSorting(ListItemSort.NO_AUTO_SORT)
|
||||||
listManager.changeIsChild(1, true)
|
listManager.changeIsChild(1, true)
|
||||||
listManager.changeIsChild(3, true)
|
listManager.changeIsChild(3, true)
|
||||||
listManager.changeIsChild(4, true)
|
listManager.changeIsChild(4, true)
|
||||||
|
@ -174,7 +174,7 @@ class ListManagerWithChangeHistoryTest : ListManagerTestBase() {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `undo and redo check all with auto-sort enabled`() {
|
fun `undo and redo check all with auto-sort enabled`() {
|
||||||
setSorting(ListItemSorting.autoSortByChecked)
|
setSorting(ListItemSort.AUTO_SORT_BY_CHECKED)
|
||||||
listManager.changeIsChild(3, true)
|
listManager.changeIsChild(3, true)
|
||||||
listManager.changeChecked(2, true)
|
listManager.changeChecked(2, true)
|
||||||
listManager.changeChecked(0, true)
|
listManager.changeChecked(0, true)
|
||||||
|
@ -191,7 +191,7 @@ class ListManagerWithChangeHistoryTest : ListManagerTestBase() {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `undo and redo uncheck all with auto-sort enabled`() {
|
fun `undo and redo uncheck all with auto-sort enabled`() {
|
||||||
setSorting(ListItemSorting.autoSortByChecked)
|
setSorting(ListItemSort.AUTO_SORT_BY_CHECKED)
|
||||||
listManager.changeIsChild(3, true)
|
listManager.changeIsChild(3, true)
|
||||||
listManager.changeChecked(2, true)
|
listManager.changeChecked(2, true)
|
||||||
listManager.changeChecked(0, true)
|
listManager.changeChecked(0, true)
|
||||||
|
@ -208,7 +208,7 @@ class ListManagerWithChangeHistoryTest : ListManagerTestBase() {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `undo and redo delete checked with auto-sort enabled`() {
|
fun `undo and redo delete checked with auto-sort enabled`() {
|
||||||
setSorting(ListItemSorting.autoSortByChecked)
|
setSorting(ListItemSort.AUTO_SORT_BY_CHECKED)
|
||||||
listManager.changeIsChild(3, true)
|
listManager.changeIsChild(3, true)
|
||||||
listManager.changeChecked(2, true)
|
listManager.changeChecked(2, true)
|
||||||
listManager.changeChecked(0, true)
|
listManager.changeChecked(0, true)
|
||||||
|
@ -225,7 +225,7 @@ class ListManagerWithChangeHistoryTest : ListManagerTestBase() {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `undo and redo various changes with auto-sort enabled`() {
|
fun `undo and redo various changes with auto-sort enabled`() {
|
||||||
setSorting(ListItemSorting.autoSortByChecked)
|
setSorting(ListItemSort.AUTO_SORT_BY_CHECKED)
|
||||||
listManager.changeIsChild(1, true)
|
listManager.changeIsChild(1, true)
|
||||||
listManager.changeIsChild(3, true)
|
listManager.changeIsChild(3, true)
|
||||||
listManager.changeIsChild(4, true)
|
listManager.changeIsChild(4, true)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue