mirror of
https://github.com/Yubico/yubioath-flutter.git
synced 2024-11-22 00:12:09 +03:00
Merge branch 'main' into feature/issuer_icons
This commit is contained in:
commit
8978298f01
2
.github/workflows/android.yaml
vendored
2
.github/workflows/android.yaml
vendored
@ -17,7 +17,7 @@ jobs:
|
|||||||
uses: subosito/flutter-action@v2
|
uses: subosito/flutter-action@v2
|
||||||
with:
|
with:
|
||||||
channel: 'stable'
|
channel: 'stable'
|
||||||
flutter-version: '3.7.3'
|
flutter-version: '3.7.5'
|
||||||
- run: |
|
- run: |
|
||||||
flutter config
|
flutter config
|
||||||
flutter --version
|
flutter --version
|
||||||
|
16
.github/workflows/linux.yml
vendored
16
.github/workflows/linux.yml
vendored
@ -8,7 +8,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
env:
|
env:
|
||||||
PYVER: 3.11
|
PYVER: 3.11
|
||||||
FLUTTER: '3.7.3'
|
FLUTTER: '3.7.5'
|
||||||
container:
|
container:
|
||||||
image: ubuntu:18.04
|
image: ubuntu:18.04
|
||||||
env:
|
env:
|
||||||
@ -18,7 +18,7 @@ jobs:
|
|||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: |
|
run: |
|
||||||
apt-get update
|
apt-get update
|
||||||
apt-get install -qq software-properties-common
|
apt-get install -qq software-properties-common libnotify-dev libayatana-appindicator3-dev patchelf
|
||||||
add-apt-repository -y ppa:git-core/ppa
|
add-apt-repository -y ppa:git-core/ppa
|
||||||
add-apt-repository -y ppa:deadsnakes/ppa
|
add-apt-repository -y ppa:deadsnakes/ppa
|
||||||
apt-get install -qq git python$PYVER-dev python$PYVER-venv
|
apt-get install -qq git python$PYVER-dev python$PYVER-venv
|
||||||
@ -82,6 +82,18 @@ jobs:
|
|||||||
- name: Check generated files
|
- name: Check generated files
|
||||||
run: git diff --exit-code
|
run: git diff --exit-code
|
||||||
|
|
||||||
|
- name: Embedd appindicator
|
||||||
|
run: |
|
||||||
|
patchelf --set-rpath '$ORIGIN' build/linux/x64/release/bundle/lib/libtray_manager_plugin.so
|
||||||
|
cp -L /usr/lib/x86_64-linux-gnu/libayatana-appindicator3.so.1 build/linux/x64/release/bundle/lib/
|
||||||
|
patchelf --set-rpath '$ORIGIN' build/linux/x64/release/bundle/lib/libayatana-appindicator3.so.1
|
||||||
|
cp -L /usr/lib/x86_64-linux-gnu/libayatana-indicator3.so.7 build/linux/x64/release/bundle/lib/
|
||||||
|
patchelf --set-rpath '$ORIGIN' build/linux/x64/release/bundle/lib/libayatana-indicator3.so.7
|
||||||
|
cp -L /usr/lib/x86_64-linux-gnu/libdbusmenu-glib.so.4 build/linux/x64/release/bundle/lib/
|
||||||
|
patchelf --set-rpath '$ORIGIN' build/linux/x64/release/bundle/lib/libdbusmenu-glib.so.4
|
||||||
|
cp -L /usr/lib/x86_64-linux-gnu/libdbusmenu-gtk3.so.4 build/linux/x64/release/bundle/lib/
|
||||||
|
patchelf --set-rpath '$ORIGIN' build/linux/x64/release/bundle/lib/libdbusmenu-gtk3.so.4
|
||||||
|
|
||||||
- name: Rename and archive app
|
- name: Rename and archive app
|
||||||
run: |
|
run: |
|
||||||
export REF=$(echo ${GITHUB_REF} | cut -d '/' -f 3)
|
export REF=$(echo ${GITHUB_REF} | cut -d '/' -f 3)
|
||||||
|
2
.github/workflows/macos.yml
vendored
2
.github/workflows/macos.yml
vendored
@ -49,7 +49,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
channel: 'stable'
|
channel: 'stable'
|
||||||
architecture: 'x64'
|
architecture: 'x64'
|
||||||
flutter-version: '3.7.3'
|
flutter-version: '3.7.5'
|
||||||
- run: flutter config --enable-macos-desktop
|
- run: flutter config --enable-macos-desktop
|
||||||
- run: flutter --version
|
- run: flutter --version
|
||||||
|
|
||||||
|
2
.github/workflows/windows.yml
vendored
2
.github/workflows/windows.yml
vendored
@ -45,7 +45,7 @@ jobs:
|
|||||||
- uses: subosito/flutter-action@v2
|
- uses: subosito/flutter-action@v2
|
||||||
with:
|
with:
|
||||||
channel: 'stable'
|
channel: 'stable'
|
||||||
flutter-version: '3.7.3'
|
flutter-version: '3.7.5'
|
||||||
- run: flutter config --enable-windows-desktop
|
- run: flutter config --enable-windows-desktop
|
||||||
- run: flutter --version
|
- run: flutter --version
|
||||||
|
|
||||||
|
@ -112,6 +112,7 @@ extension OathFunctions on WidgetTester {
|
|||||||
}
|
}
|
||||||
|
|
||||||
await shortWait();
|
await shortWait();
|
||||||
|
|
||||||
/// find an AccountView with issuer/name in the account list
|
/// find an AccountView with issuer/name in the account list
|
||||||
var matchingAccounts = find.descendant(
|
var matchingAccounts = find.descendant(
|
||||||
of: findAccountList(),
|
of: findAccountList(),
|
||||||
@ -146,8 +147,11 @@ extension OathFunctions on WidgetTester {
|
|||||||
expect(accountView, isNotNull);
|
expect(accountView, isNotNull);
|
||||||
|
|
||||||
if (accountView != null) {
|
if (accountView != null) {
|
||||||
await ensureVisible(find.byWidget(accountView));
|
final accountFinder = find.byWidget(accountView);
|
||||||
await tap(find.byWidget(accountView));
|
await ensureVisible(accountFinder);
|
||||||
|
final codeButtonFinder = find.descendant(
|
||||||
|
of: accountFinder, matching: find.bySubtype<FilledButton>());
|
||||||
|
await tap(codeButtonFinder);
|
||||||
await shortWait();
|
await shortWait();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -162,6 +162,8 @@ class AboutPage extends ConsumerWidget {
|
|||||||
data.insert(0, {
|
data.insert(0, {
|
||||||
'app_version': version,
|
'app_version': version,
|
||||||
'dart': Platform.version,
|
'dart': Platform.version,
|
||||||
|
'os': Platform.operatingSystem,
|
||||||
|
'os_version': Platform.operatingSystemVersion,
|
||||||
});
|
});
|
||||||
final text = const JsonEncoder.withIndent(' ').convert(data);
|
final text = const JsonEncoder.withIndent(' ').convert(data);
|
||||||
await ref.read(clipboardProvider).setText(text);
|
await ref.read(clipboardProvider).setText(text);
|
||||||
|
@ -134,5 +134,6 @@ class WindowState with _$WindowState {
|
|||||||
required bool focused,
|
required bool focused,
|
||||||
required bool visible,
|
required bool visible,
|
||||||
required bool active,
|
required bool active,
|
||||||
|
@Default(false) bool hidden,
|
||||||
}) = _WindowState;
|
}) = _WindowState;
|
||||||
}
|
}
|
||||||
|
@ -799,6 +799,7 @@ mixin _$WindowState {
|
|||||||
bool get focused => throw _privateConstructorUsedError;
|
bool get focused => throw _privateConstructorUsedError;
|
||||||
bool get visible => throw _privateConstructorUsedError;
|
bool get visible => throw _privateConstructorUsedError;
|
||||||
bool get active => throw _privateConstructorUsedError;
|
bool get active => throw _privateConstructorUsedError;
|
||||||
|
bool get hidden => throw _privateConstructorUsedError;
|
||||||
|
|
||||||
@JsonKey(ignore: true)
|
@JsonKey(ignore: true)
|
||||||
$WindowStateCopyWith<WindowState> get copyWith =>
|
$WindowStateCopyWith<WindowState> get copyWith =>
|
||||||
@ -811,7 +812,7 @@ abstract class $WindowStateCopyWith<$Res> {
|
|||||||
WindowState value, $Res Function(WindowState) then) =
|
WindowState value, $Res Function(WindowState) then) =
|
||||||
_$WindowStateCopyWithImpl<$Res, WindowState>;
|
_$WindowStateCopyWithImpl<$Res, WindowState>;
|
||||||
@useResult
|
@useResult
|
||||||
$Res call({bool focused, bool visible, bool active});
|
$Res call({bool focused, bool visible, bool active, bool hidden});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @nodoc
|
/// @nodoc
|
||||||
@ -830,6 +831,7 @@ class _$WindowStateCopyWithImpl<$Res, $Val extends WindowState>
|
|||||||
Object? focused = null,
|
Object? focused = null,
|
||||||
Object? visible = null,
|
Object? visible = null,
|
||||||
Object? active = null,
|
Object? active = null,
|
||||||
|
Object? hidden = null,
|
||||||
}) {
|
}) {
|
||||||
return _then(_value.copyWith(
|
return _then(_value.copyWith(
|
||||||
focused: null == focused
|
focused: null == focused
|
||||||
@ -844,6 +846,10 @@ class _$WindowStateCopyWithImpl<$Res, $Val extends WindowState>
|
|||||||
? _value.active
|
? _value.active
|
||||||
: active // ignore: cast_nullable_to_non_nullable
|
: active // ignore: cast_nullable_to_non_nullable
|
||||||
as bool,
|
as bool,
|
||||||
|
hidden: null == hidden
|
||||||
|
? _value.hidden
|
||||||
|
: hidden // ignore: cast_nullable_to_non_nullable
|
||||||
|
as bool,
|
||||||
) as $Val);
|
) as $Val);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -856,7 +862,7 @@ abstract class _$$_WindowStateCopyWith<$Res>
|
|||||||
__$$_WindowStateCopyWithImpl<$Res>;
|
__$$_WindowStateCopyWithImpl<$Res>;
|
||||||
@override
|
@override
|
||||||
@useResult
|
@useResult
|
||||||
$Res call({bool focused, bool visible, bool active});
|
$Res call({bool focused, bool visible, bool active, bool hidden});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @nodoc
|
/// @nodoc
|
||||||
@ -873,6 +879,7 @@ class __$$_WindowStateCopyWithImpl<$Res>
|
|||||||
Object? focused = null,
|
Object? focused = null,
|
||||||
Object? visible = null,
|
Object? visible = null,
|
||||||
Object? active = null,
|
Object? active = null,
|
||||||
|
Object? hidden = null,
|
||||||
}) {
|
}) {
|
||||||
return _then(_$_WindowState(
|
return _then(_$_WindowState(
|
||||||
focused: null == focused
|
focused: null == focused
|
||||||
@ -887,6 +894,10 @@ class __$$_WindowStateCopyWithImpl<$Res>
|
|||||||
? _value.active
|
? _value.active
|
||||||
: active // ignore: cast_nullable_to_non_nullable
|
: active // ignore: cast_nullable_to_non_nullable
|
||||||
as bool,
|
as bool,
|
||||||
|
hidden: null == hidden
|
||||||
|
? _value.hidden
|
||||||
|
: hidden // ignore: cast_nullable_to_non_nullable
|
||||||
|
as bool,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -895,7 +906,10 @@ class __$$_WindowStateCopyWithImpl<$Res>
|
|||||||
|
|
||||||
class _$_WindowState implements _WindowState {
|
class _$_WindowState implements _WindowState {
|
||||||
_$_WindowState(
|
_$_WindowState(
|
||||||
{required this.focused, required this.visible, required this.active});
|
{required this.focused,
|
||||||
|
required this.visible,
|
||||||
|
required this.active,
|
||||||
|
this.hidden = false});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
final bool focused;
|
final bool focused;
|
||||||
@ -903,10 +917,13 @@ class _$_WindowState implements _WindowState {
|
|||||||
final bool visible;
|
final bool visible;
|
||||||
@override
|
@override
|
||||||
final bool active;
|
final bool active;
|
||||||
|
@override
|
||||||
|
@JsonKey()
|
||||||
|
final bool hidden;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
return 'WindowState(focused: $focused, visible: $visible, active: $active)';
|
return 'WindowState(focused: $focused, visible: $visible, active: $active, hidden: $hidden)';
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -916,11 +933,13 @@ class _$_WindowState implements _WindowState {
|
|||||||
other is _$_WindowState &&
|
other is _$_WindowState &&
|
||||||
(identical(other.focused, focused) || other.focused == focused) &&
|
(identical(other.focused, focused) || other.focused == focused) &&
|
||||||
(identical(other.visible, visible) || other.visible == visible) &&
|
(identical(other.visible, visible) || other.visible == visible) &&
|
||||||
(identical(other.active, active) || other.active == active));
|
(identical(other.active, active) || other.active == active) &&
|
||||||
|
(identical(other.hidden, hidden) || other.hidden == hidden));
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int get hashCode => Object.hash(runtimeType, focused, visible, active);
|
int get hashCode =>
|
||||||
|
Object.hash(runtimeType, focused, visible, active, hidden);
|
||||||
|
|
||||||
@JsonKey(ignore: true)
|
@JsonKey(ignore: true)
|
||||||
@override
|
@override
|
||||||
@ -933,7 +952,8 @@ abstract class _WindowState implements WindowState {
|
|||||||
factory _WindowState(
|
factory _WindowState(
|
||||||
{required final bool focused,
|
{required final bool focused,
|
||||||
required final bool visible,
|
required final bool visible,
|
||||||
required final bool active}) = _$_WindowState;
|
required final bool active,
|
||||||
|
final bool hidden}) = _$_WindowState;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool get focused;
|
bool get focused;
|
||||||
@ -942,6 +962,8 @@ abstract class _WindowState implements WindowState {
|
|||||||
@override
|
@override
|
||||||
bool get active;
|
bool get active;
|
||||||
@override
|
@override
|
||||||
|
bool get hidden;
|
||||||
|
@override
|
||||||
@JsonKey(ignore: true)
|
@JsonKey(ignore: true)
|
||||||
_$$_WindowStateCopyWith<_$_WindowState> get copyWith =>
|
_$$_WindowStateCopyWith<_$_WindowState> get copyWith =>
|
||||||
throw _privateConstructorUsedError;
|
throw _privateConstructorUsedError;
|
||||||
|
@ -24,6 +24,7 @@ import 'package:window_manager/window_manager.dart';
|
|||||||
import '../about_page.dart';
|
import '../about_page.dart';
|
||||||
import '../android/views/android_settings_page.dart';
|
import '../android/views/android_settings_page.dart';
|
||||||
import '../core/state.dart';
|
import '../core/state.dart';
|
||||||
|
import '../desktop/state.dart';
|
||||||
import '../oath/keys.dart';
|
import '../oath/keys.dart';
|
||||||
import '../settings_page.dart';
|
import '../settings_page.dart';
|
||||||
import 'message.dart';
|
import 'message.dart';
|
||||||
@ -42,6 +43,10 @@ class CloseIntent extends Intent {
|
|||||||
const CloseIntent();
|
const CloseIntent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class HideIntent extends Intent {
|
||||||
|
const HideIntent();
|
||||||
|
}
|
||||||
|
|
||||||
class SearchIntent extends Intent {
|
class SearchIntent extends Intent {
|
||||||
const SearchIntent();
|
const SearchIntent();
|
||||||
}
|
}
|
||||||
@ -77,6 +82,12 @@ Widget registerGlobalShortcuts(
|
|||||||
windowManager.close();
|
windowManager.close();
|
||||||
return null;
|
return null;
|
||||||
}),
|
}),
|
||||||
|
HideIntent: CallbackAction<HideIntent>(onInvoke: (_) {
|
||||||
|
if (isDesktop) {
|
||||||
|
ref.read(desktopWindowStateProvider.notifier).setWindowHidden(true);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}),
|
||||||
SearchIntent: CallbackAction<SearchIntent>(onInvoke: (intent) {
|
SearchIntent: CallbackAction<SearchIntent>(onInvoke: (intent) {
|
||||||
// If the OATH view doesn't have focus, but is shown, find and select the search bar.
|
// If the OATH view doesn't have focus, but is shown, find and select the search bar.
|
||||||
final searchContext = searchAccountsField.currentContext;
|
final searchContext = searchAccountsField.currentContext;
|
||||||
@ -136,8 +147,7 @@ Widget registerGlobalShortcuts(
|
|||||||
child: Shortcuts(
|
child: Shortcuts(
|
||||||
shortcuts: {
|
shortcuts: {
|
||||||
LogicalKeySet(ctrlOrCmd, LogicalKeyboardKey.keyC): const CopyIntent(),
|
LogicalKeySet(ctrlOrCmd, LogicalKeyboardKey.keyC): const CopyIntent(),
|
||||||
LogicalKeySet(ctrlOrCmd, LogicalKeyboardKey.keyW):
|
LogicalKeySet(ctrlOrCmd, LogicalKeyboardKey.keyW): const HideIntent(),
|
||||||
const CloseIntent(),
|
|
||||||
LogicalKeySet(ctrlOrCmd, LogicalKeyboardKey.keyF):
|
LogicalKeySet(ctrlOrCmd, LogicalKeyboardKey.keyF):
|
||||||
const SearchIntent(),
|
const SearchIntent(),
|
||||||
if (isDesktop) ...{
|
if (isDesktop) ...{
|
||||||
|
@ -15,11 +15,13 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
import 'dart:ui';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:logging/logging.dart';
|
import 'package:logging/logging.dart';
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
|
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||||
import 'package:yubico_authenticator/app/logging.dart';
|
import 'package:yubico_authenticator/app/logging.dart';
|
||||||
|
|
||||||
import '../core/state.dart';
|
import '../core/state.dart';
|
||||||
@ -40,6 +42,32 @@ final supportedThemesProvider = StateProvider<List<ThemeMode>>(
|
|||||||
(ref) => throw UnimplementedError(),
|
(ref) => throw UnimplementedError(),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
final _l10nProvider = StateNotifierProvider<_L10nNotifier, AppLocalizations>(
|
||||||
|
(ref) => _L10nNotifier());
|
||||||
|
|
||||||
|
final l10nProvider = Provider<AppLocalizations>(
|
||||||
|
(ref) => ref.watch(_l10nProvider),
|
||||||
|
);
|
||||||
|
|
||||||
|
class _L10nNotifier extends StateNotifier<AppLocalizations>
|
||||||
|
with WidgetsBindingObserver {
|
||||||
|
_L10nNotifier() : super(lookupAppLocalizations(window.locale)) {
|
||||||
|
WidgetsBinding.instance.addObserver(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
WidgetsBinding.instance.removeObserver(this);
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
@protected
|
||||||
|
void didChangeLocales(List<Locale>? locales) {
|
||||||
|
state = lookupAppLocalizations(window.locale);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
final themeModeProvider = StateNotifierProvider<ThemeModeNotifier, ThemeMode>(
|
final themeModeProvider = StateNotifierProvider<ThemeModeNotifier, ThemeMode>(
|
||||||
(ref) => ThemeModeNotifier(
|
(ref) => ThemeModeNotifier(
|
||||||
ref.watch(prefProvider), ref.read(supportedThemesProvider)),
|
ref.watch(prefProvider), ref.read(supportedThemesProvider)),
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:local_notifier/local_notifier.dart';
|
||||||
|
|
||||||
import '../message.dart';
|
import '../message.dart';
|
||||||
|
|
||||||
@ -130,6 +131,24 @@ UserInteractionController promptUserInteraction(
|
|||||||
required String description,
|
required String description,
|
||||||
Widget? icon,
|
Widget? icon,
|
||||||
void Function()? onCancel,
|
void Function()? onCancel,
|
||||||
|
bool headless = false,
|
||||||
|
}) {
|
||||||
|
if (headless) {
|
||||||
|
// No support for icon or onCancel.
|
||||||
|
return _notificationUserInteraction(context,
|
||||||
|
title: title, description: description);
|
||||||
|
} else {
|
||||||
|
return _dialogUserInteraction(context,
|
||||||
|
title: title, description: description, icon: icon, onCancel: onCancel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
UserInteractionController _dialogUserInteraction(
|
||||||
|
BuildContext context, {
|
||||||
|
required String title,
|
||||||
|
required String description,
|
||||||
|
Widget? icon,
|
||||||
|
void Function()? onCancel,
|
||||||
}) {
|
}) {
|
||||||
var wasPopped = false;
|
var wasPopped = false;
|
||||||
final controller = _UserInteractionController(
|
final controller = _UserInteractionController(
|
||||||
@ -164,3 +183,60 @@ UserInteractionController promptUserInteraction(
|
|||||||
|
|
||||||
return controller;
|
return controller;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class _NotificationUserInteractionController extends UserInteractionController {
|
||||||
|
String title;
|
||||||
|
String description;
|
||||||
|
Widget? icon;
|
||||||
|
LocalNotification _notification;
|
||||||
|
_NotificationUserInteractionController({
|
||||||
|
required this.title,
|
||||||
|
required this.description,
|
||||||
|
}) : _notification = LocalNotification(
|
||||||
|
title: title,
|
||||||
|
body: description,
|
||||||
|
)..show();
|
||||||
|
|
||||||
|
@override
|
||||||
|
void close() {
|
||||||
|
_notification.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _doUpdateNotification() async {
|
||||||
|
await _notification.close();
|
||||||
|
await Future.delayed(const Duration(milliseconds: 200));
|
||||||
|
_notification = LocalNotification(title: title, body: description);
|
||||||
|
await _notification.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void updateContent({String? title, String? description, Widget? icon}) {
|
||||||
|
bool changed = false;
|
||||||
|
if (title != null) {
|
||||||
|
this.title = title;
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
if (description != null) {
|
||||||
|
this.description = description;
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
if (icon != null) {
|
||||||
|
this.icon = icon;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (changed) {
|
||||||
|
_doUpdateNotification();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
UserInteractionController _notificationUserInteraction(
|
||||||
|
BuildContext context, {
|
||||||
|
required String title,
|
||||||
|
required String description,
|
||||||
|
}) {
|
||||||
|
return _NotificationUserInteractionController(
|
||||||
|
title: title,
|
||||||
|
description: description,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
@ -23,6 +23,7 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
|
import 'package:local_notifier/local_notifier.dart';
|
||||||
import 'package:logging/logging.dart';
|
import 'package:logging/logging.dart';
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
import 'package:window_manager/window_manager.dart';
|
import 'package:window_manager/window_manager.dart';
|
||||||
@ -47,14 +48,15 @@ import 'rpc.dart';
|
|||||||
import 'devices.dart';
|
import 'devices.dart';
|
||||||
import 'qr_scanner.dart';
|
import 'qr_scanner.dart';
|
||||||
import 'state.dart';
|
import 'state.dart';
|
||||||
|
import 'systray.dart';
|
||||||
|
|
||||||
final _log = Logger('desktop.init');
|
final _log = Logger('desktop.init');
|
||||||
const String _keyWidth = 'DESKTOP_WINDOW_WIDTH';
|
const String _keyWidth = 'DESKTOP_WINDOW_WIDTH';
|
||||||
const String _keyHeight = 'DESKTOP_WINDOW_HEIGHT';
|
const String _keyHeight = 'DESKTOP_WINDOW_HEIGHT';
|
||||||
|
|
||||||
class _WindowResizeListener extends WindowListener {
|
class _WindowEventListener extends WindowListener {
|
||||||
final SharedPreferences _prefs;
|
final SharedPreferences _prefs;
|
||||||
_WindowResizeListener(this._prefs);
|
_WindowEventListener(this._prefs);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onWindowResize() async {
|
void onWindowResize() async {
|
||||||
@ -62,6 +64,13 @@ class _WindowResizeListener extends WindowListener {
|
|||||||
await _prefs.setDouble(_keyWidth, size.width);
|
await _prefs.setDouble(_keyWidth, size.width);
|
||||||
await _prefs.setDouble(_keyHeight, size.height);
|
await _prefs.setDouble(_keyHeight, size.height);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onWindowClose() async {
|
||||||
|
if (Platform.isMacOS) {
|
||||||
|
await windowManager.destroy();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<Widget> initialize(List<String> argv) async {
|
Future<Widget> initialize(List<String> argv) async {
|
||||||
@ -69,14 +78,24 @@ Future<Widget> initialize(List<String> argv) async {
|
|||||||
|
|
||||||
await windowManager.ensureInitialized();
|
await windowManager.ensureInitialized();
|
||||||
final prefs = await SharedPreferences.getInstance();
|
final prefs = await SharedPreferences.getInstance();
|
||||||
|
final isHidden = prefs.getBool(windowHidden) ?? false;
|
||||||
|
|
||||||
unawaited(windowManager.waitUntilReadyToShow().then((_) async {
|
unawaited(windowManager
|
||||||
await windowManager.setMinimumSize(const Size(270, 0));
|
.waitUntilReadyToShow(WindowOptions(
|
||||||
final width = prefs.getDouble(_keyWidth) ?? 400;
|
minimumSize: const Size(270, 0),
|
||||||
final height = prefs.getDouble(_keyHeight) ?? 720;
|
size: Size(
|
||||||
await windowManager.setSize(Size(width, height));
|
prefs.getDouble(_keyWidth) ?? 400,
|
||||||
await windowManager.show();
|
prefs.getDouble(_keyHeight) ?? 720,
|
||||||
windowManager.addListener(_WindowResizeListener(prefs));
|
),
|
||||||
|
skipTaskbar: isHidden,
|
||||||
|
))
|
||||||
|
.then((_) async {
|
||||||
|
if (isHidden) {
|
||||||
|
await windowManager.setSkipTaskbar(true);
|
||||||
|
} else {
|
||||||
|
await windowManager.show();
|
||||||
|
}
|
||||||
|
windowManager.addListener(_WindowEventListener(prefs));
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// Either use the _HELPER_PATH environment variable, or look relative to executable.
|
// Either use the _HELPER_PATH environment variable, or look relative to executable.
|
||||||
@ -106,6 +125,11 @@ Future<Widget> initialize(List<String> argv) async {
|
|||||||
final rpcFuture = _initHelper(exe!);
|
final rpcFuture = _initHelper(exe!);
|
||||||
_initLicenses();
|
_initLicenses();
|
||||||
|
|
||||||
|
await localNotifier.setup(
|
||||||
|
appName: 'Yubico Authenticator',
|
||||||
|
shortcutPolicy: ShortcutPolicy.ignore,
|
||||||
|
);
|
||||||
|
|
||||||
return ProviderScope(
|
return ProviderScope(
|
||||||
overrides: [
|
overrides: [
|
||||||
supportedAppsProvider.overrideWithValue([
|
supportedAppsProvider.overrideWithValue([
|
||||||
@ -155,6 +179,9 @@ Future<Widget> initialize(List<String> argv) async {
|
|||||||
ref.read(rpcProvider).valueOrNull?.setLogLevel(level);
|
ref.read(rpcProvider).valueOrNull?.setLogLevel(level);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Initialize systray
|
||||||
|
ref.watch(systrayProvider);
|
||||||
|
|
||||||
// Show a loading or error page while the Helper isn't ready
|
// Show a loading or error page while the Helper isn't ready
|
||||||
return ref.watch(rpcProvider).when(
|
return ref.watch(rpcProvider).when(
|
||||||
data: (data) => const MainPage(),
|
data: (data) => const MainPage(),
|
||||||
|
@ -20,6 +20,7 @@ import 'dart:math';
|
|||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
|
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||||
import 'package:logging/logging.dart';
|
import 'package:logging/logging.dart';
|
||||||
|
|
||||||
import '../../app/logging.dart';
|
import '../../app/logging.dart';
|
||||||
@ -192,9 +193,9 @@ class _DesktopOathStateNotifier extends OathStateNotifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final desktopOathCredentialListProvider = StateNotifierProvider.autoDispose
|
final desktopOathCredentialListProvider = StateNotifierProvider.autoDispose
|
||||||
.family<OathCredentialListNotifier, List<OathPair>?, DevicePath>(
|
.family<DesktopCredentialListNotifier, List<OathPair>?, DevicePath>(
|
||||||
(ref, devicePath) {
|
(ref, devicePath) {
|
||||||
var notifier = _DesktopCredentialListNotifier(
|
var notifier = DesktopCredentialListNotifier(
|
||||||
ref.watch(withContextProvider),
|
ref.watch(withContextProvider),
|
||||||
ref.watch(_sessionProvider(devicePath)),
|
ref.watch(_sessionProvider(devicePath)),
|
||||||
ref.watch(oathStateProvider(devicePath)
|
ref.watch(oathStateProvider(devicePath)
|
||||||
@ -203,6 +204,7 @@ final desktopOathCredentialListProvider = StateNotifierProvider.autoDispose
|
|||||||
ref.listen<WindowState>(windowStateProvider, (_, windowState) {
|
ref.listen<WindowState>(windowStateProvider, (_, windowState) {
|
||||||
notifier._notifyWindowState(windowState);
|
notifier._notifyWindowState(windowState);
|
||||||
}, fireImmediately: true);
|
}, fireImmediately: true);
|
||||||
|
|
||||||
return notifier;
|
return notifier;
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@ -225,12 +227,12 @@ String _formatSteam(String response) {
|
|||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
class _DesktopCredentialListNotifier extends OathCredentialListNotifier {
|
class DesktopCredentialListNotifier extends OathCredentialListNotifier {
|
||||||
final WithContext _withContext;
|
final WithContext _withContext;
|
||||||
final RpcNodeSession _session;
|
final RpcNodeSession _session;
|
||||||
final bool _locked;
|
final bool _locked;
|
||||||
Timer? _timer;
|
Timer? _timer;
|
||||||
_DesktopCredentialListNotifier(this._withContext, this._session, this._locked)
|
DesktopCredentialListNotifier(this._withContext, this._session, this._locked)
|
||||||
: super();
|
: super();
|
||||||
|
|
||||||
void _notifyWindowState(WindowState windowState) {
|
void _notifyWindowState(WindowState windowState) {
|
||||||
@ -251,7 +253,7 @@ class _DesktopCredentialListNotifier extends OathCredentialListNotifier {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Future<OathCode> calculate(OathCredential credential,
|
Future<OathCode> calculate(OathCredential credential,
|
||||||
{bool update = true}) async {
|
{bool update = true, bool headless = false}) async {
|
||||||
var now = DateTime.now().millisecondsSinceEpoch ~/ 1000;
|
var now = DateTime.now().millisecondsSinceEpoch ~/ 1000;
|
||||||
if (update) {
|
if (update) {
|
||||||
// Manually triggered, need to pad timer to avoid immediate expiration
|
// Manually triggered, need to pad timer to avoid immediate expiration
|
||||||
@ -264,12 +266,16 @@ class _DesktopCredentialListNotifier extends OathCredentialListNotifier {
|
|||||||
signaler.signals.listen((signal) async {
|
signaler.signals.listen((signal) async {
|
||||||
if (signal.status == 'touch') {
|
if (signal.status == 'touch') {
|
||||||
controller = await _withContext(
|
controller = await _withContext(
|
||||||
(context) async => promptUserInteraction(
|
(context) async {
|
||||||
context,
|
final l10n = AppLocalizations.of(context)!;
|
||||||
icon: const Icon(Icons.touch_app),
|
return promptUserInteraction(
|
||||||
title: 'Touch Required',
|
context,
|
||||||
description: 'Touch the button on your YubiKey now.',
|
icon: const Icon(Icons.touch_app),
|
||||||
),
|
title: l10n.oath_touch_required,
|
||||||
|
description: l10n.oath_touch_now,
|
||||||
|
headless: headless,
|
||||||
|
);
|
||||||
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -24,8 +24,8 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|||||||
import 'package:logging/logging.dart';
|
import 'package:logging/logging.dart';
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
import 'package:window_manager/window_manager.dart';
|
import 'package:window_manager/window_manager.dart';
|
||||||
import 'package:yubico_authenticator/app/logging.dart';
|
|
||||||
|
|
||||||
|
import '../app/logging.dart';
|
||||||
import '../app/models.dart';
|
import '../app/models.dart';
|
||||||
import '../app/state.dart';
|
import '../app/state.dart';
|
||||||
import '../core/state.dart';
|
import '../core/state.dart';
|
||||||
@ -58,20 +58,23 @@ class _RpcStateNotifier extends StateNotifier<RpcState> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final _windowStateProvider =
|
final desktopWindowStateProvider =
|
||||||
StateNotifierProvider<_WindowStateNotifier, WindowState>(
|
StateNotifierProvider<DesktopWindowStateNotifier, WindowState>(
|
||||||
(ref) => _WindowStateNotifier());
|
(ref) => DesktopWindowStateNotifier(ref.watch(prefProvider)));
|
||||||
|
|
||||||
final desktopWindowStateProvider = Provider<WindowState>(
|
const String windowHidden = 'DESKTOP_WINDOW_HIDDEN';
|
||||||
(ref) => ref.watch(_windowStateProvider),
|
|
||||||
);
|
|
||||||
|
|
||||||
class _WindowStateNotifier extends StateNotifier<WindowState>
|
class DesktopWindowStateNotifier extends StateNotifier<WindowState>
|
||||||
with WindowListener {
|
with WindowListener {
|
||||||
|
final SharedPreferences _prefs;
|
||||||
Timer? _idleTimer;
|
Timer? _idleTimer;
|
||||||
|
|
||||||
_WindowStateNotifier()
|
DesktopWindowStateNotifier(this._prefs)
|
||||||
: super(WindowState(focused: true, visible: true, active: true)) {
|
: super(WindowState(
|
||||||
|
focused: true,
|
||||||
|
visible: true,
|
||||||
|
active: true,
|
||||||
|
hidden: _prefs.getBool(windowHidden) ?? false)) {
|
||||||
_init();
|
_init();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -88,6 +91,17 @@ class _WindowStateNotifier extends StateNotifier<WindowState>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void setWindowHidden(bool hidden) async {
|
||||||
|
if (hidden) {
|
||||||
|
await windowManager.hide();
|
||||||
|
} else {
|
||||||
|
await windowManager.show();
|
||||||
|
}
|
||||||
|
await windowManager.setSkipTaskbar(hidden);
|
||||||
|
await _prefs.setBool(windowHidden, hidden);
|
||||||
|
state = state.copyWith(hidden: hidden);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
windowManager.removeListener(this);
|
windowManager.removeListener(this);
|
||||||
@ -101,6 +115,7 @@ class _WindowStateNotifier extends StateNotifier<WindowState>
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@protected
|
||||||
void onWindowEvent(String eventName) {
|
void onWindowEvent(String eventName) {
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
switch (eventName) {
|
switch (eventName) {
|
||||||
@ -144,7 +159,29 @@ class _DesktopClipboard extends AppClipboard {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> setText(String toClipboard, {bool isSensitive = false}) async {
|
Future<void> setText(String toClipboard, {bool isSensitive = false}) async {
|
||||||
await Clipboard.setData(ClipboardData(text: toClipboard));
|
// Wayland requires the window to be focused to copy to clipboard
|
||||||
|
final needsFocus = Platform.isLinux &&
|
||||||
|
Platform.environment['XDG_SESSION_TYPE'] == 'wayland';
|
||||||
|
var hidden = false;
|
||||||
|
try {
|
||||||
|
if (needsFocus && !await windowManager.isFocused()) {
|
||||||
|
if (!await windowManager.isVisible()) {
|
||||||
|
hidden = true;
|
||||||
|
await windowManager.setOpacity(0.0);
|
||||||
|
await windowManager.show();
|
||||||
|
}
|
||||||
|
await windowManager.focus();
|
||||||
|
// Window focus isn't immediate, wait until focused with 10s timeout
|
||||||
|
await Future.doWhile(() async => !await windowManager.isFocused())
|
||||||
|
.timeout(const Duration(seconds: 10));
|
||||||
|
}
|
||||||
|
await Clipboard.setData(ClipboardData(text: toClipboard));
|
||||||
|
} finally {
|
||||||
|
if (hidden) {
|
||||||
|
await windowManager.hide();
|
||||||
|
await windowManager.setOpacity(1.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
221
lib/desktop/systray.dart
Executable file
221
lib/desktop/systray.dart
Executable file
@ -0,0 +1,221 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2023 Yubico.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import 'dart:async';
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
|
import 'package:local_notifier/local_notifier.dart';
|
||||||
|
import 'package:tray_manager/tray_manager.dart';
|
||||||
|
import 'package:window_manager/window_manager.dart';
|
||||||
|
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||||
|
|
||||||
|
import '../app/models.dart';
|
||||||
|
import '../app/shortcuts.dart';
|
||||||
|
import '../app/state.dart';
|
||||||
|
import '../core/models.dart';
|
||||||
|
import '../exception/cancellation_exception.dart';
|
||||||
|
import '../oath/models.dart';
|
||||||
|
import '../oath/state.dart';
|
||||||
|
import '../oath/views/utils.dart';
|
||||||
|
import 'oath/state.dart';
|
||||||
|
import 'state.dart';
|
||||||
|
|
||||||
|
final _favoriteAccounts =
|
||||||
|
Provider.autoDispose<Pair<DevicePath?, List<OathCredential>>>(
|
||||||
|
(ref) {
|
||||||
|
final deviceData = ref.watch(currentDeviceDataProvider).valueOrNull;
|
||||||
|
if (deviceData != null) {
|
||||||
|
final credentials =
|
||||||
|
ref.watch(desktopOathCredentialListProvider(deviceData.node.path));
|
||||||
|
final favorites = ref.watch(favoritesProvider);
|
||||||
|
final listed = credentials
|
||||||
|
?.map((e) => e.credential)
|
||||||
|
.where((c) => favorites.contains(c.id))
|
||||||
|
.toList() ??
|
||||||
|
[];
|
||||||
|
return Pair(deviceData.node.path, listed);
|
||||||
|
}
|
||||||
|
return Pair(null, []);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
final systrayProvider = Provider.autoDispose((ref) {
|
||||||
|
final systray = _Systray(ref, ref.watch(l10nProvider));
|
||||||
|
|
||||||
|
// Keep track of which accounts to show
|
||||||
|
ref.listen(
|
||||||
|
_favoriteAccounts,
|
||||||
|
(_, next) {
|
||||||
|
systray._updateCredentials(next);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
// Keep track of the shown/hidden state of the app
|
||||||
|
ref.listen(windowStateProvider.select((value) => value.hidden), (_, hidden) {
|
||||||
|
systray._setHidden(hidden);
|
||||||
|
}, fireImmediately: true);
|
||||||
|
|
||||||
|
ref.onDispose(systray.dispose);
|
||||||
|
|
||||||
|
return systray;
|
||||||
|
});
|
||||||
|
|
||||||
|
Future<OathCode?> _calculateCode(
|
||||||
|
DevicePath devicePath, OathCredential credential, Ref ref) async {
|
||||||
|
try {
|
||||||
|
return await (ref
|
||||||
|
.read(desktopOathCredentialListProvider(devicePath).notifier))
|
||||||
|
.calculate(credential, headless: true);
|
||||||
|
} on CancellationException catch (_) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String _getIcon() {
|
||||||
|
if (Platform.isMacOS) {
|
||||||
|
return 'resources/icons/systray-template.eps';
|
||||||
|
}
|
||||||
|
if (Platform.isWindows) {
|
||||||
|
return 'resources/icons/com.yubico.yubioath.ico';
|
||||||
|
}
|
||||||
|
return 'resources/icons/com.yubico.yubioath-32x32.png';
|
||||||
|
}
|
||||||
|
|
||||||
|
class _Systray extends TrayListener {
|
||||||
|
final Ref _ref;
|
||||||
|
final AppLocalizations _l10n;
|
||||||
|
int _lastClick = 0;
|
||||||
|
DevicePath _devicePath = DevicePath([]);
|
||||||
|
List<OathCredential> _credentials = [];
|
||||||
|
bool _isHidden = false;
|
||||||
|
_Systray(this._ref, this._l10n) {
|
||||||
|
_init();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _init() async {
|
||||||
|
await trayManager.setIcon(_getIcon(), isTemplate: true);
|
||||||
|
if (!Platform.isLinux) {
|
||||||
|
await trayManager.setToolTip(_l10n.general_app_name);
|
||||||
|
}
|
||||||
|
await _updateContextMenu();
|
||||||
|
|
||||||
|
// Doesn't seem to work on Linux
|
||||||
|
trayManager.addListener(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void dispose() {
|
||||||
|
trayManager.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _updateCredentials(Pair<DevicePath?, List<OathCredential>> pair) {
|
||||||
|
if (!listEquals(_credentials, pair.second)) {
|
||||||
|
_devicePath = pair.first ?? _devicePath;
|
||||||
|
_credentials = pair.second;
|
||||||
|
_updateContextMenu();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _setHidden(bool hidden) async {
|
||||||
|
_isHidden = hidden;
|
||||||
|
await _updateContextMenu();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onTrayIconMouseDown() {
|
||||||
|
if (Platform.isMacOS) {
|
||||||
|
trayManager.popUpContextMenu();
|
||||||
|
} else {
|
||||||
|
final now = DateTime.now().millisecondsSinceEpoch;
|
||||||
|
if (now - _lastClick < 500) {
|
||||||
|
_lastClick = 0;
|
||||||
|
if (_isHidden) {
|
||||||
|
_ref.read(desktopWindowStateProvider.notifier).setWindowHidden(false);
|
||||||
|
} else {
|
||||||
|
windowManager.focus();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
_lastClick = now;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onTrayIconRightMouseDown() {
|
||||||
|
trayManager.popUpContextMenu();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _updateContextMenu() async {
|
||||||
|
await trayManager.setContextMenu(
|
||||||
|
Menu(
|
||||||
|
items: [
|
||||||
|
..._credentials.map(
|
||||||
|
(e) {
|
||||||
|
final label = getTextName(e);
|
||||||
|
return MenuItem(
|
||||||
|
label: label,
|
||||||
|
onClick: (_) async {
|
||||||
|
final code = await _calculateCode(_devicePath, e, _ref);
|
||||||
|
if (code != null) {
|
||||||
|
await _ref
|
||||||
|
.read(clipboardProvider)
|
||||||
|
.setText(code.value, isSensitive: true);
|
||||||
|
final notification = LocalNotification(
|
||||||
|
title: _l10n.systray_oath_copied,
|
||||||
|
body: _l10n.systray_oath_copied_to_clipboard(label),
|
||||||
|
silent: true,
|
||||||
|
);
|
||||||
|
await notification.show();
|
||||||
|
await Future.delayed(const Duration(seconds: 4));
|
||||||
|
await notification.close();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
if (_credentials.isEmpty)
|
||||||
|
MenuItem(
|
||||||
|
label: _l10n.systray_no_pinned,
|
||||||
|
disabled: true,
|
||||||
|
),
|
||||||
|
MenuItem.separator(),
|
||||||
|
MenuItem(
|
||||||
|
label: _isHidden
|
||||||
|
? _l10n.general_show_window
|
||||||
|
: _l10n.general_hide_window,
|
||||||
|
onClick: (_) {
|
||||||
|
_ref
|
||||||
|
.read(desktopWindowStateProvider.notifier)
|
||||||
|
.setWindowHidden(!_isHidden);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
MenuItem.separator(),
|
||||||
|
MenuItem(
|
||||||
|
label: _l10n.general_quit,
|
||||||
|
onClick: (_) {
|
||||||
|
_ref.read(withContextProvider)(
|
||||||
|
(context) async {
|
||||||
|
Actions.invoke(context, const CloseIntent());
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -25,6 +25,8 @@
|
|||||||
"oath_scanned_qr": "Scanned QR code",
|
"oath_scanned_qr": "Scanned QR code",
|
||||||
"oath_scan_qr": "Scan QR code",
|
"oath_scan_qr": "Scan QR code",
|
||||||
"oath_require_touch": "Require touch",
|
"oath_require_touch": "Require touch",
|
||||||
|
"oath_touch_required": "Touch Required",
|
||||||
|
"oath_touch_now": "Touch the button on your YubiKey now",
|
||||||
"oath_sec": "sec",
|
"oath_sec": "sec",
|
||||||
"oath_digits": "digits",
|
"oath_digits": "digits",
|
||||||
"oath_success_delete_account": "Account deleted",
|
"oath_success_delete_account": "Account deleted",
|
||||||
@ -114,6 +116,7 @@
|
|||||||
"mgmt_toggle_applications": "Toggle applications",
|
"mgmt_toggle_applications": "Toggle applications",
|
||||||
"mgmt_save": "Save",
|
"mgmt_save": "Save",
|
||||||
|
|
||||||
|
"general_app_name": "Yubico Authenticator",
|
||||||
"general_about": "About",
|
"general_about": "About",
|
||||||
"general_terms_of_use": "Terms of use",
|
"general_terms_of_use": "Terms of use",
|
||||||
"general_privacy_policy": "Privacy policy",
|
"general_privacy_policy": "Privacy policy",
|
||||||
@ -139,6 +142,9 @@
|
|||||||
"general_setup": "Setup",
|
"general_setup": "Setup",
|
||||||
"general_manage": "Manage",
|
"general_manage": "Manage",
|
||||||
"general_configure_yubikey": "Configure YubiKey",
|
"general_configure_yubikey": "Configure YubiKey",
|
||||||
|
"general_show_window": "Show window",
|
||||||
|
"general_hide_window": "Hide window",
|
||||||
|
"general_quit": "Quit",
|
||||||
|
|
||||||
"fido_press_fingerprint_begin": "Press your finger against the YubiKey to begin.",
|
"fido_press_fingerprint_begin": "Press your finger against the YubiKey to begin.",
|
||||||
"fido_keep_touching_yubikey": "Keep touching your YubiKey repeatedly\u2026",
|
"fido_keep_touching_yubikey": "Keep touching your YubiKey repeatedly\u2026",
|
||||||
@ -281,5 +287,15 @@
|
|||||||
"placeholders": {
|
"placeholders": {
|
||||||
"version": {}
|
"version": {}
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
|
||||||
|
|
||||||
|
"systray_oath_copied": "Code copied",
|
||||||
|
"systray_oath_copied_to_clipboard": "{label} copied to clipboard.",
|
||||||
|
"@systray_oath_copied_to_clipboard" : {
|
||||||
|
"placeholders": {
|
||||||
|
"label": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"systray_no_pinned": "No pinned accounts"
|
||||||
}
|
}
|
@ -66,7 +66,14 @@ abstract class OathCredentialListNotifier
|
|||||||
@override
|
@override
|
||||||
@protected
|
@protected
|
||||||
set state(List<OathPair>? value) {
|
set state(List<OathPair>? value) {
|
||||||
super.state = value != null ? List.unmodifiable(value) : null;
|
super.state = value != null
|
||||||
|
? List.unmodifiable(value
|
||||||
|
..sort((a, b) {
|
||||||
|
String searchKey(OathCredential c) =>
|
||||||
|
((c.issuer ?? '') + c.name).toLowerCase();
|
||||||
|
return searchKey(a.credential).compareTo(searchKey(b.credential));
|
||||||
|
}))
|
||||||
|
: null;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<OathCode> calculate(OathCredential credential);
|
Future<OathCode> calculate(OathCredential credential);
|
||||||
@ -193,11 +200,6 @@ class FilteredCredentialsNotifier extends StateNotifier<List<OathPair>> {
|
|||||||
.toLowerCase()
|
.toLowerCase()
|
||||||
.contains(query.toLowerCase()))
|
.contains(query.toLowerCase()))
|
||||||
.where((pair) => pair.credential.issuer != '_hidden')
|
.where((pair) => pair.credential.issuer != '_hidden')
|
||||||
.toList()
|
.toList(),
|
||||||
..sort((a, b) {
|
|
||||||
String searchKey(OathCredential c) =>
|
|
||||||
((c.issuer ?? '') + c.name).toLowerCase();
|
|
||||||
return searchKey(a.credential).compareTo(searchKey(b.credential));
|
|
||||||
}),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
@ -156,7 +156,10 @@ class AccountDialog extends ConsumerWidget {
|
|||||||
if (helper.code == null &&
|
if (helper.code == null &&
|
||||||
(isDesktop || node.transport == Transport.usb)) {
|
(isDesktop || node.transport == Transport.usb)) {
|
||||||
Timer.run(() {
|
Timer.run(() {
|
||||||
Actions.invoke(context, const CalculateIntent());
|
// Only call if credential hasn't been deleted/renamed
|
||||||
|
if (ref.read(credentialsProvider)?.contains(credential) == true) {
|
||||||
|
Actions.invoke(context, const CalculateIntent());
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return FocusScope(
|
return FocusScope(
|
||||||
|
@ -26,6 +26,7 @@ import '../../widgets/responsive_dialog.dart';
|
|||||||
import '../models.dart';
|
import '../models.dart';
|
||||||
import '../state.dart';
|
import '../state.dart';
|
||||||
import '../keys.dart' as keys;
|
import '../keys.dart' as keys;
|
||||||
|
import 'utils.dart';
|
||||||
|
|
||||||
class DeleteAccountDialog extends ConsumerWidget {
|
class DeleteAccountDialog extends ConsumerWidget {
|
||||||
final DeviceNode device;
|
final DeviceNode device;
|
||||||
@ -34,10 +35,6 @@ class DeleteAccountDialog extends ConsumerWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
final label = credential.issuer != null
|
|
||||||
? '${credential.issuer} (${credential.name})'
|
|
||||||
: credential.name;
|
|
||||||
|
|
||||||
return ResponsiveDialog(
|
return ResponsiveDialog(
|
||||||
title: Text(AppLocalizations.of(context)!.oath_delete_account),
|
title: Text(AppLocalizations.of(context)!.oath_delete_account),
|
||||||
actions: [
|
actions: [
|
||||||
@ -75,7 +72,8 @@ class DeleteAccountDialog extends ConsumerWidget {
|
|||||||
AppLocalizations.of(context)!.oath_warning_disable_this_cred,
|
AppLocalizations.of(context)!.oath_warning_disable_this_cred,
|
||||||
style: Theme.of(context).textTheme.bodyLarge,
|
style: Theme.of(context).textTheme.bodyLarge,
|
||||||
),
|
),
|
||||||
Text('${AppLocalizations.of(context)!.oath_account} $label'),
|
Text(
|
||||||
|
'${AppLocalizations.of(context)!.oath_account} ${getTextName(credential)}'),
|
||||||
]
|
]
|
||||||
.map((e) => Padding(
|
.map((e) => Padding(
|
||||||
padding: const EdgeInsets.symmetric(vertical: 8.0),
|
padding: const EdgeInsets.symmetric(vertical: 8.0),
|
||||||
|
@ -126,7 +126,7 @@ class _UnlockedViewState extends ConsumerState<_UnlockedView> {
|
|||||||
}
|
}
|
||||||
return Actions(
|
return Actions(
|
||||||
actions: {
|
actions: {
|
||||||
SearchIntent: CallbackAction(onInvoke: (_) {
|
SearchIntent: CallbackAction<SearchIntent>(onInvoke: (_) {
|
||||||
searchController.selection = TextSelection(
|
searchController.selection = TextSelection(
|
||||||
baseOffset: 0, extentOffset: searchController.text.length);
|
baseOffset: 0, extentOffset: searchController.text.length);
|
||||||
searchFocus.requestFocus();
|
searchFocus.requestFocus();
|
||||||
|
@ -96,10 +96,6 @@ class _RenameAccountDialogState extends ConsumerState<RenameAccountDialog> {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final credential = widget.credential;
|
final credential = widget.credential;
|
||||||
|
|
||||||
final label = credential.issuer != null
|
|
||||||
? '${credential.issuer} (${credential.name})'
|
|
||||||
: credential.name;
|
|
||||||
|
|
||||||
final remaining = getRemainingKeySpace(
|
final remaining = getRemainingKeySpace(
|
||||||
oathType: credential.oathType,
|
oathType: credential.oathType,
|
||||||
period: credential.period,
|
period: credential.period,
|
||||||
@ -142,7 +138,8 @@ class _RenameAccountDialogState extends ConsumerState<RenameAccountDialog> {
|
|||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text(AppLocalizations.of(context)!.oath_rename(label)),
|
Text(AppLocalizations.of(context)!
|
||||||
|
.oath_rename(getTextName(credential))),
|
||||||
Text(AppLocalizations.of(context)!
|
Text(AppLocalizations.of(context)!
|
||||||
.oath_warning_will_change_account_displayed),
|
.oath_warning_will_change_account_displayed),
|
||||||
TextFormField(
|
TextFormField(
|
||||||
|
@ -47,3 +47,10 @@ Pair<int, int> getRemainingKeySpace(
|
|||||||
remaining - issuerSpace,
|
remaining - issuerSpace,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Gets a textual name for the account, based on the issuer and name.
|
||||||
|
String getTextName(OathCredential credential) {
|
||||||
|
return credential.issuer != null
|
||||||
|
? '${credential.issuer} (${credential.name})'
|
||||||
|
: credential.name;
|
||||||
|
}
|
||||||
|
@ -7,7 +7,9 @@
|
|||||||
#include "generated_plugin_registrant.h"
|
#include "generated_plugin_registrant.h"
|
||||||
|
|
||||||
#include <desktop_drop/desktop_drop_plugin.h>
|
#include <desktop_drop/desktop_drop_plugin.h>
|
||||||
|
#include <local_notifier/local_notifier_plugin.h>
|
||||||
#include <screen_retriever/screen_retriever_plugin.h>
|
#include <screen_retriever/screen_retriever_plugin.h>
|
||||||
|
#include <tray_manager/tray_manager_plugin.h>
|
||||||
#include <url_launcher_linux/url_launcher_plugin.h>
|
#include <url_launcher_linux/url_launcher_plugin.h>
|
||||||
#include <window_manager/window_manager_plugin.h>
|
#include <window_manager/window_manager_plugin.h>
|
||||||
|
|
||||||
@ -15,9 +17,15 @@ void fl_register_plugins(FlPluginRegistry* registry) {
|
|||||||
g_autoptr(FlPluginRegistrar) desktop_drop_registrar =
|
g_autoptr(FlPluginRegistrar) desktop_drop_registrar =
|
||||||
fl_plugin_registry_get_registrar_for_plugin(registry, "DesktopDropPlugin");
|
fl_plugin_registry_get_registrar_for_plugin(registry, "DesktopDropPlugin");
|
||||||
desktop_drop_plugin_register_with_registrar(desktop_drop_registrar);
|
desktop_drop_plugin_register_with_registrar(desktop_drop_registrar);
|
||||||
|
g_autoptr(FlPluginRegistrar) local_notifier_registrar =
|
||||||
|
fl_plugin_registry_get_registrar_for_plugin(registry, "LocalNotifierPlugin");
|
||||||
|
local_notifier_plugin_register_with_registrar(local_notifier_registrar);
|
||||||
g_autoptr(FlPluginRegistrar) screen_retriever_registrar =
|
g_autoptr(FlPluginRegistrar) screen_retriever_registrar =
|
||||||
fl_plugin_registry_get_registrar_for_plugin(registry, "ScreenRetrieverPlugin");
|
fl_plugin_registry_get_registrar_for_plugin(registry, "ScreenRetrieverPlugin");
|
||||||
screen_retriever_plugin_register_with_registrar(screen_retriever_registrar);
|
screen_retriever_plugin_register_with_registrar(screen_retriever_registrar);
|
||||||
|
g_autoptr(FlPluginRegistrar) tray_manager_registrar =
|
||||||
|
fl_plugin_registry_get_registrar_for_plugin(registry, "TrayManagerPlugin");
|
||||||
|
tray_manager_plugin_register_with_registrar(tray_manager_registrar);
|
||||||
g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar =
|
g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar =
|
||||||
fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin");
|
fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin");
|
||||||
url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar);
|
url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar);
|
||||||
|
@ -4,7 +4,9 @@
|
|||||||
|
|
||||||
list(APPEND FLUTTER_PLUGIN_LIST
|
list(APPEND FLUTTER_PLUGIN_LIST
|
||||||
desktop_drop
|
desktop_drop
|
||||||
|
local_notifier
|
||||||
screen_retriever
|
screen_retriever
|
||||||
|
tray_manager
|
||||||
url_launcher_linux
|
url_launcher_linux
|
||||||
window_manager
|
window_manager
|
||||||
)
|
)
|
||||||
|
@ -6,17 +6,21 @@ import FlutterMacOS
|
|||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
import desktop_drop
|
import desktop_drop
|
||||||
|
import local_notifier
|
||||||
import path_provider_foundation
|
import path_provider_foundation
|
||||||
import screen_retriever
|
import screen_retriever
|
||||||
import shared_preferences_foundation
|
import shared_preferences_foundation
|
||||||
|
import tray_manager
|
||||||
import url_launcher_macos
|
import url_launcher_macos
|
||||||
import window_manager
|
import window_manager
|
||||||
|
|
||||||
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
||||||
DesktopDropPlugin.register(with: registry.registrar(forPlugin: "DesktopDropPlugin"))
|
DesktopDropPlugin.register(with: registry.registrar(forPlugin: "DesktopDropPlugin"))
|
||||||
|
LocalNotifierPlugin.register(with: registry.registrar(forPlugin: "LocalNotifierPlugin"))
|
||||||
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
|
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
|
||||||
ScreenRetrieverPlugin.register(with: registry.registrar(forPlugin: "ScreenRetrieverPlugin"))
|
ScreenRetrieverPlugin.register(with: registry.registrar(forPlugin: "ScreenRetrieverPlugin"))
|
||||||
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
|
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
|
||||||
|
TrayManagerPlugin.register(with: registry.registrar(forPlugin: "TrayManagerPlugin"))
|
||||||
UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))
|
UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))
|
||||||
WindowManagerPlugin.register(with: registry.registrar(forPlugin: "WindowManagerPlugin"))
|
WindowManagerPlugin.register(with: registry.registrar(forPlugin: "WindowManagerPlugin"))
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,8 @@ PODS:
|
|||||||
- desktop_drop (0.0.1):
|
- desktop_drop (0.0.1):
|
||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
- FlutterMacOS (1.0.0)
|
- FlutterMacOS (1.0.0)
|
||||||
|
- local_notifier (0.1.0):
|
||||||
|
- FlutterMacOS
|
||||||
- path_provider_foundation (0.0.1):
|
- path_provider_foundation (0.0.1):
|
||||||
- Flutter
|
- Flutter
|
||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
@ -10,6 +12,8 @@ PODS:
|
|||||||
- shared_preferences_foundation (0.0.1):
|
- shared_preferences_foundation (0.0.1):
|
||||||
- Flutter
|
- Flutter
|
||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
|
- tray_manager (0.0.1):
|
||||||
|
- FlutterMacOS
|
||||||
- url_launcher_macos (0.0.1):
|
- url_launcher_macos (0.0.1):
|
||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
- window_manager (0.2.0):
|
- window_manager (0.2.0):
|
||||||
@ -18,9 +22,11 @@ PODS:
|
|||||||
DEPENDENCIES:
|
DEPENDENCIES:
|
||||||
- desktop_drop (from `Flutter/ephemeral/.symlinks/plugins/desktop_drop/macos`)
|
- desktop_drop (from `Flutter/ephemeral/.symlinks/plugins/desktop_drop/macos`)
|
||||||
- FlutterMacOS (from `Flutter/ephemeral`)
|
- FlutterMacOS (from `Flutter/ephemeral`)
|
||||||
|
- local_notifier (from `Flutter/ephemeral/.symlinks/plugins/local_notifier/macos`)
|
||||||
- path_provider_foundation (from `Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/macos`)
|
- path_provider_foundation (from `Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/macos`)
|
||||||
- screen_retriever (from `Flutter/ephemeral/.symlinks/plugins/screen_retriever/macos`)
|
- screen_retriever (from `Flutter/ephemeral/.symlinks/plugins/screen_retriever/macos`)
|
||||||
- shared_preferences_foundation (from `Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/macos`)
|
- shared_preferences_foundation (from `Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/macos`)
|
||||||
|
- tray_manager (from `Flutter/ephemeral/.symlinks/plugins/tray_manager/macos`)
|
||||||
- url_launcher_macos (from `Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos`)
|
- url_launcher_macos (from `Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos`)
|
||||||
- window_manager (from `Flutter/ephemeral/.symlinks/plugins/window_manager/macos`)
|
- window_manager (from `Flutter/ephemeral/.symlinks/plugins/window_manager/macos`)
|
||||||
|
|
||||||
@ -29,12 +35,16 @@ EXTERNAL SOURCES:
|
|||||||
:path: Flutter/ephemeral/.symlinks/plugins/desktop_drop/macos
|
:path: Flutter/ephemeral/.symlinks/plugins/desktop_drop/macos
|
||||||
FlutterMacOS:
|
FlutterMacOS:
|
||||||
:path: Flutter/ephemeral
|
:path: Flutter/ephemeral
|
||||||
|
local_notifier:
|
||||||
|
:path: Flutter/ephemeral/.symlinks/plugins/local_notifier/macos
|
||||||
path_provider_foundation:
|
path_provider_foundation:
|
||||||
:path: Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/macos
|
:path: Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/macos
|
||||||
screen_retriever:
|
screen_retriever:
|
||||||
:path: Flutter/ephemeral/.symlinks/plugins/screen_retriever/macos
|
:path: Flutter/ephemeral/.symlinks/plugins/screen_retriever/macos
|
||||||
shared_preferences_foundation:
|
shared_preferences_foundation:
|
||||||
:path: Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/macos
|
:path: Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/macos
|
||||||
|
tray_manager:
|
||||||
|
:path: Flutter/ephemeral/.symlinks/plugins/tray_manager/macos
|
||||||
url_launcher_macos:
|
url_launcher_macos:
|
||||||
:path: Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos
|
:path: Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos
|
||||||
window_manager:
|
window_manager:
|
||||||
@ -43,10 +53,12 @@ EXTERNAL SOURCES:
|
|||||||
SPEC CHECKSUMS:
|
SPEC CHECKSUMS:
|
||||||
desktop_drop: 69eeff437544aa619c8db7f4481b3a65f7696898
|
desktop_drop: 69eeff437544aa619c8db7f4481b3a65f7696898
|
||||||
FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24
|
FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24
|
||||||
|
local_notifier: e9506bc66fc70311e8bc7291fb70f743c081e4ff
|
||||||
path_provider_foundation: 37748e03f12783f9de2cb2c4eadfaa25fe6d4852
|
path_provider_foundation: 37748e03f12783f9de2cb2c4eadfaa25fe6d4852
|
||||||
screen_retriever: 59634572a57080243dd1bf715e55b6c54f241a38
|
screen_retriever: 59634572a57080243dd1bf715e55b6c54f241a38
|
||||||
shared_preferences_foundation: 297b3ebca31b34ec92be11acd7fb0ba932c822ca
|
shared_preferences_foundation: 986fc17f3d3251412d18b0265f9c64113a8c2472
|
||||||
url_launcher_macos: c04e4fa86382d4f94f6b38f14625708be3ae52e2
|
tray_manager: 9064e219c56d75c476e46b9a21182087930baf90
|
||||||
|
url_launcher_macos: 5335912b679c073563f29d89d33d10d459f95451
|
||||||
window_manager: 3a1844359a6295ab1e47659b1a777e36773cd6e8
|
window_manager: 3a1844359a6295ab1e47659b1a777e36773cd6e8
|
||||||
|
|
||||||
PODFILE CHECKSUM: 353c8bcc5d5b0994e508d035b5431cfe18c1dea7
|
PODFILE CHECKSUM: 353c8bcc5d5b0994e508d035b5431cfe18c1dea7
|
||||||
|
@ -4,6 +4,7 @@ import FlutterMacOS
|
|||||||
@NSApplicationMain
|
@NSApplicationMain
|
||||||
class AppDelegate: FlutterAppDelegate {
|
class AppDelegate: FlutterAppDelegate {
|
||||||
override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {
|
override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {
|
||||||
return true
|
// Keep app running if window closes
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
132
pubspec.lock
132
pubspec.lock
@ -5,18 +5,18 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: _fe_analyzer_shared
|
name: _fe_analyzer_shared
|
||||||
sha256: "569ddca58d535e601dd1584afa117710abc999d036c0cd2c51777fb257df78e8"
|
sha256: e440ac42679dfc04bbbefb58ed225c994bc7e07fccc8a68ec7d3631a127e5da9
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "53.0.0"
|
version: "54.0.0"
|
||||||
analyzer:
|
analyzer:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: analyzer
|
name: analyzer
|
||||||
sha256: "10927c4b7c7c88b1adbca278c3d5531db92e2f4b4abf04e2919a800af965f3f5"
|
sha256: "2c2e3721ee9fb36de92faa060f3480c81b23e904352b087e5c64224b1a044427"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "5.5.0"
|
version: "5.6.0"
|
||||||
archive:
|
archive:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -69,18 +69,18 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: build_daemon
|
name: build_daemon
|
||||||
sha256: "6bc5544ea6ce4428266e7ea680e945c68806c4aae2da0eb5e9ccf38df8d6acbf"
|
sha256: "757153e5d9cd88253cb13f28c2fb55a537dc31fefd98137549895b5beb7c6169"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.1.0"
|
version: "3.1.1"
|
||||||
build_resolvers:
|
build_resolvers:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: build_resolvers
|
name: build_resolvers
|
||||||
sha256: "7c35a3a7868626257d8aee47b51c26b9dba11eaddf3431117ed2744951416aab"
|
sha256: db49b8609ef8c81cca2b310618c3017c00f03a92af44c04d310b907b2d692d95
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.0"
|
version: "2.2.0"
|
||||||
build_runner:
|
build_runner:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description:
|
description:
|
||||||
@ -396,6 +396,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.1"
|
version: "2.0.1"
|
||||||
|
local_notifier:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: local_notifier
|
||||||
|
sha256: cc855aa6362c8840e3d3b35b1c3b058a3a8becdb2b03d5a9aa3f3a1e861f0a03
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.1.5"
|
||||||
logging:
|
logging:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -420,6 +428,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.2.0"
|
version: "0.2.0"
|
||||||
|
menu_base:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: menu_base
|
||||||
|
sha256: "820368014a171bd1241030278e6c2617354f492f5c703d7b7d4570a6b8b84405"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.1.1"
|
||||||
meta:
|
meta:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -488,18 +504,18 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: path_provider_linux
|
name: path_provider_linux
|
||||||
sha256: "2e32f1640f07caef0d3cb993680f181c79e54a3827b997d5ee221490d131fbd9"
|
sha256: "525ad5e07622d19447ad740b1ed5070031f7a5437f44355ae915ff56e986429a"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.8"
|
version: "2.1.9"
|
||||||
path_provider_platform_interface:
|
path_provider_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: path_provider_platform_interface
|
name: path_provider_platform_interface
|
||||||
sha256: f0abc8ebd7253741f05488b4813d936b4d07c6bae3e86148a09e342ee4b08e76
|
sha256: "57585299a729335f1298b43245842678cb9f43a6310351b18fb577d6e33165ec"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.5"
|
version: "2.0.6"
|
||||||
path_provider_windows:
|
path_provider_windows:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -528,10 +544,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: plugin_platform_interface
|
name: plugin_platform_interface
|
||||||
sha256: dbf0f707c78beedc9200146ad3cb0ab4d5da13c246336987be6940f026500d3a
|
sha256: "6a2128648c854906c53fa8e33986fc0247a1116122f9534dd20e3ab9e16a32bc"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.3"
|
version: "2.1.4"
|
||||||
pool:
|
pool:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -591,58 +607,58 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: shared_preferences
|
name: shared_preferences
|
||||||
sha256: "5949029e70abe87f75cfe59d17bf5c397619c4b74a099b10116baeb34786fad9"
|
sha256: ee6257848f822b8481691f20c3e6d2bfee2e9eccb2a3d249907fcfb198c55b41
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.17"
|
version: "2.0.18"
|
||||||
shared_preferences_android:
|
shared_preferences_android:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: shared_preferences_android
|
name: shared_preferences_android
|
||||||
sha256: "955e9736a12ba776bdd261cf030232b30eadfcd9c79b32a3250dd4a494e8c8f7"
|
sha256: a51a4f9375097f94df1c6e0a49c0374440d31ab026b59d58a7e7660675879db4
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.15"
|
version: "2.0.16"
|
||||||
shared_preferences_foundation:
|
shared_preferences_foundation:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: shared_preferences_foundation
|
name: shared_preferences_foundation
|
||||||
sha256: "2b55c18636a4edc529fa5cd44c03d3f3100c00513f518c5127c951978efcccd0"
|
sha256: "6b84fdf06b32bb336f972d373cd38b63734f3461ba56ac2ba01b56d052796259"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.3"
|
version: "2.1.4"
|
||||||
shared_preferences_linux:
|
shared_preferences_linux:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: shared_preferences_linux
|
name: shared_preferences_linux
|
||||||
sha256: f8ea038aa6da37090093974ebdcf4397010605fd2ff65c37a66f9d28394cb874
|
sha256: d7fb71e6e20cd3dfffcc823a28da3539b392e53ed5fc5c2b90b55fdaa8a7e8fa
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.3"
|
version: "2.1.4"
|
||||||
shared_preferences_platform_interface:
|
shared_preferences_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: shared_preferences_platform_interface
|
name: shared_preferences_platform_interface
|
||||||
sha256: da9431745ede5ece47bc26d5d73a9d3c6936ef6945c101a5aca46f62e52c1cf3
|
sha256: "824bfd02713e37603b2bdade0842e47d56e7db32b1dcdd1cae533fb88e2913fc"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.0"
|
version: "2.1.1"
|
||||||
shared_preferences_web:
|
shared_preferences_web:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: shared_preferences_web
|
name: shared_preferences_web
|
||||||
sha256: a4b5bc37fe1b368bbc81f953197d55e12f49d0296e7e412dfe2d2d77d6929958
|
sha256: "6737b757e49ba93de2a233df229d0b6a87728cea1684da828cbc718b65dcf9d7"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.4"
|
version: "2.0.5"
|
||||||
shared_preferences_windows:
|
shared_preferences_windows:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: shared_preferences_windows
|
name: shared_preferences_windows
|
||||||
sha256: "5eaf05ae77658d3521d0e993ede1af962d4b326cd2153d312df716dc250f00c9"
|
sha256: bd014168e8484837c39ef21065b78f305810ceabc1d4f90be6e3b392ce81b46d
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.3"
|
version: "2.1.4"
|
||||||
shelf:
|
shelf:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -659,6 +675,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.3"
|
version: "1.0.3"
|
||||||
|
shortid:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: shortid
|
||||||
|
sha256: d0b40e3dbb50497dad107e19c54ca7de0d1a274eb9b4404991e443dadb9ebedb
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.1.2"
|
||||||
sky_engine:
|
sky_engine:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description: flutter
|
description: flutter
|
||||||
@ -760,6 +784,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.1"
|
version: "1.0.1"
|
||||||
|
tray_manager:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: tray_manager
|
||||||
|
sha256: b1975a05e0c6999e983cf9a58a6a098318c896040ccebac5398a3cc9e43b9c69
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.2.0"
|
||||||
typed_data:
|
typed_data:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -772,66 +804,74 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: url_launcher
|
name: url_launcher
|
||||||
sha256: e8f2efc804810c0f2f5b485f49e7942179f56eabcfe81dce3387fec4bb55876b
|
sha256: "75f2846facd11168d007529d6cd8fcb2b750186bea046af9711f10b907e1587e"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "6.1.9"
|
version: "6.1.10"
|
||||||
url_launcher_android:
|
url_launcher_android:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: url_launcher_android
|
name: url_launcher_android
|
||||||
sha256: "3e2f6dfd2c7d9cd123296cab8ef66cfc2c1a13f5845f42c7a0f365690a8a7dd1"
|
sha256: "1f4d9ebe86f333c15d318f81dcdc08b01d45da44af74552608455ebdc08d9732"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "6.0.23"
|
version: "6.0.24"
|
||||||
url_launcher_ios:
|
url_launcher_ios:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: url_launcher_ios
|
name: url_launcher_ios
|
||||||
sha256: "0a5af0aefdd8cf820dd739886efb1637f1f24489900204f50984634c07a54815"
|
sha256: c9cd648d2f7ab56968e049d4e9116f96a85517f1dd806b96a86ea1018a3a82e5
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "6.1.0"
|
version: "6.1.1"
|
||||||
url_launcher_linux:
|
url_launcher_linux:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: url_launcher_linux
|
name: url_launcher_linux
|
||||||
sha256: "318c42cba924e18180c029be69caf0a1a710191b9ec49bb42b5998fdcccee3cc"
|
sha256: e29039160ab3730e42f3d811dc2a6d5f2864b90a70fb765ea60144b03307f682
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.0.2"
|
version: "3.0.3"
|
||||||
url_launcher_macos:
|
url_launcher_macos:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: url_launcher_macos
|
name: url_launcher_macos
|
||||||
sha256: "41988b55570df53b3dd2a7fc90c76756a963de6a8c5f8e113330cb35992e2094"
|
sha256: "2dddb3291a57b074dade66b5e07e64401dd2487caefd4e9e2f467138d8c7eb06"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.0.2"
|
version: "3.0.3"
|
||||||
url_launcher_platform_interface:
|
url_launcher_platform_interface:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: url_launcher_platform_interface
|
name: url_launcher_platform_interface
|
||||||
sha256: "4eae912628763eb48fc214522e58e942fd16ce195407dbf45638239523c759a6"
|
sha256: "6c9ca697a5ae218ce56cece69d46128169a58aa8653c1b01d26fcd4aad8c4370"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.1"
|
version: "2.1.2"
|
||||||
url_launcher_web:
|
url_launcher_web:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: url_launcher_web
|
name: url_launcher_web
|
||||||
sha256: "44d79408ce9f07052095ef1f9a693c258d6373dc3944249374e30eff7219ccb0"
|
sha256: "574cfbe2390666003c3a1d129bdc4574aaa6728f0c00a4829a81c316de69dd9b"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.14"
|
version: "2.0.15"
|
||||||
url_launcher_windows:
|
url_launcher_windows:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: url_launcher_windows
|
name: url_launcher_windows
|
||||||
sha256: b6217370f8eb1fd85c8890c539f5a639a01ab209a36db82c921ebeacefc7a615
|
sha256: "97c9067950a0d09cbd93e2e3f0383d1403989362b97102fbf446473a48079a4b"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.0.3"
|
version: "3.0.4"
|
||||||
|
uuid:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: uuid
|
||||||
|
sha256: "648e103079f7c64a36dc7d39369cabb358d377078a051d6ae2ad3aa539519313"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.0.7"
|
||||||
vector_graphics:
|
vector_graphics:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -908,10 +948,10 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: window_manager
|
name: window_manager
|
||||||
sha256: "5bdd29dc5f1f3185fc90696373a571d77968e03e5e820fb1ecdbdade3f5d8fff"
|
sha256: "492806c69879f0d28e95472bbe5e8d5940ac8c6e99cc07052fe14946974555ba"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.3.0"
|
version: "0.3.1"
|
||||||
xdg_directories:
|
xdg_directories:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -60,6 +60,8 @@ dependencies:
|
|||||||
file_picker: ^5.2.5
|
file_picker: ^5.2.5
|
||||||
archive: ^3.3.2
|
archive: ^3.3.2
|
||||||
crypto: ^3.0.2
|
crypto: ^3.0.2
|
||||||
|
tray_manager: ^0.2.0
|
||||||
|
local_notifier: ^0.1.5
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
integration_test:
|
integration_test:
|
||||||
@ -99,6 +101,7 @@ flutter:
|
|||||||
- assets/graphics/
|
- assets/graphics/
|
||||||
- assets/licenses/
|
- assets/licenses/
|
||||||
- assets/licenses/raw/
|
- assets/licenses/raw/
|
||||||
|
- resources/icons/
|
||||||
|
|
||||||
# An image asset can refer to one or more resolution-specific "variants", see
|
# An image asset can refer to one or more resolution-specific "variants", see
|
||||||
# https://flutter.dev/assets-and-images/#resolution-aware
|
# https://flutter.dev/assets-and-images/#resolution-aware
|
||||||
|
BIN
resources/icons/com.yubico.yubioath-32x32.png
Executable file
BIN
resources/icons/com.yubico.yubioath-32x32.png
Executable file
Binary file not shown.
After Width: | Height: | Size: 979 B |
122
resources/icons/systray-template.eps
Executable file
122
resources/icons/systray-template.eps
Executable file
@ -0,0 +1,122 @@
|
|||||||
|
%!PS-Adobe-3.0 EPSF-3.0
|
||||||
|
%%Creator: cairo 1.16.0 (https://cairographics.org)
|
||||||
|
%%CreationDate: Wed Feb 22 15:58:05 2023
|
||||||
|
%%Pages: 1
|
||||||
|
%%DocumentData: Clean7Bit
|
||||||
|
%%LanguageLevel: 2
|
||||||
|
%%BoundingBox: 0 0 630 663
|
||||||
|
%%EndComments
|
||||||
|
%%BeginProlog
|
||||||
|
50 dict begin
|
||||||
|
/q { gsave } bind def
|
||||||
|
/Q { grestore } bind def
|
||||||
|
/cm { 6 array astore concat } bind def
|
||||||
|
/w { setlinewidth } bind def
|
||||||
|
/J { setlinecap } bind def
|
||||||
|
/j { setlinejoin } bind def
|
||||||
|
/M { setmiterlimit } bind def
|
||||||
|
/d { setdash } bind def
|
||||||
|
/m { moveto } bind def
|
||||||
|
/l { lineto } bind def
|
||||||
|
/c { curveto } bind def
|
||||||
|
/h { closepath } bind def
|
||||||
|
/re { exch dup neg 3 1 roll 5 3 roll moveto 0 rlineto
|
||||||
|
0 exch rlineto 0 rlineto closepath } bind def
|
||||||
|
/S { stroke } bind def
|
||||||
|
/f { fill } bind def
|
||||||
|
/f* { eofill } bind def
|
||||||
|
/n { newpath } bind def
|
||||||
|
/W { clip } bind def
|
||||||
|
/W* { eoclip } bind def
|
||||||
|
/BT { } bind def
|
||||||
|
/ET { } bind def
|
||||||
|
/BDC { mark 3 1 roll /BDC pdfmark } bind def
|
||||||
|
/EMC { mark /EMC pdfmark } bind def
|
||||||
|
/cairo_store_point { /cairo_point_y exch def /cairo_point_x exch def } def
|
||||||
|
/Tj { show currentpoint cairo_store_point } bind def
|
||||||
|
/TJ {
|
||||||
|
{
|
||||||
|
dup
|
||||||
|
type /stringtype eq
|
||||||
|
{ show } { -0.001 mul 0 cairo_font_matrix dtransform rmoveto } ifelse
|
||||||
|
} forall
|
||||||
|
currentpoint cairo_store_point
|
||||||
|
} bind def
|
||||||
|
/cairo_selectfont { cairo_font_matrix aload pop pop pop 0 0 6 array astore
|
||||||
|
cairo_font exch selectfont cairo_point_x cairo_point_y moveto } bind def
|
||||||
|
/Tf { pop /cairo_font exch def /cairo_font_matrix where
|
||||||
|
{ pop cairo_selectfont } if } bind def
|
||||||
|
/Td { matrix translate cairo_font_matrix matrix concatmatrix dup
|
||||||
|
/cairo_font_matrix exch def dup 4 get exch 5 get cairo_store_point
|
||||||
|
/cairo_font where { pop cairo_selectfont } if } bind def
|
||||||
|
/Tm { 2 copy 8 2 roll 6 array astore /cairo_font_matrix exch def
|
||||||
|
cairo_store_point /cairo_font where { pop cairo_selectfont } if } bind def
|
||||||
|
/g { setgray } bind def
|
||||||
|
/rg { setrgbcolor } bind def
|
||||||
|
/d1 { setcachedevice } bind def
|
||||||
|
/cairo_data_source {
|
||||||
|
CairoDataIndex CairoData length lt
|
||||||
|
{ CairoData CairoDataIndex get /CairoDataIndex CairoDataIndex 1 add def }
|
||||||
|
{ () } ifelse
|
||||||
|
} def
|
||||||
|
/cairo_flush_ascii85_file { cairo_ascii85_file status { cairo_ascii85_file flushfile } if } def
|
||||||
|
/cairo_image { image cairo_flush_ascii85_file } def
|
||||||
|
/cairo_imagemask { imagemask cairo_flush_ascii85_file } def
|
||||||
|
%%EndProlog
|
||||||
|
%%BeginSetup
|
||||||
|
%%EndSetup
|
||||||
|
%%Page: 1 1
|
||||||
|
%%BeginPageSetup
|
||||||
|
%%PageBoundingBox: 0 0 630 663
|
||||||
|
%%EndPageSetup
|
||||||
|
q 0 0 630 663 rectclip
|
||||||
|
1 0 0 -1 0 663 cm q
|
||||||
|
0 g
|
||||||
|
66.832 172.32 m 66.832 150.871 66.758 129.871 66.832 108.871 c 66.98 86.07
|
||||||
|
74.18 65.598 88.355 47.82 c 108.457 22.621 134.855 7.848 166.43 2.145 c
|
||||||
|
196.281 -3.254 224.707 1.695 251.555 15.195 c 275.031 27.047 293.707 44.145
|
||||||
|
305.555 68.07 c 311.855 80.746 315.008 94.172 315.008 108.348 c 315.008
|
||||||
|
172.172 l 319.957 172.547 324.68 172.473 329.258 173.297 c 356.18 178.395
|
||||||
|
371.332 194.297 375.68 221.223 c 375.758 221.82 375.68 222.496 375.68 223.098
|
||||||
|
c 375.68 296.973 l 375.68 297.57 375.605 298.246 375.605 298.32 c 295.355
|
||||||
|
336.57 253.055 399.57 249.156 488.973 c 245.633 488.973 l 180.906 488.973
|
||||||
|
116.18 489.496 51.383 488.746 c 24.906 488.445 5.031 473.672 0.758 445.02
|
||||||
|
c 0.383 442.32 0.082 439.621 0.082 436.848 c 0.082 366.723 -0.145 296.598
|
||||||
|
0.156 226.473 c 0.23 199.547 15.383 179.895 41.633 174.121 c 48.605 172.621
|
||||||
|
55.957 172.996 63.156 172.473 c 64.281 172.395 65.332 172.395 66.832 172.32
|
||||||
|
c h
|
||||||
|
268.055 172.246 m 267.457 152.672 267.605 133.547 266.105 114.57 c 264.457
|
||||||
|
93.945 255.383 76.32 240.008 62.297 c 216.906 41.223 184.133 36.047 157.656
|
||||||
|
50.145 c 128.633 65.598 115.281 91.395 113.48 123.348 c 112.656 138.422
|
||||||
|
113.332 153.57 113.332 168.723 c 113.332 172.246 l h
|
||||||
|
215.332 391.32 m 215.332 386.52 l 215.332 365.297 215.332 344.07 215.258
|
||||||
|
322.848 c 215.258 320.52 215.781 318.945 217.656 317.445 c 226.957 310.172
|
||||||
|
231.98 300.496 233.48 288.797 c 237.23 260.445 209.93 235.621 182.18 242.672
|
||||||
|
c 165.082 247.02 153.98 258.121 150.383 275.297 c 146.781 292.473 152.332
|
||||||
|
307.02 166.281 317.973 c 168.156 319.395 168.832 320.895 168.832 323.223
|
||||||
|
c 168.758 344.445 168.758 365.672 168.758 386.895 c 168.758 391.246 l 184.582
|
||||||
|
391.32 199.656 391.32 215.332 391.32 c h
|
||||||
|
215.332 391.32 m f
|
||||||
|
453.98 311.973 m 551.555 312.871 629.18 390.348 629.18 487.547 c 629.105
|
||||||
|
585.348 550.805 663.047 453.531 662.82 c 355.805 662.598 278.332 584.371
|
||||||
|
278.48 487.246 c 278.707 389.598 356.781 312.645 453.98 311.973 c h
|
||||||
|
404.855 597.871 m 420.98 597.871 436.281 597.797 451.656 597.945 c 454.508
|
||||||
|
597.945 455.707 596.973 456.758 594.422 c 479.707 537.723 502.73 481.098
|
||||||
|
525.68 424.473 c 528.758 416.895 531.832 409.246 534.98 401.445 c 534.082
|
||||||
|
401.297 533.633 401.145 533.18 401.145 c 518.707 401.145 504.156 401.223
|
||||||
|
489.68 401.07 c 486.98 401.07 486.68 402.723 486.082 404.445 c 478.957
|
||||||
|
424.695 471.906 444.945 464.781 465.195 c 461.555 474.27 458.332 483.348
|
||||||
|
454.805 493.246 c 453.98 491.07 453.383 489.723 452.93 488.297 c 442.582
|
||||||
|
460.32 432.23 432.422 421.957 404.371 c 421.055 401.82 419.855 400.996
|
||||||
|
417.156 401.07 c 403.281 401.223 389.406 401.145 375.531 401.145 c 374.48
|
||||||
|
401.145 373.355 401.297 372.008 401.371 c 372.531 402.871 372.906 403.996
|
||||||
|
373.355 405.121 c 376.582 413.371 379.805 421.621 383.031 429.871 c 397.281
|
||||||
|
466.246 411.457 502.621 425.781 538.922 c 427.133 542.371 427.43 545.297
|
||||||
|
425.781 548.82 c 422.707 555.195 420.156 561.871 417.383 568.395 c 413.258
|
||||||
|
577.996 409.133 587.598 404.855 597.871 c h
|
||||||
|
404.855 597.871 m f
|
||||||
|
Q Q
|
||||||
|
showpage
|
||||||
|
%%Trailer
|
||||||
|
end
|
||||||
|
%%EOF
|
@ -7,15 +7,21 @@
|
|||||||
#include "generated_plugin_registrant.h"
|
#include "generated_plugin_registrant.h"
|
||||||
|
|
||||||
#include <desktop_drop/desktop_drop_plugin.h>
|
#include <desktop_drop/desktop_drop_plugin.h>
|
||||||
|
#include <local_notifier/local_notifier_plugin.h>
|
||||||
#include <screen_retriever/screen_retriever_plugin.h>
|
#include <screen_retriever/screen_retriever_plugin.h>
|
||||||
|
#include <tray_manager/tray_manager_plugin.h>
|
||||||
#include <url_launcher_windows/url_launcher_windows.h>
|
#include <url_launcher_windows/url_launcher_windows.h>
|
||||||
#include <window_manager/window_manager_plugin.h>
|
#include <window_manager/window_manager_plugin.h>
|
||||||
|
|
||||||
void RegisterPlugins(flutter::PluginRegistry* registry) {
|
void RegisterPlugins(flutter::PluginRegistry* registry) {
|
||||||
DesktopDropPluginRegisterWithRegistrar(
|
DesktopDropPluginRegisterWithRegistrar(
|
||||||
registry->GetRegistrarForPlugin("DesktopDropPlugin"));
|
registry->GetRegistrarForPlugin("DesktopDropPlugin"));
|
||||||
|
LocalNotifierPluginRegisterWithRegistrar(
|
||||||
|
registry->GetRegistrarForPlugin("LocalNotifierPlugin"));
|
||||||
ScreenRetrieverPluginRegisterWithRegistrar(
|
ScreenRetrieverPluginRegisterWithRegistrar(
|
||||||
registry->GetRegistrarForPlugin("ScreenRetrieverPlugin"));
|
registry->GetRegistrarForPlugin("ScreenRetrieverPlugin"));
|
||||||
|
TrayManagerPluginRegisterWithRegistrar(
|
||||||
|
registry->GetRegistrarForPlugin("TrayManagerPlugin"));
|
||||||
UrlLauncherWindowsRegisterWithRegistrar(
|
UrlLauncherWindowsRegisterWithRegistrar(
|
||||||
registry->GetRegistrarForPlugin("UrlLauncherWindows"));
|
registry->GetRegistrarForPlugin("UrlLauncherWindows"));
|
||||||
WindowManagerPluginRegisterWithRegistrar(
|
WindowManagerPluginRegisterWithRegistrar(
|
||||||
|
@ -4,7 +4,9 @@
|
|||||||
|
|
||||||
list(APPEND FLUTTER_PLUGIN_LIST
|
list(APPEND FLUTTER_PLUGIN_LIST
|
||||||
desktop_drop
|
desktop_drop
|
||||||
|
local_notifier
|
||||||
screen_retriever
|
screen_retriever
|
||||||
|
tray_manager
|
||||||
url_launcher_windows
|
url_launcher_windows
|
||||||
window_manager
|
window_manager
|
||||||
)
|
)
|
||||||
|
@ -172,7 +172,9 @@ bool Win32Window::Create(const std::wstring& title,
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool Win32Window::Show() {
|
bool Win32Window::Show() {
|
||||||
return ShowWindow(window_handle_, SW_SHOWNORMAL);
|
// We show the mindow manually
|
||||||
|
return true;
|
||||||
|
//return ShowWindow(window_handle_, SW_SHOWNORMAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
// static
|
// static
|
||||||
|
Loading…
Reference in New Issue
Block a user