Skip to content

Commit 385771f

Browse files
authored
Merge pull request #519 from im45so/add-qrcode-scanner
Add qrcode scanner
2 parents cdb2826 + fd02dce commit 385771f

File tree

7 files changed

+94
-4
lines changed

7 files changed

+94
-4
lines changed

android/app/build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ android {
5252

5353
dependencies {
5454
implementation platform('com.google.firebase:firebase-bom:32.7.4')
55+
implementation 'com.journeyapps:zxing-android-embedded:4.3.0'
5556
implementation 'com.google.firebase:firebase-analytics-ktx'
5657
implementation 'com.google.firebase:firebase-messaging-ktx'
5758
implementation 'com.squareup.okhttp3:okhttp:4.10.0'

android/app/src/main/AndroidManifest.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,11 @@
4545
tools:ignore="LockedOrientationActivity" />
4646
<activity android:name=".SettingsActivity" android:screenOrientation="portrait"
4747
tools:ignore="LockedOrientationActivity" />
48+
<activity
49+
android:name="com.journeyapps.barcodescanner.CaptureActivity"
50+
android:screenOrientation="portrait"
51+
tools:replace="screenOrientation"
52+
tools:ignore="DiscouragedApi" />
4853

4954
<service
5055
android:name=".services.StickyNotificationService"

android/app/src/main/java/com/httpsms/LoginActivity.kt

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ import com.google.android.material.button.MaterialButton
2323
import com.google.android.material.progressindicator.LinearProgressIndicator
2424
import com.google.android.material.textfield.TextInputEditText
2525
import com.google.android.material.textfield.TextInputLayout
26+
import com.journeyapps.barcodescanner.ScanContract
27+
import com.journeyapps.barcodescanner.ScanOptions
2628
import timber.log.Timber
2729
import java.net.URI
2830

@@ -36,6 +38,39 @@ class LoginActivity : AppCompatActivity() {
3638
setPhoneNumber()
3739
disableSim2()
3840
setServerURL()
41+
setupApiKeyInput()
42+
}
43+
44+
private fun setupApiKeyInput() {
45+
val apiKeyInputLayout = findViewById<TextInputLayout>(R.id.loginApiKeyTextInputLayout)
46+
val apiKeyInput = findViewById<TextInputEditText>(R.id.loginApiKeyTextInput)
47+
48+
apiKeyInput.setOnClickListener {
49+
startQrCodeScan()
50+
}
51+
52+
apiKeyInputLayout.setEndIconOnClickListener {
53+
startQrCodeScan()
54+
}
55+
}
56+
57+
private val barcodeLauncher = registerForActivityResult(ScanContract()) { result ->
58+
if (result.contents != null) {
59+
val apiKeyInput = findViewById<TextInputEditText>(R.id.loginApiKeyTextInput)
60+
apiKeyInput.setText(result.contents)
61+
Toast.makeText(this, "Scanned: ${result.contents}", Toast.LENGTH_LONG).show()
62+
} else {
63+
Toast.makeText(this, "Scan cancelled", Toast.LENGTH_SHORT).show()
64+
}
65+
}
66+
67+
private fun startQrCodeScan() {
68+
val options = ScanOptions()
69+
options.setPrompt("Scan a QR code")
70+
options.setBeepEnabled(true)
71+
options.setOrientationLocked(false)
72+
options.setCameraId(0)
73+
barcodeLauncher.launch(options)
3974
}
4075

4176
override fun onStart() {

android/app/src/main/res/layout/activity_login.xml

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,13 @@
4141
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
4242
android:layout_width="match_parent"
4343
android:layout_height="wrap_content"
44-
app:errorEnabled="true"
4544
android:hint="@string/text_area_api_key"
46-
app:layout_constraintBottom_toBottomOf="parent"
47-
app:layout_constraintTop_toTopOf="parent"
45+
app:errorEnabled="true"
46+
app:endIconMode="custom"
47+
app:endIconDrawable="@android:drawable/ic_menu_camera"
48+
app:endIconContentDescription="cameraButton"
49+
app:layout_constraintBottom_toTopOf="@+id/loginPhoneNumberLayoutSIM1"
50+
app:layout_constraintTop_toBottomOf="@+id/textView"
4851
tools:layout_editor_absoluteX="16dp">
4952

5053
<com.google.android.material.textfield.TextInputEditText
@@ -131,7 +134,6 @@
131134
android:id="@+id/linearLayout"
132135
android:layout_width="wrap_content"
133136
android:layout_height="wrap_content"
134-
android:layout_weight="50"
135137
android:orientation="vertical"
136138
app:layout_constraintEnd_toEndOf="parent"
137139
app:layout_constraintStart_toStartOf="parent"

web/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
"moment": "^2.30.1",
3939
"nuxt": "^2.18.1",
4040
"nuxt-highlightjs": "^1.0.3",
41+
"qrcode": "^1.5.0",
4142
"ufo": "^1.5.4",
4243
"vue": "^2.7.16",
4344
"vue-chartjs": "^5.3.1",

web/pages/settings/index.vue

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,26 @@
8282
copy-text="Copy API Key"
8383
notification-text="API Key copied successfully"
8484
></copy-button>
85+
<v-btn
86+
color="primary"
87+
@click="showQrCodeDialog = true"
88+
class="ml-4"
89+
>
90+
<v-icon left>{{ mdiQrcode }}</v-icon>
91+
Show QR Code
92+
</v-btn>
93+
<v-dialog v-model="showQrCodeDialog" max-width="400px">
94+
<v-card>
95+
<v-card-title>API Key QR Code</v-card-title>
96+
<v-card-text>
97+
<canvas ref="qrCodeCanvas"></canvas>
98+
</v-card-text>
99+
<v-card-actions>
100+
<v-spacer></v-spacer>
101+
<v-btn color="primary" text @click="showQrCodeDialog = false">Close</v-btn>
102+
</v-card-actions>
103+
</v-card>
104+
</v-dialog>
85105
<v-btn
86106
v-if="$vuetify.breakpoint.lgAndUp"
87107
class="ml-4"
@@ -714,7 +734,9 @@ import {
714734
mdiLinkVariant,
715735
mdiEyeOff,
716736
mdiSquareEditOutline,
737+
mdiQrcode,
717738
} from '@mdi/js'
739+
import QRCode from 'qrcode'
718740
import { ErrorMessages } from '~/plugins/errors'
719741
import LoadingButton from '~/components/LoadingButton.vue'
720742
@@ -731,6 +753,7 @@ export default Vue.extend({
731753
mdiAccountCircle,
732754
mdiShieldCheck,
733755
mdiDelete,
756+
mdiQrcode,
734757
mdiLinkVariant,
735758
mdiContentSave,
736759
mdiSquareEditOutline,
@@ -741,6 +764,7 @@ export default Vue.extend({
741764
showDiscordEdit: false,
742765
showRotateApiKey: false,
743766
rotatingApiKey: false,
767+
showQrCodeDialog: false,
744768
activeWebhook: {
745769
id: null,
746770
url: '',
@@ -803,6 +827,15 @@ export default Vue.extend({
803827
})
804828
},
805829
},
830+
watch: {
831+
showQrCodeDialog(newVal) {
832+
if (newVal && this.apiKey) {
833+
this.$nextTick(() => {
834+
this.generateQrCode(this.apiKey);
835+
});
836+
}
837+
},
838+
},
806839
async mounted() {
807840
await Promise.all([
808841
this.$store.dispatch('clearAxiosError'),
@@ -818,6 +851,16 @@ export default Vue.extend({
818851
},
819852
820853
methods: {
854+
generateQrCode(text) {
855+
const canvas = this.$refs.qrCodeCanvas;
856+
if (canvas) {
857+
QRCode.toCanvas(canvas, text, { errorCorrectionLevel: 'H' }, (err) => {
858+
if (err) {
859+
console.error(err);
860+
}
861+
});
862+
}
863+
},
821864
updateEmailNotifications() {
822865
this.notificationSettings = {
823866
webhook_enabled:

web/pnpm-lock.yaml

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)