android-notify 1.3__py3-none-any.whl → 1.60.6.dev0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of android-notify might be problematic. Click here for more details.
- android_notify/__init__.py +2 -1
- android_notify/__main__.py +24 -0
- android_notify/an_types.py +341 -0
- android_notify/an_utils.py +209 -0
- android_notify/base.py +97 -0
- android_notify/config.py +167 -0
- android_notify/core.py +171 -76
- android_notify/fallback-icons/flet-appicon.png +0 -0
- android_notify/fallback-icons/pydroid3-appicon.png +0 -0
- android_notify/styles.py +17 -7
- android_notify/sword.py +1142 -325
- android_notify-1.60.6.dev0.dist-info/METADATA +171 -0
- android_notify-1.60.6.dev0.dist-info/RECORD +24 -0
- {android_notify-1.3.dist-info → android_notify-1.60.6.dev0.dist-info}/WHEEL +1 -1
- android_notify-1.60.6.dev0.dist-info/entry_points.txt +2 -0
- {android_notify-1.3.dist-info → android_notify-1.60.6.dev0.dist-info}/top_level.txt +1 -0
- docs/examples/flet-working/src/core.py +221 -0
- docs/examples/flet-working/src/main.py +68 -0
- docs/tests/flet/adv/main.py +97 -0
- docs/tests/flet/adv/tests/__init__.py +0 -0
- docs/tests/flet/adv/tests/test_android_notify_full.py +199 -0
- docs/tests/flet/basic/src/core.py +221 -0
- docs/tests/flet/basic/src/main.py +112 -0
- docs/website/src/pages/data/laner_Sent.py +24 -0
- android_notify-1.3.dist-info/METADATA +0 -350
- android_notify-1.3.dist-info/RECORD +0 -9
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: android-notify
|
|
3
|
+
Version: 1.60.6.dev0
|
|
4
|
+
Summary: A Python package that simplifies creating Android notifications in Kivy and Flet apps.
|
|
5
|
+
Author-email: Fabian <fector101@yahoo.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://android-notify.vercel.app
|
|
8
|
+
Project-URL: Documentation, https://android-notify.vercel.app/
|
|
9
|
+
Project-URL: Source, https://github.com/fector101/android_notify
|
|
10
|
+
Project-URL: Tracker, https://github.com/fector101/android_notify/issues
|
|
11
|
+
Project-URL: Funding, https://www.buymeacoffee.com/fector101
|
|
12
|
+
Keywords: android,notifications,kivy,mobile,post-notifications,pyjnius,android-notifications,kivy-notifications,python-android,mobile-development,push-notifications,mobile-app,kivy-application
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Operating System :: Android
|
|
15
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
16
|
+
Classifier: Intended Audience :: Developers
|
|
17
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
18
|
+
Requires-Python: >=3.6
|
|
19
|
+
Description-Content-Type: text/markdown
|
|
20
|
+
Requires-Dist: pyjnius>=1.4.2
|
|
21
|
+
Provides-Extra: dev
|
|
22
|
+
Requires-Dist: kivy>=2.0.0; extra == "dev"
|
|
23
|
+
|
|
24
|
+
<div align="center">
|
|
25
|
+
<br>
|
|
26
|
+
<h1> Android-Notify </h1>
|
|
27
|
+
<p><a href='https://android-notify.vercel.app'>Android Notify</a> is a Python library for effortlessly creating and managing Android notifications in Kivy and Flet apps.</p>
|
|
28
|
+
<p>Supports various styles and ensures seamless integration, customization and Pythonic APIs.</p>
|
|
29
|
+
<!-- <br> -->
|
|
30
|
+
<!-- <img src="https://raw.githubusercontent.com/Fector101/android_notify/main/docs/imgs/democollage.jpg"> -->
|
|
31
|
+
</div>
|
|
32
|
+
<!-- Channel [CRUD]
|
|
33
|
+
The Android Notify package provides a simple yet comprehensive way to create and manage rich notifications on Android devices directly from your Python code. This library bridges the gap between Python and Android's notification system, giving you full control over notifications with a clean, Pythonic API. -->
|
|
34
|
+
|
|
35
|
+
## Features
|
|
36
|
+
|
|
37
|
+
- **Multiple Notification Styles**: Support for various notification styles including:
|
|
38
|
+
- Simple text notifications
|
|
39
|
+
- [Progress bar notifications](https://android-notify.vercel.app/components#progress-bars) (determinate and indeterminate)
|
|
40
|
+
- Large icon notifications
|
|
41
|
+
- Big picture notifications
|
|
42
|
+
- Combined image styles
|
|
43
|
+
- Custom notification Icon - [images section](https://android-notify.vercel.app/components#images)
|
|
44
|
+
- Big text notifications
|
|
45
|
+
- Inbox-style notifications
|
|
46
|
+
- Colored texts and Icons
|
|
47
|
+
|
|
48
|
+
- **Rich Functionality**:
|
|
49
|
+
- Add action buttons with custom callbacks
|
|
50
|
+
- [Update notification](https://android-notify.vercel.app/advanced-methods#updating-notification) content dynamically
|
|
51
|
+
- Manage progress bars with fine-grained control
|
|
52
|
+
- [Custom notification channels](https://android-notify.vercel.app/advanced-methods#channel-management) for Android 8.0+ (Creating and Deleting)
|
|
53
|
+
- Silent notifications
|
|
54
|
+
- Persistent notifications
|
|
55
|
+
- Click handlers and callbacks
|
|
56
|
+
- Cancel Notifications
|
|
57
|
+
|
|
58
|
+
## Quick Start
|
|
59
|
+
|
|
60
|
+
```python
|
|
61
|
+
from android_notify import Notification
|
|
62
|
+
|
|
63
|
+
# Simple notification
|
|
64
|
+
Notification(
|
|
65
|
+
title="Hello",
|
|
66
|
+
message="This is a basic notification."
|
|
67
|
+
).send()
|
|
68
|
+
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
**Sample Image:**
|
|
72
|
+

|
|
73
|
+
|
|
74
|
+
## Installation
|
|
75
|
+
|
|
76
|
+
### Kivy apps:
|
|
77
|
+
|
|
78
|
+
In your **`buildozer.spec`** file, ensure you include the following:
|
|
79
|
+
|
|
80
|
+
```ini
|
|
81
|
+
# Add pyjnius so ensure it's packaged with the build
|
|
82
|
+
requirements = python3, kivy, pyjnius, android-notify>=1.60.6.dev0
|
|
83
|
+
# Add permission for notifications
|
|
84
|
+
android.permissions = POST_NOTIFICATIONS
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### Flet apps:
|
|
88
|
+
|
|
89
|
+
In your `pyproject.toml` file, ensure you include the following:
|
|
90
|
+
|
|
91
|
+
```toml
|
|
92
|
+
[tool.flet.android]
|
|
93
|
+
dependencies = [
|
|
94
|
+
"pyjnius","android-notify>=1.60.6.dev0"
|
|
95
|
+
]
|
|
96
|
+
|
|
97
|
+
[tool.flet.android.permission]
|
|
98
|
+
"android.permission.POST_NOTIFICATIONS" = true
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### Pydroid 3
|
|
102
|
+
In the [pydroid 3](https://play.google.com/store/apps/details?id=ru.iiec.pydroid3) mobile app for running python code you can test some features.
|
|
103
|
+
- In pip section where you're asked to insert `Libary name` paste `android-notify>=1.60.6.dev0`
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
### Testing
|
|
107
|
+
Can be installed via `pip` For testing purposes:
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
pip install android_notify
|
|
111
|
+
android-notify -v
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## Documentation
|
|
115
|
+
For Dev Version use
|
|
116
|
+
```requirements = python3, kivy, pyjnius, https://github.com/Fector101/android_notify/archive/without-androidx.zip```
|
|
117
|
+
|
|
118
|
+
### To talk to BroadCast Listener From Buttons
|
|
119
|
+
|
|
120
|
+
- Make things happen without being in your app
|
|
121
|
+
```python
|
|
122
|
+
|
|
123
|
+
from android_notify import Notification
|
|
124
|
+
notification = Notification(title="Reciver Notification")
|
|
125
|
+
notification.addButton(text="Stop", receiver_name="CarouselReceiver", action="ACTION_STOP")
|
|
126
|
+
notification.addButton(text="Skip", receiver_name="CarouselReceiver", action="ACTION_SKIP")
|
|
127
|
+
```
|
|
128
|
+
You can use this [wiki](https://github.com/Fector101/android_notify/wiki/How-to#use-with-broadcast-listener-in-kivy) as a guide create a broadcast listener
|
|
129
|
+
|
|
130
|
+
### To use colored text in your notifications
|
|
131
|
+
|
|
132
|
+
- Copy the [res](https://github.com/Fector101/android_notify/tree/main/android_notify/res) folder to your app path.
|
|
133
|
+
Lastly in your `buildozer.spec` file
|
|
134
|
+
- Add `source.include_exts = xml` and `android.add_resources = ./res`
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
### To use Custom Sounds
|
|
138
|
+
|
|
139
|
+
- Put audio files in `res/raw` folder,
|
|
140
|
+
- Then from `buildozer.spec` point to res folder `android.add_resources = res`
|
|
141
|
+
- and includes it's format `source.include_exts = wav`.
|
|
142
|
+
|
|
143
|
+
Lastly From the code
|
|
144
|
+
```py
|
|
145
|
+
# Create a custom notification channel with a unique sound resource for android 8+
|
|
146
|
+
Notification.createChannel(
|
|
147
|
+
id="weird_sound_tester",
|
|
148
|
+
name="Weird Sound Tester",
|
|
149
|
+
description="A test channel used to verify custom notification sounds from the res/raw folder.",
|
|
150
|
+
res_sound_name="sneeze" # file name without .wav or .mp3
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
# Send a notification through the created channel
|
|
154
|
+
n=Notification(
|
|
155
|
+
title="Custom Sound Notification",
|
|
156
|
+
message="This tests playback of a custom sound (sneeze.wav) stored in res/raw.",
|
|
157
|
+
channel_id="weird_sound_tester" # important tells notification to use right channel
|
|
158
|
+
)
|
|
159
|
+
n.setSound("sneeze")# for android 7 below
|
|
160
|
+
n.send()
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
### For full documentation, examples, and advanced usage, API reference visit the [documentation](https://android-notify.vercel.app)
|
|
164
|
+
|
|
165
|
+
## ☕ Support the Project
|
|
166
|
+
|
|
167
|
+
If you find this project helpful, consider buying me a coffee! 😊 Or Giving it a star on 🌟 [GitHub](https://github.com/Fector101/android_notify/) Your support helps maintain and improve the project.
|
|
168
|
+
|
|
169
|
+
<a href="https://www.buymeacoffee.com/fector101" target="_blank">
|
|
170
|
+
<img src="https://cdn.buymeacoffee.com/buttons/v2/default-yellow.png" alt="Buy Me A Coffee" height="60">
|
|
171
|
+
</a>
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
android_notify/__init__.py,sha256=lcLjyfegXgU7cyGhfSphAOBipXwemrVkdYy3mcF6X5Y,172
|
|
2
|
+
android_notify/__main__.py,sha256=TqZBjgW5wO61QvCpZje-YDKhBehtxXJSa71aM0RHom8,684
|
|
3
|
+
android_notify/an_types.py,sha256=8qFBx4qLOz_Z2VxVWW3BxBx3jOj0DGoJ6T8dxnkcTB0,10428
|
|
4
|
+
android_notify/an_utils.py,sha256=1GD9E8a3zVAA7ThttGQbqZQj333e5I44WqY4uyLDaxs,7448
|
|
5
|
+
android_notify/base.py,sha256=6HKFseaVwPgxk1BKSocUIN-x-uYFW6IbhUJyb2mPoBE,3709
|
|
6
|
+
android_notify/config.py,sha256=VN1ixPL4p7qDCD4u1oyI3sQEvWaifWp8hUgFHHhns6M,5384
|
|
7
|
+
android_notify/core.py,sha256=3tHMKBwkGR8QKOJNu1feN9l9hKXnDlfY9ao7LCKMnFk,9133
|
|
8
|
+
android_notify/styles.py,sha256=-8ueetUAec1xCXkMV2dF8Ywep_bu-5hiT9_eyh5TTyU,899
|
|
9
|
+
android_notify/sword.py,sha256=Z_N3d-4gfy09v4n67lYxjwR0j8EDXRXH0fVzj6T9plo,49496
|
|
10
|
+
android_notify/fallback-icons/flet-appicon.png,sha256=HLLHbB0MYu4Gimp6C7oe5QHK1frNKgRUHvht88JqBdE,32278
|
|
11
|
+
android_notify/fallback-icons/pydroid3-appicon.png,sha256=R54qUn-ohBLKxMauwQpkJtIPIkEFRA2AEdTAEdD3e9w,13403
|
|
12
|
+
docs/examples/flet-working/src/core.py,sha256=Pzhp6IhmHadvg_7zYhBBGz8araeVsuy44MVUVpW91cY,9261
|
|
13
|
+
docs/examples/flet-working/src/main.py,sha256=zTc6hOid6OBLiH9X1ha4MzvZE5qtVDjFFi7SJmfzsk4,1838
|
|
14
|
+
docs/tests/flet/adv/main.py,sha256=xVPB4HL1xD2aWUucemb8ha60z-rr9em_Kw-5oUPfp8s,3555
|
|
15
|
+
docs/tests/flet/adv/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
16
|
+
docs/tests/flet/adv/tests/test_android_notify_full.py,sha256=jlB0ME_F1HlcgIQeHQTMtj9fIVa80_SizHkIM5pUO44,7283
|
|
17
|
+
docs/tests/flet/basic/src/core.py,sha256=Pzhp6IhmHadvg_7zYhBBGz8araeVsuy44MVUVpW91cY,9261
|
|
18
|
+
docs/tests/flet/basic/src/main.py,sha256=nnBp3tSIeBUcinmeSc2q7aAYri_e1GyfljOrFzHzSVE,3993
|
|
19
|
+
docs/website/src/pages/data/laner_Sent.py,sha256=QQI8MxDO9X37qTIiVrydajoKE-MnRpmKeLPRsgJcfbs,609
|
|
20
|
+
android_notify-1.60.6.dev0.dist-info/METADATA,sha256=wYblJ6yOf-zGQdqbVEUMK4U-uBy8TJVbz79lC2C4X5k,6697
|
|
21
|
+
android_notify-1.60.6.dev0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
22
|
+
android_notify-1.60.6.dev0.dist-info/entry_points.txt,sha256=pMWHYogqxd-a8tXQ97tjyxay8sTpFO15J0YX1CYJe4U,64
|
|
23
|
+
android_notify-1.60.6.dev0.dist-info/top_level.txt,sha256=kH29wUCpnrTwPF-o2KxkgUGFCQ96BinxQ0j7LneHKnw,20
|
|
24
|
+
android_notify-1.60.6.dev0.dist-info/RECORD,,
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
""" Non-Advanced Stuff """
|
|
2
|
+
import random
|
|
3
|
+
import os
|
|
4
|
+
ON_ANDROID = False
|
|
5
|
+
|
|
6
|
+
def on_flet_app():
|
|
7
|
+
return os.getenv("MAIN_ACTIVITY_HOST_CLASS_NAME")
|
|
8
|
+
|
|
9
|
+
def get_activity_class_name():
|
|
10
|
+
ACTIVITY_CLASS_NAME = os.getenv("MAIN_ACTIVITY_HOST_CLASS_NAME") # flet python
|
|
11
|
+
if not ACTIVITY_CLASS_NAME:
|
|
12
|
+
try:
|
|
13
|
+
from android import config
|
|
14
|
+
ACTIVITY_CLASS_NAME = getattr(config, "JAVA_NAMESPACE", None)
|
|
15
|
+
except (ImportError, AttributeError):
|
|
16
|
+
ACTIVITY_CLASS_NAME = 'org.kivy.android'
|
|
17
|
+
return ACTIVITY_CLASS_NAME
|
|
18
|
+
|
|
19
|
+
try:
|
|
20
|
+
|
|
21
|
+
from jnius import autoclass # Needs Java to be installed
|
|
22
|
+
# Get the required Java classes
|
|
23
|
+
ACTIVITY_CLASS_NAME = get_activity_class_name()
|
|
24
|
+
PythonActivity = autoclass(ACTIVITY_CLASS_NAME)
|
|
25
|
+
context = PythonActivity.mActivity # Get the app's context
|
|
26
|
+
NotificationChannel = autoclass('android.app.NotificationChannel')
|
|
27
|
+
String = autoclass('java.lang.String')
|
|
28
|
+
Intent = autoclass('android.content.Intent')
|
|
29
|
+
PendingIntent = autoclass('android.app.PendingIntent')
|
|
30
|
+
BitmapFactory = autoclass('android.graphics.BitmapFactory')
|
|
31
|
+
BuildVersion = autoclass('android.os.Build$VERSION')
|
|
32
|
+
ON_ANDROID=True
|
|
33
|
+
except Exception as e:
|
|
34
|
+
print('\nThis Package Only Runs on Android !!! ---> Check "https://github.com/Fector101/android_notify/" to see design patterns and more info.\n')
|
|
35
|
+
|
|
36
|
+
if ON_ANDROID:
|
|
37
|
+
try:
|
|
38
|
+
NotificationManagerCompat = autoclass('androidx.core.app.NotificationManagerCompat')
|
|
39
|
+
NotificationCompat = autoclass('androidx.core.app.NotificationCompat')
|
|
40
|
+
|
|
41
|
+
# Notification Design
|
|
42
|
+
NotificationCompatBuilder = autoclass('androidx.core.app.NotificationCompat$Builder')
|
|
43
|
+
NotificationCompatBigTextStyle = autoclass('androidx.core.app.NotificationCompat$BigTextStyle')
|
|
44
|
+
NotificationCompatBigPictureStyle = autoclass('androidx.core.app.NotificationCompat$BigPictureStyle')
|
|
45
|
+
NotificationCompatInboxStyle = autoclass('androidx.core.app.NotificationCompat$InboxStyle')
|
|
46
|
+
except Exception as e:
|
|
47
|
+
print("""\n
|
|
48
|
+
Dependency Error: Add the following in buildozer.spec:
|
|
49
|
+
* android.gradle_dependencies = androidx.core:core-ktx:1.15.0, androidx.core:core:1.6.0
|
|
50
|
+
* android.enable_androidx = True
|
|
51
|
+
* android.permissions = POST_NOTIFICATIONS\n
|
|
52
|
+
""")
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def get_app_root_path():
|
|
56
|
+
if on_flet_app():
|
|
57
|
+
return os.path.join(context.getFilesDir().getAbsolutePath(),'flet')
|
|
58
|
+
else:
|
|
59
|
+
from android.storage import app_storage_path # type: ignore
|
|
60
|
+
return app_storage_path()
|
|
61
|
+
|
|
62
|
+
def asks_permission_if_needed():
|
|
63
|
+
"""
|
|
64
|
+
Ask for permission to send notifications if needed.
|
|
65
|
+
"""
|
|
66
|
+
if on_flet_app():
|
|
67
|
+
VERSION_CODES = autoclass('android.os.Build$VERSION_CODES')
|
|
68
|
+
ContextCompat = autoclass('androidx.core.content.ContextCompat')
|
|
69
|
+
# if you get error `Failed to find class: androidx/core/app/ActivityCompat`
|
|
70
|
+
#in proguard-rules.pro add `-keep class androidx.core.app.ActivityCompat { *; }`
|
|
71
|
+
ActivityCompat = autoclass('androidx.core.app.ActivityCompat')
|
|
72
|
+
Manifest = autoclass('android.Manifest$permission')
|
|
73
|
+
VERSION_CODES = autoclass('android.os.Build$VERSION_CODES')
|
|
74
|
+
|
|
75
|
+
if BuildVersion.SDK_INT >= VERSION_CODES.TIRAMISU:
|
|
76
|
+
permission = Manifest.POST_NOTIFICATIONS
|
|
77
|
+
granted = ContextCompat.checkSelfPermission(context, permission)
|
|
78
|
+
|
|
79
|
+
if granted != 0: # PackageManager.PERMISSION_GRANTED == 0
|
|
80
|
+
ActivityCompat.requestPermissions(context, [permission], 101)
|
|
81
|
+
else: # android package is from p4a which is for kivy
|
|
82
|
+
try:
|
|
83
|
+
from android.permissions import request_permissions, Permission,check_permission # type: ignore
|
|
84
|
+
permissions=[Permission.POST_NOTIFICATIONS]
|
|
85
|
+
if not all(check_permission(p) for p in permissions):
|
|
86
|
+
request_permissions(permissions)
|
|
87
|
+
except Exception as e:
|
|
88
|
+
print("android_notify- error trying to request notification access: ", e)
|
|
89
|
+
|
|
90
|
+
def get_image_uri(relative_path):
|
|
91
|
+
"""
|
|
92
|
+
Get the absolute URI for an image in the assets folder.
|
|
93
|
+
:param relative_path: The relative path to the image (e.g., 'assets/imgs/icon.png').
|
|
94
|
+
:return: Absolute URI java Object (e.g., 'file:///path/to/file.png').
|
|
95
|
+
"""
|
|
96
|
+
app_root_path = get_app_root_path()
|
|
97
|
+
output_path = os.path.join(app_root_path,'app', relative_path)
|
|
98
|
+
# print(output_path,'output_path') # /data/user/0/org.laner.lan_ft/files/app/assets/imgs/icon.png
|
|
99
|
+
|
|
100
|
+
if not os.path.exists(output_path):
|
|
101
|
+
raise FileNotFoundError(f"\nImage not found at path: {output_path}\n")
|
|
102
|
+
|
|
103
|
+
Uri = autoclass('android.net.Uri')
|
|
104
|
+
return Uri.parse(f"file://{output_path}")
|
|
105
|
+
|
|
106
|
+
def get_icon_object(uri):
|
|
107
|
+
BitmapFactory = autoclass('android.graphics.BitmapFactory')
|
|
108
|
+
IconCompat = autoclass('androidx.core.graphics.drawable.IconCompat')
|
|
109
|
+
|
|
110
|
+
bitmap= BitmapFactory.decodeStream(context.getContentResolver().openInputStream(uri))
|
|
111
|
+
return IconCompat.createWithBitmap(bitmap)
|
|
112
|
+
|
|
113
|
+
def insert_app_icon(builder,custom_icon_path):
|
|
114
|
+
if custom_icon_path:
|
|
115
|
+
try:
|
|
116
|
+
uri = get_image_uri(custom_icon_path)
|
|
117
|
+
icon = get_icon_object(uri)
|
|
118
|
+
builder.setSmallIcon(icon)
|
|
119
|
+
except Exception as e:
|
|
120
|
+
print('android_notify- error: ',e)
|
|
121
|
+
builder.setSmallIcon(context.getApplicationInfo().icon)
|
|
122
|
+
else:
|
|
123
|
+
print('Found res icon -->',context.getApplicationInfo().icon,'<--')
|
|
124
|
+
builder.setSmallIcon(context.getApplicationInfo().icon)
|
|
125
|
+
|
|
126
|
+
def send_notification(
|
|
127
|
+
title:str,
|
|
128
|
+
message:str,
|
|
129
|
+
style=None,
|
|
130
|
+
img_path=None,
|
|
131
|
+
channel_name="Default Channel",
|
|
132
|
+
channel_id:str="default_channel",
|
|
133
|
+
custom_app_icon_path="",
|
|
134
|
+
|
|
135
|
+
big_picture_path='',
|
|
136
|
+
large_icon_path='',
|
|
137
|
+
big_text="",
|
|
138
|
+
lines=""
|
|
139
|
+
):
|
|
140
|
+
"""
|
|
141
|
+
Send a notification on Android.
|
|
142
|
+
|
|
143
|
+
:param title: Title of the notification.
|
|
144
|
+
:param message: Message body.
|
|
145
|
+
:param style: Style of the notification ('big_text', 'big_picture', 'inbox', 'large_icon').
|
|
146
|
+
:param img_path: Path to the image resource.
|
|
147
|
+
:param channel_id: Notification channel ID.(Default is lowercase channel name arg in lowercase)
|
|
148
|
+
"""
|
|
149
|
+
if not ON_ANDROID:
|
|
150
|
+
print('This Package Only Runs on Android !!! ---> Check "https://github.com/Fector101/android_notify/" for Documentation.')
|
|
151
|
+
return
|
|
152
|
+
|
|
153
|
+
asks_permission_if_needed()
|
|
154
|
+
channel_id=channel_name.replace(' ','_').lower().lower() if not channel_id else channel_id
|
|
155
|
+
# Get notification manager
|
|
156
|
+
notification_manager = context.getSystemService(context.NOTIFICATION_SERVICE)
|
|
157
|
+
|
|
158
|
+
# importance= autoclass('android.app.NotificationManager').IMPORTANCE_HIGH # also works #NotificationManager.IMPORTANCE_DEFAULT
|
|
159
|
+
importance= NotificationManagerCompat.IMPORTANCE_HIGH #autoclass('android.app.NotificationManager').IMPORTANCE_HIGH also works #NotificationManager.IMPORTANCE_DEFAULT
|
|
160
|
+
|
|
161
|
+
# Notification Channel (Required for Android 8.0+)
|
|
162
|
+
if BuildVersion.SDK_INT >= 26:
|
|
163
|
+
channel = NotificationChannel(channel_id, channel_name,importance)
|
|
164
|
+
notification_manager.createNotificationChannel(channel)
|
|
165
|
+
|
|
166
|
+
# Build the notification
|
|
167
|
+
builder = NotificationCompatBuilder(context, channel_id)
|
|
168
|
+
builder.setContentTitle(title)
|
|
169
|
+
builder.setContentText(message)
|
|
170
|
+
insert_app_icon(builder,custom_app_icon_path)
|
|
171
|
+
builder.setDefaults(NotificationCompat.DEFAULT_ALL)
|
|
172
|
+
builder.setPriority(NotificationCompat.PRIORITY_HIGH)
|
|
173
|
+
|
|
174
|
+
if img_path:
|
|
175
|
+
print('android_notify- img_path arg deprecated use "large_icon_path or big_picture_path or custom_app_icon_path"instead ')
|
|
176
|
+
|
|
177
|
+
big_picture = None
|
|
178
|
+
if big_picture_path:
|
|
179
|
+
try:
|
|
180
|
+
big_picture = get_image_uri(big_picture_path)
|
|
181
|
+
except FileNotFoundError as e:
|
|
182
|
+
print('android_notify- Error Getting Uri for big_picture_path: ',e)
|
|
183
|
+
|
|
184
|
+
large_icon = None
|
|
185
|
+
if large_icon_path:
|
|
186
|
+
try:
|
|
187
|
+
large_icon = get_image_uri(large_icon_path)
|
|
188
|
+
except FileNotFoundError as e:
|
|
189
|
+
print('android_notify- Error Getting Uri for large_icon_path: ',e)
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
# Apply notification styles
|
|
193
|
+
try:
|
|
194
|
+
if big_text:
|
|
195
|
+
big_text_style = NotificationCompatBigTextStyle()
|
|
196
|
+
big_text_style.bigText(big_text)
|
|
197
|
+
builder.setStyle(big_text_style)
|
|
198
|
+
|
|
199
|
+
elif lines:
|
|
200
|
+
inbox_style = NotificationCompatInboxStyle()
|
|
201
|
+
for line in lines.split("\n"):
|
|
202
|
+
inbox_style.addLine(line)
|
|
203
|
+
builder.setStyle(inbox_style)
|
|
204
|
+
|
|
205
|
+
if large_icon:
|
|
206
|
+
bitmap = BitmapFactory.decodeStream(context.getContentResolver().openInputStream(large_icon))
|
|
207
|
+
builder.setLargeIcon(bitmap)
|
|
208
|
+
|
|
209
|
+
if big_picture:
|
|
210
|
+
bitmap = BitmapFactory.decodeStream(context.getContentResolver().openInputStream(big_picture))
|
|
211
|
+
builder.setLargeIcon(bitmap)
|
|
212
|
+
big_picture_style = NotificationCompatBigPictureStyle().bigPicture(bitmap)
|
|
213
|
+
builder.setStyle(big_picture_style)
|
|
214
|
+
|
|
215
|
+
except Exception as e:
|
|
216
|
+
print('android_notify- Error Failed Adding Style: ',e)
|
|
217
|
+
# Display the notification
|
|
218
|
+
notification_id = random.randint(0, 100)
|
|
219
|
+
notification_manager.notify(notification_id, builder.build())
|
|
220
|
+
return notification_id
|
|
221
|
+
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import flet as ft
|
|
2
|
+
import os,traceback
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
md1 = """ """
|
|
6
|
+
i=0
|
|
7
|
+
|
|
8
|
+
def main(page: ft.Page):
|
|
9
|
+
page.scroll = ft.ScrollMode.ADAPTIVE
|
|
10
|
+
mdObj=ft.Markdown(
|
|
11
|
+
md1,
|
|
12
|
+
selectable=True,
|
|
13
|
+
extension_set=ft.MarkdownExtensionSet.GITHUB_WEB,
|
|
14
|
+
on_tap_link=lambda e: page.launch_url(e.data),
|
|
15
|
+
)
|
|
16
|
+
def console(a):
|
|
17
|
+
global md1
|
|
18
|
+
try:
|
|
19
|
+
with open(os.getenv("FLET_APP_CONSOLE"), "r") as f:
|
|
20
|
+
md1 = f.read()
|
|
21
|
+
mdObj.value=md1
|
|
22
|
+
except Exception as e:
|
|
23
|
+
print(e,"hello readddd it's me")
|
|
24
|
+
mdObj.value=f"{e}, hello readddd it's me"
|
|
25
|
+
finally:
|
|
26
|
+
mdObj.update()
|
|
27
|
+
|
|
28
|
+
def send_basic(e):
|
|
29
|
+
|
|
30
|
+
try:
|
|
31
|
+
from core import send_notification
|
|
32
|
+
send_notification(title='Hello World',message='From android_notify',custom_app_icon_path=f'assets/icon.png')
|
|
33
|
+
except Exception as e:
|
|
34
|
+
print("Error importing android_notify: {}".format(e))
|
|
35
|
+
|
|
36
|
+
def log_stuff(e):
|
|
37
|
+
global i
|
|
38
|
+
i+=1
|
|
39
|
+
print('Testing print visiblity: ',i,'\n')
|
|
40
|
+
console(None)
|
|
41
|
+
|
|
42
|
+
def request_permission(e):
|
|
43
|
+
try:
|
|
44
|
+
from core import asks_permission_if_needed
|
|
45
|
+
asks_permission_if_needed()
|
|
46
|
+
except Exception as e:
|
|
47
|
+
print( f"pyjnius android_ import error: {traceback.format_exc()}" )
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
page.add(
|
|
53
|
+
ft.OutlinedButton(
|
|
54
|
+
"Request Permission if Needed",
|
|
55
|
+
on_click=request_permission,
|
|
56
|
+
), ft.OutlinedButton(
|
|
57
|
+
"Refresh Prints",
|
|
58
|
+
on_click=console,
|
|
59
|
+
), ft.OutlinedButton(
|
|
60
|
+
"Testing print visiblity",
|
|
61
|
+
on_click=log_stuff,
|
|
62
|
+
)
|
|
63
|
+
)
|
|
64
|
+
btn = ft.ElevatedButton("Click me!", on_click=send_basic)
|
|
65
|
+
page.add(btn)
|
|
66
|
+
page.add(mdObj)
|
|
67
|
+
|
|
68
|
+
ft.app(main)
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import os,traceback,io,sys,unittest
|
|
2
|
+
import flet as ft
|
|
3
|
+
from contextlib import redirect_stdout
|
|
4
|
+
from android_notify.core import get_app_root_path,asks_permission_if_needed
|
|
5
|
+
|
|
6
|
+
md1=''
|
|
7
|
+
i=0
|
|
8
|
+
def main(page: ft.Page):
|
|
9
|
+
page.scroll = ft.ScrollMode.ADAPTIVE
|
|
10
|
+
page.add(ft.Text("1111111111 Android Notify Test Results", size=24, weight=ft.FontWeight.BOLD))
|
|
11
|
+
logs_path=os.path.join(get_app_root_path(),'last.txt')
|
|
12
|
+
mdObj = ft.Markdown(
|
|
13
|
+
md1,
|
|
14
|
+
selectable=True,
|
|
15
|
+
extension_set=ft.MarkdownExtensionSet.GITHUB_WEB,
|
|
16
|
+
on_tap_link=lambda e: page.launch_url(e.data),
|
|
17
|
+
)
|
|
18
|
+
page.add(mdObj)
|
|
19
|
+
def console(a):
|
|
20
|
+
global md1,i
|
|
21
|
+
i+=1
|
|
22
|
+
print('Testing print visibility: ',i,'\n')
|
|
23
|
+
try:
|
|
24
|
+
if os.getenv("FLET_APP_CONSOLE"):
|
|
25
|
+
with open(os.getenv("FLET_APP_CONSOLE"), "r") as f:
|
|
26
|
+
md1 = f.read()
|
|
27
|
+
mdObj.value = md1
|
|
28
|
+
|
|
29
|
+
with open(logs_path, 'r') as logf:
|
|
30
|
+
mdObj.value = logf.read() + md1
|
|
31
|
+
except Exception as err:
|
|
32
|
+
mdObj.value = f"Error reading log: {err}"
|
|
33
|
+
finally:
|
|
34
|
+
mdObj.update()
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def send_basic(e):
|
|
38
|
+
"""Send a notification to verify android_notify works"""
|
|
39
|
+
try:
|
|
40
|
+
from android_notify import Notification
|
|
41
|
+
Notification(title="Hello World", message="From android_notify").send()
|
|
42
|
+
except Exception as err:
|
|
43
|
+
mdObj.value = f"Notification error: {err}"
|
|
44
|
+
mdObj.update()
|
|
45
|
+
def asks_permission_if_needed_(e):
|
|
46
|
+
asks_permission_if_needed()
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def ensure_tests_folder():
|
|
50
|
+
try:
|
|
51
|
+
base_path = get_app_root_path()
|
|
52
|
+
except Exception:
|
|
53
|
+
base_path = os.path.dirname(__file__)
|
|
54
|
+
|
|
55
|
+
tests_path = os.path.join(base_path, "tests")
|
|
56
|
+
os.makedirs(tests_path, exist_ok=True)
|
|
57
|
+
init_file = os.path.join(tests_path, "__init__.py")
|
|
58
|
+
if not os.path.exists(init_file):
|
|
59
|
+
open(init_file, "w").close()
|
|
60
|
+
|
|
61
|
+
page.add(ft.Text(f"{tests_path}", size=24, weight=ft.FontWeight.BOLD))
|
|
62
|
+
return tests_path
|
|
63
|
+
|
|
64
|
+
page.add(ft.Text("2222222222 Android Notify Test Results", size=24, weight=ft.FontWeight.BOLD))
|
|
65
|
+
|
|
66
|
+
def run_tests(e=None):
|
|
67
|
+
"""Run tests and log results to /sdcard/flet_app_console.txt"""
|
|
68
|
+
tests_path = ensure_tests_folder()
|
|
69
|
+
|
|
70
|
+
try:
|
|
71
|
+
with open(logs_path, "w") as logf, redirect_stdout(logf):
|
|
72
|
+
loader = unittest.TestLoader()
|
|
73
|
+
suite = loader.discover(start_dir=tests_path, pattern="test_*.py")
|
|
74
|
+
print("Discovered tests:",suite.countTestCases())
|
|
75
|
+
if suite.countTestCases() ==0:
|
|
76
|
+
print("No tests found")
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
runner = unittest.TextTestRunner(stream=logf, verbosity=2)
|
|
80
|
+
runner.run(suite)
|
|
81
|
+
|
|
82
|
+
mdObj.value = f"Tests complete. Log saved to:\n`{logs_path}`"
|
|
83
|
+
except Exception as err:
|
|
84
|
+
mdObj.value = f"Test error:\n{traceback.format_exc()}"
|
|
85
|
+
mdObj.update()
|
|
86
|
+
def has_per(e=None):
|
|
87
|
+
from android_notify import NotificationHandler
|
|
88
|
+
print('permmm:',NotificationHandler.has_permission())
|
|
89
|
+
page.add(
|
|
90
|
+
ft.OutlinedButton("🔔 Send Basic Notification", on_click=send_basic),
|
|
91
|
+
ft.OutlinedButton("🔁 Refresh Prints", on_click=console),
|
|
92
|
+
ft.OutlinedButton("🧪 Run Tests", on_click=run_tests),
|
|
93
|
+
ft.OutlinedButton("🧪permmm", on_click=has_per),
|
|
94
|
+
ft.OutlinedButton("🧪asks_permission_if_needed", on_click=asks_permission_if_needed_),
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
ft.app(main)
|
|
File without changes
|