2022-10-04 13:12:54 +03:00
|
|
|
/*
|
2023-10-10 09:54:25 +03:00
|
|
|
* Copyright (C) 2022-2024 Yubico.
|
2022-10-04 13:12:54 +03:00
|
|
|
*
|
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
* you may not use this file except in compliance with the License.
|
|
|
|
* You may obtain a copy of the License at
|
|
|
|
*
|
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
*
|
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
* See the License for the specific language governing permissions and
|
|
|
|
* limitations under the License.
|
|
|
|
*/
|
|
|
|
|
2022-03-03 18:43:36 +03:00
|
|
|
import 'dart:async';
|
2022-08-31 16:49:43 +03:00
|
|
|
import 'dart:convert';
|
2022-03-03 18:43:36 +03:00
|
|
|
|
2022-05-11 15:02:31 +03:00
|
|
|
import 'package:flutter/foundation.dart';
|
2022-03-03 18:43:36 +03:00
|
|
|
import 'package:flutter/material.dart';
|
2022-08-31 16:49:43 +03:00
|
|
|
import 'package:flutter/services.dart';
|
2022-03-03 18:43:36 +03:00
|
|
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
|
|
import 'package:logging/logging.dart';
|
2022-03-25 10:36:29 +03:00
|
|
|
import 'package:shared_preferences/shared_preferences.dart';
|
2022-03-03 18:43:36 +03:00
|
|
|
|
2022-03-25 10:36:29 +03:00
|
|
|
import '../app/app.dart';
|
2024-02-03 18:12:51 +03:00
|
|
|
import '../app/features.dart' as features;
|
2023-11-27 13:41:05 +03:00
|
|
|
import '../app/logging.dart';
|
2022-03-14 13:48:39 +03:00
|
|
|
import '../app/models.dart';
|
2022-04-05 12:10:37 +03:00
|
|
|
import '../app/state.dart';
|
2022-03-25 10:36:29 +03:00
|
|
|
import '../app/views/main_page.dart';
|
|
|
|
import '../core/state.dart';
|
2024-01-02 19:52:35 +03:00
|
|
|
import '../fido/state.dart';
|
2022-03-16 12:37:52 +03:00
|
|
|
import '../management/state.dart';
|
2022-03-28 11:58:09 +03:00
|
|
|
import '../oath/state.dart';
|
2022-09-21 16:29:34 +03:00
|
|
|
import 'app_methods.dart';
|
2024-01-02 19:52:35 +03:00
|
|
|
import 'fido/state.dart';
|
2023-11-27 13:41:05 +03:00
|
|
|
import 'logger.dart';
|
2022-03-16 12:37:52 +03:00
|
|
|
import 'management/state.dart';
|
2023-11-27 13:41:05 +03:00
|
|
|
import 'oath/otp_auth_link_handler.dart';
|
2022-03-03 19:24:26 +03:00
|
|
|
import 'oath/state.dart';
|
2022-03-28 11:58:09 +03:00
|
|
|
import 'qr_scanner/qr_scanner_provider.dart';
|
|
|
|
import 'state.dart';
|
2022-08-16 15:05:53 +03:00
|
|
|
import 'tap_request_dialog.dart';
|
2023-11-27 13:41:05 +03:00
|
|
|
import 'window_state_provider.dart';
|
2022-03-03 18:43:36 +03:00
|
|
|
|
2022-03-25 10:36:29 +03:00
|
|
|
Future<Widget> initialize() async {
|
2022-09-08 19:34:47 +03:00
|
|
|
_initSystemUi();
|
|
|
|
|
2022-05-11 15:02:31 +03:00
|
|
|
if (kDebugMode) {
|
|
|
|
Logger.root.level = Levels.DEBUG;
|
|
|
|
}
|
|
|
|
|
2022-08-31 16:49:43 +03:00
|
|
|
_initLicenses();
|
|
|
|
|
2024-03-15 16:24:42 +03:00
|
|
|
final isArc = await getAndroidIsArc();
|
|
|
|
|
2022-03-25 10:36:29 +03:00
|
|
|
return ProviderScope(
|
|
|
|
overrides: [
|
|
|
|
prefProvider.overrideWithValue(await SharedPreferences.getInstance()),
|
2023-01-02 20:02:32 +03:00
|
|
|
logLevelProvider.overrideWith((ref) => AndroidLogger()),
|
2023-09-29 15:12:11 +03:00
|
|
|
attachedDevicesProvider.overrideWith(
|
|
|
|
() => AndroidAttachedDevicesNotifier(),
|
|
|
|
),
|
2022-11-30 17:27:32 +03:00
|
|
|
currentDeviceDataProvider.overrideWith(
|
2023-09-29 15:12:11 +03:00
|
|
|
(ref) => ref.watch(androidDeviceDataProvider),
|
2022-11-30 17:27:32 +03:00
|
|
|
),
|
2023-11-15 10:21:50 +03:00
|
|
|
oathStateProvider.overrideWithProvider(androidOathStateProvider.call),
|
2022-03-25 10:36:29 +03:00
|
|
|
credentialListProvider
|
2023-11-15 10:21:50 +03:00
|
|
|
.overrideWithProvider(androidCredentialListProvider.call),
|
2024-03-14 16:31:11 +03:00
|
|
|
currentSectionProvider.overrideWith(
|
|
|
|
(ref) => androidCurrentSectionNotifier(ref),
|
|
|
|
),
|
2023-11-15 10:21:50 +03:00
|
|
|
managementStateProvider.overrideWithProvider(androidManagementState.call),
|
2022-11-30 17:27:32 +03:00
|
|
|
currentDeviceProvider.overrideWith(
|
2023-09-29 15:12:11 +03:00
|
|
|
() => AndroidCurrentDeviceNotifier(),
|
2022-11-30 17:27:32 +03:00
|
|
|
),
|
2022-11-08 16:21:19 +03:00
|
|
|
qrScannerProvider
|
2022-11-30 17:27:32 +03:00
|
|
|
.overrideWith(androidQrScannerProvider(await getHasCamera())),
|
2023-09-29 15:12:11 +03:00
|
|
|
windowStateProvider
|
|
|
|
.overrideWith((ref) => ref.watch(androidWindowStateProvider)),
|
2022-11-30 17:27:32 +03:00
|
|
|
clipboardProvider.overrideWith(
|
2023-09-29 15:12:11 +03:00
|
|
|
(ref) => ref.watch(androidClipboardProvider),
|
2022-11-30 17:27:32 +03:00
|
|
|
),
|
2022-09-22 18:28:52 +03:00
|
|
|
androidSdkVersionProvider.overrideWithValue(await getAndroidSdkVersion()),
|
2023-02-08 19:12:49 +03:00
|
|
|
androidNfcSupportProvider.overrideWithValue(await getHasNfc()),
|
2024-03-19 12:18:14 +03:00
|
|
|
supportedSectionsProvider.overrideWithValue([
|
|
|
|
Section.home,
|
|
|
|
Section.accounts,
|
2024-03-22 15:08:17 +03:00
|
|
|
Section.fingerprints,
|
|
|
|
Section.passkeys
|
2024-03-19 12:18:14 +03:00
|
|
|
]),
|
2024-03-15 16:24:42 +03:00
|
|
|
// this specifies the priority of sections to show when
|
|
|
|
// the connected YubiKey does not support current section
|
2024-04-05 16:13:13 +03:00
|
|
|
androidSectionPriority.overrideWithValue(
|
|
|
|
[Section.accounts, Section.fingerprints, Section.passkeys]),
|
2023-09-29 15:12:11 +03:00
|
|
|
supportedThemesProvider.overrideWith(
|
|
|
|
(ref) => ref.watch(androidSupportedThemesProvider),
|
2023-10-10 09:54:25 +03:00
|
|
|
),
|
2024-01-24 19:13:03 +03:00
|
|
|
defaultColorProvider.overrideWithValue(await getPrimaryColor()),
|
2024-01-02 19:52:35 +03:00
|
|
|
|
|
|
|
// FIDO
|
|
|
|
fidoStateProvider.overrideWithProvider(androidFidoStateProvider.call),
|
|
|
|
fingerprintProvider.overrideWithProvider(androidFingerprintProvider.call),
|
|
|
|
credentialProvider.overrideWithProvider(androidCredentialProvider.call),
|
2022-03-25 10:36:29 +03:00
|
|
|
],
|
2022-08-12 10:46:11 +03:00
|
|
|
child: DismissKeyboard(
|
|
|
|
child: YubicoAuthenticatorApp(page: Consumer(
|
|
|
|
builder: (context, ref, child) {
|
2024-02-05 16:11:17 +03:00
|
|
|
Timer.run(() {
|
|
|
|
ref.read(featureFlagProvider.notifier)
|
|
|
|
// TODO: Load feature flags from file/config?
|
|
|
|
//..loadConfig(config)
|
|
|
|
// Disable unimplemented feature
|
|
|
|
..setFeature(features.piv, false)
|
|
|
|
..setFeature(features.otp, false)
|
2024-03-15 16:24:42 +03:00
|
|
|
..setFeature(features.fido, !isArc)
|
2024-02-05 16:11:17 +03:00
|
|
|
..setFeature(features.management, false);
|
|
|
|
});
|
2024-02-03 18:12:51 +03:00
|
|
|
|
2022-08-12 10:46:11 +03:00
|
|
|
// activates window state provider
|
|
|
|
ref.read(androidWindowStateProvider);
|
2022-05-11 15:53:41 +03:00
|
|
|
|
2022-10-07 15:02:24 +03:00
|
|
|
// initializes global handler for dialogs
|
2022-08-16 15:05:53 +03:00
|
|
|
ref.read(androidDialogProvider);
|
|
|
|
|
2022-10-07 15:02:24 +03:00
|
|
|
// set context which will handle otpauth links
|
2022-09-30 16:52:50 +03:00
|
|
|
setupOtpAuthLinkHandler(context);
|
|
|
|
|
2023-02-08 19:12:49 +03:00
|
|
|
setupAppMethodsChannel(ref);
|
|
|
|
|
2022-08-12 10:46:11 +03:00
|
|
|
return const MainPage();
|
|
|
|
},
|
|
|
|
)),
|
|
|
|
),
|
2022-03-25 10:36:29 +03:00
|
|
|
);
|
2022-03-03 18:43:36 +03:00
|
|
|
}
|
2022-08-12 10:46:11 +03:00
|
|
|
|
|
|
|
class DismissKeyboard extends StatelessWidget {
|
|
|
|
final Widget child;
|
2022-08-31 16:49:43 +03:00
|
|
|
|
2022-08-12 10:46:11 +03:00
|
|
|
const DismissKeyboard({super.key, required this.child});
|
|
|
|
|
|
|
|
@override
|
|
|
|
Widget build(BuildContext context) {
|
|
|
|
return GestureDetector(
|
|
|
|
onTap: () {
|
|
|
|
// De-select any selected node when tapping outside.
|
|
|
|
FocusScopeNode currentFocus = FocusScope.of(context);
|
|
|
|
if (!currentFocus.hasPrimaryFocus &&
|
|
|
|
currentFocus.focusedChild != null) {
|
|
|
|
FocusManager.instance.primaryFocus?.unfocus();
|
|
|
|
}
|
|
|
|
},
|
|
|
|
child: child,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
2022-08-31 16:49:43 +03:00
|
|
|
|
2022-09-08 19:34:47 +03:00
|
|
|
void _initSystemUi() async {
|
|
|
|
await SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge,
|
|
|
|
overlays: SystemUiOverlay.values);
|
|
|
|
}
|
2022-09-01 18:42:59 +03:00
|
|
|
|
2022-09-08 19:34:47 +03:00
|
|
|
void _initLicenses() async {
|
2022-10-25 17:08:52 +03:00
|
|
|
const licenseDir = 'assets/licenses/raw';
|
2022-09-01 18:42:59 +03:00
|
|
|
|
2022-08-31 16:49:43 +03:00
|
|
|
final androidProjectsToLicenseUrl = await rootBundle.loadStructuredData<List>(
|
2022-09-01 18:42:59 +03:00
|
|
|
'$licenseDir/android.json',
|
2022-08-31 16:49:43 +03:00
|
|
|
(value) async => jsonDecode(value),
|
|
|
|
);
|
|
|
|
|
|
|
|
// mapping from url to license text
|
2022-09-01 18:42:59 +03:00
|
|
|
final fileMap = await rootBundle.loadStructuredData<Map>(
|
|
|
|
'$licenseDir/map.json',
|
2022-09-08 19:34:47 +03:00
|
|
|
(value) async => jsonDecode(value),
|
2022-08-31 16:49:43 +03:00
|
|
|
);
|
|
|
|
|
2022-09-01 17:31:37 +03:00
|
|
|
final urlToLicense = <String, String>{};
|
2022-09-01 18:42:59 +03:00
|
|
|
fileMap.forEach((url, file) async {
|
|
|
|
String licenseText = url;
|
|
|
|
try {
|
|
|
|
licenseText = await rootBundle.loadString('$licenseDir/$file');
|
|
|
|
urlToLicense[url] = licenseText;
|
|
|
|
} catch (_) {
|
|
|
|
// failed to read license file, will use the url
|
|
|
|
}
|
2022-08-31 16:49:43 +03:00
|
|
|
});
|
|
|
|
|
|
|
|
if (androidProjectsToLicenseUrl.isNotEmpty) {
|
|
|
|
LicenseRegistry.addLicense(() async* {
|
|
|
|
for (final e in androidProjectsToLicenseUrl) {
|
|
|
|
var licenseUrl = e['PackageLicense'];
|
|
|
|
var content = licenseUrl;
|
|
|
|
if (urlToLicense.containsKey(licenseUrl)) {
|
|
|
|
content = '${urlToLicense[licenseUrl]}\n\n$licenseUrl\n\n';
|
|
|
|
}
|
|
|
|
yield LicenseEntryWithLineBreaks([e['PackageName']], content);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|