android-notify 1.60.4.dev0__tar.gz → 1.60.5.dev0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. {android_notify-1.60.4.dev0 → android_notify-1.60.5.dev0}/PKG-INFO +4 -4
  2. {android_notify-1.60.4.dev0 → android_notify-1.60.5.dev0}/README.md +3 -3
  3. android_notify-1.60.5.dev0/android_notify/an_types.py +341 -0
  4. {android_notify-1.60.4.dev0 → android_notify-1.60.5.dev0}/android_notify/an_utils.py +67 -16
  5. {android_notify-1.60.4.dev0 → android_notify-1.60.5.dev0}/android_notify/config.py +59 -36
  6. {android_notify-1.60.4.dev0 → android_notify-1.60.5.dev0}/android_notify/core.py +91 -67
  7. {android_notify-1.60.4.dev0 → android_notify-1.60.5.dev0}/android_notify/sword.py +189 -154
  8. {android_notify-1.60.4.dev0 → android_notify-1.60.5.dev0}/android_notify.egg-info/PKG-INFO +4 -4
  9. {android_notify-1.60.4.dev0 → android_notify-1.60.5.dev0}/pyproject.toml +1 -1
  10. android_notify-1.60.4.dev0/android_notify/an_types.py +0 -251
  11. {android_notify-1.60.4.dev0 → android_notify-1.60.5.dev0}/android_notify/__init__.py +0 -0
  12. {android_notify-1.60.4.dev0 → android_notify-1.60.5.dev0}/android_notify/__main__.py +0 -0
  13. {android_notify-1.60.4.dev0 → android_notify-1.60.5.dev0}/android_notify/base.py +0 -0
  14. {android_notify-1.60.4.dev0 → android_notify-1.60.5.dev0}/android_notify/fallback-icons/flet-appicon.png +0 -0
  15. {android_notify-1.60.4.dev0 → android_notify-1.60.5.dev0}/android_notify/fallback-icons/pydroid3-appicon.png +0 -0
  16. {android_notify-1.60.4.dev0 → android_notify-1.60.5.dev0}/android_notify/styles.py +0 -0
  17. {android_notify-1.60.4.dev0 → android_notify-1.60.5.dev0}/android_notify.egg-info/SOURCES.txt +0 -0
  18. {android_notify-1.60.4.dev0 → android_notify-1.60.5.dev0}/android_notify.egg-info/dependency_links.txt +0 -0
  19. {android_notify-1.60.4.dev0 → android_notify-1.60.5.dev0}/android_notify.egg-info/entry_points.txt +0 -0
  20. {android_notify-1.60.4.dev0 → android_notify-1.60.5.dev0}/android_notify.egg-info/requires.txt +0 -0
  21. {android_notify-1.60.4.dev0 → android_notify-1.60.5.dev0}/android_notify.egg-info/top_level.txt +0 -0
  22. {android_notify-1.60.4.dev0 → android_notify-1.60.5.dev0}/docs/examples/flet-working/src/core.py +0 -0
  23. {android_notify-1.60.4.dev0 → android_notify-1.60.5.dev0}/docs/examples/flet-working/src/main.py +0 -0
  24. {android_notify-1.60.4.dev0 → android_notify-1.60.5.dev0}/docs/tests/flet/adv/main.py +0 -0
  25. {android_notify-1.60.4.dev0 → android_notify-1.60.5.dev0}/docs/tests/flet/adv/tests/__init__.py +0 -0
  26. {android_notify-1.60.4.dev0 → android_notify-1.60.5.dev0}/docs/tests/flet/adv/tests/test_android_notify_full.py +0 -0
  27. {android_notify-1.60.4.dev0 → android_notify-1.60.5.dev0}/docs/tests/flet/basic/src/core.py +0 -0
  28. {android_notify-1.60.4.dev0 → android_notify-1.60.5.dev0}/docs/tests/flet/basic/src/main.py +0 -0
  29. {android_notify-1.60.4.dev0 → android_notify-1.60.5.dev0}/docs/website/src/pages/data/laner_Sent.py +0 -0
  30. {android_notify-1.60.4.dev0 → android_notify-1.60.5.dev0}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: android-notify
3
- Version: 1.60.4.dev0
3
+ Version: 1.60.5.dev0
4
4
  Summary: A Python package that simplifies creating Android notifications in Kivy and Flet apps.
5
5
  Author-email: Fabian <fector101@yahoo.com>
6
6
  License-Expression: MIT
@@ -79,7 +79,7 @@ In your **`buildozer.spec`** file, ensure you include the following:
79
79
 
80
80
  ```ini
81
81
  # Add pyjnius so ensure it's packaged with the build
82
- requirements = python3, kivy, pyjnius, android-notify==1.60.4.dev0
82
+ requirements = python3, kivy, pyjnius, android-notify==1.60.5.dev0
83
83
  # Add permission for notifications
84
84
  android.permissions = POST_NOTIFICATIONS
85
85
  ```
@@ -91,7 +91,7 @@ In your `pyproject.toml` file, ensure you include the following:
91
91
  ```toml
92
92
  [tool.flet.android]
93
93
  dependencies = [
94
- "pyjnius","android-notify==1.60.4.dev0"
94
+ "pyjnius","android-notify==1.60.5.dev0"
95
95
  ]
96
96
 
97
97
  [tool.flet.android.permission]
@@ -100,7 +100,7 @@ dependencies = [
100
100
 
101
101
  ### Pydroid 3
102
102
  In the [pydroid 3](https://play.google.com/store/apps/details?id=ru.iiec.pydroid3) mobile app for running python code you can test some features.
103
- - In pip section where you're asked to insert `Libary name` paste `android-notify==1.60.4.dev0`
103
+ - In pip section where you're asked to insert `Libary name` paste `android-notify==1.60.5.dev0`
104
104
 
105
105
 
106
106
  ### Testing
@@ -56,7 +56,7 @@ In your **`buildozer.spec`** file, ensure you include the following:
56
56
 
57
57
  ```ini
58
58
  # Add pyjnius so ensure it's packaged with the build
59
- requirements = python3, kivy, pyjnius, android-notify==1.60.4.dev0
59
+ requirements = python3, kivy, pyjnius, android-notify==1.60.5.dev0
60
60
  # Add permission for notifications
61
61
  android.permissions = POST_NOTIFICATIONS
62
62
  ```
@@ -68,7 +68,7 @@ In your `pyproject.toml` file, ensure you include the following:
68
68
  ```toml
69
69
  [tool.flet.android]
70
70
  dependencies = [
71
- "pyjnius","android-notify==1.60.4.dev0"
71
+ "pyjnius","android-notify==1.60.5.dev0"
72
72
  ]
73
73
 
74
74
  [tool.flet.android.permission]
@@ -77,7 +77,7 @@ dependencies = [
77
77
 
78
78
  ### Pydroid 3
79
79
  In the [pydroid 3](https://play.google.com/store/apps/details?id=ru.iiec.pydroid3) mobile app for running python code you can test some features.
80
- - In pip section where you're asked to insert `Libary name` paste `android-notify==1.60.4.dev0`
80
+ - In pip section where you're asked to insert `Libary name` paste `android-notify==1.60.5.dev0`
81
81
 
82
82
 
83
83
  ### Testing
@@ -0,0 +1,341 @@
1
+ """For autocomplete Storing Reference to Available Methods"""
2
+ from typing import Literal
3
+
4
+ Importance = Literal['urgent', 'high', 'medium', 'low', 'none']
5
+ """
6
+ :argument urgent - Makes a sound and appears as a heads-up notification.
7
+
8
+ :argument high - Makes a sound.
9
+
10
+ :argument urgent - Makes no sound.
11
+
12
+ :argument urgent - Makes no sound and doesn't appear in the status bar.
13
+
14
+ :argument urgent - Makes no sound and doesn't in the status bar or shade.
15
+ """
16
+
17
+
18
+ # For Dev
19
+ # Idea for typing autocompletion and reference
20
+ class Bundle:
21
+ def putString(self, key, value):
22
+ print(f"[MOCK] Bundle.putString called with key={key}, value={value}")
23
+
24
+ def putInt(self, key, value):
25
+ print(f"[MOCK] Bundle.putInt called with key={key}, value={value}")
26
+
27
+
28
+ class String(str):
29
+ def __new__(cls, value):
30
+ print(f"[MOCK] String created with value={value}")
31
+ return str.__new__(cls, value)
32
+
33
+
34
+ class Intent:
35
+ FLAG_ACTIVITY_NEW_TASK = 'FACADE_FLAG_ACTIVITY_NEW_TASK'
36
+ CATEGORY_DEFAULT = 'FACADE_FLAG_CATEGORY_DEFAULT'
37
+
38
+ def __init__(self, context='', activity=''):
39
+ self.obj = {}
40
+ print(f"[MOCK] Intent initialized with context={context}, activity={activity}")
41
+
42
+ def setAction(self, action):
43
+ print(f"[MOCK] Intent.setAction called with: {action}")
44
+ return self
45
+
46
+ def addFlags(self, *flags):
47
+ print(f"[MOCK] Intent.addFlags called with: {flags}")
48
+ return self
49
+
50
+ def setData(self, uri):
51
+ print(f"[MOCK] Intent.setData called with: {uri}")
52
+ return self
53
+
54
+ def setFlags(self, intent_flag):
55
+ print(f"[MOCK] Intent.setFlags called with: {intent_flag}")
56
+ return self
57
+
58
+ def addCategory(self, intent_category):
59
+ print(f"[MOCK] Intent.addCategory called with: {intent_category}")
60
+ return self
61
+
62
+ def getAction(self):
63
+ print("[MOCK] Intent.getAction called")
64
+ return self
65
+
66
+ def getStringExtra(self, key):
67
+ print(f"[MOCK] Intent.getStringExtra called with key={key}")
68
+ return self
69
+
70
+ def putExtra(self, key, value):
71
+ self.obj[key] = value
72
+ print(f"[MOCK] Intent.putExtra called with key={key}, value={value}")
73
+
74
+ def putExtras(self, bundle: Bundle):
75
+ self.obj['bundle'] = bundle
76
+ print(f"[MOCK] Intent.putExtras called with bundle={bundle}")
77
+
78
+
79
+ class PendingIntent:
80
+ FLAG_IMMUTABLE = ''
81
+ FLAG_UPDATE_CURRENT = ''
82
+
83
+ def getActivity(self, context, value, action_intent, pending_intent_type):
84
+ print(
85
+ f"[MOCK] PendingIntent.getActivity called with context={context}, value={value}, action_intent={action_intent}, type={pending_intent_type}")
86
+
87
+
88
+ class BitmapFactory:
89
+ def decodeStream(self, stream):
90
+ print(f"[MOCK] BitmapFactory.decodeStream called with stream={stream}")
91
+
92
+
93
+ class BuildVersion:
94
+ SDK_INT = 0
95
+
96
+ class Manifest:
97
+ POST_NOTIFICATIONS = 'FACADE_IMPORT'
98
+
99
+ class Settings:
100
+ ACTION_APP_NOTIFICATION_SETTINGS = 'FACADE_IMPORT_ACTION_APP_NOTIFICATION_SETTINGS'
101
+ EXTRA_APP_PACKAGE = 'FACADE_IMPORT_EXTRA_APP_PACKAGE'
102
+ ACTION_APPLICATION_DETAILS_SETTINGS = 'FACADE_IMPORT_ACTION_APPLICATION_DETAILS_SETTINGS'
103
+
104
+ class Uri:
105
+ def __init__(self,package_name):
106
+ print("FACADE_URI")
107
+
108
+ class NotificationManager:
109
+ pass
110
+
111
+ class NotificationManagerClass:
112
+ pass
113
+
114
+
115
+ class NotificationChannel:
116
+ def __init__(self, channel_id, channel_name, importance):
117
+ self.description = None
118
+ self.channel_id = channel_id
119
+ self.channel = None
120
+ print(
121
+ f"[MOCK] NotificationChannel initialized with id={channel_id}, name={channel_name}, importance={importance}")
122
+
123
+ def createNotificationChannel(self, channel):
124
+ self.channel = channel
125
+ print(f"[MOCK] NotificationChannel.createNotificationChannel called with channel={channel}")
126
+
127
+ def getNotificationChannel(self, channel_id):
128
+ self.channel_id = channel_id
129
+ print(f"[MOCK] NotificationChannel.getNotificationChannel called with id={channel_id}")
130
+
131
+ def setDescription(self, description):
132
+ self.description = description
133
+ print(f"[MOCK] NotificationChannel.setDescription called with description={description}")
134
+
135
+ def getId(self):
136
+ print(f"[MOCK] NotificationChannel.getId called, returning {self.channel_id}")
137
+ return self.channel_id
138
+
139
+
140
+ class IconCompat:
141
+ def createWithBitmap(self, bitmap):
142
+ print(f"[MOCK] IconCompat.createWithBitmap called with bitmap={bitmap}")
143
+
144
+
145
+ class Color:
146
+ def __init__(self):
147
+ print("[MOCK] Color initialized")
148
+
149
+ def parseColor(self, color: str):
150
+ print(f"[MOCK] Color.parseColor called with color={color}")
151
+ return self
152
+
153
+
154
+ class RemoteViews:
155
+ def __init__(self, package_name, small_layout_id):
156
+ print(f"[MOCK] RemoteViews initialized with package_name={package_name}, layout_id={small_layout_id}")
157
+
158
+ def createWithBitmap(self, bitmap):
159
+ print(f"[MOCK] RemoteViews.createWithBitmap called with bitmap={bitmap}")
160
+
161
+ def setTextViewText(self, id, text):
162
+ print(f"[MOCK] RemoteViews.setTextViewText called with id={id}, text={text}")
163
+
164
+ def setTextColor(self, id, color: Color):
165
+ print(f"[MOCK] RemoteViews.setTextColor called with id={id}, color={color}")
166
+
167
+
168
+ class NotificationManagerCompat:
169
+ IMPORTANCE_HIGH = 4
170
+ IMPORTANCE_DEFAULT = 3
171
+ IMPORTANCE_LOW = ''
172
+ IMPORTANCE_MIN = ''
173
+ IMPORTANCE_NONE = ''
174
+
175
+ class AndroidNotification:
176
+ DEFAULT_ALL = 3
177
+ PRIORITY_HIGH = 4
178
+ PRIORITY_DEFAULT = ''
179
+ PRIORITY_LOW = ''
180
+ PRIORITY_MIN = ''
181
+
182
+ class NotificationCompat:
183
+ DEFAULT_ALL = 3
184
+ PRIORITY_HIGH = 4
185
+ PRIORITY_DEFAULT = ''
186
+ PRIORITY_LOW = ''
187
+ PRIORITY_MIN = ''
188
+
189
+
190
+ class MActions:
191
+ def clear(self):
192
+ """This Removes all buttons"""
193
+ print('[MOCK] MActions.clear called')
194
+
195
+
196
+ class NotificationCompatBuilder:
197
+ def __init__(self, context, channel_id):
198
+ self.mActions = MActions()
199
+ print(f"[MOCK] NotificationCompatBuilder initialized with context={context}, channel_id={channel_id}")
200
+
201
+ def setProgress(self, max_value, current_value, endless):
202
+ print(f"[MOCK] setProgress called with max={max_value}, current={current_value}, endless={endless}")
203
+
204
+ def setStyle(self, style):
205
+ print(f"[MOCK] setStyle called with style={style}")
206
+
207
+ def setContentTitle(self, title):
208
+ print(f"[MOCK] setContentTitle called with title={title}")
209
+
210
+ def setContentText(self, text):
211
+ print(f"[MOCK] setContentText called with text={text}")
212
+
213
+ def setSmallIcon(self, icon):
214
+ print(f"[MOCK] setSmallIcon called with icon={icon}")
215
+
216
+ def setLargeIcon(self, icon):
217
+ print(f"[MOCK] setLargeIcon called with icon={icon}")
218
+
219
+ def setAutoCancel(self, auto_cancel: bool):
220
+ print(f"[MOCK] setAutoCancel called with auto_cancel={auto_cancel}")
221
+
222
+ def setPriority(self, priority: Importance):
223
+ print(f"[MOCK] setPriority called with priority={priority}")
224
+
225
+ def setDefaults(self, defaults):
226
+ print(f"[MOCK] setDefaults called with defaults={defaults}")
227
+
228
+ def setOngoing(self, persistent: bool):
229
+ print(f"[MOCK] setOngoing called with persistent={persistent}")
230
+
231
+ def setOnlyAlertOnce(self, state):
232
+ print(f"[MOCK] setOnlyAlertOnce called with state={state}")
233
+
234
+ def build(self):
235
+ print("[MOCK] build called")
236
+
237
+ def setContentIntent(self, pending_action_intent: PendingIntent):
238
+ print(f"[MOCK] setContentIntent called with {pending_action_intent}")
239
+
240
+ def addAction(self, icon_int, action_text, pending_action_intent):
241
+ print(f"[MOCK] addAction called with icon={icon_int}, text={action_text}, intent={pending_action_intent}")
242
+
243
+ def setShowWhen(self, state):
244
+ print(f"[MOCK] setShowWhen called with state={state}")
245
+
246
+ def setWhen(self, time_ms):
247
+ print(f"[MOCK] setWhen called with time_ms={time_ms}")
248
+
249
+ def setCustomContentView(self, layout):
250
+ print(f"[MOCK] setCustomContentView called with layout={layout}")
251
+
252
+ def setCustomBigContentView(self, layout):
253
+ print(f"[MOCK] setCustomBigContentView called with layout={layout}")
254
+
255
+ def setSubText(self, text):
256
+ print(f"[MOCK] setSubText called with text={text}")
257
+
258
+ def setColor(self, color: Color) -> None:
259
+ print(f"[MOCK] setColor called with color={color}")
260
+
261
+
262
+ class NotificationCompatBigTextStyle:
263
+ def bigText(self, body):
264
+ print(f"[MOCK] NotificationCompatBigTextStyle.bigText called with body={body}")
265
+ return self
266
+
267
+
268
+ class NotificationCompatBigPictureStyle:
269
+ def bigPicture(self, bitmap):
270
+ print(f"[MOCK] NotificationCompatBigPictureStyle.bigPicture called with bitmap={bitmap}")
271
+ return self
272
+
273
+
274
+ class NotificationCompatInboxStyle:
275
+ def addLine(self, line):
276
+ print(f"[MOCK] NotificationCompatInboxStyle.addLine called with line={line}")
277
+ return self
278
+
279
+
280
+ class NotificationCompatDecoratedCustomViewStyle:
281
+ def __init__(self):
282
+ print("[MOCK] NotificationCompatDecoratedCustomViewStyle initialized")
283
+
284
+
285
+ class Permission:
286
+ POST_NOTIFICATIONS = ''
287
+
288
+
289
+ def check_permission(permission: Permission.POST_NOTIFICATIONS):
290
+ print(f"[MOCK] check_permission called with {permission}")
291
+ print(permission)
292
+
293
+
294
+ def request_permissions(_list: [], _callback):
295
+ print(f"[MOCK] request_permissions called with {_list}")
296
+ _callback()
297
+
298
+
299
+ class AndroidActivity:
300
+ def bind(self, on_new_intent):
301
+ print(f"[MOCK] AndroidActivity.bind called with {on_new_intent}")
302
+
303
+ def unbind(self, on_new_intent):
304
+ print(f"[MOCK] AndroidActivity.unbind called with {on_new_intent}")
305
+
306
+
307
+ class PythonActivity:
308
+ mActivity = "[MOCK] mActivity used"
309
+ def __init__(self):
310
+ print("[MOCK] PythonActivity initialized")
311
+
312
+
313
+ class DummyIcon:
314
+ icon = 101
315
+
316
+ def __init__(self):
317
+ print("[MOCK] DummyIcon initialized")
318
+
319
+
320
+ class Context:
321
+ def __init__(self):
322
+ print("[MOCK] Context initialized")
323
+ pass
324
+
325
+ @staticmethod
326
+ def getApplicationInfo():
327
+ print("[MOCK] Context.getApplicationInfo called")
328
+ return DummyIcon
329
+
330
+ @staticmethod
331
+ def getResources():
332
+ print("[MOCK] Context.getResources called")
333
+ return None
334
+
335
+ @staticmethod
336
+ def getPackageName():
337
+ print("[MOCK] Context.getPackageName called")
338
+ return None # TODO get package name from buildozer.spec file
339
+
340
+ # Now writing Knowledge from errors
341
+ # notify.(int, Builder.build()) # must be int
@@ -4,16 +4,18 @@ import inspect, os, re, traceback
4
4
  from .config import autoclass
5
5
  from .an_types import Importance
6
6
  from .config import (
7
- get_python_activity_context, app_storage_path,ON_ANDROID,
8
- BitmapFactory, BuildVersion, Bundle,
9
- NotificationManagerClass,AndroidNotification
10
- )
7
+ get_python_activity_context, app_storage_path, ON_ANDROID,
8
+ BitmapFactory, BuildVersion, Bundle,
9
+ NotificationManagerClass, AndroidNotification, Intent, Settings, Uri, String, Manifest
10
+
11
+ )
11
12
 
12
13
  if ON_ANDROID:
13
14
  Color = autoclass('android.graphics.Color')
14
15
  else:
15
16
  from .an_types import Color
16
17
 
18
+
17
19
  def can_accept_arguments(func, *args, **kwargs):
18
20
  try:
19
21
  sig = inspect.signature(func)
@@ -22,11 +24,13 @@ def can_accept_arguments(func, *args, **kwargs):
22
24
  except TypeError:
23
25
  return False
24
26
 
27
+
25
28
  if ON_ANDROID:
26
29
  context = get_python_activity_context()
27
30
  else:
28
31
  context = None
29
32
 
33
+
30
34
  def get_android_importance(importance: Importance):
31
35
  """
32
36
  Returns Android Importance Values
@@ -50,6 +54,7 @@ def get_android_importance(importance: Importance):
50
54
  return value
51
55
  # side-note 'medium' = NotificationCompat.PRIORITY_LOW and 'low' = NotificationCompat.PRIORITY_MIN # weird but from docs
52
56
 
57
+
53
58
  def generate_channel_id(channel_name: str) -> str:
54
59
  """
55
60
  Generate a readable and consistent channel ID from a channel name.
@@ -68,15 +73,15 @@ def generate_channel_id(channel_name: str) -> str:
68
73
  channel_id = channel_id.strip('_')
69
74
  return channel_id[:50]
70
75
 
76
+
71
77
  def get_img_from_path(relative_path):
72
- app_folder = os.path.join(app_storage_path(), 'app')
78
+ app_folder = os.path.join(app_storage_path(), 'app')
73
79
  img_full_path = os.path.join(app_folder, relative_path)
74
- img_name = os.path.basename(img_full_path)
75
80
  if not os.path.exists(img_full_path):
76
- print(f"\nImage - {img_name} not found at path: {app_folder}, (Local images gotten from App Path)")
81
+ print(f'\nImage: "{img_full_path}" Not Found, (Local images gotten from App Path)')
77
82
  try:
78
83
  print("- These are the existing files in your app Folder:")
79
- print('[' + ', '.join(os.listdir(app_folder)) + ']')
84
+ print('[' + ', '.join(os.listdir(app_folder)) + ']\n')
80
85
  except Exception as could_not_get_files_in_path_error:
81
86
  print('Exception: ', could_not_get_files_in_path_error)
82
87
  print("Couldn't get Files in App Folder")
@@ -84,6 +89,7 @@ def get_img_from_path(relative_path):
84
89
  return get_bitmap_from_path(img_full_path)
85
90
  # TODO test with a badly written Image and catch error
86
91
 
92
+
87
93
  def setLayoutText(layout, id, text, color):
88
94
  # checked if self.title_color available before entering method
89
95
  if id and text:
@@ -91,6 +97,7 @@ def setLayoutText(layout, id, text, color):
91
97
  if color:
92
98
  layout.setTextColor(id, Color.parseColor(color))
93
99
 
100
+
94
101
  def get_bitmap_from_url(url, callback, logs):
95
102
  """Gets Bitmap from url
96
103
 
@@ -119,6 +126,7 @@ def get_bitmap_from_url(url, callback, logs):
119
126
  print('Error Type ', extracting_bitmap_frm_URL_error)
120
127
  print('Failed to get Bitmap from URL ', traceback.format_exc())
121
128
 
129
+
122
130
  def add_data_to_intent(intent, title):
123
131
  """Persist Some data to notification object for later use"""
124
132
  bundle = Bundle()
@@ -127,13 +135,14 @@ def add_data_to_intent(intent, title):
127
135
  bundle.putInt("notify_id", 101)
128
136
  intent.putExtras(bundle)
129
137
 
138
+
130
139
  def get_sound_uri(res_sound_name):
131
- if not res_sound_name:
132
- return None
140
+ if not res_sound_name: # Incase it's None
141
+ return None
142
+
143
+ package_name = context.getPackageName()
144
+ return Uri.parse(f"android.resource://{package_name}/raw/{res_sound_name}")
133
145
 
134
- package_name = context.getPackageName()
135
- Uri = autoclass('android.net.Uri')
136
- return Uri.parse(f"android.resource://{package_name}/raw/{res_sound_name}")
137
146
 
138
147
  def get_package_path():
139
148
  """
@@ -142,10 +151,11 @@ def get_package_path():
142
151
  """
143
152
  return os.path.dirname(os.path.abspath(__file__))
144
153
 
154
+
145
155
  def get_bitmap_from_path(img_full_path):
146
- Uri = autoclass('android.net.Uri')
147
- uri = Uri.parse(f"file://{img_full_path}")
148
- return BitmapFactory.decodeStream(context.getContentResolver().openInputStream(uri))
156
+ uri = Uri.parse(f"file://{img_full_path}")
157
+ return BitmapFactory.decodeStream(context.getContentResolver().openInputStream(uri))
158
+
149
159
 
150
160
  def icon_finder(icon_name):
151
161
  """Get the full path to an icon file."""
@@ -156,3 +166,44 @@ def icon_finder(icon_name):
156
166
  # Fallback if pkg_resources not available
157
167
  package_dir = get_package_path()
158
168
  return os.path.join(package_dir, "fallback-icons", icon_name)
169
+
170
+
171
+ def can_show_permission_request_popup():
172
+ """
173
+ Check if we can show permission request popup for POST_NOTIFICATIONS
174
+ :return: bool
175
+ """
176
+ if not ON_ANDROID:
177
+ return False
178
+
179
+ if BuildVersion.SDK_INT < 33:
180
+ return False
181
+
182
+ return context.shouldShowRequestPermissionRationale(Manifest.POST_NOTIFICATIONS)
183
+
184
+
185
+ def open_settings_screen():
186
+ if not context:
187
+ print("android_notify - Can't open settings screen, No context [not On Android]")
188
+ return None
189
+ intent = Intent()
190
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
191
+ package_name = String(context.getPackageName()) # String() is very important else fails silently with a toast
192
+ # saying "The app wasn't found in the list of installed apps" - Xiaomi or "unable to find application to perform this action" - Samsung and Techno
193
+
194
+ if BuildVersion.SDK_INT >= 26: # Android 8.0 - android.os.Build.VERSION_CODES.O
195
+ intent.setAction(Settings.ACTION_APP_NOTIFICATION_SETTINGS)
196
+ intent.putExtra(Settings.EXTRA_APP_PACKAGE, package_name)
197
+ elif BuildVersion.SDK_INT >= 22: # Android 5.0 - Build.VERSION_CODES.LOLLIPOP
198
+ intent.setAction("android.settings.APP_NOTIFICATION_SETTINGS")
199
+ intent.putExtra("app_package", package_name)
200
+ intent.putExtra("app_uid", context.getApplicationInfo().uid)
201
+ else: # Last Retort is to open App Settings Screen
202
+ intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
203
+ intent.addCategory(Intent.CATEGORY_DEFAULT)
204
+ intent.setData(Uri.parse("package:" + package_name))
205
+
206
+ context.startActivity(intent)
207
+ return None
208
+
209
+ # https://stackoverflow.com/a/45192258/19961621