diff --git a/android/app/build.gradle b/android/app/build.gradle
index 7416c22e..ceb73cbb 100644
--- a/android/app/build.gradle
+++ b/android/app/build.gradle
@@ -52,6 +52,7 @@ android {
dependencies {
implementation platform('com.google.firebase:firebase-bom:32.7.4')
+ implementation 'com.journeyapps:zxing-android-embedded:4.3.0'
implementation 'com.google.firebase:firebase-analytics-ktx'
implementation 'com.google.firebase:firebase-messaging-ktx'
implementation 'com.squareup.okhttp3:okhttp:4.10.0'
diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml
index f04d6f26..cf5ef65c 100644
--- a/android/app/src/main/AndroidManifest.xml
+++ b/android/app/src/main/AndroidManifest.xml
@@ -45,6 +45,11 @@
tools:ignore="LockedOrientationActivity" />
+
(R.id.loginApiKeyTextInputLayout)
+ val apiKeyInput = findViewById(R.id.loginApiKeyTextInput)
+
+ apiKeyInput.setOnClickListener {
+ startQrCodeScan()
+ }
+
+ apiKeyInputLayout.setEndIconOnClickListener {
+ startQrCodeScan()
+ }
+ }
+
+ private val barcodeLauncher = registerForActivityResult(ScanContract()) { result ->
+ if (result.contents != null) {
+ val apiKeyInput = findViewById(R.id.loginApiKeyTextInput)
+ apiKeyInput.setText(result.contents)
+ Toast.makeText(this, "Scanned: ${result.contents}", Toast.LENGTH_LONG).show()
+ } else {
+ Toast.makeText(this, "Scan cancelled", Toast.LENGTH_SHORT).show()
+ }
+ }
+
+ private fun startQrCodeScan() {
+ val options = ScanOptions()
+ options.setPrompt("Scan a QR code")
+ options.setBeepEnabled(true)
+ options.setOrientationLocked(false)
+ options.setCameraId(0)
+ barcodeLauncher.launch(options)
}
override fun onStart() {
diff --git a/android/app/src/main/res/layout/activity_login.xml b/android/app/src/main/res/layout/activity_login.xml
index 0a1f046c..cc4d4eb1 100644
--- a/android/app/src/main/res/layout/activity_login.xml
+++ b/android/app/src/main/res/layout/activity_login.xml
@@ -41,10 +41,13 @@
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- app:errorEnabled="true"
android:hint="@string/text_area_api_key"
- app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintTop_toTopOf="parent"
+ app:errorEnabled="true"
+ app:endIconMode="custom"
+ app:endIconDrawable="@android:drawable/ic_menu_camera"
+ app:endIconContentDescription="cameraButton"
+ app:layout_constraintBottom_toTopOf="@+id/loginPhoneNumberLayoutSIM1"
+ app:layout_constraintTop_toBottomOf="@+id/textView"
tools:layout_editor_absoluteX="16dp">
+
+ {{ mdiQrcode }}
+ Show QR Code
+
+
+
+ API Key QR Code
+
+
+
+
+
+ Close
+
+
+
{
+ this.generateQrCode(this.apiKey);
+ });
+ }
+ },
+ },
async mounted() {
await Promise.all([
this.$store.dispatch('clearAxiosError'),
@@ -818,6 +851,16 @@ export default Vue.extend({
},
methods: {
+ generateQrCode(text) {
+ const canvas = this.$refs.qrCodeCanvas;
+ if (canvas) {
+ QRCode.toCanvas(canvas, text, { errorCorrectionLevel: 'H' }, (err) => {
+ if (err) {
+ console.error(err);
+ }
+ });
+ }
+ },
updateEmailNotifications() {
this.notificationSettings = {
webhook_enabled:
diff --git a/web/pnpm-lock.yaml b/web/pnpm-lock.yaml
index b4e47417..926b6825 100644
--- a/web/pnpm-lock.yaml
+++ b/web/pnpm-lock.yaml
@@ -56,6 +56,9 @@ importers:
nuxt-highlightjs:
specifier: ^1.0.3
version: 1.0.3
+ qrcode:
+ specifier: ^1.5.0
+ version: 1.5.0
ufo:
specifier: ^1.5.4
version: 1.5.4