diff --git a/android/app/build.gradle.kts b/android/app/build.gradle.kts
index b704371..f263269 100644
--- a/android/app/build.gradle.kts
+++ b/android/app/build.gradle.kts
@@ -6,7 +6,7 @@ plugins {
}
android {
- namespace = "xyz.dailitation.linesofcodes.liteauthconfig.liteauthconfig"
+ namespace = "xyz.dailitation.linesofcodes.liteauthconfig"
compileSdk = flutter.compileSdkVersion
ndkVersion = flutter.ndkVersion
@@ -15,13 +15,15 @@ android {
targetCompatibility = JavaVersion.VERSION_11
}
- kotlinOptions {
- jvmTarget = JavaVersion.VERSION_11.toString()
+ kotlin {
+ compilerOptions {
+ jvmTarget = org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_11
+ }
}
defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
- applicationId = "xyz.dailitation.linesofcodes.liteauthconfig.liteauthconfig"
+ applicationId = "xyz.dailitation.linesofcodes.liteauthconfig"
// You can update the following values to match your application needs.
// For more information, see: https://flutter.dev/to/review-gradle-config.
minSdk = flutter.minSdkVersion
diff --git a/android/app/src/main/kotlin/xyz/dailitation/linesofcodes/liteauthconfig/liteauthconfig/MainActivity.kt b/android/app/src/main/kotlin/xyz/dailitation/linesofcodes/liteauthconfig/MainActivity.kt
similarity index 58%
rename from android/app/src/main/kotlin/xyz/dailitation/linesofcodes/liteauthconfig/liteauthconfig/MainActivity.kt
rename to android/app/src/main/kotlin/xyz/dailitation/linesofcodes/liteauthconfig/MainActivity.kt
index 9a76ed2..411fb3b 100644
--- a/android/app/src/main/kotlin/xyz/dailitation/linesofcodes/liteauthconfig/liteauthconfig/MainActivity.kt
+++ b/android/app/src/main/kotlin/xyz/dailitation/linesofcodes/liteauthconfig/MainActivity.kt
@@ -1,4 +1,4 @@
-package xyz.dailitation.linesofcodes.liteauthconfig.liteauthconfig
+package xyz.dailitation.linesofcodes.liteauthconfig
import io.flutter.embedding.android.FlutterActivity
diff --git a/android/build/reports/problems/problems-report.html b/android/build/reports/problems/problems-report.html
new file mode 100644
index 0000000..c0feb4e
--- /dev/null
+++ b/android/build/reports/problems/problems-report.html
@@ -0,0 +1,663 @@
+
+
+
+
+
+
+
+
+
+
+
+
+ Gradle Configuration Cache
+
+
+
+
+
+
+ Loading...
+
+
+
+
+
+
+
diff --git a/android/settings.gradle.kts b/android/settings.gradle.kts
index 93d6cd2..68fe453 100644
--- a/android/settings.gradle.kts
+++ b/android/settings.gradle.kts
@@ -19,8 +19,8 @@ pluginManagement {
plugins {
id("dev.flutter.flutter-plugin-loader") version "1.0.0"
- id("com.android.application") version "8.13.0" apply false
- id("org.jetbrains.kotlin.android") version "2.2.21" apply false
+ id("com.android.application") version "8.13.2" apply false
+ id("org.jetbrains.kotlin.android") version "2.3.0" apply false
}
include(":app")
diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj
index 9a1256a..94b623e 100644
--- a/ios/Runner.xcodeproj/project.pbxproj
+++ b/ios/Runner.xcodeproj/project.pbxproj
@@ -368,7 +368,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
- PRODUCT_BUNDLE_IDENTIFIER = xyz.dailitation.linesofcodes.liteauthconfig.liteauthconfig;
+ PRODUCT_BUNDLE_IDENTIFIER = xyz.dailitation.linesofcodes.liteauthconfig;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;
@@ -384,7 +384,7 @@
CURRENT_PROJECT_VERSION = 1;
GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0;
- PRODUCT_BUNDLE_IDENTIFIER = xyz.dailitation.linesofcodes.liteauthconfig.liteauthconfig.RunnerTests;
+ PRODUCT_BUNDLE_IDENTIFIER = xyz.dailitation.linesofcodes.liteauthconfig.RunnerTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
@@ -401,7 +401,7 @@
CURRENT_PROJECT_VERSION = 1;
GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0;
- PRODUCT_BUNDLE_IDENTIFIER = xyz.dailitation.linesofcodes.liteauthconfig.liteauthconfig.RunnerTests;
+ PRODUCT_BUNDLE_IDENTIFIER = xyz.dailitation.linesofcodes.liteauthconfig.RunnerTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
@@ -416,7 +416,7 @@
CURRENT_PROJECT_VERSION = 1;
GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0;
- PRODUCT_BUNDLE_IDENTIFIER = xyz.dailitation.linesofcodes.liteauthconfig.liteauthconfig.RunnerTests;
+ PRODUCT_BUNDLE_IDENTIFIER = xyz.dailitation.linesofcodes.liteauthconfig.RunnerTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
@@ -547,7 +547,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
- PRODUCT_BUNDLE_IDENTIFIER = xyz.dailitation.linesofcodes.liteauthconfig.liteauthconfig;
+ PRODUCT_BUNDLE_IDENTIFIER = xyz.dailitation.linesofcodes.liteauthconfig;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
@@ -569,7 +569,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
- PRODUCT_BUNDLE_IDENTIFIER = xyz.dailitation.linesofcodes.liteauthconfig.liteauthconfig;
+ PRODUCT_BUNDLE_IDENTIFIER = xyz.dailitation.linesofcodes.liteauthconfig;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;
diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist
index 68e84e0..3b7e3b8 100644
--- a/ios/Runner/Info.plist
+++ b/ios/Runner/Info.plist
@@ -47,5 +47,8 @@
NFCReaderUsageDescription
Interacting with the liteauth device
+
+ NSCameraUsageDescription
+ This app needs camera access to scan QR codes
diff --git a/lib/dialogs/esptouchdialog.dart b/lib/dialogs/esptouchdialog.dart
index e92c2c3..7b8d5e4 100644
--- a/lib/dialogs/esptouchdialog.dart
+++ b/lib/dialogs/esptouchdialog.dart
@@ -43,6 +43,8 @@ class _ESPTouchDialog extends State {
final touchStream = task.execute();
final sub = touchStream.listen((data) async {
+ var conf = AppConfig();
+
final dev = Device(
name: widget.name,
routerSsid: widget.ssid,
@@ -50,11 +52,14 @@ class _ESPTouchDialog extends State {
networkPassword: widget.password,
);
+ if (widget.entryIndex != null) {
+ final oldDev = conf.devices[widget.entryIndex!];
+ dev.token = oldDev.token;
+ }
+
dev.ip = data.ip;
dev.bssid = data.bssid;
- var conf = AppConfig();
-
if (widget.addNewEntry) {
conf.devices.add(dev);
} else {
diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb
index cdd504d..a4d13a1 100644
--- a/lib/l10n/app_en.arb
+++ b/lib/l10n/app_en.arb
@@ -29,13 +29,31 @@
"deletion": "Deletion",
"deviceDeleteConfirm": "Are you sure you want to delete this device forever?",
"reconfigureNetwork": "Reconfigure Network",
- "factoryReset": "Factory Reset",
- "resetConfirm": "Are you sure you want to proceed with the factory reset? Your data will be lost and unrecoverable, and you must setup the device again.",
- "reset": "Reset",
"status": "Status: ",
"online": "Online",
"offline": "Offline",
"refresh": "Refresh",
+ "cannotBeEmpty": "{field} cannot be empty!",
+ "@cannotBeEmpty": {
+ "placeholders": {
+ "field": {
+ "type": "String",
+ "example": "SSID"
+ }
+ }
+ },
+ "tokenForbidden": "Token cannot be requested. Please manually enter the token or physically factory reset the device.",
+ "requestFailedWithCode": "Request failed with code {code}",
+ "@requestFailedWithCode": {
+ "placeholders": {
+ "code": {
+ "type": "int",
+ "example": "400"
+ }
+ }
+ },
+ "additionalActions": "Additional Actions",
+ "dangerousActions": "Dangerous Actions",
"lastKnownIP": "Last Known IP: {ip}",
"@lastKnownIP": {
"placeholders": {
@@ -54,5 +72,29 @@
}
}
},
- "activityLogs": "Activity Logs"
+ "activityLogs": "Activity Logs",
+
+ "factoryReset": "Factory Reset",
+ "resetConfirm": "Are you sure you want to proceed with the factory reset? Your data will be lost and unrecoverable, and you must setup the device again.",
+ "reset": "Reset",
+ "resetSuccessful": "Reset Successful",
+ "resetFailedCode": "Reset failed with code {code}",
+ "@resetFailedCode": {
+ "placeholders": {
+ "code": {
+ "type": "int",
+ "example": "400"
+ }
+ }
+ },
+
+ "token": "Token",
+ "manageToken": "Manage Token",
+ "tokenNotFound": "Token not found. Please enter one or do a manual factory reset.",
+ "viewTokenWarn": "Tokens are sensitive. Please do not share this token publicly.",
+ "showToken": "Show Token",
+ "copyToken": "Copy Token",
+ "copied": "Copied",
+ "saveToken": "Save Token",
+ "saved": "Saved!"
}
\ No newline at end of file
diff --git a/lib/l10n/app_localizations.dart b/lib/l10n/app_localizations.dart
index a30a73c..df96840 100644
--- a/lib/l10n/app_localizations.dart
+++ b/lib/l10n/app_localizations.dart
@@ -260,24 +260,6 @@ abstract class AppLocalizations {
/// **'Reconfigure Network'**
String get reconfigureNetwork;
- /// No description provided for @factoryReset.
- ///
- /// In en, this message translates to:
- /// **'Factory Reset'**
- String get factoryReset;
-
- /// No description provided for @resetConfirm.
- ///
- /// In en, this message translates to:
- /// **'Are you sure you want to proceed with the factory reset? Your data will be lost and unrecoverable, and you must setup the device again.'**
- String get resetConfirm;
-
- /// No description provided for @reset.
- ///
- /// In en, this message translates to:
- /// **'Reset'**
- String get reset;
-
/// No description provided for @status.
///
/// In en, this message translates to:
@@ -302,6 +284,36 @@ abstract class AppLocalizations {
/// **'Refresh'**
String get refresh;
+ /// No description provided for @cannotBeEmpty.
+ ///
+ /// In en, this message translates to:
+ /// **'{field} cannot be empty!'**
+ String cannotBeEmpty(String field);
+
+ /// No description provided for @tokenForbidden.
+ ///
+ /// In en, this message translates to:
+ /// **'Token cannot be requested. Please manually enter the token or physically factory reset the device.'**
+ String get tokenForbidden;
+
+ /// No description provided for @requestFailedWithCode.
+ ///
+ /// In en, this message translates to:
+ /// **'Request failed with code {code}'**
+ String requestFailedWithCode(int code);
+
+ /// No description provided for @additionalActions.
+ ///
+ /// In en, this message translates to:
+ /// **'Additional Actions'**
+ String get additionalActions;
+
+ /// No description provided for @dangerousActions.
+ ///
+ /// In en, this message translates to:
+ /// **'Dangerous Actions'**
+ String get dangerousActions;
+
/// No description provided for @lastKnownIP.
///
/// In en, this message translates to:
@@ -319,6 +331,90 @@ abstract class AppLocalizations {
/// In en, this message translates to:
/// **'Activity Logs'**
String get activityLogs;
+
+ /// No description provided for @factoryReset.
+ ///
+ /// In en, this message translates to:
+ /// **'Factory Reset'**
+ String get factoryReset;
+
+ /// No description provided for @resetConfirm.
+ ///
+ /// In en, this message translates to:
+ /// **'Are you sure you want to proceed with the factory reset? Your data will be lost and unrecoverable, and you must setup the device again.'**
+ String get resetConfirm;
+
+ /// No description provided for @reset.
+ ///
+ /// In en, this message translates to:
+ /// **'Reset'**
+ String get reset;
+
+ /// No description provided for @resetSuccessful.
+ ///
+ /// In en, this message translates to:
+ /// **'Reset Successful'**
+ String get resetSuccessful;
+
+ /// No description provided for @resetFailedCode.
+ ///
+ /// In en, this message translates to:
+ /// **'Reset failed with code {code}'**
+ String resetFailedCode(int code);
+
+ /// No description provided for @token.
+ ///
+ /// In en, this message translates to:
+ /// **'Token'**
+ String get token;
+
+ /// No description provided for @manageToken.
+ ///
+ /// In en, this message translates to:
+ /// **'Manage Token'**
+ String get manageToken;
+
+ /// No description provided for @tokenNotFound.
+ ///
+ /// In en, this message translates to:
+ /// **'Token not found. Please enter one or do a manual factory reset.'**
+ String get tokenNotFound;
+
+ /// No description provided for @viewTokenWarn.
+ ///
+ /// In en, this message translates to:
+ /// **'Tokens are sensitive. Please do not share this token publicly.'**
+ String get viewTokenWarn;
+
+ /// No description provided for @showToken.
+ ///
+ /// In en, this message translates to:
+ /// **'Show Token'**
+ String get showToken;
+
+ /// No description provided for @copyToken.
+ ///
+ /// In en, this message translates to:
+ /// **'Copy Token'**
+ String get copyToken;
+
+ /// No description provided for @copied.
+ ///
+ /// In en, this message translates to:
+ /// **'Copied'**
+ String get copied;
+
+ /// No description provided for @saveToken.
+ ///
+ /// In en, this message translates to:
+ /// **'Save Token'**
+ String get saveToken;
+
+ /// No description provided for @saved.
+ ///
+ /// In en, this message translates to:
+ /// **'Saved!'**
+ String get saved;
}
class _AppLocalizationsDelegate
diff --git a/lib/l10n/app_localizations_en.dart b/lib/l10n/app_localizations_en.dart
index a498c6e..cb82f5b 100644
--- a/lib/l10n/app_localizations_en.dart
+++ b/lib/l10n/app_localizations_en.dart
@@ -96,16 +96,6 @@ class AppLocalizationsEn extends AppLocalizations {
@override
String get reconfigureNetwork => 'Reconfigure Network';
- @override
- String get factoryReset => 'Factory Reset';
-
- @override
- String get resetConfirm =>
- 'Are you sure you want to proceed with the factory reset? Your data will be lost and unrecoverable, and you must setup the device again.';
-
- @override
- String get reset => 'Reset';
-
@override
String get status => 'Status: ';
@@ -118,6 +108,26 @@ class AppLocalizationsEn extends AppLocalizations {
@override
String get refresh => 'Refresh';
+ @override
+ String cannotBeEmpty(String field) {
+ return '$field cannot be empty!';
+ }
+
+ @override
+ String get tokenForbidden =>
+ 'Token cannot be requested. Please manually enter the token or physically factory reset the device.';
+
+ @override
+ String requestFailedWithCode(int code) {
+ return 'Request failed with code $code';
+ }
+
+ @override
+ String get additionalActions => 'Additional Actions';
+
+ @override
+ String get dangerousActions => 'Dangerous Actions';
+
@override
String lastKnownIP(String ip) {
return 'Last Known IP: $ip';
@@ -130,4 +140,51 @@ class AppLocalizationsEn extends AppLocalizations {
@override
String get activityLogs => 'Activity Logs';
+
+ @override
+ String get factoryReset => 'Factory Reset';
+
+ @override
+ String get resetConfirm =>
+ 'Are you sure you want to proceed with the factory reset? Your data will be lost and unrecoverable, and you must setup the device again.';
+
+ @override
+ String get reset => 'Reset';
+
+ @override
+ String get resetSuccessful => 'Reset Successful';
+
+ @override
+ String resetFailedCode(int code) {
+ return 'Reset failed with code $code';
+ }
+
+ @override
+ String get token => 'Token';
+
+ @override
+ String get manageToken => 'Manage Token';
+
+ @override
+ String get tokenNotFound =>
+ 'Token not found. Please enter one or do a manual factory reset.';
+
+ @override
+ String get viewTokenWarn =>
+ 'Tokens are sensitive. Please do not share this token publicly.';
+
+ @override
+ String get showToken => 'Show Token';
+
+ @override
+ String get copyToken => 'Copy Token';
+
+ @override
+ String get copied => 'Copied';
+
+ @override
+ String get saveToken => 'Save Token';
+
+ @override
+ String get saved => 'Saved!';
}
diff --git a/lib/l10n/app_localizations_th.dart b/lib/l10n/app_localizations_th.dart
index 874d059..7f25708 100644
--- a/lib/l10n/app_localizations_th.dart
+++ b/lib/l10n/app_localizations_th.dart
@@ -95,16 +95,6 @@ class AppLocalizationsTh extends AppLocalizations {
@override
String get reconfigureNetwork => 'ตั้งค่าเครือข่ายใหม่';
- @override
- String get factoryReset => 'คืนค่าโรงงาน';
-
- @override
- String get resetConfirm =>
- 'คุณแน่ใจหรือไม่ว่าต้องการคืนค่าจากโรงงาน ข้อมูลของคุณจะหายไปและไม่สามารถกู้คืนได้ และคุณจำเป็นต้องตั้งค่าอุปกรณ์ใหม่';
-
- @override
- String get reset => 'คืนค่า';
-
@override
String get status => 'สถานะ: ';
@@ -117,6 +107,26 @@ class AppLocalizationsTh extends AppLocalizations {
@override
String get refresh => 'รีเฟรช';
+ @override
+ String cannotBeEmpty(String field) {
+ return '$field ไม่สามารถเป็นค่าว่างได้!';
+ }
+
+ @override
+ String get tokenForbidden =>
+ 'ไม่สามารถรับค่าโทเค็นได้ โปรดใส่โทเค็นด้วยตนเองหรือคืนค่าโรงงานอุปกรณ์';
+
+ @override
+ String requestFailedWithCode(int code) {
+ return 'คำขอล้มเหลวด้วยรหัส $code';
+ }
+
+ @override
+ String get additionalActions => 'การกระทำเพิ่มเติม';
+
+ @override
+ String get dangerousActions => 'การกระทำอันตราย';
+
@override
String lastKnownIP(String ip) {
return 'ที่อยู่ IP ที่ทราบล่าสุด: $ip';
@@ -129,4 +139,51 @@ class AppLocalizationsTh extends AppLocalizations {
@override
String get activityLogs => 'รายการกิจกรรม';
+
+ @override
+ String get factoryReset => 'คืนค่าโรงงาน';
+
+ @override
+ String get resetConfirm =>
+ 'คุณแน่ใจหรือไม่ว่าต้องการคืนค่าจากโรงงาน ข้อมูลของคุณจะหายไปและไม่สามารถกู้คืนได้ และคุณจำเป็นต้องตั้งค่าอุปกรณ์ใหม่';
+
+ @override
+ String get reset => 'คืนค่า';
+
+ @override
+ String get resetSuccessful => 'คืนค่าสำเร็จ';
+
+ @override
+ String resetFailedCode(int code) {
+ return 'การคืนค่าล้มเหลวด้วยรหัส $code';
+ }
+
+ @override
+ String get token => 'โทเค็น';
+
+ @override
+ String get manageToken => 'จัดการโทเค็น';
+
+ @override
+ String get tokenNotFound =>
+ 'ไม่พบโทเค็น โปรดใส่โทเค็นหรือทำการคืนค่าโรงงานด้วยตนเอง';
+
+ @override
+ String get viewTokenWarn =>
+ 'โทเค็นเป็นข้อมูลละเอียดอ่อน โปรดอย่าเผยแพร่โทเค็นของคุณ';
+
+ @override
+ String get showToken => 'แสดงโทเค็น';
+
+ @override
+ String get copyToken => 'คัดลอกโทเค็น';
+
+ @override
+ String get copied => 'คัดลอกแล้ว';
+
+ @override
+ String get saveToken => 'บันทึกโทเค็น';
+
+ @override
+ String get saved => 'บันทึกแล้ว';
}
diff --git a/lib/l10n/app_th.arb b/lib/l10n/app_th.arb
index 71a9c00..d7ca26c 100644
--- a/lib/l10n/app_th.arb
+++ b/lib/l10n/app_th.arb
@@ -29,14 +29,32 @@
"deletion": "การลบ",
"deviceDeleteConfirm": "คุณแน่ใจหรือไม่ที่จะลบอุปกรณ์นี้อย่างถาวร",
"reconfigureNetwork": "ตั้งค่าเครือข่ายใหม่",
- "factoryReset": "คืนค่าโรงงาน",
- "resetConfirm": "คุณแน่ใจหรือไม่ว่าต้องการคืนค่าจากโรงงาน ข้อมูลของคุณจะหายไปและไม่สามารถกู้คืนได้ และคุณจำเป็นต้องตั้งค่าอุปกรณ์ใหม่",
- "reset": "คืนค่า",
"status": "สถานะ: ",
"online": "ออนไลน์",
"offline": "ออฟไลน์",
"refresh": "รีเฟรช",
+ "cannotBeEmpty": "{field} ไม่สามารถเป็นค่าว่างได้!",
+ "tokenForbidden": "ไม่สามารถรับค่าโทเค็นได้ โปรดใส่โทเค็นด้วยตนเองหรือคืนค่าโรงงานอุปกรณ์",
+ "requestFailedWithCode": "คำขอล้มเหลวด้วยรหัส {code}",
+ "additionalActions": "การกระทำเพิ่มเติม",
+ "dangerousActions": "การกระทำอันตราย",
"lastKnownIP": "ที่อยู่ IP ที่ทราบล่าสุด: {ip}",
"routerSsid": "SSID เราเตอร์: {ssid}",
- "activityLogs": "รายการกิจกรรม"
+ "activityLogs": "รายการกิจกรรม",
+
+ "factoryReset": "คืนค่าโรงงาน",
+ "resetConfirm": "คุณแน่ใจหรือไม่ว่าต้องการคืนค่าจากโรงงาน ข้อมูลของคุณจะหายไปและไม่สามารถกู้คืนได้ และคุณจำเป็นต้องตั้งค่าอุปกรณ์ใหม่",
+ "reset": "คืนค่า",
+ "resetSuccessful": "คืนค่าสำเร็จ",
+ "resetFailedCode": "การคืนค่าล้มเหลวด้วยรหัส {code}",
+
+ "token": "โทเค็น",
+ "manageToken": "จัดการโทเค็น",
+ "tokenNotFound": "ไม่พบโทเค็น โปรดใส่โทเค็นหรือทำการคืนค่าโรงงานด้วยตนเอง",
+ "viewTokenWarn": "โทเค็นเป็นข้อมูลละเอียดอ่อน โปรดอย่าเผยแพร่โทเค็นของคุณ",
+ "showToken": "แสดงโทเค็น",
+ "copyToken": "คัดลอกโทเค็น",
+ "copied": "คัดลอกแล้ว",
+ "saveToken": "บันทึกโทเค็น",
+ "saved": "บันทึกแล้ว"
}
\ No newline at end of file
diff --git a/lib/main.dart b/lib/main.dart
index 070651c..563d35d 100644
--- a/lib/main.dart
+++ b/lib/main.dart
@@ -49,7 +49,6 @@ class MyApp extends StatelessWidget {
title: 'liteauthconfig',
localizationsDelegates: AppLocalizations.localizationsDelegates,
supportedLocales: AppLocalizations.supportedLocales,
- locale: Locale("th"),
theme: ThemeData.light().copyWith(
colorScheme: ColorScheme.fromSeed(
seedColor: SystemTheme.accentColor.accent,
@@ -65,6 +64,7 @@ class MyApp extends StatelessWidget {
textColor: Colors.white,
iconColor: Colors.white,
),
+ bottomSheetTheme: BottomSheetThemeData(backgroundColor: darkColors.inverseSurface)
),
home: DeviceListPage(),
);
diff --git a/lib/models/appconfig.dart b/lib/models/appconfig.dart
index e403b03..875bebe 100644
--- a/lib/models/appconfig.dart
+++ b/lib/models/appconfig.dart
@@ -40,6 +40,7 @@ class Device {
String routerSsid;
String routerBssid;
String networkPassword;
+ String token;
String? ip;
String? bssid;
@@ -47,6 +48,7 @@ class Device {
required this.name,
required this.routerSsid,
required this.routerBssid,
+ this.token = "",
this.networkPassword = "",
});
diff --git a/lib/models/appconfig.g.dart b/lib/models/appconfig.g.dart
index d819495..aa87bd5 100644
--- a/lib/models/appconfig.g.dart
+++ b/lib/models/appconfig.g.dart
@@ -20,6 +20,7 @@ Device _$DeviceFromJson(Map json) =>
name: json['name'] as String,
routerSsid: json['routerSsid'] as String,
routerBssid: json['routerBssid'] as String,
+ token: json['token'] as String? ?? "",
networkPassword: json['networkPassword'] as String? ?? "",
)
..ip = json['ip'] as String?
@@ -30,6 +31,7 @@ Map _$DeviceToJson(Device instance) => {
'routerSsid': instance.routerSsid,
'routerBssid': instance.routerBssid,
'networkPassword': instance.networkPassword,
+ 'token': instance.token,
'ip': instance.ip,
'bssid': instance.bssid,
};
diff --git a/lib/models/device_status.g.dart b/lib/models/device_status.g.dart
new file mode 100644
index 0000000..b1d5bde
--- /dev/null
+++ b/lib/models/device_status.g.dart
@@ -0,0 +1,18 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+part of 'device_status.dart';
+
+// **************************************************************************
+// JsonSerializableGenerator
+// **************************************************************************
+
+DeviceStatus _$DeviceStatusFromJson(Map json) => DeviceStatus(
+ entryNfc: json['entryNfc'] as bool? ?? false,
+ exitNfc: json['exitNfc'] as bool? ?? false,
+);
+
+Map _$DeviceStatusToJson(DeviceStatus instance) =>
+ {
+ 'entryNfc': instance.entryNfc,
+ 'exitNfc': instance.exitNfc,
+ };
diff --git a/lib/models/logentry.dart b/lib/models/logentry.dart
new file mode 100644
index 0000000..dbde823
--- /dev/null
+++ b/lib/models/logentry.dart
@@ -0,0 +1,16 @@
+import 'package:json_annotation/json_annotation.dart';
+
+part 'logentry.g.dart';
+
+@JsonSerializable()
+class LogEntry {
+ DateTime time;
+ String uid;
+
+ LogEntry({required this.time, required this.uid});
+
+ factory LogEntry.fromJson(Map json) =>
+ _$LogEntryFromJson(json);
+
+ Map toJson() => _$LogEntryToJson(this);
+}
diff --git a/lib/models/logentry.g.dart b/lib/models/logentry.g.dart
new file mode 100644
index 0000000..069549f
--- /dev/null
+++ b/lib/models/logentry.g.dart
@@ -0,0 +1,17 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+part of 'logentry.dart';
+
+// **************************************************************************
+// JsonSerializableGenerator
+// **************************************************************************
+
+LogEntry _$LogEntryFromJson(Map json) => LogEntry(
+ time: DateTime.parse(json['time'] as String),
+ uid: json['uid'] as String,
+);
+
+Map _$LogEntryToJson(LogEntry instance) => {
+ 'time': instance.time.toIso8601String(),
+ 'uid': instance.uid,
+};
diff --git a/lib/pages/deviceinfo.dart b/lib/pages/deviceinfo.dart
index e10d05b..660db18 100644
--- a/lib/pages/deviceinfo.dart
+++ b/lib/pages/deviceinfo.dart
@@ -1,11 +1,15 @@
import 'dart:async';
-import 'dart:convert';
+import 'dart:io';
+import 'package:dio/dio.dart';
+import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:liteauthconfig/models/appconfig.dart';
import 'package:liteauthconfig/l10n/app_localizations.dart';
import 'package:liteauthconfig/models/device_status.dart';
+import 'package:liteauthconfig/models/logentry.dart';
import 'package:liteauthconfig/pages/reconfigure_network.dart';
+import 'package:liteauthconfig/pages/tokeninfo.dart';
import 'package:liteauthconfig/utils/network.dart';
class DeviceInfoPage extends StatefulWidget {
@@ -17,13 +21,18 @@ class DeviceInfoPage extends StatefulWidget {
State createState() => _DeviceInfoPageState();
}
+enum TokenState { requesting, exists, error }
+
class _DeviceInfoPageState extends State {
+ late Dio httpClient;
late Device device;
DeviceStatus? deviceStatus;
String statusMessage = "";
bool online = false;
bool loading = false;
Timer? refreshTimer;
+ late Timer logPollTimer;
+ List logEntries = [];
@override
void initState() {
@@ -31,40 +40,67 @@ class _DeviceInfoPageState extends State {
refreshDevice();
checkStatus();
+ logPollTimer = Timer.periodic(
+ Duration(seconds: 15),
+ (timer) => queryLogs(),
+ );
}
void refreshDevice() {
device = AppConfig().devices[widget.deviceIndex];
}
+ Future checkToken() async {
+ var appLocal = AppLocalizations.of(context)!;
+ var resp = await httpClient.get("https://liteauth.local/api/getToken");
+
+ if (resp.statusCode != 200) {
+ statusMessage = appLocal.tokenForbidden;
+ return;
+ }
+
+ device.token = resp.data;
+ final config = AppConfig();
+ config.devices[widget.deviceIndex] = device;
+ await config.save();
+ }
+
Future checkStatus() async {
statusMessage = "";
setState(() {
loading = true;
});
- var client = await createEspHttpClient();
+ httpClient = await createEspHttpClient(token: device.token);
try {
- var resp = await client.get(
- Uri.parse("https://liteauth.local/api/status"),
- );
- deviceStatus = DeviceStatus.fromJson(jsonDecode(resp.body));
- online = resp.statusCode >= 200 && resp.statusCode < 300;
+ var resp = await httpClient.get("https://liteauth.local/api/status");
+ deviceStatus = DeviceStatus.fromJson(resp.data);
+ online = resp.statusCode! >= 200 && resp.statusCode! < 300;
} catch (e) {
statusMessage = e.toString();
online = false;
- refreshTimer ??= Timer.periodic(const Duration(seconds: 5), (
+ refreshTimer ??= Timer.periodic(const Duration(seconds: 10), (
timer,
) async {
+ if (loading) return;
await checkStatus();
if (online) {
+ statusMessage = "";
timer.cancel();
refreshTimer = null;
}
});
}
+ if (online && device.token.isEmpty) {
+ try {
+ await checkToken();
+ } catch (e) {
+ statusMessage = e.toString();
+ }
+ }
+
if (mounted) {
setState(() {
loading = false;
@@ -75,28 +111,96 @@ class _DeviceInfoPageState extends State {
void factoryReset() async {
var appLocal = AppLocalizations.of(context)!;
- var reset = await showDialog(context: context, builder: (ctx) => AlertDialog.adaptive(
- title: Text(appLocal.factoryReset),
- content: Text(appLocal.resetConfirm),
- actions: [
- TextButton(onPressed: () {
- Navigator.of(ctx).pop(false);
- }, child: Text(appLocal.cancel)),
- TextButton(onPressed: () {
- Navigator.of(ctx).pop(true);
- }, child: Text(appLocal.reset)),
- ],
- )) ?? false;
+ var reset =
+ await showDialog(
+ context: context,
+ builder: (ctx) => AlertDialog.adaptive(
+ title: Text(appLocal.factoryReset),
+ content: Text(appLocal.resetConfirm),
+ actions: [
+ TextButton(
+ onPressed: () {
+ Navigator.of(ctx).pop(false);
+ },
+ child: Text(appLocal.cancel),
+ ),
+ TextButton(
+ onPressed: () {
+ Navigator.of(ctx).pop(true);
+ },
+ child: Text(appLocal.reset),
+ ),
+ ],
+ ),
+ ) ??
+ false;
if (!reset) return;
- var client = await createEspHttpClient();
- client.get(Uri.parse("https://liteauth.local/api/factoryReset"));
+ if (!mounted) {
+ return;
+ }
+
+ Navigator.pop(context);
+
+ setState(() {
+ loading = true;
+ });
+ final resp = await httpClient.get(
+ "https://liteauth.local/api/factoryReset",
+ );
+ setState(() {
+ loading = false;
+ });
+
+ if (resp.statusCode != 200 && mounted) {
+ ScaffoldMessenger.of(context).showSnackBar(
+ SnackBar(content: Text(appLocal.resetFailedCode(resp.statusCode!))),
+ );
+ return;
+ }
+
+ if (mounted) {
+ ScaffoldMessenger.of(
+ context,
+ ).showSnackBar(SnackBar(content: Text(appLocal.resetSuccessful)));
+ }
+
+ device.token = "";
+ var config = AppConfig();
+ config.devices[widget.deviceIndex] = device;
+ await config.save();
+ reconfigureNetwork();
+ }
+
+ void queryLogs() async {
+ if (!online) return;
+
+ setState(() {
+ loading = true;
+ });
+
+ try {
+ var resp = await httpClient.get("https://liteauth.local/api/logs");
+ print(DateTime.now().toIso8601String());
+ logEntries = (resp.data as List).reversed
+ .map((data) => LogEntry.fromJson(data))
+ .toList();
+ } catch (e) {
+ if (kDebugMode) {
+ print(e);
+ }
+ }
+
+ setState(() {
+ loading = false;
+ });
}
@override
void dispose() {
refreshTimer?.cancel();
+ logPollTimer.cancel();
super.dispose();
}
@@ -115,81 +219,7 @@ class _DeviceInfoPageState extends State {
)
: null,
actions: [
- MenuAnchor(
- builder: (context, controller, child) {
- return IconButton(
- onPressed: () {
- if (controller.isOpen) {
- controller.close();
- } else {
- controller.open();
- }
- },
- icon: Icon(Icons.adaptive.more),
- );
- },
- menuChildren: [
- MenuItemButton(
- leadingIcon: const Icon(Icons.refresh),
- onPressed: checkStatus,
- child: Text(appLocal.refresh),
- ),
- MenuItemButton(
- leadingIcon: const Icon(Icons.network_wifi),
- child: Text(appLocal.reconfigureNetwork),
- onPressed: () {
- Navigator.push(
- context,
- MaterialPageRoute(
- builder: (context) =>
- ReconfigureNetworkPage(widget.deviceIndex),
- ),
- ).then((result) {
- if (result) {
- refreshDevice();
- checkStatus();
- }
- });
- },
- ),
- MenuItemButton(
- leadingIcon: const Icon(Icons.history),
- child: Text(appLocal.factoryReset),
- onPressed: factoryReset,
- ),
- MenuItemButton(
- leadingIcon: const Icon(Icons.delete),
- child: Text(appLocal.delete),
- onPressed: () {
- showDialog(
- context: context,
- builder: (context) => AlertDialog.adaptive(
- title: Text(appLocal.deletion),
- content: Text(appLocal.deviceDeleteConfirm),
- actions: [
- TextButton(
- onPressed: () {
- Navigator.pop(context);
- },
- child: Text(appLocal.cancel),
- ),
- TextButton(
- onPressed: () {
- AppConfig()
- ..devices.removeAt(widget.deviceIndex)
- ..save();
- Navigator.pop(context);
- Navigator.pop(context);
- },
- child: Text(appLocal.delete),
- ),
- ],
- ),
- );
- },
- ),
- ],
- ),
+ IconButton(onPressed: moreActions, icon: Icon(Icons.adaptive.more)),
],
),
body: SingleChildScrollView(
@@ -251,6 +281,25 @@ class _DeviceInfoPageState extends State {
),
],
),
+ ListView.builder(
+ shrinkWrap: true,
+ itemCount: logEntries.length,
+ itemBuilder: (ctx, idx) {
+ var entry = logEntries[idx];
+ return Row(
+ spacing: 8,
+ children: [
+ Text(
+ // DateFormat.yMd().add_jms().format(
+ // entry.time.toLocal(),
+ // ),
+ entry.time.toIso8601String(),
+ ),
+ Text(entry.uid),
+ ],
+ );
+ },
+ ),
],
),
],
@@ -259,4 +308,119 @@ class _DeviceInfoPageState extends State {
),
);
}
+
+ void reconfigureNetwork() {
+ Navigator.push(
+ context,
+ MaterialPageRoute(
+ builder: (context) => ReconfigureNetworkPage(widget.deviceIndex),
+ ),
+ ).then((result) {
+ if (result == true) {
+ refreshDevice();
+ checkStatus();
+ }
+ });
+ }
+
+ void moreActions() {
+ final appTheme = Theme.of(context);
+ final appLocal = AppLocalizations.of(context)!;
+
+ showModalBottomSheet(
+ context: context,
+ builder: (ctx) => Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Padding(
+ padding: const EdgeInsets.fromLTRB(16, 12, 16, 0),
+ child: Text(
+ appLocal.additionalActions,
+ style: appTheme.textTheme.titleMedium?.copyWith(
+ fontWeight: FontWeight.bold,
+ ),
+ ),
+ ),
+ ListTile(
+ leading: const Icon(Icons.refresh),
+ onTap: () {
+ if (online) {
+ queryLogs();
+ } else {
+ checkStatus();
+ }
+ Navigator.pop(context);
+ },
+ title: Text(appLocal.refresh),
+ ),
+ ListTile(
+ leading: const Icon(Icons.key),
+ onTap: () {
+ Navigator.of(ctx).push(
+ MaterialPageRoute(
+ builder: (context) =>
+ TokenInfo(widget.deviceIndex, device.token),
+ ),
+ );
+ },
+ title: Text(appLocal.manageToken),
+ ),
+ Visibility(
+ visible: Platform.isAndroid || Platform.isIOS,
+ child: ListTile(
+ leading: const Icon(Icons.network_wifi),
+ title: Text(appLocal.reconfigureNetwork),
+ onTap: reconfigureNetwork,
+ ),
+ ),
+ Padding(
+ padding: const EdgeInsets.fromLTRB(16, 0, 16, 0),
+ child: Text(
+ appLocal.dangerousActions,
+ style: appTheme.textTheme.titleMedium?.copyWith(
+ fontWeight: FontWeight.bold,
+ ),
+ ),
+ ),
+ ListTile(
+ leading: const Icon(Icons.history),
+ title: Text(appLocal.factoryReset),
+ onTap: factoryReset,
+ ),
+ ListTile(
+ leading: const Icon(Icons.delete),
+ title: Text(appLocal.delete),
+ onTap: () {
+ showDialog(
+ context: context,
+ builder: (context) => AlertDialog.adaptive(
+ title: Text(appLocal.deletion),
+ content: Text(appLocal.deviceDeleteConfirm),
+ actions: [
+ TextButton(
+ onPressed: () {
+ Navigator.pop(context);
+ },
+ child: Text(appLocal.cancel),
+ ),
+ TextButton(
+ onPressed: () {
+ AppConfig()
+ ..devices.removeAt(widget.deviceIndex)
+ ..save();
+ Navigator.pop(context);
+ Navigator.pop(context);
+ Navigator.pop(context);
+ },
+ child: Text(appLocal.delete),
+ ),
+ ],
+ ),
+ );
+ },
+ ),
+ ],
+ ),
+ );
+ }
}
diff --git a/lib/pages/devicelist.dart b/lib/pages/devicelist.dart
index a17d76c..d56a56c 100644
--- a/lib/pages/devicelist.dart
+++ b/lib/pages/devicelist.dart
@@ -31,6 +31,7 @@ class _DeviceListPageState extends State {
if (!(Platform.isAndroid || Platform.isIOS)) return;
availability = await NfcManager.instance.checkAvailability();
+ setState(() {});
if (availability != NfcAvailability.enabled) return;
diff --git a/lib/pages/qrscanner.dart b/lib/pages/qrscanner.dart
new file mode 100644
index 0000000..8e8ed50
--- /dev/null
+++ b/lib/pages/qrscanner.dart
@@ -0,0 +1,16 @@
+import 'package:flutter/material.dart';
+import 'package:mobile_scanner/mobile_scanner.dart';
+
+class QRScanner extends StatelessWidget {
+ const QRScanner({super.key});
+
+ @override
+ Widget build(BuildContext context) {
+ return MobileScanner(
+ onDetect: (result) {
+ Navigator.pop(context, result);
+ },
+ );
+ }
+
+}
\ No newline at end of file
diff --git a/lib/pages/registerpage.dart b/lib/pages/registerpage.dart
index df835a2..e91c7c2 100644
--- a/lib/pages/registerpage.dart
+++ b/lib/pages/registerpage.dart
@@ -4,7 +4,6 @@ import 'package:flutter/material.dart';
import 'package:liteauthconfig/models/appconfig.dart';
import 'package:liteauthconfig/dialogs/esptouchdialog.dart';
import 'package:liteauthconfig/l10n/app_localizations.dart';
-import 'package:multicast_dns/multicast_dns.dart';
import 'package:network_info_plus/network_info_plus.dart';
import 'package:permission_handler/permission_handler.dart';
@@ -44,7 +43,7 @@ class _RegisterPageState extends State {
var appLocal = AppLocalizations.of(context)!;
var appTheme = Theme.of(context);
- if (await Permission.location.isDenied) {
+ if ((Platform.isAndroid || Platform.isIOS) && await Permission.location.isDenied) {
showDialog(
context: context,
builder: (context) => AlertDialog.adaptive(
@@ -70,7 +69,7 @@ class _RegisterPageState extends State {
],
),
);
- } else if (await Permission.location.isGranted) {
+ } else if (!(Platform.isAndroid || Platform.isIOS) || await Permission.location.isGranted) {
fetchNetworkInfo();
}
}
@@ -95,12 +94,29 @@ class _RegisterPageState extends State {
}
void submit() async {
+ var appLocal = AppLocalizations.of(context)!;
+ var name = nameController.text.isEmpty ? ssidController.text : nameController.text;
+
+ if (ssidController.text.isEmpty) {
+ ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(
+ appLocal.cannotBeEmpty("SSID")
+ ),));
+ return;
+ }
+
+ if (bssidController.text.isEmpty) {
+ ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(
+ appLocal.cannotBeEmpty("BSSID")
+ ),));
+ return;
+ }
+
if (widget.setup) {
await showDialog(
context: context,
barrierDismissible: false,
builder: (context) => ESPTouchDialog(
- name: nameController.text,
+ name: name,
ssid: ssidController.text,
bssid: bssidController.text,
password: passwordController.text,
@@ -109,26 +125,12 @@ class _RegisterPageState extends State {
);
} else {
final dev = Device(
- name: nameController.text,
+ name: name,
routerSsid: ssidController.text,
routerBssid: bssidController.text,
networkPassword: passwordController.text,
);
- final MDnsClient client = MDnsClient();
- await client.start();
-
- // TODO: Change to dynamic mDNS name
- String name = "liteauth.local";
-
- await for (final PtrResourceRecord ptr
- in client.lookup(
- ResourceRecordQuery.serverPointer(name),
- )) {
- await for (final SrvResourceRecord srv in client.lookup(ResourceRecordQuery.service(ptr.domainName))) {
- // TODO: Actually implement record lookup
- }
- }
AppConfig().devices.add(dev);
}
if (context.mounted) {
diff --git a/lib/pages/tokeninfo.dart b/lib/pages/tokeninfo.dart
new file mode 100644
index 0000000..922fba1
--- /dev/null
+++ b/lib/pages/tokeninfo.dart
@@ -0,0 +1,163 @@
+import 'dart:io';
+
+import 'package:flutter/material.dart';
+import 'package:flutter/services.dart';
+import 'package:gap/gap.dart';
+import 'package:liteauthconfig/l10n/app_localizations.dart';
+import 'package:liteauthconfig/models/appconfig.dart';
+import 'package:liteauthconfig/pages/qrscanner.dart';
+import 'package:mobile_scanner/mobile_scanner.dart';
+import 'package:qr_flutter/qr_flutter.dart';
+
+class TokenInfo extends StatefulWidget {
+ final int deviceIndex;
+ final String token;
+
+ const TokenInfo(this.deviceIndex, this.token, {super.key});
+
+ @override
+ State createState() => _TokenInfoState();
+}
+
+class _TokenInfoState extends State {
+ TextEditingController tokenController = TextEditingController();
+ bool showToken = false;
+
+ @override
+ void initState() {
+ tokenController.text = widget.token;
+ super.initState();
+ }
+
+ Future saveToken() async {
+ final appLocal = AppLocalizations.of(context)!;
+ var config = AppConfig();
+
+ config.devices[widget.deviceIndex].token =
+ tokenController.text;
+ await config.save();
+
+ if (mounted) {
+ ScaffoldMessenger.of(context).showSnackBar(
+ SnackBar(content: Text(appLocal.saved)),
+ );
+ }
+ }
+
+ Future scanQrCode() async {
+ var code = await Navigator.push(
+ context,
+ MaterialPageRoute(builder: (ctx) => QRScanner()),
+ );
+ var value = code?.barcodes.first.rawValue;
+
+ if (code == null || value == null) return;
+
+ tokenController.text = value;
+ await saveToken();
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ final appLocal = AppLocalizations.of(context)!;
+
+ return Scaffold(
+ appBar: AppBar(title: Text(appLocal.token)),
+ body: SingleChildScrollView(
+ child: Padding(
+ padding: EdgeInsetsGeometry.all(16),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Text(appLocal.viewTokenWarn),
+
+ Row(
+ children: [
+ Checkbox.adaptive(
+ semanticLabel: appLocal.showToken,
+ value: showToken,
+ onChanged: (newValue) {
+ setState(() {
+ showToken = newValue!;
+ });
+ },
+ ),
+ Text(appLocal.showToken),
+ ],
+ ),
+
+ Visibility(
+ visible: showToken,
+ child: Center(
+ child: Column(
+ children: [
+ Visibility(
+ visible: tokenController.text.isNotEmpty,
+ replacement: Text(appLocal.tokenNotFound),
+ child: Column(
+ children: [
+ SizedBox.square(
+ dimension: 200,
+ child: QrImageView(
+ data: tokenController.text,
+ backgroundColor: Colors.white,
+ padding: EdgeInsets.all(16),
+ ),
+ ),
+ Gap(16),
+ OutlinedButton(
+ onPressed: () async {
+ await Clipboard.setData(
+ ClipboardData(text: tokenController.text),
+ );
+ ScaffoldMessenger.of(context).showSnackBar(
+ SnackBar(content: Text(appLocal.copied)),
+ );
+ },
+ child: Row(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ const Icon(Icons.copy),
+ Gap(8),
+ Text(appLocal.copyToken),
+ ],
+ ),
+ ),
+ ],
+ ),
+ ),
+ Gap(16),
+ TextField(
+ controller: tokenController,
+ decoration: InputDecoration(
+ border: OutlineInputBorder(),
+ labelText: appLocal.token,
+ suffixIcon: IconButton(
+ onPressed: Platform.isLinux || Platform.isWindows
+ ? null
+ : scanQrCode,
+ icon: const Icon(Icons.qr_code_scanner),
+ ),
+ ),
+ onChanged: (newValue) {
+ setState(() {});
+ },
+ ),
+ Gap(16),
+ FilledButton(
+ onPressed: tokenController.text == widget.token
+ ? null
+ : saveToken,
+ child: Text(appLocal.saveToken),
+ ),
+ ],
+ ),
+ ),
+ ),
+ ],
+ ),
+ ),
+ ),
+ );
+ }
+}
diff --git a/lib/utils/network.dart b/lib/utils/network.dart
index 3e1ce2b..2a01424 100644
--- a/lib/utils/network.dart
+++ b/lib/utils/network.dart
@@ -1,17 +1,32 @@
import 'dart:io';
+import 'package:dio/dio.dart';
+import 'package:dio/io.dart';
import 'package:flutter/services.dart';
-import 'package:http/http.dart' as http;
-import 'package:http/io_client.dart';
-Future createEspHttpClient() async {
+Future createEspHttpClient({String token = ""}) async {
+ Dio dio = Dio();
+
final certData = await rootBundle.load("assets/certificates/rootCA.crt");
final certBytes = certData.buffer.asUint8List();
- SecurityContext securityContext = SecurityContext.defaultContext;
- securityContext.setTrustedCertificatesBytes(certBytes);
+ dio.httpClientAdapter = IOHttpClientAdapter(
+ createHttpClient: () {
+ SecurityContext securityContext = SecurityContext.defaultContext;
+ securityContext.setTrustedCertificatesBytes(certBytes);
- HttpClient client = HttpClient(context: securityContext);
+ return HttpClient(context: securityContext);
+ }
+ );
- return IOClient(client);
+ if (token.isNotEmpty) {
+ dio.interceptors.add(InterceptorsWrapper(onRequest: (RequestOptions options, RequestInterceptorHandler handler) {
+ options.headers.addAll({
+ "Authorization": "Bearer $token"
+ });
+ return handler.next(options);
+ }));
+ }
+
+ return dio;
}
\ No newline at end of file
diff --git a/linux/CMakeLists.txt b/linux/CMakeLists.txt
index f75d142..d7aea84 100644
--- a/linux/CMakeLists.txt
+++ b/linux/CMakeLists.txt
@@ -7,7 +7,7 @@ project(runner LANGUAGES CXX)
set(BINARY_NAME "liteauthconfig")
# The unique GTK application identifier for this application. See:
# https://wiki.gnome.org/HowDoI/ChooseApplicationID
-set(APPLICATION_ID "xyz.dailitation.linesofcodes.liteauthconfig.liteauthconfig")
+set(APPLICATION_ID "xyz.dailitation.linesofcodes.liteauthconfig")
# Explicitly opt in to modern CMake behaviors to avoid warnings with recent
# versions of CMake.
diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift
index 1112ba9..5c3d497 100644
--- a/macos/Flutter/GeneratedPluginRegistrant.swift
+++ b/macos/Flutter/GeneratedPluginRegistrant.swift
@@ -6,11 +6,13 @@ import FlutterMacOS
import Foundation
import flutter_secure_storage_darwin
+import mobile_scanner
import network_info_plus
import system_theme
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
FlutterSecureStorageDarwinPlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStorageDarwinPlugin"))
+ MobileScannerPlugin.register(with: registry.registrar(forPlugin: "MobileScannerPlugin"))
NetworkInfoPlusPlugin.register(with: registry.registrar(forPlugin: "NetworkInfoPlusPlugin"))
SystemThemePlugin.register(with: registry.registrar(forPlugin: "SystemThemePlugin"))
}
diff --git a/macos/Runner.xcodeproj/project.pbxproj b/macos/Runner.xcodeproj/project.pbxproj
index e3dd5e5..3d6b461 100644
--- a/macos/Runner.xcodeproj/project.pbxproj
+++ b/macos/Runner.xcodeproj/project.pbxproj
@@ -385,7 +385,7 @@
CURRENT_PROJECT_VERSION = 1;
GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0;
- PRODUCT_BUNDLE_IDENTIFIER = xyz.dailitation.linesofcodes.liteauthconfig.liteauthconfig.RunnerTests;
+ PRODUCT_BUNDLE_IDENTIFIER = xyz.dailitation.linesofcodes.liteauthconfig.RunnerTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/liteauthconfig.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/liteauthconfig";
@@ -399,7 +399,7 @@
CURRENT_PROJECT_VERSION = 1;
GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0;
- PRODUCT_BUNDLE_IDENTIFIER = xyz.dailitation.linesofcodes.liteauthconfig.liteauthconfig.RunnerTests;
+ PRODUCT_BUNDLE_IDENTIFIER = xyz.dailitation.linesofcodes.liteauthconfig.RunnerTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/liteauthconfig.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/liteauthconfig";
@@ -413,7 +413,7 @@
CURRENT_PROJECT_VERSION = 1;
GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0;
- PRODUCT_BUNDLE_IDENTIFIER = xyz.dailitation.linesofcodes.liteauthconfig.liteauthconfig.RunnerTests;
+ PRODUCT_BUNDLE_IDENTIFIER = xyz.dailitation.linesofcodes.liteauthconfig.RunnerTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/liteauthconfig.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/liteauthconfig";
diff --git a/macos/Runner/Configs/AppInfo.xcconfig b/macos/Runner/Configs/AppInfo.xcconfig
index c8236d1..de61a48 100644
--- a/macos/Runner/Configs/AppInfo.xcconfig
+++ b/macos/Runner/Configs/AppInfo.xcconfig
@@ -8,7 +8,7 @@
PRODUCT_NAME = liteauthconfig
// The application's bundle identifier
-PRODUCT_BUNDLE_IDENTIFIER = xyz.dailitation.linesofcodes.liteauthconfig.liteauthconfig
+PRODUCT_BUNDLE_IDENTIFIER = xyz.dailitation.linesofcodes.liteauthconfig
// The copyright displayed in application information
PRODUCT_COPYRIGHT = Copyright © 2025 xyz.dailitation.linesofcodes.liteauthconfig. All rights reserved.
diff --git a/pubspec.lock b/pubspec.lock
index 9401ed7..3a4d73a 100644
--- a/pubspec.lock
+++ b/pubspec.lock
@@ -177,6 +177,22 @@ packages:
url: "https://pub.dev"
source: hosted
version: "0.7.11"
+ dio:
+ dependency: "direct main"
+ description:
+ name: dio
+ sha256: b9d46faecab38fc8cc286f80bc4d61a3bb5d4ac49e51ed877b4d6706efe57b25
+ url: "https://pub.dev"
+ source: hosted
+ version: "5.9.1"
+ dio_web_adapter:
+ dependency: transitive
+ description:
+ name: dio_web_adapter
+ sha256: "7586e476d70caecaf1686d21eee7247ea43ef5c345eab9e0cc3583ff13378d78"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.1.1"
esptouch_flutter:
dependency: "direct main"
description:
@@ -302,6 +318,14 @@ packages:
description: flutter
source: sdk
version: "0.0.0"
+ gap:
+ dependency: "direct main"
+ description:
+ name: gap
+ sha256: f19387d4e32f849394758b91377f9153a1b41d79513ef7668c088c77dbc6955d
+ url: "https://pub.dev"
+ source: hosted
+ version: "3.0.1"
glob:
dependency: transitive
description:
@@ -426,10 +450,10 @@ packages:
dependency: transitive
description:
name: lints
- sha256: a5e2b223cb7c9c8efdc663ef484fdd95bb243bff242ef5b13e26883547fce9a0
+ sha256: "12f842a479589fea194fe5c5a3095abc7be0c1f2ddfa9a0e76aed1dbd26a87df"
url: "https://pub.dev"
source: hosted
- version: "6.0.0"
+ version: "6.1.0"
logging:
dependency: transitive
description:
@@ -470,14 +494,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.0.0"
- multicast_dns:
+ mobile_scanner:
dependency: "direct main"
description:
- name: multicast_dns
- sha256: de72ada5c3db6fdd6ad4ae99452fe05fb403c4bb37c67ceb255ddd37d2b5b1eb
+ name: mobile_scanner
+ sha256: c6184bf2913dd66be244108c9c27ca04b01caf726321c44b0e7a7a1e32d41044
url: "https://pub.dev"
source: hosted
- version: "0.3.3"
+ version: "7.1.4"
native_toolchain_c:
dependency: transitive
description:
@@ -702,6 +726,22 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.5.0"
+ qr:
+ dependency: transitive
+ description:
+ name: qr
+ sha256: "5a1d2586170e172b8a8c8470bbbffd5eb0cd38a66c0d77155ea138d3af3a4445"
+ url: "https://pub.dev"
+ source: hosted
+ version: "3.0.2"
+ qr_flutter:
+ dependency: "direct main"
+ description:
+ name: qr_flutter
+ sha256: "5095f0fc6e3f71d08adef8feccc8cea4f12eec18a2e31c2e8d82cb6019f4b097"
+ url: "https://pub.dev"
+ source: hosted
+ version: "4.1.0"
quiver:
dependency: transitive
description:
diff --git a/pubspec.yaml b/pubspec.yaml
index 281d2c6..bb22a7a 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -51,10 +51,13 @@ dependencies:
path: ^1.9.1
json_annotation: ^4.10.0
flutter_speed_dial: ^7.0.0
- multicast_dns: ^0.3.3
http: ^1.6.0
nfc_manager: ^4.1.1
nfc_manager_ndef: ^1.1.0
+ qr_flutter: ^4.1.0
+ dio: ^5.9.1
+ gap: ^3.0.1
+ mobile_scanner: ^7.1.4
dev_dependencies:
flutter_test: