feat(android): QR pairing — ZXing scanner + ScanPairingActivity + strings PT-PT
- Adiciona dependência zxing-android-embedded:4.3.0 - Adiciona permissão CAMERA e regista ScanPairingActivity no Manifest - Cria ScanPairingActivity: scan QR → parse JSON → POST claim-device - Adiciona preferência "Emparelhar dispositivo" nas definições do servidor - Adiciona handler de clique em WhatSmsServerSettingsFragment - Strings PT-PT: scan_qr_to_pair, pairing_success/failed/cancelled/error - Bump versionName para 3.2.0 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -7,6 +7,7 @@
|
||||
android:required="false" />
|
||||
|
||||
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
|
||||
<uses-permission android:name="android.permission.CAMERA" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_CONNECTED_DEVICE" />
|
||||
@@ -84,6 +85,10 @@
|
||||
android:foregroundServiceType="dataSync"
|
||||
tools:node="merge" />
|
||||
|
||||
<activity
|
||||
android:name=".ui.ScanPairingActivity"
|
||||
android:exported="false" />
|
||||
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:exported="true">
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
package me.capcom.smsgateway.ui
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Intent
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.widget.Toast
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import com.google.zxing.integration.android.IntentIntegrator
|
||||
import com.google.zxing.integration.android.IntentResult
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import me.capcom.smsgateway.R
|
||||
import me.capcom.smsgateway.modules.gateway.GatewaySettings
|
||||
import okhttp3.MediaType.Companion.toMediaType
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import okhttp3.RequestBody.Companion.toRequestBody
|
||||
import org.json.JSONObject
|
||||
import org.koin.android.ext.android.inject
|
||||
|
||||
class ScanPairingActivity : AppCompatActivity() {
|
||||
private val settings: GatewaySettings by inject()
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
IntentIntegrator(this).apply {
|
||||
setPrompt(getString(R.string.scan_qr_to_pair))
|
||||
setBeepEnabled(true)
|
||||
setOrientationLocked(false)
|
||||
initiateScan()
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("OVERRIDE_DEPRECATION")
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
val result: IntentResult =
|
||||
IntentIntegrator.parseActivityResult(requestCode, resultCode, data)
|
||||
?: return super.onActivityResult(requestCode, resultCode, data)
|
||||
if (result.contents == null) {
|
||||
Toast.makeText(this, R.string.pairing_cancelled, Toast.LENGTH_SHORT).show()
|
||||
setResult(Activity.RESULT_CANCELED)
|
||||
finish()
|
||||
return
|
||||
}
|
||||
claimDevice(result.contents)
|
||||
}
|
||||
|
||||
private fun claimDevice(qrContent: String) {
|
||||
val deviceId = settings.deviceId
|
||||
val username = settings.username
|
||||
val password = settings.password
|
||||
if (deviceId == null || username == null || password == null) {
|
||||
Toast.makeText(this, R.string.pairing_not_registered, Toast.LENGTH_LONG).show()
|
||||
setResult(Activity.RESULT_CANCELED)
|
||||
finish()
|
||||
return
|
||||
}
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
try {
|
||||
val qr = JSONObject(qrContent)
|
||||
val claimUrl = qr.getString("claimUrl")
|
||||
val pairingToken = qr.getString("pairingToken")
|
||||
val body = JSONObject().apply {
|
||||
put("pairingToken", pairingToken)
|
||||
put("deviceId", deviceId)
|
||||
put("username", username)
|
||||
put("password", password)
|
||||
put("deviceName", Build.MODEL)
|
||||
}.toString()
|
||||
val client = OkHttpClient()
|
||||
val request = Request.Builder()
|
||||
.url(claimUrl)
|
||||
.post(body.toRequestBody("application/json".toMediaType()))
|
||||
.build()
|
||||
val response = client.newCall(request).execute()
|
||||
val ok = response.isSuccessful
|
||||
withContext(Dispatchers.Main) {
|
||||
if (ok) {
|
||||
Toast.makeText(this@ScanPairingActivity, R.string.pairing_success, Toast.LENGTH_SHORT).show()
|
||||
setResult(Activity.RESULT_OK)
|
||||
} else {
|
||||
Toast.makeText(this@ScanPairingActivity, R.string.pairing_failed, Toast.LENGTH_LONG).show()
|
||||
setResult(Activity.RESULT_CANCELED)
|
||||
}
|
||||
finish()
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
withContext(Dispatchers.Main) {
|
||||
Toast.makeText(this@ScanPairingActivity, getString(R.string.pairing_error, e.message), Toast.LENGTH_LONG).show()
|
||||
setResult(Activity.RESULT_CANCELED)
|
||||
finish()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+125
-1
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1 +1,57 @@
|
||||
PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPFByZWZlcmVuY2VTY3JlZW4geG1sbnM6YW5kcm9pZD0iaHR0cDovL3NjaGVtYXMuYW5kcm9pZC5jb20vYXBrL3Jlcy9hbmRyb2lkIgogICAgeG1sbnM6YXBwPSJodHRwOi8vc2NoZW1hcy5hbmRyb2lkLmNvbS9hcGsvcmVzLWF1dG8iPgogICAgPFByZWZlcmVuY2VDYXRlZ29yeSBhcHA6dGl0bGU9IkBzdHJpbmcvc2VydmVyIj4KICAgICAgICA8UHJlZmVyZW5jZQogICAgICAgICAgICBhcHA6ZW5hYmxlQ29weWluZz0idHJ1ZSIKICAgICAgICAgICAgYXBwOmljb249IkBkcmF3YWJsZS9pY19zZXJ2ZXIiCiAgICAgICAgICAgIGFwcDprZXk9InRyYW5zaWVudC5zZXJ2ZXJfdXJsIgogICAgICAgICAgICBhcHA6cGVyc2lzdGVudD0iZmFsc2UiCiAgICAgICAgICAgIGFwcDpzZWxlY3RhYmxlPSJmYWxzZSIKICAgICAgICAgICAgYXBwOnRpdGxlPSJAc3RyaW5nL2FwaV91cmwiIC8+CiAgICAgICAgPExpc3RQcmVmZXJlbmNlCiAgICAgICAgICAgIGFwcDppY29uPSJAZHJhd2FibGUvaWNfbm90aWZpY2F0aW9ucyIKICAgICAgICAgICAgYXBwOmtleT0iZ2F0ZXdheS5ub3RpZmljYXRpb25fY2hhbm5lbCIKICAgICAgICAgICAgYXBwOmRlZmF1bHRWYWx1ZT0iQVVUTyIKICAgICAgICAgICAgYXBwOmVudHJpZXM9IkBhcnJheS9ub3RpZmljYXRpb25fY2hhbm5lbHNfdGl0bGVzIgogICAgICAgICAgICBhcHA6ZW50cnlWYWx1ZXM9IkBhcnJheS9ub3RpZmljYXRpb25fY2hhbm5lbHNfdmFsdWVzIgogICAgICAgICAgICBhcHA6dGl0bGU9IkBzdHJpbmcvbm90aWZpY2F0aW9uX2NoYW5uZWwiCiAgICAgICAgICAgIGFwcDp1c2VTaW1wbGVTdW1tYXJ5UHJvdmlkZXI9InRydWUiIC8+CiAgICA8L1ByZWZlcmVuY2VDYXRlZ29yeT4KICAgIDxQcmVmZXJlbmNlQ2F0ZWdvcnkgYXBwOnRpdGxlPSJAc3RyaW5nL2NyZWRlbnRpYWxzIj4KICAgICAgICA8RWRpdFRleHRQcmVmZXJlbmNlCiAgICAgICAgICAgIGFwcDplbmFibGVDb3B5aW5nPSJ0cnVlIgogICAgICAgICAgICBhcHA6aWNvbj0iQGRyYXdhYmxlL2ljX3VzZXJuYW1lIgogICAgICAgICAgICBhcHA6a2V5PSJnYXRld2F5LnVzZXJuYW1lIgogICAgICAgICAgICBhcHA6cGVyc2lzdGVudD0iZmFsc2UiCiAgICAgICAgICAgIGFwcDpzZWxlY3RhYmxlPSJmYWxzZSIKICAgICAgICAgICAgYXBwOnRpdGxlPSJAc3RyaW5nL3VzZXJuYW1lIiAvPgogICAgICAgIDxFZGl0VGV4dFByZWZlcmVuY2UKICAgICAgICAgICAgYXBwOmVuYWJsZUNvcHlpbmc9InRydWUiCiAgICAgICAgICAgIGFwcDppY29uPSJAZHJhd2FibGUvaWNfcGFzc3dvcmQiCiAgICAgICAgICAgIGFwcDprZXk9ImdhdGV3YXkucGFzc3dvcmQiCiAgICAgICAgICAgIGFwcDpwZXJzaXN0ZW50PSJmYWxzZSIKICAgICAgICAgICAgYXBwOnRpdGxlPSJAc3RyaW5nL3Bhc3N3b3JkIiAvPgogICAgICAgIDxQcmVmZXJlbmNlCiAgICAgICAgICAgIGFuZHJvaWQ6aWNvbj0iQGRyYXdhYmxlL2ljX2NvZGUiCiAgICAgICAgICAgIGFuZHJvaWQ6a2V5PSJnYXRld2F5LmxvZ2luX2NvZGUiCiAgICAgICAgICAgIGFuZHJvaWQ6cGVyc2lzdGVudD0iZmFsc2UiCiAgICAgICAgICAgIGFuZHJvaWQ6c3VtbWFyeT0iQHN0cmluZy91c2VfdGhpc19jb2RlX3RvX3NpZ25faW5fb25fYW5vdGhlcl9kZXZpY2UiCiAgICAgICAgICAgIGFuZHJvaWQ6dGl0bGU9IkBzdHJpbmcvbG9naW5fY29kZSIKICAgICAgICAgICAgYXBwOmVuYWJsZUNvcHlpbmc9InRydWUiIC8+CiAgICA8L1ByZWZlcmVuY2VDYXRlZ29yeT4KICAgIDxQcmVmZXJlbmNlQ2F0ZWdvcnkgYXBwOnRpdGxlPSJAc3RyaW5nL2RldmljZSI+CiAgICAgICAgPFByZWZlcmVuY2UKICAgICAgICAgICAgYW5kcm9pZDppY29uPSJAZHJhd2FibGUvaWNfZGV2aWNlX2lkIgogICAgICAgICAgICBhbmRyb2lkOmtleT0idHJhbnNpZW50LmRldmljZV9pZCIKICAgICAgICAgICAgYW5kcm9pZDp0aXRsZT0iQHN0cmluZy9kZXZpY2VfaWQiCiAgICAgICAgICAgIGFwcDplbmFibGVDb3B5aW5nPSJ0cnVlIgogICAgICAgICAgICBhcHA6cGVyc2lzdGVudD0iZmFsc2UiIC8+CiAgICA8L1ByZWZlcmVuY2VDYXRlZ29yeT4KICAgIDxQcmVmZXJlbmNlIGFwcDpzdW1tYXJ5PSJAc3RyaW5nL3Jlc3RhcnRfcmVxdWlyZWRfdG9fYXBwbHlfY2hhbmdlcyIgLz4KPC9QcmVmZXJlbmNlU2NyZWVuPgo=
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
<PreferenceCategory app:title="@string/server">
|
||||
<Preference
|
||||
app:enableCopying="true"
|
||||
app:icon="@drawable/ic_server"
|
||||
app:key="transient.server_url"
|
||||
app:persistent="false"
|
||||
app:selectable="false"
|
||||
app:title="@string/api_url" />
|
||||
<ListPreference
|
||||
app:icon="@drawable/ic_notifications"
|
||||
app:key="gateway.notification_channel"
|
||||
app:defaultValue="AUTO"
|
||||
app:entries="@array/notification_channels_titles"
|
||||
app:entryValues="@array/notification_channels_values"
|
||||
app:title="@string/notification_channel"
|
||||
app:useSimpleSummaryProvider="true" />
|
||||
</PreferenceCategory>
|
||||
<PreferenceCategory app:title="@string/credentials">
|
||||
<EditTextPreference
|
||||
app:enableCopying="true"
|
||||
app:icon="@drawable/ic_username"
|
||||
app:key="gateway.username"
|
||||
app:persistent="false"
|
||||
app:selectable="false"
|
||||
app:title="@string/username" />
|
||||
<EditTextPreference
|
||||
app:enableCopying="true"
|
||||
app:icon="@drawable/ic_password"
|
||||
app:key="gateway.password"
|
||||
app:persistent="false"
|
||||
app:title="@string/password" />
|
||||
<Preference
|
||||
android:icon="@drawable/ic_code"
|
||||
android:key="gateway.login_code"
|
||||
android:persistent="false"
|
||||
android:summary="@string/use_this_code_to_sign_in_on_another_device"
|
||||
android:title="@string/login_code"
|
||||
app:enableCopying="true" />
|
||||
</PreferenceCategory>
|
||||
<PreferenceCategory app:title="@string/device">
|
||||
<Preference
|
||||
android:icon="@drawable/ic_device_id"
|
||||
android:key="transient.device_id"
|
||||
android:title="@string/device_id"
|
||||
app:enableCopying="true"
|
||||
app:persistent="false" />
|
||||
<Preference
|
||||
app:key="action.pair_device"
|
||||
app:icon="@drawable/ic_cloud_server"
|
||||
app:title="@string/pair_device"
|
||||
app:summary="@string/pair_device_summary" />
|
||||
</PreferenceCategory>
|
||||
<Preference app:summary="@string/restart_required_to_apply_changes" />
|
||||
</PreferenceScreen>
|
||||
|
||||
Reference in New Issue
Block a user