Kotlinized fragments

This commit is contained in:
J-Jamet
2019-07-10 23:37:40 +02:00
parent 6a5ed7f460
commit d937ca85a2
37 changed files with 1423 additions and 1606 deletions

View File

@@ -36,6 +36,7 @@ import android.widget.TextView
import android.widget.Toast
import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.activities.lock.LockingHideActivity
import com.kunzisoft.keepass.activities.view.EntryContentsView
import com.kunzisoft.keepass.app.App
import com.kunzisoft.keepass.database.element.EntryVersioned
import com.kunzisoft.keepass.database.element.PwNodeId
@@ -48,7 +49,6 @@ import com.kunzisoft.keepass.timeout.ClipboardHelper
import com.kunzisoft.keepass.timeout.TimeoutHelper
import com.kunzisoft.keepass.utils.MenuUtil
import com.kunzisoft.keepass.utils.Util
import com.kunzisoft.keepass.view.EntryContentsView
class EntryActivity : LockingHideActivity() {

View File

@@ -29,38 +29,26 @@ import android.view.Menu
import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import android.widget.EditText
import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.ScrollView
import android.widget.Toast
import android.widget.*
import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.activities.dialogs.GeneratePasswordDialogFragment
import com.kunzisoft.keepass.activities.dialogs.IconPickerDialogFragment
import com.kunzisoft.keepass.activities.dialogs.IconPickerDialogFragment.Companion.KEY_ICON_STANDARD
import com.kunzisoft.keepass.activities.lock.LockingHideActivity
import com.kunzisoft.keepass.activities.view.EntryEditCustomField
import com.kunzisoft.keepass.app.App
import com.kunzisoft.keepass.database.action.node.ActionNodeValues
import com.kunzisoft.keepass.database.action.node.AddEntryRunnable
import com.kunzisoft.keepass.database.action.node.AfterActionNodeFinishRunnable
import com.kunzisoft.keepass.database.action.node.UpdateEntryRunnable
import com.kunzisoft.keepass.database.element.Database
import com.kunzisoft.keepass.database.element.EntryVersioned
import com.kunzisoft.keepass.database.element.GroupVersioned
import com.kunzisoft.keepass.database.element.PwDate
import com.kunzisoft.keepass.database.element.PwIcon
import com.kunzisoft.keepass.database.element.PwIconStandard
import com.kunzisoft.keepass.database.element.PwNodeId
import com.kunzisoft.keepass.database.element.*
import com.kunzisoft.keepass.database.element.security.ProtectedString
import com.kunzisoft.keepass.dialogs.GeneratePasswordDialogFragment
import com.kunzisoft.keepass.dialogs.IconPickerDialogFragment
import com.kunzisoft.keepass.education.EntryEditActivityEducation
import com.kunzisoft.keepass.settings.PreferencesUtil
import com.kunzisoft.keepass.tasks.ActionRunnable
import com.kunzisoft.keepass.timeout.TimeoutHelper
import com.kunzisoft.keepass.utils.MenuUtil
import com.kunzisoft.keepass.utils.Util
import com.kunzisoft.keepass.view.EntryEditCustomField
import com.kunzisoft.keepass.dialogs.IconPickerDialogFragment.KEY_ICON_STANDARD
class EntryEditActivity : LockingHideActivity(), IconPickerDialogFragment.IconPickerListener, GeneratePasswordDialogFragment.GeneratePasswordListener {

View File

@@ -47,8 +47,8 @@ import com.kunzisoft.keepass.autofill.AutofillHelper
import com.kunzisoft.keepass.database.action.AssignPasswordInDatabaseRunnable
import com.kunzisoft.keepass.database.action.CreateDatabaseRunnable
import com.kunzisoft.keepass.database.action.ProgressDialogRunnable
import com.kunzisoft.keepass.dialogs.AssignMasterKeyDialogFragment
import com.kunzisoft.keepass.dialogs.CreateFileDialogFragment
import com.kunzisoft.keepass.activities.dialogs.AssignMasterKeyDialogFragment
import com.kunzisoft.keepass.activities.dialogs.CreateFileDialogFragment
import com.kunzisoft.keepass.education.FileDatabaseSelectActivityEducation
import com.kunzisoft.keepass.fileselect.*
import com.kunzisoft.keepass.fileselect.database.FileDatabaseHistory
@@ -376,8 +376,10 @@ class FileDatabaseSelectActivity : StylishActivity(),
}
override fun onDefinePathDialogPositiveClick(pathFile: Uri): Boolean {
override fun onDefinePathDialogPositiveClick(pathFile: Uri?): Boolean {
mDatabaseFileUri = pathFile
if (pathFile == null)
return false
return if (createDatabaseFile(pathFile)) {
AssignMasterKeyDialogFragment().show(supportFragmentManager, "passwordDialog")
true
@@ -385,7 +387,7 @@ class FileDatabaseSelectActivity : StylishActivity(),
false
}
override fun onDefinePathDialogNegativeClick(pathFile: Uri): Boolean {
override fun onDefinePathDialogNegativeClick(pathFile: Uri?): Boolean {
return true
}

View File

@@ -42,6 +42,7 @@ import android.view.MenuItem
import android.view.View
import android.widget.ImageView
import android.widget.TextView
import android.widget.Toast
import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.activities.lock.LockingActivity
@@ -62,12 +63,12 @@ import com.kunzisoft.keepass.database.action.node.MoveEntryRunnable
import com.kunzisoft.keepass.database.action.node.MoveGroupRunnable
import com.kunzisoft.keepass.database.action.node.UpdateGroupRunnable
import com.kunzisoft.keepass.database.element.*
import com.kunzisoft.keepass.dialogs.AssignMasterKeyDialogFragment
import com.kunzisoft.keepass.dialogs.GroupEditDialogFragment
import com.kunzisoft.keepass.dialogs.IconPickerDialogFragment
import com.kunzisoft.keepass.dialogs.PasswordEncodingDialogHelper
import com.kunzisoft.keepass.dialogs.ReadOnlyDialog
import com.kunzisoft.keepass.dialogs.SortDialogFragment
import com.kunzisoft.keepass.activities.dialogs.AssignMasterKeyDialogFragment
import com.kunzisoft.keepass.activities.dialogs.GroupEditDialogFragment
import com.kunzisoft.keepass.activities.dialogs.IconPickerDialogFragment
import com.kunzisoft.keepass.activities.dialogs.PasswordEncodingDialogHelper
import com.kunzisoft.keepass.activities.dialogs.ReadOnlyDialog
import com.kunzisoft.keepass.activities.dialogs.SortDialogFragment
import com.kunzisoft.keepass.education.GroupActivityEducation
import com.kunzisoft.keepass.magikeyboard.KeyboardEntryNotificationService
import com.kunzisoft.keepass.magikeyboard.KeyboardHelper
@@ -77,7 +78,7 @@ import com.kunzisoft.keepass.model.Field
import com.kunzisoft.keepass.settings.PreferencesUtil
import com.kunzisoft.keepass.timeout.TimeoutHelper
import com.kunzisoft.keepass.utils.MenuUtil
import com.kunzisoft.keepass.view.AddNodeButtonView
import com.kunzisoft.keepass.activities.view.AddNodeButtonView
import net.cachapa.expandablelayout.ExpandableLayout
@@ -774,54 +775,59 @@ class GroupActivity : LockingActivity(),
AssignMasterKeyDialogFragment().show(supportFragmentManager, "passwordDialog")
}
override fun approveEditGroup(action: GroupEditDialogFragment.EditGroupDialogAction,
name: String,
icon: PwIcon) {
override fun approveEditGroup(action: GroupEditDialogFragment.EditGroupDialogAction?,
name: String?,
icon: PwIcon?) {
val database = App.currentDatabase
when (action) {
GroupEditDialogFragment.EditGroupDialogAction.CREATION -> {
// If group creation
mCurrentGroup?.let { currentGroup ->
// Build the group
database.createGroup()?.let { newGroup->
newGroup.title = name
newGroup.icon = icon
// Not really needed here because added in runnable but safe
newGroup.parent = currentGroup
if (name.isNullOrEmpty() || icon == null)
Toast.makeText(this, R.string.error_no_name, Toast.LENGTH_LONG).show()
else {
when (action) {
GroupEditDialogFragment.EditGroupDialogAction.CREATION -> {
// If group creation
mCurrentGroup?.let { currentGroup ->
// Build the group
database.createGroup()?.let { newGroup ->
newGroup.title = name
newGroup.icon = icon
// Not really needed here because added in runnable but safe
newGroup.parent = currentGroup
// If group created save it in the database
Thread(AddGroupRunnable(this,
App.currentDatabase,
newGroup,
currentGroup,
AfterAddNodeRunnable(),
!readOnly)
).start()
// If group created save it in the database
Thread(AddGroupRunnable(this,
App.currentDatabase,
newGroup,
currentGroup,
AfterAddNodeRunnable(),
!readOnly)
).start()
}
}
}
GroupEditDialogFragment.EditGroupDialogAction.UPDATE ->
// If update add new elements
mOldGroupToUpdate?.let { oldGroupToUpdate ->
GroupVersioned(oldGroupToUpdate).let { updateGroup ->
updateGroup.title = name
// TODO custom icon
updateGroup.icon = icon
listNodesFragment?.removeNode(oldGroupToUpdate)
// If group updated save it in the database
Thread(UpdateGroupRunnable(this,
App.currentDatabase,
oldGroupToUpdate,
updateGroup,
AfterUpdateNodeRunnable(),
!readOnly)
).start()
}
}
else -> {
}
}
GroupEditDialogFragment.EditGroupDialogAction.UPDATE ->
// If update add new elements
mOldGroupToUpdate?.let { oldGroupToUpdate ->
GroupVersioned(oldGroupToUpdate).let { updateGroup ->
updateGroup.title = name
// TODO custom icon
updateGroup.icon = icon
listNodesFragment?.removeNode(oldGroupToUpdate)
// If group updated save it in the database
Thread(UpdateGroupRunnable(this,
App.currentDatabase,
oldGroupToUpdate,
updateGroup,
AfterUpdateNodeRunnable(),
!readOnly)
).start()
}
}
else -> {}
}
}
@@ -874,9 +880,9 @@ class GroupActivity : LockingActivity(),
}
}
override fun cancelEditGroup(action: GroupEditDialogFragment.EditGroupDialogAction,
name: String,
iconId: PwIcon) {
override fun cancelEditGroup(action: GroupEditDialogFragment.EditGroupDialogAction?,
name: String?,
iconId: PwIcon?) {
// Do nothing here
}

View File

@@ -20,7 +20,7 @@ import com.kunzisoft.keepass.adapters.NodeAdapter
import com.kunzisoft.keepass.database.SortNodeEnum
import com.kunzisoft.keepass.database.element.GroupVersioned
import com.kunzisoft.keepass.database.element.NodeVersioned
import com.kunzisoft.keepass.dialogs.SortDialogFragment
import com.kunzisoft.keepass.activities.dialogs.SortDialogFragment
import com.kunzisoft.keepass.settings.PreferencesUtil
import com.kunzisoft.keepass.activities.stylish.StylishFragment

View File

@@ -52,7 +52,7 @@ import com.kunzisoft.keepass.autofill.AutofillHelper
import com.kunzisoft.keepass.database.action.LoadDatabaseRunnable
import com.kunzisoft.keepass.database.action.ProgressDialogRunnable
import com.kunzisoft.keepass.database.element.Database
import com.kunzisoft.keepass.dialogs.PasswordEncodingDialogHelper
import com.kunzisoft.keepass.activities.dialogs.PasswordEncodingDialogHelper
import com.kunzisoft.keepass.education.PasswordActivityEducation
import com.kunzisoft.keepass.fileselect.KeyFileHelper
import com.kunzisoft.keepass.fingerprint.FingerPrintAnimatedVector

View File

@@ -17,7 +17,7 @@
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
*
*/
package com.kunzisoft.keepass.dialogs
package com.kunzisoft.keepass.activities.dialogs
import android.app.Dialog
import android.content.Context
@@ -57,7 +57,6 @@ class AssignMasterKeyDialogFragment : DialogFragment() {
interface AssignPasswordDialogListener {
fun onAssignKeyDialogPositiveClick(masterPasswordChecked: Boolean, masterPassword: String?,
keyFileChecked: Boolean, keyFile: Uri?)
fun onAssignKeyDialogNegativeClick(masterPasswordChecked: Boolean, masterPassword: String?,
keyFileChecked: Boolean, keyFile: Uri?)
}
@@ -74,10 +73,9 @@ class AssignMasterKeyDialogFragment : DialogFragment() {
}
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
activity?.let { notNullActivity ->
val builder = AlertDialog.Builder(notNullActivity)
val inflater = notNullActivity.layoutInflater
activity?.let { activity ->
val builder = AlertDialog.Builder(activity)
val inflater = activity.layoutInflater
rootView = inflater.inflate(R.layout.set_password, null)
builder.setView(rootView)
@@ -126,14 +124,12 @@ class AssignMasterKeyDialogFragment : DialogFragment() {
mKeyFile = null
var error = verifyPassword() || verifyFile()
if (!passwordCheckBox!!.isChecked && !keyFileCheckBox!!.isChecked) {
error = true
showNoKeyConfirmationDialog()
}
if (!error) {
mListener!!.onAssignKeyDialogPositiveClick(
mListener?.onAssignKeyDialogPositiveClick(
passwordCheckBox!!.isChecked, mMasterPassword,
keyFileCheckBox!!.isChecked, mKeyFile)
dismiss()

View File

@@ -0,0 +1,211 @@
/*
* 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.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 { dialog1 ->
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 && mDatabaseFileExtension != null) {
var path = Uri.Builder().path(folderPathView!!.text.toString())
.appendPath(fileNameView!!.text.toString() + mDatabaseFileExtension!!)
.build()
path = UriUtil.translate(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
}
}

View File

@@ -0,0 +1,188 @@
/*
* 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.content.Context
import android.os.Bundle
import android.support.v4.app.DialogFragment
import android.support.v7.app.AlertDialog
import android.view.View
import android.widget.*
import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.password.PasswordGenerator
import com.kunzisoft.keepass.settings.PreferencesUtil
import com.kunzisoft.keepass.utils.Util
class GeneratePasswordDialogFragment : DialogFragment() {
private var mListener: GeneratePasswordListener? = null
private var root: View? = null
private var lengthTextView: EditText? = null
private var passwordView: EditText? = null
private var uppercaseBox: CompoundButton? = null
private var lowercaseBox: CompoundButton? = null
private var digitsBox: CompoundButton? = null
private var minusBox: CompoundButton? = null
private var underlineBox: CompoundButton? = null
private var spaceBox: CompoundButton? = null
private var specialsBox: CompoundButton? = null
private var bracketsBox: CompoundButton? = null
private var extendedBox: CompoundButton? = null
override fun onAttach(context: Context?) {
super.onAttach(context)
try {
mListener = context as GeneratePasswordListener?
} catch (e: ClassCastException) {
throw ClassCastException(context?.toString()
+ " must implement " + GeneratePasswordListener::class.java.name)
}
}
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
activity?.let { activity ->
val builder = AlertDialog.Builder(activity)
val inflater = activity.layoutInflater
root = inflater.inflate(R.layout.generate_password, null)
passwordView = root?.findViewById(R.id.password)
Util.applyFontVisibilityTo(context, passwordView)
lengthTextView = root?.findViewById(R.id.length)
uppercaseBox = root?.findViewById(R.id.cb_uppercase)
lowercaseBox = root?.findViewById(R.id.cb_lowercase)
digitsBox = root?.findViewById(R.id.cb_digits)
minusBox = root?.findViewById(R.id.cb_minus)
underlineBox = root?.findViewById(R.id.cb_underline)
spaceBox = root?.findViewById(R.id.cb_space)
specialsBox = root?.findViewById(R.id.cb_specials)
bracketsBox = root?.findViewById(R.id.cb_brackets)
extendedBox = root?.findViewById(R.id.cb_extended)
assignDefaultCharacters()
val seekBar = root?.findViewById<SeekBar>(R.id.seekbar_length)
seekBar?.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {
override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {
lengthTextView?.setText(progress.toString())
}
override fun onStartTrackingTouch(seekBar: SeekBar) {}
override fun onStopTrackingTouch(seekBar: SeekBar) {}
})
seekBar?.progress = PreferencesUtil.getDefaultPasswordLength(context)
root?.findViewById<Button>(R.id.generate_password_button)
?.setOnClickListener { fillPassword() }
builder.setView(root)
.setPositiveButton(R.string.accept) { _, _ ->
val bundle = Bundle()
bundle.putString(KEY_PASSWORD_ID, passwordView!!.text.toString())
mListener?.acceptPassword(bundle)
dismiss()
}
.setNegativeButton(R.string.cancel) { _, _ ->
val bundle = Bundle()
mListener?.cancelPassword(bundle)
dismiss()
}
// Pre-populate a password to possibly save the user a few clicks
fillPassword()
return builder.create()
}
return super.onCreateDialog(savedInstanceState)
}
private fun assignDefaultCharacters() {
uppercaseBox?.isChecked = false
lowercaseBox?.isChecked = false
digitsBox?.isChecked = false
minusBox?.isChecked = false
underlineBox?.isChecked = false
spaceBox?.isChecked = false
specialsBox?.isChecked = false
bracketsBox?.isChecked = false
extendedBox?.isChecked = false
val defaultPasswordChars = PreferencesUtil.getDefaultPasswordCharacters(context)
for (passwordChar in defaultPasswordChars) {
when (passwordChar) {
getString(R.string.value_password_uppercase) -> uppercaseBox?.isChecked = true
getString(R.string.value_password_lowercase) -> lowercaseBox?.isChecked = true
getString(R.string.value_password_digits) -> digitsBox?.isChecked = true
getString(R.string.value_password_minus) -> minusBox?.isChecked = true
getString(R.string.value_password_underline) -> underlineBox?.isChecked = true
getString(R.string.value_password_space) -> spaceBox?.isChecked = true
getString(R.string.value_password_special) -> specialsBox?.isChecked = true
getString(R.string.value_password_brackets) -> bracketsBox?.isChecked = true
getString(R.string.value_password_extended) -> extendedBox?.isChecked = true
}
}
}
private fun fillPassword() {
root?.findViewById<EditText>(R.id.password)?.setText(generatePassword())
}
fun generatePassword(): String {
var password = ""
try {
val length = Integer.valueOf(root?.findViewById<EditText>(R.id.length)?.text.toString())
val generator = PasswordGenerator(activity)
password = generator.generatePassword(length,
uppercaseBox?.isChecked == true,
lowercaseBox?.isChecked == true,
digitsBox?.isChecked == true,
minusBox?.isChecked == true,
underlineBox?.isChecked == true,
spaceBox?.isChecked == true,
specialsBox?.isChecked == true,
bracketsBox?.isChecked == true,
extendedBox?.isChecked == true)
} catch (e: NumberFormatException) {
Toast.makeText(context, R.string.error_wrong_length, Toast.LENGTH_LONG).show()
} catch (e: IllegalArgumentException) {
Toast.makeText(context, e.message, Toast.LENGTH_LONG).show()
}
return password
}
interface GeneratePasswordListener {
fun acceptPassword(bundle: Bundle)
fun cancelPassword(bundle: Bundle)
}
companion object {
const val KEY_PASSWORD_ID = "KEY_PASSWORD_ID"
}
}

View File

@@ -0,0 +1,199 @@
/*
* 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.content.Context
import android.graphics.Color
import android.os.Bundle
import android.support.v4.app.DialogFragment
import android.support.v7.app.AlertDialog
import android.widget.ImageView
import android.widget.TextView
import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.activities.dialogs.GroupEditDialogFragment.EditGroupDialogAction.CREATION
import com.kunzisoft.keepass.activities.dialogs.GroupEditDialogFragment.EditGroupDialogAction.UPDATE
import com.kunzisoft.keepass.app.App
import com.kunzisoft.keepass.database.element.Database
import com.kunzisoft.keepass.database.element.GroupVersioned
import com.kunzisoft.keepass.database.element.PwIcon
class GroupEditDialogFragment : DialogFragment(), IconPickerDialogFragment.IconPickerListener {
private var mDatabase: Database? = null
private var editGroupListener: EditGroupListener? = null
private var editGroupDialogAction: EditGroupDialogAction? = null
private var nameGroup: String? = null
private var iconGroup: PwIcon? = null
private var iconButton: ImageView? = null
private var iconColor: Int = 0
enum class EditGroupDialogAction {
CREATION, UPDATE, NONE;
companion object {
fun getActionFromOrdinal(ordinal: Int): EditGroupDialogAction {
return values()[ordinal]
}
}
}
override fun onAttach(context: Context?) {
super.onAttach(context)
// Verify that the host activity implements the callback interface
try {
// Instantiate the NoticeDialogListener so we can send events to the host
editGroupListener = context as EditGroupListener?
} catch (e: ClassCastException) {
// The activity doesn't implement the interface, throw exception
throw ClassCastException(context?.toString()
+ " must implement " + GroupEditDialogFragment::class.java.name)
}
}
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
activity?.let { activity ->
val root = activity.layoutInflater.inflate(R.layout.group_edit, null)
val nameField = root?.findViewById<TextView>(R.id.group_edit_name)
iconButton = root?.findViewById(R.id.group_edit_icon_button)
// Retrieve the textColor to tint the icon
val ta = activity.theme.obtainStyledAttributes(intArrayOf(android.R.attr.textColorPrimary))
iconColor = ta.getColor(0, Color.WHITE)
ta.recycle()
// Init elements
mDatabase = App.currentDatabase
editGroupDialogAction = EditGroupDialogAction.NONE
nameGroup = ""
iconGroup = mDatabase?.iconFactory?.folderIcon
if (savedInstanceState != null
&& savedInstanceState.containsKey(KEY_ACTION_ID)
&& savedInstanceState.containsKey(KEY_NAME)
&& savedInstanceState.containsKey(KEY_ICON)) {
editGroupDialogAction = EditGroupDialogAction.getActionFromOrdinal(savedInstanceState.getInt(KEY_ACTION_ID))
nameGroup = savedInstanceState.getString(KEY_NAME)
iconGroup = savedInstanceState.getParcelable(KEY_ICON)
} else {
arguments?.apply {
if (containsKey(KEY_ACTION_ID))
editGroupDialogAction = EditGroupDialogAction.getActionFromOrdinal(getInt(KEY_ACTION_ID))
if (containsKey(KEY_NAME) && containsKey(KEY_ICON)) {
nameGroup = getString(KEY_NAME)
iconGroup = getParcelable(KEY_ICON)
}
}
}
// populate the name
nameField?.text = nameGroup
// populate the icon
assignIconView()
val builder = AlertDialog.Builder(activity)
builder.setView(root)
.setPositiveButton(android.R.string.ok) { _, _ ->
editGroupListener?.approveEditGroup(
editGroupDialogAction,
nameField?.text.toString(),
iconGroup)
this@GroupEditDialogFragment.dialog.cancel()
}
.setNegativeButton(R.string.cancel) { _, _ ->
editGroupListener?.cancelEditGroup(
editGroupDialogAction,
nameField?.text.toString(),
iconGroup)
this@GroupEditDialogFragment.dialog.cancel()
}
iconButton?.setOnClickListener { _ ->
fragmentManager?.let {
IconPickerDialogFragment().show(it, "IconPickerDialogFragment")
}
}
return builder.create()
}
return super.onCreateDialog(savedInstanceState)
}
private fun assignIconView() {
mDatabase?.drawFactory
?.assignDatabaseIconTo(
context,
iconButton,
iconGroup,
iconColor)
}
override fun iconPicked(bundle: Bundle) {
iconGroup = bundle.getParcelable(IconPickerDialogFragment.KEY_ICON_STANDARD)
assignIconView()
}
override fun onSaveInstanceState(outState: Bundle) {
outState.putInt(KEY_ACTION_ID, editGroupDialogAction!!.ordinal)
outState.putString(KEY_NAME, nameGroup)
outState.putParcelable(KEY_ICON, iconGroup)
super.onSaveInstanceState(outState)
}
interface EditGroupListener {
fun approveEditGroup(action: EditGroupDialogAction?, name: String?, icon: PwIcon?)
fun cancelEditGroup(action: EditGroupDialogAction?, name: String?, icon: PwIcon?)
}
companion object {
const val TAG_CREATE_GROUP = "TAG_CREATE_GROUP"
const val KEY_NAME = "KEY_NAME"
const val KEY_ICON = "KEY_ICON"
const val KEY_ACTION_ID = "KEY_ACTION_ID"
fun build(): GroupEditDialogFragment {
val bundle = Bundle()
bundle.putInt(KEY_ACTION_ID, CREATION.ordinal)
val fragment = GroupEditDialogFragment()
fragment.arguments = bundle
return fragment
}
fun build(group: GroupVersioned): GroupEditDialogFragment {
val bundle = Bundle()
bundle.putString(KEY_NAME, group.title)
bundle.putParcelable(KEY_ICON, group.icon)
bundle.putInt(KEY_ACTION_ID, UPDATE.ordinal)
val fragment = GroupEditDialogFragment()
fragment.arguments = bundle
return fragment
}
}
}

View File

@@ -0,0 +1,138 @@
/*
* 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.content.Context
import android.content.res.ColorStateList
import android.graphics.Color
import android.os.Bundle
import android.support.v4.app.DialogFragment
import android.support.v4.widget.ImageViewCompat
import android.support.v7.app.AlertDialog
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.BaseAdapter
import android.widget.GridView
import android.widget.ImageView
import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.activities.stylish.StylishActivity
import com.kunzisoft.keepass.database.element.PwIconStandard
import com.kunzisoft.keepass.icons.IconPack
import com.kunzisoft.keepass.icons.IconPackChooser
class IconPickerDialogFragment : DialogFragment() {
private var iconPickerListener: IconPickerListener? = null
private var iconPack: IconPack? = null
override fun onAttach(context: Context?) {
super.onAttach(context)
try {
iconPickerListener = context as IconPickerListener?
} catch (e: ClassCastException) {
// The activity doesn't implement the interface, throw exception
throw ClassCastException(context!!.toString()
+ " must implement " + IconPickerListener::class.java.name)
}
}
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
activity?.let { activity ->
val builder = AlertDialog.Builder(activity)
iconPack = IconPackChooser.getSelectedIconPack(context)
// Inflate and set the layout for the dialog
// Pass null as the parent view because its going in the dialog layout
val root = activity.layoutInflater.inflate(R.layout.icon_picker, null)
builder.setView(root)
val currIconGridView = root.findViewById<GridView>(R.id.IconGridView)
currIconGridView.adapter = ImageAdapter(activity)
currIconGridView.setOnItemClickListener { _, _, position, _ ->
val bundle = Bundle()
bundle.putParcelable(KEY_ICON_STANDARD, PwIconStandard(position))
iconPickerListener?.iconPicked(bundle)
dismiss()
}
builder.setNegativeButton(R.string.cancel) { _, _ -> this@IconPickerDialogFragment.dialog.cancel() }
return builder.create()
}
return super.onCreateDialog(savedInstanceState)
}
inner class ImageAdapter internal constructor(private val context: Context) : BaseAdapter() {
override fun getCount(): Int {
return iconPack?.numberOfIcons() ?: 0
}
override fun getItem(position: Int): Any? {
return null
}
override fun getItemId(position: Int): Long {
return 0
}
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
val currentView: View = convertView
?: (context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater)
.inflate(R.layout.icon, parent, false)
iconPack?.let { iconPack ->
val iconImageView = currentView.findViewById<ImageView>(R.id.icon_image)
iconImageView.setImageResource(iconPack.iconToResId(position))
// Assign color if icons are tintable
if (iconPack.tintable()) {
// Retrieve the textColor to tint the icon
val ta = context.theme.obtainStyledAttributes(intArrayOf(android.R.attr.textColor))
ImageViewCompat.setImageTintList(iconImageView, ColorStateList.valueOf(ta.getColor(0, Color.BLACK)))
ta?.recycle()
}
}
return currentView
}
}
interface IconPickerListener {
fun iconPicked(bundle: Bundle)
}
companion object {
const val KEY_ICON_STANDARD = "KEY_ICON_STANDARD"
fun launch(activity: StylishActivity) {
// Create an instance of the dialog fragment and show it
val dialog = IconPickerDialogFragment()
dialog.show(activity.supportFragmentManager, "IconPickerDialogFragment")
}
}
}

View File

@@ -0,0 +1,67 @@
/*
* 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.content.Intent
import android.content.Intent.FLAG_ACTIVITY_NEW_TASK
import android.os.Bundle
import android.provider.Settings
import android.support.v4.app.DialogFragment
import android.support.v7.app.AlertDialog
import android.view.View
import com.kunzisoft.keepass.BuildConfig
import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.utils.Util
class KeyboardExplanationDialogFragment : DialogFragment() {
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
activity?.let {
val builder = AlertDialog.Builder(activity!!)
val inflater = activity!!.layoutInflater
val rootView = inflater.inflate(R.layout.keyboard_explanation, null)
rootView.findViewById<View>(R.id.keyboards_activate_setting_path1_text)
.setOnClickListener { launchActivateKeyboardSetting() }
rootView.findViewById<View>(R.id.keyboards_activate_setting_path2_text)
.setOnClickListener { launchActivateKeyboardSetting() }
val containerKeyboardSwitcher = rootView.findViewById<View>(R.id.container_keyboard_switcher)
if (BuildConfig.CLOSED_STORE) {
containerKeyboardSwitcher.setOnClickListener { Util.gotoUrl(context, R.string.keyboard_switcher_play_store) }
} else {
containerKeyboardSwitcher.setOnClickListener { Util.gotoUrl(context, R.string.keyboard_switcher_f_droid) }
}
builder.setView(rootView)
.setPositiveButton(android.R.string.ok) { _, _ -> }
return builder.create()
}
return super.onCreateDialog(savedInstanceState)
}
private fun launchActivateKeyboardSetting() {
val intent = Intent(Settings.ACTION_INPUT_METHOD_SETTINGS)
intent.addFlags(FLAG_ACTIVITY_NEW_TASK)
startActivity(intent)
}
}

View File

@@ -17,7 +17,7 @@
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
*
*/
package com.kunzisoft.keepass.dialogs
package com.kunzisoft.keepass.activities.dialogs
import android.app.AlertDialog
import android.content.Context

View File

@@ -0,0 +1,75 @@
/*
* 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.content.ActivityNotFoundException
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.widget.Toast
import com.kunzisoft.keepass.BuildConfig
import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.utils.Util
/**
* Custom Dialog that asks the user to download the pro version or make a donation.
*/
class ProFeatureDialogFragment : DialogFragment() {
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
activity?.let { activity ->
// Use the Builder class for convenient dialog construction
val builder = AlertDialog.Builder(activity)
val stringBuilder = SpannableStringBuilder()
if (BuildConfig.CLOSED_STORE) {
// TODO HtmlCompat with androidX
stringBuilder.append(Html.fromHtml(getString(R.string.html_text_ad_free))).append("\n\n")
stringBuilder.append(Html.fromHtml(getString(R.string.html_text_buy_pro)))
builder.setPositiveButton(R.string.download) { _, _ ->
try {
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 {
stringBuilder.append(Html.fromHtml(getString(R.string.html_text_feature_generosity))).append("\n\n")
stringBuilder.append(Html.fromHtml(getString(R.string.html_text_donation)))
builder.setPositiveButton(R.string.contribute) { _, _ ->
try {
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.setNegativeButton(android.R.string.cancel) { _, _ -> dismiss() }
// Create the AlertDialog object and return it
return builder.create()
}
return super.onCreateDialog(savedInstanceState)
}
}

View File

@@ -0,0 +1,51 @@
/*
* 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.AlertDialog
import android.content.Context
import android.os.Build
import android.os.Bundle
import android.preference.PreferenceManager
import com.kunzisoft.keepass.R
class ReadOnlyDialog(context: Context) : AlertDialog(context) {
override fun onCreate(savedInstanceState: Bundle) {
val ctx = context
var warning = ctx.getString(R.string.read_only_warning)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
warning = warning + "\n\n" + context.getString(R.string.read_only_kitkat_warning)
}
setMessage(warning)
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)
}
}

View File

@@ -0,0 +1,189 @@
/*
* 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.content.Context
import android.os.Bundle
import android.support.annotation.IdRes
import android.support.v4.app.DialogFragment
import android.support.v7.app.AlertDialog
import android.view.View
import android.widget.CompoundButton
import android.widget.RadioGroup
import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.database.SortNodeEnum
class SortDialogFragment : DialogFragment() {
private var mListener: SortSelectionListener? = null
private var mSortNodeEnum: SortNodeEnum? = null
@IdRes
private var mCheckedId: Int = 0
private var mGroupsBefore: Boolean = false
private var mAscending: Boolean = false
private var mRecycleBinBottom: Boolean = false
override fun onAttach(context: Context?) {
super.onAttach(context)
try {
mListener = context as SortSelectionListener?
} catch (e: ClassCastException) {
throw ClassCastException(context!!.toString()
+ " must implement " + SortSelectionListener::class.java.name)
}
}
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
activity?.let { activity ->
val builder = AlertDialog.Builder(activity)
mSortNodeEnum = SortNodeEnum.TITLE
mAscending = true
mGroupsBefore = true
var recycleBinAllowed = false
mRecycleBinBottom = true
arguments?.apply {
if (containsKey(SORT_NODE_ENUM_BUNDLE_KEY))
getString(SORT_NODE_ENUM_BUNDLE_KEY)?.let {
mSortNodeEnum = SortNodeEnum.valueOf(it)
}
if (containsKey(SORT_ASCENDING_BUNDLE_KEY))
mAscending = getBoolean(SORT_ASCENDING_BUNDLE_KEY)
if (containsKey(SORT_GROUPS_BEFORE_BUNDLE_KEY))
mGroupsBefore = getBoolean(SORT_GROUPS_BEFORE_BUNDLE_KEY)
if (containsKey(SORT_RECYCLE_BIN_BOTTOM_BUNDLE_KEY)) {
recycleBinAllowed = true
mRecycleBinBottom = getBoolean(SORT_RECYCLE_BIN_BOTTOM_BUNDLE_KEY)
}
}
mCheckedId = retrieveViewFromEnum(mSortNodeEnum!!)
val rootView = activity.layoutInflater.inflate(R.layout.sort_selection, null)
builder.setTitle(R.string.sort_menu)
builder.setView(rootView)
// Add action buttons
.setPositiveButton(android.R.string.ok
) { _, _ -> mListener?.onSortSelected(mSortNodeEnum!!, mAscending, mGroupsBefore, mRecycleBinBottom) }
.setNegativeButton(R.string.cancel) { _, _ -> }
val ascendingView = rootView.findViewById<CompoundButton>(R.id.sort_selection_ascending)
// Check if is ascending or descending
ascendingView.isChecked = mAscending
ascendingView.setOnCheckedChangeListener { _, isChecked -> mAscending = isChecked }
val groupsBeforeView = rootView.findViewById<CompoundButton>(R.id.sort_selection_groups_before)
// Check if groups before
groupsBeforeView.isChecked = mGroupsBefore
groupsBeforeView.setOnCheckedChangeListener { _, isChecked -> mGroupsBefore = isChecked }
val recycleBinBottomView = rootView.findViewById<CompoundButton>(R.id.sort_selection_recycle_bin_bottom)
if (!recycleBinAllowed) {
recycleBinBottomView.visibility = View.GONE
} else {
// Check if recycle bin at the bottom
recycleBinBottomView.isChecked = mRecycleBinBottom
recycleBinBottomView.setOnCheckedChangeListener { _, isChecked -> mRecycleBinBottom = isChecked }
}
val sortSelectionRadioGroupView = rootView.findViewById<RadioGroup>(R.id.sort_selection_radio_group)
// Check value by default
sortSelectionRadioGroupView.check(mCheckedId)
sortSelectionRadioGroupView.setOnCheckedChangeListener { _, checkedId -> mSortNodeEnum = retrieveSortEnumFromViewId(checkedId) }
return builder.create()
}
return super.onCreateDialog(savedInstanceState)
}
@IdRes
private fun retrieveViewFromEnum(sortNodeEnum: SortNodeEnum): Int {
return when (sortNodeEnum) {
SortNodeEnum.DB -> R.id.sort_selection_db
SortNodeEnum.TITLE -> R.id.sort_selection_title
SortNodeEnum.USERNAME -> R.id.sort_selection_username
SortNodeEnum.CREATION_TIME -> R.id.sort_selection_creation_time
SortNodeEnum.LAST_MODIFY_TIME -> R.id.sort_selection_last_modify_time
SortNodeEnum.LAST_ACCESS_TIME -> R.id.sort_selection_last_access_time
}
}
private fun retrieveSortEnumFromViewId(@IdRes checkedId: Int): SortNodeEnum {
// Change enum
return when (checkedId) {
R.id.sort_selection_db -> SortNodeEnum.DB
R.id.sort_selection_title -> SortNodeEnum.TITLE
R.id.sort_selection_username -> SortNodeEnum.USERNAME
R.id.sort_selection_creation_time -> SortNodeEnum.CREATION_TIME
R.id.sort_selection_last_modify_time -> SortNodeEnum.LAST_MODIFY_TIME
R.id.sort_selection_last_access_time -> SortNodeEnum.LAST_ACCESS_TIME
else -> SortNodeEnum.TITLE
}
}
interface SortSelectionListener {
fun onSortSelected(sortNodeEnum: SortNodeEnum,
ascending: Boolean,
groupsBefore: Boolean,
recycleBinBottom: Boolean)
}
companion object {
private const val SORT_NODE_ENUM_BUNDLE_KEY = "SORT_NODE_ENUM_BUNDLE_KEY"
private const val SORT_ASCENDING_BUNDLE_KEY = "SORT_ASCENDING_BUNDLE_KEY"
private const val SORT_GROUPS_BEFORE_BUNDLE_KEY = "SORT_GROUPS_BEFORE_BUNDLE_KEY"
private const val SORT_RECYCLE_BIN_BOTTOM_BUNDLE_KEY = "SORT_RECYCLE_BIN_BOTTOM_BUNDLE_KEY"
private fun buildBundle(sortNodeEnum: SortNodeEnum,
ascending: Boolean,
groupsBefore: Boolean): Bundle {
val bundle = Bundle()
bundle.putString(SORT_NODE_ENUM_BUNDLE_KEY, sortNodeEnum.name)
bundle.putBoolean(SORT_ASCENDING_BUNDLE_KEY, ascending)
bundle.putBoolean(SORT_GROUPS_BEFORE_BUNDLE_KEY, groupsBefore)
return bundle
}
fun getInstance(sortNodeEnum: SortNodeEnum,
ascending: Boolean,
groupsBefore: Boolean): SortDialogFragment {
val bundle = buildBundle(sortNodeEnum, ascending, groupsBefore)
val fragment = SortDialogFragment()
fragment.arguments = bundle
return fragment
}
fun getInstance(sortNodeEnum: SortNodeEnum,
ascending: Boolean,
groupsBefore: Boolean,
recycleBinBottom: Boolean): SortDialogFragment {
val bundle = buildBundle(sortNodeEnum, ascending, groupsBefore)
bundle.putBoolean(SORT_RECYCLE_BIN_BOTTOM_BUNDLE_KEY, recycleBinBottom)
val fragment = SortDialogFragment()
fragment.arguments = bundle
return fragment
}
}
}

View File

@@ -0,0 +1,125 @@
/*
* 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.os.Build
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.method.LinkMovementMethod
import android.widget.TextView
import com.kunzisoft.keepass.R
class UnavailableFeatureDialogFragment : DialogFragment() {
private var minVersionRequired = Build.VERSION_CODES.M
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
activity?.let { activity ->
arguments?.apply {
if (containsKey(MIN_REQUIRED_VERSION_ARG))
minVersionRequired = getInt(MIN_REQUIRED_VERSION_ARG)
}
val rootView = activity.layoutInflater.inflate(R.layout.unavailable_feature, null)
val messageView = rootView.findViewById<TextView>(R.id.unavailable_feature_message)
val builder = AlertDialog.Builder(activity)
val message = SpannableStringBuilder()
message.append(getString(R.string.unavailable_feature_text))
.append("\n\n")
if (Build.VERSION.SDK_INT < minVersionRequired) {
message.append(getString(R.string.unavailable_feature_version,
androidNameFromApiNumber(Build.VERSION.SDK_INT, Build.VERSION.RELEASE),
androidNameFromApiNumber(minVersionRequired)))
message.append("\n\n")
.append(Html.fromHtml("<a href=\"https://source.android.com/setup/build-numbers\">CodeNames</a>"))
} else
message.append(getString(R.string.unavailable_feature_hardware))
messageView.text = message
messageView.movementMethod = LinkMovementMethod.getInstance()
builder.setView(rootView)
.setPositiveButton(android.R.string.ok) { _, _ -> }
return builder.create()
}
return super.onCreateDialog(savedInstanceState)
}
private fun androidNameFromApiNumber(apiNumber: Int, releaseVersion: String = ""): String {
var version = releaseVersion
val builder = StringBuilder()
val fields = Build.VERSION_CODES::class.java.fields
var apiName = ""
for (field in fields) {
val fieldName = field.name
var fieldValue = -1
try {
fieldValue = field.getInt(Any())
} catch (e: IllegalArgumentException) {
e.printStackTrace()
} catch (e: IllegalAccessException) {
e.printStackTrace()
} catch (e: NullPointerException) {
e.printStackTrace()
}
if (fieldValue == apiNumber) {
apiName = fieldName
}
}
if (apiName.isEmpty()) {
val mapper = arrayOf("ANDROID BASE", "ANDROID BASE 1.1", "CUPCAKE", "DONUT", "ECLAIR", "ECLAIR_0_1", "ECLAIR_MR1", "FROYO", "GINGERBREAD", "GINGERBREAD_MR1", "HONEYCOMB", "HONEYCOMB_MR1", "HONEYCOMB_MR2", "ICE_CREAM_SANDWICH", "ICE_CREAM_SANDWICH_MR1", "JELLY_BEAN", "JELLY_BEAN", "JELLY_BEAN", "KITKAT", "KITKAT", "LOLLIPOOP", "LOLLIPOOP_MR1", "MARSHMALLOW", "NOUGAT", "NOUGAT", "OREO", "OREO")
val index = apiNumber - 1
apiName = if (index < mapper.size) mapper[index] else "UNKNOWN_VERSION"
}
if (version.isEmpty()) {
val versions = arrayOf("1.0", "1.1", "1.5", "1.6", "2.0", "2.0.1", "2.1", "2.2.X", "2.3", "2.3.3", "3.0", "3.1", "3.2.0", "4.0.1", "4.0.3", "4.1.0", "4.2.0", "4.3.0", "4.4", "4.4", "5.0", "5.1", "6.0", "7.0", "7.1", "8.0.0", "8.1.0")
val index = apiNumber - 1
version = if (index < versions.size) versions[index] else "UNKNOWN_VERSION"
}
builder.append("\n\t")
if (apiName.isNotEmpty())
builder.append(apiName).append(" ")
if (version.isNotEmpty())
builder.append(version).append(" ")
builder.append("(API ").append(apiNumber).append(")")
builder.append("\n")
return builder.toString()
}
companion object {
private const val MIN_REQUIRED_VERSION_ARG = "MIN_REQUIRED_VERSION_ARG"
fun getInstance(minVersionRequired: Int): UnavailableFeatureDialogFragment {
val fragment = UnavailableFeatureDialogFragment()
val args = Bundle()
args.putInt(MIN_REQUIRED_VERSION_ARG, minVersionRequired)
fragment.arguments = args
return fragment
}
}
}

View File

@@ -0,0 +1,85 @@
/*
* 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.content.ActivityNotFoundException
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.widget.Toast
import com.kunzisoft.keepass.BuildConfig
import com.kunzisoft.keepass.R
import com.kunzisoft.keepass.utils.Util
/**
* Custom Dialog that asks the user to download the pro version or make a donation.
*/
class UnderDevelopmentFeatureDialogFragment : DialogFragment() {
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
activity?.let { activity ->
// Use the Builder class for convenient dialog construction
val builder = AlertDialog.Builder(activity!!)
val stringBuilder = SpannableStringBuilder()
if (BuildConfig.CLOSED_STORE) {
if (BuildConfig.FULL_VERSION) {
stringBuilder.append(Html.fromHtml(getString(R.string.html_text_dev_feature_thanks))).append("\n\n")
.append(Html.fromHtml(getString(R.string.html_rose))).append("\n\n")
.append(Html.fromHtml(getString(R.string.html_text_dev_feature_work_hard))).append("\n")
.append(Html.fromHtml(getString(R.string.html_text_dev_feature_upgrade))).append(" ")
builder.setPositiveButton(android.R.string.ok) { dialog, id -> dismiss() }
} else {
stringBuilder.append(Html.fromHtml(getString(R.string.html_text_dev_feature))).append("\n\n")
.append(Html.fromHtml(getString(R.string.html_text_dev_feature_buy_pro))).append("\n")
.append(Html.fromHtml(getString(R.string.html_text_dev_feature_encourage)))
builder.setPositiveButton(R.string.download) { dialog, id ->
try {
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) { dialog, id -> dismiss() }
}
} else {
stringBuilder.append(Html.fromHtml(getString(R.string.html_text_dev_feature))).append("\n\n")
.append(Html.fromHtml(getString(R.string.html_text_dev_feature_contibute))).append(" ")
.append(Html.fromHtml(getString(R.string.html_text_dev_feature_encourage)))
builder.setPositiveButton(R.string.contribute) { dialog, id ->
try {
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) { dialog, id -> dismiss() }
}
builder.setMessage(stringBuilder)
// Create the AlertDialog object and return it
return builder.create()
}
return super.onCreateDialog(savedInstanceState)
}
}

View File

@@ -17,7 +17,7 @@
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
*
*/
package com.kunzisoft.keepass.view;
package com.kunzisoft.keepass.activities.view;
import android.annotation.SuppressLint;
import android.content.Context;
@@ -61,12 +61,12 @@ public class AddNodeButtonView extends RelativeLayout {
public AddNodeButtonView(Context context) {
this(context, null);
}
public AddNodeButtonView(Context context, AttributeSet attrs) {
super(context, attrs);
inflate(context);
}
protected void inflate(Context context) {
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
assert inflater != null;

View File

@@ -16,7 +16,7 @@
* 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.view
package com.kunzisoft.keepass.activities.view
import android.content.Context
import android.graphics.Color

View File

@@ -17,7 +17,7 @@
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
*
*/
package com.kunzisoft.keepass.view;
package com.kunzisoft.keepass.activities.view;
import android.content.Context;
import android.support.v4.content.ContextCompat;

View File

@@ -17,7 +17,7 @@
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
*
*/
package com.kunzisoft.keepass.view;
package com.kunzisoft.keepass.activities.view;
import android.content.Context;
import android.text.method.PasswordTransformationMethod;

View File

@@ -17,7 +17,7 @@
* along with KeePass DX. If not, see <http://www.gnu.org/licenses/>.
*
*/
package com.kunzisoft.keepass.view;
package com.kunzisoft.keepass.activities.view;
import android.content.Context;
import android.util.AttributeSet;

View File

@@ -1,216 +0,0 @@
/*
* Copyright 2017 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.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.annotation.NonNull;
import android.support.v4.app.DialogFragment;
import android.support.v7.app.AlertDialog;
import android.view.ActionMode;
import android.view.LayoutInflater;
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;
import java.io.File;
public class CreateFileDialogFragment extends DialogFragment implements AdapterView.OnItemSelectedListener{
private final int FILE_CODE = 3853;
private EditText folderPathView;
private EditText fileNameView;
private DefinePathDialogListener mListener;
private String extension;
private Uri uriPath;
private Button positiveButton;
private Button negativeButton;
public interface DefinePathDialogListener {
boolean onDefinePathDialogPositiveClick(Uri pathFile);
boolean onDefinePathDialogNegativeClick(Uri pathFile);
}
@Override
public void onAttach(Context activity) {
super.onAttach(activity);
try {
mListener = (DefinePathDialogListener) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString()
+ " must implement " + DefinePathDialogListener.class.getName());
}
}
@NonNull
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
assert getActivity() != null;
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
LayoutInflater inflater = getActivity().getLayoutInflater();
View rootView = inflater.inflate(R.layout.file_creation, null);
builder.setView(rootView)
.setTitle(R.string.create_keepass_file)
// Add action buttons
.setPositiveButton(android.R.string.ok, (dialog, id) -> {})
.setNegativeButton(R.string.cancel, (dialog, id) -> {});
// To prevent crash issue #69 https://github.com/Kunzisoft/KeePassDX/issues/69
ActionMode.Callback actionCopyBarCallback = new ActionMode.Callback() {
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
if (positiveButton != null && negativeButton != null) {
positiveButton.setEnabled(false);
negativeButton.setEnabled(false);
}
return true;
}
public void onDestroyActionMode(ActionMode mode) {
if (positiveButton != null && negativeButton != null) {
positiveButton.setEnabled(true);
negativeButton.setEnabled(true);
}
}
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
return true;
}
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
return true;
}
};
// Folder selection
View browseView = rootView.findViewById(R.id.browse_button);
folderPathView = rootView.findViewById(R.id.folder_path);
folderPathView.setCustomSelectionActionModeCallback(actionCopyBarCallback);
fileNameView = rootView.findViewById(R.id.filename);
fileNameView.setCustomSelectionActionModeCallback(actionCopyBarCallback);
String defaultPath = Environment.getExternalStorageDirectory().getPath()
+ getString(R.string.database_file_path_default);
folderPathView.setText(defaultPath);
browseView.setOnClickListener(v -> {
Intent i = new Intent(getContext(), FilePickerStylishActivity.class);
i.putExtra(FilePickerActivity.EXTRA_ALLOW_MULTIPLE, false);
i.putExtra(FilePickerActivity.EXTRA_ALLOW_CREATE_DIR, true);
i.putExtra(FilePickerActivity.EXTRA_MODE, FilePickerActivity.MODE_DIR);
i.putExtra(FilePickerActivity.EXTRA_START_PATH,
Environment.getExternalStorageDirectory().getPath());
startActivityForResult(i, FILE_CODE);
});
// Init path
uriPath = null;
// Extension
extension = getString(R.string.database_file_extension_default);
Spinner spinner = rootView.findViewById(R.id.file_types);
spinner.setOnItemSelectedListener(this);
// Spinner Drop down elements
String[] fileTypes = getResources().getStringArray(R.array.file_types);
ArrayAdapter<String> dataAdapter = new ArrayAdapter<>(getActivity(), android.R.layout.simple_spinner_item, fileTypes);
dataAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spinner.setAdapter(dataAdapter);
// Or text if only one item https://github.com/Kunzisoft/KeePassDX/issues/105
if (fileTypes.length == 1) {
ViewGroup.LayoutParams params = spinner.getLayoutParams();
spinner.setVisibility(View.GONE);
TextView extensionTextView = new TextView(getContext());
extensionTextView.setText(extension);
extensionTextView.setLayoutParams(params);
ViewGroup parentView = (ViewGroup) spinner.getParent();
parentView.addView(extensionTextView);
}
AlertDialog dialog = builder.create();
dialog.setOnShowListener(dialog1 -> {
positiveButton = dialog.getButton(DialogInterface.BUTTON_POSITIVE);
negativeButton = dialog.getButton(DialogInterface.BUTTON_NEGATIVE);
positiveButton.setOnClickListener(v -> {
if(mListener.onDefinePathDialogPositiveClick(buildPath()))
dismiss();
});
negativeButton.setOnClickListener(v -> {
if(mListener.onDefinePathDialogNegativeClick(buildPath())) {
dismiss();
}
});
});
return dialog;
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (data != null && requestCode == FILE_CODE && resultCode == Activity.RESULT_OK) {
uriPath = data.getData();
if (uriPath != null) {
File file = Utils.getFileForUri(uriPath);
folderPathView.setText(file.getPath());
}
}
}
@Override
public void onItemSelected(AdapterView<?> adapterView, View view, int position, long id) {
extension = adapterView.getItemAtPosition(position).toString();
}
@Override
public void onNothingSelected(AdapterView<?> adapterView) {
// Do nothing
}
private Uri buildPath() {
Uri path = new Uri.Builder().path(folderPathView.getText().toString())
.appendPath(fileNameView.getText().toString() + extension)
.build();
path = UriUtil.translate(getContext(), path);
return path;
}
}

View File

@@ -1,214 +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.dialogs;
import android.app.Dialog;
import android.content.Context;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v4.app.DialogFragment;
import android.support.v7.app.AlertDialog;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.Button;
import android.widget.CompoundButton;
import android.widget.EditText;
import android.widget.SeekBar;
import android.widget.Toast;
import com.kunzisoft.keepass.R;
import com.kunzisoft.keepass.password.PasswordGenerator;
import com.kunzisoft.keepass.settings.PreferencesUtil;
import com.kunzisoft.keepass.utils.Util;
import java.util.Set;
public class GeneratePasswordDialogFragment extends DialogFragment {
public static final String KEY_PASSWORD_ID = "KEY_PASSWORD_ID";
private GeneratePasswordListener mListener;
private View root;
private EditText lengthTextView;
private EditText passwordView;
private CompoundButton uppercaseBox;
private CompoundButton lowercaseBox;
private CompoundButton digitsBox;
private CompoundButton minusBox;
private CompoundButton underlineBox;
private CompoundButton spaceBox;
private CompoundButton specialsBox;
private CompoundButton bracketsBox;
private CompoundButton extendedBox;
@Override
public void onAttach(Context context) {
super.onAttach(context);
try {
mListener = (GeneratePasswordListener) context;
} catch (ClassCastException e) {
throw new ClassCastException(context.toString()
+ " must implement " + GeneratePasswordListener.class.getName());
}
}
@NonNull
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
LayoutInflater inflater = getActivity().getLayoutInflater();
root = inflater.inflate(R.layout.generate_password, null);
passwordView = root.findViewById(R.id.password);
Util.applyFontVisibilityTo(getContext(), passwordView);
lengthTextView = root.findViewById(R.id.length);
uppercaseBox = root.findViewById(R.id.cb_uppercase);
lowercaseBox = root.findViewById(R.id.cb_lowercase);
digitsBox = root.findViewById(R.id.cb_digits);
minusBox = root.findViewById(R.id.cb_minus);
underlineBox = root.findViewById(R.id.cb_underline);
spaceBox = root.findViewById(R.id.cb_space);
specialsBox = root.findViewById(R.id.cb_specials);
bracketsBox = root.findViewById(R.id.cb_brackets);
extendedBox = root.findViewById(R.id.cb_extended);
assignDefaultCharacters();
SeekBar seekBar = root.findViewById(R.id.seekbar_length);
seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
lengthTextView.setText(String.valueOf(progress));
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {}
});
seekBar.setProgress(PreferencesUtil.getDefaultPasswordLength(getContext()));
Button genPassButton = root.findViewById(R.id.generate_password_button);
genPassButton.setOnClickListener(v -> fillPassword());
builder.setView(root)
.setPositiveButton(R.string.accept, (dialog, id) -> {
Bundle bundle = new Bundle();
bundle.putString(KEY_PASSWORD_ID, passwordView.getText().toString());
mListener.acceptPassword(bundle);
dismiss();
})
.setNegativeButton(R.string.cancel, (dialog, id) -> {
Bundle bundle = new Bundle();
mListener.cancelPassword(bundle);
dismiss();
});
// Pre-populate a password to possibly save the user a few clicks
fillPassword();
return builder.create();
}
private void assignDefaultCharacters() {
uppercaseBox.setChecked(false);
lowercaseBox.setChecked(false);
digitsBox.setChecked(false);
minusBox.setChecked(false);
underlineBox.setChecked(false);
spaceBox.setChecked(false);
specialsBox.setChecked(false);
bracketsBox.setChecked(false);
extendedBox.setChecked(false);
Set<String> defaultPasswordChars =
PreferencesUtil.getDefaultPasswordCharacters(getContext());
for(String passwordChar : defaultPasswordChars) {
if (passwordChar.equals(getString(R.string.value_password_uppercase))) {
uppercaseBox.setChecked(true);
}
else if (passwordChar.equals(getString(R.string.value_password_lowercase))) {
lowercaseBox.setChecked(true);
}
else if (passwordChar.equals(getString(R.string.value_password_digits))) {
digitsBox.setChecked(true);
}
else if (passwordChar.equals(getString(R.string.value_password_minus))) {
minusBox.setChecked(true);
}
else if (passwordChar.equals(getString(R.string.value_password_underline))) {
underlineBox.setChecked(true);
}
else if (passwordChar.equals(getString(R.string.value_password_space))) {
spaceBox.setChecked(true);
}
else if (passwordChar.equals(getString(R.string.value_password_special))) {
specialsBox.setChecked(true);
}
else if (passwordChar.equals(getString(R.string.value_password_brackets))) {
bracketsBox.setChecked(true);
}
else if (passwordChar.equals(getString(R.string.value_password_extended))) {
extendedBox.setChecked(true);
}
}
}
private void fillPassword() {
EditText txtPassword = root.findViewById(R.id.password);
txtPassword.setText(generatePassword());
}
public String generatePassword() {
String password = "";
try {
int length = Integer.valueOf(((EditText) root.findViewById(R.id.length)).getText().toString());
PasswordGenerator generator = new PasswordGenerator(getActivity());
password = generator.generatePassword(length,
uppercaseBox.isChecked(),
lowercaseBox.isChecked(),
digitsBox.isChecked(),
minusBox.isChecked(),
underlineBox.isChecked(),
spaceBox.isChecked(),
specialsBox.isChecked(),
bracketsBox.isChecked(),
extendedBox.isChecked());
} catch (NumberFormatException e) {
Toast.makeText(getContext(), R.string.error_wrong_length, Toast.LENGTH_LONG).show();
} catch (IllegalArgumentException e) {
Toast.makeText(getContext(), e.getMessage(), Toast.LENGTH_LONG).show();
}
return password;
}
public interface GeneratePasswordListener {
void acceptPassword(Bundle bundle);
void cancelPassword(Bundle bundle);
}
}

View File

@@ -1,216 +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.dialogs;
import android.app.Dialog;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v4.app.DialogFragment;
import android.support.v7.app.AlertDialog;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import com.kunzisoft.keepass.R;
import com.kunzisoft.keepass.app.App;
import com.kunzisoft.keepass.database.element.Database;
import com.kunzisoft.keepass.database.element.GroupVersioned;
import com.kunzisoft.keepass.database.element.PwIcon;
import static com.kunzisoft.keepass.dialogs.GroupEditDialogFragment.EditGroupDialogAction.CREATION;
import static com.kunzisoft.keepass.dialogs.GroupEditDialogFragment.EditGroupDialogAction.UPDATE;
import static com.kunzisoft.keepass.dialogs.GroupEditDialogFragment.EditGroupDialogAction.getActionFromOrdinal;
public class GroupEditDialogFragment extends DialogFragment
implements IconPickerDialogFragment.IconPickerListener {
public static final String TAG_CREATE_GROUP = "TAG_CREATE_GROUP";
public static final String KEY_NAME = "KEY_NAME";
public static final String KEY_ICON = "KEY_ICON";
public static final String KEY_ACTION_ID = "KEY_ACTION_ID";
private Database database;
private EditGroupListener editGroupListener;
private EditGroupDialogAction editGroupDialogAction;
private String nameGroup;
private PwIcon iconGroup;
private ImageView iconButton;
private int iconColor;
public enum EditGroupDialogAction {
CREATION, UPDATE, NONE;
public static EditGroupDialogAction getActionFromOrdinal(int ordinal) {
return EditGroupDialogAction.values()[ordinal];
}
}
public static GroupEditDialogFragment build() {
Bundle bundle = new Bundle();
bundle.putInt(KEY_ACTION_ID, CREATION.ordinal());
GroupEditDialogFragment fragment = new GroupEditDialogFragment();
fragment.setArguments(bundle);
return fragment;
}
public static GroupEditDialogFragment build(GroupVersioned group) {
Bundle bundle = new Bundle();
bundle.putString(KEY_NAME, group.getTitle());
bundle.putParcelable(KEY_ICON, group.getIcon());
bundle.putInt(KEY_ACTION_ID, UPDATE.ordinal());
GroupEditDialogFragment fragment = new GroupEditDialogFragment();
fragment.setArguments(bundle);
return fragment;
}
@Override
public void onAttach(Context context) {
super.onAttach(context);
// Verify that the host activity implements the callback interface
try {
// Instantiate the NoticeDialogListener so we can send events to the host
editGroupListener = (EditGroupListener) context;
} catch (ClassCastException e) {
// The activity doesn't implement the interface, throw exception
throw new ClassCastException(context.toString()
+ " must implement " + GroupEditDialogFragment.class.getName());
}
}
@NonNull
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
assert getActivity() != null;
LayoutInflater inflater = getActivity().getLayoutInflater();
View root = inflater.inflate(R.layout.group_edit, null);
TextView nameField = root.findViewById(R.id.group_edit_name);
iconButton = root.findViewById(R.id.group_edit_icon_button);
// Retrieve the textColor to tint the icon
int[] attrs = {android.R.attr.textColorPrimary};
TypedArray ta = getActivity().getTheme().obtainStyledAttributes(attrs);
iconColor = ta.getColor(0, Color.WHITE);
ta.recycle();
// Init elements
database = App.Companion.getCurrentDatabase();
editGroupDialogAction = EditGroupDialogAction.NONE;
nameGroup = "";
iconGroup = database.getIconFactory().getFolderIcon();
if (savedInstanceState != null
&& savedInstanceState.containsKey(KEY_ACTION_ID)
&& savedInstanceState.containsKey(KEY_NAME)
&& savedInstanceState.containsKey(KEY_ICON)) {
editGroupDialogAction = getActionFromOrdinal(savedInstanceState.getInt(KEY_ACTION_ID));
nameGroup = savedInstanceState.getString(KEY_NAME);
iconGroup = savedInstanceState.getParcelable(KEY_ICON);
} else {
if (getArguments() != null
&& getArguments().containsKey(KEY_ACTION_ID))
editGroupDialogAction = EditGroupDialogAction.getActionFromOrdinal(getArguments().getInt(KEY_ACTION_ID));
if (getArguments() != null
&& getArguments().containsKey(KEY_NAME)
&& getArguments().containsKey(KEY_ICON)) {
nameGroup = getArguments().getString(KEY_NAME);
iconGroup = getArguments().getParcelable(KEY_ICON);
}
}
// populate the name
nameField.setText(nameGroup);
// populate the icon
assignIconView();
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setView(root)
.setPositiveButton(android.R.string.ok, (dialog, id) -> {
String name = nameField.getText().toString();
if ( name.length() > 0 ) {
editGroupListener.approveEditGroup(
editGroupDialogAction,
name,
iconGroup);
GroupEditDialogFragment.this.getDialog().cancel();
}
else {
Toast.makeText(getContext(), R.string.error_no_name, Toast.LENGTH_LONG).show();
}
})
.setNegativeButton(R.string.cancel, (dialog, id) -> {
String name = nameField.getText().toString();
editGroupListener.cancelEditGroup(
editGroupDialogAction,
name,
iconGroup);
GroupEditDialogFragment.this.getDialog().cancel();
});
iconButton.setOnClickListener(v -> {
IconPickerDialogFragment iconPickerDialogFragment = new IconPickerDialogFragment();
if (getFragmentManager() != null)
iconPickerDialogFragment.show(getFragmentManager(), "IconPickerDialogFragment");
});
return builder.create();
}
private void assignIconView() {
database.getDrawFactory()
.assignDatabaseIconTo(
getContext(),
iconButton,
iconGroup,
iconColor);
}
@Override
public void iconPicked(Bundle bundle) {
iconGroup = bundle.getParcelable(IconPickerDialogFragment.KEY_ICON_STANDARD);
assignIconView();
}
@Override
public void onSaveInstanceState(Bundle outState) {
outState.putInt(KEY_ACTION_ID, editGroupDialogAction.ordinal());
outState.putString(KEY_NAME, nameGroup);
outState.putParcelable(KEY_ICON, iconGroup);
super.onSaveInstanceState(outState);
}
public interface EditGroupListener {
void approveEditGroup(EditGroupDialogAction action, String name, PwIcon selectedIcon);
void cancelEditGroup(EditGroupDialogAction action, String name, PwIcon selectedIcon);
}
}

View File

@@ -1,152 +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.dialogs;
import android.app.Dialog;
import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v4.app.DialogFragment;
import android.support.v4.widget.ImageViewCompat;
import android.support.v7.app.AlertDialog;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.GridView;
import android.widget.ImageView;
import com.kunzisoft.keepass.R;
import com.kunzisoft.keepass.database.element.PwIconStandard;
import com.kunzisoft.keepass.icons.IconPack;
import com.kunzisoft.keepass.icons.IconPackChooser;
import com.kunzisoft.keepass.activities.stylish.StylishActivity;
public class IconPickerDialogFragment extends DialogFragment {
public static final String KEY_ICON_STANDARD = "KEY_ICON_STANDARD";
private IconPickerListener iconPickerListener;
private IconPack iconPack;
public static void launch(StylishActivity activity) {
// Create an instance of the dialog fragment and show it
IconPickerDialogFragment dialog = new IconPickerDialogFragment();
dialog.show(activity.getSupportFragmentManager(), "IconPickerDialogFragment");
}
@Override
public void onAttach(Context context) {
super.onAttach(context);
try {
iconPickerListener = (IconPickerListener) context;
} catch (ClassCastException e) {
// The activity doesn't implement the interface, throw exception
throw new ClassCastException(context.toString()
+ " must implement " + IconPickerListener.class.getName());
}
}
@NonNull
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
// Get the layout inflater
LayoutInflater inflater = getActivity().getLayoutInflater();
iconPack = IconPackChooser.getSelectedIconPack(getContext());
// Inflate and set the layout for the dialog
// Pass null as the parent view because its going in the dialog layout
View root = inflater.inflate(R.layout.icon_picker, null);
builder.setView(root);
GridView currIconGridView = root.findViewById(R.id.IconGridView);
currIconGridView.setAdapter(new ImageAdapter(this.getContext()));
currIconGridView.setOnItemClickListener((parent, v, position, id) -> {
Bundle bundle = new Bundle();
bundle.putParcelable(KEY_ICON_STANDARD, new PwIconStandard(position));
iconPickerListener.iconPicked(bundle);
dismiss();
});
builder.setNegativeButton(R.string.cancel, (dialog, id) ->
IconPickerDialogFragment.this.getDialog().cancel());
return builder.create();
}
public class ImageAdapter extends BaseAdapter {
private Context context;
ImageAdapter(Context c) {
context = c;
}
public int getCount() {
/* Return number of KeePass icons */
return iconPack.numberOfIcons();
}
public Object getItem(int position) {
return null;
}
public long getItemId(int position) {
return 0;
}
public View getView(int position, View convertView, ViewGroup parent) {
View currView;
if(convertView == null) {
LayoutInflater li = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
assert li != null;
currView = li.inflate(R.layout.icon, parent, false);
}
else {
currView = convertView;
}
ImageView iv = currView.findViewById(R.id.icon_image);
iv.setImageResource(iconPack.iconToResId(position));
// Assign color if icons are tintable
if (iconPack.tintable()) {
// Retrieve the textColor to tint the icon
int[] attrs = {android.R.attr.textColor};
assert getContext() != null;
TypedArray ta = getContext().getTheme().obtainStyledAttributes(attrs);
int iconColor = ta.getColor(0, Color.BLACK);
ImageViewCompat.setImageTintList(iv, ColorStateList.valueOf(iconColor));
ta.recycle();
}
return currView;
}
}
public interface IconPickerListener {
void iconPicked(Bundle bundle);
}
}

View File

@@ -1,75 +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.dialogs;
import android.app.Dialog;
import android.content.Intent;
import android.os.Bundle;
import android.provider.Settings;
import android.support.annotation.NonNull;
import android.support.v4.app.DialogFragment;
import android.support.v7.app.AlertDialog;
import android.view.LayoutInflater;
import android.view.View;
import com.kunzisoft.keepass.BuildConfig;
import com.kunzisoft.keepass.R;
import com.kunzisoft.keepass.utils.Util;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
public class KeyboardExplanationDialogFragment extends DialogFragment {
@NonNull
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
assert getActivity() != null;
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
LayoutInflater inflater = getActivity().getLayoutInflater();
View rootView = inflater.inflate(R.layout.keyboard_explanation, null);
View fingerprintSettingPath1TextView = rootView.findViewById(R.id.keyboards_activate_setting_path1_text);
fingerprintSettingPath1TextView.setOnClickListener(
view -> launchActivateKeyboardSetting());
View fingerprintSettingPath2TextView = rootView.findViewById(R.id.keyboards_activate_setting_path2_text);
fingerprintSettingPath2TextView.setOnClickListener(
view -> launchActivateKeyboardSetting());
View containerKeyboardSwitcher = rootView.findViewById(R.id.container_keyboard_switcher);
if(BuildConfig.CLOSED_STORE) {
containerKeyboardSwitcher.setOnClickListener(
view -> Util.gotoUrl(getContext(), R.string.keyboard_switcher_play_store));
} else {
containerKeyboardSwitcher.setOnClickListener(
view -> Util.gotoUrl(getContext(), R.string.keyboard_switcher_f_droid));
}
builder.setView(rootView)
.setPositiveButton(android.R.string.ok, (dialog, id) -> {});
return builder.create();
}
private void launchActivateKeyboardSetting() {
Intent intent = new Intent(Settings.ACTION_INPUT_METHOD_SETTINGS);
intent.addFlags(FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
}
}

View File

@@ -1,74 +0,0 @@
/*
* Copyright 2018 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.dialogs;
import android.app.Dialog;
import android.content.ActivityNotFoundException;
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.widget.Toast;
import com.kunzisoft.keepass.BuildConfig;
import com.kunzisoft.keepass.R;
import com.kunzisoft.keepass.utils.Util;
/**
* Custom Dialog that asks the user to download the pro version or make a donation.
*/
public class ProFeatureDialogFragment extends DialogFragment {
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
// Use the Builder class for convenient dialog construction
assert getActivity() != null;
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
SpannableStringBuilder stringBuilder = new SpannableStringBuilder();
if (BuildConfig.CLOSED_STORE) {
stringBuilder.append(Html.fromHtml(getString(R.string.html_text_ad_free))).append("\n\n");
stringBuilder.append(Html.fromHtml(getString(R.string.html_text_buy_pro)));
builder.setPositiveButton(R.string.download, (dialog, id) -> {
try {
Util.gotoUrl(getContext(), R.string.app_pro_url);
} catch (ActivityNotFoundException e) {
Toast.makeText(getContext(), R.string.error_failed_to_launch_link, Toast.LENGTH_LONG).show();
}
});
}
else {
stringBuilder.append(Html.fromHtml(getString(R.string.html_text_feature_generosity))).append("\n\n");
stringBuilder.append(Html.fromHtml(getString(R.string.html_text_donation)));
builder.setPositiveButton(R.string.contribute, (dialog, id) -> {
try {
Util.gotoUrl(getContext(), R.string.contribution_url);
} catch (ActivityNotFoundException e) {
Toast.makeText(getContext(), R.string.error_failed_to_launch_link, Toast.LENGTH_LONG).show();
}
});
}
builder.setMessage(stringBuilder);
builder.setNegativeButton(android.R.string.cancel, (dialog, id) -> dismiss());
// Create the AlertDialog object and return it
return builder.create();
}
}

View File

@@ -1,38 +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.dialogs;
import android.content.Context;
import android.os.Build;
import com.kunzisoft.keepass.R;
public class ReadOnlyDialog extends WarningDialog {
public ReadOnlyDialog(Context context) {
super(context, R.string.show_read_only_warning);
warning = context.getString(R.string.read_only_warning);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
warning = warning.concat("\n\n").concat(context.getString(R.string.read_only_kitkat_warning));
}
}
}

View File

@@ -1,213 +0,0 @@
/*
* Copyright 2018 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.dialogs;
import android.app.Dialog;
import android.content.Context;
import android.os.Bundle;
import android.support.annotation.IdRes;
import android.support.annotation.NonNull;
import android.support.v4.app.DialogFragment;
import android.support.v7.app.AlertDialog;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.CompoundButton;
import android.widget.RadioGroup;
import com.kunzisoft.keepass.R;
import com.kunzisoft.keepass.database.SortNodeEnum;
public class SortDialogFragment extends DialogFragment {
private static final String SORT_NODE_ENUM_BUNDLE_KEY = "SORT_NODE_ENUM_BUNDLE_KEY";
private static final String SORT_ASCENDING_BUNDLE_KEY = "SORT_ASCENDING_BUNDLE_KEY";
private static final String SORT_GROUPS_BEFORE_BUNDLE_KEY = "SORT_GROUPS_BEFORE_BUNDLE_KEY";
private static final String SORT_RECYCLE_BIN_BOTTOM_BUNDLE_KEY = "SORT_RECYCLE_BIN_BOTTOM_BUNDLE_KEY";
private SortSelectionListener mListener;
private SortNodeEnum sortNodeEnum;
private @IdRes
int mCheckedId;
private boolean mGroupsBefore;
private boolean mAscending;
private boolean mRecycleBinBottom;
private static Bundle buildBundle(SortNodeEnum sortNodeEnum,
boolean ascending,
boolean groupsBefore) {
Bundle bundle = new Bundle();
bundle.putString(SORT_NODE_ENUM_BUNDLE_KEY, sortNodeEnum.name());
bundle.putBoolean(SORT_ASCENDING_BUNDLE_KEY, ascending);
bundle.putBoolean(SORT_GROUPS_BEFORE_BUNDLE_KEY, groupsBefore);
return bundle;
}
public static SortDialogFragment getInstance(SortNodeEnum sortNodeEnum,
boolean ascending,
boolean groupsBefore) {
Bundle bundle = buildBundle(sortNodeEnum, ascending, groupsBefore);
SortDialogFragment fragment = new SortDialogFragment();
fragment.setArguments(bundle);
return fragment;
}
public static SortDialogFragment getInstance(SortNodeEnum sortNodeEnum,
boolean ascending,
boolean groupsBefore,
boolean recycleBinBottom) {
Bundle bundle = buildBundle(sortNodeEnum, ascending, groupsBefore);
bundle.putBoolean(SORT_RECYCLE_BIN_BOTTOM_BUNDLE_KEY, recycleBinBottom);
SortDialogFragment fragment = new SortDialogFragment();
fragment.setArguments(bundle);
return fragment;
}
@Override
public void onAttach(Context context) {
super.onAttach(context);
try {
mListener = (SortSelectionListener) context;
} catch (ClassCastException e) {
throw new ClassCastException(context.toString()
+ " must implement " + SortSelectionListener.class.getName());
}
}
@NonNull
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
assert getActivity() != null;
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
LayoutInflater inflater = getActivity().getLayoutInflater();
sortNodeEnum = SortNodeEnum.TITLE;
mAscending = true;
mGroupsBefore = true;
boolean recycleBinAllowed = false;
mRecycleBinBottom = true;
if (getArguments() != null) {
if (getArguments().containsKey(SORT_NODE_ENUM_BUNDLE_KEY))
sortNodeEnum = SortNodeEnum.valueOf(getArguments().getString(SORT_NODE_ENUM_BUNDLE_KEY));
if (getArguments().containsKey(SORT_ASCENDING_BUNDLE_KEY))
mAscending = getArguments().getBoolean(SORT_ASCENDING_BUNDLE_KEY);
if (getArguments().containsKey(SORT_GROUPS_BEFORE_BUNDLE_KEY))
mGroupsBefore = getArguments().getBoolean(SORT_GROUPS_BEFORE_BUNDLE_KEY);
if (getArguments().containsKey(SORT_RECYCLE_BIN_BOTTOM_BUNDLE_KEY)) {
recycleBinAllowed = true;
mRecycleBinBottom = getArguments().getBoolean(SORT_RECYCLE_BIN_BOTTOM_BUNDLE_KEY);
}
}
mCheckedId = retrieveViewFromEnum(sortNodeEnum);
View rootView = inflater.inflate(R.layout.sort_selection, null);
builder.setTitle(R.string.sort_menu);
builder.setView(rootView)
// Add action buttons
.setPositiveButton(android.R.string.ok,
(dialog, id) -> mListener.onSortSelected(sortNodeEnum, mAscending, mGroupsBefore, mRecycleBinBottom))
.setNegativeButton(R.string.cancel, (dialog, id) -> {});
CompoundButton ascendingView = rootView.findViewById(R.id.sort_selection_ascending);
// Check if is ascending or descending
ascendingView.setChecked(mAscending);
ascendingView.setOnCheckedChangeListener(
(buttonView, isChecked) -> mAscending = isChecked);
CompoundButton groupsBeforeView = rootView.findViewById(R.id.sort_selection_groups_before);
// Check if groups before
groupsBeforeView.setChecked(mGroupsBefore);
groupsBeforeView.setOnCheckedChangeListener(
(buttonView, isChecked) -> mGroupsBefore = isChecked);
CompoundButton recycleBinBottomView = rootView.findViewById(R.id.sort_selection_recycle_bin_bottom);
if (!recycleBinAllowed) {
recycleBinBottomView.setVisibility(View.GONE);
} else {
// Check if recycle bin at the bottom
recycleBinBottomView.setChecked(mRecycleBinBottom);
recycleBinBottomView.setOnCheckedChangeListener(
(buttonView, isChecked) -> mRecycleBinBottom = isChecked);
}
RadioGroup sortSelectionRadioGroupView = rootView.findViewById(R.id.sort_selection_radio_group);
// Check value by default
sortSelectionRadioGroupView.check(mCheckedId);
sortSelectionRadioGroupView.setOnCheckedChangeListener(
(group, checkedId) -> sortNodeEnum = retrieveSortEnumFromViewId(checkedId));
return builder.create();
}
private @IdRes
int retrieveViewFromEnum(SortNodeEnum sortNodeEnum) {
switch (sortNodeEnum) {
case DB:
return R.id.sort_selection_db;
default:
case TITLE:
return R.id.sort_selection_title;
case USERNAME:
return R.id.sort_selection_username;
case CREATION_TIME:
return R.id.sort_selection_creation_time;
case LAST_MODIFY_TIME:
return R.id.sort_selection_last_modify_time;
case LAST_ACCESS_TIME:
return R.id.sort_selection_last_access_time;
}
}
private SortNodeEnum retrieveSortEnumFromViewId(@IdRes int checkedId) {
SortNodeEnum sortNodeEnum;
// Change enum
switch (checkedId) {
case R.id.sort_selection_db:
sortNodeEnum = SortNodeEnum.DB;
break;
default:
case R.id.sort_selection_title:
sortNodeEnum = SortNodeEnum.TITLE;
break;
case R.id.sort_selection_username:
sortNodeEnum = SortNodeEnum.USERNAME;
break;
case R.id.sort_selection_creation_time:
sortNodeEnum = SortNodeEnum.CREATION_TIME;
break;
case R.id.sort_selection_last_modify_time:
sortNodeEnum = SortNodeEnum.LAST_MODIFY_TIME;
break;
case R.id.sort_selection_last_access_time:
sortNodeEnum = SortNodeEnum.LAST_ACCESS_TIME;
break;
}
return sortNodeEnum;
}
public interface SortSelectionListener {
void onSortSelected(SortNodeEnum sortNodeEnum,
boolean ascending,
boolean groupsBefore,
boolean recycleBinBottom);
}
}

View File

@@ -1,143 +0,0 @@
/*
* Copyright 2018 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.dialogs;
import android.app.Dialog;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v4.app.DialogFragment;
import android.support.v7.app.AlertDialog;
import android.text.Html;
import android.text.SpannableStringBuilder;
import android.text.method.LinkMovementMethod;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.TextView;
import com.kunzisoft.keepass.R;
import java.lang.reflect.Field;
public class UnavailableFeatureDialogFragment extends DialogFragment {
private static final String MIN_REQUIRED_VERSION_ARG = "MIN_REQUIRED_VERSION_ARG";
private int minVersionRequired = Build.VERSION_CODES.M;
public static UnavailableFeatureDialogFragment getInstance(int minVersionRequired) {
UnavailableFeatureDialogFragment fragment = new UnavailableFeatureDialogFragment();
Bundle args = new Bundle();
args.putInt(MIN_REQUIRED_VERSION_ARG, minVersionRequired);
fragment.setArguments(args);
return fragment;
}
@NonNull
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
if (getArguments() != null && getArguments().containsKey(MIN_REQUIRED_VERSION_ARG))
minVersionRequired = getArguments().getInt(MIN_REQUIRED_VERSION_ARG);
assert getActivity() != null;
LayoutInflater inflater = getActivity().getLayoutInflater();
View rootView = inflater.inflate(R.layout.unavailable_feature, null);
TextView messageView = rootView.findViewById(R.id.unavailable_feature_message);
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
SpannableStringBuilder message = new SpannableStringBuilder();
message.append(getString(R.string.unavailable_feature_text))
.append("\n\n");
if(Build.VERSION.SDK_INT < minVersionRequired) {
message.append(getString(R.string.unavailable_feature_version,
androidNameFromApiNumber(Build.VERSION.SDK_INT, Build.VERSION.RELEASE),
androidNameFromApiNumber(minVersionRequired)));
message.append("\n\n")
.append(Html.fromHtml("<a href=\"https://source.android.com/setup/build-numbers\">CodeNames</a>"));
} else
message.append(getString(R.string.unavailable_feature_hardware));
messageView.setText(message);
messageView.setMovementMethod(LinkMovementMethod.getInstance());
builder.setView(rootView)
.setPositiveButton(android.R.string.ok, (dialog, id) -> { });
return builder.create();
}
private String androidNameFromApiNumber(int apiNumber) {
return androidNameFromApiNumber(apiNumber, "");
}
private String androidNameFromApiNumber(int apiNumber, String releaseVersion) {
StringBuilder builder = new StringBuilder();
Field[] fields = Build.VERSION_CODES.class.getFields();
String apiName = "";
for (Field field : fields) {
String fieldName = field.getName();
int fieldValue = -1;
try {
fieldValue = field.getInt(new Object());
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (NullPointerException e) {
e.printStackTrace();
}
if (fieldValue == apiNumber) {
apiName = fieldName;
}
}
if (apiName.isEmpty()) {
String[] mapper = new String[]{
"ANDROID BASE", "ANDROID BASE 1.1", "CUPCAKE", "DONUT",
"ECLAIR", "ECLAIR_0_1", "ECLAIR_MR1", "FROYO",
"GINGERBREAD", "GINGERBREAD_MR1", "HONEYCOMB", "HONEYCOMB_MR1",
"HONEYCOMB_MR2", "ICE_CREAM_SANDWICH", "ICE_CREAM_SANDWICH_MR1", "JELLY_BEAN",
"JELLY_BEAN", "JELLY_BEAN", "KITKAT", "KITKAT",
"LOLLIPOOP", "LOLLIPOOP_MR1", "MARSHMALLOW", "NOUGAT",
"NOUGAT", "OREO", "OREO"};
int index = apiNumber - 1;
apiName = index < mapper.length ? mapper[index] : "UNKNOWN_VERSION";
}
if (releaseVersion.isEmpty()) {
String[] versions = new String[]{
"1.0", "1.1", "1.5", "1.6",
"2.0", "2.0.1", "2.1", "2.2.X",
"2.3", "2.3.3", "3.0", "3.1",
"3.2.0", "4.0.1", "4.0.3", "4.1.0",
"4.2.0", "4.3.0", "4.4", "4.4",
"5.0", "5.1", "6.0", "7.0",
"7.1", "8.0.0", "8.1.0"};
int index = apiNumber - 1;
releaseVersion = index < versions.length ? versions[index] : "UNKNOWN_VERSION";
}
builder.append("\n\t");
if (!apiName.isEmpty())
builder.append(apiName).append(" ");
if (!releaseVersion.isEmpty())
builder.append(releaseVersion).append(" ");
builder.append("(API ").append(apiNumber).append(")");
builder.append("\n");
return builder.toString();
}
}

View File

@@ -1,85 +0,0 @@
/*
* Copyright 2018 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.dialogs;
import android.app.Dialog;
import android.content.ActivityNotFoundException;
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.widget.Toast;
import com.kunzisoft.keepass.BuildConfig;
import com.kunzisoft.keepass.R;
import com.kunzisoft.keepass.utils.Util;
/**
* Custom Dialog that asks the user to download the pro version or make a donation.
*/
public class UnderDevelopmentFeatureDialogFragment extends DialogFragment {
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
// Use the Builder class for convenient dialog construction
assert getActivity() != null;
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
SpannableStringBuilder stringBuilder = new SpannableStringBuilder();
if (BuildConfig.CLOSED_STORE) {
if (BuildConfig.FULL_VERSION) {
stringBuilder.append(Html.fromHtml(getString(R.string.html_text_dev_feature_thanks))).append("\n\n")
.append(Html.fromHtml(getString(R.string.html_rose))).append("\n\n")
.append(Html.fromHtml(getString(R.string.html_text_dev_feature_work_hard))).append("\n")
.append(Html.fromHtml(getString(R.string.html_text_dev_feature_upgrade))).append(" ");
builder.setPositiveButton(android.R.string.ok, (dialog, id) -> dismiss());
} else {
stringBuilder.append(Html.fromHtml(getString(R.string.html_text_dev_feature))).append("\n\n")
.append(Html.fromHtml(getString(R.string.html_text_dev_feature_buy_pro))).append("\n")
.append(Html.fromHtml(getString(R.string.html_text_dev_feature_encourage)));
builder.setPositiveButton(R.string.download, (dialog, id) -> {
try {
Util.gotoUrl(getContext(), R.string.app_pro_url);
} catch (ActivityNotFoundException e) {
Toast.makeText(getContext(), R.string.error_failed_to_launch_link, Toast.LENGTH_LONG).show();
}
});
builder.setNegativeButton(android.R.string.cancel, (dialog, id) -> dismiss());
}
}
else {
stringBuilder.append(Html.fromHtml(getString(R.string.html_text_dev_feature))).append("\n\n")
.append(Html.fromHtml(getString(R.string.html_text_dev_feature_contibute))).append(" ")
.append(Html.fromHtml(getString(R.string.html_text_dev_feature_encourage)));
builder.setPositiveButton(R.string.contribute, (dialog, id) -> {
try {
Util.gotoUrl(getContext(), R.string.contribution_url);
} catch (ActivityNotFoundException e) {
Toast.makeText(getContext(), R.string.error_failed_to_launch_link, Toast.LENGTH_LONG).show();
}
});
builder.setNegativeButton(android.R.string.cancel, (dialog, id) -> dismiss());
}
builder.setMessage(stringBuilder);
// Create the AlertDialog object and return it
return builder.create();
}
}

View File

@@ -1,78 +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.dialogs;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.preference.PreferenceManager;
import com.kunzisoft.keepass.R;
public class WarningDialog extends AlertDialog {
protected String warning;
private int showKey;
public WarningDialog(Context context, int dontShowKey) {
super(context);
this.showKey = dontShowKey;
}
public WarningDialog(Context context, int warningKey, int dontShowKey) {
this(context, dontShowKey);
warning = context.getString(warningKey);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
Context ctx = getContext();
setMessage(warning);
setButton(AlertDialog.BUTTON1, ctx.getText(android.R.string.ok), new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dismiss();
}
});
setButton(AlertDialog.BUTTON2, ctx.getText(R.string.beta_dontask), new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Context ctx = getContext();
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(ctx);
SharedPreferences.Editor edit = prefs.edit();
edit.putBoolean(ctx.getString(showKey), false);
edit.commit();
dismiss();
}
});
super.onCreate(savedInstanceState);
}
}

View File

@@ -46,14 +46,14 @@ import com.kunzisoft.keepass.R;
import com.kunzisoft.keepass.activities.ReadOnlyHelper;
import com.kunzisoft.keepass.app.App;
import com.kunzisoft.keepass.database.element.Database;
import com.kunzisoft.keepass.dialogs.ProFeatureDialogFragment;
import com.kunzisoft.keepass.dialogs.UnavailableFeatureDialogFragment;
import com.kunzisoft.keepass.dialogs.UnderDevelopmentFeatureDialogFragment;
import com.kunzisoft.keepass.activities.dialogs.ProFeatureDialogFragment;
import com.kunzisoft.keepass.activities.dialogs.UnavailableFeatureDialogFragment;
import com.kunzisoft.keepass.activities.dialogs.UnderDevelopmentFeatureDialogFragment;
import com.kunzisoft.keepass.education.Education;
import com.kunzisoft.keepass.fileselect.database.FileDatabaseHistory;
import com.kunzisoft.keepass.fingerprint.FingerPrintHelper;
import com.kunzisoft.keepass.icons.IconPackChooser;
import com.kunzisoft.keepass.dialogs.KeyboardExplanationDialogFragment;
import com.kunzisoft.keepass.activities.dialogs.KeyboardExplanationDialogFragment;
import com.kunzisoft.keepass.settings.preferencedialogfragment.DatabaseDescriptionPreferenceDialogFragmentCompat;
import com.kunzisoft.keepass.settings.preferencedialogfragment.DatabaseEncryptionAlgorithmPreferenceDialogFragmentCompat;
import com.kunzisoft.keepass.settings.preferencedialogfragment.DatabaseKeyDerivationPreferenceDialogFragmentCompat;
@@ -194,7 +194,7 @@ public class NestedSettingsFragment extends PreferenceFragmentCompat
FragmentManager fragmentManager = getFragmentManager();
assert fragmentManager != null;
((SwitchPreference) preference).setChecked(false);
UnavailableFeatureDialogFragment.getInstance(Build.VERSION_CODES.M)
UnavailableFeatureDialogFragment.Companion.getInstance(Build.VERSION_CODES.M)
.show(getFragmentManager(), "unavailableFeatureDialog");
return false;
});
@@ -297,7 +297,7 @@ public class NestedSettingsFragment extends PreferenceFragmentCompat
((SwitchPreference) preference).setChecked(false);
FragmentManager fragmentManager = getFragmentManager();
assert fragmentManager != null;
UnavailableFeatureDialogFragment.getInstance(Build.VERSION_CODES.O)
UnavailableFeatureDialogFragment.Companion.getInstance(Build.VERSION_CODES.O)
.show(fragmentManager, "unavailableFeatureDialog");
return false;
});

View File

@@ -64,7 +64,8 @@
android:fillViewport="true"
android:scrollbarStyle="insideOverlay">
<com.kunzisoft.keepass.view.EntryContentsView android:id="@+id/entry_contents"
<com.kunzisoft.keepass.activities.view.EntryContentsView
android:id="@+id/entry_contents"
android:layout_height="wrap_content"
android:layout_width="match_parent" />

View File

@@ -119,7 +119,7 @@
android:background="?android:attr/windowBackground" />
</LinearLayout>
<com.kunzisoft.keepass.view.AddNodeButtonView
<com.kunzisoft.keepass.activities.view.AddNodeButtonView
android:id="@+id/add_node_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"