mirror of
https://github.com/Kunzisoft/KeePassDX.git
synced 2025-12-04 15:49:33 +01:00
Add room and fully manage database file history by SQL
This commit is contained in:
@@ -81,6 +81,7 @@ android {
|
||||
|
||||
def supportVersion = "27.1.1"
|
||||
def spongycastleVersion = "1.58.0.0"
|
||||
def room_version = "1.1.1"
|
||||
|
||||
dependencies {
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
|
||||
@@ -90,6 +91,10 @@ dependencies {
|
||||
implementation "com.android.support:preference-v14:$supportVersion"
|
||||
implementation "com.android.support:cardview-v7:$supportVersion"
|
||||
implementation 'com.android.support.constraint:constraint-layout:1.1.3'
|
||||
|
||||
implementation "android.arch.persistence.room:runtime:$room_version"
|
||||
kapt "android.arch.persistence.room:compiler:$room_version"
|
||||
|
||||
implementation "com.madgag.spongycastle:core:$spongycastleVersion"
|
||||
implementation "com.madgag.spongycastle:prov:$spongycastleVersion"
|
||||
// Expandable view
|
||||
|
||||
@@ -53,10 +53,7 @@ import com.kunzisoft.keepass.database.action.CreateDatabaseRunnable
|
||||
import com.kunzisoft.keepass.database.action.ProgressDialogThread
|
||||
import com.kunzisoft.keepass.database.element.Database
|
||||
import com.kunzisoft.keepass.education.FileDatabaseSelectActivityEducation
|
||||
import com.kunzisoft.keepass.fileselect.DeleteFileHistoryAsyncTask
|
||||
import com.kunzisoft.keepass.fileselect.FileDatabaseModel
|
||||
import com.kunzisoft.keepass.fileselect.OpenFileHistoryAsyncTask
|
||||
import com.kunzisoft.keepass.fileselect.database.FileDatabaseHistory
|
||||
import com.kunzisoft.keepass.fileselect.*
|
||||
import com.kunzisoft.keepass.magikeyboard.KeyboardHelper
|
||||
import com.kunzisoft.keepass.settings.PreferencesUtil
|
||||
import com.kunzisoft.keepass.tasks.ActionRunnable
|
||||
@@ -165,8 +162,7 @@ class FileDatabaseSelectActivity : StylishActivity(),
|
||||
})
|
||||
|
||||
// Construct adapter with listeners
|
||||
mAdapterDatabaseHistory = FileDatabaseHistoryAdapter(this@FileDatabaseSelectActivity,
|
||||
mFileDatabaseHistory?.databaseUriList ?: ArrayList())
|
||||
mAdapterDatabaseHistory = FileDatabaseHistoryAdapter(this@FileDatabaseSelectActivity)
|
||||
mAdapterDatabaseHistory?.setOnItemClickListener(this)
|
||||
mAdapterDatabaseHistory?.setFileSelectClearListener(this)
|
||||
mAdapterDatabaseHistory?.setFileInformationShowListener(this)
|
||||
@@ -230,8 +226,9 @@ class FileDatabaseSelectActivity : StylishActivity(),
|
||||
private fun performedNextEducation(fileDatabaseSelectActivityEducation: FileDatabaseSelectActivityEducation) {
|
||||
// If no recent files
|
||||
if (createButtonView != null && createButtonView!!.visibility == View.VISIBLE
|
||||
&& mFileDatabaseHistory != null
|
||||
&& !mFileDatabaseHistory!!.hasRecentFiles() && fileDatabaseSelectActivityEducation.checkAndPerformedCreateDatabaseEducation(
|
||||
&& mAdapterDatabaseHistory != null
|
||||
&& mAdapterDatabaseHistory!!.itemCount > 0
|
||||
&& fileDatabaseSelectActivityEducation.checkAndPerformedCreateDatabaseEducation(
|
||||
createButtonView!!,
|
||||
{
|
||||
createNewFile()
|
||||
@@ -264,7 +261,7 @@ class FileDatabaseSelectActivity : StylishActivity(),
|
||||
Log.e(TAG, error, e)
|
||||
}
|
||||
|
||||
private fun launchPasswordActivity(fileName: String, keyFile: String) {
|
||||
private fun launchPasswordActivity(fileName: String, keyFile: String?) {
|
||||
EntrySelectionHelper.doEntrySelectionAction(intent,
|
||||
{
|
||||
try {
|
||||
@@ -327,8 +324,15 @@ class FileDatabaseSelectActivity : StylishActivity(),
|
||||
super.onResume()
|
||||
|
||||
updateExternalStorageWarning()
|
||||
updateFileListVisibility()
|
||||
mAdapterDatabaseHistory!!.notifyDataSetChanged()
|
||||
|
||||
// Construct adapter with listeners
|
||||
mFileDatabaseHistory?.getAll { databaseFileHistoryList ->
|
||||
databaseFileHistoryList?.let {
|
||||
mAdapterDatabaseHistory?.addDatabaseFileHistoryList(it)
|
||||
updateFileListVisibility()
|
||||
mAdapterDatabaseHistory?.notifyDataSetChanged()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onSaveInstanceState(outState: Bundle) {
|
||||
@@ -404,12 +408,9 @@ class FileDatabaseSelectActivity : StylishActivity(),
|
||||
|
||||
}
|
||||
|
||||
override fun onFileItemOpenListener(itemPosition: Int) {
|
||||
OpenFileHistoryAsyncTask({ fileName, keyFile ->
|
||||
if (fileName != null && keyFile != null)
|
||||
launchPasswordActivity(fileName, keyFile)
|
||||
updateFileListVisibility()
|
||||
}, mFileDatabaseHistory).execute(itemPosition)
|
||||
override fun onFileItemOpenListener(fileHistoryEntity: DatabaseFileHistoryEntity) {
|
||||
launchPasswordActivity(fileHistoryEntity.databaseUri, fileHistoryEntity.keyFileUri)
|
||||
updateFileListVisibility()
|
||||
}
|
||||
|
||||
override fun onClickFileInformation(fileDatabaseModel: FileDatabaseModel) {
|
||||
@@ -417,13 +418,16 @@ class FileDatabaseSelectActivity : StylishActivity(),
|
||||
}
|
||||
|
||||
override fun onFileSelectClearListener(fileDatabaseModel: FileDatabaseModel): Boolean {
|
||||
DeleteFileHistoryAsyncTask({
|
||||
fileDatabaseModel.fileUri?.let {
|
||||
mFileDatabaseHistory?.deleteDatabaseUri(it)
|
||||
|
||||
fileDatabaseModel.databaseFileUri?.let {
|
||||
mFileDatabaseHistory?.deleteDatabaseUri(it) { fileHistoryDeleted ->
|
||||
fileHistoryDeleted?.let { databaseFileHistoryDeleted ->
|
||||
mAdapterDatabaseHistory?.deleteDatabaseFileHistory(databaseFileHistoryDeleted)
|
||||
mAdapterDatabaseHistory?.notifyDataSetChanged()
|
||||
updateFileListVisibility()
|
||||
}
|
||||
}
|
||||
mAdapterDatabaseHistory?.notifyDataSetChanged()
|
||||
updateFileListVisibility()
|
||||
}, mFileDatabaseHistory, mAdapterDatabaseHistory).execute(fileDatabaseModel)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
@@ -555,11 +555,12 @@ class PasswordActivity : StylishActivity(),
|
||||
private const val KEY_PASSWORD = "password"
|
||||
private const val KEY_LAUNCH_IMMEDIATELY = "launchImmediately"
|
||||
|
||||
private fun buildAndLaunchIntent(activity: Activity, fileName: String, keyFile: String,
|
||||
private fun buildAndLaunchIntent(activity: Activity, fileName: String, keyFile: String?,
|
||||
intentBuildLauncher: (Intent) -> Unit) {
|
||||
val intent = Intent(activity, PasswordActivity::class.java)
|
||||
intent.putExtra(UriIntentInitTask.KEY_FILENAME, fileName)
|
||||
intent.putExtra(UriIntentInitTask.KEY_KEYFILE, keyFile)
|
||||
if (keyFile != null)
|
||||
intent.putExtra(UriIntentInitTask.KEY_KEYFILE, keyFile)
|
||||
intentBuildLauncher.invoke(intent)
|
||||
}
|
||||
|
||||
@@ -589,7 +590,7 @@ class PasswordActivity : StylishActivity(),
|
||||
fun launch(
|
||||
activity: Activity,
|
||||
fileName: String,
|
||||
keyFile: String) {
|
||||
keyFile: String?) {
|
||||
verifyFileNameUriFromLaunch(fileName)
|
||||
buildAndLaunchIntent(activity, fileName, keyFile) { intent ->
|
||||
activity.startActivity(intent)
|
||||
@@ -606,7 +607,7 @@ class PasswordActivity : StylishActivity(),
|
||||
fun launchForKeyboardResult(
|
||||
activity: Activity,
|
||||
fileName: String,
|
||||
keyFile: String) {
|
||||
keyFile: String?) {
|
||||
verifyFileNameUriFromLaunch(fileName)
|
||||
|
||||
buildAndLaunchIntent(activity, fileName, keyFile) { intent ->
|
||||
@@ -625,7 +626,7 @@ class PasswordActivity : StylishActivity(),
|
||||
fun launchForAutofillResult(
|
||||
activity: Activity,
|
||||
fileName: String,
|
||||
keyFile: String,
|
||||
keyFile: String?,
|
||||
assistStructure: AssistStructure?) {
|
||||
verifyFileNameUriFromLaunch(fileName)
|
||||
|
||||
|
||||
@@ -50,7 +50,7 @@ class FileInformationDialogFragment : DialogFragment() {
|
||||
arguments?.apply {
|
||||
if (containsKey(FILE_SELECT_BEEN_ARG)) {
|
||||
(getSerializable(FILE_SELECT_BEEN_ARG) as FileDatabaseModel?)?.let { fileDatabaseModel ->
|
||||
fileDatabaseModel.fileUri?.let { fileUri ->
|
||||
fileDatabaseModel.databaseFileUri?.let { fileUri ->
|
||||
filePathView.text = Uri.decode(fileUri.toString())
|
||||
}
|
||||
fileNameView.text = fileDatabaseModel.fileName
|
||||
|
||||
@@ -6,15 +6,15 @@ import android.net.Uri
|
||||
import android.os.AsyncTask
|
||||
|
||||
import com.kunzisoft.keepass.R
|
||||
import com.kunzisoft.keepass.fileselect.database.FileDatabaseHistory
|
||||
import com.kunzisoft.keepass.fileselect.FileDatabaseHistory
|
||||
import com.kunzisoft.keepass.utils.UriUtil
|
||||
|
||||
import java.io.File
|
||||
import java.lang.ref.WeakReference
|
||||
|
||||
class UriIntentInitTask(private val weakContext: WeakReference<Context>,
|
||||
private val uriIntentInitTaskCallback: UriIntentInitTaskCallback,
|
||||
private val isKeyFileNeeded: Boolean)
|
||||
private val uriIntentInitTaskCallback: UriIntentInitTaskCallback,
|
||||
private val isKeyFileNeeded: Boolean)
|
||||
: AsyncTask<Intent, Void, Int>() {
|
||||
|
||||
private var databaseUri: Uri? = null
|
||||
@@ -46,13 +46,9 @@ class UriIntentInitTask(private val weakContext: WeakReference<Context>,
|
||||
return R.string.file_not_found
|
||||
}
|
||||
|
||||
if (keyFileUri == null) {
|
||||
keyFileUri = getKeyFileUri(databaseUri)
|
||||
}
|
||||
return null
|
||||
} else if (incoming.scheme == "content") {
|
||||
if (keyFileUri == null) {
|
||||
keyFileUri = getKeyFileUri(databaseUri)
|
||||
}
|
||||
return null
|
||||
} else {
|
||||
return R.string.error_can_not_handle_uri
|
||||
}
|
||||
@@ -61,22 +57,22 @@ class UriIntentInitTask(private val weakContext: WeakReference<Context>,
|
||||
databaseUri = UriUtil.parseUriFile(intent.getStringExtra(KEY_FILENAME))
|
||||
keyFileUri = UriUtil.parseUriFile(intent.getStringExtra(KEY_KEYFILE))
|
||||
|
||||
if (keyFileUri == null || keyFileUri!!.toString().isEmpty()) {
|
||||
keyFileUri = getKeyFileUri(databaseUri)
|
||||
}
|
||||
return null
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
public override fun onPostExecute(result: Int?) {
|
||||
uriIntentInitTaskCallback.onPostInitTask(databaseUri, keyFileUri, result)
|
||||
}
|
||||
|
||||
private fun getKeyFileUri(databaseUri: Uri?): Uri? {
|
||||
return if (isKeyFileNeeded) {
|
||||
FileDatabaseHistory.getInstance(weakContext).getKeyFileUriByDatabaseUri(databaseUri!!)
|
||||
if (isKeyFileNeeded && (keyFileUri == null || keyFileUri!!.toString().isEmpty())) {
|
||||
// Retrieve KeyFile in a thread if needed
|
||||
databaseUri?.let { databaseUriNotNull ->
|
||||
FileDatabaseHistory.getInstance(weakContext)
|
||||
.getKeyFileUriByDatabaseUri(databaseUriNotNull) {
|
||||
uriIntentInitTaskCallback.onPostInitTask(databaseUri, it, result)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
null
|
||||
uriIntentInitTaskCallback.onPostInitTask(databaseUri, keyFileUri, result)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -28,10 +28,11 @@ import android.view.*
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
import com.kunzisoft.keepass.R
|
||||
import com.kunzisoft.keepass.fileselect.DatabaseFileHistoryEntity
|
||||
import com.kunzisoft.keepass.fileselect.FileDatabaseModel
|
||||
import com.kunzisoft.keepass.settings.PreferencesUtil
|
||||
|
||||
class FileDatabaseHistoryAdapter(private val context: Context, private val listFiles: List<String>)
|
||||
class FileDatabaseHistoryAdapter(private val context: Context)
|
||||
: RecyclerView.Adapter<FileDatabaseHistoryAdapter.FileDatabaseHistoryViewHolder>() {
|
||||
|
||||
private val inflater: LayoutInflater = LayoutInflater.from(context)
|
||||
@@ -39,6 +40,8 @@ class FileDatabaseHistoryAdapter(private val context: Context, private val listF
|
||||
private var fileSelectClearListener: FileSelectClearListener? = null
|
||||
private var fileInformationShowListener: FileInformationShowListener? = null
|
||||
|
||||
private val listDatabaseFiles = ArrayList<DatabaseFileHistoryEntity>()
|
||||
|
||||
@ColorInt
|
||||
private val defaultColor: Int
|
||||
@ColorInt
|
||||
@@ -60,25 +63,36 @@ class FileDatabaseHistoryAdapter(private val context: Context, private val listF
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: FileDatabaseHistoryViewHolder, position: Int) {
|
||||
val fileDatabaseModel = FileDatabaseModel(context, listFiles[position])
|
||||
val fileHistoryEntity = listDatabaseFiles[position]
|
||||
|
||||
val fileDatabaseInfo = FileDatabaseModel(context, fileHistoryEntity.databaseUri)
|
||||
// Context menu creation
|
||||
holder.fileContainer.setOnCreateContextMenuListener(ContextMenuBuilder(fileDatabaseModel))
|
||||
holder.fileContainer.setOnCreateContextMenuListener(ContextMenuBuilder(fileDatabaseInfo))
|
||||
// Click item to open file
|
||||
if (fileItemOpenListener != null)
|
||||
holder.fileContainer.setOnClickListener(FileItemClickListener(position))
|
||||
holder.fileContainer.setOnClickListener(FileItemClickListener(fileHistoryEntity))
|
||||
// Assign file name
|
||||
if (PreferencesUtil.isFullFilePathEnable(context))
|
||||
holder.fileName.text = Uri.decode(fileDatabaseModel.fileUri.toString())
|
||||
holder.fileName.text = Uri.decode(fileDatabaseInfo.databaseFileUri.toString())
|
||||
else
|
||||
holder.fileName.text = fileDatabaseModel.fileName
|
||||
holder.fileName.text = fileDatabaseInfo.fileName
|
||||
holder.fileName.textSize = PreferencesUtil.getListTextSize(context)
|
||||
// Click on information
|
||||
if (fileInformationShowListener != null)
|
||||
holder.fileInformation.setOnClickListener(FileInformationClickListener(fileDatabaseModel))
|
||||
holder.fileInformation.setOnClickListener(FileInformationClickListener(fileDatabaseInfo))
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int {
|
||||
return listFiles.size
|
||||
return listDatabaseFiles.size
|
||||
}
|
||||
|
||||
fun addDatabaseFileHistoryList(listDatabaseFileHistoryToAdd: List<DatabaseFileHistoryEntity>) {
|
||||
listDatabaseFiles.clear()
|
||||
listDatabaseFiles.addAll(listDatabaseFileHistoryToAdd)
|
||||
}
|
||||
|
||||
fun deleteDatabaseFileHistory(databaseFileHistoryToDelete: DatabaseFileHistoryEntity) {
|
||||
listDatabaseFiles.remove(databaseFileHistoryToDelete)
|
||||
}
|
||||
|
||||
fun setOnItemClickListener(fileItemOpenListener: FileItemOpenListener) {
|
||||
@@ -94,7 +108,7 @@ class FileDatabaseHistoryAdapter(private val context: Context, private val listF
|
||||
}
|
||||
|
||||
interface FileItemOpenListener {
|
||||
fun onFileItemOpenListener(itemPosition: Int)
|
||||
fun onFileItemOpenListener(fileHistoryEntity: DatabaseFileHistoryEntity)
|
||||
}
|
||||
|
||||
interface FileSelectClearListener {
|
||||
@@ -105,10 +119,10 @@ class FileDatabaseHistoryAdapter(private val context: Context, private val listF
|
||||
fun onClickFileInformation(fileDatabaseModel: FileDatabaseModel)
|
||||
}
|
||||
|
||||
private inner class FileItemClickListener(private val position: Int) : View.OnClickListener {
|
||||
private inner class FileItemClickListener(private val fileHistoryEntity: DatabaseFileHistoryEntity) : View.OnClickListener {
|
||||
|
||||
override fun onClick(v: View) {
|
||||
fileItemOpenListener?.onFileItemOpenListener(position)
|
||||
fileItemOpenListener?.onFileItemOpenListener(fileHistoryEntity)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ import android.util.Log
|
||||
import com.kunzisoft.keepass.R
|
||||
import com.kunzisoft.keepass.database.element.Database
|
||||
import com.kunzisoft.keepass.database.exception.*
|
||||
import com.kunzisoft.keepass.fileselect.database.FileDatabaseHistory
|
||||
import com.kunzisoft.keepass.fileselect.FileDatabaseHistory
|
||||
import com.kunzisoft.keepass.tasks.ActionRunnable
|
||||
import com.kunzisoft.keepass.tasks.ProgressTaskUpdater
|
||||
import java.io.FileNotFoundException
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.kunzisoft.keepass.fileselect
|
||||
|
||||
import android.arch.persistence.db.SupportSQLiteQuery
|
||||
import android.arch.persistence.room.*
|
||||
|
||||
@Dao
|
||||
interface DatabaseFileHistoryDao {
|
||||
@Query("SELECT * FROM database_file_history ORDER BY updated DESC")
|
||||
fun getAll(): List<DatabaseFileHistoryEntity>
|
||||
|
||||
@Query("SELECT * FROM database_file_history WHERE database_uri = :databaseUriString")
|
||||
fun getByDatabaseUri(databaseUriString: String): DatabaseFileHistoryEntity?
|
||||
|
||||
@Insert
|
||||
fun add(vararg databaseFileHistory: DatabaseFileHistoryEntity)
|
||||
|
||||
@Update
|
||||
fun update(vararg databaseFileHistory: DatabaseFileHistoryEntity)
|
||||
|
||||
@Delete
|
||||
fun delete(databaseFileHistory: DatabaseFileHistoryEntity): Int
|
||||
|
||||
/* TODO Replace (Insert not yet supported)
|
||||
@Query("REPLACE INTO database_file_history(keyfile_uri) VALUES(null)")
|
||||
fun deleteAllKeyFiles()
|
||||
*/
|
||||
@RawQuery
|
||||
fun deleteAllKeyFiles(query: SupportSQLiteQuery): Boolean
|
||||
|
||||
@Query("DELETE FROM database_file_history")
|
||||
fun deleteAll()
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.kunzisoft.keepass.fileselect
|
||||
|
||||
import android.arch.persistence.room.Database
|
||||
import android.arch.persistence.room.Room
|
||||
import android.arch.persistence.room.RoomDatabase
|
||||
import android.content.Context
|
||||
|
||||
@Database(entities = [DatabaseFileHistoryEntity::class], version = 1)
|
||||
abstract class DatabaseFileHistoryDatabase : RoomDatabase() {
|
||||
abstract fun databaseFileHistoryDao(): DatabaseFileHistoryDao
|
||||
|
||||
companion object {
|
||||
fun getDatabase(applicationContext: Context): DatabaseFileHistoryDatabase {
|
||||
return Room.databaseBuilder(
|
||||
applicationContext,
|
||||
DatabaseFileHistoryDatabase::class.java, "database-name"
|
||||
).build()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.kunzisoft.keepass.fileselect
|
||||
|
||||
import android.arch.persistence.room.ColumnInfo
|
||||
import android.arch.persistence.room.Entity
|
||||
import android.arch.persistence.room.PrimaryKey
|
||||
|
||||
@Entity(tableName = "database_file_history")
|
||||
data class DatabaseFileHistoryEntity(
|
||||
@PrimaryKey
|
||||
@ColumnInfo(name = "database_uri")
|
||||
val databaseUri: String,
|
||||
|
||||
@ColumnInfo(name = "database_alias")
|
||||
val databaseAlias: String,
|
||||
|
||||
@ColumnInfo(name = "keyfile_uri")
|
||||
var keyFileUri: String?,
|
||||
|
||||
@ColumnInfo(name = "updated")
|
||||
val updated: Long
|
||||
) {
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
|
||||
other as DatabaseFileHistoryEntity
|
||||
|
||||
if (databaseUri != other.databaseUri) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
return databaseUri.hashCode()
|
||||
}
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
/*
|
||||
* Copyright 2019 Jeremy Jamet / Kunzisoft.
|
||||
*
|
||||
* This file is part of KeePass DX.
|
||||
*
|
||||
* KeePass DX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePass DX is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.kunzisoft.keepass.fileselect
|
||||
|
||||
import android.os.AsyncTask
|
||||
import com.kunzisoft.keepass.adapters.FileDatabaseHistoryAdapter
|
||||
|
||||
import com.kunzisoft.keepass.fileselect.database.FileDatabaseHistory
|
||||
|
||||
class DeleteFileHistoryAsyncTask(private val afterDeleteFileHistoryListener: (() -> Unit)?,
|
||||
private val fileHistory: FileDatabaseHistory?,
|
||||
private val adapter: FileDatabaseHistoryAdapter?)
|
||||
: AsyncTask<FileDatabaseModel, Void, Void>() {
|
||||
|
||||
override fun doInBackground(vararg args: FileDatabaseModel): Void? {
|
||||
args[0].fileUri?.let {
|
||||
fileHistory?.deleteDatabaseUri(it)
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
override fun onPostExecute(v: Void?) {
|
||||
adapter?.notifyDataSetChanged()
|
||||
if (adapter == null || adapter.itemCount == 0) {
|
||||
afterDeleteFileHistoryListener?.invoke()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
/*
|
||||
* Copyright 2019 Jeremy Jamet / Kunzisoft.
|
||||
*
|
||||
* This file is part of KeePass DX.
|
||||
*
|
||||
* KeePass DX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePass DX is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.kunzisoft.keepass.fileselect
|
||||
|
||||
import android.arch.persistence.db.SimpleSQLiteQuery
|
||||
import android.content.Context
|
||||
import android.net.Uri
|
||||
import android.os.AsyncTask
|
||||
import com.kunzisoft.keepass.utils.SingletonHolderParameter
|
||||
import java.lang.ref.WeakReference
|
||||
|
||||
class FileDatabaseHistory(val context: WeakReference<Context>) {
|
||||
|
||||
private val databaseFileHistoryDao =
|
||||
DatabaseFileHistoryDatabase
|
||||
.getDatabase(context.get()!!)
|
||||
.databaseFileHistoryDao()
|
||||
|
||||
fun getAll(fileHistoryResultListener: (fileHistoryResult: List<DatabaseFileHistoryEntity>?) -> Unit) {
|
||||
ActionFileHistoryAsyncTask(
|
||||
{
|
||||
databaseFileHistoryDao.getAll()
|
||||
},
|
||||
{
|
||||
fileHistoryResultListener.invoke(it)
|
||||
}
|
||||
).execute()
|
||||
}
|
||||
|
||||
fun addDatabaseUri(databaseUri: Uri, keyFileUri: Uri? = null) {
|
||||
ActionFileHistoryAsyncTask(
|
||||
{
|
||||
val newDatabaseFileHistory = DatabaseFileHistoryEntity(
|
||||
databaseUri.toString(),
|
||||
"",
|
||||
keyFileUri?.toString(),
|
||||
System.currentTimeMillis()
|
||||
)
|
||||
// Update values if history element not yet in the database
|
||||
if (databaseFileHistoryDao.getByDatabaseUri(newDatabaseFileHistory.databaseUri) == null) {
|
||||
databaseFileHistoryDao.add(newDatabaseFileHistory)
|
||||
} else {
|
||||
databaseFileHistoryDao.update(newDatabaseFileHistory)
|
||||
}
|
||||
}
|
||||
).execute()
|
||||
}
|
||||
|
||||
fun getKeyFileUriByDatabaseUri(databaseUri: Uri,
|
||||
keyFileUriResultListener: (Uri?) -> Unit) {
|
||||
ActionFileHistoryAsyncTask(
|
||||
{
|
||||
databaseFileHistoryDao.getByDatabaseUri(databaseUri.toString())
|
||||
},
|
||||
{
|
||||
it?.let { fileHistoryEntity ->
|
||||
fileHistoryEntity.keyFileUri?.let { keyFileUri ->
|
||||
keyFileUriResultListener.invoke(Uri.parse(keyFileUri))
|
||||
}
|
||||
} ?: keyFileUriResultListener.invoke(null)
|
||||
}
|
||||
).execute()
|
||||
}
|
||||
|
||||
fun deleteDatabaseUri(databaseUri: Uri,
|
||||
fileHistoryDeletedResult: (DatabaseFileHistoryEntity?) -> Unit) {
|
||||
|
||||
val databaseFileHistoryDeleted = DatabaseFileHistoryEntity(
|
||||
databaseUri.toString(),
|
||||
"",
|
||||
null,
|
||||
0)
|
||||
|
||||
ActionFileHistoryAsyncTask(
|
||||
{
|
||||
databaseFileHistoryDao.delete(databaseFileHistoryDeleted)
|
||||
},
|
||||
{
|
||||
if (it != null && it > 0)
|
||||
fileHistoryDeletedResult.invoke(databaseFileHistoryDeleted)
|
||||
else
|
||||
fileHistoryDeletedResult.invoke(null)
|
||||
}
|
||||
).execute()
|
||||
}
|
||||
|
||||
fun deleteAllKeyFiles() {
|
||||
// TODO replace for unsupported query databaseFileHistoryDao.deleteAllKeyFiles()
|
||||
ActionFileHistoryAsyncTask(
|
||||
{
|
||||
databaseFileHistoryDao
|
||||
.deleteAllKeyFiles(SimpleSQLiteQuery("REPLACE INTO database_file_history(keyfile_uri) VALUES(null)"))
|
||||
}
|
||||
).execute()
|
||||
}
|
||||
|
||||
fun deleteAll() {
|
||||
ActionFileHistoryAsyncTask(
|
||||
{
|
||||
databaseFileHistoryDao.deleteAll()
|
||||
}
|
||||
).execute()
|
||||
}
|
||||
|
||||
/**
|
||||
* Private class to invoke each method in a separate thread
|
||||
*/
|
||||
private class ActionFileHistoryAsyncTask<T>(
|
||||
private val action: () -> T ,
|
||||
private val afterActionFileHistoryListener: ((fileHistoryResult: T?) -> Unit)? = null
|
||||
) : AsyncTask<Void, Void, T>() {
|
||||
|
||||
override fun doInBackground(vararg args: Void?): T? {
|
||||
return action.invoke()
|
||||
}
|
||||
|
||||
override fun onPostExecute(result: T?) {
|
||||
afterActionFileHistoryListener?.invoke(result)
|
||||
}
|
||||
}
|
||||
|
||||
companion object : SingletonHolderParameter<FileDatabaseHistory, WeakReference<Context>>(::FileDatabaseHistory)
|
||||
}
|
||||
@@ -30,26 +30,26 @@ import java.util.Date
|
||||
class FileDatabaseModel(context: Context, pathFile: String) : Serializable {
|
||||
|
||||
var fileName: String? = ""
|
||||
var fileUri: Uri? = null
|
||||
var databaseFileUri: Uri? = null
|
||||
var lastModification = Date()
|
||||
var size: Long = 0L
|
||||
|
||||
init {
|
||||
fileUri = Uri.parse(pathFile)
|
||||
if (EXTERNAL_STORAGE_AUTHORITY == fileUri!!.authority) {
|
||||
val file = DocumentFile.fromSingleUri(context, fileUri)
|
||||
databaseFileUri = Uri.parse(pathFile)
|
||||
if (EXTERNAL_STORAGE_AUTHORITY == databaseFileUri!!.authority) {
|
||||
val file = DocumentFile.fromSingleUri(context, databaseFileUri)
|
||||
size = file.length()
|
||||
fileName = file.name
|
||||
lastModification = Date(file.lastModified())
|
||||
} else {
|
||||
val file = File(fileUri!!.path!!)
|
||||
val file = File(databaseFileUri!!.path!!)
|
||||
size = file.length()
|
||||
fileName = file.name
|
||||
lastModification = Date(file.lastModified())
|
||||
}
|
||||
|
||||
if (fileName == null || fileName!!.isEmpty()) {
|
||||
fileName = fileUri!!.path
|
||||
fileName = databaseFileUri!!.path
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
/*
|
||||
* Copyright 2019 Jeremy Jamet / Kunzisoft.
|
||||
*
|
||||
* This file is part of KeePass DX.
|
||||
*
|
||||
* KeePass DX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePass DX is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.kunzisoft.keepass.fileselect
|
||||
|
||||
import android.os.AsyncTask
|
||||
|
||||
import com.kunzisoft.keepass.fileselect.database.FileDatabaseHistory
|
||||
|
||||
class OpenFileHistoryAsyncTask(private val afterOpenFileHistoryListener: ((fileName: String?, keyFile: String?) -> Unit)?,
|
||||
private val fileHistory: FileDatabaseHistory?)
|
||||
: AsyncTask<Int, Void, Void>() {
|
||||
|
||||
private var fileName: String? = null
|
||||
private var keyFile: String? = null
|
||||
|
||||
override fun doInBackground(vararg args: Int?): Void? {
|
||||
args[0]?.let {
|
||||
fileName = fileHistory?.getDatabaseAt(it)
|
||||
keyFile = fileHistory?.getKeyFileAt(it)
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
override fun onPostExecute(v: Void?) {
|
||||
afterOpenFileHistoryListener?.invoke(fileName, keyFile)
|
||||
}
|
||||
}
|
||||
@@ -1,227 +0,0 @@
|
||||
/*
|
||||
* Copyright 2017 Brian Pellin, Jeremy Jamet / Kunzisoft.
|
||||
*
|
||||
* This file is part of KeePass DX.
|
||||
*
|
||||
* KeePass DX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePass DX is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.kunzisoft.keepass.fileselect.database;
|
||||
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.database.SQLException;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileFilter;
|
||||
|
||||
public class FileDatabaseHelper {
|
||||
|
||||
private static final String TAG = FileDatabaseHelper.class.getName();
|
||||
|
||||
static final String LAST_FILENAME = "lastFile";
|
||||
static final String LAST_KEYFILE = "lastKey";
|
||||
|
||||
public static final String DATABASE_NAME = "keepassdroid"; // TODO Change db name
|
||||
static final String FILE_TABLE = "files";
|
||||
static final int DATABASE_VERSION = 1;
|
||||
|
||||
public static final int MAX_FILES = 5;
|
||||
|
||||
public static final String KEY_FILE_ID = "_id";
|
||||
public static final String KEY_FILE_FILENAME = "fileName";
|
||||
public static final String KEY_FILE_KEYFILE = "keyFile";
|
||||
public static final String KEY_FILE_UPDATED = "updated";
|
||||
|
||||
static final String DATABASE_CREATE =
|
||||
"create table " + FILE_TABLE + " ( " + KEY_FILE_ID + " integer primary key autoincrement, "
|
||||
+ KEY_FILE_FILENAME + " text not null, " + KEY_FILE_KEYFILE + " text, "
|
||||
+ KEY_FILE_UPDATED + " integer not null);";
|
||||
|
||||
private final Context mCtx;
|
||||
private SQLiteDatabase mDb;
|
||||
|
||||
public FileDatabaseHelper(Context ctx) {
|
||||
mCtx = ctx;
|
||||
}
|
||||
|
||||
public FileDatabaseHelper open() throws SQLException {
|
||||
mDb = new FileDatabaseHistoryHelper(mCtx).getWritableDatabase();
|
||||
return this;
|
||||
}
|
||||
|
||||
public boolean isOpen() {
|
||||
return mDb.isOpen();
|
||||
}
|
||||
|
||||
public void close() {
|
||||
mDb.close();
|
||||
}
|
||||
|
||||
public long createFile(String fileName, String keyFile) {
|
||||
|
||||
// Check to see if this filename is already used
|
||||
Cursor cursor;
|
||||
try {
|
||||
cursor = mDb.query(true, FILE_TABLE, new String[] {KEY_FILE_ID},
|
||||
KEY_FILE_FILENAME + "=?", new String[] {fileName}, null, null, null, null);
|
||||
} catch (Exception e ) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
long result;
|
||||
// If there is an existing entry update it with the new key file
|
||||
if ( cursor.getCount() > 0 ) {
|
||||
cursor.moveToFirst();
|
||||
long id = cursor.getLong(cursor.getColumnIndexOrThrow(KEY_FILE_ID));
|
||||
|
||||
ContentValues vals = new ContentValues();
|
||||
vals.put(KEY_FILE_KEYFILE, keyFile);
|
||||
vals.put(KEY_FILE_UPDATED, System.currentTimeMillis());
|
||||
|
||||
result = mDb.update(FILE_TABLE, vals, KEY_FILE_ID + " = " + id, null);
|
||||
|
||||
// Otherwise add the new entry
|
||||
} else {
|
||||
ContentValues vals = new ContentValues();
|
||||
vals.put(KEY_FILE_FILENAME, fileName);
|
||||
vals.put(KEY_FILE_KEYFILE, keyFile);
|
||||
vals.put(KEY_FILE_UPDATED, System.currentTimeMillis());
|
||||
|
||||
result = mDb.insert(FILE_TABLE, null, vals);
|
||||
|
||||
}
|
||||
// Delete all but the last five records
|
||||
try {
|
||||
deleteAllBut(MAX_FILES);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
cursor.close();
|
||||
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
private void deleteAllBut(int limit) {
|
||||
Cursor cursor = mDb.query(FILE_TABLE, new String[] {KEY_FILE_UPDATED}, null, null, null, null, KEY_FILE_UPDATED);
|
||||
|
||||
if ( cursor.getCount() > limit ) {
|
||||
cursor.moveToFirst();
|
||||
long time = cursor.getLong(cursor.getColumnIndexOrThrow(KEY_FILE_UPDATED));
|
||||
|
||||
mDb.execSQL("DELETE FROM " + FILE_TABLE + " WHERE " + KEY_FILE_UPDATED + "<" + time + ";");
|
||||
}
|
||||
|
||||
cursor.close();
|
||||
|
||||
}
|
||||
|
||||
public void deleteAllKeys() {
|
||||
ContentValues vals = new ContentValues();
|
||||
vals.put(KEY_FILE_KEYFILE, "");
|
||||
|
||||
mDb.update(FILE_TABLE, vals, null, null);
|
||||
}
|
||||
|
||||
public void deleteFile(String filename) {
|
||||
mDb.delete(FILE_TABLE, KEY_FILE_FILENAME + " = ?", new String[] {filename});
|
||||
}
|
||||
|
||||
|
||||
public Cursor fetchAllFiles() {
|
||||
Cursor ret;
|
||||
ret = mDb.query(FILE_TABLE, new String[] {KEY_FILE_ID, KEY_FILE_FILENAME, KEY_FILE_KEYFILE }, null, null, null, null, KEY_FILE_UPDATED + " DESC", Integer.toString(MAX_FILES));
|
||||
return ret;
|
||||
}
|
||||
|
||||
public Cursor fetchFile(long fileId) throws SQLException {
|
||||
Cursor cursor = mDb.query(true, FILE_TABLE, new String[] {KEY_FILE_FILENAME, KEY_FILE_KEYFILE},
|
||||
KEY_FILE_ID + "=" + fileId, null, null, null, null, null);
|
||||
|
||||
if ( cursor != null ) {
|
||||
cursor.moveToFirst();
|
||||
}
|
||||
|
||||
return cursor;
|
||||
|
||||
}
|
||||
|
||||
public String getFileByName(String name) {
|
||||
Cursor cursor = mDb.query(true, FILE_TABLE, new String[] {KEY_FILE_KEYFILE},
|
||||
KEY_FILE_FILENAME + "= ?", new String[] {name}, null, null, null, null);
|
||||
|
||||
if ( cursor == null ) {
|
||||
return "";
|
||||
}
|
||||
|
||||
String filename;
|
||||
|
||||
if ( cursor.moveToFirst() ) {
|
||||
filename = cursor.getString(0);
|
||||
} else {
|
||||
// Cursor is empty
|
||||
filename = "";
|
||||
}
|
||||
cursor.close();
|
||||
return filename;
|
||||
}
|
||||
|
||||
public boolean hasRecentFiles() {
|
||||
Cursor cursor = fetchAllFiles();
|
||||
|
||||
boolean hasRecent = cursor.getCount() > 0;
|
||||
cursor.close();
|
||||
|
||||
return hasRecent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a database including its journal file and other auxiliary files
|
||||
* that may have been created by the database engine.
|
||||
*
|
||||
* @param ctx Context to get database path
|
||||
* @return True if the database was successfully deleted.
|
||||
*/
|
||||
public static boolean deleteDatabase(Context ctx) {
|
||||
File file = ctx.getDatabasePath(DATABASE_NAME);
|
||||
if (file == null) {
|
||||
throw new IllegalArgumentException("file must not be null");
|
||||
}
|
||||
|
||||
boolean deleted = false;
|
||||
deleted |= file.delete();
|
||||
deleted |= new File(file.getPath() + "-journal").delete();
|
||||
deleted |= new File(file.getPath() + "-shm").delete();
|
||||
deleted |= new File(file.getPath() + "-wal").delete();
|
||||
|
||||
File dir = file.getParentFile();
|
||||
if (dir != null) {
|
||||
final String prefix = file.getName() + "-mj";
|
||||
final FileFilter filter = new FileFilter() {
|
||||
@Override
|
||||
public boolean accept(File candidate) {
|
||||
return candidate.getName().startsWith(prefix);
|
||||
}
|
||||
};
|
||||
for (File masterJournal : dir.listFiles(filter)) {
|
||||
deleted |= masterJournal.delete();
|
||||
}
|
||||
}
|
||||
return deleted;
|
||||
}
|
||||
}
|
||||
@@ -1,258 +0,0 @@
|
||||
/*
|
||||
* Copyright 2019 Jeremy Jamet / Kunzisoft.
|
||||
*
|
||||
* This file is part of KeePass DX.
|
||||
*
|
||||
* KeePass DX is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* KeePass DX is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.kunzisoft.keepass.fileselect.database
|
||||
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
import android.content.SharedPreferences.OnSharedPreferenceChangeListener
|
||||
import android.net.Uri
|
||||
import android.preference.PreferenceManager
|
||||
import com.kunzisoft.keepass.R
|
||||
import com.kunzisoft.keepass.utils.SingletonHolderParameter
|
||||
import com.kunzisoft.keepass.utils.UriUtil
|
||||
import java.lang.ref.WeakReference
|
||||
import java.util.*
|
||||
|
||||
class FileDatabaseHistory private constructor(private val context: WeakReference<Context>) {
|
||||
|
||||
private val mDatabasesUriList = ArrayList<String>()
|
||||
private val mKeyFilesUriList = ArrayList<String>()
|
||||
|
||||
private val mPreferences: SharedPreferences = PreferenceManager.getDefaultSharedPreferences(context.get())
|
||||
|
||||
var isEnabled: Boolean = false
|
||||
|
||||
val databaseUriList: List<String>
|
||||
get() {
|
||||
init()
|
||||
return mDatabasesUriList
|
||||
}
|
||||
|
||||
private val onSharedPreferenceChangeListener = OnSharedPreferenceChangeListener { sharedPreferences, key ->
|
||||
if (key == context.get()?.getString(R.string.recentfile_key)) {
|
||||
isEnabled = sharedPreferences.getBoolean(
|
||||
context.get()?.getString(R.string.recentfile_key),
|
||||
context.get()?.resources?.getBoolean(R.bool.recentfile_default) ?: isEnabled)
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
mPreferences.unregisterOnSharedPreferenceChangeListener(onSharedPreferenceChangeListener)
|
||||
context.get()?.resources?.let {
|
||||
isEnabled = mPreferences.getBoolean(
|
||||
it.getString(R.string.recentfile_key),
|
||||
it.getBoolean(R.bool.recentfile_default))
|
||||
}
|
||||
mPreferences.registerOnSharedPreferenceChangeListener(onSharedPreferenceChangeListener)
|
||||
}
|
||||
|
||||
private var init = false
|
||||
@Synchronized
|
||||
private fun init() {
|
||||
if (!init) {
|
||||
if (!upgradeFromSQL()) {
|
||||
loadPrefs()
|
||||
}
|
||||
|
||||
init = true
|
||||
}
|
||||
}
|
||||
|
||||
private fun upgradeFromSQL(): Boolean {
|
||||
|
||||
try {
|
||||
// Check for a database to upgrade from
|
||||
if (context.get()?.getDatabasePath(FileDatabaseHelper.DATABASE_NAME)?.exists() != true) {
|
||||
return false
|
||||
}
|
||||
|
||||
mDatabasesUriList.clear()
|
||||
mKeyFilesUriList.clear()
|
||||
|
||||
val helper = FileDatabaseHelper(context.get())
|
||||
helper.open()
|
||||
val cursor = helper.fetchAllFiles()
|
||||
|
||||
val dbIndex = cursor.getColumnIndex(FileDatabaseHelper.KEY_FILE_FILENAME)
|
||||
val keyIndex = cursor.getColumnIndex(FileDatabaseHelper.KEY_FILE_KEYFILE)
|
||||
|
||||
if (cursor.moveToFirst()) {
|
||||
while (cursor.moveToNext()) {
|
||||
mDatabasesUriList.add(cursor.getString(dbIndex))
|
||||
mKeyFilesUriList.add(cursor.getString(keyIndex))
|
||||
}
|
||||
}
|
||||
|
||||
savePrefs()
|
||||
|
||||
cursor.close()
|
||||
helper.close()
|
||||
|
||||
} catch (e: Exception) {
|
||||
// If upgrading fails, we'll just give up on it.
|
||||
}
|
||||
|
||||
try {
|
||||
FileDatabaseHelper.deleteDatabase(context.get())
|
||||
} catch (e: Exception) {
|
||||
// If we fail to delete it, just move on
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
@JvmOverloads
|
||||
fun addDatabaseUri(databaseUri: Uri?, keyFileUri: Uri? = null) {
|
||||
if (!isEnabled || databaseUri == null) return
|
||||
|
||||
init()
|
||||
|
||||
// Remove any existing instance of the same filename
|
||||
deleteDatabaseUri(databaseUri, false)
|
||||
|
||||
mDatabasesUriList.add(0, databaseUri.toString())
|
||||
|
||||
val key = keyFileUri?.toString() ?: ""
|
||||
mKeyFilesUriList.add(0, key)
|
||||
|
||||
trimLists()
|
||||
savePrefs()
|
||||
}
|
||||
|
||||
fun hasRecentFiles(): Boolean {
|
||||
if (!isEnabled) return false
|
||||
|
||||
init()
|
||||
|
||||
return mDatabasesUriList.size > 0
|
||||
}
|
||||
|
||||
fun getDatabaseAt(i: Int): String {
|
||||
init()
|
||||
return mDatabasesUriList[i]
|
||||
}
|
||||
|
||||
fun getKeyFileAt(i: Int): String {
|
||||
init()
|
||||
return mKeyFilesUriList[i]
|
||||
}
|
||||
|
||||
private fun loadPrefs() {
|
||||
loadList(DB_KEY, mDatabasesUriList)
|
||||
loadList(KEY_FILE_KEY, mKeyFilesUriList)
|
||||
}
|
||||
|
||||
private fun savePrefs() {
|
||||
saveList(DB_KEY, mDatabasesUriList)
|
||||
saveList(KEY_FILE_KEY, mKeyFilesUriList)
|
||||
}
|
||||
|
||||
private fun loadList(keyPrefix: String, list: MutableList<String>) {
|
||||
val size = mPreferences.getInt(keyPrefix, 0)
|
||||
|
||||
list.clear()
|
||||
for (i in 0 until size) {
|
||||
mPreferences.getString(keyPrefix + "_" + i, "")?.let {
|
||||
list.add(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun saveList(keyPrefix: String, list: List<String>) {
|
||||
val edit = mPreferences.edit()
|
||||
val size = list.size
|
||||
edit.putInt(keyPrefix, size)
|
||||
|
||||
for (i in 0 until size) {
|
||||
edit.putString(keyPrefix + "_" + i, list[i])
|
||||
}
|
||||
edit.apply()
|
||||
}
|
||||
|
||||
@JvmOverloads
|
||||
fun deleteDatabaseUri(uri: Uri, save: Boolean = true) {
|
||||
init()
|
||||
|
||||
val uriName = uri.toString()
|
||||
val fileName = uri.path
|
||||
|
||||
for (i in mDatabasesUriList.indices) {
|
||||
val entry = mDatabasesUriList[i]
|
||||
if (uriName == entry || fileName == entry) {
|
||||
mDatabasesUriList.removeAt(i)
|
||||
mKeyFilesUriList.removeAt(i)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if (save) {
|
||||
savePrefs()
|
||||
}
|
||||
}
|
||||
|
||||
fun getKeyFileUriByDatabaseUri(uri: Uri): Uri? {
|
||||
if (!isEnabled)
|
||||
return null
|
||||
init()
|
||||
val size = mDatabasesUriList.size
|
||||
for (i in 0 until size) {
|
||||
if (uri == UriUtil.parseUriFile(mDatabasesUriList[i])) {
|
||||
return UriUtil.parseUriFile(mKeyFilesUriList[i])
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
fun deleteAll() {
|
||||
init()
|
||||
|
||||
mDatabasesUriList.clear()
|
||||
mKeyFilesUriList.clear()
|
||||
|
||||
savePrefs()
|
||||
}
|
||||
|
||||
fun deleteAllKeys() {
|
||||
init()
|
||||
|
||||
mKeyFilesUriList.clear()
|
||||
|
||||
val size = mDatabasesUriList.size
|
||||
for (i in 0 until size) {
|
||||
mKeyFilesUriList.add("")
|
||||
}
|
||||
|
||||
savePrefs()
|
||||
}
|
||||
|
||||
private fun trimLists() {
|
||||
val size = mDatabasesUriList.size
|
||||
for (i in FileDatabaseHelper.MAX_FILES until size) {
|
||||
mDatabasesUriList.removeAt(i)
|
||||
mKeyFilesUriList.removeAt(i)
|
||||
}
|
||||
}
|
||||
|
||||
companion object : SingletonHolderParameter<FileDatabaseHistory, WeakReference<Context>>(::FileDatabaseHistory) {
|
||||
|
||||
private const val DB_KEY = "recent_databases"
|
||||
private const val KEY_FILE_KEY = "recent_keyfiles"
|
||||
}
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
package com.kunzisoft.keepass.fileselect.database;
|
||||
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.database.sqlite.SQLiteOpenHelper;
|
||||
import android.util.Log;
|
||||
|
||||
class FileDatabaseHistoryHelper extends SQLiteOpenHelper {
|
||||
private final SharedPreferences settings;
|
||||
|
||||
FileDatabaseHistoryHelper(Context context) {
|
||||
super(context, FileDatabaseHelper.DATABASE_NAME, null, FileDatabaseHelper.DATABASE_VERSION);
|
||||
settings = context.getSharedPreferences("PasswordActivity", Context.MODE_PRIVATE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(SQLiteDatabase sqLiteDatabase) {
|
||||
sqLiteDatabase.execSQL(FileDatabaseHelper.DATABASE_CREATE);
|
||||
|
||||
// Migrate preference to database if it is set.
|
||||
String lastFile = settings.getString(FileDatabaseHelper.LAST_FILENAME, "");
|
||||
String lastKey = settings.getString(FileDatabaseHelper.LAST_KEYFILE,"");
|
||||
|
||||
if ( lastFile.length() > 0 ) {
|
||||
ContentValues contentValues = new ContentValues();
|
||||
contentValues.put(FileDatabaseHelper.KEY_FILE_FILENAME, lastFile);
|
||||
contentValues.put(FileDatabaseHelper.KEY_FILE_UPDATED, System.currentTimeMillis());
|
||||
|
||||
if ( lastKey.length() > 0 ) {
|
||||
contentValues.put(FileDatabaseHelper.KEY_FILE_KEYFILE, lastKey);
|
||||
}
|
||||
|
||||
sqLiteDatabase.insert(FileDatabaseHelper.FILE_TABLE, null, contentValues);
|
||||
|
||||
// Clear old preferences
|
||||
deletePrefs(settings);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
|
||||
// Only one database version so far
|
||||
}
|
||||
|
||||
private void deletePrefs(SharedPreferences prefs) {
|
||||
// We won't worry too much if this fails
|
||||
try {
|
||||
SharedPreferences.Editor editor = prefs.edit();
|
||||
editor.remove(FileDatabaseHelper.LAST_FILENAME);
|
||||
editor.remove(FileDatabaseHelper.LAST_KEYFILE);
|
||||
editor.apply();
|
||||
} catch (Exception e) {
|
||||
Log.e(FileDatabaseHelper.class.getName(), "Unable to delete database preference", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -47,7 +47,7 @@ import com.kunzisoft.keepass.activities.helpers.ReadOnlyHelper
|
||||
import com.kunzisoft.keepass.activities.stylish.Stylish
|
||||
import com.kunzisoft.keepass.database.element.Database
|
||||
import com.kunzisoft.keepass.education.Education
|
||||
import com.kunzisoft.keepass.fileselect.database.FileDatabaseHistory
|
||||
import com.kunzisoft.keepass.fileselect.FileDatabaseHistory
|
||||
import com.kunzisoft.keepass.fingerprint.FingerPrintHelper
|
||||
import com.kunzisoft.keepass.fingerprint.FingerPrintViewsManager
|
||||
import com.kunzisoft.keepass.icons.IconPackChooser
|
||||
@@ -119,7 +119,7 @@ class NestedSettingsFragment : PreferenceFragmentCompat(), Preference.OnPreferen
|
||||
val keyFile = findPreference(getString(R.string.keyfile_key))
|
||||
keyFile.setOnPreferenceChangeListener { _, newValue ->
|
||||
if (!(newValue as Boolean)) {
|
||||
FileDatabaseHistory.getInstance(WeakReference(activity.applicationContext)).deleteAllKeys()
|
||||
FileDatabaseHistory.getInstance(WeakReference(activity.applicationContext)).deleteAllKeyFiles()
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user