From 278f9b308dab647b538a35eb6f9cb64bc0cb6d54 Mon Sep 17 00:00:00 2001 From: Kenneth Date: Wed, 9 Feb 2022 10:41:39 +0000 Subject: [PATCH 01/18] Start impl of Windows notifications --- .../example/lib/main.dart | 2 +- .../example/pubspec.yaml | 6 + .../example/windows/.gitignore | 21 ++ .../example/windows/CMakeLists.txt | 95 +++++++ .../example/windows/flutter/CMakeLists.txt | 103 ++++++++ .../flutter/generated_plugin_registrant.cc | 17 ++ .../flutter/generated_plugin_registrant.h | 15 ++ .../windows/flutter/generated_plugins.cmake | 17 ++ .../example/windows/runner/CMakeLists.txt | 17 ++ .../example/windows/runner/Runner.rc | 121 +++++++++ .../example/windows/runner/flutter_window.cpp | 61 +++++ .../example/windows/runner/flutter_window.h | 33 +++ .../example/windows/runner/main.cpp | 43 +++ .../example/windows/runner/resource.h | 16 ++ .../windows/runner/resources/app_icon.ico | Bin 0 -> 33772 bytes .../windows/runner/runner.exe.manifest | 20 ++ .../example/windows/runner/utils.cpp | 64 +++++ .../example/windows/runner/utils.h | 19 ++ .../example/windows/runner/win32_window.cpp | 245 ++++++++++++++++++ .../example/windows/runner/win32_window.h | 98 +++++++ .../flutter_local_notifications_plugin.dart | 12 + .../platform_flutter_local_notifications.dart | 13 + flutter_local_notifications/pubspec.yaml | 6 + .../windows/.gitignore | 17 ++ .../windows/CMakeLists.txt | 65 +++++ .../flutter_local_notifications_plugin.cpp | 141 ++++++++++ .../flutter_local_notifications_plugin.h | 23 ++ .../flutter_local_notifications/methods.h | 7 + .../windows/methods.cpp | 6 + .../windows/utils/utils.cpp | 12 + .../windows/utils/utils.h | 11 + 31 files changed, 1325 insertions(+), 1 deletion(-) create mode 100644 flutter_local_notifications/example/windows/.gitignore create mode 100644 flutter_local_notifications/example/windows/CMakeLists.txt create mode 100644 flutter_local_notifications/example/windows/flutter/CMakeLists.txt create mode 100644 flutter_local_notifications/example/windows/flutter/generated_plugin_registrant.cc create mode 100644 flutter_local_notifications/example/windows/flutter/generated_plugin_registrant.h create mode 100644 flutter_local_notifications/example/windows/flutter/generated_plugins.cmake create mode 100644 flutter_local_notifications/example/windows/runner/CMakeLists.txt create mode 100644 flutter_local_notifications/example/windows/runner/Runner.rc create mode 100644 flutter_local_notifications/example/windows/runner/flutter_window.cpp create mode 100644 flutter_local_notifications/example/windows/runner/flutter_window.h create mode 100644 flutter_local_notifications/example/windows/runner/main.cpp create mode 100644 flutter_local_notifications/example/windows/runner/resource.h create mode 100644 flutter_local_notifications/example/windows/runner/resources/app_icon.ico create mode 100644 flutter_local_notifications/example/windows/runner/runner.exe.manifest create mode 100644 flutter_local_notifications/example/windows/runner/utils.cpp create mode 100644 flutter_local_notifications/example/windows/runner/utils.h create mode 100644 flutter_local_notifications/example/windows/runner/win32_window.cpp create mode 100644 flutter_local_notifications/example/windows/runner/win32_window.h create mode 100644 flutter_local_notifications/windows/.gitignore create mode 100644 flutter_local_notifications/windows/CMakeLists.txt create mode 100644 flutter_local_notifications/windows/flutter_local_notifications_plugin.cpp create mode 100644 flutter_local_notifications/windows/include/flutter_local_notifications/flutter_local_notifications_plugin.h create mode 100644 flutter_local_notifications/windows/include/flutter_local_notifications/methods.h create mode 100644 flutter_local_notifications/windows/methods.cpp create mode 100644 flutter_local_notifications/windows/utils/utils.cpp create mode 100644 flutter_local_notifications/windows/utils/utils.h diff --git a/flutter_local_notifications/example/lib/main.dart b/flutter_local_notifications/example/lib/main.dart index ed4c693d1..eec636a47 100644 --- a/flutter_local_notifications/example/lib/main.dart +++ b/flutter_local_notifications/example/lib/main.dart @@ -210,7 +210,7 @@ Future main() async { } Future _configureLocalTimeZone() async { - if (kIsWeb || Platform.isLinux) { + if (kIsWeb || Platform.isLinux || Platform.isWindows) { return; } tz.initializeTimeZones(); diff --git a/flutter_local_notifications/example/pubspec.yaml b/flutter_local_notifications/example/pubspec.yaml index bb5792d96..07f4f5ad9 100644 --- a/flutter_local_notifications/example/pubspec.yaml +++ b/flutter_local_notifications/example/pubspec.yaml @@ -24,6 +24,7 @@ dev_dependencies: sdk: flutter integration_test: sdk: flutter + msix: ^2.8.17 dependency_overrides: flutter_local_notifications_platform_interface: @@ -40,3 +41,8 @@ flutter: environment: sdk: '>=2.12.0-0 <3.0.0' flutter: '>=1.26.0-0' # allows for using integration_test from SDK + +msix_config: + display_name: Flutter Local Notifications Example + identity_name: Com.Example.FlutterLocalNotificationsExample + debug: true diff --git a/flutter_local_notifications/example/windows/.gitignore b/flutter_local_notifications/example/windows/.gitignore new file mode 100644 index 000000000..571c3131e --- /dev/null +++ b/flutter_local_notifications/example/windows/.gitignore @@ -0,0 +1,21 @@ +flutter/ephemeral/ + +# Visual Studio user-specific files. +*.suo +*.user +*.userosscache +*.sln.docstates + +# Visual Studio build-related files. +x64/ +x86/ + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ + +.vs +out +flutter/ephemeral diff --git a/flutter_local_notifications/example/windows/CMakeLists.txt b/flutter_local_notifications/example/windows/CMakeLists.txt new file mode 100644 index 000000000..1633297a0 --- /dev/null +++ b/flutter_local_notifications/example/windows/CMakeLists.txt @@ -0,0 +1,95 @@ +cmake_minimum_required(VERSION 3.14) +project(example LANGUAGES CXX) + +set(BINARY_NAME "example") + +cmake_policy(SET CMP0063 NEW) + +set(CMAKE_INSTALL_RPATH "$ORIGIN/lib") + +# Configure build options. +get_property(IS_MULTICONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) +if(IS_MULTICONFIG) + set(CMAKE_CONFIGURATION_TYPES "Debug;Profile;Release" + CACHE STRING "" FORCE) +else() + if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + set(CMAKE_BUILD_TYPE "Debug" CACHE + STRING "Flutter build mode" FORCE) + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS + "Debug" "Profile" "Release") + endif() +endif() + +set(CMAKE_EXE_LINKER_FLAGS_PROFILE "${CMAKE_EXE_LINKER_FLAGS_RELEASE}") +set(CMAKE_SHARED_LINKER_FLAGS_PROFILE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE}") +set(CMAKE_C_FLAGS_PROFILE "${CMAKE_C_FLAGS_RELEASE}") +set(CMAKE_CXX_FLAGS_PROFILE "${CMAKE_CXX_FLAGS_RELEASE}") + +# Use Unicode for all projects. +add_definitions(-DUNICODE -D_UNICODE) + +# Compilation settings that should be applied to most targets. +function(APPLY_STANDARD_SETTINGS TARGET) + target_compile_features(${TARGET} PUBLIC cxx_std_17) + target_compile_options(${TARGET} PRIVATE /W4 /WX /wd"4100") + target_compile_options(${TARGET} PRIVATE /EHsc) + target_compile_definitions(${TARGET} PRIVATE "_HAS_EXCEPTIONS=0") + target_compile_definitions(${TARGET} PRIVATE "$<$:_DEBUG>") +endfunction() + +set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") + +# Flutter library and tool build rules. +add_subdirectory(${FLUTTER_MANAGED_DIR}) + +# Application build +add_subdirectory("runner") + +# Generated plugin build rules, which manage building the plugins and adding +# them to the application. +include(flutter/generated_plugins.cmake) + + +# === Installation === +# Support files are copied into place next to the executable, so that it can +# run in place. This is done instead of making a separate bundle (as on Linux) +# so that building and running from within Visual Studio will work. +set(BUILD_BUNDLE_DIR "$") +# Make the "install" step default, as it's required to run. +set(CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD 1) +if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) + set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) +endif() + +set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") +set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}") + +install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) + +if(PLUGIN_BUNDLED_LIBRARIES) + install(FILES "${PLUGIN_BUNDLED_LIBRARIES}" + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) +endif() + +# Fully re-copy the assets directory on each build to avoid having stale files +# from a previous install. +set(FLUTTER_ASSET_DIR_NAME "flutter_assets") +install(CODE " + file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") + " COMPONENT Runtime) +install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" + DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) + +# Install the AOT library on non-Debug builds only. +install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" + CONFIGURATIONS Profile;Release + COMPONENT Runtime) diff --git a/flutter_local_notifications/example/windows/flutter/CMakeLists.txt b/flutter_local_notifications/example/windows/flutter/CMakeLists.txt new file mode 100644 index 000000000..b2e4bd8d6 --- /dev/null +++ b/flutter_local_notifications/example/windows/flutter/CMakeLists.txt @@ -0,0 +1,103 @@ +cmake_minimum_required(VERSION 3.14) + +set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") + +# Configuration provided via flutter tool. +include(${EPHEMERAL_DIR}/generated_config.cmake) + +# TODO: Move the rest of this into files in ephemeral. See +# https://github.com/flutter/flutter/issues/57146. +set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper") + +# === Flutter Library === +set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll") + +# Published to parent scope for install step. +set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) +set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) +set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) +set(AOT_LIBRARY "${PROJECT_DIR}/build/windows/app.so" PARENT_SCOPE) + +list(APPEND FLUTTER_LIBRARY_HEADERS + "flutter_export.h" + "flutter_windows.h" + "flutter_messenger.h" + "flutter_plugin_registrar.h" + "flutter_texture_registrar.h" +) +list(TRANSFORM FLUTTER_LIBRARY_HEADERS PREPEND "${EPHEMERAL_DIR}/") +add_library(flutter INTERFACE) +target_include_directories(flutter INTERFACE + "${EPHEMERAL_DIR}" +) +target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}.lib") +add_dependencies(flutter flutter_assemble) + +# === Wrapper === +list(APPEND CPP_WRAPPER_SOURCES_CORE + "core_implementations.cc" + "standard_codec.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_CORE PREPEND "${WRAPPER_ROOT}/") +list(APPEND CPP_WRAPPER_SOURCES_PLUGIN + "plugin_registrar.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_PLUGIN PREPEND "${WRAPPER_ROOT}/") +list(APPEND CPP_WRAPPER_SOURCES_APP + "flutter_engine.cc" + "flutter_view_controller.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_APP PREPEND "${WRAPPER_ROOT}/") + +# Wrapper sources needed for a plugin. +add_library(flutter_wrapper_plugin STATIC + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_PLUGIN} +) +apply_standard_settings(flutter_wrapper_plugin) +set_target_properties(flutter_wrapper_plugin PROPERTIES + POSITION_INDEPENDENT_CODE ON) +set_target_properties(flutter_wrapper_plugin PROPERTIES + CXX_VISIBILITY_PRESET hidden) +target_link_libraries(flutter_wrapper_plugin PUBLIC flutter) +target_include_directories(flutter_wrapper_plugin PUBLIC + "${WRAPPER_ROOT}/include" +) +add_dependencies(flutter_wrapper_plugin flutter_assemble) + +# Wrapper sources needed for the runner. +add_library(flutter_wrapper_app STATIC + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_APP} +) +apply_standard_settings(flutter_wrapper_app) +target_link_libraries(flutter_wrapper_app PUBLIC flutter) +target_include_directories(flutter_wrapper_app PUBLIC + "${WRAPPER_ROOT}/include" +) +add_dependencies(flutter_wrapper_app flutter_assemble) + +# === Flutter tool backend === +# _phony_ is a non-existent file to force this command to run every time, +# since currently there's no way to get a full input/output list from the +# flutter tool. +set(PHONY_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/_phony_") +set_source_files_properties("${PHONY_OUTPUT}" PROPERTIES SYMBOLIC TRUE) +add_custom_command( + OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} + ${CPP_WRAPPER_SOURCES_CORE} ${CPP_WRAPPER_SOURCES_PLUGIN} + ${CPP_WRAPPER_SOURCES_APP} + ${PHONY_OUTPUT} + COMMAND ${CMAKE_COMMAND} -E env + ${FLUTTER_TOOL_ENVIRONMENT} + "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat" + windows-x64 $ + VERBATIM +) +add_custom_target(flutter_assemble DEPENDS + "${FLUTTER_LIBRARY}" + ${FLUTTER_LIBRARY_HEADERS} + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_PLUGIN} + ${CPP_WRAPPER_SOURCES_APP} +) diff --git a/flutter_local_notifications/example/windows/flutter/generated_plugin_registrant.cc b/flutter_local_notifications/example/windows/flutter/generated_plugin_registrant.cc new file mode 100644 index 000000000..d5a662fbd --- /dev/null +++ b/flutter_local_notifications/example/windows/flutter/generated_plugin_registrant.cc @@ -0,0 +1,17 @@ +// +// Generated file. Do not edit. +// + +// clang-format off + +#include "generated_plugin_registrant.h" + +#include +#include + +void RegisterPlugins(flutter::PluginRegistry* registry) { + FlutterLocalNotificationsPluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("FlutterLocalNotificationsPlugin")); + UrlLauncherWindowsRegisterWithRegistrar( + registry->GetRegistrarForPlugin("UrlLauncherWindows")); +} diff --git a/flutter_local_notifications/example/windows/flutter/generated_plugin_registrant.h b/flutter_local_notifications/example/windows/flutter/generated_plugin_registrant.h new file mode 100644 index 000000000..dc139d85a --- /dev/null +++ b/flutter_local_notifications/example/windows/flutter/generated_plugin_registrant.h @@ -0,0 +1,15 @@ +// +// Generated file. Do not edit. +// + +// clang-format off + +#ifndef GENERATED_PLUGIN_REGISTRANT_ +#define GENERATED_PLUGIN_REGISTRANT_ + +#include + +// Registers Flutter plugins. +void RegisterPlugins(flutter::PluginRegistry* registry); + +#endif // GENERATED_PLUGIN_REGISTRANT_ diff --git a/flutter_local_notifications/example/windows/flutter/generated_plugins.cmake b/flutter_local_notifications/example/windows/flutter/generated_plugins.cmake new file mode 100644 index 000000000..41753c0a3 --- /dev/null +++ b/flutter_local_notifications/example/windows/flutter/generated_plugins.cmake @@ -0,0 +1,17 @@ +# +# Generated file, do not edit. +# + +list(APPEND FLUTTER_PLUGIN_LIST + flutter_local_notifications + url_launcher_windows +) + +set(PLUGIN_BUNDLED_LIBRARIES) + +foreach(plugin ${FLUTTER_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/windows plugins/${plugin}) + target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) + list(APPEND PLUGIN_BUNDLED_LIBRARIES $) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) +endforeach(plugin) diff --git a/flutter_local_notifications/example/windows/runner/CMakeLists.txt b/flutter_local_notifications/example/windows/runner/CMakeLists.txt new file mode 100644 index 000000000..de2d8916b --- /dev/null +++ b/flutter_local_notifications/example/windows/runner/CMakeLists.txt @@ -0,0 +1,17 @@ +cmake_minimum_required(VERSION 3.14) +project(runner LANGUAGES CXX) + +add_executable(${BINARY_NAME} WIN32 + "flutter_window.cpp" + "main.cpp" + "utils.cpp" + "win32_window.cpp" + "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" + "Runner.rc" + "runner.exe.manifest" +) +apply_standard_settings(${BINARY_NAME}) +target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX") +target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app) +target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}") +add_dependencies(${BINARY_NAME} flutter_assemble) diff --git a/flutter_local_notifications/example/windows/runner/Runner.rc b/flutter_local_notifications/example/windows/runner/Runner.rc new file mode 100644 index 000000000..81d27749b --- /dev/null +++ b/flutter_local_notifications/example/windows/runner/Runner.rc @@ -0,0 +1,121 @@ +// Microsoft Visual C++ generated resource script. +// +#pragma code_page(65001) +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "winres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (United States) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""winres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_APP_ICON ICON "resources\\app_icon.ico" + + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +#ifdef FLUTTER_BUILD_NUMBER +#define VERSION_AS_NUMBER FLUTTER_BUILD_NUMBER +#else +#define VERSION_AS_NUMBER 1,0,0 +#endif + +#ifdef FLUTTER_BUILD_NAME +#define VERSION_AS_STRING #FLUTTER_BUILD_NAME +#else +#define VERSION_AS_STRING "1.0.0" +#endif + +VS_VERSION_INFO VERSIONINFO + FILEVERSION VERSION_AS_NUMBER + PRODUCTVERSION VERSION_AS_NUMBER + FILEFLAGSMASK VS_FFI_FILEFLAGSMASK +#ifdef _DEBUG + FILEFLAGS VS_FF_DEBUG +#else + FILEFLAGS 0x0L +#endif + FILEOS VOS__WINDOWS32 + FILETYPE VFT_APP + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904e4" + BEGIN + VALUE "CompanyName", "com.dexterous" "\0" + VALUE "FileDescription", "example" "\0" + VALUE "FileVersion", VERSION_AS_STRING "\0" + VALUE "InternalName", "example" "\0" + VALUE "LegalCopyright", "Copyright (C) 2022 com.dexterous. All rights reserved." "\0" + VALUE "OriginalFilename", "example.exe" "\0" + VALUE "ProductName", "example" "\0" + VALUE "ProductVersion", VERSION_AS_STRING "\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1252 + END +END + +#endif // English (United States) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED diff --git a/flutter_local_notifications/example/windows/runner/flutter_window.cpp b/flutter_local_notifications/example/windows/runner/flutter_window.cpp new file mode 100644 index 000000000..b43b9095e --- /dev/null +++ b/flutter_local_notifications/example/windows/runner/flutter_window.cpp @@ -0,0 +1,61 @@ +#include "flutter_window.h" + +#include + +#include "flutter/generated_plugin_registrant.h" + +FlutterWindow::FlutterWindow(const flutter::DartProject& project) + : project_(project) {} + +FlutterWindow::~FlutterWindow() {} + +bool FlutterWindow::OnCreate() { + if (!Win32Window::OnCreate()) { + return false; + } + + RECT frame = GetClientArea(); + + // The size here must match the window dimensions to avoid unnecessary surface + // creation / destruction in the startup path. + flutter_controller_ = std::make_unique( + frame.right - frame.left, frame.bottom - frame.top, project_); + // Ensure that basic setup of the controller was successful. + if (!flutter_controller_->engine() || !flutter_controller_->view()) { + return false; + } + RegisterPlugins(flutter_controller_->engine()); + SetChildContent(flutter_controller_->view()->GetNativeWindow()); + return true; +} + +void FlutterWindow::OnDestroy() { + if (flutter_controller_) { + flutter_controller_ = nullptr; + } + + Win32Window::OnDestroy(); +} + +LRESULT +FlutterWindow::MessageHandler(HWND hwnd, UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept { + // Give Flutter, including plugins, an opportunity to handle window messages. + if (flutter_controller_) { + std::optional result = + flutter_controller_->HandleTopLevelWindowProc(hwnd, message, wparam, + lparam); + if (result) { + return *result; + } + } + + switch (message) { + case WM_FONTCHANGE: + flutter_controller_->engine()->ReloadSystemFonts(); + break; + } + + return Win32Window::MessageHandler(hwnd, message, wparam, lparam); +} diff --git a/flutter_local_notifications/example/windows/runner/flutter_window.h b/flutter_local_notifications/example/windows/runner/flutter_window.h new file mode 100644 index 000000000..6da0652f0 --- /dev/null +++ b/flutter_local_notifications/example/windows/runner/flutter_window.h @@ -0,0 +1,33 @@ +#ifndef RUNNER_FLUTTER_WINDOW_H_ +#define RUNNER_FLUTTER_WINDOW_H_ + +#include +#include + +#include + +#include "win32_window.h" + +// A window that does nothing but host a Flutter view. +class FlutterWindow : public Win32Window { + public: + // Creates a new FlutterWindow hosting a Flutter view running |project|. + explicit FlutterWindow(const flutter::DartProject& project); + virtual ~FlutterWindow(); + + protected: + // Win32Window: + bool OnCreate() override; + void OnDestroy() override; + LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam, + LPARAM const lparam) noexcept override; + + private: + // The project to run. + flutter::DartProject project_; + + // The Flutter instance hosted by this window. + std::unique_ptr flutter_controller_; +}; + +#endif // RUNNER_FLUTTER_WINDOW_H_ diff --git a/flutter_local_notifications/example/windows/runner/main.cpp b/flutter_local_notifications/example/windows/runner/main.cpp new file mode 100644 index 000000000..bcb57b0e2 --- /dev/null +++ b/flutter_local_notifications/example/windows/runner/main.cpp @@ -0,0 +1,43 @@ +#include +#include +#include + +#include "flutter_window.h" +#include "utils.h" + +int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev, + _In_ wchar_t *command_line, _In_ int show_command) { + // Attach to console when present (e.g., 'flutter run') or create a + // new console when running with a debugger. + if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) { + CreateAndAttachConsole(); + } + + // Initialize COM, so that it is available for use in the library and/or + // plugins. + ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); + + flutter::DartProject project(L"data"); + + std::vector command_line_arguments = + GetCommandLineArguments(); + + project.set_dart_entrypoint_arguments(std::move(command_line_arguments)); + + FlutterWindow window(project); + Win32Window::Point origin(10, 10); + Win32Window::Size size(1280, 720); + if (!window.CreateAndShow(L"example", origin, size)) { + return EXIT_FAILURE; + } + window.SetQuitOnClose(true); + + ::MSG msg; + while (::GetMessage(&msg, nullptr, 0, 0)) { + ::TranslateMessage(&msg); + ::DispatchMessage(&msg); + } + + ::CoUninitialize(); + return EXIT_SUCCESS; +} diff --git a/flutter_local_notifications/example/windows/runner/resource.h b/flutter_local_notifications/example/windows/runner/resource.h new file mode 100644 index 000000000..66a65d1e4 --- /dev/null +++ b/flutter_local_notifications/example/windows/runner/resource.h @@ -0,0 +1,16 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by Runner.rc +// +#define IDI_APP_ICON 101 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 102 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/flutter_local_notifications/example/windows/runner/resources/app_icon.ico b/flutter_local_notifications/example/windows/runner/resources/app_icon.ico new file mode 100644 index 0000000000000000000000000000000000000000..c04e20caf6370ebb9253ad831cc31de4a9c965f6 GIT binary patch literal 33772 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 literal 0 HcmV?d00001 diff --git a/flutter_local_notifications/example/windows/runner/runner.exe.manifest b/flutter_local_notifications/example/windows/runner/runner.exe.manifest new file mode 100644 index 000000000..c977c4a42 --- /dev/null +++ b/flutter_local_notifications/example/windows/runner/runner.exe.manifest @@ -0,0 +1,20 @@ + + + + + PerMonitorV2 + + + + + + + + + + + + + + + diff --git a/flutter_local_notifications/example/windows/runner/utils.cpp b/flutter_local_notifications/example/windows/runner/utils.cpp new file mode 100644 index 000000000..d19bdbbcc --- /dev/null +++ b/flutter_local_notifications/example/windows/runner/utils.cpp @@ -0,0 +1,64 @@ +#include "utils.h" + +#include +#include +#include +#include + +#include + +void CreateAndAttachConsole() { + if (::AllocConsole()) { + FILE *unused; + if (freopen_s(&unused, "CONOUT$", "w", stdout)) { + _dup2(_fileno(stdout), 1); + } + if (freopen_s(&unused, "CONOUT$", "w", stderr)) { + _dup2(_fileno(stdout), 2); + } + std::ios::sync_with_stdio(); + FlutterDesktopResyncOutputStreams(); + } +} + +std::vector GetCommandLineArguments() { + // Convert the UTF-16 command line arguments to UTF-8 for the Engine to use. + int argc; + wchar_t** argv = ::CommandLineToArgvW(::GetCommandLineW(), &argc); + if (argv == nullptr) { + return std::vector(); + } + + std::vector command_line_arguments; + + // Skip the first argument as it's the binary name. + for (int i = 1; i < argc; i++) { + command_line_arguments.push_back(Utf8FromUtf16(argv[i])); + } + + ::LocalFree(argv); + + return command_line_arguments; +} + +std::string Utf8FromUtf16(const wchar_t* utf16_string) { + if (utf16_string == nullptr) { + return std::string(); + } + int target_length = ::WideCharToMultiByte( + CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, + -1, nullptr, 0, nullptr, nullptr); + if (target_length == 0) { + return std::string(); + } + std::string utf8_string; + utf8_string.resize(target_length); + int converted_length = ::WideCharToMultiByte( + CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, + -1, utf8_string.data(), + target_length, nullptr, nullptr); + if (converted_length == 0) { + return std::string(); + } + return utf8_string; +} diff --git a/flutter_local_notifications/example/windows/runner/utils.h b/flutter_local_notifications/example/windows/runner/utils.h new file mode 100644 index 000000000..3879d5475 --- /dev/null +++ b/flutter_local_notifications/example/windows/runner/utils.h @@ -0,0 +1,19 @@ +#ifndef RUNNER_UTILS_H_ +#define RUNNER_UTILS_H_ + +#include +#include + +// Creates a console for the process, and redirects stdout and stderr to +// it for both the runner and the Flutter library. +void CreateAndAttachConsole(); + +// Takes a null-terminated wchar_t* encoded in UTF-16 and returns a std::string +// encoded in UTF-8. Returns an empty std::string on failure. +std::string Utf8FromUtf16(const wchar_t* utf16_string); + +// Gets the command line arguments passed in as a std::vector, +// encoded in UTF-8. Returns an empty std::vector on failure. +std::vector GetCommandLineArguments(); + +#endif // RUNNER_UTILS_H_ diff --git a/flutter_local_notifications/example/windows/runner/win32_window.cpp b/flutter_local_notifications/example/windows/runner/win32_window.cpp new file mode 100644 index 000000000..c10f08dc7 --- /dev/null +++ b/flutter_local_notifications/example/windows/runner/win32_window.cpp @@ -0,0 +1,245 @@ +#include "win32_window.h" + +#include + +#include "resource.h" + +namespace { + +constexpr const wchar_t kWindowClassName[] = L"FLUTTER_RUNNER_WIN32_WINDOW"; + +// The number of Win32Window objects that currently exist. +static int g_active_window_count = 0; + +using EnableNonClientDpiScaling = BOOL __stdcall(HWND hwnd); + +// Scale helper to convert logical scaler values to physical using passed in +// scale factor +int Scale(int source, double scale_factor) { + return static_cast(source * scale_factor); +} + +// Dynamically loads the |EnableNonClientDpiScaling| from the User32 module. +// This API is only needed for PerMonitor V1 awareness mode. +void EnableFullDpiSupportIfAvailable(HWND hwnd) { + HMODULE user32_module = LoadLibraryA("User32.dll"); + if (!user32_module) { + return; + } + auto enable_non_client_dpi_scaling = + reinterpret_cast( + GetProcAddress(user32_module, "EnableNonClientDpiScaling")); + if (enable_non_client_dpi_scaling != nullptr) { + enable_non_client_dpi_scaling(hwnd); + FreeLibrary(user32_module); + } +} + +} // namespace + +// Manages the Win32Window's window class registration. +class WindowClassRegistrar { + public: + ~WindowClassRegistrar() = default; + + // Returns the singleton registar instance. + static WindowClassRegistrar* GetInstance() { + if (!instance_) { + instance_ = new WindowClassRegistrar(); + } + return instance_; + } + + // Returns the name of the window class, registering the class if it hasn't + // previously been registered. + const wchar_t* GetWindowClass(); + + // Unregisters the window class. Should only be called if there are no + // instances of the window. + void UnregisterWindowClass(); + + private: + WindowClassRegistrar() = default; + + static WindowClassRegistrar* instance_; + + bool class_registered_ = false; +}; + +WindowClassRegistrar* WindowClassRegistrar::instance_ = nullptr; + +const wchar_t* WindowClassRegistrar::GetWindowClass() { + if (!class_registered_) { + WNDCLASS window_class{}; + window_class.hCursor = LoadCursor(nullptr, IDC_ARROW); + window_class.lpszClassName = kWindowClassName; + window_class.style = CS_HREDRAW | CS_VREDRAW; + window_class.cbClsExtra = 0; + window_class.cbWndExtra = 0; + window_class.hInstance = GetModuleHandle(nullptr); + window_class.hIcon = + LoadIcon(window_class.hInstance, MAKEINTRESOURCE(IDI_APP_ICON)); + window_class.hbrBackground = 0; + window_class.lpszMenuName = nullptr; + window_class.lpfnWndProc = Win32Window::WndProc; + RegisterClass(&window_class); + class_registered_ = true; + } + return kWindowClassName; +} + +void WindowClassRegistrar::UnregisterWindowClass() { + UnregisterClass(kWindowClassName, nullptr); + class_registered_ = false; +} + +Win32Window::Win32Window() { + ++g_active_window_count; +} + +Win32Window::~Win32Window() { + --g_active_window_count; + Destroy(); +} + +bool Win32Window::CreateAndShow(const std::wstring& title, + const Point& origin, + const Size& size) { + Destroy(); + + const wchar_t* window_class = + WindowClassRegistrar::GetInstance()->GetWindowClass(); + + const POINT target_point = {static_cast(origin.x), + static_cast(origin.y)}; + HMONITOR monitor = MonitorFromPoint(target_point, MONITOR_DEFAULTTONEAREST); + UINT dpi = FlutterDesktopGetDpiForMonitor(monitor); + double scale_factor = dpi / 96.0; + + HWND window = CreateWindow( + window_class, title.c_str(), WS_OVERLAPPEDWINDOW | WS_VISIBLE, + Scale(origin.x, scale_factor), Scale(origin.y, scale_factor), + Scale(size.width, scale_factor), Scale(size.height, scale_factor), + nullptr, nullptr, GetModuleHandle(nullptr), this); + + if (!window) { + return false; + } + + return OnCreate(); +} + +// static +LRESULT CALLBACK Win32Window::WndProc(HWND const window, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept { + if (message == WM_NCCREATE) { + auto window_struct = reinterpret_cast(lparam); + SetWindowLongPtr(window, GWLP_USERDATA, + reinterpret_cast(window_struct->lpCreateParams)); + + auto that = static_cast(window_struct->lpCreateParams); + EnableFullDpiSupportIfAvailable(window); + that->window_handle_ = window; + } else if (Win32Window* that = GetThisFromHandle(window)) { + return that->MessageHandler(window, message, wparam, lparam); + } + + return DefWindowProc(window, message, wparam, lparam); +} + +LRESULT +Win32Window::MessageHandler(HWND hwnd, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept { + switch (message) { + case WM_DESTROY: + window_handle_ = nullptr; + Destroy(); + if (quit_on_close_) { + PostQuitMessage(0); + } + return 0; + + case WM_DPICHANGED: { + auto newRectSize = reinterpret_cast(lparam); + LONG newWidth = newRectSize->right - newRectSize->left; + LONG newHeight = newRectSize->bottom - newRectSize->top; + + SetWindowPos(hwnd, nullptr, newRectSize->left, newRectSize->top, newWidth, + newHeight, SWP_NOZORDER | SWP_NOACTIVATE); + + return 0; + } + case WM_SIZE: { + RECT rect = GetClientArea(); + if (child_content_ != nullptr) { + // Size and position the child window. + MoveWindow(child_content_, rect.left, rect.top, rect.right - rect.left, + rect.bottom - rect.top, TRUE); + } + return 0; + } + + case WM_ACTIVATE: + if (child_content_ != nullptr) { + SetFocus(child_content_); + } + return 0; + } + + return DefWindowProc(window_handle_, message, wparam, lparam); +} + +void Win32Window::Destroy() { + OnDestroy(); + + if (window_handle_) { + DestroyWindow(window_handle_); + window_handle_ = nullptr; + } + if (g_active_window_count == 0) { + WindowClassRegistrar::GetInstance()->UnregisterWindowClass(); + } +} + +Win32Window* Win32Window::GetThisFromHandle(HWND const window) noexcept { + return reinterpret_cast( + GetWindowLongPtr(window, GWLP_USERDATA)); +} + +void Win32Window::SetChildContent(HWND content) { + child_content_ = content; + SetParent(content, window_handle_); + RECT frame = GetClientArea(); + + MoveWindow(content, frame.left, frame.top, frame.right - frame.left, + frame.bottom - frame.top, true); + + SetFocus(child_content_); +} + +RECT Win32Window::GetClientArea() { + RECT frame; + GetClientRect(window_handle_, &frame); + return frame; +} + +HWND Win32Window::GetHandle() { + return window_handle_; +} + +void Win32Window::SetQuitOnClose(bool quit_on_close) { + quit_on_close_ = quit_on_close; +} + +bool Win32Window::OnCreate() { + // No-op; provided for subclasses. + return true; +} + +void Win32Window::OnDestroy() { + // No-op; provided for subclasses. +} diff --git a/flutter_local_notifications/example/windows/runner/win32_window.h b/flutter_local_notifications/example/windows/runner/win32_window.h new file mode 100644 index 000000000..17ba43112 --- /dev/null +++ b/flutter_local_notifications/example/windows/runner/win32_window.h @@ -0,0 +1,98 @@ +#ifndef RUNNER_WIN32_WINDOW_H_ +#define RUNNER_WIN32_WINDOW_H_ + +#include + +#include +#include +#include + +// A class abstraction for a high DPI-aware Win32 Window. Intended to be +// inherited from by classes that wish to specialize with custom +// rendering and input handling +class Win32Window { + public: + struct Point { + unsigned int x; + unsigned int y; + Point(unsigned int x, unsigned int y) : x(x), y(y) {} + }; + + struct Size { + unsigned int width; + unsigned int height; + Size(unsigned int width, unsigned int height) + : width(width), height(height) {} + }; + + Win32Window(); + virtual ~Win32Window(); + + // Creates and shows a win32 window with |title| and position and size using + // |origin| and |size|. New windows are created on the default monitor. Window + // sizes are specified to the OS in physical pixels, hence to ensure a + // consistent size to will treat the width height passed in to this function + // as logical pixels and scale to appropriate for the default monitor. Returns + // true if the window was created successfully. + bool CreateAndShow(const std::wstring& title, + const Point& origin, + const Size& size); + + // Release OS resources associated with window. + void Destroy(); + + // Inserts |content| into the window tree. + void SetChildContent(HWND content); + + // Returns the backing Window handle to enable clients to set icon and other + // window properties. Returns nullptr if the window has been destroyed. + HWND GetHandle(); + + // If true, closing this window will quit the application. + void SetQuitOnClose(bool quit_on_close); + + // Return a RECT representing the bounds of the current client area. + RECT GetClientArea(); + + protected: + // Processes and route salient window messages for mouse handling, + // size change and DPI. Delegates handling of these to member overloads that + // inheriting classes can handle. + virtual LRESULT MessageHandler(HWND window, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept; + + // Called when CreateAndShow is called, allowing subclass window-related + // setup. Subclasses should return false if setup fails. + virtual bool OnCreate(); + + // Called when Destroy is called. + virtual void OnDestroy(); + + private: + friend class WindowClassRegistrar; + + // OS callback called by message pump. Handles the WM_NCCREATE message which + // is passed when the non-client area is being created and enables automatic + // non-client DPI scaling so that the non-client area automatically + // responsponds to changes in DPI. All other messages are handled by + // MessageHandler. + static LRESULT CALLBACK WndProc(HWND const window, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept; + + // Retrieves a class instance pointer for |window| + static Win32Window* GetThisFromHandle(HWND const window) noexcept; + + bool quit_on_close_ = false; + + // window handle for top level window. + HWND window_handle_ = nullptr; + + // window handle for hosted content. + HWND child_content_ = nullptr; +}; + +#endif // RUNNER_WIN32_WINDOW_H_ diff --git a/flutter_local_notifications/lib/src/flutter_local_notifications_plugin.dart b/flutter_local_notifications/lib/src/flutter_local_notifications_plugin.dart index c25900f19..793b71709 100644 --- a/flutter_local_notifications/lib/src/flutter_local_notifications_plugin.dart +++ b/flutter_local_notifications/lib/src/flutter_local_notifications_plugin.dart @@ -40,6 +40,9 @@ class FlutterLocalNotificationsPlugin { } else if (defaultTargetPlatform == TargetPlatform.linux) { FlutterLocalNotificationsPlatform.instance = LinuxFlutterLocalNotificationsPlugin(); + } else if (defaultTargetPlatform == TargetPlatform.windows) { + FlutterLocalNotificationsPlatform.instance = + WindowsFlutterLocalNotificationsPlugin(); } } @@ -85,6 +88,11 @@ class FlutterLocalNotificationsPlugin { FlutterLocalNotificationsPlatform.instance is LinuxFlutterLocalNotificationsPlugin) { return FlutterLocalNotificationsPlatform.instance as T?; + } else if (defaultTargetPlatform == TargetPlatform.windows && + T == WindowsFlutterLocalNotificationsPlugin && + FlutterLocalNotificationsPlatform.instance + is WindowsFlutterLocalNotificationsPlugin) { + return FlutterLocalNotificationsPlatform.instance as T?; } return null; @@ -175,6 +183,10 @@ class FlutterLocalNotificationsPlugin { return await resolvePlatformSpecificImplementation< MacOSFlutterLocalNotificationsPlugin>() ?.getNotificationAppLaunchDetails(); + } else if (defaultTargetPlatform == TargetPlatform.windows) { + return await resolvePlatformSpecificImplementation< + WindowsFlutterLocalNotificationsPlugin>() + ?.getNotificationAppLaunchDetails(); } else { return await FlutterLocalNotificationsPlatform.instance .getNotificationAppLaunchDetails() ?? diff --git a/flutter_local_notifications/lib/src/platform_flutter_local_notifications.dart b/flutter_local_notifications/lib/src/platform_flutter_local_notifications.dart index 227bb0803..0325df60b 100644 --- a/flutter_local_notifications/lib/src/platform_flutter_local_notifications.dart +++ b/flutter_local_notifications/lib/src/platform_flutter_local_notifications.dart @@ -850,6 +850,19 @@ class MacOSFlutterLocalNotificationsPlugin } } +class WindowsFlutterLocalNotificationsPlugin + extends MethodChannelFlutterLocalNotificationsPlugin { + @override + Future show(int id, String? title, String? body, {String? payload}) { + return _channel.invokeMethod('show', { + 'id': id, + 'title': title, + 'body': body, + 'payload': payload ?? '', + }); + } +} + /// Checks [backgroundHandler] method, if not `null`, for eligibility to /// be used as a background callback. /// diff --git a/flutter_local_notifications/pubspec.yaml b/flutter_local_notifications/pubspec.yaml index e02e70747..cf9694de7 100644 --- a/flutter_local_notifications/pubspec.yaml +++ b/flutter_local_notifications/pubspec.yaml @@ -13,6 +13,10 @@ dependencies: flutter_local_notifications_platform_interface: ^6.0.0 timezone: ^0.8.0 +dependency_overrides: + flutter_local_notifications_platform_interface: + path: ../flutter_local_notifications_platform_interface + dev_dependencies: flutter_driver: sdk: flutter @@ -33,6 +37,8 @@ flutter: pluginClass: FlutterLocalNotificationsPlugin linux: default_package: flutter_local_notifications_linux + windows: + pluginClass: FlutterLocalNotificationsPlugin environment: sdk: '>=2.12.0 <3.0.0' diff --git a/flutter_local_notifications/windows/.gitignore b/flutter_local_notifications/windows/.gitignore new file mode 100644 index 000000000..b3eb2be16 --- /dev/null +++ b/flutter_local_notifications/windows/.gitignore @@ -0,0 +1,17 @@ +flutter/ + +# Visual Studio user-specific files. +*.suo +*.user +*.userosscache +*.sln.docstates + +# Visual Studio build-related files. +x64/ +x86/ + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ diff --git a/flutter_local_notifications/windows/CMakeLists.txt b/flutter_local_notifications/windows/CMakeLists.txt new file mode 100644 index 000000000..c604cdd31 --- /dev/null +++ b/flutter_local_notifications/windows/CMakeLists.txt @@ -0,0 +1,65 @@ +cmake_minimum_required(VERSION 3.14) +set(PROJECT_NAME "flutter_local_notifications") +project(${PROJECT_NAME} LANGUAGES CXX) +include(FetchContent) + +# This value is used when generating builds using this plugin, so it must +# not be changed +set(PLUGIN_NAME "flutter_local_notifications_plugin") + +# nuget configuration +set(NUGET_PACKAGES_PATH "${CMAKE_BINARY_DIR}/packages") +set(CPPWINRT_VERSION "2.0.220131.2") + +FetchContent_Declare(nuget + URL "https://dist.nuget.org/win-x86-commandline/v6.0.0/nuget.exe" + URL_HASH SHA256=04eb6c4fe4213907e2773e1be1bbbd730e9a655a3c9c58387ce8d4a714a5b9e1 + DOWNLOAD_NO_EXTRACT true +) +find_program(NUGET nuget) +if (NOT NUGET) + message("Nuget.exe not found, trying to download or use cached version.") + FetchContent_MakeAvailable(nuget) + set(NUGET ${nuget_SOURCE_DIR}/nuget.exe) +endif() + +function(nuget_install pkg ver) + execute_process(COMMAND + ${NUGET} install ${pkg} -Version ${ver} -OutputDirectory ${NUGET_PACKAGES_PATH} + RESULT_VARIABLE result) + if (NOT result EQUAL 0) + message(FATAL_ERROR "Failed to install nuget package ${pkg}, version ${ver}") + endif() +endfunction() + +add_library(${PLUGIN_NAME} SHARED + "flutter_local_notifications_plugin.cpp" + "methods.cpp" + "utils/utils.cpp") + +# setup c++/winrt +nuget_install("Microsoft.Windows.CppWinRT" ${CPPWINRT_VERSION}) +set(CPPWINRT ${NUGET_PACKAGES_PATH}/Microsoft.Windows.CppWinRT.${CPPWINRT_VERSION}/bin/cppwinrt.exe) +execute_process(COMMAND + ${CPPWINRT} -input sdk -output include + WORKING_DIRECTORY ${CMAKE_BINARY_DIR} + RESULT_VARIABLE ret) +if (NOT ret EQUAL 0) + message(FATAL_ERROR "Result ${ret} ${CPPWINRT} Failed to run cppwinrt.exe") +endif() + +include_directories(BEFORE SYSTEM ${CMAKE_BINARY_DIR}/include) + +apply_standard_settings(${PLUGIN_NAME}) +set_target_properties(${PLUGIN_NAME} PROPERTIES + CXX_VISIBILITY_PRESET hidden) +target_compile_definitions(${PLUGIN_NAME} PRIVATE FLUTTER_PLUGIN_IMPL) +target_include_directories(${PLUGIN_NAME} INTERFACE + "${CMAKE_CURRENT_SOURCE_DIR}/include") +target_link_libraries(${PLUGIN_NAME} PRIVATE flutter flutter_wrapper_plugin) + +# List of absolute paths to libraries that should be bundled with the plugin +set(flutter_local_notifications_bundled_libraries + "" + PARENT_SCOPE +) diff --git a/flutter_local_notifications/windows/flutter_local_notifications_plugin.cpp b/flutter_local_notifications/windows/flutter_local_notifications_plugin.cpp new file mode 100644 index 000000000..354447339 --- /dev/null +++ b/flutter_local_notifications/windows/flutter_local_notifications_plugin.cpp @@ -0,0 +1,141 @@ +#include "include/flutter_local_notifications/flutter_local_notifications_plugin.h" +#include "include/flutter_local_notifications/methods.h" +#include "utils/utils.h" + +// This must be included before many other Windows headers. +#include +#include +#include +#include +#include + +// For getPlatformVersion; remove unless needed for your plugin implementation. +#include + +#include +#include +#include + +#include +#include +#include + +using namespace winrt::Windows::Data::Xml::Dom; + +namespace { + + class FlutterLocalNotificationsPlugin : public flutter::Plugin { + public: + static void RegisterWithRegistrar(flutter::PluginRegistrarWindows* registrar); + + FlutterLocalNotificationsPlugin(); + + virtual ~FlutterLocalNotificationsPlugin(); + + private: + winrt::Windows::UI::Notifications::ToastNotificationManager toastManager; + + // Called when a method is called on this plugin's channel from Dart. + void HandleMethodCall( + const flutter::MethodCall& method_call, + std::unique_ptr> result); + + void ShowNotification( + const std::string& title, + const std::string& body, + const std::optional& payload); + }; + + // static + void FlutterLocalNotificationsPlugin::RegisterWithRegistrar( + flutter::PluginRegistrarWindows* registrar) { + auto channel = + std::make_unique>( + registrar->messenger(), "dexterous.com/flutter/local_notifications", + &flutter::StandardMethodCodec::GetInstance()); + + auto plugin = std::make_unique(); + + channel->SetMethodCallHandler( + [plugin_pointer = plugin.get()](const auto& call, auto result) { + plugin_pointer->HandleMethodCall(call, std::move(result)); + }); + + registrar->AddPlugin(std::move(plugin)); + + SetCurrentProcessExplicitAppUserModelID(L"Com.Example.Flutter.FlutterLocalNotificationPlugin"); + } + + FlutterLocalNotificationsPlugin::FlutterLocalNotificationsPlugin() : + toastManager{} {} + + FlutterLocalNotificationsPlugin::~FlutterLocalNotificationsPlugin() {} + + void FlutterLocalNotificationsPlugin::HandleMethodCall( + const flutter::MethodCall& method_call, + std::unique_ptr> result) { + std::cout << method_call.method_name() << std::endl; + std::cout << Method::GET_NOTIFICATION_APP_LAUNCH_DETAILS << std::endl; + const auto& method_name = method_call.method_name(); + if (method_name == Method::GET_NOTIFICATION_APP_LAUNCH_DETAILS) { + result->Success(); + } + else if (method_name == Method::SHOW) { + const auto args = std::get_if(method_call.arguments()); + if (args != nullptr) { + const auto& title = Utils::GetString("title", args).value(); + const auto& body = Utils::GetString("body", args).value(); + const auto payload = Utils::GetString("payload", args); + + ShowNotification(title, body, payload); + result->Success(); + } + else { + result->Error("INTERNAL", "flutter_local_notifications encountered an internal error."); + } + } + else { + result->NotImplemented(); + } + } + + void FlutterLocalNotificationsPlugin::ShowNotification( + const std::string& title, + const std::string& body, + const std::optional& payload) { + /*XmlDocument doc; + doc.LoadXml(L"\ + \ + \ + \ + \ + \ + \ + \ + "); + + if (payload.has_value()) { + doc.DocumentElement().SetAttribute(L"launch", winrt::to_hstring(payload.value())); + }*/ + //doc.SelectSingleNode(L"//text[1]").InnerText(winrt::to_hstring(title)); + //doc.SelectSingleNode(L"//text[2]").InnerText(winrt::to_hstring(body)); + + const auto doc = winrt::Windows::UI::Notifications::ToastNotificationManager::GetTemplateContent(winrt::Windows::UI::Notifications::ToastTemplateType::ToastText01); + + XmlNodeList nodes = doc.GetElementsByTagName(L"text"); + nodes.Item(0).AppendChild(doc.CreateTextNode(winrt::to_hstring(title))); + + winrt::Windows::UI::Notifications::ToastNotification notif{ doc }; + const auto notifier = winrt::Windows::UI::Notifications::ToastNotificationManager::CreateToastNotifier(L"example"); + + notifier.Show(notif); + } + +} // namespace + +void FlutterLocalNotificationsPluginRegisterWithRegistrar( + FlutterDesktopPluginRegistrarRef registrar) { + FlutterLocalNotificationsPlugin::RegisterWithRegistrar( + flutter::PluginRegistrarManager::GetInstance() + ->GetRegistrar(registrar)); +} diff --git a/flutter_local_notifications/windows/include/flutter_local_notifications/flutter_local_notifications_plugin.h b/flutter_local_notifications/windows/include/flutter_local_notifications/flutter_local_notifications_plugin.h new file mode 100644 index 000000000..dda06d016 --- /dev/null +++ b/flutter_local_notifications/windows/include/flutter_local_notifications/flutter_local_notifications_plugin.h @@ -0,0 +1,23 @@ +#ifndef FLUTTER_PLUGIN_FLUTTER_LOCAL_NOTIFICATIONS_PLUGIN_H_ +#define FLUTTER_PLUGIN_FLUTTER_LOCAL_NOTIFICATIONS_PLUGIN_H_ + +#include + +#ifdef FLUTTER_PLUGIN_IMPL +#define FLUTTER_PLUGIN_EXPORT __declspec(dllexport) +#else +#define FLUTTER_PLUGIN_EXPORT __declspec(dllimport) +#endif + +#if defined(__cplusplus) +extern "C" { +#endif + +FLUTTER_PLUGIN_EXPORT void FlutterLocalNotificationsPluginRegisterWithRegistrar( + FlutterDesktopPluginRegistrarRef registrar); + +#if defined(__cplusplus) +} // extern "C" +#endif + +#endif // FLUTTER_PLUGIN_FLUTTER_LOCAL_NOTIFICATIONS_PLUGIN_H_ diff --git a/flutter_local_notifications/windows/include/flutter_local_notifications/methods.h b/flutter_local_notifications/windows/include/flutter_local_notifications/methods.h new file mode 100644 index 000000000..b890c23b9 --- /dev/null +++ b/flutter_local_notifications/windows/include/flutter_local_notifications/methods.h @@ -0,0 +1,7 @@ +#include + +namespace Method +{ + extern const std::string GET_NOTIFICATION_APP_LAUNCH_DETAILS; + extern const std::string SHOW; +} \ No newline at end of file diff --git a/flutter_local_notifications/windows/methods.cpp b/flutter_local_notifications/windows/methods.cpp new file mode 100644 index 000000000..ccb4f0533 --- /dev/null +++ b/flutter_local_notifications/windows/methods.cpp @@ -0,0 +1,6 @@ +#include "include/flutter_local_notifications/methods.h" + +#include + +const std::string Method::GET_NOTIFICATION_APP_LAUNCH_DETAILS = "getNotificationAppLaunchDetails"; +const std::string Method::SHOW = "show"; diff --git a/flutter_local_notifications/windows/utils/utils.cpp b/flutter_local_notifications/windows/utils/utils.cpp new file mode 100644 index 000000000..c649f5e35 --- /dev/null +++ b/flutter_local_notifications/windows/utils/utils.cpp @@ -0,0 +1,12 @@ +#include "utils.h" + +#include + +std::optional Utils::GetString(const std::string& key, const flutter::EncodableMap* m) { + const auto pair = m->find(key); + if (pair == m->end()) { + return std::nullopt; + } + const auto& str = std::get(pair->second); + return str; +} diff --git a/flutter_local_notifications/windows/utils/utils.h b/flutter_local_notifications/windows/utils/utils.h new file mode 100644 index 000000000..81483089c --- /dev/null +++ b/flutter_local_notifications/windows/utils/utils.h @@ -0,0 +1,11 @@ +#ifndef UTILS_H_ +#define UTILS_H_ + +#include +#include + +namespace Utils { + std::optional GetString(const std::string& key, const flutter::EncodableMap* m); +} + +#endif // !UTILS_H From ac1cd51a446ea40c72d3e58152543a0d81f97db1 Mon Sep 17 00:00:00 2001 From: Kenneth Date: Wed, 9 Feb 2022 22:06:18 +0000 Subject: [PATCH 02/18] Remove dependency override --- flutter_local_notifications/pubspec.yaml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/flutter_local_notifications/pubspec.yaml b/flutter_local_notifications/pubspec.yaml index cf9694de7..522fc2107 100644 --- a/flutter_local_notifications/pubspec.yaml +++ b/flutter_local_notifications/pubspec.yaml @@ -13,10 +13,6 @@ dependencies: flutter_local_notifications_platform_interface: ^6.0.0 timezone: ^0.8.0 -dependency_overrides: - flutter_local_notifications_platform_interface: - path: ../flutter_local_notifications_platform_interface - dev_dependencies: flutter_driver: sdk: flutter From e0370c0e9a80f57ac555f9eb86002c283ba4fb5e Mon Sep 17 00:00:00 2001 From: Kenneth Date: Thu, 10 Feb 2022 21:40:27 +0000 Subject: [PATCH 03/18] Fix notification content not showing --- .../example/windows/runner/RCa04060 | Bin 0 -> 6140 bytes .../example/windows/runner/RCb04060 | Bin 0 -> 6140 bytes .../example/windows/runner/main.cpp | 3 ++ .../windows/CMakeLists.txt | 7 ++-- .../flutter_local_notifications_plugin.cpp | 35 ++++++------------ .../windows/utils/utils.cpp | 2 +- 6 files changed, 19 insertions(+), 28 deletions(-) create mode 100644 flutter_local_notifications/example/windows/runner/RCa04060 create mode 100644 flutter_local_notifications/example/windows/runner/RCb04060 diff --git a/flutter_local_notifications/example/windows/runner/RCa04060 b/flutter_local_notifications/example/windows/runner/RCa04060 new file mode 100644 index 0000000000000000000000000000000000000000..a890c5b8c104a80079cadbf1a154fdc8e88c6819 GIT binary patch literal 6140 zcmdUzTTfd@5Xa}aQoqBgd1=+k#oXHTjf+jJ5R5Q(S}Br+upv~zMi`P*>WA;|zjKyz z@iDPUAB>LdJ-cUTb}s*!o!vG2XxBEjktNo!n(f;k_MWF_Q^xN2S8r@rc0q3xdMCV1 z>~DG(cAw6;gwF%L`#?3IH{t6!?EvZt4bde^fxqh9o3v~Y8zOyxWWOmTVuRMvp4KhpLDZfi(Ppr?_6h6`}!D2u2 zBv@#at|JXcHe^f|6Wv2tNj9Fc;vMw2%&CW!wT?@0%a?Qb-0+_;Zvu}I_PY(EBm2>I zY}?j(8un94BbhId;~~sV(5Ad*-9A+4xRcUr1^LAhuxlsQwKKebU~TKL_Ng^(mtMz? z?a2OOq|5(X+hgR2_h>0;v3gIGF1CpsqOYSN^U@_Yn_%?>pM2nZf(4rPC;x4<-bYW_ zqsjONzghc6?rPV9EsGv;R61R?HyXD20xY=}V#g7afQsi}r5I<9!qvk~lqh;O!TZXA z$`MmAk`RkMMy?+ty~~>;YD#R*qGX!JTRzLYO2?EL6Z;`VaE!$vE9_be6nYGHiMJln zxQ`?Q(0Ih7b^hf^=2D));}pyfpgM-$v&Ipl$Bg!94+E~_h|fEOcbnLLZNI>~%e*~C zoTfZuz*o-#dCzVTDKQ!@Q|YP@qxkBh*&C!dcmWY0Tx6K2&Jyvv>!101!+sC64Qx7} zat})!hP)&C>TLd=9l&=jdDZQfxBh0DO_Z$`U#Ti{)!0Y1H{^3yja^iG={S`^{jbU6 zNZ(vao@H0rVsCVtE5i?wTT$hY{I(eW@^;)xJr=XU>a}^SxsL5nH;f-qT1Me^Zl_7W znW~I2wvnH;`z7Q!RZps0RO__CS7M*YZK^S}H>%n?ht4Y;(f;F%4VIDe2z{>t7uod|i^`W*)D`!SHl6*V)6{-A&1~Q~ogIAlueN-h zue->p{XgTbiovT??NgtSV~F3LwrMA&7uuwG^qP8k6cjB`4_FXJgr9j<^*gd5SkWC> z8COM)%W(QX{GXi~vbSOs_OZa-qdlIAzas9>pi(4Fv1WG9=O|SM@aWX3vVoVk)4Y;N zk-g>X@CK;xvy-x2b#32d{M*mxt5@Mii9CB}r^yBu^gAQ3wPY3DYVr!%MLh3po#eD9jKsxnYqd-bdnEi*q3{H}d56#NHi#3p+H literal 0 HcmV?d00001 diff --git a/flutter_local_notifications/example/windows/runner/RCb04060 b/flutter_local_notifications/example/windows/runner/RCb04060 new file mode 100644 index 0000000000000000000000000000000000000000..a890c5b8c104a80079cadbf1a154fdc8e88c6819 GIT binary patch literal 6140 zcmdUzTTfd@5Xa}aQoqBgd1=+k#oXHTjf+jJ5R5Q(S}Br+upv~zMi`P*>WA;|zjKyz z@iDPUAB>LdJ-cUTb}s*!o!vG2XxBEjktNo!n(f;k_MWF_Q^xN2S8r@rc0q3xdMCV1 z>~DG(cAw6;gwF%L`#?3IH{t6!?EvZt4bde^fxqh9o3v~Y8zOyxWWOmTVuRMvp4KhpLDZfi(Ppr?_6h6`}!D2u2 zBv@#at|JXcHe^f|6Wv2tNj9Fc;vMw2%&CW!wT?@0%a?Qb-0+_;Zvu}I_PY(EBm2>I zY}?j(8un94BbhId;~~sV(5Ad*-9A+4xRcUr1^LAhuxlsQwKKebU~TKL_Ng^(mtMz? z?a2OOq|5(X+hgR2_h>0;v3gIGF1CpsqOYSN^U@_Yn_%?>pM2nZf(4rPC;x4<-bYW_ zqsjONzghc6?rPV9EsGv;R61R?HyXD20xY=}V#g7afQsi}r5I<9!qvk~lqh;O!TZXA z$`MmAk`RkMMy?+ty~~>;YD#R*qGX!JTRzLYO2?EL6Z;`VaE!$vE9_be6nYGHiMJln zxQ`?Q(0Ih7b^hf^=2D));}pyfpgM-$v&Ipl$Bg!94+E~_h|fEOcbnLLZNI>~%e*~C zoTfZuz*o-#dCzVTDKQ!@Q|YP@qxkBh*&C!dcmWY0Tx6K2&Jyvv>!101!+sC64Qx7} zat})!hP)&C>TLd=9l&=jdDZQfxBh0DO_Z$`U#Ti{)!0Y1H{^3yja^iG={S`^{jbU6 zNZ(vao@H0rVsCVtE5i?wTT$hY{I(eW@^;)xJr=XU>a}^SxsL5nH;f-qT1Me^Zl_7W znW~I2wvnH;`z7Q!RZps0RO__CS7M*YZK^S}H>%n?ht4Y;(f;F%4VIDe2z{>t7uod|i^`W*)D`!SHl6*V)6{-A&1~Q~ogIAlueN-h zue->p{XgTbiovT??NgtSV~F3LwrMA&7uuwG^qP8k6cjB`4_FXJgr9j<^*gd5SkWC> z8COM)%W(QX{GXi~vbSOs_OZa-qdlIAzas9>pi(4Fv1WG9=O|SM@aWX3vVoVk)4Y;N zk-g>X@CK;xvy-x2b#32d{M*mxt5@Mii9CB}r^yBu^gAQ3wPY3DYVr!%MLh3po#eD9jKsxnYqd-bdnEi*q3{H}d56#NHi#3p+H literal 0 HcmV?d00001 diff --git a/flutter_local_notifications/example/windows/runner/main.cpp b/flutter_local_notifications/example/windows/runner/main.cpp index bcb57b0e2..cb42a21ae 100644 --- a/flutter_local_notifications/example/windows/runner/main.cpp +++ b/flutter_local_notifications/example/windows/runner/main.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include "flutter_window.h" #include "utils.h" @@ -17,6 +18,8 @@ int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev, // plugins. ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); + SetCurrentProcessExplicitAppUserModelID(L"com.dexterous.example"); + flutter::DartProject project(L"data"); std::vector command_line_arguments = diff --git a/flutter_local_notifications/windows/CMakeLists.txt b/flutter_local_notifications/windows/CMakeLists.txt index c604cdd31..8a2a3fcfd 100644 --- a/flutter_local_notifications/windows/CMakeLists.txt +++ b/flutter_local_notifications/windows/CMakeLists.txt @@ -9,7 +9,6 @@ set(PLUGIN_NAME "flutter_local_notifications_plugin") # nuget configuration set(NUGET_PACKAGES_PATH "${CMAKE_BINARY_DIR}/packages") -set(CPPWINRT_VERSION "2.0.220131.2") FetchContent_Declare(nuget URL "https://dist.nuget.org/win-x86-commandline/v6.0.0/nuget.exe" @@ -28,7 +27,7 @@ function(nuget_install pkg ver) ${NUGET} install ${pkg} -Version ${ver} -OutputDirectory ${NUGET_PACKAGES_PATH} RESULT_VARIABLE result) if (NOT result EQUAL 0) - message(FATAL_ERROR "Failed to install nuget package ${pkg}, version ${ver}") + message(FATAL_ERROR "Failed to install nuget package ${pkg}, version ${ver}, ${result}") endif() endfunction() @@ -38,8 +37,10 @@ add_library(${PLUGIN_NAME} SHARED "utils/utils.cpp") # setup c++/winrt -nuget_install("Microsoft.Windows.CppWinRT" ${CPPWINRT_VERSION}) +set(CPPWINRT_VERSION "2.0.220131.2") set(CPPWINRT ${NUGET_PACKAGES_PATH}/Microsoft.Windows.CppWinRT.${CPPWINRT_VERSION}/bin/cppwinrt.exe) + +nuget_install("Microsoft.Windows.CppWinRT" ${CPPWINRT_VERSION}) execute_process(COMMAND ${CPPWINRT} -input sdk -output include WORKING_DIRECTORY ${CMAKE_BINARY_DIR} diff --git a/flutter_local_notifications/windows/flutter_local_notifications_plugin.cpp b/flutter_local_notifications/windows/flutter_local_notifications_plugin.cpp index 354447339..12216797c 100644 --- a/flutter_local_notifications/windows/flutter_local_notifications_plugin.cpp +++ b/flutter_local_notifications/windows/flutter_local_notifications_plugin.cpp @@ -83,8 +83,8 @@ namespace { else if (method_name == Method::SHOW) { const auto args = std::get_if(method_call.arguments()); if (args != nullptr) { - const auto& title = Utils::GetString("title", args).value(); - const auto& body = Utils::GetString("body", args).value(); + const auto title = Utils::GetString("title", args).value(); + const auto body = Utils::GetString("body", args).value(); const auto payload = Utils::GetString("payload", args); ShowNotification(title, body, payload); @@ -103,34 +103,21 @@ namespace { const std::string& title, const std::string& body, const std::optional& payload) { - /*XmlDocument doc; - doc.LoadXml(L"\ - \ - \ - \ - \ - \ - \ - \ - "); - - if (payload.has_value()) { - doc.DocumentElement().SetAttribute(L"launch", winrt::to_hstring(payload.value())); - }*/ - //doc.SelectSingleNode(L"//text[1]").InnerText(winrt::to_hstring(title)); - //doc.SelectSingleNode(L"//text[2]").InnerText(winrt::to_hstring(body)); - - const auto doc = winrt::Windows::UI::Notifications::ToastNotificationManager::GetTemplateContent(winrt::Windows::UI::Notifications::ToastTemplateType::ToastText01); - - XmlNodeList nodes = doc.GetElementsByTagName(L"text"); + + // obtain a notification template with a title and a body + const auto doc = winrt::Windows::UI::Notifications::ToastNotificationManager::GetTemplateContent(winrt::Windows::UI::Notifications::ToastTemplateType::ToastText02); + // find all tags + const auto nodes = doc.GetElementsByTagName(L"text"); + // change the text of the first nodes.Item(0).AppendChild(doc.CreateTextNode(winrt::to_hstring(title))); + // change the text of the second + nodes.Item(1).AppendChild(doc.CreateTextNode(winrt::to_hstring(body))); winrt::Windows::UI::Notifications::ToastNotification notif{ doc }; - const auto notifier = winrt::Windows::UI::Notifications::ToastNotificationManager::CreateToastNotifier(L"example"); + const auto notifier = winrt::Windows::UI::Notifications::ToastNotificationManager::CreateToastNotifier(L"com.dexterous.example"); notifier.Show(notif); } - } // namespace void FlutterLocalNotificationsPluginRegisterWithRegistrar( diff --git a/flutter_local_notifications/windows/utils/utils.cpp b/flutter_local_notifications/windows/utils/utils.cpp index c649f5e35..40a35915d 100644 --- a/flutter_local_notifications/windows/utils/utils.cpp +++ b/flutter_local_notifications/windows/utils/utils.cpp @@ -3,7 +3,7 @@ #include std::optional Utils::GetString(const std::string& key, const flutter::EncodableMap* m) { - const auto pair = m->find(key); + const auto pair = m->find(flutter::EncodableValue(key)); if (pair == m->end()) { return std::nullopt; } From 7b5dfa37695fb3d048312f962dbf48b0ca21440f Mon Sep 17 00:00:00 2001 From: Kenneth Date: Thu, 10 Feb 2022 22:32:55 +0000 Subject: [PATCH 04/18] Implement initialize method --- .../example/lib/main.dart | 12 ++++--- .../lib/flutter_local_notifications.dart | 1 + .../flutter_local_notifications_plugin.dart | 5 +++ .../lib/src/initialization_settings.dart | 5 +++ .../platform_flutter_local_notifications.dart | 33 ++++++++++++++----- .../windows/initialization_settings.dart | 10 ++++++ .../windows/method_channel_mappers.dart | 10 ++++++ .../flutter_local_notifications_plugin.cpp | 28 ++++++++++++---- .../flutter_local_notifications/methods.h | 5 +++ .../windows/methods.cpp | 1 + .../windows/utils/utils.h | 6 ++++ 11 files changed, 98 insertions(+), 18 deletions(-) create mode 100644 flutter_local_notifications/lib/src/platform_specifics/windows/initialization_settings.dart create mode 100644 flutter_local_notifications/lib/src/platform_specifics/windows/method_channel_mappers.dart diff --git a/flutter_local_notifications/example/lib/main.dart b/flutter_local_notifications/example/lib/main.dart index 2af9972e9..a07902f4b 100644 --- a/flutter_local_notifications/example/lib/main.dart +++ b/flutter_local_notifications/example/lib/main.dart @@ -182,11 +182,16 @@ Future main() async { defaultActionName: 'Open notification', defaultIcon: AssetsLinuxIcon('icons/app_icon.png'), ); + const WindowsInitializationSettings initializationSettingsWindows = + WindowsInitializationSettings( + appName: 'Flutter Local Notifications Example', + ); final InitializationSettings initializationSettings = InitializationSettings( android: initializationSettingsAndroid, iOS: initializationSettingsIOS, macOS: initializationSettingsMacOS, linux: initializationSettingsLinux, + windows: initializationSettingsWindows, ); await flutterLocalNotificationsPlugin.initialize( initializationSettings, @@ -733,7 +738,7 @@ class _HomePageState extends State { onPressed: () async { await _stopForegroundService(); }, - ), + ), ], if (!kIsWeb && (Platform.isIOS || Platform.isMacOS)) ...[ @@ -2153,8 +2158,6 @@ class _HomePageState extends State { ?.stopForegroundService(); } - - Future _createNotificationChannel() async { const AndroidNotificationChannel androidNotificationChannel = AndroidNotificationChannel( @@ -2417,7 +2420,8 @@ Future _showLinuxNotificationWithByteDataIcon() async { data: iconBytes, width: iconData.width, height: iconData.height, - channels: 4, // The icon has an alpha channel + channels: 4, + // The icon has an alpha channel hasAlpha: true, ), ), diff --git a/flutter_local_notifications/lib/flutter_local_notifications.dart b/flutter_local_notifications/lib/flutter_local_notifications.dart index 92740ee2e..a261b56c6 100644 --- a/flutter_local_notifications/lib/flutter_local_notifications.dart +++ b/flutter_local_notifications/lib/flutter_local_notifications.dart @@ -43,5 +43,6 @@ export 'src/platform_specifics/ios/notification_details.dart'; export 'src/platform_specifics/macos/initialization_settings.dart'; export 'src/platform_specifics/macos/notification_attachment.dart'; export 'src/platform_specifics/macos/notification_details.dart'; +export 'src/platform_specifics/windows/initialization_settings.dart'; export 'src/typedefs.dart'; export 'src/types.dart'; diff --git a/flutter_local_notifications/lib/src/flutter_local_notifications_plugin.dart b/flutter_local_notifications/lib/src/flutter_local_notifications_plugin.dart index 793b71709..1882c511b 100644 --- a/flutter_local_notifications/lib/src/flutter_local_notifications_plugin.dart +++ b/flutter_local_notifications/lib/src/flutter_local_notifications_plugin.dart @@ -150,6 +150,11 @@ class FlutterLocalNotificationsPlugin { LinuxFlutterLocalNotificationsPlugin>() ?.initialize(initializationSettings.linux!, onSelectNotification: onSelectNotification); + } else if (defaultTargetPlatform == TargetPlatform.windows) { + return await resolvePlatformSpecificImplementation< + WindowsFlutterLocalNotificationsPlugin>() + ?.initialize(initializationSettings.windows!, + onSelectNotification: onSelectNotification); } return true; } diff --git a/flutter_local_notifications/lib/src/initialization_settings.dart b/flutter_local_notifications/lib/src/initialization_settings.dart index 801ae6082..0b6565523 100644 --- a/flutter_local_notifications/lib/src/initialization_settings.dart +++ b/flutter_local_notifications/lib/src/initialization_settings.dart @@ -1,5 +1,6 @@ import 'package:flutter_local_notifications_linux/flutter_local_notifications_linux.dart'; +import '../flutter_local_notifications.dart'; import 'platform_specifics/android/initialization_settings.dart'; import 'platform_specifics/ios/initialization_settings.dart'; import 'platform_specifics/macos/initialization_settings.dart'; @@ -12,6 +13,7 @@ class InitializationSettings { this.iOS, this.macOS, this.linux, + this.windows, }); /// Settings for Android. @@ -25,4 +27,7 @@ class InitializationSettings { /// Settings for Linux. final LinuxInitializationSettings? linux; + + /// Settings for Windows. + final WindowsInitializationSettings? windows; } diff --git a/flutter_local_notifications/lib/src/platform_flutter_local_notifications.dart b/flutter_local_notifications/lib/src/platform_flutter_local_notifications.dart index 0325df60b..b076f5336 100644 --- a/flutter_local_notifications/lib/src/platform_flutter_local_notifications.dart +++ b/flutter_local_notifications/lib/src/platform_flutter_local_notifications.dart @@ -25,6 +25,8 @@ import 'platform_specifics/ios/notification_details.dart'; import 'platform_specifics/macos/initialization_settings.dart'; import 'platform_specifics/macos/method_channel_mappers.dart'; import 'platform_specifics/macos/notification_details.dart'; +import 'platform_specifics/windows/initialization_settings.dart'; +import 'platform_specifics/windows/method_channel_mappers.dart'; import 'type_mappers.dart'; import 'typedefs.dart'; import 'types.dart'; @@ -850,17 +852,32 @@ class MacOSFlutterLocalNotificationsPlugin } } +/// Windows implementation of the flutter_local_notifications plugin. class WindowsFlutterLocalNotificationsPlugin extends MethodChannelFlutterLocalNotificationsPlugin { + /// Initializes the plugin. + /// + /// Call this method on application before using the plugin further. + /// This should only be done once. When a notification created by this plugin + /// was used to launch the app, calling [initialize] is what will trigger to + /// the [onSelectNotification] callback to be fire. + /// + /// To handle when a notification launched an application, use + /// [getNotificationAppLaunchDetails]. + Future initialize( + WindowsInitializationSettings settings, { + SelectNotificationCallback? onSelectNotification, + }) => + _channel.invokeMethod('initialize', settings.toMap()); + @override - Future show(int id, String? title, String? body, {String? payload}) { - return _channel.invokeMethod('show', { - 'id': id, - 'title': title, - 'body': body, - 'payload': payload ?? '', - }); - } + Future show(int id, String? title, String? body, {String? payload}) => + _channel.invokeMethod('show', { + 'id': id, + 'title': title, + 'body': body, + 'payload': payload ?? '', + }); } /// Checks [backgroundHandler] method, if not `null`, for eligibility to diff --git a/flutter_local_notifications/lib/src/platform_specifics/windows/initialization_settings.dart b/flutter_local_notifications/lib/src/platform_specifics/windows/initialization_settings.dart new file mode 100644 index 000000000..fbbf6b405 --- /dev/null +++ b/flutter_local_notifications/lib/src/platform_specifics/windows/initialization_settings.dart @@ -0,0 +1,10 @@ +/// Plugin initialization settings for Windows. +class WindowsInitializationSettings { + /// Creates a new settings object for initializing this plugin on Windows. + const WindowsInitializationSettings({ + required this.appName, + }); + + /// The name of the app that should be shown in the notification toast. + final String appName; +} diff --git a/flutter_local_notifications/lib/src/platform_specifics/windows/method_channel_mappers.dart b/flutter_local_notifications/lib/src/platform_specifics/windows/method_channel_mappers.dart new file mode 100644 index 000000000..87c0b7e27 --- /dev/null +++ b/flutter_local_notifications/lib/src/platform_specifics/windows/method_channel_mappers.dart @@ -0,0 +1,10 @@ +import 'initialization_settings.dart'; + +/// An extension on [WindowsInitializationSettings] that provides mapping +/// to method channel serializable values. +extension WindowsInitializationSettingsMapper on WindowsInitializationSettings { + /// Maps [WindowsInitializationSettings] to a [Map]. + Map toMap() => { + 'appName': appName, + }; +} diff --git a/flutter_local_notifications/windows/flutter_local_notifications_plugin.cpp b/flutter_local_notifications/windows/flutter_local_notifications_plugin.cpp index 12216797c..406fc5c6e 100644 --- a/flutter_local_notifications/windows/flutter_local_notifications_plugin.cpp +++ b/flutter_local_notifications/windows/flutter_local_notifications_plugin.cpp @@ -33,13 +33,15 @@ namespace { virtual ~FlutterLocalNotificationsPlugin(); private: - winrt::Windows::UI::Notifications::ToastNotificationManager toastManager; + std::optional toastNotifier; // Called when a method is called on this plugin's channel from Dart. void HandleMethodCall( const flutter::MethodCall& method_call, std::unique_ptr> result); + void Initialize(const std::string& appName); + void ShowNotification( const std::string& title, const std::string& body, @@ -62,12 +64,9 @@ namespace { }); registrar->AddPlugin(std::move(plugin)); - - SetCurrentProcessExplicitAppUserModelID(L"Com.Example.Flutter.FlutterLocalNotificationPlugin"); } - FlutterLocalNotificationsPlugin::FlutterLocalNotificationsPlugin() : - toastManager{} {} + FlutterLocalNotificationsPlugin::FlutterLocalNotificationsPlugin() {} FlutterLocalNotificationsPlugin::~FlutterLocalNotificationsPlugin() {} @@ -76,10 +75,23 @@ namespace { std::unique_ptr> result) { std::cout << method_call.method_name() << std::endl; std::cout << Method::GET_NOTIFICATION_APP_LAUNCH_DETAILS << std::endl; + const auto& method_name = method_call.method_name(); if (method_name == Method::GET_NOTIFICATION_APP_LAUNCH_DETAILS) { result->Success(); } + else if (method_name == Method::INITIALIZE) { + const auto args = std::get_if(method_call.arguments()); + if (args != nullptr) { + const auto appName = Utils::GetString("appName", args).value(); + + Initialize(appName); + result->Success(true); + } + else { + result->Error("INTERNAL", "flutter_local_notifications encountered an internal error."); + } + } else if (method_name == Method::SHOW) { const auto args = std::get_if(method_call.arguments()); if (args != nullptr) { @@ -99,6 +111,10 @@ namespace { } } + void FlutterLocalNotificationsPlugin::Initialize(const std::string& appName) { + toastNotifier = winrt::Windows::UI::Notifications::ToastNotificationManager::CreateToastNotifier(winrt::to_hstring(appName)); + } + void FlutterLocalNotificationsPlugin::ShowNotification( const std::string& title, const std::string& body, @@ -114,7 +130,7 @@ namespace { nodes.Item(1).AppendChild(doc.CreateTextNode(winrt::to_hstring(body))); winrt::Windows::UI::Notifications::ToastNotification notif{ doc }; - const auto notifier = winrt::Windows::UI::Notifications::ToastNotificationManager::CreateToastNotifier(L"com.dexterous.example"); + const auto notifier = winrt::Windows::UI::Notifications::ToastNotificationManager::CreateToastNotifier(L"Test App Name"); notifier.Show(notif); } diff --git a/flutter_local_notifications/windows/include/flutter_local_notifications/methods.h b/flutter_local_notifications/windows/include/flutter_local_notifications/methods.h index b890c23b9..7bbbb1c38 100644 --- a/flutter_local_notifications/windows/include/flutter_local_notifications/methods.h +++ b/flutter_local_notifications/windows/include/flutter_local_notifications/methods.h @@ -1,7 +1,12 @@ #include +/// +/// Defines names of methods of this plugin that are callable +/// through Flutter's method channel. +/// namespace Method { + extern const std::string INITIALIZE; extern const std::string GET_NOTIFICATION_APP_LAUNCH_DETAILS; extern const std::string SHOW; } \ No newline at end of file diff --git a/flutter_local_notifications/windows/methods.cpp b/flutter_local_notifications/windows/methods.cpp index ccb4f0533..5763cb3f0 100644 --- a/flutter_local_notifications/windows/methods.cpp +++ b/flutter_local_notifications/windows/methods.cpp @@ -4,3 +4,4 @@ const std::string Method::GET_NOTIFICATION_APP_LAUNCH_DETAILS = "getNotificationAppLaunchDetails"; const std::string Method::SHOW = "show"; +const std::string Method::INITIALIZE = "initialize"; diff --git a/flutter_local_notifications/windows/utils/utils.h b/flutter_local_notifications/windows/utils/utils.h index 81483089c..b3b01ac66 100644 --- a/flutter_local_notifications/windows/utils/utils.h +++ b/flutter_local_notifications/windows/utils/utils.h @@ -5,6 +5,12 @@ #include namespace Utils { + /// + /// Retrieves the string value stored with the given key in the given EncodableMap. + /// + /// The key that maps to the desired string value. + /// The EncodabeMap that stores the key-value pair. + /// The string value that the key maps to, or nullopt if none is found. std::optional GetString(const std::string& key, const flutter::EncodableMap* m); } From 1fd635ed52434d7a6d2df82fe9d8aa0077b50223 Mon Sep 17 00:00:00 2001 From: Kenneth Date: Thu, 10 Feb 2022 23:45:24 +0000 Subject: [PATCH 05/18] WIP impl of cancelling notifications --- .../flutter_local_notifications_plugin.cpp | 41 +++++++++++++++---- .../flutter_local_notifications/methods.h | 1 + .../windows/methods.cpp | 1 + .../windows/utils/utils.cpp | 5 ++- .../windows/utils/utils.h | 3 +- 5 files changed, 40 insertions(+), 11 deletions(-) diff --git a/flutter_local_notifications/windows/flutter_local_notifications_plugin.cpp b/flutter_local_notifications/windows/flutter_local_notifications_plugin.cpp index 406fc5c6e..9a831bdd8 100644 --- a/flutter_local_notifications/windows/flutter_local_notifications_plugin.cpp +++ b/flutter_local_notifications/windows/flutter_local_notifications_plugin.cpp @@ -33,6 +33,7 @@ namespace { virtual ~FlutterLocalNotificationsPlugin(); private: + std::map activeNotifications; std::optional toastNotifier; // Called when a method is called on this plugin's channel from Dart. @@ -43,9 +44,12 @@ namespace { void Initialize(const std::string& appName); void ShowNotification( + const int id, const std::string& title, const std::string& body, const std::optional& payload); + + void CancelNotification(const int id); }; // static @@ -83,7 +87,7 @@ namespace { else if (method_name == Method::INITIALIZE) { const auto args = std::get_if(method_call.arguments()); if (args != nullptr) { - const auto appName = Utils::GetString("appName", args).value(); + const auto appName = Utils::GetMapValue("appName", args).value(); Initialize(appName); result->Success(true); @@ -94,12 +98,24 @@ namespace { } else if (method_name == Method::SHOW) { const auto args = std::get_if(method_call.arguments()); - if (args != nullptr) { - const auto title = Utils::GetString("title", args).value(); - const auto body = Utils::GetString("body", args).value(); - const auto payload = Utils::GetString("payload", args); + if (args != nullptr && toastNotifier.has_value()) { + const auto id = Utils::GetMapValue("id", args).value(); + const auto title = Utils::GetMapValue("title", args).value(); + const auto body = Utils::GetMapValue("body", args).value(); + const auto payload = Utils::GetMapValue("payload", args); - ShowNotification(title, body, payload); + ShowNotification(id, title, body, payload); + result->Success(); + } + else { + result->Error("INTERNAL", "flutter_local_notifications encountered an internal error."); + } + } + else if (method_name == Method::CANCEL && toastNotifier.has_value()) { + const auto args = method_call.arguments(); + if (std::holds_alternative(*args)) { + const auto id = std::get(*args); + CancelNotification(id); result->Success(); } else { @@ -116,6 +132,7 @@ namespace { } void FlutterLocalNotificationsPlugin::ShowNotification( + const int id, const std::string& title, const std::string& body, const std::optional& payload) { @@ -130,9 +147,17 @@ namespace { nodes.Item(1).AppendChild(doc.CreateTextNode(winrt::to_hstring(body))); winrt::Windows::UI::Notifications::ToastNotification notif{ doc }; - const auto notifier = winrt::Windows::UI::Notifications::ToastNotificationManager::CreateToastNotifier(L"Test App Name"); - notifier.Show(notif); + toastNotifier.value().Show(notif); + activeNotifications[id] = notif; + } + + void FlutterLocalNotificationsPlugin::CancelNotification(const int id) { + const auto p = activeNotifications.find(id); + if (p != activeNotifications.end()) { + const auto& notif = p->second; + toastNotifier.value().Hide(notif); + } } } // namespace diff --git a/flutter_local_notifications/windows/include/flutter_local_notifications/methods.h b/flutter_local_notifications/windows/include/flutter_local_notifications/methods.h index 7bbbb1c38..f6c4d288b 100644 --- a/flutter_local_notifications/windows/include/flutter_local_notifications/methods.h +++ b/flutter_local_notifications/windows/include/flutter_local_notifications/methods.h @@ -9,4 +9,5 @@ namespace Method extern const std::string INITIALIZE; extern const std::string GET_NOTIFICATION_APP_LAUNCH_DETAILS; extern const std::string SHOW; + extern const std::string CANCEL; } \ No newline at end of file diff --git a/flutter_local_notifications/windows/methods.cpp b/flutter_local_notifications/windows/methods.cpp index 5763cb3f0..36820194b 100644 --- a/flutter_local_notifications/windows/methods.cpp +++ b/flutter_local_notifications/windows/methods.cpp @@ -5,3 +5,4 @@ const std::string Method::GET_NOTIFICATION_APP_LAUNCH_DETAILS = "getNotificationAppLaunchDetails"; const std::string Method::SHOW = "show"; const std::string Method::INITIALIZE = "initialize"; +const std::string Method::CANCEL = "cancel"; diff --git a/flutter_local_notifications/windows/utils/utils.cpp b/flutter_local_notifications/windows/utils/utils.cpp index 40a35915d..e1780bdc3 100644 --- a/flutter_local_notifications/windows/utils/utils.cpp +++ b/flutter_local_notifications/windows/utils/utils.cpp @@ -2,11 +2,12 @@ #include -std::optional Utils::GetString(const std::string& key, const flutter::EncodableMap* m) { +template +std::optional Utils::GetMapValue(const std::string& key, const flutter::EncodableMap* m) { const auto pair = m->find(flutter::EncodableValue(key)); if (pair == m->end()) { return std::nullopt; } - const auto& str = std::get(pair->second); + const auto& str = std::get(pair->second); return str; } diff --git a/flutter_local_notifications/windows/utils/utils.h b/flutter_local_notifications/windows/utils/utils.h index b3b01ac66..190572778 100644 --- a/flutter_local_notifications/windows/utils/utils.h +++ b/flutter_local_notifications/windows/utils/utils.h @@ -11,7 +11,8 @@ namespace Utils { /// The key that maps to the desired string value. /// The EncodabeMap that stores the key-value pair. /// The string value that the key maps to, or nullopt if none is found. - std::optional GetString(const std::string& key, const flutter::EncodableMap* m); + template + std::optional GetMapValue(const std::string& key, const flutter::EncodableMap* m); } #endif // !UTILS_H From 1fad545f2c01cf103204e6dcdc0a5ad739019597 Mon Sep 17 00:00:00 2001 From: Kenneth Date: Fri, 11 Feb 2022 21:49:21 +0000 Subject: [PATCH 06/18] Add support for optional title/body --- .../windows/CMakeLists.txt | 2 +- .../flutter_local_notifications_plugin.cpp | 31 +++++++++++-------- .../windows/utils/utils.cpp | 13 -------- .../windows/utils/utils.h | 12 ++++++- 4 files changed, 30 insertions(+), 28 deletions(-) delete mode 100644 flutter_local_notifications/windows/utils/utils.cpp diff --git a/flutter_local_notifications/windows/CMakeLists.txt b/flutter_local_notifications/windows/CMakeLists.txt index 8a2a3fcfd..ae8838cac 100644 --- a/flutter_local_notifications/windows/CMakeLists.txt +++ b/flutter_local_notifications/windows/CMakeLists.txt @@ -34,7 +34,7 @@ endfunction() add_library(${PLUGIN_NAME} SHARED "flutter_local_notifications_plugin.cpp" "methods.cpp" - "utils/utils.cpp") + "utils/utils.h") # setup c++/winrt set(CPPWINRT_VERSION "2.0.220131.2") diff --git a/flutter_local_notifications/windows/flutter_local_notifications_plugin.cpp b/flutter_local_notifications/windows/flutter_local_notifications_plugin.cpp index 9a831bdd8..8c8e4d0a7 100644 --- a/flutter_local_notifications/windows/flutter_local_notifications_plugin.cpp +++ b/flutter_local_notifications/windows/flutter_local_notifications_plugin.cpp @@ -33,7 +33,7 @@ namespace { virtual ~FlutterLocalNotificationsPlugin(); private: - std::map activeNotifications; + std::map activeNotifications; std::optional toastNotifier; // Called when a method is called on this plugin's channel from Dart. @@ -45,8 +45,8 @@ namespace { void ShowNotification( const int id, - const std::string& title, - const std::string& body, + const std::optional& title, + const std::optional& body, const std::optional& payload); void CancelNotification(const int id); @@ -100,8 +100,8 @@ namespace { const auto args = std::get_if(method_call.arguments()); if (args != nullptr && toastNotifier.has_value()) { const auto id = Utils::GetMapValue("id", args).value(); - const auto title = Utils::GetMapValue("title", args).value(); - const auto body = Utils::GetMapValue("body", args).value(); + const auto title = Utils::GetMapValue("title", args); + const auto body = Utils::GetMapValue("body", args); const auto payload = Utils::GetMapValue("payload", args); ShowNotification(id, title, body, payload); @@ -133,30 +133,35 @@ namespace { void FlutterLocalNotificationsPlugin::ShowNotification( const int id, - const std::string& title, - const std::string& body, + const std::optional& title, + const std::optional& body, const std::optional& payload) { // obtain a notification template with a title and a body const auto doc = winrt::Windows::UI::Notifications::ToastNotificationManager::GetTemplateContent(winrt::Windows::UI::Notifications::ToastTemplateType::ToastText02); // find all tags const auto nodes = doc.GetElementsByTagName(L"text"); - // change the text of the first - nodes.Item(0).AppendChild(doc.CreateTextNode(winrt::to_hstring(title))); - // change the text of the second - nodes.Item(1).AppendChild(doc.CreateTextNode(winrt::to_hstring(body))); + if (title.has_value()) { + // change the text of the first , which will be the title + nodes.Item(0).AppendChild(doc.CreateTextNode(winrt::to_hstring(title.value()))); + } + if (body.has_value()) { + // change the text of the second , which will be the body + nodes.Item(1).AppendChild(doc.CreateTextNode(winrt::to_hstring(body.value()))); + } + winrt::Windows::UI::Notifications::ToastNotification notif{ doc }; toastNotifier.value().Show(notif); - activeNotifications[id] = notif; + activeNotifications[id] = ¬if; } void FlutterLocalNotificationsPlugin::CancelNotification(const int id) { const auto p = activeNotifications.find(id); if (p != activeNotifications.end()) { const auto& notif = p->second; - toastNotifier.value().Hide(notif); + toastNotifier.value().Hide(*notif); } } } // namespace diff --git a/flutter_local_notifications/windows/utils/utils.cpp b/flutter_local_notifications/windows/utils/utils.cpp deleted file mode 100644 index e1780bdc3..000000000 --- a/flutter_local_notifications/windows/utils/utils.cpp +++ /dev/null @@ -1,13 +0,0 @@ -#include "utils.h" - -#include - -template -std::optional Utils::GetMapValue(const std::string& key, const flutter::EncodableMap* m) { - const auto pair = m->find(flutter::EncodableValue(key)); - if (pair == m->end()) { - return std::nullopt; - } - const auto& str = std::get(pair->second); - return str; -} diff --git a/flutter_local_notifications/windows/utils/utils.h b/flutter_local_notifications/windows/utils/utils.h index 190572778..08766348c 100644 --- a/flutter_local_notifications/windows/utils/utils.h +++ b/flutter_local_notifications/windows/utils/utils.h @@ -12,7 +12,17 @@ namespace Utils { /// The EncodabeMap that stores the key-value pair. /// The string value that the key maps to, or nullopt if none is found. template - std::optional GetMapValue(const std::string& key, const flutter::EncodableMap* m); + std::optional GetMapValue(const std::string& key, const flutter::EncodableMap* m) { + const auto pair = m->find(flutter::EncodableValue(key)); + if (pair == m->end()) { + return std::nullopt; + } + const auto val = pair->second; + if (std::holds_alternative(val)) { + return std::get(val); + } + return std::nullopt; + } } #endif // !UTILS_H From 8c53f574ea820122d5aca0f66457ac11530e75e7 Mon Sep 17 00:00:00 2001 From: Kenneth Date: Sun, 13 Feb 2022 23:42:28 +0000 Subject: [PATCH 07/18] Impl Registry logic for notification handling --- .../example/lib/main.dart | 1 + .../example/windows/runner/main.cpp | 2 - .../windows/initialization_settings.dart | 16 ++ .../windows/method_channel_mappers.dart | 3 + .../windows/CMakeLists.txt | 3 +- .../flutter_local_notifications_plugin.cpp | 69 ++++- .../flutter_local_notifications/methods.h | 1 + .../windows/methods.cpp | 1 + .../windows/registration.cpp | 236 ++++++++++++++++++ .../windows/registration.h | 22 ++ 10 files changed, 339 insertions(+), 15 deletions(-) create mode 100644 flutter_local_notifications/windows/registration.cpp create mode 100644 flutter_local_notifications/windows/registration.h diff --git a/flutter_local_notifications/example/lib/main.dart b/flutter_local_notifications/example/lib/main.dart index a07902f4b..bbe5483c5 100644 --- a/flutter_local_notifications/example/lib/main.dart +++ b/flutter_local_notifications/example/lib/main.dart @@ -185,6 +185,7 @@ Future main() async { const WindowsInitializationSettings initializationSettingsWindows = WindowsInitializationSettings( appName: 'Flutter Local Notifications Example', + appUserModelId: 'Com.Dexterous.FlutterLocalNotificationsExample', ); final InitializationSettings initializationSettings = InitializationSettings( android: initializationSettingsAndroid, diff --git a/flutter_local_notifications/example/windows/runner/main.cpp b/flutter_local_notifications/example/windows/runner/main.cpp index cb42a21ae..87602243e 100644 --- a/flutter_local_notifications/example/windows/runner/main.cpp +++ b/flutter_local_notifications/example/windows/runner/main.cpp @@ -18,8 +18,6 @@ int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev, // plugins. ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); - SetCurrentProcessExplicitAppUserModelID(L"com.dexterous.example"); - flutter::DartProject project(L"data"); std::vector command_line_arguments = diff --git a/flutter_local_notifications/lib/src/platform_specifics/windows/initialization_settings.dart b/flutter_local_notifications/lib/src/platform_specifics/windows/initialization_settings.dart index fbbf6b405..a4036e483 100644 --- a/flutter_local_notifications/lib/src/platform_specifics/windows/initialization_settings.dart +++ b/flutter_local_notifications/lib/src/platform_specifics/windows/initialization_settings.dart @@ -1,10 +1,26 @@ +import 'package:flutter/widgets.dart'; + /// Plugin initialization settings for Windows. class WindowsInitializationSettings { /// Creates a new settings object for initializing this plugin on Windows. const WindowsInitializationSettings({ required this.appName, + required this.appUserModelId, + this.iconPath, + this.iconBackgroundColor, }); /// The name of the app that should be shown in the notification toast. final String appName; + + /// The unique app user model ID that identifies the app, + /// in the form of CompanyName.ProductName.SubProduct.VersionInformation. + /// + /// See https://docs.microsoft.com/en-us/windows/win32/shell/appids + /// for more information. + final String appUserModelId; + + final String? iconPath; + + final Color? iconBackgroundColor; } diff --git a/flutter_local_notifications/lib/src/platform_specifics/windows/method_channel_mappers.dart b/flutter_local_notifications/lib/src/platform_specifics/windows/method_channel_mappers.dart index 87c0b7e27..01c577cc0 100644 --- a/flutter_local_notifications/lib/src/platform_specifics/windows/method_channel_mappers.dart +++ b/flutter_local_notifications/lib/src/platform_specifics/windows/method_channel_mappers.dart @@ -6,5 +6,8 @@ extension WindowsInitializationSettingsMapper on WindowsInitializationSettings { /// Maps [WindowsInitializationSettings] to a [Map]. Map toMap() => { 'appName': appName, + 'aumid': appUserModelId, + 'iconPath': iconPath, + 'iconBgColor': iconBackgroundColor, }; } diff --git a/flutter_local_notifications/windows/CMakeLists.txt b/flutter_local_notifications/windows/CMakeLists.txt index ae8838cac..16807d156 100644 --- a/flutter_local_notifications/windows/CMakeLists.txt +++ b/flutter_local_notifications/windows/CMakeLists.txt @@ -34,7 +34,8 @@ endfunction() add_library(${PLUGIN_NAME} SHARED "flutter_local_notifications_plugin.cpp" "methods.cpp" - "utils/utils.h") + "utils/utils.h" + "registration.cpp") # setup c++/winrt set(CPPWINRT_VERSION "2.0.220131.2") diff --git a/flutter_local_notifications/windows/flutter_local_notifications_plugin.cpp b/flutter_local_notifications/windows/flutter_local_notifications_plugin.cpp index 8c8e4d0a7..ee440d911 100644 --- a/flutter_local_notifications/windows/flutter_local_notifications_plugin.cpp +++ b/flutter_local_notifications/windows/flutter_local_notifications_plugin.cpp @@ -1,10 +1,12 @@ #include "include/flutter_local_notifications/flutter_local_notifications_plugin.h" #include "include/flutter_local_notifications/methods.h" #include "utils/utils.h" +#include "registration.h" // This must be included before many other Windows headers. #include #include +#include #include #include #include @@ -33,23 +35,47 @@ namespace { virtual ~FlutterLocalNotificationsPlugin(); private: - std::map activeNotifications; std::optional toastNotifier; + std::optional toastNotificationHistory; // Called when a method is called on this plugin's channel from Dart. void HandleMethodCall( const flutter::MethodCall& method_call, std::unique_ptr> result); - void Initialize(const std::string& appName); - + /// + /// Initializes this plugin. + /// + /// The display name of this app that should be shown in the notification toast. + void Initialize( + const std::string& appName, + const std::string& aumid, + const std::optional& iconPath, + const std::optional& iconBgColor); + + /// + /// Displays a single notification toast. + /// + /// A unique ID that identifies this notification. It can be used to cancel/dismiss the notification. + /// An optional title of the notification. + /// An optional body of the notification. + /// void ShowNotification( const int id, const std::optional& title, const std::optional& body, const std::optional& payload); + /// + /// Dismisses the notification that has the given ID. + /// + /// The ID of the notification to be dismissed. void CancelNotification(const int id); + + /// + /// Dismisses all currently active notifications. + /// + void CancelAllNotifications(); }; // static @@ -78,7 +104,6 @@ namespace { const flutter::MethodCall& method_call, std::unique_ptr> result) { std::cout << method_call.method_name() << std::endl; - std::cout << Method::GET_NOTIFICATION_APP_LAUNCH_DETAILS << std::endl; const auto& method_name = method_call.method_name(); if (method_name == Method::GET_NOTIFICATION_APP_LAUNCH_DETAILS) { @@ -88,8 +113,11 @@ namespace { const auto args = std::get_if(method_call.arguments()); if (args != nullptr) { const auto appName = Utils::GetMapValue("appName", args).value(); + const auto aumid = Utils::GetMapValue("aumid", args).value(); + const auto iconPath = Utils::GetMapValue("iconPath", args); + const auto iconBgColor = Utils::GetMapValue("iconBgColor", args); - Initialize(appName); + Initialize(appName, aumid, iconPath, iconBgColor); result->Success(true); } else { @@ -122,13 +150,23 @@ namespace { result->Error("INTERNAL", "flutter_local_notifications encountered an internal error."); } } + else if (method_name == Method::CANCEL_ALL && toastNotifier.has_value()) { + CancelAllNotifications(); + result->Success(); + } else { result->NotImplemented(); } } - void FlutterLocalNotificationsPlugin::Initialize(const std::string& appName) { - toastNotifier = winrt::Windows::UI::Notifications::ToastNotificationManager::CreateToastNotifier(winrt::to_hstring(appName)); + void FlutterLocalNotificationsPlugin::Initialize( + const std::string& appName, + const std::string& aumid, + const std::optional& iconPath, + const std::optional& iconBgColor) { + std::cout << "Initialize" << std::endl; + PluginRegistration::RegisterApp(aumid, appName, iconPath, iconBgColor); + toastNotifier = winrt::Windows::UI::Notifications::ToastNotificationManager::CreateToastNotifier(winrt::to_hstring(aumid)); } void FlutterLocalNotificationsPlugin::ShowNotification( @@ -152,17 +190,24 @@ namespace { } winrt::Windows::UI::Notifications::ToastNotification notif{ doc }; + notif.Tag(winrt::to_hstring(id)); toastNotifier.value().Show(notif); - activeNotifications[id] = ¬if; } void FlutterLocalNotificationsPlugin::CancelNotification(const int id) { - const auto p = activeNotifications.find(id); - if (p != activeNotifications.end()) { - const auto& notif = p->second; - toastNotifier.value().Hide(*notif); + if (!toastNotificationHistory.has_value()) { + toastNotificationHistory = winrt::Windows::UI::Notifications::ToastNotificationManager::History(); + } + toastNotificationHistory.value().Remove(winrt::to_hstring(id)); + } + + void FlutterLocalNotificationsPlugin::CancelAllNotifications() { + + if (!toastNotificationHistory.has_value()) { + toastNotificationHistory = winrt::Windows::UI::Notifications::ToastNotificationManager::History(); } + toastNotificationHistory.value().Clear(); } } // namespace diff --git a/flutter_local_notifications/windows/include/flutter_local_notifications/methods.h b/flutter_local_notifications/windows/include/flutter_local_notifications/methods.h index f6c4d288b..ed60d8039 100644 --- a/flutter_local_notifications/windows/include/flutter_local_notifications/methods.h +++ b/flutter_local_notifications/windows/include/flutter_local_notifications/methods.h @@ -10,4 +10,5 @@ namespace Method extern const std::string GET_NOTIFICATION_APP_LAUNCH_DETAILS; extern const std::string SHOW; extern const std::string CANCEL; + extern const std::string CANCEL_ALL; } \ No newline at end of file diff --git a/flutter_local_notifications/windows/methods.cpp b/flutter_local_notifications/windows/methods.cpp index 36820194b..b4a9f5064 100644 --- a/flutter_local_notifications/windows/methods.cpp +++ b/flutter_local_notifications/windows/methods.cpp @@ -6,3 +6,4 @@ const std::string Method::GET_NOTIFICATION_APP_LAUNCH_DETAILS = "getNotification const std::string Method::SHOW = "show"; const std::string Method::INITIALIZE = "initialize"; const std::string Method::CANCEL = "cancel"; +const std::string Method::CANCEL_ALL = "cancelAll"; diff --git a/flutter_local_notifications/windows/registration.cpp b/flutter_local_notifications/windows/registration.cpp new file mode 100644 index 000000000..bd704f190 --- /dev/null +++ b/flutter_local_notifications/windows/registration.cpp @@ -0,0 +1,236 @@ +// Huge credit to these StackOverflow answers: +// https://stackoverflow.com/questions/51947833/activation-from-c-winrt-dll +// https://stackoverflow.com/questions/67005337/how-works-notifications-on-windows-registry-no-shortlink + +#include "registration.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +/// +/// The GUID that identifies the notification activation callback. +/// 68d0c89d-760f-4f79-a067-ae8d4220ccc1 +/// +static constexpr winrt::guid CALLBACK_GUID{ + 0x68d0c89d, 0x760f, 0x4f79, {0xa0, 0x67, 0xae, 0x8d, 0x42, 0x20, 0xcc, 0xc1} +}; + +const std::string CALLBACK_GUID_STR = "{68d0c89d-760f-4f79-a067-ae8d4220ccc1}"; + +/// +/// This callback will be called when a notification sent by this plugin is clicked on. +/// +struct NotificationActivationCallback : winrt::implements +{ + HRESULT __stdcall Activate( + LPCWSTR app, + LPCWSTR args, + [[maybe_unused]] NOTIFICATION_USER_INPUT_DATA const* data, + [[maybe_unused]] ULONG count) noexcept final + { + try { + std::wcout << L"Example" << L" has been called back from a notification." << std::endl; + std::wcout << L"Value of the 'app' parameter is '" << app << L"'." << std::endl; + std::wcout << L"Value of the 'args' parameter is '" << args << L"'." << std::endl; + return S_OK; + } + catch (...) { + return winrt::to_hresult(); + } + } +}; + +/// +/// A class factory that creates an instance of NotificationActivationCallback. +/// +struct NotificationActivationCallbackFactory : winrt::implements +{ + HRESULT __stdcall CreateInstance( + IUnknown* outer, + GUID const& iid, + void** result) noexcept final + { + *result = nullptr; + + if (outer) { + return CLASS_E_NOAGGREGATION; + } + + return winrt::make()->QueryInterface(iid, result); + } + + HRESULT __stdcall LockServer(BOOL) noexcept final { + return S_OK; + } +}; + +struct RegistryHandle +{ + using type = HKEY; + + static void close(type value) noexcept { + WINRT_VERIFY_(ERROR_SUCCESS, RegCloseKey(value)); + } + + static constexpr type invalid() noexcept { + return nullptr; + } +}; + +/// +/// A handle to a registry key. +/// +using RegistryKey = winrt::handle_type; + +/// +/// Updates the Registry to enable notifications. +/// +/// The app user model ID of the app. Provided during initialization of the plugin. +/// The display name of the app. The name will be shown on the notification toasts. +/// An optional path to the icon of the app. The icon will be shown on the notification toasts +/// An optional string that specifies the background color of the icon, in the format of AARRGGBB. +void UpdateRegistry( + const std::string& aumid, + const std::string& appName, + const std::optional& iconPath, + const std::optional& iconBgColor +) { + std::cout << "Update registry" << std::endl; + + std::stringstream ss; + ss << "Software\\Microsoft\\Windows\\CurrentVersion\\PushNotifications\\Backup\\" << aumid; + const auto key_path = ss.str(); + RegistryKey key; + + // create registry key + winrt::check_win32(RegCreateKeyExA( + HKEY_CURRENT_USER, + key_path.c_str(), + 0, + nullptr, + 0, + KEY_WRITE, + nullptr, + key.put(), + nullptr)); + + // put the following key values under the key + // appType = app:desktop + // Setting = s:banner,s:toast,s:audio,c:toast,c:ringing + // wnsId = NonImmersivePackage + const std::string appType = "app:desktop"; + const std::string setting = "s:banner,s:toast,s:audio,c:toast,c:ringing"; + const std::string wnsId = "NonImmersivePackage"; + winrt::check_win32(RegSetValueExA( + key.get(), + "appType", + 0, + REG_SZ, + reinterpret_cast(appType.c_str()), + static_cast(appType.size() + 1 * sizeof(char)))); + + winrt::check_win32(RegSetValueExA( + key.get(), + "Setting", + 0, + REG_SZ, + reinterpret_cast(setting.c_str()), + static_cast(setting.size() + 1 * sizeof(char)))); + + winrt::check_win32(RegSetValueExA( + key.get(), + "wnsId", + 0, + REG_SZ, + reinterpret_cast(wnsId.c_str()), + static_cast(wnsId.size() + 1 * sizeof(char)))); + + ss.clear(); + ss.str(std::string()); + ss << "Software\\Classes\\AppUserModelId\\" << aumid; + const auto appInfoKeyPath = ss.str(); + std::cout << "aumid " << appInfoKeyPath << std::endl; + RegistryKey appInfoKey; + + // create registry key + winrt::check_win32(RegCreateKeyExA( + HKEY_CURRENT_USER, + appInfoKeyPath.c_str(), + 0, + nullptr, + 0, + KEY_WRITE, + nullptr, + appInfoKey.put(), + nullptr)); + + winrt::check_win32(RegSetValueExA( + appInfoKey.get(), + "DisplayName", + 0, + REG_SZ, + reinterpret_cast(appName.c_str()), + static_cast(appName.size() + 1 * sizeof(char)))); + + if (iconPath.has_value()) { + const auto v = iconPath.value(); + winrt::check_win32(RegSetValueExA( + appInfoKey.get(), + "IconUri", + 0, + REG_SZ, + reinterpret_cast(v.c_str()), + static_cast(v.size() + 1 * sizeof(char)))); + } + + if (iconBgColor.has_value()) { + const auto v = iconBgColor.value(); + winrt::check_win32(RegSetValueExA( + appInfoKey.get(), + "IconBackgroundColor", + 0, + REG_SZ, + reinterpret_cast(v.c_str()), + static_cast(v.size() + 1 * sizeof(char)))); + } + + winrt::check_win32(RegSetValueExA( + appInfoKey.get(), + "CustomActivator", + 0, + REG_SZ, + reinterpret_cast(CALLBACK_GUID_STR.c_str()), + static_cast(CALLBACK_GUID_STR.size() + 1 * sizeof(char)))); +} + +void RegisterCallback() { + DWORD registration{}; + + winrt::check_hresult(CoRegisterClassObject( + CALLBACK_GUID, + winrt::make().get(), + CLSCTX_LOCAL_SERVER, + REGCLS_SINGLEUSE, + ®istration)); +} + +void PluginRegistration::RegisterApp( + const std::string& aumid, + const std::string& appName, + const std::optional& iconPath, + const std::optional& iconBgColor +) { + std::cout << "register app" << std::endl; + UpdateRegistry(aumid, appName, iconPath, iconBgColor); + RegisterCallback(); +} diff --git a/flutter_local_notifications/windows/registration.h b/flutter_local_notifications/windows/registration.h new file mode 100644 index 000000000..c5a99b1e4 --- /dev/null +++ b/flutter_local_notifications/windows/registration.h @@ -0,0 +1,22 @@ +#ifndef PLUGIN_REGISTRATRION_H_ +#define PLUGIN_REGISTRATRION_H_ + +#include +#include + +namespace PluginRegistration { + /// + /// Registers the running app to the Windows Registry. + /// + /// The app user model ID that identifies the app. + /// The display name of the app. + /// An optional path to the icon of the app. + /// An optional background color of the icon, in AARRGGBB format. + void RegisterApp( + const std::string& aumid, + const std::string& appName, + const std::optional& iconPath, + const std::optional& iconBgColor); +} + +#endif // !PLUGIN_REGISTRATRION_H_ From b8beedd9ca4e58776ad9805f6df74fe27143fdee Mon Sep 17 00:00:00 2001 From: Kenneth Date: Sun, 13 Feb 2022 23:54:43 +0000 Subject: [PATCH 08/18] Tidy up code --- .../flutter_local_notifications_plugin.cpp | 21 +++++++------- .../windows/registration.cpp | 29 ++++++++++++++----- .../windows/registration.h | 3 ++ 3 files changed, 36 insertions(+), 17 deletions(-) diff --git a/flutter_local_notifications/windows/flutter_local_notifications_plugin.cpp b/flutter_local_notifications/windows/flutter_local_notifications_plugin.cpp index ee440d911..917ed5345 100644 --- a/flutter_local_notifications/windows/flutter_local_notifications_plugin.cpp +++ b/flutter_local_notifications/windows/flutter_local_notifications_plugin.cpp @@ -46,7 +46,10 @@ namespace { /// /// Initializes this plugin. /// - /// The display name of this app that should be shown in the notification toast. + /// The app user model ID that identifies the app. + /// The display name of the app. + /// An optional path to the icon of the app. + /// An optional background color of the icon, in AARRGGBB format. void Initialize( const std::string& appName, const std::string& aumid, @@ -102,9 +105,8 @@ namespace { void FlutterLocalNotificationsPlugin::HandleMethodCall( const flutter::MethodCall& method_call, - std::unique_ptr> result) { - std::cout << method_call.method_name() << std::endl; - + std::unique_ptr> result + ) { const auto& method_name = method_call.method_name(); if (method_name == Method::GET_NOTIFICATION_APP_LAUNCH_DETAILS) { result->Success(); @@ -163,8 +165,8 @@ namespace { const std::string& appName, const std::string& aumid, const std::optional& iconPath, - const std::optional& iconBgColor) { - std::cout << "Initialize" << std::endl; + const std::optional& iconBgColor + ) { PluginRegistration::RegisterApp(aumid, appName, iconPath, iconBgColor); toastNotifier = winrt::Windows::UI::Notifications::ToastNotificationManager::CreateToastNotifier(winrt::to_hstring(aumid)); } @@ -173,8 +175,8 @@ namespace { const int id, const std::optional& title, const std::optional& body, - const std::optional& payload) { - + const std::optional& payload + ) { // obtain a notification template with a title and a body const auto doc = winrt::Windows::UI::Notifications::ToastNotificationManager::GetTemplateContent(winrt::Windows::UI::Notifications::ToastTemplateType::ToastText02); // find all tags @@ -188,7 +190,7 @@ namespace { // change the text of the second , which will be the body nodes.Item(1).AppendChild(doc.CreateTextNode(winrt::to_hstring(body.value()))); } - + winrt::Windows::UI::Notifications::ToastNotification notif{ doc }; notif.Tag(winrt::to_hstring(id)); @@ -203,7 +205,6 @@ namespace { } void FlutterLocalNotificationsPlugin::CancelAllNotifications() { - if (!toastNotificationHistory.has_value()) { toastNotificationHistory = winrt::Windows::UI::Notifications::ToastNotificationManager::History(); } diff --git a/flutter_local_notifications/windows/registration.cpp b/flutter_local_notifications/windows/registration.cpp index bd704f190..9c9ee87f9 100644 --- a/flutter_local_notifications/windows/registration.cpp +++ b/flutter_local_notifications/windows/registration.cpp @@ -25,6 +25,9 @@ static constexpr winrt::guid CALLBACK_GUID{ 0x68d0c89d, 0x760f, 0x4f79, {0xa0, 0x67, 0xae, 0x8d, 0x42, 0x20, 0xcc, 0xc1} }; +/// +/// String representation of the callback GUID. +/// const std::string CALLBACK_GUID_STR = "{68d0c89d-760f-4f79-a067-ae8d4220ccc1}"; /// @@ -94,6 +97,11 @@ using RegistryKey = winrt::handle_type; /// /// Updates the Registry to enable notifications. +/// +/// Related resources: +///
    +///
  • https://docs.microsoft.com/en-us/windows/apps/design/shell/tiles-and-notifications/send-local-toast-other-apps
  • +///
///
/// The app user model ID of the app. Provided during initialization of the plugin. /// The display name of the app. The name will be shown on the notification toasts. @@ -105,17 +113,16 @@ void UpdateRegistry( const std::optional& iconPath, const std::optional& iconBgColor ) { - std::cout << "Update registry" << std::endl; - std::stringstream ss; ss << "Software\\Microsoft\\Windows\\CurrentVersion\\PushNotifications\\Backup\\" << aumid; - const auto key_path = ss.str(); + const auto notifSettingsKeyPath = ss.str(); RegistryKey key; // create registry key + // HKEY_CURRENT_USER\Software\Microsoft\\Windows\CurrentVersion\PushNotifications\Backup winrt::check_win32(RegCreateKeyExA( HKEY_CURRENT_USER, - key_path.c_str(), + notifSettingsKeyPath.c_str(), 0, nullptr, 0, @@ -125,9 +132,12 @@ void UpdateRegistry( nullptr)); // put the following key values under the key + // HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\PushNotifications\Backup\ + // // appType = app:desktop // Setting = s:banner,s:toast,s:audio,c:toast,c:ringing // wnsId = NonImmersivePackage + const std::string appType = "app:desktop"; const std::string setting = "s:banner,s:toast,s:audio,c:toast,c:ringing"; const std::string wnsId = "NonImmersivePackage"; @@ -138,7 +148,6 @@ void UpdateRegistry( REG_SZ, reinterpret_cast(appType.c_str()), static_cast(appType.size() + 1 * sizeof(char)))); - winrt::check_win32(RegSetValueExA( key.get(), "Setting", @@ -146,7 +155,6 @@ void UpdateRegistry( REG_SZ, reinterpret_cast(setting.c_str()), static_cast(setting.size() + 1 * sizeof(char)))); - winrt::check_win32(RegSetValueExA( key.get(), "wnsId", @@ -155,14 +163,16 @@ void UpdateRegistry( reinterpret_cast(wnsId.c_str()), static_cast(wnsId.size() + 1 * sizeof(char)))); + // now, we register app info to the Registry. + ss.clear(); ss.str(std::string()); ss << "Software\\Classes\\AppUserModelId\\" << aumid; const auto appInfoKeyPath = ss.str(); - std::cout << "aumid " << appInfoKeyPath << std::endl; RegistryKey appInfoKey; // create registry key + // HKEY_CURRENT_USER\Software\Classes\AppUserModelId\ winrt::check_win32(RegCreateKeyExA( HKEY_CURRENT_USER, appInfoKeyPath.c_str(), @@ -204,6 +214,7 @@ void UpdateRegistry( static_cast(v.size() + 1 * sizeof(char)))); } + // register the guid of the notification activation callback winrt::check_win32(RegSetValueExA( appInfoKey.get(), "CustomActivator", @@ -213,6 +224,10 @@ void UpdateRegistry( static_cast(CALLBACK_GUID_STR.size() + 1 * sizeof(char)))); } +/// +/// Register the notificatio activation callback factory +/// and the guid of the callback. +/// void RegisterCallback() { DWORD registration{}; diff --git a/flutter_local_notifications/windows/registration.h b/flutter_local_notifications/windows/registration.h index c5a99b1e4..c9127e7c8 100644 --- a/flutter_local_notifications/windows/registration.h +++ b/flutter_local_notifications/windows/registration.h @@ -4,6 +4,9 @@ #include #include +/// +/// Contains logic for handling Registry values. +/// namespace PluginRegistration { /// /// Registers the running app to the Windows Registry. From 34e187cfca039604ca66c68058846f277c4375c6 Mon Sep 17 00:00:00 2001 From: Kenneth Date: Mon, 14 Feb 2022 00:06:25 +0000 Subject: [PATCH 09/18] Remove dependency overrides --- flutter_local_notifications/example/pubspec.yaml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/flutter_local_notifications/example/pubspec.yaml b/flutter_local_notifications/example/pubspec.yaml index 07f4f5ad9..562f115b3 100644 --- a/flutter_local_notifications/example/pubspec.yaml +++ b/flutter_local_notifications/example/pubspec.yaml @@ -26,12 +26,6 @@ dev_dependencies: sdk: flutter msix: ^2.8.17 -dependency_overrides: - flutter_local_notifications_platform_interface: - path: ../../flutter_local_notifications_platform_interface/ - flutter_local_notifications_linux: - path: ../../flutter_local_notifications_linux/ - flutter: uses-material-design: true assets: From 5400c0bcaccc00b7f01caf77d1a0e45fa4c23721 Mon Sep 17 00:00:00 2001 From: Kenneth Date: Mon, 14 Feb 2022 11:39:48 +0000 Subject: [PATCH 10/18] Implement cancel/cancelAll --- .../platform_flutter_local_notifications.dart | 18 +++++++- .../flutter_local_notifications_plugin.cpp | 42 ++++++++++++++----- 2 files changed, 47 insertions(+), 13 deletions(-) diff --git a/flutter_local_notifications/lib/src/platform_flutter_local_notifications.dart b/flutter_local_notifications/lib/src/platform_flutter_local_notifications.dart index b076f5336..be526da02 100644 --- a/flutter_local_notifications/lib/src/platform_flutter_local_notifications.dart +++ b/flutter_local_notifications/lib/src/platform_flutter_local_notifications.dart @@ -871,13 +871,27 @@ class WindowsFlutterLocalNotificationsPlugin _channel.invokeMethod('initialize', settings.toMap()); @override - Future show(int id, String? title, String? body, {String? payload}) => - _channel.invokeMethod('show', { + Future show( + int id, + String? title, + String? body, { + String? payload, + String? group, + }) => + _channel.invokeMethod('show', { 'id': id, 'title': title, 'body': body, + 'group': group, 'payload': payload ?? '', }); + + @override + Future cancel(int id, {String? group}) => + _channel.invokeMethod('cancel', { + 'id': id, + 'group': group, + }); } /// Checks [backgroundHandler] method, if not `null`, for eligibility to diff --git a/flutter_local_notifications/windows/flutter_local_notifications_plugin.cpp b/flutter_local_notifications/windows/flutter_local_notifications_plugin.cpp index 917ed5345..012a443d6 100644 --- a/flutter_local_notifications/windows/flutter_local_notifications_plugin.cpp +++ b/flutter_local_notifications/windows/flutter_local_notifications_plugin.cpp @@ -35,6 +35,7 @@ namespace { virtual ~FlutterLocalNotificationsPlugin(); private: + std::wstring _aumid; std::optional toastNotifier; std::optional toastNotificationHistory; @@ -67,13 +68,15 @@ namespace { const int id, const std::optional& title, const std::optional& body, - const std::optional& payload); + const std::optional& payload, + const std::optional& group); /// /// Dismisses the notification that has the given ID. /// /// The ID of the notification to be dismissed. - void CancelNotification(const int id); + /// The group the notification is in. Default is the aumid of this app. + void CancelNotification(const int id, const std::optional& group); /// /// Dismisses all currently active notifications. @@ -133,8 +136,9 @@ namespace { const auto title = Utils::GetMapValue("title", args); const auto body = Utils::GetMapValue("body", args); const auto payload = Utils::GetMapValue("payload", args); + const auto group = Utils::GetMapValue("group", args); - ShowNotification(id, title, body, payload); + ShowNotification(id, title, body, payload, group); result->Success(); } else { @@ -142,10 +146,12 @@ namespace { } } else if (method_name == Method::CANCEL && toastNotifier.has_value()) { - const auto args = method_call.arguments(); - if (std::holds_alternative(*args)) { - const auto id = std::get(*args); - CancelNotification(id); + const auto args = std::get_if(method_call.arguments()); + if (args != nullptr) { + const auto id = Utils::GetMapValue("id", args).value(); + const auto group = Utils::GetMapValue("group", args); + + CancelNotification(id, group); result->Success(); } else { @@ -167,6 +173,7 @@ namespace { const std::optional& iconPath, const std::optional& iconBgColor ) { + _aumid = winrt::to_hstring(aumid); PluginRegistration::RegisterApp(aumid, appName, iconPath, iconBgColor); toastNotifier = winrt::Windows::UI::Notifications::ToastNotificationManager::CreateToastNotifier(winrt::to_hstring(aumid)); } @@ -175,7 +182,8 @@ namespace { const int id, const std::optional& title, const std::optional& body, - const std::optional& payload + const std::optional& payload, + const std::optional& group ) { // obtain a notification template with a title and a body const auto doc = winrt::Windows::UI::Notifications::ToastNotificationManager::GetTemplateContent(winrt::Windows::UI::Notifications::ToastTemplateType::ToastText02); @@ -193,22 +201,34 @@ namespace { winrt::Windows::UI::Notifications::ToastNotification notif{ doc }; notif.Tag(winrt::to_hstring(id)); + if (group.has_value()) { + notif.Group(winrt::to_hstring(group.value())); + } + else { + notif.Group(_aumid); + } toastNotifier.value().Show(notif); } - void FlutterLocalNotificationsPlugin::CancelNotification(const int id) { + void FlutterLocalNotificationsPlugin::CancelNotification(const int id, const std::optional& group) { if (!toastNotificationHistory.has_value()) { toastNotificationHistory = winrt::Windows::UI::Notifications::ToastNotificationManager::History(); } - toastNotificationHistory.value().Remove(winrt::to_hstring(id)); + + if (group.has_value()) { + toastNotificationHistory.value().Remove(winrt::to_hstring(id), winrt::to_hstring(group.value()), _aumid); + } + else { + toastNotificationHistory.value().Remove(winrt::to_hstring(id), _aumid, _aumid); + } } void FlutterLocalNotificationsPlugin::CancelAllNotifications() { if (!toastNotificationHistory.has_value()) { toastNotificationHistory = winrt::Windows::UI::Notifications::ToastNotificationManager::History(); } - toastNotificationHistory.value().Clear(); + toastNotificationHistory.value().Clear(_aumid); } } // namespace From 0c21c9d57d4f4966a8eaba71943f49d1adebed1d Mon Sep 17 00:00:00 2001 From: Kenneth Date: Sat, 5 Mar 2022 14:20:34 +0000 Subject: [PATCH 11/18] WIP: Impl onSelectNotification --- .../platform_flutter_local_notifications.dart | 19 +- .../flutter_local_notifications_plugin.cpp | 316 ++++++++---------- .../flutter_local_notifications_plugin.h | 77 +++++ .../flutter_local_notifications/methods.h | 3 +- .../windows/methods.cpp | 1 + .../windows/registration.cpp | 35 +- .../windows/registration.h | 6 +- 7 files changed, 263 insertions(+), 194 deletions(-) diff --git a/flutter_local_notifications/lib/src/platform_flutter_local_notifications.dart b/flutter_local_notifications/lib/src/platform_flutter_local_notifications.dart index be526da02..a73eb0b1e 100644 --- a/flutter_local_notifications/lib/src/platform_flutter_local_notifications.dart +++ b/flutter_local_notifications/lib/src/platform_flutter_local_notifications.dart @@ -855,6 +855,8 @@ class MacOSFlutterLocalNotificationsPlugin /// Windows implementation of the flutter_local_notifications plugin. class WindowsFlutterLocalNotificationsPlugin extends MethodChannelFlutterLocalNotificationsPlugin { + SelectNotificationCallback? onSelectNotification; + /// Initializes the plugin. /// /// Call this method on application before using the plugin further. @@ -867,8 +869,12 @@ class WindowsFlutterLocalNotificationsPlugin Future initialize( WindowsInitializationSettings settings, { SelectNotificationCallback? onSelectNotification, - }) => - _channel.invokeMethod('initialize', settings.toMap()); + }) { + this.onSelectNotification = onSelectNotification; + _channel.setMethodCallHandler(_onMethodCallFromNative); + + return _channel.invokeMethod('initialize', settings.toMap()); + } @override Future show( @@ -892,6 +898,15 @@ class WindowsFlutterLocalNotificationsPlugin 'id': id, 'group': group, }); + + Future _onMethodCallFromNative(MethodCall call) async { + print('call $call'); + switch (call.method) { + case 'selectNotification': + print('notification selected'); + break; + } + } } /// Checks [backgroundHandler] method, if not `null`, for eligibility to diff --git a/flutter_local_notifications/windows/flutter_local_notifications_plugin.cpp b/flutter_local_notifications/windows/flutter_local_notifications_plugin.cpp index 012a443d6..51456dfc4 100644 --- a/flutter_local_notifications/windows/flutter_local_notifications_plugin.cpp +++ b/flutter_local_notifications/windows/flutter_local_notifications_plugin.cpp @@ -24,213 +24,159 @@ using namespace winrt::Windows::Data::Xml::Dom; -namespace { - - class FlutterLocalNotificationsPlugin : public flutter::Plugin { - public: - static void RegisterWithRegistrar(flutter::PluginRegistrarWindows* registrar); - - FlutterLocalNotificationsPlugin(); - - virtual ~FlutterLocalNotificationsPlugin(); - - private: - std::wstring _aumid; - std::optional toastNotifier; - std::optional toastNotificationHistory; - - // Called when a method is called on this plugin's channel from Dart. - void HandleMethodCall( - const flutter::MethodCall& method_call, - std::unique_ptr> result); - - /// - /// Initializes this plugin. - /// - /// The app user model ID that identifies the app. - /// The display name of the app. - /// An optional path to the icon of the app. - /// An optional background color of the icon, in AARRGGBB format. - void Initialize( - const std::string& appName, - const std::string& aumid, - const std::optional& iconPath, - const std::optional& iconBgColor); - - /// - /// Displays a single notification toast. - /// - /// A unique ID that identifies this notification. It can be used to cancel/dismiss the notification. - /// An optional title of the notification. - /// An optional body of the notification. - /// - void ShowNotification( - const int id, - const std::optional& title, - const std::optional& body, - const std::optional& payload, - const std::optional& group); - - /// - /// Dismisses the notification that has the given ID. - /// - /// The ID of the notification to be dismissed. - /// The group the notification is in. Default is the aumid of this app. - void CancelNotification(const int id, const std::optional& group); - - /// - /// Dismisses all currently active notifications. - /// - void CancelAllNotifications(); - }; - - // static - void FlutterLocalNotificationsPlugin::RegisterWithRegistrar( - flutter::PluginRegistrarWindows* registrar) { - auto channel = - std::make_unique>( - registrar->messenger(), "dexterous.com/flutter/local_notifications", - &flutter::StandardMethodCodec::GetInstance()); - - auto plugin = std::make_unique(); - - channel->SetMethodCallHandler( - [plugin_pointer = plugin.get()](const auto& call, auto result) { - plugin_pointer->HandleMethodCall(call, std::move(result)); - }); - - registrar->AddPlugin(std::move(plugin)); - } +// static +void FlutterLocalNotificationsPlugin::RegisterWithRegistrar( + flutter::PluginRegistrarWindows* registrar) { + auto channel = + std::make_unique( + registrar->messenger(), "dexterous.com/flutter/local_notifications", + &flutter::StandardMethodCodec::GetInstance()); + + auto plugin = std::make_unique(*channel); + + channel->SetMethodCallHandler( + [plugin_pointer = plugin.get()](const auto& call, auto result) { + plugin_pointer->HandleMethodCall(call, std::move(result)); + }); + + registrar->AddPlugin(std::move(plugin)); +} - FlutterLocalNotificationsPlugin::FlutterLocalNotificationsPlugin() {} +FlutterLocalNotificationsPlugin::FlutterLocalNotificationsPlugin(PluginMethodChannel& channel) : + channel(channel) {} - FlutterLocalNotificationsPlugin::~FlutterLocalNotificationsPlugin() {} +FlutterLocalNotificationsPlugin::~FlutterLocalNotificationsPlugin() {} - void FlutterLocalNotificationsPlugin::HandleMethodCall( - const flutter::MethodCall& method_call, - std::unique_ptr> result - ) { - const auto& method_name = method_call.method_name(); - if (method_name == Method::GET_NOTIFICATION_APP_LAUNCH_DETAILS) { - result->Success(); +PluginMethodChannel& FlutterLocalNotificationsPlugin::GetPluginMethodChannel() { + return channel; +} + +void FlutterLocalNotificationsPlugin::HandleMethodCall( + const flutter::MethodCall& method_call, + std::unique_ptr> result +) { + const auto& method_name = method_call.method_name(); + if (method_name == Method::GET_NOTIFICATION_APP_LAUNCH_DETAILS) { + result->Success(); + } + else if (method_name == Method::INITIALIZE) { + const auto args = std::get_if(method_call.arguments()); + if (args != nullptr) { + const auto appName = Utils::GetMapValue("appName", args).value(); + const auto aumid = Utils::GetMapValue("aumid", args).value(); + const auto iconPath = Utils::GetMapValue("iconPath", args); + const auto iconBgColor = Utils::GetMapValue("iconBgColor", args); + + Initialize(appName, aumid, iconPath, iconBgColor); + result->Success(true); } - else if (method_name == Method::INITIALIZE) { - const auto args = std::get_if(method_call.arguments()); - if (args != nullptr) { - const auto appName = Utils::GetMapValue("appName", args).value(); - const auto aumid = Utils::GetMapValue("aumid", args).value(); - const auto iconPath = Utils::GetMapValue("iconPath", args); - const auto iconBgColor = Utils::GetMapValue("iconBgColor", args); - - Initialize(appName, aumid, iconPath, iconBgColor); - result->Success(true); - } - else { - result->Error("INTERNAL", "flutter_local_notifications encountered an internal error."); - } + else { + result->Error("INTERNAL", "flutter_local_notifications encountered an internal error."); } - else if (method_name == Method::SHOW) { - const auto args = std::get_if(method_call.arguments()); - if (args != nullptr && toastNotifier.has_value()) { - const auto id = Utils::GetMapValue("id", args).value(); - const auto title = Utils::GetMapValue("title", args); - const auto body = Utils::GetMapValue("body", args); - const auto payload = Utils::GetMapValue("payload", args); - const auto group = Utils::GetMapValue("group", args); - - ShowNotification(id, title, body, payload, group); - result->Success(); - } - else { - result->Error("INTERNAL", "flutter_local_notifications encountered an internal error."); - } + } + else if (method_name == Method::SHOW) { + channel.InvokeMethod("test", nullptr, nullptr); + + const auto args = std::get_if(method_call.arguments()); + if (args != nullptr && toastNotifier.has_value()) { + const auto id = Utils::GetMapValue("id", args).value(); + const auto title = Utils::GetMapValue("title", args); + const auto body = Utils::GetMapValue("body", args); + const auto payload = Utils::GetMapValue("payload", args); + const auto group = Utils::GetMapValue("group", args); + + ShowNotification(id, title, body, payload, group); + result->Success(); } - else if (method_name == Method::CANCEL && toastNotifier.has_value()) { - const auto args = std::get_if(method_call.arguments()); - if (args != nullptr) { - const auto id = Utils::GetMapValue("id", args).value(); - const auto group = Utils::GetMapValue("group", args); - - CancelNotification(id, group); - result->Success(); - } - else { - result->Error("INTERNAL", "flutter_local_notifications encountered an internal error."); - } + else { + result->Error("INTERNAL", "flutter_local_notifications encountered an internal error."); } - else if (method_name == Method::CANCEL_ALL && toastNotifier.has_value()) { - CancelAllNotifications(); + } + else if (method_name == Method::CANCEL && toastNotifier.has_value()) { + const auto args = std::get_if(method_call.arguments()); + if (args != nullptr) { + const auto id = Utils::GetMapValue("id", args).value(); + const auto group = Utils::GetMapValue("group", args); + + CancelNotification(id, group); result->Success(); } else { - result->NotImplemented(); + result->Error("INTERNAL", "flutter_local_notifications encountered an internal error."); } } - - void FlutterLocalNotificationsPlugin::Initialize( - const std::string& appName, - const std::string& aumid, - const std::optional& iconPath, - const std::optional& iconBgColor - ) { - _aumid = winrt::to_hstring(aumid); - PluginRegistration::RegisterApp(aumid, appName, iconPath, iconBgColor); - toastNotifier = winrt::Windows::UI::Notifications::ToastNotificationManager::CreateToastNotifier(winrt::to_hstring(aumid)); + else if (method_name == Method::CANCEL_ALL && toastNotifier.has_value()) { + CancelAllNotifications(); + result->Success(); + } + else { + result->NotImplemented(); } +} - void FlutterLocalNotificationsPlugin::ShowNotification( - const int id, - const std::optional& title, - const std::optional& body, - const std::optional& payload, - const std::optional& group - ) { - // obtain a notification template with a title and a body - const auto doc = winrt::Windows::UI::Notifications::ToastNotificationManager::GetTemplateContent(winrt::Windows::UI::Notifications::ToastTemplateType::ToastText02); - // find all tags - const auto nodes = doc.GetElementsByTagName(L"text"); - - if (title.has_value()) { - // change the text of the first , which will be the title - nodes.Item(0).AppendChild(doc.CreateTextNode(winrt::to_hstring(title.value()))); - } - if (body.has_value()) { - // change the text of the second , which will be the body - nodes.Item(1).AppendChild(doc.CreateTextNode(winrt::to_hstring(body.value()))); - } +void FlutterLocalNotificationsPlugin::Initialize( + const std::string& appName, + const std::string& aumid, + const std::optional& iconPath, + const std::optional& iconBgColor +) { + _aumid = winrt::to_hstring(aumid); + PluginRegistration::RegisterApp(aumid, appName, iconPath, iconBgColor, this); + toastNotifier = winrt::Windows::UI::Notifications::ToastNotificationManager::CreateToastNotifier(winrt::to_hstring(aumid)); +} - winrt::Windows::UI::Notifications::ToastNotification notif{ doc }; - notif.Tag(winrt::to_hstring(id)); - if (group.has_value()) { - notif.Group(winrt::to_hstring(group.value())); - } - else { - notif.Group(_aumid); - } +void FlutterLocalNotificationsPlugin::ShowNotification( + const int id, + const std::optional& title, + const std::optional& body, + const std::optional& payload, + const std::optional& group +) { + // obtain a notification template with a title and a body + const auto doc = winrt::Windows::UI::Notifications::ToastNotificationManager::GetTemplateContent(winrt::Windows::UI::Notifications::ToastTemplateType::ToastText02); + // find all tags + const auto nodes = doc.GetElementsByTagName(L"text"); + + if (title.has_value()) { + // change the text of the first , which will be the title + nodes.Item(0).AppendChild(doc.CreateTextNode(winrt::to_hstring(title.value()))); + } + if (body.has_value()) { + // change the text of the second , which will be the body + nodes.Item(1).AppendChild(doc.CreateTextNode(winrt::to_hstring(body.value()))); + } - toastNotifier.value().Show(notif); + winrt::Windows::UI::Notifications::ToastNotification notif{ doc }; + notif.Tag(winrt::to_hstring(id)); + if (group.has_value()) { + notif.Group(winrt::to_hstring(group.value())); + } + else { + notif.Group(_aumid); } - void FlutterLocalNotificationsPlugin::CancelNotification(const int id, const std::optional& group) { - if (!toastNotificationHistory.has_value()) { - toastNotificationHistory = winrt::Windows::UI::Notifications::ToastNotificationManager::History(); - } + toastNotifier.value().Show(notif); +} - if (group.has_value()) { - toastNotificationHistory.value().Remove(winrt::to_hstring(id), winrt::to_hstring(group.value()), _aumid); - } - else { - toastNotificationHistory.value().Remove(winrt::to_hstring(id), _aumid, _aumid); - } +void FlutterLocalNotificationsPlugin::CancelNotification(const int id, const std::optional& group) { + if (!toastNotificationHistory.has_value()) { + toastNotificationHistory = winrt::Windows::UI::Notifications::ToastNotificationManager::History(); } - void FlutterLocalNotificationsPlugin::CancelAllNotifications() { - if (!toastNotificationHistory.has_value()) { - toastNotificationHistory = winrt::Windows::UI::Notifications::ToastNotificationManager::History(); - } - toastNotificationHistory.value().Clear(_aumid); + if (group.has_value()) { + toastNotificationHistory.value().Remove(winrt::to_hstring(id), winrt::to_hstring(group.value()), _aumid); + } + else { + toastNotificationHistory.value().Remove(winrt::to_hstring(id), _aumid, _aumid); } -} // namespace +} + +void FlutterLocalNotificationsPlugin::CancelAllNotifications() { + if (!toastNotificationHistory.has_value()) { + toastNotificationHistory = winrt::Windows::UI::Notifications::ToastNotificationManager::History(); + } + toastNotificationHistory.value().Clear(_aumid); +} void FlutterLocalNotificationsPluginRegisterWithRegistrar( FlutterDesktopPluginRegistrarRef registrar) { diff --git a/flutter_local_notifications/windows/include/flutter_local_notifications/flutter_local_notifications_plugin.h b/flutter_local_notifications/windows/include/flutter_local_notifications/flutter_local_notifications_plugin.h index dda06d016..37be0aac4 100644 --- a/flutter_local_notifications/windows/include/flutter_local_notifications/flutter_local_notifications_plugin.h +++ b/flutter_local_notifications/windows/include/flutter_local_notifications/flutter_local_notifications_plugin.h @@ -1,7 +1,18 @@ #ifndef FLUTTER_PLUGIN_FLUTTER_LOCAL_NOTIFICATIONS_PLUGIN_H_ #define FLUTTER_PLUGIN_FLUTTER_LOCAL_NOTIFICATIONS_PLUGIN_H_ +#pragma once +#include +#include + #include +#include +#include +#include +#include +#include + +#include #ifdef FLUTTER_PLUGIN_IMPL #define FLUTTER_PLUGIN_EXPORT __declspec(dllexport) @@ -20,4 +31,70 @@ FLUTTER_PLUGIN_EXPORT void FlutterLocalNotificationsPluginRegisterWithRegistrar( } // extern "C" #endif +/// +/// Defines the type of the method channel used by this plugin. +/// +typedef flutter::MethodChannel PluginMethodChannel; + +class FlutterLocalNotificationsPlugin : public flutter::Plugin { +public: + static void RegisterWithRegistrar(flutter::PluginRegistrarWindows* registrar); + + PluginMethodChannel& GetPluginMethodChannel(); + + FlutterLocalNotificationsPlugin(PluginMethodChannel& channel); + + virtual ~FlutterLocalNotificationsPlugin(); + +private: + std::wstring _aumid; + std::optional toastNotifier; + std::optional toastNotificationHistory; + PluginMethodChannel& channel; + + // Called when a method is called on this plugin's channel from Dart. + void HandleMethodCall( + const flutter::MethodCall& method_call, + std::unique_ptr> result); + + /// + /// Initializes this plugin. + /// + /// The app user model ID that identifies the app. + /// The display name of the app. + /// An optional path to the icon of the app. + /// An optional background color of the icon, in AARRGGBB format. + void Initialize( + const std::string& appName, + const std::string& aumid, + const std::optional& iconPath, + const std::optional& iconBgColor); + + /// + /// Displays a single notification toast. + /// + /// A unique ID that identifies this notification. It can be used to cancel/dismiss the notification. + /// An optional title of the notification. + /// An optional body of the notification. + /// + void ShowNotification( + const int id, + const std::optional& title, + const std::optional& body, + const std::optional& payload, + const std::optional& group); + + /// + /// Dismisses the notification that has the given ID. + /// + /// The ID of the notification to be dismissed. + /// The group the notification is in. Default is the aumid of this app. + void CancelNotification(const int id, const std::optional& group); + + /// + /// Dismisses all currently active notifications. + /// + void CancelAllNotifications(); +}; + #endif // FLUTTER_PLUGIN_FLUTTER_LOCAL_NOTIFICATIONS_PLUGIN_H_ diff --git a/flutter_local_notifications/windows/include/flutter_local_notifications/methods.h b/flutter_local_notifications/windows/include/flutter_local_notifications/methods.h index ed60d8039..0286ff96d 100644 --- a/flutter_local_notifications/windows/include/flutter_local_notifications/methods.h +++ b/flutter_local_notifications/windows/include/flutter_local_notifications/methods.h @@ -11,4 +11,5 @@ namespace Method extern const std::string SHOW; extern const std::string CANCEL; extern const std::string CANCEL_ALL; -} \ No newline at end of file + extern const std::string SELECT_NOTIFICATION; +} diff --git a/flutter_local_notifications/windows/methods.cpp b/flutter_local_notifications/windows/methods.cpp index b4a9f5064..295d46885 100644 --- a/flutter_local_notifications/windows/methods.cpp +++ b/flutter_local_notifications/windows/methods.cpp @@ -7,3 +7,4 @@ const std::string Method::SHOW = "show"; const std::string Method::INITIALIZE = "initialize"; const std::string Method::CANCEL = "cancel"; const std::string Method::CANCEL_ALL = "cancelAll"; +const std::string Method::SELECT_NOTIFICATION = "selectNotification"; diff --git a/flutter_local_notifications/windows/registration.cpp b/flutter_local_notifications/windows/registration.cpp index 9c9ee87f9..654380778 100644 --- a/flutter_local_notifications/windows/registration.cpp +++ b/flutter_local_notifications/windows/registration.cpp @@ -3,6 +3,8 @@ // https://stackoverflow.com/questions/67005337/how-works-notifications-on-windows-registry-no-shortlink #include "registration.h" +#include "include/flutter_local_notifications/flutter_local_notifications_plugin.h" +#include "include/flutter_local_notifications/methods.h" #include #include @@ -35,6 +37,8 @@ const std::string CALLBACK_GUID_STR = "{68d0c89d-760f-4f79-a067-ae8d4220ccc1}"; /// struct NotificationActivationCallback : winrt::implements { + FlutterLocalNotificationsPlugin* plugin = nullptr; + HRESULT __stdcall Activate( LPCWSTR app, LPCWSTR args, @@ -45,6 +49,10 @@ struct NotificationActivationCallback : winrt::implementsGetPluginMethodChannel().InvokeMethod(Method::SELECT_NOTIFICATION, nullptr, nullptr); + } return S_OK; } catch (...) { @@ -58,18 +66,27 @@ struct NotificationActivationCallback : winrt::implements struct NotificationActivationCallbackFactory : winrt::implements { + FlutterLocalNotificationsPlugin* plugin; + HRESULT __stdcall CreateInstance( IUnknown* outer, GUID const& iid, void** result) noexcept final { + std::cout << "CreateInstance" << std::endl; + *result = nullptr; if (outer) { return CLASS_E_NOAGGREGATION; } - return winrt::make()->QueryInterface(iid, result); + const auto cb = winrt::make_self(); + cb.get()->plugin = plugin; + + std::cout << plugin << std::endl; + + return cb->QueryInterface(iid, result); } HRESULT __stdcall LockServer(BOOL) noexcept final { @@ -228,12 +245,19 @@ void UpdateRegistry( /// Register the notificatio activation callback factory /// and the guid of the callback. /// -void RegisterCallback() { +void RegisterCallback(FlutterLocalNotificationsPlugin* plugin) { DWORD registration{}; + const auto factory_ref = winrt::make_self(); + const auto factory = factory_ref.get(); + factory->plugin = plugin; + + std::cout << factory->plugin << std::endl; + std::cout << plugin << std::endl; + winrt::check_hresult(CoRegisterClassObject( CALLBACK_GUID, - winrt::make().get(), + factory, CLSCTX_LOCAL_SERVER, REGCLS_SINGLEUSE, ®istration)); @@ -243,9 +267,10 @@ void PluginRegistration::RegisterApp( const std::string& aumid, const std::string& appName, const std::optional& iconPath, - const std::optional& iconBgColor + const std::optional& iconBgColor, + FlutterLocalNotificationsPlugin* plugin ) { std::cout << "register app" << std::endl; UpdateRegistry(aumid, appName, iconPath, iconBgColor); - RegisterCallback(); + RegisterCallback(plugin); } diff --git a/flutter_local_notifications/windows/registration.h b/flutter_local_notifications/windows/registration.h index c9127e7c8..cebbc9930 100644 --- a/flutter_local_notifications/windows/registration.h +++ b/flutter_local_notifications/windows/registration.h @@ -1,6 +1,8 @@ #ifndef PLUGIN_REGISTRATRION_H_ #define PLUGIN_REGISTRATRION_H_ +#include "include/flutter_local_notifications/flutter_local_notifications_plugin.h" + #include #include @@ -15,11 +17,13 @@ namespace PluginRegistration { /// The display name of the app. /// An optional path to the icon of the app. /// An optional background color of the icon, in AARRGGBB format. + /// The instance of the plugin calling this function void RegisterApp( const std::string& aumid, const std::string& appName, const std::optional& iconPath, - const std::optional& iconBgColor); + const std::optional& iconBgColor, + FlutterLocalNotificationsPlugin* plugin); } #endif // !PLUGIN_REGISTRATRION_H_ From d2f66e925bd83c927aebc52b01d4598cb4b34d32 Mon Sep 17 00:00:00 2001 From: Kenneth Date: Sat, 5 Mar 2022 23:57:39 +0000 Subject: [PATCH 12/18] Fix read access violation --- .../flutter_local_notifications_plugin.cpp | 319 +++++++++++------- .../flutter_local_notifications_plugin.h | 61 ---- .../windows/registration.cpp | 22 +- .../windows/registration.h | 3 +- 4 files changed, 197 insertions(+), 208 deletions(-) diff --git a/flutter_local_notifications/windows/flutter_local_notifications_plugin.cpp b/flutter_local_notifications/windows/flutter_local_notifications_plugin.cpp index 51456dfc4..f9acbd40a 100644 --- a/flutter_local_notifications/windows/flutter_local_notifications_plugin.cpp +++ b/flutter_local_notifications/windows/flutter_local_notifications_plugin.cpp @@ -23,159 +23,216 @@ #include using namespace winrt::Windows::Data::Xml::Dom; +namespace { + + class FlutterLocalNotificationsPlugin : public flutter::Plugin { + public: + static void RegisterWithRegistrar(flutter::PluginRegistrarWindows* registrar); + + FlutterLocalNotificationsPlugin(std::shared_ptr channel); + + virtual ~FlutterLocalNotificationsPlugin(); + + private: + std::wstring _aumid; + std::optional toastNotifier; + std::optional toastNotificationHistory; + std::shared_ptr channel; + + // Called when a method is called on this plugin's channel from Dart. + void HandleMethodCall( + const flutter::MethodCall& method_call, + std::unique_ptr> result); + + /// + /// Initializes this plugin. + /// + /// The app user model ID that identifies the app. + /// The display name of the app. + /// An optional path to the icon of the app. + /// An optional background color of the icon, in AARRGGBB format. + void Initialize( + const std::string& appName, + const std::string& aumid, + const std::optional& iconPath, + const std::optional& iconBgColor); + + /// + /// Displays a single notification toast. + /// + /// A unique ID that identifies this notification. It can be used to cancel/dismiss the notification. + /// An optional title of the notification. + /// An optional body of the notification. + /// + void ShowNotification( + const int id, + const std::optional& title, + const std::optional& body, + const std::optional& payload, + const std::optional& group); + + /// + /// Dismisses the notification that has the given ID. + /// + /// The ID of the notification to be dismissed. + /// The group the notification is in. Default is the aumid of this app. + void CancelNotification(const int id, const std::optional& group); + + /// + /// Dismisses all currently active notifications. + /// + void CancelAllNotifications(); + }; + + // static + void FlutterLocalNotificationsPlugin::RegisterWithRegistrar( + flutter::PluginRegistrarWindows* registrar) { + auto channel = + std::make_shared( + registrar->messenger(), "dexterous.com/flutter/local_notifications", + &flutter::StandardMethodCodec::GetInstance()); + + auto plugin = std::make_unique(channel); + + channel->SetMethodCallHandler( + [plugin_pointer = plugin.get()](const auto& call, auto result) { + plugin_pointer->HandleMethodCall(call, std::move(result)); + }); + + registrar->AddPlugin(std::move(plugin)); + } -// static -void FlutterLocalNotificationsPlugin::RegisterWithRegistrar( - flutter::PluginRegistrarWindows* registrar) { - auto channel = - std::make_unique( - registrar->messenger(), "dexterous.com/flutter/local_notifications", - &flutter::StandardMethodCodec::GetInstance()); - - auto plugin = std::make_unique(*channel); - - channel->SetMethodCallHandler( - [plugin_pointer = plugin.get()](const auto& call, auto result) { - plugin_pointer->HandleMethodCall(call, std::move(result)); - }); - - registrar->AddPlugin(std::move(plugin)); -} - -FlutterLocalNotificationsPlugin::FlutterLocalNotificationsPlugin(PluginMethodChannel& channel) : - channel(channel) {} - -FlutterLocalNotificationsPlugin::~FlutterLocalNotificationsPlugin() {} + FlutterLocalNotificationsPlugin::FlutterLocalNotificationsPlugin(std::shared_ptr channel) : + channel(channel) {} -PluginMethodChannel& FlutterLocalNotificationsPlugin::GetPluginMethodChannel() { - return channel; -} + FlutterLocalNotificationsPlugin::~FlutterLocalNotificationsPlugin() {} -void FlutterLocalNotificationsPlugin::HandleMethodCall( - const flutter::MethodCall& method_call, - std::unique_ptr> result -) { - const auto& method_name = method_call.method_name(); - if (method_name == Method::GET_NOTIFICATION_APP_LAUNCH_DETAILS) { - result->Success(); - } - else if (method_name == Method::INITIALIZE) { - const auto args = std::get_if(method_call.arguments()); - if (args != nullptr) { - const auto appName = Utils::GetMapValue("appName", args).value(); - const auto aumid = Utils::GetMapValue("aumid", args).value(); - const auto iconPath = Utils::GetMapValue("iconPath", args); - const auto iconBgColor = Utils::GetMapValue("iconBgColor", args); - - Initialize(appName, aumid, iconPath, iconBgColor); - result->Success(true); + void FlutterLocalNotificationsPlugin::HandleMethodCall( + const flutter::MethodCall& method_call, + std::unique_ptr> result + ) { + const auto& method_name = method_call.method_name(); + if (method_name == Method::GET_NOTIFICATION_APP_LAUNCH_DETAILS) { + result->Success(); } - else { - result->Error("INTERNAL", "flutter_local_notifications encountered an internal error."); + else if (method_name == Method::INITIALIZE) { + const auto args = std::get_if(method_call.arguments()); + if (args != nullptr) { + const auto appName = Utils::GetMapValue("appName", args).value(); + const auto aumid = Utils::GetMapValue("aumid", args).value(); + const auto iconPath = Utils::GetMapValue("iconPath", args); + const auto iconBgColor = Utils::GetMapValue("iconBgColor", args); + + Initialize(appName, aumid, iconPath, iconBgColor); + result->Success(true); + } + else { + result->Error("INTERNAL", "flutter_local_notifications encountered an internal error."); + } } - } - else if (method_name == Method::SHOW) { - channel.InvokeMethod("test", nullptr, nullptr); - - const auto args = std::get_if(method_call.arguments()); - if (args != nullptr && toastNotifier.has_value()) { - const auto id = Utils::GetMapValue("id", args).value(); - const auto title = Utils::GetMapValue("title", args); - const auto body = Utils::GetMapValue("body", args); - const auto payload = Utils::GetMapValue("payload", args); - const auto group = Utils::GetMapValue("group", args); - - ShowNotification(id, title, body, payload, group); - result->Success(); + else if (method_name == Method::SHOW) { + channel->InvokeMethod("test", nullptr); + + const auto args = std::get_if(method_call.arguments()); + if (args != nullptr && toastNotifier.has_value()) { + const auto id = Utils::GetMapValue("id", args).value(); + const auto title = Utils::GetMapValue("title", args); + const auto body = Utils::GetMapValue("body", args); + const auto payload = Utils::GetMapValue("payload", args); + const auto group = Utils::GetMapValue("group", args); + + ShowNotification(id, title, body, payload, group); + result->Success(); + } + else { + result->Error("INTERNAL", "flutter_local_notifications encountered an internal error."); + } } - else { - result->Error("INTERNAL", "flutter_local_notifications encountered an internal error."); + else if (method_name == Method::CANCEL && toastNotifier.has_value()) { + const auto args = std::get_if(method_call.arguments()); + if (args != nullptr) { + const auto id = Utils::GetMapValue("id", args).value(); + const auto group = Utils::GetMapValue("group", args); + + CancelNotification(id, group); + result->Success(); + } + else { + result->Error("INTERNAL", "flutter_local_notifications encountered an internal error."); + } } - } - else if (method_name == Method::CANCEL && toastNotifier.has_value()) { - const auto args = std::get_if(method_call.arguments()); - if (args != nullptr) { - const auto id = Utils::GetMapValue("id", args).value(); - const auto group = Utils::GetMapValue("group", args); - - CancelNotification(id, group); + else if (method_name == Method::CANCEL_ALL && toastNotifier.has_value()) { + CancelAllNotifications(); result->Success(); } else { - result->Error("INTERNAL", "flutter_local_notifications encountered an internal error."); + result->NotImplemented(); } } - else if (method_name == Method::CANCEL_ALL && toastNotifier.has_value()) { - CancelAllNotifications(); - result->Success(); - } - else { - result->NotImplemented(); - } -} -void FlutterLocalNotificationsPlugin::Initialize( - const std::string& appName, - const std::string& aumid, - const std::optional& iconPath, - const std::optional& iconBgColor -) { - _aumid = winrt::to_hstring(aumid); - PluginRegistration::RegisterApp(aumid, appName, iconPath, iconBgColor, this); - toastNotifier = winrt::Windows::UI::Notifications::ToastNotificationManager::CreateToastNotifier(winrt::to_hstring(aumid)); -} - -void FlutterLocalNotificationsPlugin::ShowNotification( - const int id, - const std::optional& title, - const std::optional& body, - const std::optional& payload, - const std::optional& group -) { - // obtain a notification template with a title and a body - const auto doc = winrt::Windows::UI::Notifications::ToastNotificationManager::GetTemplateContent(winrt::Windows::UI::Notifications::ToastTemplateType::ToastText02); - // find all tags - const auto nodes = doc.GetElementsByTagName(L"text"); - - if (title.has_value()) { - // change the text of the first , which will be the title - nodes.Item(0).AppendChild(doc.CreateTextNode(winrt::to_hstring(title.value()))); - } - if (body.has_value()) { - // change the text of the second , which will be the body - nodes.Item(1).AppendChild(doc.CreateTextNode(winrt::to_hstring(body.value()))); + void FlutterLocalNotificationsPlugin::Initialize( + const std::string& appName, + const std::string& aumid, + const std::optional& iconPath, + const std::optional& iconBgColor + ) { + _aumid = winrt::to_hstring(aumid); + PluginRegistration::RegisterApp(aumid, appName, iconPath, iconBgColor, channel); + toastNotifier = winrt::Windows::UI::Notifications::ToastNotificationManager::CreateToastNotifier(winrt::to_hstring(aumid)); } - winrt::Windows::UI::Notifications::ToastNotification notif{ doc }; - notif.Tag(winrt::to_hstring(id)); - if (group.has_value()) { - notif.Group(winrt::to_hstring(group.value())); - } - else { - notif.Group(_aumid); - } + void FlutterLocalNotificationsPlugin::ShowNotification( + const int id, + const std::optional& title, + const std::optional& body, + const std::optional& payload, + const std::optional& group + ) { + // obtain a notification template with a title and a body + const auto doc = winrt::Windows::UI::Notifications::ToastNotificationManager::GetTemplateContent(winrt::Windows::UI::Notifications::ToastTemplateType::ToastText02); + // find all tags + const auto nodes = doc.GetElementsByTagName(L"text"); + + if (title.has_value()) { + // change the text of the first , which will be the title + nodes.Item(0).AppendChild(doc.CreateTextNode(winrt::to_hstring(title.value()))); + } + if (body.has_value()) { + // change the text of the second , which will be the body + nodes.Item(1).AppendChild(doc.CreateTextNode(winrt::to_hstring(body.value()))); + } - toastNotifier.value().Show(notif); -} + winrt::Windows::UI::Notifications::ToastNotification notif{ doc }; + notif.Tag(winrt::to_hstring(id)); + if (group.has_value()) { + notif.Group(winrt::to_hstring(group.value())); + } + else { + notif.Group(_aumid); + } -void FlutterLocalNotificationsPlugin::CancelNotification(const int id, const std::optional& group) { - if (!toastNotificationHistory.has_value()) { - toastNotificationHistory = winrt::Windows::UI::Notifications::ToastNotificationManager::History(); + toastNotifier.value().Show(notif); } - if (group.has_value()) { - toastNotificationHistory.value().Remove(winrt::to_hstring(id), winrt::to_hstring(group.value()), _aumid); - } - else { - toastNotificationHistory.value().Remove(winrt::to_hstring(id), _aumid, _aumid); + void FlutterLocalNotificationsPlugin::CancelNotification(const int id, const std::optional& group) { + if (!toastNotificationHistory.has_value()) { + toastNotificationHistory = winrt::Windows::UI::Notifications::ToastNotificationManager::History(); + } + + if (group.has_value()) { + toastNotificationHistory.value().Remove(winrt::to_hstring(id), winrt::to_hstring(group.value()), _aumid); + } + else { + toastNotificationHistory.value().Remove(winrt::to_hstring(id), _aumid, _aumid); + } } -} -void FlutterLocalNotificationsPlugin::CancelAllNotifications() { - if (!toastNotificationHistory.has_value()) { - toastNotificationHistory = winrt::Windows::UI::Notifications::ToastNotificationManager::History(); + void FlutterLocalNotificationsPlugin::CancelAllNotifications() { + if (!toastNotificationHistory.has_value()) { + toastNotificationHistory = winrt::Windows::UI::Notifications::ToastNotificationManager::History(); + } + toastNotificationHistory.value().Clear(_aumid); } - toastNotificationHistory.value().Clear(_aumid); } void FlutterLocalNotificationsPluginRegisterWithRegistrar( diff --git a/flutter_local_notifications/windows/include/flutter_local_notifications/flutter_local_notifications_plugin.h b/flutter_local_notifications/windows/include/flutter_local_notifications/flutter_local_notifications_plugin.h index 37be0aac4..d50c367fc 100644 --- a/flutter_local_notifications/windows/include/flutter_local_notifications/flutter_local_notifications_plugin.h +++ b/flutter_local_notifications/windows/include/flutter_local_notifications/flutter_local_notifications_plugin.h @@ -36,65 +36,4 @@ FLUTTER_PLUGIN_EXPORT void FlutterLocalNotificationsPluginRegisterWithRegistrar( ///
typedef flutter::MethodChannel PluginMethodChannel; -class FlutterLocalNotificationsPlugin : public flutter::Plugin { -public: - static void RegisterWithRegistrar(flutter::PluginRegistrarWindows* registrar); - - PluginMethodChannel& GetPluginMethodChannel(); - - FlutterLocalNotificationsPlugin(PluginMethodChannel& channel); - - virtual ~FlutterLocalNotificationsPlugin(); - -private: - std::wstring _aumid; - std::optional toastNotifier; - std::optional toastNotificationHistory; - PluginMethodChannel& channel; - - // Called when a method is called on this plugin's channel from Dart. - void HandleMethodCall( - const flutter::MethodCall& method_call, - std::unique_ptr> result); - - /// - /// Initializes this plugin. - /// - /// The app user model ID that identifies the app. - /// The display name of the app. - /// An optional path to the icon of the app. - /// An optional background color of the icon, in AARRGGBB format. - void Initialize( - const std::string& appName, - const std::string& aumid, - const std::optional& iconPath, - const std::optional& iconBgColor); - - /// - /// Displays a single notification toast. - /// - /// A unique ID that identifies this notification. It can be used to cancel/dismiss the notification. - /// An optional title of the notification. - /// An optional body of the notification. - /// - void ShowNotification( - const int id, - const std::optional& title, - const std::optional& body, - const std::optional& payload, - const std::optional& group); - - /// - /// Dismisses the notification that has the given ID. - /// - /// The ID of the notification to be dismissed. - /// The group the notification is in. Default is the aumid of this app. - void CancelNotification(const int id, const std::optional& group); - - /// - /// Dismisses all currently active notifications. - /// - void CancelAllNotifications(); -}; - #endif // FLUTTER_PLUGIN_FLUTTER_LOCAL_NOTIFICATIONS_PLUGIN_H_ diff --git a/flutter_local_notifications/windows/registration.cpp b/flutter_local_notifications/windows/registration.cpp index 654380778..62a4301a2 100644 --- a/flutter_local_notifications/windows/registration.cpp +++ b/flutter_local_notifications/windows/registration.cpp @@ -37,7 +37,7 @@ const std::string CALLBACK_GUID_STR = "{68d0c89d-760f-4f79-a067-ae8d4220ccc1}"; /// struct NotificationActivationCallback : winrt::implements { - FlutterLocalNotificationsPlugin* plugin = nullptr; + std::shared_ptr channel; HRESULT __stdcall Activate( LPCWSTR app, @@ -49,10 +49,7 @@ struct NotificationActivationCallback : winrt::implementsGetPluginMethodChannel().InvokeMethod(Method::SELECT_NOTIFICATION, nullptr, nullptr); - } + channel->InvokeMethod(Method::SELECT_NOTIFICATION, nullptr, nullptr); return S_OK; } catch (...) { @@ -66,7 +63,7 @@ struct NotificationActivationCallback : winrt::implements struct NotificationActivationCallbackFactory : winrt::implements { - FlutterLocalNotificationsPlugin* plugin; + std::shared_ptr channel; HRESULT __stdcall CreateInstance( IUnknown* outer, @@ -82,9 +79,7 @@ struct NotificationActivationCallbackFactory : winrt::implements(); - cb.get()->plugin = plugin; - - std::cout << plugin << std::endl; + cb.get()->channel = channel; return cb->QueryInterface(iid, result); } @@ -245,15 +240,12 @@ void UpdateRegistry( /// Register the notificatio activation callback factory /// and the guid of the callback. /// -void RegisterCallback(FlutterLocalNotificationsPlugin* plugin) { +void RegisterCallback(std::shared_ptr channel) { DWORD registration{}; const auto factory_ref = winrt::make_self(); const auto factory = factory_ref.get(); - factory->plugin = plugin; - - std::cout << factory->plugin << std::endl; - std::cout << plugin << std::endl; + factory->channel = channel; winrt::check_hresult(CoRegisterClassObject( CALLBACK_GUID, @@ -268,7 +260,7 @@ void PluginRegistration::RegisterApp( const std::string& appName, const std::optional& iconPath, const std::optional& iconBgColor, - FlutterLocalNotificationsPlugin* plugin + std::shared_ptr plugin ) { std::cout << "register app" << std::endl; UpdateRegistry(aumid, appName, iconPath, iconBgColor); diff --git a/flutter_local_notifications/windows/registration.h b/flutter_local_notifications/windows/registration.h index cebbc9930..247f8c399 100644 --- a/flutter_local_notifications/windows/registration.h +++ b/flutter_local_notifications/windows/registration.h @@ -5,6 +5,7 @@ #include #include +#include /// /// Contains logic for handling Registry values. @@ -23,7 +24,7 @@ namespace PluginRegistration { const std::string& appName, const std::optional& iconPath, const std::optional& iconBgColor, - FlutterLocalNotificationsPlugin* plugin); + std::shared_ptr plugin); } #endif // !PLUGIN_REGISTRATRION_H_ From dd813e12f852b3ab165190c96b1e7513c42b8ab9 Mon Sep 17 00:00:00 2001 From: Alexandre Zollinger Chohfi Date: Mon, 28 Mar 2022 14:31:13 -0700 Subject: [PATCH 13/18] Small fix on notification. --- .../lib/src/platform_flutter_local_notifications.dart | 9 +++++---- flutter_local_notifications/windows/registration.cpp | 6 ++---- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/flutter_local_notifications/lib/src/platform_flutter_local_notifications.dart b/flutter_local_notifications/lib/src/platform_flutter_local_notifications.dart index a73eb0b1e..7347b8675 100644 --- a/flutter_local_notifications/lib/src/platform_flutter_local_notifications.dart +++ b/flutter_local_notifications/lib/src/platform_flutter_local_notifications.dart @@ -855,7 +855,7 @@ class MacOSFlutterLocalNotificationsPlugin /// Windows implementation of the flutter_local_notifications plugin. class WindowsFlutterLocalNotificationsPlugin extends MethodChannelFlutterLocalNotificationsPlugin { - SelectNotificationCallback? onSelectNotification; + SelectNotificationCallback? _onSelectNotification; /// Initializes the plugin. /// @@ -870,8 +870,8 @@ class WindowsFlutterLocalNotificationsPlugin WindowsInitializationSettings settings, { SelectNotificationCallback? onSelectNotification, }) { - this.onSelectNotification = onSelectNotification; - _channel.setMethodCallHandler(_onMethodCallFromNative); + this._onSelectNotification = onSelectNotification; + _channel.setMethodCallHandler(_handleMethod); return _channel.invokeMethod('initialize', settings.toMap()); } @@ -899,11 +899,12 @@ class WindowsFlutterLocalNotificationsPlugin 'group': group, }); - Future _onMethodCallFromNative(MethodCall call) async { + Future _handleMethod(MethodCall call) async { print('call $call'); switch (call.method) { case 'selectNotification': print('notification selected'); + _onSelectNotification?.call(call.arguments); break; } } diff --git a/flutter_local_notifications/windows/registration.cpp b/flutter_local_notifications/windows/registration.cpp index 62a4301a2..59171d439 100644 --- a/flutter_local_notifications/windows/registration.cpp +++ b/flutter_local_notifications/windows/registration.cpp @@ -9,8 +9,6 @@ #include #include #include -#include -#include #include #include #include @@ -131,7 +129,7 @@ void UpdateRegistry( RegistryKey key; // create registry key - // HKEY_CURRENT_USER\Software\Microsoft\\Windows\CurrentVersion\PushNotifications\Backup + // HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\PushNotifications\Backup winrt::check_win32(RegCreateKeyExA( HKEY_CURRENT_USER, notifSettingsKeyPath.c_str(), @@ -251,7 +249,7 @@ void RegisterCallback(std::shared_ptr channel) { CALLBACK_GUID, factory, CLSCTX_LOCAL_SERVER, - REGCLS_SINGLEUSE, + REGCLS_MULTIPLEUSE, ®istration)); } From 57ecbf0a6fbb54493ec3e09368df07594db90561 Mon Sep 17 00:00:00 2001 From: Kenneth Date: Thu, 31 Mar 2022 12:24:33 +0100 Subject: [PATCH 14/18] Pass payload to Dart on notification selected --- .../example/pubspec.yaml | 45 +++++++++++++++++++ .../flutter_local_notifications/pubspec.yaml | 41 +++++++++++++++++ .../pubspec.yaml | 22 +++++++++ .../pubspec.yaml | 16 +++++++ .../example/lib/main.dart | 1 + .../flutter_local_notifications_plugin.dart | 4 ++ .../platform_flutter_local_notifications.dart | 11 +++-- .../flutter_local_notifications_plugin.cpp | 32 ++++++++++--- .../windows/registration.cpp | 6 ++- 9 files changed, 165 insertions(+), 13 deletions(-) create mode 100644 .dart_tool/melos_tool/flutter_local_notifications/example/pubspec.yaml create mode 100644 .dart_tool/melos_tool/flutter_local_notifications/pubspec.yaml create mode 100644 .dart_tool/melos_tool/flutter_local_notifications_linux/pubspec.yaml create mode 100644 .dart_tool/melos_tool/flutter_local_notifications_platform_interface/pubspec.yaml diff --git a/.dart_tool/melos_tool/flutter_local_notifications/example/pubspec.yaml b/.dart_tool/melos_tool/flutter_local_notifications/example/pubspec.yaml new file mode 100644 index 000000000..98237739c --- /dev/null +++ b/.dart_tool/melos_tool/flutter_local_notifications/example/pubspec.yaml @@ -0,0 +1,45 @@ +# Generated file - do not commit this file. +description: "Demonstrates how to use the flutter_local_notifications plugin." +name: "flutter_local_notifications_example" +publish_to: "none" +dependencies: + cupertino_icons: "^1.0.2" + device_info: "^2.0.2" + flutter_native_timezone: "^2.0.0" + http: "^0.13.4" + image: "^3.0.8" + path_provider: "^2.0.0" + rxdart: "^0.27.2" + shared_preferences: "^2.0.1" + url_launcher: "^6.0.17" + flutter: + sdk: "flutter" + flutter_local_notifications: + path: "../" +dependency_overrides: + flutter_local_notifications: + path: ".." + flutter_local_notifications_linux: + path: "..\\..\\flutter_local_notifications_linux" + flutter_local_notifications_platform_interface: + path: "..\\..\\flutter_local_notifications_platform_interface" +dev_dependencies: + msix: "^2.8.17" + flutter_driver: + sdk: "flutter" + flutter_test: + sdk: "flutter" + integration_test: + sdk: "flutter" +environment: + flutter: ">=1.26.0-0" + sdk: ">=2.12.0-0 <3.0.0" +flutter: + assets: + - "icons/" + - "sound/" + uses-material-design: true +msix_config: + display_name: "Flutter Local Notifications Example" + identity_name: "Com.Example.FlutterLocalNotificationsExample" + debug: true diff --git a/.dart_tool/melos_tool/flutter_local_notifications/pubspec.yaml b/.dart_tool/melos_tool/flutter_local_notifications/pubspec.yaml new file mode 100644 index 000000000..5ad81bdd3 --- /dev/null +++ b/.dart_tool/melos_tool/flutter_local_notifications/pubspec.yaml @@ -0,0 +1,41 @@ +# Generated file - do not commit this file. +description: "A cross platform plugin for displaying and scheduling local notifications for Flutter applications with the ability to customise for each platform." +homepage: "https://github.com/MaikuB/flutter_local_notifications/tree/master/flutter_local_notifications" +name: "flutter_local_notifications" +version: "9.2.0" +dependencies: + clock: "^1.1.0" + flutter_local_notifications_linux: "^0.4.1" + flutter_local_notifications_platform_interface: "^6.0.0" + timezone: "^0.8.0" + flutter: + sdk: "flutter" +dependency_overrides: + flutter_local_notifications_linux: + path: "..\\flutter_local_notifications_linux" + flutter_local_notifications_platform_interface: + path: "..\\flutter_local_notifications_platform_interface" +dev_dependencies: + mockito: "^5.0.8" + plugin_platform_interface: "^2.0.0" + flutter_driver: + sdk: "flutter" + flutter_test: + sdk: "flutter" +environment: + flutter: ">=2.2.0" + sdk: ">=2.12.0 <3.0.0" +flutter: + plugin: + platforms: + android: + package: "com.dexterous.flutterlocalnotifications" + pluginClass: "FlutterLocalNotificationsPlugin" + ios: + pluginClass: "FlutterLocalNotificationsPlugin" + linux: + default_package: "flutter_local_notifications_linux" + macos: + pluginClass: "FlutterLocalNotificationsPlugin" + windows: + pluginClass: "FlutterLocalNotificationsPlugin" diff --git a/.dart_tool/melos_tool/flutter_local_notifications_linux/pubspec.yaml b/.dart_tool/melos_tool/flutter_local_notifications_linux/pubspec.yaml new file mode 100644 index 000000000..1b8919c40 --- /dev/null +++ b/.dart_tool/melos_tool/flutter_local_notifications_linux/pubspec.yaml @@ -0,0 +1,22 @@ +# Generated file - do not commit this file. +description: "Linux implementation of the flutter_local_notifications plugin" +homepage: "https://github.com/MaikuB/flutter_local_notifications/tree/master/flutter_local_notifications" +name: "flutter_local_notifications_linux" +version: "0.4.1+1" +dependencies: + dbus: "^0.6.0" + flutter_local_notifications_platform_interface: "^6.0.0" + path: "^1.8.0" + xdg_directories: "^0.2.0" + flutter: + sdk: "flutter" +dependency_overrides: + flutter_local_notifications_platform_interface: + path: "..\\flutter_local_notifications_platform_interface" +dev_dependencies: + mocktail: "^0.1.4" + flutter_test: + sdk: "flutter" +environment: + flutter: ">=2.2.0" + sdk: ">=2.12.0 <3.0.0" diff --git a/.dart_tool/melos_tool/flutter_local_notifications_platform_interface/pubspec.yaml b/.dart_tool/melos_tool/flutter_local_notifications_platform_interface/pubspec.yaml new file mode 100644 index 000000000..cdbbafdd6 --- /dev/null +++ b/.dart_tool/melos_tool/flutter_local_notifications_platform_interface/pubspec.yaml @@ -0,0 +1,16 @@ +# Generated file - do not commit this file. +description: "A common platform interface for the flutter_local_notifications plugin." +homepage: "https://github.com/MaikuB/flutter_local_notifications/tree/master/flutter_local_notifications_platform_interface" +name: "flutter_local_notifications_platform_interface" +version: "6.0.0" +dependencies: + plugin_platform_interface: "^2.0.0" + flutter: + sdk: "flutter" +dev_dependencies: + mockito: "^5.0.8" + flutter_test: + sdk: "flutter" +environment: + flutter: ">=2.2.0" + sdk: ">=2.12.0 <3.0.0" diff --git a/flutter_local_notifications/example/lib/main.dart b/flutter_local_notifications/example/lib/main.dart index bbe5483c5..370955ebe 100644 --- a/flutter_local_notifications/example/lib/main.dart +++ b/flutter_local_notifications/example/lib/main.dart @@ -919,6 +919,7 @@ class _HomePageState extends State { ); Future _showNotification() async { + print('show notification'); const AndroidNotificationDetails androidPlatformChannelSpecifics = AndroidNotificationDetails('your channel id', 'your channel name', channelDescription: 'your channel description', diff --git a/flutter_local_notifications/lib/src/flutter_local_notifications_plugin.dart b/flutter_local_notifications/lib/src/flutter_local_notifications_plugin.dart index 1882c511b..fdda763be 100644 --- a/flutter_local_notifications/lib/src/flutter_local_notifications_plugin.dart +++ b/flutter_local_notifications/lib/src/flutter_local_notifications_plugin.dart @@ -234,6 +234,10 @@ class FlutterLocalNotificationsPlugin { ?.show(id, title, body, notificationDetails: notificationDetails?.linux, payload: payload); + } else if (defaultTargetPlatform == TargetPlatform.windows) { + await resolvePlatformSpecificImplementation< + WindowsFlutterLocalNotificationsPlugin>() + ?.show(id, title, body, payload: payload); } else { await FlutterLocalNotificationsPlatform.instance.show(id, title, body); } diff --git a/flutter_local_notifications/lib/src/platform_flutter_local_notifications.dart b/flutter_local_notifications/lib/src/platform_flutter_local_notifications.dart index 7347b8675..9b6f4fa01 100644 --- a/flutter_local_notifications/lib/src/platform_flutter_local_notifications.dart +++ b/flutter_local_notifications/lib/src/platform_flutter_local_notifications.dart @@ -4,7 +4,6 @@ import 'dart:ui'; import 'package:clock/clock.dart'; import 'package:flutter/services.dart'; -import 'package:flutter/widgets.dart'; import 'package:flutter_local_notifications_platform_interface/flutter_local_notifications_platform_interface.dart'; import 'package:timezone/timezone.dart'; @@ -870,7 +869,7 @@ class WindowsFlutterLocalNotificationsPlugin WindowsInitializationSettings settings, { SelectNotificationCallback? onSelectNotification, }) { - this._onSelectNotification = onSelectNotification; + _onSelectNotification = onSelectNotification; _channel.setMethodCallHandler(_handleMethod); return _channel.invokeMethod('initialize', settings.toMap()); @@ -889,7 +888,7 @@ class WindowsFlutterLocalNotificationsPlugin 'title': title, 'body': body, 'group': group, - 'payload': payload ?? '', + 'payload': payload, }); @override @@ -900,11 +899,11 @@ class WindowsFlutterLocalNotificationsPlugin }); Future _handleMethod(MethodCall call) async { - print('call $call'); switch (call.method) { case 'selectNotification': - print('notification selected'); - _onSelectNotification?.call(call.arguments); + if (call.arguments is String) { + _onSelectNotification?.call(call.arguments); + } break; } } diff --git a/flutter_local_notifications/windows/flutter_local_notifications_plugin.cpp b/flutter_local_notifications/windows/flutter_local_notifications_plugin.cpp index f9acbd40a..1d9783002 100644 --- a/flutter_local_notifications/windows/flutter_local_notifications_plugin.cpp +++ b/flutter_local_notifications/windows/flutter_local_notifications_plugin.cpp @@ -189,23 +189,45 @@ namespace { const std::optional& group ) { // obtain a notification template with a title and a body - const auto doc = winrt::Windows::UI::Notifications::ToastNotificationManager::GetTemplateContent(winrt::Windows::UI::Notifications::ToastTemplateType::ToastText02); + //const auto doc = winrt::Windows::UI::Notifications::ToastNotificationManager::GetTemplateContent(winrt::Windows::UI::Notifications::ToastTemplateType::ToastText02); // find all tags - const auto nodes = doc.GetElementsByTagName(L"text"); + //const auto nodes = doc.GetElementsByTagName(L"text"); + + XmlDocument doc; + doc.LoadXml(L"\ + \ + \ + \ + \ + \ + "); + + const auto bindingNode = doc.SelectSingleNode(L"//binding[1]"); if (title.has_value()) { // change the text of the first , which will be the title - nodes.Item(0).AppendChild(doc.CreateTextNode(winrt::to_hstring(title.value()))); + const auto textNode = doc.CreateElement(L"text"); + textNode.InnerText(winrt::to_hstring(*title)); + bindingNode.AppendChild(textNode); } if (body.has_value()) { // change the text of the second , which will be the body - nodes.Item(1).AppendChild(doc.CreateTextNode(winrt::to_hstring(body.value()))); + //nodes.Item(1).AppendChild(doc.CreateTextNode(winrt::to_hstring(body.value()))); + const auto textNode = doc.CreateElement(L"text"); + textNode.InnerText(winrt::to_hstring(*body)); + bindingNode.AppendChild(textNode); } + if (payload.has_value()) { + std::cout << "payload: " << *payload << std::endl; + doc.DocumentElement().SetAttribute(L"launch", winrt::to_hstring(*payload)); + } + + std::cout << winrt::to_string(doc.GetXml()) << std::endl; winrt::Windows::UI::Notifications::ToastNotification notif{ doc }; notif.Tag(winrt::to_hstring(id)); if (group.has_value()) { - notif.Group(winrt::to_hstring(group.value())); + notif.Group(winrt::to_hstring(*group)); } else { notif.Group(_aumid); diff --git a/flutter_local_notifications/windows/registration.cpp b/flutter_local_notifications/windows/registration.cpp index 59171d439..14ded76ca 100644 --- a/flutter_local_notifications/windows/registration.cpp +++ b/flutter_local_notifications/windows/registration.cpp @@ -6,11 +6,12 @@ #include "include/flutter_local_notifications/flutter_local_notifications_plugin.h" #include "include/flutter_local_notifications/methods.h" +#include +#include #include #include #include #include -#include #include #include @@ -47,7 +48,8 @@ struct NotificationActivationCallback : winrt::implementsInvokeMethod(Method::SELECT_NOTIFICATION, nullptr, nullptr); + const std::string payload = CW2A(args); + channel->InvokeMethod(Method::SELECT_NOTIFICATION, std::make_unique(payload), nullptr); return S_OK; } catch (...) { From 0baa7db0b641e18158e7690c874e1b2458649543 Mon Sep 17 00:00:00 2001 From: Kenneth Date: Thu, 31 Mar 2022 12:26:23 +0100 Subject: [PATCH 15/18] Remove melos files --- .../example/pubspec.yaml | 45 ------------------- .../flutter_local_notifications/pubspec.yaml | 41 ----------------- .../pubspec.yaml | 22 --------- .../pubspec.yaml | 16 ------- 4 files changed, 124 deletions(-) delete mode 100644 .dart_tool/melos_tool/flutter_local_notifications/example/pubspec.yaml delete mode 100644 .dart_tool/melos_tool/flutter_local_notifications/pubspec.yaml delete mode 100644 .dart_tool/melos_tool/flutter_local_notifications_linux/pubspec.yaml delete mode 100644 .dart_tool/melos_tool/flutter_local_notifications_platform_interface/pubspec.yaml diff --git a/.dart_tool/melos_tool/flutter_local_notifications/example/pubspec.yaml b/.dart_tool/melos_tool/flutter_local_notifications/example/pubspec.yaml deleted file mode 100644 index 98237739c..000000000 --- a/.dart_tool/melos_tool/flutter_local_notifications/example/pubspec.yaml +++ /dev/null @@ -1,45 +0,0 @@ -# Generated file - do not commit this file. -description: "Demonstrates how to use the flutter_local_notifications plugin." -name: "flutter_local_notifications_example" -publish_to: "none" -dependencies: - cupertino_icons: "^1.0.2" - device_info: "^2.0.2" - flutter_native_timezone: "^2.0.0" - http: "^0.13.4" - image: "^3.0.8" - path_provider: "^2.0.0" - rxdart: "^0.27.2" - shared_preferences: "^2.0.1" - url_launcher: "^6.0.17" - flutter: - sdk: "flutter" - flutter_local_notifications: - path: "../" -dependency_overrides: - flutter_local_notifications: - path: ".." - flutter_local_notifications_linux: - path: "..\\..\\flutter_local_notifications_linux" - flutter_local_notifications_platform_interface: - path: "..\\..\\flutter_local_notifications_platform_interface" -dev_dependencies: - msix: "^2.8.17" - flutter_driver: - sdk: "flutter" - flutter_test: - sdk: "flutter" - integration_test: - sdk: "flutter" -environment: - flutter: ">=1.26.0-0" - sdk: ">=2.12.0-0 <3.0.0" -flutter: - assets: - - "icons/" - - "sound/" - uses-material-design: true -msix_config: - display_name: "Flutter Local Notifications Example" - identity_name: "Com.Example.FlutterLocalNotificationsExample" - debug: true diff --git a/.dart_tool/melos_tool/flutter_local_notifications/pubspec.yaml b/.dart_tool/melos_tool/flutter_local_notifications/pubspec.yaml deleted file mode 100644 index 5ad81bdd3..000000000 --- a/.dart_tool/melos_tool/flutter_local_notifications/pubspec.yaml +++ /dev/null @@ -1,41 +0,0 @@ -# Generated file - do not commit this file. -description: "A cross platform plugin for displaying and scheduling local notifications for Flutter applications with the ability to customise for each platform." -homepage: "https://github.com/MaikuB/flutter_local_notifications/tree/master/flutter_local_notifications" -name: "flutter_local_notifications" -version: "9.2.0" -dependencies: - clock: "^1.1.0" - flutter_local_notifications_linux: "^0.4.1" - flutter_local_notifications_platform_interface: "^6.0.0" - timezone: "^0.8.0" - flutter: - sdk: "flutter" -dependency_overrides: - flutter_local_notifications_linux: - path: "..\\flutter_local_notifications_linux" - flutter_local_notifications_platform_interface: - path: "..\\flutter_local_notifications_platform_interface" -dev_dependencies: - mockito: "^5.0.8" - plugin_platform_interface: "^2.0.0" - flutter_driver: - sdk: "flutter" - flutter_test: - sdk: "flutter" -environment: - flutter: ">=2.2.0" - sdk: ">=2.12.0 <3.0.0" -flutter: - plugin: - platforms: - android: - package: "com.dexterous.flutterlocalnotifications" - pluginClass: "FlutterLocalNotificationsPlugin" - ios: - pluginClass: "FlutterLocalNotificationsPlugin" - linux: - default_package: "flutter_local_notifications_linux" - macos: - pluginClass: "FlutterLocalNotificationsPlugin" - windows: - pluginClass: "FlutterLocalNotificationsPlugin" diff --git a/.dart_tool/melos_tool/flutter_local_notifications_linux/pubspec.yaml b/.dart_tool/melos_tool/flutter_local_notifications_linux/pubspec.yaml deleted file mode 100644 index 1b8919c40..000000000 --- a/.dart_tool/melos_tool/flutter_local_notifications_linux/pubspec.yaml +++ /dev/null @@ -1,22 +0,0 @@ -# Generated file - do not commit this file. -description: "Linux implementation of the flutter_local_notifications plugin" -homepage: "https://github.com/MaikuB/flutter_local_notifications/tree/master/flutter_local_notifications" -name: "flutter_local_notifications_linux" -version: "0.4.1+1" -dependencies: - dbus: "^0.6.0" - flutter_local_notifications_platform_interface: "^6.0.0" - path: "^1.8.0" - xdg_directories: "^0.2.0" - flutter: - sdk: "flutter" -dependency_overrides: - flutter_local_notifications_platform_interface: - path: "..\\flutter_local_notifications_platform_interface" -dev_dependencies: - mocktail: "^0.1.4" - flutter_test: - sdk: "flutter" -environment: - flutter: ">=2.2.0" - sdk: ">=2.12.0 <3.0.0" diff --git a/.dart_tool/melos_tool/flutter_local_notifications_platform_interface/pubspec.yaml b/.dart_tool/melos_tool/flutter_local_notifications_platform_interface/pubspec.yaml deleted file mode 100644 index cdbbafdd6..000000000 --- a/.dart_tool/melos_tool/flutter_local_notifications_platform_interface/pubspec.yaml +++ /dev/null @@ -1,16 +0,0 @@ -# Generated file - do not commit this file. -description: "A common platform interface for the flutter_local_notifications plugin." -homepage: "https://github.com/MaikuB/flutter_local_notifications/tree/master/flutter_local_notifications_platform_interface" -name: "flutter_local_notifications_platform_interface" -version: "6.0.0" -dependencies: - plugin_platform_interface: "^2.0.0" - flutter: - sdk: "flutter" -dev_dependencies: - mockito: "^5.0.8" - flutter_test: - sdk: "flutter" -environment: - flutter: ">=2.2.0" - sdk: ">=2.12.0 <3.0.0" From 5ad64641640be66a7f2eb96b63ae94aa53bfc43a Mon Sep 17 00:00:00 2001 From: Kenneth Date: Wed, 13 Apr 2022 22:01:52 +0100 Subject: [PATCH 16/18] Attempting to handle msix --- .../windows/notification_action.dart | 0 .../windows/notification_details.dart | 0 .../flutter_local_notifications_plugin.cpp | 38 ++++++++++++++++++- 3 files changed, 37 insertions(+), 1 deletion(-) create mode 100644 flutter_local_notifications/lib/src/platform_specifics/windows/notification_action.dart create mode 100644 flutter_local_notifications/lib/src/platform_specifics/windows/notification_details.dart diff --git a/flutter_local_notifications/lib/src/platform_specifics/windows/notification_action.dart b/flutter_local_notifications/lib/src/platform_specifics/windows/notification_action.dart new file mode 100644 index 000000000..e69de29bb diff --git a/flutter_local_notifications/lib/src/platform_specifics/windows/notification_details.dart b/flutter_local_notifications/lib/src/platform_specifics/windows/notification_details.dart new file mode 100644 index 000000000..e69de29bb diff --git a/flutter_local_notifications/windows/flutter_local_notifications_plugin.cpp b/flutter_local_notifications/windows/flutter_local_notifications_plugin.cpp index 1d9783002..9d01bcf0b 100644 --- a/flutter_local_notifications/windows/flutter_local_notifications_plugin.cpp +++ b/flutter_local_notifications/windows/flutter_local_notifications_plugin.cpp @@ -6,10 +6,13 @@ // This must be included before many other Windows headers. #include #include +#include +#include #include #include #include #include +#include // For getPlatformVersion; remove unless needed for your plugin implementation. #include @@ -82,6 +85,8 @@ namespace { /// Dismisses all currently active notifications. /// void CancelAllNotifications(); + + std::optional HasIdentity(); }; // static @@ -170,6 +175,34 @@ namespace { } } + std::optional FlutterLocalNotificationsPlugin::HasIdentity() { + if (!IsWindows8OrGreater()) { + // OS is windows 7 or lower + return false; + } + + UINT32 length; + auto err = GetCurrentPackageFullName(&length, nullptr); + if (err != ERROR_INSUFFICIENT_BUFFER) { + if (err == APPMODEL_ERROR_NO_PACKAGE) + return false; + + return std::nullopt; + } + + PWSTR fullName = (PWSTR)malloc(length * sizeof(*fullName)); + if (fullName == nullptr) + return std::nullopt; + + err = GetCurrentPackageFullName(&length, fullName); + if (err != ERROR_SUCCESS) + return std::nullopt; + + free(fullName); + + return true; + } + void FlutterLocalNotificationsPlugin::Initialize( const std::string& appName, const std::string& aumid, @@ -178,7 +211,10 @@ namespace { ) { _aumid = winrt::to_hstring(aumid); PluginRegistration::RegisterApp(aumid, appName, iconPath, iconBgColor, channel); - toastNotifier = winrt::Windows::UI::Notifications::ToastNotificationManager::CreateToastNotifier(winrt::to_hstring(aumid)); + if (HasIdentity()) + toastNotifier = winrt::Windows::UI::Notifications::ToastNotificationManager::CreateToastNotifier(); + else + toastNotifier = winrt::Windows::UI::Notifications::ToastNotificationManager::CreateToastNotifier(winrt::to_hstring(aumid)); } void FlutterLocalNotificationsPlugin::ShowNotification( From effca5aaab1bcabc567c39a1f8da79ab239bf6d5 Mon Sep 17 00:00:00 2001 From: Kenneth Date: Wed, 13 Apr 2022 22:06:44 +0100 Subject: [PATCH 17/18] Forgot to check for optional value --- .../windows/flutter_local_notifications_plugin.cpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/flutter_local_notifications/windows/flutter_local_notifications_plugin.cpp b/flutter_local_notifications/windows/flutter_local_notifications_plugin.cpp index 9d01bcf0b..9ed3d4f03 100644 --- a/flutter_local_notifications/windows/flutter_local_notifications_plugin.cpp +++ b/flutter_local_notifications/windows/flutter_local_notifications_plugin.cpp @@ -54,7 +54,8 @@ namespace { /// The display name of the app. /// An optional path to the icon of the app. /// An optional background color of the icon, in AARRGGBB format. - void Initialize( + /// Whether the initialization was successful. + bool Initialize( const std::string& appName, const std::string& aumid, const std::optional& iconPath, @@ -203,7 +204,7 @@ namespace { return true; } - void FlutterLocalNotificationsPlugin::Initialize( + bool FlutterLocalNotificationsPlugin::Initialize( const std::string& appName, const std::string& aumid, const std::optional& iconPath, @@ -211,7 +212,12 @@ namespace { ) { _aumid = winrt::to_hstring(aumid); PluginRegistration::RegisterApp(aumid, appName, iconPath, iconBgColor, channel); - if (HasIdentity()) + + const auto hasIdentity = HasIdentity(); + if (!hasIdentity.has_value()) + return false; + + if (hasIdentity.value()) toastNotifier = winrt::Windows::UI::Notifications::ToastNotificationManager::CreateToastNotifier(); else toastNotifier = winrt::Windows::UI::Notifications::ToastNotificationManager::CreateToastNotifier(winrt::to_hstring(aumid)); From 31b1ee7f5a4b3e0f5986054e5a0e55b03b79e83e Mon Sep 17 00:00:00 2001 From: Kenneth Date: Tue, 19 Apr 2022 12:56:06 +0100 Subject: [PATCH 18/18] Fix Initialize not returning value --- .../windows/flutter_local_notifications_plugin.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/flutter_local_notifications/windows/flutter_local_notifications_plugin.cpp b/flutter_local_notifications/windows/flutter_local_notifications_plugin.cpp index 9ed3d4f03..792cfe576 100644 --- a/flutter_local_notifications/windows/flutter_local_notifications_plugin.cpp +++ b/flutter_local_notifications/windows/flutter_local_notifications_plugin.cpp @@ -129,8 +129,7 @@ namespace { const auto iconPath = Utils::GetMapValue("iconPath", args); const auto iconBgColor = Utils::GetMapValue("iconBgColor", args); - Initialize(appName, aumid, iconPath, iconBgColor); - result->Success(true); + result->Success(Initialize(appName, aumid, iconPath, iconBgColor)); } else { result->Error("INTERNAL", "flutter_local_notifications encountered an internal error."); @@ -221,6 +220,8 @@ namespace { toastNotifier = winrt::Windows::UI::Notifications::ToastNotificationManager::CreateToastNotifier(); else toastNotifier = winrt::Windows::UI::Notifications::ToastNotificationManager::CreateToastNotifier(winrt::to_hstring(aumid)); + + return true; } void FlutterLocalNotificationsPlugin::ShowNotification(