android-notify 1.60.9.dev0__tar.gz → 1.60.10__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 (54) hide show
  1. {android_notify-1.60.9.dev0 → android_notify-1.60.10}/PKG-INFO +45 -28
  2. {android_notify-1.60.9.dev0 → android_notify-1.60.10}/README.md +44 -27
  3. {android_notify-1.60.9.dev0 → android_notify-1.60.10}/android_notify/config.py +1 -2
  4. {android_notify-1.60.9.dev0 → android_notify-1.60.10}/android_notify/core.py +4 -3
  5. {android_notify-1.60.9.dev0 → android_notify-1.60.10}/android_notify/internal/java_classes.py +17 -10
  6. {android_notify-1.60.9.dev0 → android_notify-1.60.10}/android_notify/internal/logger.py +9 -7
  7. {android_notify-1.60.9.dev0 → android_notify-1.60.10}/android_notify/internal/permissions.py +21 -8
  8. {android_notify-1.60.9.dev0 → android_notify-1.60.10}/android_notify/widgets/images.py +1 -2
  9. {android_notify-1.60.9.dev0 → android_notify-1.60.10}/android_notify/widgets/texts.py +1 -5
  10. {android_notify-1.60.9.dev0 → android_notify-1.60.10}/android_notify.egg-info/PKG-INFO +45 -28
  11. {android_notify-1.60.9.dev0 → android_notify-1.60.10}/android_notify.egg-info/SOURCES.txt +0 -2
  12. {android_notify-1.60.9.dev0 → android_notify-1.60.10}/pyproject.toml +1 -1
  13. android_notify-1.60.9.dev0/android_notify/tests/flet/basic/src/core.py +0 -221
  14. android_notify-1.60.9.dev0/android_notify/tests/flet/flet-working/src/core.py +0 -221
  15. {android_notify-1.60.9.dev0 → android_notify-1.60.10}/android_notify/__init__.py +0 -0
  16. {android_notify-1.60.9.dev0 → android_notify-1.60.10}/android_notify/__main__.py +0 -0
  17. {android_notify-1.60.9.dev0 → android_notify-1.60.10}/android_notify/base.py +0 -0
  18. {android_notify-1.60.9.dev0 → android_notify-1.60.10}/android_notify/fallback-icons/flet-appicon.png +0 -0
  19. {android_notify-1.60.9.dev0 → android_notify-1.60.10}/android_notify/fallback-icons/pydroid3-appicon.png +0 -0
  20. {android_notify-1.60.9.dev0 → android_notify-1.60.10}/android_notify/internal/an_types.py +0 -0
  21. {android_notify-1.60.9.dev0 → android_notify-1.60.10}/android_notify/internal/android.py +0 -0
  22. {android_notify-1.60.9.dev0 → android_notify-1.60.10}/android_notify/internal/channels.py +0 -0
  23. {android_notify-1.60.9.dev0 → android_notify-1.60.10}/android_notify/internal/facade.py +0 -0
  24. {android_notify-1.60.9.dev0 → android_notify-1.60.10}/android_notify/internal/helper.py +0 -0
  25. {android_notify-1.60.9.dev0 → android_notify-1.60.10}/android_notify/internal/intents.py +0 -0
  26. {android_notify-1.60.9.dev0 → android_notify-1.60.10}/android_notify/styles.py +0 -0
  27. {android_notify-1.60.9.dev0 → android_notify-1.60.10}/android_notify/sword.py +0 -0
  28. {android_notify-1.60.9.dev0 → android_notify-1.60.10}/android_notify/tests/__init__.py +0 -0
  29. {android_notify-1.60.9.dev0 → android_notify-1.60.10}/android_notify/tests/android_notify_test.py +0 -0
  30. {android_notify-1.60.9.dev0 → android_notify-1.60.10}/android_notify/tests/base_test.py +0 -0
  31. {android_notify-1.60.9.dev0 → android_notify-1.60.10}/android_notify/tests/flet/adv/main.py +0 -0
  32. {android_notify-1.60.9.dev0 → android_notify-1.60.10}/android_notify/tests/flet/adv/tests/__init__.py +0 -0
  33. {android_notify-1.60.9.dev0 → android_notify-1.60.10}/android_notify/tests/flet/adv/tests/test_android_notify_full.py +0 -0
  34. {android_notify-1.60.9.dev0 → android_notify-1.60.10}/android_notify/tests/flet/basic/src/main.py +0 -0
  35. {android_notify-1.60.9.dev0 → android_notify-1.60.10}/android_notify/tests/flet/flet-working/src/main.py +0 -0
  36. {android_notify-1.60.9.dev0 → android_notify-1.60.10}/android_notify/tests/main.py +0 -0
  37. {android_notify-1.60.9.dev0 → android_notify-1.60.10}/android_notify/tests/p4a/hook.py +0 -0
  38. {android_notify-1.60.9.dev0 → android_notify-1.60.10}/android_notify/tests/serivces/wallpaper.py +0 -0
  39. {android_notify-1.60.9.dev0 → android_notify-1.60.10}/android_notify/tests/test_basic_notifications.py +0 -0
  40. {android_notify-1.60.9.dev0 → android_notify-1.60.10}/android_notify/tests/test_notification_actions.py +0 -0
  41. {android_notify-1.60.9.dev0 → android_notify-1.60.10}/android_notify/tests/test_notification_appearance.py +0 -0
  42. {android_notify-1.60.9.dev0 → android_notify-1.60.10}/android_notify/tests/test_notification_behavior.py +0 -0
  43. {android_notify-1.60.9.dev0 → android_notify-1.60.10}/android_notify/tests/test_notification_channels.py +0 -0
  44. {android_notify-1.60.9.dev0 → android_notify-1.60.10}/android_notify/tests/test_notification_clear.py +0 -0
  45. {android_notify-1.60.9.dev0 → android_notify-1.60.10}/android_notify/tests/test_notification_permission.py +0 -0
  46. {android_notify-1.60.9.dev0 → android_notify-1.60.10}/android_notify/tests/test_notification_progress.py +0 -0
  47. {android_notify-1.60.9.dev0 → android_notify-1.60.10}/android_notify/tests/test_notification_sound.py +0 -0
  48. {android_notify-1.60.9.dev0 → android_notify-1.60.10}/android_notify/tests/test_notification_styles.py +0 -0
  49. {android_notify-1.60.9.dev0 → android_notify-1.60.10}/android_notify.egg-info/dependency_links.txt +0 -0
  50. {android_notify-1.60.9.dev0 → android_notify-1.60.10}/android_notify.egg-info/entry_points.txt +0 -0
  51. {android_notify-1.60.9.dev0 → android_notify-1.60.10}/android_notify.egg-info/requires.txt +0 -0
  52. {android_notify-1.60.9.dev0 → android_notify-1.60.10}/android_notify.egg-info/top_level.txt +0 -0
  53. {android_notify-1.60.9.dev0 → android_notify-1.60.10}/docs/website/src/pages/data/laner_Sent.py +0 -0
  54. {android_notify-1.60.9.dev0 → android_notify-1.60.10}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: android-notify
3
- Version: 1.60.9.dev0
3
+ Version: 1.60.10
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
@@ -81,9 +81,14 @@ In your **`buildozer.spec`** file, ensure you include the following:
81
81
 
82
82
  ```ini
83
83
  # Add pyjnius so ensure it's packaged with the build
84
- requirements = python3, kivy, pyjnius, android-notify==1.60.9.dev0
84
+ requirements = python3, kivy, pyjnius, android-notify
85
85
  # Add permission for notifications
86
86
  android.permissions = POST_NOTIFICATIONS
87
+
88
+ # Required dependency (write exactly as shown, no quotation marks)
89
+ android.gradle_dependencies = androidx.core:core-ktx:1.15.0
90
+ android.enable_androidx = True
91
+ android.api = 35
87
92
  ```
88
93
 
89
94
  </details>
@@ -93,13 +98,13 @@ android.permissions = POST_NOTIFICATIONS
93
98
  <summary><b>Flet apps:</b></summary>
94
99
  <br/>
95
100
 
96
- In your `pyproject.toml` file, ensure you include the following:
101
+ In your `pyproject.toml` file, ensure you include the following:
97
102
 
98
103
 
99
104
  ```toml
100
105
  [tool.flet.android]
101
106
  dependencies = [
102
- "pyjnius","android-notify==1.60.9.dev0"
107
+ "pyjnius","android-notify==1.60.10.dev0"
103
108
  ]
104
109
 
105
110
  [tool.flet.android.permission]
@@ -109,7 +114,35 @@ dependencies = [
109
114
 
110
115
  </details>
111
116
 
117
+ <details>
118
+
119
+ <summary><b>Desktop</b></summary>
120
+ <br/>
121
+
122
+ For IDE IntelliSense Can be installed via `pip install`:
123
+
124
+ ```bash
125
+ pip install android_notify
126
+ android-notify -v
127
+ ```
128
+
129
+ </details>
130
+
131
+ ------
132
+ ## Installing without Androidx
133
+ How to use without `gradle_dependencies`
134
+ Use `android-notify==1.60.10.dev0` to install via `pip`
135
+
136
+ <details>
137
+ <summary><b>In Kivy</b></summary>
138
+ <br/>
139
+
140
+ ```ini
141
+ # buildozer.spec
142
+ requirements = python3, kivy, pyjnius, android-notify==1.60.10.dev0
143
+ ```
112
144
 
145
+ </details>
113
146
 
114
147
  <details>
115
148
 
@@ -117,10 +150,10 @@ dependencies = [
117
150
  <br/>
118
151
 
119
152
  On 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.
120
- - In pip section where you're asked to insert `Libary name` paste `android-notify==1.60.9.dev0`
153
+ - In pip section where you're asked to insert `Libary name` paste `android-notify==1.60.10.dev0`
121
154
  - Minimal working example
122
155
  ```py
123
- # Testing with `android-notify==1.60.9.dev0` on pydroid
156
+ # Testing with `android-notify==1.60.10.dev0` on pydroid
124
157
  from kivy.app import App
125
158
  from kivy.uix.boxlayout import BoxLayout
126
159
  from kivy.uix.button import Button
@@ -160,23 +193,11 @@ if __name__ == "__main__":
160
193
  </details>
161
194
 
162
195
 
163
- <details>
164
-
165
- <summary><b>Desktop</b></summary>
166
- <br/>
167
-
168
- For IDE IntelliSense Can be installed via `pip install`:
169
-
170
- ```bash
171
- pip install android_notify
172
- android-notify -v
173
- ```
174
- </details>
175
196
 
176
197
  ## Documentation
177
- For Dev Version use
198
+ For Dev Version usage
178
199
  ```ini
179
- requirements = python3, kivy, pyjnius, https://github.com/Fector101/android_notify/archive/without-androidx.zip
200
+ requirements = python3, kivy, pyjnius, https://github.com/Fector101/android_notify/archive/main.zip
180
201
  ```
181
202
 
182
203
  <details>
@@ -185,7 +206,7 @@ requirements = python3, kivy, pyjnius, https://github.com/Fector101/android_noti
185
206
  - Make things happen without being in your app
186
207
  ```python
187
208
  from android_notify import Notification
188
- notification = Notification(title="Receiver Notification")
209
+ notification = Notification(title="Reciver Notification")
189
210
  notification.addButton(text="Stop", receiver_name="CarouselReceiver", action="ACTION_STOP")
190
211
  notification.addButton(text="Skip", receiver_name="CarouselReceiver", action="ACTION_SKIP")
191
212
  ```
@@ -248,7 +269,7 @@ Notification.createChannel(id='shake', name="Shake Passage", vibrate=True)
248
269
 
249
270
  n=Notification(title='Vibrate',channel_id='shake')
250
271
  n.setVibrate() # for less than android 8
251
- n.fVibrate() # To Force Vibrate (when user turned off in settings)
272
+ n.fVibrate() # To Force Vibrate
252
273
  n.send()
253
274
  ```
254
275
 
@@ -286,9 +307,5 @@ from android_notify import Notification, NotificationHandler
286
307
 
287
308
  ## ☕ Support the Project
288
309
 
289
- If you find this project helpful, consider buying me a coffee! 😊
290
- Or Giving it a star on 🌟 [GitHub](https://github.com/Fector101/android_notify/) Your support helps maintain and improve the project.
291
-
292
- <a href="https://www.buymeacoffee.com/fector101" target="_blank">
293
- <img src="https://cdn.buymeacoffee.com/buttons/v2/default-yellow.png" alt="Buy Me A Coffee" height="60">
294
- </a>
310
+ If you find this project helpful, your support would help me continue working on open-source projects
311
+ [donate](https://www.buymeacoffee.com/fector101)
@@ -58,9 +58,14 @@ In your **`buildozer.spec`** file, ensure you include the following:
58
58
 
59
59
  ```ini
60
60
  # Add pyjnius so ensure it's packaged with the build
61
- requirements = python3, kivy, pyjnius, android-notify==1.60.9.dev0
61
+ requirements = python3, kivy, pyjnius, android-notify
62
62
  # Add permission for notifications
63
63
  android.permissions = POST_NOTIFICATIONS
64
+
65
+ # Required dependency (write exactly as shown, no quotation marks)
66
+ android.gradle_dependencies = androidx.core:core-ktx:1.15.0
67
+ android.enable_androidx = True
68
+ android.api = 35
64
69
  ```
65
70
 
66
71
  </details>
@@ -70,13 +75,13 @@ android.permissions = POST_NOTIFICATIONS
70
75
  <summary><b>Flet apps:</b></summary>
71
76
  <br/>
72
77
 
73
- In your `pyproject.toml` file, ensure you include the following:
78
+ In your `pyproject.toml` file, ensure you include the following:
74
79
 
75
80
 
76
81
  ```toml
77
82
  [tool.flet.android]
78
83
  dependencies = [
79
- "pyjnius","android-notify==1.60.9.dev0"
84
+ "pyjnius","android-notify==1.60.10.dev0"
80
85
  ]
81
86
 
82
87
  [tool.flet.android.permission]
@@ -86,7 +91,35 @@ dependencies = [
86
91
 
87
92
  </details>
88
93
 
94
+ <details>
95
+
96
+ <summary><b>Desktop</b></summary>
97
+ <br/>
98
+
99
+ For IDE IntelliSense Can be installed via `pip install`:
100
+
101
+ ```bash
102
+ pip install android_notify
103
+ android-notify -v
104
+ ```
105
+
106
+ </details>
107
+
108
+ ------
109
+ ## Installing without Androidx
110
+ How to use without `gradle_dependencies`
111
+ Use `android-notify==1.60.10.dev0` to install via `pip`
112
+
113
+ <details>
114
+ <summary><b>In Kivy</b></summary>
115
+ <br/>
116
+
117
+ ```ini
118
+ # buildozer.spec
119
+ requirements = python3, kivy, pyjnius, android-notify==1.60.10.dev0
120
+ ```
89
121
 
122
+ </details>
90
123
 
91
124
  <details>
92
125
 
@@ -94,10 +127,10 @@ dependencies = [
94
127
  <br/>
95
128
 
96
129
  On 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.
97
- - In pip section where you're asked to insert `Libary name` paste `android-notify==1.60.9.dev0`
130
+ - In pip section where you're asked to insert `Libary name` paste `android-notify==1.60.10.dev0`
98
131
  - Minimal working example
99
132
  ```py
100
- # Testing with `android-notify==1.60.9.dev0` on pydroid
133
+ # Testing with `android-notify==1.60.10.dev0` on pydroid
101
134
  from kivy.app import App
102
135
  from kivy.uix.boxlayout import BoxLayout
103
136
  from kivy.uix.button import Button
@@ -137,23 +170,11 @@ if __name__ == "__main__":
137
170
  </details>
138
171
 
139
172
 
140
- <details>
141
-
142
- <summary><b>Desktop</b></summary>
143
- <br/>
144
-
145
- For IDE IntelliSense Can be installed via `pip install`:
146
-
147
- ```bash
148
- pip install android_notify
149
- android-notify -v
150
- ```
151
- </details>
152
173
 
153
174
  ## Documentation
154
- For Dev Version use
175
+ For Dev Version usage
155
176
  ```ini
156
- requirements = python3, kivy, pyjnius, https://github.com/Fector101/android_notify/archive/without-androidx.zip
177
+ requirements = python3, kivy, pyjnius, https://github.com/Fector101/android_notify/archive/main.zip
157
178
  ```
158
179
 
159
180
  <details>
@@ -162,7 +183,7 @@ requirements = python3, kivy, pyjnius, https://github.com/Fector101/android_noti
162
183
  - Make things happen without being in your app
163
184
  ```python
164
185
  from android_notify import Notification
165
- notification = Notification(title="Receiver Notification")
186
+ notification = Notification(title="Reciver Notification")
166
187
  notification.addButton(text="Stop", receiver_name="CarouselReceiver", action="ACTION_STOP")
167
188
  notification.addButton(text="Skip", receiver_name="CarouselReceiver", action="ACTION_SKIP")
168
189
  ```
@@ -225,7 +246,7 @@ Notification.createChannel(id='shake', name="Shake Passage", vibrate=True)
225
246
 
226
247
  n=Notification(title='Vibrate',channel_id='shake')
227
248
  n.setVibrate() # for less than android 8
228
- n.fVibrate() # To Force Vibrate (when user turned off in settings)
249
+ n.fVibrate() # To Force Vibrate
229
250
  n.send()
230
251
  ```
231
252
 
@@ -263,9 +284,5 @@ from android_notify import Notification, NotificationHandler
263
284
 
264
285
  ## ☕ Support the Project
265
286
 
266
- If you find this project helpful, consider buying me a coffee! 😊
267
- Or Giving it a star on 🌟 [GitHub](https://github.com/Fector101/android_notify/) Your support helps maintain and improve the project.
268
-
269
- <a href="https://www.buymeacoffee.com/fector101" target="_blank">
270
- <img src="https://cdn.buymeacoffee.com/buttons/v2/default-yellow.png" alt="Buy Me A Coffee" height="60">
271
- </a>
287
+ If you find this project helpful, your support would help me continue working on open-source projects
288
+ [donate](https://www.buymeacoffee.com/fector101)
@@ -1,7 +1,6 @@
1
1
  import os
2
2
 
3
- __version__ = "1.60.9.dev0"
4
-
3
+ __version__ = "1.60.10"
5
4
 
6
5
  from .internal.java_classes import autoclass, cast, NotificationManager
7
6
  from .internal.logger import logger
@@ -6,7 +6,7 @@ from android_notify.internal.logger import logger
6
6
  from android_notify.config import get_python_activity, on_android_platform, get_python_activity_context
7
7
  from android_notify.internal.permissions import has_notification_permission, ask_notification_permission
8
8
  from android_notify.internal.java_classes import autoclass, BuildVersion, BitmapFactory, NotificationChannel, NotificationManagerCompat, NotificationCompat, NotificationCompatBuilder, \
9
- NotificationCompatBigTextStyle, NotificationCompatBigPictureStyle, NotificationCompatInboxStyle
9
+ NotificationCompatBigTextStyle, NotificationCompatBigPictureStyle, NotificationCompatInboxStyle, IconCompat
10
10
 
11
11
 
12
12
 
@@ -60,7 +60,6 @@ def get_image_uri(relative_path):
60
60
 
61
61
  def get_icon_object(uri):
62
62
  context = get_python_activity_context()
63
- IconCompat = autoclass('android.graphics.drawable.Icon')
64
63
  bitmap = BitmapFactory.decodeStream(context.getContentResolver().openInputStream(uri))
65
64
  return IconCompat.createWithBitmap(bitmap)
66
65
 
@@ -115,7 +114,9 @@ def send_notification(
115
114
  return None
116
115
  context = get_python_activity_context()
117
116
 
118
- asks_permission_if_needed(legacy=True)
117
+
118
+ asks_permission_if_needed()
119
+
119
120
  channel_id = channel_name.replace(' ', '_').lower().lower() if not channel_id else channel_id
120
121
  # Get notification manager
121
122
  notification_manager = context.getSystemService(context.NOTIFICATION_SERVICE)
@@ -39,24 +39,31 @@ if on_android_platform():
39
39
  Manifest = autoclass('android.Manifest$permission')
40
40
  Color = autoclass('android.graphics.Color')
41
41
  Context = autoclass('android.content.Context')
42
- PackageManager = autoclass("android.content.pm.PackageManager")
43
42
  except Exception as e:
44
43
  from .facade import *
45
44
  logger.exception("Didn't get Basic Java Classes")
46
45
 
47
46
  # noinspection PyBroadException
48
47
  try:
49
- NotificationManagerCompat = NotificationManager # I need other files identical to main branch so it's easier to maintain
50
- NotificationCompat = autoclass("android.app.Notification")
51
- NotificationCompatBuilder = autoclass('android.app.Notification$Builder')
52
- NotificationCompatBigTextStyle = autoclass('android.app.Notification$BigTextStyle')
53
- NotificationCompatBigPictureStyle = autoclass('android.app.Notification$BigPictureStyle')
54
- NotificationCompatInboxStyle = autoclass('android.app.Notification$InboxStyle')
48
+ NotificationManagerCompat = autoclass('androidx.core.app.NotificationManagerCompat')
49
+ NotificationCompat = autoclass('androidx.core.app.NotificationCompat')
50
+ NotificationCompatBuilder = autoclass('androidx.core.app.NotificationCompat$Builder')
51
+ IconCompat = autoclass('androidx.core.graphics.drawable.IconCompat')
55
52
 
56
- except Exception as styles_import_error:
57
- logger.exception(styles_import_error)
58
- from .facade import *
53
+ # Notification Design
54
+ NotificationCompatBigTextStyle = autoclass('androidx.core.app.NotificationCompat$BigTextStyle')
55
+ NotificationCompatBigPictureStyle = autoclass('androidx.core.app.NotificationCompat$BigPictureStyle')
56
+ NotificationCompatInboxStyle = autoclass('androidx.core.app.NotificationCompat$InboxStyle')
57
+ NotificationCompatDecoratedCustomViewStyle = autoclass('androidx.core.app.NotificationCompat$DecoratedCustomViewStyle')
58
+
59
+ except Exception as dependencies_import_error:
60
+ logger.exception("""
61
+ Dependency Error: Add the following in buildozer.spec:
62
+ * android.gradle_dependencies = androidx.core:core-ktx:1.15.0, androidx.core:core:1.6.0
63
+ * android.enable_androidx = True
64
+ """)
59
65
 
66
+ from .facade import *
60
67
  else:
61
68
  cast = lambda x, y: x
62
69
  autoclass = lambda x: None
@@ -40,17 +40,12 @@ def android_print(msg):
40
40
 
41
41
 
42
42
  def kivy_logger_patch():
43
- if not on_kivy_android():
43
+ if on_flet_app():
44
44
  return
45
45
 
46
- handler = logging.StreamHandler(sys.stdout)
47
- formatter = KivyColorFormatter()
48
- handler.setFormatter(formatter)
49
-
46
+ # logs got weird in kivy app (duplicates logs)
50
47
  # Avoid duplicate logs if root logger is configured
51
48
  logger.propagate = False
52
- logger.addHandler(handler)
53
- logger._configured = True
54
49
 
55
50
 
56
51
  class KivyColorFormatter(logging.Formatter):
@@ -76,6 +71,13 @@ class KivyColorFormatter(logging.Formatter):
76
71
 
77
72
 
78
73
  logger = logging.getLogger("android_notify")
74
+
75
+ handler = logging.StreamHandler(sys.stdout)
76
+ formatter = KivyColorFormatter()
77
+ handler.setFormatter(formatter)
78
+ logger.addHandler(handler)
79
+ logger._configured = True
80
+
79
81
  kivy_logger_patch()
80
82
 
81
83
  env_level = os.getenv("ANDROID_NOTIFY_LOGLEVEL")
@@ -5,7 +5,7 @@ import os.path
5
5
 
6
6
  from .logger import logger
7
7
  from android_notify.config import on_android_platform, on_flet_app, get_python_activity_context
8
- from android_notify.internal.java_classes import autoclass, BuildVersion, Manifest, Intent, String, Settings, Uri, PackageManager, NotificationManagerCompat
8
+ from android_notify.internal.java_classes import autoclass, BuildVersion, Manifest, Intent, String, Settings, Uri, NotificationManagerCompat
9
9
  from android_notify.internal.helper import execute_callback
10
10
 
11
11
 
@@ -19,10 +19,10 @@ def has_notification_permission():
19
19
 
20
20
  if BuildVersion.SDK_INT < 33: # Android 12 and below
21
21
  try:
22
- # NotificationManagerCompat is actually NotificationManager from android_notify.internal.java_classes
23
22
  context = get_python_activity_context()
24
- nm = context.getSystemService(NotificationManagerCompat)
25
- return nm.areNotificationsEnabled()
23
+ func_from = getattr(NotificationManagerCompat, "from")
24
+ compat_manager = func_from(context)
25
+ return compat_manager.areNotificationsEnabled()
26
26
  except Exception as error_checking_permission:
27
27
  logger.exception(f"On Android 12 and below Error checking permission: {error_checking_permission}")
28
28
  return None
@@ -30,8 +30,10 @@ def has_notification_permission():
30
30
  if on_flet_app():
31
31
  context = get_python_activity_context()
32
32
  permission = Manifest.POST_NOTIFICATIONS
33
-
34
- return PackageManager.PERMISSION_GRANTED == context.checkSelfPermission(permission)
33
+ ContextCompat = autoclass('androidx.core.content.ContextCompat')
34
+ # if you get error `Failed to find class: androidx/core/app/ActivityCompat`
35
+ # in proguard-rules.pro add `-keep class androidx.core.app.ActivityCompat { *; }`
36
+ return ContextCompat.checkSelfPermission(context, permission)
35
37
  else:
36
38
  from android.permissions import Permission, check_permission # type: ignore
37
39
  return check_permission(Permission.POST_NOTIFICATIONS)
@@ -80,10 +82,21 @@ def ask_notification_permission(callback=None, set_requesting_state=None, legacy
80
82
  execute_callback(callback, grants[0])
81
83
  execute_callback(set_requesting_state, False,from_who="package")
82
84
 
83
- if legacy or on_flet_app():
85
+ if legacy:
84
86
  # TODO Handle activity with request code
87
+ PackageManager = autoclass("android.content.pm.PackageManager")
88
+ permission = Manifest.POST_NOTIFICATIONS
89
+
90
+ granted = context.checkSelfPermission(permission)
91
+ if granted != PackageManager.PERMISSION_GRANTED:
92
+ context.requestPermissions([permission], 101)
93
+ return None
94
+ elif on_flet_app():
95
+ # TODO Callback when user answers request question
96
+ # Can't bind activity result method is from p4a which is only on kivy
97
+ ActivityCompat = autoclass('androidx.core.app.ActivityCompat')
85
98
  permission = Manifest.POST_NOTIFICATIONS
86
- context.requestPermissions([permission], 101)
99
+ ActivityCompat.requestPermissions(context, [permission], 101)
87
100
  return None
88
101
  else:
89
102
  from android.permissions import request_permissions, Permission # type: ignore
@@ -4,7 +4,7 @@ For image related things
4
4
  import os
5
5
 
6
6
  from android_notify.config import app_storage_path, on_flet_app, on_android_platform, get_python_activity_context
7
- from android_notify.internal.java_classes import autoclass, BitmapFactory, Uri, BuildVersion, Color
7
+ from android_notify.internal.java_classes import autoclass, BitmapFactory, Uri, BuildVersion, Color, IconCompat
8
8
  from android_notify.internal.helper import get_package_path
9
9
  from android_notify.internal.logger import logger
10
10
 
@@ -128,7 +128,6 @@ def set_small_icon_with_bitmap(bitmap, builder):
128
128
  logger.warning("Bitmap Insert as Icon Not available below Android 6")
129
129
  return False
130
130
  try:
131
- IconCompat = autoclass('android.graphics.drawable.Icon')
132
131
  icon = IconCompat.createWithBitmap(bitmap)
133
132
  builder.setSmallIcon(icon)
134
133
  return True
@@ -106,7 +106,7 @@ def set_lines(builder, lines):
106
106
 
107
107
 
108
108
 
109
- from android_notify.internal.java_classes import Color, RemoteViews
109
+ from android_notify.internal.java_classes import Color, RemoteViews, NotificationCompatDecoratedCustomViewStyle
110
110
  from android_notify.internal.logger import logger
111
111
  from android_notify.config import get_python_activity_context
112
112
 
@@ -121,10 +121,6 @@ def setLayoutText(layout, text_id, text, color):
121
121
 
122
122
  def set_custom_colors(builder, title, message, title_color, message_color):
123
123
  # Load layout
124
- from android_notify.internal.java_classes import autoclass
125
- NotificationCompatDecoratedCustomViewStyle = autoclass(
126
- 'android.app.Notification$DecoratedCustomViewStyle')
127
-
128
124
  context = get_python_activity_context()
129
125
  resources = context.getResources()
130
126
  package_name = context.getPackageName()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: android-notify
3
- Version: 1.60.9.dev0
3
+ Version: 1.60.10
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
@@ -81,9 +81,14 @@ In your **`buildozer.spec`** file, ensure you include the following:
81
81
 
82
82
  ```ini
83
83
  # Add pyjnius so ensure it's packaged with the build
84
- requirements = python3, kivy, pyjnius, android-notify==1.60.9.dev0
84
+ requirements = python3, kivy, pyjnius, android-notify
85
85
  # Add permission for notifications
86
86
  android.permissions = POST_NOTIFICATIONS
87
+
88
+ # Required dependency (write exactly as shown, no quotation marks)
89
+ android.gradle_dependencies = androidx.core:core-ktx:1.15.0
90
+ android.enable_androidx = True
91
+ android.api = 35
87
92
  ```
88
93
 
89
94
  </details>
@@ -93,13 +98,13 @@ android.permissions = POST_NOTIFICATIONS
93
98
  <summary><b>Flet apps:</b></summary>
94
99
  <br/>
95
100
 
96
- In your `pyproject.toml` file, ensure you include the following:
101
+ In your `pyproject.toml` file, ensure you include the following:
97
102
 
98
103
 
99
104
  ```toml
100
105
  [tool.flet.android]
101
106
  dependencies = [
102
- "pyjnius","android-notify==1.60.9.dev0"
107
+ "pyjnius","android-notify==1.60.10.dev0"
103
108
  ]
104
109
 
105
110
  [tool.flet.android.permission]
@@ -109,7 +114,35 @@ dependencies = [
109
114
 
110
115
  </details>
111
116
 
117
+ <details>
118
+
119
+ <summary><b>Desktop</b></summary>
120
+ <br/>
121
+
122
+ For IDE IntelliSense Can be installed via `pip install`:
123
+
124
+ ```bash
125
+ pip install android_notify
126
+ android-notify -v
127
+ ```
128
+
129
+ </details>
130
+
131
+ ------
132
+ ## Installing without Androidx
133
+ How to use without `gradle_dependencies`
134
+ Use `android-notify==1.60.10.dev0` to install via `pip`
135
+
136
+ <details>
137
+ <summary><b>In Kivy</b></summary>
138
+ <br/>
139
+
140
+ ```ini
141
+ # buildozer.spec
142
+ requirements = python3, kivy, pyjnius, android-notify==1.60.10.dev0
143
+ ```
112
144
 
145
+ </details>
113
146
 
114
147
  <details>
115
148
 
@@ -117,10 +150,10 @@ dependencies = [
117
150
  <br/>
118
151
 
119
152
  On 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.
120
- - In pip section where you're asked to insert `Libary name` paste `android-notify==1.60.9.dev0`
153
+ - In pip section where you're asked to insert `Libary name` paste `android-notify==1.60.10.dev0`
121
154
  - Minimal working example
122
155
  ```py
123
- # Testing with `android-notify==1.60.9.dev0` on pydroid
156
+ # Testing with `android-notify==1.60.10.dev0` on pydroid
124
157
  from kivy.app import App
125
158
  from kivy.uix.boxlayout import BoxLayout
126
159
  from kivy.uix.button import Button
@@ -160,23 +193,11 @@ if __name__ == "__main__":
160
193
  </details>
161
194
 
162
195
 
163
- <details>
164
-
165
- <summary><b>Desktop</b></summary>
166
- <br/>
167
-
168
- For IDE IntelliSense Can be installed via `pip install`:
169
-
170
- ```bash
171
- pip install android_notify
172
- android-notify -v
173
- ```
174
- </details>
175
196
 
176
197
  ## Documentation
177
- For Dev Version use
198
+ For Dev Version usage
178
199
  ```ini
179
- requirements = python3, kivy, pyjnius, https://github.com/Fector101/android_notify/archive/without-androidx.zip
200
+ requirements = python3, kivy, pyjnius, https://github.com/Fector101/android_notify/archive/main.zip
180
201
  ```
181
202
 
182
203
  <details>
@@ -185,7 +206,7 @@ requirements = python3, kivy, pyjnius, https://github.com/Fector101/android_noti
185
206
  - Make things happen without being in your app
186
207
  ```python
187
208
  from android_notify import Notification
188
- notification = Notification(title="Receiver Notification")
209
+ notification = Notification(title="Reciver Notification")
189
210
  notification.addButton(text="Stop", receiver_name="CarouselReceiver", action="ACTION_STOP")
190
211
  notification.addButton(text="Skip", receiver_name="CarouselReceiver", action="ACTION_SKIP")
191
212
  ```
@@ -248,7 +269,7 @@ Notification.createChannel(id='shake', name="Shake Passage", vibrate=True)
248
269
 
249
270
  n=Notification(title='Vibrate',channel_id='shake')
250
271
  n.setVibrate() # for less than android 8
251
- n.fVibrate() # To Force Vibrate (when user turned off in settings)
272
+ n.fVibrate() # To Force Vibrate
252
273
  n.send()
253
274
  ```
254
275
 
@@ -286,9 +307,5 @@ from android_notify import Notification, NotificationHandler
286
307
 
287
308
  ## ☕ Support the Project
288
309
 
289
- If you find this project helpful, consider buying me a coffee! 😊
290
- Or Giving it a star on 🌟 [GitHub](https://github.com/Fector101/android_notify/) Your support helps maintain and improve the project.
291
-
292
- <a href="https://www.buymeacoffee.com/fector101" target="_blank">
293
- <img src="https://cdn.buymeacoffee.com/buttons/v2/default-yellow.png" alt="Buy Me A Coffee" height="60">
294
- </a>
310
+ If you find this project helpful, your support would help me continue working on open-source projects
311
+ [donate](https://www.buymeacoffee.com/fector101)
@@ -41,9 +41,7 @@ android_notify/tests/test_notification_styles.py
41
41
  android_notify/tests/flet/adv/main.py
42
42
  android_notify/tests/flet/adv/tests/__init__.py
43
43
  android_notify/tests/flet/adv/tests/test_android_notify_full.py
44
- android_notify/tests/flet/basic/src/core.py
45
44
  android_notify/tests/flet/basic/src/main.py
46
- android_notify/tests/flet/flet-working/src/core.py
47
45
  android_notify/tests/flet/flet-working/src/main.py
48
46
  android_notify/tests/p4a/hook.py
49
47
  android_notify/tests/serivces/wallpaper.py
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "android-notify"
7
- version = "1.60.9.dev0"
7
+ version = "1.60.10"
8
8
  description = "A Python package that simplifies creating Android notifications in Kivy and Flet apps."
9
9
  readme = { file = "README.md", content-type = "text/markdown" }
10
10
  authors = [
@@ -1,221 +0,0 @@
1
- """ Non-Advanced Stuff """
2
- import random
3
- import os
4
- ON_ANDROID = False
5
-
6
- def on_flet_app():
7
- return os.getenv("MAIN_ACTIVITY_HOST_CLASS_NAME")
8
-
9
- def get_activity_class_name():
10
- ACTIVITY_CLASS_NAME = os.getenv("MAIN_ACTIVITY_HOST_CLASS_NAME") # flet python
11
- if not ACTIVITY_CLASS_NAME:
12
- try:
13
- from android import config
14
- ACTIVITY_CLASS_NAME = getattr(config, "JAVA_NAMESPACE", None)
15
- except (ImportError, AttributeError):
16
- ACTIVITY_CLASS_NAME = 'org.kivy.android'
17
- return ACTIVITY_CLASS_NAME
18
-
19
- try:
20
-
21
- from jnius import autoclass # Needs Java to be installed
22
- # Get the required Java classes
23
- ACTIVITY_CLASS_NAME = get_activity_class_name()
24
- PythonActivity = autoclass(ACTIVITY_CLASS_NAME)
25
- context = PythonActivity.mActivity # Get the app's context
26
- NotificationChannel = autoclass('android.app.NotificationChannel')
27
- String = autoclass('java.lang.String')
28
- Intent = autoclass('android.content.Intent')
29
- PendingIntent = autoclass('android.app.PendingIntent')
30
- BitmapFactory = autoclass('android.graphics.BitmapFactory')
31
- BuildVersion = autoclass('android.os.Build$VERSION')
32
- ON_ANDROID=True
33
- except Exception as e:
34
- print('\nThis Package Only Runs on Android !!! ---> Check "https://github.com/Fector101/android_notify/" to see design patterns and more info.\n')
35
-
36
- if ON_ANDROID:
37
- try:
38
- NotificationManagerCompat = autoclass('androidx.core.app.NotificationManagerCompat')
39
- NotificationCompat = autoclass('androidx.core.app.NotificationCompat')
40
-
41
- # Notification Design
42
- NotificationCompatBuilder = autoclass('androidx.core.app.NotificationCompat$Builder')
43
- NotificationCompatBigTextStyle = autoclass('androidx.core.app.NotificationCompat$BigTextStyle')
44
- NotificationCompatBigPictureStyle = autoclass('androidx.core.app.NotificationCompat$BigPictureStyle')
45
- NotificationCompatInboxStyle = autoclass('androidx.core.app.NotificationCompat$InboxStyle')
46
- except Exception as e:
47
- print("""\n
48
- Dependency Error: Add the following in buildozer.spec:
49
- * android.gradle_dependencies = androidx.core:core-ktx:1.15.0, androidx.core:core:1.6.0
50
- * android.enable_androidx = True
51
- * android.permissions = POST_NOTIFICATIONS\n
52
- """)
53
-
54
-
55
- def get_app_root_path():
56
- if on_flet_app():
57
- return os.path.join(context.getFilesDir().getAbsolutePath(),'flet')
58
- else:
59
- from android.storage import app_storage_path # type: ignore
60
- return app_storage_path()
61
-
62
- def asks_permission_if_needed():
63
- """
64
- Ask for permission to send notifications if needed.
65
- """
66
- if on_flet_app():
67
- VERSION_CODES = autoclass('android.os.Build$VERSION_CODES')
68
- ContextCompat = autoclass('androidx.core.content.ContextCompat')
69
- # if you get error `Failed to find class: androidx/core/app/ActivityCompat`
70
- #in proguard-rules.pro add `-keep class androidx.core.app.ActivityCompat { *; }`
71
- ActivityCompat = autoclass('androidx.core.app.ActivityCompat')
72
- Manifest = autoclass('android.Manifest$permission')
73
- VERSION_CODES = autoclass('android.os.Build$VERSION_CODES')
74
-
75
- if BuildVersion.SDK_INT >= VERSION_CODES.TIRAMISU:
76
- permission = Manifest.POST_NOTIFICATIONS
77
- granted = ContextCompat.checkSelfPermission(context, permission)
78
-
79
- if granted != 0: # PackageManager.PERMISSION_GRANTED == 0
80
- ActivityCompat.requestPermissions(context, [permission], 101)
81
- else: # android package is from p4a which is for kivy
82
- try:
83
- from android.permissions import request_permissions, Permission,check_permission # type: ignore
84
- permissions=[Permission.POST_NOTIFICATIONS]
85
- if not all(check_permission(p) for p in permissions):
86
- request_permissions(permissions)
87
- except Exception as e:
88
- print("android_notify- error trying to request notification access: ", e)
89
-
90
- def get_image_uri(relative_path):
91
- """
92
- Get the absolute URI for an image in the assets folder.
93
- :param relative_path: The relative path to the image (e.g., 'assets/imgs/icon.png').
94
- :return: Absolute URI java Object (e.g., 'file:///path/to/file.png').
95
- """
96
- app_root_path = get_app_root_path()
97
- output_path = os.path.join(app_root_path,'app', relative_path)
98
- # print(output_path,'output_path') # /data/user/0/org.laner.lan_ft/files/app/assets/imgs/icon.png
99
-
100
- if not os.path.exists(output_path):
101
- raise FileNotFoundError(f"\nImage not found at path: {output_path}\n")
102
-
103
- Uri = autoclass('android.net.Uri')
104
- return Uri.parse(f"file://{output_path}")
105
-
106
- def get_icon_object(uri):
107
- BitmapFactory = autoclass('android.graphics.BitmapFactory')
108
- IconCompat = autoclass('androidx.core.graphics.drawable.IconCompat')
109
-
110
- bitmap= BitmapFactory.decodeStream(context.getContentResolver().openInputStream(uri))
111
- return IconCompat.createWithBitmap(bitmap)
112
-
113
- def insert_app_icon(builder,custom_icon_path):
114
- if custom_icon_path:
115
- try:
116
- uri = get_image_uri(custom_icon_path)
117
- icon = get_icon_object(uri)
118
- builder.setSmallIcon(icon)
119
- except Exception as e:
120
- print('android_notify- error: ',e)
121
- builder.setSmallIcon(context.getApplicationInfo().icon)
122
- else:
123
- print('Found res icon -->',context.getApplicationInfo().icon,'<--')
124
- builder.setSmallIcon(context.getApplicationInfo().icon)
125
-
126
- def send_notification(
127
- title:str,
128
- message:str,
129
- style=None,
130
- img_path=None,
131
- channel_name="Default Channel",
132
- channel_id:str="default_channel",
133
- custom_app_icon_path="",
134
-
135
- big_picture_path='',
136
- large_icon_path='',
137
- big_text="",
138
- lines=""
139
- ):
140
- """
141
- Send a notification on Android.
142
-
143
- :param title: Title of the notification.
144
- :param message: Message body.
145
- :param style: Style of the notification ('big_text', 'big_picture', 'inbox', 'large_icon').
146
- :param img_path: Path to the image resource.
147
- :param channel_id: Notification channel ID.(Default is lowercase channel name arg in lowercase)
148
- """
149
- if not ON_ANDROID:
150
- print('This Package Only Runs on Android !!! ---> Check "https://github.com/Fector101/android_notify/" for Documentation.')
151
- return
152
-
153
- asks_permission_if_needed()
154
- channel_id=channel_name.replace(' ','_').lower().lower() if not channel_id else channel_id
155
- # Get notification manager
156
- notification_manager = context.getSystemService(context.NOTIFICATION_SERVICE)
157
-
158
- # importance= autoclass('android.app.NotificationManager').IMPORTANCE_HIGH # also works #NotificationManager.IMPORTANCE_DEFAULT
159
- importance= NotificationManagerCompat.IMPORTANCE_HIGH #autoclass('android.app.NotificationManager').IMPORTANCE_HIGH also works #NotificationManager.IMPORTANCE_DEFAULT
160
-
161
- # Notification Channel (Required for Android 8.0+)
162
- if BuildVersion.SDK_INT >= 26:
163
- channel = NotificationChannel(channel_id, channel_name,importance)
164
- notification_manager.createNotificationChannel(channel)
165
-
166
- # Build the notification
167
- builder = NotificationCompatBuilder(context, channel_id)
168
- builder.setContentTitle(title)
169
- builder.setContentText(message)
170
- insert_app_icon(builder,custom_app_icon_path)
171
- builder.setDefaults(NotificationCompat.DEFAULT_ALL)
172
- builder.setPriority(NotificationCompat.PRIORITY_HIGH)
173
-
174
- if img_path:
175
- print('android_notify- img_path arg deprecated use "large_icon_path or big_picture_path or custom_app_icon_path"instead ')
176
-
177
- big_picture = None
178
- if big_picture_path:
179
- try:
180
- big_picture = get_image_uri(big_picture_path)
181
- except FileNotFoundError as e:
182
- print('android_notify- Error Getting Uri for big_picture_path: ',e)
183
-
184
- large_icon = None
185
- if large_icon_path:
186
- try:
187
- large_icon = get_image_uri(large_icon_path)
188
- except FileNotFoundError as e:
189
- print('android_notify- Error Getting Uri for large_icon_path: ',e)
190
-
191
-
192
- # Apply notification styles
193
- try:
194
- if big_text:
195
- big_text_style = NotificationCompatBigTextStyle()
196
- big_text_style.bigText(big_text)
197
- builder.setStyle(big_text_style)
198
-
199
- elif lines:
200
- inbox_style = NotificationCompatInboxStyle()
201
- for line in lines.split("\n"):
202
- inbox_style.addLine(line)
203
- builder.setStyle(inbox_style)
204
-
205
- if large_icon:
206
- bitmap = BitmapFactory.decodeStream(context.getContentResolver().openInputStream(large_icon))
207
- builder.setLargeIcon(bitmap)
208
-
209
- if big_picture:
210
- bitmap = BitmapFactory.decodeStream(context.getContentResolver().openInputStream(big_picture))
211
- builder.setLargeIcon(bitmap)
212
- big_picture_style = NotificationCompatBigPictureStyle().bigPicture(bitmap)
213
- builder.setStyle(big_picture_style)
214
-
215
- except Exception as e:
216
- print('android_notify- Error Failed Adding Style: ',e)
217
- # Display the notification
218
- notification_id = random.randint(0, 100)
219
- notification_manager.notify(notification_id, builder.build())
220
- return notification_id
221
-
@@ -1,221 +0,0 @@
1
- """ Non-Advanced Stuff """
2
- import random
3
- import os
4
- ON_ANDROID = False
5
-
6
- def on_flet_app():
7
- return os.getenv("MAIN_ACTIVITY_HOST_CLASS_NAME")
8
-
9
- def get_activity_class_name():
10
- ACTIVITY_CLASS_NAME = os.getenv("MAIN_ACTIVITY_HOST_CLASS_NAME") # flet python
11
- if not ACTIVITY_CLASS_NAME:
12
- try:
13
- from android import config
14
- ACTIVITY_CLASS_NAME = getattr(config, "JAVA_NAMESPACE", None)
15
- except (ImportError, AttributeError):
16
- ACTIVITY_CLASS_NAME = 'org.kivy.android'
17
- return ACTIVITY_CLASS_NAME
18
-
19
- try:
20
-
21
- from jnius import autoclass # Needs Java to be installed
22
- # Get the required Java classes
23
- ACTIVITY_CLASS_NAME = get_activity_class_name()
24
- PythonActivity = autoclass(ACTIVITY_CLASS_NAME)
25
- context = PythonActivity.mActivity # Get the app's context
26
- NotificationChannel = autoclass('android.app.NotificationChannel')
27
- String = autoclass('java.lang.String')
28
- Intent = autoclass('android.content.Intent')
29
- PendingIntent = autoclass('android.app.PendingIntent')
30
- BitmapFactory = autoclass('android.graphics.BitmapFactory')
31
- BuildVersion = autoclass('android.os.Build$VERSION')
32
- ON_ANDROID=True
33
- except Exception as e:
34
- print('\nThis Package Only Runs on Android !!! ---> Check "https://github.com/Fector101/android_notify/" to see design patterns and more info.\n')
35
-
36
- if ON_ANDROID:
37
- try:
38
- NotificationManagerCompat = autoclass('androidx.core.app.NotificationManagerCompat')
39
- NotificationCompat = autoclass('androidx.core.app.NotificationCompat')
40
-
41
- # Notification Design
42
- NotificationCompatBuilder = autoclass('androidx.core.app.NotificationCompat$Builder')
43
- NotificationCompatBigTextStyle = autoclass('androidx.core.app.NotificationCompat$BigTextStyle')
44
- NotificationCompatBigPictureStyle = autoclass('androidx.core.app.NotificationCompat$BigPictureStyle')
45
- NotificationCompatInboxStyle = autoclass('androidx.core.app.NotificationCompat$InboxStyle')
46
- except Exception as e:
47
- print("""\n
48
- Dependency Error: Add the following in buildozer.spec:
49
- * android.gradle_dependencies = androidx.core:core-ktx:1.15.0, androidx.core:core:1.6.0
50
- * android.enable_androidx = True
51
- * android.permissions = POST_NOTIFICATIONS\n
52
- """)
53
-
54
-
55
- def get_app_root_path():
56
- if on_flet_app():
57
- return os.path.join(context.getFilesDir().getAbsolutePath(),'flet')
58
- else:
59
- from android.storage import app_storage_path # type: ignore
60
- return app_storage_path()
61
-
62
- def asks_permission_if_needed():
63
- """
64
- Ask for permission to send notifications if needed.
65
- """
66
- if on_flet_app():
67
- VERSION_CODES = autoclass('android.os.Build$VERSION_CODES')
68
- ContextCompat = autoclass('androidx.core.content.ContextCompat')
69
- # if you get error `Failed to find class: androidx/core/app/ActivityCompat`
70
- #in proguard-rules.pro add `-keep class androidx.core.app.ActivityCompat { *; }`
71
- ActivityCompat = autoclass('androidx.core.app.ActivityCompat')
72
- Manifest = autoclass('android.Manifest$permission')
73
- VERSION_CODES = autoclass('android.os.Build$VERSION_CODES')
74
-
75
- if BuildVersion.SDK_INT >= VERSION_CODES.TIRAMISU:
76
- permission = Manifest.POST_NOTIFICATIONS
77
- granted = ContextCompat.checkSelfPermission(context, permission)
78
-
79
- if granted != 0: # PackageManager.PERMISSION_GRANTED == 0
80
- ActivityCompat.requestPermissions(context, [permission], 101)
81
- else: # android package is from p4a which is for kivy
82
- try:
83
- from android.permissions import request_permissions, Permission,check_permission # type: ignore
84
- permissions=[Permission.POST_NOTIFICATIONS]
85
- if not all(check_permission(p) for p in permissions):
86
- request_permissions(permissions)
87
- except Exception as e:
88
- print("android_notify- error trying to request notification access: ", e)
89
-
90
- def get_image_uri(relative_path):
91
- """
92
- Get the absolute URI for an image in the assets folder.
93
- :param relative_path: The relative path to the image (e.g., 'assets/imgs/icon.png').
94
- :return: Absolute URI java Object (e.g., 'file:///path/to/file.png').
95
- """
96
- app_root_path = get_app_root_path()
97
- output_path = os.path.join(app_root_path,'app', relative_path)
98
- # print(output_path,'output_path') # /data/user/0/org.laner.lan_ft/files/app/assets/imgs/icon.png
99
-
100
- if not os.path.exists(output_path):
101
- raise FileNotFoundError(f"\nImage not found at path: {output_path}\n")
102
-
103
- Uri = autoclass('android.net.Uri')
104
- return Uri.parse(f"file://{output_path}")
105
-
106
- def get_icon_object(uri):
107
- BitmapFactory = autoclass('android.graphics.BitmapFactory')
108
- IconCompat = autoclass('androidx.core.graphics.drawable.IconCompat')
109
-
110
- bitmap= BitmapFactory.decodeStream(context.getContentResolver().openInputStream(uri))
111
- return IconCompat.createWithBitmap(bitmap)
112
-
113
- def insert_app_icon(builder,custom_icon_path):
114
- if custom_icon_path:
115
- try:
116
- uri = get_image_uri(custom_icon_path)
117
- icon = get_icon_object(uri)
118
- builder.setSmallIcon(icon)
119
- except Exception as e:
120
- print('android_notify- error: ',e)
121
- builder.setSmallIcon(context.getApplicationInfo().icon)
122
- else:
123
- print('Found res icon -->',context.getApplicationInfo().icon,'<--')
124
- builder.setSmallIcon(context.getApplicationInfo().icon)
125
-
126
- def send_notification(
127
- title:str,
128
- message:str,
129
- style=None,
130
- img_path=None,
131
- channel_name="Default Channel",
132
- channel_id:str="default_channel",
133
- custom_app_icon_path="",
134
-
135
- big_picture_path='',
136
- large_icon_path='',
137
- big_text="",
138
- lines=""
139
- ):
140
- """
141
- Send a notification on Android.
142
-
143
- :param title: Title of the notification.
144
- :param message: Message body.
145
- :param style: Style of the notification ('big_text', 'big_picture', 'inbox', 'large_icon').
146
- :param img_path: Path to the image resource.
147
- :param channel_id: Notification channel ID.(Default is lowercase channel name arg in lowercase)
148
- """
149
- if not ON_ANDROID:
150
- print('This Package Only Runs on Android !!! ---> Check "https://github.com/Fector101/android_notify/" for Documentation.')
151
- return
152
-
153
- asks_permission_if_needed()
154
- channel_id=channel_name.replace(' ','_').lower().lower() if not channel_id else channel_id
155
- # Get notification manager
156
- notification_manager = context.getSystemService(context.NOTIFICATION_SERVICE)
157
-
158
- # importance= autoclass('android.app.NotificationManager').IMPORTANCE_HIGH # also works #NotificationManager.IMPORTANCE_DEFAULT
159
- importance= NotificationManagerCompat.IMPORTANCE_HIGH #autoclass('android.app.NotificationManager').IMPORTANCE_HIGH also works #NotificationManager.IMPORTANCE_DEFAULT
160
-
161
- # Notification Channel (Required for Android 8.0+)
162
- if BuildVersion.SDK_INT >= 26:
163
- channel = NotificationChannel(channel_id, channel_name,importance)
164
- notification_manager.createNotificationChannel(channel)
165
-
166
- # Build the notification
167
- builder = NotificationCompatBuilder(context, channel_id)
168
- builder.setContentTitle(title)
169
- builder.setContentText(message)
170
- insert_app_icon(builder,custom_app_icon_path)
171
- builder.setDefaults(NotificationCompat.DEFAULT_ALL)
172
- builder.setPriority(NotificationCompat.PRIORITY_HIGH)
173
-
174
- if img_path:
175
- print('android_notify- img_path arg deprecated use "large_icon_path or big_picture_path or custom_app_icon_path"instead ')
176
-
177
- big_picture = None
178
- if big_picture_path:
179
- try:
180
- big_picture = get_image_uri(big_picture_path)
181
- except FileNotFoundError as e:
182
- print('android_notify- Error Getting Uri for big_picture_path: ',e)
183
-
184
- large_icon = None
185
- if large_icon_path:
186
- try:
187
- large_icon = get_image_uri(large_icon_path)
188
- except FileNotFoundError as e:
189
- print('android_notify- Error Getting Uri for large_icon_path: ',e)
190
-
191
-
192
- # Apply notification styles
193
- try:
194
- if big_text:
195
- big_text_style = NotificationCompatBigTextStyle()
196
- big_text_style.bigText(big_text)
197
- builder.setStyle(big_text_style)
198
-
199
- elif lines:
200
- inbox_style = NotificationCompatInboxStyle()
201
- for line in lines.split("\n"):
202
- inbox_style.addLine(line)
203
- builder.setStyle(inbox_style)
204
-
205
- if large_icon:
206
- bitmap = BitmapFactory.decodeStream(context.getContentResolver().openInputStream(large_icon))
207
- builder.setLargeIcon(bitmap)
208
-
209
- if big_picture:
210
- bitmap = BitmapFactory.decodeStream(context.getContentResolver().openInputStream(big_picture))
211
- builder.setLargeIcon(bitmap)
212
- big_picture_style = NotificationCompatBigPictureStyle().bigPicture(bitmap)
213
- builder.setStyle(big_picture_style)
214
-
215
- except Exception as e:
216
- print('android_notify- Error Failed Adding Style: ',e)
217
- # Display the notification
218
- notification_id = random.randint(0, 100)
219
- notification_manager.notify(notification_id, builder.build())
220
- return notification_id
221
-