Add QR code scanning for server configuration
Adds QR code scanning functionality to both ServerSetupActivity and SettingsActivity, allowing users to auto-fill server configuration by scanning a QR code from the server's admin dashboard. Changes: - Add ZXing Android Embedded library (v4.3.0) for QR scanning - Add CAMERA permission to AndroidManifest - Create QrServerConfig model to parse and validate QR JSON data - Create QrScannerUtil for shared scanning functionality - Extend PermissionUtil with camera permission handling - Add "Scan QR Code" button to ServerSetupActivity with hint text - Add "Scan QR Code" button to SettingsActivity - Add QR scan icon and string resources - Configure Gradle Java toolchain auto-detection The QR code contains JSON with server address, port, and client key which are automatically populated into the input fields when scanned.
This commit is contained in:
@@ -56,6 +56,9 @@ dependencies {
|
|||||||
implementation 'com.android.support:support-annotations:28.0.0'
|
implementation 'com.android.support:support-annotations:28.0.0'
|
||||||
implementation 'androidx.palette:palette:1.0.0'
|
implementation 'androidx.palette:palette:1.0.0'
|
||||||
|
|
||||||
|
// QR Code scanning
|
||||||
|
implementation 'com.journeyapps:zxing-android-embedded:4.3.0'
|
||||||
|
|
||||||
// Room
|
// Room
|
||||||
def room_version = "2.6.1"
|
def room_version = "2.6.1"
|
||||||
implementation "androidx.room:room-runtime:$room_version"
|
implementation "androidx.room:room-runtime:$room_version"
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
||||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC" />
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC" />
|
||||||
|
<uses-permission android:name="android.permission.CAMERA" />
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:allowBackup="false"
|
android:allowBackup="false"
|
||||||
|
|||||||
@@ -0,0 +1,88 @@
|
|||||||
|
package com.mattintech.simplelogupload.model;
|
||||||
|
|
||||||
|
import org.json.JSONException;
|
||||||
|
import org.json.JSONObject;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Model class for QR code server configuration
|
||||||
|
*/
|
||||||
|
public class QrServerConfig {
|
||||||
|
private final String server;
|
||||||
|
private final int port;
|
||||||
|
private final String key;
|
||||||
|
|
||||||
|
private QrServerConfig(String server, int port, String key) {
|
||||||
|
this.server = server;
|
||||||
|
this.port = port;
|
||||||
|
this.key = key;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getServer() {
|
||||||
|
return server;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getPort() {
|
||||||
|
return port;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getKey() {
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse QR code JSON data
|
||||||
|
*
|
||||||
|
* @param qrData The QR code string data
|
||||||
|
* @return QrServerConfig object
|
||||||
|
* @throws QrConfigException if parsing fails or data is invalid
|
||||||
|
*/
|
||||||
|
public static QrServerConfig fromQrData(String qrData) throws QrConfigException {
|
||||||
|
if (qrData == null || qrData.trim().isEmpty()) {
|
||||||
|
throw new QrConfigException("QR code data is empty");
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
JSONObject json = new JSONObject(qrData);
|
||||||
|
|
||||||
|
// Extract fields
|
||||||
|
if (!json.has("server")) {
|
||||||
|
throw new QrConfigException("Missing 'server' field in QR code");
|
||||||
|
}
|
||||||
|
if (!json.has("port")) {
|
||||||
|
throw new QrConfigException("Missing 'port' field in QR code");
|
||||||
|
}
|
||||||
|
if (!json.has("key")) {
|
||||||
|
throw new QrConfigException("Missing 'key' field in QR code");
|
||||||
|
}
|
||||||
|
|
||||||
|
String server = json.getString("server");
|
||||||
|
int port = json.getInt("port");
|
||||||
|
String key = json.getString("key");
|
||||||
|
|
||||||
|
// Validate
|
||||||
|
if (server.trim().isEmpty()) {
|
||||||
|
throw new QrConfigException("Server address cannot be empty");
|
||||||
|
}
|
||||||
|
if (port <= 0 || port > 65535) {
|
||||||
|
throw new QrConfigException("Port must be between 1 and 65535");
|
||||||
|
}
|
||||||
|
if (key.trim().isEmpty()) {
|
||||||
|
throw new QrConfigException("Client key cannot be empty");
|
||||||
|
}
|
||||||
|
|
||||||
|
return new QrServerConfig(server.trim(), port, key.trim());
|
||||||
|
|
||||||
|
} catch (JSONException e) {
|
||||||
|
throw new QrConfigException("Invalid QR code format: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Custom exception for QR configuration errors
|
||||||
|
*/
|
||||||
|
public static class QrConfigException extends Exception {
|
||||||
|
public QrConfigException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -24,7 +24,8 @@ public class PermissionUtil {
|
|||||||
|
|
||||||
public static final int REQUEST_MANAGE_EXTERNAL_STORAGE = 101;
|
public static final int REQUEST_MANAGE_EXTERNAL_STORAGE = 101;
|
||||||
public static final int REQUEST_NOTIFICATION_PERMISSION = 102;
|
public static final int REQUEST_NOTIFICATION_PERMISSION = 102;
|
||||||
|
public static final int REQUEST_CAMERA_PERMISSION = 103;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if the app has notification permission
|
* Checks if the app has notification permission
|
||||||
*
|
*
|
||||||
@@ -93,4 +94,31 @@ public class PermissionUtil {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the app has camera permission
|
||||||
|
*
|
||||||
|
* @param context The context
|
||||||
|
* @return true if the app has camera permission, false otherwise
|
||||||
|
*/
|
||||||
|
public static boolean hasCameraPermission(Context context) {
|
||||||
|
return ContextCompat.checkSelfPermission(context, Manifest.permission.CAMERA)
|
||||||
|
== PackageManager.PERMISSION_GRANTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Requests camera permission
|
||||||
|
*
|
||||||
|
* @param activity The activity
|
||||||
|
*/
|
||||||
|
public static void requestCameraPermission(Activity activity) {
|
||||||
|
if (!hasCameraPermission(activity)) {
|
||||||
|
Log.d(TAG, "Requesting camera permission");
|
||||||
|
ActivityCompat.requestPermissions(
|
||||||
|
activity,
|
||||||
|
new String[]{Manifest.permission.CAMERA},
|
||||||
|
REQUEST_CAMERA_PERMISSION
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,54 @@
|
|||||||
|
package com.mattintech.simplelogupload.util;
|
||||||
|
|
||||||
|
import com.journeyapps.barcodescanner.ScanOptions;
|
||||||
|
import com.mattintech.simplelogupload.model.QrServerConfig;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility class for QR code scanning
|
||||||
|
*/
|
||||||
|
public class QrScannerUtil {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback interface for QR scan results
|
||||||
|
*/
|
||||||
|
public interface QrScanCallback {
|
||||||
|
void onQrScanned(QrServerConfig config);
|
||||||
|
void onQrScanCancelled();
|
||||||
|
void onQrScanError(String errorMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create scan options for QR code scanning
|
||||||
|
*
|
||||||
|
* @return ScanOptions configured for QR codes
|
||||||
|
*/
|
||||||
|
public static ScanOptions createScanOptions() {
|
||||||
|
ScanOptions options = new ScanOptions();
|
||||||
|
options.setDesiredBarcodeFormats(ScanOptions.QR_CODE);
|
||||||
|
options.setPrompt("Scan server configuration QR code");
|
||||||
|
options.setBeepEnabled(true);
|
||||||
|
options.setBarcodeImageEnabled(false);
|
||||||
|
options.setOrientationLocked(true);
|
||||||
|
return options;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Process scan result
|
||||||
|
*
|
||||||
|
* @param contents The scanned QR code contents
|
||||||
|
* @param callback The callback to handle the result
|
||||||
|
*/
|
||||||
|
public static void processScanResult(String contents, QrScanCallback callback) {
|
||||||
|
if (contents == null) {
|
||||||
|
callback.onQrScanCancelled();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
QrServerConfig config = QrServerConfig.fromQrData(contents);
|
||||||
|
callback.onQrScanned(config);
|
||||||
|
} catch (QrServerConfig.QrConfigException e) {
|
||||||
|
callback.onQrScanError(e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,28 +1,38 @@
|
|||||||
package com.mattintech.simplelogupload.view;
|
package com.mattintech.simplelogupload.view;
|
||||||
|
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.widget.Button;
|
import android.widget.Button;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import androidx.activity.result.ActivityResultLauncher;
|
||||||
import androidx.appcompat.app.AppCompatActivity;
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
|
|
||||||
import com.google.android.material.textfield.TextInputEditText;
|
import com.google.android.material.textfield.TextInputEditText;
|
||||||
|
import com.journeyapps.barcodescanner.ScanContract;
|
||||||
|
import com.journeyapps.barcodescanner.ScanOptions;
|
||||||
|
import com.journeyapps.barcodescanner.ScanIntentResult;
|
||||||
import com.mattintech.simplelogupload.R;
|
import com.mattintech.simplelogupload.R;
|
||||||
import com.mattintech.simplelogupload.constant.AppConstants;
|
import com.mattintech.simplelogupload.constant.AppConstants;
|
||||||
|
import com.mattintech.simplelogupload.model.QrServerConfig;
|
||||||
|
import com.mattintech.simplelogupload.util.PermissionUtil;
|
||||||
import com.mattintech.simplelogupload.util.PreferencesUtil;
|
import com.mattintech.simplelogupload.util.PreferencesUtil;
|
||||||
|
import com.mattintech.simplelogupload.util.QrScannerUtil;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Activity for server setup
|
* Activity for server setup
|
||||||
*/
|
*/
|
||||||
public class ServerSetupActivity extends AppCompatActivity {
|
public class ServerSetupActivity extends AppCompatActivity {
|
||||||
private static final String TAG = AppConstants.APP_TAG + "ServerSetupActivity";
|
private static final String TAG = AppConstants.APP_TAG + "ServerSetupActivity";
|
||||||
|
|
||||||
private TextInputEditText serverAddressInput;
|
private TextInputEditText serverAddressInput;
|
||||||
private TextInputEditText serverPortInput;
|
private TextInputEditText serverPortInput;
|
||||||
private TextInputEditText clientKeyInput;
|
private TextInputEditText clientKeyInput;
|
||||||
|
private ActivityResultLauncher<ScanOptions> qrScannerLauncher;
|
||||||
|
private Button scanQrButton;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
@@ -34,7 +44,17 @@ public class ServerSetupActivity extends AppCompatActivity {
|
|||||||
serverPortInput = findViewById(R.id.serverPortInput);
|
serverPortInput = findViewById(R.id.serverPortInput);
|
||||||
clientKeyInput = findViewById(R.id.clientKeyInput);
|
clientKeyInput = findViewById(R.id.clientKeyInput);
|
||||||
Button finishSetupButton = findViewById(R.id.finishSetupButton);
|
Button finishSetupButton = findViewById(R.id.finishSetupButton);
|
||||||
|
scanQrButton = findViewById(R.id.scanQrButton);
|
||||||
|
|
||||||
|
// Initialize QR scanner launcher
|
||||||
|
qrScannerLauncher = registerForActivityResult(
|
||||||
|
new ScanContract(),
|
||||||
|
result -> handleQrScanResult(result)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Set up QR scan button click listener
|
||||||
|
scanQrButton.setOnClickListener(v -> initiateQrScan());
|
||||||
|
|
||||||
// Set up action bar
|
// Set up action bar
|
||||||
if (getSupportActionBar() != null) {
|
if (getSupportActionBar() != null) {
|
||||||
getSupportActionBar().setTitle("Server Setup");
|
getSupportActionBar().setTitle("Server Setup");
|
||||||
@@ -103,7 +123,81 @@ public class ServerSetupActivity extends AppCompatActivity {
|
|||||||
Toast.makeText(this, "Error saving settings: " + e.getMessage(), Toast.LENGTH_LONG).show();
|
Toast.makeText(this, "Error saving settings: " + e.getMessage(), Toast.LENGTH_LONG).show();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initiate QR code scan
|
||||||
|
*/
|
||||||
|
private void initiateQrScan() {
|
||||||
|
// Check camera permission
|
||||||
|
if (!PermissionUtil.hasCameraPermission(this)) {
|
||||||
|
PermissionUtil.requestCameraPermission(this);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Launch scanner
|
||||||
|
ScanOptions options = QrScannerUtil.createScanOptions();
|
||||||
|
qrScannerLauncher.launch(options);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle QR scan result
|
||||||
|
*
|
||||||
|
* @param result The scan result
|
||||||
|
*/
|
||||||
|
private void handleQrScanResult(ScanIntentResult result) {
|
||||||
|
QrScannerUtil.processScanResult(
|
||||||
|
result.getContents(),
|
||||||
|
new QrScannerUtil.QrScanCallback() {
|
||||||
|
@Override
|
||||||
|
public void onQrScanned(QrServerConfig config) {
|
||||||
|
// Auto-fill the input fields
|
||||||
|
serverAddressInput.setText(config.getServer());
|
||||||
|
serverPortInput.setText(String.valueOf(config.getPort()));
|
||||||
|
clientKeyInput.setText(config.getKey());
|
||||||
|
|
||||||
|
Toast.makeText(ServerSetupActivity.this,
|
||||||
|
R.string.qr_scan_success,
|
||||||
|
Toast.LENGTH_SHORT).show();
|
||||||
|
|
||||||
|
Log.d(TAG, "QR code scanned successfully");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onQrScanCancelled() {
|
||||||
|
Toast.makeText(ServerSetupActivity.this,
|
||||||
|
R.string.qr_scan_cancelled,
|
||||||
|
Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onQrScanError(String errorMessage) {
|
||||||
|
Toast.makeText(ServerSetupActivity.this,
|
||||||
|
getString(R.string.qr_scan_error, errorMessage),
|
||||||
|
Toast.LENGTH_LONG).show();
|
||||||
|
Log.e(TAG, "QR scan error: " + errorMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle permission request results
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
|
||||||
|
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
|
||||||
|
|
||||||
|
if (requestCode == PermissionUtil.REQUEST_CAMERA_PERMISSION) {
|
||||||
|
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
||||||
|
// Permission granted, initiate scan
|
||||||
|
initiateQrScan();
|
||||||
|
} else {
|
||||||
|
// Permission denied
|
||||||
|
Toast.makeText(this, R.string.camera_permission_denied, Toast.LENGTH_LONG).show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onSupportNavigateUp() {
|
public boolean onSupportNavigateUp() {
|
||||||
onBackPressed();
|
onBackPressed();
|
||||||
|
|||||||
@@ -1,28 +1,38 @@
|
|||||||
package com.mattintech.simplelogupload.view;
|
package com.mattintech.simplelogupload.view;
|
||||||
|
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.widget.Button;
|
import android.widget.Button;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import androidx.activity.result.ActivityResultLauncher;
|
||||||
import androidx.appcompat.app.AppCompatActivity;
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
|
|
||||||
import com.google.android.material.textfield.TextInputEditText;
|
import com.google.android.material.textfield.TextInputEditText;
|
||||||
|
import com.journeyapps.barcodescanner.ScanContract;
|
||||||
|
import com.journeyapps.barcodescanner.ScanOptions;
|
||||||
|
import com.journeyapps.barcodescanner.ScanIntentResult;
|
||||||
import com.mattintech.simplelogupload.R;
|
import com.mattintech.simplelogupload.R;
|
||||||
import com.mattintech.simplelogupload.constant.AppConstants;
|
import com.mattintech.simplelogupload.constant.AppConstants;
|
||||||
|
import com.mattintech.simplelogupload.model.QrServerConfig;
|
||||||
|
import com.mattintech.simplelogupload.util.PermissionUtil;
|
||||||
import com.mattintech.simplelogupload.util.PreferencesUtil;
|
import com.mattintech.simplelogupload.util.PreferencesUtil;
|
||||||
|
import com.mattintech.simplelogupload.util.QrScannerUtil;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Activity for application settings
|
* Activity for application settings
|
||||||
*/
|
*/
|
||||||
public class SettingsActivity extends AppCompatActivity {
|
public class SettingsActivity extends AppCompatActivity {
|
||||||
private static final String TAG = AppConstants.APP_TAG + "SettingsActivity";
|
private static final String TAG = AppConstants.APP_TAG + "SettingsActivity";
|
||||||
|
|
||||||
private TextInputEditText serverAddressInput;
|
private TextInputEditText serverAddressInput;
|
||||||
private TextInputEditText serverPortInput;
|
private TextInputEditText serverPortInput;
|
||||||
private TextInputEditText clientKeyInput;
|
private TextInputEditText clientKeyInput;
|
||||||
|
private ActivityResultLauncher<ScanOptions> qrScannerLauncher;
|
||||||
|
private Button scanQrButton;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
@@ -34,7 +44,17 @@ public class SettingsActivity extends AppCompatActivity {
|
|||||||
clientKeyInput = findViewById(R.id.clientKeyInput);
|
clientKeyInput = findViewById(R.id.clientKeyInput);
|
||||||
Button saveButton = findViewById(R.id.saveSettingsButton);
|
Button saveButton = findViewById(R.id.saveSettingsButton);
|
||||||
Button resetButton = findViewById(R.id.resetSettingsButton);
|
Button resetButton = findViewById(R.id.resetSettingsButton);
|
||||||
|
scanQrButton = findViewById(R.id.scanQrButton);
|
||||||
|
|
||||||
|
// Initialize QR scanner launcher
|
||||||
|
qrScannerLauncher = registerForActivityResult(
|
||||||
|
new ScanContract(),
|
||||||
|
result -> handleQrScanResult(result)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Set up QR scan button click listener
|
||||||
|
scanQrButton.setOnClickListener(v -> initiateQrScan());
|
||||||
|
|
||||||
// Set up action bar
|
// Set up action bar
|
||||||
if (getSupportActionBar() != null) {
|
if (getSupportActionBar() != null) {
|
||||||
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||||
@@ -127,7 +147,81 @@ public class SettingsActivity extends AppCompatActivity {
|
|||||||
loadSettings();
|
loadSettings();
|
||||||
Toast.makeText(this, "Settings reset to defaults", Toast.LENGTH_SHORT).show();
|
Toast.makeText(this, "Settings reset to defaults", Toast.LENGTH_SHORT).show();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initiate QR code scan
|
||||||
|
*/
|
||||||
|
private void initiateQrScan() {
|
||||||
|
// Check camera permission
|
||||||
|
if (!PermissionUtil.hasCameraPermission(this)) {
|
||||||
|
PermissionUtil.requestCameraPermission(this);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Launch scanner
|
||||||
|
ScanOptions options = QrScannerUtil.createScanOptions();
|
||||||
|
qrScannerLauncher.launch(options);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle QR scan result
|
||||||
|
*
|
||||||
|
* @param result The scan result
|
||||||
|
*/
|
||||||
|
private void handleQrScanResult(ScanIntentResult result) {
|
||||||
|
QrScannerUtil.processScanResult(
|
||||||
|
result.getContents(),
|
||||||
|
new QrScannerUtil.QrScanCallback() {
|
||||||
|
@Override
|
||||||
|
public void onQrScanned(QrServerConfig config) {
|
||||||
|
// Auto-fill the input fields
|
||||||
|
serverAddressInput.setText(config.getServer());
|
||||||
|
serverPortInput.setText(String.valueOf(config.getPort()));
|
||||||
|
clientKeyInput.setText(config.getKey());
|
||||||
|
|
||||||
|
Toast.makeText(SettingsActivity.this,
|
||||||
|
R.string.qr_scan_success,
|
||||||
|
Toast.LENGTH_SHORT).show();
|
||||||
|
|
||||||
|
Log.d(TAG, "QR code scanned successfully");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onQrScanCancelled() {
|
||||||
|
Toast.makeText(SettingsActivity.this,
|
||||||
|
R.string.qr_scan_cancelled,
|
||||||
|
Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onQrScanError(String errorMessage) {
|
||||||
|
Toast.makeText(SettingsActivity.this,
|
||||||
|
getString(R.string.qr_scan_error, errorMessage),
|
||||||
|
Toast.LENGTH_LONG).show();
|
||||||
|
Log.e(TAG, "QR scan error: " + errorMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle permission request results
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
|
||||||
|
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
|
||||||
|
|
||||||
|
if (requestCode == PermissionUtil.REQUEST_CAMERA_PERMISSION) {
|
||||||
|
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
||||||
|
// Permission granted, initiate scan
|
||||||
|
initiateQrScan();
|
||||||
|
} else {
|
||||||
|
// Permission denied
|
||||||
|
Toast.makeText(this, R.string.camera_permission_denied, Toast.LENGTH_LONG).show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onSupportNavigateUp() {
|
public boolean onSupportNavigateUp() {
|
||||||
onBackPressed();
|
onBackPressed();
|
||||||
|
|||||||
39
app/src/main/res/drawable/ic_qr_scan.xml
Normal file
39
app/src/main/res/drawable/ic_qr_scan.xml
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M3,11h8V3H3V11zM5,5h4v4H5V5z"/>
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M3,21h8v-8H3V21zM5,15h4v4H5V15z"/>
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M13,3v8h8V3H13zM19,9h-4V5h4V9z"/>
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M19,19h2v2h-2z"/>
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M13,13h2v2h-2z"/>
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M15,15h2v2h-2z"/>
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M13,17h2v2h-2z"/>
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M15,19h2v2h-2z"/>
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M17,17h2v2h-2z"/>
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M17,13h2v2h-2z"/>
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M19,15h2v2h-2z"/>
|
||||||
|
</vector>
|
||||||
@@ -88,6 +88,27 @@
|
|||||||
|
|
||||||
</com.google.android.material.textfield.TextInputLayout>
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
|
|
||||||
|
<!-- QR Code Scan Button -->
|
||||||
|
<Button
|
||||||
|
android:id="@+id/scanQrButton"
|
||||||
|
style="?attr/materialButtonOutlinedStyle"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="24dp"
|
||||||
|
android:text="@string/scan_qr_code_button"
|
||||||
|
android:drawableLeft="@drawable/ic_qr_scan"
|
||||||
|
android:drawablePadding="8dp"
|
||||||
|
android:paddingVertical="12dp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:text="@string/scan_qr_code_hint"
|
||||||
|
android:textAlignment="center"
|
||||||
|
android:textSize="12sp"
|
||||||
|
android:textStyle="italic" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/testConnectionInfo"
|
android:id="@+id/testConnectionInfo"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
|||||||
@@ -67,6 +67,18 @@
|
|||||||
android:inputType="textPassword" />
|
android:inputType="textPassword" />
|
||||||
</com.google.android.material.textfield.TextInputLayout>
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
|
|
||||||
|
<!-- QR Code Scan Button -->
|
||||||
|
<Button
|
||||||
|
android:id="@+id/scanQrButton"
|
||||||
|
style="?attr/materialButtonOutlinedStyle"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:layout_marginBottom="8dp"
|
||||||
|
android:text="@string/scan_qr_code_button"
|
||||||
|
android:drawableLeft="@drawable/ic_qr_scan"
|
||||||
|
android:drawablePadding="8dp" />
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
android:id="@+id/saveSettingsButton"
|
android:id="@+id/saveSettingsButton"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
|||||||
@@ -67,4 +67,13 @@
|
|||||||
<string name="expiry_time_note">Maximum 24 hours (1 day)</string>
|
<string name="expiry_time_note">Maximum 24 hours (1 day)</string>
|
||||||
<string name="cancel_button">Cancel</string>
|
<string name="cancel_button">Cancel</string>
|
||||||
<string name="upload_button">Upload</string>
|
<string name="upload_button">Upload</string>
|
||||||
|
|
||||||
|
<!-- QR Code Scanning -->
|
||||||
|
<string name="scan_qr_code_button">Scan QR Code</string>
|
||||||
|
<string name="scan_qr_code_hint">Scan a QR code from your server admin panel to auto-fill settings</string>
|
||||||
|
<string name="qr_scan_success">Configuration loaded from QR code</string>
|
||||||
|
<string name="qr_scan_cancelled">QR code scan cancelled</string>
|
||||||
|
<string name="qr_scan_error">QR code error: %1$s</string>
|
||||||
|
<string name="camera_permission_required">Camera permission is required to scan QR codes</string>
|
||||||
|
<string name="camera_permission_denied">Camera permission denied. Please enable it in Settings to scan QR codes.</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -1,6 +1,11 @@
|
|||||||
android.suppressUnsupportedCompileSdk=36
|
android.suppressUnsupportedCompileSdk=36
|
||||||
android.useAndroidX=true
|
android.useAndroidX=true
|
||||||
|
|
||||||
|
# Enable Gradle toolchain auto-detection and auto-download
|
||||||
|
org.gradle.java.installations.auto-detect=true
|
||||||
|
org.gradle.java.installations.auto-download=true
|
||||||
|
org.gradle.java.installations.paths=/opt/homebrew/opt/openjdk@17/libexec/openjdk.jdk/Contents/Home
|
||||||
|
|
||||||
# JVM arguments for kapt with JDK 9+
|
# JVM arguments for kapt with JDK 9+
|
||||||
org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 \
|
org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 \
|
||||||
--add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED \
|
--add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED \
|
||||||
|
|||||||
Reference in New Issue
Block a user