android-notify 1.50__tar.gz → 1.51__tar.gz
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-1.50 → android_notify-1.51}/PKG-INFO +37 -53
- {android_notify-1.50 → android_notify-1.51}/README.md +36 -52
- {android_notify-1.50 → android_notify-1.51}/android_notify/styles.py +5 -4
- {android_notify-1.50 → android_notify-1.51}/android_notify/sword.py +139 -75
- {android_notify-1.50 → android_notify-1.51}/android_notify.egg-info/PKG-INFO +37 -53
- {android_notify-1.50 → android_notify-1.51}/setup.py +1 -1
- {android_notify-1.50 → android_notify-1.51}/android_notify/__init__.py +0 -0
- {android_notify-1.50 → android_notify-1.51}/android_notify/__main__.py +0 -0
- {android_notify-1.50 → android_notify-1.51}/android_notify/core.py +0 -0
- {android_notify-1.50 → android_notify-1.51}/android_notify.egg-info/SOURCES.txt +0 -0
- {android_notify-1.50 → android_notify-1.51}/android_notify.egg-info/dependency_links.txt +0 -0
- {android_notify-1.50 → android_notify-1.51}/android_notify.egg-info/requires.txt +0 -0
- {android_notify-1.50 → android_notify-1.51}/android_notify.egg-info/top_level.txt +0 -0
- {android_notify-1.50 → android_notify-1.51}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.2
|
|
2
2
|
Name: android-notify
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.51
|
|
4
4
|
Summary: A Python package that simpilfies creating Android notifications in Kivy apps.
|
|
5
5
|
Home-page: https://github.com/fector101/android-notify
|
|
6
6
|
Author: Fabian
|
|
@@ -144,6 +144,9 @@ Clock.schedule_once(lambda dt: notification.updateProgressBar(30, "30% downloade
|
|
|
144
144
|
|
|
145
145
|
#### Notification with an Image (Big Picture Style)
|
|
146
146
|
|
|
147
|
+
> [!NOTE]
|
|
148
|
+
> Online Images should start with `http://` or `https://`
|
|
149
|
+
|
|
147
150
|
```python
|
|
148
151
|
# Image notification
|
|
149
152
|
notification = Notification(
|
|
@@ -159,36 +162,39 @@ notification.send()
|
|
|
159
162
|
**Sample Image:**
|
|
160
163
|

|
|
161
164
|
|
|
162
|
-
####
|
|
165
|
+
#### Notification with an Image (Large Icon Style)
|
|
166
|
+
|
|
167
|
+
> [!NOTE]
|
|
168
|
+
> Online Images should start with `http://` or `https://`
|
|
163
169
|
|
|
164
170
|
```python
|
|
165
|
-
# Send a notification with inbox style
|
|
166
171
|
notification = Notification(
|
|
167
|
-
title=
|
|
168
|
-
message=
|
|
169
|
-
style=
|
|
172
|
+
title="FabianDev_",
|
|
173
|
+
message="A twitter about some programming stuff",
|
|
174
|
+
style="large_icon",
|
|
175
|
+
large_icon_path="assets/imgs/profile.png"
|
|
170
176
|
)
|
|
171
|
-
notification.send()
|
|
172
177
|
|
|
173
178
|
```
|
|
174
179
|
|
|
175
|
-
**Sample Image:**
|
|
176
|
-

|
|
177
182
|
|
|
178
|
-
#### Notification
|
|
183
|
+
#### Inbox Notification Style
|
|
179
184
|
|
|
180
185
|
```python
|
|
186
|
+
# Send a notification with inbox style
|
|
181
187
|
notification = Notification(
|
|
182
|
-
title=
|
|
183
|
-
message=
|
|
184
|
-
style=
|
|
185
|
-
large_icon_path="assets/imgs/profile.png"
|
|
188
|
+
title='Inbox Notification',
|
|
189
|
+
message='Line 1\nLine 2\nLine 3',
|
|
190
|
+
style='inbox'
|
|
186
191
|
)
|
|
192
|
+
notification.send()
|
|
187
193
|
|
|
188
194
|
```
|
|
189
195
|
|
|
190
|
-
**Sample Image:**
|
|
191
|
-

|
|
192
198
|
|
|
193
199
|
#### Notification with Buttons
|
|
194
200
|
|
|
@@ -212,16 +218,19 @@ notification.send()
|
|
|
212
218
|
**Sample Image:**
|
|
213
219
|

|
|
214
220
|
|
|
215
|
-
#### Big text notification
|
|
221
|
+
#### Big text notification
|
|
216
222
|
|
|
217
223
|
```python
|
|
218
224
|
notification = Notification(
|
|
219
225
|
title="Article",
|
|
220
|
-
|
|
226
|
+
subject="Histroy of Loerm Ipsuim"
|
|
227
|
+
message="Lorem Ipsum is simply dummy text of the printing and ...",
|
|
221
228
|
style="big_text"
|
|
222
229
|
)
|
|
223
230
|
```
|
|
224
231
|
|
|
232
|
+

|
|
233
|
+
|
|
225
234
|
## Advanced Features
|
|
226
235
|
|
|
227
236
|
### Updating Notifications
|
|
@@ -281,19 +290,13 @@ notification.send(silent=True)
|
|
|
281
290
|
|
|
282
291
|
## Functions
|
|
283
292
|
|
|
284
|
-
### NotificationHandler - To Attach Listener
|
|
285
|
-
|
|
286
|
-
Add this to your main.py App Class so it runs Once, In later Versions It'll be Attached Automatical
|
|
287
|
-
|
|
288
293
|
```python
|
|
289
294
|
from kivymd.app import MDApp
|
|
290
|
-
from android_notify import Notification
|
|
295
|
+
from android_notify import Notification
|
|
291
296
|
|
|
292
297
|
class Myapp(MDApp):
|
|
293
298
|
|
|
294
299
|
def on_start(self):
|
|
295
|
-
# Is called Once when app is Starts up
|
|
296
|
-
NotificationHandler.bindNotifyListener() # if successfull returns True
|
|
297
300
|
Notification(title="Hello", message="This is a basic notification.",callback=self.doSomething).send()
|
|
298
301
|
|
|
299
302
|
def doSomething(self):
|
|
@@ -318,8 +321,6 @@ class Myapp(MDApp):
|
|
|
318
321
|
notify1 = Notification(title="Change Colour", message="Click to change App Colour", identifer='change_app_color')
|
|
319
322
|
notify1.send()
|
|
320
323
|
|
|
321
|
-
NotificationHandler.bindNotifyListener()
|
|
322
|
-
|
|
323
324
|
def on_resume(self):
|
|
324
325
|
# Is called everytime app is reopened
|
|
325
326
|
notify_identifer = NotificationHandler.getIdentifer()
|
|
@@ -331,22 +332,8 @@ class Myapp(MDApp):
|
|
|
331
332
|
pass
|
|
332
333
|
```
|
|
333
334
|
|
|
334
|
-
|
|
335
|
-
|
|
336
335
|
### Assist
|
|
337
336
|
|
|
338
|
-
- How to Copy image to app folder
|
|
339
|
-
|
|
340
|
-
```python
|
|
341
|
-
import shutil,os # These modules come packaged with python
|
|
342
|
-
from android.storage import app_storage_path # type: ignore -- This works only on android
|
|
343
|
-
|
|
344
|
-
app_path = os.path.join(app_storage_path(),'app')
|
|
345
|
-
image_path= "/storage/emulated/0/Download/profile.png"
|
|
346
|
-
|
|
347
|
-
shutil.copy(image_path, os.path.join(app_path, "profile.png"))
|
|
348
|
-
```
|
|
349
|
-
|
|
350
337
|
- Avoiding Human Error when using different notification styles
|
|
351
338
|
|
|
352
339
|
```python
|
|
@@ -375,9 +362,8 @@ notification.send()
|
|
|
375
362
|
|
|
376
363
|
## Image Requirements
|
|
377
364
|
|
|
378
|
-
- Images
|
|
379
|
-
-
|
|
380
|
-
- Example: `assets/imgs/icon.png`
|
|
365
|
+
- Online Images should start with `http://` or `https://`
|
|
366
|
+
- Local Images must be located within your app's folder
|
|
381
367
|
|
|
382
368
|
## Error Handling
|
|
383
369
|
|
|
@@ -385,22 +371,20 @@ The library validates arguments and provides helpful error messages:
|
|
|
385
371
|
|
|
386
372
|
- Invalid style names will suggest the closest matching style
|
|
387
373
|
- Invalid arguments will list all valid options
|
|
388
|
-
- Missing image files will
|
|
374
|
+
- Missing image files will list all files in App Directory
|
|
389
375
|
|
|
390
|
-
##
|
|
376
|
+
## Limitation
|
|
391
377
|
|
|
392
378
|
1. Only works on Android devices
|
|
393
|
-
2. Images must be within the app's storage path
|
|
394
|
-
3. Channel names are limited to 40 characters
|
|
395
|
-
4. Channel IDs are limited to 50 characters
|
|
396
379
|
|
|
397
380
|
## Best Practices
|
|
398
381
|
|
|
399
382
|
1. Always handle permissions appropriately
|
|
400
|
-
2.
|
|
401
|
-
3.
|
|
402
|
-
4.
|
|
403
|
-
5.
|
|
383
|
+
2. use `NotificationStyles` to set `style`, use `style=NotificationStyles.LARGE_ICON` instead of `style="large_icon"`
|
|
384
|
+
3. Use meaningful channel names for organization
|
|
385
|
+
4. Keep progress bar updates reasonable (don't update too frequently)
|
|
386
|
+
5. Test notifications on different Android versions
|
|
387
|
+
6. Consider using silent notifications for frequent updates
|
|
404
388
|
|
|
405
389
|
## Debugging Tips
|
|
406
390
|
|
|
@@ -108,6 +108,9 @@ Clock.schedule_once(lambda dt: notification.updateProgressBar(30, "30% downloade
|
|
|
108
108
|
|
|
109
109
|
#### Notification with an Image (Big Picture Style)
|
|
110
110
|
|
|
111
|
+
> [!NOTE]
|
|
112
|
+
> Online Images should start with `http://` or `https://`
|
|
113
|
+
|
|
111
114
|
```python
|
|
112
115
|
# Image notification
|
|
113
116
|
notification = Notification(
|
|
@@ -123,36 +126,39 @@ notification.send()
|
|
|
123
126
|
**Sample Image:**
|
|
124
127
|

|
|
125
128
|
|
|
126
|
-
####
|
|
129
|
+
#### Notification with an Image (Large Icon Style)
|
|
130
|
+
|
|
131
|
+
> [!NOTE]
|
|
132
|
+
> Online Images should start with `http://` or `https://`
|
|
127
133
|
|
|
128
134
|
```python
|
|
129
|
-
# Send a notification with inbox style
|
|
130
135
|
notification = Notification(
|
|
131
|
-
title=
|
|
132
|
-
message=
|
|
133
|
-
style=
|
|
136
|
+
title="FabianDev_",
|
|
137
|
+
message="A twitter about some programming stuff",
|
|
138
|
+
style="large_icon",
|
|
139
|
+
large_icon_path="assets/imgs/profile.png"
|
|
134
140
|
)
|
|
135
|
-
notification.send()
|
|
136
141
|
|
|
137
142
|
```
|
|
138
143
|
|
|
139
|
-
**Sample Image:**
|
|
140
|
-

|
|
141
146
|
|
|
142
|
-
#### Notification
|
|
147
|
+
#### Inbox Notification Style
|
|
143
148
|
|
|
144
149
|
```python
|
|
150
|
+
# Send a notification with inbox style
|
|
145
151
|
notification = Notification(
|
|
146
|
-
title=
|
|
147
|
-
message=
|
|
148
|
-
style=
|
|
149
|
-
large_icon_path="assets/imgs/profile.png"
|
|
152
|
+
title='Inbox Notification',
|
|
153
|
+
message='Line 1\nLine 2\nLine 3',
|
|
154
|
+
style='inbox'
|
|
150
155
|
)
|
|
156
|
+
notification.send()
|
|
151
157
|
|
|
152
158
|
```
|
|
153
159
|
|
|
154
|
-
**Sample Image:**
|
|
155
|
-

|
|
156
162
|
|
|
157
163
|
#### Notification with Buttons
|
|
158
164
|
|
|
@@ -176,16 +182,19 @@ notification.send()
|
|
|
176
182
|
**Sample Image:**
|
|
177
183
|

|
|
178
184
|
|
|
179
|
-
#### Big text notification
|
|
185
|
+
#### Big text notification
|
|
180
186
|
|
|
181
187
|
```python
|
|
182
188
|
notification = Notification(
|
|
183
189
|
title="Article",
|
|
184
|
-
|
|
190
|
+
subject="Histroy of Loerm Ipsuim"
|
|
191
|
+
message="Lorem Ipsum is simply dummy text of the printing and ...",
|
|
185
192
|
style="big_text"
|
|
186
193
|
)
|
|
187
194
|
```
|
|
188
195
|
|
|
196
|
+

|
|
197
|
+
|
|
189
198
|
## Advanced Features
|
|
190
199
|
|
|
191
200
|
### Updating Notifications
|
|
@@ -245,19 +254,13 @@ notification.send(silent=True)
|
|
|
245
254
|
|
|
246
255
|
## Functions
|
|
247
256
|
|
|
248
|
-
### NotificationHandler - To Attach Listener
|
|
249
|
-
|
|
250
|
-
Add this to your main.py App Class so it runs Once, In later Versions It'll be Attached Automatical
|
|
251
|
-
|
|
252
257
|
```python
|
|
253
258
|
from kivymd.app import MDApp
|
|
254
|
-
from android_notify import Notification
|
|
259
|
+
from android_notify import Notification
|
|
255
260
|
|
|
256
261
|
class Myapp(MDApp):
|
|
257
262
|
|
|
258
263
|
def on_start(self):
|
|
259
|
-
# Is called Once when app is Starts up
|
|
260
|
-
NotificationHandler.bindNotifyListener() # if successfull returns True
|
|
261
264
|
Notification(title="Hello", message="This is a basic notification.",callback=self.doSomething).send()
|
|
262
265
|
|
|
263
266
|
def doSomething(self):
|
|
@@ -282,8 +285,6 @@ class Myapp(MDApp):
|
|
|
282
285
|
notify1 = Notification(title="Change Colour", message="Click to change App Colour", identifer='change_app_color')
|
|
283
286
|
notify1.send()
|
|
284
287
|
|
|
285
|
-
NotificationHandler.bindNotifyListener()
|
|
286
|
-
|
|
287
288
|
def on_resume(self):
|
|
288
289
|
# Is called everytime app is reopened
|
|
289
290
|
notify_identifer = NotificationHandler.getIdentifer()
|
|
@@ -295,22 +296,8 @@ class Myapp(MDApp):
|
|
|
295
296
|
pass
|
|
296
297
|
```
|
|
297
298
|
|
|
298
|
-
|
|
299
|
-
|
|
300
299
|
### Assist
|
|
301
300
|
|
|
302
|
-
- How to Copy image to app folder
|
|
303
|
-
|
|
304
|
-
```python
|
|
305
|
-
import shutil,os # These modules come packaged with python
|
|
306
|
-
from android.storage import app_storage_path # type: ignore -- This works only on android
|
|
307
|
-
|
|
308
|
-
app_path = os.path.join(app_storage_path(),'app')
|
|
309
|
-
image_path= "/storage/emulated/0/Download/profile.png"
|
|
310
|
-
|
|
311
|
-
shutil.copy(image_path, os.path.join(app_path, "profile.png"))
|
|
312
|
-
```
|
|
313
|
-
|
|
314
301
|
- Avoiding Human Error when using different notification styles
|
|
315
302
|
|
|
316
303
|
```python
|
|
@@ -339,9 +326,8 @@ notification.send()
|
|
|
339
326
|
|
|
340
327
|
## Image Requirements
|
|
341
328
|
|
|
342
|
-
- Images
|
|
343
|
-
-
|
|
344
|
-
- Example: `assets/imgs/icon.png`
|
|
329
|
+
- Online Images should start with `http://` or `https://`
|
|
330
|
+
- Local Images must be located within your app's folder
|
|
345
331
|
|
|
346
332
|
## Error Handling
|
|
347
333
|
|
|
@@ -349,22 +335,20 @@ The library validates arguments and provides helpful error messages:
|
|
|
349
335
|
|
|
350
336
|
- Invalid style names will suggest the closest matching style
|
|
351
337
|
- Invalid arguments will list all valid options
|
|
352
|
-
- Missing image files will
|
|
338
|
+
- Missing image files will list all files in App Directory
|
|
353
339
|
|
|
354
|
-
##
|
|
340
|
+
## Limitation
|
|
355
341
|
|
|
356
342
|
1. Only works on Android devices
|
|
357
|
-
2. Images must be within the app's storage path
|
|
358
|
-
3. Channel names are limited to 40 characters
|
|
359
|
-
4. Channel IDs are limited to 50 characters
|
|
360
343
|
|
|
361
344
|
## Best Practices
|
|
362
345
|
|
|
363
346
|
1. Always handle permissions appropriately
|
|
364
|
-
2.
|
|
365
|
-
3.
|
|
366
|
-
4.
|
|
367
|
-
5.
|
|
347
|
+
2. use `NotificationStyles` to set `style`, use `style=NotificationStyles.LARGE_ICON` instead of `style="large_icon"`
|
|
348
|
+
3. Use meaningful channel names for organization
|
|
349
|
+
4. Keep progress bar updates reasonable (don't update too frequently)
|
|
350
|
+
5. Test notifications on different Android versions
|
|
351
|
+
6. Consider using silent notifications for frequent updates
|
|
368
352
|
|
|
369
353
|
## Debugging Tips
|
|
370
354
|
|
|
@@ -1,15 +1,16 @@
|
|
|
1
|
+
"""Contains Safe way to call Styles"""
|
|
2
|
+
|
|
1
3
|
class NotificationStyles():
|
|
2
4
|
""" Safely Adding Styles"""
|
|
3
5
|
DEFAULT = "simple"
|
|
4
|
-
|
|
6
|
+
|
|
5
7
|
PROGRESS = "progress"
|
|
6
8
|
INBOX = "inbox"
|
|
7
9
|
BIG_TEXT = "big_text"
|
|
8
|
-
|
|
10
|
+
|
|
9
11
|
LARGE_ICON = "large_icon"
|
|
10
12
|
BIG_PICTURE = "big_picture"
|
|
11
13
|
BOTH_IMGS = "both_imgs"
|
|
12
|
-
|
|
14
|
+
|
|
13
15
|
MESSAGING = "messaging" # TODO
|
|
14
16
|
CUSTOM = "custom" # TODO
|
|
15
|
-
|
|
@@ -2,15 +2,19 @@
|
|
|
2
2
|
import difflib
|
|
3
3
|
import traceback
|
|
4
4
|
import os
|
|
5
|
+
import threading
|
|
5
6
|
import re
|
|
7
|
+
from .styles import NotificationStyles
|
|
6
8
|
|
|
7
9
|
DEV=0
|
|
8
10
|
ON_ANDROID = False
|
|
9
11
|
|
|
10
12
|
try:
|
|
13
|
+
# Android Imports
|
|
11
14
|
from jnius import autoclass,cast # Needs Java to be installed pylint: disable=W0611, C0114
|
|
12
15
|
from android import activity # pylint: disable=import-error
|
|
13
16
|
from android.config import ACTIVITY_CLASS_NAME # pylint: disable=import-error
|
|
17
|
+
from android.runnable import run_on_ui_thread # pylint: disable=import-error
|
|
14
18
|
|
|
15
19
|
# Get the required Java classes
|
|
16
20
|
Bundle = autoclass('android.os.Bundle')
|
|
@@ -28,6 +32,14 @@ except Exception as e:# pylint: disable=W0718
|
|
|
28
32
|
MESSAGE='This Package Only Runs on Android !!! ---> Check "https://github.com/Fector101/android_notify/" to see design patterns and more info.' # pylint: disable=C0301
|
|
29
33
|
print(MESSAGE if DEV else '')
|
|
30
34
|
|
|
35
|
+
# This is so no crashes when developing on PC
|
|
36
|
+
def run_on_ui_thread(func):
|
|
37
|
+
"""Fallback for Developing on PC"""
|
|
38
|
+
def wrapper(*args, **kwargs):
|
|
39
|
+
print("Simulating run on UI thread")
|
|
40
|
+
return func(*args, **kwargs)
|
|
41
|
+
return wrapper
|
|
42
|
+
|
|
31
43
|
if ON_ANDROID:
|
|
32
44
|
try:
|
|
33
45
|
from android.permissions import request_permissions, Permission,check_permission # pylint: disable=E0401
|
|
@@ -62,9 +74,12 @@ class Notification:
|
|
|
62
74
|
both_imgs == using lager icon and big picture
|
|
63
75
|
:param big_picture_path: Relative Path to the image resource.
|
|
64
76
|
:param large_icon_path: Relative Path to the image resource.
|
|
65
|
-
:param
|
|
77
|
+
:param progress_current_value: interger To set progress bar current value.
|
|
78
|
+
:param progress_max_value: interger To set Max range for progress bar.
|
|
79
|
+
:param subject: Preview text For `big_Text` style `message`.
|
|
66
80
|
---
|
|
67
81
|
(Advance Options)
|
|
82
|
+
:param callback: Function for notification Click.
|
|
68
83
|
:param channel_name: - str Defaults to "Default Channel"
|
|
69
84
|
:param channel_id: - str Defaults to "default_channel"
|
|
70
85
|
---
|
|
@@ -84,18 +99,23 @@ class Notification:
|
|
|
84
99
|
] # TODO make pattern for non-android Notifications
|
|
85
100
|
defaults={
|
|
86
101
|
'title':'Default Title',
|
|
87
|
-
'message':'Default Message',
|
|
102
|
+
'message':'Default Message',
|
|
88
103
|
'style':'simple',
|
|
89
104
|
'big_picture_path':'',
|
|
90
105
|
'large_icon_path':'',
|
|
91
106
|
'progress_max_value': 0,
|
|
92
107
|
'progress_current_value': 0,
|
|
108
|
+
'subject':'',
|
|
93
109
|
'channel_name':'Default Channel',
|
|
94
110
|
'channel_id':'default_channel',
|
|
95
111
|
'logs':True,
|
|
96
112
|
"identifer": '',
|
|
97
113
|
'callback': None
|
|
98
114
|
}
|
|
115
|
+
# TODO specify types in a better way instead of using
|
|
116
|
+
# if key not in non_string_keys: value = str(value) to fix
|
|
117
|
+
#non_string_keys=['progress_max_value','progress_current_value','callback','logs']
|
|
118
|
+
# TODO using default values to check types
|
|
99
119
|
# During Development (When running on PC)
|
|
100
120
|
logs=not ON_ANDROID
|
|
101
121
|
def __init__(self,**kwargs):
|
|
@@ -108,6 +128,7 @@ class Notification:
|
|
|
108
128
|
self.big_picture_path=''
|
|
109
129
|
self.progress_current_value=0
|
|
110
130
|
self.progress_max_value=0
|
|
131
|
+
self.subject= ''
|
|
111
132
|
|
|
112
133
|
# For Nofitication Functions
|
|
113
134
|
self.identifer=''
|
|
@@ -151,14 +172,17 @@ class Notification:
|
|
|
151
172
|
self.message=new_message
|
|
152
173
|
if ON_ANDROID:
|
|
153
174
|
self.__builder.setContentText(new_message)
|
|
154
|
-
|
|
175
|
+
return True
|
|
176
|
+
return 'Updated'
|
|
155
177
|
def updateProgressBar(self,current_value,message:str=''):
|
|
156
|
-
"""message defaults to last message"""
|
|
157
|
-
if not ON_ANDROID:
|
|
158
|
-
return
|
|
178
|
+
"""current_value is the value to set progressbar, message defaults to last message"""
|
|
159
179
|
|
|
160
180
|
if self.logs:
|
|
161
181
|
print(f'Progress Bar Update value: {current_value}')
|
|
182
|
+
|
|
183
|
+
if not ON_ANDROID:
|
|
184
|
+
return
|
|
185
|
+
|
|
162
186
|
self.__builder.setProgress(self.progress_max_value, current_value, False)
|
|
163
187
|
if message:
|
|
164
188
|
self.__builder.setContentText(String(message))
|
|
@@ -166,8 +190,16 @@ class Notification:
|
|
|
166
190
|
|
|
167
191
|
def removeProgressBar(self,message=''):
|
|
168
192
|
"""message defaults to last message"""
|
|
193
|
+
|
|
194
|
+
if self.logs:
|
|
195
|
+
print('removed')
|
|
196
|
+
|
|
197
|
+
if not ON_ANDROID:
|
|
198
|
+
return True
|
|
199
|
+
|
|
169
200
|
if message:
|
|
170
201
|
self.__builder.setContentText(String(message))
|
|
202
|
+
return True
|
|
171
203
|
self.__builder.setProgress(0, 0, False)
|
|
172
204
|
self.notification_manager.notify(self.__id, self.__builder.build())
|
|
173
205
|
|
|
@@ -183,8 +215,9 @@ class Notification:
|
|
|
183
215
|
self.notification_manager.notify(self.__id, self.__builder.build())
|
|
184
216
|
elif self.logs:
|
|
185
217
|
string_to_display=''
|
|
218
|
+
print("\n Sent Notification!!!")
|
|
186
219
|
for name,value in vars(self).items():
|
|
187
|
-
if value and name
|
|
220
|
+
if value and name in ["title", "message", "style", "subject", "large_icon_path", "big_picture_path", "progress_current_value", "progress_max_value", "channel_name"]:
|
|
188
221
|
string_to_display += f'\n {name}: {value}'
|
|
189
222
|
string_to_display +="\n (Won't Print Logs When Complied,except if selected `Notification.logs=True`)"
|
|
190
223
|
print(string_to_display)
|
|
@@ -220,7 +253,11 @@ class Notification:
|
|
|
220
253
|
checkInReference([inputted_kwargs['style']],self.style_values,'values')
|
|
221
254
|
|
|
222
255
|
def __setArgs(self,options_dict:dict):
|
|
256
|
+
non_string_keys=['progress_max_value','progress_current_value','callback','logs']
|
|
257
|
+
|
|
223
258
|
for key,value in options_dict.items():
|
|
259
|
+
if key not in non_string_keys: # Fixing Types
|
|
260
|
+
value = str(value)
|
|
224
261
|
if key == 'channel_name' and value.strip():
|
|
225
262
|
setattr(self,key, value[:40])
|
|
226
263
|
elif key == 'channel_id' and value.strip(): # If user input's a channel id (i format properly)
|
|
@@ -250,63 +287,48 @@ class Notification:
|
|
|
250
287
|
self.notification_manager.createNotificationChannel(channel)
|
|
251
288
|
|
|
252
289
|
# Build the notification
|
|
253
|
-
#
|
|
290
|
+
# str() This is to prevent Error When user does Notification.title='blah' instead of Notification(title='blah'
|
|
291
|
+
# TODO fix this by creating a on_Title method in other versions
|
|
254
292
|
self.__builder.setContentTitle(str(self.title))
|
|
255
|
-
self.
|
|
293
|
+
if self.style == NotificationStyles.BIG_TEXT:
|
|
294
|
+
self.__builder.setContentText(str(self.subject))
|
|
295
|
+
else:
|
|
296
|
+
self.__builder.setContentText(str(self.message))
|
|
256
297
|
self.__builder.setSmallIcon(context.getApplicationInfo().icon)
|
|
257
298
|
self.__builder.setDefaults(NotificationCompat.DEFAULT_ALL) # pylint: disable=E0606
|
|
258
299
|
self.__builder.setPriority(NotificationCompat.PRIORITY_DEFAULT if self.silent else NotificationCompat.PRIORITY_HIGH)
|
|
300
|
+
self.__builder.setOnlyAlertOnce(True)
|
|
259
301
|
self.__addIntentToOpenApp()
|
|
302
|
+
|
|
260
303
|
def __addNotificationStyle(self):
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
large_icon_javapath=None
|
|
264
|
-
if self.large_icon_path:
|
|
265
|
-
try:
|
|
266
|
-
large_icon_javapath = self.__get_image_uri(self.large_icon_path)
|
|
267
|
-
except FileNotFoundError as e:
|
|
268
|
-
print('Failed Adding Big Picture Bitmap: ',e)
|
|
269
|
-
|
|
270
|
-
big_pic_javapath=None
|
|
271
|
-
if self.big_picture_path:
|
|
272
|
-
try:
|
|
273
|
-
big_pic_javapath = self.__get_image_uri(self.big_picture_path)
|
|
274
|
-
except FileNotFoundError as e:
|
|
275
|
-
print('Failed Adding Lagre Icon Bitmap: ',e)
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
if self.style == "big_text":
|
|
304
|
+
if self.style == NotificationStyles.BIG_TEXT:
|
|
279
305
|
big_text_style = NotificationCompatBigTextStyle() # pylint: disable=E0606
|
|
280
306
|
big_text_style.bigText(self.message)
|
|
281
307
|
self.__builder.setStyle(big_text_style)
|
|
282
|
-
|
|
283
|
-
elif self.style ==
|
|
308
|
+
|
|
309
|
+
elif self.style == NotificationStyles.INBOX:
|
|
284
310
|
inbox_style = NotificationCompatInboxStyle() # pylint: disable=E0606
|
|
285
311
|
for line in self.message.split("\n"):
|
|
286
312
|
inbox_style.addLine(line)
|
|
287
313
|
self.__builder.setStyle(inbox_style)
|
|
288
|
-
|
|
289
|
-
elif self.style ==
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
self.
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
big_picture_style = NotificationCompatBigPictureStyle().bigPicture(big_pic_bitmap)
|
|
302
|
-
self.__builder.setStyle(big_picture_style)
|
|
303
|
-
if large_icon_javapath:
|
|
304
|
-
large_icon_bitmap = self.__getBitmap(large_icon_javapath)
|
|
305
|
-
self.__builder.setLargeIcon(large_icon_bitmap)
|
|
314
|
+
|
|
315
|
+
elif self.style == NotificationStyles.BIG_PICTURE and self.big_picture_path:
|
|
316
|
+
self.__buildImg(self.big_picture_path, self.style)
|
|
317
|
+
|
|
318
|
+
elif self.style == NotificationStyles.LARGE_ICON and self.large_icon_path:
|
|
319
|
+
self.__buildImg(self.large_icon_path, self.style)
|
|
320
|
+
|
|
321
|
+
elif self.style == NotificationStyles.BOTH_IMGS and (self.big_picture_path or self.large_icon_path):
|
|
322
|
+
if self.big_picture_path:
|
|
323
|
+
self.__buildImg(self.big_picture_path, NotificationStyles.BIG_PICTURE)
|
|
324
|
+
if self.large_icon_path:
|
|
325
|
+
self.__buildImg(self.large_icon_path, NotificationStyles.LARGE_ICON)
|
|
326
|
+
|
|
306
327
|
elif self.style == 'progress':
|
|
307
328
|
self.__builder.setContentTitle(String(self.title))
|
|
308
329
|
self.__builder.setContentText(String(self.message))
|
|
309
330
|
self.__builder.setProgress(self.progress_max_value, self.progress_current_value, False)
|
|
331
|
+
|
|
310
332
|
# elif self.style == 'custom':
|
|
311
333
|
# self.__builder = self.__doCustomStyle()
|
|
312
334
|
|
|
@@ -314,6 +336,67 @@ class Notification:
|
|
|
314
336
|
# # TODO Will implement when needed
|
|
315
337
|
# return self.__builder
|
|
316
338
|
|
|
339
|
+
def __buildImg(self, user_img,img_style):
|
|
340
|
+
if user_img.startswith('http://') or user_img.startswith('https://'):
|
|
341
|
+
thread = threading.Thread(
|
|
342
|
+
target=self.__getImgFromURL,
|
|
343
|
+
args=(user_img,img_style,)
|
|
344
|
+
)
|
|
345
|
+
thread.start()
|
|
346
|
+
else:
|
|
347
|
+
if NotificationStyles.BIG_PICTURE:
|
|
348
|
+
bitmap = self.__getImgFromPath(user_img)
|
|
349
|
+
if bitmap:
|
|
350
|
+
big_picture_style = NotificationCompatBigPictureStyle().bigPicture(bitmap) # pylint: disable=E0606
|
|
351
|
+
self.__builder.setStyle(big_picture_style)
|
|
352
|
+
elif NotificationStyles.LARGE_ICON:
|
|
353
|
+
bitmap = self.__getImgFromPath(user_img)
|
|
354
|
+
if bitmap:
|
|
355
|
+
self.__builder.setLargeIcon(bitmap)
|
|
356
|
+
|
|
357
|
+
def __getImgFromPath(self, relative_path):
|
|
358
|
+
app_folder=os.path.join(app_storage_path(),'app') # pylint: disable=possibly-used-before-assignment
|
|
359
|
+
output_path = os.path.join(app_folder, relative_path)
|
|
360
|
+
if not os.path.exists(output_path):
|
|
361
|
+
print(f"\nImage not found at path: {output_path}, (Local images gotten from App Path)")
|
|
362
|
+
print("These are the existing files in your app Folder:")
|
|
363
|
+
print('['+', '.join(os.listdir(app_folder)) + ']')
|
|
364
|
+
return None
|
|
365
|
+
Uri = autoclass('android.net.Uri')
|
|
366
|
+
uri = Uri.parse(f"file://{output_path}")
|
|
367
|
+
return BitmapFactory.decodeStream(context.getContentResolver().openInputStream(uri))
|
|
368
|
+
|
|
369
|
+
def __getImgFromURL(self,url,img_style):
|
|
370
|
+
print("getting image from URL---")
|
|
371
|
+
try:
|
|
372
|
+
URL = autoclass('java.net.URL')
|
|
373
|
+
url = URL(url)
|
|
374
|
+
connection = url.openConnection()
|
|
375
|
+
connection.connect()
|
|
376
|
+
input_stream = connection.getInputStream()
|
|
377
|
+
bitmap = BitmapFactory.decodeStream(input_stream)
|
|
378
|
+
input_stream.close()
|
|
379
|
+
self.__applyNotificationImage(bitmap,img_style)
|
|
380
|
+
except Exception as e:
|
|
381
|
+
# TODO get type of JAVA Error
|
|
382
|
+
print('Error Type ',e)
|
|
383
|
+
print('Failed to get Img from URL ',traceback.format_exc())
|
|
384
|
+
|
|
385
|
+
@run_on_ui_thread
|
|
386
|
+
def __applyNotificationImage(self,bitmap,img_style):
|
|
387
|
+
print('appying notification image thread ', bitmap)
|
|
388
|
+
try:
|
|
389
|
+
if img_style == NotificationStyles.BIG_PICTURE:
|
|
390
|
+
big_picture_style = NotificationCompatBigPictureStyle().bigPicture(bitmap) # pylint: disable=E0606
|
|
391
|
+
self.__builder.setStyle(big_picture_style)
|
|
392
|
+
elif img_style == NotificationStyles.LARGE_ICON:
|
|
393
|
+
self.__builder.setLargeIcon(bitmap)
|
|
394
|
+
self.notification_manager.notify(self.__id, self.__builder.build())
|
|
395
|
+
print('added notification image done-------')
|
|
396
|
+
except Exception as e:
|
|
397
|
+
print('I could stop ',e)
|
|
398
|
+
print('could stop get Img from URL ',traceback.format_exc())
|
|
399
|
+
|
|
317
400
|
def __getUniqueID(self):
|
|
318
401
|
notification_id = self.notification_ids[-1] + 1
|
|
319
402
|
self.notification_ids.append(notification_id)
|
|
@@ -331,24 +414,6 @@ class Notification:
|
|
|
331
414
|
if not all(check_permission(p) for p in permissions):
|
|
332
415
|
request_permissions(permissions,on_permissions_result) # pylint: disable=E0606
|
|
333
416
|
|
|
334
|
-
def __get_image_uri(self,relative_path):
|
|
335
|
-
"""
|
|
336
|
-
Get the absolute URI for an image in the assets folder.
|
|
337
|
-
:param relative_path: The relative path to the image (e.g., 'assets/imgs/icon.png').
|
|
338
|
-
:return: Absolute URI java Object (e.g., 'file:///path/to/file.png').
|
|
339
|
-
"""
|
|
340
|
-
|
|
341
|
-
output_path = os.path.join(app_storage_path(),'app', relative_path) # pylint: disable=possibly-used-before-assignment
|
|
342
|
-
# print(output_path) # /data/user/0/(package.domain+package.name)/files/app/assets/imgs/icon.png | pylint: disable=:line-too-long
|
|
343
|
-
|
|
344
|
-
if not os.path.exists(output_path):
|
|
345
|
-
# TODO Use images From Any where even Web
|
|
346
|
-
raise FileNotFoundError(f"Image not found at path: {output_path}, (Can Only Use Images in App Path)")
|
|
347
|
-
Uri = autoclass('android.net.Uri')
|
|
348
|
-
return Uri.parse(f"file://{output_path}")
|
|
349
|
-
def __getBitmap(self,img_path):
|
|
350
|
-
return BitmapFactory.decodeStream(context.getContentResolver().openInputStream(img_path))
|
|
351
|
-
|
|
352
417
|
def __generate_channel_id(self,channel_name: str) -> str:
|
|
353
418
|
"""
|
|
354
419
|
Generate a readable and consistent channel ID from a channel name.
|
|
@@ -447,6 +512,7 @@ class Notification:
|
|
|
447
512
|
class NotificationHandler:
|
|
448
513
|
"""For Notification Operations """
|
|
449
514
|
__identifer = None
|
|
515
|
+
__bound = False
|
|
450
516
|
|
|
451
517
|
@classmethod
|
|
452
518
|
def getIdentifer(cls):
|
|
@@ -509,20 +575,16 @@ class NotificationHandler:
|
|
|
509
575
|
|
|
510
576
|
@classmethod
|
|
511
577
|
def bindNotifyListener(cls):
|
|
512
|
-
"""
|
|
513
|
-
```
|
|
514
|
-
from kivy.app import App
|
|
515
|
-
from android_notify import bindNotifyListener
|
|
516
|
-
class Myapp(App):
|
|
517
|
-
def on_start(self):
|
|
518
|
-
bindNotifyListener() # if successfull returns True
|
|
519
|
-
```
|
|
520
|
-
"""
|
|
578
|
+
"""This Creates a Listener for All Notification Clicks and Functions"""
|
|
521
579
|
if not cls.is_on_android():
|
|
522
580
|
return "Not on Android"
|
|
523
|
-
#
|
|
581
|
+
#TODO keep trying BroadcastReceiver
|
|
582
|
+
if cls.__bound:
|
|
583
|
+
print("bounding done already ")
|
|
584
|
+
return True
|
|
524
585
|
try:
|
|
525
586
|
activity.bind(on_new_intent=cls.__notificationHandler)
|
|
587
|
+
cls.__bound = True
|
|
526
588
|
return True
|
|
527
589
|
except Exception as e: # pylint: disable=broad-exception-caught
|
|
528
590
|
print('Failed to bin notitfications listener',e)
|
|
@@ -545,3 +607,5 @@ class NotificationHandler:
|
|
|
545
607
|
def is_on_android():
|
|
546
608
|
"""Utility to check if the app is running on Android."""
|
|
547
609
|
return ON_ANDROID
|
|
610
|
+
|
|
611
|
+
NotificationHandler.bindNotifyListener()
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.2
|
|
2
2
|
Name: android-notify
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.51
|
|
4
4
|
Summary: A Python package that simpilfies creating Android notifications in Kivy apps.
|
|
5
5
|
Home-page: https://github.com/fector101/android-notify
|
|
6
6
|
Author: Fabian
|
|
@@ -144,6 +144,9 @@ Clock.schedule_once(lambda dt: notification.updateProgressBar(30, "30% downloade
|
|
|
144
144
|
|
|
145
145
|
#### Notification with an Image (Big Picture Style)
|
|
146
146
|
|
|
147
|
+
> [!NOTE]
|
|
148
|
+
> Online Images should start with `http://` or `https://`
|
|
149
|
+
|
|
147
150
|
```python
|
|
148
151
|
# Image notification
|
|
149
152
|
notification = Notification(
|
|
@@ -159,36 +162,39 @@ notification.send()
|
|
|
159
162
|
**Sample Image:**
|
|
160
163
|

|
|
161
164
|
|
|
162
|
-
####
|
|
165
|
+
#### Notification with an Image (Large Icon Style)
|
|
166
|
+
|
|
167
|
+
> [!NOTE]
|
|
168
|
+
> Online Images should start with `http://` or `https://`
|
|
163
169
|
|
|
164
170
|
```python
|
|
165
|
-
# Send a notification with inbox style
|
|
166
171
|
notification = Notification(
|
|
167
|
-
title=
|
|
168
|
-
message=
|
|
169
|
-
style=
|
|
172
|
+
title="FabianDev_",
|
|
173
|
+
message="A twitter about some programming stuff",
|
|
174
|
+
style="large_icon",
|
|
175
|
+
large_icon_path="assets/imgs/profile.png"
|
|
170
176
|
)
|
|
171
|
-
notification.send()
|
|
172
177
|
|
|
173
178
|
```
|
|
174
179
|
|
|
175
|
-
**Sample Image:**
|
|
176
|
-

|
|
177
182
|
|
|
178
|
-
#### Notification
|
|
183
|
+
#### Inbox Notification Style
|
|
179
184
|
|
|
180
185
|
```python
|
|
186
|
+
# Send a notification with inbox style
|
|
181
187
|
notification = Notification(
|
|
182
|
-
title=
|
|
183
|
-
message=
|
|
184
|
-
style=
|
|
185
|
-
large_icon_path="assets/imgs/profile.png"
|
|
188
|
+
title='Inbox Notification',
|
|
189
|
+
message='Line 1\nLine 2\nLine 3',
|
|
190
|
+
style='inbox'
|
|
186
191
|
)
|
|
192
|
+
notification.send()
|
|
187
193
|
|
|
188
194
|
```
|
|
189
195
|
|
|
190
|
-
**Sample Image:**
|
|
191
|
-

|
|
192
198
|
|
|
193
199
|
#### Notification with Buttons
|
|
194
200
|
|
|
@@ -212,16 +218,19 @@ notification.send()
|
|
|
212
218
|
**Sample Image:**
|
|
213
219
|

|
|
214
220
|
|
|
215
|
-
#### Big text notification
|
|
221
|
+
#### Big text notification
|
|
216
222
|
|
|
217
223
|
```python
|
|
218
224
|
notification = Notification(
|
|
219
225
|
title="Article",
|
|
220
|
-
|
|
226
|
+
subject="Histroy of Loerm Ipsuim"
|
|
227
|
+
message="Lorem Ipsum is simply dummy text of the printing and ...",
|
|
221
228
|
style="big_text"
|
|
222
229
|
)
|
|
223
230
|
```
|
|
224
231
|
|
|
232
|
+

|
|
233
|
+
|
|
225
234
|
## Advanced Features
|
|
226
235
|
|
|
227
236
|
### Updating Notifications
|
|
@@ -281,19 +290,13 @@ notification.send(silent=True)
|
|
|
281
290
|
|
|
282
291
|
## Functions
|
|
283
292
|
|
|
284
|
-
### NotificationHandler - To Attach Listener
|
|
285
|
-
|
|
286
|
-
Add this to your main.py App Class so it runs Once, In later Versions It'll be Attached Automatical
|
|
287
|
-
|
|
288
293
|
```python
|
|
289
294
|
from kivymd.app import MDApp
|
|
290
|
-
from android_notify import Notification
|
|
295
|
+
from android_notify import Notification
|
|
291
296
|
|
|
292
297
|
class Myapp(MDApp):
|
|
293
298
|
|
|
294
299
|
def on_start(self):
|
|
295
|
-
# Is called Once when app is Starts up
|
|
296
|
-
NotificationHandler.bindNotifyListener() # if successfull returns True
|
|
297
300
|
Notification(title="Hello", message="This is a basic notification.",callback=self.doSomething).send()
|
|
298
301
|
|
|
299
302
|
def doSomething(self):
|
|
@@ -318,8 +321,6 @@ class Myapp(MDApp):
|
|
|
318
321
|
notify1 = Notification(title="Change Colour", message="Click to change App Colour", identifer='change_app_color')
|
|
319
322
|
notify1.send()
|
|
320
323
|
|
|
321
|
-
NotificationHandler.bindNotifyListener()
|
|
322
|
-
|
|
323
324
|
def on_resume(self):
|
|
324
325
|
# Is called everytime app is reopened
|
|
325
326
|
notify_identifer = NotificationHandler.getIdentifer()
|
|
@@ -331,22 +332,8 @@ class Myapp(MDApp):
|
|
|
331
332
|
pass
|
|
332
333
|
```
|
|
333
334
|
|
|
334
|
-
|
|
335
|
-
|
|
336
335
|
### Assist
|
|
337
336
|
|
|
338
|
-
- How to Copy image to app folder
|
|
339
|
-
|
|
340
|
-
```python
|
|
341
|
-
import shutil,os # These modules come packaged with python
|
|
342
|
-
from android.storage import app_storage_path # type: ignore -- This works only on android
|
|
343
|
-
|
|
344
|
-
app_path = os.path.join(app_storage_path(),'app')
|
|
345
|
-
image_path= "/storage/emulated/0/Download/profile.png"
|
|
346
|
-
|
|
347
|
-
shutil.copy(image_path, os.path.join(app_path, "profile.png"))
|
|
348
|
-
```
|
|
349
|
-
|
|
350
337
|
- Avoiding Human Error when using different notification styles
|
|
351
338
|
|
|
352
339
|
```python
|
|
@@ -375,9 +362,8 @@ notification.send()
|
|
|
375
362
|
|
|
376
363
|
## Image Requirements
|
|
377
364
|
|
|
378
|
-
- Images
|
|
379
|
-
-
|
|
380
|
-
- Example: `assets/imgs/icon.png`
|
|
365
|
+
- Online Images should start with `http://` or `https://`
|
|
366
|
+
- Local Images must be located within your app's folder
|
|
381
367
|
|
|
382
368
|
## Error Handling
|
|
383
369
|
|
|
@@ -385,22 +371,20 @@ The library validates arguments and provides helpful error messages:
|
|
|
385
371
|
|
|
386
372
|
- Invalid style names will suggest the closest matching style
|
|
387
373
|
- Invalid arguments will list all valid options
|
|
388
|
-
- Missing image files will
|
|
374
|
+
- Missing image files will list all files in App Directory
|
|
389
375
|
|
|
390
|
-
##
|
|
376
|
+
## Limitation
|
|
391
377
|
|
|
392
378
|
1. Only works on Android devices
|
|
393
|
-
2. Images must be within the app's storage path
|
|
394
|
-
3. Channel names are limited to 40 characters
|
|
395
|
-
4. Channel IDs are limited to 50 characters
|
|
396
379
|
|
|
397
380
|
## Best Practices
|
|
398
381
|
|
|
399
382
|
1. Always handle permissions appropriately
|
|
400
|
-
2.
|
|
401
|
-
3.
|
|
402
|
-
4.
|
|
403
|
-
5.
|
|
383
|
+
2. use `NotificationStyles` to set `style`, use `style=NotificationStyles.LARGE_ICON` instead of `style="large_icon"`
|
|
384
|
+
3. Use meaningful channel names for organization
|
|
385
|
+
4. Keep progress bar updates reasonable (don't update too frequently)
|
|
386
|
+
5. Test notifications on different Android versions
|
|
387
|
+
6. Consider using silent notifications for frequent updates
|
|
404
388
|
|
|
405
389
|
## Debugging Tips
|
|
406
390
|
|
|
@@ -6,7 +6,7 @@ with open("README.md", "r", encoding="utf-8") as readme_data:
|
|
|
6
6
|
|
|
7
7
|
setup(
|
|
8
8
|
name="android-notify",
|
|
9
|
-
version="1.
|
|
9
|
+
version="1.51",
|
|
10
10
|
author="Fabian",
|
|
11
11
|
author_email='fector101@yahoo.com',
|
|
12
12
|
description="A Python package that simpilfies creating Android notifications in Kivy apps.",
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|