Token management, Factory reset, UX/UI Improvements

This commit is contained in:
2026-02-22 19:35:20 +07:00
parent 639a8417e6
commit 36953e35df
30 changed files with 1601 additions and 197 deletions
+6 -4
View File
@@ -6,7 +6,7 @@ plugins {
} }
android { android {
namespace = "xyz.dailitation.linesofcodes.liteauthconfig.liteauthconfig" namespace = "xyz.dailitation.linesofcodes.liteauthconfig"
compileSdk = flutter.compileSdkVersion compileSdk = flutter.compileSdkVersion
ndkVersion = flutter.ndkVersion ndkVersion = flutter.ndkVersion
@@ -15,13 +15,15 @@ android {
targetCompatibility = JavaVersion.VERSION_11 targetCompatibility = JavaVersion.VERSION_11
} }
kotlinOptions { kotlin {
jvmTarget = JavaVersion.VERSION_11.toString() compilerOptions {
jvmTarget = org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_11
}
} }
defaultConfig { defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). // 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. // You can update the following values to match your application needs.
// For more information, see: https://flutter.dev/to/review-gradle-config. // For more information, see: https://flutter.dev/to/review-gradle-config.
minSdk = flutter.minSdkVersion minSdk = flutter.minSdkVersion
@@ -1,4 +1,4 @@
package xyz.dailitation.linesofcodes.liteauthconfig.liteauthconfig package xyz.dailitation.linesofcodes.liteauthconfig
import io.flutter.embedding.android.FlutterActivity import io.flutter.embedding.android.FlutterActivity
File diff suppressed because one or more lines are too long
+2 -2
View File
@@ -19,8 +19,8 @@ pluginManagement {
plugins { plugins {
id("dev.flutter.flutter-plugin-loader") version "1.0.0" id("dev.flutter.flutter-plugin-loader") version "1.0.0"
id("com.android.application") version "8.13.0" apply false id("com.android.application") version "8.13.2" apply false
id("org.jetbrains.kotlin.android") version "2.2.21" apply false id("org.jetbrains.kotlin.android") version "2.3.0" apply false
} }
include(":app") include(":app")
+6 -6
View File
@@ -368,7 +368,7 @@
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
PRODUCT_BUNDLE_IDENTIFIER = xyz.dailitation.linesofcodes.liteauthconfig.liteauthconfig; PRODUCT_BUNDLE_IDENTIFIER = xyz.dailitation.linesofcodes.liteauthconfig;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0; SWIFT_VERSION = 5.0;
@@ -384,7 +384,7 @@
CURRENT_PROJECT_VERSION = 1; CURRENT_PROJECT_VERSION = 1;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0; 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)"; PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_OPTIMIZATION_LEVEL = "-Onone";
@@ -401,7 +401,7 @@
CURRENT_PROJECT_VERSION = 1; CURRENT_PROJECT_VERSION = 1;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0; 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)"; PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0; SWIFT_VERSION = 5.0;
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
@@ -416,7 +416,7 @@
CURRENT_PROJECT_VERSION = 1; CURRENT_PROJECT_VERSION = 1;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0; 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)"; PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0; SWIFT_VERSION = 5.0;
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
@@ -547,7 +547,7 @@
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
PRODUCT_BUNDLE_IDENTIFIER = xyz.dailitation.linesofcodes.liteauthconfig.liteauthconfig; PRODUCT_BUNDLE_IDENTIFIER = xyz.dailitation.linesofcodes.liteauthconfig;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_OPTIMIZATION_LEVEL = "-Onone";
@@ -569,7 +569,7 @@
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
PRODUCT_BUNDLE_IDENTIFIER = xyz.dailitation.linesofcodes.liteauthconfig.liteauthconfig; PRODUCT_BUNDLE_IDENTIFIER = xyz.dailitation.linesofcodes.liteauthconfig;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0; SWIFT_VERSION = 5.0;
+3
View File
@@ -47,5 +47,8 @@
<true/> <true/>
<key>NFCReaderUsageDescription</key> <key>NFCReaderUsageDescription</key>
<string>Interacting with the liteauth device</string> <string>Interacting with the liteauth device</string>
<key>NSCameraUsageDescription</key>
<string>This app needs camera access to scan QR codes</string>
</dict> </dict>
</plist> </plist>
+7 -2
View File
@@ -43,6 +43,8 @@ class _ESPTouchDialog extends State<ESPTouchDialog> {
final touchStream = task.execute(); final touchStream = task.execute();
final sub = touchStream.listen((data) async { final sub = touchStream.listen((data) async {
var conf = AppConfig();
final dev = Device( final dev = Device(
name: widget.name, name: widget.name,
routerSsid: widget.ssid, routerSsid: widget.ssid,
@@ -50,11 +52,14 @@ class _ESPTouchDialog extends State<ESPTouchDialog> {
networkPassword: widget.password, networkPassword: widget.password,
); );
if (widget.entryIndex != null) {
final oldDev = conf.devices[widget.entryIndex!];
dev.token = oldDev.token;
}
dev.ip = data.ip; dev.ip = data.ip;
dev.bssid = data.bssid; dev.bssid = data.bssid;
var conf = AppConfig();
if (widget.addNewEntry) { if (widget.addNewEntry) {
conf.devices.add(dev); conf.devices.add(dev);
} else { } else {
+46 -4
View File
@@ -29,13 +29,31 @@
"deletion": "Deletion", "deletion": "Deletion",
"deviceDeleteConfirm": "Are you sure you want to delete this device forever?", "deviceDeleteConfirm": "Are you sure you want to delete this device forever?",
"reconfigureNetwork": "Reconfigure Network", "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: ", "status": "Status: ",
"online": "Online", "online": "Online",
"offline": "Offline", "offline": "Offline",
"refresh": "Refresh", "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": "Last Known IP: {ip}",
"@lastKnownIP": { "@lastKnownIP": {
"placeholders": { "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!"
} }
+114 -18
View File
@@ -260,24 +260,6 @@ abstract class AppLocalizations {
/// **'Reconfigure Network'** /// **'Reconfigure Network'**
String get reconfigureNetwork; 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. /// No description provided for @status.
/// ///
/// In en, this message translates to: /// In en, this message translates to:
@@ -302,6 +284,36 @@ abstract class AppLocalizations {
/// **'Refresh'** /// **'Refresh'**
String get 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. /// No description provided for @lastKnownIP.
/// ///
/// In en, this message translates to: /// In en, this message translates to:
@@ -319,6 +331,90 @@ abstract class AppLocalizations {
/// In en, this message translates to: /// In en, this message translates to:
/// **'Activity Logs'** /// **'Activity Logs'**
String get activityLogs; 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 class _AppLocalizationsDelegate
+67 -10
View File
@@ -96,16 +96,6 @@ class AppLocalizationsEn extends AppLocalizations {
@override @override
String get reconfigureNetwork => 'Reconfigure Network'; 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 @override
String get status => 'Status: '; String get status => 'Status: ';
@@ -118,6 +108,26 @@ class AppLocalizationsEn extends AppLocalizations {
@override @override
String get refresh => 'Refresh'; 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 @override
String lastKnownIP(String ip) { String lastKnownIP(String ip) {
return 'Last Known IP: $ip'; return 'Last Known IP: $ip';
@@ -130,4 +140,51 @@ class AppLocalizationsEn extends AppLocalizations {
@override @override
String get activityLogs => 'Activity Logs'; 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!';
} }
+67 -10
View File
@@ -95,16 +95,6 @@ class AppLocalizationsTh extends AppLocalizations {
@override @override
String get reconfigureNetwork => 'ตั้งค่าเครือข่ายใหม่'; String get reconfigureNetwork => 'ตั้งค่าเครือข่ายใหม่';
@override
String get factoryReset => 'คืนค่าโรงงาน';
@override
String get resetConfirm =>
'คุณแน่ใจหรือไม่ว่าต้องการคืนค่าจากโรงงาน ข้อมูลของคุณจะหายไปและไม่สามารถกู้คืนได้ และคุณจำเป็นต้องตั้งค่าอุปกรณ์ใหม่';
@override
String get reset => 'คืนค่า';
@override @override
String get status => 'สถานะ: '; String get status => 'สถานะ: ';
@@ -117,6 +107,26 @@ class AppLocalizationsTh extends AppLocalizations {
@override @override
String get refresh => 'รีเฟรช'; 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 @override
String lastKnownIP(String ip) { String lastKnownIP(String ip) {
return 'ที่อยู่ IP ที่ทราบล่าสุด: $ip'; return 'ที่อยู่ IP ที่ทราบล่าสุด: $ip';
@@ -129,4 +139,51 @@ class AppLocalizationsTh extends AppLocalizations {
@override @override
String get activityLogs => 'รายการกิจกรรม'; 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 => 'บันทึกแล้ว';
} }
+22 -4
View File
@@ -29,14 +29,32 @@
"deletion": "การลบ", "deletion": "การลบ",
"deviceDeleteConfirm": "คุณแน่ใจหรือไม่ที่จะลบอุปกรณ์นี้อย่างถาวร", "deviceDeleteConfirm": "คุณแน่ใจหรือไม่ที่จะลบอุปกรณ์นี้อย่างถาวร",
"reconfigureNetwork": "ตั้งค่าเครือข่ายใหม่", "reconfigureNetwork": "ตั้งค่าเครือข่ายใหม่",
"factoryReset": "คืนค่าโรงงาน",
"resetConfirm": "คุณแน่ใจหรือไม่ว่าต้องการคืนค่าจากโรงงาน ข้อมูลของคุณจะหายไปและไม่สามารถกู้คืนได้ และคุณจำเป็นต้องตั้งค่าอุปกรณ์ใหม่",
"reset": "คืนค่า",
"status": "สถานะ: ", "status": "สถานะ: ",
"online": "ออนไลน์", "online": "ออนไลน์",
"offline": "ออฟไลน์", "offline": "ออฟไลน์",
"refresh": "รีเฟรช", "refresh": "รีเฟรช",
"cannotBeEmpty": "{field} ไม่สามารถเป็นค่าว่างได้!",
"tokenForbidden": "ไม่สามารถรับค่าโทเค็นได้ โปรดใส่โทเค็นด้วยตนเองหรือคืนค่าโรงงานอุปกรณ์",
"requestFailedWithCode": "คำขอล้มเหลวด้วยรหัส {code}",
"additionalActions": "การกระทำเพิ่มเติม",
"dangerousActions": "การกระทำอันตราย",
"lastKnownIP": "ที่อยู่ IP ที่ทราบล่าสุด: {ip}", "lastKnownIP": "ที่อยู่ IP ที่ทราบล่าสุด: {ip}",
"routerSsid": "SSID เราเตอร์: {ssid}", "routerSsid": "SSID เราเตอร์: {ssid}",
"activityLogs": "รายการกิจกรรม" "activityLogs": "รายการกิจกรรม",
"factoryReset": "คืนค่าโรงงาน",
"resetConfirm": "คุณแน่ใจหรือไม่ว่าต้องการคืนค่าจากโรงงาน ข้อมูลของคุณจะหายไปและไม่สามารถกู้คืนได้ และคุณจำเป็นต้องตั้งค่าอุปกรณ์ใหม่",
"reset": "คืนค่า",
"resetSuccessful": "คืนค่าสำเร็จ",
"resetFailedCode": "การคืนค่าล้มเหลวด้วยรหัส {code}",
"token": "โทเค็น",
"manageToken": "จัดการโทเค็น",
"tokenNotFound": "ไม่พบโทเค็น โปรดใส่โทเค็นหรือทำการคืนค่าโรงงานด้วยตนเอง",
"viewTokenWarn": "โทเค็นเป็นข้อมูลละเอียดอ่อน โปรดอย่าเผยแพร่โทเค็นของคุณ",
"showToken": "แสดงโทเค็น",
"copyToken": "คัดลอกโทเค็น",
"copied": "คัดลอกแล้ว",
"saveToken": "บันทึกโทเค็น",
"saved": "บันทึกแล้ว"
} }
+1 -1
View File
@@ -49,7 +49,6 @@ class MyApp extends StatelessWidget {
title: 'liteauthconfig', title: 'liteauthconfig',
localizationsDelegates: AppLocalizations.localizationsDelegates, localizationsDelegates: AppLocalizations.localizationsDelegates,
supportedLocales: AppLocalizations.supportedLocales, supportedLocales: AppLocalizations.supportedLocales,
locale: Locale("th"),
theme: ThemeData.light().copyWith( theme: ThemeData.light().copyWith(
colorScheme: ColorScheme.fromSeed( colorScheme: ColorScheme.fromSeed(
seedColor: SystemTheme.accentColor.accent, seedColor: SystemTheme.accentColor.accent,
@@ -65,6 +64,7 @@ class MyApp extends StatelessWidget {
textColor: Colors.white, textColor: Colors.white,
iconColor: Colors.white, iconColor: Colors.white,
), ),
bottomSheetTheme: BottomSheetThemeData(backgroundColor: darkColors.inverseSurface)
), ),
home: DeviceListPage(), home: DeviceListPage(),
); );
+2
View File
@@ -40,6 +40,7 @@ class Device {
String routerSsid; String routerSsid;
String routerBssid; String routerBssid;
String networkPassword; String networkPassword;
String token;
String? ip; String? ip;
String? bssid; String? bssid;
@@ -47,6 +48,7 @@ class Device {
required this.name, required this.name,
required this.routerSsid, required this.routerSsid,
required this.routerBssid, required this.routerBssid,
this.token = "",
this.networkPassword = "", this.networkPassword = "",
}); });
+2
View File
@@ -20,6 +20,7 @@ Device _$DeviceFromJson(Map<String, dynamic> json) =>
name: json['name'] as String, name: json['name'] as String,
routerSsid: json['routerSsid'] as String, routerSsid: json['routerSsid'] as String,
routerBssid: json['routerBssid'] as String, routerBssid: json['routerBssid'] as String,
token: json['token'] as String? ?? "",
networkPassword: json['networkPassword'] as String? ?? "", networkPassword: json['networkPassword'] as String? ?? "",
) )
..ip = json['ip'] as String? ..ip = json['ip'] as String?
@@ -30,6 +31,7 @@ Map<String, dynamic> _$DeviceToJson(Device instance) => <String, dynamic>{
'routerSsid': instance.routerSsid, 'routerSsid': instance.routerSsid,
'routerBssid': instance.routerBssid, 'routerBssid': instance.routerBssid,
'networkPassword': instance.networkPassword, 'networkPassword': instance.networkPassword,
'token': instance.token,
'ip': instance.ip, 'ip': instance.ip,
'bssid': instance.bssid, 'bssid': instance.bssid,
}; };
+18
View File
@@ -0,0 +1,18 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'device_status.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
DeviceStatus _$DeviceStatusFromJson(Map<String, dynamic> json) => DeviceStatus(
entryNfc: json['entryNfc'] as bool? ?? false,
exitNfc: json['exitNfc'] as bool? ?? false,
);
Map<String, dynamic> _$DeviceStatusToJson(DeviceStatus instance) =>
<String, dynamic>{
'entryNfc': instance.entryNfc,
'exitNfc': instance.exitNfc,
};
+16
View File
@@ -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<String, dynamic> json) =>
_$LogEntryFromJson(json);
Map<String, dynamic> toJson() => _$LogEntryToJson(this);
}
+17
View File
@@ -0,0 +1,17 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'logentry.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
LogEntry _$LogEntryFromJson(Map<String, dynamic> json) => LogEntry(
time: DateTime.parse(json['time'] as String),
uid: json['uid'] as String,
);
Map<String, dynamic> _$LogEntryToJson(LogEntry instance) => <String, dynamic>{
'time': instance.time.toIso8601String(),
'uid': instance.uid,
};
+261 -97
View File
@@ -1,11 +1,15 @@
import 'dart:async'; 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:flutter/material.dart';
import 'package:liteauthconfig/models/appconfig.dart'; import 'package:liteauthconfig/models/appconfig.dart';
import 'package:liteauthconfig/l10n/app_localizations.dart'; import 'package:liteauthconfig/l10n/app_localizations.dart';
import 'package:liteauthconfig/models/device_status.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/reconfigure_network.dart';
import 'package:liteauthconfig/pages/tokeninfo.dart';
import 'package:liteauthconfig/utils/network.dart'; import 'package:liteauthconfig/utils/network.dart';
class DeviceInfoPage extends StatefulWidget { class DeviceInfoPage extends StatefulWidget {
@@ -17,13 +21,18 @@ class DeviceInfoPage extends StatefulWidget {
State<StatefulWidget> createState() => _DeviceInfoPageState(); State<StatefulWidget> createState() => _DeviceInfoPageState();
} }
enum TokenState { requesting, exists, error }
class _DeviceInfoPageState extends State<DeviceInfoPage> { class _DeviceInfoPageState extends State<DeviceInfoPage> {
late Dio httpClient;
late Device device; late Device device;
DeviceStatus? deviceStatus; DeviceStatus? deviceStatus;
String statusMessage = ""; String statusMessage = "";
bool online = false; bool online = false;
bool loading = false; bool loading = false;
Timer? refreshTimer; Timer? refreshTimer;
late Timer logPollTimer;
List<LogEntry> logEntries = [];
@override @override
void initState() { void initState() {
@@ -31,40 +40,67 @@ class _DeviceInfoPageState extends State<DeviceInfoPage> {
refreshDevice(); refreshDevice();
checkStatus(); checkStatus();
logPollTimer = Timer.periodic(
Duration(seconds: 15),
(timer) => queryLogs(),
);
} }
void refreshDevice() { void refreshDevice() {
device = AppConfig().devices[widget.deviceIndex]; 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 { Future checkStatus() async {
statusMessage = ""; statusMessage = "";
setState(() { setState(() {
loading = true; loading = true;
}); });
var client = await createEspHttpClient(); httpClient = await createEspHttpClient(token: device.token);
try { try {
var resp = await client.get( var resp = await httpClient.get("https://liteauth.local/api/status");
Uri.parse("https://liteauth.local/api/status"), deviceStatus = DeviceStatus.fromJson(resp.data);
); online = resp.statusCode! >= 200 && resp.statusCode! < 300;
deviceStatus = DeviceStatus.fromJson(jsonDecode(resp.body));
online = resp.statusCode >= 200 && resp.statusCode < 300;
} catch (e) { } catch (e) {
statusMessage = e.toString(); statusMessage = e.toString();
online = false; online = false;
refreshTimer ??= Timer.periodic(const Duration(seconds: 5), ( refreshTimer ??= Timer.periodic(const Duration(seconds: 10), (
timer, timer,
) async { ) async {
if (loading) return;
await checkStatus(); await checkStatus();
if (online) { if (online) {
statusMessage = "";
timer.cancel(); timer.cancel();
refreshTimer = null; refreshTimer = null;
} }
}); });
} }
if (online && device.token.isEmpty) {
try {
await checkToken();
} catch (e) {
statusMessage = e.toString();
}
}
if (mounted) { if (mounted) {
setState(() { setState(() {
loading = false; loading = false;
@@ -75,28 +111,96 @@ class _DeviceInfoPageState extends State<DeviceInfoPage> {
void factoryReset() async { void factoryReset() async {
var appLocal = AppLocalizations.of(context)!; var appLocal = AppLocalizations.of(context)!;
var reset = await showDialog<bool>(context: context, builder: (ctx) => AlertDialog.adaptive( var reset =
title: Text(appLocal.factoryReset), await showDialog<bool>(
content: Text(appLocal.resetConfirm), context: context,
actions: [ builder: (ctx) => AlertDialog.adaptive(
TextButton(onPressed: () { title: Text(appLocal.factoryReset),
Navigator.of(ctx).pop(false); content: Text(appLocal.resetConfirm),
}, child: Text(appLocal.cancel)), actions: [
TextButton(onPressed: () { TextButton(
Navigator.of(ctx).pop(true); onPressed: () {
}, child: Text(appLocal.reset)), Navigator.of(ctx).pop(false);
], },
)) ?? false; child: Text(appLocal.cancel),
),
TextButton(
onPressed: () {
Navigator.of(ctx).pop(true);
},
child: Text(appLocal.reset),
),
],
),
) ??
false;
if (!reset) return; if (!reset) return;
var client = await createEspHttpClient(); if (!mounted) {
client.get(Uri.parse("https://liteauth.local/api/factoryReset")); 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<dynamic>).reversed
.map((data) => LogEntry.fromJson(data))
.toList();
} catch (e) {
if (kDebugMode) {
print(e);
}
}
setState(() {
loading = false;
});
} }
@override @override
void dispose() { void dispose() {
refreshTimer?.cancel(); refreshTimer?.cancel();
logPollTimer.cancel();
super.dispose(); super.dispose();
} }
@@ -115,81 +219,7 @@ class _DeviceInfoPageState extends State<DeviceInfoPage> {
) )
: null, : null,
actions: [ actions: [
MenuAnchor( IconButton(onPressed: moreActions, icon: Icon(Icons.adaptive.more)),
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),
),
],
),
);
},
),
],
),
], ],
), ),
body: SingleChildScrollView( body: SingleChildScrollView(
@@ -251,6 +281,25 @@ class _DeviceInfoPageState extends State<DeviceInfoPage> {
), ),
], ],
), ),
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<DeviceInfoPage> {
), ),
); );
} }
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),
),
],
),
);
},
),
],
),
);
}
} }
+1
View File
@@ -31,6 +31,7 @@ class _DeviceListPageState extends State<DeviceListPage> {
if (!(Platform.isAndroid || Platform.isIOS)) return; if (!(Platform.isAndroid || Platform.isIOS)) return;
availability = await NfcManager.instance.checkAvailability(); availability = await NfcManager.instance.checkAvailability();
setState(() {});
if (availability != NfcAvailability.enabled) return; if (availability != NfcAvailability.enabled) return;
+16
View File
@@ -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);
},
);
}
}
+21 -19
View File
@@ -4,7 +4,6 @@ import 'package:flutter/material.dart';
import 'package:liteauthconfig/models/appconfig.dart'; import 'package:liteauthconfig/models/appconfig.dart';
import 'package:liteauthconfig/dialogs/esptouchdialog.dart'; import 'package:liteauthconfig/dialogs/esptouchdialog.dart';
import 'package:liteauthconfig/l10n/app_localizations.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:network_info_plus/network_info_plus.dart';
import 'package:permission_handler/permission_handler.dart'; import 'package:permission_handler/permission_handler.dart';
@@ -44,7 +43,7 @@ class _RegisterPageState extends State<RegisterPage> {
var appLocal = AppLocalizations.of(context)!; var appLocal = AppLocalizations.of(context)!;
var appTheme = Theme.of(context); var appTheme = Theme.of(context);
if (await Permission.location.isDenied) { if ((Platform.isAndroid || Platform.isIOS) && await Permission.location.isDenied) {
showDialog( showDialog(
context: context, context: context,
builder: (context) => AlertDialog.adaptive( builder: (context) => AlertDialog.adaptive(
@@ -70,7 +69,7 @@ class _RegisterPageState extends State<RegisterPage> {
], ],
), ),
); );
} else if (await Permission.location.isGranted) { } else if (!(Platform.isAndroid || Platform.isIOS) || await Permission.location.isGranted) {
fetchNetworkInfo(); fetchNetworkInfo();
} }
} }
@@ -95,12 +94,29 @@ class _RegisterPageState extends State<RegisterPage> {
} }
void submit() async { 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) { if (widget.setup) {
await showDialog( await showDialog(
context: context, context: context,
barrierDismissible: false, barrierDismissible: false,
builder: (context) => ESPTouchDialog( builder: (context) => ESPTouchDialog(
name: nameController.text, name: name,
ssid: ssidController.text, ssid: ssidController.text,
bssid: bssidController.text, bssid: bssidController.text,
password: passwordController.text, password: passwordController.text,
@@ -109,26 +125,12 @@ class _RegisterPageState extends State<RegisterPage> {
); );
} else { } else {
final dev = Device( final dev = Device(
name: nameController.text, name: name,
routerSsid: ssidController.text, routerSsid: ssidController.text,
routerBssid: bssidController.text, routerBssid: bssidController.text,
networkPassword: passwordController.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<PtrResourceRecord>(
ResourceRecordQuery.serverPointer(name),
)) {
await for (final SrvResourceRecord srv in client.lookup(ResourceRecordQuery.service(ptr.domainName))) {
// TODO: Actually implement record lookup
}
}
AppConfig().devices.add(dev); AppConfig().devices.add(dev);
} }
if (context.mounted) { if (context.mounted) {
+163
View File
@@ -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<StatefulWidget> createState() => _TokenInfoState();
}
class _TokenInfoState extends State<TokenInfo> {
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<BarcodeCapture>(
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),
),
],
),
),
),
],
),
),
),
);
}
}
+22 -7
View File
@@ -1,17 +1,32 @@
import 'dart:io'; import 'dart:io';
import 'package:dio/dio.dart';
import 'package:dio/io.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:http/http.dart' as http;
import 'package:http/io_client.dart';
Future<http.Client> createEspHttpClient() async { Future<Dio> createEspHttpClient({String token = ""}) async {
Dio dio = Dio();
final certData = await rootBundle.load("assets/certificates/rootCA.crt"); final certData = await rootBundle.load("assets/certificates/rootCA.crt");
final certBytes = certData.buffer.asUint8List(); final certBytes = certData.buffer.asUint8List();
SecurityContext securityContext = SecurityContext.defaultContext; dio.httpClientAdapter = IOHttpClientAdapter(
securityContext.setTrustedCertificatesBytes(certBytes); 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;
} }
+1 -1
View File
@@ -7,7 +7,7 @@ project(runner LANGUAGES CXX)
set(BINARY_NAME "liteauthconfig") set(BINARY_NAME "liteauthconfig")
# The unique GTK application identifier for this application. See: # The unique GTK application identifier for this application. See:
# https://wiki.gnome.org/HowDoI/ChooseApplicationID # 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 # Explicitly opt in to modern CMake behaviors to avoid warnings with recent
# versions of CMake. # versions of CMake.
@@ -6,11 +6,13 @@ import FlutterMacOS
import Foundation import Foundation
import flutter_secure_storage_darwin import flutter_secure_storage_darwin
import mobile_scanner
import network_info_plus import network_info_plus
import system_theme import system_theme
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
FlutterSecureStorageDarwinPlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStorageDarwinPlugin")) FlutterSecureStorageDarwinPlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStorageDarwinPlugin"))
MobileScannerPlugin.register(with: registry.registrar(forPlugin: "MobileScannerPlugin"))
NetworkInfoPlusPlugin.register(with: registry.registrar(forPlugin: "NetworkInfoPlusPlugin")) NetworkInfoPlusPlugin.register(with: registry.registrar(forPlugin: "NetworkInfoPlusPlugin"))
SystemThemePlugin.register(with: registry.registrar(forPlugin: "SystemThemePlugin")) SystemThemePlugin.register(with: registry.registrar(forPlugin: "SystemThemePlugin"))
} }
+3 -3
View File
@@ -385,7 +385,7 @@
CURRENT_PROJECT_VERSION = 1; CURRENT_PROJECT_VERSION = 1;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0; 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)"; PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0; SWIFT_VERSION = 5.0;
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/liteauthconfig.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/liteauthconfig"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/liteauthconfig.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/liteauthconfig";
@@ -399,7 +399,7 @@
CURRENT_PROJECT_VERSION = 1; CURRENT_PROJECT_VERSION = 1;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0; 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)"; PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0; SWIFT_VERSION = 5.0;
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/liteauthconfig.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/liteauthconfig"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/liteauthconfig.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/liteauthconfig";
@@ -413,7 +413,7 @@
CURRENT_PROJECT_VERSION = 1; CURRENT_PROJECT_VERSION = 1;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0; 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)"; PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0; SWIFT_VERSION = 5.0;
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/liteauthconfig.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/liteauthconfig"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/liteauthconfig.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/liteauthconfig";
+1 -1
View File
@@ -8,7 +8,7 @@
PRODUCT_NAME = liteauthconfig PRODUCT_NAME = liteauthconfig
// The application's bundle identifier // 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 // The copyright displayed in application information
PRODUCT_COPYRIGHT = Copyright © 2025 xyz.dailitation.linesofcodes.liteauthconfig. All rights reserved. PRODUCT_COPYRIGHT = Copyright © 2025 xyz.dailitation.linesofcodes.liteauthconfig. All rights reserved.
+46 -6
View File
@@ -177,6 +177,22 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.7.11" 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: esptouch_flutter:
dependency: "direct main" dependency: "direct main"
description: description:
@@ -302,6 +318,14 @@ packages:
description: flutter description: flutter
source: sdk source: sdk
version: "0.0.0" version: "0.0.0"
gap:
dependency: "direct main"
description:
name: gap
sha256: f19387d4e32f849394758b91377f9153a1b41d79513ef7668c088c77dbc6955d
url: "https://pub.dev"
source: hosted
version: "3.0.1"
glob: glob:
dependency: transitive dependency: transitive
description: description:
@@ -426,10 +450,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: lints name: lints
sha256: a5e2b223cb7c9c8efdc663ef484fdd95bb243bff242ef5b13e26883547fce9a0 sha256: "12f842a479589fea194fe5c5a3095abc7be0c1f2ddfa9a0e76aed1dbd26a87df"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "6.0.0" version: "6.1.0"
logging: logging:
dependency: transitive dependency: transitive
description: description:
@@ -470,14 +494,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.0.0" version: "2.0.0"
multicast_dns: mobile_scanner:
dependency: "direct main" dependency: "direct main"
description: description:
name: multicast_dns name: mobile_scanner
sha256: de72ada5c3db6fdd6ad4ae99452fe05fb403c4bb37c67ceb255ddd37d2b5b1eb sha256: c6184bf2913dd66be244108c9c27ca04b01caf726321c44b0e7a7a1e32d41044
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.3.3" version: "7.1.4"
native_toolchain_c: native_toolchain_c:
dependency: transitive dependency: transitive
description: description:
@@ -702,6 +726,22 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.5.0" 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: quiver:
dependency: transitive dependency: transitive
description: description:
+4 -1
View File
@@ -51,10 +51,13 @@ dependencies:
path: ^1.9.1 path: ^1.9.1
json_annotation: ^4.10.0 json_annotation: ^4.10.0
flutter_speed_dial: ^7.0.0 flutter_speed_dial: ^7.0.0
multicast_dns: ^0.3.3
http: ^1.6.0 http: ^1.6.0
nfc_manager: ^4.1.1 nfc_manager: ^4.1.1
nfc_manager_ndef: ^1.1.0 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: dev_dependencies:
flutter_test: flutter_test: