Add the ability to show qr codes of stored accounts

Signed-off-by: Tilo Spannagel <development@tilosp.de>
This commit is contained in:
Tilo Spannagel 2020-03-07 16:07:14 +01:00
parent b8b9834f06
commit 5a165fd212
4 changed files with 86 additions and 0 deletions

View file

@ -276,6 +276,46 @@ public class Entry {
return jsonObj; return jsonObj;
} }
public Uri toUri() {
String type;
switch (this.type) {
case TOTP:
type = "totp";
break;
case HOTP:
type = "hotp";
break;
default:
return null;
}
Uri.Builder builder = new Uri.Builder()
.scheme("otpauth")
.authority(type)
.appendPath(this.label)
.appendQueryParameter("secret", new Base32().encodeAsString(this.secret));
if (this.issuer != null) {
builder.appendQueryParameter("issuer", this.issuer);
}
switch (this.type) {
case HOTP:
builder.appendQueryParameter("counter", Long.toString(this.counter));
case TOTP:
if (this.period != TokenCalculator.TOTP_DEFAULT_PERIOD)
builder.appendQueryParameter("period", Integer.toString(this.period));
break;
}
if (this.digits != TokenCalculator.TOTP_DEFAULT_DIGITS) {
builder.appendQueryParameter("digits", Integer.toString(this.digits));
}
if (this.algorithm != TokenCalculator.DEFAULT_ALGORITHM) {
builder.appendQueryParameter("algorithm", this.algorithm.name());
}
for (String tag : this.tags) {
builder.appendQueryParameter("tags", tag);
}
return builder.build();
}
public boolean isTimeBased() { public boolean isTimeBased() {
return type == OTPType.TOTP || type == OTPType.STEAM; return type == OTPType.TOTP || type == OTPType.STEAM;
} }

View file

@ -27,6 +27,8 @@ import android.content.ClipData;
import android.content.ClipboardManager; import android.content.ClipboardManager;
import android.content.Context; import android.content.Context;
import android.content.DialogInterface; import android.content.DialogInterface;
import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.net.Uri; import android.net.Uri;
import android.os.Handler; import android.os.Handler;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
@ -46,9 +48,13 @@ import android.widget.Filter;
import android.widget.Filterable; import android.widget.Filterable;
import android.widget.FrameLayout; import android.widget.FrameLayout;
import android.widget.GridView; import android.widget.GridView;
import android.widget.ImageView;
import android.widget.LinearLayout; import android.widget.LinearLayout;
import android.widget.Toast; import android.widget.Toast;
import com.google.zxing.BarcodeFormat;
import com.journeyapps.barcodescanner.BarcodeEncoder;
import org.shadowice.flocke.andotp.Activities.MainActivity; import org.shadowice.flocke.andotp.Activities.MainActivity;
import org.shadowice.flocke.andotp.Database.Entry; import org.shadowice.flocke.andotp.Database.Entry;
import org.shadowice.flocke.andotp.Dialogs.TagsDialog; import org.shadowice.flocke.andotp.Dialogs.TagsDialog;
@ -681,6 +687,35 @@ public class EntriesCardAdapter extends RecyclerView.Adapter<EntryViewHolder>
.show(); .show();
} }
private void showQRCode(final int pos) {
Uri uri = displayedEntries.get(pos).toUri();
if (uri != null) {
Bitmap bitmap;
try {
bitmap = new BarcodeEncoder().encodeBitmap(uri.toString(), BarcodeFormat.QR_CODE, 0, 0);
} catch(Exception ignored) {
Toast.makeText(context, R.string.toast_qr_failed_to_generate, Toast.LENGTH_LONG).show();
return;
}
BitmapDrawable drawable = new BitmapDrawable(context.getResources(), bitmap);
drawable.setFilterBitmap(false);
ImageView image = new ImageView(context);
image.setAdjustViewBounds(true);
image.setScaleType(ImageView.ScaleType.FIT_CENTER);
image.setImageDrawable(drawable);
new AlertDialog.Builder(context)
.setTitle(R.string.dialog_title_qr_code)
.setPositiveButton(android.R.string.ok, (dialog, which) -> {})
.setView(image)
.create()
.show();
} else {
Toast.makeText(context, R.string.toast_qr_unsuported, Toast.LENGTH_LONG).show();
}
}
private void showPopupMenu(View view, final int pos) { private void showPopupMenu(View view, final int pos) {
View menuItemView = view.findViewById(R.id.menuButton); View menuItemView = view.findViewById(R.id.menuButton);
PopupMenu popup = new PopupMenu(view.getContext(), menuItemView); PopupMenu popup = new PopupMenu(view.getContext(), menuItemView);
@ -707,6 +742,9 @@ public class EntriesCardAdapter extends RecyclerView.Adapter<EntryViewHolder>
} else if (id == R.id.menu_popup_remove) { } else if (id == R.id.menu_popup_remove) {
removeItem(pos); removeItem(pos);
return true; return true;
} else if (id == R.id.menu_popup_show_qr_code) {
showQRCode(pos);
return true;
} else { } else {
return false; return false;
} }

View file

@ -20,4 +20,8 @@
android:id="@+id/menu_popup_remove" android:id="@+id/menu_popup_remove"
android:title="@string/menu_popup_remove" /> android:title="@string/menu_popup_remove" />
<item
android:id="@+id/menu_popup_show_qr_code"
android:title="@string/menu_popup_show_qr_code" />
</menu> </menu>

View file

@ -57,6 +57,7 @@
<string name="menu_popup_change_image">Change image</string> <string name="menu_popup_change_image">Change image</string>
<string name="menu_popup_edit_tags">Edit tags</string> <string name="menu_popup_edit_tags">Edit tags</string>
<string name="menu_popup_remove">Remove</string> <string name="menu_popup_remove">Remove</string>
<string name="menu_popup_show_qr_code">Show QR Code</string>
<!-- Toast messages --> <!-- Toast messages -->
<string name="toast_auth_failed">Authentication failed, please try again!</string> <string name="toast_auth_failed">Authentication failed, please try again!</string>
@ -71,6 +72,8 @@
<string name="toast_qr_error">Could not find/confirm QR code</string> <string name="toast_qr_error">Could not find/confirm QR code</string>
<string name="toast_qr_checksum_exception">Checksum verification failed while decoding QR code</string> <string name="toast_qr_checksum_exception">Checksum verification failed while decoding QR code</string>
<string name="toast_qr_format_error">Format error in QR code</string> <string name="toast_qr_format_error">Format error in QR code</string>
<string name="toast_qr_unsuported">QR Code not supported</string>
<string name="toast_qr_failed_to_generate">Failed to generate QR Code</string>
<!-- Dialogs --> <!-- Dialogs -->
<string name="dialog_title_auth">Authenticate</string> <string name="dialog_title_auth">Authenticate</string>
@ -80,6 +83,7 @@
<string name="dialog_title_counter">Counter</string> <string name="dialog_title_counter">Counter</string>
<string name="dialog_title_used_tokens">Used tokens</string> <string name="dialog_title_used_tokens">Used tokens</string>
<string name="dialog_title_keystore_error">KeyStore error</string> <string name="dialog_title_keystore_error">KeyStore error</string>
<string name="dialog_title_qr_code">QR Code</string>
<string name="dialog_title_enter_password">Enter password</string> <string name="dialog_title_enter_password">Enter password</string>