Skip to content

Commit

Permalink
replace default icon and function improve (#6)
Browse files Browse the repository at this point in the history
* replace default icon and function improve

* add account screen add the tab
  • Loading branch information
Wangggym authored Nov 3, 2024
1 parent be3ef6d commit f8e4fe7
Show file tree
Hide file tree
Showing 48 changed files with 256 additions and 124 deletions.
2 changes: 1 addition & 1 deletion android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<application
android:label="two_factor_authentication"
android:name="${applicationName}"
android:icon="@mipmap/ic_launcher">
android:icon="@mipmap/launcher_icon">
<activity
android:name=".MainActivity"
android:exported="true"
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/icon/icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions ios/Runner.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -540,7 +540,7 @@
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = AppIcon;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
Expand Down Expand Up @@ -597,7 +597,7 @@
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = AppIcon;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
174 changes: 123 additions & 51 deletions lib/screens/add_account_screen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ class _AddAccountScreenState extends State<AddAccountScreen> {
final _nameController = TextEditingController();
final _secretController = TextEditingController();
final _issuerController = TextEditingController();
final _urlController = TextEditingController();
bool _isUrlMode = true; // Track which input mode is selected

@override
Widget build(BuildContext context) {
Expand All @@ -28,47 +30,106 @@ class _AddAccountScreenState extends State<AddAccountScreen> {
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
TextFormField(
controller: _nameController,
decoration: const InputDecoration(
labelText: 'Account Name',
hintText: 'Enter account name (e.g. johndoe@example.com)',
),
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please enter an account name';
}
return null;
},
),
const SizedBox(height: 16),
TextFormField(
controller: _secretController,
decoration: const InputDecoration(
labelText: 'Secret Key',
hintText: 'Enter secret key or scan QR code',
),
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please enter a secret key';
}
return null;
// Input mode selector
SegmentedButton<bool>(
segments: const [
ButtonSegment(
value: true,
label: Text('By Key URL'),
),
ButtonSegment(
value: false,
label: Text('By Secret Key'),
),
],
selected: {_isUrlMode},
onSelectionChanged: (Set<bool> newSelection) {
setState(() {
_isUrlMode = newSelection.first;
});
},
),
const SizedBox(height: 16),
TextFormField(
controller: _issuerController,
decoration: const InputDecoration(
labelText: 'Issuer',
hintText: 'Enter issuer (e.g. GitHub, Google)',

// Show different fields based on mode
if (_isUrlMode)
TextFormField(
controller: _urlController,
decoration: const InputDecoration(
labelText: 'URL',
hintText: 'Enter otpauth:// URL',
),
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please enter a URL';
}
if (!value.startsWith('otpauth://')) {
return 'Invalid OTP URL format';
}
return null;
},
onChanged: (url) {
if (url.startsWith('otpauth://')) {
try {
final account = OTPAccount.fromUri(Uri.parse(url));
setState(() {
_nameController.text = account.name;
_secretController.text = account.secret;
_issuerController.text = account.issuer;
});
} catch (e) {
// Invalid URL format, ignore
}
}
},
)
else
Column(
children: [
TextFormField(
controller: _nameController,
decoration: const InputDecoration(
labelText: 'Account Name',
hintText:
'Enter account name (e.g. johndoe@example.com)',
),
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please enter an account name';
}
return null;
},
),
const SizedBox(height: 16),
TextFormField(
controller: _secretController,
decoration: const InputDecoration(
labelText: 'Secret Key',
hintText: 'Enter secret key',
),
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please enter a secret key';
}
return null;
},
),
const SizedBox(height: 16),
TextFormField(
controller: _issuerController,
decoration: const InputDecoration(
labelText: 'Issuer',
hintText: 'Enter issuer (e.g. GitHub, Google)',
),
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please enter an issuer';
}
return null;
},
),
],
),
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please enter an issuer';
}
return null;
},
),
const SizedBox(height: 16),
ElevatedButton.icon(
onPressed: _scanQRCode,
Expand All @@ -79,14 +140,26 @@ class _AddAccountScreenState extends State<AddAccountScreen> {
ElevatedButton(
onPressed: () {
if (_formKey.currentState!.validate()) {
Navigator.pop(
context,
OTPAccount(
name: _nameController.text,
secret: _secretController.text,
issuer: _issuerController.text,
),
);
if (_isUrlMode) {
try {
final account =
OTPAccount.fromUri(Uri.parse(_urlController.text));
Navigator.pop(context, account);
} catch (e) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Invalid URL format: $e')),
);
}
} else {
Navigator.pop(
context,
OTPAccount(
name: _nameController.text,
secret: _secretController.text,
issuer: _issuerController.text,
),
);
}
}
},
child: const Text('Add Account'),
Expand All @@ -99,7 +172,7 @@ class _AddAccountScreenState extends State<AddAccountScreen> {
}

Future<void> _scanQRCode() async {
final currentContext = context; // 保存context
final currentContext = context;
try {
final qrCode = await Navigator.push(
context,
Expand All @@ -108,20 +181,18 @@ class _AddAccountScreenState extends State<AddAccountScreen> {
),
);

// 移除不必要的空值检查
if (qrCode != null) {
final uri = Uri.parse(qrCode);
final account = OTPAccount.fromUri(uri);
setState(() {
_isUrlMode = true; // Switch to URL mode
_urlController.text = qrCode;
final account = OTPAccount.fromUri(Uri.parse(qrCode));
_nameController.text = account.name;
_secretController.text = account.secret;
_issuerController.text = account.issuer;
});
}

// 在使用context前检查
if (!currentContext.mounted) return;
// 使用context的代码
} catch (e) {
if (!currentContext.mounted) return;
ScaffoldMessenger.of(currentContext).showSnackBar(
Expand All @@ -135,6 +206,7 @@ class _AddAccountScreenState extends State<AddAccountScreen> {
_nameController.dispose();
_secretController.dispose();
_issuerController.dispose();
_urlController.dispose();
super.dispose();
}
}
2 changes: 1 addition & 1 deletion lib/screens/edit_account_screen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ class _EditAccountScreenState extends State<EditAccountScreen> {
}

String _generateKeyUri() {
return 'otpauth://totp/${widget.account.issuer}:${widget.account.name}?secret=${widget.account.secret}&issuer=${widget.account.issuer}&period=30&digits=6&algorithm=SHA1';
return 'otpauth://totp/${widget.account.issuer}:${widget.account.name}?secret=${widget.account.secret}&issuer=${widget.account.issuer}';
}

void _copyToClipboard(String text, String message) {
Expand Down
Loading

0 comments on commit f8e4fe7

Please sign in to comment.