New option to show the previous token

Closes #506
This commit is contained in:
Jakob Nixdorf 2021-03-28 20:56:22 +02:00
parent aea7fa6964
commit 8107256cde
No known key found for this signature in database
GPG key ID: BE99BF86574A7DBC
11 changed files with 195 additions and 93 deletions

View file

@ -491,7 +491,8 @@ public class MainActivity extends BaseActivity
key.equals(getString(R.string.settings_key_theme_mode)) || key.equals(getString(R.string.settings_key_theme_mode)) ||
key.equals(getString(R.string.settings_key_theme_black_auto)) || key.equals(getString(R.string.settings_key_theme_black_auto)) ||
key.equals(getString(R.string.settings_key_hide_global_timeout)) || key.equals(getString(R.string.settings_key_hide_global_timeout)) ||
key.equals(getString(R.string.settings_key_hide_issuer))) { key.equals(getString(R.string.settings_key_hide_issuer)) ||
key.equals(getString(R.string.settings_key_show_prev_token))) {
recreateActivity = true; recreateActivity = true;
} }
} }

View file

@ -70,6 +70,7 @@ public class Entry {
private String issuer; private String issuer;
private String label; private String label;
private String currentOTP; private String currentOTP;
private String prevOTP;
private boolean visible = false; private boolean visible = false;
private Runnable hideTask = null; private Runnable hideTask = null;
private long last_update = 0; private long last_update = 0;
@ -150,7 +151,12 @@ public class Entry {
String counter = uri.getQueryParameter("counter"); String counter = uri.getQueryParameter("counter");
String issuer = uri.getQueryParameter("issuer"); String issuer = uri.getQueryParameter("issuer");
String label = getStrippedLabel(issuer, uri.getPath().substring(1));
String label = "";
if (uri.getPath() != null)
label = getStrippedLabel(issuer, uri.getPath().substring(1));
String period = uri.getQueryParameter("period"); String period = uri.getQueryParameter("period");
String digits = uri.getQueryParameter("digits"); String digits = uri.getQueryParameter("digits");
String algorithm = uri.getQueryParameter("algorithm"); String algorithm = uri.getQueryParameter("algorithm");
@ -172,6 +178,10 @@ public class Entry {
this.issuer = issuer; this.issuer = issuer;
this.label = label; this.label = label;
if (secret == null)
throw new Exception("Empty secret");
if(type == OTPType.MOTP) { if(type == OTPType.MOTP) {
this.secret = secret.getBytes(); this.secret = secret.getBytes();
} else { } else {
@ -475,6 +485,10 @@ public class Entry {
return currentOTP; return currentOTP;
} }
public String getPrevOTP() {
return prevOTP;
}
public String getPin() { public String getPin() {
return pin; return pin;
} }
@ -492,18 +506,48 @@ public class Entry {
} }
public boolean updateOTP(boolean updateNow) { public boolean updateOTP(boolean updateNow) {
if (type == OTPType.TOTP || type == OTPType.STEAM) { if (type == OTPType.TOTP || type == OTPType.STEAM || type == OTPType.MOTP) {
long time = System.currentTimeMillis() / 1000; long time = System.currentTimeMillis() / 1000;
long counter = time / this.getPeriod(); long counter = time / this.getPeriod();
if (updateNow || counter > last_update) { if (updateNow || counter > last_update) {
if (type == OTPType.TOTP) // Store the previous token so we don't have to recalculate it every time
currentOTP = TokenCalculator.TOTP_RFC6238(secret, period, digits, algorithm); if (currentOTP != null && !currentOTP.isEmpty())
else if (type == OTPType.STEAM) prevOTP = currentOTP;
currentOTP = TokenCalculator.TOTP_Steam(secret, period, digits, algorithm); else
prevOTP = "";
switch (type) {
case TOTP:
currentOTP = TokenCalculator.TOTP_RFC6238(secret, period, digits, algorithm, 0);
if (prevOTP == null || prevOTP.isEmpty())
prevOTP = TokenCalculator.TOTP_RFC6238(secret, period, digits, algorithm, -1);
break;
case STEAM:
currentOTP = TokenCalculator.TOTP_Steam(secret, period, digits, algorithm, 0);
if (prevOTP == null || prevOTP.isEmpty())
prevOTP = TokenCalculator.TOTP_Steam(secret, period, digits, algorithm, -1);
break;
case MOTP:
String currentPin = this.getPin();
if (currentPin.isEmpty()) {
currentOTP = MOTP_NO_PIN_CODE;
} else {
currentOTP = TokenCalculator.MOTP(currentPin, new String(this.secret), time, 0);
if (prevOTP == null || prevOTP.isEmpty())
prevOTP = TokenCalculator.MOTP(currentPin, new String(this.secret), time, -1);
}
break;
}
last_update = counter; last_update = counter;
//New OTP. Change color to default color
setColor(COLOR_DEFAULT); setColor(COLOR_DEFAULT);
return true; return true;
} else { } else {
@ -512,22 +556,6 @@ public class Entry {
} else if (type == OTPType.HOTP) { } else if (type == OTPType.HOTP) {
currentOTP = TokenCalculator.HOTP(secret, counter, digits, algorithm); currentOTP = TokenCalculator.HOTP(secret, counter, digits, algorithm);
return true; return true;
} else if (type == OTPType.MOTP) {
long time = System.currentTimeMillis() / 1000;
long counter = time / this.getPeriod();
if (counter > last_update || updateNow) {
String currentPin = this.getPin();
if (currentPin.isEmpty()) {
currentOTP = MOTP_NO_PIN_CODE;
} else {
currentOTP = TokenCalculator.MOTP(currentPin, new String(this.secret), time);
}
last_update = counter;
setColor(COLOR_DEFAULT);
return true;
} else {
return false;
}
} else { } else {
return false; return false;
} }

View file

@ -675,4 +675,8 @@ public class Settings {
String labelDisplay = getString(R.string.settings_key_label_display, R.string.settings_default_label_display); String labelDisplay = getString(R.string.settings_key_label_display, R.string.settings_default_label_display);
return Constants.LabelDisplay.valueOf(labelDisplay.toUpperCase(Locale.ENGLISH)); return Constants.LabelDisplay.valueOf(labelDisplay.toUpperCase(Locale.ENGLISH));
} }
public boolean getShowPrevToken() {
return getBoolean(R.string.settings_key_show_prev_token, false);
}
} }

View file

@ -61,19 +61,24 @@ public class TokenCalculator {
return mac.doFinal(data); return mac.doFinal(data);
} }
// TODO: Rewrite tests so this compatibility wrapper can be removed
public static int TOTP_RFC6238(byte[] secret, int period, long time, int digits, HashAlgorithm algorithm) { public static int TOTP_RFC6238(byte[] secret, int period, long time, int digits, HashAlgorithm algorithm) {
int fullToken = TOTP(secret, period, time, algorithm); return TOTP_RFC6238(secret, period, time, digits, algorithm, 0);
}
public static int TOTP_RFC6238(byte[] secret, int period, long time, int digits, HashAlgorithm algorithm, int offset) {
int fullToken = TOTP(secret, period, time, algorithm, offset);
int div = (int) Math.pow(10, digits); int div = (int) Math.pow(10, digits);
return fullToken % div; return fullToken % div;
} }
public static String TOTP_RFC6238(byte[] secret, int period, int digits, HashAlgorithm algorithm) { public static String TOTP_RFC6238(byte[] secret, int period, int digits, HashAlgorithm algorithm, int offset) {
return Tools.formatTokenString(TOTP_RFC6238(secret, period, System.currentTimeMillis() / 1000, digits, algorithm), digits); return Tools.formatTokenString(TOTP_RFC6238(secret, period, System.currentTimeMillis() / 1000, digits, algorithm, offset), digits);
} }
public static String TOTP_Steam(byte[] secret, int period, int digits, HashAlgorithm algorithm) { public static String TOTP_Steam(byte[] secret, int period, int digits, HashAlgorithm algorithm, int offset) {
int fullToken = TOTP(secret, period, System.currentTimeMillis() / 1000, algorithm); int fullToken = TOTP(secret, period, System.currentTimeMillis() / 1000, algorithm, offset);
StringBuilder tokenBuilder = new StringBuilder(); StringBuilder tokenBuilder = new StringBuilder();
@ -92,8 +97,8 @@ public class TokenCalculator {
return Tools.formatTokenString(fullToken % div, digits); return Tools.formatTokenString(fullToken % div, digits);
} }
private static int TOTP(byte[] key, int period, long time, HashAlgorithm algorithm) { private static int TOTP(byte[] key, int period, long time, HashAlgorithm algorithm, int offset) {
return HOTP(key, time / period, algorithm); return HOTP(key, (time / period) + offset, algorithm);
} }
private static int HOTP(byte[] key, long counter, HashAlgorithm algorithm) private static int HOTP(byte[] key, long counter, HashAlgorithm algorithm)
@ -119,9 +124,9 @@ public class TokenCalculator {
return r; return r;
} }
public static String MOTP(String PIN, String secret, long epoch) public static String MOTP(String PIN, String secret, long epoch, int offset)
{ {
String epochText = String.valueOf(epoch / 10); String epochText = String.valueOf((epoch / 10) + offset);
String hashText = epochText + secret + PIN; String hashText = epochText + secret + PIN;
String otp = ""; String otp = "";

View file

@ -69,11 +69,11 @@ public class EntryViewHolder extends RecyclerView.ViewHolder
private final LinearLayout coverLayout; private final LinearLayout coverLayout;
private final LinearLayout counterLayout; private final LinearLayout counterLayout;
private final FrameLayout thumbnailFrame; private final FrameLayout thumbnailFrame;
private final ImageView visibleImg;
private final ImageView thumbnailImg; private final ImageView thumbnailImg;
private final ImageButton menuButton; private final ImageButton menuButton;
private final ImageButton copyButton; private final ImageButton copyButton;
private final TextView value; private final TextView value;
private final TextView valuePrev;
private final TextView label; private final TextView label;
private final TextView counter; private final TextView counter;
private final TextView tags; private final TextView tags;
@ -86,8 +86,8 @@ public class EntryViewHolder extends RecyclerView.ViewHolder
card = v.findViewById(R.id.card_view); card = v.findViewById(R.id.card_view);
value = v.findViewById(R.id.valueText); value = v.findViewById(R.id.valueText);
valuePrev = v.findViewById(R.id.valueTextPrev);
valueLayout = v.findViewById(R.id.valueLayout); valueLayout = v.findViewById(R.id.valueLayout);
visibleImg = v.findViewById(R.id.valueImg);
thumbnailFrame = v.findViewById(R.id.thumbnailFrame); thumbnailFrame = v.findViewById(R.id.thumbnailFrame);
thumbnailImg = v.findViewById(R.id.thumbnailImg); thumbnailImg = v.findViewById(R.id.thumbnailImg);
coverLayout = v.findViewById(R.id.coverLayout); coverLayout = v.findViewById(R.id.coverLayout);
@ -106,7 +106,6 @@ public class EntryViewHolder extends RecyclerView.ViewHolder
menuButton.getDrawable().setColorFilter(colorFilter); menuButton.getDrawable().setColorFilter(colorFilter);
copyButton.getDrawable().setColorFilter(colorFilter); copyButton.getDrawable().setColorFilter(colorFilter);
visibleImg.getDrawable().setColorFilter(colorFilter);
invisibleImg.getDrawable().setColorFilter(colorFilter); invisibleImg.getDrawable().setColorFilter(colorFilter);
setupOnClickListeners(menuButton, copyButton); setupOnClickListeners(menuButton, copyButton);
@ -221,6 +220,21 @@ public class EntryViewHolder extends RecyclerView.ViewHolder
// save the unformatted token to the tag of this TextView for copy/paste // save the unformatted token to the tag of this TextView for copy/paste
value.setTag(entry.getCurrentOTP()); value.setTag(entry.getCurrentOTP());
if (settings.getShowPrevToken()) {
String tokenPrev = entry.getPrevOTP();
if (tokenPrev != null && !tokenPrev.isEmpty()) {
String tokenFormattedPrev = Tools.formatToken(tokenPrev, settings.getTokenSplitGroupSize());
valuePrev.setVisibility(View.VISIBLE);
valuePrev.setText(tokenFormattedPrev);
} else {
valuePrev.setVisibility(View.GONE);
}
} else {
valuePrev.setVisibility(View.GONE);
}
List<String> entryTags = entry.getTags(); List<String> entryTags = entry.getTags();
StringBuilder stringBuilder = new StringBuilder(); StringBuilder stringBuilder = new StringBuilder();
@ -255,11 +269,9 @@ public class EntryViewHolder extends RecyclerView.ViewHolder
if (entry.isVisible()) { if (entry.isVisible()) {
valueLayout.setVisibility(View.VISIBLE); valueLayout.setVisibility(View.VISIBLE);
coverLayout.setVisibility(View.GONE); coverLayout.setVisibility(View.GONE);
visibleImg.setVisibility(View.GONE);
} else { } else {
valueLayout.setVisibility(View.GONE); valueLayout.setVisibility(View.GONE);
coverLayout.setVisibility(View.VISIBLE); coverLayout.setVisibility(View.VISIBLE);
visibleImg.setVisibility(View.VISIBLE);
} }
} }
} }
@ -316,11 +328,9 @@ public class EntryViewHolder extends RecyclerView.ViewHolder
if (enabled) { if (enabled) {
valueLayout.setVisibility(View.GONE); valueLayout.setVisibility(View.GONE);
coverLayout.setVisibility(View.VISIBLE); coverLayout.setVisibility(View.VISIBLE);
visibleImg.setVisibility(View.VISIBLE);
} else { } else {
valueLayout.setVisibility(View.VISIBLE); valueLayout.setVisibility(View.VISIBLE);
coverLayout.setVisibility(View.GONE); coverLayout.setVisibility(View.GONE);
visibleImg.setVisibility(View.GONE);
} }
} }
@ -369,5 +379,6 @@ public class EntryViewHolder extends RecyclerView.ViewHolder
} }
value.setTextColor(textColor); value.setTextColor(textColor);
valuePrev.setTextColor(textColor);
} }
} }

View file

@ -1,13 +1,14 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView <androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/card_view" android:id="@+id/card_view"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="@dimen/activity_margin_xxsmall" android:layout_marginTop="@dimen/activity_margin_xxsmall"
android:layout_marginBottom="@dimen/activity_margin_xxsmall" android:layout_marginBottom="@dimen/activity_margin_xxsmall"
android:clickable="true" android:clickable="true"
android:focusable="true"
android:foreground="?android:attr/selectableItemBackground" android:foreground="?android:attr/selectableItemBackground"
app:contentPadding="0dp" app:contentPadding="0dp"
style="?attr/cardStyle"> style="?attr/cardStyle">
@ -21,7 +22,8 @@
android:orientation="horizontal" android:orientation="horizontal"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:baselineAligned="false"> android:baselineAligned="false"
tools:ignore="UselessParent">
<FrameLayout <FrameLayout
android:id="@+id/thumbnailFrame" android:id="@+id/thumbnailFrame"
@ -36,7 +38,8 @@
android:layout_width="@dimen/card_thumbnail_size" android:layout_width="@dimen/card_thumbnail_size"
android:layout_height="@dimen/card_thumbnail_size" android:layout_height="@dimen/card_thumbnail_size"
android:layout_gravity="center" android:layout_gravity="center"
android:src="@mipmap/ic_launcher" /> android:src="@mipmap/ic_launcher"
tools:ignore="ContentDescription" />
</FrameLayout> </FrameLayout>
@ -72,28 +75,31 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:visibility="gone"> android:visibility="gone">
<ImageView
android:id="@+id/valueImg"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_marginEnd="@dimen/activity_margin_small"
app:srcCompat="@drawable/ic_visibility_visible"/>
<TextView <TextView
android:textDirection="ltr"
android:id="@+id/valueText" android:id="@+id/valueText"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:textColor="?android:attr/textColorSecondary" android:textColor="?android:attr/textColorSecondary"
android:textDirection="ltr"
android:textSize="24sp" android:textSize="24sp"
android:textStyle="bold" /> android:textStyle="bold" />
<TextView
android:id="@+id/valueTextPrev"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="?android:attr/textColorSecondary"
android:textDirection="ltr"
android:layout_marginStart="@dimen/activity_margin_small"
tools:ignore="HardcodedText" />
</LinearLayout> </LinearLayout>
<LinearLayout <LinearLayout
android:id="@+id/coverLayout" android:id="@+id/coverLayout"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content"> android:layout_height="wrap_content"
tools:ignore="UseCompoundDrawables">
<ImageView <ImageView
android:id="@+id/coverImg" android:id="@+id/coverImg"
@ -101,6 +107,7 @@
android:layout_height="match_parent" android:layout_height="match_parent"
android:layout_marginEnd="@dimen/activity_margin_small" android:layout_marginEnd="@dimen/activity_margin_small"
android:alpha="0.4" android:alpha="0.4"
android:contentDescription="@string/label_hidden"
app:srcCompat="@drawable/ic_visibility_invisible"/> app:srcCompat="@drawable/ic_visibility_invisible"/>
<TextView <TextView
@ -122,7 +129,8 @@
android:maxLines="1" android:maxLines="1"
android:ellipsize="end" android:ellipsize="end"
android:text="Label" android:text="Label"
android:textSize="18sp" /> android:textSize="18sp"
tools:ignore="HardcodedText" />
<TextView <TextView
android:id="@+id/textViewTags" android:id="@+id/textViewTags"
@ -131,7 +139,8 @@
android:maxLines="1" android:maxLines="1"
android:ellipsize="end" android:ellipsize="end"
android:text="Tags" android:text="Tags"
android:textSize="13.5sp" /> android:textSize="13.5sp"
tools:ignore="HardcodedText" />
</LinearLayout> </LinearLayout>
@ -146,12 +155,14 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/activity_horizontal_margin" android:layout_marginEnd="@dimen/activity_horizontal_margin"
android:visibility="gone" android:visibility="gone"
android:orientation="horizontal" > android:orientation="horizontal"
tools:ignore="UseCompoundDrawables">
<ImageView <ImageView
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="match_parent" android:layout_height="match_parent"
app:srcCompat="@drawable/ic_alarm_gray" /> app:srcCompat="@drawable/ic_alarm_gray"
tools:ignore="ContentDescription" />
<TextView <TextView
android:id="@+id/customPeriod" android:id="@+id/customPeriod"
@ -190,6 +201,7 @@
android:layout_gravity="center_vertical" android:layout_gravity="center_vertical"
android:padding="@dimen/activity_margin_small" android:padding="@dimen/activity_margin_small"
android:background="?android:attr/selectableItemBackground" android:background="?android:attr/selectableItemBackground"
android:contentDescription="@string/button_card_copy"
app:srcCompat="@drawable/ic_content_copy_gray" /> app:srcCompat="@drawable/ic_content_copy_gray" />
<ImageButton <ImageButton
@ -199,6 +211,7 @@
android:layout_gravity="center_vertical" android:layout_gravity="center_vertical"
android:padding="@dimen/activity_margin_small" android:padding="@dimen/activity_margin_small"
android:background="?android:attr/selectableItemBackground" android:background="?android:attr/selectableItemBackground"
android:contentDescription="@string/button_card_options"
app:srcCompat="@drawable/ic_more_vert_gray" /> app:srcCompat="@drawable/ic_more_vert_gray" />
</LinearLayout> </LinearLayout>

View file

@ -2,15 +2,17 @@
<androidx.cardview.widget.CardView <androidx.cardview.widget.CardView
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/card_view" android:id="@+id/card_view"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="@dimen/activity_margin_xsmall" android:layout_marginTop="@dimen/activity_margin_xsmall"
android:layout_marginBottom="@dimen/activity_margin_xsmall" android:layout_marginBottom="@dimen/activity_margin_xsmall"
android:clickable="true" android:clickable="true"
android:focusable="true"
android:foreground="?android:attr/selectableItemBackground" android:foreground="?android:attr/selectableItemBackground"
app:contentPadding="0dp" app:contentPadding="0dp"
style="?attr/cardStyle"> style="?attr/cardStyle" >
<LinearLayout <LinearLayout
android:orientation="vertical" android:orientation="vertical"
@ -21,7 +23,8 @@
android:orientation="horizontal" android:orientation="horizontal"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:baselineAligned="false"> android:baselineAligned="false"
tools:ignore="UselessParent">
<FrameLayout <FrameLayout
android:id="@+id/thumbnailFrame" android:id="@+id/thumbnailFrame"
@ -36,7 +39,8 @@
android:layout_width="@dimen/card_thumbnail_size" android:layout_width="@dimen/card_thumbnail_size"
android:layout_height="@dimen/card_thumbnail_size" android:layout_height="@dimen/card_thumbnail_size"
android:layout_gravity="center" android:layout_gravity="center"
android:src="@mipmap/ic_launcher" /> android:src="@mipmap/ic_launcher"
tools:ignore="ContentDescription" />
</FrameLayout> </FrameLayout>
@ -72,28 +76,33 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:visibility="gone"> android:visibility="gone">
<ImageView
android:id="@+id/valueImg"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_marginEnd="@dimen/activity_margin_small"
app:srcCompat="@drawable/ic_visibility_visible"/>
<TextView <TextView
android:textDirection="ltr"
android:id="@+id/valueText" android:id="@+id/valueText"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:textColor="?android:attr/textColorSecondary" android:textColor="?android:attr/textColorSecondary"
android:textSize="28sp" android:textSize="28sp"
android:textStyle="bold" /> android:textStyle="bold"
android:textDirection="ltr"
tools:ignore="HardcodedText" />
<TextView
android:id="@+id/valueTextPrev"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="?android:attr/textColorSecondary"
android:textDirection="ltr"
android:layout_marginStart="@dimen/activity_margin_small"
tools:ignore="HardcodedText" />
</LinearLayout> </LinearLayout>
<LinearLayout <LinearLayout
android:id="@+id/coverLayout" android:id="@+id/coverLayout"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content"> android:layout_height="wrap_content"
android:visibility="visible"
tools:ignore="UseCompoundDrawables">
<ImageView <ImageView
android:id="@+id/coverImg" android:id="@+id/coverImg"
@ -101,7 +110,8 @@
android:layout_height="match_parent" android:layout_height="match_parent"
android:layout_marginEnd="@dimen/activity_margin_small" android:layout_marginEnd="@dimen/activity_margin_small"
android:alpha="0.4" android:alpha="0.4"
app:srcCompat="@drawable/ic_visibility_invisible"/> android:contentDescription="@string/label_hidden"
app:srcCompat="@drawable/ic_visibility_invisible" />
<TextView <TextView
android:layout_width="wrap_content" android:layout_width="wrap_content"
@ -122,7 +132,8 @@
android:maxLines="1" android:maxLines="1"
android:ellipsize="end" android:ellipsize="end"
android:text="Label" android:text="Label"
android:textSize="18sp" /> android:textSize="18sp"
tools:ignore="HardcodedText" />
<TextView <TextView
android:id="@+id/textViewTags" android:id="@+id/textViewTags"
@ -131,7 +142,8 @@
android:maxLines="1" android:maxLines="1"
android:ellipsize="end" android:ellipsize="end"
android:text="Tags" android:text="Tags"
android:textSize="13.5sp" /> android:textSize="13.5sp"
tools:ignore="HardcodedText" />
</LinearLayout> </LinearLayout>
@ -146,12 +158,14 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/activity_horizontal_margin" android:layout_marginEnd="@dimen/activity_horizontal_margin"
android:visibility="gone" android:visibility="gone"
android:orientation="horizontal" > android:orientation="horizontal"
tools:ignore="UseCompoundDrawables">
<ImageView <ImageView
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="match_parent" android:layout_height="match_parent"
app:srcCompat="@drawable/ic_alarm_gray" /> app:srcCompat="@drawable/ic_alarm_gray"
tools:ignore="ContentDescription" />
<TextView <TextView
android:id="@+id/customPeriod" android:id="@+id/customPeriod"

View file

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView <androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/card_view" android:id="@+id/card_view"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@ -9,6 +9,7 @@
android:layout_marginBottom="@dimen/activity_margin_xsmall" android:layout_marginBottom="@dimen/activity_margin_xsmall"
app:contentPadding="0dp" app:contentPadding="0dp"
android:clickable="true" android:clickable="true"
android:focusable="true"
android:foreground="?android:attr/selectableItemBackground" android:foreground="?android:attr/selectableItemBackground"
style="?attr/cardStyle"> style="?attr/cardStyle">
@ -21,7 +22,8 @@
android:orientation="horizontal" android:orientation="horizontal"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:baselineAligned="false"> android:baselineAligned="false"
tools:ignore="UselessParent">
<FrameLayout <FrameLayout
android:id="@+id/thumbnailFrame" android:id="@+id/thumbnailFrame"
@ -29,14 +31,16 @@
android:layout_height="match_parent" android:layout_height="match_parent"
android:padding="@dimen/activity_margin" android:padding="@dimen/activity_margin"
android:paddingStart="@dimen/card_padding_without_corners" android:paddingStart="@dimen/card_padding_without_corners"
android:background="?attr/thumbnailBackground"> android:background="?attr/thumbnailBackground"
tools:ignore="RtlSymmetry">
<ImageView <ImageView
android:id="@+id/thumbnailImg" android:id="@+id/thumbnailImg"
android:layout_width="@dimen/card_thumbnail_size" android:layout_width="@dimen/card_thumbnail_size"
android:layout_height="@dimen/card_thumbnail_size" android:layout_height="@dimen/card_thumbnail_size"
android:layout_gravity="center" android:layout_gravity="center"
android:src="@mipmap/ic_launcher" /> android:src="@mipmap/ic_launcher"
tools:ignore="ContentDescription" />
</FrameLayout> </FrameLayout>
@ -69,13 +73,6 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:visibility="gone"> android:visibility="gone">
<ImageView
android:id="@+id/valueImg"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_marginEnd="@dimen/activity_margin_small"
app:srcCompat="@drawable/ic_visibility_visible"/>
<TextView <TextView
android:textDirection="ltr" android:textDirection="ltr"
android:id="@+id/valueText" android:id="@+id/valueText"
@ -85,12 +82,22 @@
android:textSize="28sp" android:textSize="28sp"
android:textStyle="bold" /> android:textStyle="bold" />
<TextView
android:id="@+id/valueTextPrev"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/activity_margin_small"
android:textColor="?android:attr/textColorSecondary"
android:textDirection="ltr"
tools:ignore="HardcodedText" />
</LinearLayout> </LinearLayout>
<LinearLayout <LinearLayout
android:id="@+id/coverLayout" android:id="@+id/coverLayout"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content"> android:layout_height="wrap_content"
tools:ignore="UseCompoundDrawables">
<ImageView <ImageView
android:id="@+id/coverImg" android:id="@+id/coverImg"
@ -98,6 +105,7 @@
android:layout_height="match_parent" android:layout_height="match_parent"
android:layout_marginEnd="@dimen/activity_margin_small" android:layout_marginEnd="@dimen/activity_margin_small"
android:alpha="0.4" android:alpha="0.4"
android:contentDescription="@string/label_hidden"
app:srcCompat="@drawable/ic_visibility_invisible"/> app:srcCompat="@drawable/ic_visibility_invisible"/>
<TextView <TextView
@ -119,7 +127,8 @@
android:maxLines="1" android:maxLines="1"
android:ellipsize="end" android:ellipsize="end"
android:textSize="18sp" android:textSize="18sp"
android:text="Label"/> android:text="Label"
tools:ignore="HardcodedText" />
<TextView <TextView
android:id="@+id/textViewTags" android:id="@+id/textViewTags"
@ -128,7 +137,8 @@
android:maxLines="1" android:maxLines="1"
android:ellipsize="end" android:ellipsize="end"
android:textSize="13.5sp" android:textSize="13.5sp"
android:text="Tags"/> android:text="Tags"
tools:ignore="HardcodedText" />
</LinearLayout> </LinearLayout>
@ -143,12 +153,14 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/activity_horizontal_margin" android:layout_marginEnd="@dimen/activity_horizontal_margin"
android:visibility="gone" android:visibility="gone"
android:orientation="horizontal" > android:orientation="horizontal"
tools:ignore="UseCompoundDrawables">
<ImageView <ImageView
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="match_parent" android:layout_height="match_parent"
app:srcCompat="@drawable/ic_alarm_gray" /> app:srcCompat="@drawable/ic_alarm_gray"
tools:ignore="ContentDescription" />
<TextView <TextView
android:id="@+id/customPeriod" android:id="@+id/customPeriod"
@ -187,6 +199,7 @@
android:layout_gravity="center_vertical" android:layout_gravity="center_vertical"
android:padding="@dimen/activity_margin_small" android:padding="@dimen/activity_margin_small"
android:background="?android:attr/selectableItemBackground" android:background="?android:attr/selectableItemBackground"
android:contentDescription="@string/button_card_copy"
app:srcCompat="@drawable/ic_content_copy_gray" /> app:srcCompat="@drawable/ic_content_copy_gray" />
<ImageButton <ImageButton
@ -196,6 +209,7 @@
android:layout_gravity="center_vertical" android:layout_gravity="center_vertical"
android:padding="@dimen/activity_margin_small" android:padding="@dimen/activity_margin_small"
android:background="?android:attr/selectableItemBackground" android:background="?android:attr/selectableItemBackground"
android:contentDescription="@string/button_card_options"
app:srcCompat="@drawable/ic_more_vert_gray" /> app:srcCompat="@drawable/ic_more_vert_gray" />
</LinearLayout> </LinearLayout>

View file

@ -29,6 +29,7 @@
<string name="settings_key_theme_mode" translatable="false">perf_theme_mode</string> <string name="settings_key_theme_mode" translatable="false">perf_theme_mode</string>
<string name="settings_key_theme_black_auto" translatable="false">pref_theme_black_auto</string> <string name="settings_key_theme_black_auto" translatable="false">pref_theme_black_auto</string>
<string name="settings_key_theme" translatable="false">pref_theme</string> <string name="settings_key_theme" translatable="false">pref_theme</string>
<string name="settings_key_show_prev_token" translatable="false">pref_show_prev_token</string>
<string name="settings_key_label_size" translatable="false">pref_label_size_sp</string> <string name="settings_key_label_size" translatable="false">pref_label_size_sp</string>
<string name="settings_key_card_layout" translatable="false">pref_card_layout</string> <string name="settings_key_card_layout" translatable="false">pref_card_layout</string>
<string name="settings_key_label_scroll" translatable="false">pref_label_scroll</string> <!-- Deprecated --> <string name="settings_key_label_scroll" translatable="false">pref_label_scroll</string> <!-- Deprecated -->

View file

@ -1,5 +1,8 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources
xmlns:tools="http://schemas.android.com/tools"
tools:ignore="MissingTranslation">
<string name="settings_activity_title">Settings</string> <string name="settings_activity_title">Settings</string>
<!-- Categories --> <!-- Categories -->
@ -28,6 +31,7 @@
<string name="settings_title_theme_black_auto">Black theme</string> <string name="settings_title_theme_black_auto">Black theme</string>
<string name="settings_title_theme">Theme</string> <string name="settings_title_theme">Theme</string>
<string name="settings_title_card_layout">Card layout</string> <string name="settings_title_card_layout">Card layout</string>
<string name="settings_title_show_prev_token">Show previous token</string>
<string name="settings_title_label_size">Label font size</string> <string name="settings_title_label_size">Label font size</string>
<string name="settings_title_label_display">Label display</string> <string name="settings_title_label_display">Label display</string>
<string name="settings_title_tap_single">Single-tap</string> <string name="settings_title_tap_single">Single-tap</string>
@ -84,6 +88,7 @@
above 8.0 (Oreo)</string> above 8.0 (Oreo)</string>
<string name="settings_desc_theme_black_auto">Use the black theme in dark mode</string> <string name="settings_desc_theme_black_auto">Use the black theme in dark mode</string>
<string name="settings_desc_show_prev_token">Show the previous token in addition to the current one</string>
<string name="settings_desc_minimize_on_copy">App will be minimized when you copy the OTP to clipboard</string> <string name="settings_desc_minimize_on_copy">App will be minimized when you copy the OTP to clipboard</string>
<string name="settings_desc_search_includes">Specify which values should be included when searching</string> <string name="settings_desc_search_includes">Specify which values should be included when searching</string>
<string name="settings_desc_label_highlight_token">Highlights token in red if it\'s expiring in 8 seconds</string> <string name="settings_desc_label_highlight_token">Highlights token in red if it\'s expiring in 8 seconds</string>

View file

@ -143,6 +143,12 @@
android:entryValues="@array/settings_values_tap" android:entryValues="@array/settings_values_tap"
android:defaultValue="@string/settings_default_tap_double" /> android:defaultValue="@string/settings_default_tap_double" />
<CheckBoxPreference
android:key="@string/settings_key_show_prev_token"
android:title="@string/settings_title_show_prev_token"
android:summary="@string/settings_desc_show_prev_token"
android:defaultValue="false" />
<org.shadowice.flocke.andotp.Preferences.NumberPickerPreference <org.shadowice.flocke.andotp.Preferences.NumberPickerPreference
android:key="@string/settings_key_label_size" android:key="@string/settings_key_label_size"
android:title="@string/settings_title_label_size" android:title="@string/settings_title_label_size"