android-notify 1.23.1__py3-none-any.whl → 1.24__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of android-notify might be problematic. Click here for more details.
- android_notify/__init__.py +7 -1
- android_notify/_dev.py +112 -0
- android_notify/advanced_dev.py +345 -0
- android_notify/core.py +6 -6
- {android_notify-1.23.1.dist-info → android_notify-1.24.dist-info}/METADATA +23 -4
- android_notify-1.24.dist-info/RECORD +10 -0
- android_notify-1.23.1.dist-info/RECORD +0 -8
- {android_notify-1.23.1.dist-info → android_notify-1.24.dist-info}/WHEEL +0 -0
- {android_notify-1.23.1.dist-info → android_notify-1.24.dist-info}/top_level.txt +0 -0
android_notify/__init__.py
CHANGED
|
@@ -1,2 +1,8 @@
|
|
|
1
1
|
from .core import send_notification
|
|
2
|
-
from .styles import NotificationStyles
|
|
2
|
+
from .styles import NotificationStyles
|
|
3
|
+
try:
|
|
4
|
+
from .advanced import EnhancedNotificationManager
|
|
5
|
+
from ._dev import NotificationBuilder
|
|
6
|
+
except Exception as e:
|
|
7
|
+
# print('Failed to Import Advanced Features:', e)
|
|
8
|
+
pass
|
android_notify/_dev.py
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
|
|
2
|
+
class NotificationManager:
|
|
3
|
+
notification_ids=[]
|
|
4
|
+
def __init__(self, channel_id='',channel_name="Default Channel",importance_level=NotificationManagerCompat.IMPORTANCE_HIGH):
|
|
5
|
+
if not ON_ANDROID:
|
|
6
|
+
raise RuntimeError("This package only runs on Android!")
|
|
7
|
+
self.channel_name = channel_name
|
|
8
|
+
self.channel_id=channel_name.replace(' ','_').lower().lower() if not channel_id else channel_id
|
|
9
|
+
self.notification_manager = context.getSystemService(context.NOTIFICATION_SERVICE)
|
|
10
|
+
self.builder = NotificationCompatBuilder(context, self.channel_id)
|
|
11
|
+
self.notification_id = self.__getUniqueID()
|
|
12
|
+
|
|
13
|
+
# Setup Channel (For Android 8.0+)
|
|
14
|
+
if BuildVersion.SDK_INT >= 26:
|
|
15
|
+
channel = NotificationChannel(self.channel_id, channel_name, importance_level)
|
|
16
|
+
self.notification_manager.createNotificationChannel(channel)
|
|
17
|
+
|
|
18
|
+
def set_title(self, title: str):
|
|
19
|
+
"""Set the title of the notification."""
|
|
20
|
+
self.builder.setContentTitle(title)
|
|
21
|
+
def set_message(self, message: str):
|
|
22
|
+
"""Set the message of the notification."""
|
|
23
|
+
self.builder.setContentText(message)
|
|
24
|
+
def set_style(self, style: str, img_path=None):
|
|
25
|
+
"""Set the style of the notification."""
|
|
26
|
+
try:
|
|
27
|
+
if style == "big_text":
|
|
28
|
+
big_text_style = NotificationCompatBigTextStyle()
|
|
29
|
+
big_text_style.bigText(self.builder.mContentText)
|
|
30
|
+
self.builder.setStyle(big_text_style)
|
|
31
|
+
elif style == "big_picture" and img_path:
|
|
32
|
+
img = get_image_uri(img_path)
|
|
33
|
+
big_picture_style = NotificationCompatBigPictureStyle().bigPicture(img)
|
|
34
|
+
self.builder.setStyle(big_picture_style)
|
|
35
|
+
elif style == "inbox":
|
|
36
|
+
inbox_style = NotificationCompatInboxStyle()
|
|
37
|
+
for line in self.builder.mContentText.split("\n"):
|
|
38
|
+
inbox_style.addLine(line)
|
|
39
|
+
self.builder.setStyle(inbox_style)
|
|
40
|
+
except Exception as e:
|
|
41
|
+
print('Failed Adding Style:', e)
|
|
42
|
+
|
|
43
|
+
def add_button(self, action_name: str, action_intent: str):
|
|
44
|
+
"""Add a button to the notification."""
|
|
45
|
+
try:
|
|
46
|
+
intent = Intent(context, PythonActivity)
|
|
47
|
+
intent.setAction(action_intent)
|
|
48
|
+
pending_intent = PendingIntent.getActivity(
|
|
49
|
+
context,
|
|
50
|
+
0,
|
|
51
|
+
intent,
|
|
52
|
+
PendingIntent.FLAG_IMMUTABLE
|
|
53
|
+
)
|
|
54
|
+
action_text = cast('java.lang.CharSequence', String(action_name))
|
|
55
|
+
self.builder.addAction(
|
|
56
|
+
int(context.getApplicationInfo().icon),
|
|
57
|
+
action_text,
|
|
58
|
+
pending_intent
|
|
59
|
+
)
|
|
60
|
+
except Exception as e:
|
|
61
|
+
print('Failed adding button:', e)
|
|
62
|
+
def set_icon(self, img_path: str):
|
|
63
|
+
"""Set the icon of the notification."""
|
|
64
|
+
try:
|
|
65
|
+
img = get_image_uri(img_path)
|
|
66
|
+
self.builder.setLargeIcon(BitmapFactory.decodeStream(context.getContentResolver().openInputStream(img)))
|
|
67
|
+
except Exception as e:
|
|
68
|
+
print('Failed Adding Bitmap:', e)
|
|
69
|
+
|
|
70
|
+
def send(self):
|
|
71
|
+
"""Send the notification."""
|
|
72
|
+
self.builder.setSmallIcon(context.getApplicationInfo().icon)
|
|
73
|
+
self.builder.setDefaults(NotificationCompat.DEFAULT_ALL)
|
|
74
|
+
self.builder.setPriority(NotificationCompat.PRIORITY_HIGH)
|
|
75
|
+
self.notification_manager.notify(self.notification_id, self.builder.build())
|
|
76
|
+
return self.notification_id
|
|
77
|
+
|
|
78
|
+
def update(self, title=None, message=None):
|
|
79
|
+
"""Update the notification."""
|
|
80
|
+
if title:
|
|
81
|
+
self.builder.setContentTitle(title)
|
|
82
|
+
if message:
|
|
83
|
+
self.builder.setContentText(message)
|
|
84
|
+
self.notification_manager.notify(self.notification_id, self.builder.build())
|
|
85
|
+
def __get_image(self, img_path):
|
|
86
|
+
"""Get the image from the app Relative path."""
|
|
87
|
+
from android.storage import app_storage_path
|
|
88
|
+
output_path = os.path.join(app_storage_path(), 'app', img_path)
|
|
89
|
+
if not os.path.exists(output_path):
|
|
90
|
+
raise FileNotFoundError(f"Image not found at path: {output_path}")
|
|
91
|
+
Uri = autoclass('android.net.Uri')
|
|
92
|
+
return BitmapFactory.decodeStream(context.getContentResolver().openInputStream(Uri.parse(f"file://{output_path}")))
|
|
93
|
+
# return BitmapFactory.decodeStream(context.getContentResolver().openInputStream(get_image_uri(img_path)))
|
|
94
|
+
def cancel(self):
|
|
95
|
+
"""Cancel the notification."""
|
|
96
|
+
self.notification_manager.cancel(self.notification_id)
|
|
97
|
+
self.__del__()
|
|
98
|
+
def cancel_all(self):
|
|
99
|
+
"""Cancel all notifications."""
|
|
100
|
+
self.notification_manager.cancelAll()
|
|
101
|
+
def __del__(self):
|
|
102
|
+
"""Delete the notification id."""
|
|
103
|
+
self.notification_ids.remove(self.notification_id)
|
|
104
|
+
def __getUniqueID(self):
|
|
105
|
+
notification_id = random.randint(1, 100)
|
|
106
|
+
while notification_id in self.notification_ids:
|
|
107
|
+
notification_id = random.randint(1, 100)
|
|
108
|
+
self.notification_ids.append(notification_id)
|
|
109
|
+
return notification_id
|
|
110
|
+
|
|
111
|
+
# notification_manager=NotificationManager()
|
|
112
|
+
# notification_manager.s
|
|
@@ -0,0 +1,345 @@
|
|
|
1
|
+
from jnius import autoclass, cast
|
|
2
|
+
import random
|
|
3
|
+
import os
|
|
4
|
+
from enum import Enum
|
|
5
|
+
from typing import Optional, List, Union
|
|
6
|
+
from dataclasses import dataclass
|
|
7
|
+
|
|
8
|
+
class NotificationImportance(Enum):
|
|
9
|
+
MIN = 1
|
|
10
|
+
LOW = 2
|
|
11
|
+
DEFAULT = 3
|
|
12
|
+
HIGH = 4
|
|
13
|
+
|
|
14
|
+
class NotificationPriority(Enum):
|
|
15
|
+
MIN = -2
|
|
16
|
+
LOW = -1
|
|
17
|
+
DEFAULT = 0
|
|
18
|
+
HIGH = 1
|
|
19
|
+
MAX = 2
|
|
20
|
+
|
|
21
|
+
class NotificationStyle(Enum):
|
|
22
|
+
DEFAULT = "default"
|
|
23
|
+
BIG_TEXT = "big_text"
|
|
24
|
+
BIG_PICTURE = "big_picture"
|
|
25
|
+
INBOX = "inbox"
|
|
26
|
+
MESSAGING = "messaging"
|
|
27
|
+
|
|
28
|
+
@dataclass
|
|
29
|
+
class NotificationAction:
|
|
30
|
+
name: str
|
|
31
|
+
intent: str
|
|
32
|
+
icon: Optional[int] = None
|
|
33
|
+
|
|
34
|
+
class AndroidNotificationSystem:
|
|
35
|
+
"""Singleton class to handle Android-specific initialization and classes"""
|
|
36
|
+
_instance = None
|
|
37
|
+
_initialized = False
|
|
38
|
+
|
|
39
|
+
def __new__(cls):
|
|
40
|
+
if cls._instance is None:
|
|
41
|
+
cls._instance = super(AndroidNotificationSystem, cls).__new__(cls)
|
|
42
|
+
return cls._instance
|
|
43
|
+
|
|
44
|
+
def __init__(self):
|
|
45
|
+
if not self._initialized:
|
|
46
|
+
self._initialize_android_classes()
|
|
47
|
+
self._initialized = True
|
|
48
|
+
|
|
49
|
+
def _initialize_android_classes(self):
|
|
50
|
+
try:
|
|
51
|
+
# Core Android classes
|
|
52
|
+
self.PythonActivity = autoclass('org.kivy.android.PythonActivity')
|
|
53
|
+
self.NotificationChannel = autoclass('android.app.NotificationChannel')
|
|
54
|
+
self.String = autoclass('java.lang.String')
|
|
55
|
+
self.Intent = autoclass('android.content.Intent')
|
|
56
|
+
self.PendingIntent = autoclass('android.app.PendingIntent')
|
|
57
|
+
self.Context = self.PythonActivity.mActivity
|
|
58
|
+
self.BitmapFactory = autoclass('android.graphics.BitmapFactory')
|
|
59
|
+
self.BuildVersion = autoclass('android.os.Build$VERSION')
|
|
60
|
+
|
|
61
|
+
# Notification specific classes
|
|
62
|
+
self.NotificationManagerCompat = autoclass('androidx.core.app.NotificationManagerCompat')
|
|
63
|
+
self.NotificationCompat = autoclass('androidx.core.app.NotificationCompat')
|
|
64
|
+
self.NotificationBuilder = autoclass('androidx.core.app.NotificationCompat$Builder')
|
|
65
|
+
self.BigTextStyle = autoclass('androidx.core.app.NotificationCompat$BigTextStyle')
|
|
66
|
+
self.BigPictureStyle = autoclass('androidx.core.app.NotificationCompat$BigPictureStyle')
|
|
67
|
+
self.InboxStyle = autoclass('androidx.core.app.NotificationCompat$InboxStyle')
|
|
68
|
+
self.MessagingStyle = autoclass('androidx.core.app.NotificationCompat$MessagingStyle')
|
|
69
|
+
|
|
70
|
+
self.is_initialized = True
|
|
71
|
+
except Exception as e:
|
|
72
|
+
raise ImportError(
|
|
73
|
+
"Failed to initialize Android classes. Ensure you have the following in buildozer.spec:\n"
|
|
74
|
+
"android.gradle_dependencies = androidx.core:core-ktx:1.15.0, androidx.core:core:1.6.0\n"
|
|
75
|
+
"android.enable_androidx = True\n"
|
|
76
|
+
"android.permissions = POST_NOTIFICATIONS"
|
|
77
|
+
) from e
|
|
78
|
+
|
|
79
|
+
class NotificationChannel:
|
|
80
|
+
def __init__(
|
|
81
|
+
self,
|
|
82
|
+
channel_id: str,
|
|
83
|
+
name: str,
|
|
84
|
+
importance: NotificationImportance = NotificationImportance.DEFAULT,
|
|
85
|
+
description: Optional[str] = None,
|
|
86
|
+
enable_lights: bool = True,
|
|
87
|
+
enable_vibration: bool = True
|
|
88
|
+
):
|
|
89
|
+
self.android = AndroidNotificationSystem()
|
|
90
|
+
self.channel_id = channel_id
|
|
91
|
+
self.name = name
|
|
92
|
+
|
|
93
|
+
if self.android.BuildVersion.SDK_INT >= 26:
|
|
94
|
+
channel = self.android.NotificationChannel(
|
|
95
|
+
channel_id,
|
|
96
|
+
name,
|
|
97
|
+
importance.value
|
|
98
|
+
)
|
|
99
|
+
if description:
|
|
100
|
+
channel.setDescription(description)
|
|
101
|
+
channel.enableLights(enable_lights)
|
|
102
|
+
channel.enableVibration(enable_vibration)
|
|
103
|
+
|
|
104
|
+
self.android.Context.getSystemService(
|
|
105
|
+
self.android.Context.NOTIFICATION_SERVICE
|
|
106
|
+
).createNotificationChannel(channel)
|
|
107
|
+
|
|
108
|
+
class EnhancedNotificationManager:
|
|
109
|
+
"""Enhanced notification manager with more features and better organization"""
|
|
110
|
+
|
|
111
|
+
_notification_ids = set()
|
|
112
|
+
|
|
113
|
+
def __init__(
|
|
114
|
+
self,
|
|
115
|
+
channel_id: str,
|
|
116
|
+
channel_name: str,
|
|
117
|
+
importance: NotificationImportance = NotificationImportance.HIGH,
|
|
118
|
+
group_key: Optional[str] = None
|
|
119
|
+
):
|
|
120
|
+
self.android = AndroidNotificationSystem()
|
|
121
|
+
self.channel = NotificationChannel(channel_id, channel_name, importance)
|
|
122
|
+
self.builder = self.android.NotificationBuilder(
|
|
123
|
+
self.android.Context,
|
|
124
|
+
channel_id
|
|
125
|
+
)
|
|
126
|
+
self.notification_id = self._generate_unique_id()
|
|
127
|
+
self.group_key = group_key
|
|
128
|
+
|
|
129
|
+
if group_key:
|
|
130
|
+
self.builder.setGroup(group_key)
|
|
131
|
+
|
|
132
|
+
def _generate_unique_id(self) -> int:
|
|
133
|
+
while (notification_id := random.randint(1, 10000)) in self._notification_ids:
|
|
134
|
+
continue
|
|
135
|
+
self._notification_ids.add(notification_id)
|
|
136
|
+
return notification_id
|
|
137
|
+
|
|
138
|
+
def set_content(
|
|
139
|
+
self,
|
|
140
|
+
title: str,
|
|
141
|
+
message: str,
|
|
142
|
+
style: NotificationStyle = NotificationStyle.DEFAULT,
|
|
143
|
+
style_content: Optional[Union[str, str]] = None,
|
|
144
|
+
image_path: Optional[str] = None
|
|
145
|
+
):
|
|
146
|
+
"""Set notification content with enhanced styling options"""
|
|
147
|
+
self.builder.setContentTitle(title)
|
|
148
|
+
self.builder.setContentText(message)
|
|
149
|
+
|
|
150
|
+
if style == NotificationStyle.BIG_TEXT:
|
|
151
|
+
big_text = style_content or message
|
|
152
|
+
self.builder.setStyle(
|
|
153
|
+
self.android.BigTextStyle().bigText(big_text)
|
|
154
|
+
)
|
|
155
|
+
elif style == NotificationStyle.BIG_PICTURE and image_path:
|
|
156
|
+
bitmap = self._load_image(image_path)
|
|
157
|
+
if bitmap:
|
|
158
|
+
self.builder.setStyle(
|
|
159
|
+
self.android.BigPictureStyle()
|
|
160
|
+
.bigPicture(bitmap)
|
|
161
|
+
.setBigContentTitle(title)
|
|
162
|
+
)
|
|
163
|
+
elif style == NotificationStyle.INBOX and isinstance(style_content, list):
|
|
164
|
+
inbox_style = self.android.InboxStyle()
|
|
165
|
+
for line in style_content:
|
|
166
|
+
inbox_style.addLine(line)
|
|
167
|
+
self.builder.setStyle(inbox_style)
|
|
168
|
+
elif style == NotificationStyle.MESSAGING:
|
|
169
|
+
messaging_style = self.android.MessagingStyle("User")
|
|
170
|
+
if isinstance(style_content, list):
|
|
171
|
+
for msg in style_content:
|
|
172
|
+
messaging_style.addMessage(msg, 0, "Sender")
|
|
173
|
+
self.builder.setStyle(messaging_style)
|
|
174
|
+
|
|
175
|
+
def add_actions(self, actions: List[NotificationAction]):
|
|
176
|
+
"""Add multiple actions (buttons) to the notification"""
|
|
177
|
+
for action in actions:
|
|
178
|
+
intent = self.android.Intent(
|
|
179
|
+
self.android.Context,
|
|
180
|
+
self.android.PythonActivity
|
|
181
|
+
)
|
|
182
|
+
intent.setAction(action.intent)
|
|
183
|
+
|
|
184
|
+
pending_intent = self.android.PendingIntent.getActivity(
|
|
185
|
+
self.android.Context,
|
|
186
|
+
random.randint(0, 10000), # Unique request code
|
|
187
|
+
intent,
|
|
188
|
+
self.android.PendingIntent.FLAG_IMMUTABLE
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
icon = action.icon or self.android.Context.getApplicationInfo().icon
|
|
192
|
+
action_text = cast('java.lang.CharSequence',
|
|
193
|
+
self.android.String(action.name))
|
|
194
|
+
|
|
195
|
+
self.builder.addAction(icon, action_text, pending_intent)
|
|
196
|
+
|
|
197
|
+
def set_icons(
|
|
198
|
+
self,
|
|
199
|
+
small_icon_path: Optional[str] = None,
|
|
200
|
+
large_icon_path: Optional[str] = None
|
|
201
|
+
):
|
|
202
|
+
"""Set both small and large icons"""
|
|
203
|
+
if small_icon_path:
|
|
204
|
+
small_icon = self._load_image(small_icon_path)
|
|
205
|
+
if small_icon:
|
|
206
|
+
self.builder.setSmallIcon(small_icon)
|
|
207
|
+
else:
|
|
208
|
+
self.builder.setSmallIcon(
|
|
209
|
+
self.android.Context.getApplicationInfo().icon
|
|
210
|
+
)
|
|
211
|
+
|
|
212
|
+
if large_icon_path:
|
|
213
|
+
large_icon = self._load_image(large_icon_path)
|
|
214
|
+
if large_icon:
|
|
215
|
+
self.builder.setLargeIcon(large_icon)
|
|
216
|
+
|
|
217
|
+
def _load_image(self, image_path: str):
|
|
218
|
+
"""Load image from app storage with error handling"""
|
|
219
|
+
try:
|
|
220
|
+
uri = self.__get_image_uri(image_path)
|
|
221
|
+
return self.android.BitmapFactory.decodeStream(
|
|
222
|
+
self.android.Context.getContentResolver().openInputStream(uri)
|
|
223
|
+
)
|
|
224
|
+
except Exception as e:
|
|
225
|
+
print(f"Failed to load image: {e}")
|
|
226
|
+
return None
|
|
227
|
+
|
|
228
|
+
def __get_image_uri(self, relative_path: str):
|
|
229
|
+
"""Get URI for image file"""
|
|
230
|
+
from android.storage import app_storage_path # type: ignore
|
|
231
|
+
|
|
232
|
+
output_path = os.path.join(
|
|
233
|
+
app_storage_path(),
|
|
234
|
+
'app',
|
|
235
|
+
relative_path
|
|
236
|
+
)
|
|
237
|
+
if not os.path.exists(output_path):
|
|
238
|
+
raise FileNotFoundError(
|
|
239
|
+
f"Image not found at path: {output_path}"
|
|
240
|
+
)
|
|
241
|
+
|
|
242
|
+
Uri = autoclass('android.net.Uri')
|
|
243
|
+
return Uri.parse(f"file://{output_path}")
|
|
244
|
+
|
|
245
|
+
def set_priority(self, priority: NotificationPriority):
|
|
246
|
+
"""Set notification priority"""
|
|
247
|
+
self.builder.setPriority(priority.value)
|
|
248
|
+
|
|
249
|
+
def set_ongoing(self, ongoing: bool = True):
|
|
250
|
+
"""Set whether notification is ongoing"""
|
|
251
|
+
self.builder.setOngoing(ongoing)
|
|
252
|
+
|
|
253
|
+
def set_auto_cancel(self, auto_cancel: bool = True):
|
|
254
|
+
"""Set whether notification is automatically canceled on click"""
|
|
255
|
+
self.builder.setAutoCancel(auto_cancel)
|
|
256
|
+
|
|
257
|
+
def send(self) -> int:
|
|
258
|
+
"""Send the notification and return its ID"""
|
|
259
|
+
notification_manager = self.android.Context.getSystemService(
|
|
260
|
+
self.android.Context.NOTIFICATION_SERVICE
|
|
261
|
+
)
|
|
262
|
+
notification_manager.notify(
|
|
263
|
+
self.notification_id,
|
|
264
|
+
self.builder.build()
|
|
265
|
+
)
|
|
266
|
+
return self.notification_id
|
|
267
|
+
|
|
268
|
+
def update(
|
|
269
|
+
self,
|
|
270
|
+
title: Optional[str] = None,
|
|
271
|
+
message: Optional[str] = None,
|
|
272
|
+
progress: Optional[tuple] = None
|
|
273
|
+
):
|
|
274
|
+
"""Update existing notification"""
|
|
275
|
+
if title:
|
|
276
|
+
self.builder.setContentTitle(title)
|
|
277
|
+
if message:
|
|
278
|
+
self.builder.setContentText(message)
|
|
279
|
+
if progress:
|
|
280
|
+
current, max_value = progress
|
|
281
|
+
self.builder.setProgress(max_value, current, False)
|
|
282
|
+
|
|
283
|
+
self.send()
|
|
284
|
+
|
|
285
|
+
def cancel(self):
|
|
286
|
+
"""Cancel this notification"""
|
|
287
|
+
notification_manager = self.android.Context.getSystemService(
|
|
288
|
+
self.android.Context.NOTIFICATION_SERVICE
|
|
289
|
+
)
|
|
290
|
+
notification_manager.cancel(self.notification_id)
|
|
291
|
+
self._notification_ids.remove(self.notification_id)
|
|
292
|
+
|
|
293
|
+
@classmethod
|
|
294
|
+
def cancel_all(cls):
|
|
295
|
+
"""Cancel all notifications"""
|
|
296
|
+
android = AndroidNotificationSystem()
|
|
297
|
+
notification_manager = android.Context.getSystemService(
|
|
298
|
+
android.Context.NOTIFICATION_SERVICE
|
|
299
|
+
)
|
|
300
|
+
notification_manager.cancelAll()
|
|
301
|
+
cls._notification_ids.clear()
|
|
302
|
+
|
|
303
|
+
# Example usage:
|
|
304
|
+
def example_usage():
|
|
305
|
+
# Create a notification
|
|
306
|
+
notification = EnhancedNotificationManager(
|
|
307
|
+
channel_id="messages",
|
|
308
|
+
channel_name="Messages",
|
|
309
|
+
importance=NotificationImportance.HIGH
|
|
310
|
+
)
|
|
311
|
+
|
|
312
|
+
# Set basic content
|
|
313
|
+
notification.set_content(
|
|
314
|
+
title="New Message",
|
|
315
|
+
message="Hello World!",
|
|
316
|
+
style=NotificationStyle.BIG_TEXT
|
|
317
|
+
)
|
|
318
|
+
|
|
319
|
+
# Add actions
|
|
320
|
+
actions = [
|
|
321
|
+
NotificationAction("Reply", "REPLY_ACTION"),
|
|
322
|
+
NotificationAction("Delete", "DELETE_ACTION")
|
|
323
|
+
]
|
|
324
|
+
notification.add_actions(actions)
|
|
325
|
+
|
|
326
|
+
# Set icons
|
|
327
|
+
notification.set_icons(
|
|
328
|
+
large_icon_path="assets/imgs/profile.png"
|
|
329
|
+
)
|
|
330
|
+
|
|
331
|
+
# Configure behavior
|
|
332
|
+
notification.set_priority(NotificationPriority.HIGH)
|
|
333
|
+
notification.set_auto_cancel(True)
|
|
334
|
+
|
|
335
|
+
# Send the notification
|
|
336
|
+
notification_id = notification.send()
|
|
337
|
+
|
|
338
|
+
# Later, update the notification
|
|
339
|
+
notification.update(
|
|
340
|
+
message="Updated message!",
|
|
341
|
+
progress=(50, 100)
|
|
342
|
+
)
|
|
343
|
+
|
|
344
|
+
# Cancel when done
|
|
345
|
+
notification.cancel()
|
android_notify/core.py
CHANGED
|
@@ -26,7 +26,6 @@ if ON_ANDROID:
|
|
|
26
26
|
# Notification Design
|
|
27
27
|
NotificationCompatBuilder = autoclass('androidx.core.app.NotificationCompat$Builder')
|
|
28
28
|
NotificationCompatBigTextStyle = autoclass('androidx.core.app.NotificationCompat$BigTextStyle')
|
|
29
|
-
# NotificationCompatBigTextStyle = autoclass('android.app.Notification$BigTextStyle')
|
|
30
29
|
NotificationCompatBigPictureStyle = autoclass('androidx.core.app.NotificationCompat$BigPictureStyle')
|
|
31
30
|
NotificationCompatInboxStyle = autoclass('androidx.core.app.NotificationCompat$InboxStyle')
|
|
32
31
|
except Exception as e:
|
|
@@ -65,7 +64,7 @@ def get_image_uri(relative_path):
|
|
|
65
64
|
return Uri.parse(f"file://{output_path}")
|
|
66
65
|
|
|
67
66
|
|
|
68
|
-
def send_notification(title:str, message:str, style=None, img_path=None, channel_id:str="default_channel"):
|
|
67
|
+
def send_notification(title:str, message:str, style=None, img_path=None, channel_name="Default Channel",channel_id:str="default_channel"):
|
|
69
68
|
"""
|
|
70
69
|
Send a notification on Android.
|
|
71
70
|
|
|
@@ -73,13 +72,13 @@ def send_notification(title:str, message:str, style=None, img_path=None, channel
|
|
|
73
72
|
:param message: Message body.
|
|
74
73
|
:param style: Style of the notification ('big_text', 'big_picture', 'inbox', 'large_icon').
|
|
75
74
|
:param img_path: Path to the image resource.
|
|
76
|
-
:param channel_id: Notification channel ID.
|
|
75
|
+
:param channel_id: Notification channel ID.(Default is lowercase channel name arg in lowercase)
|
|
77
76
|
"""
|
|
78
77
|
if not ON_ANDROID:
|
|
79
78
|
print('This Package Only Runs on Android !!! ---> Check "https://github.com/Fector101/android_notify/" for Documentation.')
|
|
80
79
|
return
|
|
81
80
|
asks_permission_if_needed()
|
|
82
|
-
|
|
81
|
+
channel_id=channel_name.replace(' ','_').lower().lower() if not channel_id else channel_id
|
|
83
82
|
# Get notification manager
|
|
84
83
|
notification_manager = context.getSystemService(context.NOTIFICATION_SERVICE)
|
|
85
84
|
|
|
@@ -88,7 +87,7 @@ def send_notification(title:str, message:str, style=None, img_path=None, channel
|
|
|
88
87
|
|
|
89
88
|
# Notification Channel (Required for Android 8.0+)
|
|
90
89
|
if BuildVersion.SDK_INT >= 26:
|
|
91
|
-
channel = NotificationChannel(channel_id,
|
|
90
|
+
channel = NotificationChannel(channel_id, channel_name,importance)
|
|
92
91
|
notification_manager.createNotificationChannel(channel)
|
|
93
92
|
|
|
94
93
|
# Build the notification
|
|
@@ -130,4 +129,5 @@ def send_notification(title:str, message:str, style=None, img_path=None, channel
|
|
|
130
129
|
# Display the notification
|
|
131
130
|
notification_id = random.randint(0, 100)
|
|
132
131
|
notification_manager.notify(notification_id, builder.build())
|
|
133
|
-
return notification_id
|
|
132
|
+
return notification_id
|
|
133
|
+
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: android-notify
|
|
3
|
-
Version: 1.
|
|
4
|
-
Summary: A Python package
|
|
3
|
+
Version: 1.24
|
|
4
|
+
Summary: A Python package that simpilfies creating Android Post notifications using PyJNIus in Kivy apps.
|
|
5
5
|
Home-page: https://github.com/fector101/android-notify
|
|
6
6
|
Author: Fabian
|
|
7
7
|
Author-email: fector101@yahoo.com
|
|
@@ -14,7 +14,7 @@ Keywords: android,notifications,kivy,mobile,post notifications,pyjnius,android n
|
|
|
14
14
|
Classifier: Programming Language :: Python :: 3
|
|
15
15
|
Classifier: License :: OSI Approved :: MIT License
|
|
16
16
|
Classifier: Operating System :: Android
|
|
17
|
-
Classifier: Development Status ::
|
|
17
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
18
18
|
Classifier: Intended Audience :: Developers
|
|
19
19
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
20
20
|
Requires-Python: >=3.6
|
|
@@ -128,6 +128,25 @@ send_notification(
|
|
|
128
128
|
|
|
129
129
|
---
|
|
130
130
|
|
|
131
|
+
### Version 1.24 (Channel Name)
|
|
132
|
+
|
|
133
|
+
- Can be found in App Settings where user can turn on/off specific Notifications
|
|
134
|
+
|
|
135
|
+
```python
|
|
136
|
+
# channel_id default is now channel_name in lowercase with spaces replaced for underscores(_)
|
|
137
|
+
# In later version channel_id will be used to reference notification
|
|
138
|
+
send_notification(
|
|
139
|
+
title="Downlooad Notifications"
|
|
140
|
+
message="How to Catch a Fish.mp4"
|
|
141
|
+
channel_name="Download finished"
|
|
142
|
+
)
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
**Sample Image:**
|
|
146
|
+

|
|
147
|
+
|
|
148
|
+
---
|
|
149
|
+
|
|
131
150
|
### **Assist**
|
|
132
151
|
|
|
133
152
|
- How to Copy image to app folder
|
|
@@ -242,7 +261,7 @@ For feedback or contributions, feel free to reach out!
|
|
|
242
261
|
|
|
243
262
|
## ☕ Support the Project
|
|
244
263
|
|
|
245
|
-
If you find this project helpful, consider buying me a coffee! Your support helps maintain and improve the project.
|
|
264
|
+
If you find this project helpful, consider buying me a coffee! Or Giving it a star on 🌟 [GitHub](https://github.com/Fector101/android_notify/) Your support helps maintain and improve the project.
|
|
246
265
|
|
|
247
266
|
<a href="https://www.buymeacoffee.com/fector101" target="_blank">
|
|
248
267
|
<img src="https://cdn.buymeacoffee.com/buttons/v2/default-yellow.png" alt="Buy Me A Coffee" height="60">
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
android_notify/__init__.py,sha256=37Y04a1PbxeQlOpruIUhr7KibSZRfrV9uvSeelCNO58,261
|
|
2
|
+
android_notify/__main__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
3
|
+
android_notify/_dev.py,sha256=zGHGQu8Tv2RBtxiPDdIR0H-TLbzDocktIu_d78IgQgU,5207
|
|
4
|
+
android_notify/advanced_dev.py,sha256=sUY3OjTC3arvtqOKtDpor14QSTRrwsuMI9susyfXwZo,12065
|
|
5
|
+
android_notify/core.py,sha256=JkI5uiJof_gbuJJQ6YqgwWBTOWkhSKsGtVR8A9IIUN4,6024
|
|
6
|
+
android_notify/styles.py,sha256=P_8sAqb3Hbf_vbhqSoCVjKeqJ05Fr_CksO-HX5pj8pU,134
|
|
7
|
+
android_notify-1.24.dist-info/METADATA,sha256=hD2y3u_5Jg2o82BX1gPGiAQNVYLqIJuoGyzN7YRL8N8,7253
|
|
8
|
+
android_notify-1.24.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
|
|
9
|
+
android_notify-1.24.dist-info/top_level.txt,sha256=IR1ONMrRSRINZpWn2X0dL5gbWwWINsK7PW8Jy2p4fU8,15
|
|
10
|
+
android_notify-1.24.dist-info/RECORD,,
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
android_notify/__init__.py,sha256=DOUMyhwcXFBbamkxf0fJCBtRemTGXTiJTrQpKV_XF_A,74
|
|
2
|
-
android_notify/__main__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
3
|
-
android_notify/core.py,sha256=_47jZI0es_5PsqEVXL-WVsj815HOzn5hEU5d8s3-6j0,5948
|
|
4
|
-
android_notify/styles.py,sha256=P_8sAqb3Hbf_vbhqSoCVjKeqJ05Fr_CksO-HX5pj8pU,134
|
|
5
|
-
android_notify-1.23.1.dist-info/METADATA,sha256=GGxGcWmZa0GZtJxUxYFn_5Su9jV7K8iIF9uY_3DHRbE,6650
|
|
6
|
-
android_notify-1.23.1.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
|
|
7
|
-
android_notify-1.23.1.dist-info/top_level.txt,sha256=IR1ONMrRSRINZpWn2X0dL5gbWwWINsK7PW8Jy2p4fU8,15
|
|
8
|
-
android_notify-1.23.1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|