mirror of
https://github.com/Kunzisoft/KeePassDX.git
synced 2025-12-04 15:49:33 +01:00
Merge branch 'develop' of github.com:Kunzisoft/KeePassDX into develop
This commit is contained in:
@@ -1,3 +1,7 @@
|
|||||||
|
KeepassDX (2.5.0.0beta23)
|
||||||
|
* New, more secure database creation workflow
|
||||||
|
* Add alias for history files (WARNING: history is erased)
|
||||||
|
|
||||||
KeepassDX (2.5.0.0beta22)
|
KeepassDX (2.5.0.0beta22)
|
||||||
* Rebuild code for actions
|
* Rebuild code for actions
|
||||||
* Add UUID as entry view
|
* Add UUID as entry view
|
||||||
|
|||||||
1
app/.gitignore
vendored
1
app/.gitignore
vendored
@@ -1 +1,2 @@
|
|||||||
|
.cxx
|
||||||
.externalNativeBuild
|
.externalNativeBuild
|
||||||
|
|||||||
@@ -4,15 +4,15 @@ apply plugin: 'kotlin-android-extensions'
|
|||||||
apply plugin: 'kotlin-kapt'
|
apply plugin: 'kotlin-kapt'
|
||||||
|
|
||||||
android {
|
android {
|
||||||
compileSdkVersion 27
|
compileSdkVersion 28
|
||||||
buildToolsVersion '28.0.3'
|
buildToolsVersion '28.0.3'
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId "com.kunzisoft.keepass"
|
applicationId "com.kunzisoft.keepass"
|
||||||
minSdkVersion 14
|
minSdkVersion 14
|
||||||
targetSdkVersion 27
|
targetSdkVersion 28
|
||||||
versionCode = 22
|
versionCode = 23
|
||||||
versionName = "2.5.0.0beta22"
|
versionName = "2.5.0.0beta23"
|
||||||
multiDexEnabled true
|
multiDexEnabled true
|
||||||
|
|
||||||
testApplicationId = "com.kunzisoft.keepass.tests"
|
testApplicationId = "com.kunzisoft.keepass.tests"
|
||||||
@@ -79,42 +79,34 @@ android {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def supportVersion = "27.1.1"
|
|
||||||
def spongycastleVersion = "1.58.0.0"
|
def spongycastleVersion = "1.58.0.0"
|
||||||
def permissionDispatcherVersion = "3.3.1"
|
def room_version = "2.1.0"
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
|
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
|
||||||
implementation "com.android.support:appcompat-v7:$supportVersion"
|
implementation 'androidx.appcompat:appcompat:1.0.2'
|
||||||
implementation "com.android.support:design:$supportVersion"
|
implementation 'androidx.preference:preference:1.0.0'
|
||||||
implementation "com.android.support:preference-v7:$supportVersion"
|
implementation 'androidx.legacy:legacy-preference-v14:1.0.0'
|
||||||
implementation "com.android.support:preference-v14:$supportVersion"
|
implementation 'androidx.cardview:cardview:1.0.0'
|
||||||
implementation "com.android.support:cardview-v7:$supportVersion"
|
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
|
||||||
implementation 'com.android.support.constraint:constraint-layout:1.1.3'
|
implementation 'com.google.android.material:material:1.0.0'
|
||||||
|
|
||||||
|
implementation "androidx.room:room-runtime:$room_version"
|
||||||
|
kapt "androidx.room:room-compiler:$room_version"
|
||||||
|
|
||||||
implementation "com.madgag.spongycastle:core:$spongycastleVersion"
|
implementation "com.madgag.spongycastle:core:$spongycastleVersion"
|
||||||
implementation "com.madgag.spongycastle:prov:$spongycastleVersion"
|
implementation "com.madgag.spongycastle:prov:$spongycastleVersion"
|
||||||
// Expandable view
|
// Expandable view
|
||||||
implementation 'net.cachapa.expandablelayout:expandablelayout:2.9.2'
|
implementation 'net.cachapa.expandablelayout:expandablelayout:2.9.2'
|
||||||
// Time
|
// Time
|
||||||
implementation 'joda-time:joda-time:2.9.9'
|
implementation 'joda-time:joda-time:2.9.9'
|
||||||
implementation 'org.sufficientlysecure:html-textview:3.5'
|
// Education
|
||||||
implementation 'com.nononsenseapps:filepicker:4.1.0'
|
|
||||||
implementation 'com.getkeepsafe.taptargetview:taptargetview:1.12.0'
|
implementation 'com.getkeepsafe.taptargetview:taptargetview:1.12.0'
|
||||||
// Permissions
|
|
||||||
implementation("com.github.hotchemi:permissionsdispatcher:$permissionDispatcherVersion") {
|
|
||||||
// if you don't use android.app.Fragment you can exclude support for them
|
|
||||||
exclude module: "support-v13"
|
|
||||||
}
|
|
||||||
kapt "com.github.hotchemi:permissionsdispatcher-processor:$permissionDispatcherVersion"
|
|
||||||
// Apache Commons Collections
|
// Apache Commons Collections
|
||||||
implementation 'commons-collections:commons-collections:3.2.1'
|
implementation 'commons-collections:commons-collections:3.2.1'
|
||||||
implementation 'org.apache.commons:commons-io:1.3.2'
|
implementation 'org.apache.commons:commons-io:1.3.2'
|
||||||
// Base64
|
// Base64
|
||||||
implementation 'biz.source_code:base64coder:2010-12-19'
|
implementation 'biz.source_code:base64coder:2010-12-19'
|
||||||
// IO-Extras
|
|
||||||
implementation 'com.github.davidmoten:io-extras:0.1'
|
|
||||||
implementation 'com.google.code.gson:gson:2.8.4'
|
|
||||||
implementation 'com.google.guava:guava:23.0-android'
|
|
||||||
// Icon pack
|
// Icon pack
|
||||||
implementation project(path: ':icon-pack-classic')
|
implementation project(path: ':icon-pack-classic')
|
||||||
implementation project(path: ':icon-pack-material')
|
implementation project(path: ':icon-pack-material')
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
|
||||||
package="com.kunzisoft.keepass"
|
package="com.kunzisoft.keepass"
|
||||||
android:installLocation="auto">
|
android:installLocation="auto">
|
||||||
<supports-screens
|
<supports-screens
|
||||||
@@ -8,6 +7,7 @@
|
|||||||
android:normalScreens="true"
|
android:normalScreens="true"
|
||||||
android:largeScreens="true"
|
android:largeScreens="true"
|
||||||
android:anyDensity="true" />
|
android:anyDensity="true" />
|
||||||
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||||
<uses-permission android:name="android.permission.USE_FINGERPRINT"/>
|
<uses-permission android:name="android.permission.USE_FINGERPRINT"/>
|
||||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
|
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
|
||||||
<uses-permission android:name="android.permission.VIBRATE"/>
|
<uses-permission android:name="android.permission.VIBRATE"/>
|
||||||
@@ -48,7 +48,7 @@
|
|||||||
<category android:name="android.intent.category.BROWSABLE" />
|
<category android:name="android.intent.category.BROWSABLE" />
|
||||||
<data android:scheme="file" />
|
<data android:scheme="file" />
|
||||||
<data android:scheme="content" />
|
<data android:scheme="content" />
|
||||||
<data android:mimeType="application/octet-stream" />
|
<data android:mimeType="*/*" />
|
||||||
<data android:host="*" />
|
<data android:host="*" />
|
||||||
<data android:pathPattern=".*\\.kdb" />
|
<data android:pathPattern=".*\\.kdb" />
|
||||||
<data android:pathPattern=".*\\..*\\.kdb" />
|
<data android:pathPattern=".*\\..*\\.kdb" />
|
||||||
@@ -71,29 +71,28 @@
|
|||||||
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.kdbx" />
|
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.kdbx" />
|
||||||
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.kdbx" />
|
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.kdbx" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
<intent-filter tools:ignore="AppLinkUrlError">
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.VIEW"/>
|
<action android:name="android.intent.action.VIEW"/>
|
||||||
<category android:name="android.intent.category.DEFAULT"/>
|
<category android:name="android.intent.category.DEFAULT"/>
|
||||||
<category android:name="android.intent.category.BROWSABLE"/>
|
<category android:name="android.intent.category.BROWSABLE"/>
|
||||||
<data android:mimeType="application/octet-stream"/>
|
<data android:scheme="file" />
|
||||||
</intent-filter>
|
<data android:scheme="content" />
|
||||||
</activity>
|
<data android:mimeType="application/octet-stream" />
|
||||||
<!-- Folder picker -->
|
<data android:mimeType="application/x-kdb" />
|
||||||
<provider
|
<data android:mimeType="application/x-kdbx" />
|
||||||
android:name="android.support.v4.content.FileProvider"
|
<data android:mimeType="application/x-keepass" />
|
||||||
android:authorities="${applicationId}.provider"
|
<data android:host="*" />
|
||||||
android:exported="false"
|
<data android:pathPattern=".*" />
|
||||||
android:grantUriPermissions="true">
|
<data android:pathPattern=".*\\.*" />
|
||||||
<meta-data
|
<data android:pathPattern=".*\\..*\\.*" />
|
||||||
android:name="android.support.FILE_PROVIDER_PATHS"
|
<data android:pathPattern=".*\\..*\\..*\\.*" />
|
||||||
android:resource="@xml/nnf_provider_paths" />
|
<data android:pathPattern=".*\\..*\\..*\\..*\\.*" />
|
||||||
</provider>
|
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\.*" />
|
||||||
<activity
|
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\.*" />
|
||||||
android:name=".activities.stylish.FilePickerStylishActivity"
|
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\.*" />
|
||||||
android:label="@string/app_name">
|
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.*" />
|
||||||
<intent-filter>
|
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.*" />
|
||||||
<action android:name="android.intent.action.GET_CONTENT" />
|
<data android:pathPattern=".*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\..*\\.*" />
|
||||||
<category android:name="android.intent.category.DEFAULT" />
|
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
<!-- Main Activity -->
|
<!-- Main Activity -->
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ package com.kunzisoft.keepass.activities
|
|||||||
|
|
||||||
import android.content.pm.PackageManager.NameNotFoundException
|
import android.content.pm.PackageManager.NameNotFoundException
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.support.v7.widget.Toolbar
|
import androidx.appcompat.widget.Toolbar
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.view.MenuItem
|
import android.view.MenuItem
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
|
|||||||
@@ -19,15 +19,14 @@
|
|||||||
package com.kunzisoft.keepass.activities
|
package com.kunzisoft.keepass.activities
|
||||||
|
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.content.ActivityNotFoundException
|
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.graphics.Color
|
import android.graphics.Color
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.os.Handler
|
import android.os.Handler
|
||||||
import android.support.design.widget.CollapsingToolbarLayout
|
import com.google.android.material.appbar.CollapsingToolbarLayout
|
||||||
import android.support.v7.app.AlertDialog
|
import androidx.appcompat.app.AlertDialog
|
||||||
import android.support.v7.widget.Toolbar
|
import androidx.appcompat.widget.Toolbar
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.view.Menu
|
import android.view.Menu
|
||||||
import android.view.MenuItem
|
import android.view.MenuItem
|
||||||
@@ -45,12 +44,11 @@ import com.kunzisoft.keepass.icons.assignDatabaseIcon
|
|||||||
import com.kunzisoft.keepass.magikeyboard.MagikIME
|
import com.kunzisoft.keepass.magikeyboard.MagikIME
|
||||||
import com.kunzisoft.keepass.notifications.ClipboardEntryNotificationService
|
import com.kunzisoft.keepass.notifications.ClipboardEntryNotificationService
|
||||||
import com.kunzisoft.keepass.settings.PreferencesUtil
|
import com.kunzisoft.keepass.settings.PreferencesUtil
|
||||||
import com.kunzisoft.keepass.settings.PreferencesUtil.isFirstTimeAskAllowCopyPasswordAndProtectedFields
|
|
||||||
import com.kunzisoft.keepass.settings.SettingsAutofillActivity
|
import com.kunzisoft.keepass.settings.SettingsAutofillActivity
|
||||||
import com.kunzisoft.keepass.timeout.ClipboardHelper
|
import com.kunzisoft.keepass.timeout.ClipboardHelper
|
||||||
import com.kunzisoft.keepass.timeout.TimeoutHelper
|
import com.kunzisoft.keepass.timeout.TimeoutHelper
|
||||||
import com.kunzisoft.keepass.utils.MenuUtil
|
import com.kunzisoft.keepass.utils.MenuUtil
|
||||||
import com.kunzisoft.keepass.utils.Util
|
import com.kunzisoft.keepass.utils.UriUtil
|
||||||
import com.kunzisoft.keepass.view.EntryContentsView
|
import com.kunzisoft.keepass.view.EntryContentsView
|
||||||
|
|
||||||
class EntryActivity : LockingHideActivity() {
|
class EntryActivity : LockingHideActivity() {
|
||||||
@@ -164,9 +162,33 @@ class EntryActivity : LockingHideActivity() {
|
|||||||
getString(R.string.entry_user_name)))
|
getString(R.string.entry_user_name)))
|
||||||
})
|
})
|
||||||
|
|
||||||
val allowCopyPassword = PreferencesUtil.allowCopyPasswordAndProtectedFields(this)
|
val isFirstTimeAskAllowCopyPasswordAndProtectedFields =
|
||||||
entryContentsView?.assignPassword(entry.password, allowCopyPassword)
|
PreferencesUtil.isFirstTimeAskAllowCopyPasswordAndProtectedFields(this)
|
||||||
if (allowCopyPassword) {
|
val allowCopyPasswordAndProtectedFields =
|
||||||
|
PreferencesUtil.allowCopyPasswordAndProtectedFields(this)
|
||||||
|
|
||||||
|
val showWarningClipboardDialogOnClickListener = View.OnClickListener {
|
||||||
|
AlertDialog.Builder(this@EntryActivity)
|
||||||
|
.setMessage(getString(R.string.allow_copy_password_warning) +
|
||||||
|
"\n\n" +
|
||||||
|
getString(R.string.clipboard_warning))
|
||||||
|
.create().apply {
|
||||||
|
setButton(AlertDialog.BUTTON_POSITIVE, getText(R.string.enable)) {dialog, _ ->
|
||||||
|
PreferencesUtil.setAllowCopyPasswordAndProtectedFields(this@EntryActivity, true)
|
||||||
|
dialog.dismiss()
|
||||||
|
fillEntryDataInContentsView(entry)
|
||||||
|
}
|
||||||
|
setButton(AlertDialog.BUTTON_NEGATIVE, getText(R.string.disable)) { dialog, _ ->
|
||||||
|
PreferencesUtil.setAllowCopyPasswordAndProtectedFields(this@EntryActivity, false)
|
||||||
|
dialog.dismiss()
|
||||||
|
fillEntryDataInContentsView(entry)
|
||||||
|
}
|
||||||
|
show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
entryContentsView?.assignPassword(entry.password, allowCopyPasswordAndProtectedFields)
|
||||||
|
if (allowCopyPasswordAndProtectedFields) {
|
||||||
entryContentsView?.assignPasswordCopyListener(View.OnClickListener {
|
entryContentsView?.assignPasswordCopyListener(View.OnClickListener {
|
||||||
clipboardHelper?.timeoutCopyToClipboard(entry.password,
|
clipboardHelper?.timeoutCopyToClipboard(entry.password,
|
||||||
getString(R.string.copy_field,
|
getString(R.string.copy_field,
|
||||||
@@ -174,27 +196,8 @@ class EntryActivity : LockingHideActivity() {
|
|||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
// If dialog not already shown
|
// If dialog not already shown
|
||||||
if (isFirstTimeAskAllowCopyPasswordAndProtectedFields(this)) {
|
if (isFirstTimeAskAllowCopyPasswordAndProtectedFields) {
|
||||||
entryContentsView?.assignPasswordCopyListener(View.OnClickListener {
|
entryContentsView?.assignPasswordCopyListener(showWarningClipboardDialogOnClickListener)
|
||||||
val message = getString(R.string.allow_copy_password_warning) +
|
|
||||||
"\n\n" +
|
|
||||||
getString(R.string.clipboard_warning)
|
|
||||||
val warningDialog = AlertDialog.Builder(this@EntryActivity)
|
|
||||||
.setMessage(message).create()
|
|
||||||
warningDialog.setButton(AlertDialog.BUTTON_POSITIVE, getText(android.R.string.ok)
|
|
||||||
) { dialog, _ ->
|
|
||||||
PreferencesUtil.setAllowCopyPasswordAndProtectedFields(this@EntryActivity, true)
|
|
||||||
dialog.dismiss()
|
|
||||||
fillEntryDataInContentsView(entry)
|
|
||||||
}
|
|
||||||
warningDialog.setButton(AlertDialog.BUTTON_NEGATIVE, getText(android.R.string.cancel)
|
|
||||||
) { dialog, _ ->
|
|
||||||
PreferencesUtil.setAllowCopyPasswordAndProtectedFields(this@EntryActivity, false)
|
|
||||||
dialog.dismiss()
|
|
||||||
fillEntryDataInContentsView(entry)
|
|
||||||
}
|
|
||||||
warningDialog.show()
|
|
||||||
})
|
|
||||||
} else {
|
} else {
|
||||||
entryContentsView?.assignPasswordCopyListener(null)
|
entryContentsView?.assignPasswordCopyListener(null)
|
||||||
}
|
}
|
||||||
@@ -209,13 +212,23 @@ class EntryActivity : LockingHideActivity() {
|
|||||||
entryContentsView?.clearExtraFields()
|
entryContentsView?.clearExtraFields()
|
||||||
|
|
||||||
entry.fields.doActionToAllCustomProtectedField { label, value ->
|
entry.fields.doActionToAllCustomProtectedField { label, value ->
|
||||||
val showAction = !value.isProtected || PreferencesUtil.allowCopyPasswordAndProtectedFields(this@EntryActivity)
|
|
||||||
entryContentsView?.addExtraField(label, value, showAction, View.OnClickListener {
|
val allowCopyProtectedField = !value.isProtected || allowCopyPasswordAndProtectedFields
|
||||||
clipboardHelper?.timeoutCopyToClipboard(
|
if (allowCopyProtectedField) {
|
||||||
value.toString(),
|
entryContentsView?.addExtraField(label, value, allowCopyProtectedField, View.OnClickListener {
|
||||||
getString(R.string.copy_field, label)
|
clipboardHelper?.timeoutCopyToClipboard(
|
||||||
)
|
value.toString(),
|
||||||
})
|
getString(R.string.copy_field, label)
|
||||||
|
)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
// If dialog not already shown
|
||||||
|
if (isFirstTimeAskAllowCopyPasswordAndProtectedFields) {
|
||||||
|
entryContentsView?.addExtraField(label, value, allowCopyProtectedField, showWarningClipboardDialogOnClickListener)
|
||||||
|
} else {
|
||||||
|
entryContentsView?.addExtraField(label, value, allowCopyProtectedField, null)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -306,7 +319,7 @@ class EntryActivity : LockingHideActivity() {
|
|||||||
|
|
||||||
private fun performedNextEducation(entryActivityEducation: EntryActivityEducation,
|
private fun performedNextEducation(entryActivityEducation: EntryActivityEducation,
|
||||||
menu: Menu) {
|
menu: Menu) {
|
||||||
if (entryContentsView?.isUserNamePresent == true
|
val entryCopyEducationPerformed = entryContentsView?.isUserNamePresent == true
|
||||||
&& entryActivityEducation.checkAndPerformedEntryCopyEducation(
|
&& entryActivityEducation.checkAndPerformedEntryCopyEducation(
|
||||||
findViewById(R.id.entry_user_name_action_image),
|
findViewById(R.id.entry_user_name_action_image),
|
||||||
{
|
{
|
||||||
@@ -317,23 +330,29 @@ class EntryActivity : LockingHideActivity() {
|
|||||||
{
|
{
|
||||||
// Launch autofill settings
|
// Launch autofill settings
|
||||||
startActivity(Intent(this@EntryActivity, SettingsAutofillActivity::class.java))
|
startActivity(Intent(this@EntryActivity, SettingsAutofillActivity::class.java))
|
||||||
}))
|
})
|
||||||
else if (toolbar?.findViewById<View>(R.id.menu_edit) != null && entryActivityEducation.checkAndPerformedEntryEditEducation(
|
|
||||||
toolbar!!.findViewById(R.id.menu_edit),
|
if (!entryCopyEducationPerformed) {
|
||||||
{
|
// entryEditEducationPerformed
|
||||||
onOptionsItemSelected(menu.findItem(R.id.menu_edit))
|
toolbar?.findViewById<View>(R.id.menu_edit) != null && entryActivityEducation.checkAndPerformedEntryEditEducation(
|
||||||
},
|
toolbar!!.findViewById(R.id.menu_edit),
|
||||||
{
|
{
|
||||||
// Open Keepass doc to create field references
|
onOptionsItemSelected(menu.findItem(R.id.menu_edit))
|
||||||
startActivity(Intent(Intent.ACTION_VIEW,
|
},
|
||||||
Uri.parse(getString(R.string.field_references_url))))
|
{
|
||||||
}))
|
// Open Keepass doc to create field references
|
||||||
;
|
startActivity(Intent(Intent.ACTION_VIEW,
|
||||||
|
Uri.parse(getString(R.string.field_references_url))))
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||||
when (item.itemId) {
|
when (item.itemId) {
|
||||||
R.id.menu_contribute -> return MenuUtil.onContributionItemSelected(this)
|
R.id.menu_contribute -> {
|
||||||
|
MenuUtil.onContributionItemSelected(this)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
R.id.menu_toggle_pass -> {
|
R.id.menu_toggle_pass -> {
|
||||||
mShowPassword = !mShowPassword
|
mShowPassword = !mShowPassword
|
||||||
@@ -357,11 +376,7 @@ class EntryActivity : LockingHideActivity() {
|
|||||||
url = "http://$url"
|
url = "http://$url"
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
UriUtil.gotoUrl(this, url)
|
||||||
Util.gotoUrl(this, url)
|
|
||||||
} catch (e: ActivityNotFoundException) {
|
|
||||||
Toast.makeText(this, R.string.no_url_handler, Toast.LENGTH_LONG).show()
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ import android.app.Activity
|
|||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.os.Handler
|
import android.os.Handler
|
||||||
import android.support.v7.widget.Toolbar
|
import androidx.appcompat.widget.Toolbar
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.view.Menu
|
import android.view.Menu
|
||||||
import android.view.MenuItem
|
import android.view.MenuItem
|
||||||
@@ -45,7 +45,9 @@ import com.kunzisoft.keepass.timeout.TimeoutHelper
|
|||||||
import com.kunzisoft.keepass.utils.MenuUtil
|
import com.kunzisoft.keepass.utils.MenuUtil
|
||||||
import com.kunzisoft.keepass.view.EntryEditContentsView
|
import com.kunzisoft.keepass.view.EntryEditContentsView
|
||||||
|
|
||||||
class EntryEditActivity : LockingHideActivity(), IconPickerDialogFragment.IconPickerListener, GeneratePasswordDialogFragment.GeneratePasswordListener {
|
class EntryEditActivity : LockingHideActivity(),
|
||||||
|
IconPickerDialogFragment.IconPickerListener,
|
||||||
|
GeneratePasswordDialogFragment.GeneratePasswordListener {
|
||||||
|
|
||||||
private var mDatabase: Database? = null
|
private var mDatabase: Database? = null
|
||||||
|
|
||||||
@@ -154,33 +156,6 @@ class EntryEditActivity : LockingHideActivity(), IconPickerDialogFragment.IconPi
|
|||||||
|
|
||||||
// Verify the education views
|
// Verify the education views
|
||||||
entryEditActivityEducation = EntryEditActivityEducation(this)
|
entryEditActivityEducation = EntryEditActivityEducation(this)
|
||||||
entryEditActivityEducation?.let {
|
|
||||||
Handler().post { performedNextEducation(it) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun performedNextEducation(entryEditActivityEducation: EntryEditActivityEducation) {
|
|
||||||
val passwordView = entryEditContentsView?.generatePasswordView
|
|
||||||
val addNewFieldView = entryEditContentsView?.addNewFieldView
|
|
||||||
|
|
||||||
if (passwordView != null
|
|
||||||
&& entryEditActivityEducation.checkAndPerformedGeneratePasswordEducation(
|
|
||||||
passwordView,
|
|
||||||
{
|
|
||||||
openPasswordGenerator()
|
|
||||||
},
|
|
||||||
{
|
|
||||||
performedNextEducation(entryEditActivityEducation)
|
|
||||||
}
|
|
||||||
))
|
|
||||||
else if (mNewEntry != null && mNewEntry!!.allowExtraFields() && !mNewEntry!!.containsCustomFields()
|
|
||||||
&& addNewFieldView != null && addNewFieldView.visibility == View.VISIBLE
|
|
||||||
&& entryEditActivityEducation.checkAndPerformedEntryNewFieldEducation(
|
|
||||||
addNewFieldView,
|
|
||||||
{
|
|
||||||
addNewCustomField()
|
|
||||||
}))
|
|
||||||
;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun populateViewsWithEntry(newEntry: EntryVersioned) {
|
private fun populateViewsWithEntry(newEntry: EntryVersioned) {
|
||||||
@@ -309,9 +284,39 @@ class EntryEditActivity : LockingHideActivity(), IconPickerDialogFragment.IconPi
|
|||||||
inflater.inflate(R.menu.database_lock, menu)
|
inflater.inflate(R.menu.database_lock, menu)
|
||||||
MenuUtil.contributionMenuInflater(inflater, menu)
|
MenuUtil.contributionMenuInflater(inflater, menu)
|
||||||
|
|
||||||
|
entryEditActivityEducation?.let {
|
||||||
|
Handler().post { performedNextEducation(it) }
|
||||||
|
}
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun performedNextEducation(entryEditActivityEducation: EntryEditActivityEducation) {
|
||||||
|
val passwordView = entryEditContentsView?.generatePasswordView
|
||||||
|
val addNewFieldView = entryEditContentsView?.addNewFieldView
|
||||||
|
|
||||||
|
val generatePasswordEducationPerformed = passwordView != null
|
||||||
|
&& entryEditActivityEducation.checkAndPerformedGeneratePasswordEducation(
|
||||||
|
passwordView,
|
||||||
|
{
|
||||||
|
openPasswordGenerator()
|
||||||
|
},
|
||||||
|
{
|
||||||
|
performedNextEducation(entryEditActivityEducation)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
if (!generatePasswordEducationPerformed) {
|
||||||
|
// entryNewFieldEducationPerformed
|
||||||
|
mNewEntry != null && mNewEntry!!.allowExtraFields() && !mNewEntry!!.containsCustomFields()
|
||||||
|
&& addNewFieldView != null && addNewFieldView.visibility == View.VISIBLE
|
||||||
|
&& entryEditActivityEducation.checkAndPerformedEntryNewFieldEducation(
|
||||||
|
addNewFieldView,
|
||||||
|
{
|
||||||
|
addNewCustomField()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||||
when (item.itemId) {
|
when (item.itemId) {
|
||||||
R.id.menu_lock -> {
|
R.id.menu_lock -> {
|
||||||
@@ -319,7 +324,10 @@ class EntryEditActivity : LockingHideActivity(), IconPickerDialogFragment.IconPi
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
R.id.menu_contribute -> return MenuUtil.onContributionItemSelected(this)
|
R.id.menu_contribute -> {
|
||||||
|
MenuUtil.onContributionItemSelected(this)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
android.R.id.home -> finish()
|
android.R.id.home -> finish()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
*/
|
*/
|
||||||
package com.kunzisoft.keepass.activities
|
package com.kunzisoft.keepass.activities
|
||||||
|
|
||||||
import android.Manifest
|
import android.annotation.SuppressLint
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.app.assist.AssistStructure
|
import android.app.assist.AssistStructure
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
@@ -29,56 +29,43 @@ import android.os.Bundle
|
|||||||
import android.os.Environment
|
import android.os.Environment
|
||||||
import android.os.Handler
|
import android.os.Handler
|
||||||
import android.preference.PreferenceManager
|
import android.preference.PreferenceManager
|
||||||
import android.support.annotation.RequiresApi
|
import androidx.annotation.RequiresApi
|
||||||
import android.support.v7.app.AlertDialog
|
import com.google.android.material.snackbar.Snackbar
|
||||||
import android.support.v7.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import android.support.v7.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import android.support.v7.widget.Toolbar
|
import androidx.recyclerview.widget.SimpleItemAnimator
|
||||||
|
import androidx.appcompat.widget.Toolbar
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.view.Menu
|
import android.view.Menu
|
||||||
import android.view.MenuItem
|
import android.view.MenuItem
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.widget.EditText
|
import android.widget.EditText
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
import android.widget.Toast
|
|
||||||
import com.kunzisoft.keepass.R
|
import com.kunzisoft.keepass.R
|
||||||
import com.kunzisoft.keepass.activities.dialogs.AssignMasterKeyDialogFragment
|
import com.kunzisoft.keepass.activities.dialogs.AssignMasterKeyDialogFragment
|
||||||
import com.kunzisoft.keepass.activities.dialogs.CreateFileDialogFragment
|
import com.kunzisoft.keepass.activities.dialogs.BrowserDialogFragment
|
||||||
import com.kunzisoft.keepass.activities.dialogs.FileInformationDialogFragment
|
|
||||||
import com.kunzisoft.keepass.activities.helpers.EntrySelectionHelper
|
import com.kunzisoft.keepass.activities.helpers.EntrySelectionHelper
|
||||||
import com.kunzisoft.keepass.activities.helpers.KeyFileHelper
|
import com.kunzisoft.keepass.activities.helpers.OpenFileHelper
|
||||||
import com.kunzisoft.keepass.activities.stylish.StylishActivity
|
import com.kunzisoft.keepass.activities.stylish.StylishActivity
|
||||||
import com.kunzisoft.keepass.adapters.FileDatabaseHistoryAdapter
|
import com.kunzisoft.keepass.adapters.FileDatabaseHistoryAdapter
|
||||||
|
import com.kunzisoft.keepass.app.database.FileDatabaseHistory
|
||||||
import com.kunzisoft.keepass.autofill.AutofillHelper
|
import com.kunzisoft.keepass.autofill.AutofillHelper
|
||||||
import com.kunzisoft.keepass.database.action.CreateDatabaseRunnable
|
import com.kunzisoft.keepass.database.action.CreateDatabaseRunnable
|
||||||
import com.kunzisoft.keepass.database.action.ProgressDialogThread
|
import com.kunzisoft.keepass.database.action.ProgressDialogThread
|
||||||
import com.kunzisoft.keepass.database.element.Database
|
import com.kunzisoft.keepass.database.element.Database
|
||||||
import com.kunzisoft.keepass.education.FileDatabaseSelectActivityEducation
|
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.magikeyboard.KeyboardHelper
|
import com.kunzisoft.keepass.magikeyboard.KeyboardHelper
|
||||||
import com.kunzisoft.keepass.settings.PreferencesUtil
|
import com.kunzisoft.keepass.settings.PreferencesUtil
|
||||||
import com.kunzisoft.keepass.tasks.ActionRunnable
|
import com.kunzisoft.keepass.tasks.ActionRunnable
|
||||||
import com.kunzisoft.keepass.utils.MenuUtil
|
import com.kunzisoft.keepass.utils.MenuUtil
|
||||||
import com.kunzisoft.keepass.utils.UriUtil
|
import com.kunzisoft.keepass.utils.UriUtil
|
||||||
|
import com.kunzisoft.keepass.view.asError
|
||||||
|
import kotlinx.android.synthetic.main.activity_file_selection.*
|
||||||
import net.cachapa.expandablelayout.ExpandableLayout
|
import net.cachapa.expandablelayout.ExpandableLayout
|
||||||
import permissions.dispatcher.*
|
|
||||||
import java.io.File
|
|
||||||
import java.io.FileNotFoundException
|
import java.io.FileNotFoundException
|
||||||
import java.io.IOException
|
|
||||||
import java.lang.ref.WeakReference
|
|
||||||
import java.net.URLDecoder
|
|
||||||
import java.util.*
|
|
||||||
|
|
||||||
@RuntimePermissions
|
|
||||||
class FileDatabaseSelectActivity : StylishActivity(),
|
class FileDatabaseSelectActivity : StylishActivity(),
|
||||||
CreateFileDialogFragment.DefinePathDialogListener,
|
AssignMasterKeyDialogFragment.AssignPasswordDialogListener {
|
||||||
AssignMasterKeyDialogFragment.AssignPasswordDialogListener,
|
|
||||||
FileDatabaseHistoryAdapter.FileItemOpenListener,
|
|
||||||
FileDatabaseHistoryAdapter.FileSelectClearListener,
|
|
||||||
FileDatabaseHistoryAdapter.FileInformationShowListener {
|
|
||||||
|
|
||||||
// Views
|
// Views
|
||||||
private var fileListContainer: View? = null
|
private var fileListContainer: View? = null
|
||||||
@@ -96,14 +83,14 @@ class FileDatabaseSelectActivity : StylishActivity(),
|
|||||||
|
|
||||||
private var mDatabaseFileUri: Uri? = null
|
private var mDatabaseFileUri: Uri? = null
|
||||||
|
|
||||||
private var mKeyFileHelper: KeyFileHelper? = null
|
private var mOpenFileHelper: OpenFileHelper? = null
|
||||||
|
|
||||||
private var mDefaultPath: String? = null
|
private var mDefaultPath: String? = null
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
mFileDatabaseHistory = FileDatabaseHistory.getInstance(WeakReference(applicationContext))
|
mFileDatabaseHistory = FileDatabaseHistory.getInstance(applicationContext)
|
||||||
|
|
||||||
setContentView(R.layout.activity_file_selection)
|
setContentView(R.layout.activity_file_selection)
|
||||||
fileListContainer = findViewById(R.id.container_file_list)
|
fileListContainer = findViewById(R.id.container_file_list)
|
||||||
@@ -131,10 +118,6 @@ class FileDatabaseSelectActivity : StylishActivity(),
|
|||||||
fileSelectExpandableLayout?.expand()
|
fileSelectExpandableLayout?.expand()
|
||||||
}
|
}
|
||||||
|
|
||||||
// History list
|
|
||||||
val databaseFileListView = findViewById<RecyclerView>(R.id.file_list)
|
|
||||||
databaseFileListView.layoutManager = LinearLayoutManager(this)
|
|
||||||
|
|
||||||
// Open button
|
// Open button
|
||||||
openButtonView = findViewById(R.id.open_database)
|
openButtonView = findViewById(R.id.open_database)
|
||||||
openButtonView?.setOnClickListener { _ ->
|
openButtonView?.setOnClickListener { _ ->
|
||||||
@@ -148,21 +131,56 @@ class FileDatabaseSelectActivity : StylishActivity(),
|
|||||||
|
|
||||||
// Create button
|
// Create button
|
||||||
createButtonView = findViewById(R.id.create_database)
|
createButtonView = findViewById(R.id.create_database)
|
||||||
createButtonView?.setOnClickListener { openCreateFileDialogFragmentWithPermissionCheck() }
|
if (Intent(Intent.ACTION_CREATE_DOCUMENT).apply {
|
||||||
|
addCategory(Intent.CATEGORY_OPENABLE)
|
||||||
|
type = "application/x-keepass"
|
||||||
|
}.resolveActivity(packageManager) == null) {
|
||||||
|
// No Activity found that can handle this intent.
|
||||||
|
createButtonView?.visibility = View.GONE
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
// There is an activity which can handle this intent.
|
||||||
|
createButtonView?.visibility = View.VISIBLE
|
||||||
|
}
|
||||||
|
|
||||||
mKeyFileHelper = KeyFileHelper(this)
|
createButtonView?.setOnClickListener { createNewFile() }
|
||||||
|
|
||||||
|
mOpenFileHelper = OpenFileHelper(this)
|
||||||
browseButtonView = findViewById(R.id.browse_button)
|
browseButtonView = findViewById(R.id.browse_button)
|
||||||
browseButtonView?.setOnClickListener(mKeyFileHelper!!.getOpenFileOnClickViewListener {
|
browseButtonView?.setOnClickListener(mOpenFileHelper!!.getOpenFileOnClickViewListener {
|
||||||
Uri.parse("file://" + openFileNameView!!.text.toString())
|
Uri.parse("file://" + openFileNameView!!.text.toString())
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
// History list
|
||||||
|
val fileDatabaseHistoryRecyclerView = findViewById<RecyclerView>(R.id.file_list)
|
||||||
|
fileDatabaseHistoryRecyclerView.layoutManager = LinearLayoutManager(this, RecyclerView.VERTICAL, false)
|
||||||
|
// Removes blinks
|
||||||
|
(fileDatabaseHistoryRecyclerView.itemAnimator as SimpleItemAnimator).supportsChangeAnimations = false
|
||||||
// Construct adapter with listeners
|
// Construct adapter with listeners
|
||||||
mAdapterDatabaseHistory = FileDatabaseHistoryAdapter(this@FileDatabaseSelectActivity,
|
mAdapterDatabaseHistory = FileDatabaseHistoryAdapter(this)
|
||||||
mFileDatabaseHistory?.databaseUriList ?: ArrayList())
|
mAdapterDatabaseHistory?.setOnFileDatabaseHistoryOpenListener { fileDatabaseHistoryEntityToOpen ->
|
||||||
mAdapterDatabaseHistory?.setOnItemClickListener(this)
|
launchPasswordActivity(
|
||||||
mAdapterDatabaseHistory?.setFileSelectClearListener(this)
|
fileDatabaseHistoryEntityToOpen.databaseUri,
|
||||||
mAdapterDatabaseHistory?.setFileInformationShowListener(this)
|
fileDatabaseHistoryEntityToOpen.keyFileUri)
|
||||||
databaseFileListView.adapter = mAdapterDatabaseHistory
|
updateFileListVisibility()
|
||||||
|
}
|
||||||
|
mAdapterDatabaseHistory?.setOnFileDatabaseHistoryDeleteListener { fileDatabaseHistoryToDelete ->
|
||||||
|
// Remove from app database
|
||||||
|
mFileDatabaseHistory?.deleteFileDatabaseHistory(fileDatabaseHistoryToDelete) { fileHistoryDeleted ->
|
||||||
|
// Remove from adapter
|
||||||
|
fileHistoryDeleted?.let { databaseFileHistoryDeleted ->
|
||||||
|
mAdapterDatabaseHistory?.deleteDatabaseFileHistory(databaseFileHistoryDeleted)
|
||||||
|
mAdapterDatabaseHistory?.notifyDataSetChanged()
|
||||||
|
updateFileListVisibility()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
|
mAdapterDatabaseHistory?.setOnSaveAliasListener { fileDatabaseHistoryWithNewAlias ->
|
||||||
|
mFileDatabaseHistory?.addOrUpdateFileDatabaseHistory(fileDatabaseHistoryWithNewAlias)
|
||||||
|
}
|
||||||
|
fileDatabaseHistoryRecyclerView.adapter = mAdapterDatabaseHistory
|
||||||
|
|
||||||
// Load default database if not an orientation change
|
// Load default database if not an orientation change
|
||||||
if (!(savedInstanceState != null
|
if (!(savedInstanceState != null
|
||||||
@@ -171,68 +189,48 @@ class FileDatabaseSelectActivity : StylishActivity(),
|
|||||||
val prefs = PreferenceManager.getDefaultSharedPreferences(this)
|
val prefs = PreferenceManager.getDefaultSharedPreferences(this)
|
||||||
val fileName = prefs.getString(PasswordActivity.KEY_DEFAULT_FILENAME, "")
|
val fileName = prefs.getString(PasswordActivity.KEY_DEFAULT_FILENAME, "")
|
||||||
|
|
||||||
if (fileName != null && fileName.isNotEmpty()) {
|
try {
|
||||||
val dbUri = UriUtil.parseUriFile(fileName)
|
UriUtil.verifyFilePath(fileName) { path ->
|
||||||
var scheme: String? = null
|
launchPasswordActivityWithPath(path)
|
||||||
if (dbUri != null)
|
|
||||||
scheme = dbUri.scheme
|
|
||||||
|
|
||||||
if (scheme != null && scheme.isNotEmpty() && scheme.equals("file", ignoreCase = true)) {
|
|
||||||
val path = dbUri!!.path
|
|
||||||
val db = File(path!!)
|
|
||||||
|
|
||||||
if (db.exists()) {
|
|
||||||
launchPasswordActivityWithPath(path)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (dbUri != null)
|
|
||||||
launchPasswordActivityWithPath(dbUri.toString())
|
|
||||||
}
|
}
|
||||||
|
} catch (e: FileNotFoundException) {
|
||||||
|
Log.e(TAG, "Unable to launch Password Activity", e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Handler().post { performedNextEducation(FileDatabaseSelectActivityEducation(this)) }
|
// Retrieve the database URI provided by file manager after an orientation change
|
||||||
|
if (savedInstanceState != null
|
||||||
|
&& savedInstanceState.containsKey(EXTRA_DATABASE_URI)) {
|
||||||
|
mDatabaseFileUri = savedInstanceState.getParcelable(EXTRA_DATABASE_URI)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun performedNextEducation(fileDatabaseSelectActivityEducation: FileDatabaseSelectActivityEducation) {
|
/**
|
||||||
// If no recent files
|
* Create a new file by calling the content provider
|
||||||
if (createButtonView != null
|
*/
|
||||||
&& mFileDatabaseHistory != null
|
@SuppressLint("InlinedApi")
|
||||||
&& !mFileDatabaseHistory!!.hasRecentFiles() && fileDatabaseSelectActivityEducation.checkAndPerformedCreateDatabaseEducation(
|
private fun createNewFile() {
|
||||||
createButtonView!!,
|
try {
|
||||||
{
|
startActivityForResult(Intent(
|
||||||
openCreateFileDialogFragmentWithPermissionCheck()
|
Intent.ACTION_CREATE_DOCUMENT).apply {
|
||||||
},
|
addCategory(Intent.CATEGORY_OPENABLE)
|
||||||
{
|
type = "application/x-keepass"
|
||||||
// But if the user cancel, it can also select a database
|
putExtra(Intent.EXTRA_TITLE, getString(R.string.database_file_name_default) +
|
||||||
performedNextEducation(fileDatabaseSelectActivityEducation)
|
getString(R.string.database_file_extension_default))
|
||||||
}))
|
},
|
||||||
else if (browseButtonView != null
|
CREATE_FILE_REQUEST_CODE)
|
||||||
&& fileDatabaseSelectActivityEducation.checkAndPerformedSelectDatabaseEducation(
|
} catch (e: Exception) {
|
||||||
browseButtonView!!,
|
BrowserDialogFragment().show(supportFragmentManager, "browserDialog")
|
||||||
{tapTargetView ->
|
}
|
||||||
tapTargetView?.let {
|
|
||||||
mKeyFileHelper?.openFileOnClickViewListener?.onClick(it)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
fileSelectExpandableButtonView?.let {
|
|
||||||
fileDatabaseSelectActivityEducation
|
|
||||||
.checkAndPerformedOpenLinkDatabaseEducation(it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
))
|
|
||||||
;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun fileNoFoundAction(e: FileNotFoundException) {
|
private fun fileNoFoundAction(e: FileNotFoundException) {
|
||||||
val error = getString(R.string.file_not_found_content)
|
val error = getString(R.string.file_not_found_content)
|
||||||
Toast.makeText(this@FileDatabaseSelectActivity,
|
Snackbar.make(activity_file_selection_coordinator_layout, error, Snackbar.LENGTH_LONG).asError().show()
|
||||||
error, Toast.LENGTH_LONG).show()
|
|
||||||
Log.e(TAG, error, e)
|
Log.e(TAG, error, e)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun launchPasswordActivity(fileName: String, keyFile: String) {
|
private fun launchPasswordActivity(fileName: String, keyFile: String?) {
|
||||||
EntrySelectionHelper.doEntrySelectionAction(intent,
|
EntrySelectionHelper.doEntrySelectionAction(intent,
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
@@ -295,26 +293,23 @@ class FileDatabaseSelectActivity : StylishActivity(),
|
|||||||
super.onResume()
|
super.onResume()
|
||||||
|
|
||||||
updateExternalStorageWarning()
|
updateExternalStorageWarning()
|
||||||
updateFileListVisibility()
|
|
||||||
mAdapterDatabaseHistory!!.notifyDataSetChanged()
|
// Construct adapter with listeners
|
||||||
|
mFileDatabaseHistory?.getAllFileDatabaseHistories { databaseFileHistoryList ->
|
||||||
|
databaseFileHistoryList?.let {
|
||||||
|
mAdapterDatabaseHistory?.addDatabaseFileHistoryList(it)
|
||||||
|
updateFileListVisibility()
|
||||||
|
mAdapterDatabaseHistory?.notifyDataSetChanged()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onSaveInstanceState(outState: Bundle) {
|
override fun onSaveInstanceState(outState: Bundle) {
|
||||||
super.onSaveInstanceState(outState)
|
super.onSaveInstanceState(outState)
|
||||||
// only to keep the current activity
|
// only to keep the current activity
|
||||||
outState.putBoolean(EXTRA_STAY, true)
|
outState.putBoolean(EXTRA_STAY, true)
|
||||||
}
|
// to retrieve the URI of a created database after an orientation change
|
||||||
|
outState.putParcelable(EXTRA_DATABASE_URI, mDatabaseFileUri)
|
||||||
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
|
|
||||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
|
|
||||||
// NOTE: delegate the permission handling to generated method
|
|
||||||
onRequestPermissionsResult(requestCode, grantResults)
|
|
||||||
}
|
|
||||||
|
|
||||||
@NeedsPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
|
||||||
fun openCreateFileDialogFragment() {
|
|
||||||
val createFileDialogFragment = CreateFileDialogFragment()
|
|
||||||
createFileDialogFragment.show(supportFragmentManager, "createFileDialogFragment")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateFileListVisibility() {
|
private fun updateFileListVisibility() {
|
||||||
@@ -324,82 +319,6 @@ class FileDatabaseSelectActivity : StylishActivity(),
|
|||||||
fileListContainer?.visibility = View.VISIBLE
|
fileListContainer?.visibility = View.VISIBLE
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Create file for database
|
|
||||||
* @return If not created, return false
|
|
||||||
*/
|
|
||||||
private fun createDatabaseFile(path: Uri): Boolean {
|
|
||||||
|
|
||||||
val pathString = URLDecoder.decode(path.path, "UTF-8")
|
|
||||||
// Make sure file name exists
|
|
||||||
if (pathString.isEmpty()) {
|
|
||||||
Log.e(TAG, getString(R.string.error_filename_required))
|
|
||||||
Toast.makeText(this@FileDatabaseSelectActivity,
|
|
||||||
R.string.error_filename_required,
|
|
||||||
Toast.LENGTH_LONG).show()
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Try to create the file
|
|
||||||
val file = File(pathString)
|
|
||||||
try {
|
|
||||||
if (file.exists()) {
|
|
||||||
Log.e(TAG, getString(R.string.error_database_exists) + " " + file)
|
|
||||||
Toast.makeText(this@FileDatabaseSelectActivity,
|
|
||||||
R.string.error_database_exists,
|
|
||||||
Toast.LENGTH_LONG).show()
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
val parent = file.parentFile
|
|
||||||
|
|
||||||
if (parent == null || parent.exists() && !parent.isDirectory) {
|
|
||||||
Log.e(TAG, getString(R.string.error_invalid_path) + " " + file)
|
|
||||||
Toast.makeText(this@FileDatabaseSelectActivity,
|
|
||||||
R.string.error_invalid_path,
|
|
||||||
Toast.LENGTH_LONG).show()
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!parent.exists()) {
|
|
||||||
// Create parent directory
|
|
||||||
if (!parent.mkdirs()) {
|
|
||||||
Log.e(TAG, getString(R.string.error_could_not_create_parent) + " " + parent)
|
|
||||||
Toast.makeText(this@FileDatabaseSelectActivity,
|
|
||||||
R.string.error_could_not_create_parent,
|
|
||||||
Toast.LENGTH_LONG).show()
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return file.createNewFile()
|
|
||||||
} catch (e: IOException) {
|
|
||||||
Log.e(TAG, getString(R.string.error_could_not_create_parent) + " " + e.localizedMessage)
|
|
||||||
e.printStackTrace()
|
|
||||||
Toast.makeText(
|
|
||||||
this@FileDatabaseSelectActivity,
|
|
||||||
getText(R.string.error_file_not_create).toString() + " "
|
|
||||||
+ e.localizedMessage,
|
|
||||||
Toast.LENGTH_LONG).show()
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onDefinePathDialogPositiveClick(pathFile: Uri?): Boolean {
|
|
||||||
mDatabaseFileUri = pathFile
|
|
||||||
if (pathFile == null)
|
|
||||||
return false
|
|
||||||
return if (createDatabaseFile(pathFile)) {
|
|
||||||
AssignMasterKeyDialogFragment().show(supportFragmentManager, "passwordDialog")
|
|
||||||
true
|
|
||||||
} else
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onDefinePathDialogNegativeClick(pathFile: Uri?): Boolean {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onAssignKeyDialogPositiveClick(
|
override fun onAssignKeyDialogPositiveClick(
|
||||||
masterPasswordChecked: Boolean, masterPassword: String?,
|
masterPasswordChecked: Boolean, masterPassword: String?,
|
||||||
keyFileChecked: Boolean, keyFile: Uri?) {
|
keyFileChecked: Boolean, keyFile: Uri?) {
|
||||||
@@ -418,22 +337,21 @@ class FileDatabaseSelectActivity : StylishActivity(),
|
|||||||
keyFileChecked,
|
keyFileChecked,
|
||||||
keyFile,
|
keyFile,
|
||||||
true, // TODO get readonly
|
true, // TODO get readonly
|
||||||
LaunchGroupActivityFinish(databaseUri)
|
LaunchGroupActivityFinish(databaseUri, keyFile)
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
R.string.progress_create)
|
R.string.progress_create)
|
||||||
.start()
|
.start()
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
val error = "Unable to create database with this password and key file"
|
val error = getString(R.string.error_create_database_file)
|
||||||
Toast.makeText(this, error, Toast.LENGTH_LONG).show()
|
Snackbar.make(activity_file_selection_coordinator_layout, error, Snackbar.LENGTH_LONG).asError().show()
|
||||||
Log.e(TAG, error + " " + e.message)
|
Log.e(TAG, error, e)
|
||||||
// TODO remove
|
|
||||||
e.printStackTrace()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private inner class LaunchGroupActivityFinish internal constructor(private val fileURI: Uri) : ActionRunnable() {
|
private inner class LaunchGroupActivityFinish(private val databaseFileUri: Uri,
|
||||||
|
private val keyFileUri: Uri?) : ActionRunnable() {
|
||||||
|
|
||||||
override fun run() {
|
override fun run() {
|
||||||
finishRun(true, null)
|
finishRun(true, null)
|
||||||
@@ -443,7 +361,7 @@ class FileDatabaseSelectActivity : StylishActivity(),
|
|||||||
runOnUiThread {
|
runOnUiThread {
|
||||||
if (result.isSuccess) {
|
if (result.isSuccess) {
|
||||||
// Add database to recent files
|
// Add database to recent files
|
||||||
mFileDatabaseHistory?.addDatabaseUri(fileURI)
|
mFileDatabaseHistory?.addOrUpdateDatabaseUri(databaseFileUri, keyFileUri)
|
||||||
mAdapterDatabaseHistory?.notifyDataSetChanged()
|
mAdapterDatabaseHistory?.notifyDataSetChanged()
|
||||||
updateFileListVisibility()
|
updateFileListVisibility()
|
||||||
GroupActivity.launch(this@FileDatabaseSelectActivity)
|
GroupActivity.launch(this@FileDatabaseSelectActivity)
|
||||||
@@ -460,29 +378,6 @@ 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 onClickFileInformation(fileDatabaseModel: FileDatabaseModel) {
|
|
||||||
FileInformationDialogFragment.newInstance(fileDatabaseModel).show(supportFragmentManager, "fileInformation")
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onFileSelectClearListener(fileDatabaseModel: FileDatabaseModel): Boolean {
|
|
||||||
DeleteFileHistoryAsyncTask({
|
|
||||||
fileDatabaseModel.fileUri?.let {
|
|
||||||
mFileDatabaseHistory?.deleteDatabaseUri(it)
|
|
||||||
}
|
|
||||||
mAdapterDatabaseHistory?.notifyDataSetChanged()
|
|
||||||
updateFileListVisibility()
|
|
||||||
}, mFileDatabaseHistory, mAdapterDatabaseHistory).execute(fileDatabaseModel)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||||
super.onActivityResult(requestCode, resultCode, data)
|
super.onActivityResult(requestCode, resultCode, data)
|
||||||
|
|
||||||
@@ -490,7 +385,7 @@ class FileDatabaseSelectActivity : StylishActivity(),
|
|||||||
AutofillHelper.onActivityResultSetResultAndFinish(this, requestCode, resultCode, data)
|
AutofillHelper.onActivityResultSetResultAndFinish(this, requestCode, resultCode, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
mKeyFileHelper?.onActivityResultCallback(requestCode, resultCode, data
|
mOpenFileHelper?.onActivityResultCallback(requestCode, resultCode, data
|
||||||
) { uri ->
|
) { uri ->
|
||||||
if (uri != null) {
|
if (uri != null) {
|
||||||
if (PreferencesUtil.autoOpenSelectedFile(this@FileDatabaseSelectActivity)) {
|
if (PreferencesUtil.autoOpenSelectedFile(this@FileDatabaseSelectActivity)) {
|
||||||
@@ -501,33 +396,62 @@ class FileDatabaseSelectActivity : StylishActivity(),
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@OnShowRationale(Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
// Retrieve the created URI from the file manager
|
||||||
internal fun showRationaleForExternalStorage(request: PermissionRequest) {
|
if (requestCode == CREATE_FILE_REQUEST_CODE && resultCode == Activity.RESULT_OK) {
|
||||||
AlertDialog.Builder(this)
|
mDatabaseFileUri = data?.data
|
||||||
.setMessage(R.string.permission_external_storage_rationale_write_database)
|
if (mDatabaseFileUri != null) {
|
||||||
.setPositiveButton(R.string.allow) { _, _ -> request.proceed() }
|
AssignMasterKeyDialogFragment().show(supportFragmentManager, "passwordDialog")
|
||||||
.setNegativeButton(R.string.cancel) { _, _ -> request.cancel() }
|
}
|
||||||
.show()
|
// else {
|
||||||
}
|
// TODO Show error
|
||||||
|
// }
|
||||||
@OnPermissionDenied(Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
}
|
||||||
internal fun showDeniedForExternalStorage() {
|
|
||||||
Toast.makeText(this, R.string.permission_external_storage_denied, Toast.LENGTH_SHORT).show()
|
|
||||||
}
|
|
||||||
|
|
||||||
@OnNeverAskAgain(Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
|
||||||
internal fun showNeverAskForExternalStorage() {
|
|
||||||
Toast.makeText(this, R.string.permission_external_storage_never_ask, Toast.LENGTH_SHORT).show()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreateOptionsMenu(menu: Menu): Boolean {
|
override fun onCreateOptionsMenu(menu: Menu): Boolean {
|
||||||
super.onCreateOptionsMenu(menu)
|
super.onCreateOptionsMenu(menu)
|
||||||
MenuUtil.defaultMenuInflater(menuInflater, menu)
|
MenuUtil.defaultMenuInflater(menuInflater, menu)
|
||||||
|
|
||||||
|
Handler().post { performedNextEducation(FileDatabaseSelectActivityEducation(this)) }
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun performedNextEducation(fileDatabaseSelectActivityEducation: FileDatabaseSelectActivityEducation) {
|
||||||
|
// If no recent files
|
||||||
|
val createDatabaseEducationPerformed = createButtonView != null && createButtonView!!.visibility == View.VISIBLE
|
||||||
|
&& mAdapterDatabaseHistory != null
|
||||||
|
&& mAdapterDatabaseHistory!!.itemCount > 0
|
||||||
|
&& fileDatabaseSelectActivityEducation.checkAndPerformedCreateDatabaseEducation(
|
||||||
|
createButtonView!!,
|
||||||
|
{
|
||||||
|
createNewFile()
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// But if the user cancel, it can also select a database
|
||||||
|
performedNextEducation(fileDatabaseSelectActivityEducation)
|
||||||
|
})
|
||||||
|
if (!createDatabaseEducationPerformed) {
|
||||||
|
// selectDatabaseEducationPerformed
|
||||||
|
browseButtonView != null
|
||||||
|
&& fileDatabaseSelectActivityEducation.checkAndPerformedSelectDatabaseEducation(
|
||||||
|
browseButtonView!!,
|
||||||
|
{tapTargetView ->
|
||||||
|
tapTargetView?.let {
|
||||||
|
mOpenFileHelper?.openFileOnClickViewListener?.onClick(it)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fileSelectExpandableButtonView?.let {
|
||||||
|
fileDatabaseSelectActivityEducation
|
||||||
|
.checkAndPerformedOpenLinkDatabaseEducation(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||||
return MenuUtil.onDefaultMenuOptionsItemSelected(this, item) && super.onOptionsItemSelected(item)
|
return MenuUtil.onDefaultMenuOptionsItemSelected(this, item) && super.onOptionsItemSelected(item)
|
||||||
}
|
}
|
||||||
@@ -536,6 +460,9 @@ class FileDatabaseSelectActivity : StylishActivity(),
|
|||||||
|
|
||||||
private const val TAG = "FileDbSelectActivity"
|
private const val TAG = "FileDbSelectActivity"
|
||||||
private const val EXTRA_STAY = "EXTRA_STAY"
|
private const val EXTRA_STAY = "EXTRA_STAY"
|
||||||
|
private const val EXTRA_DATABASE_URI = "EXTRA_DATABASE_URI"
|
||||||
|
|
||||||
|
private const val CREATE_FILE_REQUEST_CODE = 3853
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* -------------------------
|
* -------------------------
|
||||||
|
|||||||
@@ -29,11 +29,10 @@ import android.graphics.Color
|
|||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.os.Handler
|
import android.os.Handler
|
||||||
import android.preference.PreferenceManager
|
import androidx.annotation.RequiresApi
|
||||||
import android.support.annotation.RequiresApi
|
import androidx.fragment.app.FragmentManager
|
||||||
import android.support.v4.app.FragmentManager
|
import androidx.appcompat.widget.SearchView
|
||||||
import android.support.v7.widget.SearchView
|
import androidx.appcompat.widget.Toolbar
|
||||||
import android.support.v7.widget.Toolbar
|
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.view.Menu
|
import android.view.Menu
|
||||||
import android.view.MenuItem
|
import android.view.MenuItem
|
||||||
@@ -183,7 +182,7 @@ class GroupActivity : LockingActivity(),
|
|||||||
// Attach fragment to content view
|
// Attach fragment to content view
|
||||||
supportFragmentManager.beginTransaction().replace(
|
supportFragmentManager.beginTransaction().replace(
|
||||||
R.id.nodes_list_fragment_container,
|
R.id.nodes_list_fragment_container,
|
||||||
mListNodesFragment,
|
mListNodesFragment!!,
|
||||||
fragmentTag)
|
fragmentTag)
|
||||||
.commit()
|
.commit()
|
||||||
|
|
||||||
@@ -628,8 +627,9 @@ class GroupActivity : LockingActivity(),
|
|||||||
|
|
||||||
private fun performedNextEducation(groupActivityEducation: GroupActivityEducation,
|
private fun performedNextEducation(groupActivityEducation: GroupActivityEducation,
|
||||||
menu: Menu) {
|
menu: Menu) {
|
||||||
|
|
||||||
// If no node, show education to add new one
|
// If no node, show education to add new one
|
||||||
if (mListNodesFragment != null
|
val addNodeButtonEducationPerformed = mListNodesFragment != null
|
||||||
&& mListNodesFragment!!.isEmpty
|
&& mListNodesFragment!!.isEmpty
|
||||||
&& addNodeButtonView?.addButtonView != null
|
&& addNodeButtonView?.addButtonView != null
|
||||||
&& addNodeButtonView!!.isEnable
|
&& addNodeButtonView!!.isEnable
|
||||||
@@ -641,38 +641,48 @@ class GroupActivity : LockingActivity(),
|
|||||||
{
|
{
|
||||||
performedNextEducation(groupActivityEducation, menu)
|
performedNextEducation(groupActivityEducation, menu)
|
||||||
}
|
}
|
||||||
))
|
)
|
||||||
else if (toolbar != null
|
if (!addNodeButtonEducationPerformed) {
|
||||||
&& toolbar!!.findViewById<View>(R.id.menu_search) != null
|
|
||||||
&& groupActivityEducation.checkAndPerformedSearchMenuEducation(
|
val searchMenuEducationPerformed = toolbar != null
|
||||||
toolbar!!.findViewById(R.id.menu_search),
|
&& toolbar!!.findViewById<View>(R.id.menu_search) != null
|
||||||
{
|
&& groupActivityEducation.checkAndPerformedSearchMenuEducation(
|
||||||
menu.findItem(R.id.menu_search).expandActionView()
|
toolbar!!.findViewById(R.id.menu_search),
|
||||||
},
|
{
|
||||||
{
|
menu.findItem(R.id.menu_search).expandActionView()
|
||||||
performedNextEducation(groupActivityEducation, menu)
|
},
|
||||||
}))
|
{
|
||||||
else if (toolbar != null
|
performedNextEducation(groupActivityEducation, menu)
|
||||||
&& toolbar!!.findViewById<View>(R.id.menu_sort) != null
|
})
|
||||||
&& groupActivityEducation.checkAndPerformedSortMenuEducation(
|
|
||||||
|
if (!searchMenuEducationPerformed) {
|
||||||
|
|
||||||
|
val sortMenuEducationPerformed = toolbar != null
|
||||||
|
&& toolbar!!.findViewById<View>(R.id.menu_sort) != null
|
||||||
|
&& groupActivityEducation.checkAndPerformedSortMenuEducation(
|
||||||
toolbar!!.findViewById(R.id.menu_sort),
|
toolbar!!.findViewById(R.id.menu_sort),
|
||||||
{
|
{
|
||||||
onOptionsItemSelected(menu.findItem(R.id.menu_sort))
|
onOptionsItemSelected(menu.findItem(R.id.menu_sort))
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
performedNextEducation(groupActivityEducation, menu)
|
performedNextEducation(groupActivityEducation, menu)
|
||||||
}))
|
})
|
||||||
else if (toolbar != null
|
|
||||||
&& toolbar!!.findViewById<View>(R.id.menu_lock) != null
|
if (!sortMenuEducationPerformed) {
|
||||||
&& groupActivityEducation.checkAndPerformedLockMenuEducation(
|
// lockMenuEducationPerformed
|
||||||
toolbar!!.findViewById(R.id.menu_lock),
|
toolbar != null
|
||||||
{
|
&& toolbar!!.findViewById<View>(R.id.menu_lock) != null
|
||||||
onOptionsItemSelected(menu.findItem(R.id.menu_lock))
|
&& groupActivityEducation.checkAndPerformedLockMenuEducation(
|
||||||
},
|
toolbar!!.findViewById(R.id.menu_lock),
|
||||||
{
|
{
|
||||||
performedNextEducation(groupActivityEducation, menu)
|
onOptionsItemSelected(menu.findItem(R.id.menu_lock))
|
||||||
}))
|
},
|
||||||
;
|
{
|
||||||
|
performedNextEducation(groupActivityEducation, menu)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun startActivity(intent: Intent) {
|
override fun startActivity(intent: Intent) {
|
||||||
@@ -857,11 +867,9 @@ class GroupActivity : LockingActivity(),
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun showWarnings() {
|
private fun showWarnings() {
|
||||||
// TODO Preferences
|
|
||||||
if (Database.getInstance().isReadOnly) {
|
if (Database.getInstance().isReadOnly) {
|
||||||
val prefs = PreferenceManager.getDefaultSharedPreferences(this)
|
if (PreferencesUtil.showReadOnlyWarning(this)) {
|
||||||
if (prefs.getBoolean(getString(R.string.show_read_only_warning), true)) {
|
ReadOnlyDialog().show(supportFragmentManager, "readOnlyDialog")
|
||||||
ReadOnlyDialog(this).show()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ import android.content.Intent
|
|||||||
import android.content.SharedPreferences
|
import android.content.SharedPreferences
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.preference.PreferenceManager
|
import android.preference.PreferenceManager
|
||||||
import android.support.v7.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import android.support.v7.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.Menu
|
import android.view.Menu
|
||||||
@@ -130,7 +130,7 @@ class ListNodesFragment : StylishFragment(), SortDialogFragment.SortSelectionLis
|
|||||||
|
|
||||||
onScrollListener?.let { onScrollListener ->
|
onScrollListener?.let { onScrollListener ->
|
||||||
listView?.addOnScrollListener(object : RecyclerView.OnScrollListener() {
|
listView?.addOnScrollListener(object : RecyclerView.OnScrollListener() {
|
||||||
override fun onScrolled(recyclerView: RecyclerView?, dx: Int, dy: Int) {
|
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
|
||||||
super.onScrolled(recyclerView, dx, dy)
|
super.onScrolled(recyclerView, dx, dy)
|
||||||
onScrollListener.onScrolled(dy)
|
onScrollListener.onScrolled(dy)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,6 @@
|
|||||||
*/
|
*/
|
||||||
package com.kunzisoft.keepass.activities
|
package com.kunzisoft.keepass.activities
|
||||||
|
|
||||||
import android.Manifest
|
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.app.assist.AssistStructure
|
import android.app.assist.AssistStructure
|
||||||
import android.app.backup.BackupManager
|
import android.app.backup.BackupManager
|
||||||
@@ -32,11 +31,12 @@ import android.os.Build
|
|||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.os.Handler
|
import android.os.Handler
|
||||||
import android.preference.PreferenceManager
|
import android.preference.PreferenceManager
|
||||||
import android.support.annotation.RequiresApi
|
import androidx.annotation.RequiresApi
|
||||||
import android.support.v7.app.AlertDialog
|
import com.google.android.material.snackbar.Snackbar
|
||||||
import android.support.v7.widget.Toolbar
|
import androidx.appcompat.widget.Toolbar
|
||||||
import android.text.Editable
|
import android.text.Editable
|
||||||
import android.text.TextWatcher
|
import android.text.TextWatcher
|
||||||
|
import android.util.Log
|
||||||
import android.view.KeyEvent
|
import android.view.KeyEvent
|
||||||
import android.view.Menu
|
import android.view.Menu
|
||||||
import android.view.MenuItem
|
import android.view.MenuItem
|
||||||
@@ -45,9 +45,14 @@ import android.view.inputmethod.EditorInfo.IME_ACTION_DONE
|
|||||||
import android.widget.*
|
import android.widget.*
|
||||||
import com.kunzisoft.keepass.R
|
import com.kunzisoft.keepass.R
|
||||||
import com.kunzisoft.keepass.activities.dialogs.PasswordEncodingDialogFragment
|
import com.kunzisoft.keepass.activities.dialogs.PasswordEncodingDialogFragment
|
||||||
import com.kunzisoft.keepass.activities.helpers.*
|
import com.kunzisoft.keepass.utils.ClipDataCompat
|
||||||
|
import com.kunzisoft.keepass.activities.helpers.EntrySelectionHelper
|
||||||
|
import com.kunzisoft.keepass.activities.helpers.OpenFileHelper
|
||||||
|
import com.kunzisoft.keepass.activities.helpers.ReadOnlyHelper
|
||||||
import com.kunzisoft.keepass.activities.lock.LockingActivity
|
import com.kunzisoft.keepass.activities.lock.LockingActivity
|
||||||
import com.kunzisoft.keepass.activities.stylish.StylishActivity
|
import com.kunzisoft.keepass.activities.stylish.StylishActivity
|
||||||
|
import com.kunzisoft.keepass.utils.FileDatabaseInfo
|
||||||
|
import com.kunzisoft.keepass.app.database.FileDatabaseHistory
|
||||||
import com.kunzisoft.keepass.autofill.AutofillHelper
|
import com.kunzisoft.keepass.autofill.AutofillHelper
|
||||||
import com.kunzisoft.keepass.database.action.LoadDatabaseRunnable
|
import com.kunzisoft.keepass.database.action.LoadDatabaseRunnable
|
||||||
import com.kunzisoft.keepass.database.action.ProgressDialogThread
|
import com.kunzisoft.keepass.database.action.ProgressDialogThread
|
||||||
@@ -61,14 +66,13 @@ import com.kunzisoft.keepass.tasks.ActionRunnable
|
|||||||
import com.kunzisoft.keepass.utils.MenuUtil
|
import com.kunzisoft.keepass.utils.MenuUtil
|
||||||
import com.kunzisoft.keepass.utils.UriUtil
|
import com.kunzisoft.keepass.utils.UriUtil
|
||||||
import com.kunzisoft.keepass.view.FingerPrintInfoView
|
import com.kunzisoft.keepass.view.FingerPrintInfoView
|
||||||
import permissions.dispatcher.*
|
import com.kunzisoft.keepass.view.asError
|
||||||
import java.io.File
|
import kotlinx.android.synthetic.main.activity_password.*
|
||||||
import java.io.FileNotFoundException
|
import java.io.FileNotFoundException
|
||||||
|
import java.lang.Exception
|
||||||
import java.lang.ref.WeakReference
|
import java.lang.ref.WeakReference
|
||||||
|
|
||||||
@RuntimePermissions
|
class PasswordActivity : StylishActivity() {
|
||||||
class PasswordActivity : StylishActivity(),
|
|
||||||
UriIntentInitTaskCallback {
|
|
||||||
|
|
||||||
// Views
|
// Views
|
||||||
private var toolbar: Toolbar? = null
|
private var toolbar: Toolbar? = null
|
||||||
@@ -87,7 +91,7 @@ class PasswordActivity : StylishActivity(),
|
|||||||
private var prefs: SharedPreferences? = null
|
private var prefs: SharedPreferences? = null
|
||||||
|
|
||||||
private var mRememberKeyFile: Boolean = false
|
private var mRememberKeyFile: Boolean = false
|
||||||
private var mKeyFileHelper: KeyFileHelper? = null
|
private var mOpenFileHelper: OpenFileHelper? = null
|
||||||
|
|
||||||
private var readOnly: Boolean = false
|
private var readOnly: Boolean = false
|
||||||
|
|
||||||
@@ -121,8 +125,8 @@ class PasswordActivity : StylishActivity(),
|
|||||||
readOnly = ReadOnlyHelper.retrieveReadOnlyFromInstanceStateOrPreference(this, savedInstanceState)
|
readOnly = ReadOnlyHelper.retrieveReadOnlyFromInstanceStateOrPreference(this, savedInstanceState)
|
||||||
|
|
||||||
val browseView = findViewById<View>(R.id.browse_button)
|
val browseView = findViewById<View>(R.id.browse_button)
|
||||||
mKeyFileHelper = KeyFileHelper(this@PasswordActivity)
|
mOpenFileHelper = OpenFileHelper(this@PasswordActivity)
|
||||||
browseView.setOnClickListener(mKeyFileHelper!!.openFileOnClickViewListener)
|
browseView.setOnClickListener(mOpenFileHelper!!.openFileOnClickViewListener)
|
||||||
|
|
||||||
passwordView?.setOnEditorActionListener(onEditorActionListener)
|
passwordView?.setOnEditorActionListener(onEditorActionListener)
|
||||||
passwordView?.addTextChangedListener(object : TextWatcher {
|
passwordView?.addTextChangedListener(object : TextWatcher {
|
||||||
@@ -172,8 +176,7 @@ class PasswordActivity : StylishActivity(),
|
|||||||
// For check shutdown
|
// For check shutdown
|
||||||
super.onResume()
|
super.onResume()
|
||||||
|
|
||||||
UriIntentInitTask(WeakReference(this), this, mRememberKeyFile)
|
initUriFromIntent()
|
||||||
.execute(intent)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onSaveInstanceState(outState: Bundle) {
|
override fun onSaveInstanceState(outState: Bundle) {
|
||||||
@@ -181,26 +184,56 @@ class PasswordActivity : StylishActivity(),
|
|||||||
super.onSaveInstanceState(outState)
|
super.onSaveInstanceState(outState)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onPostInitTask(databaseFileUri: Uri?, keyFileUri: Uri?, errorStringId: Int?) {
|
private fun initUriFromIntent() {
|
||||||
mDatabaseFileUri = databaseFileUri
|
|
||||||
|
|
||||||
if (errorStringId != null) {
|
val databaseUri: Uri?
|
||||||
Toast.makeText(this@PasswordActivity, errorStringId, Toast.LENGTH_LONG).show()
|
val keyFileUri: Uri?
|
||||||
finish()
|
|
||||||
return
|
// If is a view intent
|
||||||
|
val action = intent.action
|
||||||
|
if (action != null && action == VIEW_INTENT) {
|
||||||
|
|
||||||
|
val databaseUriRetrieve = intent.data
|
||||||
|
// Stop activity here if we can't verify database URI
|
||||||
|
try {
|
||||||
|
UriUtil.verifyFileUri(databaseUriRetrieve)
|
||||||
|
} catch (e : Exception) {
|
||||||
|
Log.e(TAG, "File URI not validate", e)
|
||||||
|
Toast.makeText(this@PasswordActivity, e.message, Toast.LENGTH_LONG).show()
|
||||||
|
finish()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
databaseUri = databaseUriRetrieve
|
||||||
|
keyFileUri = ClipDataCompat.getUriFromIntent(intent, KEY_KEYFILE)
|
||||||
|
|
||||||
|
} else {
|
||||||
|
databaseUri = UriUtil.parseUriFile(intent.getStringExtra(KEY_FILENAME))
|
||||||
|
keyFileUri = UriUtil.parseUriFile(intent.getStringExtra(KEY_KEYFILE))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify permission to read file
|
// Post init uri with KeyFile if needed
|
||||||
if (databaseFileUri != null && !databaseFileUri.scheme!!.contains("content"))
|
if (mRememberKeyFile && (keyFileUri == null || keyFileUri.toString().isEmpty())) {
|
||||||
doNothingWithPermissionCheck()
|
// Retrieve KeyFile in a thread
|
||||||
|
databaseUri?.let { databaseUriNotNull ->
|
||||||
|
FileDatabaseHistory.getInstance(applicationContext)
|
||||||
|
.getKeyFileUriByDatabaseUri(databaseUriNotNull) {
|
||||||
|
onPostInitUri(databaseUri, it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
onPostInitUri(databaseUri, keyFileUri)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun onPostInitUri(databaseFileUri: Uri?, keyFileUri: Uri?) {
|
||||||
|
mDatabaseFileUri = databaseFileUri
|
||||||
|
|
||||||
// Define title
|
// Define title
|
||||||
val dbUriString = databaseFileUri?.toString() ?: ""
|
databaseFileUri?.let {
|
||||||
if (dbUriString.isNotEmpty()) {
|
FileDatabaseInfo(this, it).retrieveDatabaseTitle { title ->
|
||||||
if (PreferencesUtil.isFullFilePathEnable(this))
|
filenameView?.text = title
|
||||||
filenameView?.text = dbUriString
|
}
|
||||||
else
|
|
||||||
filenameView?.text = File(databaseFileUri!!.path!!).name // TODO Encapsulate
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Define Key File text
|
// Define Key File text
|
||||||
@@ -216,7 +249,7 @@ class PasswordActivity : StylishActivity(),
|
|||||||
newDefaultFileName = databaseFileUri?.toString() ?: newDefaultFileName
|
newDefaultFileName = databaseFileUri?.toString() ?: newDefaultFileName
|
||||||
}
|
}
|
||||||
|
|
||||||
prefs?.edit()?.apply() {
|
prefs?.edit()?.apply {
|
||||||
putString(KEY_DEFAULT_FILENAME, newDefaultFileName)
|
putString(KEY_DEFAULT_FILENAME, newDefaultFileName)
|
||||||
apply()
|
apply()
|
||||||
}
|
}
|
||||||
@@ -418,7 +451,7 @@ class PasswordActivity : StylishActivity(),
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (result.message != null && result.message!!.isNotEmpty()) {
|
if (result.message != null && result.message!!.isNotEmpty()) {
|
||||||
Toast.makeText(this@PasswordActivity, result.message, Toast.LENGTH_LONG).show()
|
Snackbar.make(activity_password_coordinator_layout, result.message!!, Snackbar.LENGTH_LONG).asError().show()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -465,32 +498,40 @@ class PasswordActivity : StylishActivity(),
|
|||||||
|
|
||||||
private fun performedNextEducation(passwordActivityEducation: PasswordActivityEducation,
|
private fun performedNextEducation(passwordActivityEducation: PasswordActivityEducation,
|
||||||
menu: Menu) {
|
menu: Menu) {
|
||||||
if (toolbar != null
|
val unlockEducationPerformed = toolbar != null
|
||||||
&& passwordActivityEducation.checkAndPerformedFingerprintUnlockEducation(
|
&& passwordActivityEducation.checkAndPerformedUnlockEducation(
|
||||||
toolbar!!,
|
toolbar!!,
|
||||||
{
|
{
|
||||||
performedNextEducation(passwordActivityEducation, menu)
|
performedNextEducation(passwordActivityEducation, menu)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
performedNextEducation(passwordActivityEducation, menu)
|
performedNextEducation(passwordActivityEducation, menu)
|
||||||
}))
|
})
|
||||||
else if (toolbar != null
|
if (!unlockEducationPerformed) {
|
||||||
&& toolbar!!.findViewById<View>(R.id.menu_open_file_read_mode_key) != null
|
|
||||||
&& passwordActivityEducation.checkAndPerformedReadOnlyEducation(
|
val readOnlyEducationPerformed = toolbar != null
|
||||||
toolbar!!.findViewById(R.id.menu_open_file_read_mode_key),
|
&& toolbar!!.findViewById<View>(R.id.menu_open_file_read_mode_key) != null
|
||||||
{
|
&& passwordActivityEducation.checkAndPerformedReadOnlyEducation(
|
||||||
onOptionsItemSelected(menu.findItem(R.id.menu_open_file_read_mode_key))
|
toolbar!!.findViewById(R.id.menu_open_file_read_mode_key),
|
||||||
performedNextEducation(passwordActivityEducation, menu)
|
{
|
||||||
},
|
onOptionsItemSelected(menu.findItem(R.id.menu_open_file_read_mode_key))
|
||||||
{
|
performedNextEducation(passwordActivityEducation, menu)
|
||||||
performedNextEducation(passwordActivityEducation, menu)
|
},
|
||||||
}))
|
{
|
||||||
else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
|
performedNextEducation(passwordActivityEducation, menu)
|
||||||
&& PreferencesUtil.isFingerprintEnable(applicationContext)
|
})
|
||||||
&& FingerPrintHelper.isFingerprintSupported(getSystemService(FingerprintManager::class.java))
|
|
||||||
&& fingerPrintInfoView != null && fingerPrintInfoView?.fingerPrintImageView != null
|
if (!readOnlyEducationPerformed) {
|
||||||
&& passwordActivityEducation.checkAndPerformedFingerprintEducation(fingerPrintInfoView?.fingerPrintImageView!!))
|
|
||||||
;
|
// fingerprintEducationPerformed
|
||||||
|
(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
|
||||||
|
&& PreferencesUtil.isFingerprintEnable(applicationContext)
|
||||||
|
&& FingerPrintHelper.isFingerprintSupported(getSystemService(FingerprintManager::class.java))
|
||||||
|
&& fingerPrintInfoView != null && fingerPrintInfoView?.fingerPrintImageView != null
|
||||||
|
&& passwordActivityEducation.checkAndPerformedFingerprintEducation(fingerPrintInfoView?.fingerPrintImageView!!))
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun changeOpenFileReadIcon(togglePassword: MenuItem) {
|
private fun changeOpenFileReadIcon(togglePassword: MenuItem) {
|
||||||
@@ -520,12 +561,6 @@ class PasswordActivity : StylishActivity(),
|
|||||||
return super.onOptionsItemSelected(item)
|
return super.onOptionsItemSelected(item)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) {
|
|
||||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
|
|
||||||
// NOTE: delegate the permission handling to generated method
|
|
||||||
onRequestPermissionsResult(requestCode, grantResults)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onActivityResult(
|
override fun onActivityResult(
|
||||||
requestCode: Int,
|
requestCode: Int,
|
||||||
resultCode: Int,
|
resultCode: Int,
|
||||||
@@ -538,7 +573,7 @@ class PasswordActivity : StylishActivity(),
|
|||||||
}
|
}
|
||||||
|
|
||||||
var keyFileResult = false
|
var keyFileResult = false
|
||||||
mKeyFileHelper?.let {
|
mOpenFileHelper?.let {
|
||||||
keyFileResult = it.onActivityResultCallback(requestCode, resultCode, data
|
keyFileResult = it.onActivityResultCallback(requestCode, resultCode, data
|
||||||
) { uri ->
|
) { uri ->
|
||||||
if (uri != null) {
|
if (uri != null) {
|
||||||
@@ -557,64 +592,28 @@ class PasswordActivity : StylishActivity(),
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@NeedsPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
|
||||||
fun doNothing() {
|
|
||||||
}
|
|
||||||
|
|
||||||
@OnShowRationale(Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
|
||||||
internal fun showRationaleForExternalStorage(request: PermissionRequest) {
|
|
||||||
AlertDialog.Builder(this)
|
|
||||||
.setMessage(R.string.permission_external_storage_rationale_read_database)
|
|
||||||
.setPositiveButton(R.string.allow) { _, _ -> request.proceed() }
|
|
||||||
.setNegativeButton(R.string.cancel) { _, _ -> request.cancel() }
|
|
||||||
.show()
|
|
||||||
}
|
|
||||||
|
|
||||||
@OnPermissionDenied(Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
|
||||||
internal fun showDeniedForExternalStorage() {
|
|
||||||
Toast.makeText(this, R.string.permission_external_storage_denied, Toast.LENGTH_SHORT).show()
|
|
||||||
finish()
|
|
||||||
}
|
|
||||||
|
|
||||||
@OnNeverAskAgain(Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
|
||||||
internal fun showNeverAskForExternalStorage() {
|
|
||||||
Toast.makeText(this, R.string.permission_external_storage_never_ask, Toast.LENGTH_SHORT).show()
|
|
||||||
finish()
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
private val TAG = PasswordActivity::class.java.name
|
private val TAG = PasswordActivity::class.java.name
|
||||||
|
|
||||||
const val KEY_DEFAULT_FILENAME = "defaultFileName"
|
const val KEY_DEFAULT_FILENAME = "defaultFileName"
|
||||||
|
|
||||||
|
private const val KEY_FILENAME = "fileName"
|
||||||
|
private const val KEY_KEYFILE = "keyFile"
|
||||||
|
private const val VIEW_INTENT = "android.intent.action.VIEW"
|
||||||
|
|
||||||
private const val KEY_PASSWORD = "password"
|
private const val KEY_PASSWORD = "password"
|
||||||
private const val KEY_LAUNCH_IMMEDIATELY = "launchImmediately"
|
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) {
|
intentBuildLauncher: (Intent) -> Unit) {
|
||||||
val intent = Intent(activity, PasswordActivity::class.java)
|
val intent = Intent(activity, PasswordActivity::class.java)
|
||||||
intent.putExtra(UriIntentInitTask.KEY_FILENAME, fileName)
|
intent.putExtra(KEY_FILENAME, fileName)
|
||||||
intent.putExtra(UriIntentInitTask.KEY_KEYFILE, keyFile)
|
if (keyFile != null)
|
||||||
|
intent.putExtra(KEY_KEYFILE, keyFile)
|
||||||
intentBuildLauncher.invoke(intent)
|
intentBuildLauncher.invoke(intent)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Throws(FileNotFoundException::class)
|
|
||||||
private fun verifyFileNameUriFromLaunch(fileName: String) {
|
|
||||||
if (fileName.isEmpty()) {
|
|
||||||
throw FileNotFoundException()
|
|
||||||
}
|
|
||||||
|
|
||||||
val uri = UriUtil.parseUriFile(fileName)
|
|
||||||
val scheme = uri?.scheme
|
|
||||||
if (scheme != null && scheme.isNotEmpty() && scheme.equals("file", ignoreCase = true)) {
|
|
||||||
val dbFile = File(uri.path!!)
|
|
||||||
if (!dbFile.exists()) {
|
|
||||||
throw FileNotFoundException()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* -------------------------
|
* -------------------------
|
||||||
* Standard Launch
|
* Standard Launch
|
||||||
@@ -625,8 +624,8 @@ class PasswordActivity : StylishActivity(),
|
|||||||
fun launch(
|
fun launch(
|
||||||
activity: Activity,
|
activity: Activity,
|
||||||
fileName: String,
|
fileName: String,
|
||||||
keyFile: String) {
|
keyFile: String?) {
|
||||||
verifyFileNameUriFromLaunch(fileName)
|
UriUtil.verifyFilePath(fileName)
|
||||||
buildAndLaunchIntent(activity, fileName, keyFile) { intent ->
|
buildAndLaunchIntent(activity, fileName, keyFile) { intent ->
|
||||||
activity.startActivity(intent)
|
activity.startActivity(intent)
|
||||||
}
|
}
|
||||||
@@ -642,8 +641,8 @@ class PasswordActivity : StylishActivity(),
|
|||||||
fun launchForKeyboardResult(
|
fun launchForKeyboardResult(
|
||||||
activity: Activity,
|
activity: Activity,
|
||||||
fileName: String,
|
fileName: String,
|
||||||
keyFile: String) {
|
keyFile: String?) {
|
||||||
verifyFileNameUriFromLaunch(fileName)
|
UriUtil.verifyFilePath(fileName)
|
||||||
|
|
||||||
buildAndLaunchIntent(activity, fileName, keyFile) { intent ->
|
buildAndLaunchIntent(activity, fileName, keyFile) { intent ->
|
||||||
KeyboardHelper.startActivityForKeyboardSelection(activity, intent)
|
KeyboardHelper.startActivityForKeyboardSelection(activity, intent)
|
||||||
@@ -661,9 +660,9 @@ class PasswordActivity : StylishActivity(),
|
|||||||
fun launchForAutofillResult(
|
fun launchForAutofillResult(
|
||||||
activity: Activity,
|
activity: Activity,
|
||||||
fileName: String,
|
fileName: String,
|
||||||
keyFile: String,
|
keyFile: String?,
|
||||||
assistStructure: AssistStructure?) {
|
assistStructure: AssistStructure?) {
|
||||||
verifyFileNameUriFromLaunch(fileName)
|
UriUtil.verifyFilePath(fileName)
|
||||||
|
|
||||||
if (assistStructure != null) {
|
if (assistStructure != null) {
|
||||||
buildAndLaunchIntent(activity, fileName, keyFile) { intent ->
|
buildAndLaunchIntent(activity, fileName, keyFile) { intent ->
|
||||||
|
|||||||
@@ -25,16 +25,16 @@ import android.content.DialogInterface
|
|||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.support.v4.app.DialogFragment
|
import com.google.android.material.textfield.TextInputLayout
|
||||||
import android.support.v7.app.AlertDialog
|
import androidx.fragment.app.DialogFragment
|
||||||
|
import androidx.appcompat.app.AlertDialog
|
||||||
import android.text.Editable
|
import android.text.Editable
|
||||||
import android.text.TextWatcher
|
import android.text.TextWatcher
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.widget.CompoundButton
|
import android.widget.CompoundButton
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
import android.widget.Toast
|
|
||||||
import com.kunzisoft.keepass.R
|
import com.kunzisoft.keepass.R
|
||||||
import com.kunzisoft.keepass.activities.helpers.KeyFileHelper
|
import com.kunzisoft.keepass.activities.helpers.OpenFileHelper
|
||||||
import com.kunzisoft.keepass.utils.UriUtil
|
import com.kunzisoft.keepass.utils.UriUtil
|
||||||
|
|
||||||
class AssignMasterKeyDialogFragment : DialogFragment() {
|
class AssignMasterKeyDialogFragment : DialogFragment() {
|
||||||
@@ -43,15 +43,39 @@ class AssignMasterKeyDialogFragment : DialogFragment() {
|
|||||||
private var mKeyFile: Uri? = null
|
private var mKeyFile: Uri? = null
|
||||||
|
|
||||||
private var rootView: View? = null
|
private var rootView: View? = null
|
||||||
|
|
||||||
private var passwordCheckBox: CompoundButton? = null
|
private var passwordCheckBox: CompoundButton? = null
|
||||||
private var passView: TextView? = null
|
private var passwordView: TextView? = null
|
||||||
private var passConfView: TextView? = null
|
private var passwordRepeatTextInputLayout: TextInputLayout? = null
|
||||||
|
private var passwordRepeatView: TextView? = null
|
||||||
|
|
||||||
|
private var keyFileTextInputLayout: TextInputLayout? = null
|
||||||
private var keyFileCheckBox: CompoundButton? = null
|
private var keyFileCheckBox: CompoundButton? = null
|
||||||
private var keyFileView: TextView? = null
|
private var keyFileView: TextView? = null
|
||||||
|
|
||||||
private var mListener: AssignPasswordDialogListener? = null
|
private var mListener: AssignPasswordDialogListener? = null
|
||||||
|
|
||||||
private var mKeyFileHelper: KeyFileHelper? = null
|
private var mOpenFileHelper: OpenFileHelper? = null
|
||||||
|
|
||||||
|
private val passwordTextWatcher = object : TextWatcher {
|
||||||
|
override fun beforeTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {}
|
||||||
|
|
||||||
|
override fun onTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {}
|
||||||
|
|
||||||
|
override fun afterTextChanged(editable: Editable) {
|
||||||
|
passwordCheckBox?.isChecked = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val keyFileTextWatcher = object : TextWatcher {
|
||||||
|
override fun beforeTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {}
|
||||||
|
|
||||||
|
override fun onTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {}
|
||||||
|
|
||||||
|
override fun afterTextChanged(editable: Editable) {
|
||||||
|
keyFileCheckBox?.isChecked = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
interface AssignPasswordDialogListener {
|
interface AssignPasswordDialogListener {
|
||||||
fun onAssignKeyDialogPositiveClick(masterPasswordChecked: Boolean, masterPassword: String?,
|
fun onAssignKeyDialogPositiveClick(masterPasswordChecked: Boolean, masterPassword: String?,
|
||||||
@@ -83,33 +107,17 @@ class AssignMasterKeyDialogFragment : DialogFragment() {
|
|||||||
.setNegativeButton(R.string.cancel) { _, _ -> }
|
.setNegativeButton(R.string.cancel) { _, _ -> }
|
||||||
|
|
||||||
passwordCheckBox = rootView?.findViewById(R.id.password_checkbox)
|
passwordCheckBox = rootView?.findViewById(R.id.password_checkbox)
|
||||||
passView = rootView?.findViewById(R.id.pass_password)
|
passwordView = rootView?.findViewById(R.id.pass_password)
|
||||||
passView?.addTextChangedListener(object : TextWatcher {
|
passwordRepeatTextInputLayout = rootView?.findViewById(R.id.password_repeat_input_layout)
|
||||||
override fun beforeTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {}
|
passwordRepeatView = rootView?.findViewById(R.id.pass_conf_password)
|
||||||
|
|
||||||
override fun onTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {}
|
|
||||||
|
|
||||||
override fun afterTextChanged(editable: Editable) {
|
|
||||||
passwordCheckBox?.isChecked = true
|
|
||||||
}
|
|
||||||
})
|
|
||||||
passConfView = rootView?.findViewById(R.id.pass_conf_password)
|
|
||||||
|
|
||||||
|
keyFileTextInputLayout = rootView?.findViewById(R.id.keyfile_input_layout)
|
||||||
keyFileCheckBox = rootView?.findViewById(R.id.keyfile_checkox)
|
keyFileCheckBox = rootView?.findViewById(R.id.keyfile_checkox)
|
||||||
keyFileView = rootView?.findViewById(R.id.pass_keyfile)
|
keyFileView = rootView?.findViewById(R.id.pass_keyfile)
|
||||||
keyFileView?.addTextChangedListener(object : TextWatcher {
|
|
||||||
override fun beforeTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {}
|
|
||||||
|
|
||||||
override fun onTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {}
|
mOpenFileHelper = OpenFileHelper(this)
|
||||||
|
|
||||||
override fun afterTextChanged(editable: Editable) {
|
|
||||||
keyFileCheckBox?.isChecked = true
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
mKeyFileHelper = KeyFileHelper(this)
|
|
||||||
rootView?.findViewById<View>(R.id.browse_button)?.setOnClickListener { view ->
|
rootView?.findViewById<View>(R.id.browse_button)?.setOnClickListener { view ->
|
||||||
mKeyFileHelper?.openFileOnClickViewListener?.onClick(view) }
|
mOpenFileHelper?.openFileOnClickViewListener?.onClick(view) }
|
||||||
|
|
||||||
val dialog = builder.create()
|
val dialog = builder.create()
|
||||||
|
|
||||||
@@ -149,20 +157,35 @@ class AssignMasterKeyDialogFragment : DialogFragment() {
|
|||||||
return super.onCreateDialog(savedInstanceState)
|
return super.onCreateDialog(savedInstanceState)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onResume() {
|
||||||
|
super.onResume()
|
||||||
|
|
||||||
|
// To check checkboxes if a text is present
|
||||||
|
passwordView?.addTextChangedListener(passwordTextWatcher)
|
||||||
|
keyFileView?.addTextChangedListener(keyFileTextWatcher)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onPause() {
|
||||||
|
super.onPause()
|
||||||
|
|
||||||
|
passwordView?.removeTextChangedListener(passwordTextWatcher)
|
||||||
|
keyFileView?.removeTextChangedListener(keyFileTextWatcher)
|
||||||
|
}
|
||||||
|
|
||||||
private fun verifyPassword(): Boolean {
|
private fun verifyPassword(): Boolean {
|
||||||
var error = false
|
var error = false
|
||||||
if (passwordCheckBox != null
|
if (passwordCheckBox != null
|
||||||
&& passwordCheckBox!!.isChecked
|
&& passwordCheckBox!!.isChecked
|
||||||
&& passView != null
|
&& passwordView != null
|
||||||
&& passConfView != null) {
|
&& passwordRepeatView != null) {
|
||||||
mMasterPassword = passView!!.text.toString()
|
mMasterPassword = passwordView!!.text.toString()
|
||||||
val confPassword = passConfView!!.text.toString()
|
val confPassword = passwordRepeatView!!.text.toString()
|
||||||
|
|
||||||
// Verify that passwords match
|
// Verify that passwords match
|
||||||
if (mMasterPassword != confPassword) {
|
if (mMasterPassword != confPassword) {
|
||||||
error = true
|
error = true
|
||||||
// Passwords do not match
|
// Passwords do not match
|
||||||
Toast.makeText(context, R.string.error_pass_match, Toast.LENGTH_LONG).show()
|
passwordRepeatTextInputLayout?.error = getString(R.string.error_pass_match)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mMasterPassword == null || mMasterPassword!!.isEmpty()) {
|
if (mMasterPassword == null || mMasterPassword!!.isEmpty()) {
|
||||||
@@ -183,7 +206,8 @@ class AssignMasterKeyDialogFragment : DialogFragment() {
|
|||||||
// Verify that a keyfile is set
|
// Verify that a keyfile is set
|
||||||
if (keyFile == null || keyFile.toString().isEmpty()) {
|
if (keyFile == null || keyFile.toString().isEmpty()) {
|
||||||
error = true
|
error = true
|
||||||
Toast.makeText(context, R.string.error_nokeyfile, Toast.LENGTH_LONG).show()
|
// TODO better keyfile check
|
||||||
|
keyFileTextInputLayout?.error = getString(R.string.error_nokeyfile)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return error
|
return error
|
||||||
@@ -224,7 +248,7 @@ class AssignMasterKeyDialogFragment : DialogFragment() {
|
|||||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||||
super.onActivityResult(requestCode, resultCode, data)
|
super.onActivityResult(requestCode, resultCode, data)
|
||||||
|
|
||||||
mKeyFileHelper?.onActivityResultCallback(requestCode, resultCode, data
|
mOpenFileHelper?.onActivityResultCallback(requestCode, resultCode, data
|
||||||
) { uri ->
|
) { uri ->
|
||||||
UriUtil.parseUriFile(uri)?.let { pathUri ->
|
UriUtil.parseUriFile(uri)?.let { pathUri ->
|
||||||
keyFileCheckBox?.isChecked = true
|
keyFileCheckBox?.isChecked = true
|
||||||
|
|||||||
@@ -21,11 +21,12 @@ package com.kunzisoft.keepass.activities.dialogs
|
|||||||
|
|
||||||
import android.app.Dialog
|
import android.app.Dialog
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.support.v4.app.DialogFragment
|
import androidx.fragment.app.DialogFragment
|
||||||
import android.support.v7.app.AlertDialog
|
import androidx.appcompat.app.AlertDialog
|
||||||
import android.widget.Button
|
import android.widget.Button
|
||||||
|
import android.widget.TextView
|
||||||
import com.kunzisoft.keepass.R
|
import com.kunzisoft.keepass.R
|
||||||
import com.kunzisoft.keepass.utils.Util
|
import com.kunzisoft.keepass.utils.UriUtil
|
||||||
|
|
||||||
class BrowserDialogFragment : DialogFragment() {
|
class BrowserDialogFragment : DialogFragment() {
|
||||||
|
|
||||||
@@ -37,15 +38,18 @@ class BrowserDialogFragment : DialogFragment() {
|
|||||||
builder.setView(root)
|
builder.setView(root)
|
||||||
.setNegativeButton(R.string.cancel) { _, _ -> }
|
.setNegativeButton(R.string.cancel) { _, _ -> }
|
||||||
|
|
||||||
val market = root.findViewById<Button>(R.id.install_market)
|
val textDescription = root.findViewById<TextView>(R.id.file_manager_install_description)
|
||||||
|
textDescription.text = getString(R.string.file_manager_install_description)
|
||||||
|
|
||||||
|
val market = root.findViewById<Button>(R.id.file_manager_install_play_store)
|
||||||
market.setOnClickListener {
|
market.setOnClickListener {
|
||||||
Util.gotoUrl(context!!, R.string.filemanager_play_store)
|
UriUtil.gotoUrl(context!!, R.string.filemanager_play_store)
|
||||||
dismiss()
|
dismiss()
|
||||||
}
|
}
|
||||||
|
|
||||||
val web = root.findViewById<Button>(R.id.install_web)
|
val web = root.findViewById<Button>(R.id.file_manager_install_f_droid)
|
||||||
web.setOnClickListener {
|
web.setOnClickListener {
|
||||||
Util.gotoUrl(context!!, R.string.filemanager_f_droid)
|
UriUtil.gotoUrl(context!!, R.string.filemanager_f_droid)
|
||||||
dismiss()
|
dismiss()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,213 +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.activities.dialogs
|
|
||||||
|
|
||||||
import android.app.Activity
|
|
||||||
import android.app.Dialog
|
|
||||||
import android.content.Context
|
|
||||||
import android.content.DialogInterface
|
|
||||||
import android.content.Intent
|
|
||||||
import android.net.Uri
|
|
||||||
import android.os.Bundle
|
|
||||||
import android.os.Environment
|
|
||||||
import android.support.v4.app.DialogFragment
|
|
||||||
import android.support.v7.app.AlertDialog
|
|
||||||
import android.view.ActionMode
|
|
||||||
import android.view.Menu
|
|
||||||
import android.view.MenuItem
|
|
||||||
import android.view.View
|
|
||||||
import android.view.ViewGroup
|
|
||||||
import android.widget.AdapterView
|
|
||||||
import android.widget.ArrayAdapter
|
|
||||||
import android.widget.Button
|
|
||||||
import android.widget.EditText
|
|
||||||
import android.widget.Spinner
|
|
||||||
import android.widget.TextView
|
|
||||||
|
|
||||||
import com.kunzisoft.keepass.R
|
|
||||||
import com.kunzisoft.keepass.activities.stylish.FilePickerStylishActivity
|
|
||||||
import com.kunzisoft.keepass.utils.UriUtil
|
|
||||||
import com.nononsenseapps.filepicker.FilePickerActivity
|
|
||||||
import com.nononsenseapps.filepicker.Utils
|
|
||||||
|
|
||||||
class CreateFileDialogFragment : DialogFragment(), AdapterView.OnItemSelectedListener {
|
|
||||||
|
|
||||||
private val FILE_CODE = 3853
|
|
||||||
|
|
||||||
private var folderPathView: EditText? = null
|
|
||||||
private var fileNameView: EditText? = null
|
|
||||||
private var positiveButton: Button? = null
|
|
||||||
private var negativeButton: Button? = null
|
|
||||||
|
|
||||||
private var mDefinePathDialogListener: DefinePathDialogListener? = null
|
|
||||||
|
|
||||||
private var mDatabaseFileExtension: String? = null
|
|
||||||
private var mUriPath: Uri? = null
|
|
||||||
|
|
||||||
interface DefinePathDialogListener {
|
|
||||||
fun onDefinePathDialogPositiveClick(pathFile: Uri?): Boolean
|
|
||||||
fun onDefinePathDialogNegativeClick(pathFile: Uri?): Boolean
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onAttach(activity: Context?) {
|
|
||||||
super.onAttach(activity)
|
|
||||||
try {
|
|
||||||
mDefinePathDialogListener = activity as DefinePathDialogListener?
|
|
||||||
} catch (e: ClassCastException) {
|
|
||||||
throw ClassCastException(activity?.toString()
|
|
||||||
+ " must implement " + DefinePathDialogListener::class.java.name)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
|
||||||
activity?.let { activity ->
|
|
||||||
val builder = AlertDialog.Builder(activity)
|
|
||||||
val inflater = activity.layoutInflater
|
|
||||||
|
|
||||||
val rootView = inflater.inflate(R.layout.fragment_file_creation, null)
|
|
||||||
builder.setView(rootView)
|
|
||||||
.setTitle(R.string.create_keepass_file)
|
|
||||||
// Add action buttons
|
|
||||||
.setPositiveButton(android.R.string.ok) { _, _ -> }
|
|
||||||
.setNegativeButton(R.string.cancel) { _, _ -> }
|
|
||||||
|
|
||||||
// To prevent crash issue #69 https://github.com/Kunzisoft/KeePassDX/issues/69
|
|
||||||
val actionCopyBarCallback = object : ActionMode.Callback {
|
|
||||||
|
|
||||||
override fun onPrepareActionMode(mode: ActionMode, menu: Menu): Boolean {
|
|
||||||
positiveButton?.isEnabled = false
|
|
||||||
negativeButton?.isEnabled = false
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onDestroyActionMode(mode: ActionMode) {
|
|
||||||
positiveButton?.isEnabled = true
|
|
||||||
negativeButton?.isEnabled = true
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCreateActionMode(mode: ActionMode, menu: Menu): Boolean {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onActionItemClicked(mode: ActionMode, item: MenuItem): Boolean {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Folder selection
|
|
||||||
val browseView = rootView.findViewById<View>(R.id.browse_button)
|
|
||||||
folderPathView = rootView.findViewById(R.id.folder_path)
|
|
||||||
folderPathView?.customSelectionActionModeCallback = actionCopyBarCallback
|
|
||||||
fileNameView = rootView.findViewById(R.id.filename)
|
|
||||||
fileNameView?.customSelectionActionModeCallback = actionCopyBarCallback
|
|
||||||
|
|
||||||
val defaultPath = Environment.getExternalStorageDirectory().path + getString(R.string.database_file_path_default)
|
|
||||||
folderPathView?.setText(defaultPath)
|
|
||||||
browseView.setOnClickListener { _ ->
|
|
||||||
Intent(context, FilePickerStylishActivity::class.java).apply {
|
|
||||||
putExtra(FilePickerActivity.EXTRA_ALLOW_MULTIPLE, false)
|
|
||||||
putExtra(FilePickerActivity.EXTRA_ALLOW_CREATE_DIR, true)
|
|
||||||
putExtra(FilePickerActivity.EXTRA_MODE, FilePickerActivity.MODE_DIR)
|
|
||||||
putExtra(FilePickerActivity.EXTRA_START_PATH,
|
|
||||||
Environment.getExternalStorageDirectory().path)
|
|
||||||
startActivityForResult(this, FILE_CODE)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Init path
|
|
||||||
mUriPath = null
|
|
||||||
|
|
||||||
// Extension
|
|
||||||
mDatabaseFileExtension = getString(R.string.database_file_extension_default)
|
|
||||||
val spinner = rootView.findViewById<Spinner>(R.id.file_types)
|
|
||||||
spinner.onItemSelectedListener = this
|
|
||||||
|
|
||||||
// Spinner Drop down elements
|
|
||||||
val fileTypes = resources.getStringArray(R.array.file_types)
|
|
||||||
val dataAdapter = ArrayAdapter(activity, android.R.layout.simple_spinner_item, fileTypes)
|
|
||||||
dataAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
|
|
||||||
spinner.adapter = dataAdapter
|
|
||||||
// Or text if only one item https://github.com/Kunzisoft/KeePassDX/issues/105
|
|
||||||
if (fileTypes.size == 1) {
|
|
||||||
val params = spinner.layoutParams
|
|
||||||
spinner.visibility = View.GONE
|
|
||||||
val extensionTextView = TextView(context)
|
|
||||||
extensionTextView.text = mDatabaseFileExtension
|
|
||||||
extensionTextView.layoutParams = params
|
|
||||||
val parentView = spinner.parent as ViewGroup
|
|
||||||
parentView.addView(extensionTextView)
|
|
||||||
}
|
|
||||||
|
|
||||||
val dialog = builder.create()
|
|
||||||
|
|
||||||
dialog.setOnShowListener { _ ->
|
|
||||||
positiveButton = dialog.getButton(DialogInterface.BUTTON_POSITIVE)
|
|
||||||
negativeButton = dialog.getButton(DialogInterface.BUTTON_NEGATIVE)
|
|
||||||
positiveButton?.setOnClickListener { _ ->
|
|
||||||
mDefinePathDialogListener?.let {
|
|
||||||
if (it.onDefinePathDialogPositiveClick(buildPath()))
|
|
||||||
dismiss()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
negativeButton?.setOnClickListener { _->
|
|
||||||
mDefinePathDialogListener?.let {
|
|
||||||
if (it.onDefinePathDialogNegativeClick(buildPath())) {
|
|
||||||
dismiss()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return dialog
|
|
||||||
}
|
|
||||||
return super.onCreateDialog(savedInstanceState)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun buildPath(): Uri? {
|
|
||||||
if (folderPathView != null && fileNameView != null && mDatabaseFileExtension != null) {
|
|
||||||
var path = Uri.Builder().path(folderPathView!!.text.toString())
|
|
||||||
.appendPath(fileNameView!!.text.toString() + mDatabaseFileExtension!!)
|
|
||||||
.build()
|
|
||||||
context?.let { context ->
|
|
||||||
path = UriUtil.translateUri(context, path)
|
|
||||||
}
|
|
||||||
return path
|
|
||||||
}
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
|
||||||
if (requestCode == FILE_CODE && resultCode == Activity.RESULT_OK) {
|
|
||||||
mUriPath = data?.data
|
|
||||||
mUriPath?.let {
|
|
||||||
val file = Utils.getFileForUri(it)
|
|
||||||
folderPathView?.setText(file.path)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onItemSelected(adapterView: AdapterView<*>, view: View, position: Int, id: Long) {
|
|
||||||
mDatabaseFileExtension = adapterView.getItemAtPosition(position).toString()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onNothingSelected(adapterView: AdapterView<*>) {
|
|
||||||
// Do nothing
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,99 +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.activities.dialogs
|
|
||||||
|
|
||||||
import android.app.Dialog
|
|
||||||
import android.net.Uri
|
|
||||||
import android.os.Bundle
|
|
||||||
import android.support.v4.app.DialogFragment
|
|
||||||
import android.support.v7.app.AlertDialog
|
|
||||||
import android.view.View
|
|
||||||
import android.widget.TextView
|
|
||||||
import com.kunzisoft.keepass.R
|
|
||||||
import com.kunzisoft.keepass.fileselect.FileDatabaseModel
|
|
||||||
import java.text.DateFormat
|
|
||||||
|
|
||||||
class FileInformationDialogFragment : DialogFragment() {
|
|
||||||
|
|
||||||
private var fileSizeContainerView: View? = null
|
|
||||||
private var fileModificationContainerView: View? = null
|
|
||||||
|
|
||||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
|
||||||
activity?.let { activity ->
|
|
||||||
val builder = AlertDialog.Builder(activity)
|
|
||||||
val inflater = activity.layoutInflater
|
|
||||||
val root = inflater.inflate(R.layout.fragment_file_selection_information, null)
|
|
||||||
val fileNameView = root.findViewById<TextView>(R.id.file_filename)
|
|
||||||
val filePathView = root.findViewById<TextView>(R.id.file_path)
|
|
||||||
fileSizeContainerView = root.findViewById(R.id.file_size_container)
|
|
||||||
val fileSizeView = root.findViewById<TextView>(R.id.file_size)
|
|
||||||
fileModificationContainerView = root.findViewById(R.id.file_modification_container)
|
|
||||||
val fileModificationView = root.findViewById<TextView>(R.id.file_modification)
|
|
||||||
|
|
||||||
arguments?.apply {
|
|
||||||
if (containsKey(FILE_SELECT_BEEN_ARG)) {
|
|
||||||
(getSerializable(FILE_SELECT_BEEN_ARG) as FileDatabaseModel?)?.let { fileDatabaseModel ->
|
|
||||||
fileDatabaseModel.fileUri?.let { fileUri ->
|
|
||||||
filePathView.text = Uri.decode(fileUri.toString())
|
|
||||||
}
|
|
||||||
fileNameView.text = fileDatabaseModel.fileName
|
|
||||||
|
|
||||||
if (fileDatabaseModel.notFound()) {
|
|
||||||
hideFileInfo()
|
|
||||||
} else {
|
|
||||||
showFileInfo()
|
|
||||||
fileSizeView.text = fileDatabaseModel.size.toString()
|
|
||||||
fileModificationView.text = DateFormat.getDateTimeInstance()
|
|
||||||
.format(fileDatabaseModel.lastModification)
|
|
||||||
}
|
|
||||||
} ?: hideFileInfo()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
builder.setView(root)
|
|
||||||
builder.setPositiveButton(android.R.string.ok) { _, _ -> }
|
|
||||||
return builder.create()
|
|
||||||
}
|
|
||||||
return super.onCreateDialog(savedInstanceState)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun showFileInfo() {
|
|
||||||
fileSizeContainerView?.visibility = View.VISIBLE
|
|
||||||
fileModificationContainerView?.visibility = View.VISIBLE
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun hideFileInfo() {
|
|
||||||
fileSizeContainerView?.visibility = View.GONE
|
|
||||||
fileModificationContainerView?.visibility = View.GONE
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
|
|
||||||
private const val FILE_SELECT_BEEN_ARG = "FILE_SELECT_BEEN_ARG"
|
|
||||||
|
|
||||||
fun newInstance(fileDatabaseModel: FileDatabaseModel): FileInformationDialogFragment {
|
|
||||||
val fileInformationDialogFragment = FileInformationDialogFragment()
|
|
||||||
val args = Bundle()
|
|
||||||
args.putSerializable(FILE_SELECT_BEEN_ARG, fileDatabaseModel)
|
|
||||||
fileInformationDialogFragment.arguments = args
|
|
||||||
return fileInformationDialogFragment
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -22,14 +22,18 @@ package com.kunzisoft.keepass.activities.dialogs
|
|||||||
import android.app.Dialog
|
import android.app.Dialog
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.support.v4.app.DialogFragment
|
import com.google.android.material.textfield.TextInputLayout
|
||||||
import android.support.v7.app.AlertDialog
|
import androidx.fragment.app.DialogFragment
|
||||||
|
import androidx.appcompat.app.AlertDialog
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.widget.*
|
import android.widget.Button
|
||||||
|
import android.widget.CompoundButton
|
||||||
|
import android.widget.EditText
|
||||||
|
import android.widget.SeekBar
|
||||||
import com.kunzisoft.keepass.R
|
import com.kunzisoft.keepass.R
|
||||||
import com.kunzisoft.keepass.password.PasswordGenerator
|
import com.kunzisoft.keepass.password.PasswordGenerator
|
||||||
import com.kunzisoft.keepass.settings.PreferencesUtil
|
import com.kunzisoft.keepass.settings.PreferencesUtil
|
||||||
import com.kunzisoft.keepass.utils.applyFontVisibility
|
import com.kunzisoft.keepass.view.applyFontVisibility
|
||||||
|
|
||||||
class GeneratePasswordDialogFragment : DialogFragment() {
|
class GeneratePasswordDialogFragment : DialogFragment() {
|
||||||
|
|
||||||
@@ -37,6 +41,7 @@ class GeneratePasswordDialogFragment : DialogFragment() {
|
|||||||
|
|
||||||
private var root: View? = null
|
private var root: View? = null
|
||||||
private var lengthTextView: EditText? = null
|
private var lengthTextView: EditText? = null
|
||||||
|
private var passwordInputLayoutView: TextInputLayout? = null
|
||||||
private var passwordView: EditText? = null
|
private var passwordView: EditText? = null
|
||||||
|
|
||||||
private var uppercaseBox: CompoundButton? = null
|
private var uppercaseBox: CompoundButton? = null
|
||||||
@@ -65,6 +70,7 @@ class GeneratePasswordDialogFragment : DialogFragment() {
|
|||||||
val inflater = activity.layoutInflater
|
val inflater = activity.layoutInflater
|
||||||
root = inflater.inflate(R.layout.fragment_generate_password, null)
|
root = inflater.inflate(R.layout.fragment_generate_password, null)
|
||||||
|
|
||||||
|
passwordInputLayoutView = root?.findViewById(R.id.password_input_layout)
|
||||||
passwordView = root?.findViewById(R.id.password)
|
passwordView = root?.findViewById(R.id.password)
|
||||||
passwordView?.applyFontVisibility()
|
passwordView?.applyFontVisibility()
|
||||||
|
|
||||||
@@ -162,8 +168,7 @@ class GeneratePasswordDialogFragment : DialogFragment() {
|
|||||||
try {
|
try {
|
||||||
val length = Integer.valueOf(root?.findViewById<EditText>(R.id.length)?.text.toString())
|
val length = Integer.valueOf(root?.findViewById<EditText>(R.id.length)?.text.toString())
|
||||||
|
|
||||||
val generator = PasswordGenerator(resources)
|
password = PasswordGenerator(resources).generatePassword(length,
|
||||||
password = generator.generatePassword(length,
|
|
||||||
uppercaseBox?.isChecked == true,
|
uppercaseBox?.isChecked == true,
|
||||||
lowercaseBox?.isChecked == true,
|
lowercaseBox?.isChecked == true,
|
||||||
digitsBox?.isChecked == true,
|
digitsBox?.isChecked == true,
|
||||||
@@ -174,9 +179,9 @@ class GeneratePasswordDialogFragment : DialogFragment() {
|
|||||||
bracketsBox?.isChecked == true,
|
bracketsBox?.isChecked == true,
|
||||||
extendedBox?.isChecked == true)
|
extendedBox?.isChecked == true)
|
||||||
} catch (e: NumberFormatException) {
|
} catch (e: NumberFormatException) {
|
||||||
Toast.makeText(context, R.string.error_wrong_length, Toast.LENGTH_LONG).show()
|
passwordInputLayoutView?.error = getString(R.string.error_wrong_length)
|
||||||
} catch (e: IllegalArgumentException) {
|
} catch (e: IllegalArgumentException) {
|
||||||
Toast.makeText(context, e.message, Toast.LENGTH_LONG).show()
|
passwordInputLayoutView?.error = e.message
|
||||||
}
|
}
|
||||||
|
|
||||||
return password
|
return password
|
||||||
|
|||||||
@@ -23,9 +23,9 @@ import android.app.Dialog
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.graphics.Color
|
import android.graphics.Color
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.support.design.widget.TextInputLayout
|
import com.google.android.material.textfield.TextInputLayout
|
||||||
import android.support.v4.app.DialogFragment
|
import androidx.fragment.app.DialogFragment
|
||||||
import android.support.v7.app.AlertDialog
|
import androidx.appcompat.app.AlertDialog
|
||||||
import android.widget.Button
|
import android.widget.Button
|
||||||
import android.widget.ImageView
|
import android.widget.ImageView
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
|
|||||||
@@ -24,9 +24,9 @@ import android.content.Context
|
|||||||
import android.content.res.ColorStateList
|
import android.content.res.ColorStateList
|
||||||
import android.graphics.Color
|
import android.graphics.Color
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.support.v4.app.DialogFragment
|
import androidx.fragment.app.DialogFragment
|
||||||
import android.support.v4.widget.ImageViewCompat
|
import androidx.core.widget.ImageViewCompat
|
||||||
import android.support.v7.app.AlertDialog
|
import androidx.appcompat.app.AlertDialog
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
|
|||||||
@@ -24,12 +24,12 @@ import android.content.Intent
|
|||||||
import android.content.Intent.FLAG_ACTIVITY_NEW_TASK
|
import android.content.Intent.FLAG_ACTIVITY_NEW_TASK
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.provider.Settings
|
import android.provider.Settings
|
||||||
import android.support.v4.app.DialogFragment
|
import androidx.fragment.app.DialogFragment
|
||||||
import android.support.v7.app.AlertDialog
|
import androidx.appcompat.app.AlertDialog
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import com.kunzisoft.keepass.BuildConfig
|
import com.kunzisoft.keepass.BuildConfig
|
||||||
import com.kunzisoft.keepass.R
|
import com.kunzisoft.keepass.R
|
||||||
import com.kunzisoft.keepass.utils.Util
|
import com.kunzisoft.keepass.utils.UriUtil
|
||||||
|
|
||||||
class KeyboardExplanationDialogFragment : DialogFragment() {
|
class KeyboardExplanationDialogFragment : DialogFragment() {
|
||||||
|
|
||||||
@@ -47,9 +47,9 @@ class KeyboardExplanationDialogFragment : DialogFragment() {
|
|||||||
|
|
||||||
val containerKeyboardSwitcher = rootView.findViewById<View>(R.id.container_keyboard_switcher)
|
val containerKeyboardSwitcher = rootView.findViewById<View>(R.id.container_keyboard_switcher)
|
||||||
if (BuildConfig.CLOSED_STORE) {
|
if (BuildConfig.CLOSED_STORE) {
|
||||||
containerKeyboardSwitcher.setOnClickListener { Util.gotoUrl(context!!, R.string.keyboard_switcher_play_store) }
|
containerKeyboardSwitcher.setOnClickListener { UriUtil.gotoUrl(context!!, R.string.keyboard_switcher_play_store) }
|
||||||
} else {
|
} else {
|
||||||
containerKeyboardSwitcher.setOnClickListener { Util.gotoUrl(context!!, R.string.keyboard_switcher_f_droid) }
|
containerKeyboardSwitcher.setOnClickListener { UriUtil.gotoUrl(context!!, R.string.keyboard_switcher_f_droid) }
|
||||||
}
|
}
|
||||||
|
|
||||||
builder.setView(rootView)
|
builder.setView(rootView)
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ import android.app.AlertDialog
|
|||||||
import android.app.Dialog
|
import android.app.Dialog
|
||||||
import android.content.DialogInterface
|
import android.content.DialogInterface
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.support.v4.app.DialogFragment
|
import androidx.fragment.app.DialogFragment
|
||||||
import com.kunzisoft.keepass.R
|
import com.kunzisoft.keepass.R
|
||||||
|
|
||||||
class PasswordEncodingDialogFragment : DialogFragment() {
|
class PasswordEncodingDialogFragment : DialogFragment() {
|
||||||
|
|||||||
@@ -20,17 +20,15 @@
|
|||||||
package com.kunzisoft.keepass.activities.dialogs
|
package com.kunzisoft.keepass.activities.dialogs
|
||||||
|
|
||||||
import android.app.Dialog
|
import android.app.Dialog
|
||||||
import android.content.ActivityNotFoundException
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.support.v4.app.DialogFragment
|
|
||||||
import android.support.v7.app.AlertDialog
|
|
||||||
import android.text.Html
|
|
||||||
import android.text.SpannableStringBuilder
|
import android.text.SpannableStringBuilder
|
||||||
import android.widget.Toast
|
import androidx.appcompat.app.AlertDialog
|
||||||
|
import androidx.core.text.HtmlCompat
|
||||||
|
import androidx.core.text.HtmlCompat.FROM_HTML_MODE_LEGACY
|
||||||
|
import androidx.fragment.app.DialogFragment
|
||||||
import com.kunzisoft.keepass.BuildConfig
|
import com.kunzisoft.keepass.BuildConfig
|
||||||
import com.kunzisoft.keepass.R
|
import com.kunzisoft.keepass.R
|
||||||
import com.kunzisoft.keepass.utils.Util
|
import com.kunzisoft.keepass.utils.UriUtil
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Custom Dialog that asks the user to download the pro version or make a donation.
|
* Custom Dialog that asks the user to download the pro version or make a donation.
|
||||||
@@ -44,25 +42,16 @@ class ProFeatureDialogFragment : DialogFragment() {
|
|||||||
|
|
||||||
val stringBuilder = SpannableStringBuilder()
|
val stringBuilder = SpannableStringBuilder()
|
||||||
if (BuildConfig.CLOSED_STORE) {
|
if (BuildConfig.CLOSED_STORE) {
|
||||||
// TODO HtmlCompat with androidX
|
stringBuilder.append(HtmlCompat.fromHtml(getString(R.string.html_text_ad_free), FROM_HTML_MODE_LEGACY)).append("\n\n")
|
||||||
stringBuilder.append(Html.fromHtml(getString(R.string.html_text_ad_free))).append("\n\n")
|
stringBuilder.append(HtmlCompat.fromHtml(getString(R.string.html_text_buy_pro), FROM_HTML_MODE_LEGACY))
|
||||||
stringBuilder.append(Html.fromHtml(getString(R.string.html_text_buy_pro)))
|
|
||||||
builder.setPositiveButton(R.string.download) { _, _ ->
|
builder.setPositiveButton(R.string.download) { _, _ ->
|
||||||
try {
|
UriUtil.gotoUrl(context!!, R.string.app_pro_url)
|
||||||
Util.gotoUrl(context!!, R.string.app_pro_url)
|
|
||||||
} catch (e: ActivityNotFoundException) {
|
|
||||||
Toast.makeText(context, R.string.error_failed_to_launch_link, Toast.LENGTH_LONG).show()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
stringBuilder.append(Html.fromHtml(getString(R.string.html_text_feature_generosity))).append("\n\n")
|
stringBuilder.append(HtmlCompat.fromHtml(getString(R.string.html_text_feature_generosity), FROM_HTML_MODE_LEGACY)).append("\n\n")
|
||||||
stringBuilder.append(Html.fromHtml(getString(R.string.html_text_donation)))
|
stringBuilder.append(HtmlCompat.fromHtml(getString(R.string.html_text_donation), FROM_HTML_MODE_LEGACY))
|
||||||
builder.setPositiveButton(R.string.contribute) { _, _ ->
|
builder.setPositiveButton(R.string.contribute) { _, _ ->
|
||||||
try {
|
UriUtil.gotoUrl(context!!, R.string.contribution_url)
|
||||||
Util.gotoUrl(context!!, R.string.contribution_url)
|
|
||||||
} catch (e: ActivityNotFoundException) {
|
|
||||||
Toast.makeText(context, R.string.error_failed_to_launch_link, Toast.LENGTH_LONG).show()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
builder.setMessage(stringBuilder)
|
builder.setMessage(stringBuilder)
|
||||||
|
|||||||
@@ -19,33 +19,38 @@
|
|||||||
*/
|
*/
|
||||||
package com.kunzisoft.keepass.activities.dialogs
|
package com.kunzisoft.keepass.activities.dialogs
|
||||||
|
|
||||||
import android.app.AlertDialog
|
import android.app.Dialog
|
||||||
import android.content.Context
|
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.preference.PreferenceManager
|
import android.preference.PreferenceManager
|
||||||
|
import androidx.fragment.app.DialogFragment
|
||||||
import com.kunzisoft.keepass.R
|
import com.kunzisoft.keepass.R
|
||||||
|
|
||||||
class ReadOnlyDialog(context: Context) : AlertDialog(context) {
|
class ReadOnlyDialog : DialogFragment() {
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle) {
|
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||||
val ctx = context
|
activity?.let { activity ->
|
||||||
var warning = ctx.getString(R.string.read_only_warning)
|
// Use the Builder class for convenient dialog construction
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
val builder = androidx.appcompat.app.AlertDialog.Builder(activity)
|
||||||
warning = warning + "\n\n" + context.getString(R.string.read_only_kitkat_warning)
|
|
||||||
|
var warning = getString(R.string.read_only_warning)
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
||||||
|
warning = warning + "\n\n" + getString(R.string.read_only_kitkat_warning)
|
||||||
|
}
|
||||||
|
builder.setMessage(warning)
|
||||||
|
|
||||||
|
builder.setPositiveButton(getString(android.R.string.ok)) { _, _ -> dismiss() }
|
||||||
|
builder.setNegativeButton(getString(R.string.beta_dontask)) { _, _ ->
|
||||||
|
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
|
||||||
|
val edit = prefs.edit()
|
||||||
|
edit.putBoolean(getString(R.string.show_read_only_warning), false)
|
||||||
|
edit.apply()
|
||||||
|
dismiss()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the AlertDialog object and return it
|
||||||
|
return builder.create()
|
||||||
}
|
}
|
||||||
setMessage(warning)
|
return super.onCreateDialog(savedInstanceState)
|
||||||
|
|
||||||
setButton(BUTTON_POSITIVE, ctx.getText(android.R.string.ok)) { _, _ -> dismiss() }
|
|
||||||
setButton(BUTTON_NEGATIVE, ctx.getText(R.string.beta_dontask)) { _, _ ->
|
|
||||||
val prefs = PreferenceManager.getDefaultSharedPreferences(ctx)
|
|
||||||
val edit = prefs.edit()
|
|
||||||
edit.putBoolean(ctx.getString(R.string.show_read_only_warning), false)
|
|
||||||
edit.apply()
|
|
||||||
dismiss()
|
|
||||||
}
|
|
||||||
|
|
||||||
super.onCreate(savedInstanceState)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,9 +22,9 @@ package com.kunzisoft.keepass.activities.dialogs
|
|||||||
import android.app.Dialog
|
import android.app.Dialog
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.support.annotation.IdRes
|
import androidx.annotation.IdRes
|
||||||
import android.support.v4.app.DialogFragment
|
import androidx.fragment.app.DialogFragment
|
||||||
import android.support.v7.app.AlertDialog
|
import androidx.appcompat.app.AlertDialog
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.widget.CompoundButton
|
import android.widget.CompoundButton
|
||||||
import android.widget.RadioGroup
|
import android.widget.RadioGroup
|
||||||
|
|||||||
@@ -22,12 +22,13 @@ package com.kunzisoft.keepass.activities.dialogs
|
|||||||
import android.app.Dialog
|
import android.app.Dialog
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.support.v4.app.DialogFragment
|
import androidx.fragment.app.DialogFragment
|
||||||
import android.support.v7.app.AlertDialog
|
import androidx.appcompat.app.AlertDialog
|
||||||
import android.text.Html
|
import android.text.Html
|
||||||
import android.text.SpannableStringBuilder
|
import android.text.SpannableStringBuilder
|
||||||
import android.text.method.LinkMovementMethod
|
import android.text.method.LinkMovementMethod
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
|
import androidx.core.text.HtmlCompat
|
||||||
import com.kunzisoft.keepass.R
|
import com.kunzisoft.keepass.R
|
||||||
|
|
||||||
class UnavailableFeatureDialogFragment : DialogFragment() {
|
class UnavailableFeatureDialogFragment : DialogFragment() {
|
||||||
@@ -53,7 +54,7 @@ class UnavailableFeatureDialogFragment : DialogFragment() {
|
|||||||
androidNameFromApiNumber(Build.VERSION.SDK_INT, Build.VERSION.RELEASE),
|
androidNameFromApiNumber(Build.VERSION.SDK_INT, Build.VERSION.RELEASE),
|
||||||
androidNameFromApiNumber(minVersionRequired)))
|
androidNameFromApiNumber(minVersionRequired)))
|
||||||
message.append("\n\n")
|
message.append("\n\n")
|
||||||
.append(Html.fromHtml("<a href=\"https://source.android.com/setup/build-numbers\">CodeNames</a>"))
|
.append(HtmlCompat.fromHtml("<a href=\"https://source.android.com/setup/build-numbers\">CodeNames</a>", HtmlCompat.FROM_HTML_MODE_LEGACY))
|
||||||
} else
|
} else
|
||||||
message.append(getString(R.string.unavailable_feature_hardware))
|
message.append(getString(R.string.unavailable_feature_hardware))
|
||||||
|
|
||||||
|
|||||||
@@ -20,17 +20,14 @@
|
|||||||
package com.kunzisoft.keepass.activities.dialogs
|
package com.kunzisoft.keepass.activities.dialogs
|
||||||
|
|
||||||
import android.app.Dialog
|
import android.app.Dialog
|
||||||
import android.content.ActivityNotFoundException
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.support.v4.app.DialogFragment
|
|
||||||
import android.support.v7.app.AlertDialog
|
|
||||||
import android.text.Html
|
|
||||||
import android.text.SpannableStringBuilder
|
import android.text.SpannableStringBuilder
|
||||||
import android.widget.Toast
|
import androidx.appcompat.app.AlertDialog
|
||||||
|
import androidx.core.text.HtmlCompat
|
||||||
|
import androidx.fragment.app.DialogFragment
|
||||||
import com.kunzisoft.keepass.BuildConfig
|
import com.kunzisoft.keepass.BuildConfig
|
||||||
import com.kunzisoft.keepass.R
|
import com.kunzisoft.keepass.R
|
||||||
import com.kunzisoft.keepass.utils.Util
|
import com.kunzisoft.keepass.utils.UriUtil
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Custom Dialog that asks the user to download the pro version or make a donation.
|
* Custom Dialog that asks the user to download the pro version or make a donation.
|
||||||
@@ -45,34 +42,26 @@ class UnderDevelopmentFeatureDialogFragment : DialogFragment() {
|
|||||||
val stringBuilder = SpannableStringBuilder()
|
val stringBuilder = SpannableStringBuilder()
|
||||||
if (BuildConfig.CLOSED_STORE) {
|
if (BuildConfig.CLOSED_STORE) {
|
||||||
if (BuildConfig.FULL_VERSION) {
|
if (BuildConfig.FULL_VERSION) {
|
||||||
stringBuilder.append(Html.fromHtml(getString(R.string.html_text_dev_feature_thanks))).append("\n\n")
|
stringBuilder.append(HtmlCompat.fromHtml(getString(R.string.html_text_dev_feature_thanks), HtmlCompat.FROM_HTML_MODE_LEGACY)).append("\n\n")
|
||||||
.append(Html.fromHtml(getString(R.string.html_rose))).append("\n\n")
|
.append(HtmlCompat.fromHtml(getString(R.string.html_rose), HtmlCompat.FROM_HTML_MODE_LEGACY)).append("\n\n")
|
||||||
.append(Html.fromHtml(getString(R.string.html_text_dev_feature_work_hard))).append("\n")
|
.append(HtmlCompat.fromHtml(getString(R.string.html_text_dev_feature_work_hard), HtmlCompat.FROM_HTML_MODE_LEGACY)).append("\n")
|
||||||
.append(Html.fromHtml(getString(R.string.html_text_dev_feature_upgrade))).append(" ")
|
.append(HtmlCompat.fromHtml(getString(R.string.html_text_dev_feature_upgrade), HtmlCompat.FROM_HTML_MODE_LEGACY)).append(" ")
|
||||||
builder.setPositiveButton(android.R.string.ok) { _, _ -> dismiss() }
|
builder.setPositiveButton(android.R.string.ok) { _, _ -> dismiss() }
|
||||||
} else {
|
} else {
|
||||||
stringBuilder.append(Html.fromHtml(getString(R.string.html_text_dev_feature))).append("\n\n")
|
stringBuilder.append(HtmlCompat.fromHtml(getString(R.string.html_text_dev_feature), HtmlCompat.FROM_HTML_MODE_LEGACY)).append("\n\n")
|
||||||
.append(Html.fromHtml(getString(R.string.html_text_dev_feature_buy_pro))).append("\n")
|
.append(HtmlCompat.fromHtml(getString(R.string.html_text_dev_feature_buy_pro), HtmlCompat.FROM_HTML_MODE_LEGACY)).append("\n")
|
||||||
.append(Html.fromHtml(getString(R.string.html_text_dev_feature_encourage)))
|
.append(HtmlCompat.fromHtml(getString(R.string.html_text_dev_feature_encourage), HtmlCompat.FROM_HTML_MODE_LEGACY))
|
||||||
builder.setPositiveButton(R.string.download) { _, _ ->
|
builder.setPositiveButton(R.string.download) { _, _ ->
|
||||||
try {
|
UriUtil.gotoUrl(context!!, R.string.app_pro_url)
|
||||||
Util.gotoUrl(context!!, R.string.app_pro_url)
|
|
||||||
} catch (e: ActivityNotFoundException) {
|
|
||||||
Toast.makeText(context, R.string.error_failed_to_launch_link, Toast.LENGTH_LONG).show()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
builder.setNegativeButton(android.R.string.cancel) { _, _ -> dismiss() }
|
builder.setNegativeButton(android.R.string.cancel) { _, _ -> dismiss() }
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
stringBuilder.append(Html.fromHtml(getString(R.string.html_text_dev_feature))).append("\n\n")
|
stringBuilder.append(HtmlCompat.fromHtml(getString(R.string.html_text_dev_feature), HtmlCompat.FROM_HTML_MODE_LEGACY)).append("\n\n")
|
||||||
.append(Html.fromHtml(getString(R.string.html_text_dev_feature_contibute))).append(" ")
|
.append(HtmlCompat.fromHtml(getString(R.string.html_text_dev_feature_contibute), HtmlCompat.FROM_HTML_MODE_LEGACY)).append(" ")
|
||||||
.append(Html.fromHtml(getString(R.string.html_text_dev_feature_encourage)))
|
.append(HtmlCompat.fromHtml(getString(R.string.html_text_dev_feature_encourage), HtmlCompat.FROM_HTML_MODE_LEGACY))
|
||||||
builder.setPositiveButton(R.string.contribute) { _, _ ->
|
builder.setPositiveButton(R.string.contribute) { _, _ ->
|
||||||
try {
|
UriUtil.gotoUrl(context!!, R.string.contribution_url)
|
||||||
Util.gotoUrl(context!!, R.string.contribution_url)
|
|
||||||
} catch (e: ActivityNotFoundException) {
|
|
||||||
Toast.makeText(context, R.string.error_failed_to_launch_link, Toast.LENGTH_LONG).show()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
builder.setNegativeButton(android.R.string.cancel) { _, _ -> dismiss() }
|
builder.setNegativeButton(android.R.string.cancel) { _, _ -> dismiss() }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,15 +26,14 @@ import android.content.Intent
|
|||||||
import android.content.pm.PackageManager
|
import android.content.pm.PackageManager
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.support.v4.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import android.support.v4.app.FragmentActivity
|
import androidx.fragment.app.FragmentActivity
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import com.kunzisoft.keepass.activities.dialogs.BrowserDialogFragment
|
import com.kunzisoft.keepass.activities.dialogs.BrowserDialogFragment
|
||||||
import com.kunzisoft.keepass.fileselect.StorageAF
|
|
||||||
import com.kunzisoft.keepass.utils.UriUtil
|
import com.kunzisoft.keepass.utils.UriUtil
|
||||||
|
|
||||||
class KeyFileHelper {
|
class OpenFileHelper {
|
||||||
|
|
||||||
private var activity: Activity? = null
|
private var activity: Activity? = null
|
||||||
private var fragment: Fragment? = null
|
private var fragment: Fragment? = null
|
||||||
@@ -56,9 +55,9 @@ class KeyFileHelper {
|
|||||||
|
|
||||||
override fun onClick(v: View) {
|
override fun onClick(v: View) {
|
||||||
try {
|
try {
|
||||||
if (activity != null && StorageAF.useStorageFramework(activity!!)) {
|
try {
|
||||||
openActivityWithActionOpenDocument()
|
openActivityWithActionOpenDocument()
|
||||||
} else {
|
} catch(e: Exception) {
|
||||||
openActivityWithActionGetContent()
|
openActivityWithActionGetContent()
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
@@ -72,7 +71,7 @@ class KeyFileHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun openActivityWithActionOpenDocument() {
|
private fun openActivityWithActionOpenDocument() {
|
||||||
val i = Intent(StorageAF.ACTION_OPEN_DOCUMENT)
|
val i = Intent(ACTION_OPEN_DOCUMENT)
|
||||||
i.addCategory(Intent.CATEGORY_OPENABLE)
|
i.addCategory(Intent.CATEGORY_OPENABLE)
|
||||||
i.type = "*/*"
|
i.type = "*/*"
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
||||||
@@ -158,7 +157,7 @@ class KeyFileHelper {
|
|||||||
val browserDialogFragment = BrowserDialogFragment()
|
val browserDialogFragment = BrowserDialogFragment()
|
||||||
if (fragment != null && fragment!!.fragmentManager != null)
|
if (fragment != null && fragment!!.fragmentManager != null)
|
||||||
browserDialogFragment.show(fragment!!.fragmentManager!!, "browserDialog")
|
browserDialogFragment.show(fragment!!.fragmentManager!!, "browserDialog")
|
||||||
else if (activity!!.fragmentManager != null)
|
else if ((activity as FragmentActivity).supportFragmentManager != null)
|
||||||
browserDialogFragment.show((activity as FragmentActivity).supportFragmentManager, "browserDialog")
|
browserDialogFragment.show((activity as FragmentActivity).supportFragmentManager, "browserDialog")
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Log.e(TAG, "Can't open BrowserDialog", e)
|
Log.e(TAG, "Can't open BrowserDialog", e)
|
||||||
@@ -193,18 +192,16 @@ class KeyFileHelper {
|
|||||||
if (data != null) {
|
if (data != null) {
|
||||||
var uri = data.data
|
var uri = data.data
|
||||||
if (uri != null) {
|
if (uri != null) {
|
||||||
if (StorageAF.useStorageFramework(activity!!)) {
|
try {
|
||||||
try {
|
// try to persist read and write permissions
|
||||||
// try to persist read and write permissions
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
activity?.contentResolver?.apply {
|
||||||
activity?.contentResolver?.apply {
|
takePersistableUriPermission(uri!!, Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||||
takePersistableUriPermission(uri!!, Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
takePersistableUriPermission(uri!!, Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
|
||||||
takePersistableUriPermission(uri!!, Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
|
||||||
// nop
|
|
||||||
}
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
// nop
|
||||||
}
|
}
|
||||||
if (requestCode == GET_CONTENT) {
|
if (requestCode == GET_CONTENT) {
|
||||||
uri = UriUtil.translateUri(activity!!, uri)
|
uri = UriUtil.translateUri(activity!!, uri)
|
||||||
@@ -221,7 +218,18 @@ class KeyFileHelper {
|
|||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
private const val TAG = "KeyFileHelper"
|
private const val TAG = "OpenFileHelper"
|
||||||
|
|
||||||
|
private var ACTION_OPEN_DOCUMENT: String
|
||||||
|
|
||||||
|
init {
|
||||||
|
ACTION_OPEN_DOCUMENT = try {
|
||||||
|
val openDocument = Intent::class.java.getField("ACTION_OPEN_DOCUMENT")
|
||||||
|
openDocument.get(null) as String
|
||||||
|
} catch (e: Exception) {
|
||||||
|
"android.intent.action.OPEN_DOCUMENT"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const val OPEN_INTENTS_FILE_BROWSE = "org.openintents.action.PICK_FILE"
|
const val OPEN_INTENTS_FILE_BROWSE = "org.openintents.action.PICK_FILE"
|
||||||
|
|
||||||
@@ -1,88 +0,0 @@
|
|||||||
package com.kunzisoft.keepass.activities.helpers
|
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import android.content.Intent
|
|
||||||
import android.net.Uri
|
|
||||||
import android.os.AsyncTask
|
|
||||||
|
|
||||||
import com.kunzisoft.keepass.R
|
|
||||||
import com.kunzisoft.keepass.fileselect.database.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)
|
|
||||||
: AsyncTask<Intent, Void, Int>() {
|
|
||||||
|
|
||||||
private var databaseUri: Uri? = null
|
|
||||||
private var keyFileUri: Uri? = null
|
|
||||||
|
|
||||||
override fun doInBackground(vararg args: Intent): Int? {
|
|
||||||
val intent = args[0]
|
|
||||||
val action = intent.action
|
|
||||||
|
|
||||||
// If is a view intent
|
|
||||||
if (action != null && action == VIEW_INTENT) {
|
|
||||||
val incoming = intent.data
|
|
||||||
databaseUri = incoming
|
|
||||||
keyFileUri = ClipDataCompat.getUriFromIntent(intent, KEY_KEYFILE)
|
|
||||||
|
|
||||||
if (incoming == null) {
|
|
||||||
return R.string.error_can_not_handle_uri
|
|
||||||
} else if (incoming.scheme == "file") {
|
|
||||||
val fileName = incoming.path
|
|
||||||
|
|
||||||
if (fileName?.isNotEmpty() == true) {
|
|
||||||
// No file name
|
|
||||||
return R.string.file_not_found
|
|
||||||
}
|
|
||||||
|
|
||||||
val dbFile = File(fileName)
|
|
||||||
if (!dbFile.exists()) {
|
|
||||||
// File does not exist
|
|
||||||
return R.string.file_not_found
|
|
||||||
}
|
|
||||||
|
|
||||||
if (keyFileUri == null) {
|
|
||||||
keyFileUri = getKeyFileUri(databaseUri)
|
|
||||||
}
|
|
||||||
} else if (incoming.scheme == "content") {
|
|
||||||
if (keyFileUri == null) {
|
|
||||||
keyFileUri = getKeyFileUri(databaseUri)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return R.string.error_can_not_handle_uri
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
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!!)
|
|
||||||
} else {
|
|
||||||
null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
const val KEY_FILENAME = "fileName"
|
|
||||||
const val KEY_KEYFILE = "keyFile"
|
|
||||||
private const val VIEW_INTENT = "android.intent.action.VIEW"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,26 +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.activities.helpers
|
|
||||||
|
|
||||||
import android.net.Uri
|
|
||||||
|
|
||||||
interface UriIntentInitTaskCallback {
|
|
||||||
fun onPostInitTask(databaseFileUri: Uri?, keyFileUri: Uri?, errorStringId: Int?)
|
|
||||||
}
|
|
||||||
@@ -1,49 +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.activities.stylish
|
|
||||||
|
|
||||||
import android.os.Bundle
|
|
||||||
import android.support.annotation.StyleRes
|
|
||||||
import android.util.Log
|
|
||||||
|
|
||||||
import com.nononsenseapps.filepicker.FilePickerActivity
|
|
||||||
|
|
||||||
/**
|
|
||||||
* FilePickerActivity class with a style compatibility
|
|
||||||
*/
|
|
||||||
class FilePickerStylishActivity : FilePickerActivity() {
|
|
||||||
|
|
||||||
@StyleRes
|
|
||||||
private var themeId: Int = 0
|
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
|
||||||
this.themeId = Stylish.getFilePickerThemeId(this)
|
|
||||||
setTheme(themeId)
|
|
||||||
super.onCreate(savedInstanceState)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onResume() {
|
|
||||||
super.onResume()
|
|
||||||
if (Stylish.getFilePickerThemeId(this) != this.themeId) {
|
|
||||||
Log.d(this.javaClass.name, "Theme change detected, restarting activity")
|
|
||||||
this.recreate()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -20,8 +20,8 @@
|
|||||||
package com.kunzisoft.keepass.activities.stylish
|
package com.kunzisoft.keepass.activities.stylish
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.support.annotation.StyleRes
|
import androidx.annotation.StyleRes
|
||||||
import android.support.v7.preference.PreferenceManager
|
import androidx.preference.PreferenceManager
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
|
|
||||||
import com.kunzisoft.keepass.R
|
import com.kunzisoft.keepass.R
|
||||||
@@ -68,15 +68,4 @@ object Stylish {
|
|||||||
else -> R.style.KeepassDXStyle_Light
|
else -> R.style.KeepassDXStyle_Light
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@StyleRes
|
|
||||||
fun getFilePickerThemeId(context: Context): Int {
|
|
||||||
return when {
|
|
||||||
themeString.equals(context.getString(R.string.list_style_name_dark)) -> R.style.KeepassDXStyle_FilePickerStyle_Dark
|
|
||||||
themeString.equals(context.getString(R.string.list_style_name_blue)) -> R.style.KeepassDXStyle_FilePickerStyle_Blue
|
|
||||||
themeString.equals(context.getString(R.string.list_style_name_red)) -> R.style.KeepassDXStyle_FilePickerStyle_Red
|
|
||||||
themeString.equals(context.getString(R.string.list_style_name_purple)) -> R.style.KeepassDXStyle_FilePickerStyle_Purple
|
|
||||||
else -> R.style.KeepassDXStyle_FilePickerStyle
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,8 +20,8 @@
|
|||||||
package com.kunzisoft.keepass.activities.stylish
|
package com.kunzisoft.keepass.activities.stylish
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.support.annotation.StyleRes
|
import androidx.annotation.StyleRes
|
||||||
import android.support.v7.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
|
|
||||||
abstract class StylishActivity : AppCompatActivity() {
|
abstract class StylishActivity : AppCompatActivity() {
|
||||||
|
|||||||
@@ -23,9 +23,9 @@ import android.content.Context
|
|||||||
import android.graphics.Color
|
import android.graphics.Color
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.support.annotation.StyleRes
|
import androidx.annotation.StyleRes
|
||||||
import android.support.v4.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import android.support.v7.view.ContextThemeWrapper
|
import androidx.appcompat.view.ContextThemeWrapper
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package com.kunzisoft.keepass.magikeyboard.adapter
|
package com.kunzisoft.keepass.adapters
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.support.v7.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
@@ -21,23 +21,30 @@ package com.kunzisoft.keepass.adapters
|
|||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.support.annotation.ColorInt
|
import androidx.annotation.ColorInt
|
||||||
import android.support.v7.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import android.util.TypedValue
|
import android.util.TypedValue
|
||||||
import android.view.*
|
import android.view.*
|
||||||
|
import android.widget.EditText
|
||||||
import android.widget.ImageView
|
import android.widget.ImageView
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
|
import android.widget.ViewSwitcher
|
||||||
import com.kunzisoft.keepass.R
|
import com.kunzisoft.keepass.R
|
||||||
import com.kunzisoft.keepass.fileselect.FileDatabaseModel
|
import com.kunzisoft.keepass.app.database.FileDatabaseHistoryEntity
|
||||||
import com.kunzisoft.keepass.settings.PreferencesUtil
|
import com.kunzisoft.keepass.utils.FileDatabaseInfo
|
||||||
|
|
||||||
class FileDatabaseHistoryAdapter(private val context: Context, private val listFiles: List<String>)
|
class FileDatabaseHistoryAdapter(private val context: Context)
|
||||||
: RecyclerView.Adapter<FileDatabaseHistoryAdapter.FileDatabaseHistoryViewHolder>() {
|
: RecyclerView.Adapter<FileDatabaseHistoryAdapter.FileDatabaseHistoryViewHolder>() {
|
||||||
|
|
||||||
private val inflater: LayoutInflater = LayoutInflater.from(context)
|
private val inflater: LayoutInflater = LayoutInflater.from(context)
|
||||||
private var fileItemOpenListener: FileItemOpenListener? = null
|
private var fileItemOpenListener: ((FileDatabaseHistoryEntity)->Unit)? = null
|
||||||
private var fileSelectClearListener: FileSelectClearListener? = null
|
private var fileSelectClearListener: ((FileDatabaseHistoryEntity)->Boolean)? = null
|
||||||
private var fileInformationShowListener: FileInformationShowListener? = null
|
private var saveAliasListener: ((FileDatabaseHistoryEntity)->Unit)? = null
|
||||||
|
|
||||||
|
private val listDatabaseFiles = ArrayList<FileDatabaseHistoryEntity>()
|
||||||
|
|
||||||
|
private var mExpandedPosition = -1
|
||||||
|
private var mPreviousExpandedPosition = -1
|
||||||
|
|
||||||
@ColorInt
|
@ColorInt
|
||||||
private val defaultColor: Int
|
private val defaultColor: Int
|
||||||
@@ -45,7 +52,6 @@ class FileDatabaseHistoryAdapter(private val context: Context, private val listF
|
|||||||
private val warningColor: Int
|
private val warningColor: Int
|
||||||
|
|
||||||
init {
|
init {
|
||||||
|
|
||||||
val typedValue = TypedValue()
|
val typedValue = TypedValue()
|
||||||
val theme = context.theme
|
val theme = context.theme
|
||||||
theme.resolveAttribute(R.attr.colorAccent, typedValue, true)
|
theme.resolveAttribute(R.attr.colorAccent, typedValue, true)
|
||||||
@@ -60,91 +66,120 @@ class FileDatabaseHistoryAdapter(private val context: Context, private val listF
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onBindViewHolder(holder: FileDatabaseHistoryViewHolder, position: Int) {
|
override fun onBindViewHolder(holder: FileDatabaseHistoryViewHolder, position: Int) {
|
||||||
val fileDatabaseModel = FileDatabaseModel(context, listFiles[position])
|
// Get info from position
|
||||||
// Context menu creation
|
val fileHistoryEntity = listDatabaseFiles[position]
|
||||||
holder.fileContainer.setOnCreateContextMenuListener(ContextMenuBuilder(fileDatabaseModel))
|
val fileDatabaseInfo = FileDatabaseInfo(context, fileHistoryEntity.databaseUri)
|
||||||
|
|
||||||
// Click item to open file
|
// Click item to open file
|
||||||
if (fileItemOpenListener != null)
|
if (fileItemOpenListener != null)
|
||||||
holder.fileContainer.setOnClickListener(FileItemClickListener(position))
|
holder.fileContainer.setOnClickListener {
|
||||||
// Assign file name
|
fileItemOpenListener?.invoke(fileHistoryEntity)
|
||||||
if (PreferencesUtil.isFullFilePathEnable(context))
|
}
|
||||||
holder.fileName.text = Uri.decode(fileDatabaseModel.fileUri.toString())
|
|
||||||
else
|
// File alias
|
||||||
holder.fileName.text = fileDatabaseModel.fileName
|
holder.fileAlias.text = fileDatabaseInfo.retrieveDatabaseAlias(fileHistoryEntity.databaseAlias)
|
||||||
holder.fileName.textSize = PreferencesUtil.getListTextSize(context)
|
|
||||||
|
// File path
|
||||||
|
holder.filePath.text = Uri.decode(fileDatabaseInfo.fileUri.toString())
|
||||||
|
|
||||||
|
holder.filePreciseInfoContainer.visibility = if (fileDatabaseInfo.found()) {
|
||||||
|
// Modification
|
||||||
|
holder.fileModification.text = fileDatabaseInfo.getModificationString()
|
||||||
|
// Size
|
||||||
|
holder.fileSize.text = fileDatabaseInfo.getSizeString()
|
||||||
|
|
||||||
|
View.VISIBLE
|
||||||
|
} else
|
||||||
|
View.GONE
|
||||||
|
|
||||||
// Click on information
|
// Click on information
|
||||||
if (fileInformationShowListener != null)
|
val isExpanded = position == mExpandedPosition
|
||||||
holder.fileInformation.setOnClickListener(FileInformationClickListener(fileDatabaseModel))
|
//This line hides or shows the layout in question
|
||||||
|
holder.fileExpandContainer.visibility = if (isExpanded) View.VISIBLE else View.GONE
|
||||||
|
|
||||||
|
// Save alias modification
|
||||||
|
holder.fileAliasCloseButton.setOnClickListener {
|
||||||
|
// Change the alias
|
||||||
|
fileHistoryEntity.databaseAlias = holder.fileAliasEdit.text.toString()
|
||||||
|
saveAliasListener?.invoke(fileHistoryEntity)
|
||||||
|
|
||||||
|
// Finish save mode
|
||||||
|
holder.fileMainSwitcher.showPrevious()
|
||||||
|
// Refresh current position to show alias
|
||||||
|
notifyItemChanged(position)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Open alias modification
|
||||||
|
holder.fileModifyButton.setOnClickListener {
|
||||||
|
holder.fileAliasEdit.setText(holder.fileAlias.text)
|
||||||
|
holder.fileMainSwitcher.showNext()
|
||||||
|
}
|
||||||
|
|
||||||
|
holder.fileDeleteButton.setOnClickListener {
|
||||||
|
fileSelectClearListener?.invoke(fileHistoryEntity)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isExpanded) {
|
||||||
|
mPreviousExpandedPosition = position
|
||||||
|
}
|
||||||
|
|
||||||
|
holder.fileInformation.setOnClickListener {
|
||||||
|
mExpandedPosition = if (isExpanded) -1 else position
|
||||||
|
|
||||||
|
// Notify change
|
||||||
|
if (mPreviousExpandedPosition < itemCount)
|
||||||
|
notifyItemChanged(mPreviousExpandedPosition)
|
||||||
|
notifyItemChanged(position)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Refresh View / Close alias modification if not contains fileAlias
|
||||||
|
if (holder.fileMainSwitcher.currentView.findViewById<View>(R.id.file_alias)
|
||||||
|
!= holder.fileAlias)
|
||||||
|
holder.fileMainSwitcher.showPrevious()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getItemCount(): Int {
|
override fun getItemCount(): Int {
|
||||||
return listFiles.size
|
return listDatabaseFiles.size
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setOnItemClickListener(fileItemOpenListener: FileItemOpenListener) {
|
fun addDatabaseFileHistoryList(listFileDatabaseHistoryToAdd: List<FileDatabaseHistoryEntity>) {
|
||||||
this.fileItemOpenListener = fileItemOpenListener
|
listDatabaseFiles.clear()
|
||||||
|
listDatabaseFiles.addAll(listFileDatabaseHistoryToAdd)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setFileSelectClearListener(fileSelectClearListener: FileSelectClearListener) {
|
fun deleteDatabaseFileHistory(fileDatabaseHistoryToDelete: FileDatabaseHistoryEntity) {
|
||||||
this.fileSelectClearListener = fileSelectClearListener
|
listDatabaseFiles.remove(fileDatabaseHistoryToDelete)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setFileInformationShowListener(fileInformationShowListener: FileInformationShowListener) {
|
fun setOnFileDatabaseHistoryOpenListener(listener : ((FileDatabaseHistoryEntity)->Unit)?) {
|
||||||
this.fileInformationShowListener = fileInformationShowListener
|
this.fileItemOpenListener = listener
|
||||||
}
|
}
|
||||||
|
|
||||||
interface FileItemOpenListener {
|
fun setOnFileDatabaseHistoryDeleteListener(listener : ((FileDatabaseHistoryEntity)->Boolean)?) {
|
||||||
fun onFileItemOpenListener(itemPosition: Int)
|
this.fileSelectClearListener = listener
|
||||||
}
|
}
|
||||||
|
|
||||||
interface FileSelectClearListener {
|
fun setOnSaveAliasListener(listener : ((FileDatabaseHistoryEntity)->Unit)?) {
|
||||||
fun onFileSelectClearListener(fileDatabaseModel: FileDatabaseModel): Boolean
|
this.saveAliasListener = listener
|
||||||
}
|
|
||||||
|
|
||||||
interface FileInformationShowListener {
|
|
||||||
fun onClickFileInformation(fileDatabaseModel: FileDatabaseModel)
|
|
||||||
}
|
|
||||||
|
|
||||||
private inner class FileItemClickListener(private val position: Int) : View.OnClickListener {
|
|
||||||
|
|
||||||
override fun onClick(v: View) {
|
|
||||||
fileItemOpenListener?.onFileItemOpenListener(position)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private inner class FileInformationClickListener(private val fileDatabaseModel: FileDatabaseModel) : View.OnClickListener {
|
|
||||||
|
|
||||||
override fun onClick(view: View) {
|
|
||||||
fileInformationShowListener?.onClickFileInformation(fileDatabaseModel)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private inner class ContextMenuBuilder(private val fileDatabaseModel: FileDatabaseModel) : View.OnCreateContextMenuListener {
|
|
||||||
|
|
||||||
private val mOnMyActionClickListener = MenuItem.OnMenuItemClickListener { item ->
|
|
||||||
if (fileSelectClearListener == null)
|
|
||||||
return@OnMenuItemClickListener false
|
|
||||||
when (item.itemId) {
|
|
||||||
MENU_CLEAR -> fileSelectClearListener!!.onFileSelectClearListener(fileDatabaseModel)
|
|
||||||
else -> false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCreateContextMenu(contextMenu: ContextMenu?, view: View?, contextMenuInfo: ContextMenu.ContextMenuInfo?) {
|
|
||||||
contextMenu?.add(Menu.NONE, MENU_CLEAR, Menu.NONE, R.string.remove_from_filelist)
|
|
||||||
?.setOnMenuItemClickListener(mOnMyActionClickListener)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inner class FileDatabaseHistoryViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
|
inner class FileDatabaseHistoryViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
|
||||||
|
|
||||||
var fileContainer: View = itemView.findViewById(R.id.file_container)
|
var fileContainer: ViewGroup = itemView.findViewById(R.id.file_container_basic_info)
|
||||||
var fileName: TextView = itemView.findViewById(R.id.file_filename)
|
|
||||||
|
var fileAlias: TextView = itemView.findViewById(R.id.file_alias)
|
||||||
var fileInformation: ImageView = itemView.findViewById(R.id.file_information)
|
var fileInformation: ImageView = itemView.findViewById(R.id.file_information)
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
var fileMainSwitcher: ViewSwitcher = itemView.findViewById(R.id.file_main_switcher)
|
||||||
|
var fileAliasEdit: EditText = itemView.findViewById(R.id.file_alias_edit)
|
||||||
|
var fileAliasCloseButton: ImageView = itemView.findViewById(R.id.file_alias_save)
|
||||||
|
|
||||||
private const val MENU_CLEAR = 1
|
var fileExpandContainer: ViewGroup = itemView.findViewById(R.id.file_expand_container)
|
||||||
|
var fileModifyButton: ImageView = itemView.findViewById(R.id.file_modify_button)
|
||||||
|
var fileDeleteButton: ImageView = itemView.findViewById(R.id.file_delete_button)
|
||||||
|
var filePath: TextView = itemView.findViewById(R.id.file_path)
|
||||||
|
var filePreciseInfoContainer: ViewGroup = itemView.findViewById(R.id.file_precise_info_container)
|
||||||
|
var fileModification: TextView = itemView.findViewById(R.id.file_modification)
|
||||||
|
var fileSize: TextView = itemView.findViewById(R.id.file_size)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,9 +21,9 @@ package com.kunzisoft.keepass.adapters
|
|||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.graphics.Color
|
import android.graphics.Color
|
||||||
import android.support.v7.util.SortedList
|
import androidx.recyclerview.widget.SortedList
|
||||||
import android.support.v7.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import android.support.v7.widget.util.SortedListAdapterCallback
|
import androidx.recyclerview.widget.SortedListAdapterCallback
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.view.*
|
import android.view.*
|
||||||
import android.widget.ImageView
|
import android.widget.ImageView
|
||||||
@@ -45,10 +45,13 @@ class NodeAdapter
|
|||||||
|
|
||||||
private val nodeSortedList: SortedList<NodeVersioned>
|
private val nodeSortedList: SortedList<NodeVersioned>
|
||||||
private val inflater: LayoutInflater = LayoutInflater.from(context)
|
private val inflater: LayoutInflater = LayoutInflater.from(context)
|
||||||
private var textSize: Float = 0.toFloat()
|
|
||||||
private var subtextSize: Float = 0.toFloat()
|
private var calculateViewTypeTextSize = Array(2) { true} // number of view type
|
||||||
private var infoTextSize: Float = 0.toFloat()
|
private var prefTextSize: Float = 0F
|
||||||
private var iconSize: Float = 0.toFloat()
|
private var subtextSize: Float = 0F
|
||||||
|
private var infoTextSize: Float = 0F
|
||||||
|
private var numberChildrenTextSize: Float = 0F
|
||||||
|
private var iconSize: Float = 0F
|
||||||
private var listSort: SortNodeEnum = SortNodeEnum.DB
|
private var listSort: SortNodeEnum = SortNodeEnum.DB
|
||||||
private var ascendingSort: Boolean = true
|
private var ascendingSort: Boolean = true
|
||||||
private var groupsBeforeSort: Boolean = true
|
private var groupsBeforeSort: Boolean = true
|
||||||
@@ -122,19 +125,16 @@ class NodeAdapter
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun assignPreferences() {
|
private fun assignPreferences() {
|
||||||
val textSizeDefault = java.lang.Float.parseFloat(context.getString(R.string.list_size_default))
|
this.prefTextSize = PreferencesUtil.getListTextSize(context) / java.lang.Float.parseFloat(context.getString(R.string.list_size_default))
|
||||||
this.textSize = PreferencesUtil.getListTextSize(context)
|
|
||||||
this.subtextSize = context.resources.getInteger(R.integer.list_small_size_default) * textSize / textSizeDefault
|
|
||||||
this.infoTextSize = context.resources.getInteger(R.integer.list_tiny_size_default) * textSize / textSizeDefault
|
|
||||||
// Retrieve the icon size
|
|
||||||
val iconDefaultSize = context.resources.getDimension(R.dimen.list_icon_size_default)
|
|
||||||
this.iconSize = iconDefaultSize * textSize / textSizeDefault
|
|
||||||
this.listSort = PreferencesUtil.getListSort(context)
|
this.listSort = PreferencesUtil.getListSort(context)
|
||||||
this.ascendingSort = PreferencesUtil.getAscendingSort(context)
|
this.ascendingSort = PreferencesUtil.getAscendingSort(context)
|
||||||
this.groupsBeforeSort = PreferencesUtil.getGroupsBeforeSort(context)
|
this.groupsBeforeSort = PreferencesUtil.getGroupsBeforeSort(context)
|
||||||
this.recycleBinBottomSort = PreferencesUtil.getRecycleBinBottomSort(context)
|
this.recycleBinBottomSort = PreferencesUtil.getRecycleBinBottomSort(context)
|
||||||
this.showUserNames = PreferencesUtil.showUsernamesListEntries(context)
|
this.showUserNames = PreferencesUtil.showUsernamesListEntries(context)
|
||||||
this.showNumberEntries = PreferencesUtil.showNumberEntries(context)
|
this.showNumberEntries = PreferencesUtil.showNumberEntries(context)
|
||||||
|
|
||||||
|
// Reinit textSize for all view type
|
||||||
|
calculateViewTypeTextSize.forEachIndexed { index, _ -> calculateViewTypeTextSize[index] = true }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -212,16 +212,41 @@ class NodeAdapter
|
|||||||
return NodeViewHolder(view)
|
return NodeViewHolder(view)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun calculateTextSize(holder: NodeViewHolder, viewType: Int) {
|
||||||
|
if (calculateViewTypeTextSize[viewType]) {
|
||||||
|
this.subtextSize = holder.subText.textSize * prefTextSize
|
||||||
|
this.infoTextSize = holder.text.textSize * prefTextSize
|
||||||
|
holder.numberChildren?.let {
|
||||||
|
this.numberChildrenTextSize = it.textSize * prefTextSize
|
||||||
|
}
|
||||||
|
this.iconSize = context.resources.getDimension(R.dimen.list_icon_size_default) * prefTextSize
|
||||||
|
calculateViewTypeTextSize[viewType] = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun onBindViewHolder(holder: NodeViewHolder, position: Int) {
|
override fun onBindViewHolder(holder: NodeViewHolder, position: Int) {
|
||||||
val subNode = nodeSortedList.get(position)
|
val subNode = nodeSortedList.get(position)
|
||||||
|
|
||||||
|
calculateTextSize(holder, getItemViewType(position))
|
||||||
|
|
||||||
// Assign image
|
// Assign image
|
||||||
val iconColor = when (subNode.type) {
|
val iconColor = when (subNode.type) {
|
||||||
Type.GROUP -> iconGroupColor
|
Type.GROUP -> iconGroupColor
|
||||||
Type.ENTRY -> iconEntryColor
|
Type.ENTRY -> iconEntryColor
|
||||||
}
|
}
|
||||||
holder.icon.assignDatabaseIcon(mDatabase.drawFactory, subNode.icon, iconColor)
|
holder.icon.apply {
|
||||||
|
assignDatabaseIcon(mDatabase.drawFactory, subNode.icon, iconColor)
|
||||||
|
// Relative size of the icon
|
||||||
|
layoutParams?.apply {
|
||||||
|
height = iconSize.toInt()
|
||||||
|
width = iconSize.toInt()
|
||||||
|
}
|
||||||
|
}
|
||||||
// Assign text
|
// Assign text
|
||||||
holder.text.text = subNode.title
|
holder.text.apply {
|
||||||
|
text = subNode.title
|
||||||
|
textSize = infoTextSize
|
||||||
|
}
|
||||||
// Assign click
|
// Assign click
|
||||||
holder.container.setOnClickListener { nodeClickCallback?.onNodeClick(subNode) }
|
holder.container.setOnClickListener { nodeClickCallback?.onNodeClick(subNode) }
|
||||||
// Context menu
|
// Context menu
|
||||||
@@ -230,36 +255,34 @@ class NodeAdapter
|
|||||||
ContextMenuBuilder(menuInflater, subNode, readOnly, isASearchResult, nodeMenuListener))
|
ContextMenuBuilder(menuInflater, subNode, readOnly, isASearchResult, nodeMenuListener))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add username
|
// Add subText with username
|
||||||
holder.subText.text = ""
|
holder.subText.apply {
|
||||||
holder.subText.visibility = View.GONE
|
text = ""
|
||||||
if (subNode.type == Type.ENTRY) {
|
visibility = View.GONE
|
||||||
val entry = subNode as EntryVersioned
|
if (subNode.type == Type.ENTRY) {
|
||||||
|
val entry = subNode as EntryVersioned
|
||||||
|
|
||||||
mDatabase.startManageEntry(entry)
|
mDatabase.startManageEntry(entry)
|
||||||
|
|
||||||
holder.text.text = entry.getVisualTitle()
|
holder.text.text = entry.getVisualTitle()
|
||||||
|
|
||||||
val username = entry.username
|
val username = entry.username
|
||||||
if (showUserNames && username.isNotEmpty()) {
|
if (showUserNames && username.isNotEmpty()) {
|
||||||
holder.subText.visibility = View.VISIBLE
|
visibility = View.VISIBLE
|
||||||
holder.subText.text = username
|
text = username
|
||||||
|
}
|
||||||
|
|
||||||
|
mDatabase.stopManageEntry(entry)
|
||||||
}
|
}
|
||||||
|
textSize = subtextSize
|
||||||
mDatabase.stopManageEntry(entry)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Assign image and text size
|
// Add number of entries in groups
|
||||||
// Relative size of the icon
|
|
||||||
holder.icon.layoutParams?.height = iconSize.toInt()
|
|
||||||
holder.icon.layoutParams?.width = iconSize.toInt()
|
|
||||||
holder.text.textSize = textSize
|
|
||||||
holder.subText.textSize = subtextSize
|
|
||||||
if (subNode.type == Type.GROUP) {
|
if (subNode.type == Type.GROUP) {
|
||||||
if (showNumberEntries) {
|
if (showNumberEntries) {
|
||||||
holder.numberChildren?.apply {
|
holder.numberChildren?.apply {
|
||||||
text = (subNode as GroupVersioned).getChildEntries(true).size.toString()
|
text = (subNode as GroupVersioned).getChildEntries(true).size.toString()
|
||||||
textSize = infoTextSize
|
textSize = numberChildrenTextSize
|
||||||
visibility = View.VISIBLE
|
visibility = View.VISIBLE
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ package com.kunzisoft.keepass.adapters
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.database.Cursor
|
import android.database.Cursor
|
||||||
import android.graphics.Color
|
import android.graphics.Color
|
||||||
import android.support.v4.widget.CursorAdapter
|
import androidx.cursoradapter.widget.CursorAdapter
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
@@ -38,7 +38,7 @@ import com.kunzisoft.keepass.settings.PreferencesUtil
|
|||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
class SearchEntryCursorAdapter(context: Context, private val database: Database)
|
class SearchEntryCursorAdapter(context: Context, private val database: Database)
|
||||||
: CursorAdapter(context, null, FLAG_REGISTER_CONTENT_OBSERVER) {
|
: androidx.cursoradapter.widget.CursorAdapter(context, null, FLAG_REGISTER_CONTENT_OBSERVER) {
|
||||||
|
|
||||||
private val cursorInflater: LayoutInflater = context.getSystemService(
|
private val cursorInflater: LayoutInflater = context.getSystemService(
|
||||||
Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
|
Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
*/
|
*/
|
||||||
package com.kunzisoft.keepass.app
|
package com.kunzisoft.keepass.app
|
||||||
|
|
||||||
import android.support.multidex.MultiDexApplication
|
import androidx.multidex.MultiDexApplication
|
||||||
import com.kunzisoft.keepass.activities.stylish.Stylish
|
import com.kunzisoft.keepass.activities.stylish.Stylish
|
||||||
import com.kunzisoft.keepass.database.element.Database
|
import com.kunzisoft.keepass.database.element.Database
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
package com.kunzisoft.keepass.app.database
|
||||||
|
|
||||||
|
import androidx.room.Database
|
||||||
|
import androidx.room.Room
|
||||||
|
import androidx.room.RoomDatabase
|
||||||
|
import android.content.Context
|
||||||
|
|
||||||
|
@Database(entities = [FileDatabaseHistoryEntity::class], version = 1)
|
||||||
|
abstract class AppDatabase : RoomDatabase() {
|
||||||
|
abstract fun databaseFileHistoryDao(): FileDatabaseHistoryDao
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun getDatabase(applicationContext: Context): AppDatabase {
|
||||||
|
return Room.databaseBuilder(
|
||||||
|
applicationContext,
|
||||||
|
AppDatabase::class.java, "com.kunzisoft.keepass.database"
|
||||||
|
).build()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,152 @@
|
|||||||
|
/*
|
||||||
|
* 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.app.database
|
||||||
|
|
||||||
|
import androidx.sqlite.db.SimpleSQLiteQuery
|
||||||
|
import android.content.Context
|
||||||
|
import android.net.Uri
|
||||||
|
import android.os.AsyncTask
|
||||||
|
import com.kunzisoft.keepass.utils.SingletonHolderParameter
|
||||||
|
|
||||||
|
class FileDatabaseHistory(applicationContext: Context) {
|
||||||
|
|
||||||
|
private val databaseFileHistoryDao =
|
||||||
|
AppDatabase
|
||||||
|
.getDatabase(applicationContext)
|
||||||
|
.databaseFileHistoryDao()
|
||||||
|
|
||||||
|
fun getFileDatabaseHistory(databaseUri: Uri,
|
||||||
|
fileHistoryResultListener: (fileDatabaseHistoryResult: FileDatabaseHistoryEntity?) -> Unit) {
|
||||||
|
ActionFileHistoryAsyncTask(
|
||||||
|
{
|
||||||
|
databaseFileHistoryDao.getByDatabaseUri(databaseUri.toString())
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fileHistoryResultListener.invoke(it)
|
||||||
|
}
|
||||||
|
).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 getAllFileDatabaseHistories(fileHistoryResultListener: (fileDatabaseHistoryResult: List<FileDatabaseHistoryEntity>?) -> Unit) {
|
||||||
|
ActionFileHistoryAsyncTask(
|
||||||
|
{
|
||||||
|
databaseFileHistoryDao.getAll()
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fileHistoryResultListener.invoke(it)
|
||||||
|
}
|
||||||
|
).execute()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun addOrUpdateDatabaseUri(databaseUri: Uri, keyFileUri: Uri? = null) {
|
||||||
|
addOrUpdateFileDatabaseHistory(FileDatabaseHistoryEntity(
|
||||||
|
databaseUri.toString(),
|
||||||
|
"",
|
||||||
|
keyFileUri?.toString(),
|
||||||
|
System.currentTimeMillis()
|
||||||
|
), true)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun addOrUpdateFileDatabaseHistory(fileDatabaseHistory: FileDatabaseHistoryEntity, unmodifiedAlias: Boolean = false) {
|
||||||
|
ActionFileHistoryAsyncTask(
|
||||||
|
{
|
||||||
|
val fileDatabaseHistoryRetrieve = databaseFileHistoryDao.getByDatabaseUri(fileDatabaseHistory.databaseUri)
|
||||||
|
|
||||||
|
if (unmodifiedAlias) {
|
||||||
|
fileDatabaseHistory.databaseAlias = fileDatabaseHistoryRetrieve?.databaseAlias ?: ""
|
||||||
|
}
|
||||||
|
// Update values if history element not yet in the database
|
||||||
|
if (fileDatabaseHistoryRetrieve == null) {
|
||||||
|
databaseFileHistoryDao.add(fileDatabaseHistory)
|
||||||
|
} else {
|
||||||
|
databaseFileHistoryDao.update(fileDatabaseHistory)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
).execute()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun deleteFileDatabaseHistory(fileDatabaseHistory: FileDatabaseHistoryEntity,
|
||||||
|
fileHistoryDeletedResult: (FileDatabaseHistoryEntity?) -> Unit) {
|
||||||
|
ActionFileHistoryAsyncTask(
|
||||||
|
{
|
||||||
|
databaseFileHistoryDao.delete(fileDatabaseHistory)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
if (it != null && it > 0)
|
||||||
|
fileHistoryDeletedResult.invoke(fileDatabaseHistory)
|
||||||
|
else
|
||||||
|
fileHistoryDeletedResult.invoke(null)
|
||||||
|
}
|
||||||
|
).execute()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun deleteAllKeyFiles() {
|
||||||
|
// TODO replace for unsupported query databaseFileHistoryDao.deleteAllKeyFiles()
|
||||||
|
ActionFileHistoryAsyncTask(
|
||||||
|
{
|
||||||
|
databaseFileHistoryDao
|
||||||
|
.deleteAllKeyFiles(SimpleSQLiteQuery("REPLACE INTO file_database_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, Context>(::FileDatabaseHistory)
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
package com.kunzisoft.keepass.app.database
|
||||||
|
|
||||||
|
import androidx.sqlite.db.SupportSQLiteQuery
|
||||||
|
import androidx.room.*
|
||||||
|
|
||||||
|
@Dao
|
||||||
|
interface FileDatabaseHistoryDao {
|
||||||
|
@Query("SELECT * FROM file_database_history ORDER BY updated DESC")
|
||||||
|
fun getAll(): List<FileDatabaseHistoryEntity>
|
||||||
|
|
||||||
|
@Query("SELECT * FROM file_database_history WHERE database_uri = :databaseUriString")
|
||||||
|
fun getByDatabaseUri(databaseUriString: String): FileDatabaseHistoryEntity?
|
||||||
|
|
||||||
|
@Insert
|
||||||
|
fun add(vararg fileDatabaseHistory: FileDatabaseHistoryEntity)
|
||||||
|
|
||||||
|
@Update
|
||||||
|
fun update(vararg fileDatabaseHistory: FileDatabaseHistoryEntity)
|
||||||
|
|
||||||
|
@Delete
|
||||||
|
fun delete(fileDatabaseHistory: FileDatabaseHistoryEntity): Int
|
||||||
|
|
||||||
|
/* TODO Replace (Insert not yet supported)
|
||||||
|
@Query("REPLACE INTO file_database_history(keyfile_uri) VALUES(null)")
|
||||||
|
fun deleteAllKeyFiles()
|
||||||
|
*/
|
||||||
|
@RawQuery
|
||||||
|
fun deleteAllKeyFiles(query: SupportSQLiteQuery): Boolean
|
||||||
|
|
||||||
|
@Query("DELETE FROM file_database_history")
|
||||||
|
fun deleteAll()
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
package com.kunzisoft.keepass.app.database
|
||||||
|
|
||||||
|
import androidx.room.ColumnInfo
|
||||||
|
import androidx.room.Entity
|
||||||
|
import androidx.room.PrimaryKey
|
||||||
|
|
||||||
|
@Entity(tableName = "file_database_history")
|
||||||
|
data class FileDatabaseHistoryEntity(
|
||||||
|
@PrimaryKey
|
||||||
|
@ColumnInfo(name = "database_uri")
|
||||||
|
val databaseUri: String,
|
||||||
|
|
||||||
|
@ColumnInfo(name = "database_alias")
|
||||||
|
var 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 FileDatabaseHistoryEntity
|
||||||
|
|
||||||
|
if (databaseUri != other.databaseUri) return false
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hashCode(): Int {
|
||||||
|
return databaseUri.hashCode()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -26,7 +26,7 @@ import android.content.Intent
|
|||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.service.autofill.Dataset
|
import android.service.autofill.Dataset
|
||||||
import android.service.autofill.FillResponse
|
import android.service.autofill.FillResponse
|
||||||
import android.support.annotation.RequiresApi
|
import androidx.annotation.RequiresApi
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.view.autofill.AutofillManager
|
import android.view.autofill.AutofillManager
|
||||||
import android.view.autofill.AutofillValue
|
import android.view.autofill.AutofillValue
|
||||||
|
|||||||
@@ -26,8 +26,8 @@ import android.content.Intent
|
|||||||
import android.content.IntentSender
|
import android.content.IntentSender
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.support.annotation.RequiresApi
|
import androidx.annotation.RequiresApi
|
||||||
import android.support.v7.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import com.kunzisoft.keepass.activities.FileDatabaseSelectActivity
|
import com.kunzisoft.keepass.activities.FileDatabaseSelectActivity
|
||||||
import com.kunzisoft.keepass.activities.GroupActivity
|
import com.kunzisoft.keepass.activities.GroupActivity
|
||||||
import com.kunzisoft.keepass.database.element.Database
|
import com.kunzisoft.keepass.database.element.Database
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ package com.kunzisoft.keepass.autofill
|
|||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.CancellationSignal
|
import android.os.CancellationSignal
|
||||||
import android.service.autofill.*
|
import android.service.autofill.*
|
||||||
import android.support.annotation.RequiresApi
|
import androidx.annotation.RequiresApi
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.widget.RemoteViews
|
import android.widget.RemoteViews
|
||||||
import com.kunzisoft.keepass.R
|
import com.kunzisoft.keepass.R
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ package com.kunzisoft.keepass.autofill
|
|||||||
|
|
||||||
import android.app.assist.AssistStructure
|
import android.app.assist.AssistStructure
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.support.annotation.RequiresApi
|
import androidx.annotation.RequiresApi
|
||||||
import android.text.InputType
|
import android.text.InputType
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.view.View
|
import android.view.View
|
||||||
|
|||||||
@@ -26,6 +26,8 @@ import android.app.backup.SharedPreferencesBackupHelper
|
|||||||
@SuppressLint("NewApi")
|
@SuppressLint("NewApi")
|
||||||
class SettingsBackupAgent : BackupAgentHelper() {
|
class SettingsBackupAgent : BackupAgentHelper() {
|
||||||
|
|
||||||
|
//TODO Backup
|
||||||
|
|
||||||
override fun onCreate() {
|
override fun onCreate() {
|
||||||
val defaultPrefs = this.packageName + "_preferences"
|
val defaultPrefs = this.packageName + "_preferences"
|
||||||
val prefHelper = SharedPreferencesBackupHelper(this, defaultPrefs)
|
val prefHelper = SharedPreferencesBackupHelper(this, defaultPrefs)
|
||||||
|
|||||||
@@ -22,12 +22,12 @@ package com.kunzisoft.keepass.database.action
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.preference.PreferenceManager
|
import android.preference.PreferenceManager
|
||||||
import android.support.annotation.StringRes
|
import androidx.annotation.StringRes
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import com.kunzisoft.keepass.R
|
import com.kunzisoft.keepass.R
|
||||||
import com.kunzisoft.keepass.database.element.Database
|
import com.kunzisoft.keepass.database.element.Database
|
||||||
import com.kunzisoft.keepass.database.exception.*
|
import com.kunzisoft.keepass.database.exception.*
|
||||||
import com.kunzisoft.keepass.fileselect.database.FileDatabaseHistory
|
import com.kunzisoft.keepass.app.database.FileDatabaseHistory
|
||||||
import com.kunzisoft.keepass.tasks.ActionRunnable
|
import com.kunzisoft.keepass.tasks.ActionRunnable
|
||||||
import com.kunzisoft.keepass.tasks.ProgressTaskUpdater
|
import com.kunzisoft.keepass.tasks.ProgressTaskUpdater
|
||||||
import java.io.FileNotFoundException
|
import java.io.FileNotFoundException
|
||||||
@@ -119,7 +119,9 @@ class LoadDatabaseRunnable(private val mWeakContext: WeakReference<Context>,
|
|||||||
if (!mRememberKeyFile) {
|
if (!mRememberKeyFile) {
|
||||||
keyFileUri = null
|
keyFileUri = null
|
||||||
}
|
}
|
||||||
FileDatabaseHistory.getInstance(mWeakContext).addDatabaseUri(uri, keyFileUri)
|
mWeakContext.get()?.let {
|
||||||
|
FileDatabaseHistory.getInstance(it).addOrUpdateDatabaseUri(uri, keyFileUri)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onFinishRun(result: Result) {
|
override fun onFinishRun(result: Result) {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
package com.kunzisoft.keepass.database.action
|
package com.kunzisoft.keepass.database.action
|
||||||
|
|
||||||
import android.support.v4.app.FragmentActivity
|
import androidx.fragment.app.FragmentActivity
|
||||||
import com.kunzisoft.keepass.R
|
import com.kunzisoft.keepass.R
|
||||||
import com.kunzisoft.keepass.tasks.ActionRunnable
|
import com.kunzisoft.keepass.tasks.ActionRunnable
|
||||||
import com.kunzisoft.keepass.tasks.ProgressTaskUpdater
|
import com.kunzisoft.keepass.tasks.ProgressTaskUpdater
|
||||||
|
|||||||
@@ -3,8 +3,8 @@ package com.kunzisoft.keepass.database.action
|
|||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.os.AsyncTask
|
import android.os.AsyncTask
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.support.annotation.StringRes
|
import androidx.annotation.StringRes
|
||||||
import android.support.v4.app.FragmentActivity
|
import androidx.fragment.app.FragmentActivity
|
||||||
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService
|
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService
|
||||||
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.DATABASE_TASK_TITLE_KEY
|
import com.kunzisoft.keepass.notifications.DatabaseTaskNotificationService.Companion.DATABASE_TASK_TITLE_KEY
|
||||||
import com.kunzisoft.keepass.tasks.ActionRunnable
|
import com.kunzisoft.keepass.tasks.ActionRunnable
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
package com.kunzisoft.keepass.database.action.node
|
package com.kunzisoft.keepass.database.action.node
|
||||||
|
|
||||||
import android.support.v4.app.FragmentActivity
|
import androidx.fragment.app.FragmentActivity
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import com.kunzisoft.keepass.database.action.SaveDatabaseRunnable
|
import com.kunzisoft.keepass.database.action.SaveDatabaseRunnable
|
||||||
import com.kunzisoft.keepass.database.element.Database
|
import com.kunzisoft.keepass.database.element.Database
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
*/
|
*/
|
||||||
package com.kunzisoft.keepass.database.action.node
|
package com.kunzisoft.keepass.database.action.node
|
||||||
|
|
||||||
import android.support.v4.app.FragmentActivity
|
import androidx.fragment.app.FragmentActivity
|
||||||
import com.kunzisoft.keepass.database.element.Database
|
import com.kunzisoft.keepass.database.element.Database
|
||||||
import com.kunzisoft.keepass.database.element.EntryVersioned
|
import com.kunzisoft.keepass.database.element.EntryVersioned
|
||||||
import com.kunzisoft.keepass.database.element.GroupVersioned
|
import com.kunzisoft.keepass.database.element.GroupVersioned
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
*/
|
*/
|
||||||
package com.kunzisoft.keepass.database.action.node
|
package com.kunzisoft.keepass.database.action.node
|
||||||
|
|
||||||
import android.support.v4.app.FragmentActivity
|
import androidx.fragment.app.FragmentActivity
|
||||||
import com.kunzisoft.keepass.database.element.Database
|
import com.kunzisoft.keepass.database.element.Database
|
||||||
import com.kunzisoft.keepass.database.element.GroupVersioned
|
import com.kunzisoft.keepass.database.element.GroupVersioned
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
*/
|
*/
|
||||||
package com.kunzisoft.keepass.database.action.node
|
package com.kunzisoft.keepass.database.action.node
|
||||||
|
|
||||||
import android.support.v4.app.FragmentActivity
|
import androidx.fragment.app.FragmentActivity
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import com.kunzisoft.keepass.R
|
import com.kunzisoft.keepass.R
|
||||||
import com.kunzisoft.keepass.database.element.Database
|
import com.kunzisoft.keepass.database.element.Database
|
||||||
|
|||||||
@@ -20,7 +20,7 @@
|
|||||||
package com.kunzisoft.keepass.database.action.node
|
package com.kunzisoft.keepass.database.action.node
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.support.v4.app.FragmentActivity
|
import androidx.fragment.app.FragmentActivity
|
||||||
import com.kunzisoft.keepass.database.element.Database
|
import com.kunzisoft.keepass.database.element.Database
|
||||||
import com.kunzisoft.keepass.database.element.EntryVersioned
|
import com.kunzisoft.keepass.database.element.EntryVersioned
|
||||||
import com.kunzisoft.keepass.database.element.GroupVersioned
|
import com.kunzisoft.keepass.database.element.GroupVersioned
|
||||||
|
|||||||
@@ -20,7 +20,7 @@
|
|||||||
package com.kunzisoft.keepass.database.action.node
|
package com.kunzisoft.keepass.database.action.node
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.support.v4.app.FragmentActivity
|
import androidx.fragment.app.FragmentActivity
|
||||||
|
|
||||||
import com.kunzisoft.keepass.database.element.Database
|
import com.kunzisoft.keepass.database.element.Database
|
||||||
import com.kunzisoft.keepass.database.element.GroupVersioned
|
import com.kunzisoft.keepass.database.element.GroupVersioned
|
||||||
@@ -61,10 +61,11 @@ class DeleteGroupRunnable(context: FragmentActivity,
|
|||||||
mParent?.let {
|
mParent?.let {
|
||||||
database.undoRecycle(mGroupToDelete, it)
|
database.undoRecycle(mGroupToDelete, it)
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
|
// else {
|
||||||
// Let's not bother recovering from a failure to save a deleted tree. It is too much work.
|
// Let's not bother recovering from a failure to save a deleted tree. It is too much work.
|
||||||
// TODO database.undoDeleteGroupFrom(mGroup, mParent);
|
// TODO database.undoDeleteGroupFrom(mGroup, mParent);
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add position in bundle to delete the node in view
|
// Add position in bundle to delete the node in view
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
*/
|
*/
|
||||||
package com.kunzisoft.keepass.database.action.node
|
package com.kunzisoft.keepass.database.action.node
|
||||||
|
|
||||||
import android.support.v4.app.FragmentActivity
|
import androidx.fragment.app.FragmentActivity
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import com.kunzisoft.keepass.R
|
import com.kunzisoft.keepass.R
|
||||||
import com.kunzisoft.keepass.database.element.Database
|
import com.kunzisoft.keepass.database.element.Database
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
*/
|
*/
|
||||||
package com.kunzisoft.keepass.database.action.node
|
package com.kunzisoft.keepass.database.action.node
|
||||||
|
|
||||||
import android.support.v4.app.FragmentActivity
|
import androidx.fragment.app.FragmentActivity
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import com.kunzisoft.keepass.R
|
import com.kunzisoft.keepass.R
|
||||||
import com.kunzisoft.keepass.database.element.Database
|
import com.kunzisoft.keepass.database.element.Database
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
*/
|
*/
|
||||||
package com.kunzisoft.keepass.database.action.node
|
package com.kunzisoft.keepass.database.action.node
|
||||||
|
|
||||||
import android.support.v4.app.FragmentActivity
|
import androidx.fragment.app.FragmentActivity
|
||||||
import com.kunzisoft.keepass.database.element.Database
|
import com.kunzisoft.keepass.database.element.Database
|
||||||
import com.kunzisoft.keepass.database.element.EntryVersioned
|
import com.kunzisoft.keepass.database.element.EntryVersioned
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
*/
|
*/
|
||||||
package com.kunzisoft.keepass.database.action.node
|
package com.kunzisoft.keepass.database.action.node
|
||||||
|
|
||||||
import android.support.v4.app.FragmentActivity
|
import androidx.fragment.app.FragmentActivity
|
||||||
import com.kunzisoft.keepass.database.element.Database
|
import com.kunzisoft.keepass.database.element.Database
|
||||||
import com.kunzisoft.keepass.database.element.GroupVersioned
|
import com.kunzisoft.keepass.database.element.GroupVersioned
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ package com.kunzisoft.keepass.education
|
|||||||
|
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.graphics.Color
|
import android.graphics.Color
|
||||||
import android.support.v4.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import com.getkeepsafe.taptargetview.TapTarget
|
import com.getkeepsafe.taptargetview.TapTarget
|
||||||
import com.getkeepsafe.taptargetview.TapTargetView
|
import com.getkeepsafe.taptargetview.TapTargetView
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ package com.kunzisoft.keepass.education
|
|||||||
|
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.graphics.Color
|
import android.graphics.Color
|
||||||
import android.support.v4.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import com.getkeepsafe.taptargetview.TapTarget
|
import com.getkeepsafe.taptargetview.TapTarget
|
||||||
import com.getkeepsafe.taptargetview.TapTargetView
|
import com.getkeepsafe.taptargetview.TapTargetView
|
||||||
@@ -11,9 +11,9 @@ import com.kunzisoft.keepass.R
|
|||||||
class PasswordActivityEducation(activity: Activity)
|
class PasswordActivityEducation(activity: Activity)
|
||||||
: Education(activity) {
|
: Education(activity) {
|
||||||
|
|
||||||
fun checkAndPerformedFingerprintUnlockEducation(educationView: View,
|
fun checkAndPerformedUnlockEducation(educationView: View,
|
||||||
onEducationViewClick: ((TapTargetView?) -> Unit)? = null,
|
onEducationViewClick: ((TapTargetView?) -> Unit)? = null,
|
||||||
onOuterViewClick: ((TapTargetView?) -> Unit)? = null): Boolean {
|
onOuterViewClick: ((TapTargetView?) -> Unit)? = null): Boolean {
|
||||||
return checkAndPerformedEducation(!isEducationUnlockPerformed(activity),
|
return checkAndPerformedEducation(!isEducationUnlockPerformed(activity),
|
||||||
TapTarget.forView(educationView,
|
TapTarget.forView(educationView,
|
||||||
activity.getString(R.string.education_unlock_title),
|
activity.getString(R.string.education_unlock_title),
|
||||||
|
|||||||
@@ -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()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,64 +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.content.Context
|
|
||||||
import android.net.Uri
|
|
||||||
import android.support.v4.provider.DocumentFile
|
|
||||||
|
|
||||||
import java.io.File
|
|
||||||
import java.io.Serializable
|
|
||||||
import java.util.Date
|
|
||||||
|
|
||||||
class FileDatabaseModel(context: Context, pathFile: String) : Serializable {
|
|
||||||
|
|
||||||
var fileName: String? = ""
|
|
||||||
var fileUri: 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)
|
|
||||||
size = file.length()
|
|
||||||
fileName = file.name
|
|
||||||
lastModification = Date(file.lastModified())
|
|
||||||
} else {
|
|
||||||
val file = File(fileUri!!.path!!)
|
|
||||||
size = file.length()
|
|
||||||
fileName = file.name
|
|
||||||
lastModification = Date(file.lastModified())
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fileName == null || fileName!!.isEmpty()) {
|
|
||||||
fileName = fileUri!!.path
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun notFound(): Boolean {
|
|
||||||
return size == 0L
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
|
|
||||||
private const val EXTERNAL_STORAGE_AUTHORITY = "com.android.externalstorage.documents"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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,49 +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.content.Context
|
|
||||||
import android.content.Intent
|
|
||||||
import android.os.Build
|
|
||||||
import android.preference.PreferenceManager
|
|
||||||
import com.kunzisoft.keepass.R
|
|
||||||
|
|
||||||
object StorageAF {
|
|
||||||
|
|
||||||
var ACTION_OPEN_DOCUMENT: String
|
|
||||||
|
|
||||||
init {
|
|
||||||
ACTION_OPEN_DOCUMENT = try {
|
|
||||||
val openDocument = Intent::class.java.getField("ACTION_OPEN_DOCUMENT")
|
|
||||||
openDocument.get(null) as String
|
|
||||||
} catch (e: Exception) {
|
|
||||||
"android.intent.action.OPEN_DOCUMENT"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun useStorageFramework(context: Context): Boolean {
|
|
||||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
|
|
||||||
return prefs.getBoolean(context.getString(R.string.saf_key),
|
|
||||||
context.resources.getBoolean(R.bool.settings_saf_default))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -24,7 +24,7 @@ import android.graphics.drawable.Animatable2
|
|||||||
import android.graphics.drawable.AnimatedVectorDrawable
|
import android.graphics.drawable.AnimatedVectorDrawable
|
||||||
import android.graphics.drawable.Drawable
|
import android.graphics.drawable.Drawable
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.support.annotation.RequiresApi
|
import androidx.annotation.RequiresApi
|
||||||
import android.widget.ImageView
|
import android.widget.ImageView
|
||||||
|
|
||||||
import com.kunzisoft.keepass.R
|
import com.kunzisoft.keepass.R
|
||||||
|
|||||||
@@ -23,9 +23,9 @@ import android.app.Dialog
|
|||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.support.annotation.RequiresApi
|
import androidx.annotation.RequiresApi
|
||||||
import android.support.v4.app.DialogFragment
|
import androidx.fragment.app.DialogFragment
|
||||||
import android.support.v7.app.AlertDialog
|
import androidx.appcompat.app.AlertDialog
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import com.kunzisoft.keepass.R
|
import com.kunzisoft.keepass.R
|
||||||
|
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ import android.os.CancellationSignal
|
|||||||
import android.security.keystore.KeyGenParameterSpec
|
import android.security.keystore.KeyGenParameterSpec
|
||||||
import android.security.keystore.KeyPermanentlyInvalidatedException
|
import android.security.keystore.KeyPermanentlyInvalidatedException
|
||||||
import android.security.keystore.KeyProperties
|
import android.security.keystore.KeyProperties
|
||||||
import android.support.annotation.RequiresApi
|
import androidx.annotation.RequiresApi
|
||||||
import android.util.Base64
|
import android.util.Base64
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
|
|
||||||
@@ -289,7 +289,7 @@ class FingerPrintHelper(context: Context, private val fingerPrintCallback: Finge
|
|||||||
// fingerprint hardware supported and api level OK
|
// fingerprint hardware supported and api level OK
|
||||||
return (isFingerprintSupported(fingerprintManager)
|
return (isFingerprintSupported(fingerprintManager)
|
||||||
// fingerprints enrolled
|
// fingerprints enrolled
|
||||||
&& fingerprintManager != null && fingerprintManager!!.hasEnrolledFingerprints()
|
&& fingerprintManager != null && fingerprintManager.hasEnrolledFingerprints()
|
||||||
// and lockscreen configured
|
// and lockscreen configured
|
||||||
&& keyguardManager != null && keyguardManager!!.isKeyguardSecure)
|
&& keyguardManager != null && keyguardManager!!.isKeyguardSecure)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,8 +6,8 @@ import android.hardware.fingerprint.FingerprintManager
|
|||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Handler
|
import android.os.Handler
|
||||||
import android.support.annotation.RequiresApi
|
import androidx.annotation.RequiresApi
|
||||||
import android.support.v7.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.view.Menu
|
import android.view.Menu
|
||||||
import android.view.MenuInflater
|
import android.view.MenuInflater
|
||||||
|
|||||||
@@ -28,8 +28,8 @@ import android.graphics.Color
|
|||||||
import android.graphics.drawable.BitmapDrawable
|
import android.graphics.drawable.BitmapDrawable
|
||||||
import android.graphics.drawable.ColorDrawable
|
import android.graphics.drawable.ColorDrawable
|
||||||
import android.graphics.drawable.Drawable
|
import android.graphics.drawable.Drawable
|
||||||
import android.support.v4.content.res.ResourcesCompat
|
import androidx.core.content.res.ResourcesCompat
|
||||||
import android.support.v4.widget.ImageViewCompat
|
import androidx.core.widget.ImageViewCompat
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.widget.ImageView
|
import android.widget.ImageView
|
||||||
import com.kunzisoft.keepass.R
|
import com.kunzisoft.keepass.R
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package com.kunzisoft.keepass.magikeyboard
|
package com.kunzisoft.keepass.magikeyboard
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.support.v7.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import com.kunzisoft.keepass.activities.FileDatabaseSelectActivity
|
import com.kunzisoft.keepass.activities.FileDatabaseSelectActivity
|
||||||
import com.kunzisoft.keepass.activities.GroupActivity
|
import com.kunzisoft.keepass.activities.GroupActivity
|
||||||
import com.kunzisoft.keepass.database.element.Database
|
import com.kunzisoft.keepass.database.element.Database
|
||||||
@@ -10,10 +10,6 @@ import com.kunzisoft.keepass.timeout.TimeoutHelper
|
|||||||
|
|
||||||
class KeyboardLauncherActivity : AppCompatActivity() {
|
class KeyboardLauncherActivity : AppCompatActivity() {
|
||||||
|
|
||||||
companion object {
|
|
||||||
val TAG = KeyboardLauncherActivity::class.java.name!!
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
if (Database.getInstance().loaded && TimeoutHelper.checkTime(this))
|
if (Database.getInstance().loaded && TimeoutHelper.checkTime(this))
|
||||||
GroupActivity.launchForKeyboardSelection(this, PreferencesUtil.enableReadOnlyDatabase(this))
|
GroupActivity.launchForKeyboardSelection(this, PreferencesUtil.enableReadOnlyDatabase(this))
|
||||||
|
|||||||
@@ -27,8 +27,6 @@ import android.inputmethodservice.InputMethodService
|
|||||||
import android.inputmethodservice.Keyboard
|
import android.inputmethodservice.Keyboard
|
||||||
import android.inputmethodservice.KeyboardView
|
import android.inputmethodservice.KeyboardView
|
||||||
import android.media.AudioManager
|
import android.media.AudioManager
|
||||||
import android.support.v7.widget.LinearLayoutManager
|
|
||||||
import android.support.v7.widget.RecyclerView
|
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.view.*
|
import android.view.*
|
||||||
import android.view.inputmethod.EditorInfo
|
import android.view.inputmethod.EditorInfo
|
||||||
@@ -37,7 +35,7 @@ import android.widget.FrameLayout
|
|||||||
import android.widget.PopupWindow
|
import android.widget.PopupWindow
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
import com.kunzisoft.keepass.R
|
import com.kunzisoft.keepass.R
|
||||||
import com.kunzisoft.keepass.magikeyboard.adapter.FieldsAdapter
|
import com.kunzisoft.keepass.adapters.FieldsAdapter
|
||||||
import com.kunzisoft.keepass.model.EntryInfo
|
import com.kunzisoft.keepass.model.EntryInfo
|
||||||
import com.kunzisoft.keepass.model.Field
|
import com.kunzisoft.keepass.model.Field
|
||||||
import com.kunzisoft.keepass.notifications.KeyboardEntryNotificationService
|
import com.kunzisoft.keepass.notifications.KeyboardEntryNotificationService
|
||||||
@@ -107,14 +105,14 @@ class MagikIME : InputMethodService(), KeyboardView.OnKeyboardActionListener {
|
|||||||
popupCustomKeys?.inputMethodMode = PopupWindow.INPUT_METHOD_NEEDED
|
popupCustomKeys?.inputMethodMode = PopupWindow.INPUT_METHOD_NEEDED
|
||||||
popupCustomKeys?.contentView = popupFieldsView
|
popupCustomKeys?.contentView = popupFieldsView
|
||||||
|
|
||||||
val recyclerView = popupFieldsView.findViewById<RecyclerView>(R.id.keyboard_popup_fields_list)
|
val recyclerView = popupFieldsView.findViewById<androidx.recyclerview.widget.RecyclerView>(R.id.keyboard_popup_fields_list)
|
||||||
fieldsAdapter = FieldsAdapter(this)
|
fieldsAdapter = FieldsAdapter(this)
|
||||||
fieldsAdapter?.onItemClickListener = object : FieldsAdapter.OnItemClickListener {
|
fieldsAdapter?.onItemClickListener = object : FieldsAdapter.OnItemClickListener {
|
||||||
override fun onItemClick(item: Field) {
|
override fun onItemClick(item: Field) {
|
||||||
currentInputConnection.commitText(item.protectedValue.toString(), 1)
|
currentInputConnection.commitText(item.protectedValue.toString(), 1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
recyclerView.layoutManager = LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, true)
|
recyclerView.layoutManager = androidx.recyclerview.widget.LinearLayoutManager(this, androidx.recyclerview.widget.LinearLayoutManager.HORIZONTAL, true)
|
||||||
recyclerView.adapter = fieldsAdapter
|
recyclerView.adapter = fieldsAdapter
|
||||||
|
|
||||||
val closeView = popupFieldsView.findViewById<View>(R.id.keyboard_popup_close)
|
val closeView = popupFieldsView.findViewById<View>(R.id.keyboard_popup_close)
|
||||||
@@ -194,7 +192,7 @@ class MagikIME : InputMethodService(), KeyboardView.OnKeyboardActionListener {
|
|||||||
KEY_BACK_KEYBOARD -> try {
|
KEY_BACK_KEYBOARD -> try {
|
||||||
val imeManager = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
|
val imeManager = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
|
||||||
if (window.window != null)
|
if (window.window != null)
|
||||||
imeManager.switchToLastInputMethod(window.window!!.attributes.token)
|
imeManager.switchToLastInputMethod(window.window!!.attributes.token) // TODO Deprecated
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Log.e(TAG, "Unable to switch to the previous IME", e)
|
Log.e(TAG, "Unable to switch to the previous IME", e)
|
||||||
val imeManager = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
|
val imeManager = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import android.content.BroadcastReceiver
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.IntentFilter
|
import android.content.IntentFilter
|
||||||
import android.support.v7.preference.PreferenceManager
|
import androidx.preference.PreferenceManager
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import com.kunzisoft.keepass.R
|
import com.kunzisoft.keepass.R
|
||||||
import com.kunzisoft.keepass.magikeyboard.MagikIME
|
import com.kunzisoft.keepass.magikeyboard.MagikIME
|
||||||
@@ -103,7 +103,9 @@ class KeyboardEntryNotificationService : NotificationService() {
|
|||||||
val keyboardTimeout = sharedPreferences.getString(getString(R.string.keyboard_entry_timeout_key),
|
val keyboardTimeout = sharedPreferences.getString(getString(R.string.keyboard_entry_timeout_key),
|
||||||
getString(R.string.timeout_default))
|
getString(R.string.timeout_default))
|
||||||
notificationTimeoutMilliSecs = try {
|
notificationTimeoutMilliSecs = try {
|
||||||
java.lang.Long.parseLong(keyboardTimeout)
|
keyboardTimeout?.let {
|
||||||
|
java.lang.Long.parseLong(keyboardTimeout)
|
||||||
|
} ?: 0
|
||||||
} catch (e: NumberFormatException) {
|
} catch (e: NumberFormatException) {
|
||||||
TimeoutHelper.DEFAULT_TIMEOUT
|
TimeoutHelper.DEFAULT_TIMEOUT
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import android.content.Context
|
|||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.IBinder
|
import android.os.IBinder
|
||||||
import android.support.v4.app.NotificationCompat
|
import androidx.core.app.NotificationCompat
|
||||||
import android.util.TypedValue
|
import android.util.TypedValue
|
||||||
import com.kunzisoft.keepass.R
|
import com.kunzisoft.keepass.R
|
||||||
import com.kunzisoft.keepass.activities.stylish.Stylish
|
import com.kunzisoft.keepass.activities.stylish.Stylish
|
||||||
|
|||||||
@@ -20,7 +20,7 @@
|
|||||||
package com.kunzisoft.keepass.settings
|
package com.kunzisoft.keepass.settings
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.support.v7.widget.Toolbar
|
import androidx.appcompat.widget.Toolbar
|
||||||
import android.view.MenuItem
|
import android.view.MenuItem
|
||||||
|
|
||||||
import com.kunzisoft.keepass.R
|
import com.kunzisoft.keepass.R
|
||||||
|
|||||||
@@ -20,7 +20,7 @@
|
|||||||
package com.kunzisoft.keepass.settings
|
package com.kunzisoft.keepass.settings
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.support.v7.preference.PreferenceFragmentCompat
|
import androidx.preference.PreferenceFragmentCompat
|
||||||
|
|
||||||
import com.kunzisoft.keepass.R
|
import com.kunzisoft.keepass.R
|
||||||
|
|
||||||
|
|||||||
@@ -21,8 +21,8 @@ package com.kunzisoft.keepass.settings
|
|||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.support.v7.preference.Preference
|
import androidx.preference.Preference
|
||||||
import android.support.v7.preference.PreferenceFragmentCompat
|
import androidx.preference.PreferenceFragmentCompat
|
||||||
import com.kunzisoft.keepass.R
|
import com.kunzisoft.keepass.R
|
||||||
import com.kunzisoft.keepass.activities.dialogs.AssignMasterKeyDialogFragment
|
import com.kunzisoft.keepass.activities.dialogs.AssignMasterKeyDialogFragment
|
||||||
import com.kunzisoft.keepass.database.element.Database
|
import com.kunzisoft.keepass.database.element.Database
|
||||||
|
|||||||
@@ -27,13 +27,13 @@ import android.net.Uri
|
|||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.provider.Settings
|
import android.provider.Settings
|
||||||
import android.support.annotation.RequiresApi
|
import androidx.annotation.RequiresApi
|
||||||
import android.support.v14.preference.SwitchPreference
|
import androidx.preference.SwitchPreference
|
||||||
import android.support.v4.app.DialogFragment
|
import androidx.fragment.app.DialogFragment
|
||||||
import android.support.v7.app.AlertDialog
|
import androidx.appcompat.app.AlertDialog
|
||||||
import android.support.v7.preference.Preference
|
import androidx.preference.Preference
|
||||||
import android.support.v7.preference.PreferenceCategory
|
import androidx.preference.PreferenceCategory
|
||||||
import android.support.v7.preference.PreferenceFragmentCompat
|
import androidx.preference.PreferenceFragmentCompat
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.view.autofill.AutofillManager
|
import android.view.autofill.AutofillManager
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
@@ -45,14 +45,13 @@ import com.kunzisoft.keepass.activities.dialogs.UnavailableFeatureDialogFragment
|
|||||||
import com.kunzisoft.keepass.activities.dialogs.UnderDevelopmentFeatureDialogFragment
|
import com.kunzisoft.keepass.activities.dialogs.UnderDevelopmentFeatureDialogFragment
|
||||||
import com.kunzisoft.keepass.activities.helpers.ReadOnlyHelper
|
import com.kunzisoft.keepass.activities.helpers.ReadOnlyHelper
|
||||||
import com.kunzisoft.keepass.activities.stylish.Stylish
|
import com.kunzisoft.keepass.activities.stylish.Stylish
|
||||||
|
import com.kunzisoft.keepass.app.database.FileDatabaseHistory
|
||||||
import com.kunzisoft.keepass.database.element.Database
|
import com.kunzisoft.keepass.database.element.Database
|
||||||
import com.kunzisoft.keepass.education.Education
|
import com.kunzisoft.keepass.education.Education
|
||||||
import com.kunzisoft.keepass.fileselect.database.FileDatabaseHistory
|
|
||||||
import com.kunzisoft.keepass.fingerprint.FingerPrintHelper
|
import com.kunzisoft.keepass.fingerprint.FingerPrintHelper
|
||||||
import com.kunzisoft.keepass.fingerprint.FingerPrintViewsManager
|
import com.kunzisoft.keepass.fingerprint.FingerPrintViewsManager
|
||||||
import com.kunzisoft.keepass.icons.IconPackChooser
|
import com.kunzisoft.keepass.icons.IconPackChooser
|
||||||
import com.kunzisoft.keepass.settings.preferencedialogfragment.*
|
import com.kunzisoft.keepass.settings.preferencedialogfragment.*
|
||||||
import java.lang.ref.WeakReference
|
|
||||||
|
|
||||||
class NestedSettingsFragment : PreferenceFragmentCompat(), Preference.OnPreferenceClickListener {
|
class NestedSettingsFragment : PreferenceFragmentCompat(), Preference.OnPreferenceClickListener {
|
||||||
|
|
||||||
@@ -119,7 +118,7 @@ class NestedSettingsFragment : PreferenceFragmentCompat(), Preference.OnPreferen
|
|||||||
val keyFile = findPreference(getString(R.string.keyfile_key))
|
val keyFile = findPreference(getString(R.string.keyfile_key))
|
||||||
keyFile.setOnPreferenceChangeListener { _, newValue ->
|
keyFile.setOnPreferenceChangeListener { _, newValue ->
|
||||||
if (!(newValue as Boolean)) {
|
if (!(newValue as Boolean)) {
|
||||||
FileDatabaseHistory.getInstance(WeakReference(activity.applicationContext)).deleteAllKeys()
|
FileDatabaseHistory.getInstance(activity.applicationContext).deleteAllKeyFiles()
|
||||||
}
|
}
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
@@ -127,24 +126,7 @@ class NestedSettingsFragment : PreferenceFragmentCompat(), Preference.OnPreferen
|
|||||||
val recentHistory = findPreference(getString(R.string.recentfile_key))
|
val recentHistory = findPreference(getString(R.string.recentfile_key))
|
||||||
recentHistory.setOnPreferenceChangeListener { _, newValue ->
|
recentHistory.setOnPreferenceChangeListener { _, newValue ->
|
||||||
if (!(newValue as Boolean)) {
|
if (!(newValue as Boolean)) {
|
||||||
FileDatabaseHistory.getInstance(WeakReference(activity.applicationContext)).deleteAll()
|
FileDatabaseHistory.getInstance(activity.applicationContext).deleteAll()
|
||||||
}
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
val storageAccessFramework = findPreference(getString(R.string.saf_key)) as SwitchPreference
|
|
||||||
storageAccessFramework.setOnPreferenceChangeListener { _, newValue ->
|
|
||||||
if (!(newValue as Boolean) && context != null) {
|
|
||||||
val alertDialog = AlertDialog.Builder(context!!)
|
|
||||||
.setMessage(getString(R.string.warning_disabling_storage_access_framework)).create()
|
|
||||||
alertDialog.setButton(AlertDialog.BUTTON_POSITIVE, getText(android.R.string.ok)
|
|
||||||
) { dialog, _ -> dialog.dismiss() }
|
|
||||||
alertDialog.setButton(AlertDialog.BUTTON_NEGATIVE, getText(android.R.string.cancel)
|
|
||||||
) { dialog, _ ->
|
|
||||||
storageAccessFramework.isChecked = true
|
|
||||||
dialog.dismiss()
|
|
||||||
}
|
|
||||||
alertDialog.show()
|
|
||||||
}
|
}
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
@@ -418,11 +400,11 @@ class NestedSettingsFragment : PreferenceFragmentCompat(), Preference.OnPreferen
|
|||||||
.setMessage(message)
|
.setMessage(message)
|
||||||
.create()
|
.create()
|
||||||
.apply {
|
.apply {
|
||||||
setButton(AlertDialog.BUTTON_POSITIVE, getText(android.R.string.ok))
|
setButton(AlertDialog.BUTTON_POSITIVE, getText(R.string.enable))
|
||||||
{ dialog, _ ->
|
{ dialog, _ ->
|
||||||
dialog.dismiss()
|
dialog.dismiss()
|
||||||
}
|
}
|
||||||
setButton(AlertDialog.BUTTON_NEGATIVE, getText(android.R.string.cancel))
|
setButton(AlertDialog.BUTTON_NEGATIVE, getText(R.string.disable))
|
||||||
{ dialog, _ ->
|
{ dialog, _ ->
|
||||||
copyPasswordPreference.isChecked = false
|
copyPasswordPreference.isChecked = false
|
||||||
dialog.dismiss()
|
dialog.dismiss()
|
||||||
|
|||||||
@@ -28,6 +28,11 @@ import java.util.*
|
|||||||
|
|
||||||
object PreferencesUtil {
|
object PreferencesUtil {
|
||||||
|
|
||||||
|
fun showReadOnlyWarning(context: Context): Boolean {
|
||||||
|
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
|
||||||
|
return prefs.getBoolean(context.getString(R.string.show_read_only_warning), true)
|
||||||
|
}
|
||||||
|
|
||||||
fun omitBackup(context: Context): Boolean {
|
fun omitBackup(context: Context): Boolean {
|
||||||
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
|
val prefs = PreferenceManager.getDefaultSharedPreferences(context)
|
||||||
return prefs.getBoolean(context.getString(R.string.omitbackup_key),
|
return prefs.getBoolean(context.getString(R.string.omitbackup_key),
|
||||||
|
|||||||
@@ -25,8 +25,8 @@ import android.content.DialogInterface
|
|||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.support.v4.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import android.support.v7.widget.Toolbar
|
import androidx.appcompat.widget.Toolbar
|
||||||
import android.view.MenuItem
|
import android.view.MenuItem
|
||||||
import com.kunzisoft.keepass.R
|
import com.kunzisoft.keepass.R
|
||||||
import com.kunzisoft.keepass.activities.dialogs.AssignMasterKeyDialogFragment
|
import com.kunzisoft.keepass.activities.dialogs.AssignMasterKeyDialogFragment
|
||||||
@@ -38,8 +38,10 @@ import com.kunzisoft.keepass.database.action.ProgressDialogSaveDatabaseThread
|
|||||||
import com.kunzisoft.keepass.database.element.Database
|
import com.kunzisoft.keepass.database.element.Database
|
||||||
import com.kunzisoft.keepass.timeout.TimeoutHelper
|
import com.kunzisoft.keepass.timeout.TimeoutHelper
|
||||||
|
|
||||||
|
open class SettingsActivity
|
||||||
open class SettingsActivity : LockingActivity(), MainPreferenceFragment.Callback, AssignMasterKeyDialogFragment.AssignPasswordDialogListener {
|
: LockingActivity(),
|
||||||
|
MainPreferenceFragment.Callback,
|
||||||
|
AssignMasterKeyDialogFragment.AssignPasswordDialogListener {
|
||||||
|
|
||||||
private var backupManager: BackupManager? = null
|
private var backupManager: BackupManager? = null
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,7 @@
|
|||||||
package com.kunzisoft.keepass.settings
|
package com.kunzisoft.keepass.settings
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.support.v4.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
|
|
||||||
class SettingsAutofillActivity : SettingsActivity() {
|
class SettingsAutofillActivity : SettingsActivity() {
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package com.kunzisoft.keepass.settings.preference
|
package com.kunzisoft.keepass.settings.preference
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.support.v7.preference.DialogPreference
|
import androidx.preference.DialogPreference
|
||||||
import android.util.AttributeSet
|
import android.util.AttributeSet
|
||||||
|
|
||||||
import com.kunzisoft.keepass.R
|
import com.kunzisoft.keepass.R
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package com.kunzisoft.keepass.settings.preference
|
package com.kunzisoft.keepass.settings.preference
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.support.v7.preference.ListPreference
|
import androidx.preference.ListPreference
|
||||||
import android.util.AttributeSet
|
import android.util.AttributeSet
|
||||||
import com.kunzisoft.keepass.R
|
import com.kunzisoft.keepass.R
|
||||||
import com.kunzisoft.keepass.icons.IconPackChooser
|
import com.kunzisoft.keepass.icons.IconPackChooser
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package com.kunzisoft.keepass.settings.preference
|
package com.kunzisoft.keepass.settings.preference
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.support.v7.preference.DialogPreference
|
import androidx.preference.DialogPreference
|
||||||
import android.util.AttributeSet
|
import android.util.AttributeSet
|
||||||
|
|
||||||
import com.kunzisoft.keepass.R
|
import com.kunzisoft.keepass.R
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package com.kunzisoft.keepass.settings.preference
|
package com.kunzisoft.keepass.settings.preference
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.support.v7.preference.DialogPreference
|
import androidx.preference.DialogPreference
|
||||||
import android.util.AttributeSet
|
import android.util.AttributeSet
|
||||||
|
|
||||||
import com.kunzisoft.keepass.R
|
import com.kunzisoft.keepass.R
|
||||||
|
|||||||
@@ -20,15 +20,17 @@
|
|||||||
package com.kunzisoft.keepass.settings.preferencedialogfragment
|
package com.kunzisoft.keepass.settings.preferencedialogfragment
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.support.v7.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import android.support.v7.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import com.kunzisoft.keepass.R
|
import com.kunzisoft.keepass.R
|
||||||
import com.kunzisoft.keepass.database.element.PwEncryptionAlgorithm
|
import com.kunzisoft.keepass.database.element.PwEncryptionAlgorithm
|
||||||
import com.kunzisoft.keepass.settings.preferencedialogfragment.adapter.ListRadioItemAdapter
|
import com.kunzisoft.keepass.settings.preferencedialogfragment.adapter.ListRadioItemAdapter
|
||||||
import com.kunzisoft.keepass.tasks.ActionRunnable
|
import com.kunzisoft.keepass.tasks.ActionRunnable
|
||||||
|
|
||||||
class DatabaseEncryptionAlgorithmPreferenceDialogFragmentCompat : DatabaseSavePreferenceDialogFragmentCompat(), ListRadioItemAdapter.RadioItemSelectedCallback<PwEncryptionAlgorithm> {
|
class DatabaseEncryptionAlgorithmPreferenceDialogFragmentCompat
|
||||||
|
: DatabaseSavePreferenceDialogFragmentCompat(),
|
||||||
|
ListRadioItemAdapter.RadioItemSelectedCallback<PwEncryptionAlgorithm> {
|
||||||
|
|
||||||
private var algorithmSelected: PwEncryptionAlgorithm? = null
|
private var algorithmSelected: PwEncryptionAlgorithm? = null
|
||||||
|
|
||||||
|
|||||||
@@ -20,16 +20,18 @@
|
|||||||
package com.kunzisoft.keepass.settings.preferencedialogfragment
|
package com.kunzisoft.keepass.settings.preferencedialogfragment
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.support.v7.preference.Preference
|
import androidx.preference.Preference
|
||||||
import android.support.v7.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import android.support.v7.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import com.kunzisoft.keepass.R
|
import com.kunzisoft.keepass.R
|
||||||
import com.kunzisoft.keepass.crypto.keyDerivation.KdfEngine
|
import com.kunzisoft.keepass.crypto.keyDerivation.KdfEngine
|
||||||
import com.kunzisoft.keepass.settings.preferencedialogfragment.adapter.ListRadioItemAdapter
|
import com.kunzisoft.keepass.settings.preferencedialogfragment.adapter.ListRadioItemAdapter
|
||||||
import com.kunzisoft.keepass.tasks.ActionRunnable
|
import com.kunzisoft.keepass.tasks.ActionRunnable
|
||||||
|
|
||||||
class DatabaseKeyDerivationPreferenceDialogFragmentCompat : DatabaseSavePreferenceDialogFragmentCompat(), ListRadioItemAdapter.RadioItemSelectedCallback<KdfEngine> {
|
class DatabaseKeyDerivationPreferenceDialogFragmentCompat
|
||||||
|
: DatabaseSavePreferenceDialogFragmentCompat(),
|
||||||
|
ListRadioItemAdapter.RadioItemSelectedCallback<KdfEngine> {
|
||||||
|
|
||||||
private var kdfEngineSelected: KdfEngine? = null
|
private var kdfEngineSelected: KdfEngine? = null
|
||||||
private var roundPreference: Preference? = null
|
private var roundPreference: Preference? = null
|
||||||
|
|||||||
@@ -19,8 +19,8 @@
|
|||||||
*/
|
*/
|
||||||
package com.kunzisoft.keepass.settings.preferencedialogfragment
|
package com.kunzisoft.keepass.settings.preferencedialogfragment
|
||||||
|
|
||||||
import android.support.annotation.StringRes
|
import androidx.annotation.StringRes
|
||||||
import android.support.v7.preference.PreferenceDialogFragmentCompat
|
import androidx.preference.PreferenceDialogFragmentCompat
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,7 @@
|
|||||||
package com.kunzisoft.keepass.settings.preferencedialogfragment.adapter
|
package com.kunzisoft.keepass.settings.preferencedialogfragment.adapter
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.support.v7.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
|
|||||||
@@ -23,16 +23,16 @@ import android.annotation.SuppressLint
|
|||||||
import android.app.Dialog
|
import android.app.Dialog
|
||||||
import android.content.DialogInterface
|
import android.content.DialogInterface
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.support.annotation.StringRes
|
import androidx.annotation.StringRes
|
||||||
import android.support.v4.app.DialogFragment
|
import androidx.fragment.app.DialogFragment
|
||||||
import android.support.v4.app.FragmentActivity
|
import androidx.fragment.app.FragmentActivity
|
||||||
import android.support.v7.app.AlertDialog
|
import androidx.appcompat.app.AlertDialog
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.widget.ProgressBar
|
import android.widget.ProgressBar
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
import com.kunzisoft.keepass.R
|
import com.kunzisoft.keepass.R
|
||||||
import com.kunzisoft.keepass.utils.lockScreenOrientation
|
import com.kunzisoft.keepass.view.lockScreenOrientation
|
||||||
import com.kunzisoft.keepass.utils.unlockScreenOrientation
|
import com.kunzisoft.keepass.view.unlockScreenOrientation
|
||||||
|
|
||||||
open class ProgressTaskDialogFragment : DialogFragment(), ProgressTaskUpdater {
|
open class ProgressTaskDialogFragment : DialogFragment(), ProgressTaskUpdater {
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
*/
|
*/
|
||||||
package com.kunzisoft.keepass.tasks
|
package com.kunzisoft.keepass.tasks
|
||||||
|
|
||||||
import android.support.annotation.StringRes
|
import androidx.annotation.StringRes
|
||||||
|
|
||||||
interface ProgressTaskUpdater {
|
interface ProgressTaskUpdater {
|
||||||
fun updateMessage(@StringRes resId: Int)
|
fun updateMessage(@StringRes resId: Int)
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import android.content.ClipboardManager
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.os.Handler
|
import android.os.Handler
|
||||||
import android.preference.PreferenceManager
|
import android.preference.PreferenceManager
|
||||||
|
import androidx.annotation.IntegerRes
|
||||||
import android.text.SpannableString
|
import android.text.SpannableString
|
||||||
import android.text.method.LinkMovementMethod
|
import android.text.method.LinkMovementMethod
|
||||||
import android.text.util.Linkify
|
import android.text.util.Linkify
|
||||||
@@ -46,7 +47,7 @@ class ClipboardHelper(private val context: Context) {
|
|||||||
|
|
||||||
@JvmOverloads
|
@JvmOverloads
|
||||||
fun timeoutCopyToClipboard(text: String, toastString: String = "") {
|
fun timeoutCopyToClipboard(text: String, toastString: String = "") {
|
||||||
if (!toastString.isEmpty())
|
if (toastString.isNotEmpty())
|
||||||
Toast.makeText(context, toastString, Toast.LENGTH_LONG).show()
|
Toast.makeText(context, toastString, Toast.LENGTH_LONG).show()
|
||||||
try {
|
try {
|
||||||
copyToClipboard(text)
|
copyToClipboard(text)
|
||||||
@@ -107,38 +108,37 @@ class ClipboardHelper(private val context: Context) {
|
|||||||
override fun run() {
|
override fun run() {
|
||||||
val currentClip = getClipboard(mCtx).toString()
|
val currentClip = getClipboard(mCtx).toString()
|
||||||
if (currentClip == mClearText) {
|
if (currentClip == mClearText) {
|
||||||
try {
|
|
||||||
|
@IntegerRes
|
||||||
|
val stringErrorId = try {
|
||||||
cleanClipboard()
|
cleanClipboard()
|
||||||
uiThreadCallback.post {
|
R.string.clipboard_cleared
|
||||||
Toast.makeText(mCtx,
|
|
||||||
R.string.clipboard_cleared,
|
|
||||||
Toast.LENGTH_LONG).show()
|
|
||||||
}
|
|
||||||
} catch (e: SamsungClipboardException) {
|
} catch (e: SamsungClipboardException) {
|
||||||
uiThreadCallback.post {
|
R.string.clipboard_error_clear
|
||||||
Toast.makeText(mCtx,
|
}
|
||||||
R.string.clipboard_error_clear,
|
uiThreadCallback.post {
|
||||||
Toast.LENGTH_LONG).show()
|
Toast.makeText(mCtx, stringErrorId, Toast.LENGTH_LONG).show()
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun showSamsungDialog() {
|
private fun showSamsungDialog() {
|
||||||
val text = context.getString(R.string.clipboard_error)+
|
val textDescription = context.getString(R.string.clipboard_error)+
|
||||||
System.getProperty("line.separator") +
|
System.getProperty("line.separator") +
|
||||||
context.getString(R.string.clipboard_error_url)
|
context.getString(R.string.clipboard_error_url)
|
||||||
val s = SpannableString(text)
|
val spannableString = SpannableString(textDescription)
|
||||||
val tv = TextView(context)
|
val textView = TextView(context).apply {
|
||||||
tv.text = s
|
text = spannableString
|
||||||
tv.autoLinkMask = Activity.RESULT_OK
|
autoLinkMask = Activity.RESULT_OK
|
||||||
tv.movementMethod = LinkMovementMethod.getInstance()
|
movementMethod = LinkMovementMethod.getInstance()
|
||||||
Linkify.addLinks(s, Linkify.WEB_URLS)
|
}
|
||||||
val builder = AlertDialog.Builder(context)
|
|
||||||
builder.setTitle(R.string.clipboard_error_title)
|
Linkify.addLinks(spannableString, Linkify.WEB_URLS)
|
||||||
|
AlertDialog.Builder(context)
|
||||||
|
.setTitle(R.string.clipboard_error_title)
|
||||||
|
.setView(textView)
|
||||||
.setPositiveButton(android.R.string.ok) { dialog, _ -> dialog.dismiss() }
|
.setPositiveButton(android.R.string.ok) { dialog, _ -> dialog.dismiss() }
|
||||||
.setView(tv)
|
|
||||||
.show()
|
.show()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
package com.kunzisoft.keepass.activities.helpers;
|
package com.kunzisoft.keepass.utils;
|
||||||
|
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
package com.kunzisoft.keepass.utils
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.net.Uri
|
||||||
|
import com.kunzisoft.keepass.app.database.FileDatabaseHistory
|
||||||
|
import com.kunzisoft.keepass.settings.PreferencesUtil
|
||||||
|
|
||||||
|
class FileDatabaseInfo : FileInfo {
|
||||||
|
|
||||||
|
constructor(context: Context, fileUri: Uri): super(context, fileUri)
|
||||||
|
|
||||||
|
constructor(context: Context, filePath: String): super(context, filePath)
|
||||||
|
|
||||||
|
fun retrieveDatabaseAlias(alias: String): String {
|
||||||
|
return when {
|
||||||
|
alias.isNotEmpty() -> alias
|
||||||
|
PreferencesUtil.isFullFilePathEnable(context) -> filePath ?: ""
|
||||||
|
else -> fileName ?: ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun retrieveDatabaseTitle(titleCallback: (String)->Unit) {
|
||||||
|
|
||||||
|
FileDatabaseHistory.getInstance(context.applicationContext).getFileDatabaseHistory(fileUri) {
|
||||||
|
fileDatabaseHistoryEntity ->
|
||||||
|
|
||||||
|
titleCallback.invoke(retrieveDatabaseAlias(fileDatabaseHistoryEntity?.databaseAlias ?: ""))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
92
app/src/main/java/com/kunzisoft/keepass/utils/FileInfo.kt
Normal file
92
app/src/main/java/com/kunzisoft/keepass/utils/FileInfo.kt
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
/*
|
||||||
|
* 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.utils
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.net.Uri
|
||||||
|
import androidx.documentfile.provider.DocumentFile
|
||||||
|
import com.kunzisoft.keepass.R
|
||||||
|
import java.io.File
|
||||||
|
import java.io.Serializable
|
||||||
|
import java.text.DateFormat
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
open class FileInfo : Serializable {
|
||||||
|
|
||||||
|
var context: Context
|
||||||
|
var fileUri: Uri
|
||||||
|
var filePath: String? = null
|
||||||
|
var fileName: String? = ""
|
||||||
|
var lastModification = Date()
|
||||||
|
var size: Long = 0L
|
||||||
|
|
||||||
|
constructor(context: Context, fileUri: Uri) {
|
||||||
|
this.context = context
|
||||||
|
this.fileUri = fileUri
|
||||||
|
init()
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(context: Context, filePath: String) {
|
||||||
|
this.context = context
|
||||||
|
this.fileUri = Uri.parse(filePath)
|
||||||
|
init()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun init() {
|
||||||
|
this.filePath = fileUri.path
|
||||||
|
if (EXTERNAL_STORAGE_AUTHORITY == fileUri.authority) {
|
||||||
|
DocumentFile.fromSingleUri(context, fileUri)?.let { file ->
|
||||||
|
size = file.length()
|
||||||
|
fileName = file.name
|
||||||
|
lastModification = Date(file.lastModified())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
filePath?.let {
|
||||||
|
File(it).let { file ->
|
||||||
|
size = file.length()
|
||||||
|
fileName = file.name
|
||||||
|
lastModification = Date(file.lastModified())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fileName == null || fileName!!.isEmpty()) {
|
||||||
|
fileName = filePath
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun found(): Boolean {
|
||||||
|
return size != 0L
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getModificationString(): String {
|
||||||
|
return DateFormat.getDateTimeInstance()
|
||||||
|
.format(lastModification)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getSizeString(): String {
|
||||||
|
return (size.toString() + " " + context.getString(R.string.bytes))
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
|
||||||
|
private const val EXTERNAL_STORAGE_AUTHORITY = "com.android.externalstorage.documents"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -21,7 +21,7 @@ package com.kunzisoft.keepass.utils;
|
|||||||
|
|
||||||
import android.os.Parcel;
|
import android.os.Parcel;
|
||||||
import android.os.Parcelable;
|
import android.os.Parcelable;
|
||||||
import android.support.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import com.kunzisoft.keepass.stream.ActionReadBytes;
|
import com.kunzisoft.keepass.stream.ActionReadBytes;
|
||||||
@@ -145,6 +145,7 @@ public class MemUtil {
|
|||||||
return compressedDataStream;
|
return compressedDataStream;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO Remove
|
||||||
|
|
||||||
// For writing to a Parcel
|
// For writing to a Parcel
|
||||||
public static <K extends Parcelable,V extends Parcelable> void writeParcelableMap(
|
public static <K extends Parcelable,V extends Parcelable> void writeParcelableMap(
|
||||||
|
|||||||
@@ -19,18 +19,16 @@
|
|||||||
*/
|
*/
|
||||||
package com.kunzisoft.keepass.utils
|
package com.kunzisoft.keepass.utils
|
||||||
|
|
||||||
import android.content.ActivityNotFoundException
|
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.view.Menu
|
import android.view.Menu
|
||||||
import android.view.MenuInflater
|
import android.view.MenuInflater
|
||||||
import android.view.MenuItem
|
import android.view.MenuItem
|
||||||
import android.widget.Toast
|
|
||||||
import com.kunzisoft.keepass.BuildConfig
|
import com.kunzisoft.keepass.BuildConfig
|
||||||
import com.kunzisoft.keepass.R
|
import com.kunzisoft.keepass.R
|
||||||
import com.kunzisoft.keepass.activities.AboutActivity
|
import com.kunzisoft.keepass.activities.AboutActivity
|
||||||
import com.kunzisoft.keepass.activities.helpers.ReadOnlyHelper.READ_ONLY_DEFAULT
|
import com.kunzisoft.keepass.activities.helpers.ReadOnlyHelper.READ_ONLY_DEFAULT
|
||||||
import com.kunzisoft.keepass.settings.SettingsActivity
|
|
||||||
import com.kunzisoft.keepass.activities.stylish.StylishActivity
|
import com.kunzisoft.keepass.activities.stylish.StylishActivity
|
||||||
|
import com.kunzisoft.keepass.settings.SettingsActivity
|
||||||
|
|
||||||
|
|
||||||
object MenuUtil {
|
object MenuUtil {
|
||||||
@@ -45,15 +43,8 @@ object MenuUtil {
|
|||||||
inflater.inflate(R.menu.default_menu, menu)
|
inflater.inflate(R.menu.default_menu, menu)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onContributionItemSelected(activity: StylishActivity): Boolean {
|
fun onContributionItemSelected(activity: StylishActivity) {
|
||||||
try {
|
UriUtil.gotoUrl(activity, R.string.contribution_url)
|
||||||
Util.gotoUrl(activity, R.string.contribution_url)
|
|
||||||
} catch (e: ActivityNotFoundException) {
|
|
||||||
Toast.makeText(activity, R.string.error_failed_to_launch_link, Toast.LENGTH_LONG).show()
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -62,7 +53,10 @@ object MenuUtil {
|
|||||||
@JvmOverloads
|
@JvmOverloads
|
||||||
fun onDefaultMenuOptionsItemSelected(activity: StylishActivity, item: MenuItem, readOnly: Boolean = READ_ONLY_DEFAULT, timeoutEnable: Boolean = false): Boolean {
|
fun onDefaultMenuOptionsItemSelected(activity: StylishActivity, item: MenuItem, readOnly: Boolean = READ_ONLY_DEFAULT, timeoutEnable: Boolean = false): Boolean {
|
||||||
when (item.itemId) {
|
when (item.itemId) {
|
||||||
R.id.menu_contribute -> return onContributionItemSelected(activity)
|
R.id.menu_contribute -> {
|
||||||
|
onContributionItemSelected(activity)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
R.id.menu_app_settings -> {
|
R.id.menu_app_settings -> {
|
||||||
// To avoid flickering when launch settings in a LockingActivity
|
// To avoid flickering when launch settings in a LockingActivity
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user