android-notify 1.58__py3-none-any.whl → 1.59__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.
- android_notify/{types_idea.py → an_types.py} +20 -1
- android_notify/base.py +10 -3
- android_notify/sword.py +394 -180
- android_notify-1.59.dist-info/METADATA +117 -0
- android_notify-1.59.dist-info/RECORD +11 -0
- {android_notify-1.58.dist-info → android_notify-1.59.dist-info}/WHEEL +1 -1
- android_notify-1.58.dist-info/METADATA +0 -575
- android_notify-1.58.dist-info/RECORD +0 -11
- {android_notify-1.58.dist-info → android_notify-1.59.dist-info}/top_level.txt +0 -0
android_notify/sword.py
CHANGED
|
@@ -2,34 +2,40 @@
|
|
|
2
2
|
import traceback
|
|
3
3
|
import os,re
|
|
4
4
|
import threading
|
|
5
|
-
|
|
5
|
+
from .an_types import Importance
|
|
6
6
|
from .styles import NotificationStyles
|
|
7
7
|
from .base import BaseNotification
|
|
8
8
|
DEV=0
|
|
9
9
|
ON_ANDROID = False
|
|
10
10
|
|
|
11
|
+
# noinspection PyBroadException
|
|
11
12
|
try:
|
|
12
13
|
# Android Imports
|
|
13
|
-
from jnius import autoclass,cast
|
|
14
|
-
from android import activity
|
|
15
|
-
from android.config import ACTIVITY_CLASS_NAME
|
|
16
|
-
from android.runnable import run_on_ui_thread
|
|
14
|
+
from jnius import autoclass,cast
|
|
15
|
+
from android import activity
|
|
16
|
+
from android.config import ACTIVITY_CLASS_NAME
|
|
17
|
+
from android.runnable import run_on_ui_thread
|
|
17
18
|
|
|
18
|
-
# Get the required Java classes
|
|
19
|
+
# Get the required Java classes needs to on android to import
|
|
19
20
|
Bundle = autoclass('android.os.Bundle')
|
|
20
21
|
PythonActivity = autoclass('org.kivy.android.PythonActivity')
|
|
21
22
|
String = autoclass('java.lang.String')
|
|
22
23
|
Intent = autoclass('android.content.Intent')
|
|
23
24
|
PendingIntent = autoclass('android.app.PendingIntent')
|
|
24
|
-
context = PythonActivity.mActivity
|
|
25
|
+
context = PythonActivity.mActivity
|
|
25
26
|
BitmapFactory = autoclass('android.graphics.BitmapFactory')
|
|
26
27
|
BuildVersion = autoclass('android.os.Build$VERSION')
|
|
28
|
+
VersionCodes = autoclass('android.os.Build$VERSION_CODES')
|
|
27
29
|
NotificationManager = autoclass('android.app.NotificationManager')
|
|
28
30
|
NotificationChannel = autoclass('android.app.NotificationChannel')
|
|
29
31
|
IconCompat = autoclass('androidx.core.graphics.drawable.IconCompat')
|
|
30
32
|
ON_ANDROID = True
|
|
31
|
-
except Exception as e
|
|
32
|
-
|
|
33
|
+
except Exception as e:
|
|
34
|
+
if e.name != 'android':
|
|
35
|
+
print('Exception: ',e)
|
|
36
|
+
print(traceback.format_exc())
|
|
37
|
+
|
|
38
|
+
MESSAGE='This Package Only Runs on Android !!! ---> Check "https://github.com/Fector101/android_notify/" to see design patterns and more info.'
|
|
33
39
|
# from .types_idea import *
|
|
34
40
|
# print(MESSAGE) Already Printing in core.py
|
|
35
41
|
|
|
@@ -43,18 +49,18 @@ except Exception as e:# pylint: disable=W0718
|
|
|
43
49
|
|
|
44
50
|
if ON_ANDROID:
|
|
45
51
|
try:
|
|
46
|
-
from android.permissions import request_permissions, Permission,check_permission
|
|
47
|
-
from android.storage import app_storage_path
|
|
52
|
+
from android.permissions import request_permissions, Permission,check_permission
|
|
53
|
+
from android.storage import app_storage_path
|
|
48
54
|
|
|
49
55
|
NotificationManagerCompat = autoclass('androidx.core.app.NotificationManagerCompat')
|
|
50
56
|
NotificationCompat = autoclass('androidx.core.app.NotificationCompat')
|
|
51
57
|
|
|
52
58
|
# Notification Design
|
|
53
|
-
NotificationCompatBuilder = autoclass('androidx.core.app.NotificationCompat$Builder')
|
|
54
|
-
NotificationCompatBigTextStyle = autoclass('androidx.core.app.NotificationCompat$BigTextStyle')
|
|
55
|
-
NotificationCompatBigPictureStyle = autoclass('androidx.core.app.NotificationCompat$BigPictureStyle')
|
|
59
|
+
NotificationCompatBuilder = autoclass('androidx.core.app.NotificationCompat$Builder')
|
|
60
|
+
NotificationCompatBigTextStyle = autoclass('androidx.core.app.NotificationCompat$BigTextStyle')
|
|
61
|
+
NotificationCompatBigPictureStyle = autoclass('androidx.core.app.NotificationCompat$BigPictureStyle')
|
|
56
62
|
NotificationCompatInboxStyle = autoclass('androidx.core.app.NotificationCompat$InboxStyle')
|
|
57
|
-
except Exception as e
|
|
63
|
+
except Exception as e:
|
|
58
64
|
print(e)
|
|
59
65
|
print("""
|
|
60
66
|
Dependency Error: Add the following in buildozer.spec:
|
|
@@ -74,11 +80,12 @@ class Notification(BaseNotification):
|
|
|
74
80
|
both_imgs == using lager icon and big picture
|
|
75
81
|
:param big_picture_path: Relative Path to the image resource.
|
|
76
82
|
:param large_icon_path: Relative Path to the image resource.
|
|
77
|
-
:param progress_current_value:
|
|
78
|
-
:param progress_max_value:
|
|
79
|
-
:param body: large text For `big_Text` style, while `message` acts as
|
|
83
|
+
:param progress_current_value: integer To set progress bar current value.
|
|
84
|
+
:param progress_max_value: integer To set Max range for progress bar.
|
|
85
|
+
:param body: large text For `big_Text` style, while `message` acts as subtitle.
|
|
80
86
|
---
|
|
81
87
|
(Advance Options)
|
|
88
|
+
:param id: Pass in Old 'id' to use old instance
|
|
82
89
|
:param callback: Function for notification Click.
|
|
83
90
|
:param channel_name: - str Defaults to "Default Channel"
|
|
84
91
|
:param channel_id: - str Defaults to "default_channel"
|
|
@@ -86,17 +93,19 @@ class Notification(BaseNotification):
|
|
|
86
93
|
(Options during Dev On PC)
|
|
87
94
|
:param logs: - Bool Defaults to True
|
|
88
95
|
"""
|
|
89
|
-
|
|
96
|
+
|
|
97
|
+
notification_ids = [0]
|
|
90
98
|
button_ids=[0]
|
|
91
99
|
btns_box={}
|
|
92
100
|
main_functions={}
|
|
93
101
|
|
|
94
102
|
# During Development (When running on PC)
|
|
95
103
|
BaseNotification.logs=not ON_ANDROID
|
|
96
|
-
def __init__(self,**kwargs):
|
|
104
|
+
def __init__(self,**kwargs): #@dataclass already does work
|
|
97
105
|
super().__init__(**kwargs)
|
|
98
106
|
|
|
99
|
-
self.__id = self.
|
|
107
|
+
self.__id = self.id or self.__get_unique_id() # Different use from self.name all notifications require `integers` id's not `strings`
|
|
108
|
+
self.id = self.__id # To use same Notification in different instances
|
|
100
109
|
|
|
101
110
|
# To Track progressbar last update (According to Android Docs Don't update bar to often, I also faced so issues when doing that)
|
|
102
111
|
self.__update_timer = None
|
|
@@ -104,19 +113,151 @@ class Notification(BaseNotification):
|
|
|
104
113
|
self.__progress_bar_title = ''
|
|
105
114
|
self.__cooldown = 0
|
|
106
115
|
|
|
107
|
-
self.
|
|
116
|
+
self.__built_parameter_filled=False
|
|
117
|
+
self.__using_set_priority_method=False
|
|
118
|
+
|
|
119
|
+
self.__format_channel(self.channel_name, self.channel_id)
|
|
108
120
|
if not ON_ANDROID:
|
|
109
121
|
return
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
self.
|
|
122
|
+
|
|
123
|
+
NotificationHandler.asks_permission()
|
|
124
|
+
notification_service = context.getSystemService(context.NOTIFICATION_SERVICE)
|
|
125
|
+
self.notification_manager = cast(NotificationManager, notification_service)
|
|
126
|
+
self.__builder = NotificationCompatBuilder(context, self.channel_id)
|
|
127
|
+
|
|
128
|
+
def cancel(self,_id=0):
|
|
129
|
+
"""
|
|
130
|
+
Removes a Notification instance from tray
|
|
131
|
+
:param _id: not required uses Notification instance id as default
|
|
132
|
+
"""
|
|
133
|
+
if ON_ANDROID:
|
|
134
|
+
self.notification_manager.cancel(_id or self.__id)
|
|
135
|
+
if self.logs:
|
|
136
|
+
print('Removed Notification.')
|
|
137
|
+
|
|
138
|
+
@classmethod
|
|
139
|
+
def cancelAll(cls):
|
|
140
|
+
"""
|
|
141
|
+
Removes all app Notifications from tray
|
|
142
|
+
"""
|
|
143
|
+
if ON_ANDROID:
|
|
144
|
+
cls.__return_notification_manger().cancelAll()
|
|
145
|
+
if cls.logs:
|
|
146
|
+
print('Removed All Notifications.')
|
|
147
|
+
|
|
148
|
+
@classmethod
|
|
149
|
+
def createChannel(cls, id, name:str, description='',importance:Importance='urgent'):
|
|
150
|
+
"""
|
|
151
|
+
Creates a user visible toggle button for specific notifications, Required For Android 8.0+
|
|
152
|
+
:param id: Used to send other notifications later through same channel.
|
|
153
|
+
:param name: user-visible channel name.
|
|
154
|
+
:param description: user-visible detail about channel (Not required defaults to empty str).
|
|
155
|
+
:param importance: ['urgent', 'high', 'medium', 'low', 'none'] defaults to 'urgent' i.e. makes a sound and shows briefly
|
|
156
|
+
:return: boolean if channel created
|
|
157
|
+
"""
|
|
158
|
+
|
|
159
|
+
if not ON_ANDROID:
|
|
160
|
+
return False
|
|
161
|
+
|
|
162
|
+
notification_manager=cls.__return_notification_manger()
|
|
163
|
+
android_importance_value = cls.__get_android_importance(importance)
|
|
164
|
+
|
|
165
|
+
if BuildVersion.SDK_INT >= 26 and notification_manager.getNotificationChannel(id) is None:
|
|
166
|
+
channel = NotificationChannel(id, name, android_importance_value)
|
|
167
|
+
if description:
|
|
168
|
+
channel.setDescription(description)
|
|
169
|
+
notification_manager.createNotificationChannel(channel)
|
|
170
|
+
return True
|
|
171
|
+
return False
|
|
172
|
+
|
|
173
|
+
@classmethod
|
|
174
|
+
def deleteChannel(cls, channel_id):
|
|
175
|
+
"""Delete a Channel Matching channel_id"""
|
|
176
|
+
if not ON_ANDROID:
|
|
177
|
+
return None
|
|
178
|
+
|
|
179
|
+
cls.__return_notification_manger().deleteNotificationChannel(channel_id)
|
|
180
|
+
|
|
181
|
+
@classmethod
|
|
182
|
+
def deleteAllChannel(cls):
|
|
183
|
+
"""Deletes all notification channel
|
|
184
|
+
:returns amount deleted
|
|
185
|
+
"""
|
|
186
|
+
|
|
187
|
+
amount = 0
|
|
188
|
+
if not ON_ANDROID:
|
|
189
|
+
return amount
|
|
190
|
+
|
|
191
|
+
notification_manager = cls.__return_notification_manger()
|
|
192
|
+
channels = cls.getChannels()
|
|
193
|
+
for index in range(channels.size()):
|
|
194
|
+
amount += 1
|
|
195
|
+
channel = channels.get(index)
|
|
196
|
+
channel_id = channel.getId()
|
|
197
|
+
notification_manager.deleteNotificationChannel(channel_id)
|
|
198
|
+
return amount
|
|
199
|
+
|
|
200
|
+
def refresh(self):
|
|
201
|
+
"""TO apply new components on notification"""
|
|
202
|
+
if self.__built_parameter_filled:
|
|
203
|
+
# Don't dispatch before filling required values `self.__create_basic_notification`
|
|
204
|
+
# We generally shouldn't dispatch till user call .send()
|
|
205
|
+
self.__dispatch_notification()
|
|
206
|
+
|
|
207
|
+
def setBigPicture(self,path):
|
|
208
|
+
"""
|
|
209
|
+
set a Big Picture at the bottom
|
|
210
|
+
:param path: can be `Relative Path` or `URL`
|
|
211
|
+
:return:
|
|
212
|
+
"""
|
|
213
|
+
if ON_ANDROID:
|
|
214
|
+
self.__build_img(path, NotificationStyles.BIG_PICTURE)
|
|
215
|
+
elif self.logs:
|
|
216
|
+
# When on android there are other logs
|
|
217
|
+
print('Done setting big picture')
|
|
218
|
+
|
|
219
|
+
def setSmallIcon(self,path):
|
|
220
|
+
"""
|
|
221
|
+
sets small icon to the top right
|
|
222
|
+
:param path: can be `Relative Path` or `URL`
|
|
223
|
+
:return:
|
|
224
|
+
"""
|
|
225
|
+
if ON_ANDROID:
|
|
226
|
+
self.__insert_app_icon(path)
|
|
227
|
+
elif self.logs:
|
|
228
|
+
# When on android there are other logs
|
|
229
|
+
print('Done setting small icon')
|
|
230
|
+
|
|
231
|
+
def setLargeIcon(self,path):
|
|
232
|
+
"""
|
|
233
|
+
sets Large icon to the right
|
|
234
|
+
:param path: can be `Relative Path` or `URL`
|
|
235
|
+
:return:
|
|
236
|
+
"""
|
|
237
|
+
if ON_ANDROID:
|
|
238
|
+
self.__build_img(path, NotificationStyles.LARGE_ICON)
|
|
239
|
+
elif self.logs:
|
|
240
|
+
#When on android there are other logs
|
|
241
|
+
print('Done setting large icon')
|
|
242
|
+
|
|
243
|
+
def setBigText(self,body):
|
|
244
|
+
if ON_ANDROID:
|
|
245
|
+
big_text_style = NotificationCompatBigTextStyle()
|
|
246
|
+
big_text_style.bigText(str(body))
|
|
247
|
+
self.__builder.setStyle(big_text_style)
|
|
248
|
+
elif self.logs:
|
|
249
|
+
# When on android there are other logs
|
|
250
|
+
print('Done setting big text')
|
|
251
|
+
|
|
114
252
|
def showInfiniteProgressBar(self):
|
|
115
253
|
"""Displays an (Infinite) progress Bar in Notification, that continues loading indefinitely.
|
|
116
254
|
Can be Removed By `removeProgressBar` Method
|
|
117
255
|
"""
|
|
118
|
-
self.
|
|
119
|
-
|
|
256
|
+
if self.logs:
|
|
257
|
+
print('Showing infinite progressbar')
|
|
258
|
+
if ON_ANDROID:
|
|
259
|
+
self.__builder.setProgress(0,0, True)
|
|
260
|
+
self.refresh()
|
|
120
261
|
|
|
121
262
|
def updateTitle(self,new_title):
|
|
122
263
|
"""Changes Old Title
|
|
@@ -129,7 +270,7 @@ class Notification(BaseNotification):
|
|
|
129
270
|
print(f'new notification title: {self.title}')
|
|
130
271
|
if ON_ANDROID:
|
|
131
272
|
self.__builder.setContentTitle(String(self.title))
|
|
132
|
-
self.
|
|
273
|
+
self.refresh()
|
|
133
274
|
|
|
134
275
|
def updateMessage(self,new_message):
|
|
135
276
|
"""Changes Old Message
|
|
@@ -142,7 +283,7 @@ class Notification(BaseNotification):
|
|
|
142
283
|
print(f'new notification message: {self.message}')
|
|
143
284
|
if ON_ANDROID:
|
|
144
285
|
self.__builder.setContentText(String(self.message))
|
|
145
|
-
self.
|
|
286
|
+
self.refresh()
|
|
146
287
|
|
|
147
288
|
def updateProgressBar(self,current_value:int,message:str='',title:str='',cooldown=0.5):
|
|
148
289
|
"""Updates progress bar current value
|
|
@@ -151,7 +292,7 @@ class Notification(BaseNotification):
|
|
|
151
292
|
current_value (int): the value from progressbar current progress
|
|
152
293
|
message (str): defaults to last message
|
|
153
294
|
title (str): defaults to last title
|
|
154
|
-
cooldown (float, optional):
|
|
295
|
+
cooldown (float, optional): Little Time to Wait before change actually reflects, to avoid android Ignoring Change, Defaults to 0.5secs
|
|
155
296
|
|
|
156
297
|
NOTE: There is a 0.5sec delay for value change, if updating title,msg with progressbar frequently pass them in too to avoid update issues
|
|
157
298
|
"""
|
|
@@ -187,7 +328,7 @@ class Notification(BaseNotification):
|
|
|
187
328
|
if self.__progress_bar_title:
|
|
188
329
|
self.updateTitle(self.__progress_bar_title)
|
|
189
330
|
|
|
190
|
-
self.
|
|
331
|
+
self.refresh()
|
|
191
332
|
self.__update_timer = None
|
|
192
333
|
|
|
193
334
|
|
|
@@ -204,7 +345,7 @@ class Notification(BaseNotification):
|
|
|
204
345
|
message (str, optional): notification message. Defaults to 'last message'.
|
|
205
346
|
show_on_update (bool, optional): To show notification briefly when progressbar removed. Defaults to True.
|
|
206
347
|
title (str, optional): notification title. Defaults to 'last title'.
|
|
207
|
-
cooldown (float, optional):
|
|
348
|
+
cooldown (float, optional): Little Time to Wait before change actually reflects, to avoid android Ignoring Change, Defaults to 0.5secs
|
|
208
349
|
|
|
209
350
|
In-Built Delay of 0.5sec According to Android Docs Don't Update Progressbar too Frequently
|
|
210
351
|
"""
|
|
@@ -220,7 +361,6 @@ class Notification(BaseNotification):
|
|
|
220
361
|
self.__update_timer = None
|
|
221
362
|
|
|
222
363
|
|
|
223
|
-
|
|
224
364
|
def delayed_update():
|
|
225
365
|
if self.logs:
|
|
226
366
|
msg = message or self.message
|
|
@@ -236,11 +376,23 @@ class Notification(BaseNotification):
|
|
|
236
376
|
self.updateTitle(title)
|
|
237
377
|
self.__builder.setOnlyAlertOnce(not show_on_update)
|
|
238
378
|
self.__builder.setProgress(0, 0, False)
|
|
239
|
-
self.
|
|
379
|
+
self.refresh()
|
|
240
380
|
|
|
241
381
|
# Incase `self.updateProgressBar delayed_update` is called right before this method, so android doesn't bounce update
|
|
242
382
|
threading.Timer(cooldown, delayed_update).start()
|
|
243
383
|
|
|
384
|
+
def setPriority(self,importance:Importance):
|
|
385
|
+
"""
|
|
386
|
+
For devices less than android 8
|
|
387
|
+
:param importance: ['urgent', 'high', 'medium', 'low', 'none'] defaults to 'urgent' i.e. makes a sound and shows briefly
|
|
388
|
+
:return:
|
|
389
|
+
"""
|
|
390
|
+
self.__using_set_priority_method=True
|
|
391
|
+
if ON_ANDROID:
|
|
392
|
+
android_importance_value = self.__get_android_importance(importance)
|
|
393
|
+
if not isinstance(android_importance_value, str): # Can be an empty str if importance='none'
|
|
394
|
+
self.__builder.setPriority(android_importance_value)
|
|
395
|
+
|
|
244
396
|
def send(self,silent:bool=False,persistent=False,close_on_click=True):
|
|
245
397
|
"""Sends notification
|
|
246
398
|
|
|
@@ -249,26 +401,29 @@ class Notification(BaseNotification):
|
|
|
249
401
|
persistent (bool): True To not remove Notification When User hits clears All notifications button
|
|
250
402
|
close_on_click (bool): True if you want Notification to be removed when clicked
|
|
251
403
|
"""
|
|
252
|
-
self.silent=
|
|
404
|
+
self.silent= silent or self.silent
|
|
253
405
|
if ON_ANDROID:
|
|
254
|
-
self.
|
|
255
|
-
self.
|
|
406
|
+
self.__start_notification_build(persistent, close_on_click)
|
|
407
|
+
self.__dispatch_notification()
|
|
408
|
+
|
|
256
409
|
if self.logs:
|
|
257
410
|
string_to_display=''
|
|
258
411
|
print("\n Sent Notification!!!")
|
|
259
412
|
for name,value in vars(self).items():
|
|
260
|
-
if value and name in ["title", "message", "style", "body", "large_icon_path", "big_picture_path", "progress_current_value", "progress_max_value", "channel_name"]:
|
|
413
|
+
if value and name in ["title", "message", "style", "body", "large_icon_path", "big_picture_path", "progress_current_value", "progress_max_value", "channel_name",'body','name']:
|
|
261
414
|
if name == "progress_max_value":
|
|
262
415
|
if self.style == NotificationStyles.PROGRESS:
|
|
263
416
|
string_to_display += f'\n {name}: {value}'
|
|
417
|
+
elif name == "body":
|
|
418
|
+
if self.style == NotificationStyles.BIG_TEXT:
|
|
419
|
+
string_to_display += f'\n {name}: {value}'
|
|
264
420
|
else:
|
|
265
421
|
string_to_display += f'\n {name}: {value}'
|
|
266
422
|
|
|
267
423
|
string_to_display +="\n (Won't Print Logs When Complied,except if selected `Notification.logs=True`)"
|
|
268
424
|
print(string_to_display)
|
|
269
425
|
if DEV:
|
|
270
|
-
print(f'channel_name: {self.channel_name}, Channel ID: {self.channel_id},
|
|
271
|
-
print('Can\'t Send Package Only Runs on Android !!! ---> Check "https://github.com/Fector101/android_notify/" for Documentation.\n' if DEV else '\n') # pylint: disable=C0301
|
|
426
|
+
print(f'channel_name: {self.channel_name}, Channel ID: {self.channel_id}, __id: {self.__id}')
|
|
272
427
|
|
|
273
428
|
def addButton(self, text:str,on_release):
|
|
274
429
|
"""For adding action buttons
|
|
@@ -283,7 +438,7 @@ class Notification(BaseNotification):
|
|
|
283
438
|
if not ON_ANDROID:
|
|
284
439
|
return
|
|
285
440
|
|
|
286
|
-
btn_id= self.
|
|
441
|
+
btn_id= self.__get_id_for_button()
|
|
287
442
|
action = f"BTN_ACTION_{btn_id}"
|
|
288
443
|
|
|
289
444
|
action_intent = Intent(context, PythonActivity)
|
|
@@ -325,9 +480,9 @@ class Notification(BaseNotification):
|
|
|
325
480
|
"""
|
|
326
481
|
if ON_ANDROID:
|
|
327
482
|
self.__builder.mActions.clear()
|
|
328
|
-
self.
|
|
483
|
+
self.refresh()
|
|
329
484
|
if self.logs:
|
|
330
|
-
print('Removed
|
|
485
|
+
print('Removed Notification Buttons')
|
|
331
486
|
|
|
332
487
|
@run_on_ui_thread
|
|
333
488
|
def addNotificationStyle(self,style:str,already_sent=False):
|
|
@@ -340,108 +495,103 @@ class Notification(BaseNotification):
|
|
|
340
495
|
"""
|
|
341
496
|
|
|
342
497
|
if not ON_ANDROID:
|
|
343
|
-
# TODO for logs when not on android and style related to imgs
|
|
498
|
+
# TODO for logs when not on android and style related to imgs extract app path from buildozer.spec and print
|
|
344
499
|
return False
|
|
345
500
|
|
|
346
501
|
if style == NotificationStyles.BIG_TEXT:
|
|
347
|
-
|
|
348
|
-
big_text_style.bigText(str(self.body))
|
|
349
|
-
self.__builder.setStyle(big_text_style)
|
|
502
|
+
self.setBigText(self.body)
|
|
350
503
|
|
|
351
504
|
elif style == NotificationStyles.INBOX:
|
|
352
|
-
inbox_style = NotificationCompatInboxStyle()
|
|
505
|
+
inbox_style = NotificationCompatInboxStyle()
|
|
353
506
|
for line in self.message.split("\n"):
|
|
354
507
|
inbox_style.addLine(str(line))
|
|
355
508
|
self.__builder.setStyle(inbox_style)
|
|
356
509
|
|
|
357
510
|
elif (style == NotificationStyles.LARGE_ICON and self.large_icon_path) or (style == NotificationStyles.BIG_PICTURE and self.big_picture_path):
|
|
358
511
|
img = self.large_icon_path if style == NotificationStyles.LARGE_ICON else self.big_picture_path
|
|
359
|
-
self.
|
|
512
|
+
self.__build_img(img, style)
|
|
360
513
|
|
|
361
514
|
elif style == NotificationStyles.BOTH_IMGS and (self.big_picture_path or self.large_icon_path):
|
|
362
515
|
if self.big_picture_path:
|
|
363
|
-
self.
|
|
516
|
+
self.setBigPicture(self.big_picture_path)
|
|
364
517
|
if self.large_icon_path:
|
|
365
|
-
self.
|
|
518
|
+
self.setLargeIcon(self.large_icon_path)
|
|
366
519
|
|
|
367
520
|
elif style == NotificationStyles.PROGRESS:
|
|
368
521
|
self.__builder.setProgress(self.progress_max_value, self.progress_current_value, False)
|
|
369
522
|
|
|
370
523
|
if already_sent:
|
|
371
|
-
self.
|
|
524
|
+
self.refresh()
|
|
372
525
|
|
|
373
526
|
return True
|
|
374
|
-
# elif style == 'custom':
|
|
375
|
-
# self.__builder = self.__doCustomStyle()
|
|
376
527
|
|
|
377
|
-
def
|
|
528
|
+
def __dispatch_notification(self):
|
|
378
529
|
self.notification_manager.notify(self.__id, self.__builder.build())
|
|
379
|
-
|
|
380
|
-
|
|
530
|
+
|
|
531
|
+
def __start_notification_build(self, persistent, close_on_click):
|
|
532
|
+
self.__create_basic_notification(persistent, close_on_click)
|
|
381
533
|
if self.style not in ['simple','']:
|
|
382
534
|
self.addNotificationStyle(self.style)
|
|
383
535
|
|
|
384
|
-
def
|
|
385
|
-
# Notification Channel (Required for Android 8.0+)
|
|
386
|
-
# print("THis is cchannel is ",self.channel_id) #"
|
|
536
|
+
def __create_basic_notification(self, persistent, close_on_click):
|
|
387
537
|
if BuildVersion.SDK_INT >= 26 and self.notification_manager.getNotificationChannel(self.channel_id) is None:
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
self.channel_id,
|
|
392
|
-
self.channel_name,
|
|
393
|
-
importance
|
|
394
|
-
)
|
|
395
|
-
self.notification_manager.createNotificationChannel(channel)
|
|
396
|
-
|
|
538
|
+
self.createChannel(self.channel_id, self.channel_name)
|
|
539
|
+
elif not self.__using_set_priority_method:
|
|
540
|
+
self.setPriority('medium' if self.silent else 'urgent')
|
|
397
541
|
# Build the notification
|
|
398
542
|
# str() This is to prevent Error When user does Notification.title='blah' instead of Notification(title='blah'
|
|
399
|
-
# TODO fix this by creating a
|
|
543
|
+
# TODO fix this by creating a on_title method in other versions
|
|
400
544
|
self.__builder.setContentTitle(str(self.title))
|
|
401
545
|
self.__builder.setContentText(str(self.message))
|
|
402
|
-
self.
|
|
403
|
-
self.__builder.setDefaults(NotificationCompat.DEFAULT_ALL)
|
|
404
|
-
self.__builder.setPriority(NotificationCompat.PRIORITY_DEFAULT if self.silent else NotificationCompat.PRIORITY_HIGH)
|
|
546
|
+
self.__insert_app_icon()
|
|
547
|
+
self.__builder.setDefaults(NotificationCompat.DEFAULT_ALL)
|
|
405
548
|
self.__builder.setOnlyAlertOnce(True)
|
|
406
549
|
self.__builder.setOngoing(persistent)
|
|
407
550
|
self.__builder.setAutoCancel(close_on_click)
|
|
408
|
-
self.
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
self.
|
|
551
|
+
self.__add_intent_to_open_app()
|
|
552
|
+
self.__built_parameter_filled = True
|
|
553
|
+
|
|
554
|
+
def __insert_app_icon(self,path=''):
|
|
555
|
+
if path or self.app_icon not in ['','Defaults to package app icon']:
|
|
556
|
+
if self.logs:
|
|
557
|
+
print('getting custom icon...')
|
|
558
|
+
self.__set_icon_from_bitmap(path or self.app_icon)
|
|
416
559
|
else:
|
|
560
|
+
if self.logs:
|
|
561
|
+
print('using default icon...')
|
|
417
562
|
self.__builder.setSmallIcon(context.getApplicationInfo().icon)
|
|
418
563
|
|
|
419
|
-
def
|
|
564
|
+
def __build_img(self, user_img, img_style):
|
|
420
565
|
if user_img.startswith('http://') or user_img.startswith('https://'):
|
|
421
|
-
def callback(
|
|
422
|
-
self.
|
|
566
|
+
def callback(bitmap_):
|
|
567
|
+
self.__apply_notification_image(bitmap_,img_style)
|
|
423
568
|
thread = threading.Thread(
|
|
424
|
-
target=self.
|
|
569
|
+
target=self.__get_bitmap_from_url,
|
|
425
570
|
args=[user_img,callback]
|
|
426
571
|
)
|
|
427
572
|
thread.start()
|
|
428
573
|
else:
|
|
429
|
-
bitmap = self.
|
|
574
|
+
bitmap = self.__get_img_from_path(user_img)
|
|
430
575
|
if bitmap:
|
|
431
|
-
self.
|
|
576
|
+
self.__apply_notification_image(bitmap, img_style)
|
|
432
577
|
|
|
433
|
-
def
|
|
434
|
-
"""Path can be link or relative path"""
|
|
578
|
+
def __set_icon_from_bitmap(self, img_path):
|
|
579
|
+
"""Path can be a link or relative path"""
|
|
435
580
|
if img_path.startswith('http://') or img_path.startswith('https://'):
|
|
436
|
-
def callback(
|
|
437
|
-
|
|
438
|
-
|
|
581
|
+
def callback(bitmap_):
|
|
582
|
+
if bitmap_:
|
|
583
|
+
icon_ = IconCompat.createWithBitmap(bitmap_)
|
|
584
|
+
self.__builder.setSmallIcon(icon_)
|
|
585
|
+
else:
|
|
586
|
+
if self.logs:
|
|
587
|
+
print('Using Default Icon as fallback......')
|
|
588
|
+
self.__builder.setSmallIcon(context.getApplicationInfo().icon)
|
|
439
589
|
threading.Thread(
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
590
|
+
target=self.__get_bitmap_from_url,
|
|
591
|
+
args=[img_path,callback]
|
|
592
|
+
).start()
|
|
443
593
|
else:
|
|
444
|
-
bitmap = self.
|
|
594
|
+
bitmap = self.__get_img_from_path(img_path)
|
|
445
595
|
if bitmap:
|
|
446
596
|
icon = IconCompat.createWithBitmap(bitmap)
|
|
447
597
|
self.__builder.setSmallIcon(icon)
|
|
@@ -450,8 +600,9 @@ class Notification(BaseNotification):
|
|
|
450
600
|
print('Failed getting img for custom notification icon defaulting to app icon')
|
|
451
601
|
self.__builder.setSmallIcon(context.getApplicationInfo().icon)
|
|
452
602
|
|
|
453
|
-
|
|
454
|
-
|
|
603
|
+
@staticmethod
|
|
604
|
+
def __get_img_from_path(relative_path):
|
|
605
|
+
app_folder=os.path.join(app_storage_path(),'app')
|
|
455
606
|
output_path = os.path.join(app_folder, relative_path)
|
|
456
607
|
if not os.path.exists(output_path):
|
|
457
608
|
print(f"\nImage not found at path: {output_path}, (Local images gotten from App Path)")
|
|
@@ -463,7 +614,7 @@ class Notification(BaseNotification):
|
|
|
463
614
|
uri = Uri.parse(f"file://{output_path}")
|
|
464
615
|
return BitmapFactory.decodeStream(context.getContentResolver().openInputStream(uri))
|
|
465
616
|
|
|
466
|
-
def
|
|
617
|
+
def __get_bitmap_from_url(self, url, callback):
|
|
467
618
|
"""Gets Bitmap from url
|
|
468
619
|
|
|
469
620
|
Args:
|
|
@@ -483,52 +634,36 @@ class Notification(BaseNotification):
|
|
|
483
634
|
if bitmap:
|
|
484
635
|
callback(bitmap)
|
|
485
636
|
else:
|
|
486
|
-
print('Error No Bitmap ------------')
|
|
487
|
-
except Exception as
|
|
488
|
-
|
|
489
|
-
|
|
637
|
+
print('Error No Bitmap for small icon ------------')
|
|
638
|
+
except Exception as extracting_bitmap_frm_URL_error:
|
|
639
|
+
callback(None)
|
|
640
|
+
# TODO get all types of JAVA Error that can fail here
|
|
641
|
+
print('Error Type ',extracting_bitmap_frm_URL_error)
|
|
490
642
|
print('Failed to get Bitmap from URL ',traceback.format_exc())
|
|
491
643
|
|
|
492
644
|
@run_on_ui_thread
|
|
493
|
-
def
|
|
494
|
-
if self.logs:
|
|
495
|
-
print('appying notification image-------')
|
|
645
|
+
def __apply_notification_image(self, bitmap, img_style):
|
|
496
646
|
try:
|
|
497
|
-
if img_style == NotificationStyles.BIG_PICTURE:
|
|
498
|
-
big_picture_style = NotificationCompatBigPictureStyle().bigPicture(bitmap)
|
|
647
|
+
if img_style == NotificationStyles.BIG_PICTURE and bitmap:
|
|
648
|
+
big_picture_style = NotificationCompatBigPictureStyle().bigPicture(bitmap)
|
|
499
649
|
self.__builder.setStyle(big_picture_style)
|
|
500
|
-
elif img_style == NotificationStyles.LARGE_ICON:
|
|
650
|
+
elif img_style == NotificationStyles.LARGE_ICON and bitmap:
|
|
501
651
|
self.__builder.setLargeIcon(bitmap)
|
|
502
|
-
|
|
652
|
+
# LargeIcon requires smallIcon to be already set
|
|
653
|
+
# 'setLarge, setBigPic' tries to dispatch before filling required values `self.__create_basic_notification`
|
|
654
|
+
self.refresh()
|
|
503
655
|
if self.logs:
|
|
504
656
|
print('Done adding image to notification-------')
|
|
505
|
-
except Exception as
|
|
657
|
+
except Exception as notification_image_error:
|
|
506
658
|
img = self.large_icon_path if img_style == NotificationStyles.LARGE_ICON else self.big_picture_path
|
|
507
|
-
print(f'Failed adding Image of style: {img_style} || From path: {img}, Exception {
|
|
508
|
-
print('could
|
|
509
|
-
|
|
510
|
-
def __getUniqueID(self):
|
|
511
|
-
notification_id = self.notification_ids[-1] + 1
|
|
512
|
-
self.notification_ids.append(notification_id)
|
|
513
|
-
return notification_id
|
|
514
|
-
|
|
515
|
-
def __asks_permission_if_needed(self):
|
|
516
|
-
"""
|
|
517
|
-
Ask for permission to send notifications if needed.
|
|
518
|
-
"""
|
|
519
|
-
def on_permissions_result(permissions, grant): # pylint: disable=unused-argument
|
|
520
|
-
if self.logs:
|
|
521
|
-
print("Permission Grant State: ",grant)
|
|
522
|
-
|
|
523
|
-
permissions=[Permission.POST_NOTIFICATIONS] # pylint: disable=E0606
|
|
524
|
-
if not all(check_permission(p) for p in permissions):
|
|
525
|
-
request_permissions(permissions,on_permissions_result) # pylint: disable=E0606
|
|
659
|
+
print(f'Failed adding Image of style: {img_style} || From path: {img}, Exception {notification_image_error}')
|
|
660
|
+
print('could not get Img traceback: ',traceback.format_exc())
|
|
526
661
|
|
|
527
|
-
def
|
|
662
|
+
def __add_intent_to_open_app(self):
|
|
528
663
|
intent = Intent(context, PythonActivity)
|
|
529
|
-
action = str(self.
|
|
664
|
+
action = str(self.name or self.__id)
|
|
530
665
|
intent.setAction(action)
|
|
531
|
-
self.
|
|
666
|
+
self.__add_data_to_intent(intent)
|
|
532
667
|
self.main_functions[action]=self.callback
|
|
533
668
|
intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP)
|
|
534
669
|
|
|
@@ -538,32 +673,38 @@ class Notification(BaseNotification):
|
|
|
538
673
|
)
|
|
539
674
|
self.__builder.setContentIntent(pending_intent)
|
|
540
675
|
|
|
541
|
-
def
|
|
542
|
-
"""
|
|
676
|
+
def __add_data_to_intent(self, intent):
|
|
677
|
+
"""Persist Some data to notification object for later use"""
|
|
543
678
|
bundle = Bundle()
|
|
544
679
|
bundle.putString("title", self.title or 'Title Placeholder')
|
|
545
|
-
bundle.putInt("notify_id", self.__id)
|
|
680
|
+
# bundle.putInt("notify_id", self.__id)
|
|
681
|
+
bundle.putInt("notify_id", 101)
|
|
546
682
|
intent.putExtras(bundle)
|
|
547
683
|
|
|
548
|
-
def
|
|
684
|
+
def __get_id_for_button(self):
|
|
549
685
|
btn_id = self.button_ids[-1] + 1
|
|
550
686
|
self.button_ids.append(btn_id)
|
|
551
687
|
return btn_id
|
|
552
688
|
|
|
553
|
-
def
|
|
554
|
-
|
|
555
|
-
|
|
689
|
+
def __format_channel(self, channel_name:str='Default Channel',channel_id:str='default_channel'):
|
|
690
|
+
"""
|
|
691
|
+
Formats and sets self.channel_name and self.channel_id to formatted version
|
|
692
|
+
:param channel_name:
|
|
693
|
+
:param channel_id:
|
|
694
|
+
:return:
|
|
695
|
+
"""
|
|
696
|
+
# Shorten channel name # android docs as at most 40 chars
|
|
697
|
+
if channel_name != 'Default Channel':
|
|
698
|
+
cleaned_name = channel_name.strip()
|
|
556
699
|
self.channel_name = cleaned_name[:40] if cleaned_name else 'Default Channel'
|
|
557
700
|
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
# Generate channel_id from channel_name if only channel_name is provided
|
|
563
|
-
generated_id = self.__generate_channel_id(inputted_kwargs['channel_name'])
|
|
564
|
-
self.channel_id = generated_id
|
|
701
|
+
# If no channel_id then generating channel_id from passed in channel_name
|
|
702
|
+
if channel_id == 'default_channel':
|
|
703
|
+
generated_id = self.__generate_channel_id(channel_name)
|
|
704
|
+
self.channel_id = generated_id
|
|
565
705
|
|
|
566
|
-
|
|
706
|
+
@staticmethod
|
|
707
|
+
def __generate_channel_id(channel_name: str) -> str:
|
|
567
708
|
"""
|
|
568
709
|
Generate a readable and consistent channel ID from a channel name.
|
|
569
710
|
|
|
@@ -581,40 +722,84 @@ class Notification(BaseNotification):
|
|
|
581
722
|
channel_id = channel_id.strip('_')
|
|
582
723
|
return channel_id[:50]
|
|
583
724
|
|
|
725
|
+
def __get_unique_id(self):
|
|
726
|
+
notification_id = self.notification_ids[-1] + 1
|
|
727
|
+
self.notification_ids.append(notification_id)
|
|
728
|
+
return notification_id
|
|
729
|
+
|
|
730
|
+
@staticmethod
|
|
731
|
+
def __return_notification_manger():
|
|
732
|
+
notification_service = context.getSystemService(context.NOTIFICATION_SERVICE)
|
|
733
|
+
return cast(NotificationManager, notification_service)
|
|
734
|
+
|
|
735
|
+
@classmethod
|
|
736
|
+
def getChannels(cls):
|
|
737
|
+
"""Return all existing channels"""
|
|
738
|
+
if not ON_ANDROID:
|
|
739
|
+
return []
|
|
740
|
+
|
|
741
|
+
return cls.__return_notification_manger().getNotificationChannels()
|
|
742
|
+
|
|
743
|
+
@staticmethod
|
|
744
|
+
def __get_android_importance(importance:Importance):
|
|
745
|
+
"""
|
|
746
|
+
Returns Android Importance Values
|
|
747
|
+
:param importance: ['urgent','high','medium','low','none']
|
|
748
|
+
:return: Android equivalent int or empty str
|
|
749
|
+
"""
|
|
750
|
+
value=''
|
|
751
|
+
if importance == 'urgent':
|
|
752
|
+
value = NotificationCompat.PRIORITY_HIGH if BuildVersion.SDK_INT <= 25 else NotificationManagerCompat.IMPORTANCE_HIGH
|
|
753
|
+
elif importance == 'high':
|
|
754
|
+
value = NotificationCompat.PRIORITY_DEFAULT if BuildVersion.SDK_INT <= 25 else NotificationManagerCompat.IMPORTANCE_DEFAULT
|
|
755
|
+
elif importance == 'medium':
|
|
756
|
+
value = NotificationCompat.PRIORITY_LOW if BuildVersion.SDK_INT <= 25 else NotificationManagerCompat.IMPORTANCE_LOW
|
|
757
|
+
elif importance == 'low':
|
|
758
|
+
value = NotificationCompat.PRIORITY_MIN if BuildVersion.SDK_INT <= 25 else NotificationManagerCompat.IMPORTANCE_MIN
|
|
759
|
+
elif importance == 'none':
|
|
760
|
+
value = '' if BuildVersion.SDK_INT <= 25 else NotificationManagerCompat.IMPORTANCE_NONE
|
|
761
|
+
|
|
762
|
+
return value
|
|
763
|
+
# side-note 'medium' = NotificationCompat.PRIORITY_LOW and 'low' = NotificationCompat.PRIORITY_MIN # weird but from docs
|
|
764
|
+
# TODO method to create channel groups
|
|
765
|
+
|
|
766
|
+
|
|
584
767
|
class NotificationHandler:
|
|
585
768
|
"""For Notification Operations """
|
|
586
|
-
|
|
769
|
+
__name = None
|
|
587
770
|
__bound = False
|
|
588
771
|
|
|
589
772
|
@classmethod
|
|
590
|
-
def
|
|
591
|
-
"""Returns
|
|
773
|
+
def get_name(cls):
|
|
774
|
+
"""Returns name or id str for Clicked Notification."""
|
|
592
775
|
if not cls.is_on_android():
|
|
593
776
|
return "Not on Android"
|
|
594
777
|
|
|
595
|
-
saved_intent = cls.
|
|
596
|
-
|
|
778
|
+
saved_intent = cls.__name
|
|
779
|
+
cls.__name = None # so value won't be set when opening app not from notification
|
|
780
|
+
# print('saved_intent ',saved_intent)
|
|
781
|
+
# if not saved_intent or (isinstance(saved_intent, str) and saved_intent.startswith("android.intent")):
|
|
597
782
|
# All other notifications are not None after First notification opens app
|
|
598
783
|
# NOTE these notifications are also from Last time app was opened and they Still Give Value after first one opens App
|
|
599
784
|
# TODO Find a way to get intent when App if Swiped From recents
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
print('
|
|
785
|
+
# Below action is always None
|
|
786
|
+
# __PythonActivity = autoclass(ACTIVITY_CLASS_NAME)
|
|
787
|
+
# __mactivity = __PythonActivity.mActivity
|
|
788
|
+
# __context = cast('android.content.Context', __mactivity)
|
|
789
|
+
# __Intent = autoclass('android.content.Intent')
|
|
790
|
+
# __intent = __Intent(__context, __PythonActivity)
|
|
791
|
+
# action = __intent.getAction()
|
|
792
|
+
# print('Start up Intent ----', action)
|
|
793
|
+
# print('start Up Title --->',__intent.getStringExtra("title"))
|
|
608
794
|
|
|
609
795
|
return saved_intent
|
|
610
796
|
|
|
611
797
|
@classmethod
|
|
612
|
-
def
|
|
798
|
+
def __notification_handler(cls, intent):
|
|
613
799
|
"""Calls Function Attached to notification on click.
|
|
614
800
|
Don't Call this function manual, it's Already Attach to Notification.
|
|
615
801
|
|
|
616
|
-
|
|
617
|
-
str: The Identiter of Nofication that was clicked.
|
|
802
|
+
Sets self.__name #action of Notification that was clicked from Notification.name or Notification.id
|
|
618
803
|
"""
|
|
619
804
|
if not cls.is_on_android():
|
|
620
805
|
return "Not on Android"
|
|
@@ -623,13 +808,13 @@ class NotificationHandler:
|
|
|
623
808
|
if DEV:
|
|
624
809
|
print("notifty_functions ",notifty_functions)
|
|
625
810
|
print("buttons_object", buttons_object)
|
|
626
|
-
action = None
|
|
627
811
|
try:
|
|
628
812
|
action = intent.getAction()
|
|
629
|
-
cls.
|
|
813
|
+
cls.__name = action
|
|
630
814
|
|
|
631
|
-
print("The Action --> ",action)
|
|
815
|
+
# print("The Action --> ",action)
|
|
632
816
|
if action == "android.intent.action.MAIN": # Not Open From Notification
|
|
817
|
+
cls.__name = None
|
|
633
818
|
return 'Not notification'
|
|
634
819
|
|
|
635
820
|
print(intent.getStringExtra("title"))
|
|
@@ -638,12 +823,11 @@ class NotificationHandler:
|
|
|
638
823
|
notifty_functions[action]()
|
|
639
824
|
elif action in buttons_object:
|
|
640
825
|
buttons_object[action]()
|
|
641
|
-
except Exception as
|
|
826
|
+
except Exception as notification_handler_function_error:
|
|
827
|
+
print("Error Type ",notification_handler_function_error)
|
|
642
828
|
print('Failed to run function: ', traceback.format_exc())
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
print('Notify Hanlder Failed ',e)
|
|
646
|
-
return action
|
|
829
|
+
except Exception as extracting_notification_props_error:
|
|
830
|
+
print('Notify Handler Failed ',extracting_notification_props_error)
|
|
647
831
|
|
|
648
832
|
@classmethod
|
|
649
833
|
def bindNotifyListener(cls):
|
|
@@ -652,15 +836,16 @@ class NotificationHandler:
|
|
|
652
836
|
return "Not on Android"
|
|
653
837
|
#TODO keep trying BroadcastReceiver
|
|
654
838
|
if cls.__bound:
|
|
655
|
-
print("
|
|
839
|
+
print("binding done already ")
|
|
656
840
|
return True
|
|
657
841
|
try:
|
|
658
|
-
activity.bind(on_new_intent=cls.
|
|
842
|
+
activity.bind(on_new_intent=cls.__notification_handler)
|
|
659
843
|
cls.__bound = True
|
|
660
844
|
return True
|
|
661
|
-
except Exception as
|
|
662
|
-
print('Failed to bin
|
|
845
|
+
except Exception as binding_listener_error:
|
|
846
|
+
print('Failed to bin notifications listener',binding_listener_error)
|
|
663
847
|
return False
|
|
848
|
+
|
|
664
849
|
@classmethod
|
|
665
850
|
def unbindNotifyListener(cls):
|
|
666
851
|
"""Removes Listener for Notifications Click"""
|
|
@@ -669,10 +854,10 @@ class NotificationHandler:
|
|
|
669
854
|
|
|
670
855
|
#Beta TODO use BroadcastReceiver
|
|
671
856
|
try:
|
|
672
|
-
activity.unbind(on_new_intent=cls.
|
|
857
|
+
activity.unbind(on_new_intent=cls.__notification_handler)
|
|
673
858
|
return True
|
|
674
|
-
except Exception as
|
|
675
|
-
print("Failed to unbind notifications listener: ",
|
|
859
|
+
except Exception as unbinding_listener_error:
|
|
860
|
+
print("Failed to unbind notifications listener: ",unbinding_listener_error)
|
|
676
861
|
return False
|
|
677
862
|
|
|
678
863
|
@staticmethod
|
|
@@ -680,4 +865,33 @@ class NotificationHandler:
|
|
|
680
865
|
"""Utility to check if the app is running on Android."""
|
|
681
866
|
return ON_ANDROID
|
|
682
867
|
|
|
868
|
+
@staticmethod
|
|
869
|
+
def has_permission():
|
|
870
|
+
"""
|
|
871
|
+
Checks if device has permission to send notifications
|
|
872
|
+
returns True if device has permission
|
|
873
|
+
"""
|
|
874
|
+
if not ON_ANDROID:
|
|
875
|
+
return True
|
|
876
|
+
return check_permission(Permission.POST_NOTIFICATIONS)
|
|
877
|
+
|
|
878
|
+
@classmethod
|
|
879
|
+
def asks_permission(cls,callback=None):
|
|
880
|
+
"""
|
|
881
|
+
Ask for permission to send notifications if needed.
|
|
882
|
+
"""
|
|
883
|
+
if not ON_ANDROID:
|
|
884
|
+
return
|
|
885
|
+
def on_permissions_result(permissions, grant):
|
|
886
|
+
print("Permission Grant State: ",grant)
|
|
887
|
+
try:
|
|
888
|
+
if callback:
|
|
889
|
+
callback()
|
|
890
|
+
except Exception as request_permission_error:
|
|
891
|
+
print('Exception: ',request_permission_error)
|
|
892
|
+
print('Permission request callback error: ',traceback.format_exc())
|
|
893
|
+
if not cls.has_permission():
|
|
894
|
+
request_permissions([Permission.POST_NOTIFICATIONS],on_permissions_result)
|
|
895
|
+
|
|
896
|
+
|
|
683
897
|
NotificationHandler.bindNotifyListener()
|