android-notify 1.55__py3-none-any.whl → 1.56__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/base.py +82 -0
- android_notify/styles.py +3 -3
- android_notify/sword.py +66 -131
- {android_notify-1.55.dist-info → android_notify-1.56.dist-info}/METADATA +5 -1
- android_notify-1.56.dist-info/RECORD +10 -0
- android_notify/facade.py +0 -28
- android_notify-1.55.dist-info/RECORD +0 -10
- {android_notify-1.55.dist-info → android_notify-1.56.dist-info}/WHEEL +0 -0
- {android_notify-1.55.dist-info → android_notify-1.56.dist-info}/top_level.txt +0 -0
android_notify/base.py
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
"""Assists Notification Class with Args keeps sub class cleaner"""
|
|
2
|
+
from dataclasses import dataclass, fields
|
|
3
|
+
import difflib
|
|
4
|
+
from .styles import NotificationStyles
|
|
5
|
+
|
|
6
|
+
@dataclass
|
|
7
|
+
class BaseNotification:
|
|
8
|
+
"""Encapsulator"""
|
|
9
|
+
|
|
10
|
+
# Basic options
|
|
11
|
+
title: str = ''
|
|
12
|
+
message: str = ''
|
|
13
|
+
style: str = 'simple'
|
|
14
|
+
|
|
15
|
+
# Style-specific attributes
|
|
16
|
+
big_picture_path: str = ''
|
|
17
|
+
large_icon_path: str = ''
|
|
18
|
+
progress_max_value: int = 100
|
|
19
|
+
progress_current_value: float = 0.0 # Also Takes in Ints
|
|
20
|
+
body: str = ''
|
|
21
|
+
|
|
22
|
+
# Notification Functions
|
|
23
|
+
identifer: str = ''
|
|
24
|
+
callback: object = None
|
|
25
|
+
|
|
26
|
+
# Advanced Options
|
|
27
|
+
app_icon: str = 'Defaults to package app icon'
|
|
28
|
+
channel_name: str = 'Default Channel'
|
|
29
|
+
channel_id: str = 'default_channel'
|
|
30
|
+
silent: bool = False
|
|
31
|
+
logs: bool = False
|
|
32
|
+
|
|
33
|
+
def __init__(self, **kwargs):
|
|
34
|
+
"""Custom init to handle validation before dataclass assigns values"""
|
|
35
|
+
# Validate provided arguments
|
|
36
|
+
self.validate_args(kwargs)
|
|
37
|
+
|
|
38
|
+
# Assign validated values using the normal dataclass behavior
|
|
39
|
+
for field_ in fields(self):
|
|
40
|
+
field_name = field_.name
|
|
41
|
+
setattr(self, field_name, kwargs.get(field_name, getattr(self, field_name)))
|
|
42
|
+
|
|
43
|
+
def validate_args(self, inputted_kwargs):
|
|
44
|
+
"""Check for unexpected arguments and suggest corrections before Python validation"""
|
|
45
|
+
default_fields = {field.name : field.type for field in fields(self)} #{'title': <class 'str'>, 'message': <class 'str'>,...
|
|
46
|
+
allowed_fields_keys = set(default_fields.keys())
|
|
47
|
+
|
|
48
|
+
# Identify invalid arguments
|
|
49
|
+
invalid_args = set(inputted_kwargs) - allowed_fields_keys
|
|
50
|
+
if invalid_args:
|
|
51
|
+
suggestions = []
|
|
52
|
+
for arg in invalid_args:
|
|
53
|
+
closest_match = difflib.get_close_matches(arg, allowed_fields_keys, n=1, cutoff=0.6)
|
|
54
|
+
if closest_match:
|
|
55
|
+
suggestions.append(f"* '{arg}' is invalid -> Did you mean '{closest_match[0]}'?")
|
|
56
|
+
else:
|
|
57
|
+
suggestions.append(f"* '{arg}' is not a valid argument.")
|
|
58
|
+
|
|
59
|
+
suggestion_text = '\n'.join(suggestions)
|
|
60
|
+
raise ValueError(f"Invalid arguments provided:\n{suggestion_text}")
|
|
61
|
+
|
|
62
|
+
# Validating types
|
|
63
|
+
for each_arg in inputted_kwargs.keys():
|
|
64
|
+
expected_type = default_fields[each_arg]
|
|
65
|
+
actual_value = inputted_kwargs[each_arg]
|
|
66
|
+
|
|
67
|
+
# Allow both int and float for progress_current_value
|
|
68
|
+
if each_arg == "progress_current_value":
|
|
69
|
+
if not isinstance(actual_value, (int, float)):
|
|
70
|
+
raise TypeError(f"Expected '{each_arg}' to be int or float, got {type(actual_value)} instead.")
|
|
71
|
+
else:
|
|
72
|
+
if not isinstance(actual_value, expected_type):
|
|
73
|
+
raise TypeError(f"Expected '{each_arg}' to be {expected_type}, got {type(actual_value)} instead.")
|
|
74
|
+
|
|
75
|
+
# Validate `style` values
|
|
76
|
+
style_values = [value for key, value in vars(NotificationStyles).items() if not key.startswith("__")]
|
|
77
|
+
if 'style' in inputted_kwargs and inputted_kwargs['style'] not in ['',*style_values]:
|
|
78
|
+
inputted_style=inputted_kwargs['style']
|
|
79
|
+
allowed_styles=', '.join(style_values)
|
|
80
|
+
raise ValueError(
|
|
81
|
+
f"Invalid style '{inputted_style}'. Allowed styles: {allowed_styles}"
|
|
82
|
+
)
|
android_notify/styles.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""Contains Safe way to call Styles"""
|
|
2
2
|
|
|
3
|
-
class NotificationStyles
|
|
3
|
+
class NotificationStyles:
|
|
4
4
|
""" Safely Adding Styles"""
|
|
5
5
|
DEFAULT = "simple"
|
|
6
6
|
|
|
@@ -12,5 +12,5 @@ class NotificationStyles():
|
|
|
12
12
|
BIG_PICTURE = "big_picture"
|
|
13
13
|
BOTH_IMGS = "both_imgs"
|
|
14
14
|
|
|
15
|
-
MESSAGING = "messaging" # TODO
|
|
16
|
-
CUSTOM = "custom" # TODO
|
|
15
|
+
# MESSAGING = "messaging" # TODO
|
|
16
|
+
# CUSTOM = "custom" # TODO
|
android_notify/sword.py
CHANGED
|
@@ -5,7 +5,7 @@ import os
|
|
|
5
5
|
import threading
|
|
6
6
|
import re
|
|
7
7
|
from .styles import NotificationStyles
|
|
8
|
-
from .
|
|
8
|
+
from .base import BaseNotification
|
|
9
9
|
DEV=0
|
|
10
10
|
ON_ANDROID = False
|
|
11
11
|
|
|
@@ -31,7 +31,7 @@ try:
|
|
|
31
31
|
ON_ANDROID = True
|
|
32
32
|
except Exception as e:# pylint: disable=W0718
|
|
33
33
|
MESSAGE='This Package Only Runs on Android !!! ---> Check "https://github.com/Fector101/android_notify/" to see design patterns and more info.' # pylint: disable=C0301
|
|
34
|
-
print(MESSAGE)
|
|
34
|
+
# print(MESSAGE) Already Printing in core.py
|
|
35
35
|
|
|
36
36
|
# This is so no crashes when developing on PC
|
|
37
37
|
def run_on_ui_thread(func):
|
|
@@ -55,8 +55,7 @@ if ON_ANDROID:
|
|
|
55
55
|
NotificationCompatBigPictureStyle = autoclass('androidx.core.app.NotificationCompat$BigPictureStyle') # pylint: disable=C0301
|
|
56
56
|
NotificationCompatInboxStyle = autoclass('androidx.core.app.NotificationCompat$InboxStyle')
|
|
57
57
|
except Exception as e:# pylint: disable=W0718
|
|
58
|
-
print(e)
|
|
59
|
-
# print(e if DEV else '')
|
|
58
|
+
print(e)
|
|
60
59
|
print("""
|
|
61
60
|
Dependency Error: Add the following in buildozer.spec:
|
|
62
61
|
* android.gradle_dependencies = androidx.core:core-ktx:1.15.0, androidx.core:core:1.6.0
|
|
@@ -91,46 +90,28 @@ class Notification(BaseNotification):
|
|
|
91
90
|
button_ids=[0]
|
|
92
91
|
btns_box={}
|
|
93
92
|
main_functions={}
|
|
94
|
-
style_values=[
|
|
95
|
-
'','simple',
|
|
96
|
-
'progress','big_text',
|
|
97
|
-
'inbox', 'big_picture',
|
|
98
|
-
'large_icon','both_imgs',
|
|
99
|
-
'custom'
|
|
100
|
-
] # TODO make pattern for non-android Notifications
|
|
101
|
-
defaults={
|
|
102
|
-
'title':'Default Title',
|
|
103
|
-
'message':'Default Message',
|
|
104
|
-
'style':'simple',
|
|
105
|
-
'big_picture_path':'',
|
|
106
|
-
'large_icon_path':'',
|
|
107
|
-
'progress_max_value': 0.5,
|
|
108
|
-
'progress_current_value': 0.5,
|
|
109
|
-
'body':'',
|
|
110
|
-
'channel_name':'Default Channel',
|
|
111
|
-
'channel_id':'default_channel',
|
|
112
|
-
'logs':True,
|
|
113
|
-
"identifer": '',
|
|
114
|
-
'callback': None,
|
|
115
|
-
'app_icon': 'Defaults to package app icon'
|
|
116
|
-
}
|
|
117
93
|
|
|
118
94
|
# During Development (When running on PC)
|
|
119
|
-
logs=not ON_ANDROID
|
|
95
|
+
BaseNotification.logs=not ON_ANDROID
|
|
120
96
|
def __init__(self,**kwargs): #pylint: disable=W0231 #@dataclass already does work
|
|
121
|
-
|
|
122
|
-
|
|
97
|
+
super().__init__(**kwargs)
|
|
98
|
+
|
|
123
99
|
self.__id = self.__getUniqueID()
|
|
124
|
-
self.__setArgs(kwargs)
|
|
125
100
|
self.__update_timer = None # To Track progressbar last update (According to Android Docs Don't update bar to often, I also faced so issues when doing that)
|
|
126
|
-
|
|
101
|
+
self.__formatChannel(kwargs)
|
|
127
102
|
if not ON_ANDROID:
|
|
128
103
|
return
|
|
129
104
|
# TODO make send method wait for __asks_permission_if_needed method
|
|
130
105
|
self.__asks_permission_if_needed()
|
|
131
106
|
self.notification_manager = context.getSystemService(context.NOTIFICATION_SERVICE)
|
|
132
107
|
self.__builder=NotificationCompatBuilder(context, self.channel_id)# pylint: disable=E0606
|
|
133
|
-
|
|
108
|
+
def showInfiniteProgressBar(self):
|
|
109
|
+
"""Displays an (Infinite) progress Bar in Notification, that continues loading indefinitely.
|
|
110
|
+
Can be Removed By `removeProgressBar` Method
|
|
111
|
+
"""
|
|
112
|
+
self.__builder.setProgress(0,0, True)
|
|
113
|
+
self.__dispatchNotification()
|
|
114
|
+
|
|
134
115
|
def updateTitle(self,new_title):
|
|
135
116
|
"""Changes Old Title
|
|
136
117
|
|
|
@@ -142,7 +123,7 @@ class Notification(BaseNotification):
|
|
|
142
123
|
print(f'new notification title: {self.title}')
|
|
143
124
|
if ON_ANDROID:
|
|
144
125
|
self.__builder.setContentTitle(String(self.title))
|
|
145
|
-
self.
|
|
126
|
+
self.__dispatchNotification()
|
|
146
127
|
|
|
147
128
|
def updateMessage(self,new_message):
|
|
148
129
|
"""Changes Old Message
|
|
@@ -155,7 +136,7 @@ class Notification(BaseNotification):
|
|
|
155
136
|
print(f'new notification message: {self.message}')
|
|
156
137
|
if ON_ANDROID:
|
|
157
138
|
self.__builder.setContentText(String(self.message))
|
|
158
|
-
self.
|
|
139
|
+
self.__dispatchNotification()
|
|
159
140
|
|
|
160
141
|
def updateProgressBar(self,current_value:int,message:str='',title:str=''):
|
|
161
142
|
"""Updates progress bar current value
|
|
@@ -189,14 +170,14 @@ class Notification(BaseNotification):
|
|
|
189
170
|
self.updateMessage(message)
|
|
190
171
|
if title:
|
|
191
172
|
self.updateTitle(title)
|
|
192
|
-
self.
|
|
173
|
+
self.__dispatchNotification()
|
|
193
174
|
self.__update_timer = None
|
|
194
175
|
|
|
195
176
|
|
|
196
177
|
# Start a new timer that runs after 0.5 seconds
|
|
197
178
|
self.__update_timer = threading.Timer(0.5, delayed_update)
|
|
198
179
|
self.__update_timer.start()
|
|
199
|
-
|
|
180
|
+
|
|
200
181
|
def removeProgressBar(self,message='',show_on_update=True, title:str='') -> None:
|
|
201
182
|
"""Removes Progress Bar from Notification
|
|
202
183
|
|
|
@@ -223,7 +204,7 @@ class Notification(BaseNotification):
|
|
|
223
204
|
if title:
|
|
224
205
|
self.updateTitle(title)
|
|
225
206
|
self.__builder.setProgress(0, 0, False)
|
|
226
|
-
self.
|
|
207
|
+
self.__dispatchNotification()
|
|
227
208
|
return True
|
|
228
209
|
|
|
229
210
|
def send(self,silent:bool=False,persistent=False,close_on_click=True):
|
|
@@ -237,13 +218,18 @@ class Notification(BaseNotification):
|
|
|
237
218
|
self.silent=self.silent or silent
|
|
238
219
|
if ON_ANDROID:
|
|
239
220
|
self.__startNotificationBuild(persistent,close_on_click)
|
|
240
|
-
self.
|
|
241
|
-
|
|
221
|
+
self.__dispatchNotification()
|
|
222
|
+
if self.logs:
|
|
242
223
|
string_to_display=''
|
|
243
224
|
print("\n Sent Notification!!!")
|
|
244
225
|
for name,value in vars(self).items():
|
|
245
226
|
if value and name in ["title", "message", "style", "body", "large_icon_path", "big_picture_path", "progress_current_value", "progress_max_value", "channel_name"]:
|
|
246
|
-
|
|
227
|
+
if name == "progress_max_value":
|
|
228
|
+
if self.style == NotificationStyles.PROGRESS:
|
|
229
|
+
string_to_display += f'\n {name}: {value}'
|
|
230
|
+
else:
|
|
231
|
+
string_to_display += f'\n {name}: {value}'
|
|
232
|
+
|
|
247
233
|
string_to_display +="\n (Won't Print Logs When Complied,except if selected `Notification.logs=True`)"
|
|
248
234
|
print(string_to_display)
|
|
249
235
|
if DEV:
|
|
@@ -304,7 +290,7 @@ class Notification(BaseNotification):
|
|
|
304
290
|
"""
|
|
305
291
|
if ON_ANDROID:
|
|
306
292
|
self.__builder.mActions.clear()
|
|
307
|
-
self.
|
|
293
|
+
self.__dispatchNotification()
|
|
308
294
|
if self.logs:
|
|
309
295
|
print('Removed Notication Buttons')
|
|
310
296
|
|
|
@@ -343,60 +329,17 @@ class Notification(BaseNotification):
|
|
|
343
329
|
self.__buildImg(self.large_icon_path, NotificationStyles.LARGE_ICON)
|
|
344
330
|
|
|
345
331
|
elif style == NotificationStyles.PROGRESS:
|
|
346
|
-
self.__builder.setContentTitle(String(self.title))
|
|
347
|
-
self.__builder.setContentText(String(self.message))
|
|
348
332
|
self.__builder.setProgress(self.progress_max_value, self.progress_current_value, False)
|
|
349
333
|
|
|
350
334
|
if already_sent:
|
|
351
|
-
self.
|
|
335
|
+
self.__dispatchNotification()
|
|
352
336
|
|
|
353
337
|
return True
|
|
354
338
|
# elif style == 'custom':
|
|
355
339
|
# self.__builder = self.__doCustomStyle()
|
|
356
340
|
|
|
357
|
-
def
|
|
358
|
-
|
|
359
|
-
def checkInReference(inputted_keywords,accepteable_inputs,input_type):
|
|
360
|
-
def singularForm(plural_form):
|
|
361
|
-
return plural_form[:-1]
|
|
362
|
-
invalid_args= set(inputted_keywords) - set(accepteable_inputs)
|
|
363
|
-
if invalid_args:
|
|
364
|
-
suggestions=[]
|
|
365
|
-
for arg in invalid_args:
|
|
366
|
-
closest_match = difflib.get_close_matches(arg,accepteable_inputs,n=2,cutoff=0.6)
|
|
367
|
-
if closest_match:
|
|
368
|
-
suggestions.append(f"* '{arg}' Invalid -> Did you mean '{closest_match[0]}'? ") # pylint: disable=C0301
|
|
369
|
-
else:
|
|
370
|
-
suggestions.append(f"* {arg} is not a valid {singularForm(input_type)}.")
|
|
371
|
-
suggestion_text='\n'.join(suggestions)
|
|
372
|
-
hint_msg=singularForm(input_type) if len(invalid_args) < 2 else input_type
|
|
373
|
-
|
|
374
|
-
raise ValueError(f"Invalid {hint_msg} provided: \n\t{suggestion_text}\n\t* list of valid {input_type}: [{', '.join(accepteable_inputs)}]")
|
|
375
|
-
|
|
376
|
-
allowed_keywords=self.defaults.keys()
|
|
377
|
-
inputted_keywords_=inputted_kwargs.keys()
|
|
378
|
-
checkInReference(inputted_keywords_,allowed_keywords,'arguments')
|
|
379
|
-
|
|
380
|
-
# Validate style values
|
|
381
|
-
if 'style' in inputted_keywords_ and inputted_kwargs['style'] not in self.style_values:
|
|
382
|
-
checkInReference([inputted_kwargs['style']],self.style_values,'values')
|
|
383
|
-
|
|
384
|
-
def __setArgs(self,options_dict:dict):
|
|
385
|
-
non_string_keys=['progress_max_value','progress_current_value','callback','logs']
|
|
386
|
-
|
|
387
|
-
for key,value in options_dict.items():
|
|
388
|
-
if key not in non_string_keys: # Fixing Types
|
|
389
|
-
value = str(value)
|
|
390
|
-
if key == 'channel_name' and value.strip():
|
|
391
|
-
setattr(self,key, value[:40])
|
|
392
|
-
elif key == 'channel_id' and value.strip(): # If user input's a channel id (i format properly)
|
|
393
|
-
setattr(self,key, self.__generate_channel_id(value))
|
|
394
|
-
else:
|
|
395
|
-
setattr(self,key, value if value or isinstance(value, bool) else self.defaults[key])
|
|
396
|
-
|
|
397
|
-
if "channel_id" not in options_dict and 'channel_name' in options_dict: # if User doesn't input channel id but inputs channel_name
|
|
398
|
-
setattr(self,'channel_id', self.__generate_channel_id(options_dict['channel_name']))
|
|
399
|
-
|
|
341
|
+
def __dispatchNotification(self):
|
|
342
|
+
self.notification_manager.notify(self.__id, self.__builder.build())
|
|
400
343
|
def __startNotificationBuild(self,persistent,close_on_click):
|
|
401
344
|
self.__createBasicNotification(persistent,close_on_click)
|
|
402
345
|
if self.style not in ['simple','']:
|
|
@@ -427,7 +370,7 @@ class Notification(BaseNotification):
|
|
|
427
370
|
self.__builder.setOngoing(persistent)
|
|
428
371
|
self.__builder.setAutoCancel(close_on_click)
|
|
429
372
|
self.__addIntentToOpenApp()
|
|
430
|
-
|
|
373
|
+
|
|
431
374
|
# def __doCustomStyle(self):
|
|
432
375
|
# # TODO Will implement when needed
|
|
433
376
|
# return self.__builder
|
|
@@ -436,7 +379,7 @@ class Notification(BaseNotification):
|
|
|
436
379
|
self.__setIconFromBitmap(self.app_icon)
|
|
437
380
|
else:
|
|
438
381
|
self.__builder.setSmallIcon(context.getApplicationInfo().icon)
|
|
439
|
-
|
|
382
|
+
|
|
440
383
|
def __buildImg(self, user_img,img_style):
|
|
441
384
|
if user_img.startswith('http://') or user_img.startswith('https://'):
|
|
442
385
|
def callback(bitmap):
|
|
@@ -450,7 +393,7 @@ class Notification(BaseNotification):
|
|
|
450
393
|
bitmap = self.__getImgFromPath(user_img)
|
|
451
394
|
if bitmap:
|
|
452
395
|
self.__applyNotificationImage(bitmap,img_style)
|
|
453
|
-
|
|
396
|
+
|
|
454
397
|
def __setIconFromBitmap(self,img_path):
|
|
455
398
|
"""Path can be link or relative path"""
|
|
456
399
|
if img_path.startswith('http://') or img_path.startswith('https://'):
|
|
@@ -470,8 +413,7 @@ class Notification(BaseNotification):
|
|
|
470
413
|
if self.logs:
|
|
471
414
|
print('Failed getting img for custom notification icon defaulting to app icon')
|
|
472
415
|
self.__builder.setSmallIcon(context.getApplicationInfo().icon)
|
|
473
|
-
|
|
474
|
-
|
|
416
|
+
|
|
475
417
|
def __getImgFromPath(self, relative_path):
|
|
476
418
|
app_folder=os.path.join(app_storage_path(),'app') # pylint: disable=possibly-used-before-assignment
|
|
477
419
|
output_path = os.path.join(app_folder, relative_path)
|
|
@@ -511,25 +453,6 @@ class Notification(BaseNotification):
|
|
|
511
453
|
print('Error Type ',e)
|
|
512
454
|
print('Failed to get Bitmap from URL ',traceback.format_exc())
|
|
513
455
|
|
|
514
|
-
# def __getImgFromURL(self,url,img_style):
|
|
515
|
-
# if self.logs:
|
|
516
|
-
# print("getting image from URL---")
|
|
517
|
-
# try:
|
|
518
|
-
# URL = autoclass('java.net.URL')
|
|
519
|
-
# url = URL(url)
|
|
520
|
-
# connection = url.openConnection()
|
|
521
|
-
# connection.connect()
|
|
522
|
-
# input_stream = connection.getInputStream()
|
|
523
|
-
# bitmap = BitmapFactory.decodeStream(input_stream)
|
|
524
|
-
# input_stream.close()
|
|
525
|
-
# if bitmap:
|
|
526
|
-
# self.__applyNotificationImage(bitmap,img_style)
|
|
527
|
-
|
|
528
|
-
# except Exception as e:
|
|
529
|
-
# # TODO get type of JAVA Error
|
|
530
|
-
# print('Error Type ',e)
|
|
531
|
-
# print('Failed to get Img from URL ',traceback.format_exc())
|
|
532
|
-
|
|
533
456
|
@run_on_ui_thread
|
|
534
457
|
def __applyNotificationImage(self,bitmap,img_style):
|
|
535
458
|
if self.logs:
|
|
@@ -540,7 +463,7 @@ class Notification(BaseNotification):
|
|
|
540
463
|
self.__builder.setStyle(big_picture_style)
|
|
541
464
|
elif img_style == NotificationStyles.LARGE_ICON:
|
|
542
465
|
self.__builder.setLargeIcon(bitmap)
|
|
543
|
-
self.
|
|
466
|
+
self.__dispatchNotification()
|
|
544
467
|
if self.logs:
|
|
545
468
|
print('Done adding image to notification-------')
|
|
546
469
|
except Exception as e:
|
|
@@ -565,24 +488,6 @@ class Notification(BaseNotification):
|
|
|
565
488
|
if not all(check_permission(p) for p in permissions):
|
|
566
489
|
request_permissions(permissions,on_permissions_result) # pylint: disable=E0606
|
|
567
490
|
|
|
568
|
-
def __generate_channel_id(self,channel_name: str) -> str:
|
|
569
|
-
"""
|
|
570
|
-
Generate a readable and consistent channel ID from a channel name.
|
|
571
|
-
|
|
572
|
-
Args:
|
|
573
|
-
channel_name (str): The name of the notification channel.
|
|
574
|
-
|
|
575
|
-
Returns:
|
|
576
|
-
str: A sanitized channel ID.
|
|
577
|
-
"""
|
|
578
|
-
# Normalize the channel name
|
|
579
|
-
channel_id = channel_name.strip().lower()
|
|
580
|
-
# Replace spaces and special characters with underscores
|
|
581
|
-
channel_id = re.sub(r'[^a-z0-9]+', '_', channel_id)
|
|
582
|
-
# Remove leading/trailing underscores
|
|
583
|
-
channel_id = channel_id.strip('_')
|
|
584
|
-
return channel_id[:50]
|
|
585
|
-
|
|
586
491
|
def __addIntentToOpenApp(self):
|
|
587
492
|
intent = Intent(context, PythonActivity)
|
|
588
493
|
action = str(self.identifer) or f"ACTION_{self.__id}"
|
|
@@ -609,6 +514,36 @@ class Notification(BaseNotification):
|
|
|
609
514
|
self.button_ids.append(btn_id)
|
|
610
515
|
return btn_id
|
|
611
516
|
|
|
517
|
+
def __formatChannel(self, inputted_kwargs):
|
|
518
|
+
if 'channel_name' in inputted_kwargs:
|
|
519
|
+
cleaned_name = inputted_kwargs['channel_name'].strip()
|
|
520
|
+
self.channel_name = cleaned_name[:40] if cleaned_name else 'Default Channel'
|
|
521
|
+
|
|
522
|
+
if 'channel_id' in inputted_kwargs:
|
|
523
|
+
cleaned_id = inputted_kwargs['channel_id'].strip()
|
|
524
|
+
self.channel_id = self.__generate_channel_id(cleaned_id) if cleaned_id else 'default_channel'
|
|
525
|
+
elif 'channel_name' in inputted_kwargs:
|
|
526
|
+
# Generate channel_id from channel_name if only channel_name is provided
|
|
527
|
+
generated_id = self.__generate_channel_id(inputted_kwargs['channel_name'])
|
|
528
|
+
self.channel_id = generated_id
|
|
529
|
+
|
|
530
|
+
def __generate_channel_id(self,channel_name: str) -> str:
|
|
531
|
+
"""
|
|
532
|
+
Generate a readable and consistent channel ID from a channel name.
|
|
533
|
+
|
|
534
|
+
Args:
|
|
535
|
+
channel_name (str): The name of the notification channel.
|
|
536
|
+
|
|
537
|
+
Returns:
|
|
538
|
+
str: A sanitized channel ID.
|
|
539
|
+
"""
|
|
540
|
+
# Normalize the channel name
|
|
541
|
+
channel_id = channel_name.strip().lower()
|
|
542
|
+
# Replace spaces and special characters with underscores
|
|
543
|
+
channel_id = re.sub(r'[^a-z0-9]+', '_', channel_id)
|
|
544
|
+
# Remove leading/trailing underscores
|
|
545
|
+
channel_id = channel_id.strip('_')
|
|
546
|
+
return channel_id[:50]
|
|
612
547
|
|
|
613
548
|
class NotificationHandler:
|
|
614
549
|
"""For Notification Operations """
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.2
|
|
2
2
|
Name: android-notify
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.56
|
|
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
|
|
@@ -464,6 +464,10 @@ args
|
|
|
464
464
|
|
|
465
465
|
- new_message : String to be set as New notification Message
|
|
466
466
|
|
|
467
|
+
### Instance.showInfiniteProgressBar
|
|
468
|
+
|
|
469
|
+
Displays an Infinite Progress Bar in Notification, Can be Removed using `removeProgressBar` and updated using `updateProgressBar` method
|
|
470
|
+
|
|
467
471
|
### Instance.updateProgressBar
|
|
468
472
|
|
|
469
473
|
if updating title,msg with progressbar frequenlty pass them in too to avoid update issues.
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
android_notify/__init__.py,sha256=lcLjyfegXgU7cyGhfSphAOBipXwemrVkdYy3mcF6X5Y,172
|
|
2
|
+
android_notify/__main__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
3
|
+
android_notify/base.py,sha256=QkNSNFX8KtNnRRm0r1wXsQa6TWMRKySn-3PT-XpY1mM,3377
|
|
4
|
+
android_notify/core.py,sha256=Per4HFwYwP-mxHJqnwcLlWXsbZsSeeAYN49MmFU2qVk,6113
|
|
5
|
+
android_notify/styles.py,sha256=QBkCY8ZO26FnS-jPtRLf9CKEEnnYylmH9enCa5CNDes,354
|
|
6
|
+
android_notify/sword.py,sha256=OzSaIlxegxStd5fy_RuFyOBCTBSJff2VOHpD5qqASfo,27851
|
|
7
|
+
android_notify-1.56.dist-info/METADATA,sha256=2UdPjO20-SVKnQ5Xh1I-cx0iwNRHrVT24UHd2lyKBXs,16568
|
|
8
|
+
android_notify-1.56.dist-info/WHEEL,sha256=jB7zZ3N9hIM9adW7qlTAyycLYW9npaWKLRzaoVcLKcM,91
|
|
9
|
+
android_notify-1.56.dist-info/top_level.txt,sha256=IR1ONMrRSRINZpWn2X0dL5gbWwWINsK7PW8Jy2p4fU8,15
|
|
10
|
+
android_notify-1.56.dist-info/RECORD,,
|
android_notify/facade.py
DELETED
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
"""Assists Notification Class with Args auto-complete: keeps sub class cleaner"""
|
|
2
|
-
from dataclasses import dataclass
|
|
3
|
-
|
|
4
|
-
@dataclass
|
|
5
|
-
class BaseNotification:
|
|
6
|
-
"""Encapsulator"""
|
|
7
|
-
|
|
8
|
-
# Basic options
|
|
9
|
-
title: str = ''
|
|
10
|
-
message: str = ''
|
|
11
|
-
style: str = 'simple'
|
|
12
|
-
|
|
13
|
-
# Style specfic attributes
|
|
14
|
-
big_picture_path: str = ''
|
|
15
|
-
large_icon_path: str = ''
|
|
16
|
-
progress_max_value: int = 100
|
|
17
|
-
progress_current_value: int = 0
|
|
18
|
-
body: str = ''
|
|
19
|
-
|
|
20
|
-
# For Nofitication Functions
|
|
21
|
-
identifer: str = ''
|
|
22
|
-
callback: object = None
|
|
23
|
-
|
|
24
|
-
# Advance Options
|
|
25
|
-
channel_name: str = 'Default Channel'
|
|
26
|
-
channel_id: str = 'default_channel'
|
|
27
|
-
silent: bool = False
|
|
28
|
-
app_icon: str = 'Defaults to package app icon'
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
android_notify/__init__.py,sha256=lcLjyfegXgU7cyGhfSphAOBipXwemrVkdYy3mcF6X5Y,172
|
|
2
|
-
android_notify/__main__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
3
|
-
android_notify/core.py,sha256=Per4HFwYwP-mxHJqnwcLlWXsbZsSeeAYN49MmFU2qVk,6113
|
|
4
|
-
android_notify/facade.py,sha256=A19qXM703aUeZux87SsM2BpDpXOpiC-kVdRS7yZy-5k,729
|
|
5
|
-
android_notify/styles.py,sha256=3olKruhAbTrk5OzuhDnk_Pgpv8XYk8dWFmr48Q9rQVk,352
|
|
6
|
-
android_notify/sword.py,sha256=BwjmVEXpFpFyknlrpOHY38qKm-t3UEnUHUPXLHrii_A,30886
|
|
7
|
-
android_notify-1.55.dist-info/METADATA,sha256=ebm_bvvLThI-l4EASaOfsaLRtGNgtGCcwOtWdeMwV2I,16392
|
|
8
|
-
android_notify-1.55.dist-info/WHEEL,sha256=jB7zZ3N9hIM9adW7qlTAyycLYW9npaWKLRzaoVcLKcM,91
|
|
9
|
-
android_notify-1.55.dist-info/top_level.txt,sha256=IR1ONMrRSRINZpWn2X0dL5gbWwWINsK7PW8Jy2p4fU8,15
|
|
10
|
-
android_notify-1.55.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|