Move "allow screenshots" to Troubleshooting, show warning.

This commit is contained in:
Dain Nilsson 2022-09-08 12:17:44 +02:00
parent 32076aee51
commit 7e3a6205ce
No known key found for this signature in database
GPG Key ID: F04367096FBA95E8
6 changed files with 217 additions and 186 deletions

View File

@ -12,6 +12,7 @@ import 'app/logging.dart';
import 'app/message.dart';
import 'app/state.dart';
import 'core/state.dart';
import 'android/state.dart';
import 'desktop/state.dart';
import 'version.dart';
import 'widgets/responsive_dialog.dart';
@ -25,151 +26,173 @@ class AboutPage extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
return ResponsiveDialog(
title: const Text('About'),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Image.asset('assets/graphics/app-icon.png', scale: 1 / 0.75),
Padding(
padding: const EdgeInsets.only(top: 24.0),
child: Text(
Platform.isAndroid
? 'Yubico Authenticator Preview'
: 'Yubico Authenticator',
style: Theme.of(context).textTheme.titleMedium,
),
),
const Text(version),
const Text(''),
const Text('Copyright © 2022 Yubico'),
const Text('All rights reserved'),
const Text(''),
Row(
mainAxisSize: MainAxisSize.min,
children: [
TextButton(
child: Text(
AppLocalizations.of(context)!.general_terms_of_use,
style: const TextStyle(decoration: TextDecoration.underline),
),
onPressed: () {
launchUrl(
Uri.parse(
'https://www.yubico.com/support/terms-conditions/yubico-license-agreement/'),
mode: LaunchMode.externalApplication,
);
},
title: Text(AppLocalizations.of(context)!.general_about),
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 32),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Image.asset('assets/graphics/app-icon.png', scale: 1 / 0.75),
Padding(
padding: const EdgeInsets.only(top: 24.0),
child: Text(
'Yubico Authenticator',
style: Theme.of(context).textTheme.titleMedium,
),
TextButton(
child: Text(
AppLocalizations.of(context)!.general_privacy_policy,
style: const TextStyle(decoration: TextDecoration.underline),
),
onPressed: () {
launchUrl(
Uri.parse(
'https://www.yubico.com/support/terms-conditions/privacy-notice/'),
mode: LaunchMode.externalApplication,
);
},
),
],
),
TextButton(
child: Text(
AppLocalizations.of(context)!.general_open_src_licenses,
style: const TextStyle(decoration: TextDecoration.underline),
),
onPressed: () {
Navigator.of(context).push(MaterialPageRoute<void>(
builder: (BuildContext context) => const LicensePage(
applicationVersion: version,
),
settings: const RouteSettings(name: 'licenses'),
));
},
),
const Padding(
padding: EdgeInsets.only(top: 24.0, bottom: 8.0),
child: Divider(),
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 16.0),
child: Text(
AppLocalizations.of(context)!.general_help_and_feedback,
style: Theme.of(context).textTheme.titleMedium,
),
),
Row(
mainAxisSize: MainAxisSize.min,
children: [
TextButton(
child: Text(
AppLocalizations.of(context)!.general_send_feedback,
style: const TextStyle(decoration: TextDecoration.underline),
),
onPressed: () {
launchUrl(
Uri.parse('https://forms.gle/nYPVWcFnqoprZX1S9'),
mode: LaunchMode.externalApplication,
);
},
),
TextButton(
child: Text(
AppLocalizations.of(context)!.general_i_need_help,
style: const TextStyle(decoration: TextDecoration.underline),
),
onPressed: () {
launchUrl(
Uri.parse('https://support.yubico.com/support/home'),
mode: LaunchMode.externalApplication,
);
},
),
],
),
const Padding(
padding: EdgeInsets.only(top: 24.0, bottom: 8.0),
child: Divider(),
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 16.0),
child: Text(
AppLocalizations.of(context)!.general_troubleshooting,
style: Theme.of(context).textTheme.titleMedium,
),
),
const LoggingPanel(),
if (isDesktop) ...[
const SizedBox(height: 12.0),
ActionChip(
avatar: const Icon(Icons.bug_report_outlined),
label:
Text(AppLocalizations.of(context)!.general_run_diagnostics),
onPressed: () async {
_log.info('Running diagnostics...');
final response =
await ref.read(rpcProvider).command('diagnose', []);
final data = response['diagnostics'] as List;
data.insert(0, {
'app_version': version,
'dart': Platform.version,
});
final text = const JsonEncoder.withIndent(' ').convert(data);
await Clipboard.setData(ClipboardData(text: text));
await ref.read(withContextProvider)(
(context) async {
showMessage(
context,
AppLocalizations.of(context)!
.general_diagnostics_copied);
const Text(version),
const Text(''),
const Text('Copyright © 2022 Yubico'),
const Text('All rights reserved'),
const Text(''),
Row(
mainAxisSize: MainAxisSize.min,
children: [
TextButton(
child: Text(
AppLocalizations.of(context)!.general_terms_of_use,
style:
const TextStyle(decoration: TextDecoration.underline),
),
onPressed: () {
launchUrl(
Uri.parse(
'https://www.yubico.com/support/terms-conditions/yubico-license-agreement/'),
mode: LaunchMode.externalApplication,
);
},
);
),
TextButton(
child: Text(
AppLocalizations.of(context)!.general_privacy_policy,
style:
const TextStyle(decoration: TextDecoration.underline),
),
onPressed: () {
launchUrl(
Uri.parse(
'https://www.yubico.com/support/terms-conditions/privacy-notice/'),
mode: LaunchMode.externalApplication,
);
},
),
],
),
TextButton(
child: Text(
AppLocalizations.of(context)!.general_open_src_licenses,
style: const TextStyle(decoration: TextDecoration.underline),
),
onPressed: () {
Navigator.of(context).push(MaterialPageRoute<void>(
builder: (BuildContext context) => const LicensePage(
applicationVersion: version,
),
settings: const RouteSettings(name: 'licenses'),
));
},
),
]
],
const Padding(
padding: EdgeInsets.only(top: 24.0, bottom: 8.0),
child: Divider(),
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 16.0),
child: Text(
AppLocalizations.of(context)!.general_help_and_feedback,
style: Theme.of(context).textTheme.titleMedium,
),
),
Row(
mainAxisSize: MainAxisSize.min,
children: [
TextButton(
child: Text(
AppLocalizations.of(context)!.general_send_feedback,
style:
const TextStyle(decoration: TextDecoration.underline),
),
onPressed: () {
launchUrl(
Uri.parse('https://forms.gle/nYPVWcFnqoprZX1S9'),
mode: LaunchMode.externalApplication,
);
},
),
TextButton(
child: Text(
AppLocalizations.of(context)!.general_i_need_help,
style:
const TextStyle(decoration: TextDecoration.underline),
),
onPressed: () {
launchUrl(
Uri.parse('https://support.yubico.com/support/home'),
mode: LaunchMode.externalApplication,
);
},
),
],
),
const Padding(
padding: EdgeInsets.only(top: 24.0, bottom: 8.0),
child: Divider(),
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 16.0),
child: Text(
AppLocalizations.of(context)!.general_troubleshooting,
style: Theme.of(context).textTheme.titleMedium,
),
),
const LoggingPanel(),
// Diagnostics (desktop only)
if (isDesktop) ...[
const SizedBox(height: 12.0),
ActionChip(
avatar: const Icon(Icons.bug_report_outlined),
label:
Text(AppLocalizations.of(context)!.general_run_diagnostics),
onPressed: () async {
_log.info('Running diagnostics...');
final response =
await ref.read(rpcProvider).command('diagnose', []);
final data = response['diagnostics'] as List;
data.insert(0, {
'app_version': version,
'dart': Platform.version,
});
final text = const JsonEncoder.withIndent(' ').convert(data);
await Clipboard.setData(ClipboardData(text: text));
await ref.read(withContextProvider)(
(context) async {
showMessage(
context,
AppLocalizations.of(context)!
.general_diagnostics_copied);
},
);
},
),
],
// Enable screenshots (Android only)
if (isAndroid) ...[
const SizedBox(height: 12.0),
FilterChip(
label: Text(
AppLocalizations.of(context)!.general_allow_screenshots),
selected: ref.watch(androidAllowScreenshotsProvider),
onSelected: (value) async {
ref
.read(androidAllowScreenshotsProvider.notifier)
.setAllowScreenshots(value);
},
),
],
],
),
),
);
}
@ -201,8 +224,6 @@ class LoggingPanel extends ConsumerWidget {
onChanged: (level) {
ref.read(logLevelProvider.notifier).setLogLevel(level);
_log.debug('Log level set to $level');
showMessage(context,
'${AppLocalizations.of(context)!.general_log_level_set_to} $level');
},
),
ActionChip(

View File

@ -1,4 +0,0 @@
import 'package:flutter/services.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
final appMethodsProvider = Provider<MethodChannel>( (ref) => const MethodChannel('app.methods'));

View File

@ -6,6 +6,21 @@ import '../app/state.dart';
import 'devices.dart';
const _contextChannel = MethodChannel('android.state.appContext');
const _methodsChannel = MethodChannel('app.methods');
final androidAllowScreenshotsProvider =
StateNotifierProvider<AllowScreenshotsNotifier, bool>(
(ref) => AllowScreenshotsNotifier(),
);
class AllowScreenshotsNotifier extends StateNotifier<bool> {
AllowScreenshotsNotifier() : super(false);
void setAllowScreenshots(bool value) async {
state = value;
await _methodsChannel.invokeMethod('hideAppThumbnail', !value);
}
}
final androidSubPageProvider =
StateNotifierProvider<CurrentAppNotifier, Application>((ref) {

View File

@ -1,18 +1,12 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:logging/logging.dart';
import 'package:yubico_authenticator/android/app_methods.dart';
import 'package:yubico_authenticator/app/logging.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:yubico_authenticator/core/state.dart';
import '../../core/state.dart';
import '../../app/state.dart';
import '../../widgets/list_title.dart';
import '../../widgets/responsive_dialog.dart';
final _log = Logger('android_settings_page');
final _hideAppThumbnailProvider = StateProvider<bool>((ref) => true);
const String _prefNfcOpenApp = 'prefNfcOpenApp';
const String _prefNfcBypassTouch = 'prefNfcBypassTouch';
const String _prefNfcCopyOtp = 'prefNfcCopyOtp';
@ -88,7 +82,6 @@ class _AndroidSettingsPageState extends ConsumerState<AndroidSettingsPage> {
prefs.getString(_prefClipKbdLayout) ?? _defaultClipKbdLayout;
final nfcBypassTouch = prefs.getBool(_prefNfcBypassTouch) ?? false;
final themeMode = ref.watch(themeModeProvider);
final hideAppThumbnail = ref.watch(_hideAppThumbnailProvider);
final theme = Theme.of(context);
@ -149,21 +142,6 @@ class _AndroidSettingsPageState extends ConsumerState<AndroidSettingsPage> {
ref.read(themeModeProvider.notifier).setThemeMode(newMode);
},
),
const ListTitle('Security'),
SwitchListTile(
title: const Text('Hide app thumbnail'),
value: hideAppThumbnail,
onChanged: (value) async {
try {
bool hideAppThumbnail = await ref
.read(appMethodsProvider)
.invokeMethod('hideAppThumbnail', value);
ref.read(_hideAppThumbnailProvider.notifier).state =
hideAppThumbnail;
} catch (e) {
_log.error('Failed to call hideAppThumbnail', e);
}
}),
],
),
),

View File

@ -4,6 +4,9 @@ import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:logging/logging.dart';
import '../core/state.dart';
import '../android/state.dart';
String _pad(int value, int zeroes) => value.toString().padLeft(zeroes, '0');
extension DateTimeFormat on DateTime {
@ -85,23 +88,41 @@ class LogWarningOverlay extends StatelessWidget {
children: [
child,
Consumer(builder: (context, ref, _) {
if (ref.watch(logLevelProvider
.select((level) => level.value <= Level.CONFIG.value))) {
return const Align(
alignment: Alignment.bottomCenter,
child: IgnorePointer(
child: Text(
'WARNING: Potentially sensitive data is being logged!',
textDirection: TextDirection.ltr,
style: TextStyle(
color: Colors.red,
fontWeight: FontWeight.bold,
),
final sensitiveLogs = ref.watch(logLevelProvider
.select((level) => level.value <= Level.CONFIG.value));
final allowScreenshots =
isAndroid ? ref.watch(androidAllowScreenshotsProvider) : false;
if (!(sensitiveLogs || allowScreenshots)) {
return const SizedBox();
}
final String message;
if (sensitiveLogs && allowScreenshots) {
message =
'Potentially sensitive data is being logged, and other apps can potentially record the screen';
} else if (sensitiveLogs) {
message = 'Potentially sensitive data is being logged';
} else if (allowScreenshots) {
message = 'Other apps can potentially record the screen';
} else {
return const SizedBox();
}
return Align(
alignment: Alignment.bottomCenter,
child: IgnorePointer(
child: Text(
'WARNING: $message!',
textDirection: TextDirection.ltr,
textAlign: TextAlign.center,
style: const TextStyle(
color: Colors.red,
fontWeight: FontWeight.bold,
),
),
);
}
return const SizedBox();
),
);
}),
],
);

View File

@ -81,6 +81,7 @@
"mgmt_toggle_applications": "Toggle applications",
"mgmt_save": "Save",
"general_about": "About",
"general_terms_of_use": "Terms of use",
"general_privacy_policy": "Privacy policy",
"general_open_src_licenses": "Open source licenses",
@ -92,10 +93,9 @@
"general_run_diagnostics": "Run diagnostics",
"general_diagnostics_copied": "Diagnostic data copied to clipboard",
"general_log_level": "Log level",
"general_log_level_set_to": "Log level set to",
"general_copy_log": "Copy log",
"general_log_copied": "Log copied to clipboard",
"general_allow_screenshots": "Allow screenshots",