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
|
||||
with:
|
||||
channel: 'stable'
|
||||
flutter-version: '3.7.3'
|
||||
flutter-version: '3.7.5'
|
||||
- run: |
|
||||
flutter config
|
||||
flutter --version
|
||||
|
16
.github/workflows/linux.yml
vendored
16
.github/workflows/linux.yml
vendored
@ -8,7 +8,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
PYVER: 3.11
|
||||
FLUTTER: '3.7.3'
|
||||
FLUTTER: '3.7.5'
|
||||
container:
|
||||
image: ubuntu:18.04
|
||||
env:
|
||||
@ -18,7 +18,7 @@ jobs:
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
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:deadsnakes/ppa
|
||||
apt-get install -qq git python$PYVER-dev python$PYVER-venv
|
||||
@ -82,6 +82,18 @@ jobs:
|
||||
- name: Check generated files
|
||||
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
|
||||
run: |
|
||||
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:
|
||||
channel: 'stable'
|
||||
architecture: 'x64'
|
||||
flutter-version: '3.7.3'
|
||||
flutter-version: '3.7.5'
|
||||
- run: flutter config --enable-macos-desktop
|
||||
- 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
|
||||
with:
|
||||
channel: 'stable'
|
||||
flutter-version: '3.7.3'
|
||||
flutter-version: '3.7.5'
|
||||
- run: flutter config --enable-windows-desktop
|
||||
- run: flutter --version
|
||||
|
||||
|
@ -112,6 +112,7 @@ extension OathFunctions on WidgetTester {
|
||||
}
|
||||
|
||||
await shortWait();
|
||||
|
||||
/// find an AccountView with issuer/name in the account list
|
||||
var matchingAccounts = find.descendant(
|
||||
of: findAccountList(),
|
||||
@ -146,8 +147,11 @@ extension OathFunctions on WidgetTester {
|
||||
expect(accountView, isNotNull);
|
||||
|
||||
if (accountView != null) {
|
||||
await ensureVisible(find.byWidget(accountView));
|
||||
await tap(find.byWidget(accountView));
|
||||
final accountFinder = find.byWidget(accountView);
|
||||
await ensureVisible(accountFinder);
|
||||
final codeButtonFinder = find.descendant(
|
||||
of: accountFinder, matching: find.bySubtype<FilledButton>());
|
||||
await tap(codeButtonFinder);
|
||||
await shortWait();
|
||||
}
|
||||
}
|
||||
|
@ -162,6 +162,8 @@ class AboutPage extends ConsumerWidget {
|
||||
data.insert(0, {
|
||||
'app_version': version,
|
||||
'dart': Platform.version,
|
||||
'os': Platform.operatingSystem,
|
||||
'os_version': Platform.operatingSystemVersion,
|
||||
});
|
||||
final text = const JsonEncoder.withIndent(' ').convert(data);
|
||||
await ref.read(clipboardProvider).setText(text);
|
||||
|
@ -134,5 +134,6 @@ class WindowState with _$WindowState {
|
||||
required bool focused,
|
||||
required bool visible,
|
||||
required bool active,
|
||||
@Default(false) bool hidden,
|
||||
}) = _WindowState;
|
||||
}
|
||||
|
@ -799,6 +799,7 @@ mixin _$WindowState {
|
||||
bool get focused => throw _privateConstructorUsedError;
|
||||
bool get visible => throw _privateConstructorUsedError;
|
||||
bool get active => throw _privateConstructorUsedError;
|
||||
bool get hidden => throw _privateConstructorUsedError;
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
$WindowStateCopyWith<WindowState> get copyWith =>
|
||||
@ -811,7 +812,7 @@ abstract class $WindowStateCopyWith<$Res> {
|
||||
WindowState value, $Res Function(WindowState) then) =
|
||||
_$WindowStateCopyWithImpl<$Res, WindowState>;
|
||||
@useResult
|
||||
$Res call({bool focused, bool visible, bool active});
|
||||
$Res call({bool focused, bool visible, bool active, bool hidden});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@ -830,6 +831,7 @@ class _$WindowStateCopyWithImpl<$Res, $Val extends WindowState>
|
||||
Object? focused = null,
|
||||
Object? visible = null,
|
||||
Object? active = null,
|
||||
Object? hidden = null,
|
||||
}) {
|
||||
return _then(_value.copyWith(
|
||||
focused: null == focused
|
||||
@ -844,6 +846,10 @@ class _$WindowStateCopyWithImpl<$Res, $Val extends WindowState>
|
||||
? _value.active
|
||||
: active // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
hidden: null == hidden
|
||||
? _value.hidden
|
||||
: hidden // ignore: cast_nullable_to_non_nullable
|
||||
as bool,
|
||||
) as $Val);
|
||||
}
|
||||
}
|
||||
@ -856,7 +862,7 @@ abstract class _$$_WindowStateCopyWith<$Res>
|
||||
__$$_WindowStateCopyWithImpl<$Res>;
|
||||
@override
|
||||
@useResult
|
||||
$Res call({bool focused, bool visible, bool active});
|
||||
$Res call({bool focused, bool visible, bool active, bool hidden});
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
@ -873,6 +879,7 @@ class __$$_WindowStateCopyWithImpl<$Res>
|
||||
Object? focused = null,
|
||||
Object? visible = null,
|
||||
Object? active = null,
|
||||
Object? hidden = null,
|
||||
}) {
|
||||
return _then(_$_WindowState(
|
||||
focused: null == focused
|
||||
@ -887,6 +894,10 @@ class __$$_WindowStateCopyWithImpl<$Res>
|
||||
? _value.active
|
||||
: active // ignore: cast_nullable_to_non_nullable
|
||||
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 {
|
||||
_$_WindowState(
|
||||
{required this.focused, required this.visible, required this.active});
|
||||
{required this.focused,
|
||||
required this.visible,
|
||||
required this.active,
|
||||
this.hidden = false});
|
||||
|
||||
@override
|
||||
final bool focused;
|
||||
@ -903,10 +917,13 @@ class _$_WindowState implements _WindowState {
|
||||
final bool visible;
|
||||
@override
|
||||
final bool active;
|
||||
@override
|
||||
@JsonKey()
|
||||
final bool hidden;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'WindowState(focused: $focused, visible: $visible, active: $active)';
|
||||
return 'WindowState(focused: $focused, visible: $visible, active: $active, hidden: $hidden)';
|
||||
}
|
||||
|
||||
@override
|
||||
@ -916,11 +933,13 @@ class _$_WindowState implements _WindowState {
|
||||
other is _$_WindowState &&
|
||||
(identical(other.focused, focused) || other.focused == focused) &&
|
||||
(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
|
||||
int get hashCode => Object.hash(runtimeType, focused, visible, active);
|
||||
int get hashCode =>
|
||||
Object.hash(runtimeType, focused, visible, active, hidden);
|
||||
|
||||
@JsonKey(ignore: true)
|
||||
@override
|
||||
@ -933,7 +952,8 @@ abstract class _WindowState implements WindowState {
|
||||
factory _WindowState(
|
||||
{required final bool focused,
|
||||
required final bool visible,
|
||||
required final bool active}) = _$_WindowState;
|
||||
required final bool active,
|
||||
final bool hidden}) = _$_WindowState;
|
||||
|
||||
@override
|
||||
bool get focused;
|
||||
@ -942,6 +962,8 @@ abstract class _WindowState implements WindowState {
|
||||
@override
|
||||
bool get active;
|
||||
@override
|
||||
bool get hidden;
|
||||
@override
|
||||
@JsonKey(ignore: true)
|
||||
_$$_WindowStateCopyWith<_$_WindowState> get copyWith =>
|
||||
throw _privateConstructorUsedError;
|
||||
|
@ -24,6 +24,7 @@ import 'package:window_manager/window_manager.dart';
|
||||
import '../about_page.dart';
|
||||
import '../android/views/android_settings_page.dart';
|
||||
import '../core/state.dart';
|
||||
import '../desktop/state.dart';
|
||||
import '../oath/keys.dart';
|
||||
import '../settings_page.dart';
|
||||
import 'message.dart';
|
||||
@ -42,6 +43,10 @@ class CloseIntent extends Intent {
|
||||
const CloseIntent();
|
||||
}
|
||||
|
||||
class HideIntent extends Intent {
|
||||
const HideIntent();
|
||||
}
|
||||
|
||||
class SearchIntent extends Intent {
|
||||
const SearchIntent();
|
||||
}
|
||||
@ -77,6 +82,12 @@ Widget registerGlobalShortcuts(
|
||||
windowManager.close();
|
||||
return null;
|
||||
}),
|
||||
HideIntent: CallbackAction<HideIntent>(onInvoke: (_) {
|
||||
if (isDesktop) {
|
||||
ref.read(desktopWindowStateProvider.notifier).setWindowHidden(true);
|
||||
}
|
||||
return null;
|
||||
}),
|
||||
SearchIntent: CallbackAction<SearchIntent>(onInvoke: (intent) {
|
||||
// If the OATH view doesn't have focus, but is shown, find and select the search bar.
|
||||
final searchContext = searchAccountsField.currentContext;
|
||||
@ -136,8 +147,7 @@ Widget registerGlobalShortcuts(
|
||||
child: Shortcuts(
|
||||
shortcuts: {
|
||||
LogicalKeySet(ctrlOrCmd, LogicalKeyboardKey.keyC): const CopyIntent(),
|
||||
LogicalKeySet(ctrlOrCmd, LogicalKeyboardKey.keyW):
|
||||
const CloseIntent(),
|
||||
LogicalKeySet(ctrlOrCmd, LogicalKeyboardKey.keyW): const HideIntent(),
|
||||
LogicalKeySet(ctrlOrCmd, LogicalKeyboardKey.keyF):
|
||||
const SearchIntent(),
|
||||
if (isDesktop) ...{
|
||||
|
@ -15,11 +15,13 @@
|
||||
*/
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:ui';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:logging/logging.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 '../core/state.dart';
|
||||
@ -40,6 +42,32 @@ final supportedThemesProvider = StateProvider<List<ThemeMode>>(
|
||||
(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>(
|
||||
(ref) => ThemeModeNotifier(
|
||||
ref.watch(prefProvider), ref.read(supportedThemesProvider)),
|
||||
|
@ -17,6 +17,7 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:local_notifier/local_notifier.dart';
|
||||
|
||||
import '../message.dart';
|
||||
|
||||
@ -130,6 +131,24 @@ UserInteractionController promptUserInteraction(
|
||||
required String description,
|
||||
Widget? icon,
|
||||
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;
|
||||
final controller = _UserInteractionController(
|
||||
@ -164,3 +183,60 @@ UserInteractionController promptUserInteraction(
|
||||
|
||||
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_gen/gen_l10n/app_localizations.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:local_notifier/local_notifier.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'package:window_manager/window_manager.dart';
|
||||
@ -47,14 +48,15 @@ import 'rpc.dart';
|
||||
import 'devices.dart';
|
||||
import 'qr_scanner.dart';
|
||||
import 'state.dart';
|
||||
import 'systray.dart';
|
||||
|
||||
final _log = Logger('desktop.init');
|
||||
const String _keyWidth = 'DESKTOP_WINDOW_WIDTH';
|
||||
const String _keyHeight = 'DESKTOP_WINDOW_HEIGHT';
|
||||
|
||||
class _WindowResizeListener extends WindowListener {
|
||||
class _WindowEventListener extends WindowListener {
|
||||
final SharedPreferences _prefs;
|
||||
_WindowResizeListener(this._prefs);
|
||||
_WindowEventListener(this._prefs);
|
||||
|
||||
@override
|
||||
void onWindowResize() async {
|
||||
@ -62,6 +64,13 @@ class _WindowResizeListener extends WindowListener {
|
||||
await _prefs.setDouble(_keyWidth, size.width);
|
||||
await _prefs.setDouble(_keyHeight, size.height);
|
||||
}
|
||||
|
||||
@override
|
||||
void onWindowClose() async {
|
||||
if (Platform.isMacOS) {
|
||||
await windowManager.destroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<Widget> initialize(List<String> argv) async {
|
||||
@ -69,14 +78,24 @@ Future<Widget> initialize(List<String> argv) async {
|
||||
|
||||
await windowManager.ensureInitialized();
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
final isHidden = prefs.getBool(windowHidden) ?? false;
|
||||
|
||||
unawaited(windowManager.waitUntilReadyToShow().then((_) async {
|
||||
await windowManager.setMinimumSize(const Size(270, 0));
|
||||
final width = prefs.getDouble(_keyWidth) ?? 400;
|
||||
final height = prefs.getDouble(_keyHeight) ?? 720;
|
||||
await windowManager.setSize(Size(width, height));
|
||||
await windowManager.show();
|
||||
windowManager.addListener(_WindowResizeListener(prefs));
|
||||
unawaited(windowManager
|
||||
.waitUntilReadyToShow(WindowOptions(
|
||||
minimumSize: const Size(270, 0),
|
||||
size: Size(
|
||||
prefs.getDouble(_keyWidth) ?? 400,
|
||||
prefs.getDouble(_keyHeight) ?? 720,
|
||||
),
|
||||
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.
|
||||
@ -106,6 +125,11 @@ Future<Widget> initialize(List<String> argv) async {
|
||||
final rpcFuture = _initHelper(exe!);
|
||||
_initLicenses();
|
||||
|
||||
await localNotifier.setup(
|
||||
appName: 'Yubico Authenticator',
|
||||
shortcutPolicy: ShortcutPolicy.ignore,
|
||||
);
|
||||
|
||||
return ProviderScope(
|
||||
overrides: [
|
||||
supportedAppsProvider.overrideWithValue([
|
||||
@ -155,6 +179,9 @@ Future<Widget> initialize(List<String> argv) async {
|
||||
ref.read(rpcProvider).valueOrNull?.setLogLevel(level);
|
||||
});
|
||||
|
||||
// Initialize systray
|
||||
ref.watch(systrayProvider);
|
||||
|
||||
// Show a loading or error page while the Helper isn't ready
|
||||
return ref.watch(rpcProvider).when(
|
||||
data: (data) => const MainPage(),
|
||||
|
@ -20,6 +20,7 @@ import 'dart:math';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
|
||||
import '../../app/logging.dart';
|
||||
@ -192,9 +193,9 @@ class _DesktopOathStateNotifier extends OathStateNotifier {
|
||||
}
|
||||
|
||||
final desktopOathCredentialListProvider = StateNotifierProvider.autoDispose
|
||||
.family<OathCredentialListNotifier, List<OathPair>?, DevicePath>(
|
||||
.family<DesktopCredentialListNotifier, List<OathPair>?, DevicePath>(
|
||||
(ref, devicePath) {
|
||||
var notifier = _DesktopCredentialListNotifier(
|
||||
var notifier = DesktopCredentialListNotifier(
|
||||
ref.watch(withContextProvider),
|
||||
ref.watch(_sessionProvider(devicePath)),
|
||||
ref.watch(oathStateProvider(devicePath)
|
||||
@ -203,6 +204,7 @@ final desktopOathCredentialListProvider = StateNotifierProvider.autoDispose
|
||||
ref.listen<WindowState>(windowStateProvider, (_, windowState) {
|
||||
notifier._notifyWindowState(windowState);
|
||||
}, fireImmediately: true);
|
||||
|
||||
return notifier;
|
||||
},
|
||||
);
|
||||
@ -225,12 +227,12 @@ String _formatSteam(String response) {
|
||||
return value;
|
||||
}
|
||||
|
||||
class _DesktopCredentialListNotifier extends OathCredentialListNotifier {
|
||||
class DesktopCredentialListNotifier extends OathCredentialListNotifier {
|
||||
final WithContext _withContext;
|
||||
final RpcNodeSession _session;
|
||||
final bool _locked;
|
||||
Timer? _timer;
|
||||
_DesktopCredentialListNotifier(this._withContext, this._session, this._locked)
|
||||
DesktopCredentialListNotifier(this._withContext, this._session, this._locked)
|
||||
: super();
|
||||
|
||||
void _notifyWindowState(WindowState windowState) {
|
||||
@ -251,7 +253,7 @@ class _DesktopCredentialListNotifier extends OathCredentialListNotifier {
|
||||
|
||||
@override
|
||||
Future<OathCode> calculate(OathCredential credential,
|
||||
{bool update = true}) async {
|
||||
{bool update = true, bool headless = false}) async {
|
||||
var now = DateTime.now().millisecondsSinceEpoch ~/ 1000;
|
||||
if (update) {
|
||||
// Manually triggered, need to pad timer to avoid immediate expiration
|
||||
@ -264,12 +266,16 @@ class _DesktopCredentialListNotifier extends OathCredentialListNotifier {
|
||||
signaler.signals.listen((signal) async {
|
||||
if (signal.status == 'touch') {
|
||||
controller = await _withContext(
|
||||
(context) async => promptUserInteraction(
|
||||
context,
|
||||
icon: const Icon(Icons.touch_app),
|
||||
title: 'Touch Required',
|
||||
description: 'Touch the button on your YubiKey now.',
|
||||
),
|
||||
(context) async {
|
||||
final l10n = AppLocalizations.of(context)!;
|
||||
return promptUserInteraction(
|
||||
context,
|
||||
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:shared_preferences/shared_preferences.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/state.dart';
|
||||
import '../core/state.dart';
|
||||
@ -58,20 +58,23 @@ class _RpcStateNotifier extends StateNotifier<RpcState> {
|
||||
}
|
||||
}
|
||||
|
||||
final _windowStateProvider =
|
||||
StateNotifierProvider<_WindowStateNotifier, WindowState>(
|
||||
(ref) => _WindowStateNotifier());
|
||||
final desktopWindowStateProvider =
|
||||
StateNotifierProvider<DesktopWindowStateNotifier, WindowState>(
|
||||
(ref) => DesktopWindowStateNotifier(ref.watch(prefProvider)));
|
||||
|
||||
final desktopWindowStateProvider = Provider<WindowState>(
|
||||
(ref) => ref.watch(_windowStateProvider),
|
||||
);
|
||||
const String windowHidden = 'DESKTOP_WINDOW_HIDDEN';
|
||||
|
||||
class _WindowStateNotifier extends StateNotifier<WindowState>
|
||||
class DesktopWindowStateNotifier extends StateNotifier<WindowState>
|
||||
with WindowListener {
|
||||
final SharedPreferences _prefs;
|
||||
Timer? _idleTimer;
|
||||
|
||||
_WindowStateNotifier()
|
||||
: super(WindowState(focused: true, visible: true, active: true)) {
|
||||
DesktopWindowStateNotifier(this._prefs)
|
||||
: super(WindowState(
|
||||
focused: true,
|
||||
visible: true,
|
||||
active: true,
|
||||
hidden: _prefs.getBool(windowHidden) ?? false)) {
|
||||
_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
|
||||
void dispose() {
|
||||
windowManager.removeListener(this);
|
||||
@ -101,6 +115,7 @@ class _WindowStateNotifier extends StateNotifier<WindowState>
|
||||
}
|
||||
|
||||
@override
|
||||
@protected
|
||||
void onWindowEvent(String eventName) {
|
||||
if (mounted) {
|
||||
switch (eventName) {
|
||||
@ -144,7 +159,29 @@ class _DesktopClipboard extends AppClipboard {
|
||||
|
||||
@override
|
||||
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_scan_qr": "Scan QR code",
|
||||
"oath_require_touch": "Require touch",
|
||||
"oath_touch_required": "Touch Required",
|
||||
"oath_touch_now": "Touch the button on your YubiKey now",
|
||||
"oath_sec": "sec",
|
||||
"oath_digits": "digits",
|
||||
"oath_success_delete_account": "Account deleted",
|
||||
@ -114,6 +116,7 @@
|
||||
"mgmt_toggle_applications": "Toggle applications",
|
||||
"mgmt_save": "Save",
|
||||
|
||||
"general_app_name": "Yubico Authenticator",
|
||||
"general_about": "About",
|
||||
"general_terms_of_use": "Terms of use",
|
||||
"general_privacy_policy": "Privacy policy",
|
||||
@ -139,6 +142,9 @@
|
||||
"general_setup": "Setup",
|
||||
"general_manage": "Manage",
|
||||
"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_keep_touching_yubikey": "Keep touching your YubiKey repeatedly\u2026",
|
||||
@ -281,5 +287,15 @@
|
||||
"placeholders": {
|
||||
"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
|
||||
@protected
|
||||
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);
|
||||
@ -193,11 +200,6 @@ class FilteredCredentialsNotifier extends StateNotifier<List<OathPair>> {
|
||||
.toLowerCase()
|
||||
.contains(query.toLowerCase()))
|
||||
.where((pair) => pair.credential.issuer != '_hidden')
|
||||
.toList()
|
||||
..sort((a, b) {
|
||||
String searchKey(OathCredential c) =>
|
||||
((c.issuer ?? '') + c.name).toLowerCase();
|
||||
return searchKey(a.credential).compareTo(searchKey(b.credential));
|
||||
}),
|
||||
.toList(),
|
||||
);
|
||||
}
|
@ -156,7 +156,10 @@ class AccountDialog extends ConsumerWidget {
|
||||
if (helper.code == null &&
|
||||
(isDesktop || node.transport == Transport.usb)) {
|
||||
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(
|
||||
|
@ -26,6 +26,7 @@ import '../../widgets/responsive_dialog.dart';
|
||||
import '../models.dart';
|
||||
import '../state.dart';
|
||||
import '../keys.dart' as keys;
|
||||
import 'utils.dart';
|
||||
|
||||
class DeleteAccountDialog extends ConsumerWidget {
|
||||
final DeviceNode device;
|
||||
@ -34,10 +35,6 @@ class DeleteAccountDialog extends ConsumerWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final label = credential.issuer != null
|
||||
? '${credential.issuer} (${credential.name})'
|
||||
: credential.name;
|
||||
|
||||
return ResponsiveDialog(
|
||||
title: Text(AppLocalizations.of(context)!.oath_delete_account),
|
||||
actions: [
|
||||
@ -75,7 +72,8 @@ class DeleteAccountDialog extends ConsumerWidget {
|
||||
AppLocalizations.of(context)!.oath_warning_disable_this_cred,
|
||||
style: Theme.of(context).textTheme.bodyLarge,
|
||||
),
|
||||
Text('${AppLocalizations.of(context)!.oath_account} $label'),
|
||||
Text(
|
||||
'${AppLocalizations.of(context)!.oath_account} ${getTextName(credential)}'),
|
||||
]
|
||||
.map((e) => Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 8.0),
|
||||
|
@ -126,7 +126,7 @@ class _UnlockedViewState extends ConsumerState<_UnlockedView> {
|
||||
}
|
||||
return Actions(
|
||||
actions: {
|
||||
SearchIntent: CallbackAction(onInvoke: (_) {
|
||||
SearchIntent: CallbackAction<SearchIntent>(onInvoke: (_) {
|
||||
searchController.selection = TextSelection(
|
||||
baseOffset: 0, extentOffset: searchController.text.length);
|
||||
searchFocus.requestFocus();
|
||||
|
@ -96,10 +96,6 @@ class _RenameAccountDialogState extends ConsumerState<RenameAccountDialog> {
|
||||
Widget build(BuildContext context) {
|
||||
final credential = widget.credential;
|
||||
|
||||
final label = credential.issuer != null
|
||||
? '${credential.issuer} (${credential.name})'
|
||||
: credential.name;
|
||||
|
||||
final remaining = getRemainingKeySpace(
|
||||
oathType: credential.oathType,
|
||||
period: credential.period,
|
||||
@ -142,7 +138,8 @@ class _RenameAccountDialogState extends ConsumerState<RenameAccountDialog> {
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(AppLocalizations.of(context)!.oath_rename(label)),
|
||||
Text(AppLocalizations.of(context)!
|
||||
.oath_rename(getTextName(credential))),
|
||||
Text(AppLocalizations.of(context)!
|
||||
.oath_warning_will_change_account_displayed),
|
||||
TextFormField(
|
||||
|
@ -47,3 +47,10 @@ Pair<int, int> getRemainingKeySpace(
|
||||
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 <desktop_drop/desktop_drop_plugin.h>
|
||||
#include <local_notifier/local_notifier_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 <window_manager/window_manager_plugin.h>
|
||||
|
||||
@ -15,9 +17,15 @@ void fl_register_plugins(FlPluginRegistry* registry) {
|
||||
g_autoptr(FlPluginRegistrar) desktop_drop_registrar =
|
||||
fl_plugin_registry_get_registrar_for_plugin(registry, "DesktopDropPlugin");
|
||||
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 =
|
||||
fl_plugin_registry_get_registrar_for_plugin(registry, "ScreenRetrieverPlugin");
|
||||
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 =
|
||||
fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin");
|
||||
url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar);
|
||||
|
@ -4,7 +4,9 @@
|
||||
|
||||
list(APPEND FLUTTER_PLUGIN_LIST
|
||||
desktop_drop
|
||||
local_notifier
|
||||
screen_retriever
|
||||
tray_manager
|
||||
url_launcher_linux
|
||||
window_manager
|
||||
)
|
||||
|
@ -6,17 +6,21 @@ import FlutterMacOS
|
||||
import Foundation
|
||||
|
||||
import desktop_drop
|
||||
import local_notifier
|
||||
import path_provider_foundation
|
||||
import screen_retriever
|
||||
import shared_preferences_foundation
|
||||
import tray_manager
|
||||
import url_launcher_macos
|
||||
import window_manager
|
||||
|
||||
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
||||
DesktopDropPlugin.register(with: registry.registrar(forPlugin: "DesktopDropPlugin"))
|
||||
LocalNotifierPlugin.register(with: registry.registrar(forPlugin: "LocalNotifierPlugin"))
|
||||
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
|
||||
ScreenRetrieverPlugin.register(with: registry.registrar(forPlugin: "ScreenRetrieverPlugin"))
|
||||
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
|
||||
TrayManagerPlugin.register(with: registry.registrar(forPlugin: "TrayManagerPlugin"))
|
||||
UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))
|
||||
WindowManagerPlugin.register(with: registry.registrar(forPlugin: "WindowManagerPlugin"))
|
||||
}
|
||||
|
@ -2,6 +2,8 @@ PODS:
|
||||
- desktop_drop (0.0.1):
|
||||
- FlutterMacOS
|
||||
- FlutterMacOS (1.0.0)
|
||||
- local_notifier (0.1.0):
|
||||
- FlutterMacOS
|
||||
- path_provider_foundation (0.0.1):
|
||||
- Flutter
|
||||
- FlutterMacOS
|
||||
@ -10,6 +12,8 @@ PODS:
|
||||
- shared_preferences_foundation (0.0.1):
|
||||
- Flutter
|
||||
- FlutterMacOS
|
||||
- tray_manager (0.0.1):
|
||||
- FlutterMacOS
|
||||
- url_launcher_macos (0.0.1):
|
||||
- FlutterMacOS
|
||||
- window_manager (0.2.0):
|
||||
@ -18,9 +22,11 @@ PODS:
|
||||
DEPENDENCIES:
|
||||
- desktop_drop (from `Flutter/ephemeral/.symlinks/plugins/desktop_drop/macos`)
|
||||
- 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`)
|
||||
- screen_retriever (from `Flutter/ephemeral/.symlinks/plugins/screen_retriever/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`)
|
||||
- window_manager (from `Flutter/ephemeral/.symlinks/plugins/window_manager/macos`)
|
||||
|
||||
@ -29,12 +35,16 @@ EXTERNAL SOURCES:
|
||||
:path: Flutter/ephemeral/.symlinks/plugins/desktop_drop/macos
|
||||
FlutterMacOS:
|
||||
:path: Flutter/ephemeral
|
||||
local_notifier:
|
||||
:path: Flutter/ephemeral/.symlinks/plugins/local_notifier/macos
|
||||
path_provider_foundation:
|
||||
:path: Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/macos
|
||||
screen_retriever:
|
||||
:path: Flutter/ephemeral/.symlinks/plugins/screen_retriever/macos
|
||||
shared_preferences_foundation:
|
||||
:path: Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/macos
|
||||
tray_manager:
|
||||
:path: Flutter/ephemeral/.symlinks/plugins/tray_manager/macos
|
||||
url_launcher_macos:
|
||||
:path: Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos
|
||||
window_manager:
|
||||
@ -43,10 +53,12 @@ EXTERNAL SOURCES:
|
||||
SPEC CHECKSUMS:
|
||||
desktop_drop: 69eeff437544aa619c8db7f4481b3a65f7696898
|
||||
FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24
|
||||
local_notifier: e9506bc66fc70311e8bc7291fb70f743c081e4ff
|
||||
path_provider_foundation: 37748e03f12783f9de2cb2c4eadfaa25fe6d4852
|
||||
screen_retriever: 59634572a57080243dd1bf715e55b6c54f241a38
|
||||
shared_preferences_foundation: 297b3ebca31b34ec92be11acd7fb0ba932c822ca
|
||||
url_launcher_macos: c04e4fa86382d4f94f6b38f14625708be3ae52e2
|
||||
shared_preferences_foundation: 986fc17f3d3251412d18b0265f9c64113a8c2472
|
||||
tray_manager: 9064e219c56d75c476e46b9a21182087930baf90
|
||||
url_launcher_macos: 5335912b679c073563f29d89d33d10d459f95451
|
||||
window_manager: 3a1844359a6295ab1e47659b1a777e36773cd6e8
|
||||
|
||||
PODFILE CHECKSUM: 353c8bcc5d5b0994e508d035b5431cfe18c1dea7
|
||||
|
@ -4,6 +4,7 @@ import FlutterMacOS
|
||||
@NSApplicationMain
|
||||
class AppDelegate: FlutterAppDelegate {
|
||||
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
|
||||
description:
|
||||
name: _fe_analyzer_shared
|
||||
sha256: "569ddca58d535e601dd1584afa117710abc999d036c0cd2c51777fb257df78e8"
|
||||
sha256: e440ac42679dfc04bbbefb58ed225c994bc7e07fccc8a68ec7d3631a127e5da9
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "53.0.0"
|
||||
version: "54.0.0"
|
||||
analyzer:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: analyzer
|
||||
sha256: "10927c4b7c7c88b1adbca278c3d5531db92e2f4b4abf04e2919a800af965f3f5"
|
||||
sha256: "2c2e3721ee9fb36de92faa060f3480c81b23e904352b087e5c64224b1a044427"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.5.0"
|
||||
version: "5.6.0"
|
||||
archive:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@ -69,18 +69,18 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: build_daemon
|
||||
sha256: "6bc5544ea6ce4428266e7ea680e945c68806c4aae2da0eb5e9ccf38df8d6acbf"
|
||||
sha256: "757153e5d9cd88253cb13f28c2fb55a537dc31fefd98137549895b5beb7c6169"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.1.0"
|
||||
version: "3.1.1"
|
||||
build_resolvers:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: build_resolvers
|
||||
sha256: "7c35a3a7868626257d8aee47b51c26b9dba11eaddf3431117ed2744951416aab"
|
||||
sha256: db49b8609ef8c81cca2b310618c3017c00f03a92af44c04d310b907b2d692d95
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.0"
|
||||
version: "2.2.0"
|
||||
build_runner:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
@ -396,6 +396,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
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:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@ -420,6 +428,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
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:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -488,18 +504,18 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_linux
|
||||
sha256: "2e32f1640f07caef0d3cb993680f181c79e54a3827b997d5ee221490d131fbd9"
|
||||
sha256: "525ad5e07622d19447ad740b1ed5070031f7a5437f44355ae915ff56e986429a"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.8"
|
||||
version: "2.1.9"
|
||||
path_provider_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_platform_interface
|
||||
sha256: f0abc8ebd7253741f05488b4813d936b4d07c6bae3e86148a09e342ee4b08e76
|
||||
sha256: "57585299a729335f1298b43245842678cb9f43a6310351b18fb577d6e33165ec"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.5"
|
||||
version: "2.0.6"
|
||||
path_provider_windows:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -528,10 +544,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: plugin_platform_interface
|
||||
sha256: dbf0f707c78beedc9200146ad3cb0ab4d5da13c246336987be6940f026500d3a
|
||||
sha256: "6a2128648c854906c53fa8e33986fc0247a1116122f9534dd20e3ab9e16a32bc"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.3"
|
||||
version: "2.1.4"
|
||||
pool:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -591,58 +607,58 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: shared_preferences
|
||||
sha256: "5949029e70abe87f75cfe59d17bf5c397619c4b74a099b10116baeb34786fad9"
|
||||
sha256: ee6257848f822b8481691f20c3e6d2bfee2e9eccb2a3d249907fcfb198c55b41
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.17"
|
||||
version: "2.0.18"
|
||||
shared_preferences_android:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_android
|
||||
sha256: "955e9736a12ba776bdd261cf030232b30eadfcd9c79b32a3250dd4a494e8c8f7"
|
||||
sha256: a51a4f9375097f94df1c6e0a49c0374440d31ab026b59d58a7e7660675879db4
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.15"
|
||||
version: "2.0.16"
|
||||
shared_preferences_foundation:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_foundation
|
||||
sha256: "2b55c18636a4edc529fa5cd44c03d3f3100c00513f518c5127c951978efcccd0"
|
||||
sha256: "6b84fdf06b32bb336f972d373cd38b63734f3461ba56ac2ba01b56d052796259"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.3"
|
||||
version: "2.1.4"
|
||||
shared_preferences_linux:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_linux
|
||||
sha256: f8ea038aa6da37090093974ebdcf4397010605fd2ff65c37a66f9d28394cb874
|
||||
sha256: d7fb71e6e20cd3dfffcc823a28da3539b392e53ed5fc5c2b90b55fdaa8a7e8fa
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.3"
|
||||
version: "2.1.4"
|
||||
shared_preferences_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_platform_interface
|
||||
sha256: da9431745ede5ece47bc26d5d73a9d3c6936ef6945c101a5aca46f62e52c1cf3
|
||||
sha256: "824bfd02713e37603b2bdade0842e47d56e7db32b1dcdd1cae533fb88e2913fc"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.0"
|
||||
version: "2.1.1"
|
||||
shared_preferences_web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_web
|
||||
sha256: a4b5bc37fe1b368bbc81f953197d55e12f49d0296e7e412dfe2d2d77d6929958
|
||||
sha256: "6737b757e49ba93de2a233df229d0b6a87728cea1684da828cbc718b65dcf9d7"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.4"
|
||||
version: "2.0.5"
|
||||
shared_preferences_windows:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_windows
|
||||
sha256: "5eaf05ae77658d3521d0e993ede1af962d4b326cd2153d312df716dc250f00c9"
|
||||
sha256: bd014168e8484837c39ef21065b78f305810ceabc1d4f90be6e3b392ce81b46d
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.3"
|
||||
version: "2.1.4"
|
||||
shelf:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -659,6 +675,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.3"
|
||||
shortid:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shortid
|
||||
sha256: d0b40e3dbb50497dad107e19c54ca7de0d1a274eb9b4404991e443dadb9ebedb
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.1.2"
|
||||
sky_engine:
|
||||
dependency: transitive
|
||||
description: flutter
|
||||
@ -760,6 +784,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
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:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -772,66 +804,74 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: url_launcher
|
||||
sha256: e8f2efc804810c0f2f5b485f49e7942179f56eabcfe81dce3387fec4bb55876b
|
||||
sha256: "75f2846facd11168d007529d6cd8fcb2b750186bea046af9711f10b907e1587e"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.1.9"
|
||||
version: "6.1.10"
|
||||
url_launcher_android:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_android
|
||||
sha256: "3e2f6dfd2c7d9cd123296cab8ef66cfc2c1a13f5845f42c7a0f365690a8a7dd1"
|
||||
sha256: "1f4d9ebe86f333c15d318f81dcdc08b01d45da44af74552608455ebdc08d9732"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.0.23"
|
||||
version: "6.0.24"
|
||||
url_launcher_ios:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_ios
|
||||
sha256: "0a5af0aefdd8cf820dd739886efb1637f1f24489900204f50984634c07a54815"
|
||||
sha256: c9cd648d2f7ab56968e049d4e9116f96a85517f1dd806b96a86ea1018a3a82e5
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.1.0"
|
||||
version: "6.1.1"
|
||||
url_launcher_linux:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_linux
|
||||
sha256: "318c42cba924e18180c029be69caf0a1a710191b9ec49bb42b5998fdcccee3cc"
|
||||
sha256: e29039160ab3730e42f3d811dc2a6d5f2864b90a70fb765ea60144b03307f682
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.2"
|
||||
version: "3.0.3"
|
||||
url_launcher_macos:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_macos
|
||||
sha256: "41988b55570df53b3dd2a7fc90c76756a963de6a8c5f8e113330cb35992e2094"
|
||||
sha256: "2dddb3291a57b074dade66b5e07e64401dd2487caefd4e9e2f467138d8c7eb06"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.2"
|
||||
version: "3.0.3"
|
||||
url_launcher_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_platform_interface
|
||||
sha256: "4eae912628763eb48fc214522e58e942fd16ce195407dbf45638239523c759a6"
|
||||
sha256: "6c9ca697a5ae218ce56cece69d46128169a58aa8653c1b01d26fcd4aad8c4370"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.1"
|
||||
version: "2.1.2"
|
||||
url_launcher_web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_web
|
||||
sha256: "44d79408ce9f07052095ef1f9a693c258d6373dc3944249374e30eff7219ccb0"
|
||||
sha256: "574cfbe2390666003c3a1d129bdc4574aaa6728f0c00a4829a81c316de69dd9b"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.14"
|
||||
version: "2.0.15"
|
||||
url_launcher_windows:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_windows
|
||||
sha256: b6217370f8eb1fd85c8890c539f5a639a01ab209a36db82c921ebeacefc7a615
|
||||
sha256: "97c9067950a0d09cbd93e2e3f0383d1403989362b97102fbf446473a48079a4b"
|
||||
url: "https://pub.dev"
|
||||
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:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@ -908,10 +948,10 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: window_manager
|
||||
sha256: "5bdd29dc5f1f3185fc90696373a571d77968e03e5e820fb1ecdbdade3f5d8fff"
|
||||
sha256: "492806c69879f0d28e95472bbe5e8d5940ac8c6e99cc07052fe14946974555ba"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.3.0"
|
||||
version: "0.3.1"
|
||||
xdg_directories:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -60,6 +60,8 @@ dependencies:
|
||||
file_picker: ^5.2.5
|
||||
archive: ^3.3.2
|
||||
crypto: ^3.0.2
|
||||
tray_manager: ^0.2.0
|
||||
local_notifier: ^0.1.5
|
||||
|
||||
dev_dependencies:
|
||||
integration_test:
|
||||
@ -99,6 +101,7 @@ flutter:
|
||||
- assets/graphics/
|
||||
- assets/licenses/
|
||||
- assets/licenses/raw/
|
||||
- resources/icons/
|
||||
|
||||
# An image asset can refer to one or more resolution-specific "variants", see
|
||||
# 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 <desktop_drop/desktop_drop_plugin.h>
|
||||
#include <local_notifier/local_notifier_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 <window_manager/window_manager_plugin.h>
|
||||
|
||||
void RegisterPlugins(flutter::PluginRegistry* registry) {
|
||||
DesktopDropPluginRegisterWithRegistrar(
|
||||
registry->GetRegistrarForPlugin("DesktopDropPlugin"));
|
||||
LocalNotifierPluginRegisterWithRegistrar(
|
||||
registry->GetRegistrarForPlugin("LocalNotifierPlugin"));
|
||||
ScreenRetrieverPluginRegisterWithRegistrar(
|
||||
registry->GetRegistrarForPlugin("ScreenRetrieverPlugin"));
|
||||
TrayManagerPluginRegisterWithRegistrar(
|
||||
registry->GetRegistrarForPlugin("TrayManagerPlugin"));
|
||||
UrlLauncherWindowsRegisterWithRegistrar(
|
||||
registry->GetRegistrarForPlugin("UrlLauncherWindows"));
|
||||
WindowManagerPluginRegisterWithRegistrar(
|
||||
|
@ -4,7 +4,9 @@
|
||||
|
||||
list(APPEND FLUTTER_PLUGIN_LIST
|
||||
desktop_drop
|
||||
local_notifier
|
||||
screen_retriever
|
||||
tray_manager
|
||||
url_launcher_windows
|
||||
window_manager
|
||||
)
|
||||
|
@ -172,7 +172,9 @@ bool Win32Window::Create(const std::wstring& title,
|
||||
}
|
||||
|
||||
bool Win32Window::Show() {
|
||||
return ShowWindow(window_handle_, SW_SHOWNORMAL);
|
||||
// We show the mindow manually
|
||||
return true;
|
||||
//return ShowWindow(window_handle_, SW_SHOWNORMAL);
|
||||
}
|
||||
|
||||
// static
|
||||
|
Loading…
Reference in New Issue
Block a user