From 7dfe85450d1ff5ac361ef88bac4f51c1daffbb9b Mon Sep 17 00:00:00 2001 From: J-Jamet Date: Sun, 7 Jul 2019 16:37:57 +0200 Subject: [PATCH] Kotlinized EntryActivity --- .../keepass/activities/EntryActivity.java | 484 ------------------ .../keepass/activities/EntryActivity.kt | 389 ++++++++++++++ .../keepass/activities/GroupActivity.java | 2 +- .../NotificationEntryCopyManager.kt | 90 ++++ .../keepass/view/EntryContentsView.java | 253 --------- .../keepass/view/EntryContentsView.kt | 250 +++++++++ 6 files changed, 730 insertions(+), 738 deletions(-) delete mode 100644 app/src/main/java/com/kunzisoft/keepass/activities/EntryActivity.java create mode 100644 app/src/main/java/com/kunzisoft/keepass/activities/EntryActivity.kt create mode 100644 app/src/main/java/com/kunzisoft/keepass/notifications/NotificationEntryCopyManager.kt delete mode 100644 app/src/main/java/com/kunzisoft/keepass/view/EntryContentsView.java create mode 100644 app/src/main/java/com/kunzisoft/keepass/view/EntryContentsView.kt diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/EntryActivity.java b/app/src/main/java/com/kunzisoft/keepass/activities/EntryActivity.java deleted file mode 100644 index 09a27ea44..000000000 --- a/app/src/main/java/com/kunzisoft/keepass/activities/EntryActivity.java +++ /dev/null @@ -1,484 +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 . - * - */ -package com.kunzisoft.keepass.activities; - -import android.app.Activity; -import android.content.ActivityNotFoundException; -import android.content.Intent; -import android.content.res.TypedArray; -import android.graphics.Color; -import android.net.Uri; -import android.os.Bundle; -import android.os.Handler; -import android.support.v7.app.AlertDialog; -import android.support.v7.widget.Toolbar; -import android.util.Log; -import android.view.Menu; -import android.view.MenuInflater; -import android.view.MenuItem; -import android.widget.ImageView; -import android.widget.TextView; -import android.widget.Toast; - -import com.kunzisoft.keepass.R; -import com.kunzisoft.keepass.activities.lock.LockingHideActivity; -import com.kunzisoft.keepass.app.App; -import com.kunzisoft.keepass.database.element.Database; -import com.kunzisoft.keepass.database.element.EntryVersioned; -import com.kunzisoft.keepass.database.element.PwNodeId; -import com.kunzisoft.keepass.database.element.security.ProtectedString; -import com.kunzisoft.keepass.education.EntryActivityEducation; -import com.kunzisoft.keepass.notifications.NotificationCopyingService; -import com.kunzisoft.keepass.notifications.NotificationField; -import com.kunzisoft.keepass.settings.PreferencesUtil; -import com.kunzisoft.keepass.settings.SettingsAutofillActivity; -import com.kunzisoft.keepass.timeout.ClipboardHelper; -import com.kunzisoft.keepass.timeout.TimeoutHelper; -import com.kunzisoft.keepass.utils.EmptyUtils; -import com.kunzisoft.keepass.utils.MenuUtil; -import com.kunzisoft.keepass.utils.Util; -import com.kunzisoft.keepass.view.EntryContentsView; - -import java.util.ArrayList; -import java.util.Date; - -import kotlin.Unit; -import kotlin.jvm.functions.Function2; - -import static com.kunzisoft.keepass.settings.PreferencesUtil.isClipboardNotificationsEnable; -import static com.kunzisoft.keepass.settings.PreferencesUtil.isFirstTimeAskAllowCopyPasswordAndProtectedFields; - -public class EntryActivity extends LockingHideActivity { - private final static String TAG = EntryActivity.class.getName(); - - public static final String KEY_ENTRY = "entry"; - - private ImageView titleIconView; - private TextView titleView; - private EntryContentsView entryContentsView; - private Toolbar toolbar; - - protected EntryVersioned mEntry; - private boolean mShowPassword; - - private ClipboardHelper clipboardHelper; - private boolean firstLaunchOfActivity; - - private int iconColor; - - public static void launch(Activity activity, EntryVersioned pw, boolean readOnly) { - if (TimeoutHelper.INSTANCE.checkTimeAndLockIfTimeout(activity)) { - Intent intent = new Intent(activity, EntryActivity.class); - intent.putExtra(KEY_ENTRY, pw.getNodeId()); - ReadOnlyHelper.INSTANCE.putReadOnlyInIntent(intent, readOnly); - activity.startActivityForResult(intent, EntryEditActivity.ADD_OR_UPDATE_ENTRY_REQUEST_CODE); - } - } - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - setContentView(R.layout.entry_view); - - toolbar = findViewById(R.id.toolbar); - setSupportActionBar(toolbar); - assert getSupportActionBar() != null; - getSupportActionBar().setHomeAsUpIndicator(R.drawable.ic_close_white_24dp); - getSupportActionBar().setDisplayHomeAsUpEnabled(true); - getSupportActionBar().setDisplayShowHomeEnabled(true); - - Database db = App.Companion.getCurrentDatabase(); - setReadOnly(db.isReadOnly() || getReadOnly()); - - mShowPassword = !PreferencesUtil.isPasswordMask(this); - - // Get Entry from UUID - Intent i = getIntent(); - PwNodeId keyEntry; - try { - keyEntry = i.getParcelableExtra(KEY_ENTRY); - mEntry = db.getEntryById(keyEntry); - } catch (ClassCastException e) { - Log.e(TAG, "Unable to retrieve the entry key"); - } - if (mEntry == null) { - Toast.makeText(this, R.string.entry_not_found, Toast.LENGTH_LONG).show(); - finish(); - return; - } - - // Update last access time. - mEntry.touch(false, false); - - // Retrieve the textColor to tint the icon - int[] attrs = {R.attr.textColorInverse}; - TypedArray ta = getTheme().obtainStyledAttributes(attrs); - iconColor = ta.getColor(0, Color.WHITE); - - // Refresh Menu contents in case onCreateMenuOptions was called before mEntry was set - invalidateOptionsMenu(); - - // Get views - titleIconView = findViewById(R.id.entry_icon); - titleView = findViewById(R.id.entry_title); - entryContentsView = findViewById(R.id.entry_contents); - entryContentsView.applyFontVisibilityToFields(PreferencesUtil.fieldFontIsInVisibility(this)); - - // Init the clipboard helper - clipboardHelper = new ClipboardHelper(this); - firstLaunchOfActivity = true; - } - - @Override - protected void onResume() { - super.onResume(); - - // Fill data in resume to update from EntryEditActivity - fillData(); - invalidateOptionsMenu(); - - Database database = App.Companion.getCurrentDatabase(); - // Start to manage field reference to copy a value from ref - database.startManageEntry(mEntry); - - boolean containsUsernameToCopy = - mEntry.getUsername().length() > 0; - boolean containsPasswordToCopy = - (mEntry.getPassword().length() > 0 - && PreferencesUtil.allowCopyPasswordAndProtectedFields(this)); - boolean containsExtraFieldToCopy = - (mEntry.allowExtraFields() - && ((mEntry.containsCustomFields() - && mEntry.containsCustomFieldsNotProtected()) - || (mEntry.containsCustomFields() - && mEntry.containsCustomFieldsProtected() - && PreferencesUtil.allowCopyPasswordAndProtectedFields(this)) - ) - ); - - // If notifications enabled in settings - // Don't if application timeout - if (firstLaunchOfActivity && isClipboardNotificationsEnable(getApplicationContext())) { - if (containsUsernameToCopy - || containsPasswordToCopy - || containsExtraFieldToCopy - ) { - // username already copied, waiting for user's action before copy password. - Intent intent = new Intent(this, NotificationCopyingService.class); - intent.setAction(NotificationCopyingService.ACTION_NEW_NOTIFICATION); - if (mEntry.getTitle() != null) - intent.putExtra(NotificationCopyingService.EXTRA_ENTRY_TITLE, mEntry.getTitle()); - // Construct notification fields - ArrayList notificationFields = new ArrayList<>(); - // Add username if exists to notifications - if (containsUsernameToCopy) - notificationFields.add( - new NotificationField( - NotificationField.NotificationFieldId.USERNAME, - mEntry.getUsername(), - getResources())); - // Add password to notifications - if (containsPasswordToCopy) { - notificationFields.add( - new NotificationField( - NotificationField.NotificationFieldId.PASSWORD, - mEntry.getPassword(), - getResources())); - } - // Add extra fields - if (containsExtraFieldToCopy) { - try { - mEntry.getFields().doActionToAllCustomProtectedField(new Function2() { - private int anonymousFieldNumber = 0; - @Override - public Unit invoke(String key, ProtectedString value) { - //If value is not protected or allowed - if (!value.isProtected() || PreferencesUtil.allowCopyPasswordAndProtectedFields(EntryActivity.this)) { - notificationFields.add( - new NotificationField( - NotificationField.NotificationFieldId.getAnonymousFieldId()[anonymousFieldNumber], - value.toString(), - key, - getResources())); - anonymousFieldNumber++; - } - return null; - } - }); - } catch (ArrayIndexOutOfBoundsException e) { - Log.w(TAG, "Only " + NotificationField.NotificationFieldId.getAnonymousFieldId().length + - " anonymous notifications are available"); - } - } - // Add notifications - intent.putParcelableArrayListExtra(NotificationCopyingService.EXTRA_FIELDS, notificationFields); - - startService(intent); - } - - database.stopManageEntry(mEntry); - } - firstLaunchOfActivity = false; - } - - protected void fillData() { - Database database = App.Companion.getCurrentDatabase(); - database.startManageEntry(mEntry); - // Assign title icon - database.getDrawFactory().assignDatabaseIconTo(this, titleIconView, mEntry.getIcon(), iconColor); - - // Assign title text - titleView.setText(mEntry.getVisualTitle()); - - // Assign basic fields - entryContentsView.assignUserName(mEntry.getUsername()); - entryContentsView.assignUserNameCopyListener(view -> - clipboardHelper.timeoutCopyToClipboard(mEntry.getUsername(), - getString(R.string.copy_field, getString(R.string.entry_user_name))) - ); - - boolean allowCopyPassword = PreferencesUtil.allowCopyPasswordAndProtectedFields(this); - entryContentsView.assignPassword(mEntry.getPassword(), allowCopyPassword); - if (allowCopyPassword) { - entryContentsView.assignPasswordCopyListener(view -> - clipboardHelper.timeoutCopyToClipboard(mEntry.getPassword(), - getString(R.string.copy_field, getString(R.string.entry_password))) - ); - } else { - // If dialog not already shown - if (isFirstTimeAskAllowCopyPasswordAndProtectedFields(this)) { - entryContentsView.assignPasswordCopyListener(v -> { - String message = getString(R.string.allow_copy_password_warning) + - "\n\n" + - getString(R.string.clipboard_warning); - AlertDialog warningDialog = new AlertDialog.Builder(EntryActivity.this) - .setMessage(message).create(); - warningDialog.setButton(AlertDialog.BUTTON1, getText(android.R.string.ok), - (dialog, which) -> { - PreferencesUtil.setAllowCopyPasswordAndProtectedFields(EntryActivity.this, true); - dialog.dismiss(); - fillData(); - }); - warningDialog.setButton(AlertDialog.BUTTON2, getText(android.R.string.cancel), - (dialog, which) -> { - PreferencesUtil.setAllowCopyPasswordAndProtectedFields(EntryActivity.this, false); - dialog.dismiss(); - fillData(); - }); - warningDialog.show(); - }); - } else { - entryContentsView.assignPasswordCopyListener(null); - } - } - - entryContentsView.assignURL(mEntry.getUrl()); - - entryContentsView.setHiddenPasswordStyle(!mShowPassword); - entryContentsView.assignComment(mEntry.getNotes()); - - // Assign custom fields - if (mEntry.allowExtraFields()) { - entryContentsView.clearExtraFields(); - - mEntry.getFields().doActionToAllCustomProtectedField((label, value) -> { - boolean showAction = (!value.isProtected() || PreferencesUtil.allowCopyPasswordAndProtectedFields(EntryActivity.this)); - entryContentsView.addExtraField(label, value, showAction, view -> - clipboardHelper.timeoutCopyToClipboard( - value.toString(), - getString(R.string.copy_field, label) - ) - ); - return null; - }); - } - - // Assign dates - entryContentsView.assignCreationDate(mEntry.getCreationTime().getDate()); - entryContentsView.assignModificationDate(mEntry.getLastModificationTime().getDate()); - entryContentsView.assignLastAccessDate(mEntry.getLastAccessTime().getDate()); - Date expires = mEntry.getExpiryTime().getDate(); - if ( mEntry.isExpires() ) { - entryContentsView.assignExpiresDate(expires); - } else { - entryContentsView.assignExpiresDate(getString(R.string.never)); - } - - database.stopManageEntry(mEntry); - } - - @Override - protected void onActivityResult(int requestCode, int resultCode, Intent data) { - super.onActivityResult(requestCode, resultCode, data); - switch (requestCode) { - case EntryEditActivity.ADD_OR_UPDATE_ENTRY_REQUEST_CODE: - // Not directly get the entry from intent data but from database - fillData(); - break; - } - } - - private void changeShowPasswordIcon(MenuItem togglePassword) { - if ( mShowPassword ) { - togglePassword.setTitle(R.string.menu_hide_password); - togglePassword.setIcon(R.drawable.ic_visibility_off_white_24dp); - } else { - togglePassword.setTitle(R.string.menu_showpass); - togglePassword.setIcon(R.drawable.ic_visibility_white_24dp); - } - } - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - super.onCreateOptionsMenu(menu); - - MenuInflater inflater = getMenuInflater(); - MenuUtil.INSTANCE.contributionMenuInflater(inflater, menu); - inflater.inflate(R.menu.entry, menu); - inflater.inflate(R.menu.database_lock, menu); - - if (getReadOnly()) { - MenuItem edit = menu.findItem(R.id.menu_edit); - if (edit != null) - edit.setVisible(false); - } - - MenuItem togglePassword = menu.findItem(R.id.menu_toggle_pass); - if (entryContentsView != null && togglePassword != null) { - if (entryContentsView.isPasswordPresent() || entryContentsView.atLeastOneFieldProtectedPresent()) { - changeShowPasswordIcon(togglePassword); - } else { - togglePassword.setVisible(false); - } - } - - MenuItem gotoUrl = menu.findItem(R.id.menu_goto_url); - if (gotoUrl != null) { - // In API >= 11 onCreateOptionsMenu may be called before onCreate completes - // so mEntry may not be set - if (mEntry == null) { - gotoUrl.setVisible(false); - } else { - String url = mEntry.getUrl(); - if (EmptyUtils.INSTANCE.isNullOrEmpty(url)) { - // disable button if url is not available - gotoUrl.setVisible(false); - } - } - } - - // Show education views - new Handler().post(() -> performedNextEducation(new EntryActivityEducation(this), menu)); - - return true; - } - - private void performedNextEducation(EntryActivityEducation entryActivityEducation, - Menu menu) { - if (entryContentsView != null - && entryContentsView.isUserNamePresent() - && entryActivityEducation.checkAndPerformedEntryCopyEducation( - findViewById(R.id.entry_user_name_action_image), - tapTargetView -> { - clipboardHelper.timeoutCopyToClipboard(mEntry.getUsername(), - getString(R.string.copy_field, - getString(R.string.entry_user_name))); - return null; - }, - tapTargetView -> { - // Launch autofill settings - startActivity(new Intent(EntryActivity.this, SettingsAutofillActivity.class)); - return null; - }) - ); - else if (toolbar.findViewById(R.id.menu_edit) != null - && entryActivityEducation.checkAndPerformedEntryEditEducation( - toolbar.findViewById(R.id.menu_edit), - tapTargetView -> { - onOptionsItemSelected(menu.findItem(R.id.menu_edit)); - return null; - }, - tapTargetView -> { - // Open Keepass doc to create field references - startActivity(new Intent(Intent.ACTION_VIEW, - Uri.parse(getString(R.string.field_references_url)))); - return null; - }) - ); - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - switch ( item.getItemId() ) { - case R.id.menu_contribute: - return MenuUtil.INSTANCE.onContributionItemSelected(this); - - case R.id.menu_toggle_pass: - mShowPassword = !mShowPassword; - changeShowPasswordIcon(item); - entryContentsView.setHiddenPasswordStyle(!mShowPassword); - return true; - - case R.id.menu_edit: - EntryEditActivity.launch(EntryActivity.this, mEntry); - return true; - - case R.id.menu_goto_url: - String url; - url = mEntry.getUrl(); - - // Default http:// if no protocol specified - if ( ! url.contains("://") ) { - url = "http://" + url; - } - - try { - Util.gotoUrl(this, url); - } catch (ActivityNotFoundException e) { - Toast.makeText(this, R.string.no_url_handler, Toast.LENGTH_LONG).show(); - } - return true; - - case R.id.menu_lock: - lockAndExit(); - return true; - - case android.R.id.home : - finish(); // close this activity and return to preview activity (if there is any) - } - - return super.onOptionsItemSelected(item); - } - - - @Override - public void finish() { - // Transit data in previous Activity after an update - /* - TODO Slowdown when add entry as result - Intent intent = new Intent(); - intent.putExtra(EntryEditActivity.ADD_OR_UPDATE_ENTRY_KEY, mEntry); - setResult(EntryEditActivity.UPDATE_ENTRY_RESULT_CODE, intent); - */ - super.finish(); - } -} diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/EntryActivity.kt b/app/src/main/java/com/kunzisoft/keepass/activities/EntryActivity.kt new file mode 100644 index 000000000..afe2d1d2a --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/activities/EntryActivity.kt @@ -0,0 +1,389 @@ +/* + * 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 . + */ +package com.kunzisoft.keepass.activities + +import android.app.Activity +import android.content.ActivityNotFoundException +import android.content.Intent +import android.graphics.Color +import android.net.Uri +import android.os.Bundle +import android.os.Handler +import android.support.v7.app.AlertDialog +import android.support.v7.widget.Toolbar +import android.util.Log +import android.view.Menu +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.LockingHideActivity +import com.kunzisoft.keepass.app.App +import com.kunzisoft.keepass.database.element.EntryVersioned +import com.kunzisoft.keepass.database.element.PwNodeId +import com.kunzisoft.keepass.education.EntryActivityEducation +import com.kunzisoft.keepass.notifications.NotificationEntryCopyManager +import com.kunzisoft.keepass.settings.PreferencesUtil +import com.kunzisoft.keepass.settings.PreferencesUtil.isFirstTimeAskAllowCopyPasswordAndProtectedFields +import com.kunzisoft.keepass.settings.SettingsAutofillActivity +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() { + + private var titleIconView: ImageView? = null + private var titleView: TextView? = null + private var entryContentsView: EntryContentsView? = null + private var toolbar: Toolbar? = null + + private var mEntry: EntryVersioned? = null + private var mShowPassword: Boolean = false + + private var clipboardHelper: ClipboardHelper? = null + private var firstLaunchOfActivity: Boolean = false + + private var iconColor: Int = 0 + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + setContentView(R.layout.entry_view) + + toolbar = findViewById(R.id.toolbar) + setSupportActionBar(toolbar) + supportActionBar?.setHomeAsUpIndicator(R.drawable.ic_close_white_24dp) + supportActionBar?.setDisplayHomeAsUpEnabled(true) + supportActionBar?.setDisplayShowHomeEnabled(true) + + val currentDatabase = App.currentDatabase + readOnly = currentDatabase.isReadOnly || readOnly + + mShowPassword = !PreferencesUtil.isPasswordMask(this) + + // Get Entry from UUID + try { + val keyEntry: PwNodeId<*> = intent.getParcelableExtra(KEY_ENTRY) + mEntry = currentDatabase.getEntryById(keyEntry) + } catch (e: ClassCastException) { + Log.e(TAG, "Unable to retrieve the entry key") + } + + if (mEntry == null) { + Toast.makeText(this, R.string.entry_not_found, Toast.LENGTH_LONG).show() + finish() + return + } + + // Update last access time. + mEntry?.touch(modified = false, touchParents = false) + + // Retrieve the textColor to tint the icon + iconColor = theme. + obtainStyledAttributes(intArrayOf(R.attr.textColorInverse)) + .getColor(0, Color.WHITE) + + // Refresh Menu contents in case onCreateMenuOptions was called before mEntry was set + invalidateOptionsMenu() + + // Get views + titleIconView = findViewById(R.id.entry_icon) + titleView = findViewById(R.id.entry_title) + entryContentsView = findViewById(R.id.entry_contents) + entryContentsView?.applyFontVisibilityToFields(PreferencesUtil.fieldFontIsInVisibility(this)) + + // Init the clipboard helper + clipboardHelper = ClipboardHelper(this) + firstLaunchOfActivity = true + } + + override fun onResume() { + super.onResume() + + mEntry?.let { entry -> + // Fill data in resume to update from EntryEditActivity + fillEntryDataInContentsView(entry) + // Refresh Menu + invalidateOptionsMenu() + // Manage entry copy to start notification if allowed + NotificationEntryCopyManager.launchNotificationIfAllowed(this, + firstLaunchOfActivity, + entry) + } + + firstLaunchOfActivity = false + } + + private fun fillEntryDataInContentsView(entry: EntryVersioned) { + + val database = App.currentDatabase + database.startManageEntry(entry) + // Assign title icon + database.drawFactory.assignDatabaseIconTo(this, titleIconView, entry.icon, iconColor) + + // Assign title text + titleView?.text = entry.getVisualTitle() + + // Assign basic fields + entryContentsView?.assignUserName(entry.username) + entryContentsView?.assignUserNameCopyListener(View.OnClickListener { + clipboardHelper?.timeoutCopyToClipboard(entry.username, + getString(R.string.copy_field, + getString(R.string.entry_user_name))) + }) + + val allowCopyPassword = PreferencesUtil.allowCopyPasswordAndProtectedFields(this) + entryContentsView?.assignPassword(entry.password, allowCopyPassword) + if (allowCopyPassword) { + entryContentsView?.assignPasswordCopyListener(View.OnClickListener { + clipboardHelper?.timeoutCopyToClipboard(entry.password, + getString(R.string.copy_field, + getString(R.string.entry_password))) + }) + } else { + // If dialog not already shown + if (isFirstTimeAskAllowCopyPasswordAndProtectedFields(this)) { + entryContentsView?.assignPasswordCopyListener(View.OnClickListener { + val message = getString(R.string.allow_copy_password_warning) + + "\n\n" + + getString(R.string.clipboard_warning) + val warningDialog = AlertDialog.Builder(this@EntryActivity) + .setMessage(message).create() + warningDialog.setButton(AlertDialog.BUTTON1, getText(android.R.string.ok) + ) { dialog, _ -> + PreferencesUtil.setAllowCopyPasswordAndProtectedFields(this@EntryActivity, true) + dialog.dismiss() + fillEntryDataInContentsView(entry) + } + warningDialog.setButton(AlertDialog.BUTTON2, getText(android.R.string.cancel) + ) { dialog, _ -> + PreferencesUtil.setAllowCopyPasswordAndProtectedFields(this@EntryActivity, false) + dialog.dismiss() + fillEntryDataInContentsView(entry) + } + warningDialog.show() + }) + } else { + entryContentsView?.assignPasswordCopyListener(null) + } + } + + entryContentsView?.assignURL(entry.url) + entryContentsView?.setHiddenPasswordStyle(!mShowPassword) + entryContentsView?.assignComment(entry.notes) + + // Assign custom fields + if (entry.allowExtraFields()) { + entryContentsView?.clearExtraFields() + + entry.fields.doActionToAllCustomProtectedField { label, value -> + val showAction = !value.isProtected || PreferencesUtil.allowCopyPasswordAndProtectedFields(this@EntryActivity) + entryContentsView?.addExtraField(label, value, showAction, View.OnClickListener { + clipboardHelper?.timeoutCopyToClipboard( + value.toString(), + getString(R.string.copy_field, label) + ) + }) + } + } + + // Assign dates + entry.creationTime.date?.let { + entryContentsView?.assignCreationDate(it) + } + entry.lastModificationTime.date?.let { + entryContentsView?.assignModificationDate(it) + } + entry.lastAccessTime.date?.let { + entryContentsView?.assignLastAccessDate(it) + } + val expires = entry.expiryTime.date + if (entry.isExpires && expires != null) { + entryContentsView?.assignExpiresDate(expires) + } else { + entryContentsView?.assignExpiresDate(getString(R.string.never)) + } + + database.stopManageEntry(entry) + } + + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + super.onActivityResult(requestCode, resultCode, data) + when (requestCode) { + EntryEditActivity.ADD_OR_UPDATE_ENTRY_REQUEST_CODE -> + // Not directly get the entry from intent data but from database + mEntry?.let { + fillEntryDataInContentsView(it) + } + } + } + + private fun changeShowPasswordIcon(togglePassword: MenuItem?) { + if (mShowPassword) { + togglePassword?.setTitle(R.string.menu_hide_password) + togglePassword?.setIcon(R.drawable.ic_visibility_off_white_24dp) + } else { + togglePassword?.setTitle(R.string.menu_showpass) + togglePassword?.setIcon(R.drawable.ic_visibility_white_24dp) + } + } + + override fun onCreateOptionsMenu(menu: Menu): Boolean { + super.onCreateOptionsMenu(menu) + + val inflater = menuInflater + MenuUtil.contributionMenuInflater(inflater, menu) + inflater.inflate(R.menu.entry, menu) + inflater.inflate(R.menu.database_lock, menu) + + if (readOnly) { + menu.findItem(R.id.menu_edit)?.isVisible = false + } + + val togglePassword = menu.findItem(R.id.menu_toggle_pass) + entryContentsView?.let { + if (it.isPasswordPresent || it.atLeastOneFieldProtectedPresent()) { + changeShowPasswordIcon(togglePassword) + } else { + togglePassword?.isVisible = false + } + } + + val gotoUrl = menu.findItem(R.id.menu_goto_url) + gotoUrl?.apply { + // In API >= 11 onCreateOptionsMenu may be called before onCreate completes + // so mEntry may not be set + if (mEntry == null) { + isVisible = false + } else { + if (mEntry?.url?.isEmpty() != false) { + // disable button if url is not available + isVisible = false + } + } + } + + // Show education views + Handler().post { performedNextEducation(EntryActivityEducation(this), menu) } + + return true + } + + private fun performedNextEducation(entryActivityEducation: EntryActivityEducation, + menu: Menu) { + if (entryContentsView?.isUserNamePresent == true + && entryActivityEducation.checkAndPerformedEntryCopyEducation( + findViewById(R.id.entry_user_name_action_image), + { + clipboardHelper?.timeoutCopyToClipboard(mEntry!!.username, + getString(R.string.copy_field, + getString(R.string.entry_user_name))) + }, + { + // Launch autofill settings + startActivity(Intent(this@EntryActivity, SettingsAutofillActivity::class.java)) + })) + else if (toolbar?.findViewById(R.id.menu_edit) != null && entryActivityEducation.checkAndPerformedEntryEditEducation( + toolbar!!.findViewById(R.id.menu_edit), + { + onOptionsItemSelected(menu.findItem(R.id.menu_edit)) + }, + { + // Open Keepass doc to create field references + startActivity(Intent(Intent.ACTION_VIEW, + Uri.parse(getString(R.string.field_references_url)))) + })) + ; + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + when (item.itemId) { + R.id.menu_contribute -> return MenuUtil.onContributionItemSelected(this) + + R.id.menu_toggle_pass -> { + mShowPassword = !mShowPassword + changeShowPasswordIcon(item) + entryContentsView?.setHiddenPasswordStyle(!mShowPassword) + return true + } + + R.id.menu_edit -> { + EntryEditActivity.launch(this@EntryActivity, mEntry) + return true + } + + R.id.menu_goto_url -> { + var url: String = mEntry?.url ?: "" + + // Default http:// if no protocol specified + if (!url.contains("://")) { + url = "http://$url" + } + + try { + Util.gotoUrl(this, url) + } catch (e: ActivityNotFoundException) { + Toast.makeText(this, R.string.no_url_handler, Toast.LENGTH_LONG).show() + } + + return true + } + + R.id.menu_lock -> { + lockAndExit() + return true + } + + android.R.id.home -> finish() // close this activity and return to preview activity (if there is any) + } + + return super.onOptionsItemSelected(item) + } + + + override fun finish() { + // Transit data in previous Activity after an update + /* + TODO Slowdown when add entry as result + Intent intent = new Intent(); + intent.putExtra(EntryEditActivity.ADD_OR_UPDATE_ENTRY_KEY, mEntry); + setResult(EntryEditActivity.UPDATE_ENTRY_RESULT_CODE, intent); + */ + super.finish() + } + + companion object { + private val TAG = EntryActivity::class.java.name + + const val KEY_ENTRY = "entry" + + fun launch(activity: Activity, pw: EntryVersioned, readOnly: Boolean) { + if (TimeoutHelper.checkTimeAndLockIfTimeout(activity)) { + val intent = Intent(activity, EntryActivity::class.java) + intent.putExtra(KEY_ENTRY, pw.nodeId) + ReadOnlyHelper.putReadOnlyInIntent(intent, readOnly) + activity.startActivityForResult(intent, EntryEditActivity.ADD_OR_UPDATE_ENTRY_REQUEST_CODE) + } + } + } +} diff --git a/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.java b/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.java index 3b69e8688..4a0266b63 100644 --- a/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.java +++ b/app/src/main/java/com/kunzisoft/keepass/activities/GroupActivity.java @@ -503,7 +503,7 @@ public class GroupActivity extends LockingActivity EntryVersioned entry = ((EntryVersioned) node); EntrySelectionHelper.INSTANCE.doEntrySelectionAction(getIntent(), () -> { - EntryActivity.launch(GroupActivity.this, entry, getReadOnly()); + EntryActivity.Companion.launch(GroupActivity.this, entry, getReadOnly()); return null; }, () -> { diff --git a/app/src/main/java/com/kunzisoft/keepass/notifications/NotificationEntryCopyManager.kt b/app/src/main/java/com/kunzisoft/keepass/notifications/NotificationEntryCopyManager.kt new file mode 100644 index 000000000..1573dea8d --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/notifications/NotificationEntryCopyManager.kt @@ -0,0 +1,90 @@ +package com.kunzisoft.keepass.notifications + +import android.content.Context +import android.content.Intent +import android.util.Log +import com.kunzisoft.keepass.app.App +import com.kunzisoft.keepass.database.element.EntryVersioned +import com.kunzisoft.keepass.database.element.security.ProtectedString +import com.kunzisoft.keepass.settings.PreferencesUtil +import java.util.* + + +object NotificationEntryCopyManager { + + fun launchNotificationIfAllowed(context: Context, firstLaunch: Boolean, entry: EntryVersioned) { + // Start to manage field reference to copy a value from ref + val database = App.currentDatabase + database.startManageEntry(entry) + + val containsUsernameToCopy = entry.username.isNotEmpty() + val containsPasswordToCopy = entry.password.isNotEmpty() + && PreferencesUtil.allowCopyPasswordAndProtectedFields(context) + val containsExtraFieldToCopy = entry.allowExtraFields() + && (entry.containsCustomFields() + && entry.containsCustomFieldsNotProtected() + || (entry.containsCustomFields() + && entry.containsCustomFieldsProtected() + && PreferencesUtil.allowCopyPasswordAndProtectedFields(context)) + ) + + // If notifications enabled in settings + // Don't if application timeout + if (firstLaunch + && PreferencesUtil.isClipboardNotificationsEnable(context.applicationContext)) { + if (containsUsernameToCopy || containsPasswordToCopy || containsExtraFieldToCopy) { + + // username already copied, waiting for user's action before copy password. + val intent = Intent(context, NotificationCopyingService::class.java) + intent.action = NotificationCopyingService.ACTION_NEW_NOTIFICATION + intent.putExtra(NotificationCopyingService.EXTRA_ENTRY_TITLE, entry.title) + // Construct notification fields + val notificationFields = ArrayList() + // Add username if exists to notifications + if (containsUsernameToCopy) + notificationFields.add( + NotificationField( + NotificationField.NotificationFieldId.USERNAME, + entry.username, + context.resources)) + // Add password to notifications + if (containsPasswordToCopy) { + notificationFields.add( + NotificationField( + NotificationField.NotificationFieldId.PASSWORD, + entry.password, + context.resources)) + } + // Add extra fields + if (containsExtraFieldToCopy) { + try { + entry.fields.doActionToAllCustomProtectedField(object : (String, ProtectedString) -> Unit { + private var anonymousFieldNumber = 0 + override fun invoke(key: String, value: ProtectedString) { + //If value is not protected or allowed + if (!value.isProtected + || PreferencesUtil.allowCopyPasswordAndProtectedFields(context)) { + notificationFields.add( + NotificationField( + NotificationField.NotificationFieldId.getAnonymousFieldId()[anonymousFieldNumber], + value.toString(), + key, + context.resources)) + anonymousFieldNumber++ + } + } + }) + } catch (e: ArrayIndexOutOfBoundsException) { + Log.w("NotificationEntryCopyMg", "Only " + NotificationField.NotificationFieldId.getAnonymousFieldId().size + + " anonymous notifications are available") + } + + } + // Add notifications + intent.putParcelableArrayListExtra(NotificationCopyingService.EXTRA_FIELDS, notificationFields) + context.startService(intent) + } + } + database.stopManageEntry(entry) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/kunzisoft/keepass/view/EntryContentsView.java b/app/src/main/java/com/kunzisoft/keepass/view/EntryContentsView.java deleted file mode 100644 index 9eeea705d..000000000 --- a/app/src/main/java/com/kunzisoft/keepass/view/EntryContentsView.java +++ /dev/null @@ -1,253 +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 . - * - */ -package com.kunzisoft.keepass.view; - -import android.content.Context; -import android.content.res.TypedArray; -import android.graphics.Color; -import android.support.v4.content.ContextCompat; -import android.text.method.PasswordTransformationMethod; -import android.util.AttributeSet; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.ImageView; -import android.widget.LinearLayout; -import android.widget.TextView; - -import com.kunzisoft.keepass.R; -import com.kunzisoft.keepass.database.element.security.ProtectedString; -import com.kunzisoft.keepass.utils.Util; - -import java.text.DateFormat; -import java.util.Date; - -public class EntryContentsView extends LinearLayout { - - private boolean fontInVisibility; - private int colorAccent; - - private View userNameContainerView; - private TextView userNameView; - private ImageView userNameActionView; - - private View passwordContainerView; - private TextView passwordView; - private ImageView passwordActionView; - - private View urlContainerView; - private TextView urlView; - - private View commentContainerView; - private TextView commentView; - - private ViewGroup extrasView; - - private DateFormat dateFormat; - private DateFormat timeFormat; - - private TextView creationDateView; - private TextView modificationDateView; - private TextView lastAccessDateView; - private TextView expiresDateView; - - public EntryContentsView(Context context) { - this(context, null); - } - - public EntryContentsView(Context context, AttributeSet attrs) { - super(context, attrs); - - fontInVisibility = false; - - dateFormat = android.text.format.DateFormat.getDateFormat(context); - timeFormat = android.text.format.DateFormat.getTimeFormat(context); - - inflate(context); - - int[] attrColorAccent = {R.attr.colorAccentCompat}; - TypedArray taColorAccent = context.getTheme().obtainStyledAttributes(attrColorAccent); - this.colorAccent = taColorAccent.getColor(0, Color.BLACK); - taColorAccent.recycle(); - } - - private void inflate(Context context) { - LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); - assert inflater != null; - inflater.inflate(R.layout.entry_view_contents, this); - - userNameContainerView = findViewById(R.id.entry_user_name_container); - userNameView = findViewById(R.id.entry_user_name); - userNameActionView = findViewById(R.id.entry_user_name_action_image); - - passwordContainerView = findViewById(R.id.entry_password_container); - passwordView = findViewById(R.id.entry_password); - passwordActionView = findViewById(R.id.entry_password_action_image); - - urlContainerView = findViewById(R.id.entry_url_container); - urlView = findViewById(R.id.entry_url); - - commentContainerView = findViewById(R.id.entry_notes_container); - commentView = findViewById(R.id.entry_notes); - - extrasView = findViewById(R.id.extra_strings); - - creationDateView = findViewById(R.id.entry_created); - modificationDateView = findViewById(R.id.entry_modified); - lastAccessDateView = findViewById(R.id.entry_accessed); - expiresDateView = findViewById(R.id.entry_expires); - } - - public void applyFontVisibilityToFields(boolean fontInVisibility) { - this.fontInVisibility = fontInVisibility; - } - - public void assignUserName(String userName) { - if (userName != null && !userName.isEmpty()) { - userNameContainerView.setVisibility(VISIBLE); - userNameView.setText(userName); - if (fontInVisibility) - Util.applyFontVisibilityTo(getContext(), userNameView); - } else { - userNameContainerView.setVisibility(GONE); - } - } - - public void assignUserNameCopyListener(OnClickListener onClickListener) { - userNameActionView.setOnClickListener(onClickListener); - } - - public boolean isUserNamePresent() { - return userNameContainerView.getVisibility() == VISIBLE; - } - - public void assignPassword(String password, boolean allowCopyPassword) { - if (password != null && !password.isEmpty()) { - passwordContainerView.setVisibility(VISIBLE); - passwordView.setText(password); - if (fontInVisibility) - Util.applyFontVisibilityTo(getContext(), passwordView); - if (!allowCopyPassword) { - passwordActionView.setColorFilter(ContextCompat.getColor(getContext(), R.color.grey_dark)); - } else { - passwordActionView.setColorFilter(colorAccent); - } - } else { - passwordContainerView.setVisibility(GONE); - } - } - - public void assignPasswordCopyListener(OnClickListener onClickListener) { - if (onClickListener == null) - setClickable(false); - passwordActionView.setOnClickListener(onClickListener); - } - - public boolean isPasswordPresent() { - return passwordContainerView.getVisibility() == VISIBLE; - } - - public boolean atLeastOneFieldProtectedPresent() { - for (int i = 0; i < extrasView.getChildCount(); i++) { - View childCustomView = extrasView.getChildAt(i); - if (childCustomView instanceof EntryCustomFieldProtected) - return true; - } - return false; - } - - public void setHiddenPasswordStyle(boolean hiddenStyle) { - if ( !hiddenStyle ) { - passwordView.setTransformationMethod(null); - } else { - passwordView.setTransformationMethod(PasswordTransformationMethod.getInstance()); - } - // Hidden style for custom fields - for (int i = 0; i < extrasView.getChildCount(); i++) { - View childCustomView = extrasView.getChildAt(i); - if (childCustomView instanceof EntryCustomFieldProtected) - ((EntryCustomFieldProtected) childCustomView).setHiddenPasswordStyle(hiddenStyle); - } - } - - public void assignURL(String url) { - if (url != null && !url.isEmpty()) { - urlContainerView.setVisibility(VISIBLE); - urlView.setText(url); - } else { - urlContainerView.setVisibility(GONE); - } - } - - public void assignComment(String comment) { - if (comment != null && !comment.isEmpty()) { - commentContainerView.setVisibility(VISIBLE); - commentView.setText(comment); - if (fontInVisibility) - Util.applyFontVisibilityTo(getContext(), commentView); - } else { - commentContainerView.setVisibility(GONE); - } - } - - public void addExtraField(String title, ProtectedString value, boolean showAction, OnClickListener onActionClickListener) { - EntryCustomField entryCustomField; - if (value.isProtected()) - entryCustomField = new EntryCustomFieldProtected(getContext(), null, title, value, showAction, onActionClickListener); - else - entryCustomField = new EntryCustomField(getContext(), null, title, value, showAction, onActionClickListener); - entryCustomField.applyFontVisibility(fontInVisibility); - extrasView.addView(entryCustomField); - } - - public void clearExtraFields() { - extrasView.removeAllViews(); - } - - private String getDateTime(Date date) { - return dateFormat.format(date) + " " + timeFormat.format(date); - } - - public void assignCreationDate(Date date) { - creationDateView.setText(getDateTime(date)); - } - - public void assignModificationDate(Date date) { - modificationDateView.setText(getDateTime(date)); - } - - public void assignLastAccessDate(Date date) { - lastAccessDateView.setText(getDateTime(date)); - } - - public void assignExpiresDate(Date date) { - expiresDateView.setText(getDateTime(date)); - } - - public void assignExpiresDate(String constString) { - expiresDateView.setText(constString); - } - - @Override - protected LayoutParams generateDefaultLayoutParams() { - return new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT); - } - -} diff --git a/app/src/main/java/com/kunzisoft/keepass/view/EntryContentsView.kt b/app/src/main/java/com/kunzisoft/keepass/view/EntryContentsView.kt new file mode 100644 index 000000000..cc38c8bcf --- /dev/null +++ b/app/src/main/java/com/kunzisoft/keepass/view/EntryContentsView.kt @@ -0,0 +1,250 @@ +/* + * 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 . + */ +package com.kunzisoft.keepass.view + +import android.content.Context +import android.graphics.Color +import android.support.v4.content.ContextCompat +import android.text.method.PasswordTransformationMethod +import android.util.AttributeSet +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.ImageView +import android.widget.LinearLayout +import android.widget.TextView +import com.kunzisoft.keepass.R +import com.kunzisoft.keepass.database.element.security.ProtectedString +import com.kunzisoft.keepass.utils.Util +import java.text.DateFormat +import java.util.* + +class EntryContentsView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) : LinearLayout(context, attrs) { + + private var fontInVisibility: Boolean = false + private val colorAccent: Int + + private var userNameContainerView: View? = null + private var userNameView: TextView? = null + private var userNameActionView: ImageView? = null + + private var passwordContainerView: View? = null + private var passwordView: TextView? = null + private var passwordActionView: ImageView? = null + + private var urlContainerView: View? = null + private var urlView: TextView? = null + + private var commentContainerView: View? = null + private var commentView: TextView? = null + + private var extrasView: ViewGroup? = null + + private val dateFormat: DateFormat + private val timeFormat: DateFormat + + private var creationDateView: TextView? = null + private var modificationDateView: TextView? = null + private var lastAccessDateView: TextView? = null + private var expiresDateView: TextView? = null + + val isUserNamePresent: Boolean + get() = userNameContainerView!!.visibility == View.VISIBLE + + val isPasswordPresent: Boolean + get() = passwordContainerView!!.visibility == View.VISIBLE + + init { + + fontInVisibility = false + + dateFormat = android.text.format.DateFormat.getDateFormat(context) + timeFormat = android.text.format.DateFormat.getTimeFormat(context) + + inflate(context) + + val attrColorAccent = intArrayOf(R.attr.colorAccentCompat) + val taColorAccent = context.theme.obtainStyledAttributes(attrColorAccent) + this.colorAccent = taColorAccent.getColor(0, Color.BLACK) + taColorAccent.recycle() + } + + private fun inflate(context: Context) { + val inflater = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater + inflater.inflate(R.layout.entry_view_contents, this) + + userNameContainerView = findViewById(R.id.entry_user_name_container) + userNameView = findViewById(R.id.entry_user_name) + userNameActionView = findViewById(R.id.entry_user_name_action_image) + + passwordContainerView = findViewById(R.id.entry_password_container) + passwordView = findViewById(R.id.entry_password) + passwordActionView = findViewById(R.id.entry_password_action_image) + + urlContainerView = findViewById(R.id.entry_url_container) + urlView = findViewById(R.id.entry_url) + + commentContainerView = findViewById(R.id.entry_notes_container) + commentView = findViewById(R.id.entry_notes) + + extrasView = findViewById(R.id.extra_strings) + + creationDateView = findViewById(R.id.entry_created) + modificationDateView = findViewById(R.id.entry_modified) + lastAccessDateView = findViewById(R.id.entry_accessed) + expiresDateView = findViewById(R.id.entry_expires) + } + + fun applyFontVisibilityToFields(fontInVisibility: Boolean) { + this.fontInVisibility = fontInVisibility + } + + fun assignUserName(userName: String?) { + if (userName != null && userName.isNotEmpty()) { + userNameContainerView?.visibility = View.VISIBLE + userNameView?.apply { + text = userName + if (fontInVisibility) + Util.applyFontVisibilityTo(context, this) + } + } else { + userNameContainerView?.visibility = View.GONE + } + } + + fun assignUserNameCopyListener(onClickListener: OnClickListener) { + userNameActionView?.setOnClickListener(onClickListener) + } + + fun assignPassword(password: String?, allowCopyPassword: Boolean) { + if (password != null && password.isNotEmpty()) { + passwordContainerView?.visibility = View.VISIBLE + passwordView?.apply { + text = password + if (fontInVisibility) + Util.applyFontVisibilityTo(context, this) + } + if (!allowCopyPassword) { + passwordActionView?.setColorFilter(ContextCompat.getColor(context, R.color.grey_dark)) + } else { + passwordActionView?.setColorFilter(colorAccent) + } + } else { + passwordContainerView?.visibility = View.GONE + } + } + + fun assignPasswordCopyListener(onClickListener: OnClickListener?) { + if (onClickListener == null) + isClickable = false + passwordActionView?.setOnClickListener(onClickListener) + } + + fun atLeastOneFieldProtectedPresent(): Boolean { + extrasView?.let { + for (i in 0 until it.childCount) { + val childCustomView = it.getChildAt(i) + if (childCustomView is EntryCustomFieldProtected) + return true + } + } + return false + } + + fun setHiddenPasswordStyle(hiddenStyle: Boolean) { + if (!hiddenStyle) { + passwordView?.transformationMethod = null + } else { + passwordView?.transformationMethod = PasswordTransformationMethod.getInstance() + } + // Hidden style for custom fields + extrasView?.let { + for (i in 0 until it.childCount) { + val childCustomView = it.getChildAt(i) + if (childCustomView is EntryCustomFieldProtected) + childCustomView.setHiddenPasswordStyle(hiddenStyle) + } + } + } + + fun assignURL(url: String?) { + if (url != null && url.isNotEmpty()) { + urlContainerView?.visibility = View.VISIBLE + urlView?.text = url + } else { + urlContainerView?.visibility = View.GONE + } + } + + fun assignComment(comment: String?) { + if (comment != null && comment.isNotEmpty()) { + commentContainerView?.visibility = View.VISIBLE + commentView?.apply { + text = comment + if (fontInVisibility) + Util.applyFontVisibilityTo(context, this) + } + + } else { + commentContainerView?.visibility = View.GONE + } + } + + fun addExtraField(title: String, value: ProtectedString, showAction: Boolean, onActionClickListener: OnClickListener) { + val entryCustomField: EntryCustomField + if (value.isProtected) + entryCustomField = EntryCustomFieldProtected(context, null, title, value, showAction, onActionClickListener) + else + entryCustomField = EntryCustomField(context, null, title, value, showAction, onActionClickListener) + entryCustomField.applyFontVisibility(fontInVisibility) + extrasView?.addView(entryCustomField) + } + + fun clearExtraFields() { + extrasView?.removeAllViews() + } + + private fun getDateTime(date: Date): String { + return dateFormat.format(date) + " " + timeFormat.format(date) + } + + fun assignCreationDate(date: Date) { + creationDateView?.text = getDateTime(date) + } + + fun assignModificationDate(date: Date) { + modificationDateView?.text = getDateTime(date) + } + + fun assignLastAccessDate(date: Date) { + lastAccessDateView?.text = getDateTime(date) + } + + fun assignExpiresDate(date: Date) { + expiresDateView?.text = getDateTime(date) + } + + fun assignExpiresDate(constString: String) { + expiresDateView?.text = constString + } + + override fun generateDefaultLayoutParams(): LayoutParams { + return LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT) + } +}