android-notify 1.57.1__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 +462 -214
- android_notify-1.59.dist-info/METADATA +117 -0
- android_notify-1.59.dist-info/RECORD +11 -0
- {android_notify-1.57.1.dist-info → android_notify-1.59.dist-info}/WHEEL +1 -1
- android_notify-1.57.1.dist-info/METADATA +0 -575
- android_notify-1.57.1.dist-info/RECORD +0 -11
- {android_notify-1.57.1.dist-info → android_notify-1.59.dist-info}/top_level.txt +0 -0
android_notify/sword.py
CHANGED
|
@@ -1,36 +1,41 @@
|
|
|
1
1
|
"""This Module Contain Class for creating Notification With Java"""
|
|
2
|
-
import difflib
|
|
3
2
|
import traceback
|
|
4
|
-
import os
|
|
3
|
+
import os,re
|
|
5
4
|
import threading
|
|
6
|
-
import
|
|
5
|
+
from .an_types import Importance
|
|
7
6
|
from .styles import NotificationStyles
|
|
8
7
|
from .base import BaseNotification
|
|
9
8
|
DEV=0
|
|
10
9
|
ON_ANDROID = False
|
|
11
10
|
|
|
11
|
+
# noinspection PyBroadException
|
|
12
12
|
try:
|
|
13
13
|
# Android Imports
|
|
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
|
|
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
|
|
18
18
|
|
|
19
|
-
# Get the required Java classes
|
|
19
|
+
# Get the required Java classes needs to on android to import
|
|
20
20
|
Bundle = autoclass('android.os.Bundle')
|
|
21
21
|
PythonActivity = autoclass('org.kivy.android.PythonActivity')
|
|
22
22
|
String = autoclass('java.lang.String')
|
|
23
23
|
Intent = autoclass('android.content.Intent')
|
|
24
24
|
PendingIntent = autoclass('android.app.PendingIntent')
|
|
25
|
-
context = PythonActivity.mActivity
|
|
25
|
+
context = PythonActivity.mActivity
|
|
26
26
|
BitmapFactory = autoclass('android.graphics.BitmapFactory')
|
|
27
27
|
BuildVersion = autoclass('android.os.Build$VERSION')
|
|
28
|
+
VersionCodes = autoclass('android.os.Build$VERSION_CODES')
|
|
28
29
|
NotificationManager = autoclass('android.app.NotificationManager')
|
|
29
30
|
NotificationChannel = autoclass('android.app.NotificationChannel')
|
|
30
31
|
IconCompat = autoclass('androidx.core.graphics.drawable.IconCompat')
|
|
31
32
|
ON_ANDROID = True
|
|
32
|
-
except Exception as e
|
|
33
|
-
|
|
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.'
|
|
34
39
|
# from .types_idea import *
|
|
35
40
|
# print(MESSAGE) Already Printing in core.py
|
|
36
41
|
|
|
@@ -44,18 +49,18 @@ except Exception as e:# pylint: disable=W0718
|
|
|
44
49
|
|
|
45
50
|
if ON_ANDROID:
|
|
46
51
|
try:
|
|
47
|
-
from android.permissions import request_permissions, Permission,check_permission
|
|
48
|
-
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
|
|
49
54
|
|
|
50
55
|
NotificationManagerCompat = autoclass('androidx.core.app.NotificationManagerCompat')
|
|
51
56
|
NotificationCompat = autoclass('androidx.core.app.NotificationCompat')
|
|
52
57
|
|
|
53
58
|
# Notification Design
|
|
54
|
-
NotificationCompatBuilder = autoclass('androidx.core.app.NotificationCompat$Builder')
|
|
55
|
-
NotificationCompatBigTextStyle = autoclass('androidx.core.app.NotificationCompat$BigTextStyle')
|
|
56
|
-
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')
|
|
57
62
|
NotificationCompatInboxStyle = autoclass('androidx.core.app.NotificationCompat$InboxStyle')
|
|
58
|
-
except Exception as e
|
|
63
|
+
except Exception as e:
|
|
59
64
|
print(e)
|
|
60
65
|
print("""
|
|
61
66
|
Dependency Error: Add the following in buildozer.spec:
|
|
@@ -75,11 +80,12 @@ class Notification(BaseNotification):
|
|
|
75
80
|
both_imgs == using lager icon and big picture
|
|
76
81
|
:param big_picture_path: Relative Path to the image resource.
|
|
77
82
|
:param large_icon_path: Relative Path to the image resource.
|
|
78
|
-
:param progress_current_value:
|
|
79
|
-
:param progress_max_value:
|
|
80
|
-
: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.
|
|
81
86
|
---
|
|
82
87
|
(Advance Options)
|
|
88
|
+
:param id: Pass in Old 'id' to use old instance
|
|
83
89
|
:param callback: Function for notification Click.
|
|
84
90
|
:param channel_name: - str Defaults to "Default Channel"
|
|
85
91
|
:param channel_id: - str Defaults to "default_channel"
|
|
@@ -87,32 +93,172 @@ class Notification(BaseNotification):
|
|
|
87
93
|
(Options during Dev On PC)
|
|
88
94
|
:param logs: - Bool Defaults to True
|
|
89
95
|
"""
|
|
90
|
-
|
|
96
|
+
|
|
97
|
+
notification_ids = [0]
|
|
91
98
|
button_ids=[0]
|
|
92
99
|
btns_box={}
|
|
93
100
|
main_functions={}
|
|
94
101
|
|
|
95
102
|
# During Development (When running on PC)
|
|
96
103
|
BaseNotification.logs=not ON_ANDROID
|
|
97
|
-
def __init__(self,**kwargs):
|
|
104
|
+
def __init__(self,**kwargs): #@dataclass already does work
|
|
98
105
|
super().__init__(**kwargs)
|
|
99
106
|
|
|
100
|
-
self.__id = self.
|
|
101
|
-
self.
|
|
102
|
-
|
|
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
|
|
109
|
+
|
|
110
|
+
# To Track progressbar last update (According to Android Docs Don't update bar to often, I also faced so issues when doing that)
|
|
111
|
+
self.__update_timer = None
|
|
112
|
+
self.__progress_bar_msg = ''
|
|
113
|
+
self.__progress_bar_title = ''
|
|
114
|
+
self.__cooldown = 0
|
|
115
|
+
|
|
116
|
+
self.__built_parameter_filled=False
|
|
117
|
+
self.__using_set_priority_method=False
|
|
118
|
+
|
|
119
|
+
self.__format_channel(self.channel_name, self.channel_id)
|
|
103
120
|
if not ON_ANDROID:
|
|
104
121
|
return
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
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
|
+
|
|
109
252
|
def showInfiniteProgressBar(self):
|
|
110
253
|
"""Displays an (Infinite) progress Bar in Notification, that continues loading indefinitely.
|
|
111
254
|
Can be Removed By `removeProgressBar` Method
|
|
112
255
|
"""
|
|
113
|
-
self.
|
|
114
|
-
|
|
115
|
-
|
|
256
|
+
if self.logs:
|
|
257
|
+
print('Showing infinite progressbar')
|
|
258
|
+
if ON_ANDROID:
|
|
259
|
+
self.__builder.setProgress(0,0, True)
|
|
260
|
+
self.refresh()
|
|
261
|
+
|
|
116
262
|
def updateTitle(self,new_title):
|
|
117
263
|
"""Changes Old Title
|
|
118
264
|
|
|
@@ -124,7 +270,7 @@ class Notification(BaseNotification):
|
|
|
124
270
|
print(f'new notification title: {self.title}')
|
|
125
271
|
if ON_ANDROID:
|
|
126
272
|
self.__builder.setContentTitle(String(self.title))
|
|
127
|
-
self.
|
|
273
|
+
self.refresh()
|
|
128
274
|
|
|
129
275
|
def updateMessage(self,new_message):
|
|
130
276
|
"""Changes Old Message
|
|
@@ -137,111 +283,154 @@ class Notification(BaseNotification):
|
|
|
137
283
|
print(f'new notification message: {self.message}')
|
|
138
284
|
if ON_ANDROID:
|
|
139
285
|
self.__builder.setContentText(String(self.message))
|
|
140
|
-
self.
|
|
286
|
+
self.refresh()
|
|
141
287
|
|
|
142
|
-
def updateProgressBar(self,current_value:int,message:str='',title:str='',
|
|
288
|
+
def updateProgressBar(self,current_value:int,message:str='',title:str='',cooldown=0.5):
|
|
143
289
|
"""Updates progress bar current value
|
|
144
|
-
|
|
290
|
+
|
|
145
291
|
Args:
|
|
146
292
|
current_value (int): the value from progressbar current progress
|
|
147
293
|
message (str): defaults to last message
|
|
148
294
|
title (str): defaults to last title
|
|
149
|
-
|
|
150
|
-
|
|
295
|
+
cooldown (float, optional): Little Time to Wait before change actually reflects, to avoid android Ignoring Change, Defaults to 0.5secs
|
|
296
|
+
|
|
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
|
|
151
298
|
"""
|
|
152
|
-
# replacing new value for when timer is called
|
|
153
|
-
self.progress_current_value = current_value
|
|
154
299
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
300
|
+
# replacing new values for when timer is called
|
|
301
|
+
self.progress_current_value = current_value
|
|
302
|
+
self.__progress_bar_msg = message
|
|
303
|
+
self.__progress_bar_title = title
|
|
304
|
+
|
|
305
|
+
if self.__update_timer and self.__update_timer.is_alive():
|
|
306
|
+
# Make Logs too Dirty
|
|
307
|
+
# if self.logs:
|
|
308
|
+
# remaining = self.__cooldown - (time.time() - self.__timer_start_time)
|
|
309
|
+
# print(f'Progressbar update too soon, waiting for cooldown ({max(0, remaining):.2f}s)')
|
|
158
310
|
return
|
|
159
311
|
|
|
160
312
|
def delayed_update():
|
|
161
|
-
if self.__update_timer is None:
|
|
162
|
-
|
|
313
|
+
if self.__update_timer is None: # Ensure we are not executing an old timer
|
|
314
|
+
if self.logs:
|
|
315
|
+
print('ProgressBar update skipped: bar has been removed.')
|
|
163
316
|
return
|
|
164
317
|
if self.logs:
|
|
165
318
|
print(f'Progress Bar Update value: {self.progress_current_value}')
|
|
166
319
|
|
|
167
320
|
if not ON_ANDROID:
|
|
321
|
+
self.__update_timer = None
|
|
168
322
|
return
|
|
323
|
+
|
|
169
324
|
self.__builder.setProgress(self.progress_max_value, self.progress_current_value, False)
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
325
|
+
|
|
326
|
+
if self.__progress_bar_msg:
|
|
327
|
+
self.updateMessage(self.__progress_bar_msg)
|
|
328
|
+
if self.__progress_bar_title:
|
|
329
|
+
self.updateTitle(self.__progress_bar_title)
|
|
330
|
+
|
|
331
|
+
self.refresh()
|
|
175
332
|
self.__update_timer = None
|
|
176
333
|
|
|
177
334
|
|
|
178
335
|
# Start a new timer that runs after 0.5 seconds
|
|
179
|
-
self.
|
|
336
|
+
# self.__timer_start_time = time.time() # for logs
|
|
337
|
+
self.__cooldown = cooldown
|
|
338
|
+
self.__update_timer = threading.Timer(cooldown, delayed_update)
|
|
180
339
|
self.__update_timer.start()
|
|
181
340
|
|
|
182
|
-
def removeProgressBar(self,message='',show_on_update=True, title:str='')
|
|
341
|
+
def removeProgressBar(self,message='',show_on_update=True, title:str='',cooldown=0.5):
|
|
183
342
|
"""Removes Progress Bar from Notification
|
|
184
343
|
|
|
185
344
|
Args:
|
|
186
345
|
message (str, optional): notification message. Defaults to 'last message'.
|
|
187
|
-
show_on_update (bool, optional): To show notification
|
|
346
|
+
show_on_update (bool, optional): To show notification briefly when progressbar removed. Defaults to True.
|
|
188
347
|
title (str, optional): notification title. Defaults to 'last title'.
|
|
348
|
+
cooldown (float, optional): Little Time to Wait before change actually reflects, to avoid android Ignoring Change, Defaults to 0.5secs
|
|
349
|
+
|
|
350
|
+
In-Built Delay of 0.5sec According to Android Docs Don't Update Progressbar too Frequently
|
|
189
351
|
"""
|
|
352
|
+
|
|
353
|
+
# To Cancel any queued timer from `updateProgressBar` method and to avoid race effect incase it somehow gets called while in this method
|
|
354
|
+
# Avoiding Running `updateProgressBar.delayed_update` at all
|
|
355
|
+
# so didn't just set `self.__progress_bar_title` and `self.progress_current_value` to 0
|
|
190
356
|
if self.__update_timer:
|
|
357
|
+
# Make Logs too Dirty
|
|
358
|
+
# if self.logs:
|
|
359
|
+
# print('cancelled progressbar stream update because about to remove',self.progress_current_value)
|
|
191
360
|
self.__update_timer.cancel()
|
|
192
361
|
self.__update_timer = None
|
|
193
|
-
|
|
194
|
-
if self.logs:
|
|
195
|
-
msg = message or self.message
|
|
196
|
-
title_=title or self.title
|
|
197
|
-
print(f'removed progress bar with message: {msg} and title: {title_}')
|
|
198
362
|
|
|
199
|
-
if not ON_ANDROID:
|
|
200
|
-
return False
|
|
201
363
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
364
|
+
def delayed_update():
|
|
365
|
+
if self.logs:
|
|
366
|
+
msg = message or self.message
|
|
367
|
+
title_=title or self.title
|
|
368
|
+
print(f'removed progress bar with message: {msg} and title: {title_}')
|
|
369
|
+
|
|
370
|
+
if not ON_ANDROID:
|
|
371
|
+
return
|
|
372
|
+
|
|
373
|
+
if message:
|
|
374
|
+
self.updateMessage(message)
|
|
375
|
+
if title:
|
|
376
|
+
self.updateTitle(title)
|
|
377
|
+
self.__builder.setOnlyAlertOnce(not show_on_update)
|
|
378
|
+
self.__builder.setProgress(0, 0, False)
|
|
379
|
+
self.refresh()
|
|
380
|
+
|
|
381
|
+
# Incase `self.updateProgressBar delayed_update` is called right before this method, so android doesn't bounce update
|
|
382
|
+
threading.Timer(cooldown, delayed_update).start()
|
|
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)
|
|
210
395
|
|
|
211
396
|
def send(self,silent:bool=False,persistent=False,close_on_click=True):
|
|
212
397
|
"""Sends notification
|
|
213
|
-
|
|
398
|
+
|
|
214
399
|
Args:
|
|
215
400
|
silent (bool): True if you don't want to show briefly on screen
|
|
216
401
|
persistent (bool): True To not remove Notification When User hits clears All notifications button
|
|
217
402
|
close_on_click (bool): True if you want Notification to be removed when clicked
|
|
218
403
|
"""
|
|
219
|
-
self.silent=
|
|
404
|
+
self.silent= silent or self.silent
|
|
220
405
|
if ON_ANDROID:
|
|
221
|
-
self.
|
|
222
|
-
self.
|
|
406
|
+
self.__start_notification_build(persistent, close_on_click)
|
|
407
|
+
self.__dispatch_notification()
|
|
408
|
+
|
|
223
409
|
if self.logs:
|
|
224
410
|
string_to_display=''
|
|
225
411
|
print("\n Sent Notification!!!")
|
|
226
412
|
for name,value in vars(self).items():
|
|
227
|
-
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']:
|
|
228
414
|
if name == "progress_max_value":
|
|
229
415
|
if self.style == NotificationStyles.PROGRESS:
|
|
230
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}'
|
|
231
420
|
else:
|
|
232
421
|
string_to_display += f'\n {name}: {value}'
|
|
233
422
|
|
|
234
423
|
string_to_display +="\n (Won't Print Logs When Complied,except if selected `Notification.logs=True`)"
|
|
235
424
|
print(string_to_display)
|
|
236
425
|
if DEV:
|
|
237
|
-
print(f'channel_name: {self.channel_name}, Channel ID: {self.channel_id},
|
|
238
|
-
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}')
|
|
239
427
|
|
|
240
428
|
def addButton(self, text:str,on_release):
|
|
241
429
|
"""For adding action buttons
|
|
242
430
|
|
|
243
431
|
Args:
|
|
244
432
|
text (str): Text For Button
|
|
433
|
+
on_release: function to be called when button is clicked
|
|
245
434
|
"""
|
|
246
435
|
if self.logs:
|
|
247
436
|
print('Added Button: ', text)
|
|
@@ -249,7 +438,7 @@ class Notification(BaseNotification):
|
|
|
249
438
|
if not ON_ANDROID:
|
|
250
439
|
return
|
|
251
440
|
|
|
252
|
-
btn_id= self.
|
|
441
|
+
btn_id= self.__get_id_for_button()
|
|
253
442
|
action = f"BTN_ACTION_{btn_id}"
|
|
254
443
|
|
|
255
444
|
action_intent = Intent(context, PythonActivity)
|
|
@@ -291,9 +480,9 @@ class Notification(BaseNotification):
|
|
|
291
480
|
"""
|
|
292
481
|
if ON_ANDROID:
|
|
293
482
|
self.__builder.mActions.clear()
|
|
294
|
-
self.
|
|
483
|
+
self.refresh()
|
|
295
484
|
if self.logs:
|
|
296
|
-
print('Removed
|
|
485
|
+
print('Removed Notification Buttons')
|
|
297
486
|
|
|
298
487
|
@run_on_ui_thread
|
|
299
488
|
def addNotificationStyle(self,style:str,already_sent=False):
|
|
@@ -306,108 +495,103 @@ class Notification(BaseNotification):
|
|
|
306
495
|
"""
|
|
307
496
|
|
|
308
497
|
if not ON_ANDROID:
|
|
309
|
-
# 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
|
|
310
499
|
return False
|
|
311
500
|
|
|
312
501
|
if style == NotificationStyles.BIG_TEXT:
|
|
313
|
-
|
|
314
|
-
big_text_style.bigText(str(self.body))
|
|
315
|
-
self.__builder.setStyle(big_text_style)
|
|
502
|
+
self.setBigText(self.body)
|
|
316
503
|
|
|
317
504
|
elif style == NotificationStyles.INBOX:
|
|
318
|
-
inbox_style = NotificationCompatInboxStyle()
|
|
505
|
+
inbox_style = NotificationCompatInboxStyle()
|
|
319
506
|
for line in self.message.split("\n"):
|
|
320
507
|
inbox_style.addLine(str(line))
|
|
321
508
|
self.__builder.setStyle(inbox_style)
|
|
322
509
|
|
|
323
510
|
elif (style == NotificationStyles.LARGE_ICON and self.large_icon_path) or (style == NotificationStyles.BIG_PICTURE and self.big_picture_path):
|
|
324
511
|
img = self.large_icon_path if style == NotificationStyles.LARGE_ICON else self.big_picture_path
|
|
325
|
-
self.
|
|
512
|
+
self.__build_img(img, style)
|
|
326
513
|
|
|
327
514
|
elif style == NotificationStyles.BOTH_IMGS and (self.big_picture_path or self.large_icon_path):
|
|
328
515
|
if self.big_picture_path:
|
|
329
|
-
self.
|
|
516
|
+
self.setBigPicture(self.big_picture_path)
|
|
330
517
|
if self.large_icon_path:
|
|
331
|
-
self.
|
|
518
|
+
self.setLargeIcon(self.large_icon_path)
|
|
332
519
|
|
|
333
520
|
elif style == NotificationStyles.PROGRESS:
|
|
334
521
|
self.__builder.setProgress(self.progress_max_value, self.progress_current_value, False)
|
|
335
522
|
|
|
336
523
|
if already_sent:
|
|
337
|
-
self.
|
|
524
|
+
self.refresh()
|
|
338
525
|
|
|
339
526
|
return True
|
|
340
|
-
# elif style == 'custom':
|
|
341
|
-
# self.__builder = self.__doCustomStyle()
|
|
342
527
|
|
|
343
|
-
def
|
|
528
|
+
def __dispatch_notification(self):
|
|
344
529
|
self.notification_manager.notify(self.__id, self.__builder.build())
|
|
345
|
-
|
|
346
|
-
|
|
530
|
+
|
|
531
|
+
def __start_notification_build(self, persistent, close_on_click):
|
|
532
|
+
self.__create_basic_notification(persistent, close_on_click)
|
|
347
533
|
if self.style not in ['simple','']:
|
|
348
534
|
self.addNotificationStyle(self.style)
|
|
349
535
|
|
|
350
|
-
def
|
|
351
|
-
# Notification Channel (Required for Android 8.0+)
|
|
352
|
-
# print("THis is cchannel is ",self.channel_id) #"
|
|
536
|
+
def __create_basic_notification(self, persistent, close_on_click):
|
|
353
537
|
if BuildVersion.SDK_INT >= 26 and self.notification_manager.getNotificationChannel(self.channel_id) is None:
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
self.channel_id,
|
|
358
|
-
self.channel_name,
|
|
359
|
-
importance
|
|
360
|
-
)
|
|
361
|
-
self.notification_manager.createNotificationChannel(channel)
|
|
362
|
-
|
|
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')
|
|
363
541
|
# Build the notification
|
|
364
542
|
# str() This is to prevent Error When user does Notification.title='blah' instead of Notification(title='blah'
|
|
365
|
-
# TODO fix this by creating a
|
|
543
|
+
# TODO fix this by creating a on_title method in other versions
|
|
366
544
|
self.__builder.setContentTitle(str(self.title))
|
|
367
545
|
self.__builder.setContentText(str(self.message))
|
|
368
|
-
self.
|
|
369
|
-
self.__builder.setDefaults(NotificationCompat.DEFAULT_ALL)
|
|
370
|
-
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)
|
|
371
548
|
self.__builder.setOnlyAlertOnce(True)
|
|
372
549
|
self.__builder.setOngoing(persistent)
|
|
373
550
|
self.__builder.setAutoCancel(close_on_click)
|
|
374
|
-
self.
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
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)
|
|
382
559
|
else:
|
|
560
|
+
if self.logs:
|
|
561
|
+
print('using default icon...')
|
|
383
562
|
self.__builder.setSmallIcon(context.getApplicationInfo().icon)
|
|
384
563
|
|
|
385
|
-
def
|
|
564
|
+
def __build_img(self, user_img, img_style):
|
|
386
565
|
if user_img.startswith('http://') or user_img.startswith('https://'):
|
|
387
|
-
def callback(
|
|
388
|
-
self.
|
|
566
|
+
def callback(bitmap_):
|
|
567
|
+
self.__apply_notification_image(bitmap_,img_style)
|
|
389
568
|
thread = threading.Thread(
|
|
390
|
-
target=self.
|
|
569
|
+
target=self.__get_bitmap_from_url,
|
|
391
570
|
args=[user_img,callback]
|
|
392
571
|
)
|
|
393
572
|
thread.start()
|
|
394
573
|
else:
|
|
395
|
-
bitmap = self.
|
|
574
|
+
bitmap = self.__get_img_from_path(user_img)
|
|
396
575
|
if bitmap:
|
|
397
|
-
self.
|
|
576
|
+
self.__apply_notification_image(bitmap, img_style)
|
|
398
577
|
|
|
399
|
-
def
|
|
400
|
-
"""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"""
|
|
401
580
|
if img_path.startswith('http://') or img_path.startswith('https://'):
|
|
402
|
-
def callback(
|
|
403
|
-
|
|
404
|
-
|
|
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)
|
|
405
589
|
threading.Thread(
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
590
|
+
target=self.__get_bitmap_from_url,
|
|
591
|
+
args=[img_path,callback]
|
|
592
|
+
).start()
|
|
409
593
|
else:
|
|
410
|
-
bitmap = self.
|
|
594
|
+
bitmap = self.__get_img_from_path(img_path)
|
|
411
595
|
if bitmap:
|
|
412
596
|
icon = IconCompat.createWithBitmap(bitmap)
|
|
413
597
|
self.__builder.setSmallIcon(icon)
|
|
@@ -416,8 +600,9 @@ class Notification(BaseNotification):
|
|
|
416
600
|
print('Failed getting img for custom notification icon defaulting to app icon')
|
|
417
601
|
self.__builder.setSmallIcon(context.getApplicationInfo().icon)
|
|
418
602
|
|
|
419
|
-
|
|
420
|
-
|
|
603
|
+
@staticmethod
|
|
604
|
+
def __get_img_from_path(relative_path):
|
|
605
|
+
app_folder=os.path.join(app_storage_path(),'app')
|
|
421
606
|
output_path = os.path.join(app_folder, relative_path)
|
|
422
607
|
if not os.path.exists(output_path):
|
|
423
608
|
print(f"\nImage not found at path: {output_path}, (Local images gotten from App Path)")
|
|
@@ -429,7 +614,7 @@ class Notification(BaseNotification):
|
|
|
429
614
|
uri = Uri.parse(f"file://{output_path}")
|
|
430
615
|
return BitmapFactory.decodeStream(context.getContentResolver().openInputStream(uri))
|
|
431
616
|
|
|
432
|
-
def
|
|
617
|
+
def __get_bitmap_from_url(self, url, callback):
|
|
433
618
|
"""Gets Bitmap from url
|
|
434
619
|
|
|
435
620
|
Args:
|
|
@@ -449,52 +634,36 @@ class Notification(BaseNotification):
|
|
|
449
634
|
if bitmap:
|
|
450
635
|
callback(bitmap)
|
|
451
636
|
else:
|
|
452
|
-
print('Error No Bitmap ------------')
|
|
453
|
-
except Exception as
|
|
454
|
-
|
|
455
|
-
|
|
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)
|
|
456
642
|
print('Failed to get Bitmap from URL ',traceback.format_exc())
|
|
457
643
|
|
|
458
644
|
@run_on_ui_thread
|
|
459
|
-
def
|
|
460
|
-
if self.logs:
|
|
461
|
-
print('appying notification image-------')
|
|
645
|
+
def __apply_notification_image(self, bitmap, img_style):
|
|
462
646
|
try:
|
|
463
|
-
if img_style == NotificationStyles.BIG_PICTURE:
|
|
464
|
-
big_picture_style = NotificationCompatBigPictureStyle().bigPicture(bitmap)
|
|
647
|
+
if img_style == NotificationStyles.BIG_PICTURE and bitmap:
|
|
648
|
+
big_picture_style = NotificationCompatBigPictureStyle().bigPicture(bitmap)
|
|
465
649
|
self.__builder.setStyle(big_picture_style)
|
|
466
|
-
elif img_style == NotificationStyles.LARGE_ICON:
|
|
650
|
+
elif img_style == NotificationStyles.LARGE_ICON and bitmap:
|
|
467
651
|
self.__builder.setLargeIcon(bitmap)
|
|
468
|
-
|
|
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()
|
|
469
655
|
if self.logs:
|
|
470
656
|
print('Done adding image to notification-------')
|
|
471
|
-
except Exception as
|
|
657
|
+
except Exception as notification_image_error:
|
|
472
658
|
img = self.large_icon_path if img_style == NotificationStyles.LARGE_ICON else self.big_picture_path
|
|
473
|
-
print(f'Failed adding Image of style: {img_style} || From path: {img}, Exception {
|
|
474
|
-
print('could
|
|
475
|
-
|
|
476
|
-
def __getUniqueID(self):
|
|
477
|
-
notification_id = self.notification_ids[-1] + 1
|
|
478
|
-
self.notification_ids.append(notification_id)
|
|
479
|
-
return notification_id
|
|
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())
|
|
480
661
|
|
|
481
|
-
def
|
|
482
|
-
"""
|
|
483
|
-
Ask for permission to send notifications if needed.
|
|
484
|
-
"""
|
|
485
|
-
def on_permissions_result(permissions, grant): # pylint: disable=unused-argument
|
|
486
|
-
if self.logs:
|
|
487
|
-
print("Permission Grant State: ",grant)
|
|
488
|
-
|
|
489
|
-
permissions=[Permission.POST_NOTIFICATIONS] # pylint: disable=E0606
|
|
490
|
-
if not all(check_permission(p) for p in permissions):
|
|
491
|
-
request_permissions(permissions,on_permissions_result) # pylint: disable=E0606
|
|
492
|
-
|
|
493
|
-
def __addIntentToOpenApp(self):
|
|
662
|
+
def __add_intent_to_open_app(self):
|
|
494
663
|
intent = Intent(context, PythonActivity)
|
|
495
|
-
action = str(self.
|
|
664
|
+
action = str(self.name or self.__id)
|
|
496
665
|
intent.setAction(action)
|
|
497
|
-
self.
|
|
666
|
+
self.__add_data_to_intent(intent)
|
|
498
667
|
self.main_functions[action]=self.callback
|
|
499
668
|
intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP)
|
|
500
669
|
|
|
@@ -504,32 +673,38 @@ class Notification(BaseNotification):
|
|
|
504
673
|
)
|
|
505
674
|
self.__builder.setContentIntent(pending_intent)
|
|
506
675
|
|
|
507
|
-
def
|
|
508
|
-
"""
|
|
676
|
+
def __add_data_to_intent(self, intent):
|
|
677
|
+
"""Persist Some data to notification object for later use"""
|
|
509
678
|
bundle = Bundle()
|
|
510
679
|
bundle.putString("title", self.title or 'Title Placeholder')
|
|
511
|
-
bundle.putInt("notify_id", self.__id)
|
|
680
|
+
# bundle.putInt("notify_id", self.__id)
|
|
681
|
+
bundle.putInt("notify_id", 101)
|
|
512
682
|
intent.putExtras(bundle)
|
|
513
683
|
|
|
514
|
-
def
|
|
684
|
+
def __get_id_for_button(self):
|
|
515
685
|
btn_id = self.button_ids[-1] + 1
|
|
516
686
|
self.button_ids.append(btn_id)
|
|
517
687
|
return btn_id
|
|
518
688
|
|
|
519
|
-
def
|
|
520
|
-
|
|
521
|
-
|
|
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()
|
|
522
699
|
self.channel_name = cleaned_name[:40] if cleaned_name else 'Default Channel'
|
|
523
700
|
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
# Generate channel_id from channel_name if only channel_name is provided
|
|
529
|
-
generated_id = self.__generate_channel_id(inputted_kwargs['channel_name'])
|
|
530
|
-
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
|
|
531
705
|
|
|
532
|
-
|
|
706
|
+
@staticmethod
|
|
707
|
+
def __generate_channel_id(channel_name: str) -> str:
|
|
533
708
|
"""
|
|
534
709
|
Generate a readable and consistent channel ID from a channel name.
|
|
535
710
|
|
|
@@ -547,40 +722,84 @@ class Notification(BaseNotification):
|
|
|
547
722
|
channel_id = channel_id.strip('_')
|
|
548
723
|
return channel_id[:50]
|
|
549
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
|
+
|
|
550
767
|
class NotificationHandler:
|
|
551
768
|
"""For Notification Operations """
|
|
552
|
-
|
|
769
|
+
__name = None
|
|
553
770
|
__bound = False
|
|
554
771
|
|
|
555
772
|
@classmethod
|
|
556
|
-
def
|
|
557
|
-
"""Returns
|
|
773
|
+
def get_name(cls):
|
|
774
|
+
"""Returns name or id str for Clicked Notification."""
|
|
558
775
|
if not cls.is_on_android():
|
|
559
776
|
return "Not on Android"
|
|
560
777
|
|
|
561
|
-
saved_intent = cls.
|
|
562
|
-
|
|
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")):
|
|
563
782
|
# All other notifications are not None after First notification opens app
|
|
564
783
|
# NOTE these notifications are also from Last time app was opened and they Still Give Value after first one opens App
|
|
565
784
|
# TODO Find a way to get intent when App if Swiped From recents
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
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"))
|
|
574
794
|
|
|
575
795
|
return saved_intent
|
|
576
796
|
|
|
577
797
|
@classmethod
|
|
578
|
-
def
|
|
798
|
+
def __notification_handler(cls, intent):
|
|
579
799
|
"""Calls Function Attached to notification on click.
|
|
580
800
|
Don't Call this function manual, it's Already Attach to Notification.
|
|
581
801
|
|
|
582
|
-
|
|
583
|
-
str: The Identiter of Nofication that was clicked.
|
|
802
|
+
Sets self.__name #action of Notification that was clicked from Notification.name or Notification.id
|
|
584
803
|
"""
|
|
585
804
|
if not cls.is_on_android():
|
|
586
805
|
return "Not on Android"
|
|
@@ -589,13 +808,13 @@ class NotificationHandler:
|
|
|
589
808
|
if DEV:
|
|
590
809
|
print("notifty_functions ",notifty_functions)
|
|
591
810
|
print("buttons_object", buttons_object)
|
|
592
|
-
action = None
|
|
593
811
|
try:
|
|
594
812
|
action = intent.getAction()
|
|
595
|
-
cls.
|
|
813
|
+
cls.__name = action
|
|
596
814
|
|
|
597
|
-
print("The Action --> ",action)
|
|
815
|
+
# print("The Action --> ",action)
|
|
598
816
|
if action == "android.intent.action.MAIN": # Not Open From Notification
|
|
817
|
+
cls.__name = None
|
|
599
818
|
return 'Not notification'
|
|
600
819
|
|
|
601
820
|
print(intent.getStringExtra("title"))
|
|
@@ -604,12 +823,11 @@ class NotificationHandler:
|
|
|
604
823
|
notifty_functions[action]()
|
|
605
824
|
elif action in buttons_object:
|
|
606
825
|
buttons_object[action]()
|
|
607
|
-
except Exception as
|
|
826
|
+
except Exception as notification_handler_function_error:
|
|
827
|
+
print("Error Type ",notification_handler_function_error)
|
|
608
828
|
print('Failed to run function: ', traceback.format_exc())
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
print('Notify Hanlder Failed ',e)
|
|
612
|
-
return action
|
|
829
|
+
except Exception as extracting_notification_props_error:
|
|
830
|
+
print('Notify Handler Failed ',extracting_notification_props_error)
|
|
613
831
|
|
|
614
832
|
@classmethod
|
|
615
833
|
def bindNotifyListener(cls):
|
|
@@ -618,15 +836,16 @@ class NotificationHandler:
|
|
|
618
836
|
return "Not on Android"
|
|
619
837
|
#TODO keep trying BroadcastReceiver
|
|
620
838
|
if cls.__bound:
|
|
621
|
-
print("
|
|
839
|
+
print("binding done already ")
|
|
622
840
|
return True
|
|
623
841
|
try:
|
|
624
|
-
activity.bind(on_new_intent=cls.
|
|
842
|
+
activity.bind(on_new_intent=cls.__notification_handler)
|
|
625
843
|
cls.__bound = True
|
|
626
844
|
return True
|
|
627
|
-
except Exception as
|
|
628
|
-
print('Failed to bin
|
|
845
|
+
except Exception as binding_listener_error:
|
|
846
|
+
print('Failed to bin notifications listener',binding_listener_error)
|
|
629
847
|
return False
|
|
848
|
+
|
|
630
849
|
@classmethod
|
|
631
850
|
def unbindNotifyListener(cls):
|
|
632
851
|
"""Removes Listener for Notifications Click"""
|
|
@@ -635,10 +854,10 @@ class NotificationHandler:
|
|
|
635
854
|
|
|
636
855
|
#Beta TODO use BroadcastReceiver
|
|
637
856
|
try:
|
|
638
|
-
activity.unbind(on_new_intent=cls.
|
|
857
|
+
activity.unbind(on_new_intent=cls.__notification_handler)
|
|
639
858
|
return True
|
|
640
|
-
except Exception as
|
|
641
|
-
print("Failed to unbind notifications listener: ",
|
|
859
|
+
except Exception as unbinding_listener_error:
|
|
860
|
+
print("Failed to unbind notifications listener: ",unbinding_listener_error)
|
|
642
861
|
return False
|
|
643
862
|
|
|
644
863
|
@staticmethod
|
|
@@ -646,4 +865,33 @@ class NotificationHandler:
|
|
|
646
865
|
"""Utility to check if the app is running on Android."""
|
|
647
866
|
return ON_ANDROID
|
|
648
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
|
+
|
|
649
897
|
NotificationHandler.bindNotifyListener()
|