├── .github ├── ISSUE_TEMPLATE │ └── cn-bug-report.yaml └── workflows │ ├── Release.yml │ ├── mirrorchyan.yml │ └── mirrorchyan_release_note.yml ├── .gitignore ├── .idea └── .idea.MFAAvalonia │ └── .idea │ ├── .gitignore │ ├── avalonia.xml │ ├── encodings.xml │ ├── indexLayout.xml │ └── vcs.xml ├── Directory.Build.props ├── LICENSE ├── LazyStaticGenerator ├── Generators │ ├── LazyStaticGenerator.cs │ └── MaaPropertyGenerator.cs └── LazyStaticGenerator.csproj ├── MFAAvalonia.sln ├── MFAAvalonia.sln.DotSettings.user ├── MFAAvalonia ├── App.axaml ├── App.axaml.cs ├── Assets │ ├── Localization │ │ ├── Strings.Designer.cs │ │ ├── Strings.en-us.resx │ │ ├── Strings.resx │ │ └── Strings.zh-hant.resx │ ├── Style │ │ ├── Geometry.axaml │ │ ├── MdXaml.axaml │ │ ├── MdXamlDocStyle.axaml │ │ └── MdXamlStyles.axaml │ ├── Theme │ │ ├── DarkTheme.axaml │ │ └── LightTheme.axaml │ └── logo.ico ├── Configuration │ ├── ConfigurationKeys.cs │ ├── ConfigurationManager.cs │ ├── GlobalConfiguration.cs │ └── MFAConfiguration.cs ├── Extensions │ ├── CheckBoxExtensions.cs │ ├── ComboBoxExtensions.cs │ ├── DragDropExtensions.cs │ ├── IconProperties.cs │ ├── MFAExtensions.cs │ ├── MaaFW │ │ ├── MaaControllerTypes.cs │ │ ├── MaaInterface.cs │ │ ├── MaaInterfaceAdvancedOption.cs │ │ ├── MaaNode.cs │ │ └── MaaProcessor.cs │ ├── ScrollViewerExtensions.cs │ └── ServiceProviderExtension.cs ├── Helper │ ├── AutoInitDictionary.cs │ ├── AvaloniaMemoryCracker.cs │ ├── BrushHelper.cs │ ├── Converters │ │ ├── AnnouncementTitleConverter.cs │ │ ├── CustomIsEnabledConverter.cs │ │ ├── DeviceDisplayConverter.cs │ │ ├── DynamicWidthConverter.cs │ │ ├── GenericSingleOrListConverter.cs │ │ ├── MaaInterfaceSelectAdvancedConverter.cs │ │ ├── MaaInterfaceSelectOptionConverter.cs │ │ ├── ReplaceConverter.cs │ │ ├── SingleOrNestedListConverter.cs │ │ ├── StringOrBoolOrObjectConverter.cs │ │ ├── StringOrObjectConverter.cs │ │ ├── TitleConverter.cs │ │ ├── UIntOrObjectConverter.cs │ │ ├── UniversalEnumConverter.cs │ │ └── UniversalEqualityConverter.cs │ ├── DispatcherHelper.cs │ ├── EmulatorHelper.cs │ ├── ExternalNotificationHelper.cs │ ├── GlobalHotkeyService.cs │ ├── IconHelper.cs │ ├── Instances.cs │ ├── JsonHelper.cs │ ├── LangKeys.cs │ ├── LangKeys.tt │ ├── LanguageHelper.cs │ ├── LoggerHelper.cs │ ├── MFAUrls.cs │ ├── ServiceRegistry.cs │ ├── SimpleEncryptionHelper.cs │ ├── TaskManager.cs │ ├── ThemeHelper.cs │ ├── ToastHelper.cs │ ├── ToastNotification.cs │ ├── TrayIconManager.cs │ ├── ValueType │ │ ├── DragItemViewModel.cs │ │ ├── MFAHotKey.cs │ │ ├── MFATask.cs │ │ └── ObservableQueue.cs │ ├── VersionChecker.cs │ └── ViewsHelper.cs ├── Img │ └── preview.png ├── MFAAvalonia.csproj ├── MFAAvalonia.desktop ├── MFAAvalonia.ico ├── NuGet.Config ├── Program.cs ├── Utilities │ ├── Attributes │ │ ├── LazyStaticAttribute.cs │ │ ├── MaaJsonPropertyAttribute.cs │ │ └── MaaPropertyAttribute.cs │ └── UrlUtilities.cs ├── ViewLocator.cs ├── ViewModels │ ├── Other │ │ ├── LocalizationViewModel.cs │ │ ├── LogItemViewModel.cs │ │ ├── SupportedLanguage.cs │ │ └── ThemeItemViewModel.cs │ ├── Pages │ │ ├── ResourcesViewModel.cs │ │ ├── SettingsViewModel.cs │ │ └── TaskQueueViewModel.cs │ ├── UsersControls │ │ ├── AdbEditorDialogViewModel.cs │ │ ├── AddTaskDialogViewModel.cs │ │ ├── CustomThemeDialogViewModel.cs │ │ └── Settings │ │ │ ├── ConnectSettingsUserControlModel.cs │ │ │ ├── ExternalNotificationSettingsUserControlModel.cs │ │ │ ├── GameSettingsUserControlModel.cs │ │ │ ├── GuiSettingsUserControlModel.cs │ │ │ ├── PerformanceUserControlModel.cs │ │ │ ├── StartSettingsUserControlModel.cs │ │ │ ├── TimerSettingsUserControlModel.cs │ │ │ └── VersionUpdateSettingsUserControlModel.cs │ ├── ViewModelBase.cs │ └── Windows │ │ ├── AnnouncementViewModel.cs │ │ └── RootViewModel.cs ├── Views │ ├── Pages │ │ ├── ResourcesView.axaml │ │ ├── ResourcesView.axaml.cs │ │ ├── SettingsView.axaml │ │ ├── SettingsView.axaml.cs │ │ ├── TaskQueueView.axaml │ │ └── TaskQueueView.axaml.cs │ ├── UserControls │ │ ├── AdbEditorDialogView.axaml │ │ ├── AdbEditorDialogView.axaml.cs │ │ ├── AddTaskDialogView.axaml │ │ ├── AddTaskDialogView.axaml.cs │ │ ├── CustomThemeDialogView.axaml │ │ ├── CustomThemeDialogView.axaml.cs │ │ ├── DragAdorner.cs │ │ ├── HotKeyEditor.axaml │ │ ├── HotKeyEditor.axaml.cs │ │ ├── PinButton.cs │ │ └── Settings │ │ │ ├── AboutUserControl.axaml │ │ │ ├── AboutUserControl.axaml.cs │ │ │ ├── ConfigurationMgrUserControl.axaml │ │ │ ├── ConfigurationMgrUserControl.axaml.cs │ │ │ ├── ConnectSettingsUserControl.axaml │ │ │ ├── ConnectSettingsUserControl.axaml.cs │ │ │ ├── ExternalNotificationSettingsUserControl.axaml │ │ │ ├── ExternalNotificationSettingsUserControl.axaml.cs │ │ │ ├── GameSettingsUserControl.axaml │ │ │ ├── GameSettingsUserControl.axaml.cs │ │ │ ├── GuiSettingsUserControl.axaml │ │ │ ├── GuiSettingsUserControl.axaml.cs │ │ │ ├── HotKeySettingsUserControl.axaml │ │ │ ├── HotKeySettingsUserControl.axaml.cs │ │ │ ├── PerformanceUserControl.axaml │ │ │ ├── PerformanceUserControl.axaml.cs │ │ │ ├── StartSettingsUserControl.axaml │ │ │ ├── StartSettingsUserControl.axaml.cs │ │ │ ├── TimerSettingsUserControl.axaml │ │ │ ├── TimerSettingsUserControl.axaml.cs │ │ │ ├── VersionUpdateSettingsUserControl.axaml │ │ │ └── VersionUpdateSettingsUserControl.axaml.cs │ └── Windows │ │ ├── AnnouncementView.axaml │ │ ├── AnnouncementView.axaml.cs │ │ ├── ErrorView.axaml │ │ ├── ErrorView.axaml.cs │ │ ├── RootView.axaml │ │ └── RootView.axaml.cs └── app.manifest ├── MFAUpdater ├── MFAUpdater.csproj ├── Program.cs └── logo.ico ├── README.md ├── README_en.md ├── SukiUI ├── Animations │ ├── SquishyDragExtensions.cs │ ├── SquishyOverExtensions.cs │ ├── SukiEasingOut.cs │ └── SukiSpringEase.cs ├── ColorTheme │ ├── Dark.axaml │ ├── Light.axaml │ └── NotificationColor.cs ├── Content │ ├── Icons.cs │ ├── Images │ │ └── icons8-file-explorer-new-48.png │ └── Shaders │ │ ├── Background │ │ ├── backgroundshadcn.sksl │ │ ├── bubble.sksl │ │ ├── bubblestrong.sksl │ │ ├── cells.sksl │ │ ├── flat.sksl │ │ ├── gradient.sksl │ │ ├── gradientdarker.sksl │ │ ├── gradientsoft.sksl │ │ └── waves.sksl │ │ └── Loading │ │ ├── glow.sksl │ │ ├── pellets.sksl │ │ └── simple.sksl ├── Controls │ ├── BusyArea.axaml │ ├── BusyArea.axaml.cs │ ├── CircleProgressBar.axaml │ ├── CircleProgressBar.axaml.cs │ ├── CodeView.axaml │ ├── CodeView.axaml.cs │ ├── ContentExpandControl.axaml │ ├── ContentExpandControl.axaml.cs │ ├── Experimental │ │ ├── ChatUI │ │ │ ├── ChatMessage.cs │ │ │ ├── ChatUI.axaml │ │ │ └── ChatUI.axaml.cs │ │ └── DesktopEnvironment │ │ │ ├── InternalWindow.axaml │ │ │ ├── InternalWindow.axaml.cs │ │ │ ├── SDESoftware.cs │ │ │ ├── SukiDesktopEnvironment.axaml │ │ │ ├── SukiDesktopEnvironment.axaml.cs │ │ │ ├── WindowManager.axaml │ │ │ └── WindowManager.axaml.cs │ ├── GlassMorphism │ │ ├── BlurBackground.cs │ │ ├── GlassCard.axaml │ │ └── GlassCard.axaml.cs │ ├── GroupBox.axaml │ ├── GroupBox.axaml.cs │ ├── Hosts │ │ ├── SukiDialogHost.axaml │ │ ├── SukiDialogHost.cs │ │ ├── SukiMessageBoxHost.axaml │ │ ├── SukiMessageBoxHost.axaml.cs │ │ ├── SukiToastHost.axaml │ │ └── SukiToastHost.cs │ ├── InfoBadge.axaml │ ├── InfoBadge.axaml.cs │ ├── InfoBar.axaml │ ├── InfoBar.axaml.cs │ ├── Loading.cs │ ├── PasswordBox.axaml │ ├── PasswordBox.axaml.cs │ ├── PropertyGrid │ │ ├── CategoryViewModel.cs │ │ ├── DateTimePickerSelectedDateConverter.cs │ │ ├── IPropertyViewModel.cs │ │ ├── IPropertyViewModel{T}.cs │ │ ├── InstanceViewModel.cs │ │ ├── PropertyGrid.axaml │ │ ├── PropertyGrid.axaml.cs │ │ ├── PropertyGridDialog.axaml │ │ ├── PropertyGridDialog.axaml.cs │ │ ├── PropertyGridTemplateSelector.axaml │ │ ├── PropertyGridTemplateSelector.axaml.cs │ │ ├── PropertyGridWindow.axaml │ │ ├── PropertyGridWindow.axaml.cs │ │ └── ViewModels │ │ │ ├── BoolViewModel.cs │ │ │ ├── ComplexTypeViewModel.cs │ │ │ ├── DateTimeOffsetViewModel.cs │ │ │ ├── DateTimeViewModel.cs │ │ │ ├── DecimalViewModel.cs │ │ │ ├── DoubleViewModel.cs │ │ │ ├── EnumViewModel.cs │ │ │ ├── FloatViewModel.cs │ │ │ ├── IntegerViewModel.cs │ │ │ ├── LongViewModel.cs │ │ │ ├── PropertyViewModelBase.cs │ │ │ └── StringViewModel.cs │ ├── Settings │ │ ├── SettingsLayout.axaml │ │ ├── SettingsLayout.axaml.cs │ │ └── SettingsLayoutItem.cs │ ├── Stepper.axaml │ ├── Stepper.axaml.cs │ ├── SukiBackground.cs │ ├── SukiDialog.axaml │ ├── SukiDialog.cs │ ├── SukiMainHost.axaml │ ├── SukiMainHost.axaml.cs │ ├── SukiSideMenu.axaml │ ├── SukiSideMenu.axaml.cs │ ├── SukiSideMenuItem.axaml │ ├── SukiSideMenuItem.axaml.cs │ ├── SukiStackPage.axaml │ ├── SukiStackPage.axaml.cs │ ├── SukiToast.axaml │ ├── SukiToast.axaml.cs │ ├── SukiTransitioningContentControl.axaml │ ├── SukiTransitioningContentControl.axaml.cs │ ├── SukiWindow.axaml │ ├── SukiWindow.axaml.cs │ ├── WaveProgress.axaml │ └── WaveProgress.axaml.cs ├── Converters │ ├── AdvancedIconSelectorConverter.cs │ ├── BoolToPasswordCharConverter.cs │ ├── ComboBoxItemConverter.cs │ ├── IfConditionConverter.cs │ ├── InfoBadgeOverflowConverter.cs │ ├── IsCheckedToClassConverter.cs │ ├── IsNotNullOrEmptyToDouble.cs │ ├── ProgressToContentConverter.cs │ ├── SideMenuScrollerToOpacityMask.cs │ ├── SideMenuScrollerToVisibilityBool.cs │ └── WaveProgress │ │ ├── WaveProgressGradientOffsetConverter.cs │ │ ├── WaveProgressValueColorConverter.cs │ │ ├── WaveProgressValueConverter.cs │ │ └── WaveProgressValueTextConverter.cs ├── CustomFont │ ├── Quicksand-Bold.ttf │ ├── Quicksand-Light.ttf │ ├── Quicksand-Medium.ttf │ ├── Quicksand-Regular.ttf │ └── Quicksand-SemiBold.ttf ├── Dialogs │ ├── DialogContentMaxWidthValueConverter.cs │ ├── FluentSukiDialogBuilder.cs │ ├── ISukiDialog.cs │ ├── ISukiDialogManager.cs │ ├── SukiDialogBuilder.cs │ ├── SukiDialogManager.cs │ └── SukiDialogManagerEventArgs.cs ├── Enums │ ├── CornerPosition.cs │ ├── SideMenuTogglePaneButtonPositionOptions.cs │ ├── SukiBackgroundStyle.cs │ ├── SukiButtonStyles.cs │ ├── SukiColor.cs │ └── ToastLocation.cs ├── Extensions │ ├── ColorExtensions.cs │ ├── ControlExtensions.cs │ ├── EnumExtensions.cs │ ├── MenuIconExtensions.cs │ ├── ResourceNodeExtensions.cs │ ├── TabControlExtensions.cs │ └── WindowExtensions.cs ├── Helpers │ ├── AnimationExtensions.cs │ ├── CompositionAnimationHelper.cs │ ├── ConditionalXAML │ │ ├── If.cs │ │ └── InlineSharp.cs │ ├── ControlAnimations.cs │ ├── DialogPool.cs │ ├── FluentAnimator.cs │ ├── SukiObservableObject.cs │ ├── ToastPool.cs │ └── ViewLocator.cs ├── Locale │ ├── en-us.axaml │ ├── en-us.axaml.cs │ ├── zh-hans.axaml │ ├── zh-hant.axaml │ ├── zh_hans.axaml.cs │ └── zh_hant.axaml.cs ├── MessageBox │ ├── SukiMessageBox.cs │ ├── SukiMessageBoxButtons.cs │ ├── SukiMessageBoxButtonsFactory.cs │ ├── SukiMessageBoxIcons.cs │ ├── SukiMessageBoxIconsFactory.cs │ ├── SukiMessageBoxOptions.cs │ └── SukiMessageBoxResult.cs ├── Models │ └── SukiColorTheme.cs ├── OIG.N5o-removebg-preview.png ├── Properties │ ├── AssemblyInfo.cs │ └── PublishProfiles │ │ ├── FolderProfile.pubxml │ │ └── FolderProfile.pubxml.user ├── Roboto-Medium.ttf ├── Roboto-Regular.ttf ├── SukiUI.csproj ├── Theme │ ├── AutoCompleteBoxStyles.axaml │ ├── BorderStyles.xaml │ ├── Button.axaml │ ├── ButtonLoadingStyles.axaml │ ├── Calendar │ │ ├── Calendar.axaml │ │ ├── CalendarButton.axaml │ │ ├── CalendarDayButton.axaml │ │ └── CalendarItem.axaml │ ├── CalendarDatePickerStyle.axaml │ ├── CheckBoxStyles.axaml │ ├── Colors.xaml │ ├── ComboBoxItemStyle.axaml │ ├── ComboBoxStyles.xaml │ ├── ComboboxConverter.cs │ ├── ContextMenu.axaml │ ├── DataGridStyle.axaml │ ├── DatePicker.axaml │ ├── DropDownButton.axaml │ ├── Expander.axaml │ ├── FlyoutPresenter.axaml │ ├── HyperlinkButton.axaml │ ├── Index.axaml │ ├── Index.axaml.cs │ ├── ListBoxItemStyle.axaml │ ├── ListBoxStyles.xaml │ ├── ManagedFileChooser.axaml │ ├── ManagedFileChooserConverters.cs │ ├── Menu.axaml │ ├── MenuFlyoutPresenter.axaml │ ├── MenuItem.axaml │ ├── NotificationCardStyle.axaml │ ├── NotificationStyle.axaml │ ├── NumericUpDownExtensions.cs │ ├── NumericUpDownStyles.xaml │ ├── PathIcon.axaml │ ├── ProgressBar.axaml │ ├── ProgressBarStyles.xaml │ ├── RadioButtonStyles.xaml │ ├── RichTextBoxStyles.axaml │ ├── ScrollBarStyle.axaml │ ├── ScrollViewerStyles.axaml │ ├── ScrollableExtensions.cs │ ├── Separator.axaml │ ├── Shadcn │ │ ├── BlackWhiteTheme.axaml │ │ ├── ShadDarkStyles.axaml │ │ └── Shadcn.cs │ ├── SliderStyles.xaml │ ├── SplitButton.axaml │ ├── SplitView.axaml │ ├── TabControl.axaml │ ├── TabItem.axaml │ ├── TextBlock.xaml │ ├── TextBoxExtensions.cs │ ├── TextBoxStyles.xaml │ ├── TextEraserButton.axaml │ ├── TextEraserButton.axaml.cs │ ├── TextStyles.axaml │ ├── TimePickerStyle.axaml │ ├── ToggleButton.axaml │ ├── ToggleSwitch.axaml │ ├── ToolTipStyles.axaml │ └── TreeViewStyles.xaml ├── Toasts │ ├── FluentSukiToastBuilder.cs │ ├── ISukiToast.cs │ ├── ISukiToastManager.cs │ ├── SukiToastBuilder.cs │ ├── SukiToastDismissSource.cs │ ├── SukiToastDismissedEventArgs.cs │ ├── SukiToastManager.cs │ └── SukiToastQueuedEventArgs.cs ├── Utilities │ ├── Effects │ │ ├── EffectBackgroundDraw.cs │ │ ├── EffectDrawBase.cs │ │ └── SukiEffect.cs │ └── IsExternalInit.cs └── suki_photo.ico ├── cliff.toml ├── dependencies.json └── workflows └── install.yml /.github/ISSUE_TEMPLATE/cn-bug-report.yaml: -------------------------------------------------------------------------------- 1 | name: Bug 反馈(使用中文) 2 | description: 运行错误、操作异常、连接错误等 3 | labels: ["bug"] 4 | body: 5 | - type: checkboxes 6 | id: checks 7 | attributes: 8 | label: 在提问之前... 9 | description: | 10 | 请确认自己完成了要求之后再进行勾选 11 | options: 12 | - label: 我知道这个项目是独立的通用GUI项目,在这里反馈资源问题(如M9A)并不正确 13 | required: true 14 | - label: 我理解 Issue 是用于反馈和解决问题的,而非吐槽评论区,将尽可能提供更多信息帮助问题解决 15 | required: true 16 | - label: 我填写了简短且清晰明确的标题,以便开发者在翻阅 Issue 列表时能快速确定大致问题。而不是“一个建议”、“卡住了”等 17 | required: true 18 | - label: 我使用的是当前更新版本的最新版,且已查看版本发布至今和 Pull Requests 中尚未发布的更新内容,并未提及该 Bug 已被修复的情况 19 | required: true 20 | - label: 我已检查了置顶议题(Pinned Issue)(公告)、活跃议题(Open Issue)、已关闭议题(Closed Issue),确认我的问题未被提及 21 | required: true 22 | - type: textarea 23 | id: describe 24 | attributes: 25 | label: 问题描述 26 | description: 描述问题时请尽可能详细 27 | validations: 28 | required: true 29 | - type: textarea 30 | id: version 31 | attributes: 32 | label: Version 33 | description: >- 34 | 请提供 MFA 版本。可在 MFA -> 设置 -> 软件更新中找到。 35 | placeholder: | 36 | MFA Version: 37 | validations: 38 | required: true 39 | - type: textarea 40 | id: logs 41 | attributes: 42 | label: 日志和配置文件 43 | description: | 44 | **请在关闭 MFA 后,上传以下 5 个文件:** 45 | 1. `debug` 文件夹中的 `maa.log` 日志文件,并说明问题出现的大致时间点 46 | 2. `log` 文件夹中的最新log 47 | 48 | 如果你在使用 MacBook,请点击屏幕左上角的“文件”,点击“打开日志文件夹” 49 | **请直接将完整的文件拖拽进来,而非自己裁切或复制的片段;若文件体积过大可压缩后再上传** 50 | placeholder: | 51 | 请确认上传文件前已关闭 MFA 52 | validations: 53 | required: true 54 | - type: textarea 55 | id: configuration 56 | attributes: 57 | label: 配置信息 58 | description: | 59 | 请说明操作系统及版本、模拟器品牌、模拟器分辨率、DPI、帧率; 60 | 最后请说明 GPU 加速推理是否开启,若开启请提供 GPU 型号。 61 | validations: 62 | required: true 63 | - type: textarea 64 | id: screenshots 65 | attributes: 66 | label: 截图或录屏 67 | description: | 68 | 可上传屏幕截图或录制以帮助解释你的问题,包括但不限于 MFA 软件截图、游戏画面截图 69 | 若是**识别相关问题**,请尽可能提供模拟器自带的截图工具截取的无遮挡的**原图**(或通过 adb 截取原图) 70 | 用其他的工具(如QQ/微信)截取的图片包含窗口边框且长宽比、分辨率不固定,不利于我们排除bug 71 | 若文件体积过大可压缩后再上传 72 | validations: 73 | required: false 74 | - type: textarea 75 | id: others 76 | attributes: 77 | label: 还有别的吗? 78 | description: | 79 | 任何能让我们对你所遇到的问题有更多了解的东西 80 | validations: 81 | required: false 82 | -------------------------------------------------------------------------------- /.github/workflows/mirrorchyan.yml: -------------------------------------------------------------------------------- 1 | name: mirrorchyan 2 | 3 | on: 4 | workflow_dispatch: 5 | 6 | jobs: 7 | mirrorchyan: 8 | runs-on: macos-latest 9 | strategy: 10 | fail-fast: false 11 | matrix: 12 | os: [win, osx, linux] 13 | arch: [arm64, x64] 14 | 15 | steps: 16 | - uses: MirrorChyan/uploading-action@v1 17 | if: always() 18 | with: 19 | filetype: latest-release 20 | filename: "MFAAvalonia-*-${{ matrix.os }}-${{ matrix.arch }}.zip" 21 | mirrorchyan_rid: MFAAvalonia 22 | 23 | github_token: ${{ secrets.GITHUB_TOKEN }} 24 | owner: ${{ github.repository_owner }} 25 | repo: ${{ github.event.repository.name }} 26 | upload_token: ${{ secrets.MirrorChyanUploadToken }} 27 | os: ${{ matrix.os }} 28 | arch: ${{ matrix.arch }} 29 | -------------------------------------------------------------------------------- /.github/workflows/mirrorchyan_release_note.yml: -------------------------------------------------------------------------------- 1 | name: mirrorchyan_release_note 2 | 3 | on: 4 | workflow_dispatch: 5 | release: 6 | types: [edited] 7 | 8 | jobs: 9 | mirrorchyan: 10 | runs-on: macos-latest 11 | 12 | steps: 13 | - id: uploading 14 | uses: MirrorChyan/release-note-action@v1 15 | with: 16 | mirrorchyan_rid: MFAAvalonia 17 | 18 | upload_token: ${{ secrets.MirrorChyanUploadToken }} 19 | github_token: ${{ secrets.GITHUB_TOKEN }} 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | bin/ 2 | obj/ 3 | /packages/ 4 | riderModule.iml 5 | /_ReSharper.Caches/ -------------------------------------------------------------------------------- /.idea/.idea.MFAAvalonia/.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # 默认忽略的文件 2 | /shelf/ 3 | /workspace.xml 4 | # Rider 忽略的文件 5 | /.idea.MFAAvalonia.iml 6 | /modules.xml 7 | /contentModel.xml 8 | /projectSettingsUpdater.xml 9 | # 基于编辑器的 HTTP 客户端请求 10 | /httpRequests/ 11 | # Datasource local storage ignored files 12 | /dataSources/ 13 | /dataSources.local.xml 14 | -------------------------------------------------------------------------------- /.idea/.idea.MFAAvalonia/.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /.idea/.idea.MFAAvalonia/.idea/indexLayout.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | .github 6 | workflows 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /.idea/.idea.MFAAvalonia/.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /Directory.Build.props: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 0.1.232-* 6 | all 7 | 8 | 9 | -------------------------------------------------------------------------------- /LazyStaticGenerator/LazyStaticGenerator.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netstandard2.0 5 | Library 6 | false 7 | enable 8 | preview 9 | false 10 | true 11 | true 12 | 13 | LazyStaticGenerator 14 | LazyStaticGenerator 15 | true 16 | 1.0.0 17 | 1.0.0 18 | 1.0.0 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /MFAAvalonia.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MFAAvalonia", "MFAAvalonia\MFAAvalonia.csproj", "{0DEB7018-7094-4C4E-808E-46BA4A1D77FA}" 4 | EndProject 5 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LazyStaticGenerator", "LazyStaticGenerator\LazyStaticGenerator.csproj", "{C05457BE-61C5-4732-B3F6-3BC1C1C390AA}" 6 | EndProject 7 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MFAUpdater", "MFAUpdater\MFAUpdater.csproj", "{B13E6AA2-CFE7-41F5-91EF-7F1C53566680}" 8 | EndProject 9 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SukiUI", "SukiUI\SukiUI.csproj", "{07048BB2-1A22-48C9-87F9-34D4664DC2A9}" 10 | EndProject 11 | Global 12 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 13 | Debug|Any CPU = Debug|Any CPU 14 | Release|Any CPU = Release|Any CPU 15 | EndGlobalSection 16 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 17 | {0DEB7018-7094-4C4E-808E-46BA4A1D77FA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 18 | {0DEB7018-7094-4C4E-808E-46BA4A1D77FA}.Debug|Any CPU.Build.0 = Debug|Any CPU 19 | {0DEB7018-7094-4C4E-808E-46BA4A1D77FA}.Release|Any CPU.ActiveCfg = Release|Any CPU 20 | {0DEB7018-7094-4C4E-808E-46BA4A1D77FA}.Release|Any CPU.Build.0 = Release|Any CPU 21 | {C05457BE-61C5-4732-B3F6-3BC1C1C390AA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 22 | {C05457BE-61C5-4732-B3F6-3BC1C1C390AA}.Debug|Any CPU.Build.0 = Debug|Any CPU 23 | {C05457BE-61C5-4732-B3F6-3BC1C1C390AA}.Release|Any CPU.ActiveCfg = Release|Any CPU 24 | {C05457BE-61C5-4732-B3F6-3BC1C1C390AA}.Release|Any CPU.Build.0 = Release|Any CPU 25 | {B13E6AA2-CFE7-41F5-91EF-7F1C53566680}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 26 | {B13E6AA2-CFE7-41F5-91EF-7F1C53566680}.Debug|Any CPU.Build.0 = Debug|Any CPU 27 | {B13E6AA2-CFE7-41F5-91EF-7F1C53566680}.Release|Any CPU.ActiveCfg = Release|Any CPU 28 | {B13E6AA2-CFE7-41F5-91EF-7F1C53566680}.Release|Any CPU.Build.0 = Release|Any CPU 29 | {07048BB2-1A22-48C9-87F9-34D4664DC2A9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 30 | {07048BB2-1A22-48C9-87F9-34D4664DC2A9}.Debug|Any CPU.Build.0 = Debug|Any CPU 31 | {07048BB2-1A22-48C9-87F9-34D4664DC2A9}.Release|Any CPU.ActiveCfg = Release|Any CPU 32 | {07048BB2-1A22-48C9-87F9-34D4664DC2A9}.Release|Any CPU.Build.0 = Release|Any CPU 33 | EndGlobalSection 34 | EndGlobal 35 | -------------------------------------------------------------------------------- /MFAAvalonia/App.axaml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /MFAAvalonia/Assets/Style/MdXaml.axaml: -------------------------------------------------------------------------------- 1 |  7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /MFAAvalonia/Assets/Style/MdXamlDocStyle.axaml: -------------------------------------------------------------------------------- 1 |  4 | 10 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /MFAAvalonia/Assets/Theme/DarkTheme.axaml: -------------------------------------------------------------------------------- 1 |  2 | 3 | -------------------------------------------------------------------------------- /MFAAvalonia/Assets/Theme/LightTheme.axaml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | -------------------------------------------------------------------------------- /MFAAvalonia/Assets/logo.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SweetSmellFox/MFAAvalonia/1c21cad95195cf84dc9c23d445117135847b2b8d/MFAAvalonia/Assets/logo.ico -------------------------------------------------------------------------------- /MFAAvalonia/Extensions/MaaFW/MaaControllerTypes.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace MFAAvalonia.Extensions.MaaFW; 4 | 5 | public enum MaaControllerTypes 6 | { 7 | None = 0, 8 | Win32, 9 | Adb 10 | } 11 | 12 | public static class MaaControllerHelper 13 | { 14 | public static string ToResourceKey(this MaaControllerTypes type) 15 | { 16 | return type switch 17 | { 18 | MaaControllerTypes.Win32 => "TabWin32", 19 | MaaControllerTypes.Adb => "TabADB", 20 | _ => "TabADB" 21 | }; 22 | } 23 | 24 | public static MaaControllerTypes ToMaaControllerTypes(this string? type, MaaControllerTypes defaultValue = MaaControllerTypes.Adb) 25 | { 26 | if (string.IsNullOrWhiteSpace(type)) 27 | return defaultValue; 28 | if (type.Contains("win32", StringComparison.OrdinalIgnoreCase)) 29 | return MaaControllerTypes.Win32; 30 | if (type.Contains("adb", StringComparison.OrdinalIgnoreCase)) 31 | return MaaControllerTypes.Adb; 32 | return defaultValue; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /MFAAvalonia/Extensions/ServiceProviderExtension.cs: -------------------------------------------------------------------------------- 1 | using Avalonia.Markup.Xaml; 2 | using Microsoft.Extensions.DependencyInjection; 3 | using System; 4 | 5 | namespace MFAAvalonia.Extensions; 6 | 7 | public class ServiceProviderExtension: MarkupExtension 8 | { 9 | public Type ServiceType { get; set; } 10 | 11 | public override object ProvideValue(IServiceProvider serviceProvider) 12 | { 13 | return App.Services.GetRequiredService(ServiceType); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /MFAAvalonia/Helper/AutoInitDictionary.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace MFAAvalonia.Helper; 4 | 5 | public class AutoInitDictionary : Dictionary 6 | { 7 | public AutoInitDictionary() 8 | { 9 | this["exploreCount"] = 0; 10 | } 11 | 12 | // 重写索引器,确保不存在的键被访问时初始化为0 13 | public new int this[string key] 14 | { 15 | get 16 | { 17 | if (!ContainsKey(key)) 18 | { 19 | this[key] = 0; 20 | } 21 | 22 | return base[key]; 23 | } 24 | set => base[key] = value; 25 | } 26 | } -------------------------------------------------------------------------------- /MFAAvalonia/Helper/AvaloniaMemoryCracker.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using System.Runtime.InteropServices; 4 | using System.Threading; 5 | using System.Threading.Tasks; 6 | 7 | namespace MFAAvalonia.Helper; 8 | 9 | public class AvaloniaMemoryCracker 10 | { 11 | #region 平台相关API 12 | 13 | [DllImport("kernel32.dll")] 14 | private extern static bool SetProcessWorkingSetSize(IntPtr proc, int min, int max); 15 | 16 | private static bool IsWindows => 17 | RuntimeInformation.IsOSPlatform(OSPlatform.Windows); 18 | 19 | #endregion 20 | 21 | #region 核心逻辑 22 | 23 | /// 启动内存优化守护进程 24 | /// 清理间隔秒数(默认30秒) 25 | public void Cracker(int intervalSeconds = 30) 26 | { 27 | Task.Factory.StartNew(() => 28 | { 29 | while (true) 30 | { 31 | try 32 | { 33 | PerformMemoryCleanup(); 34 | Thread.Sleep(TimeSpan.FromSeconds(intervalSeconds)); 35 | } 36 | catch 37 | { 38 | // 异常处理可扩展日志记录 39 | } 40 | } 41 | }, TaskCreationOptions.LongRunning); 42 | } 43 | 44 | /// 执行内存清理三步策略 45 | private void PerformMemoryCleanup() 46 | { 47 | // 第一步:触发托管堆GC回收[1](@ref) 48 | GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced); 49 | GC.WaitForPendingFinalizers(); 50 | 51 | // 第二步:针对Windows平台优化工作集[1](@ref) 52 | if (IsWindows) 53 | { 54 | SetProcessWorkingSetSize( 55 | Process.GetCurrentProcess().Handle, 56 | -1, -1 57 | ); 58 | } 59 | 60 | // 第三步:可选扩展点(如内存池管理) 61 | // 可在此处集成引用计数或内存碎片整理逻辑[1](@ref) 62 | } 63 | 64 | #endregion 65 | } 66 | -------------------------------------------------------------------------------- /MFAAvalonia/Helper/Converters/AnnouncementTitleConverter.cs: -------------------------------------------------------------------------------- 1 | using Avalonia; 2 | using Avalonia.Data.Converters; 3 | using Avalonia.Markup.Xaml; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Globalization; 7 | 8 | namespace MFAAvalonia.Helper.Converters; 9 | 10 | public class AnnouncementTitleConverter : MarkupExtension, IMultiValueConverter 11 | { 12 | public override object ProvideValue(IServiceProvider serviceProvider) => this; 13 | 14 | public object? Convert(IList values, Type targetType, object? parameter, CultureInfo culture) 15 | { 16 | // 安全解包参数(处理 UnsetValue 和 null) 17 | var isReleaseNote = SafeGetValue(values, 0); 18 | var titleA = SafeGetValue(values, 1); 19 | var titleB = SafeGetValue(values, 2); 20 | var result = isReleaseNote ? titleA : titleB; 21 | 22 | return result; 23 | } 24 | 25 | public object[] ConvertBack(object? value, Type[] targetTypes, object? parameter, CultureInfo culture) 26 | { 27 | throw new NotSupportedException(); 28 | } 29 | 30 | /// 31 | /// 安全获取绑定值(处理 UnsetValue 和类型转换) 32 | /// 33 | private T SafeGetValue(IList values, int index, T defaultValue = default) 34 | { 35 | if (index >= values.Count) return defaultValue; 36 | 37 | var value = values[index]; 38 | 39 | // 处理 Avalonia 的 UnsetValue 40 | if (value is UnsetValueType || value == null) return defaultValue; 41 | 42 | try 43 | { 44 | return (T)System.Convert.ChangeType(value, typeof(T)); 45 | } 46 | catch 47 | { 48 | return defaultValue; 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /MFAAvalonia/Helper/Converters/CustomIsEnabledConverter.cs: -------------------------------------------------------------------------------- 1 | using Avalonia.Data.Converters; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Globalization; 5 | 6 | namespace MFAAvalonia.Helper.Converters; 7 | 8 | public class CustomIsEnabledConverter : IMultiValueConverter 9 | { 10 | public object Convert(IList values, Type targetType, object parameter, CultureInfo culture) 11 | { 12 | if (values is [bool isChecked, bool idle]) 13 | { 14 | bool isCheckedValue = isChecked; 15 | return (isCheckedValue && idle) || !isCheckedValue; 16 | } 17 | 18 | return false; 19 | } 20 | 21 | public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) 22 | { 23 | return []; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /MFAAvalonia/Helper/Converters/DeviceDisplayConverter.cs: -------------------------------------------------------------------------------- 1 | using Avalonia.Controls; 2 | using Avalonia.Controls.Templates; 3 | using Avalonia.Data.Converters; 4 | using Avalonia.Markup.Xaml; 5 | using Avalonia.Markup.Xaml.Templates; 6 | using FluentAvalonia.UI.Controls; 7 | using MaaFramework.Binding; 8 | using System; 9 | using System.Globalization; 10 | 11 | namespace MFAAvalonia.Helper.Converters; 12 | 13 | public class DeviceDisplayConverter : MarkupExtension, IValueConverter 14 | { 15 | public override object ProvideValue(IServiceProvider serviceProvider) => this; 16 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 17 | { 18 | if (value is AdbDeviceInfo device) 19 | return $"{device.Name} ({device.AdbSerial})"; 20 | 21 | return value?.ToString() ?? string.Empty; 22 | } 23 | 24 | public object ConvertBack(object? value, Type targetType, object? parameter, CultureInfo cultureInfo) => throw new NotSupportedException(); 25 | } 26 | -------------------------------------------------------------------------------- /MFAAvalonia/Helper/Converters/DynamicWidthConverter.cs: -------------------------------------------------------------------------------- 1 | using Avalonia.Data.Converters; 2 | using Avalonia.Markup.Xaml; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Globalization; 6 | 7 | namespace MFAAvalonia.Helper.Converters; 8 | 9 | public class DynamicWidthConverter : MarkupExtension, IMultiValueConverter 10 | { 11 | public override object ProvideValue(IServiceProvider serviceProvider) => this; 12 | 13 | public object Convert(IList values, Type targetType, object parameter, CultureInfo culture) 14 | { 15 | var minWidth = 100d; 16 | if (values.Count < 3 || !double.TryParse(values[0]?.ToString(), out double parentWidth) || !double.TryParse(values[1]?.ToString(), out double siblingWidth) || !double.TryParse(values[2]?.ToString(), out minWidth)) 17 | return minWidth; 18 | 19 | var availableWidth = parentWidth - siblingWidth - 15; 20 | if (availableWidth < minWidth) 21 | minWidth = Math.Max(parentWidth - 15, minWidth); 22 | return Math.Max(availableWidth, minWidth); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /MFAAvalonia/Helper/Converters/GenericSingleOrListConverter.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using Newtonsoft.Json.Linq; 3 | using System; 4 | using System.Collections.Generic; 5 | 6 | namespace MFAAvalonia.Helper.Converters; 7 | 8 | /// 9 | /// 支持单个对象或对象数组的泛型转换器 10 | /// (兼容 Newtonsoft.Json 的高级动态处理特性)[1](@ref) 11 | /// 12 | /// 目标类型 13 | [JsonObject(ItemRequired = Required.AllowNull)] 14 | public class GenericSingleOrListConverter : JsonConverter 15 | { 16 | public override bool CanConvert(Type objectType) => 17 | objectType == typeof(T) || objectType == typeof(IEnumerable); 18 | 19 | public override object ReadJson(JsonReader reader, 20 | Type objectType, 21 | object existingValue, 22 | JsonSerializer serializer) 23 | { 24 | var token = JToken.Load(reader); 25 | 26 | return token switch 27 | { 28 | JObject obj => [ 29 | obj.ToObject(serializer) 30 | ], 31 | JArray arr => arr.ToObject>(serializer), 32 | JValue { Type: JTokenType.Null } => null, 33 | _ => HandlePrimitive(token, serializer) 34 | }; 35 | } 36 | 37 | private List HandlePrimitive(JToken token, JsonSerializer serializer) 38 | { 39 | // 特殊处理字符串类型 40 | if (typeof(T) == typeof(string)) 41 | { 42 | return new List 43 | { 44 | (T)(object)token.ToString() 45 | }; 46 | } 47 | 48 | if (typeof(T).IsPrimitive || IsSupportedValueType()) 49 | { 50 | return new List 51 | { 52 | token.ToObject(serializer) 53 | }; 54 | } 55 | 56 | throw new JsonSerializationException($"类型 {typeof(T)} 不支持基础值转换"); 57 | 58 | bool IsSupportedValueType() => 59 | typeof(T).IsValueType && Nullable.GetUnderlyingType(typeof(T)) == null; 60 | } 61 | 62 | public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) 63 | { 64 | var list = value switch 65 | { 66 | IEnumerable collection => collection, 67 | T single => new List 68 | { 69 | single 70 | }, 71 | _ => throw new JsonException("不支持的序列化类型") 72 | }; 73 | 74 | serializer.Serialize(writer, list); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /MFAAvalonia/Helper/Converters/ReplaceConverter.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using Newtonsoft.Json.Linq; 3 | using System; 4 | using System.Collections.Generic; 5 | 6 | namespace MFAAvalonia.Helper.Converters; 7 | 8 | public class ReplaceConverter : JsonConverter 9 | { 10 | public override bool CanConvert(Type objectType) 11 | { 12 | return objectType == typeof(List); 13 | } 14 | 15 | public override object ReadJson(JsonReader reader, 16 | Type objectType, 17 | object existingValue, 18 | JsonSerializer serializer) 19 | { 20 | var token = JToken.Load(reader); 21 | if (token.Type == JTokenType.Array) 22 | { 23 | if (token.First?.Type == JTokenType.Array) 24 | { 25 | return token.ToObject>(); 26 | } 27 | 28 | var list = new List 29 | { 30 | token.ToObject() ?? [] 31 | }; 32 | return list; 33 | } 34 | 35 | throw new JsonSerializationException("Unexpected token type: " + token.Type); 36 | } 37 | 38 | public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) 39 | { 40 | if (value is List list) 41 | { 42 | if (list.Count == 1) 43 | { 44 | serializer.Serialize(writer, list[0]); 45 | } 46 | else 47 | { 48 | serializer.Serialize(writer, list); 49 | } 50 | } 51 | else 52 | { 53 | writer.WriteValue(value?.ToString() ?? string.Empty); 54 | } 55 | 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /MFAAvalonia/Helper/Converters/StringOrBoolOrObjectConverter.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using Newtonsoft.Json.Linq; 3 | using System; 4 | 5 | namespace MFAAvalonia.Helper.Converters; 6 | 7 | public class StringOrBoolOrObjectConverter : JsonConverter 8 | { 9 | // 允许转换的类型:字符串或动态对象 10 | public override bool CanConvert(Type objectType) 11 | { 12 | return objectType == typeof(string) || objectType == typeof(bool) || objectType == typeof(object); 13 | } 14 | 15 | // 反序列化逻辑 16 | public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) 17 | { 18 | JToken token = JToken.Load(reader); 19 | 20 | // 情况 1:JSON 值为字符串 21 | if (token.Type == JTokenType.String) 22 | { 23 | return token.ToObject(); 24 | } 25 | if (token.Type == JTokenType.Boolean) 26 | { 27 | return token.ToObject(); 28 | } 29 | 30 | // 情况 2:JSON 值为对象(动态解析为 JObject) 31 | if (token.Type == JTokenType.Object) 32 | { 33 | // 使用 JObject 动态处理任意结构 34 | return token.ToObject(); 35 | } 36 | 37 | throw new JsonSerializationException($"不支持的 JSON 类型:{token.Type}"); 38 | } 39 | 40 | // 序列化逻辑 41 | public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) 42 | { 43 | // 情况 1:输入为字符串类型 44 | if (value is string str) 45 | { 46 | writer.WriteValue(str); 47 | return; 48 | } 49 | if (value is bool b) 50 | { 51 | writer.WriteValue(b); 52 | return; 53 | } 54 | // 情况 2:输入为动态对象(如 JObject 或自定义类型) 55 | if (value is JObject jObj) 56 | { 57 | jObj.WriteTo(writer); 58 | } 59 | else 60 | { 61 | // 处理其他动态对象(如字典) 62 | JToken.FromObject(value, serializer).WriteTo(writer); 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /MFAAvalonia/Helper/Converters/StringOrObjectConverter.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using Newtonsoft.Json.Linq; 3 | using System; 4 | 5 | namespace MFAAvalonia.Helper.Converters; 6 | 7 | public class StringOrObjectConverter : JsonConverter 8 | { 9 | // 允许转换的类型:字符串或动态对象 10 | public override bool CanConvert(Type objectType) 11 | { 12 | return objectType == typeof(string) || objectType == typeof(object); 13 | } 14 | 15 | // 反序列化逻辑 16 | public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) 17 | { 18 | JToken token = JToken.Load(reader); 19 | 20 | // 情况 1:JSON 值为字符串 21 | if (token.Type == JTokenType.String) 22 | { 23 | return token.ToObject(); 24 | } 25 | 26 | // 情况 2:JSON 值为对象(动态解析为 JObject) 27 | if (token.Type == JTokenType.Object) 28 | { 29 | // 使用 JObject 动态处理任意结构 30 | return token.ToObject(); 31 | } 32 | 33 | throw new JsonSerializationException($"不支持的 JSON 类型:{token.Type}"); 34 | } 35 | 36 | // 序列化逻辑 37 | public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) 38 | { 39 | // 情况 1:输入为字符串类型 40 | if (value is string str) 41 | { 42 | writer.WriteValue(str); 43 | return; 44 | } 45 | 46 | // 情况 2:输入为动态对象(如 JObject 或自定义类型) 47 | if (value is JObject jObj) 48 | { 49 | jObj.WriteTo(writer); 50 | } 51 | else 52 | { 53 | // 处理其他动态对象(如字典) 54 | JToken.FromObject(value, serializer).WriteTo(writer); 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /MFAAvalonia/Helper/Converters/TitleConverter.cs: -------------------------------------------------------------------------------- 1 | using Avalonia; 2 | using Avalonia.Data.Converters; 3 | using Avalonia.Markup.Xaml; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Globalization; 7 | 8 | namespace MFAAvalonia.Helper.Converters; 9 | 10 | public class TitleConverter : MarkupExtension, IMultiValueConverter 11 | { 12 | public override object ProvideValue(IServiceProvider serviceProvider) => this; 13 | 14 | public object? Convert(IList values, Type targetType, object? parameter, CultureInfo culture) 15 | { 16 | // 安全解包参数(处理 UnsetValue 和 null) 17 | var customTitle = SafeGetValue(values, 0); 18 | var isCustomVisible = SafeGetValue(values, 1); 19 | var appName = SafeGetValue(values, 2); 20 | var appVersion = SafeGetValue(values, 3); 21 | var resourceName = SafeGetValue(values, 4); 22 | var resourceVersion = SafeGetValue(values, 5); 23 | var isResourceVisible = SafeGetValue(values, 6); 24 | 25 | var result = $"{appName} {appVersion}"; 26 | // 主逻辑 27 | if (isCustomVisible && !string.IsNullOrEmpty(customTitle)) 28 | result = customTitle; 29 | 30 | if (isResourceVisible && !string.IsNullOrEmpty(resourceName)) 31 | result = $"{appName} {appVersion} | {resourceName} {resourceVersion}"; 32 | return result; 33 | } 34 | 35 | public object[] ConvertBack(object? value, Type[] targetTypes, object? parameter, CultureInfo culture) 36 | { 37 | throw new NotSupportedException(); 38 | } 39 | 40 | /// 41 | /// 安全获取绑定值(处理 UnsetValue 和类型转换) 42 | /// 43 | private T SafeGetValue(IList values, int index, T defaultValue = default) 44 | { 45 | if (index >= values.Count) return defaultValue; 46 | 47 | var value = values[index]; 48 | 49 | // 处理 Avalonia 的 UnsetValue 50 | if (value is UnsetValueType || value == null) return defaultValue; 51 | 52 | try 53 | { 54 | return (T)System.Convert.ChangeType(value, typeof(T)); 55 | } 56 | catch 57 | { 58 | return defaultValue; 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /MFAAvalonia/Helper/Converters/UniversalEnumConverter.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using System; 3 | using System.Numerics; 4 | 5 | namespace MFAAvalonia.Helper.Converters; 6 | 7 | 8 | public class UniversalEnumConverter : JsonConverter where T : struct, Enum 9 | { 10 | public override T ReadJson(JsonReader reader, Type objectType, T existingValue, bool hasExistingValue, JsonSerializer serializer) 11 | { 12 | if (reader.Value is BigInteger bigInt) 13 | return ConvertFromBigInteger(bigInt); 14 | if (reader.Value != null && reader.TokenType == JsonToken.Integer) 15 | return (T)Enum.ToObject(typeof(T), reader.Value); 16 | if (reader.TokenType == JsonToken.String) 17 | return Enum.Parse(reader.Value?.ToString() ?? string.Empty, true); 18 | throw new JsonException($"无法转换 {reader.Value} 到 {typeof(T)}"); 19 | } 20 | private T ConvertFromBigInteger(BigInteger bigInt) 21 | { 22 | Type underlyingType = Enum.GetUnderlyingType(typeof(T)); 23 | 24 | try 25 | { 26 | checked 27 | { 28 | return underlyingType switch 29 | { 30 | _ when underlyingType == typeof(ulong) => (T)Enum.ToObject(typeof(T), (ulong)bigInt), 31 | _ when underlyingType == typeof(long) => (T)Enum.ToObject(typeof(T), (long)bigInt), 32 | _ when underlyingType == typeof(uint) => (T)Enum.ToObject(typeof(T), (uint)bigInt), 33 | _ when underlyingType == typeof(int) => (T)Enum.ToObject(typeof(T), (int)bigInt), 34 | _ => throw new NotSupportedException($"不支持的底层类型: {underlyingType.Name}") 35 | }; 36 | } 37 | } 38 | catch (OverflowException) 39 | { 40 | throw new JsonException($"数值 {bigInt} 超过 {underlyingType.Name} 的范围"); 41 | } 42 | } 43 | public override void WriteJson(JsonWriter writer, T value, JsonSerializer serializer) 44 | { 45 | writer.WriteValue(value.ToString()); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /MFAAvalonia/Helper/Converters/UniversalEqualityConverter.cs: -------------------------------------------------------------------------------- 1 | using Avalonia.Data; 2 | using Avalonia.Data.Converters; 3 | using Avalonia.Markup.Xaml; 4 | using Avalonia.Markup.Xaml.MarkupExtensions; 5 | using MFAAvalonia.Extensions.MaaFW; 6 | using System; 7 | using System.Globalization; 8 | 9 | namespace MFAAvalonia.Helper.Converters; 10 | 11 | public class UniversalEqualityConverter : MarkupExtension, IValueConverter 12 | { 13 | public override object ProvideValue(IServiceProvider serviceProvider) => this; 14 | 15 | public object Convert(object? value, Type targetType, object? parameter, CultureInfo culture) 16 | { 17 | if (value == null || parameter == null) 18 | return false; 19 | var paramStr = parameter.ToString(); 20 | var valueStr = value.ToString(); 21 | 22 | return valueStr == paramStr; 23 | } 24 | 25 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 26 | { 27 | if (value is true) 28 | { 29 | if (targetType.IsEnum && parameter != null) 30 | { 31 | if (parameter is string strParam && Enum.IsDefined(targetType, strParam)) 32 | return Enum.Parse(targetType, strParam); 33 | 34 | if (parameter.GetType() == targetType) 35 | return parameter; 36 | } 37 | 38 | // 处理数字类型 39 | if (parameter is string strVal) 40 | { 41 | try 42 | { 43 | return System.Convert.ChangeType(strVal, targetType); 44 | } 45 | catch 46 | { 47 | /* 忽略转换错误 */ 48 | } 49 | } 50 | 51 | // 处理普通对象 52 | if (parameter?.GetType() == targetType) 53 | return parameter; 54 | } 55 | 56 | return BindingOperations.DoNothing; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /MFAAvalonia/Helper/DispatcherHelper.cs: -------------------------------------------------------------------------------- 1 | using Avalonia; 2 | using Avalonia.Threading; 3 | using System; 4 | 5 | namespace MFAAvalonia.Helper; 6 | 7 | public static class DispatcherHelper 8 | { 9 | public static void RunOnMainThread(Action action) 10 | { 11 | if (Dispatcher.UIThread.CheckAccess()) 12 | { 13 | action(); 14 | } 15 | 16 | Dispatcher.UIThread.Invoke(action); 17 | 18 | } 19 | 20 | public static T RunOnMainThread(Func func) 21 | { 22 | if (Dispatcher.UIThread.CheckAccess()) 23 | { 24 | return func(); 25 | } 26 | 27 | return Dispatcher.UIThread.Invoke(func); 28 | 29 | } 30 | 31 | public static void PostOnMainThread(Action func) 32 | { 33 | if (Dispatcher.UIThread.CheckAccess()) 34 | { 35 | func(); 36 | } 37 | 38 | Dispatcher.UIThread.Post(func); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /MFAAvalonia/Helper/IconHelper.cs: -------------------------------------------------------------------------------- 1 | using Avalonia; 2 | using Avalonia.Controls; 3 | using Avalonia.Media; 4 | using Avalonia.Media.Imaging; 5 | using Avalonia.Platform; 6 | using MFAAvalonia.Helper; 7 | using System; 8 | using System.IO; 9 | using System.Runtime.InteropServices; 10 | 11 | namespace MFAAvalonia.Helper; 12 | 13 | public static class IconHelper 14 | { 15 | private static readonly Lazy LazyIcon = new(LoadIconWithFallback); 16 | public static Bitmap Icon => LazyIcon.Value; 17 | public static WindowIcon WindowIcon => new WindowIcon(Icon); 18 | 19 | private static Bitmap LoadIconWithFallback() 20 | { 21 | try 22 | { 23 | // 尝试从执行目录加载 24 | var exeDirectory = AppContext.BaseDirectory; 25 | var iconPath = Path.Combine(exeDirectory, "logo.ico"); 26 | 27 | if (File.Exists(iconPath)) 28 | { 29 | using var fileStream = File.OpenRead(iconPath); 30 | return new Bitmap(fileStream); 31 | } 32 | 33 | // 尝试从嵌入资源加载 34 | var uri = new Uri("avares://MFAAvalonia/Assets/logo.ico"); 35 | if (AssetLoader.Exists(uri)) 36 | { 37 | var assets = AssetLoader.Open(uri); 38 | return new Bitmap(assets); 39 | } 40 | 41 | LoggerHelper.Warning("未找到内嵌图标资源"); 42 | return CreateEmptyImage(); 43 | } 44 | catch (Exception ex) 45 | { 46 | LoggerHelper.Error($"图标加载失败: {ex}"); 47 | return CreateEmptyImage(); 48 | } 49 | } 50 | 51 | private static Bitmap CreateEmptyImage() 52 | { 53 | // 创建 1x1 透明位图 54 | var size = new PixelSize(1, 1); 55 | var dpi = new Vector(96, 96); 56 | var format = PixelFormat.Rgba8888; 57 | 58 | var writeableBitmap = new WriteableBitmap(size, dpi, format); 59 | 60 | using var buffer = writeableBitmap.Lock(); 61 | 62 | var pixels = new byte[buffer.Size.Width * buffer.Size.Height * 4]; 63 | 64 | for (var i = 0; i < pixels.Length; i += 4) 65 | { 66 | pixels[i] = 0; // R 67 | pixels[i + 1] = 0; // G 68 | pixels[i + 2] = 0; // B 69 | pixels[i + 3] = 0; // A 70 | } 71 | 72 | Marshal.Copy(pixels, 0, buffer.Address, pixels.Length); 73 | 74 | 75 | return writeableBitmap; 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /MFAAvalonia/Helper/LangKeys.tt: -------------------------------------------------------------------------------- 1 | <#@ template debug="false" hostspecific="true" language="C#" #> 2 | <#@ assembly name="System.Core" #> 3 | <#@ assembly name="System.Xml" #> 4 | <#@ assembly name="System.Xml.Linq" #> 5 | <#@ import namespace="System.Linq" #> 6 | <#@ import namespace="System.Text" #> 7 | <#@ import namespace="System.Collections.Generic" #> 8 | <#@ import namespace="System.Xml.Linq" #> 9 | <#@ import namespace="System.IO" #> 10 | <#@ output extension=".cs" #> 11 | //------------------------------------------------------------------------------ 12 | // 13 | // This code was generated by a tool. 14 | // Changes to this file may cause incorrect behavior and will be lost if 15 | // the code is regenerated. 16 | // 17 | //------------------------------------------------------------------------------ 18 | <# 19 | const string ResourceFileName = "../Assets/Localization/Strings.resx"; 20 | #> 21 | 22 | namespace MFAAvalonia.Helper; 23 | 24 | public static class LangKeys 25 | { 26 | <# 27 | var resourceKeys = XElement.Load(this.Host.ResolvePath(ResourceFileName)) 28 | .Elements("data") 29 | .Select(item => item.Attribute("name")?.Value) 30 | .Where(item => item != null); 31 | 32 | var resourceDesignerName = Path.GetFileNameWithoutExtension(ResourceFileName); 33 | 34 | foreach (string resourceKey in resourceKeys) 35 | { 36 | #> 37 | public static readonly string <#= resourceKey #> = "<#= resourceKey #>"; 38 | <# 39 | } 40 | #> 41 | } -------------------------------------------------------------------------------- /MFAAvalonia/Helper/LoggerHelper.cs: -------------------------------------------------------------------------------- 1 | using MFAAvalonia.Helper; 2 | using Serilog; 3 | using System; 4 | 5 | namespace MFAAvalonia.Helper; 6 | 7 | public static class LoggerHelper 8 | { 9 | private static readonly ILogger Logger = new LoggerConfiguration() 10 | .WriteTo.File( 11 | $"logs/log-{DateTime.Now.ToString("yyyy-MM-dd")}.txt", 12 | outputTemplate: "[{Timestamp:yyyy-MM-dd HH:mm:ss.fff}][{Level:u3}] {Message:lj}{NewLine}{Exception}").CreateLogger(); 13 | 14 | public static void Info(object message) 15 | { 16 | Logger.Information(message.ToString() ?? string.Empty); 17 | Console.WriteLine("[INFO]" + message); 18 | } 19 | 20 | public static void Error(object message) 21 | { 22 | Logger.Error(message.ToString() ?? string.Empty); 23 | Console.WriteLine("[ERROR]" + message); 24 | } 25 | 26 | public static void Error(object message, Exception e) 27 | { 28 | Logger.Error(message.ToString()); 29 | Logger.Error(e.ToString()); 30 | Console.WriteLine("[ERROR]" + message); 31 | Console.WriteLine("[ERROR]" + e); 32 | } 33 | public static void Warning(object message) 34 | { 35 | Logger.Warning(message.ToString() ?? string.Empty); 36 | Console.WriteLine("[WARN]" + message); 37 | } 38 | 39 | 40 | } 41 | -------------------------------------------------------------------------------- /MFAAvalonia/Helper/MFAUrls.cs: -------------------------------------------------------------------------------- 1 | using CommunityToolkit.Mvvm.Input; 2 | using Org.BouncyCastle.Ocsp; 3 | using System; 4 | using System.Diagnostics; 5 | using System.Windows.Input; 6 | 7 | namespace MFAAvalonia.Helper; 8 | 9 | public class MFAUrls 10 | { 11 | public const string GitHub = "https://github.com/SweetSmellFox/MFAAvalonia"; 12 | 13 | public const string GitHubIssues = $"{GitHub}/issues"; 14 | 15 | public const string NewIssueUri = $"{GitHubIssues}/new?assignees=&labels=bug&template=cn-bug-report.yaml"; 16 | 17 | public const string PurchaseLink = "https://mirrorchyan.com"; 18 | } 19 | -------------------------------------------------------------------------------- /MFAAvalonia/Helper/ServiceRegistry.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace MFAAvalonia.Helper; 5 | 6 | // ServiceRegistry.cs 7 | public static class ServiceRegistry 8 | { 9 | public static List RegisteredTypes { get; } = new(); 10 | 11 | public static void Register(Type type) => RegisteredTypes.Add(type); 12 | } -------------------------------------------------------------------------------- /MFAAvalonia/Helper/ThemeHelper.cs: -------------------------------------------------------------------------------- 1 | using Avalonia.Styling; 2 | using SukiUI; 3 | 4 | namespace MFAAvalonia.Helper; 5 | 6 | public static class ThemeHelper 7 | { 8 | static ThemeHelper() 9 | { 10 | 11 | } 12 | public static void UpdateThemeIndexChanged(int value) 13 | { 14 | switch (value) 15 | { 16 | case 0: 17 | SukiTheme.GetInstance().ChangeBaseTheme(ThemeVariant.Light); 18 | break; 19 | case 1: 20 | SukiTheme.GetInstance().ChangeBaseTheme(ThemeVariant.Dark); 21 | break; 22 | default: 23 | // ThemeManager.Current.UsingWindowsAppTheme = true; 24 | break; 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /MFAAvalonia/Helper/ToastHelper.cs: -------------------------------------------------------------------------------- 1 | using Avalonia.Controls.Notifications; 2 | using SukiUI.Toasts; 3 | using System; 4 | 5 | namespace MFAAvalonia.Helper; 6 | 7 | public static class ToastHelper 8 | { 9 | public static SukiToastBuilder CreateToastByType(NotificationType toastType, string title = "", string content = "", int duration = 3) 10 | { 11 | return Instances.ToastManager.CreateToast() 12 | .WithTitle(title) 13 | .WithContent( 14 | content) 15 | .OfType(toastType).Dismiss().After(TimeSpan.FromSeconds(duration)) 16 | .Dismiss().ByClicking(); 17 | } 18 | 19 | public static void Success(string title = "", string content = "", int duration = 3) 20 | { 21 | DispatcherHelper.RunOnMainThread(() => CreateToastByType(NotificationType.Success, title, content, duration).Queue()); 22 | } 23 | 24 | public static void Info(string title = "", string content = "", int duration = 3) 25 | { 26 | DispatcherHelper.RunOnMainThread(() => CreateToastByType(NotificationType.Information, title, content, duration).Queue()); 27 | } 28 | 29 | public static void Warn(string title = "", string content = "", int duration = 3) 30 | { 31 | DispatcherHelper.RunOnMainThread(() => CreateToastByType(NotificationType.Warning, title, content, duration).Queue()); 32 | } 33 | 34 | public static void Error(string title = "", string content = "", int duration = 3) 35 | { 36 | DispatcherHelper.RunOnMainThread(() => CreateToastByType(NotificationType.Error, title, content, duration).Queue()); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /MFAAvalonia/Helper/ToastNotification.cs: -------------------------------------------------------------------------------- 1 | using Avalonia.Controls.Notifications; 2 | using DesktopNotifications.Apple; 3 | using DesktopNotifications.FreeDesktop; 4 | using DesktopNotifications.Windows; 5 | using MFAAvalonia.Helper; 6 | using System; 7 | using System.Collections.Generic; 8 | using INotificationManager = DesktopNotifications.INotificationManager; 9 | using Notification = DesktopNotifications.Notification; 10 | 11 | namespace MFAAvalonia.Helper; 12 | 13 | public static class ToastNotification 14 | { 15 | private static INotificationManager GetNotificationManager() 16 | { 17 | 18 | if (OperatingSystem.IsWindows()) 19 | { 20 | return new WindowsNotificationManager(); 21 | } 22 | if (OperatingSystem.IsMacOS() || OperatingSystem.IsIOS()) 23 | { 24 | return new AppleNotificationManager(); 25 | } 26 | if (OperatingSystem.IsLinux() || OperatingSystem.IsFreeBSD()) 27 | { 28 | return new FreeDesktopNotificationManager(); 29 | } 30 | 31 | throw new PlatformNotSupportedException("Current system does not support any available notification manager"); 32 | } 33 | 34 | public static void Show(string title = "", string message = "") 35 | { 36 | 37 | try 38 | { 39 | var notificationManager = GetNotificationManager(); 40 | 41 | var notification = new Notification 42 | { 43 | Title = title, 44 | Body = message 45 | }; 46 | 47 | notificationManager.ShowNotification(notification); 48 | } 49 | catch (Exception e) 50 | { 51 | LoggerHelper.Error(e); 52 | } 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /MFAAvalonia/Helper/ValueType/MFAHotKey.cs: -------------------------------------------------------------------------------- 1 | using Avalonia.Input; 2 | using CommunityToolkit.Mvvm.ComponentModel; 3 | using MFAAvalonia.Extensions; 4 | using System; 5 | using System.Collections.Generic; 6 | 7 | namespace MFAAvalonia.Helper.ValueType; 8 | 9 | public partial class MFAHotKey : ObservableObject 10 | { 11 | public static readonly MFAHotKey NOTSET = new(true) 12 | { 13 | ResourceKey = "HotKeyNotSet", 14 | }; 15 | 16 | public static readonly MFAHotKey ERROR = new(true) 17 | { 18 | ResourceKey = "HotKeyOccupiedWarning", 19 | }; 20 | 21 | public static readonly MFAHotKey PRESSING = new(true) 22 | { 23 | ResourceKey = "HotKeyPressing", 24 | }; 25 | 26 | [ObservableProperty] private bool _isTip; 27 | [ObservableProperty] private KeyGesture? _gesture; 28 | [ObservableProperty] private string _resourceKey; 29 | partial void OnResourceKeyChanged(string value) 30 | { 31 | UpdateName(); 32 | 33 | } 34 | 35 | public MFAHotKey(bool isTip = false) 36 | { 37 | IsTip = isTip; 38 | LanguageHelper.LanguageChanged += (_, _) => UpdateName(); 39 | UpdateName(); 40 | } 41 | 42 | public MFAHotKey(KeyGesture gesture) 43 | { 44 | Gesture = gesture; 45 | UpdateName(); 46 | } 47 | 48 | [ObservableProperty] private string _name = string.Empty; 49 | 50 | public void UpdateName() 51 | { 52 | Name = IsTip ? ResourceKey.ToLocalization() : (Gesture?.ToString() ?? ""); 53 | } 54 | 55 | // 保留 Equals/GetHashCode 等核心逻辑(修改为比较 KeyGesture) 56 | public bool Equals(MFAHotKey? other) => 57 | other != null && Gesture == other.Gesture; 58 | 59 | // 新增 KeyGesture 解析逻辑 60 | 61 | public static MFAHotKey Parse(string input) 62 | { 63 | if (string.IsNullOrWhiteSpace(input)) 64 | return NOTSET; 65 | try 66 | { 67 | return new MFAHotKey(KeyGesture.Parse(input)); 68 | } 69 | catch (Exception) 70 | { 71 | return NOTSET; 72 | } 73 | } 74 | 75 | public override string ToString() 76 | => Name; 77 | } 78 | -------------------------------------------------------------------------------- /MFAAvalonia/Helper/ValueType/MFATask.cs: -------------------------------------------------------------------------------- 1 | using CommunityToolkit.Mvvm.ComponentModel; 2 | using MaaFramework.Binding; 3 | using MFAAvalonia.Extensions.MaaFW; 4 | using MFAAvalonia.Views.Windows; 5 | using MFAAvalonia.Helper; 6 | using Serilog; 7 | using System; 8 | using System.Collections.Generic; 9 | using System.Threading; 10 | using System.Threading.Tasks; 11 | 12 | namespace MFAAvalonia.Helper.ValueType; 13 | 14 | public partial class MFATask : ObservableObject 15 | { 16 | public enum MFATaskType 17 | { 18 | MFA, 19 | MAAFW 20 | } 21 | 22 | public enum MFATaskStatus 23 | { 24 | NOT_STARTED, 25 | STOPPED, 26 | SUCCEEDED, 27 | FAILED, 28 | } 29 | 30 | [ObservableProperty] private string? _name = string.Empty; 31 | [ObservableProperty] private MFATaskType _type = MFATaskType.MFA; 32 | [ObservableProperty] private int _count = 1; 33 | [ObservableProperty] private Func _action; 34 | [ObservableProperty] private Dictionary _tasks = new(); 35 | 36 | 37 | public async Task Run(CancellationToken token) 38 | { 39 | try 40 | { 41 | for (int i = 0; i < Count; i++) 42 | { 43 | token.ThrowIfCancellationRequested(); 44 | if (Type == MFATaskType.MAAFW) 45 | RootView.AddLogByKey("TaskStart", null, true, Name ?? string.Empty); 46 | await Action(); 47 | } 48 | return MFATaskStatus.SUCCEEDED; 49 | } 50 | catch (MaaJobStatusException) 51 | { 52 | LoggerHelper.Error($"Task {Name} failed to run"); 53 | return MFATaskStatus.FAILED; 54 | } 55 | catch (OperationCanceledException) 56 | { 57 | return MFATaskStatus.STOPPED; 58 | } 59 | catch (Exception ex) 60 | { 61 | LoggerHelper.Error(ex); 62 | return MFATaskStatus.FAILED; 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /MFAAvalonia/Helper/ValueType/ObservableQueue.cs: -------------------------------------------------------------------------------- 1 | using CommunityToolkit.Mvvm.ComponentModel; 2 | using System; 3 | using System.Collections.Generic; 4 | 5 | namespace MFAAvalonia.Helper.ValueType; 6 | 7 | public partial class ObservableQueue : ObservableObject 8 | { 9 | private readonly Queue _queue = new(); 10 | 11 | [ObservableProperty] private int _count; 12 | 13 | public EventHandler? CountChanged; 14 | 15 | public ObservableQueue() 16 | { 17 | Count = _queue.Count; 18 | } 19 | partial void OnCountChanged(int oldValue, int newValue) 20 | { 21 | CountChanged?.Invoke(this, new CountChangedEventArgs(oldValue, newValue)); 22 | } 23 | 24 | public void Enqueue(T task) 25 | { 26 | _queue.Enqueue(task); 27 | Count = _queue.Count; 28 | } 29 | 30 | public T Dequeue() 31 | { 32 | var task = _queue.Dequeue(); 33 | Count = _queue.Count; 34 | return task; 35 | } 36 | 37 | public void Clear() 38 | { 39 | _queue.Clear(); 40 | Count = _queue.Count; 41 | } 42 | 43 | public class CountChangedEventArgs(int oldValue, int newValue) : EventArgs 44 | { 45 | public int OldValue => oldValue; 46 | public int NewValue => newValue; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /MFAAvalonia/Img/preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SweetSmellFox/MFAAvalonia/1c21cad95195cf84dc9c23d445117135847b2b8d/MFAAvalonia/Img/preview.png -------------------------------------------------------------------------------- /MFAAvalonia/MFAAvalonia.desktop: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | Name=testapp 3 | Type=Application 4 | Exec=/usr/share/testapp/testapp 5 | Icon=/usr/share/icons/testapp.png -------------------------------------------------------------------------------- /MFAAvalonia/MFAAvalonia.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SweetSmellFox/MFAAvalonia/1c21cad95195cf84dc9c23d445117135847b2b8d/MFAAvalonia/MFAAvalonia.ico -------------------------------------------------------------------------------- /MFAAvalonia/NuGet.Config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /MFAAvalonia/Program.cs: -------------------------------------------------------------------------------- 1 | using Avalonia; 2 | using Avalonia.Controls; 3 | using Avalonia.Controls.ApplicationLifetimes; 4 | using MFAAvalonia.Configuration; 5 | using MFAAvalonia.Views.Windows; 6 | using MFAAvalonia.Helper; 7 | using Newtonsoft.Json; 8 | using System; 9 | using System.Collections.Generic; 10 | 11 | namespace MFAAvalonia; 12 | 13 | sealed class Program 14 | { 15 | public static Dictionary ParseArguments(string[] args) 16 | { 17 | var parameters = new Dictionary(StringComparer.OrdinalIgnoreCase); 18 | for (int i = 0; i < args.Length; i++) 19 | { 20 | // 识别以 "-" 或 "--" 开头的键 21 | if (args[i].StartsWith("-")) 22 | { 23 | string key = args[i].TrimStart('-').ToLower(); 24 | // 检查下一个元素是否为值(非键) 25 | if (i + 1 < args.Length && !args[i + 1].StartsWith("-")) 26 | { 27 | parameters[key] = args[i + 1]; 28 | i++; // 跳过已处理的值 29 | } 30 | else 31 | { 32 | parameters[key] = ""; // 标记无值的键 33 | } 34 | } 35 | } 36 | return parameters; 37 | } 38 | 39 | // Initialization code. Don't use any Avalonia, third-party APIs or any 40 | // SynchronizationContext-reliant code before AppMain is called: things aren't initialized 41 | // yet and stuff might break. 42 | public static Dictionary Args { get; private set; } = new(); 43 | [STAThread] 44 | public static void Main(string[] args) 45 | { 46 | try 47 | { 48 | var parsedArgs = ParseArguments(args); 49 | LoggerHelper.Info("解析参数:" + JsonConvert.SerializeObject(parsedArgs, Formatting.Indented)); 50 | Args = parsedArgs; 51 | BuildAvaloniaApp() 52 | .StartWithClassicDesktopLifetime(args, ShutdownMode.OnMainWindowClose); 53 | } 54 | catch (Exception e) 55 | { 56 | LoggerHelper.Error($"总异常捕获:{e}"); 57 | } 58 | } 59 | 60 | // Avalonia configuration, don't remove; also used by visual designer. 61 | public static AppBuilder BuildAvaloniaApp() 62 | { 63 | return AppBuilder.Configure() 64 | .UsePlatformDetect() 65 | .WithInterFont() 66 | .LogToTrace(); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /MFAAvalonia/Utilities/Attributes/LazyStaticAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace MFAAvalonia.Utilities.Attributes 4 | { 5 | 6 | [AttributeUsage(AttributeTargets.Class, Inherited = false)] 7 | public class LazyStaticAttribute : Attribute; 8 | } -------------------------------------------------------------------------------- /MFAAvalonia/Utilities/Attributes/MaaJsonPropertyAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace MFAAvalonia.Utilities.Attributes; 4 | 5 | [AttributeUsage(AttributeTargets.Field)] 6 | public class MaaJsonPropertyAttribute(string name) : Attribute 7 | { 8 | public string Name => name; 9 | 10 | } -------------------------------------------------------------------------------- /MFAAvalonia/Utilities/Attributes/MaaPropertyAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace MFAAvalonia.Utilities.Attributes; 4 | 5 | [AttributeUsage(AttributeTargets.Class, Inherited = false)] 6 | public class MaaPropertyAttribute : Attribute; 7 | -------------------------------------------------------------------------------- /MFAAvalonia/Utilities/UrlUtilities.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using System.Runtime.InteropServices; 4 | using System.Windows.Input; 5 | 6 | namespace MFAAvalonia.Utilities; 7 | 8 | public static class UrlUtilities 9 | { 10 | /// 11 | /// Open the URL in the default browser. 12 | /// 13 | /// 14 | public static void OpenUrl(string url) 15 | { 16 | if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) 17 | Process.Start(new ProcessStartInfo(url.Replace("&", "^&")) 18 | { 19 | UseShellExecute = true 20 | }); 21 | else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) 22 | Process.Start("xdg-open", url); 23 | else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) 24 | Process.Start("open", url); 25 | } 26 | 27 | public static OpenLinkCommand OpenLink = new(); 28 | 29 | public class OpenLinkCommand : ICommand 30 | { 31 | public bool CanExecute(object parameter) => true; 32 | 33 | public void Execute(object parameter) 34 | { 35 | if (!(parameter is string str1)) 36 | return; 37 | try 38 | { 39 | OpenUrl(str1); 40 | } 41 | catch 42 | { 43 | } 44 | } 45 | #pragma warning disable CS0067 //从不使用事件 46 | public event EventHandler CanExecuteChanged; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /MFAAvalonia/ViewLocator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Avalonia.Controls; 3 | using Avalonia.Controls.Templates; 4 | using CommunityToolkit.Mvvm.ComponentModel; 5 | using MFAAvalonia.Helper; 6 | using MFAAvalonia.ViewModels; 7 | using System.Collections.Generic; 8 | 9 | namespace MFAAvalonia; 10 | 11 | public class ViewLocator(ViewsHelper views) : IDataTemplate 12 | { 13 | private readonly Dictionary _controlCache = []; 14 | 15 | public Control Build(object? param) 16 | { 17 | if (param is null) 18 | { 19 | return CreateText("Data is null."); 20 | } 21 | 22 | if (_controlCache.TryGetValue(param, out var control)) 23 | { 24 | return control; 25 | } 26 | 27 | if (views.TryCreateView(param, out var view)) 28 | { 29 | _controlCache.Add(param, view); 30 | 31 | return view; 32 | } 33 | 34 | return CreateText($"No View For {param.GetType().Name}."); 35 | } 36 | 37 | public bool Match(object? data) => data is ObservableObject; 38 | 39 | private static TextBlock CreateText(string text) => new TextBlock { Text = text }; 40 | } 41 | -------------------------------------------------------------------------------- /MFAAvalonia/ViewModels/Other/LocalizationViewModel.cs: -------------------------------------------------------------------------------- 1 | using CommunityToolkit.Mvvm.ComponentModel; 2 | using MFAAvalonia.Extensions; 3 | using MFAAvalonia.Helper; 4 | using System; 5 | 6 | namespace MFAAvalonia.ViewModels.Other; 7 | 8 | public partial class LocalizationViewModel : ViewModelBase 9 | { 10 | [ObservableProperty] private string _resourceKey = string.Empty; 11 | 12 | partial void OnResourceKeyChanged(string value) 13 | { 14 | UpdateName(); 15 | } 16 | 17 | public LocalizationViewModel() { } 18 | 19 | private readonly string[]? _formatArgsKeys; 20 | 21 | public LocalizationViewModel(string resourceKey) 22 | { 23 | ResourceKey = resourceKey; 24 | LanguageHelper.LanguageChanged += OnLanguageChanged; 25 | } 26 | 27 | public LocalizationViewModel(string resourceKey, params string[] keys) 28 | { 29 | ResourceKey = resourceKey; 30 | _formatArgsKeys = keys; 31 | LanguageHelper.LanguageChanged += OnLanguageChanged; 32 | } 33 | 34 | private void OnLanguageChanged(object sender, EventArgs e) 35 | { 36 | UpdateName(); 37 | } 38 | 39 | [ObservableProperty] private string _name = string.Empty; 40 | [ObservableProperty] private object? _other; 41 | 42 | private void UpdateName() 43 | { 44 | if (string.IsNullOrWhiteSpace(ResourceKey)) 45 | return; 46 | if (_formatArgsKeys != null && _formatArgsKeys.Length != 0) 47 | Name = ResourceKey.ToLocalizationFormatted(true, _formatArgsKeys); 48 | else 49 | Name = ResourceKey.ToLocalization(); 50 | } 51 | 52 | 53 | public override string ToString() 54 | => ResourceKey; 55 | } 56 | -------------------------------------------------------------------------------- /MFAAvalonia/ViewModels/Other/SupportedLanguage.cs: -------------------------------------------------------------------------------- 1 | using CommunityToolkit.Mvvm.ComponentModel; 2 | 3 | namespace MFAAvalonia.ViewModels.Other; 4 | 5 | public partial class SupportedLanguage(string key, string name) : ViewModelBase 6 | { 7 | [ObservableProperty] private string _name = name; 8 | [ObservableProperty] private string _key = key; 9 | } 10 | -------------------------------------------------------------------------------- /MFAAvalonia/ViewModels/Other/ThemeItemViewModel.cs: -------------------------------------------------------------------------------- 1 |  2 | using CommunityToolkit.Mvvm.ComponentModel; 3 | using MFAAvalonia.Helper; 4 | using MFAAvalonia.ViewModels.UsersControls.Settings; 5 | using SukiUI.Models; 6 | using TextMateSharp.Themes; 7 | 8 | namespace MFAAvalonia.ViewModels.Other; 9 | 10 | public partial class ThemeItemViewModel(SukiColorTheme theme, GuiSettingsUserControlModel settingsModel) : ViewModelBase 11 | { 12 | [ObservableProperty] private bool _isSelected = settingsModel.CurrentColorTheme.DisplayName == theme.DisplayName; 13 | 14 | [ObservableProperty] private SukiColorTheme _theme = theme; 15 | 16 | 17 | partial void OnIsSelectedChanged(bool value) 18 | { 19 | if (value) 20 | Instances.GuiSettingsUserControlModel.CurrentColorTheme = Theme; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /MFAAvalonia/ViewModels/Pages/ResourcesViewModel.cs: -------------------------------------------------------------------------------- 1 | namespace MFAAvalonia.ViewModels.Pages; 2 | 3 | public class ResourcesViewModel : ViewModelBase 4 | { 5 | 6 | } 7 | -------------------------------------------------------------------------------- /MFAAvalonia/ViewModels/UsersControls/AdbEditorDialogViewModel.cs: -------------------------------------------------------------------------------- 1 | using Avalonia; 2 | using Avalonia.Controls; 3 | using Avalonia.Data; 4 | using Avalonia.Platform.Storage; 5 | using CommunityToolkit.Mvvm.ComponentModel; 6 | using CommunityToolkit.Mvvm.Input; 7 | using MaaFramework.Binding; 8 | using MFAAvalonia.Extensions; 9 | using MFAAvalonia.Helper; 10 | using MFAAvalonia.Views.UserControls; 11 | using SukiUI.Dialogs; 12 | using System.Threading.Tasks; 13 | 14 | namespace MFAAvalonia.ViewModels.UsersControls; 15 | 16 | public partial class AdbEditorDialogViewModel : ObservableObject 17 | { 18 | [ObservableProperty] private string _adbName = "Emulator".ToLocalization(); 19 | [ObservableProperty] private string _adbPath = string.Empty; 20 | [ObservableProperty] private string _adbSerial = string.Empty; 21 | [ObservableProperty] private string _adbConfig = "{}"; 22 | public ISukiDialog Dialog { get; set; } 23 | public AdbEditorDialogViewModel(AdbDeviceInfo? info, ISukiDialog dialog) 24 | { 25 | AdbName = info?.Name ?? AdbName; 26 | AdbPath = info?.AdbPath ?? AdbPath; 27 | AdbSerial = info?.AdbSerial ?? AdbSerial; 28 | AdbConfig = info?.Config ?? AdbConfig; 29 | Dialog = dialog; 30 | } 31 | 32 | [RelayCommand] 33 | async private Task Load() 34 | { 35 | var storageProvider = Instances.RootView.StorageProvider; 36 | 37 | // 配置文件选择器选项 38 | var options = new FilePickerOpenOptions 39 | { 40 | Title = "LoadFileTitle".ToLocalization(), 41 | FileTypeFilter = 42 | [ 43 | new FilePickerFileType("AllFilter".ToLocalization()) 44 | { 45 | Patterns = ["*"] // 支持所有文件类型 46 | } 47 | ] 48 | }; 49 | 50 | var result = await storageProvider.OpenFilePickerAsync(options); 51 | 52 | // 处理选择结果 53 | if (result is { Count: > 0 } && result[0].TryGetLocalPath() is { } path) 54 | { 55 | AdbPath = path; 56 | } 57 | } 58 | 59 | [RelayCommand] 60 | public void Save() 61 | { 62 | Instances.TaskQueueViewModel.Devices = [Output]; 63 | Instances.TaskQueueViewModel.CurrentDevice = Output; 64 | 65 | Dialog.Dismiss(); 66 | } 67 | 68 | public AdbDeviceInfo Output => new(AdbName, AdbPath, AdbSerial, AdbScreencapMethods.Default, 69 | AdbInputMethods.MinitouchAndAdbKey, AdbConfig); 70 | } 71 | -------------------------------------------------------------------------------- /MFAAvalonia/ViewModels/UsersControls/AddTaskDialogViewModel.cs: -------------------------------------------------------------------------------- 1 | using CommunityToolkit.Mvvm.ComponentModel; 2 | using CommunityToolkit.Mvvm.Input; 3 | using MFAAvalonia.Configuration; 4 | using MFAAvalonia.Extensions.MaaFW; 5 | using MFAAvalonia.Helper; 6 | using MFAAvalonia.Helper.ValueType; 7 | using SukiUI.Dialogs; 8 | using System.Collections.Generic; 9 | using System.Collections.ObjectModel; 10 | using System.Linq; 11 | 12 | namespace MFAAvalonia.ViewModels.UsersControls.Settings; 13 | 14 | public partial class AddTaskDialogViewModel : ViewModelBase 15 | { 16 | [ObservableProperty] private DragItemViewModel? _output; 17 | 18 | private ObservableCollection _item; 19 | 20 | public ObservableCollection Items 21 | { 22 | get => _item; 23 | set => SetProperty(ref _item, value); 24 | } 25 | 26 | public List Sources 27 | { 28 | get; 29 | set; 30 | } 31 | private int _selectedIndex; 32 | 33 | public int SelectedIndex 34 | { 35 | get => _selectedIndex; 36 | set => 37 | SetProperty(ref _selectedIndex, value); 38 | } 39 | 40 | public ISukiDialog Dialog { get; set; } 41 | public AddTaskDialogViewModel(ISukiDialog dialog, ICollection sources) 42 | { 43 | Dialog = dialog; 44 | Sources = sources.ToList(); 45 | _item = new ObservableCollection(sources); 46 | SelectedIndex = -1; 47 | } 48 | 49 | [RelayCommand] 50 | void Add() 51 | { 52 | if (Output != null) 53 | { 54 | var output = Output.Clone(); 55 | if (output.InterfaceItem.Option != null) 56 | output.InterfaceItem.Option.ForEach(MaaProcessor.Instance.SetDefaultOptionValue); 57 | 58 | Instances.TaskQueueViewModel.TaskItemViewModels.Add(Output.Clone()); 59 | ConfigurationManager.Current.SetValue(ConfigurationKeys.TaskItems, Instances.TaskQueueViewModel.TaskItemViewModels.ToList().Select(model => model.InterfaceItem)); 60 | } 61 | 62 | Dialog.Dismiss(); 63 | } 64 | 65 | [RelayCommand] 66 | void Cancel() 67 | { 68 | Dialog.Dismiss(); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /MFAAvalonia/ViewModels/UsersControls/CustomThemeDialogViewModel.cs: -------------------------------------------------------------------------------- 1 | using Avalonia.Media; 2 | using CommunityToolkit.Mvvm.ComponentModel; 3 | using CommunityToolkit.Mvvm.Input; 4 | using MFAAvalonia.Extensions; 5 | using MFAAvalonia.Helper; 6 | using MFAAvalonia.ViewModels; 7 | using SukiUI; 8 | using SukiUI.Dialogs; 9 | using SukiUI.Models; 10 | using System.Linq; 11 | using TextMateSharp.Themes; 12 | 13 | namespace MFAAvalonia.Views.UserControls; 14 | 15 | public partial class CustomThemeDialogViewModel(SukiTheme theme, ISukiDialog dialog) : ViewModelBase 16 | { 17 | [ObservableProperty] private string _displayName = "Pink"; 18 | [ObservableProperty] private Color _primaryColor = Colors.DeepPink; 19 | [ObservableProperty] private Color _accentColor = Colors.Pink; 20 | 21 | [RelayCommand] 22 | private void TryCreateTheme() 23 | { 24 | if (string.IsNullOrEmpty(DisplayName)) return; 25 | if (theme.ColorThemes.Any(t => t.DisplayName == DisplayName)) 26 | { 27 | ToastHelper.Error("ColorThemeAlreadyExists".ToLocalization()); 28 | dialog.Dismiss(); 29 | return; 30 | } 31 | var color = new SukiColorTheme(DisplayName, PrimaryColor, AccentColor); 32 | Instances.GuiSettingsUserControlModel.AddOtherColor(color); 33 | theme.AddColorTheme(color); 34 | theme.ChangeColorTheme(color); 35 | dialog.Dismiss(); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /MFAAvalonia/ViewModels/UsersControls/Settings/PerformanceUserControlModel.cs: -------------------------------------------------------------------------------- 1 | using Avalonia.Collections; 2 | using CommunityToolkit.Mvvm.ComponentModel; 3 | using MaaFramework.Binding; 4 | using MFAAvalonia.Configuration; 5 | using MFAAvalonia.Extensions.MaaFW; 6 | using MFAAvalonia.Helper.Converters; 7 | using MFAAvalonia.ViewModels.Other; 8 | 9 | namespace MFAAvalonia.ViewModels.UsersControls.Settings; 10 | 11 | public partial class PerformanceUserControlModel : ViewModelBase 12 | { 13 | public static AvaloniaList GpuOptions => 14 | [ 15 | new("GpuOptionAuto") 16 | { 17 | Other = InferenceDevice.Auto 18 | }, 19 | new("GpuOptionDisable") 20 | { 21 | Other = InferenceDevice.CPU 22 | } 23 | ]; 24 | 25 | [ObservableProperty] private InferenceDevice _gpuOption = ConfigurationManager.Current.GetValue(ConfigurationKeys.GPUOption, InferenceDevice.Auto, InferenceDevice.GPU0, new UniversalEnumConverter()); 26 | 27 | partial void OnGpuOptionChanged(InferenceDevice value) => HandleStringPropertyChanged(ConfigurationKeys.GPUOption, value, v => 28 | { 29 | MaaProcessor.Instance.MaaTasker?.Resource?.SetOption_InferenceDevice(v); 30 | }); 31 | } 32 | -------------------------------------------------------------------------------- /MFAAvalonia/ViewModels/ViewModelBase.cs: -------------------------------------------------------------------------------- 1 | using CommunityToolkit.Mvvm.ComponentModel; 2 | using MFAAvalonia.Configuration; 3 | using System; 4 | using System.Diagnostics.CodeAnalysis; 5 | using System.Runtime.CompilerServices; 6 | 7 | namespace MFAAvalonia.ViewModels; 8 | 9 | public class ViewModelBase : ObservableObject 10 | { 11 | protected ViewModelBase() 12 | { 13 | Initialize(); 14 | } 15 | 16 | protected virtual void Initialize() { } 17 | 18 | protected void HandlePropertyChanged(string configKey, T newValue, Action? action = null) 19 | { 20 | ConfigurationManager.Current.SetValue(configKey, newValue); 21 | action?.Invoke(newValue); 22 | } 23 | 24 | protected void HandleStringPropertyChanged(string configKey, T newValue, Action? action = null) 25 | { 26 | ConfigurationManager.Current.SetValue(configKey, newValue.ToString()); 27 | action?.Invoke(newValue); 28 | } 29 | 30 | protected void HandlePropertyChanged(string configKey, T newValue, Action? action) 31 | { 32 | ConfigurationManager.Current.SetValue(configKey, newValue); 33 | action?.Invoke(); 34 | } 35 | 36 | protected bool? SetNewProperty([NotNullIfNotNull(nameof(newValue))] ref T field, 37 | T newValue, 38 | [CallerMemberName] string? propertyName = null) 39 | { 40 | OnPropertyChanging(propertyName); 41 | 42 | field = newValue; 43 | 44 | OnPropertyChanged(propertyName); 45 | 46 | return true; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /MFAAvalonia/Views/Pages/ResourcesView.axaml: -------------------------------------------------------------------------------- 1 |  12 | 13 | 14 | 15 | 19 | -------------------------------------------------------------------------------- /MFAAvalonia/Views/Pages/ResourcesView.axaml.cs: -------------------------------------------------------------------------------- 1 | using Avalonia; 2 | using Avalonia.Controls; 3 | using Avalonia.Markup.Xaml; 4 | using MFAAvalonia.Helper; 5 | 6 | namespace MFAAvalonia.Views.Pages; 7 | 8 | public partial class ResourcesView : UserControl 9 | { 10 | public ResourcesView() 11 | { 12 | DataContext = Instances.ResourcesViewModel; 13 | InitializeComponent(); 14 | } 15 | } 16 | 17 | -------------------------------------------------------------------------------- /MFAAvalonia/Views/Pages/SettingsView.axaml.cs: -------------------------------------------------------------------------------- 1 | using Avalonia; 2 | using Avalonia.Controls; 3 | using Avalonia.Markup.Xaml; 4 | using MFAAvalonia.Helper; 5 | 6 | namespace MFAAvalonia.Views.Pages; 7 | 8 | public partial class SettingsView : UserControl 9 | { 10 | public SettingsView() 11 | { 12 | DataContext = Instances.SettingsViewModel; 13 | InitializeComponent(); 14 | } 15 | } 16 | 17 | -------------------------------------------------------------------------------- /MFAAvalonia/Views/UserControls/AdbEditorDialogView.axaml.cs: -------------------------------------------------------------------------------- 1 | using Avalonia; 2 | using Avalonia.Controls; 3 | using Avalonia.Data; 4 | using Avalonia.Input; 5 | using Avalonia.Interactivity; 6 | using Avalonia.Markup.Xaml; 7 | using Avalonia.Platform.Storage; 8 | using MaaFramework.Binding; 9 | using MFAAvalonia.Extensions; 10 | using MFAAvalonia.Helper; 11 | using System.Collections.Generic; 12 | using System.Linq; 13 | using System.Threading.Tasks; 14 | 15 | namespace MFAAvalonia.Views.UserControls; 16 | 17 | public partial class AdbEditorDialogView : UserControl 18 | { 19 | public AdbEditorDialogView() 20 | { 21 | InitializeComponent(); 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /MFAAvalonia/Views/UserControls/AddTaskDialogView.axaml.cs: -------------------------------------------------------------------------------- 1 | using Avalonia; 2 | using Avalonia.Controls; 3 | using Avalonia.Interactivity; 4 | using Avalonia.Markup.Xaml; 5 | using MFAAvalonia.Extensions; 6 | using MFAAvalonia.Helper; 7 | using MFAAvalonia.Helper.ValueType; 8 | using MFAAvalonia.ViewModels.UsersControls.Settings; 9 | using System; 10 | 11 | namespace MFAAvalonia.Views.UserControls; 12 | 13 | public partial class AddTaskDialogView : UserControl 14 | { 15 | public AddTaskDialogView() 16 | { 17 | InitializeComponent(); 18 | } 19 | 20 | private void SearchBar_OnSearchStarted(object sender, RoutedEventArgs e) 21 | { 22 | string key = SearchBar.Text; 23 | 24 | if (DataContext is AddTaskDialogViewModel vm) 25 | { 26 | if (string.IsNullOrEmpty(key)) 27 | { 28 | 29 | vm.Items.Clear(); 30 | vm.Items.AddRange(vm.Sources); 31 | } 32 | else 33 | { 34 | key = key.ToLower(); 35 | vm.Items.Clear(); 36 | foreach (DragItemViewModel item in vm.Sources) 37 | { 38 | string name = item.Name.ToLower(); 39 | if (name.Contains(key)) 40 | vm.Items.Add(item); 41 | } 42 | } 43 | } 44 | 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /MFAAvalonia/Views/UserControls/CustomThemeDialogView.axaml: -------------------------------------------------------------------------------- 1 |  15 | 16 | 17 | 18 | 19 | 20 |