Add support for multi-code migrations
This commit is contained in:
parent
37a7eef5e1
commit
5af785804f
@ -38,7 +38,8 @@
|
||||
</activity>
|
||||
<activity android:name=".scanner.QRScannerActivity"
|
||||
android:exported="true"
|
||||
android:theme="@style/Theme.CringeAuthenticator.None">
|
||||
android:theme="@style/Theme.CringeAuthenticator.None"
|
||||
android:configChanges="orientation|screenSize">
|
||||
</activity>
|
||||
<activity android:name=".urihandler.URIHandlerActivity"
|
||||
android:exported="true">
|
||||
|
@ -0,0 +1,26 @@
|
||||
package com.cringe_studios.cringe_authenticator.model;
|
||||
|
||||
public class OTPMigrationPart {
|
||||
|
||||
private OTPData[] otps;
|
||||
private int batchIndex;
|
||||
private int batchSize;
|
||||
|
||||
public OTPMigrationPart(OTPData[] otps, int batchIndex, int batchSize) {
|
||||
this.otps = otps;
|
||||
this.batchIndex = batchIndex;
|
||||
this.batchSize = batchSize;
|
||||
}
|
||||
|
||||
public OTPData[] getOTPs() {
|
||||
return otps;
|
||||
}
|
||||
|
||||
public int getBatchIndex() {
|
||||
return batchIndex;
|
||||
}
|
||||
|
||||
public int getBatchSize() {
|
||||
return batchSize;
|
||||
}
|
||||
}
|
@ -2,7 +2,6 @@ package com.cringe_studios.cringe_authenticator.scanner;
|
||||
|
||||
import android.Manifest;
|
||||
import android.app.Activity;
|
||||
import android.app.Dialog;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.media.Image;
|
||||
@ -11,6 +10,7 @@ import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
@ -31,6 +31,7 @@ import androidx.core.content.ContextCompat;
|
||||
import com.cringe_studios.cringe_authenticator.R;
|
||||
import com.cringe_studios.cringe_authenticator.databinding.ActivityQrScannerBinding;
|
||||
import com.cringe_studios.cringe_authenticator.model.OTPData;
|
||||
import com.cringe_studios.cringe_authenticator.model.OTPMigrationPart;
|
||||
import com.cringe_studios.cringe_authenticator.util.OTPParser;
|
||||
import com.cringe_studios.cringe_authenticator.util.StyledDialogBuilder;
|
||||
import com.cringe_studios.cringe_authenticator.util.ThemeUtil;
|
||||
@ -40,6 +41,9 @@ import com.google.mlkit.vision.barcode.BarcodeScanning;
|
||||
import com.google.mlkit.vision.barcode.common.Barcode;
|
||||
import com.google.mlkit.vision.common.InputImage;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
public class QRScannerActivity extends AppCompatActivity {
|
||||
@ -54,6 +58,12 @@ public class QRScannerActivity extends AppCompatActivity {
|
||||
|
||||
private boolean process = true;
|
||||
|
||||
private List<OTPData> currentCodes;
|
||||
|
||||
private OTPMigrationPart lastPart;
|
||||
|
||||
private Toast t;
|
||||
|
||||
@Override
|
||||
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
@ -69,6 +79,9 @@ public class QRScannerActivity extends AppCompatActivity {
|
||||
binding = ActivityQrScannerBinding.inflate(getLayoutInflater());
|
||||
setContentView(binding.getRoot());
|
||||
|
||||
currentCodes = new ArrayList<>();
|
||||
lastPart = null;
|
||||
|
||||
cameraProviderFuture = ProcessCameraProvider.getInstance(this);
|
||||
cameraProviderFuture.addListener(() -> {
|
||||
try {
|
||||
@ -81,11 +94,8 @@ public class QRScannerActivity extends AppCompatActivity {
|
||||
}, ContextCompat.getMainExecutor(this));
|
||||
|
||||
scanner = BarcodeScanning.getClient();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onNewIntent(Intent intent) {
|
||||
super.onNewIntent(intent);
|
||||
t = Toast.makeText(this, "0", Toast.LENGTH_LONG);
|
||||
t.show();
|
||||
}
|
||||
|
||||
void bindPreview(@NonNull ProcessCameraProvider cameraProvider) {
|
||||
@ -121,7 +131,10 @@ public class QRScannerActivity extends AppCompatActivity {
|
||||
@OptIn(markerClass = ExperimentalGetImage.class)
|
||||
@Override
|
||||
public void analyze(@NonNull ImageProxy image) {
|
||||
if(!process) return;
|
||||
if(!process) {
|
||||
image.close();
|
||||
return;
|
||||
}
|
||||
|
||||
Image mediaImage = image.getImage();
|
||||
if(mediaImage != null) {
|
||||
@ -152,19 +165,56 @@ public class QRScannerActivity extends AppCompatActivity {
|
||||
|
||||
private void importUri(Uri uri) {
|
||||
if("otpauth-migration".equalsIgnoreCase(uri.getScheme())) {
|
||||
Dialog dialog = new StyledDialogBuilder(this)
|
||||
OTPMigrationPart part;
|
||||
|
||||
try {
|
||||
part = OTPParser.parseMigration(uri);
|
||||
}catch(IllegalArgumentException e) {
|
||||
error(e.getMessage());
|
||||
return;
|
||||
}
|
||||
|
||||
if((lastPart != null && part.getBatchIndex() != lastPart.getBatchIndex() + 1) || (lastPart == null && part.getBatchIndex() > 0)) {
|
||||
// Not next batch, or first batch (if nothing was scanned yet), keep looking
|
||||
process = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if(part.getBatchIndex() == 0) {
|
||||
new StyledDialogBuilder(this)
|
||||
.setTitle(R.string.qr_scanner_migration_title)
|
||||
.setMessage(R.string.qr_scanner_migration_message)
|
||||
.setPositiveButton(R.string.yes, (d, which) -> {
|
||||
try {
|
||||
success(OTPParser.parseMigration(uri));
|
||||
}catch(IllegalArgumentException e) {
|
||||
error(e.getMessage());
|
||||
if(part.getBatchSize() == 1) {
|
||||
success(part.getOTPs());
|
||||
}else {
|
||||
currentCodes.addAll(Arrays.asList(part.getOTPs()));
|
||||
lastPart = part;
|
||||
Toast.makeText(this, getString(R.string.qr_scanner_migration_part, part.getBatchIndex()+ 1, part.getBatchSize()), Toast.LENGTH_LONG).show();
|
||||
process = true;
|
||||
}
|
||||
})
|
||||
.setNegativeButton(R.string.no, (d, which) -> cancel())
|
||||
.setOnDismissListener(d -> cancel())
|
||||
.show();
|
||||
.show()
|
||||
.setCanceledOnTouchOutside(false);
|
||||
}else {
|
||||
currentCodes.addAll(Arrays.asList(part.getOTPs()));
|
||||
Toast.makeText(this, getString(R.string.qr_scanner_migration_part, part.getBatchIndex()+ 1, part.getBatchSize()), Toast.LENGTH_LONG).show();
|
||||
|
||||
if(part.getBatchIndex() == part.getBatchSize() - 1) {
|
||||
success(currentCodes.toArray(new OTPData[0]));
|
||||
}else {
|
||||
process = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if(lastPart != null) {
|
||||
// Migration is being imported
|
||||
process = true;
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -43,7 +43,8 @@ public class URIHandlerActivity extends AppCompatActivity {
|
||||
importCodes(OTPParser.parse(uri));
|
||||
break;
|
||||
case "otpauth-migration":
|
||||
importCodes(OTPParser.parseMigration(uri));
|
||||
importCodes(OTPParser.parseMigration(uri).getOTPs());
|
||||
// TODO: notify user if there are multiple codes?
|
||||
break;
|
||||
}
|
||||
}catch(IllegalArgumentException e) {
|
||||
|
@ -4,6 +4,7 @@ import android.net.Uri;
|
||||
import android.util.Base64;
|
||||
|
||||
import com.cringe_studios.cringe_authenticator.model.OTPData;
|
||||
import com.cringe_studios.cringe_authenticator.model.OTPMigrationPart;
|
||||
import com.cringe_studios.cringe_authenticator.proto.OTPMigration;
|
||||
import com.cringe_studios.cringe_authenticator_library.OTPAlgorithm;
|
||||
import com.cringe_studios.cringe_authenticator_library.OTPType;
|
||||
@ -12,7 +13,7 @@ import com.google.protobuf.InvalidProtocolBufferException;
|
||||
|
||||
public class OTPParser {
|
||||
|
||||
public static OTPData[] parseMigration(Uri uri) throws IllegalArgumentException {
|
||||
public static OTPMigrationPart parseMigration(Uri uri) throws IllegalArgumentException {
|
||||
if(!"otpauth-migration".equals(uri.getScheme())) {
|
||||
throw new IllegalArgumentException("Wrong URI scheme");
|
||||
}
|
||||
@ -27,15 +28,16 @@ public class OTPParser {
|
||||
}
|
||||
|
||||
byte[] dataBytes = Base64.decode(data, Base64.DEFAULT);
|
||||
|
||||
try {
|
||||
OTPMigration.MigrationPayload payload = OTPMigration.MigrationPayload.parseFrom(dataBytes);
|
||||
|
||||
int count = payload.getOtpParametersCount();
|
||||
OTPData[] otps = new OTPData[count];
|
||||
|
||||
for(int i = 0; i < payload.getOtpParametersCount(); i++) {
|
||||
OTPMigration.MigrationPayload.OtpParameters params = payload.getOtpParameters(i);
|
||||
|
||||
// TODO: issuer
|
||||
|
||||
String name = params.getName();
|
||||
String issuer = params.getIssuer();
|
||||
|
||||
@ -96,7 +98,8 @@ public class OTPParser {
|
||||
|
||||
otps[i] = new OTPData(name, issuer, type, secret, algorithm, digits, period, counter, checksum);
|
||||
}
|
||||
return otps;
|
||||
|
||||
return new OTPMigrationPart(otps, payload.getBatchIndex(), payload.getBatchSize());
|
||||
} catch (InvalidProtocolBufferException e) {
|
||||
throw new IllegalArgumentException("Failed to parse migration data", e);
|
||||
}
|
||||
|
@ -48,6 +48,7 @@
|
||||
<string name="otp_add_missing_name">Name fehlt</string>
|
||||
<string name="qr_scanner_migration_title">OTP-Migration</string>
|
||||
<string name="qr_scanner_migration_message">Du scheinst zu versuchen, Codes aus einer anderen App zu importieren. Willst du alle Codes in diese Gruppe importieren?</string>
|
||||
<string name="qr_scanner_migration_part">Code %d von %d gescannt</string>
|
||||
<string-array name="view_edit_move_delete">
|
||||
<item>Anzeigen</item>
|
||||
<item>Bearbeiten</item>
|
||||
|
@ -48,6 +48,7 @@
|
||||
<string name="otp_add_missing_name">Missing name</string>
|
||||
<string name="qr_scanner_migration_title">OTP Migration</string>
|
||||
<string name="qr_scanner_migration_message">It seems like you\'re trying to import OTP codes from another app. Do you want to import all codes into this group?</string>
|
||||
<string name="qr_scanner_migration_part">Code %d of %d scanned</string>
|
||||
<string-array name="view_edit_move_delete">
|
||||
<item>View</item>
|
||||
<item>Edit</item>
|
||||
|
Loading…
Reference in New Issue
Block a user