android-notify 1.54.1__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/core.py +0 -1
- android_notify/styles.py +3 -3
- android_notify/sword.py +89 -142
- {android_notify-1.54.1.dist-info → android_notify-1.56.dist-info}/METADATA +12 -1
- android_notify-1.56.dist-info/RECORD +10 -0
- android_notify/facade.py +0 -28
- android_notify-1.54.1.dist-info/RECORD +0 -10
- {android_notify-1.54.1.dist-info → android_notify-1.56.dist-info}/WHEEL +0 -0
- {android_notify-1.54.1.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/core.py
CHANGED
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,63 +136,75 @@ 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
|
-
def updateProgressBar(self,current_value,message:str=''):
|
|
141
|
+
def updateProgressBar(self,current_value:int,message:str='',title:str=''):
|
|
161
142
|
"""Updates progress bar current value
|
|
162
143
|
|
|
163
144
|
Args:
|
|
164
|
-
current_value (
|
|
145
|
+
current_value (int): the value from progressbar current progress
|
|
165
146
|
message (str): defaults to last message
|
|
147
|
+
title (str): defaults to last title
|
|
166
148
|
|
|
167
|
-
NOTE: There is a 0.5sec delay
|
|
149
|
+
NOTE: There is a 0.5sec delay, if updating title,msg with progressbar frequenlty pass them in too to avoid update issues
|
|
168
150
|
"""
|
|
169
151
|
|
|
170
152
|
# Cancel any existing timer before setting a new one
|
|
171
153
|
if self.__update_timer:
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
def delayed_update():
|
|
154
|
+
self.__update_timer.cancel()
|
|
175
155
|
self.__update_timer = None
|
|
156
|
+
|
|
157
|
+
def delayed_update():
|
|
158
|
+
if self.__update_timer is None:
|
|
159
|
+
# Ensure we are not executing an old timer
|
|
160
|
+
return
|
|
176
161
|
if self.logs:
|
|
177
162
|
print(f'Progress Bar Update value: {current_value}')
|
|
178
163
|
|
|
179
164
|
self.progress_current_value = current_value
|
|
180
165
|
|
|
181
166
|
if not ON_ANDROID:
|
|
182
|
-
return
|
|
167
|
+
return
|
|
183
168
|
self.__builder.setProgress(self.progress_max_value, current_value, False)
|
|
184
169
|
if message:
|
|
185
170
|
self.updateMessage(message)
|
|
186
|
-
|
|
187
|
-
|
|
171
|
+
if title:
|
|
172
|
+
self.updateTitle(title)
|
|
173
|
+
self.__dispatchNotification()
|
|
174
|
+
self.__update_timer = None
|
|
175
|
+
|
|
188
176
|
|
|
189
177
|
# Start a new timer that runs after 0.5 seconds
|
|
190
178
|
self.__update_timer = threading.Timer(0.5, delayed_update)
|
|
191
179
|
self.__update_timer.start()
|
|
192
|
-
|
|
193
|
-
def removeProgressBar(self,message='',show_on_update=True) -> None:
|
|
180
|
+
|
|
181
|
+
def removeProgressBar(self,message='',show_on_update=True, title:str='') -> None:
|
|
194
182
|
"""Removes Progress Bar from Notification
|
|
195
183
|
|
|
196
184
|
Args:
|
|
197
185
|
message (str, optional): notification message. Defaults to 'last message'.
|
|
198
186
|
show_on_update (bool, optional): To show notification brifely when progressbar removed. Defaults to True.
|
|
187
|
+
title (str, optional): notification title. Defaults to 'last title'.
|
|
199
188
|
"""
|
|
200
189
|
if self.__update_timer:
|
|
201
190
|
self.__update_timer.cancel()
|
|
202
191
|
self.__update_timer = None
|
|
203
192
|
|
|
204
193
|
if self.logs:
|
|
205
|
-
|
|
194
|
+
msg = message or self.message
|
|
195
|
+
title_=title or self.title
|
|
196
|
+
print(f'removed progress bar with message: {msg} and title: {title_}')
|
|
206
197
|
|
|
207
198
|
if not ON_ANDROID:
|
|
208
199
|
return False
|
|
209
200
|
|
|
210
201
|
self.__builder.setOnlyAlertOnce(not show_on_update)
|
|
211
202
|
if message:
|
|
212
|
-
self.
|
|
203
|
+
self.updateMessage(message)
|
|
204
|
+
if title:
|
|
205
|
+
self.updateTitle(title)
|
|
213
206
|
self.__builder.setProgress(0, 0, False)
|
|
214
|
-
self.
|
|
207
|
+
self.__dispatchNotification()
|
|
215
208
|
return True
|
|
216
209
|
|
|
217
210
|
def send(self,silent:bool=False,persistent=False,close_on_click=True):
|
|
@@ -225,13 +218,18 @@ class Notification(BaseNotification):
|
|
|
225
218
|
self.silent=self.silent or silent
|
|
226
219
|
if ON_ANDROID:
|
|
227
220
|
self.__startNotificationBuild(persistent,close_on_click)
|
|
228
|
-
self.
|
|
229
|
-
|
|
221
|
+
self.__dispatchNotification()
|
|
222
|
+
if self.logs:
|
|
230
223
|
string_to_display=''
|
|
231
224
|
print("\n Sent Notification!!!")
|
|
232
225
|
for name,value in vars(self).items():
|
|
233
226
|
if value and name in ["title", "message", "style", "body", "large_icon_path", "big_picture_path", "progress_current_value", "progress_max_value", "channel_name"]:
|
|
234
|
-
|
|
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
|
+
|
|
235
233
|
string_to_display +="\n (Won't Print Logs When Complied,except if selected `Notification.logs=True`)"
|
|
236
234
|
print(string_to_display)
|
|
237
235
|
if DEV:
|
|
@@ -292,7 +290,7 @@ class Notification(BaseNotification):
|
|
|
292
290
|
"""
|
|
293
291
|
if ON_ANDROID:
|
|
294
292
|
self.__builder.mActions.clear()
|
|
295
|
-
self.
|
|
293
|
+
self.__dispatchNotification()
|
|
296
294
|
if self.logs:
|
|
297
295
|
print('Removed Notication Buttons')
|
|
298
296
|
|
|
@@ -331,60 +329,17 @@ class Notification(BaseNotification):
|
|
|
331
329
|
self.__buildImg(self.large_icon_path, NotificationStyles.LARGE_ICON)
|
|
332
330
|
|
|
333
331
|
elif style == NotificationStyles.PROGRESS:
|
|
334
|
-
self.__builder.setContentTitle(String(self.title))
|
|
335
|
-
self.__builder.setContentText(String(self.message))
|
|
336
332
|
self.__builder.setProgress(self.progress_max_value, self.progress_current_value, False)
|
|
337
333
|
|
|
338
334
|
if already_sent:
|
|
339
|
-
self.
|
|
335
|
+
self.__dispatchNotification()
|
|
340
336
|
|
|
341
337
|
return True
|
|
342
338
|
# elif style == 'custom':
|
|
343
339
|
# self.__builder = self.__doCustomStyle()
|
|
344
340
|
|
|
345
|
-
def
|
|
346
|
-
|
|
347
|
-
def checkInReference(inputted_keywords,accepteable_inputs,input_type):
|
|
348
|
-
def singularForm(plural_form):
|
|
349
|
-
return plural_form[:-1]
|
|
350
|
-
invalid_args= set(inputted_keywords) - set(accepteable_inputs)
|
|
351
|
-
if invalid_args:
|
|
352
|
-
suggestions=[]
|
|
353
|
-
for arg in invalid_args:
|
|
354
|
-
closest_match = difflib.get_close_matches(arg,accepteable_inputs,n=2,cutoff=0.6)
|
|
355
|
-
if closest_match:
|
|
356
|
-
suggestions.append(f"* '{arg}' Invalid -> Did you mean '{closest_match[0]}'? ") # pylint: disable=C0301
|
|
357
|
-
else:
|
|
358
|
-
suggestions.append(f"* {arg} is not a valid {singularForm(input_type)}.")
|
|
359
|
-
suggestion_text='\n'.join(suggestions)
|
|
360
|
-
hint_msg=singularForm(input_type) if len(invalid_args) < 2 else input_type
|
|
361
|
-
|
|
362
|
-
raise ValueError(f"Invalid {hint_msg} provided: \n\t{suggestion_text}\n\t* list of valid {input_type}: [{', '.join(accepteable_inputs)}]")
|
|
363
|
-
|
|
364
|
-
allowed_keywords=self.defaults.keys()
|
|
365
|
-
inputted_keywords_=inputted_kwargs.keys()
|
|
366
|
-
checkInReference(inputted_keywords_,allowed_keywords,'arguments')
|
|
367
|
-
|
|
368
|
-
# Validate style values
|
|
369
|
-
if 'style' in inputted_keywords_ and inputted_kwargs['style'] not in self.style_values:
|
|
370
|
-
checkInReference([inputted_kwargs['style']],self.style_values,'values')
|
|
371
|
-
|
|
372
|
-
def __setArgs(self,options_dict:dict):
|
|
373
|
-
non_string_keys=['progress_max_value','progress_current_value','callback','logs']
|
|
374
|
-
|
|
375
|
-
for key,value in options_dict.items():
|
|
376
|
-
if key not in non_string_keys: # Fixing Types
|
|
377
|
-
value = str(value)
|
|
378
|
-
if key == 'channel_name' and value.strip():
|
|
379
|
-
setattr(self,key, value[:40])
|
|
380
|
-
elif key == 'channel_id' and value.strip(): # If user input's a channel id (i format properly)
|
|
381
|
-
setattr(self,key, self.__generate_channel_id(value))
|
|
382
|
-
else:
|
|
383
|
-
setattr(self,key, value if value else self.defaults[key])
|
|
384
|
-
|
|
385
|
-
if "channel_id" not in options_dict and 'channel_name' in options_dict: # if User doesn't input channel id but inputs channel_name
|
|
386
|
-
setattr(self,'channel_id', self.__generate_channel_id(options_dict['channel_name']))
|
|
387
|
-
|
|
341
|
+
def __dispatchNotification(self):
|
|
342
|
+
self.notification_manager.notify(self.__id, self.__builder.build())
|
|
388
343
|
def __startNotificationBuild(self,persistent,close_on_click):
|
|
389
344
|
self.__createBasicNotification(persistent,close_on_click)
|
|
390
345
|
if self.style not in ['simple','']:
|
|
@@ -415,7 +370,7 @@ class Notification(BaseNotification):
|
|
|
415
370
|
self.__builder.setOngoing(persistent)
|
|
416
371
|
self.__builder.setAutoCancel(close_on_click)
|
|
417
372
|
self.__addIntentToOpenApp()
|
|
418
|
-
|
|
373
|
+
|
|
419
374
|
# def __doCustomStyle(self):
|
|
420
375
|
# # TODO Will implement when needed
|
|
421
376
|
# return self.__builder
|
|
@@ -424,7 +379,7 @@ class Notification(BaseNotification):
|
|
|
424
379
|
self.__setIconFromBitmap(self.app_icon)
|
|
425
380
|
else:
|
|
426
381
|
self.__builder.setSmallIcon(context.getApplicationInfo().icon)
|
|
427
|
-
|
|
382
|
+
|
|
428
383
|
def __buildImg(self, user_img,img_style):
|
|
429
384
|
if user_img.startswith('http://') or user_img.startswith('https://'):
|
|
430
385
|
def callback(bitmap):
|
|
@@ -438,7 +393,7 @@ class Notification(BaseNotification):
|
|
|
438
393
|
bitmap = self.__getImgFromPath(user_img)
|
|
439
394
|
if bitmap:
|
|
440
395
|
self.__applyNotificationImage(bitmap,img_style)
|
|
441
|
-
|
|
396
|
+
|
|
442
397
|
def __setIconFromBitmap(self,img_path):
|
|
443
398
|
"""Path can be link or relative path"""
|
|
444
399
|
if img_path.startswith('http://') or img_path.startswith('https://'):
|
|
@@ -458,8 +413,7 @@ class Notification(BaseNotification):
|
|
|
458
413
|
if self.logs:
|
|
459
414
|
print('Failed getting img for custom notification icon defaulting to app icon')
|
|
460
415
|
self.__builder.setSmallIcon(context.getApplicationInfo().icon)
|
|
461
|
-
|
|
462
|
-
|
|
416
|
+
|
|
463
417
|
def __getImgFromPath(self, relative_path):
|
|
464
418
|
app_folder=os.path.join(app_storage_path(),'app') # pylint: disable=possibly-used-before-assignment
|
|
465
419
|
output_path = os.path.join(app_folder, relative_path)
|
|
@@ -499,25 +453,6 @@ class Notification(BaseNotification):
|
|
|
499
453
|
print('Error Type ',e)
|
|
500
454
|
print('Failed to get Bitmap from URL ',traceback.format_exc())
|
|
501
455
|
|
|
502
|
-
# def __getImgFromURL(self,url,img_style):
|
|
503
|
-
# if self.logs:
|
|
504
|
-
# print("getting image from URL---")
|
|
505
|
-
# try:
|
|
506
|
-
# URL = autoclass('java.net.URL')
|
|
507
|
-
# url = URL(url)
|
|
508
|
-
# connection = url.openConnection()
|
|
509
|
-
# connection.connect()
|
|
510
|
-
# input_stream = connection.getInputStream()
|
|
511
|
-
# bitmap = BitmapFactory.decodeStream(input_stream)
|
|
512
|
-
# input_stream.close()
|
|
513
|
-
# if bitmap:
|
|
514
|
-
# self.__applyNotificationImage(bitmap,img_style)
|
|
515
|
-
|
|
516
|
-
# except Exception as e:
|
|
517
|
-
# # TODO get type of JAVA Error
|
|
518
|
-
# print('Error Type ',e)
|
|
519
|
-
# print('Failed to get Img from URL ',traceback.format_exc())
|
|
520
|
-
|
|
521
456
|
@run_on_ui_thread
|
|
522
457
|
def __applyNotificationImage(self,bitmap,img_style):
|
|
523
458
|
if self.logs:
|
|
@@ -528,7 +463,7 @@ class Notification(BaseNotification):
|
|
|
528
463
|
self.__builder.setStyle(big_picture_style)
|
|
529
464
|
elif img_style == NotificationStyles.LARGE_ICON:
|
|
530
465
|
self.__builder.setLargeIcon(bitmap)
|
|
531
|
-
self.
|
|
466
|
+
self.__dispatchNotification()
|
|
532
467
|
if self.logs:
|
|
533
468
|
print('Done adding image to notification-------')
|
|
534
469
|
except Exception as e:
|
|
@@ -553,24 +488,6 @@ class Notification(BaseNotification):
|
|
|
553
488
|
if not all(check_permission(p) for p in permissions):
|
|
554
489
|
request_permissions(permissions,on_permissions_result) # pylint: disable=E0606
|
|
555
490
|
|
|
556
|
-
def __generate_channel_id(self,channel_name: str) -> str:
|
|
557
|
-
"""
|
|
558
|
-
Generate a readable and consistent channel ID from a channel name.
|
|
559
|
-
|
|
560
|
-
Args:
|
|
561
|
-
channel_name (str): The name of the notification channel.
|
|
562
|
-
|
|
563
|
-
Returns:
|
|
564
|
-
str: A sanitized channel ID.
|
|
565
|
-
"""
|
|
566
|
-
# Normalize the channel name
|
|
567
|
-
channel_id = channel_name.strip().lower()
|
|
568
|
-
# Replace spaces and special characters with underscores
|
|
569
|
-
channel_id = re.sub(r'[^a-z0-9]+', '_', channel_id)
|
|
570
|
-
# Remove leading/trailing underscores
|
|
571
|
-
channel_id = channel_id.strip('_')
|
|
572
|
-
return channel_id[:50]
|
|
573
|
-
|
|
574
491
|
def __addIntentToOpenApp(self):
|
|
575
492
|
intent = Intent(context, PythonActivity)
|
|
576
493
|
action = str(self.identifer) or f"ACTION_{self.__id}"
|
|
@@ -597,6 +514,36 @@ class Notification(BaseNotification):
|
|
|
597
514
|
self.button_ids.append(btn_id)
|
|
598
515
|
return btn_id
|
|
599
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]
|
|
600
547
|
|
|
601
548
|
class NotificationHandler:
|
|
602
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,12 +464,22 @@ 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
|
|
|
473
|
+
if updating title,msg with progressbar frequenlty pass them in too to avoid update issues.
|
|
474
|
+
According to android docs updates shouldn't be too frequent.
|
|
475
|
+
|
|
476
|
+
`updateProgressBar` has a built-in delay of 0.5 secs
|
|
477
|
+
|
|
469
478
|
args
|
|
470
479
|
|
|
471
480
|
- current_value (str): the value from progressbar current progress
|
|
472
481
|
- message (str,optional): defaults to last message
|
|
482
|
+
- title (str,optional): defaults to last title
|
|
473
483
|
|
|
474
484
|
### Instance.removeProgressBar
|
|
475
485
|
|
|
@@ -479,6 +489,7 @@ args
|
|
|
479
489
|
|
|
480
490
|
- message (str, optional): notification message, Defaults to 'last message'.
|
|
481
491
|
- show_on_update (bool, optional): To show notification brifely when progressbar removed. Defaults to True.
|
|
492
|
+
- title (str, optional): notification title, Defaults to 'last title'.
|
|
482
493
|
|
|
483
494
|
### Instance.addNotificationStyle
|
|
484
495
|
|
|
@@ -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=sIFx2o58CDGkM-ktf6sGm9INxQ21BIyk4dW8Q96kmq4,6176
|
|
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=fCLUpf5OQkY8xDgYAoarv4DMtUwfLL0b_TMf444fMP8,30272
|
|
7
|
-
android_notify-1.54.1.dist-info/METADATA,sha256=V89eNy83lAkpK6XV9YSUlrvk9qm8_YhQaMTi1N67K3k,16069
|
|
8
|
-
android_notify-1.54.1.dist-info/WHEEL,sha256=jB7zZ3N9hIM9adW7qlTAyycLYW9npaWKLRzaoVcLKcM,91
|
|
9
|
-
android_notify-1.54.1.dist-info/top_level.txt,sha256=IR1ONMrRSRINZpWn2X0dL5gbWwWINsK7PW8Jy2p4fU8,15
|
|
10
|
-
android_notify-1.54.1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|