Token management, Factory reset, UX/UI Improvements
This commit is contained in:
@@ -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
-1
@@ -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
@@ -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")
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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
@@ -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
@@ -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
|
||||||
|
|||||||
@@ -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!';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
@@ -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
@@ -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(),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -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 = "",
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -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,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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,
|
||||||
|
};
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
@@ -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,
|
||||||
|
};
|
||||||
+255
-91
@@ -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 =
|
||||||
|
await showDialog<bool>(
|
||||||
|
context: context,
|
||||||
|
builder: (ctx) => AlertDialog.adaptive(
|
||||||
title: Text(appLocal.factoryReset),
|
title: Text(appLocal.factoryReset),
|
||||||
content: Text(appLocal.resetConfirm),
|
content: Text(appLocal.resetConfirm),
|
||||||
actions: [
|
actions: [
|
||||||
TextButton(onPressed: () {
|
TextButton(
|
||||||
|
onPressed: () {
|
||||||
Navigator.of(ctx).pop(false);
|
Navigator.of(ctx).pop(false);
|
||||||
}, child: Text(appLocal.cancel)),
|
},
|
||||||
TextButton(onPressed: () {
|
child: Text(appLocal.cancel),
|
||||||
|
),
|
||||||
|
TextButton(
|
||||||
|
onPressed: () {
|
||||||
Navigator.of(ctx).pop(true);
|
Navigator.of(ctx).pop(true);
|
||||||
}, child: Text(appLocal.reset)),
|
},
|
||||||
|
child: Text(appLocal.reset),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
)) ?? false;
|
),
|
||||||
|
) ??
|
||||||
|
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),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
|
|||||||
@@ -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
@@ -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) {
|
||||||
|
|||||||
@@ -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),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
+20
-5
@@ -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();
|
||||||
|
|
||||||
|
dio.httpClientAdapter = IOHttpClientAdapter(
|
||||||
|
createHttpClient: () {
|
||||||
SecurityContext securityContext = SecurityContext.defaultContext;
|
SecurityContext securityContext = SecurityContext.defaultContext;
|
||||||
securityContext.setTrustedCertificatesBytes(certBytes);
|
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;
|
||||||
}
|
}
|
||||||
@@ -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"))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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";
|
||||||
|
|||||||
@@ -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
@@ -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
@@ -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:
|
||||||
|
|||||||
Reference in New Issue
Block a user