QR scanning, Basic OTP stuff (WIP)
This commit is contained in:
parent
ff3dc4e977
commit
6e19440e3d
@ -45,4 +45,15 @@ dependencies {
|
|||||||
implementation "androidx.biometric:biometric:1.1.0"
|
implementation "androidx.biometric:biometric:1.1.0"
|
||||||
implementation 'com.cringe_studios:CringeAuthenticatorLibrary:1.0'
|
implementation 'com.cringe_studios:CringeAuthenticatorLibrary:1.0'
|
||||||
implementation 'com.google.mlkit:barcode-scanning:17.1.0'
|
implementation 'com.google.mlkit:barcode-scanning:17.1.0'
|
||||||
|
implementation 'com.google.code.gson:gson:2.8.9'
|
||||||
|
|
||||||
|
|
||||||
|
def camerax_version = "1.2.2"
|
||||||
|
implementation "androidx.camera:camera-core:${camerax_version}"
|
||||||
|
implementation "androidx.camera:camera-camera2:${camerax_version}"
|
||||||
|
implementation "androidx.camera:camera-lifecycle:${camerax_version}"
|
||||||
|
implementation "androidx.camera:camera-video:${camerax_version}"
|
||||||
|
|
||||||
|
implementation "androidx.camera:camera-view:${camerax_version}"
|
||||||
|
implementation "androidx.camera:camera-extensions:${camerax_version}"
|
||||||
}
|
}
|
@ -2,6 +2,9 @@
|
|||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:tools="http://schemas.android.com/tools">
|
xmlns:tools="http://schemas.android.com/tools">
|
||||||
|
|
||||||
|
<uses-feature android:name="android.hardware.camera" android:required="true" />
|
||||||
|
<uses-permission android:name="android.permission.CAMERA" />
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:allowBackup="true"
|
android:allowBackup="true"
|
||||||
android:dataExtractionRules="@xml/data_extraction_rules"
|
android:dataExtractionRules="@xml/data_extraction_rules"
|
||||||
@ -23,6 +26,9 @@
|
|||||||
<category android:name="android.intent.category.LAUNCHER" />
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
|
<activity android:name=".QRScannerActivity"
|
||||||
|
android:theme="@style/Theme.CringeAuthenticator">
|
||||||
|
</activity>
|
||||||
</application>
|
</application>
|
||||||
|
|
||||||
</manifest>
|
</manifest>
|
@ -1,25 +1,40 @@
|
|||||||
package com.cringe_studios.cringe_authenticator;
|
package com.cringe_studios.cringe_authenticator;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.EditText;
|
||||||
|
|
||||||
|
import androidx.activity.result.ActivityResultLauncher;
|
||||||
|
import androidx.activity.result.contract.ActivityResultContract;
|
||||||
|
import androidx.activity.result.contract.ActivityResultContracts;
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.appcompat.app.AlertDialog;
|
||||||
import androidx.appcompat.app.AppCompatActivity;
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
import androidx.biometric.BiometricPrompt;
|
import androidx.biometric.BiometricPrompt;
|
||||||
import androidx.core.content.ContextCompat;
|
import androidx.core.content.ContextCompat;
|
||||||
|
import androidx.fragment.app.Fragment;
|
||||||
import androidx.navigation.NavController;
|
import androidx.navigation.NavController;
|
||||||
import androidx.navigation.Navigation;
|
import androidx.navigation.Navigation;
|
||||||
import androidx.navigation.ui.AppBarConfiguration;
|
import androidx.navigation.ui.AppBarConfiguration;
|
||||||
import androidx.navigation.ui.NavigationUI;
|
import androidx.navigation.ui.NavigationUI;
|
||||||
|
|
||||||
import com.cringe_studios.cringe_authenticator.databinding.ActivityMainBinding;
|
import com.cringe_studios.cringe_authenticator.databinding.ActivityMainBinding;
|
||||||
|
import com.cringe_studios.cringe_authenticator.databinding.FragmentDynamicBinding;
|
||||||
import com.cringe_studios.cringe_authenticator.fragment.DynamicFragment;
|
import com.cringe_studios.cringe_authenticator.fragment.DynamicFragment;
|
||||||
import com.cringe_studios.cringe_authenticator.fragment.HomeFragment;
|
import com.cringe_studios.cringe_authenticator.fragment.HomeFragment;
|
||||||
import com.cringe_studios.cringe_authenticator.fragment.MenuFragment;
|
import com.cringe_studios.cringe_authenticator.fragment.MenuFragment;
|
||||||
import com.cringe_studios.cringe_authenticator.fragment.SettingsFragment;
|
import com.cringe_studios.cringe_authenticator.fragment.SettingsFragment;
|
||||||
import com.cringe_studios.cringe_authenticator.util.NavigationUtil;
|
import com.cringe_studios.cringe_authenticator.util.NavigationUtil;
|
||||||
|
import com.cringe_studios.cringe_authenticator.util.SettingsUtil;
|
||||||
|
import com.cringe_studios.cringe_authenticator_library.OTPType;
|
||||||
|
import com.google.android.material.color.utilities.DynamicColor;
|
||||||
|
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
|
|
||||||
@ -28,6 +43,8 @@ public class MainActivity extends AppCompatActivity {
|
|||||||
private AppBarConfiguration appBarConfiguration;
|
private AppBarConfiguration appBarConfiguration;
|
||||||
private ActivityMainBinding binding;
|
private ActivityMainBinding binding;
|
||||||
|
|
||||||
|
private ActivityResultLauncher<Void> startQRCodeScan;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
@ -54,6 +71,15 @@ public class MainActivity extends AppCompatActivity {
|
|||||||
prompt.authenticate(info);*/
|
prompt.authenticate(info);*/
|
||||||
|
|
||||||
launchApp();
|
launchApp();
|
||||||
|
|
||||||
|
startQRCodeScan = registerForActivityResult(new QRScannerContract(), obj -> {
|
||||||
|
Fragment fragment = NavigationUtil.getCurrentFragment(this);
|
||||||
|
if(fragment instanceof DynamicFragment) {
|
||||||
|
DynamicFragment frag = (DynamicFragment) fragment;
|
||||||
|
SettingsUtil.addOTP(getSharedPreferences("groups", MODE_PRIVATE), frag.getGroupName(), obj);
|
||||||
|
}
|
||||||
|
Log.i("AMOGUS", "Actually got something bruh" + obj);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void launchApp() {
|
private void launchApp() {
|
||||||
@ -74,11 +100,15 @@ public class MainActivity extends AppCompatActivity {
|
|||||||
// Inflate the menu; this adds items to the action bar if it is present.
|
// Inflate the menu; this adds items to the action bar if it is present.
|
||||||
|
|
||||||
/*if(NavigationUtil.getCurrentFragment(this) instanceof DynamicFragment) { TODO: vs. fabs?
|
/*if(NavigationUtil.getCurrentFragment(this) instanceof DynamicFragment) { TODO: vs. fabs?
|
||||||
//getMenuInflater().inflate(R.menu.menu_dynamic, menu);
|
|
||||||
getMenuInflater().inflate(R.menu.menu_dynamic, menu);
|
getMenuInflater().inflate(R.menu.menu_dynamic, menu);
|
||||||
return true;
|
return true;
|
||||||
}*/
|
}*/
|
||||||
|
|
||||||
|
if(NavigationUtil.getCurrentFragment(this) instanceof MenuFragment) {
|
||||||
|
getMenuInflater().inflate(R.menu.menu_groups, menu);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
getMenuInflater().inflate(R.menu.menu_main, menu);
|
getMenuInflater().inflate(R.menu.menu_main, menu);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -121,4 +151,20 @@ public class MainActivity extends AppCompatActivity {
|
|||||||
// TODO: add code
|
// TODO: add code
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void scanCode(View view) {
|
||||||
|
Log.i("AMOGUS", "Scan");
|
||||||
|
Intent intent = new Intent(this, QRScannerActivity.class);
|
||||||
|
startQRCodeScan.launch(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addGroup(MenuItem item) {
|
||||||
|
EditText t = new EditText(this);
|
||||||
|
new AlertDialog.Builder(this)
|
||||||
|
.setTitle("New Group")
|
||||||
|
.setView(t)
|
||||||
|
.setPositiveButton("Add", (view, which) -> {})
|
||||||
|
.setNegativeButton("Cancel", (view, which) -> {})
|
||||||
|
.show();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -0,0 +1,74 @@
|
|||||||
|
package com.cringe_studios.cringe_authenticator;
|
||||||
|
|
||||||
|
import com.cringe_studios.cringe_authenticator_library.OTP;
|
||||||
|
import com.cringe_studios.cringe_authenticator_library.OTPAlgorithm;
|
||||||
|
import com.cringe_studios.cringe_authenticator_library.OTPType;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
public class OTPData implements Serializable {
|
||||||
|
|
||||||
|
private String name;
|
||||||
|
private OTPType type;
|
||||||
|
private String secret;
|
||||||
|
private OTPAlgorithm algorithm;
|
||||||
|
private int digits;
|
||||||
|
private int period;
|
||||||
|
private int counter;
|
||||||
|
|
||||||
|
public OTPData(String name, OTPType type, String secret, OTPAlgorithm algorithm, int digits, int period, int counter) {
|
||||||
|
this.name = name;
|
||||||
|
this.type = type;
|
||||||
|
this.secret = secret;
|
||||||
|
this.algorithm = algorithm;
|
||||||
|
this.digits = digits;
|
||||||
|
this.period = period;
|
||||||
|
this.counter = counter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public OTPType getType() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSecret() {
|
||||||
|
return secret;
|
||||||
|
}
|
||||||
|
|
||||||
|
public OTPAlgorithm getAlgorithm() {
|
||||||
|
return algorithm;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getDigits() {
|
||||||
|
return digits;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getPeriod() {
|
||||||
|
return period;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getCounter() {
|
||||||
|
return counter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public OTP toOTP() {
|
||||||
|
// TODO: checksum
|
||||||
|
return OTP.createNewOTP(type, secret, algorithm, digits, counter, period, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "OTPData{" +
|
||||||
|
"name='" + name + '\'' +
|
||||||
|
", type=" + type +
|
||||||
|
", secret='" + secret + '\'' +
|
||||||
|
", algorithm=" + algorithm +
|
||||||
|
", digits=" + digits +
|
||||||
|
", period=" + period +
|
||||||
|
", counter=" + counter +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,196 @@
|
|||||||
|
package com.cringe_studios.cringe_authenticator;
|
||||||
|
|
||||||
|
import android.Manifest;
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.media.Image;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.util.Size;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.annotation.OptIn;
|
||||||
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
|
import androidx.camera.core.Camera;
|
||||||
|
import androidx.camera.core.CameraSelector;
|
||||||
|
import androidx.camera.core.ExperimentalGetImage;
|
||||||
|
import androidx.camera.core.ImageAnalysis;
|
||||||
|
import androidx.camera.core.ImageProxy;
|
||||||
|
import androidx.camera.core.Preview;
|
||||||
|
import androidx.camera.lifecycle.ProcessCameraProvider;
|
||||||
|
import androidx.core.content.ContextCompat;
|
||||||
|
import androidx.lifecycle.LifecycleOwner;
|
||||||
|
|
||||||
|
import com.cringe_studios.cringe_authenticator.databinding.ActivityQrScannerBinding;
|
||||||
|
import com.cringe_studios.cringe_authenticator_library.OTP;
|
||||||
|
import com.cringe_studios.cringe_authenticator_library.OTPAlgorithm;
|
||||||
|
import com.cringe_studios.cringe_authenticator_library.OTPType;
|
||||||
|
import com.google.android.gms.common.api.CommonStatusCodes;
|
||||||
|
import com.google.common.util.concurrent.ListenableFuture;
|
||||||
|
import com.google.mlkit.vision.barcode.BarcodeScanner;
|
||||||
|
import com.google.mlkit.vision.barcode.BarcodeScannerOptions;
|
||||||
|
import com.google.mlkit.vision.barcode.BarcodeScanning;
|
||||||
|
import com.google.mlkit.vision.barcode.common.Barcode;
|
||||||
|
import com.google.mlkit.vision.common.InputImage;
|
||||||
|
|
||||||
|
import java.net.URI;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
|
|
||||||
|
public class QRScannerActivity extends AppCompatActivity {
|
||||||
|
|
||||||
|
private ActivityQrScannerBinding binding;
|
||||||
|
|
||||||
|
private ListenableFuture<ProcessCameraProvider> cameraProviderFuture;
|
||||||
|
|
||||||
|
private BarcodeScanner scanner;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||||
|
requestPermissions(new String[] {Manifest.permission.CAMERA}, 1234);
|
||||||
|
}
|
||||||
|
|
||||||
|
binding = ActivityQrScannerBinding.inflate(getLayoutInflater());
|
||||||
|
setContentView(binding.getRoot());
|
||||||
|
|
||||||
|
cameraProviderFuture = ProcessCameraProvider.getInstance(this);
|
||||||
|
cameraProviderFuture.addListener(() -> {
|
||||||
|
Log.i("AMOGUS", "Got something");
|
||||||
|
try {
|
||||||
|
ProcessCameraProvider cameraProvider = cameraProviderFuture.get();
|
||||||
|
bindPreview(cameraProvider);
|
||||||
|
} catch (ExecutionException | InterruptedException e) {
|
||||||
|
// No errors need to be handled for this Future.
|
||||||
|
// This should never be reached.
|
||||||
|
}
|
||||||
|
}, ContextCompat.getMainExecutor(this));
|
||||||
|
|
||||||
|
BarcodeScannerOptions opts = new BarcodeScannerOptions.Builder()
|
||||||
|
.setBarcodeFormats(Barcode.FORMAT_QR_CODE)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
scanner = BarcodeScanning.getClient();
|
||||||
|
}
|
||||||
|
|
||||||
|
void bindPreview(@NonNull ProcessCameraProvider cameraProvider) {
|
||||||
|
Preview preview = new Preview.Builder()
|
||||||
|
.build();
|
||||||
|
|
||||||
|
CameraSelector cameraSelector = new CameraSelector.Builder()
|
||||||
|
.requireLensFacing(CameraSelector.LENS_FACING_BACK)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
preview.setSurfaceProvider(binding.preview.getSurfaceProvider());
|
||||||
|
|
||||||
|
ImageAnalysis analysis = new ImageAnalysis.Builder()
|
||||||
|
//.setTargetResolution(new Size(1280, 720))
|
||||||
|
.setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
|
||||||
|
.build();
|
||||||
|
analysis.setAnalyzer(ContextCompat.getMainExecutor(this), new Amogus());
|
||||||
|
|
||||||
|
Camera camera = cameraProvider.bindToLifecycle(this, cameraSelector, preview, analysis);
|
||||||
|
}
|
||||||
|
|
||||||
|
private class Amogus implements ImageAnalysis.Analyzer {
|
||||||
|
|
||||||
|
@OptIn(markerClass = ExperimentalGetImage.class)
|
||||||
|
@Override
|
||||||
|
public void analyze(@NonNull ImageProxy image) {
|
||||||
|
Image mediaImage = image.getImage();
|
||||||
|
if(mediaImage != null) {
|
||||||
|
InputImage input = InputImage.fromMediaImage(mediaImage, image.getImageInfo().getRotationDegrees());
|
||||||
|
scanner.process(input).addOnSuccessListener(barcodes -> {
|
||||||
|
//Log.i("AMOGUS", "found " + barcodes.size() + " codes");
|
||||||
|
image.close();
|
||||||
|
|
||||||
|
if(barcodes.size() >= 1) {
|
||||||
|
Barcode code = null;
|
||||||
|
|
||||||
|
Log.i("AMOGUS", "TYPE " + barcodes.get(0).getValueType());
|
||||||
|
for(Barcode c : barcodes) {
|
||||||
|
if(c.getValueType() == Barcode.TYPE_TEXT) {
|
||||||
|
code = c;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Uri uri = Uri.parse(code.getRawValue());
|
||||||
|
Log.i("AMOGUS", code.getRawValue());
|
||||||
|
Log.i("AMOGUS", uri.getHost());
|
||||||
|
Log.i("AMOGUS", uri.getPath());
|
||||||
|
|
||||||
|
String type = uri.getHost();
|
||||||
|
String accountName = uri.getPath();
|
||||||
|
String secret = uri.getQueryParameter("secret");
|
||||||
|
String algorithm = uri.getQueryParameter("algorithm");
|
||||||
|
String digits = uri.getQueryParameter("digits");
|
||||||
|
String period = uri.getQueryParameter("period");
|
||||||
|
String counter = uri.getQueryParameter("counter");
|
||||||
|
|
||||||
|
if(type == null || secret == null) {
|
||||||
|
error("Missing params");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
OTPType fType;
|
||||||
|
try {
|
||||||
|
fType = OTPType.valueOf(type.toUpperCase());
|
||||||
|
}catch(IllegalArgumentException e) {
|
||||||
|
Log.i("AMOGUS", e.toString());
|
||||||
|
error("Failed to parse OTP parameters");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(fType == OTPType.HOTP && counter == null) {
|
||||||
|
error("Missing params");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(accountName == null || accountName.length() < 2 /* Because path is /accName, so 2 letters for acc with 1 letter name */) {
|
||||||
|
// TODO: error
|
||||||
|
Log.i("AMOGUS", "Missing params");
|
||||||
|
error("Missing OTP parameters");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
accountName = accountName.substring(1);
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 0 or null for defaults (handled by Cringe-Authenticator-Library)
|
||||||
|
OTPAlgorithm fAlgorithm = algorithm == null ? null : OTPAlgorithm.valueOf(algorithm.toUpperCase());
|
||||||
|
int fDigits = digits == null ? 0 : Integer.parseInt(digits);
|
||||||
|
int fPeriod = period == null ? 0 : Integer.parseInt(period);
|
||||||
|
int fCounter = counter == null ? 0 : Integer.parseInt(counter);
|
||||||
|
|
||||||
|
success(new OTPData(accountName, fType, secret, fAlgorithm, fDigits, fPeriod, fCounter));
|
||||||
|
}catch(IllegalArgumentException e) {
|
||||||
|
Log.i("AMOGUS", e.toString());
|
||||||
|
error("Failed to parse OTP parameters");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).addOnFailureListener(e -> {});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void success(OTPData data) {
|
||||||
|
Intent result = new Intent();
|
||||||
|
result.putExtra("data", data);
|
||||||
|
setResult(Activity.RESULT_OK, result);
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void error(String errorMessage) {
|
||||||
|
Intent result = new Intent();
|
||||||
|
setResult(Activity.RESULT_CANCELED, result);
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,28 @@
|
|||||||
|
package com.cringe_studios.cringe_authenticator;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
|
||||||
|
import androidx.activity.result.contract.ActivityResultContract;
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
|
public class QRScannerContract extends ActivityResultContract<Void, OTPData> {
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public Intent createIntent(@NonNull Context context, Void theVoid) {
|
||||||
|
return new Intent(context, QRScannerActivity.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public OTPData parseResult(int result, @Nullable Intent intent) {
|
||||||
|
if(result != Activity.RESULT_OK || intent == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (OTPData) intent.getSerializableExtra("data");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,6 +1,9 @@
|
|||||||
package com.cringe_studios.cringe_authenticator.fragment;
|
package com.cringe_studios.cringe_authenticator.fragment;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.util.Log;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
@ -9,15 +12,21 @@ import androidx.annotation.NonNull;
|
|||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.fragment.app.Fragment;
|
import androidx.fragment.app.Fragment;
|
||||||
|
|
||||||
|
import com.cringe_studios.cringe_authenticator.OTPData;
|
||||||
import com.cringe_studios.cringe_authenticator.R;
|
import com.cringe_studios.cringe_authenticator.R;
|
||||||
import com.cringe_studios.cringe_authenticator.databinding.AuthenticateTotpBinding;
|
import com.cringe_studios.cringe_authenticator.databinding.AuthenticateTotpBinding;
|
||||||
import com.cringe_studios.cringe_authenticator.databinding.FragmentDynamicBinding;
|
import com.cringe_studios.cringe_authenticator.databinding.FragmentDynamicBinding;
|
||||||
import com.cringe_studios.cringe_authenticator.util.FabUtil;
|
import com.cringe_studios.cringe_authenticator.util.FabUtil;
|
||||||
import com.cringe_studios.cringe_authenticator.util.NavigationUtil;
|
import com.cringe_studios.cringe_authenticator.util.NavigationUtil;
|
||||||
|
import com.cringe_studios.cringe_authenticator.util.SettingsUtil;
|
||||||
import com.google.android.material.floatingactionbutton.FloatingActionButton;
|
import com.google.android.material.floatingactionbutton.FloatingActionButton;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
public class DynamicFragment extends Fragment {
|
public class DynamicFragment extends Fragment {
|
||||||
|
|
||||||
|
private String groupName;
|
||||||
|
|
||||||
private FragmentDynamicBinding binding;
|
private FragmentDynamicBinding binding;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -30,23 +39,43 @@ public class DynamicFragment extends Fragment {
|
|||||||
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||||
binding = FragmentDynamicBinding.inflate(inflater, container, false);
|
binding = FragmentDynamicBinding.inflate(inflater, container, false);
|
||||||
|
|
||||||
String tab = requireArguments().getString("tab");
|
groupName = requireArguments().getString("group");
|
||||||
|
|
||||||
String[] totps = new String[]{"Code 1", "Code 2", tab};
|
/*String[] totps = new String[]{"Code 1", "Code 2", groupName};
|
||||||
for(String totp : totps) {
|
for(String totp : totps) {
|
||||||
AuthenticateTotpBinding itemBinding = AuthenticateTotpBinding.inflate(inflater);
|
AuthenticateTotpBinding itemBinding = AuthenticateTotpBinding.inflate(inflater);
|
||||||
itemBinding.displayName.setText(totp);
|
itemBinding.displayName.setText(totp);
|
||||||
binding.itemList.addView(itemBinding.getRoot());
|
binding.itemList.addView(itemBinding.getRoot());
|
||||||
}
|
}*/
|
||||||
|
|
||||||
|
loadOTPs();
|
||||||
|
|
||||||
FabUtil.showFabs(getActivity());
|
FabUtil.showFabs(getActivity());
|
||||||
|
|
||||||
return binding.getRoot();
|
return binding.getRoot();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void loadOTPs() {
|
||||||
|
SharedPreferences prefs = getActivity().getSharedPreferences("groups", Context.MODE_PRIVATE);
|
||||||
|
List<OTPData> data = SettingsUtil.getOTPs(prefs, groupName);
|
||||||
|
Log.i("AMOGUS", "OTPS: " + data);
|
||||||
|
|
||||||
|
for(OTPData otp : data) {
|
||||||
|
AuthenticateTotpBinding itemBinding = AuthenticateTotpBinding.inflate(getLayoutInflater());
|
||||||
|
itemBinding.displayName.setText(otp.getName());
|
||||||
|
itemBinding.totpCode.setText(otp.toOTP().getPin());
|
||||||
|
binding.itemList.addView(itemBinding.getRoot());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onDestroyView() {
|
public void onDestroyView() {
|
||||||
super.onDestroyView();
|
super.onDestroyView();
|
||||||
this.binding = null;
|
this.binding = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getGroupName() {
|
||||||
|
return groupName;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,30 @@
|
|||||||
|
package com.cringe_studios.cringe_authenticator.util;
|
||||||
|
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
|
||||||
|
import com.cringe_studios.cringe_authenticator.OTPData;
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class SettingsUtil {
|
||||||
|
|
||||||
|
private static final Gson GSON = new Gson();
|
||||||
|
|
||||||
|
public static List<OTPData> getOTPs(SharedPreferences prefs, String group) {
|
||||||
|
String currentOTPs = prefs.getString("group." + group, "[]");
|
||||||
|
return Arrays.asList(GSON.fromJson(currentOTPs, OTPData[].class));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void addOTP(SharedPreferences prefs, String group, OTPData data) {
|
||||||
|
List<OTPData> otps = new ArrayList<>(getOTPs(prefs, group));
|
||||||
|
otps.add(data);
|
||||||
|
|
||||||
|
prefs.edit()
|
||||||
|
.putString("group." + group, GSON.toJson(otps.toArray(new OTPData[otps.size()])))
|
||||||
|
.apply();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -28,7 +28,7 @@
|
|||||||
android:layout_gravity="bottom|end"
|
android:layout_gravity="bottom|end"
|
||||||
android:layout_marginEnd="@dimen/fab_margin"
|
android:layout_marginEnd="@dimen/fab_margin"
|
||||||
android:layout_marginBottom="16dp"
|
android:layout_marginBottom="16dp"
|
||||||
app:srcCompat="@android:drawable/ic_menu_camera"
|
app:srcCompat="@android:drawable/ic_menu_edit"
|
||||||
android:visibility="gone" />
|
android:visibility="gone" />
|
||||||
|
|
||||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||||
@ -38,7 +38,8 @@
|
|||||||
android:layout_gravity="bottom|end"
|
android:layout_gravity="bottom|end"
|
||||||
android:layout_marginEnd="@dimen/fab_margin"
|
android:layout_marginEnd="@dimen/fab_margin"
|
||||||
android:layout_marginBottom="16dp"
|
android:layout_marginBottom="16dp"
|
||||||
app:srcCompat="@android:drawable/ic_menu_edit"
|
android:onClick="scanCode"
|
||||||
|
app:srcCompat="@android:drawable/ic_menu_camera"
|
||||||
android:visibility="gone" />
|
android:visibility="gone" />
|
||||||
|
|
||||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||||
|
12
app/src/main/res/layout/activity_qr_scanner.xml
Normal file
12
app/src/main/res/layout/activity_qr_scanner.xml
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<FrameLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:keepScreenOn="true"
|
||||||
|
android:id="@+id/container">
|
||||||
|
<androidx.camera.view.PreviewView
|
||||||
|
android:id="@+id/preview"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent" />
|
||||||
|
</FrameLayout>
|
17
app/src/main/res/menu/menu_groups.xml
Normal file
17
app/src/main/res/menu/menu_groups.xml
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
tools:context="com.cringe_studios.cringe_authenticator.MainActivity">
|
||||||
|
<item
|
||||||
|
android:id="@+id/action_code"
|
||||||
|
android:orderInCategory="100"
|
||||||
|
android:title="New Group"
|
||||||
|
app:showAsAction="never"
|
||||||
|
android:onClick="addGroup" />
|
||||||
|
<item
|
||||||
|
android:id="@+id/action_settings"
|
||||||
|
android:orderInCategory="100"
|
||||||
|
android:title="Settings"
|
||||||
|
app:showAsAction="never"
|
||||||
|
android:onClick="openSettings" />
|
||||||
|
</menu>
|
Loading…
Reference in New Issue
Block a user