diff --git a/app/src/main/java/com/keepassdroid/activities/EntryEditActivity.java b/app/src/main/java/com/keepassdroid/activities/EntryEditActivity.java index 9361e0267..4b98bfacd 100644 --- a/app/src/main/java/com/keepassdroid/activities/EntryEditActivity.java +++ b/app/src/main/java/com/keepassdroid/activities/EntryEditActivity.java @@ -28,12 +28,11 @@ import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; -import android.view.View.OnClickListener; import android.view.ViewGroup; +import android.widget.EditText; import android.widget.ImageButton; import android.widget.LinearLayout; import android.widget.ScrollView; -import android.widget.TextView; import android.widget.Toast; import com.keepassdroid.app.App; @@ -88,12 +87,12 @@ public class EntryEditActivity extends LockingHideActivity // Views private ScrollView scrollView; - private TextView entryTitleView; - private TextView entryUserNameView; - private TextView entryUrlView; - private TextView entryPasswordView; - private TextView entryConfirmationPasswordView; - private TextView entryCommentView; + private EditText entryTitleView; + private EditText entryUserNameView; + private EditText entryUrlView; + private EditText entryPasswordView; + private EditText entryConfirmationPasswordView; + private EditText entryCommentView; private ViewGroup entryExtraFieldsContainer; /** @@ -123,23 +122,23 @@ public class EntryEditActivity extends LockingHideActivity super.onCreate(savedInstanceState); setContentView(R.layout.entry_edit); - Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); + Toolbar toolbar = findViewById(R.id.toolbar); toolbar.setTitle(getString(R.string.app_name)); setSupportActionBar(toolbar); assert getSupportActionBar() != null; getSupportActionBar().setDisplayHomeAsUpEnabled(true); getSupportActionBar().setDisplayShowHomeEnabled(true); - scrollView = (ScrollView) findViewById(R.id.entry_scroll); + scrollView = findViewById(R.id.entry_scroll); scrollView.setScrollBarStyle(View.SCROLLBARS_INSIDE_INSET); - entryTitleView = (TextView) findViewById(R.id.entry_title); - entryUserNameView = (TextView) findViewById(R.id.entry_user_name); - entryUrlView = (TextView) findViewById(R.id.entry_url); - entryPasswordView = (TextView) findViewById(R.id.entry_password); - entryConfirmationPasswordView = (TextView) findViewById(R.id.entry_confpassword); - entryCommentView = (TextView) findViewById(R.id.entry_comment); - entryExtraFieldsContainer = (ViewGroup) findViewById(R.id.advanced_container); + entryTitleView = findViewById(R.id.entry_title); + entryUserNameView = findViewById(R.id.entry_user_name); + entryUrlView = findViewById(R.id.entry_url); + entryPasswordView = findViewById(R.id.entry_password); + entryConfirmationPasswordView = findViewById(R.id.entry_confpassword); + entryCommentView = findViewById(R.id.entry_comment); + entryExtraFieldsContainer = findViewById(R.id.advanced_container); // Likely the app has been killed exit the activity Database db = App.getDB(); @@ -165,67 +164,47 @@ public class EntryEditActivity extends LockingHideActivity } View iconButton = findViewById(R.id.icon_button); - iconButton.setOnClickListener(new View.OnClickListener() { - - public void onClick(View v) { - IconPickerDialogFragment.launch(EntryEditActivity.this); - } - }); + iconButton.setOnClickListener(v -> + IconPickerDialogFragment.launch(EntryEditActivity.this)); // Generate password button View generatePassword = findViewById(R.id.generate_button); - generatePassword.setOnClickListener(new OnClickListener() { - - public void onClick(View v) { - GeneratePasswordDialogFragment generatePasswordDialogFragment = new GeneratePasswordDialogFragment(); - generatePasswordDialogFragment.show(getSupportFragmentManager(), "PasswordGeneratorFragment"); - } - }); + generatePassword.setOnClickListener(v -> { + GeneratePasswordDialogFragment generatePasswordDialogFragment = new GeneratePasswordDialogFragment(); + generatePasswordDialogFragment.show(getSupportFragmentManager(), "PasswordGeneratorFragment"); + }); // Save button View save = findViewById(R.id.entry_save); - save.setOnClickListener(new View.OnClickListener() { + save.setOnClickListener(v -> { + if (!validateBeforeSaving()) { + return; + } + mCallbackNewEntry = populateNewEntry(); - public void onClick(View v) { - if (!validateBeforeSaving()) { - return; - } - mCallbackNewEntry = populateNewEntry(); - - OnFinish onFinish = new AfterSave(); - EntryEditActivity act = EntryEditActivity.this; - RunnableOnFinish task; - if ( mIsNew ) { - task = new AddEntry(act, App.getDB(), mCallbackNewEntry, onFinish); - } else { - task = new UpdateEntry(act, App.getDB(), mEntry, mCallbackNewEntry, onFinish); - } - ProgressTask pt = new ProgressTask(act, task, R.string.saving_database); - pt.run(); - } - - }); + OnFinish onFinish = new AfterSave(); + EntryEditActivity act = EntryEditActivity.this; + RunnableOnFinish task; + if ( mIsNew ) { + task = new AddEntry(act, App.getDB(), mCallbackNewEntry, onFinish); + } else { + task = new UpdateEntry(act, App.getDB(), mEntry, mCallbackNewEntry, onFinish); + } + ProgressTask pt = new ProgressTask(act, task, R.string.saving_database); + pt.run(); + }); if (mEntry.allowExtraFields()) { View add = findViewById(R.id.add_new_field); add.setVisibility(View.VISIBLE); - add.setOnClickListener(new View.OnClickListener() { + add.setOnClickListener(v -> { + EntryEditNewField ees = new EntryEditNewField(EntryEditActivity.this); + ees.setData("", new ProtectedString(false, "")); + entryExtraFieldsContainer.addView(ees); - @Override - public void onClick(View v) { - EntryEditNewField ees = new EntryEditNewField(EntryEditActivity.this); - ees.setData("", new ProtectedString(false, "")); - entryExtraFieldsContainer.addView(ees); - - // Scroll bottom - scrollView.post(new Runnable() { - @Override - public void run() { - scrollView.fullScroll(ScrollView.FOCUS_DOWN); - } - }); - } + // Scroll bottom + scrollView.post(() -> scrollView.fullScroll(ScrollView.FOCUS_DOWN)); }); } } @@ -334,11 +313,9 @@ public class EntryEditActivity extends LockingHideActivity } protected void fillData() { - ImageButton currIconButton = (ImageButton) findViewById(R.id.icon_button); + ImageButton currIconButton = findViewById(R.id.icon_button); App.getDB().drawFactory.assignDrawableTo(currIconButton, getResources(), mEntry.getIcon()); - boolean visibilityFont = PreferencesUtil.fieldFontIsInVisibility(this); - entryTitleView.setText(mEntry.getTitle()); entryUserNameView.setText(mEntry.getUsername()); entryUrlView.setText(mEntry.getUrl()); @@ -346,14 +323,21 @@ public class EntryEditActivity extends LockingHideActivity entryPasswordView.setText(password); entryConfirmationPasswordView.setText(password); entryCommentView.setText(mEntry.getNotes()); - Util.applyFontVisibilityToTextView(visibilityFont, entryCommentView); + + boolean visibilityFontActivated = PreferencesUtil.fieldFontIsInVisibility(this); + if (visibilityFontActivated) { + Util.applyFontVisibilityTo(entryUserNameView); + Util.applyFontVisibilityTo(entryPasswordView); + Util.applyFontVisibilityTo(entryConfirmationPasswordView); + Util.applyFontVisibilityTo(entryCommentView); + } if (mEntry.allowExtraFields()) { - LinearLayout container = (LinearLayout) findViewById(R.id.advanced_container); + LinearLayout container = findViewById(R.id.advanced_container); for (Map.Entry pair : mEntry.getExtraProtectedFields().entrySet()) { EntryEditNewField entryEditNewField = new EntryEditNewField(EntryEditActivity.this); entryEditNewField.setData(pair.getKey(), pair.getValue()); - entryEditNewField.setFontVisibility(visibilityFont); + entryEditNewField.setFontVisibility(visibilityFontActivated); container.addView(entryEditNewField); } } @@ -362,7 +346,7 @@ public class EntryEditActivity extends LockingHideActivity @Override public void iconPicked(Bundle bundle) { mSelectedIconID = bundle.getInt(IconPickerDialogFragment.KEY_ICON_ID); - ImageButton currIconButton = (ImageButton) findViewById(R.id.icon_button); + ImageButton currIconButton = findViewById(R.id.icon_button); currIconButton.setImageResource(Icons.iconToResId(mSelectedIconID)); } diff --git a/app/src/main/java/com/keepassdroid/utils/SpannableReplacer.java b/app/src/main/java/com/keepassdroid/utils/SpannableReplacer.java new file mode 100644 index 000000000..17601e6f3 --- /dev/null +++ b/app/src/main/java/com/keepassdroid/utils/SpannableReplacer.java @@ -0,0 +1,88 @@ +/* + * 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 2 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.keepassdroid.utils; + +import android.os.Parcel; +import android.text.Spannable; +import android.text.SpannableStringBuilder; +import android.text.TextUtils; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class SpannableReplacer { + private final CharSequence mSource; + private final CharSequence mReplacement; + private final Matcher mMatcher; + private int mAppendPosition; + private final boolean mIsSpannable; + + public static CharSequence replace(CharSequence source, String regex, + CharSequence replacement) { + + Pattern pattern = Pattern.compile(regex); + Matcher matcher = pattern.matcher(source); + return new SpannableReplacer(source, matcher, replacement).doReplace(); + } + + private SpannableReplacer(CharSequence source, Matcher matcher, + CharSequence replacement) { + mSource = source; + mReplacement = replacement; + mMatcher = matcher; + mAppendPosition = 0; + mIsSpannable = replacement instanceof Spannable; + } + + private CharSequence doReplace() { + SpannableStringBuilder buffer = new SpannableStringBuilder(); + while (mMatcher.find()) { + appendReplacement(buffer); + } + return appendTail(buffer); + } + + private void appendReplacement(SpannableStringBuilder buffer) { + buffer.append(mSource.subSequence(mAppendPosition, mMatcher.start())); + CharSequence replacement = mIsSpannable + ? copyCharSequenceWithSpans(mReplacement) + : mReplacement; + buffer.append(replacement); + + mAppendPosition = mMatcher.end(); + } + + public SpannableStringBuilder appendTail(SpannableStringBuilder buffer) { + buffer.append(mSource.subSequence(mAppendPosition, mSource.length())); + return buffer; + } + + // This is a weird way of copying spans, but I don't know any better way. + private CharSequence copyCharSequenceWithSpans(CharSequence string) { + Parcel parcel = Parcel.obtain(); + try { + TextUtils.writeToParcel(string, parcel, 0); + parcel.setDataPosition(0); + return TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel); + } finally { + parcel.recycle(); + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/keepassdroid/utils/Util.java b/app/src/main/java/com/keepassdroid/utils/Util.java index 4b1171f6c..f5d8a5027 100644 --- a/app/src/main/java/com/keepassdroid/utils/Util.java +++ b/app/src/main/java/com/keepassdroid/utils/Util.java @@ -24,6 +24,12 @@ import android.content.Context; import android.content.Intent; import android.graphics.Typeface; import android.net.Uri; +import android.text.Editable; +import android.text.SpannableString; +import android.text.Spanned; +import android.text.TextWatcher; +import android.text.style.StrikethroughSpan; +import android.widget.EditText; import android.widget.TextView; import java.io.IOException; @@ -51,9 +57,66 @@ public class Util { } } - public static void applyFontVisibilityToTextView(boolean applyMonospace, TextView textView) { - if (applyMonospace) - textView.setTypeface(Typeface.MONOSPACE); + private final static String stringToStrikeThrough = "0"; + + /** + * Replace font by monospace and strike through all zeros, must be called after seText() + */ + public static void applyFontVisibilityTo(final TextView textView) { + textView.setText(strikeThroughToZero(textView.getText())); + textView.setTypeface(Typeface.MONOSPACE); + } + + /** + * Replace font by monospace and strike through all zeros, must be called after seText() + */ + public static void applyFontVisibilityTo(final EditText editText) { + // Assign spans to default text + editText.setText(strikeThroughToZero(editText.getText())); + // Add spans for each new 0 character + class TextWatcherCustomFont implements TextWatcher { + + private boolean applySpannable; + + @Override + public void beforeTextChanged(CharSequence charSequence, int start, int count, int after) { + applySpannable = count < after; + } + + @Override + public void onTextChanged(CharSequence charSequence, int start, int before, int count) { + if (applySpannable) { + String text = charSequence.toString(); + if (text.contains(stringToStrikeThrough)) { + for (int index = text.indexOf(stringToStrikeThrough); + index >= 0; index = text.indexOf(stringToStrikeThrough, + index + 1)) { + editText.getText().setSpan(new StrikethroughSpan(), + index, + index + stringToStrikeThrough.length(), + Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + } + } + } + } + + @Override + public void afterTextChanged(Editable editable) {} + }; + TextWatcher textWatcher = new TextWatcherCustomFont(); + editText.addTextChangedListener(textWatcher); + editText.setTypeface(Typeface.MONOSPACE); } + private static CharSequence strikeThroughToZero(final CharSequence text) { + if (text.toString().contains(stringToStrikeThrough)) { + SpannableString spannable = new SpannableString(stringToStrikeThrough); + spannable.setSpan(new StrikethroughSpan(), + 0, + stringToStrikeThrough.length(), + Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + return SpannableReplacer.replace(text, stringToStrikeThrough, spannable); + } + return text; + } } diff --git a/app/src/main/java/com/keepassdroid/view/EntryContentsView.java b/app/src/main/java/com/keepassdroid/view/EntryContentsView.java index cd914c941..9074900cf 100644 --- a/app/src/main/java/com/keepassdroid/view/EntryContentsView.java +++ b/app/src/main/java/com/keepassdroid/view/EntryContentsView.java @@ -84,25 +84,25 @@ public class EntryContentsView extends LinearLayout { inflater.inflate(R.layout.entry_view_contents, this); userNameContainerView = findViewById(R.id.entry_user_name_container); - userNameView = (TextView) findViewById(R.id.entry_user_name); - userNameActionView = (ImageView) findViewById(R.id.entry_user_name_action_image); + userNameView = findViewById(R.id.entry_user_name); + userNameActionView = findViewById(R.id.entry_user_name_action_image); passwordContainerView = findViewById(R.id.entry_password_container); - passwordView = (TextView) findViewById(R.id.entry_password); - passwordActionView = (ImageView) findViewById(R.id.entry_password_action_image); + passwordView = findViewById(R.id.entry_password); + passwordActionView = findViewById(R.id.entry_password_action_image); urlContainerView = findViewById(R.id.entry_url_container); - urlView = (TextView) findViewById(R.id.entry_url); + urlView = findViewById(R.id.entry_url); commentContainerView = findViewById(R.id.entry_comment_container); - commentView = (TextView) findViewById(R.id.entry_comment); + commentView = findViewById(R.id.entry_comment); - extrasView = (ViewGroup) findViewById(R.id.extra_strings); + extrasView = findViewById(R.id.extra_strings); - creationDateView = (TextView) findViewById(R.id.entry_created); - modificationDateView = (TextView) findViewById(R.id.entry_modified); - lastAccessDateView = (TextView) findViewById(R.id.entry_accessed); - expiresDateView = (TextView) findViewById(R.id.entry_expires); + 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) { @@ -113,6 +113,10 @@ public class EntryContentsView extends LinearLayout { if (userName != null && !userName.isEmpty()) { userNameContainerView.setVisibility(VISIBLE); userNameView.setText(userName); + if (fontInVisibility) + Util.applyFontVisibilityTo(userNameView); + } else { + userNameContainerView.setVisibility(GONE); } } @@ -124,6 +128,10 @@ public class EntryContentsView extends LinearLayout { if (password != null && !password.isEmpty()) { passwordContainerView.setVisibility(VISIBLE); passwordView.setText(password); + if (fontInVisibility) + Util.applyFontVisibilityTo(passwordView); + } else { + passwordContainerView.setVisibility(GONE); } } @@ -147,21 +155,25 @@ public class EntryContentsView extends LinearLayout { 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); - Util.applyFontVisibilityToTextView(fontInVisibility, commentView); commentView.setText(comment); + if (fontInVisibility) + Util.applyFontVisibilityTo(commentView); + } else { + commentContainerView.setVisibility(GONE); } } public void addExtraField(String title, String value, OnClickListener onActionClickListener) { EntryNewField entryNewField = new EntryNewField(getContext(), null, title, value, onActionClickListener); - entryNewField.applyFontVisibilityToValue(fontInVisibility); + entryNewField.applyFontVisibility(fontInVisibility); extrasView.addView(entryNewField); } diff --git a/app/src/main/java/com/keepassdroid/view/EntryEditNewField.java b/app/src/main/java/com/keepassdroid/view/EntryEditNewField.java index e0326bff9..0f007a204 100644 --- a/app/src/main/java/com/keepassdroid/view/EntryEditNewField.java +++ b/app/src/main/java/com/keepassdroid/view/EntryEditNewField.java @@ -26,6 +26,7 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.CompoundButton; +import android.widget.EditText; import android.widget.RelativeLayout; import android.widget.TextView; @@ -36,7 +37,7 @@ import com.kunzisoft.keepass.R; public class EntryEditNewField extends RelativeLayout { private TextView labelView; - private TextView valueView; + private EditText valueView; private CompoundButton protectionCheckView; public EntryEditNewField(Context context) { @@ -55,16 +56,11 @@ public class EntryEditNewField extends RelativeLayout { inflater.inflate(R.layout.entry_edit_new_field, this); View deleteView = findViewById(R.id.entry_edit_new_field_delete); - deleteView.setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - deleteViewFromParent(); - } - }); + deleteView.setOnClickListener(v -> deleteViewFromParent()); - labelView = (TextView) findViewById(R.id.entry_edit_new_field_label); - valueView = (TextView) findViewById(R.id.entry_edit_new_field_value); - protectionCheckView = (CompoundButton) findViewById(R.id.protection); + labelView = findViewById(R.id.entry_edit_new_field_label); + valueView = findViewById(R.id.entry_edit_new_field_value); + protectionCheckView = findViewById(R.id.protection); } public void setData(String label, ProtectedString value) { @@ -89,7 +85,8 @@ public class EntryEditNewField extends RelativeLayout { } public void setFontVisibility(boolean applyFontVisibility) { - Util.applyFontVisibilityToTextView(applyFontVisibility, valueView); + if (applyFontVisibility) + Util.applyFontVisibilityTo(valueView); } public void deleteViewFromParent() { diff --git a/app/src/main/java/com/keepassdroid/view/EntryNewField.java b/app/src/main/java/com/keepassdroid/view/EntryNewField.java index 59f857d1d..38b6f3eeb 100644 --- a/app/src/main/java/com/keepassdroid/view/EntryNewField.java +++ b/app/src/main/java/com/keepassdroid/view/EntryNewField.java @@ -54,17 +54,18 @@ public class EntryNewField extends LinearLayout { assert inflater != null; inflater.inflate(R.layout.entry_new_field, this); - labelView = (TextView) findViewById(R.id.title); - valueView = (TextView) findViewById(R.id.value); - actionImageView = (ImageView) findViewById(R.id.action_image); + labelView = findViewById(R.id.title); + valueView = findViewById(R.id.value); + actionImageView = findViewById(R.id.action_image); setLabel(label); setValue(value); setAction(onClickActionListener); } - public void applyFontVisibilityToValue(boolean changeFont) { - Util.applyFontVisibilityToTextView(changeFont, valueView); + public void applyFontVisibility(boolean fontInVisibility) { + if (fontInVisibility) + Util.applyFontVisibilityTo(valueView); } public void setLabel(String label) { diff --git a/app/src/main/res/values/donottranslate.xml b/app/src/main/res/values/donottranslate.xml index 8b75684c3..b126789a8 100644 --- a/app/src/main/res/values/donottranslate.xml +++ b/app/src/main/res/values/donottranslate.xml @@ -79,7 +79,7 @@ true true true - false + true 300000