android-notify 1.59.4__py3-none-any.whl → 1.60.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- android_notify/__init__.py +4 -4
- android_notify/__main__.py +24 -0
- android_notify/an_types.py +226 -91
- android_notify/an_utils.py +129 -18
- android_notify/base.py +97 -90
- android_notify/config.py +145 -0
- android_notify/core.py +227 -139
- android_notify/styles.py +25 -22
- android_notify/sword.py +1043 -992
- {android_notify-1.59.4.dist-info → android_notify-1.60.1.dist-info}/METADATA +69 -12
- android_notify-1.60.1.dist-info/RECORD +13 -0
- android_notify-1.59.4.dist-info/RECORD +0 -12
- {android_notify-1.59.4.dist-info → android_notify-1.60.1.dist-info}/WHEEL +0 -0
- {android_notify-1.59.4.dist-info → android_notify-1.60.1.dist-info}/top_level.txt +0 -0
android_notify/core.py
CHANGED
|
@@ -1,139 +1,227 @@
|
|
|
1
|
-
""" Non-Advanced Stuff """
|
|
2
|
-
import random
|
|
3
|
-
import os
|
|
4
|
-
ON_ANDROID = False
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
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
|
+
path = ''
|
|
57
|
+
if on_flet_app():
|
|
58
|
+
path= os.path.join(context.getFilesDir().getAbsolutePath(),'flet')
|
|
59
|
+
else:
|
|
60
|
+
try:
|
|
61
|
+
from android.storage import app_storage_path # type: ignore
|
|
62
|
+
path = app_storage_path()
|
|
63
|
+
except Exception as e:
|
|
64
|
+
print('android-notify- Error getting apk main file path: ',e)
|
|
65
|
+
return './'
|
|
66
|
+
return os.path.join(path,'app')
|
|
67
|
+
|
|
68
|
+
def asks_permission_if_needed():
|
|
69
|
+
"""
|
|
70
|
+
Ask for permission to send notifications if needed.
|
|
71
|
+
"""
|
|
72
|
+
if on_flet_app():
|
|
73
|
+
ContextCompat = autoclass('androidx.core.content.ContextCompat')
|
|
74
|
+
# if you get error `Failed to find class: androidx/core/app/ActivityCompat`
|
|
75
|
+
#in proguard-rules.pro add `-keep class androidx.core.app.ActivityCompat { *; }`
|
|
76
|
+
ActivityCompat = autoclass('androidx.core.app.ActivityCompat')
|
|
77
|
+
Manifest = autoclass('android.Manifest$permission')
|
|
78
|
+
VERSION_CODES = autoclass('android.os.Build$VERSION_CODES')
|
|
79
|
+
|
|
80
|
+
if BuildVersion.SDK_INT >= VERSION_CODES.TIRAMISU:
|
|
81
|
+
permission = Manifest.POST_NOTIFICATIONS
|
|
82
|
+
granted = ContextCompat.checkSelfPermission(context, permission)
|
|
83
|
+
|
|
84
|
+
if granted != 0: # PackageManager.PERMISSION_GRANTED == 0
|
|
85
|
+
ActivityCompat.requestPermissions(context, [permission], 101)
|
|
86
|
+
else: # android package is from p4a which is for kivy
|
|
87
|
+
try:
|
|
88
|
+
from android.permissions import request_permissions, Permission,check_permission # type: ignore
|
|
89
|
+
permissions=[Permission.POST_NOTIFICATIONS]
|
|
90
|
+
if not all(check_permission(p) for p in permissions):
|
|
91
|
+
request_permissions(permissions)
|
|
92
|
+
except Exception as e:
|
|
93
|
+
print("android_notify- error trying to request notification access: ", e)
|
|
94
|
+
|
|
95
|
+
def get_image_uri(relative_path):
|
|
96
|
+
"""
|
|
97
|
+
Get the absolute URI for an image in the assets folder.
|
|
98
|
+
:param relative_path: The relative path to the image (e.g., 'assets/imgs/icon.png').
|
|
99
|
+
:return: Absolute URI java Object (e.g., 'file:///path/to/file.png').
|
|
100
|
+
"""
|
|
101
|
+
app_root_path = get_app_root_path()
|
|
102
|
+
output_path = os.path.join(app_root_path, relative_path)
|
|
103
|
+
# print(output_path,'output_path') # /data/user/0/org.laner.lan_ft/files/app/assets/imgs/icon.png
|
|
104
|
+
|
|
105
|
+
if not os.path.exists(output_path):
|
|
106
|
+
raise FileNotFoundError(f"\nImage not found at path: {output_path}\n")
|
|
107
|
+
|
|
108
|
+
Uri = autoclass('android.net.Uri')
|
|
109
|
+
return Uri.parse(f"file://{output_path}")
|
|
110
|
+
|
|
111
|
+
def get_icon_object(uri):
|
|
112
|
+
BitmapFactory = autoclass('android.graphics.BitmapFactory')
|
|
113
|
+
IconCompat = autoclass('androidx.core.graphics.drawable.IconCompat')
|
|
114
|
+
|
|
115
|
+
bitmap= BitmapFactory.decodeStream(context.getContentResolver().openInputStream(uri))
|
|
116
|
+
return IconCompat.createWithBitmap(bitmap)
|
|
117
|
+
|
|
118
|
+
def insert_app_icon(builder,custom_icon_path):
|
|
119
|
+
if custom_icon_path:
|
|
120
|
+
try:
|
|
121
|
+
uri = get_image_uri(custom_icon_path)
|
|
122
|
+
icon = get_icon_object(uri)
|
|
123
|
+
builder.setSmallIcon(icon)
|
|
124
|
+
except Exception as e:
|
|
125
|
+
print('android_notify- error: ',e)
|
|
126
|
+
builder.setSmallIcon(context.getApplicationInfo().icon)
|
|
127
|
+
else:
|
|
128
|
+
# print('Found res icon -->',context.getApplicationInfo().icon,'<--')
|
|
129
|
+
builder.setSmallIcon(context.getApplicationInfo().icon)
|
|
130
|
+
|
|
131
|
+
def send_notification(
|
|
132
|
+
title:str,
|
|
133
|
+
message:str,
|
|
134
|
+
style=None,
|
|
135
|
+
img_path=None,
|
|
136
|
+
channel_name="Default Channel",
|
|
137
|
+
channel_id:str="default_channel",
|
|
138
|
+
custom_app_icon_path="",
|
|
139
|
+
|
|
140
|
+
big_picture_path='',
|
|
141
|
+
large_icon_path='',
|
|
142
|
+
big_text="",
|
|
143
|
+
lines=""
|
|
144
|
+
):
|
|
145
|
+
"""
|
|
146
|
+
Send a notification on Android.
|
|
147
|
+
|
|
148
|
+
:param title: Title of the notification.
|
|
149
|
+
:param message: Message body.
|
|
150
|
+
:param style: deprecated.
|
|
151
|
+
:param img_path: Path to the image resource.
|
|
152
|
+
:param channel_id: Notification channel ID.(Default is lowercase channel name arg in lowercase)
|
|
153
|
+
"""
|
|
154
|
+
if not ON_ANDROID:
|
|
155
|
+
print('This Package Only Runs on Android !!! ---> Check "https://github.com/Fector101/android_notify/" for Documentation.')
|
|
156
|
+
return
|
|
157
|
+
|
|
158
|
+
asks_permission_if_needed()
|
|
159
|
+
channel_id=channel_name.replace(' ','_').lower().lower() if not channel_id else channel_id
|
|
160
|
+
# Get notification manager
|
|
161
|
+
notification_manager = context.getSystemService(context.NOTIFICATION_SERVICE)
|
|
162
|
+
|
|
163
|
+
# importance= autoclass('android.app.NotificationManager').IMPORTANCE_HIGH # also works #NotificationManager.IMPORTANCE_DEFAULT
|
|
164
|
+
importance= NotificationManagerCompat.IMPORTANCE_HIGH #autoclass('android.app.NotificationManager').IMPORTANCE_HIGH also works #NotificationManager.IMPORTANCE_DEFAULT
|
|
165
|
+
|
|
166
|
+
# Notification Channel (Required for Android 8.0+)
|
|
167
|
+
if BuildVersion.SDK_INT >= 26:
|
|
168
|
+
channel = NotificationChannel(channel_id, channel_name,importance)
|
|
169
|
+
notification_manager.createNotificationChannel(channel)
|
|
170
|
+
|
|
171
|
+
# Build the notification
|
|
172
|
+
builder = NotificationCompatBuilder(context, channel_id)
|
|
173
|
+
builder.setContentTitle(title)
|
|
174
|
+
builder.setContentText(message)
|
|
175
|
+
insert_app_icon(builder,custom_app_icon_path)
|
|
176
|
+
builder.setDefaults(NotificationCompat.DEFAULT_ALL)
|
|
177
|
+
builder.setPriority(NotificationCompat.PRIORITY_HIGH)
|
|
178
|
+
|
|
179
|
+
if img_path:
|
|
180
|
+
print('android_notify- img_path arg deprecated use "large_icon_path or big_picture_path or custom_app_icon_path" instead')
|
|
181
|
+
if style:
|
|
182
|
+
print('android_notify- "style" arg deprecated use args "big_picture_path", "large_icon_path", "big_text", "lines" instead')
|
|
183
|
+
|
|
184
|
+
big_picture = None
|
|
185
|
+
if big_picture_path:
|
|
186
|
+
try:
|
|
187
|
+
big_picture = get_image_uri(big_picture_path)
|
|
188
|
+
except FileNotFoundError as e:
|
|
189
|
+
print('android_notify- Error Getting Uri for big_picture_path: ',e)
|
|
190
|
+
|
|
191
|
+
large_icon = None
|
|
192
|
+
if large_icon_path:
|
|
193
|
+
try:
|
|
194
|
+
large_icon = get_image_uri(large_icon_path)
|
|
195
|
+
except FileNotFoundError as e:
|
|
196
|
+
print('android_notify- Error Getting Uri for large_icon_path: ',e)
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
# Apply notification styles
|
|
200
|
+
try:
|
|
201
|
+
if big_text:
|
|
202
|
+
big_text_style = NotificationCompatBigTextStyle()
|
|
203
|
+
big_text_style.bigText(big_text)
|
|
204
|
+
builder.setStyle(big_text_style)
|
|
205
|
+
|
|
206
|
+
elif lines:
|
|
207
|
+
inbox_style = NotificationCompatInboxStyle()
|
|
208
|
+
for line in lines.split("\n"):
|
|
209
|
+
inbox_style.addLine(line)
|
|
210
|
+
builder.setStyle(inbox_style)
|
|
211
|
+
|
|
212
|
+
if large_icon:
|
|
213
|
+
bitmap = BitmapFactory.decodeStream(context.getContentResolver().openInputStream(large_icon))
|
|
214
|
+
builder.setLargeIcon(bitmap)
|
|
215
|
+
|
|
216
|
+
if big_picture:
|
|
217
|
+
bitmap = BitmapFactory.decodeStream(context.getContentResolver().openInputStream(big_picture))
|
|
218
|
+
big_picture_style = NotificationCompatBigPictureStyle().bigPicture(bitmap)
|
|
219
|
+
builder.setStyle(big_picture_style)
|
|
220
|
+
|
|
221
|
+
except Exception as e:
|
|
222
|
+
print('android_notify- Error Failed Adding Style: ',e)
|
|
223
|
+
# Display the notification
|
|
224
|
+
notification_id = random.randint(0, 100)
|
|
225
|
+
notification_manager.notify(notification_id, builder.build())
|
|
226
|
+
return notification_id
|
|
227
|
+
|
android_notify/styles.py
CHANGED
|
@@ -1,22 +1,25 @@
|
|
|
1
|
-
"""Contains Safe way to call Styles"""
|
|
2
|
-
|
|
3
|
-
class NotificationStyles:
|
|
4
|
-
""" Safely Adding Styles"""
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
#
|
|
11
|
-
#
|
|
12
|
-
#
|
|
13
|
-
|
|
14
|
-
#
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
1
|
+
"""Contains Safe way to call Styles"""
|
|
2
|
+
|
|
3
|
+
class NotificationStyles:
|
|
4
|
+
""" Safely Adding Styles"""
|
|
5
|
+
|
|
6
|
+
# v1.59+
|
|
7
|
+
# Deprecated
|
|
8
|
+
# setBigText == Notification(...,big_picture_path="...",style=NotificationStyles.BIG_TEXT)
|
|
9
|
+
# setLargeIcon == Notification(...,large_icon_path="...",style=NotificationStyles.LARGE_ICON)
|
|
10
|
+
# setBigPicture == Notification(...,body="...",style=NotificationStyles.BIG_PICTURE)
|
|
11
|
+
# setLines == Notification(...,lines_txt="...",style=NotificationStyles.INBOX)
|
|
12
|
+
# Set progress_current_value and progress_max_value for progress style
|
|
13
|
+
|
|
14
|
+
# Use .refresh to apply any new changes after .send
|
|
15
|
+
|
|
16
|
+
DEFAULT = "simple"
|
|
17
|
+
PROGRESS = "progress"
|
|
18
|
+
INBOX = "inbox"
|
|
19
|
+
BIG_TEXT = "big_text"
|
|
20
|
+
LARGE_ICON = "large_icon"
|
|
21
|
+
BIG_PICTURE = "big_picture"
|
|
22
|
+
BOTH_IMGS = "both_imgs"
|
|
23
|
+
|
|
24
|
+
# MESSAGING = "messaging" # TODO
|
|
25
|
+
# CUSTOM = "custom" # TODO v1.60
|