├── .github
├── ISSUE_TEMPLATE
│ ├── bug_report.md
│ └── feature_request.md
├── PULL_REQUEST_TEMPLATE.md
├── dependabot.yml
└── workflows
│ ├── codeql-analysis.yml
│ ├── main.yml
│ └── nuget.yml
├── .gitignore
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── Cleanup_Solution_For_Deployment.bat
├── GitHubPackages
├── SourceAdd.cmd
├── nuget.exe
└── publish.cmd
├── LICENSE
├── README.md
├── SECURITY.md
├── Sample
└── Direct Maui
│ ├── LocalNotification.Sample.sln
│ └── LocalNotification.Sample
│ ├── App.xaml
│ ├── App.xaml.cs
│ ├── AppShell.xaml
│ ├── AppShell.xaml.cs
│ ├── LocalNotification.Sample.csproj
│ ├── MainPage.xaml
│ ├── MainPage.xaml.cs
│ ├── MauiProgram.cs
│ ├── NotificationPage.xaml
│ ├── NotificationPage.xaml.cs
│ ├── Platforms
│ ├── Android
│ │ ├── AndroidManifest.xml
│ │ ├── MainActivity.cs
│ │ ├── MainApplication.cs
│ │ ├── ManifestInfo.cs
│ │ └── Resources
│ │ │ ├── raw
│ │ │ └── good_things_happen.mp3
│ │ │ └── values
│ │ │ └── colors.xml
│ ├── Windows
│ │ ├── App.xaml
│ │ ├── App.xaml.cs
│ │ ├── Package.appxmanifest
│ │ └── app.manifest
│ └── iOS
│ │ ├── AppDelegate.cs
│ │ ├── CustomUserNotificationCenterDelegate.cs
│ │ ├── Info.plist
│ │ ├── Program.cs
│ │ └── Resources
│ │ └── good_things_happen.aiff
│ └── Resources
│ ├── AppIcon
│ ├── appicon.svg
│ └── appiconfg.svg
│ ├── Fonts
│ ├── OpenSans-Regular.ttf
│ └── OpenSans-Semibold.ttf
│ ├── Images
│ ├── dotnet_bot.svg
│ ├── i2.png
│ ├── i3.png
│ └── icon1.png
│ ├── Raw
│ ├── AboutAssets.txt
│ └── appicon1.png
│ ├── Splash
│ └── splash.svg
│ └── Styles
│ ├── Colors.xaml
│ └── Styles.xaml
├── Screenshots
├── AndroidNotificationAction.png
├── AndroidNotificationIcon.png
├── SoundFileMap.png
├── iOSNotificationAction.png
├── icon.png
├── icon64.png
└── screenRecord.gif
├── Source
├── Plugin.LocalNotification.sln
└── Plugin.LocalNotification
│ ├── AndroidOption
│ ├── AndroidAction.cs
│ ├── AndroidAlarmType.cs
│ ├── AndroidColor.cs
│ ├── AndroidGeofenceOptions.cs
│ ├── AndroidIcon.cs
│ ├── AndroidImportance.cs
│ ├── AndroidLaunch.cs
│ ├── AndroidLocalNotificationBuilder.cs
│ ├── AndroidNotificationPermission.cs
│ ├── AndroidOptions.cs
│ ├── AndroidPendingIntentFlags.cs
│ ├── AndroidPriority.cs
│ ├── AndroidProgressBar.cs
│ ├── AndroidScheduleOptions.cs
│ ├── AndroidVisibilityType.cs
│ ├── IAndroidLocalNotificationBuilder.cs
│ ├── NotificationChannelGroupRequest.cs
│ └── NotificationChannelRequest.cs
│ ├── EventArgs
│ ├── NotificationActionEventArgs.cs
│ ├── NotificationEventArgs.cs
│ └── NotificationEventReceivingArgs.cs
│ ├── ILocalNotificationBuilder.cs
│ ├── INotificationService.cs
│ ├── Json
│ ├── INotificationSerializer.cs
│ └── NotificationSerializer.cs
│ ├── LocalNotificationBuilder.cs
│ ├── LocalNotificationCenter.cs
│ ├── LocalNotificationExtensions.cs
│ ├── LocalNotificationInitializeService.cs
│ ├── NotificationAction.cs
│ ├── NotificationCategory.cs
│ ├── NotificationCategoryType.cs
│ ├── NotificationImage.cs
│ ├── NotificationPermission.cs
│ ├── NotificationRepeat.cs
│ ├── NotificationRequest.cs
│ ├── NotificationRequestGeofence.cs
│ ├── NotificationRequestSchedule.cs
│ ├── Platforms
│ ├── Android
│ │ ├── GeofenceTransitionsIntentReceiver.cs
│ │ ├── LocalNotificationCenter.cs
│ │ ├── ManifestInfo.cs
│ │ ├── NotificationActionReceiver.cs
│ │ ├── NotificationPerms.cs
│ │ ├── NotificationRepository.cs
│ │ ├── NotificationServiceImpl.cs
│ │ ├── PlatformExtensions.cs
│ │ └── ScheduledAlarmReceiver.cs
│ ├── Generic
│ │ └── NotificationServiceImpl.cs
│ ├── Windows
│ │ ├── LocalNotificationCenter.cs
│ │ ├── NotificationRepository.cs
│ │ └── NotificationServiceImpl.cs
│ └── iOS
│ │ ├── LocalNotificationCenter.cs
│ │ ├── ManifestInfo.cs
│ │ ├── NotificationServiceImpl.cs
│ │ ├── PlatformExtensions.cs
│ │ └── UserNotificationCenterDelegate.cs
│ ├── Plugin.LocalNotification.csproj
│ ├── Plugin.LocalNotification.ruleset
│ ├── Properties
│ ├── AssemblyInfo.cs
│ ├── Resources.Designer.cs
│ └── Resources.resx
│ ├── WindowsOption
│ ├── WindowsAction.cs
│ └── WindowsOptions.cs
│ └── iOSOption
│ ├── IiOSLocalNotificationBuilder.cs
│ ├── iOSAction.cs
│ ├── iOSActionIcon.cs
│ ├── iOSActionIconType.cs
│ ├── iOSActionType.cs
│ ├── iOSAuthorizationOptions.cs
│ ├── iOSGeofenceOptions.cs
│ ├── iOSLocalNotificationBuilder.cs
│ ├── iOSLocationAuthorization.cs
│ ├── iOSNotificationPermission.cs
│ ├── iOSOptions.cs
│ └── iOSPriority.cs
└── UnitTests
└── LocalNotification.UnitTests
├── GlobalUsing.cs
├── LocalNotification.UnitTests.csproj
├── Resources
└── dotnet_logo.png
├── Tests
├── LocalNotificationCenterTests.cs
└── NotificationImageTests.cs
└── xunit.runner.json
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 | title: ''
5 | labels: bug
6 | assignees: thudugala
7 |
8 | ---
9 |
10 | **Describe the bug**
11 | A clear and concise description of what the bug is.
12 |
13 | **To Reproduce**
14 | Steps to reproduce the behavior:
15 | 1. Go to '...'
16 | 2. Click on '....'
17 | 3. Scroll down to '....'
18 | 4. See error
19 |
20 | **Expected behavior**
21 | A clear and concise description of what you expected to happen.
22 |
23 | **Screenshots**
24 | If applicable, add screenshots to help explain your problem.
25 |
26 | **Platform (please complete the following information):**
27 | - OS: [e.g. iOS]
28 | - Version [e.g. 22]
29 |
30 | **Smartphone (please complete the following information):**
31 | - Device: [e.g. iPhone6]
32 | - OS: [e.g. iOS8.1]
33 | - Version [e.g. 22]
34 |
35 | **Additional context**
36 | Add any other context about the problem here.
37 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 | title: ''
5 | labels: enhancement
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Is your feature request related to a problem? Please describe.**
11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
12 |
13 | **Describe the solution you'd like**
14 | A clear and concise description of what you want to happen.
15 |
16 | **Describe alternatives you've considered**
17 | A clear and concise description of any alternative solutions or features you've considered.
18 |
19 | **Additional context**
20 | Add any other context or screenshots about the feature request here.
21 |
--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | ### What does this PR do?
2 |
3 |
4 | ##### Why are we doing this? Any context or related work?
5 |
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | # To get started with Dependabot version updates, you'll need to specify which
2 | # package ecosystems to update and where the package manifests are located.
3 | # Please see the documentation for all configuration options:
4 | # https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
5 |
6 | version: 2
7 | updates:
8 | - package-ecosystem: "nuget" # See documentation for possible values
9 | directory: "/Sample/Direct/LocalNotification.Sample.Android" # Location of package manifests
10 | schedule:
11 | interval: "daily"
12 |
13 | - package-ecosystem: "nuget" # See documentation for possible values
14 | directory: "/Sample/Direct/LocalNotification.Sample.iOS" # Location of package manifests
15 | schedule:
16 | interval: "daily"
17 |
--------------------------------------------------------------------------------
/.github/workflows/codeql-analysis.yml:
--------------------------------------------------------------------------------
1 | # For most projects, this workflow file will not need changing; you simply need
2 | # to commit it to your repository.
3 | #
4 | # You may wish to alter this file to override the set of languages analyzed,
5 | # or to provide custom queries or build logic.
6 | #
7 | # ******** NOTE ********
8 | # We have attempted to detect the languages in your repository. Please check
9 | # the `language` matrix defined below to confirm you have the correct set of
10 | # supported CodeQL languages.
11 | #
12 | name: "CodeQL"
13 |
14 | on:
15 | push:
16 | branches: [ master ]
17 | pull_request:
18 | # The branches below must be a subset of the branches above
19 | branches: [ master ]
20 | schedule:
21 | - cron: '35 0 * * 3'
22 |
23 | jobs:
24 | analyze:
25 | name: Analyze
26 | runs-on: ubuntu-latest
27 | permissions:
28 | actions: read
29 | contents: read
30 | security-events: write
31 |
32 | strategy:
33 | fail-fast: false
34 | matrix:
35 | language: [ 'csharp' ]
36 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
37 | # Learn more about CodeQL language support at https://git.io/codeql-language-support
38 |
39 | steps:
40 | - name: Checkout repository
41 | uses: actions/checkout@v2
42 |
43 | # Initializes the CodeQL tools for scanning.
44 | - name: Initialize CodeQL
45 | uses: github/codeql-action/init@v1
46 | with:
47 | languages: ${{ matrix.language }}
48 | # If you wish to specify custom queries, you can do so here or in a config file.
49 | # By default, queries listed here will override any specified in a config file.
50 | # Prefix the list here with "+" to use these queries and those in the config file.
51 | # queries: ./path/to/local/query, your-org/your-repo/queries@main
52 |
53 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
54 | # If this step fails, then you should remove it and run the build manually (see below)
55 | - name: Autobuild
56 | uses: github/codeql-action/autobuild@v1
57 |
58 | # ℹ️ Command-line programs to run using the OS shell.
59 | # 📚 https://git.io/JvXDl
60 |
61 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
62 | # and modify them (or add more) to build your code if your project
63 | # uses a compiled language
64 |
65 | #- run: |
66 | # make bootstrap
67 | # make release
68 |
69 | - name: Perform CodeQL Analysis
70 | uses: github/codeql-action/analyze@v1
71 |
--------------------------------------------------------------------------------
/.github/workflows/main.yml:
--------------------------------------------------------------------------------
1 | name: CI
2 |
3 | on: [push]
4 |
5 | jobs:
6 | # MAUI Android Build
7 | build-android:
8 | runs-on: windows-latest
9 | name: Android Build
10 | steps:
11 |
12 | - name: Checkout
13 | uses: actions/checkout@v4.2.2
14 |
15 | - name: Setup .NET 9
16 | uses: actions/setup-dotnet@v4.3.0
17 | with:
18 | dotnet-version: 9.x
19 |
20 | - uses: actions/setup-java@4.7.0
21 | with:
22 | distribution: 'microsoft'
23 | java-version: '17'
24 |
25 | - name: Install MAUI Workload
26 | run: dotnet workload install maui --ignore-failed-sources
27 |
28 | - name: Restore Dependencies
29 | run: dotnet restore 'Sample/Direct Maui/LocalNotification.Sample/LocalNotification.Sample.csproj'
30 |
31 | - name: Build MAUI Android
32 | run: dotnet publish 'Sample/Direct Maui/LocalNotification.Sample/LocalNotification.Sample.csproj' -c Release -f net9.0-android --no-restore
33 |
34 | - name: Upload Android Artifact
35 | uses: actions/upload-artifact@v4.6.0
36 | with:
37 | name: mauibeach-android-ci-build
38 | path: 'Sample/Direct Maui/LocalNotification.Sample/bin/Release/net8.0-android/*Signed.a*'
39 |
40 | # MAUI iOS Build
41 | build-ios:
42 | runs-on: macos-latest
43 | name: iOS Build
44 | steps:
45 |
46 | - name: Checkout
47 | uses: actions/checkout@v4.2.2
48 |
49 | - name: Setup .NET 9
50 | uses: actions/setup-dotnet@v4.3.0
51 | with:
52 | dotnet-version: 9.x
53 |
54 | - name: Install MAUI Workload
55 | run: dotnet workload install maui --ignore-failed-sources
56 |
57 | - name: Set XCode Version
58 | shell: bash
59 | run: |
60 | sudo xcode-select -s "/Applications/Xcode_16.2.app"
61 | echo "MD_APPLE_SDK_ROOT=/Applications/Xcode_16.2.app" >> $GITHUB_ENV
62 |
63 | - name: Install iOS Simulators
64 | run: |
65 | sudo xcode-select --switch /Applications/Xcode_16.2.app
66 | sudo xcodebuild -runFirstLaunch
67 | sudo xcrun simctl list
68 | sudo xcrun simctl create "iPhone 16" com.apple.CoreSimulator.SimDeviceType.iPhone-16 com.apple.CoreSimulator.SimRuntime.iOS-16-2
69 |
70 | - name: Restore Dependencies
71 | run: dotnet restore 'Sample/Direct Maui/LocalNotification.Sample/LocalNotification.Sample.csproj'
72 |
73 | - name: Build MAUI iOS
74 | run: dotnet build 'Sample/Direct Maui/LocalNotification.Sample/LocalNotification.Sample.csproj' -c Release -f net9.0-ios --no-restore /p:buildForSimulator=True /p:packageApp=True /p:ArchiveOnBuild=False
75 |
76 | - name: Upload iOS Artifact
77 | uses: actions/upload-artifact@v4.6.0
78 | with:
79 | name: mauibeach-ios-ci-build
80 | path: 'Sample/Direct Maui/LocalNotification.Sample/bin/Release/net8.0-ios/iossimulator-x64/**/*.app'
81 |
--------------------------------------------------------------------------------
/.github/workflows/nuget.yml:
--------------------------------------------------------------------------------
1 | name: Nuget Build
2 |
3 | on: [push]
4 |
5 | jobs:
6 | nuget:
7 | runs-on: windows-latest
8 |
9 | steps:
10 | - uses: actions/checkout@v4.2.2
11 |
12 | - name: Setup .NET 9
13 | uses: actions/setup-dotnet@v4.3.0
14 | with:
15 | dotnet-version: 9.x
16 |
17 | - name: Install MAUI Workloads
18 | run: |
19 | dotnet workload install android --ignore-failed-sources
20 | dotnet workload install ios --ignore-failed-sources
21 | dotnet workload install maui --ignore-failed-sources
22 |
23 | - name: Add private GitHub registry to NuGet
24 | run: dotnet nuget add source "https://nuget.pkg.github.com/thudugala/index.json" --name "GitHub" --username thudugala --password ${{ secrets.GITHUB_TOKEN }}
25 |
26 | - name : restore Plugin.LocalNotification.sln
27 | run: dotnet restore Source/Plugin.LocalNotification.sln
28 |
29 | - name: build Plugin.LocalNotification.csproj
30 | run: dotnet build Source/Plugin.LocalNotification/Plugin.LocalNotification.csproj --configuration Release
31 |
32 | - uses: actions/upload-artifact@v4.6.0
33 | with:
34 | name: Plugin.LocalNotification
35 | path: Source/Plugin.LocalNotification/bin/Release/
36 |
37 |
38 |
39 | - name: Push generated package to GitHub registry
40 | run: dotnet nuget push "**/*.nupkg" --api-key ${{ secrets.Elvin_Package_Registry }} --source "GitHub" --skip-duplicate
41 |
42 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | In the interest of fostering an open and welcoming environment, we as
6 | contributors and maintainers pledge to making participation in our project and
7 | our community a harassment-free experience for everyone, regardless of age, body
8 | size, disability, ethnicity, sex characteristics, gender identity and expression,
9 | level of experience, education, socio-economic status, nationality, personal
10 | appearance, race, religion, or sexual identity and orientation.
11 |
12 | ## Our Standards
13 |
14 | Examples of behavior that contributes to creating a positive environment
15 | include:
16 |
17 | * Using welcoming and inclusive language
18 | * Being respectful of differing viewpoints and experiences
19 | * Gracefully accepting constructive criticism
20 | * Focusing on what is best for the community
21 | * Showing empathy towards other community members
22 |
23 | Examples of unacceptable behavior by participants include:
24 |
25 | * The use of sexualized language or imagery and unwelcome sexual attention or
26 | advances
27 | * Trolling, insulting/derogatory comments, and personal or political attacks
28 | * Public or private harassment
29 | * Publishing others' private information, such as a physical or electronic
30 | address, without explicit permission
31 | * Other conduct which could reasonably be considered inappropriate in a
32 | professional setting
33 |
34 | ## Our Responsibilities
35 |
36 | Project maintainers are responsible for clarifying the standards of acceptable
37 | behavior and are expected to take appropriate and fair corrective action in
38 | response to any instances of unacceptable behavior.
39 |
40 | Project maintainers have the right and responsibility to remove, edit, or
41 | reject comments, commits, code, wiki edits, issues, and other contributions
42 | that are not aligned to this Code of Conduct, or to ban temporarily or
43 | permanently any contributor for other behaviors that they deem inappropriate,
44 | threatening, offensive, or harmful.
45 |
46 | ## Scope
47 |
48 | This Code of Conduct applies both within project spaces and in public spaces
49 | when an individual is representing the project or its community. Examples of
50 | representing a project or community include using an official project e-mail
51 | address, posting via an official social media account, or acting as an appointed
52 | representative at an online or offline event. Representation of a project may be
53 | further defined and clarified by project maintainers.
54 |
55 | ## Enforcement
56 |
57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be
58 | reported by contacting the project team at elvin@outlook.co.nz. All
59 | complaints will be reviewed and investigated and will result in a response that
60 | is deemed necessary and appropriate to the circumstances. The project team is
61 | obligated to maintain confidentiality with regard to the reporter of an incident.
62 | Further details of specific enforcement policies may be posted separately.
63 |
64 | Project maintainers who do not follow or enforce the Code of Conduct in good
65 | faith may face temporary or permanent repercussions as determined by other
66 | members of the project's leadership.
67 |
68 | ## Attribution
69 |
70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
72 |
73 | [homepage]: https://www.contributor-covenant.org
74 |
75 | For answers to common questions about this code of conduct, see
76 | https://www.contributor-covenant.org/faq
77 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing
2 |
3 | Thanks you for your interest in contributing to Plugin.LocalNotification
4 |
5 | ## Finding an issue to work on
6 |
7 | If you'd like to work on something that isn't in a current issue, especially if it would be a big change,
8 | please open a new issue for discussion!
9 |
10 | ## Bug Fixes
11 |
12 | Please fix all code analyzer issues before a pull request.
13 |
--------------------------------------------------------------------------------
/Cleanup_Solution_For_Deployment.bat:
--------------------------------------------------------------------------------
1 | del /s /ah /f *.suo
2 | del /s /f *.user
3 | del /s /f *.cache
4 | del /s /f *.scc
5 | del /s /f *.vssscc
6 | del /s /f *.vspscc
7 | del /s /f *.keep
8 | del /s /ah /f vssver2.scc
9 |
10 | rd /s /q bin obj ClientBin _Resharper.* _Upgrade*
11 |
12 | del dirs.txt
13 | dir /s /b /ad bin > dirs.txt
14 | dir /s /b /ad obj >> dirs.txt
15 | dir /s /b /ad ClientBin >> dirs.txt
16 | dir /s /b /ad _Resharper.* >> dirs.txt
17 | dir /s /b /ad _Upgrade* >> dirs.txt
18 |
19 | for /f "delims=;" %%i in (dirs.txt) DO rd /s /q "%%i"
20 | del dirs.txt
21 |
--------------------------------------------------------------------------------
/GitHubPackages/SourceAdd.cmd:
--------------------------------------------------------------------------------
1 | nuget source Add -Name "GitHub" -Source "https://nuget.pkg.github.com/thudugala/index.json" -UserName thudugala -Password GH_TOKEN
--------------------------------------------------------------------------------
/GitHubPackages/nuget.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thudugala/Plugin.LocalNotification/a19ff2efbe3ded88deeb0e7af4981af3067af374/GitHubPackages/nuget.exe
--------------------------------------------------------------------------------
/GitHubPackages/publish.cmd:
--------------------------------------------------------------------------------
1 | nuget push "Plugin.LocalNotification.4.1.0.nupkg" -Source "GitHub"
2 |
3 | nuget push "Plugin.LocalNotification.4.1.0.snupkg" -Source "GitHub"
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Elvin (Tharindu)
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | 
4 | [](https://www.nuget.org/packages/Plugin.LocalNotification/)
5 | [](https://www.nuget.org/packages/Plugin.LocalNotification/)
6 |
7 | # Plugin.LocalNotification
8 | The local notification plugin provides a way to show local notifications from Xamarin.Forms / .Net MAUI apps.
9 |
10 | # Setup
11 |
12 | - `Plugin.LocalNotification` Available on NuGet: https://www.nuget.org/packages/Plugin.LocalNotification
13 | - #### .Net MAUI
14 | - Install Version 10.0.0 above
15 | - Install into your project
16 | - #### Xamarin.Forms (Support ended on May 1, 2024)
17 | - - Install Version 11.0.0 below
18 | - Install into your platform-specific projects (iOS/Android), and any .NET Standard 2.0/2.1 projects required for your app.
19 |
20 | ## Platform Support
21 |
22 | | Feature | Xamarin.iOS | Xamarin.Android | net8.0-ios (>= 11) | net8.0-android (>= 11) |
23 | | ----------------------------- | ----------- | --------------- | ------------------ | ---------------------- |
24 | | Build SDK | >= 10 | >= API 31 | >= 16 | >= API 34 |
25 | | Supported OS Version | >= 10 | >= API 19 | >= 11 | >= API 21 |
26 | | Title | ✅ | ✅ | ✅ | ✅ |
27 | | Description | ✅ | ✅ | ✅ | ✅ |
28 | | Subtitle | ✅ | ✅ | ✅ | ✅ |
29 | | [Schedule](https://github.com/thudugala/Plugin.LocalNotification/wiki/3.-Scheduled-Android-notifications) | ✅ | ✅ | ✅ | ✅ |
30 | | [Repeat](https://github.com/thudugala/Plugin.LocalNotification/wiki/4.-Repeat-Notification) | ✅ | ✅ | ✅ | ✅ |
31 | | [Custom Sounds](https://github.com/thudugala/Plugin.LocalNotification/wiki/Notification-with-a-Sound-File) | ✅ | ✅ | ✅ | ✅ |
32 | | Images | ✅ | ✅ | ✅ | ✅ |
33 | | [Notification Actions](https://github.com/thudugala/Plugin.LocalNotification/wiki/5.-Notification-with-Action) | ✅ | ✅ | ✅ | ✅ |
34 | | Clear Delivered Notifications | ✅ | ✅ | ✅ | ✅ |
35 | | Get Pending Notifications | ✅ | ✅ | ✅ | ✅ |
36 | | Get Delivered Notifications | ✅ | ✅ | ✅ | ✅ |
37 | | [Location Notifications](https://github.com/thudugala/Plugin.LocalNotification/wiki/Location-Notifications) | ✅ | ✅ | ✅ | ✅ |
38 |
39 | # Usage
40 |
41 | - [Xamarin.Forms](https://github.com/thudugala/Plugin.LocalNotification/wiki#xamarinforms-support-ended-on-may-1-2024)
42 | - [.Net MAUI](https://github.com/thudugala/Plugin.LocalNotification/wiki/1.-Usage-10.0.0--.Net-MAUI)
43 |
44 | # Screen Record
45 |
46 |
47 |
48 | # Video
49 |
50 | ### Xamarin.Forms (Support ended on May 1, 2024)
51 | [](https://www.youtube.com/watch?v=-Nj_TRPlx-8)
52 |
53 | ### .Net MAUI
54 | [](https://www.youtube.com/watch?v=dWdXXGa1_hI)
55 |
56 | # SourceLink Support
57 |
58 | In Visual Studio, confirm that SourceLink is enabled.
59 | Also, Turn off "Just My Code" since, well, this isn't your code.
60 |
61 | https://docs.microsoft.com/en-us/dotnet/standard/library-guidance/sourcelink
62 |
63 | # Limitations
64 |
65 | Only support iOS and Android for the moment.
66 |
67 | # Contributing
68 |
69 | Contributions are welcome. Feel free to file issues and pull requests on the repo and they'll be reviewed as time permits.
70 |
71 | ## Thank you
72 |
73 | - Thank you for the Icons by [DinosoftLabs](https://www.iconfinder.com/dinosoftlabs) and [Iconic Hub](https://www.iconfinder.com/iconic_hub)
74 | - Thank you for the sound file by [Notification sounds](https://notificationsounds.com/notification-sounds/good-things-happen-547)
75 | - Thank you for the tutorial video by [Gerald Versluis](https://www.youtube.com/channel/UCBBZ2kXWmd8eXlHg2wEaClw)
76 |
--------------------------------------------------------------------------------
/SECURITY.md:
--------------------------------------------------------------------------------
1 | # Security Policy
2 |
3 | ## Supported Versions
4 |
5 | Use this section to tell people about which versions of your project are
6 | currently being supported with security updates.
7 |
8 | | Version | Supported |
9 | | ------- | ------------------ |
10 | | 10.x.x | :white_check_mark: |
11 | | < 10.0.0 | :x: |
12 |
13 | ## Reporting a Vulnerability
14 |
15 | Use this section to tell people how to report a vulnerability.
16 |
17 | Tell them where to go, how often they can expect to get an update on a
18 | reported vulnerability, what to expect if the vulnerability is accepted or
19 | declined, etc.
20 |
--------------------------------------------------------------------------------
/Sample/Direct Maui/LocalNotification.Sample.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 17
4 | VisualStudioVersion = 17.0.31611.283
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LocalNotification.Sample", "LocalNotification.Sample\LocalNotification.Sample.csproj", "{0178E998-AF38-446B-B49E-2E9ECFC411DB}"
7 | EndProject
8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Plugin.LocalNotification", "..\..\Source\Plugin.LocalNotification\Plugin.LocalNotification.csproj", "{EE7B30C7-5307-4B01-A194-5ED815F2DF85}"
9 | EndProject
10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LocalNotification.UnitTests", "..\..\UnitTests\LocalNotification.UnitTests\LocalNotification.UnitTests.csproj", "{A939D821-32AE-EF0C-8ADB-7C5917E17072}"
11 | EndProject
12 | Global
13 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
14 | Debug|Any CPU = Debug|Any CPU
15 | Release|Any CPU = Release|Any CPU
16 | EndGlobalSection
17 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
18 | {0178E998-AF38-446B-B49E-2E9ECFC411DB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
19 | {0178E998-AF38-446B-B49E-2E9ECFC411DB}.Debug|Any CPU.Build.0 = Debug|Any CPU
20 | {0178E998-AF38-446B-B49E-2E9ECFC411DB}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
21 | {0178E998-AF38-446B-B49E-2E9ECFC411DB}.Release|Any CPU.ActiveCfg = Release|Any CPU
22 | {0178E998-AF38-446B-B49E-2E9ECFC411DB}.Release|Any CPU.Build.0 = Release|Any CPU
23 | {0178E998-AF38-446B-B49E-2E9ECFC411DB}.Release|Any CPU.Deploy.0 = Release|Any CPU
24 | {EE7B30C7-5307-4B01-A194-5ED815F2DF85}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
25 | {EE7B30C7-5307-4B01-A194-5ED815F2DF85}.Debug|Any CPU.Build.0 = Debug|Any CPU
26 | {EE7B30C7-5307-4B01-A194-5ED815F2DF85}.Release|Any CPU.ActiveCfg = Release|Any CPU
27 | {EE7B30C7-5307-4B01-A194-5ED815F2DF85}.Release|Any CPU.Build.0 = Release|Any CPU
28 | {A939D821-32AE-EF0C-8ADB-7C5917E17072}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
29 | {A939D821-32AE-EF0C-8ADB-7C5917E17072}.Debug|Any CPU.Build.0 = Debug|Any CPU
30 | {A939D821-32AE-EF0C-8ADB-7C5917E17072}.Release|Any CPU.ActiveCfg = Release|Any CPU
31 | {A939D821-32AE-EF0C-8ADB-7C5917E17072}.Release|Any CPU.Build.0 = Release|Any CPU
32 | EndGlobalSection
33 | GlobalSection(SolutionProperties) = preSolution
34 | HideSolutionNode = FALSE
35 | EndGlobalSection
36 | GlobalSection(ExtensibilityGlobals) = postSolution
37 | SolutionGuid = {61F7FB11-1E47-470C-91E2-47F8143E1572}
38 | EndGlobalSection
39 | EndGlobal
40 |
--------------------------------------------------------------------------------
/Sample/Direct Maui/LocalNotification.Sample/App.xaml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/Sample/Direct Maui/LocalNotification.Sample/App.xaml.cs:
--------------------------------------------------------------------------------
1 | namespace LocalNotification.Sample;
2 |
3 | public partial class App : Application
4 | {
5 | private readonly MainPage mainPage;
6 |
7 | public App(MainPage mainPage)
8 | {
9 | InitializeComponent();
10 | this.mainPage = mainPage;
11 | }
12 |
13 | protected override Window CreateWindow(IActivationState? activationState)
14 | {
15 | return new Window(new NavigationPage(mainPage));
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/Sample/Direct Maui/LocalNotification.Sample/AppShell.xaml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/Sample/Direct Maui/LocalNotification.Sample/AppShell.xaml.cs:
--------------------------------------------------------------------------------
1 | namespace LocalNotification.Sample;
2 |
3 | public partial class AppShell : Shell
4 | {
5 | public AppShell()
6 | {
7 | InitializeComponent();
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/Sample/Direct Maui/LocalNotification.Sample/LocalNotification.Sample.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net9.0-android;net9.0-ios
5 |
6 |
7 |
8 | Exe
9 | LocalNotification.Sample
10 | true
11 | true
12 | enable
13 | enable
14 |
15 | XC0103
16 | true
17 |
18 | en
19 |
20 |
21 | LocalNotification.Sample
22 |
23 |
24 | com.companyname.localnotification.sample
25 |
26 |
27 | 12.0
28 | 12
29 |
30 | 15.0
31 | 21.0
32 | 10.0.17763.0
33 |
34 | 10.0.17763.0
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
--------------------------------------------------------------------------------
/Sample/Direct Maui/LocalNotification.Sample/MainPage.xaml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
41 |
46 |
51 |
52 |
53 |
54 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
--------------------------------------------------------------------------------
/Sample/Direct Maui/LocalNotification.Sample/MauiProgram.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.Logging;
2 | using Plugin.LocalNotification;
3 | using Plugin.LocalNotification.AndroidOption;
4 | using Plugin.LocalNotification.iOSOption;
5 |
6 | namespace LocalNotification.Sample;
7 |
8 | public static class MauiProgram
9 | {
10 | public static MauiApp CreateMauiApp()
11 | {
12 | var builder = MauiApp.CreateBuilder();
13 | builder
14 | .UseMauiApp()
15 | .ConfigureFonts(fonts =>
16 | {
17 | fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
18 | fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
19 | })
20 | //.UseLocalNotification();
21 | .UseLocalNotification(config =>
22 | {
23 | config.AddCategory(new NotificationCategory(NotificationCategoryType.Status)
24 | {
25 | ActionList =
26 | [
27 | new(100)
28 | {
29 | Title = "Hello",
30 | Android =
31 | {
32 | LaunchAppWhenTapped = true,
33 | IconName =
34 | {
35 | ResourceName = "i2"
36 | }
37 | },
38 | IOS =
39 | {
40 | Action = iOSActionType.Foreground
41 | },
42 | Windows =
43 | {
44 | LaunchAppWhenTapped = true
45 | }
46 | },
47 | new(101)
48 | {
49 | Title = "Close",
50 | Android =
51 | {
52 | LaunchAppWhenTapped = false,
53 | IconName =
54 | {
55 | ResourceName = "i3"
56 | }
57 | },
58 | IOS =
59 | {
60 | Action = iOSActionType.Destructive
61 | },
62 | Windows =
63 | {
64 | LaunchAppWhenTapped = false
65 | }
66 | }
67 | ]
68 | })
69 | .AddAndroid(android =>
70 | {
71 | android.AddChannel(new NotificationChannelRequest
72 | {
73 | Sound = "good_things_happen"
74 | });
75 | })
76 | .AddiOS(iOS =>
77 | {
78 | #if IOS
79 | //iOS.SetCustomUserNotificationCenterDelegate(new CustomUserNotificationCenterDelegate());
80 | #endif
81 | });
82 | });
83 |
84 |
85 | #if DEBUG
86 | LocalNotificationCenter.LogLevel = LogLevel.Debug;
87 | //builder.Logging.AddDebug();
88 | builder.Logging.AddDebug();
89 | builder.Services.AddLogging(configure => configure.AddDebug());
90 | #endif
91 |
92 | builder.Services.AddTransient();
93 |
94 | return builder.Build();
95 | }
96 | }
97 |
--------------------------------------------------------------------------------
/Sample/Direct Maui/LocalNotification.Sample/NotificationPage.xaml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
11 |
12 |
15 |
16 |
19 |
20 |
23 |
24 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/Sample/Direct Maui/LocalNotification.Sample/NotificationPage.xaml.cs:
--------------------------------------------------------------------------------
1 | using Plugin.LocalNotification;
2 |
3 | namespace LocalNotification.Sample;
4 |
5 | public partial class NotificationPage : ContentPage
6 | {
7 | private readonly INotificationService _notificationService;
8 |
9 | public NotificationPage(INotificationService notificationService, int id, string message, int tabCount)
10 | {
11 | InitializeComponent();
12 | _notificationService = notificationService;
13 |
14 | IdLabel.Text = $"Id {id}";
15 | MessageLabel.Text = $"Message {message}";
16 | TapCountLabel.Text = $"Tap Count {tabCount}";
17 | }
18 |
19 | private async void Button_Clicked(object sender, EventArgs e)
20 | {
21 | var deliveredNotificationList = await _notificationService.GetDeliveredNotificationList();
22 |
23 | if (deliveredNotificationList != null)
24 | {
25 | await DisplayAlert("Delivered Notification Count", deliveredNotificationList.Count.ToString(), "OK");
26 | }
27 |
28 | await Navigation.PopModalAsync();
29 | }
30 | }
--------------------------------------------------------------------------------
/Sample/Direct Maui/LocalNotification.Sample/Platforms/Android/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/Sample/Direct Maui/LocalNotification.Sample/Platforms/Android/MainActivity.cs:
--------------------------------------------------------------------------------
1 | using Android.App;
2 | using Android.Content.PM;
3 | using Android.OS;
4 |
5 | namespace LocalNotification.Sample;
6 |
7 | [Activity(Theme = "@style/Maui.SplashTheme",
8 | MainLauncher = true,
9 | ConfigurationChanges = ConfigChanges.ScreenSize |
10 | ConfigChanges.Orientation |
11 | ConfigChanges.UiMode |
12 | ConfigChanges.ScreenLayout |
13 | ConfigChanges.SmallestScreenSize |
14 | ConfigChanges.Density,
15 | ShowForAllUsers = true,
16 | ShowWhenLocked = true,
17 | TurnScreenOn = true)]
18 | public class MainActivity : MauiAppCompatActivity
19 | {
20 |
21 | }
22 |
--------------------------------------------------------------------------------
/Sample/Direct Maui/LocalNotification.Sample/Platforms/Android/MainApplication.cs:
--------------------------------------------------------------------------------
1 | using Android.App;
2 | using Android.Runtime;
3 |
4 | namespace LocalNotification.Sample;
5 |
6 | [Application]
7 | public class MainApplication : MauiApplication
8 | {
9 | public MainApplication(IntPtr handle, JniHandleOwnership ownership)
10 | : base(handle, ownership)
11 | {
12 | }
13 |
14 | protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();
15 | }
16 |
--------------------------------------------------------------------------------
/Sample/Direct Maui/LocalNotification.Sample/Platforms/Android/ManifestInfo.cs:
--------------------------------------------------------------------------------
1 | //Permissions for android
2 |
3 | using Android;
4 | using Android.App;
5 |
6 | [assembly: UsesPermission(Manifest.Permission.WakeLock)]
7 | [assembly: UsesPermission(Manifest.Permission.ReceiveBootCompleted)]
8 | [assembly: UsesPermission(Manifest.Permission.Vibrate)]
9 |
10 | [assembly: UsesPermission(Manifest.Permission.PostNotifications)]
11 |
--------------------------------------------------------------------------------
/Sample/Direct Maui/LocalNotification.Sample/Platforms/Android/Resources/raw/good_things_happen.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thudugala/Plugin.LocalNotification/a19ff2efbe3ded88deeb0e7af4981af3067af374/Sample/Direct Maui/LocalNotification.Sample/Platforms/Android/Resources/raw/good_things_happen.mp3
--------------------------------------------------------------------------------
/Sample/Direct Maui/LocalNotification.Sample/Platforms/Android/Resources/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #512BD4
4 | #2B0B98
5 | #2B0B98
6 |
--------------------------------------------------------------------------------
/Sample/Direct Maui/LocalNotification.Sample/Platforms/Windows/App.xaml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/Sample/Direct Maui/LocalNotification.Sample/Platforms/Windows/App.xaml.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.UI.Xaml;
2 |
3 | // To learn more about WinUI, the WinUI project structure,
4 | // and more about our project templates, see: http://aka.ms/winui-project-info.
5 |
6 | namespace LocalNotification.Sample.WinUI;
7 |
8 | ///
9 | /// Provides application-specific behavior to supplement the default Application class.
10 | ///
11 | public partial class App : MauiWinUIApplication
12 | {
13 | ///
14 | /// Initializes the singleton application object. This is the first line of authored code
15 | /// executed, and as such is the logical equivalent of main() or WinMain().
16 | ///
17 | public App()
18 | {
19 | this.InitializeComponent();
20 | }
21 |
22 | protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();
23 | }
24 |
25 |
--------------------------------------------------------------------------------
/Sample/Direct Maui/LocalNotification.Sample/Platforms/Windows/Package.appxmanifest:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | $placeholder$
17 | User Name
18 | $placeholder$.png
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
--------------------------------------------------------------------------------
/Sample/Direct Maui/LocalNotification.Sample/Platforms/Windows/app.manifest:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
11 | true/PM
12 | PerMonitorV2, PerMonitor
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/Sample/Direct Maui/LocalNotification.Sample/Platforms/iOS/AppDelegate.cs:
--------------------------------------------------------------------------------
1 | using Foundation;
2 |
3 | namespace LocalNotification.Sample;
4 |
5 | [Register("AppDelegate")]
6 | public class AppDelegate : MauiUIApplicationDelegate
7 | {
8 | protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();
9 | }
10 |
--------------------------------------------------------------------------------
/Sample/Direct Maui/LocalNotification.Sample/Platforms/iOS/CustomUserNotificationCenterDelegate.cs:
--------------------------------------------------------------------------------
1 | using Plugin.LocalNotification;
2 | using Plugin.LocalNotification.Platforms;
3 | using UserNotifications;
4 |
5 | namespace LocalNotification.Sample;
6 |
7 | public class CustomUserNotificationCenterDelegate : UserNotificationCenterDelegate
8 | {
9 | public override void DidReceiveNotificationResponse(UNUserNotificationCenter center,
10 | UNNotificationResponse response,
11 | Action completionHandler)
12 | {
13 | // If the notification is typed Plugin.LocalNotification.NotificationRequest
14 | // Call the base method else handle it by yourself.
15 |
16 | var notificationRequest = LocalNotificationCenter.GetRequest(response.Notification.Request.Content);
17 | if (notificationRequest is not null)
18 | {
19 | base.DidReceiveNotificationResponse(center, response, completionHandler);
20 | }
21 | else
22 | {
23 | // Write your code here
24 | }
25 | }
26 |
27 | public override void WillPresentNotification(UNUserNotificationCenter center, UNNotification notification,
28 | Action completionHandler)
29 | {
30 | // If the notification is typed Plugin.LocalNotification.NotificationRequest
31 | // Call the base method else handle it by yourself.
32 |
33 | if (notification is null)
34 | {
35 | return;
36 | }
37 |
38 | var notificationRequest = LocalNotificationCenter.GetRequest(notification.Request.Content);
39 |
40 | if (notificationRequest is not null)
41 | {
42 | base.WillPresentNotification(center, notification, completionHandler);
43 | }
44 | else
45 | {
46 | // Write your code here
47 | }
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/Sample/Direct Maui/LocalNotification.Sample/Platforms/iOS/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | LSRequiresIPhoneOS
6 |
7 | UIDeviceFamily
8 |
9 | 1
10 | 2
11 |
12 | UIRequiredDeviceCapabilities
13 |
14 | arm64
15 |
16 | UISupportedInterfaceOrientations
17 |
18 | UIInterfaceOrientationPortrait
19 | UIInterfaceOrientationLandscapeLeft
20 | UIInterfaceOrientationLandscapeRight
21 |
22 | UISupportedInterfaceOrientations~ipad
23 |
24 | UIInterfaceOrientationPortrait
25 | UIInterfaceOrientationPortraitUpsideDown
26 | UIInterfaceOrientationLandscapeLeft
27 | UIInterfaceOrientationLandscapeRight
28 |
29 | XSAppIconAssets
30 | Assets.xcassets/appicon.appiconset
31 |
32 |
33 |
--------------------------------------------------------------------------------
/Sample/Direct Maui/LocalNotification.Sample/Platforms/iOS/Program.cs:
--------------------------------------------------------------------------------
1 | using ObjCRuntime;
2 | using UIKit;
3 |
4 | namespace LocalNotification.Sample;
5 |
6 | public class Program
7 | {
8 | // This is the main entry point of the application.
9 | static void Main(string[] args)
10 | {
11 | // if you want to use a different Application Delegate class from "AppDelegate"
12 | // you can specify it here.
13 | UIApplication.Main(args, null, typeof(AppDelegate));
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/Sample/Direct Maui/LocalNotification.Sample/Platforms/iOS/Resources/good_things_happen.aiff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thudugala/Plugin.LocalNotification/a19ff2efbe3ded88deeb0e7af4981af3067af374/Sample/Direct Maui/LocalNotification.Sample/Platforms/iOS/Resources/good_things_happen.aiff
--------------------------------------------------------------------------------
/Sample/Direct Maui/LocalNotification.Sample/Resources/AppIcon/appiconfg.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/Sample/Direct Maui/LocalNotification.Sample/Resources/Fonts/OpenSans-Regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thudugala/Plugin.LocalNotification/a19ff2efbe3ded88deeb0e7af4981af3067af374/Sample/Direct Maui/LocalNotification.Sample/Resources/Fonts/OpenSans-Regular.ttf
--------------------------------------------------------------------------------
/Sample/Direct Maui/LocalNotification.Sample/Resources/Fonts/OpenSans-Semibold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thudugala/Plugin.LocalNotification/a19ff2efbe3ded88deeb0e7af4981af3067af374/Sample/Direct Maui/LocalNotification.Sample/Resources/Fonts/OpenSans-Semibold.ttf
--------------------------------------------------------------------------------
/Sample/Direct Maui/LocalNotification.Sample/Resources/Images/i2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thudugala/Plugin.LocalNotification/a19ff2efbe3ded88deeb0e7af4981af3067af374/Sample/Direct Maui/LocalNotification.Sample/Resources/Images/i2.png
--------------------------------------------------------------------------------
/Sample/Direct Maui/LocalNotification.Sample/Resources/Images/i3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thudugala/Plugin.LocalNotification/a19ff2efbe3ded88deeb0e7af4981af3067af374/Sample/Direct Maui/LocalNotification.Sample/Resources/Images/i3.png
--------------------------------------------------------------------------------
/Sample/Direct Maui/LocalNotification.Sample/Resources/Images/icon1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thudugala/Plugin.LocalNotification/a19ff2efbe3ded88deeb0e7af4981af3067af374/Sample/Direct Maui/LocalNotification.Sample/Resources/Images/icon1.png
--------------------------------------------------------------------------------
/Sample/Direct Maui/LocalNotification.Sample/Resources/Raw/AboutAssets.txt:
--------------------------------------------------------------------------------
1 | Any raw assets you want to be deployed with your application can be placed in
2 | this directory (and child directories). Deployment of the asset to your application
3 | is automatically handled by the following `MauiAsset` Build Action within your `.csproj`.
4 |
5 |
6 |
7 | These files will be deployed with you package and will be accessible using Essentials:
8 |
9 | async Task LoadMauiAsset()
10 | {
11 | using var stream = await FileSystem.OpenAppPackageFileAsync("AboutAssets.txt");
12 | using var reader = new StreamReader(stream);
13 |
14 | var contents = reader.ReadToEnd();
15 | }
16 |
--------------------------------------------------------------------------------
/Sample/Direct Maui/LocalNotification.Sample/Resources/Raw/appicon1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thudugala/Plugin.LocalNotification/a19ff2efbe3ded88deeb0e7af4981af3067af374/Sample/Direct Maui/LocalNotification.Sample/Resources/Raw/appicon1.png
--------------------------------------------------------------------------------
/Sample/Direct Maui/LocalNotification.Sample/Resources/Splash/splash.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/Sample/Direct Maui/LocalNotification.Sample/Resources/Styles/Colors.xaml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
7 | #512BD4
8 | #DFD8F7
9 | #2B0B98
10 | White
11 | Black
12 | #E1E1E1
13 | #C8C8C8
14 | #ACACAC
15 | #919191
16 | #6E6E6E
17 | #404040
18 | #212121
19 | #141414
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 | #F7B548
35 | #FFD590
36 | #FFE5B9
37 | #28C2D1
38 | #7BDDEF
39 | #C3F2F4
40 | #3E8EED
41 | #72ACF1
42 | #A7CBF6
43 |
44 |
--------------------------------------------------------------------------------
/Screenshots/AndroidNotificationAction.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thudugala/Plugin.LocalNotification/a19ff2efbe3ded88deeb0e7af4981af3067af374/Screenshots/AndroidNotificationAction.png
--------------------------------------------------------------------------------
/Screenshots/AndroidNotificationIcon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thudugala/Plugin.LocalNotification/a19ff2efbe3ded88deeb0e7af4981af3067af374/Screenshots/AndroidNotificationIcon.png
--------------------------------------------------------------------------------
/Screenshots/SoundFileMap.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thudugala/Plugin.LocalNotification/a19ff2efbe3ded88deeb0e7af4981af3067af374/Screenshots/SoundFileMap.png
--------------------------------------------------------------------------------
/Screenshots/iOSNotificationAction.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thudugala/Plugin.LocalNotification/a19ff2efbe3ded88deeb0e7af4981af3067af374/Screenshots/iOSNotificationAction.png
--------------------------------------------------------------------------------
/Screenshots/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thudugala/Plugin.LocalNotification/a19ff2efbe3ded88deeb0e7af4981af3067af374/Screenshots/icon.png
--------------------------------------------------------------------------------
/Screenshots/icon64.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thudugala/Plugin.LocalNotification/a19ff2efbe3ded88deeb0e7af4981af3067af374/Screenshots/icon64.png
--------------------------------------------------------------------------------
/Screenshots/screenRecord.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thudugala/Plugin.LocalNotification/a19ff2efbe3ded88deeb0e7af4981af3067af374/Screenshots/screenRecord.gif
--------------------------------------------------------------------------------
/Source/Plugin.LocalNotification.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 17
4 | VisualStudioVersion = 17.13.35931.197 d17.13
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Plugin.LocalNotification", "Plugin.LocalNotification\Plugin.LocalNotification.csproj", "{89474925-B4A7-4CD0-B3AD-EF6C24C035FE}"
7 | EndProject
8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LocalNotification.UnitTests", "..\UnitTests\LocalNotification.UnitTests\LocalNotification.UnitTests.csproj", "{F769723E-2E0B-4CF4-87F1-9F3AD85B74FB}"
9 | EndProject
10 | Global
11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
12 | Debug|Any CPU = Debug|Any CPU
13 | Release|Any CPU = Release|Any CPU
14 | EndGlobalSection
15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
16 | {89474925-B4A7-4CD0-B3AD-EF6C24C035FE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
17 | {89474925-B4A7-4CD0-B3AD-EF6C24C035FE}.Debug|Any CPU.Build.0 = Debug|Any CPU
18 | {89474925-B4A7-4CD0-B3AD-EF6C24C035FE}.Release|Any CPU.ActiveCfg = Release|Any CPU
19 | {89474925-B4A7-4CD0-B3AD-EF6C24C035FE}.Release|Any CPU.Build.0 = Release|Any CPU
20 | {F769723E-2E0B-4CF4-87F1-9F3AD85B74FB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
21 | {F769723E-2E0B-4CF4-87F1-9F3AD85B74FB}.Debug|Any CPU.Build.0 = Debug|Any CPU
22 | {F769723E-2E0B-4CF4-87F1-9F3AD85B74FB}.Release|Any CPU.ActiveCfg = Release|Any CPU
23 | {F769723E-2E0B-4CF4-87F1-9F3AD85B74FB}.Release|Any CPU.Build.0 = Release|Any CPU
24 | EndGlobalSection
25 | GlobalSection(SolutionProperties) = preSolution
26 | HideSolutionNode = FALSE
27 | EndGlobalSection
28 | GlobalSection(ExtensibilityGlobals) = postSolution
29 | SolutionGuid = {B894D7F9-202D-46F1-90D9-2785AA08B5A7}
30 | EndGlobalSection
31 | EndGlobal
32 |
--------------------------------------------------------------------------------
/Source/Plugin.LocalNotification/AndroidOption/AndroidAction.cs:
--------------------------------------------------------------------------------
1 | namespace Plugin.LocalNotification.AndroidOption;
2 |
3 | ///
4 | ///
5 | ///
6 | public class AndroidAction
7 | {
8 | ///
9 | ///
10 | ///
11 | public AndroidIcon IconName { get; set; } = new();
12 |
13 | ///
14 | /// Default is false
15 | ///
16 | public bool LaunchAppWhenTapped { get; set; } = false;
17 |
18 | ///
19 | ///
20 | ///
21 | public AndroidPendingIntentFlags PendingIntentFlags { get; set; } = AndroidPendingIntentFlags.CancelCurrent;
22 | }
--------------------------------------------------------------------------------
/Source/Plugin.LocalNotification/AndroidOption/AndroidAlarmType.cs:
--------------------------------------------------------------------------------
1 | namespace Plugin.LocalNotification.AndroidOption;
2 |
3 | ///
4 | ///
5 | ///
6 | public enum AndroidAlarmType
7 | {
8 | ///
9 | /// Alarm time in System.currentTimeMillis() (wall clock time in UTC).
10 | /// This alarm does not wake the device up; if it goes off while the device is asleep, it will not be delivered until the next time the device wakes up.
11 | ///
12 | Rtc,
13 |
14 | ///
15 | /// Alarm time in System.currentTimeMillis() (wall clock time in UTC), which will wake up the device when it goes off.
16 | ///
17 | RtcWakeup,
18 |
19 | ///
20 | /// Alarm time in SystemClock.elapsedRealtime() (time since boot, including sleep).
21 | /// This alarm does not wake the device up; if it goes off while the device is asleep, it will not be delivered until the next time the device wakes up.
22 | ///
23 | ElapsedRealtime,
24 |
25 | ///
26 | /// Alarm time in SystemClock.elapsedRealtime() (time since boot, including sleep), which will wake up the device when it goes off.
27 | ///
28 | ElapsedRealtimeWakeup
29 | }
--------------------------------------------------------------------------------
/Source/Plugin.LocalNotification/AndroidOption/AndroidColor.cs:
--------------------------------------------------------------------------------
1 | namespace Plugin.LocalNotification.AndroidOption;
2 |
3 | ///
4 | /// the notification icon and application name will have the provided ARGB color
5 | ///
6 | public class AndroidColor
7 | {
8 | ///
9 | ///
10 | ///
11 | public AndroidColor()
12 | {
13 | }
14 |
15 | ///
16 | ///
17 | ///
18 | ///
19 | public AndroidColor(int argb) => Argb = argb;
20 |
21 | ///
22 | ///
23 | ///
24 | ///
25 | public AndroidColor(string resourceName) => ResourceName = resourceName;
26 |
27 | ///
28 | /// if set, will ignore ResourceName
29 | ///
30 | public int? Argb { get; set; }
31 |
32 | ///
33 | ///
34 | ///
35 | public string ResourceName { get; set; } = string.Empty;
36 | }
--------------------------------------------------------------------------------
/Source/Plugin.LocalNotification/AndroidOption/AndroidGeofenceOptions.cs:
--------------------------------------------------------------------------------
1 | namespace Plugin.LocalNotification.AndroidOption;
2 |
3 | ///
4 | ///
5 | ///
6 | public class AndroidGeofenceOptions
7 | {
8 | ///
9 | /// Sets the expiration duration of geofence.
10 | /// This geofence will be removed automatically after this period of time.
11 | /// Time for this proximity alert, in milliseconds, or -1 to indicate no expiration.
12 | /// When positive, this geofence will be removed automatically after this amount of time.
13 | ///
14 | public long ExpirationDurationInMilliseconds { get; set; } = -1;
15 |
16 | ///
17 | /// If loitering delay is set the geofence service will send a alert roughly after user enters a geofence if the user stays inside the geofence during this period of time.
18 | /// If the user exits from the geofence in this amount of time, alert won't be sent.
19 | /// The delay for confirming dwelling, in milliseconds
20 | ///
21 | public int LoiteringDelayMilliseconds { get; set; }
22 |
23 | ///
24 | /// Sets the best-effort notification responsiveness of the geofence.
25 | /// Defaults to 0.
26 | /// Setting a big responsiveness value, for example 5 minutes, can save power significantly.
27 | /// However, setting a very small responsiveness value, for example 5 seconds,
28 | /// doesn't necessarily mean you will get notified right after the user enters a geofence:
29 | /// internally, the geofence might adjust the responsiveness value to save power when needed.
30 | /// (milliseconds) defines the best-effort description of how soon should the callback be called when the transition associated with the Geofence is triggered.
31 | /// For instance, if set to 300000 milliseconds the callback will be called 5 minutes within entering or exiting the geofence.
32 | ///
33 | public int ResponsivenessMilliseconds { get; set; }
34 | }
--------------------------------------------------------------------------------
/Source/Plugin.LocalNotification/AndroidOption/AndroidIcon.cs:
--------------------------------------------------------------------------------
1 | namespace Plugin.LocalNotification.AndroidOption;
2 |
3 | ///
4 | ///
5 | ///
6 | public class AndroidIcon
7 | {
8 | ///
9 | ///
10 | ///
11 | public AndroidIcon()
12 | {
13 | }
14 |
15 | ///
16 | ///
17 | ///
18 | ///
19 | public AndroidIcon(string resourceName)
20 | {
21 | ResourceName = resourceName;
22 | }
23 |
24 | ///
25 | ///
26 | ///
27 | ///
28 | ///
29 | public AndroidIcon(string resourceName, string? type)
30 | {
31 | ResourceName = resourceName;
32 | Type = type ?? DefaultType;
33 | }
34 |
35 | ///
36 | /// Default Group Name
37 | ///
38 | public static string DefaultType => "drawable";
39 |
40 | ///
41 | /// The name of the desired resource
42 | ///
43 | public string ResourceName { get; set; } = string.Empty;
44 |
45 | ///
46 | /// Optional default resource type to find, if "type/" is
47 | /// not included in the name. Can be null to require an
48 | /// explicit type.
49 | ///
50 | public string Type { get; set; } = DefaultType;
51 | }
--------------------------------------------------------------------------------
/Source/Plugin.LocalNotification/AndroidOption/AndroidImportance.cs:
--------------------------------------------------------------------------------
1 | namespace Plugin.LocalNotification.AndroidOption;
2 |
3 | ///
4 | ///
5 | ///
6 | public enum AndroidImportance
7 | {
8 | ///
9 | /// Value signifying that the user has not expressed an importance.
10 | /// This value is for persisting preferences, and should never be associated with an actual notification.
11 | ///
12 | Unspecified = -1000,
13 |
14 | ///
15 | /// Does not show in the shade
16 | ///
17 | None = 0,
18 |
19 | ///
20 | /// Only shows in the shade, below the fold.
21 | /// This should not be used with Service.startForeground since a foreground service is supposed to be something the user cares about so it does not make semantic sense to mark its notification as minimum importance.
22 | /// If you do this as of Android version Build.VERSION_CODES.O (26),
23 | /// the system will show a higher-priority notification about your app running in the background.
24 | ///
25 | Min = 1,
26 |
27 | ///
28 | /// Shows in the shade, and potentially in the status bar, but is not audibly intrusive.
29 | ///
30 | Low = 2,
31 |
32 | ///
33 | /// Shows everywhere, makes noise, but does not visually intrude
34 | ///
35 | Default = 3,
36 |
37 | ///
38 | /// Shows everywhere, makes noise and peeks. May use full screen intents
39 | ///
40 | High = 4,
41 |
42 | ///
43 | /// Unused
44 | ///
45 | Max = 5
46 | }
--------------------------------------------------------------------------------
/Source/Plugin.LocalNotification/AndroidOption/AndroidLaunch.cs:
--------------------------------------------------------------------------------
1 | namespace Plugin.LocalNotification.AndroidOption;
2 |
3 | ///
4 | /// Launch the App instead of posting the notification to the status bar.
5 | /// Only for use with extremely high-priority notifications demanding the user's immediate attention,
6 | /// such as an incoming phone call or alarm clock that the user has explicitly set to a particular time.
7 | /// If this facility is used for something else, please give the user an option to turn it off and use a normal notification, as this can be extremely disruptive.
8 | /// The system UI may choose to display a heads-up notification, instead of launching this app, while the user is using the device.
9 | /// Apps targeting Android Q 10 (29) and above will have to request a permission (Manifest.permission.USE_FULL_SCREEN_INTENT) in order to use full screen intents.
10 | /// To be launched, the notification must also be posted to a channel with importance level set to IMPORTANCE_HIGH or higher.
11 | ///
12 | public class AndroidLaunch
13 | {
14 | ///
15 | /// Passing true will cause this notification to be sent even if other notifications are suppressed. the default is true.
16 | ///
17 | public bool InHighPriority { get; set; } = true;
18 | }
19 |
--------------------------------------------------------------------------------
/Source/Plugin.LocalNotification/AndroidOption/AndroidLocalNotificationBuilder.cs:
--------------------------------------------------------------------------------
1 | namespace Plugin.LocalNotification.AndroidOption;
2 |
3 | ///
4 | ///
5 | ///
6 | public class AndroidLocalNotificationBuilder : IAndroidLocalNotificationBuilder
7 | {
8 | internal IList ChannelRequestList { get; } = new List();
9 |
10 | internal IList GroupChannelRequestList { get; } = new List();
11 |
12 | ///
13 | public IAndroidLocalNotificationBuilder AddChannel(NotificationChannelRequest channelRequest)
14 | {
15 | ChannelRequestList.Add(channelRequest);
16 | return this;
17 | }
18 |
19 | ///
20 | public IAndroidLocalNotificationBuilder AddChannelGroup(NotificationChannelGroupRequest groupChannelRequest)
21 | {
22 | GroupChannelRequestList.Add(groupChannelRequest);
23 | return this;
24 | }
25 | }
--------------------------------------------------------------------------------
/Source/Plugin.LocalNotification/AndroidOption/AndroidNotificationPermission.cs:
--------------------------------------------------------------------------------
1 | namespace Plugin.LocalNotification.AndroidOption;
2 |
3 | ///
4 | ///
5 | ///
6 | public class AndroidNotificationPermission
7 | {
8 | ///
9 | /// Defualt is false
10 | ///
11 | public bool RequestPermissionToScheduleExactAlarm { get; set; } = false;
12 | }
13 |
--------------------------------------------------------------------------------
/Source/Plugin.LocalNotification/AndroidOption/AndroidOptions.cs:
--------------------------------------------------------------------------------
1 | namespace Plugin.LocalNotification.AndroidOption;
2 |
3 | ///
4 | /// NotificationRequest for Android
5 | ///
6 | public class AndroidOptions
7 | {
8 | ///
9 | /// Default Channel Id.
10 | ///
11 | public static string DefaultChannelId => "Plugin.LocalNotification.GENERAL";
12 |
13 | ///
14 | /// Default Channel Name
15 | ///
16 | public static string DefaultChannelName => "General";
17 |
18 | ///
19 | /// Default Group Id
20 | ///
21 | public static string DefaultGroupId => "Plugin.LocalNotification.Group";
22 |
23 | ///
24 | /// Default Group Name
25 | ///
26 | public static string DefaultGroupName => "GeneralGroup";
27 |
28 | ///
29 | /// Setting this flag will make it so the notification is automatically canceled when the user clicks it in the panel.
30 | /// Default is true
31 | ///
32 | public bool AutoCancel { get; set; } = true;
33 |
34 | ///
35 | /// Sets or gets, The id of the channel. Must be unique per package. The value may be truncated if it is too lon
36 | /// Use this to target the Notification Channel.
37 | ///
38 | public string ChannelId { get; set; } = DefaultChannelId;
39 |
40 | ///
41 | /// If set, the notification icon and application name will have the provided ARGB color.
42 | ///
43 | public AndroidColor Color { get; set; } = new();
44 |
45 | ///
46 | /// if Set, find the icon by name from drawable and set it has the Large Icon to use in the notification layouts.
47 | /// if not set, application Icon will we used.
48 | ///
49 | public AndroidIcon IconLargeName { get; set; } = new();
50 |
51 | ///
52 | /// if Set, find the icon by name from drawable and set it has the Small Icon to use in the notification layouts.
53 | /// if not set, application Icon will we used.
54 | ///
55 | public AndroidIcon IconSmallName { get; set; } = new();
56 |
57 | ///
58 | /// Set this notification to be the group summary for a group of notifications.
59 | /// Grouped notifications may display in a cluster or stack on devices which support such rendering
60 | ///
61 | public bool IsGroupSummary { get; set; }
62 |
63 | ///
64 | /// Launch the App instead of posting the notification to the status bar.
65 | /// Only for use with extremely high-priority notifications demanding the user's immediate attention,
66 | /// such as an incoming phone call or alarm clock that the user has explicitly set to a particular time.
67 | /// If this facility is used for something else, please give the user an option to turn it off and use a normal notification, as this can be extremely disruptive.
68 | /// The system UI may choose to display a heads-up notification, instead of launching this app, while the user is using the device.
69 | /// Apps targeting Android Q 10 (29) and above will have to request a permission (Manifest.permission.USE_FULL_SCREEN_INTENT) in order to use full screen intents.
70 | /// To be launched, the notification must also be posted to a channel with importance level set to IMPORTANCE_HIGH or higher.
71 | ///
72 | public AndroidLaunch? LaunchApp { get; set; }
73 |
74 | ///
75 | /// Default is true
76 | ///
77 | public bool LaunchAppWhenTapped { get; set; } = true;
78 |
79 | ///
80 | /// If set, the LED will have the provided ARGB color.
81 | ///
82 | public int? LedColor { get; set; }
83 |
84 | ///
85 | /// Set whether this is an ongoing notification.
86 | /// Ongoing notifications differ from regular notifications in the following ways,
87 | /// Ongoing notifications are sorted above the regular notifications in the notification panel.
88 | /// Ongoing notifications do not have an 'X' close button, and are not affected by the "Clear all" button.
89 | /// Default is false
90 | ///
91 | public bool Ongoing { get; set; }
92 |
93 | ///
94 | ///
95 | ///
96 | public AndroidPendingIntentFlags PendingIntentFlags { get; set; } = AndroidPendingIntentFlags.UpdateCurrent;
97 |
98 | ///
99 | /// Set the relative priority for this notification.
100 | /// In Android, Only used if Android Api below 26.
101 | /// Use NotificationCenter.CreateNotificationChannel when Android Api equal or above 26
102 | ///
103 | public AndroidPriority Priority { get; set; } = AndroidPriority.Default;
104 |
105 | ///
106 | /// Set the progress this notification represents. The platform template will represent this using a ProgressBar.
107 | ///
108 | public AndroidProgressBar? ProgressBar { get; set; }
109 |
110 | ///
111 | /// Specifies the time at which this notification should be canceled, if it is not already canceled.
112 | ///
113 | public TimeSpan? TimeoutAfter { get; set; }
114 |
115 | ///
116 | /// Vibrate with a given pattern.
117 | /// Pass in an array of int`s that are the durations for which to turn on or off the vibrator in milliseconds.
118 | /// The first value indicates the number of milliseconds to wait before turning the vibrator on.
119 | /// The next value indicates the number of milliseconds for which to keep the vibrator on before turning it off.
120 | /// Subsequent values alternate between durations in milliseconds to turn the vibrator off or to turn the vibrator on.
121 | ///
122 | /// This method was deprecated in API level 26. use NotificationChannel
123 | ///
124 | public long[] VibrationPattern { get; set; } = [];
125 |
126 | ///
127 | /// Sphere of visibility of this notification,
128 | /// which affects how and when the SystemUI reveals the notification's presence and contents in untrusted situations (namely, on the secure lockscreen).
129 | ///
130 | public AndroidVisibilityType VisibilityType { get; set; } = AndroidVisibilityType.Private;
131 |
132 | ///
133 | /// DateTime set with When is shown in the content view.
134 | ///
135 | public DateTime? When { get; set; }
136 | }
--------------------------------------------------------------------------------
/Source/Plugin.LocalNotification/AndroidOption/AndroidPendingIntentFlags.cs:
--------------------------------------------------------------------------------
1 | namespace Plugin.LocalNotification.AndroidOption;
2 |
3 | ///
4 | ///
5 | ///
6 | [Flags]
7 | public enum AndroidPendingIntentFlags
8 | {
9 | ///
10 | /// Flag indicating that if the described PendingIntent already exists, the current one should be canceled before generating a new one.
11 | /// You can use this to retrieve a new PendingIntent when you are only changing the extra data in the Intent;
12 | /// by canceling the previous pending intent, this ensures that only entities given the new data will be able to launch it.
13 | /// If this assurance is not an issue, consider
14 | ///
15 | CancelCurrent = 0x10000000,
16 |
17 | ///
18 | /// Flag indicating that the created PendingIntent should be immutable.
19 | /// This means that the additional intent argument passed to the send methods to fill in unpopulated properties of this intent will be ignored.
20 | ///
21 | Immutable = 0x4000000,
22 |
23 | ///
24 | /// Flag indicating that if the described PendingIntent does not already exist, then simply return null instead of creating it.
25 | ///
26 | NoCreate = 0x20000000,
27 |
28 | ///
29 | /// Flag indicating that this PendingIntent can be used only once.
30 | ///
31 | OneShot = 0x40000000,
32 |
33 | ///
34 | /// Flag indicating that if the described PendingIntent already exists, then keep it but replace its extra data with what is in this new Intent.
35 | /// This can be used if you are creating intents where only the extras change,
36 | /// and don't care that any entities that received your previous PendingIntent will be able to launch it with your new extras even if they are not explicitly given to it.
37 | ///
38 | UpdateCurrent = 0x8000000
39 | }
--------------------------------------------------------------------------------
/Source/Plugin.LocalNotification/AndroidOption/AndroidPriority.cs:
--------------------------------------------------------------------------------
1 | namespace Plugin.LocalNotification.AndroidOption;
2 |
3 | ///
4 | /// Set the relative priority for this notification.
5 | /// Priority is an indication of how much of the user's valuable attention should be consumed by this notification.
6 | /// Low-priority notifications may be hidden from the user in certain situations, while the user might be interrupted for a higher-priority notification.
7 | /// The system sets a notification's priority based on various factors including the setPriority value.
8 | /// The effect may differ slightly on different platforms.
9 | ///
10 | public enum AndroidPriority
11 | {
12 | ///
13 | /// Lowest notification priority, these items might not be shown to the user except under special circumstances, such as detailed notification logs.
14 | ///
15 | Min = -2,
16 |
17 | ///
18 | /// Lower notification priority, for items that are less important.
19 | /// The UI may choose to show these items smaller, or at a different position in the list, compared with your app's Default items.
20 | ///
21 | Low = -1,
22 |
23 | ///
24 | /// If your application does not prioritize its own notifications, use this value for all notifications.
25 | ///
26 | Default = 0,
27 |
28 | ///
29 | /// Higher notification priority, for more important notifications or alerts.
30 | /// The UI may choose to show these items larger, or at a different position in notification lists, compared with your app's Default items.
31 | ///
32 | High = 1,
33 |
34 | ///
35 | /// Highest notification priority, for your application's most important items that require the user's prompt attention or input.
36 | ///
37 | Max = 2,
38 | }
--------------------------------------------------------------------------------
/Source/Plugin.LocalNotification/AndroidOption/AndroidProgressBar.cs:
--------------------------------------------------------------------------------
1 | namespace Plugin.LocalNotification.AndroidOption;
2 |
3 | ///
4 | /// Set the progress this notification represents. The platform template will represent this using a ProgressBar.
5 | ///
6 | public class AndroidProgressBar
7 | {
8 | ///
9 | /// Set whether this progress bar is in indeterminate mode
10 | ///
11 | public bool IsIndeterminate { get; set; }
12 |
13 | ///
14 | /// Set Upper limit of this progress bar's range
15 | ///
16 | public int Max { get; set; }
17 |
18 | ///
19 | /// Set progress bar's current level of progress
20 | ///
21 | public int Progress { get; set; }
22 | }
--------------------------------------------------------------------------------
/Source/Plugin.LocalNotification/AndroidOption/AndroidScheduleOptions.cs:
--------------------------------------------------------------------------------
1 | namespace Plugin.LocalNotification.AndroidOption;
2 |
3 | ///
4 | /// NotificationRequestSchedule for Android
5 | ///
6 | public class AndroidScheduleOptions
7 | {
8 | ///
9 | /// Default is RtcWakeup
10 | ///
11 | public AndroidAlarmType AlarmType { get; set; } = AndroidAlarmType.RtcWakeup;
12 |
13 | ///
14 | /// In Android, do not Schedule or show notification if NotifyTime is earlier than DateTime.Now and this time delay.
15 | /// Default is 1 min
16 | ///
17 | public TimeSpan AllowedDelay { get; set; } = TimeSpan.FromMinutes(1);
18 |
19 | ///
20 | /// internal use, only for Android
21 | ///
22 | ///
23 | internal DateTime? GetNextNotifyTimeForRepeat(DateTime? notifyTime, NotificationRepeat repeatType, TimeSpan? notifyRepeatInterval)
24 | {
25 | // NotifyTime does not change for Repeat request
26 | if (notifyTime is null)
27 | {
28 | return null;
29 | }
30 |
31 | var repeatInterval = GetNotifyRepeatInterval(repeatType, notifyRepeatInterval);
32 | if (repeatInterval == TimeSpan.Zero)
33 | {
34 | return null;
35 | }
36 |
37 | var newNotifyTime = notifyTime.Value.Add(repeatInterval);
38 | var nowTime = DateTime.Now.AddSeconds(10);
39 | while (newNotifyTime <= nowTime)
40 | {
41 | newNotifyTime = newNotifyTime.Add(repeatInterval);
42 | }
43 | return newNotifyTime;
44 | }
45 |
46 | ///
47 | /// internal use, only for Android
48 | ///
49 | ///
50 | internal TimeSpan GetNotifyRepeatInterval(NotificationRepeat repeatType, TimeSpan? notifyRepeatInterval)
51 | {
52 | var repeatInterval = TimeSpan.Zero;
53 | switch (repeatType)
54 | {
55 | case NotificationRepeat.Daily:
56 | // To be consistent with iOS, Schedule notification next day same time.
57 | repeatInterval = TimeSpan.FromDays(1);
58 | break;
59 |
60 | case NotificationRepeat.Weekly:
61 | // To be consistent with iOS, Schedule notification next week same day same time.
62 | repeatInterval = TimeSpan.FromDays(7);
63 | break;
64 |
65 | case NotificationRepeat.TimeInterval:
66 | if (notifyRepeatInterval.HasValue)
67 | {
68 | repeatInterval = notifyRepeatInterval.Value;
69 | }
70 | break;
71 |
72 | case NotificationRepeat.No:
73 | break;
74 |
75 | default:
76 | throw new ArgumentOutOfRangeException();
77 | }
78 | return repeatInterval;
79 | }
80 |
81 | ///
82 | /// To be consistent with iOS, Do not show notification if NotifyTime is earlier than (DateTime.Now - AllowedDelay)
83 | ///
84 | ///
85 | internal bool IsValidNotifyTime(DateTime timeNow, DateTime? notifyTime)
86 | {
87 | var (startTime, _) = GetNotifyTimeRange(timeNow);
88 |
89 | return startTime <= notifyTime;
90 | }
91 |
92 | internal bool IsValidShowLaterTime(DateTime timeNow, DateTime? notifyTime)
93 | {
94 | var (_, endTime) = GetNotifyTimeRange(timeNow);
95 |
96 | return notifyTime > endTime;
97 | }
98 |
99 | internal bool IsValidShowNowTime(DateTime timeNow, DateTime? notifyTime)
100 | {
101 | var (startTime, endTime) = GetNotifyTimeRange(timeNow);
102 |
103 | return startTime <= notifyTime && notifyTime <= endTime;
104 | }
105 |
106 | private (DateTime StartTime, DateTime EndTime) GetNotifyTimeRange(DateTime timeNow)
107 | {
108 | var startTime = timeNow.Subtract(AllowedDelay);
109 | var endTime = timeNow.AddMinutes(1);
110 |
111 | return (startTime, endTime);
112 | }
113 | }
--------------------------------------------------------------------------------
/Source/Plugin.LocalNotification/AndroidOption/AndroidVisibilityType.cs:
--------------------------------------------------------------------------------
1 | namespace Plugin.LocalNotification.AndroidOption;
2 |
3 | ///
4 | /// Sphere of visibility of this notification,
5 | /// which affects how and when the SystemUI reveals the notification's presence and contents in untrusted situations (namely, on the secure lockscreen).
6 | ///
7 | public enum AndroidVisibilityType
8 | {
9 | ///
10 | /// The default level,
11 | /// behaves exactly as notifications have always done on Android: The notification's icon and tickerText (if available) are shown in all situations,
12 | /// but the contents are only available if the device is unlocked for the appropriate user.
13 | ///
14 | Private,
15 |
16 | ///
17 | /// A more permissive policy can be expressed by Public;
18 | /// such a notification can be read even in an "insecure" context (that is, above a secure lockscreen).
19 | /// To modify the public version of this notification—for example, to redact some portions—see PublicVersion.
20 | ///
21 | Public,
22 |
23 | ///
24 | /// Will suppress its icon and ticker until the user has bypassed the lockscreen
25 | ///
26 | Secret
27 | }
--------------------------------------------------------------------------------
/Source/Plugin.LocalNotification/AndroidOption/IAndroidLocalNotificationBuilder.cs:
--------------------------------------------------------------------------------
1 | namespace Plugin.LocalNotification.AndroidOption;
2 |
3 | ///
4 | ///
5 | ///
6 | public interface IAndroidLocalNotificationBuilder
7 | {
8 | ///
9 | /// A representation of settings that apply to a collection of similarly themed notifications.
10 | /// Create Notification Channel when API >= 26.
11 | ///
12 | ///
13 | ///
14 | IAndroidLocalNotificationBuilder AddChannel(NotificationChannelRequest channelRequest);
15 |
16 | ///
17 | /// A grouping of related notification channels. e.g., channels that all belong to a single account.
18 | /// Create Notification Channel Group when API >= 26.
19 | /// If you'd like to further organize the appearance of your channels in the settings UI, you can create channel groups.
20 | /// This is a good idea when your app supports multiple user accounts (such as for work profiles),
21 | /// so you can create a notification channel group for each account.
22 | /// This way, users can easily identify and control multiple notification channels that have identical names.
23 | ///
24 | ///
25 | ///
26 | IAndroidLocalNotificationBuilder AddChannelGroup(NotificationChannelGroupRequest groupChannelRequest);
27 | }
--------------------------------------------------------------------------------
/Source/Plugin.LocalNotification/AndroidOption/NotificationChannelGroupRequest.cs:
--------------------------------------------------------------------------------
1 | namespace Plugin.LocalNotification.AndroidOption;
2 |
3 | ///
4 | /// A grouping of related notification channels
5 | ///
6 | public class NotificationChannelGroupRequest
7 | {
8 | private string group = AndroidOptions.DefaultGroupId;
9 | private string name = AndroidOptions.DefaultGroupName;
10 |
11 | ///
12 | /// The id of the group. Must be unique per package. the value may be truncated if it is too long
13 | ///
14 | public string Group
15 | {
16 | get => string.IsNullOrWhiteSpace(group) ? AndroidOptions.DefaultGroupId : group;
17 | set => group = string.IsNullOrWhiteSpace(value) ? AndroidOptions.DefaultGroupId : value;
18 | }
19 | ///
20 | /// The user visible name of the group, The recommended maximum length is 40 characters; the value may be truncated if it is too long.
21 | ///
22 | public string Name
23 | {
24 | get => string.IsNullOrWhiteSpace(name) ? AndroidOptions.DefaultGroupName : name;
25 | set => name = string.IsNullOrWhiteSpace(value) ? AndroidOptions.DefaultGroupName : value;
26 | }
27 |
28 | ///
29 | /// Constructor to pass values directly
30 | ///
31 | ///
32 | ///
33 | public NotificationChannelGroupRequest(string group, string name)
34 | {
35 | Group = group;
36 | Name = name;
37 | }
38 |
39 | ///
40 | /// Default Constructor
41 | ///
42 | public NotificationChannelGroupRequest()
43 | {
44 | }
45 | }
--------------------------------------------------------------------------------
/Source/Plugin.LocalNotification/AndroidOption/NotificationChannelRequest.cs:
--------------------------------------------------------------------------------
1 | namespace Plugin.LocalNotification.AndroidOption;
2 |
3 | ///
4 | /// Notification Channel Request
5 | ///
6 | public class NotificationChannelRequest
7 | {
8 | private string id = AndroidOptions.DefaultChannelId;
9 | private string name = AndroidOptions.DefaultChannelName;
10 |
11 | ///
12 | /// Sets or gets, the level of interruption of this notification channel.
13 | ///
14 | public AndroidImportance Importance { get; set; } = AndroidImportance.Default;
15 |
16 | ///
17 | /// Sets or gets, The id of the channel. Must be unique per package. The value may be truncated if it is too lon
18 | /// Also, NotificationRequest.Android.ChannelId must be set to the same Id to target this channel.
19 | ///
20 | public string Id
21 | {
22 | get => string.IsNullOrWhiteSpace(id) ? AndroidOptions.DefaultChannelId : id;
23 | set => id = string.IsNullOrWhiteSpace(value) ? AndroidOptions.DefaultChannelId : value;
24 | }
25 |
26 | ///
27 | /// Sets or gets, the user visible name of this channel, default is General.
28 | ///
29 | public string Name
30 | {
31 | get => string.IsNullOrWhiteSpace(name) ? AndroidOptions.DefaultChannelName : name;
32 | set => name = string.IsNullOrWhiteSpace(value) ? AndroidOptions.DefaultChannelName : value;
33 | }
34 |
35 | ///
36 | /// Sets or gets, the user visible description of this channel.
37 | ///
38 | public string Description { get; set; } = string.Empty;
39 |
40 | ///
41 | /// Sets or gets, what group this channel belongs to.
42 | ///
43 | public string Group { get; set; } = string.Empty;
44 |
45 | ///
46 | /// Sets or gets, the notification light color for notifications posted to this channel,
47 | /// if the device supports that feature
48 | ///
49 | public AndroidColor LightColor { get; set; } = new();
50 |
51 | ///
52 | /// Sound file name for the notification.
53 | ///
54 | public string Sound { get; set; } = string.Empty;
55 |
56 | ///
57 | /// Sets or gets, Sets whether notification posted to this channel should play sound.
58 | ///
59 | public bool EnableSound { get; set; } = true;
60 |
61 | ///
62 | /// Only modifiable before the channel is submitted.
63 | ///
64 | public long[] VibrationPattern { get; set; } = [];
65 |
66 | ///
67 | /// Sets or gets, whether or not notifications posted to this channel are shown on the lock Screen in full or redacted form.
68 | ///
69 | public AndroidVisibilityType LockScreenVisibility { get; set; } = AndroidVisibilityType.Private;
70 |
71 | ///
72 | /// Sets or gets, Sets whether notifications posted to this channel can appear as application icon badges in a Launcher.
73 | ///
74 | public bool ShowBadge { get; set; } = true;
75 |
76 | ///
77 | /// Sets or gets, Sets whether notifications posted to this channel should display notification lights, on devices that support that feature.
78 | ///
79 | public bool EnableLights { get; set; } = true;
80 |
81 | ///
82 | /// Sets or gets, Sets whether notification posted to this channel should vibrate. The vibration pattern can be set with VibrationPattern
83 | ///
84 | public bool EnableVibration { get; set; } = true;
85 |
86 | ///
87 | /// Sets or gets, Sets whether notification posted to this channel can bypass DND (Do Not Disturb) mode.
88 | ///
89 | public bool CanBypassDnd { get; set; } = false;
90 | }
--------------------------------------------------------------------------------
/Source/Plugin.LocalNotification/EventArgs/NotificationActionEventArgs.cs:
--------------------------------------------------------------------------------
1 | namespace Plugin.LocalNotification.EventArgs;
2 |
3 | ///
4 | /// Returning event when tapped on notification action.
5 | ///
6 | ///
7 | public delegate void NotificationActionTappedEventHandler(NotificationActionEventArgs e);
8 |
9 | ///
10 | ///
11 | ///
12 | public class NotificationActionEventArgs : NotificationEventArgs
13 | {
14 | ///
15 | /// The Action to execute when the notification is explicitly dismissed by the user,
16 | /// either with the "Clear All" button or by swiping it away individually.
17 | ///
18 | public const int DismissedActionId = 1000000;
19 |
20 | ///
21 | /// The Action to execute when the notification is tapped by the user.
22 | ///
23 | public const int TapActionId = 2000000;
24 |
25 | ///
26 | /// Tapped Action Id
27 | ///
28 | public int ActionId { get; set; }
29 |
30 | ///
31 | /// True if the notification is explicitly dismissed by the user
32 | ///
33 | public bool IsDismissed => ActionId == DismissedActionId;
34 |
35 | ///
36 | /// True if the notification is explicitly dismissed by the user
37 | ///
38 | public bool IsTapped => ActionId == TapActionId;
39 | }
--------------------------------------------------------------------------------
/Source/Plugin.LocalNotification/EventArgs/NotificationEventArgs.cs:
--------------------------------------------------------------------------------
1 | namespace Plugin.LocalNotification.EventArgs;
2 |
3 | ///
4 | /// Returning event when a notification is received.
5 | /// On iOS this event is fired only when the app is in foreground
6 | ///
7 | ///
8 |
9 | public delegate void NotificationReceivedEventHandler(NotificationEventArgs e);
10 |
11 | ///
12 | /// Returning event when notifications are Disabled.
13 | ///
14 | public delegate void NotificationDisabledEventHandler();
15 |
16 | ///
17 | ///
18 | ///
19 | public class NotificationEventArgs : System.EventArgs
20 | {
21 | ///
22 | /// Returning notification.
23 | ///
24 | public NotificationRequest Request { get; set; } = new ();
25 | }
--------------------------------------------------------------------------------
/Source/Plugin.LocalNotification/EventArgs/NotificationEventReceivingArgs.cs:
--------------------------------------------------------------------------------
1 | namespace Plugin.LocalNotification.EventArgs;
2 |
3 | ///
4 | ///
5 | ///
6 | public class NotificationEventReceivingArgs : NotificationEventArgs
7 | {
8 | ///
9 | /// If set to true, Notification will not popup
10 | ///
11 | public bool Handled { get; set; } = false;
12 | }
--------------------------------------------------------------------------------
/Source/Plugin.LocalNotification/ILocalNotificationBuilder.cs:
--------------------------------------------------------------------------------
1 | using Plugin.LocalNotification.AndroidOption;
2 | using Plugin.LocalNotification.iOSOption;
3 | using Plugin.LocalNotification.Json;
4 |
5 | namespace Plugin.LocalNotification;
6 |
7 | ///
8 | ///
9 | ///
10 | public interface ILocalNotificationBuilder
11 | {
12 | ///
13 | /// Register notification categories and their corresponding actions
14 | ///
15 | ///
16 | ///
17 | ILocalNotificationBuilder AddCategory(NotificationCategory category);
18 |
19 | ///
20 | ///
21 | ///
22 | ///
23 | ///
24 | ILocalNotificationBuilder SetSerializer(INotificationSerializer serializer);
25 |
26 | ///
27 | ///
28 | ///
29 | ///
30 | ///
31 | ILocalNotificationBuilder AddAndroid(Action android);
32 |
33 | ///
34 | ///
35 | ///
36 | ///
37 | ///
38 | ILocalNotificationBuilder AddiOS(Action iOS);
39 | }
--------------------------------------------------------------------------------
/Source/Plugin.LocalNotification/INotificationService.cs:
--------------------------------------------------------------------------------
1 | using Plugin.LocalNotification.EventArgs;
2 |
3 | namespace Plugin.LocalNotification;
4 |
5 | ///
6 | /// Used to display platform specific local notifications.
7 | ///
8 | public interface INotificationService
9 | {
10 | ///
11 | /// Gets a value indicating whether local notification is supported on this device.
12 | ///
13 | bool IsSupported { get; }
14 |
15 | ///
16 | /// fires when notification popup action is tapped.
17 | ///
18 | event NotificationActionTappedEventHandler? NotificationActionTapped;
19 |
20 | ///
21 | /// fires when notification is received.
22 | /// On iOS this event is fired only when the app is in foreground
23 | ///
24 | event NotificationReceivedEventHandler? NotificationReceived;
25 |
26 | ///
27 | /// fires when notification is disabled.
28 | ///
29 | event NotificationDisabledEventHandler? NotificationsDisabled;
30 |
31 | ///
32 | /// Cancel a notification match with the Id
33 | ///
34 | /// A unique identifier for the already displaying local notification.
35 | bool Cancel(params int[] notificationIdList);
36 |
37 | ///
38 | /// Cancel all notification.
39 | ///
40 | bool CancelAll();
41 |
42 | ///
43 | /// Use this method to selectively remove notifications that you no longer want displayed in Notification Center. This will not cancel future notifications
44 | ///
45 | ///
46 | bool Clear(params int[] notificationIdList);
47 |
48 | ///
49 | /// Use this method to remove all notifications displayed in Notification Center. This will not cancel future notifications.
50 | ///
51 | bool ClearAll();
52 |
53 | ///
54 | /// Get notifications that are currently delivered
55 | ///
56 | ///
57 | Task> GetDeliveredNotificationList();
58 |
59 | ///
60 | /// Internal use Only
61 | ///
62 | ///
63 | void OnNotificationActionTapped(NotificationActionEventArgs e);
64 |
65 | ///
66 | /// Internal use Only
67 | ///
68 | ///
69 | void OnNotificationReceived(NotificationEventArgs e);
70 |
71 | ///
72 | /// Internal use Only
73 | ///
74 | void OnNotificationsDisabled();
75 |
76 | ///
77 | /// Get pending notifications
78 | ///
79 | ///
80 | Task> GetPendingNotificationList();
81 |
82 | ///
83 | /// Register notification categories and their corresponding actions
84 | ///
85 | void RegisterCategoryList(HashSet categoryList);
86 |
87 | ///
88 | /// Send a local notification to the device.
89 | ///
90 | ///
91 | Task Show(NotificationRequest request);
92 |
93 | ///
94 | /// When Notification is about to be shown, this allow it to be modified.
95 | ///
96 | Func>? NotificationReceiving { get; set; }
97 |
98 | ///
99 | /// Returns whether user as allowed Notifications
100 | ///
101 | ///
102 | Task AreNotificationsEnabled(NotificationPermission? permission = null);
103 |
104 | ///
105 | /// Request Notification Permission
106 | /// Ask the user for permission to show notifications on iOS 10.0+ and Android 33+.
107 | /// Returns true if Allowed.
108 | ///
109 | ///
110 | Task RequestNotificationPermission(NotificationPermission? permission = null);
111 | }
--------------------------------------------------------------------------------
/Source/Plugin.LocalNotification/Json/INotificationSerializer.cs:
--------------------------------------------------------------------------------
1 | namespace Plugin.LocalNotification.Json;
2 |
3 | ///
4 | ///
5 | ///
6 | public interface INotificationSerializer
7 | {
8 | ///
9 | ///
10 | ///
11 | ///
12 | ///
13 | ///
14 | TValue? Deserialize(string json);
15 |
16 | ///
17 | ///
18 | ///
19 | ///
20 | ///
21 | ///
22 | string Serialize(TValue value);
23 | }
--------------------------------------------------------------------------------
/Source/Plugin.LocalNotification/Json/NotificationSerializer.cs:
--------------------------------------------------------------------------------
1 | using System.Text.Json;
2 | using System.Text.Json.Serialization;
3 |
4 | namespace Plugin.LocalNotification.Json;
5 |
6 | ///
7 | internal class NotificationSerializer : INotificationSerializer
8 | {
9 | private readonly JsonSerializerOptions _options = new()
10 | {
11 | NumberHandling = JsonNumberHandling.AllowNamedFloatingPointLiterals
12 | };
13 |
14 | ///
15 | public virtual TValue? Deserialize(string json)
16 | {
17 | return JsonSerializer.Deserialize(json, _options);
18 | }
19 |
20 | ///
21 | public virtual string Serialize(TValue value)
22 | {
23 | return JsonSerializer.Serialize(value, _options);
24 | }
25 | }
--------------------------------------------------------------------------------
/Source/Plugin.LocalNotification/LocalNotificationBuilder.cs:
--------------------------------------------------------------------------------
1 | using Plugin.LocalNotification.AndroidOption;
2 | using Plugin.LocalNotification.iOSOption;
3 | using Plugin.LocalNotification.Json;
4 |
5 | namespace Plugin.LocalNotification;
6 |
7 | ///
8 | ///
9 | ///
10 | public class LocalNotificationBuilder : ILocalNotificationBuilder
11 | {
12 | ///
13 | /// Register notification categories and their corresponding actions
14 | ///
15 | internal HashSet CategorySet { get; } = [];
16 |
17 | ///
18 | ///
19 | ///
20 | internal INotificationSerializer Serializer { get; private set; } = new NotificationSerializer();
21 |
22 | ///
23 | /// Android specific Builder.
24 | ///
25 | internal AndroidLocalNotificationBuilder AndroidBuilder { get; } = new();
26 |
27 | ///
28 | /// Android specific Builder.
29 | ///
30 | internal iOSLocalNotificationBuilder IOSBuilder { get; } = new();
31 |
32 | ///
33 | public ILocalNotificationBuilder AddAndroid(Action android)
34 | {
35 | android?.Invoke(AndroidBuilder);
36 | return this;
37 | }
38 |
39 | ///
40 | public ILocalNotificationBuilder AddiOS(Action iOS)
41 | {
42 | iOS?.Invoke(IOSBuilder);
43 | return this;
44 | }
45 |
46 | ///
47 | public ILocalNotificationBuilder AddCategory(NotificationCategory category)
48 | {
49 | CategorySet.Add(category);
50 | return this;
51 | }
52 |
53 | ///
54 | public ILocalNotificationBuilder SetSerializer(INotificationSerializer serializer)
55 | {
56 | Serializer = serializer ?? new NotificationSerializer();
57 | return this;
58 | }
59 | }
--------------------------------------------------------------------------------
/Source/Plugin.LocalNotification/LocalNotificationCenter.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.Logging;
2 | using Plugin.LocalNotification.Json;
3 |
4 | #if ANDROID || IOS || WINDOWS
5 | using Plugin.LocalNotification.Platforms;
6 | #endif
7 |
8 | namespace Plugin.LocalNotification;
9 |
10 | ///
11 | /// Cross platform INotificationService Resolver.
12 | ///
13 |
14 | public partial class LocalNotificationCenter
15 | {
16 | private static readonly Lazy implementation = new(CreateNotificationService, LazyThreadSafetyMode.PublicationOnly);
17 | private static INotificationSerializer? _serializer;
18 |
19 | private static INotificationService? CreateNotificationService() =>
20 | #if ANDROID || IOS || WINDOWS
21 | new NotificationServiceImpl();
22 | #else
23 | null;
24 | #endif
25 |
26 |
27 | ///
28 | /// Internal Logger
29 | ///
30 | internal static ILogger? Logger { get; set; }
31 |
32 | ///
33 | /// Internal Logger LogLevel
34 | ///
35 | public static LogLevel LogLevel { get; set; } = LogLevel.Trace;
36 |
37 | ///
38 | /// Platform specific INotificationService.
39 | ///
40 | public static INotificationService Current => implementation.Value;
41 |
42 | ///
43 | /// Return Notification Key.
44 | ///
45 | public const string ReturnRequest = "Plugin.LocalNotification.RETURN_REQUEST";
46 |
47 | ///
48 | /// Return Notification Action Id.
49 | ///
50 | public const string ReturnRequestActionId = "Plugin.LocalNotification.RETURN_ActionId";
51 |
52 | ///
53 | /// Return Notification Handled Key
54 | ///
55 | public const string ReturnRequestHandled = "Plugin.LocalNotification.RETURN_Handled";
56 |
57 | ///
58 | ///
59 | ///
60 | internal static INotificationSerializer Serializer
61 | {
62 | get
63 | {
64 | _serializer ??= new NotificationSerializer();
65 | return _serializer;
66 | }
67 | set => _serializer = value;
68 | }
69 |
70 | internal static NotificationRequest GetRequest(string? serializedRequest)
71 | {
72 | Logger?.LogTrace("Serialized Request [{serializedRequest}]", serializedRequest);
73 | if (string.IsNullOrWhiteSpace(serializedRequest))
74 | {
75 | return new NotificationRequest();
76 | }
77 |
78 | var request = Serializer.Deserialize(serializedRequest);
79 | return request ?? new NotificationRequest();
80 | }
81 |
82 | internal static List GetRequestList(string? serializedRequestList)
83 | {
84 | if (string.IsNullOrWhiteSpace(serializedRequestList))
85 | {
86 | return [];
87 | }
88 |
89 | var requestList = Serializer.Deserialize>(serializedRequestList);
90 | return requestList ?? [];
91 | }
92 |
93 | internal static string GetRequestListSerialize(List requestList)
94 | {
95 | if (requestList is null || requestList.Count <= 0)
96 | {
97 | // Return an empty JSON array if the list is null or empty
98 | return "[]";
99 | }
100 |
101 | foreach (var request in requestList)
102 | {
103 | if (request.Image is not null &&
104 | request.Image.Binary is not null &&
105 | request.Image.Binary?.Length > 90000)
106 | {
107 | request.Image.Binary = [];
108 | }
109 | }
110 | var serializedRequestList = Serializer.Serialize(requestList);
111 | return serializedRequestList;
112 | }
113 |
114 | internal static string GetRequestSerialize(NotificationRequest request)
115 | {
116 | if (request is null)
117 | {
118 | // Return an empty JSON array if the list is null or empty
119 | return "[]";
120 | }
121 |
122 | if (request.Image is not null &&
123 | request.Image.Binary is not null &&
124 | request.Image.Binary?.Length > 90000)
125 | {
126 | request.Image.Binary = [];
127 | }
128 | var serializedRequest = Serializer.Serialize(request);
129 |
130 | Logger?.LogTrace("Serialized Request [{serializedRequest}]", serializedRequest);
131 |
132 | return serializedRequest;
133 | }
134 | }
--------------------------------------------------------------------------------
/Source/Plugin.LocalNotification/LocalNotificationExtensions.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.DependencyInjection.Extensions;
2 | using Microsoft.Maui.LifecycleEvents;
3 |
4 | namespace Plugin.LocalNotification;
5 |
6 | ///
7 | ///
8 | ///
9 | public static class LocalNotificationExtensions
10 | {
11 | ///
12 | ///
13 | ///
14 | ///
15 | ///
16 | ///
17 | public static MauiAppBuilder UseLocalNotification(this MauiAppBuilder builder, Action? configureDelegate = null)
18 | {
19 | var localNotificationBuilder = new LocalNotificationBuilder();
20 | configureDelegate?.Invoke(localNotificationBuilder);
21 |
22 | builder.Services.AddSingleton(localNotificationBuilder);
23 | builder.Services.AddSingleton(LocalNotificationCenter.Current);
24 | builder.Services.TryAddEnumerable(ServiceDescriptor.Transient());
25 |
26 | builder.ConfigureLifecycleEvents(life =>
27 | {
28 | #if ANDROID
29 | life.AddAndroid(android =>
30 | {
31 | android.OnCreate((activity, _) =>
32 | {
33 | LocalNotificationCenter.CreateNotificationChannelGroups(localNotificationBuilder.AndroidBuilder.GroupChannelRequestList);
34 |
35 | LocalNotificationCenter.CreateNotificationChannels(localNotificationBuilder.AndroidBuilder.ChannelRequestList);
36 |
37 | LocalNotificationCenter.NotifyNotificationTapped(activity.Intent);
38 | })
39 | .OnNewIntent((_, intent) =>
40 | {
41 | LocalNotificationCenter.NotifyNotificationTapped(intent);
42 | });
43 | });
44 | #elif IOS
45 | life.AddiOS(iOS =>
46 | {
47 | iOS.FinishedLaunching((application, _) =>
48 | {
49 | LocalNotificationCenter.SetUserNotificationCenterDelegate(localNotificationBuilder.IOSBuilder.CustomUserNotificationCenterDelegate);
50 | return true;
51 | });
52 | iOS.WillEnterForeground(application =>
53 | {
54 | LocalNotificationCenter.ResetApplicationIconBadgeNumber(application);
55 | });
56 | });
57 | #elif WINDOWS
58 | life.AddWindows(windows =>
59 | {
60 | windows.OnActivated((window, args) =>
61 | {
62 | LocalNotificationCenter.SetupBackgroundActivation();
63 | });
64 | });
65 | #endif
66 | });
67 |
68 | return builder;
69 | }
70 | }
--------------------------------------------------------------------------------
/Source/Plugin.LocalNotification/LocalNotificationInitializeService.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.Logging;
2 |
3 | namespace Plugin.LocalNotification;
4 |
5 | ///
6 | ///
7 | ///
8 | public class LocalNotificationInitializeService : IMauiInitializeService
9 | {
10 | ///
11 | ///
12 | ///
13 | ///
14 | public void Initialize(IServiceProvider services)
15 | {
16 | LocalNotificationCenter.Logger = services.GetService>();
17 |
18 | var builder = services.GetService();
19 | if (builder is not null)
20 | {
21 | LocalNotificationCenter.Serializer = builder.Serializer;
22 | LocalNotificationCenter.Current.RegisterCategoryList(builder.CategorySet);
23 | }
24 | }
25 | }
--------------------------------------------------------------------------------
/Source/Plugin.LocalNotification/NotificationAction.cs:
--------------------------------------------------------------------------------
1 | using Plugin.LocalNotification.AndroidOption;
2 | using Plugin.LocalNotification.iOSOption;
3 | using Plugin.LocalNotification.WindowsOption;
4 |
5 | namespace Plugin.LocalNotification;
6 |
7 | ///
8 | ///
9 | ///
10 | ///
11 | /// ActionId is the unique identifier for the Category
12 | ///
13 | /// A unique identifier for the Action
14 | public class NotificationAction(int actionId) : IEquatable
15 | {
16 |
17 | ///
18 | /// A unique identifier for the Action
19 | ///
20 | public int ActionId { get; } = actionId;
21 |
22 | ///
23 | /// iOS specific properties.
24 | ///
25 | public iOSAction IOS { get; set; } = new();
26 |
27 | ///
28 | /// Android specific properties.
29 | ///
30 | public AndroidAction Android { get; set; } = new();
31 |
32 | ///
33 | /// Windows specific properties.
34 | ///
35 | public WindowsAction Windows { get; set; } = new();
36 |
37 | ///
38 | ///
39 | ///
40 | public string Title { get; set; } = string.Empty;
41 |
42 | ///
43 | ///
44 | ///
45 | ///
46 | ///
47 | public bool Equals(NotificationAction? other) => other != null &&
48 | ActionId == other.ActionId;
49 |
50 | ///
51 | ///
52 | ///
53 | ///
54 | ///
55 | public override bool Equals(object? obj) => Equals(obj as NotificationAction);
56 |
57 | ///
58 | ///
59 | ///
60 | ///
61 | public override int GetHashCode() => ActionId.GetHashCode();
62 | }
--------------------------------------------------------------------------------
/Source/Plugin.LocalNotification/NotificationCategory.cs:
--------------------------------------------------------------------------------
1 | namespace Plugin.LocalNotification;
2 |
3 | ///
4 | /// Categories serve as the container for actions
5 | ///
6 | ///
7 | /// CategoryType is the unique identifier for the Category
8 | ///
9 | /// A unique identifier for the Category
10 | public class NotificationCategory(NotificationCategoryType categoryType) : IEquatable
11 | {
12 | ///
13 | ///
14 | ///
15 | public HashSet ActionList { get; set; } = [];
16 |
17 | ///
18 | /// A unique identifier for the Category
19 | ///
20 | public NotificationCategoryType CategoryType { get; } = categoryType;
21 |
22 | ///
23 | ///
24 | ///
25 | ///
26 | ///
27 | public bool Equals(NotificationCategory? other) => other != null &&
28 | CategoryType == other.CategoryType;
29 |
30 | ///
31 | ///
32 | ///
33 | ///
34 | ///
35 | public override bool Equals(object? obj) => Equals(obj as NotificationCategory);
36 |
37 | ///
38 | ///
39 | ///
40 | ///
41 | public override int GetHashCode() => CategoryType.GetHashCode();
42 | }
--------------------------------------------------------------------------------
/Source/Plugin.LocalNotification/NotificationCategoryType.cs:
--------------------------------------------------------------------------------
1 | namespace Plugin.LocalNotification;
2 |
3 | ///
4 | ///
5 | ///
6 | public enum NotificationCategoryType
7 | {
8 | ///
9 | ///
10 | ///
11 | None,
12 |
13 | ///
14 | /// Ongoing information about device or contextual status
15 | ///
16 | Status,
17 |
18 | ///
19 | /// Alarm or timer
20 | ///
21 | Alarm,
22 |
23 | ///
24 | /// User-scheduled reminder
25 | ///
26 | Reminder,
27 |
28 | ///
29 | /// Calendar event
30 | ///
31 | Event,
32 |
33 | ///
34 | /// Error in background operation or authentication status
35 | ///
36 | Error,
37 |
38 | ///
39 | /// Progress of a long-running background operation
40 | ///
41 | Progress,
42 |
43 | ///
44 | /// Promotion or advertisement
45 | ///
46 | Promo,
47 |
48 | ///
49 | /// A specific, timely recommendation for a single thing. For example, a news app might want to recommend a news story it believes the user will want to read next
50 | ///
51 | Recommendation,
52 |
53 | ///
54 | /// Indication of running background service
55 | ///
56 | Service,
57 | }
--------------------------------------------------------------------------------
/Source/Plugin.LocalNotification/NotificationImage.cs:
--------------------------------------------------------------------------------
1 | namespace Plugin.LocalNotification;
2 |
3 | ///
4 | ///
5 | ///
6 | public class NotificationImage
7 | {
8 | ///
9 | /// Must be less than 90Kb
10 | ///
11 | public byte[]? Binary { get; set; } = [];
12 |
13 | ///
14 | ///
15 | ///
16 | public string? FilePath { get; set; } = string.Empty;
17 |
18 | ///
19 | ///
20 | ///
21 | public bool HasValue => string.IsNullOrWhiteSpace(ResourceName) == false ||
22 | string.IsNullOrWhiteSpace(FilePath) == false ||
23 | (Binary?.Length > 0);
24 |
25 | ///
26 | ///
27 | ///
28 | public string? ResourceName { get; set; } = string.Empty;
29 | }
--------------------------------------------------------------------------------
/Source/Plugin.LocalNotification/NotificationPermission.cs:
--------------------------------------------------------------------------------
1 | using Plugin.LocalNotification.AndroidOption;
2 | using Plugin.LocalNotification.iOSOption;
3 |
4 | namespace Plugin.LocalNotification;
5 |
6 | ///
7 | ///
8 | ///
9 | public class NotificationPermission
10 | {
11 | ///
12 | ///
13 | ///
14 | public bool AskPermission { get; set; } = true;
15 |
16 | ///
17 | ///
18 | ///
19 | public AndroidNotificationPermission Android { get; set; } = new();
20 |
21 | ///
22 | ///
23 | ///
24 | public iOSNotificationPermission IOS { get; set; } = new();
25 | }
--------------------------------------------------------------------------------
/Source/Plugin.LocalNotification/NotificationRepeat.cs:
--------------------------------------------------------------------------------
1 | namespace Plugin.LocalNotification;
2 |
3 | ///
4 | /// Set id Notification should repeat
5 | ///
6 | public enum NotificationRepeat
7 | {
8 | ///
9 | /// Notification should not repeat
10 | ///
11 | No,
12 |
13 | ///
14 | /// Notification should repeat next day at same time
15 | ///
16 | Daily,
17 |
18 | ///
19 | /// Notification should repeat next week at same day, same time
20 | ///
21 | Weekly,
22 |
23 | ///
24 | /// Notification to be delivered after the specified amount of time elapses
25 | ///
26 | TimeInterval
27 | }
--------------------------------------------------------------------------------
/Source/Plugin.LocalNotification/NotificationRequest.cs:
--------------------------------------------------------------------------------
1 | using Plugin.LocalNotification.AndroidOption;
2 | using Plugin.LocalNotification.iOSOption;
3 | using Plugin.LocalNotification.WindowsOption;
4 |
5 | namespace Plugin.LocalNotification;
6 |
7 | ///
8 | /// Notification Request
9 | ///
10 | public class NotificationRequest
11 | {
12 | ///
13 | /// Number of the badge displays on the Home Screen.
14 | ///
15 | public int BadgeNumber { get; set; }
16 |
17 | ///
18 | /// Notification category for actions
19 | ///
20 | public NotificationCategoryType CategoryType { get; set; } = NotificationCategoryType.None;
21 |
22 | ///
23 | /// Details for the notification.
24 | ///
25 | public string Description { get; set; } = string.Empty;
26 |
27 | ///
28 | /// Set this notification to be part of a group of notifications sharing the same key.
29 | /// Grouped notifications may display in a cluster or stack on devices which support such rendering.
30 | ///
31 | public string Group { get; set; } = string.Empty;
32 |
33 | ///
34 | /// Image for notification.
35 | ///
36 | public NotificationImage Image { get; set; } = new NotificationImage();
37 |
38 | ///
39 | /// Android specific properties.
40 | ///
41 | public AndroidOptions Android { get; set; } = new();
42 |
43 | ///
44 | /// iOS specific properties.
45 | ///
46 | public iOSOptions iOS { get; set; } = new();
47 |
48 | ///
49 | /// Windows specific properties.
50 | ///
51 | public WindowsOptions Windows { get; set; } = new();
52 |
53 | ///
54 | /// A unique identifier for the request
55 | /// (if identifier is not unique, a new notification request object is not created).
56 | /// You can use this identifier later to cancel a request that is still pending.
57 | ///
58 | public int NotificationId { get; set; }
59 |
60 | ///
61 | /// Returning data when tapped or received notification.
62 | ///
63 | public string ReturningData { get; set; } = string.Empty;
64 |
65 | ///
66 | /// Schedule notification (cannot be mixed with geofence)
67 | ///
68 | public NotificationRequestSchedule Schedule { get; set; } = new();
69 |
70 | ///
71 | /// Set the location aware geofence (cannot be mixed with schedule)
72 | ///
73 | public NotificationRequestGeofence Geofence { get; set; } = new();
74 |
75 | ///
76 | /// Silences this instance of the notification, regardless of the sounds or vibrations set on the notification or notification channel.
77 | ///
78 | public bool Silent { get; set; }
79 |
80 | ///
81 | /// Sound file name for the notification.
82 | /// In Android, Only used if Android Api below 26.
83 | /// Use NotificationCenter.CreateNotificationChannel when Android Api equal or above 26
84 | ///
85 | public string Sound { get; set; } = string.Empty;
86 |
87 | ///
88 | /// Subtitle for the notification.
89 | ///
90 | public string Subtitle { get; set; } = string.Empty;
91 |
92 | ///
93 | /// Title for the notification.
94 | ///
95 | public string Title { get; set; } = string.Empty;
96 |
97 | ///
98 | /// Directly call Cancel(...) on this instance.
99 | /// Notification Id set for this instance will be used to cancel this notification.
100 | ///
101 | ///
102 | public bool Cancel() => LocalNotificationCenter.Current.Cancel(NotificationId);
103 |
104 | ///
105 | /// Directly call Show() on this instance.
106 | ///
107 | ///
108 | public Task Show() => LocalNotificationCenter.Current.Show(this);
109 | }
--------------------------------------------------------------------------------
/Source/Plugin.LocalNotification/NotificationRequestGeofence.cs:
--------------------------------------------------------------------------------
1 | using Plugin.LocalNotification.AndroidOption;
2 | using Plugin.LocalNotification.iOSOption;
3 |
4 | namespace Plugin.LocalNotification;
5 |
6 | ///
7 | ///
8 | ///
9 | public class NotificationRequestGeofence
10 | {
11 | ///
12 | ///
13 | ///
14 | public GeofenceNotifyOn NotifyOn { get; set; } = GeofenceNotifyOn.OnEntry;
15 |
16 | ///
17 | /// Android specific properties.
18 | ///
19 | public AndroidGeofenceOptions Android { get; set; } = new();
20 |
21 | ///
22 | /// iOS specific properties.
23 | ///
24 | public iOSGeofenceOptions IOS { get; set; } = new();
25 |
26 | ///
27 | /// The center of the geofence
28 | ///
29 | public Position Center { get; set; } = new();
30 |
31 | ///
32 | /// The radius of the region.
33 | /// Default 5m
34 | ///
35 | public double RadiusInMeters { get; set; } = 5;
36 |
37 | ///
38 | ///
39 | ///
40 | public bool IsGeofence => Center != null && Center.IsPositionSet;
41 |
42 | ///
43 | ///
44 | ///
45 | public class Position
46 | {
47 | ///
48 | /// Latitude in degrees, between -90 and +90 inclusive
49 | ///
50 | public double Latitude { get; set; } = double.NaN;
51 |
52 | ///
53 | /// Longitude in degrees, between -180 and +180 inclusive.
54 | ///
55 | public double Longitude { get; set; } = double.NaN;
56 |
57 | ///
58 | ///
59 | ///
60 | public bool IsPositionSet => !double.IsNaN(Latitude) && !double.IsNaN(Longitude);
61 | }
62 |
63 | ///
64 | ///
65 | ///
66 | [Flags]
67 | public enum GeofenceNotifyOn
68 | {
69 | ///
70 | /// User enters the geofence
71 | ///
72 | OnEntry = 1,
73 |
74 | ///
75 | /// user exits the geofence
76 | ///
77 | OnExit = 2
78 | }
79 | }
--------------------------------------------------------------------------------
/Source/Plugin.LocalNotification/NotificationRequestSchedule.cs:
--------------------------------------------------------------------------------
1 | using Plugin.LocalNotification.AndroidOption;
2 |
3 | namespace Plugin.LocalNotification;
4 |
5 | ///
6 | /// Schedule notification
7 | ///
8 | public class NotificationRequestSchedule
9 | {
10 | ///
11 | /// Android specific properties.
12 | ///
13 | public AndroidScheduleOptions Android { get; set; } = new();
14 |
15 | ///
16 | /// Time to cancel the notification automatically.
17 | ///
18 | public DateTime? NotifyAutoCancelTime { get; set; }
19 |
20 | ///
21 | /// if Repeats = TimeInterval, then repeat again after specified amount of time elapses
22 | ///
23 | public TimeSpan? NotifyRepeatInterval { get; set; }
24 |
25 | ///
26 | /// Time to show the notification.
27 | ///
28 | public DateTime? NotifyTime { get; set; }
29 |
30 | ///
31 | /// If true, will repeat again at the time specifies in NotifyTime or NotifyRepeatInterval
32 | ///
33 | public NotificationRepeat RepeatType { get; set; } = NotificationRepeat.No;
34 | }
--------------------------------------------------------------------------------
/Source/Plugin.LocalNotification/Platforms/Android/GeofenceTransitionsIntentReceiver.cs:
--------------------------------------------------------------------------------
1 | using Android.Content;
2 |
3 | namespace Plugin.LocalNotification.Platforms;
4 |
5 | ///
6 | ///
7 | ///
8 | [BroadcastReceiver(
9 | Name = ReceiverName,
10 | Enabled = true,
11 | Exported = false,
12 | Label = "Plugin LocalNotification Geofence Transitions Receiver"
13 | )]
14 | public class GeofenceTransitionsIntentReceiver : BroadcastReceiver
15 | {
16 | ///
17 | ///
18 | ///
19 | public const string ReceiverName = "plugin.LocalNotification." + nameof(GeofenceTransitionsIntentReceiver);
20 |
21 | ///
22 | ///
23 | ///
24 | ///
25 | ///
26 | public override async void OnReceive(Context? context, Intent? intent)
27 | {
28 | try
29 | {
30 | var notificationService = TryGetDefaultDroidNotificationService();
31 |
32 | var requestSerialize = intent?.GetStringExtra(LocalNotificationCenter.ReturnRequest);
33 | if (string.IsNullOrWhiteSpace(requestSerialize))
34 | {
35 | LocalNotificationCenter.Log("Request Json Not Found");
36 | return;
37 | }
38 | var request = LocalNotificationCenter.GetRequest(requestSerialize);
39 |
40 | if (!request.Geofence.IsGeofence)
41 | {
42 | LocalNotificationCenter.Log($"Notification {request.NotificationId} has no Geofence isformation");
43 | return;
44 | }
45 | _ = await notificationService.ShowNow(request);
46 | }
47 | catch (Exception ex)
48 | {
49 | LocalNotificationCenter.Log(ex);
50 | }
51 | }
52 |
53 | private static NotificationServiceImpl TryGetDefaultDroidNotificationService() => LocalNotificationCenter.Current is NotificationServiceImpl notificationService
54 | ? notificationService
55 | : new NotificationServiceImpl();
56 | }
--------------------------------------------------------------------------------
/Source/Plugin.LocalNotification/Platforms/Android/ManifestInfo.cs:
--------------------------------------------------------------------------------
1 | //Permissions for android
2 |
3 | //[assembly: UsesPermission(Manifest.Permission.WakeLock)]
4 | //[assembly: UsesPermission(Manifest.Permission.ReceiveBootCompleted)]
5 | //[assembly: UsesPermission(Manifest.Permission.Vibrate)]
6 |
7 | //[assembly: UsesPermission(Manifest.Permission.ScheduleExactAlarm)]
8 | [assembly: System.Reflection.AssemblyMetadata("IsTrimmable", "True")]
9 |
10 | //[assembly: UsesPermission(Manifest.Permission.PostNotifications)]
--------------------------------------------------------------------------------
/Source/Plugin.LocalNotification/Platforms/Android/NotificationActionReceiver.cs:
--------------------------------------------------------------------------------
1 | using Android.Content;
2 |
3 | namespace Plugin.LocalNotification.Platforms;
4 |
5 | ///
6 | ///
7 | ///
8 | [BroadcastReceiver(
9 | Name = ReceiverName,
10 | Enabled = true,
11 | Exported = false,
12 | Label = "Plugin LocalNotification Action Receiver"
13 | )]
14 | internal class NotificationActionReceiver : BroadcastReceiver
15 | {
16 | ///
17 | ///
18 | ///
19 | public const string ReceiverName = "plugin.LocalNotification." + nameof(NotificationActionReceiver);
20 |
21 | ///
22 | ///
23 | ///
24 | ///
25 | ///
26 | public override void OnReceive(Context? context, Intent? intent)
27 | {
28 | try
29 | {
30 | LocalNotificationCenter.NotifyNotificationTapped(intent);
31 | }
32 | catch (Exception ex)
33 | {
34 | LocalNotificationCenter.Log(ex);
35 | }
36 | }
37 | }
--------------------------------------------------------------------------------
/Source/Plugin.LocalNotification/Platforms/Android/NotificationPerms.cs:
--------------------------------------------------------------------------------
1 | using Android;
2 |
3 | namespace Plugin.LocalNotification.Platforms;
4 |
5 | public partial class NotificationPerms : Permissions.BasePlatformPermission
6 | {
7 | public override (string androidPermission, bool isRuntime)[] RequiredPermissions
8 | {
9 | get
10 | {
11 | var result = new List<(string androidPermission, bool isRuntime)>();
12 | if (OperatingSystem.IsAndroidVersionAtLeast(33))
13 | {
14 | result.Add((Manifest.Permission.PostNotifications, true));
15 | }
16 | return [.. result];
17 | }
18 | }
19 | }
--------------------------------------------------------------------------------
/Source/Plugin.LocalNotification/Platforms/Android/NotificationRepository.cs:
--------------------------------------------------------------------------------
1 | using Android.Content;
2 | using Application = Android.App.Application;
3 |
4 | namespace Plugin.LocalNotification.Platforms;
5 |
6 | ///
7 | ///
8 | ///
9 | internal class NotificationRepository
10 | {
11 | private static readonly Lazy MySingleton =
12 | new(() => new NotificationRepository(), LazyThreadSafetyMode.PublicationOnly);
13 |
14 | private static readonly object Locker = new();
15 |
16 | ///
17 | ///
18 | ///
19 | internal static NotificationRepository Current => MySingleton.Value;
20 |
21 | ///
22 | ///
23 | ///
24 | private const string PendingListKey = "PendingList";
25 |
26 | ///
27 | ///
28 | ///
29 | private const string DeliveredListKey = "DeliveredList";
30 |
31 | ///
32 | ///
33 | ///
34 | ///
35 | private static ISharedPreferences? GetSharedPreferences()
36 | {
37 | const string sharedName = "plugin.LocalNotification." + nameof(NotificationRepository);
38 | return Application.Context.GetSharedPreferences(sharedName, FileCreationMode.Private);
39 | }
40 |
41 | ///
42 | ///
43 | ///
44 | ///
45 | internal static void RemoveByPendingIdList(params int[] notificationIdList)
46 | {
47 | var itemList = NotificationRepository.GetPendingList();
48 | _ = itemList.RemoveAll(r => notificationIdList.Contains(r.NotificationId));
49 | SetPendingList(itemList);
50 | }
51 |
52 | ///
53 | ///
54 | ///
55 | ///
56 | internal static void AddPendingRequest(NotificationRequest request)
57 | {
58 | var itemList = NotificationRepository.GetPendingList();
59 | _ = itemList.RemoveAll(r => request.NotificationId == r.NotificationId);
60 | _ = itemList.RemoveAll(r =>
61 | r.Schedule.NotifyTime.HasValue &&
62 | r.Schedule.Android.IsValidNotifyTime(DateTime.Now, r.Schedule.NotifyTime) == false);
63 | itemList.Add(request);
64 | SetPendingList(itemList);
65 | }
66 |
67 | ///
68 | ///
69 | ///
70 | ///
71 | internal static void AddDeliveredRequest(NotificationRequest request)
72 | {
73 | var itemList = NotificationRepository.GetDeliveredList();
74 | _ = itemList.RemoveAll(r => request.NotificationId == r.NotificationId);
75 | itemList.Add(request);
76 | SetDeliveredList(itemList);
77 | }
78 |
79 | ///
80 | ///
81 | ///
82 | internal static void RemoveDeliveredList() => SetDeliveredList(null);
83 |
84 | ///
85 | ///
86 | ///
87 | internal static void RemovePendingList() => SetPendingList(null);
88 |
89 | ///
90 | ///
91 | ///
92 | ///
93 | internal static void RemoveByDeliveredIdList(params int[] notificationIdList)
94 | {
95 | var itemList = NotificationRepository.GetDeliveredList();
96 | _ = itemList.RemoveAll(r => notificationIdList.Contains(r.NotificationId));
97 | SetDeliveredList(itemList);
98 | }
99 |
100 | ///
101 | ///
102 | ///
103 | ///
104 | internal static List GetPendingList()
105 | {
106 | var itemList = GetList(PendingListKey);
107 | return itemList;
108 | }
109 |
110 | private static void SetPendingList(List? list) => SetList(PendingListKey, list);
111 |
112 | ///
113 | ///
114 | ///
115 | ///
116 | internal static List GetDeliveredList()
117 | {
118 | var itemList = GetList(DeliveredListKey);
119 | return itemList;
120 | }
121 |
122 | private static void SetDeliveredList(List? list) => SetList(DeliveredListKey, list);
123 |
124 | private static List GetList(string key)
125 | {
126 | lock (Locker)
127 | {
128 | using var sharedPreferences = GetSharedPreferences();
129 | var jsonText = sharedPreferences?.GetString(key, string.Empty);
130 | return string.IsNullOrWhiteSpace(jsonText)
131 | ? []
132 | : LocalNotificationCenter.GetRequestList(jsonText);
133 | }
134 | }
135 |
136 | private static void SetList(string key, List? list)
137 | {
138 | lock (Locker)
139 | {
140 | using var sharedPreferences = GetSharedPreferences();
141 | using var editor = sharedPreferences?.Edit();
142 | var jsonText = string.Empty;
143 | if (list != null && list.Count != 0)
144 | {
145 | jsonText = LocalNotificationCenter.GetRequestListSerialize(list);
146 | }
147 | _ = (editor?.PutString(key, jsonText));
148 | editor?.Apply();
149 | }
150 | }
151 | }
--------------------------------------------------------------------------------
/Source/Plugin.LocalNotification/Platforms/Android/PlatformExtensions.cs:
--------------------------------------------------------------------------------
1 | using Android.App;
2 | using Android.Content;
3 | using AndroidX.Core.App;
4 | using Plugin.LocalNotification.AndroidOption;
5 | using Application = Android.App.Application;
6 |
7 | namespace Plugin.LocalNotification.Platforms;
8 |
9 | ///
10 | ///
11 | ///
12 | public static class PlatformExtensions
13 | {
14 | ///
15 | ///
16 | ///
17 | ///
18 | ///
19 | public static int ToNative(this AndroidColor color)
20 | {
21 | if (color is null)
22 | {
23 | return 0;
24 | }
25 |
26 | if (color.Argb.HasValue)
27 | {
28 | return color.Argb.Value;
29 | }
30 |
31 | if (string.IsNullOrWhiteSpace(color.ResourceName) == false)
32 | {
33 | if (OperatingSystem.IsAndroidVersionAtLeast(23))
34 | {
35 | var colorResourceId =
36 | Application.Context.Resources?.GetIdentifier(color.ResourceName, "color",
37 | Application.Context.PackageName) ?? 0;
38 |
39 | var colorId = Application.Context.GetColor(colorResourceId);
40 |
41 | return colorId;
42 | }
43 | }
44 | return 0;
45 | }
46 |
47 | ///
48 | ///
49 | ///
50 | ///
51 | ///
52 | ///
53 | public static NotificationImportance ToNative(this AndroidImportance type) => !OperatingSystem.IsAndroidVersionAtLeast(26)
54 | ? default
55 | : type switch
56 | {
57 | AndroidImportance.Unspecified => NotificationImportance.Unspecified,
58 | AndroidImportance.None => NotificationImportance.None,
59 | AndroidImportance.Min => NotificationImportance.Min,
60 | AndroidImportance.Low => NotificationImportance.Low,
61 | AndroidImportance.Default => NotificationImportance.Default,
62 | AndroidImportance.High => NotificationImportance.High,
63 | AndroidImportance.Max => NotificationImportance.Max,
64 | _ => throw new ArgumentOutOfRangeException(nameof(type), type, null)
65 | };
66 |
67 | ///
68 | ///
69 | ///
70 | ///
71 | ///
72 | public static NotificationVisibility ToNative(this AndroidVisibilityType type) => type switch
73 | {
74 | AndroidVisibilityType.Private => NotificationVisibility.Private,
75 | AndroidVisibilityType.Public => NotificationVisibility.Public,
76 | AndroidVisibilityType.Secret => NotificationVisibility.Secret,
77 | _ => throw new ArgumentOutOfRangeException(nameof(type), type, null)
78 | };
79 |
80 | ///
81 | ///
82 | ///
83 | ///
84 | ///
85 | public static string ToNative(this NotificationCategoryType type) => type switch
86 | {
87 | NotificationCategoryType.Alarm => NotificationCompat.CategoryAlarm,
88 | NotificationCategoryType.Status => NotificationCompat.CategoryStatus,
89 | NotificationCategoryType.Reminder => NotificationCompat.CategoryReminder,
90 | NotificationCategoryType.Event => NotificationCompat.CategoryEvent,
91 | NotificationCategoryType.Error => NotificationCompat.CategoryError,
92 | NotificationCategoryType.Progress => NotificationCompat.CategoryProgress,
93 | NotificationCategoryType.Promo => NotificationCompat.CategoryPromo,
94 | NotificationCategoryType.Recommendation => NotificationCompat.CategoryRecommendation,
95 | NotificationCategoryType.Service => NotificationCompat.CategoryService,
96 | _ => NotificationCompat.CategoryStatus
97 | };
98 |
99 | ///
100 | ///
101 | ///
102 | ///
103 | ///
104 | public static AlarmType ToNative(this AndroidAlarmType type) => type switch
105 | {
106 | AndroidAlarmType.Rtc => AlarmType.Rtc,
107 | AndroidAlarmType.RtcWakeup => AlarmType.RtcWakeup,
108 | AndroidAlarmType.ElapsedRealtime => AlarmType.ElapsedRealtime,
109 | AndroidAlarmType.ElapsedRealtimeWakeup => AlarmType.ElapsedRealtimeWakeup,
110 | _ => AlarmType.Rtc
111 | };
112 |
113 | ///
114 | ///
115 | ///
116 | ///
117 | ///
118 | public static PendingIntentFlags ToNative(this AndroidPendingIntentFlags type) => ((PendingIntentFlags)type).SetImmutableIfNeeded();
119 |
120 | ///
121 | ///
122 | ///
123 | ///
124 | ///
125 | public static PendingIntentFlags SetImmutableIfNeeded(this PendingIntentFlags type)
126 | {
127 | if (OperatingSystem.IsAndroidVersionAtLeast(31) &&
128 | type.HasFlag(PendingIntentFlags.Immutable) == false)
129 | {
130 | type |= PendingIntentFlags.Immutable;
131 | }
132 |
133 | return type;
134 | }
135 |
136 | internal static bool IsValidResource(this Android.Net.Uri uri, Context context)
137 | {
138 | var contentResolver = context.ContentResolver;
139 | if (contentResolver is null)
140 | {
141 | return false;
142 | }
143 |
144 | try
145 | {
146 | contentResolver.OpenInputStream(uri)?.Close();
147 | return true;
148 | }
149 | catch (Exception)
150 | {
151 | return false;
152 | }
153 | }
154 | }
--------------------------------------------------------------------------------
/Source/Plugin.LocalNotification/Platforms/Android/ScheduledAlarmReceiver.cs:
--------------------------------------------------------------------------------
1 | using Android.App;
2 | using Android.Content;
3 |
4 | namespace Plugin.LocalNotification.Platforms;
5 |
6 | [BroadcastReceiver(
7 | Name = ReceiverName,
8 | Enabled = true,
9 | Exported = false,
10 | Label = "Plugin LocalNotification Scheduled Alarm Receiver")]
11 | [IntentFilter(
12 | [
13 | Intent.ActionBootCompleted,
14 | Intent.ActionMyPackageReplaced,
15 | "android.intent.action.QUICKBOOT_POWERON",
16 | "com.htc.intent.action.QUICKBOOT_POWERON"
17 | ],
18 | Categories = new[]
19 | {
20 | Intent.CategoryHome
21 | })]
22 | internal class ScheduledAlarmReceiver : BroadcastReceiver
23 | {
24 | ///
25 | ///
26 | ///
27 | public const string ReceiverName = "plugin.LocalNotification." + nameof(ScheduledAlarmReceiver);
28 |
29 | public override async void OnReceive(Context? context, Intent? intent)
30 | {
31 | try
32 | {
33 | var notificationService = TryGetDefaultDroidNotificationService();
34 |
35 | if (intent is null)
36 | {
37 | LocalNotificationCenter.Log("No intent");
38 | return;
39 | }
40 |
41 | if (intent.Action is Intent.ActionBootCompleted or
42 | Intent.ActionMyPackageReplaced or
43 | "android.intent.action.QUICKBOOT_POWERON" or
44 | "com.htc.intent.action.QUICKBOOT_POWERON")
45 | {
46 | LocalNotificationCenter.Log("ActionBootCompleted");
47 |
48 | var requestList = NotificationRepository.GetPendingList();
49 | if (requestList.Count <= 0)
50 | {
51 | LocalNotificationCenter.Log("No Pending Notification Request");
52 | return;
53 | }
54 |
55 | foreach (var request in requestList)
56 | {
57 | await SendNotification(notificationService, request);
58 | }
59 | }
60 | else
61 | {
62 | var requestSerialize = intent.GetStringExtra(LocalNotificationCenter.ReturnRequest);
63 | if (string.IsNullOrWhiteSpace(requestSerialize))
64 | {
65 | LocalNotificationCenter.Log("Request Json Not Found");
66 | return;
67 | }
68 | var request = LocalNotificationCenter.GetRequest(requestSerialize);
69 |
70 | await SendNotification(notificationService, request);
71 | }
72 | }
73 | catch (Exception ex)
74 | {
75 | LocalNotificationCenter.Log(ex);
76 | }
77 | }
78 |
79 | private static async Task SendNotification(NotificationServiceImpl notificationService, NotificationRequest request)
80 | {
81 | if (request is null)
82 | {
83 | LocalNotificationCenter.Log("Request Not Found");
84 | return;
85 | }
86 |
87 | if (request.Schedule.NotifyTime is null)
88 | {
89 | LocalNotificationCenter.Log($"Notification {request.NotificationId} has no NotifyTime");
90 | return;
91 | }
92 |
93 | if (request.Schedule.NotifyAutoCancelTime <= DateTime.Now)
94 | {
95 | _ = notificationService.Cancel(request.NotificationId);
96 | LocalNotificationCenter.Log($"Notification {request.NotificationId} Auto Canceled");
97 | return;
98 | }
99 |
100 | var timeNow = DateTime.Now;
101 | var wasReScheduled = false;
102 | if (request.Schedule.Android.IsValidNotifyTime(timeNow, request.Schedule.NotifyTime))
103 | {
104 | if (request.Schedule.Android.IsValidShowNowTime(timeNow, request.Schedule.NotifyTime))
105 | {
106 | _ = await notificationService.ShowNow(request);
107 |
108 | if (request.Schedule.RepeatType == NotificationRepeat.No)
109 | {
110 | NotificationRepository.RemoveByPendingIdList(request.NotificationId);
111 | }
112 | }
113 | else if (request.Schedule.Android.IsValidShowLaterTime(timeNow, request.Schedule.NotifyTime))
114 | {
115 | // schedule again.
116 | wasReScheduled = true;
117 | _ = notificationService.ShowLater(request);
118 | }
119 | }
120 | else
121 | {
122 | LocalNotificationCenter.Log(
123 | "NotifyTime is earlier than (DateTime.Now - Allowed Delay), notification ignored");
124 | }
125 |
126 | // even if the request is too old to show, if it is a Repeating notification, then reschedule again
127 | if (wasReScheduled == false && request.Schedule.RepeatType != NotificationRepeat.No)
128 | {
129 | request.Schedule.NotifyTime = request.Schedule.Android.GetNextNotifyTimeForRepeat(
130 | request.Schedule.NotifyTime,
131 | request.Schedule.RepeatType,
132 | request.Schedule.NotifyRepeatInterval);
133 |
134 | if (request.Schedule.NotifyTime.HasValue)
135 | {
136 | // reschedule again.
137 | _ = notificationService.ShowLater(request);
138 | }
139 | else
140 | {
141 | LocalNotificationCenter.Log(
142 | $"Notification {request.NotificationId} New NotifyTime is null, no reschedule");
143 | }
144 | }
145 | }
146 |
147 | private static NotificationServiceImpl TryGetDefaultDroidNotificationService() => LocalNotificationCenter.Current is NotificationServiceImpl notificationService
148 | ? notificationService
149 | : new NotificationServiceImpl();
150 | }
--------------------------------------------------------------------------------
/Source/Plugin.LocalNotification/Platforms/Generic/NotificationServiceImpl.cs:
--------------------------------------------------------------------------------
1 | using Plugin.LocalNotification.EventArgs;
2 |
3 | namespace Plugin.LocalNotification.Platforms.Generic
4 | {
5 | internal class NotificationServiceImpl : INotificationService
6 | {
7 | public Func>? NotificationReceiving { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
8 |
9 | // This usually is a placeholder as .NET MAUI apps typically don't run on .NET generic targets unless through unit tests and such
10 | public bool IsSupported => false;
11 |
12 | public event NotificationActionTappedEventHandler? NotificationActionTapped;
13 | public event NotificationReceivedEventHandler? NotificationReceived;
14 | public event NotificationDisabledEventHandler? NotificationsDisabled;
15 |
16 | public Task AreNotificationsEnabled() => throw new NotImplementedException();
17 | public bool Cancel(params int[] notificationIdList) => throw new NotImplementedException();
18 | public bool CancelAll() => throw new NotImplementedException();
19 | public bool Clear(params int[] notificationIdList) => throw new NotImplementedException();
20 | public bool ClearAll() => throw new NotImplementedException();
21 | public Task> GetDeliveredNotificationList() => throw new NotImplementedException();
22 | public Task> GetPendingNotificationList() => throw new NotImplementedException();
23 | public void OnNotificationActionTapped(NotificationActionEventArgs e) => throw new NotImplementedException();
24 | public void OnNotificationReceived(NotificationEventArgs e) => throw new NotImplementedException();
25 | public void OnNotificationsDisabled() => throw new NotImplementedException();
26 | public void RegisterCategoryList(HashSet categoryList) => throw new NotImplementedException();
27 | public Task RequestNotificationPermission(NotificationPermission? permission = null) => throw new NotImplementedException();
28 | public Task Show(NotificationRequest request) => throw new NotImplementedException();
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/Source/Plugin.LocalNotification/Platforms/Windows/LocalNotificationCenter.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.Logging;
2 | using Microsoft.Toolkit.Uwp.Notifications;
3 | using Plugin.LocalNotification.EventArgs;
4 | using System.Diagnostics;
5 | using System.Runtime.CompilerServices;
6 | using Windows.UI.Notifications;
7 |
8 | namespace Plugin.LocalNotification;
9 |
10 | public partial class LocalNotificationCenter
11 | {
12 | ///
13 | ///
14 | ///
15 | ///
16 | ///
17 | public static async Task RequestNotificationPermissionAsync(NotificationPermission? permission = null)
18 | {
19 | return await Task.FromResult(true);
20 | }
21 |
22 | ///
23 | /// Need to add this because otherwise setting background activation does nothing.
24 | ///
25 | public static void SetupBackgroundActivation()
26 | {
27 | ToastNotificationManagerCompat.OnActivated += (notificationArgs) =>
28 | {
29 | // this will run everytime ToastNotification.Activated is called,
30 | // regardless of what toast is clicked and what element is clicked on.
31 | // Works for all types of ToastActivationType so long as the Windows app manifest
32 | // has been updated to support ToastNotifications.
33 |
34 | // you can check your args here, however I'll be doing mine below to keep it cleaner.
35 | // With so many ToastNotifications it would be messy to check all of them here.
36 |
37 | Debug.WriteLine($"A ToastNotification was just activated! Arguments: {notificationArgs.Argument}");
38 |
39 | NotifyNotificationTapped(notificationArgs.Argument);
40 | };
41 | }
42 |
43 | ///
44 | /// Notify Local Notification Tapped.
45 | ///
46 | ///
47 | internal static void NotifyNotificationTapped(string arguments)
48 | {
49 | try
50 | {
51 | var (actionId, request) = GetRequestFromArguments(arguments);
52 | if (actionId == -1000 || request is null)
53 | {
54 | return;
55 | }
56 | var actionArgs = new NotificationActionEventArgs
57 | {
58 | ActionId = actionId,
59 | Request = request
60 | };
61 | Current.OnNotificationActionTapped(actionArgs);
62 | }
63 | catch (Exception ex)
64 | {
65 | Log(ex);
66 | }
67 | }
68 |
69 | internal static (int, NotificationRequest?) GetRequestFromArguments(string arguments)
70 | {
71 | var args = ToastArguments.Parse(arguments);
72 |
73 | var actionId = args.GetInt(ReturnRequestActionId);
74 | var notifiactionId = args.Get(ReturnRequest);
75 |
76 | var toastNotification = ToastNotificationManager.History.GetHistory().FirstOrDefault(t => t.Tag == notifiactionId);
77 |
78 | var element = toastNotification?.Content.ChildNodes.FirstOrDefault(e => e.NodeName == "toast");
79 | var attribute = element?.Attributes.FirstOrDefault(a => a.NodeName == "launch");
80 |
81 | // TODO: get the request
82 | var request = GetRequest("");
83 | return (actionId, request);
84 | }
85 |
86 | ///
87 | ///
88 | ///
89 | ///
90 | ///
91 | internal static void Log(string? message, [CallerMemberName] string callerName = "")
92 | {
93 | var logMessage = $"{callerName}: {message}";
94 | Logger?.Log(LogLevel, logMessage);
95 | }
96 |
97 | ///
98 | ///
99 | ///
100 | ///
101 | ///
102 | ///
103 | internal static void Log(Exception? ex, string? message = null, [CallerMemberName] string callerName = "")
104 | {
105 | var logMessage = $"{callerName}: {message}";
106 | Logger?.LogError(ex, logMessage);
107 | }
108 | }
--------------------------------------------------------------------------------
/Source/Plugin.LocalNotification/Platforms/Windows/NotificationRepository.cs:
--------------------------------------------------------------------------------
1 | using Windows.Storage;
2 |
3 | namespace Plugin.LocalNotification.Platforms;
4 |
5 | ///
6 | ///
7 | ///
8 | internal class NotificationRepository
9 | {
10 | private static readonly Lazy MySingleton =
11 | new(() => new NotificationRepository(), LazyThreadSafetyMode.PublicationOnly);
12 |
13 | private static readonly object Locker = new();
14 |
15 | ///
16 | ///
17 | ///
18 | internal static NotificationRepository Current => MySingleton.Value;
19 |
20 | ///
21 | ///
22 | ///
23 | private const string PendingListKey = $"{nameof(NotificationRepository)}PendingList";
24 |
25 | ///
26 | ///
27 | ///
28 | private const string DeliveredListKey = $"{nameof(NotificationRepository)}DeliveredList";
29 |
30 | private static ApplicationDataContainer GetApplicationDataContainer()
31 | {
32 | var localSettings = ApplicationData.Current.LocalSettings;
33 | return localSettings;
34 | }
35 |
36 | ///
37 | ///
38 | ///
39 | ///
40 | internal static void RemoveByPendingIdList(params int[] notificationIdList)
41 | {
42 | var itemList = GetPendingList();
43 | _ = itemList.RemoveAll(r => notificationIdList.Contains(r.NotificationId));
44 | SetPendingList(itemList);
45 | }
46 |
47 | ///
48 | ///
49 | ///
50 | ///
51 | internal static void AddPendingRequest(NotificationRequest request)
52 | {
53 | var itemList = GetPendingList();
54 | _ = itemList.RemoveAll(r => request.NotificationId == r.NotificationId);
55 | _ = itemList.RemoveAll(r =>
56 | r.Schedule.NotifyTime.HasValue &&
57 | r.Schedule.Android.IsValidNotifyTime(DateTime.Now, r.Schedule.NotifyTime) == false);
58 | itemList.Add(request);
59 | SetPendingList(itemList);
60 | }
61 |
62 | ///
63 | ///
64 | ///
65 | ///
66 | internal static void AddDeliveredRequest(NotificationRequest request)
67 | {
68 | var itemList = GetDeliveredList();
69 | _ = itemList.RemoveAll(r => request.NotificationId == r.NotificationId);
70 | itemList.Add(request);
71 | SetDeliveredList(itemList);
72 | }
73 |
74 | ///
75 | ///
76 | ///
77 | internal static void RemoveDeliveredList() => SetDeliveredList(null);
78 |
79 | ///
80 | ///
81 | ///
82 | internal static void RemovePendingList() => SetPendingList(null);
83 |
84 | ///
85 | ///
86 | ///
87 | ///
88 | internal static void RemoveByDeliveredIdList(params int[] notificationIdList)
89 | {
90 | var itemList = GetDeliveredList();
91 | _ = (itemList?.RemoveAll(r => notificationIdList.Contains(r.NotificationId)));
92 | SetDeliveredList(itemList);
93 | }
94 |
95 | ///
96 | ///
97 | ///
98 | ///
99 | internal static List GetPendingList()
100 | {
101 | var itemList = GetList(PendingListKey);
102 | return itemList;
103 | }
104 |
105 | private static void SetPendingList(List? list) => SetList(PendingListKey, list);
106 |
107 | ///
108 | ///
109 | ///
110 | ///
111 | internal static List GetDeliveredList()
112 | {
113 | var itemList = GetList(DeliveredListKey);
114 | return itemList;
115 | }
116 |
117 | private static void SetDeliveredList(List? list) => SetList(DeliveredListKey, list);
118 |
119 | private static List GetList(string key)
120 | {
121 | lock (Locker)
122 | {
123 | var appDataContainer = GetApplicationDataContainer();
124 | var jsonText = string.Empty;
125 | if (appDataContainer.Values.TryGetValue(key, out var data))
126 | {
127 | jsonText = data.ToString();
128 | }
129 | return string.IsNullOrWhiteSpace(jsonText)
130 | ? []
131 | : LocalNotificationCenter.GetRequestList(jsonText) ?? [];
132 | }
133 | }
134 |
135 | private static void SetList(string key, List? list)
136 | {
137 | lock (Locker)
138 | {
139 | var appDataContainer = GetApplicationDataContainer();
140 | var jsonText = string.Empty;
141 | if (list != null && list.Count != 0)
142 | {
143 | jsonText = LocalNotificationCenter.GetRequestListSerialize(list);
144 | }
145 |
146 | if (appDataContainer.Values.ContainsKey(key))
147 | {
148 | appDataContainer.Values[key] = jsonText;
149 | }
150 | else
151 | {
152 | appDataContainer.Values.Add(key, jsonText);
153 | }
154 | }
155 | }
156 | }
--------------------------------------------------------------------------------
/Source/Plugin.LocalNotification/Platforms/iOS/LocalNotificationCenter.cs:
--------------------------------------------------------------------------------
1 | using Foundation;
2 | using Microsoft.Extensions.Logging;
3 | using Plugin.LocalNotification.Platforms;
4 | using System.Runtime.CompilerServices;
5 | using UIKit;
6 | using UserNotifications;
7 |
8 | namespace Plugin.LocalNotification;
9 |
10 | public partial class LocalNotificationCenter
11 | {
12 | ///
13 | /// This allow developer to change UNUserNotificationCenterDelegate,
14 | /// extend Plugin.LocalNotification.Platform.iOS.UserNotificationCenterDelegate
15 | /// Create custom IUNUserNotificationCenterDelegate
16 | /// and set it using this method
17 | ///
18 | ///
19 | public static void SetUserNotificationCenterDelegate(UserNotificationCenterDelegate? notificationDelegate = null) => UNUserNotificationCenter.Current.Delegate = notificationDelegate ?? new UserNotificationCenterDelegate();
20 |
21 | ///
22 | ///
23 | ///
24 | ///
25 | ///
26 | public static NotificationRequest? GetRequest(UNNotificationContent? notificationContent)
27 | {
28 | if (notificationContent is null)
29 | {
30 | return null;
31 | }
32 |
33 | var dictionary = notificationContent.UserInfo;
34 |
35 | if (!dictionary.ContainsKey(new NSString(ReturnRequest)))
36 | {
37 | return null;
38 | }
39 |
40 | var requestSerialize = dictionary[ReturnRequest].ToString();
41 |
42 | var request = GetRequest(requestSerialize);
43 |
44 | return request;
45 | }
46 |
47 | ///
48 | /// Reset Application Icon Badge Number when there are no notifications.
49 | ///
50 | ///
51 | public static void ResetApplicationIconBadgeNumber(UIApplication uiApplication)
52 | {
53 | try
54 | {
55 | var notificationList = new List();
56 | //Remove badges on app enter foreground if user cleared the notification in the notification panel
57 | var completionSource = new TaskCompletionSource();
58 | UNUserNotificationCenter.Current.GetDeliveredNotifications((notificationArray) =>
59 | {
60 | notificationList.AddRange(notificationArray);
61 | completionSource.SetResult(true);
62 | });
63 | completionSource.Task.Wait();
64 | if (notificationList.Count != 0)
65 | {
66 | return;
67 | }
68 |
69 | uiApplication.InvokeOnMainThread(() =>
70 | {
71 | if (UIDevice.CurrentDevice.CheckSystemVersion(16, 0))
72 | {
73 | UNUserNotificationCenter.Current.SetBadgeCount(0, (error) =>
74 | {
75 | if (error != null)
76 | {
77 | Log(error.LocalizedDescription);
78 | }
79 | });
80 | }
81 | else
82 | {
83 | uiApplication.ApplicationIconBadgeNumber = 0;
84 | UIApplication.SharedApplication.ApplicationIconBadgeNumber = 0;
85 | }
86 | });
87 | }
88 | catch (Exception ex)
89 | {
90 | Log(ex);
91 | throw;
92 | }
93 | }
94 |
95 | ///
96 | /// Reset Application Icon Badge Number when there are no notifications.
97 | ///
98 | ///
99 | public static async Task ResetApplicationIconBadgeNumberAsync(UIApplication uiApplication)
100 | {
101 | try
102 | {
103 | //Remove badges on app enter foreground if user cleared the notification in the notification panel
104 | var notificationList = await UNUserNotificationCenter.Current.GetDeliveredNotificationsAsync()
105 | .ConfigureAwait(false);
106 |
107 | if (notificationList.Length != 0)
108 | {
109 | return;
110 | }
111 |
112 | uiApplication.InvokeOnMainThread(async () =>
113 | {
114 | if (UIDevice.CurrentDevice.CheckSystemVersion(16, 0))
115 | {
116 | await UNUserNotificationCenter.Current.SetBadgeCountAsync(0);
117 | }
118 | else
119 | {
120 | uiApplication.ApplicationIconBadgeNumber = 0;
121 | UIApplication.SharedApplication.ApplicationIconBadgeNumber = 0;
122 | }
123 | });
124 | }
125 | catch (Exception ex)
126 | {
127 | Log(ex);
128 | throw;
129 | }
130 | }
131 |
132 | ///
133 | ///
134 | ///
135 | ///
136 | ///
137 | internal static void Log(string? message, [CallerMemberName] string callerName = "")
138 | {
139 | Logger?.Log(LogLevel, "{callerName}: {message}", callerName, message);
140 | }
141 |
142 | ///
143 | ///
144 | ///
145 | ///
146 | ///
147 | ///
148 | internal static void Log(Exception? ex, string? message = null, [CallerMemberName] string callerName = "")
149 | {
150 | Logger?.LogError(ex, "{callerName}: {message}", callerName, message);
151 | }
152 | }
--------------------------------------------------------------------------------
/Source/Plugin.LocalNotification/Platforms/iOS/ManifestInfo.cs:
--------------------------------------------------------------------------------
1 | [assembly: System.Reflection.AssemblyMetadata("IsTrimmable", "True")]
--------------------------------------------------------------------------------
/Source/Plugin.LocalNotification/Platforms/iOS/PlatformExtensions.cs:
--------------------------------------------------------------------------------
1 | using Plugin.LocalNotification.iOSOption;
2 | using UserNotifications;
3 |
4 | namespace Plugin.LocalNotification.Platforms;
5 |
6 | ///
7 | ///
8 | ///
9 | public static class PlatformExtensions
10 | {
11 | ///
12 | ///
13 | ///
14 | ///
15 | ///
16 | public static UNNotificationInterruptionLevel ToNative(this iOSPriority priority)
17 | {
18 | return !OperatingSystem.IsIOSVersionAtLeast(15)
19 | ? default
20 | : priority switch
21 | {
22 | iOSPriority.Passive => UNNotificationInterruptionLevel.Passive2,
23 | iOSPriority.Active => UNNotificationInterruptionLevel.Active2,
24 | iOSPriority.TimeSensitive => UNNotificationInterruptionLevel.TimeSensitive2,
25 | iOSPriority.Critical => UNNotificationInterruptionLevel.Critical2,
26 | _ => UNNotificationInterruptionLevel.Active2,
27 | };
28 | }
29 |
30 | ///
31 | ///
32 | ///
33 | ///
34 | ///
35 | public static UNAuthorizationOptions ToNative(this iOSAuthorizationOptions type)
36 | {
37 | var nativeEnum = (UNAuthorizationOptions)type;
38 | return nativeEnum;
39 | }
40 |
41 | ///
42 | ///
43 | ///
44 | ///
45 | ///
46 | public static UNNotificationActionOptions ToNative(this iOSActionType type) => type switch
47 | {
48 | iOSActionType.Foreground => UNNotificationActionOptions.Foreground,
49 | iOSActionType.Destructive => UNNotificationActionOptions.Destructive,
50 | iOSActionType.AuthenticationRequired => UNNotificationActionOptions.AuthenticationRequired,
51 | _ => UNNotificationActionOptions.None,
52 | };
53 |
54 | ///
55 | ///
56 | ///
57 | ///
58 | ///
59 | public static string ToNative(this NotificationCategoryType type) => type.ToString();
60 | }
--------------------------------------------------------------------------------
/Source/Plugin.LocalNotification/Plugin.LocalNotification.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | net8.0;net9.0;net8.0-android;net9.0-android;net8.0-ios;net9.0-ios
4 | $(TargetFrameworks);net8.0-windows10.0.19041.0;net9.0-windows10.0.19041.0
5 | $(AssemblyName) ($(TargetFramework))
6 | en-US
7 | True
8 | en
9 | false
10 |
11 | $(AssemblyName)
12 | true
13 | Elvin (Tharindu) Thudugala
14 | dotnet;android;ios;local;notification;local.notification;maui
15 | https://github.com/thudugala/Plugin.LocalNotification
16 | https://github.com/thudugala/Plugin.LocalNotification
17 | git
18 | MIT
19 | true
20 | The local notification plugin provides a way to show local notifications from MAUI apps.
21 | icon.png
22 | Copyright © Elvin (Tharindu) Thudugala
23 | 12.0.1
24 | Check: https://github.com/thudugala/Plugin.LocalNotification/releases
25 | True
26 |
27 | true
28 | true
29 | true
30 | snupkg
31 | true
32 |
33 | true
34 | true
35 | true
36 |
37 | enable
38 | enable
39 |
40 | Plugin.LocalNotification.ruleset
41 |
42 | README.md
43 |
44 | true
45 |
46 | 15.0
47 | 21.0
48 | 10.0.17763.0
49 | 10.0.17763.0
50 |
51 |
52 |
53 | true
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 | True
86 | True
87 | Resources.resx
88 |
89 |
90 |
91 |
92 |
93 | ResXFileCodeGenerator
94 | Resources.Designer.cs
95 |
96 |
97 |
98 |
--------------------------------------------------------------------------------
/Source/Plugin.LocalNotification/Plugin.LocalNotification.ruleset:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
--------------------------------------------------------------------------------
/Source/Plugin.LocalNotification/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Runtime.CompilerServices;
2 |
3 | [assembly: InternalsVisibleTo("LocalNotification.UnitTests")]
--------------------------------------------------------------------------------
/Source/Plugin.LocalNotification/Properties/Resources.Designer.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // This code was generated by a tool.
4 | // Runtime Version:4.0.30319.42000
5 | //
6 | // Changes to this file may cause incorrect behavior and will be lost if
7 | // the code is regenerated.
8 | //
9 | //------------------------------------------------------------------------------
10 |
11 | namespace Plugin.LocalNotification.Properties;
12 | using System;
13 |
14 |
15 | ///
16 | /// A strongly-typed resource class, for looking up localized strings, etc.
17 | ///
18 | // This class was auto-generated by the StronglyTypedResourceBuilder
19 | // class via a tool like ResGen or Visual Studio.
20 | // To add or remove a member, edit your .ResX file then rerun ResGen
21 | // with the /str option, or rebuild your VS project.
22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
25 | internal class Resources {
26 |
27 | private static global::System.Resources.ResourceManager resourceMan;
28 |
29 | private static global::System.Globalization.CultureInfo resourceCulture;
30 |
31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
32 | internal Resources() {
33 | }
34 |
35 | ///
36 | /// Returns the cached ResourceManager instance used by this class.
37 | ///
38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
39 | internal static global::System.Resources.ResourceManager ResourceManager {
40 | get {
41 | if (object.ReferenceEquals(resourceMan, null)) {
42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Plugin.LocalNotification.Properties.Resources", typeof(Resources).Assembly);
43 | resourceMan = temp;
44 | }
45 | return resourceMan;
46 | }
47 | }
48 |
49 | ///
50 | /// Overrides the current thread's CurrentUICulture property for all
51 | /// resource lookups using this strongly typed resource class.
52 | ///
53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
54 | internal static global::System.Globalization.CultureInfo Culture {
55 | get {
56 | return resourceCulture;
57 | }
58 | set {
59 | resourceCulture = value;
60 | }
61 | }
62 |
63 | ///
64 | /// Looks up a localized string similar to Alarm service not found.
65 | ///
66 | internal static string AndroidAlarmServiceNotFound {
67 | get {
68 | return ResourceManager.GetString("AndroidAlarmServiceNotFound", resourceCulture);
69 | }
70 | }
71 |
72 | ///
73 | /// Looks up a localized string similar to Notification service not found.
74 | ///
75 | internal static string AndroidNotificationServiceNotFound {
76 | get {
77 | return ResourceManager.GetString("AndroidNotificationServiceNotFound", resourceCulture);
78 | }
79 | }
80 |
81 | ///
82 | /// Looks up a localized string similar to [Plugin.LocalNotification] No platform plugin found. Did you install the nuget package in your app project as well?.
83 | ///
84 | internal static string PluginNotFound {
85 | get {
86 | return ResourceManager.GetString("PluginNotFound", resourceCulture);
87 | }
88 | }
89 |
90 | ///
91 | /// Looks up a localized string similar to [Plugin.LocalNotification] No Serializer found..
92 | ///
93 | internal static string PluginSerializerNotFound {
94 | get {
95 | return ResourceManager.GetString("PluginSerializerNotFound", resourceCulture);
96 | }
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/Source/Plugin.LocalNotification/Properties/Resources.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 | text/microsoft-resx
110 |
111 |
112 | 2.0
113 |
114 |
115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
116 |
117 |
118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
119 |
120 |
121 | Alarm service not found
122 |
123 |
124 | Notification service not found
125 |
126 |
127 | [Plugin.LocalNotification] No platform plugin found. Did you install the nuget package in your app project as well?
128 |
129 |
130 | [Plugin.LocalNotification] No Serializer found.
131 |
132 |
--------------------------------------------------------------------------------
/Source/Plugin.LocalNotification/WindowsOption/WindowsAction.cs:
--------------------------------------------------------------------------------
1 | namespace Plugin.LocalNotification.WindowsOption;
2 |
3 | ///
4 | ///
5 | ///
6 | public class WindowsAction
7 | {
8 | ///
9 | /// Default is false
10 | ///
11 | public bool LaunchAppWhenTapped { get; set; } = false;
12 |
13 | ///
14 | /// Default is false
15 | ///
16 | public bool DismissWhenTapped { get; set; } = false;
17 | }
--------------------------------------------------------------------------------
/Source/Plugin.LocalNotification/WindowsOption/WindowsOptions.cs:
--------------------------------------------------------------------------------
1 | namespace Plugin.LocalNotification.WindowsOption;
2 |
3 | ///
4 | ///
5 | ///
6 | public class WindowsOptions
7 | {
8 | ///
9 | /// Default is true
10 | ///
11 | public bool LaunchAppWhenTapped { get; set; } = true;
12 | }
--------------------------------------------------------------------------------
/Source/Plugin.LocalNotification/iOSOption/IiOSLocalNotificationBuilder.cs:
--------------------------------------------------------------------------------
1 | #if IOS
2 | using Plugin.LocalNotification.Platforms;
3 | #endif
4 |
5 | namespace Plugin.LocalNotification.iOSOption;
6 |
7 | ///
8 | ///
9 | ///
10 | public interface IiOSLocalNotificationBuilder
11 | {
12 | #if IOS
13 | ///
14 | /// This allow developer to change UNUserNotificationCenterDelegate,
15 | /// extend Plugin.LocalNotification.Platform.iOS.UserNotificationCenterDelegate
16 | /// Create custom IUNUserNotificationCenterDelegate
17 | /// and set it using this method
18 | ///
19 | ///
20 | ///
21 | IiOSLocalNotificationBuilder SetCustomUserNotificationCenterDelegate(UserNotificationCenterDelegate customUserNotificationCenterDelegate);
22 | #endif
23 | }
--------------------------------------------------------------------------------
/Source/Plugin.LocalNotification/iOSOption/iOSAction.cs:
--------------------------------------------------------------------------------
1 | namespace Plugin.LocalNotification.iOSOption;
2 |
3 | ///
4 | ///
5 | ///
6 | public class iOSAction
7 | {
8 | ///
9 | ///
10 | ///
11 | public iOSActionType Action { get; set; } = iOSActionType.None;
12 |
13 | ///
14 | /// An icon associated with an action.
15 | ///
16 | public iOSActionIcon Icon { get; set; } = new ();
17 | }
--------------------------------------------------------------------------------
/Source/Plugin.LocalNotification/iOSOption/iOSActionIcon.cs:
--------------------------------------------------------------------------------
1 | namespace Plugin.LocalNotification.iOSOption;
2 |
3 | ///
4 | ///
5 | ///
6 | public class iOSActionIcon
7 | {
8 | ///
9 | /// Defuat is None
10 | ///
11 | public iOSActionIconType Type { get; set; } = iOSActionIconType.None;
12 |
13 | ///
14 | /// Image Name
15 | ///
16 | public string Name { get; set; } = string.Empty;
17 | }
18 |
--------------------------------------------------------------------------------
/Source/Plugin.LocalNotification/iOSOption/iOSActionIconType.cs:
--------------------------------------------------------------------------------
1 | namespace Plugin.LocalNotification.iOSOption;
2 |
3 | ///
4 | ///
5 | ///
6 | public enum iOSActionIconType
7 | {
8 | ///
9 | /// No Image is set
10 | ///
11 | None,
12 |
13 | ///
14 | /// Creates an action icon by using a system symbol image.
15 | ///
16 | System,
17 |
18 | ///
19 | /// Creates an action icon based on an image in your app’s bundle, preferably in an asset catalog.
20 | ///
21 | Template
22 | }
23 |
--------------------------------------------------------------------------------
/Source/Plugin.LocalNotification/iOSOption/iOSActionType.cs:
--------------------------------------------------------------------------------
1 | namespace Plugin.LocalNotification.iOSOption;
2 |
3 | ///
4 | /// The behaviors you can apply to an action
5 | ///
6 | public enum iOSActionType
7 | {
8 | ///
9 | ///
10 | ///
11 | None,
12 |
13 | ///
14 | /// The action performs a destructive task
15 | ///
16 | Destructive,
17 |
18 | ///
19 | /// The action can be performed only on an unlocked device
20 | ///
21 | AuthenticationRequired,
22 |
23 | ///
24 | /// The action causes the app to launch in the foreground
25 | ///
26 | Foreground
27 | }
--------------------------------------------------------------------------------
/Source/Plugin.LocalNotification/iOSOption/iOSAuthorizationOptions.cs:
--------------------------------------------------------------------------------
1 | namespace Plugin.LocalNotification.iOSOption;
2 |
3 | ///
4 | ///
5 | ///
6 | [Flags]
7 | public enum iOSAuthorizationOptions : ulong
8 | {
9 | ///
10 | ///
11 | ///
12 | None = 0,
13 |
14 | ///
15 | ///
16 | ///
17 | Badge = 1,
18 |
19 | ///
20 | ///
21 | ///
22 | Sound = 2,
23 |
24 | ///
25 | ///
26 | ///
27 | Alert = 4,
28 |
29 | ///
30 | ///
31 | ///
32 | CarPlay = 8,
33 |
34 | ///
35 | ///
36 | ///
37 | CriticalAlert = 16,
38 |
39 | ///
40 | ///
41 | ///
42 | ProvidesAppNotificationSettings = 32,
43 |
44 | ///
45 | ///
46 | ///
47 | Provisional = 64,
48 |
49 | ///
50 | ///
51 | ///
52 | Announcement = 128,
53 |
54 | ///
55 | ///
56 | ///
57 | TimeSensitive = 256
58 | }
--------------------------------------------------------------------------------
/Source/Plugin.LocalNotification/iOSOption/iOSGeofenceOptions.cs:
--------------------------------------------------------------------------------
1 | namespace Plugin.LocalNotification.iOSOption;
2 |
3 | ///
4 | ///
5 | ///
6 | public class iOSGeofenceOptions
7 | {
8 | ///
9 | /// If you specify true for the repeats parameter,
10 | /// you must explicitly remove the notification request to stop the delivery of the associated notification
11 | /// Defualt value is false
12 | ///
13 | public bool Repeats { get; set; } = false;
14 | }
--------------------------------------------------------------------------------
/Source/Plugin.LocalNotification/iOSOption/iOSLocalNotificationBuilder.cs:
--------------------------------------------------------------------------------
1 | #if IOS
2 | using Plugin.LocalNotification.Platforms;
3 | #endif
4 |
5 | namespace Plugin.LocalNotification.iOSOption;
6 |
7 | ///
8 | public class iOSLocalNotificationBuilder : IiOSLocalNotificationBuilder
9 | {
10 | #if IOS
11 | ///
12 | ///
13 | ///
14 | internal UserNotificationCenterDelegate? CustomUserNotificationCenterDelegate { get; private set; }
15 |
16 | ///
17 | public IiOSLocalNotificationBuilder SetCustomUserNotificationCenterDelegate(UserNotificationCenterDelegate customUserNotificationCenterDelegate)
18 | {
19 | CustomUserNotificationCenterDelegate = customUserNotificationCenterDelegate;
20 | return this;
21 | }
22 | #endif
23 | }
--------------------------------------------------------------------------------
/Source/Plugin.LocalNotification/iOSOption/iOSLocationAuthorization.cs:
--------------------------------------------------------------------------------
1 | namespace Plugin.LocalNotification.iOSOption;
2 |
3 | ///
4 | ///
5 | ///
6 | public enum iOSLocationAuthorization
7 | {
8 | ///
9 | ///
10 | ///
11 | No,
12 |
13 | ///
14 | ///
15 | ///
16 | Always,
17 |
18 | ///
19 | ///
20 | ///
21 | WhenInUse
22 | }
--------------------------------------------------------------------------------
/Source/Plugin.LocalNotification/iOSOption/iOSNotificationPermission.cs:
--------------------------------------------------------------------------------
1 | namespace Plugin.LocalNotification.iOSOption;
2 |
3 | ///
4 | ///
5 | ///
6 | public class iOSNotificationPermission
7 | {
8 | ///
9 | ///
10 | ///
11 | public iOSAuthorizationOptions NotificationAuthorization { get; set; } = iOSAuthorizationOptions.Alert |
12 | iOSAuthorizationOptions.Badge |
13 | iOSAuthorizationOptions.Sound;
14 |
15 | ///
16 | ///
17 | ///
18 | public iOSLocationAuthorization LocationAuthorization { get; set; } = iOSLocationAuthorization.No;
19 | }
--------------------------------------------------------------------------------
/Source/Plugin.LocalNotification/iOSOption/iOSOptions.cs:
--------------------------------------------------------------------------------
1 | namespace Plugin.LocalNotification.iOSOption;
2 |
3 | ///
4 | /// NotificationRequest for iOS
5 | ///
6 | public class iOSOptions
7 | {
8 | ///
9 | /// Setting this flag will prevent iOS from displaying the default banner when a Notification is received in foreground
10 | /// Default is false
11 | ///
12 | public bool HideForegroundAlert { get; set; }
13 |
14 | ///
15 | /// Setting this flag will enable iOS to play the default notification sound even if the app is in foreground
16 | /// Default is true
17 | ///
18 | public bool PlayForegroundSound { get; set; } = true;
19 |
20 | ///
21 | /// Setting this flag will enable iOS to Present the notification as a banner
22 | /// Default is true
23 | ///
24 | public bool PresentAsBanner { get; set; } = true;
25 |
26 | ///
27 | /// Setting this flag will enable iOS to Show the notification in Notification Center
28 | /// Default is true
29 | ///
30 | public bool ShowInNotificationCenter { get; set; } = true;
31 |
32 | ///
33 | /// Setting this flag will enable iOS to Apply the notification's badge value to the app’s icon
34 | /// Default is true
35 | ///
36 | public bool ApplyBadgeValue { get; set; } = true;
37 |
38 | ///
39 | /// The Priority determines the degree of Priority associated with the notification.
40 | /// Default is active
41 | ///
42 | public iOSPriority Priority { get; set; } = iOSPriority.Active;
43 |
44 | ///
45 | /// The system uses the relevanceScore, a value between 0 and 1, to sort the notifications from your app. The highest score gets featured in the notification summary.
46 | ///
47 | public double RelevanceScore { get; set; }
48 |
49 | ///
50 | /// The string the notification adds to the category’s summary format string.
51 | ///
52 | public string SummaryArgument { get; set; } = string.Empty;
53 |
54 | ///
55 | /// The number of items the notification adds to the category’s summary format string.
56 | ///
57 | public int SummaryArgumentCount { get; set; }
58 | }
--------------------------------------------------------------------------------
/Source/Plugin.LocalNotification/iOSOption/iOSPriority.cs:
--------------------------------------------------------------------------------
1 | namespace Plugin.LocalNotification.iOSOption;
2 |
3 | ///
4 | ///
5 | ///
6 | public enum iOSPriority
7 | {
8 | ///
9 | /// The system adds the notification to the notification list without lighting up the screen or playing a sound.
10 | ///
11 | Passive = 0,
12 |
13 | ///
14 | /// The system presents the notification immediately, lights up the screen, and can play a sound. (default)
15 | ///
16 | Active = 1,
17 |
18 | ///
19 | /// The system presents the notification immediately, lights up the screen, and can play a sound, but won’t break through system notification controls.
20 | ///
21 | TimeSensitive = 2,
22 |
23 | ///
24 | /// The system presents the notification immediately, lights up the screen, and bypasses the mute switch to play a sound.
25 | ///
26 | Critical = 3
27 | }
--------------------------------------------------------------------------------
/UnitTests/LocalNotification.UnitTests/GlobalUsing.cs:
--------------------------------------------------------------------------------
1 | global using FluentAssertions;
2 | global using Moq;
3 | global using Plugin.LocalNotification.Json;
4 | global using System;
5 | global using System.Collections.Generic;
6 | global using System.IO;
7 | global using Xunit;
--------------------------------------------------------------------------------
/UnitTests/LocalNotification.UnitTests/LocalNotification.UnitTests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net8.0
5 | true
6 | $(BaseIntermediateOutputPath)\GF
7 |
8 | Exe
9 | Plugin.LocalNotification.UnitTests
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/UnitTests/LocalNotification.UnitTests/Resources/dotnet_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thudugala/Plugin.LocalNotification/a19ff2efbe3ded88deeb0e7af4981af3067af374/UnitTests/LocalNotification.UnitTests/Resources/dotnet_logo.png
--------------------------------------------------------------------------------
/UnitTests/LocalNotification.UnitTests/Tests/LocalNotificationCenterTests.cs:
--------------------------------------------------------------------------------
1 | namespace Plugin.LocalNotification.UnitTests.Tests;
2 |
3 | ///
4 | /// Unit tests for the class.
5 | ///
6 | public class LocalNotificationCenterTests : IDisposable
7 | {
8 | ///
9 | /// Disposes resources used by the test class. Resets the to a default instance before each test.
11 | ///
12 | public void Dispose()
13 | {
14 | LocalNotificationCenter.Serializer = new NotificationSerializer();
15 | }
16 |
17 | ///
18 | /// Tests that clears the binary
19 | /// data of large images when the binary size exceeds the limit.
20 | ///
21 | [Fact]
22 | public void GetRequestListSerialize_ShouldClearLargeImageBinary_WhenImageBinaryExceedsLimit()
23 | {
24 | // Arrange
25 | var mockSerializer = new Mock();
26 | var requestList = new List
27 | {
28 | new() {
29 | NotificationId = 1,
30 | Title = "Test Notification",
31 | Image = new NotificationImage { Binary = new byte[100000] }
32 | }
33 | };
34 | mockSerializer.Setup(s => s.Serialize(It.IsAny>())).Returns("SerializedString");
35 | LocalNotificationCenter.Serializer = mockSerializer.Object;
36 |
37 | // Act
38 | var result = LocalNotificationCenter.GetRequestListSerialize(requestList);
39 |
40 | // Assert
41 | result.Should().Be("SerializedString");
42 | requestList[0].Image.Binary.Should().BeEmpty();
43 | mockSerializer.Verify(s => s.Serialize(It.IsAny>()), Times.Once);
44 | }
45 |
46 | ///
47 | /// Tests that handles cases where
48 | /// the or its binary data is null.
49 | ///
50 | [Fact]
51 | public void GetRequestListSerialize_ShouldHandleNullNotificationImage()
52 | {
53 | // Arrange
54 | var requestList = new List
55 | {
56 | new() {
57 | Image = null // Simulate a null NotificationImage
58 | },
59 | new() {
60 | Image = new NotificationImage
61 | {
62 | Binary = null // Simulate a null Binary property
63 | }
64 | }
65 | };
66 |
67 | // Act
68 | var result = LocalNotificationCenter.GetRequestListSerialize(requestList);
69 |
70 | // Assert
71 | result.Should().NotBeNull(); // Ensure serialization does not throw
72 | (result.Contains("[]") || result.Contains("{}")).Should().BeTrue(); // Validate serialized output
73 | }
74 |
75 | ///
76 | /// Tests that returns a
77 | /// serialized string when the request list is valid.
78 | ///
79 | [Fact]
80 | public void GetRequestListSerialize_ShouldReturnSerializedString_WhenRequestListIsValid()
81 | {
82 | // Arrange
83 | var mockSerializer = new Mock();
84 | var requestList = new List
85 | {
86 | new() {
87 | NotificationId = 1,
88 | Title = "Test Notification",
89 | Image = new NotificationImage { Binary = new byte[50000] }
90 | }
91 | };
92 | mockSerializer.Setup(s => s.Serialize(It.IsAny>())).Returns("SerializedString");
93 | LocalNotificationCenter.Serializer = mockSerializer.Object;
94 |
95 | // Act
96 | var result = LocalNotificationCenter.GetRequestListSerialize(requestList);
97 |
98 | // Assert
99 | result.Should().Be("SerializedString");
100 | mockSerializer.Verify(s => s.Serialize(It.IsAny>()), Times.Once);
101 | }
102 | }
--------------------------------------------------------------------------------
/UnitTests/LocalNotification.UnitTests/Tests/NotificationImageTests.cs:
--------------------------------------------------------------------------------
1 | namespace Plugin.LocalNotification.UnitTests.Tests;
2 |
3 | ///
4 | /// Unit tests for the class.
5 | ///
6 | public class NotificationImageTests
7 | {
8 | ///
9 | /// Tests that returns false when the instance is null.
11 | ///
12 | [Fact]
13 | public void HasValue_ShouldReturnFalse_WhenNotificationImageIsNull()
14 | {
15 | // Arrange
16 | NotificationImage image = null;
17 |
18 | // Act & Assert
19 | (image?.HasValue ?? false).Should().BeFalse();
20 | }
21 |
22 | ///
23 | /// Tests that returns true when the Binary property of
24 | /// the is valid.
25 | ///
26 | [Fact]
27 | public void HasValue_ShouldReturnTrue_WhenNotificationImageBinaryIsValid()
28 | {
29 | // Arrange
30 | var assembly = typeof(NotificationImageTests).Assembly;
31 | var resourceName = "Plugin.LocalNotification.UnitTests.Resources.dotnet_logo.png"; // Correct resource name
32 |
33 | using var stream = assembly.GetManifestResourceStream(resourceName);
34 | stream.Should().NotBeNull(); // Ensure the resource is found
35 |
36 | using var memoryStream = new MemoryStream();
37 | stream.CopyTo(memoryStream);
38 |
39 | var image = new NotificationImage
40 | {
41 | Binary = memoryStream.ToArray() // Load the resource into the Binary property
42 | };
43 |
44 | // Act
45 | var result = image.HasValue;
46 |
47 | // Assert
48 | result.Should().BeTrue();
49 | }
50 |
51 | ///
52 | /// Tests that returns true when the FilePath property
53 | /// of the is valid.
54 | ///
55 | [Fact]
56 | public void HasValue_ShouldReturnTrue_WhenNotificationImageFilePathIsValid()
57 | {
58 | // Arrange
59 | var image = new NotificationImage { FilePath = "../Resources/dotnet_logo.png" };
60 |
61 | // Act
62 | var result = image.HasValue;
63 |
64 | // Assert
65 | result.Should().BeTrue();
66 | }
67 |
68 | ///
69 | /// Tests that the method handles a null correctly.
71 | ///
72 | [Fact]
73 | public void Serialize_ShouldHandleNullNotificationImage()
74 | {
75 | // Arrange
76 | var serializer = new NotificationSerializer();
77 | var request = new NotificationRequest
78 | {
79 | Image = null
80 | };
81 |
82 | // Act
83 | var json = serializer.Serialize(request); // Use the instance to call Serialize
84 |
85 | // Assert
86 | json.Should().NotBeNull();
87 | json.Should().NotContain("NotificationImage");
88 | }
89 | }
--------------------------------------------------------------------------------
/UnitTests/LocalNotification.UnitTests/xunit.runner.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://xunit.net/schema/current/xunit.runner.schema.json"
3 | }
--------------------------------------------------------------------------------