diff options
author | binsky08 <timo@binsky.org> | 2022-08-27 01:06:37 +0300 |
---|---|---|
committer | binsky08 <timo@binsky.org> | 2022-08-27 01:06:37 +0300 |
commit | 506e288df031c54a6905e0de276a3f36eab3fdf6 (patch) | |
tree | 4959f1cec49e28275bfa55a00bd8d14d0964c418 | |
parent | 5f77ef740385b444d1e5284d4a44a6c38933dd3c (diff) |
implement otp qr code scanner
Signed-off-by: binsky08 <timo@binsky.org>
-rw-r--r-- | app/build.gradle | 2 | ||||
-rw-r--r-- | app/src/main/AndroidManifest.xml | 1 | ||||
-rw-r--r-- | app/src/main/java/es/wolfi/app/passman/activities/PasswordListActivity.java | 31 | ||||
-rw-r--r-- | app/src/main/java/es/wolfi/app/passman/fragments/CredentialEditFragment.java | 51 | ||||
-rw-r--r-- | app/src/main/res/layout/content_otp_edit.xml | 110 | ||||
-rw-r--r-- | app/src/main/res/values/strings.xml | 1 |
6 files changed, 153 insertions, 43 deletions
diff --git a/app/build.gradle b/app/build.gradle index 3a55966..4007b45 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -120,6 +120,8 @@ dependencies { implementation 'com.caverock:androidsvg:1.4' implementation 'org.bouncycastle:bcpkix-jdk15on:1.70' implementation 'com.vdurmont:semver4j:3.1.0' + implementation('com.journeyapps:zxing-android-embedded:4.3.0') { transitive = false } + implementation 'com.google.zxing:core:3.4.0' testImplementation 'junit:junit:4.13.2' annotationProcessor 'com.jakewharton:butterknife-compiler:10.2.3' } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 50b0a58..4045fda 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -5,6 +5,7 @@ <uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> + <uses-permission android:name="android.permission.CAMERA" /> <queries> <package android:name="com.nextcloud.client" /> <package android:name="com.nextcloud.android.beta" /> diff --git a/app/src/main/java/es/wolfi/app/passman/activities/PasswordListActivity.java b/app/src/main/java/es/wolfi/app/passman/activities/PasswordListActivity.java index 61bad4b..d3cb942 100644 --- a/app/src/main/java/es/wolfi/app/passman/activities/PasswordListActivity.java +++ b/app/src/main/java/es/wolfi/app/passman/activities/PasswordListActivity.java @@ -22,6 +22,8 @@ package es.wolfi.app.passman.activities; +import static com.google.zxing.integration.android.IntentIntegrator.parseActivityResult; + import android.app.KeyguardManager; import android.app.ProgressDialog; import android.content.ClipData; @@ -56,6 +58,9 @@ import androidx.core.graphics.drawable.IconCompat; import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentManager; +import com.google.zxing.integration.android.IntentResult; +import com.journeyapps.barcodescanner.ScanContract; +import com.journeyapps.barcodescanner.ScanOptions; import com.koushikdutta.async.future.FutureCallback; import org.json.JSONException; @@ -100,6 +105,7 @@ public class PasswordListActivity extends AppCompatActivity implements private static final int REQUEST_CODE_KEYGUARD = 0; private static final int REQUEST_CODE_AUTHENTICATE = 1; private static final int REQUEST_CODE_CREATE_DOCUMENT = 2; + private static final int REQUEST_CODE_SCAN_QR_CODE_FOR_OTP_EDIT = 7; static boolean running = false; @@ -754,6 +760,17 @@ public class PasswordListActivity extends AppCompatActivity implements startActivityForResult(intent, activityRequestFileCode); } + public void scanQRCodeForOTP() { + ScanOptions scanOptions = new ScanOptions(); + scanOptions.setDesiredBarcodeFormats(ScanOptions.QR_CODE); // optional + scanOptions.setOrientationLocked(false); // allow barcode scanner in portrait mode + + ScanContract scanContract = new ScanContract(); + Intent intent = scanContract.createIntent(this, scanOptions); + + startActivityForResult(intent, REQUEST_CODE_SCAN_QR_CODE_FOR_OTP_EDIT); + } + @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); @@ -777,6 +794,20 @@ public class PasswordListActivity extends AppCompatActivity implements } } + if (requestCode == REQUEST_CODE_SCAN_QR_CODE_FOR_OTP_EDIT) { // scan qr code as otp config in credential edit + if (resultCode != RESULT_OK) { + Log.e("otp qr scan", "failed"); + return; + } + + CredentialEditFragment credentialEditFragment = (CredentialEditFragment) getSupportFragmentManager().findFragmentByTag("credentialEdit"); + if (credentialEditFragment != null) { + IntentResult result = parseActivityResult(resultCode, data); + credentialEditFragment.processScannedQRCodeData(result.getContents(), requestCode); + } + Log.e("otp qr scan", "successful"); + } + // Following cases should only be handled on positive result if (resultCode != RESULT_OK) return; diff --git a/app/src/main/java/es/wolfi/app/passman/fragments/CredentialEditFragment.java b/app/src/main/java/es/wolfi/app/passman/fragments/CredentialEditFragment.java index 558d7fb..57b6e04 100644 --- a/app/src/main/java/es/wolfi/app/passman/fragments/CredentialEditFragment.java +++ b/app/src/main/java/es/wolfi/app/passman/fragments/CredentialEditFragment.java @@ -25,9 +25,11 @@ import android.app.AlertDialog; import android.app.ProgressDialog; import android.content.Context; import android.content.DialogInterface; +import android.net.Uri; import android.os.Bundle; import android.os.Handler; import android.os.Looper; +import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -102,6 +104,8 @@ public class CredentialEditFragment extends Fragment implements View.OnClickList EditText otp_secret; EditText otp_digits; EditText otp_period; + EditText otp_label; + EditText otp_issuer; TextView credential_otp; ProgressBar otp_progress; @@ -177,6 +181,8 @@ public class CredentialEditFragment extends Fragment implements View.OnClickList otp_secret = view.findViewById(R.id.edit_credential_otp_secret); otp_digits = view.findViewById(R.id.edit_credential_otp_digits); otp_period = view.findViewById(R.id.edit_credential_otp_period); + otp_label = view.findViewById(R.id.otp_label); + otp_issuer = view.findViewById(R.id.otp_issuer); credential_otp = view.findViewById(R.id.credential_otp); otp_progress = view.findViewById(R.id.credential_otp_progress); @@ -228,10 +234,10 @@ public class CredentialEditFragment extends Fragment implements View.OnClickList if (otpObj.has("secret") && otpObj.getString("secret").length() > 4) { String otpSecret = otpObj.getString("secret"); otp_secret.setText(otpSecret); - - handler = new Handler(); - otp_refresh = TOTPHelper.runAndUpdate(handler, otp_progress, credential_otp, otp_digits, otp_period, otp_secret); } + + handler = new Handler(); + otp_refresh = TOTPHelper.runAndUpdate(handler, otp_progress, credential_otp, otp_digits, otp_period, otp_secret); } catch (JSONException e) { e.printStackTrace(); } @@ -285,11 +291,46 @@ public class CredentialEditFragment extends Fragment implements View.OnClickList }, 100); } + public void processScannedQRCodeData(String value, int requestCode) { + Log.d("CredentialEdit", "processScannedQRCodeData begins"); + + try { + JSONObject otpObj = new JSONObject(credential.getOtp()); + otpObj.put("qr_uri", value); + + Uri uri = Uri.parse(value); + + String type = uri.getHost().equals("totp") ? "totp" : "hotp"; + otpObj.put("type", type); + + String label = uri.getPath().replaceFirst("/", ""); + otpObj.put("label", label); + + String algo = uri.getQueryParameter("algorithm"); + String period = uri.getQueryParameter("period"); + String digits = uri.getQueryParameter("digits"); + String issuer = uri.getQueryParameter("issuer"); + + otpObj.put("algorithm", algo != null && !algo.isEmpty() ? uri.getQueryParameter("algorithm") : "SHA1"); + + otp_period.setText(period != null && !period.isEmpty() ? uri.getQueryParameter("period") : "30"); + otp_digits.setText(digits != null && !digits.isEmpty() ? uri.getQueryParameter("digits") : "6"); + otp_label.setText(label); + otp_issuer.setText(issuer != null && !issuer.isEmpty() ? issuer : ""); + otp_secret.setText(uri.getQueryParameter("secret") != null ? uri.getQueryParameter("secret") : ""); + + Log.d("CredentialEdit", "processScannedQRCodeData done"); + } catch (JSONException e) { + e.printStackTrace(); + Log.d("CredentialEdit", "processScannedQRCodeData failed"); + } + } + public View.OnClickListener getScanOtpQRCodeButtonListener() { return new View.OnClickListener() { @Override public void onClick(View view) { - // todo + ((PasswordListActivity) requireActivity()).scanQRCodeForOTP(); } }; } @@ -299,7 +340,7 @@ public class CredentialEditFragment extends Fragment implements View.OnClickList @Override public void onClick(View view) { if (otp_edit_extended.getVisibility() == View.VISIBLE) { - otp_edit_extended.setVisibility(View.INVISIBLE); + otp_edit_extended.setVisibility(View.GONE); otpEditCollapseExtendedButton.setRotation(-90); } else { otp_edit_extended.setVisibility(View.VISIBLE); diff --git a/app/src/main/res/layout/content_otp_edit.xml b/app/src/main/res/layout/content_otp_edit.xml index 2391870..2931ad5 100644 --- a/app/src/main/res/layout/content_otp_edit.xml +++ b/app/src/main/res/layout/content_otp_edit.xml @@ -55,14 +55,14 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:animateLayoutChanges="true" - android:gravity="center" + android:gravity="start|top" android:orientation="horizontal"> <androidx.appcompat.widget.AppCompatImageButton android:id="@+id/otpEditCollapseExtendedButton" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_gravity="end|center_vertical" + android:layout_gravity="top|center_vertical" android:layout_marginTop="0dp" android:layout_marginBottom="0dp" android:backgroundTint="@color/transparent" @@ -75,56 +75,90 @@ android:layout_height="wrap_content" android:animateLayoutChanges="true" android:gravity="center" - android:orientation="horizontal" - android:visibility="invisible"> + android:orientation="vertical" + android:visibility="visible"> - <LinearLayout - android:layout_width="150dp" + <TextView + style="@style/Label" + android:layout_width="match_parent" android:layout_height="wrap_content" - android:orientation="vertical"> + android:text="@string/label" /> - <TextView - style="@style/Label" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:text="@string/digits" /> + <EditText + android:id="@+id/otp_label" + style="@style/FormText" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:hint="@string/label" /> - <EditText - android:id="@+id/edit_credential_otp_digits" - style="@style/FormText" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_marginEnd="30dp" - android:digits="0123456789" - android:hint="6" - android:inputType="number" /> + <TextView + style="@style/Label" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="@string/issuer" /> + + <EditText + android:id="@+id/otp_issuer" + style="@style/FormText" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:hint="@string/issuer" /> - </LinearLayout> <LinearLayout - android:layout_width="150dp" + android:layout_width="match_parent" android:layout_height="wrap_content" - android:orientation="vertical"> + android:animateLayoutChanges="true" + android:orientation="horizontal"> - <TextView - style="@style/Label" - android:layout_width="match_parent" + <LinearLayout + android:layout_width="160dp" android:layout_height="wrap_content" - android:text="@string/period" /> - - <EditText - android:id="@+id/edit_credential_otp_period" - style="@style/FormText" - android:layout_width="match_parent" + android:orientation="vertical" + android:layout_marginEnd="12dp"> + + <TextView + style="@style/Label" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="@string/digits" /> + + <EditText + android:id="@+id/edit_credential_otp_digits" + style="@style/FormText" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:digits="0123456789" + android:hint="6" + android:inputType="number" /> + + </LinearLayout> + + <LinearLayout + android:layout_width="160dp" android:layout_height="wrap_content" - android:layout_marginEnd="30dp" - android:digits="0123456789" - android:hint="30" - android:inputType="number" /> + android:orientation="vertical"> - </LinearLayout> + <TextView + style="@style/Label" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="@string/period" /> + + <EditText + android:id="@+id/edit_credential_otp_period" + style="@style/FormText" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:digits="0123456789" + android:hint="30" + android:inputType="number" /> + </LinearLayout> + + </LinearLayout> </LinearLayout> + </LinearLayout> </LinearLayout> diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 0e65f91..3f986ce 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -90,4 +90,5 @@ <string name="digits">Digits</string> <string name="period">Period</string> <string name="secret">Secret</string> + <string name="issuer">Issuer</string> </resources> |