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/__init__.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
""""For Easier Imports For Public Classes"""
|
|
2
|
-
from .core import send_notification
|
|
3
|
-
from .styles import NotificationStyles
|
|
4
|
-
from .sword import Notification,NotificationHandler
|
|
1
|
+
""""For Easier Imports For Public Classes"""
|
|
2
|
+
from .core import send_notification
|
|
3
|
+
from .styles import NotificationStyles
|
|
4
|
+
from .sword import Notification,NotificationHandler
|
android_notify/__main__.py
CHANGED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
from .config import __version__
|
|
3
|
+
|
|
4
|
+
def print_version():
|
|
5
|
+
text = f"android_notify: v{__version__}"
|
|
6
|
+
border = '+'+'-'*(len(text) + 2)+'+'
|
|
7
|
+
print(border)
|
|
8
|
+
print(f'| {text} |')
|
|
9
|
+
print(border)
|
|
10
|
+
|
|
11
|
+
def main():
|
|
12
|
+
parser = argparse.ArgumentParser(description="Android Notify CLI")
|
|
13
|
+
parser.add_argument('-v','--version', action='store_true', help="Show the version of android_notify")
|
|
14
|
+
args = parser.parse_args()
|
|
15
|
+
|
|
16
|
+
if args.version:
|
|
17
|
+
print_version()
|
|
18
|
+
# # Placeholder for the main functionality
|
|
19
|
+
# print("Android Notify CLI is running...")
|
|
20
|
+
# DEV: pip install -e ., when edit and test project locally
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
if __name__ == "__main__":
|
|
24
|
+
main()
|
android_notify/an_types.py
CHANGED
|
@@ -1,91 +1,226 @@
|
|
|
1
|
-
"""For autocomplete Storing Reference to Available Methods"""
|
|
2
|
-
from typing import Literal
|
|
3
|
-
Importance = Literal['urgent','high','medium','low','none']
|
|
4
|
-
"""
|
|
5
|
-
:argument urgent - Makes a sound and appears as a heads-up notification.
|
|
6
|
-
|
|
7
|
-
:argument high - Makes a sound.
|
|
8
|
-
|
|
9
|
-
:argument urgent - Makes no sound.
|
|
10
|
-
|
|
11
|
-
:argument urgent - Makes no sound and doesn't appear in the status bar.
|
|
12
|
-
|
|
13
|
-
:argument urgent - Makes no sound and doesn't in the status bar or shade.
|
|
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
|
-
|
|
1
|
+
"""For autocomplete Storing Reference to Available Methods"""
|
|
2
|
+
from typing import Literal
|
|
3
|
+
Importance = Literal['urgent','high','medium','low','none']
|
|
4
|
+
"""
|
|
5
|
+
:argument urgent - Makes a sound and appears as a heads-up notification.
|
|
6
|
+
|
|
7
|
+
:argument high - Makes a sound.
|
|
8
|
+
|
|
9
|
+
:argument urgent - Makes no sound.
|
|
10
|
+
|
|
11
|
+
:argument urgent - Makes no sound and doesn't appear in the status bar.
|
|
12
|
+
|
|
13
|
+
:argument urgent - Makes no sound and doesn't in the status bar or shade.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
# For Dev
|
|
18
|
+
# Idea for typing autocompletion and reference
|
|
19
|
+
class Bundle:
|
|
20
|
+
def putString(self,key,value):
|
|
21
|
+
print(self,key,value)
|
|
22
|
+
|
|
23
|
+
def putInt(self,key,value):
|
|
24
|
+
print(self, key, value)
|
|
25
|
+
|
|
26
|
+
class String(str):
|
|
27
|
+
pass
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class Intent:
|
|
31
|
+
def __init__(self,context,activity):
|
|
32
|
+
self.obj={}
|
|
33
|
+
pass
|
|
34
|
+
|
|
35
|
+
def setAction(self,action):
|
|
36
|
+
print(action)
|
|
37
|
+
return self
|
|
38
|
+
|
|
39
|
+
def setFlags(self,intent_flag):
|
|
40
|
+
print(intent_flag)
|
|
41
|
+
return self
|
|
42
|
+
|
|
43
|
+
def getAction(self):
|
|
44
|
+
return self
|
|
45
|
+
|
|
46
|
+
def getStringExtra(self,key):
|
|
47
|
+
return self
|
|
48
|
+
|
|
49
|
+
def putExtra(self,key,value):
|
|
50
|
+
self.obj[key] = value
|
|
51
|
+
|
|
52
|
+
def putExtras(self,bundle:Bundle):
|
|
53
|
+
self.obj['bundle'] = bundle
|
|
54
|
+
|
|
55
|
+
class PendingIntent:
|
|
56
|
+
FLAG_IMMUTABLE=''
|
|
57
|
+
FLAG_UPDATE_CURRENT=''
|
|
58
|
+
|
|
59
|
+
def getActivity(self,context,value,action_intent,pending_intent_type):
|
|
60
|
+
pass
|
|
61
|
+
|
|
62
|
+
class BitmapFactory:
|
|
63
|
+
def decodeStream(self,stream):
|
|
64
|
+
pass
|
|
65
|
+
|
|
66
|
+
class BuildVersion:
|
|
67
|
+
SDK_INT=0
|
|
68
|
+
|
|
69
|
+
class NotificationManager:
|
|
70
|
+
pass
|
|
71
|
+
|
|
72
|
+
class NotificationChannel:
|
|
73
|
+
def __init__(self,channel_id,channel_name,importance):
|
|
74
|
+
self.description = None
|
|
75
|
+
self.channel_id = None
|
|
76
|
+
self.channel = None
|
|
77
|
+
def createNotificationChannel(self, channel):
|
|
78
|
+
self.channel=channel
|
|
79
|
+
|
|
80
|
+
def getNotificationChannel(self, channel_id):
|
|
81
|
+
self.channel_id=channel_id
|
|
82
|
+
pass
|
|
83
|
+
|
|
84
|
+
def setDescription(self, description):
|
|
85
|
+
self.description=description
|
|
86
|
+
|
|
87
|
+
def getId(self):
|
|
88
|
+
return self.channel_id
|
|
89
|
+
|
|
90
|
+
class IconCompat:
|
|
91
|
+
def createWithBitmap(self,bitmap):
|
|
92
|
+
pass
|
|
93
|
+
|
|
94
|
+
class Color:
|
|
95
|
+
def __init__(self):
|
|
96
|
+
pass
|
|
97
|
+
def parseColor(self,color:str):
|
|
98
|
+
return self
|
|
99
|
+
|
|
100
|
+
class RemoteViews:
|
|
101
|
+
def __init__(self, package_name, small_layout_id):
|
|
102
|
+
pass
|
|
103
|
+
def createWithBitmap(self,bitmap):
|
|
104
|
+
pass
|
|
105
|
+
def setTextViewText(self,id, text):
|
|
106
|
+
pass
|
|
107
|
+
def setTextColor(self,id, color:Color):
|
|
108
|
+
pass
|
|
109
|
+
|
|
110
|
+
class NotificationManagerCompat:
|
|
111
|
+
IMPORTANCE_HIGH=4
|
|
112
|
+
IMPORTANCE_DEFAULT=3
|
|
113
|
+
IMPORTANCE_LOW=''
|
|
114
|
+
IMPORTANCE_MIN=''
|
|
115
|
+
IMPORTANCE_NONE=''
|
|
116
|
+
|
|
117
|
+
class NotificationCompat:
|
|
118
|
+
DEFAULT_ALL=3
|
|
119
|
+
PRIORITY_HIGH=4
|
|
120
|
+
PRIORITY_DEFAULT = ''
|
|
121
|
+
PRIORITY_LOW=''
|
|
122
|
+
PRIORITY_MIN=''
|
|
123
|
+
|
|
124
|
+
class MActions:
|
|
125
|
+
def clear(self):
|
|
126
|
+
"""This Removes all buttons"""
|
|
127
|
+
print('dummy clear')
|
|
128
|
+
|
|
129
|
+
class NotificationCompatBuilder:
|
|
130
|
+
def __init__(self,context,channel_id):
|
|
131
|
+
self.mActions = MActions()
|
|
132
|
+
pass
|
|
133
|
+
def setProgress(self,max_value,current_value,endless):
|
|
134
|
+
pass
|
|
135
|
+
def setStyle(self,style):
|
|
136
|
+
pass
|
|
137
|
+
def setContentTitle(self,title):
|
|
138
|
+
pass
|
|
139
|
+
def setContentText(self,text):
|
|
140
|
+
pass
|
|
141
|
+
def setSmallIcon(self,icon):
|
|
142
|
+
pass
|
|
143
|
+
def setLargeIcon(self,icon):
|
|
144
|
+
pass
|
|
145
|
+
def setAutoCancel(self,auto_cancel:bool):
|
|
146
|
+
pass
|
|
147
|
+
def setPriority(self,priority:Importance):
|
|
148
|
+
pass
|
|
149
|
+
def setDefaults(self,defaults:NotificationCompat.DEFAULT_ALL):
|
|
150
|
+
pass
|
|
151
|
+
def setOngoing(self,persistent:bool):
|
|
152
|
+
pass
|
|
153
|
+
def setOnlyAlertOnce(self,state):
|
|
154
|
+
pass
|
|
155
|
+
def build(self):
|
|
156
|
+
pass
|
|
157
|
+
def setContentIntent(self,pending_action_intent:PendingIntent):
|
|
158
|
+
pass
|
|
159
|
+
def addAction(self,icon_int,action_text,pending_action_intent):
|
|
160
|
+
pass
|
|
161
|
+
def setShowWhen(self,state):
|
|
162
|
+
pass
|
|
163
|
+
def setWhen(self,time_ms):
|
|
164
|
+
pass
|
|
165
|
+
def setCustomContentView(self,layout):
|
|
166
|
+
pass
|
|
167
|
+
def setCustomBigContentView(self,layout):
|
|
168
|
+
pass
|
|
169
|
+
def setSubText(self,text):
|
|
170
|
+
pass
|
|
171
|
+
def setColor(self, color:Color) -> None:
|
|
172
|
+
pass
|
|
173
|
+
class NotificationCompatBigTextStyle:
|
|
174
|
+
def bigText(self,body):
|
|
175
|
+
return self
|
|
176
|
+
|
|
177
|
+
class NotificationCompatBigPictureStyle:
|
|
178
|
+
def bigPicture(self,bitmap):
|
|
179
|
+
return self
|
|
180
|
+
|
|
181
|
+
class NotificationCompatInboxStyle:
|
|
182
|
+
def addLine(self,line):
|
|
183
|
+
return self
|
|
184
|
+
|
|
185
|
+
class NotificationCompatDecoratedCustomViewStyle:
|
|
186
|
+
pass
|
|
187
|
+
|
|
188
|
+
class Permission:
|
|
189
|
+
POST_NOTIFICATIONS=''
|
|
190
|
+
|
|
191
|
+
def check_permission(permission:Permission.POST_NOTIFICATIONS):
|
|
192
|
+
print(permission)
|
|
193
|
+
|
|
194
|
+
def request_permissions(_list: [], _callback):
|
|
195
|
+
_callback()
|
|
196
|
+
|
|
197
|
+
class AndroidActivity:
|
|
198
|
+
def bind(self,on_new_intent):
|
|
199
|
+
pass
|
|
200
|
+
def unbind(self,on_new_intent):
|
|
201
|
+
pass
|
|
202
|
+
|
|
203
|
+
class PythonActivity:
|
|
204
|
+
pass
|
|
205
|
+
|
|
206
|
+
class DummyIcon:
|
|
207
|
+
icon = 101
|
|
208
|
+
pass
|
|
209
|
+
class Context:
|
|
210
|
+
def __init__(self):
|
|
211
|
+
pass
|
|
212
|
+
|
|
213
|
+
@staticmethod
|
|
214
|
+
def getApplicationInfo():
|
|
215
|
+
return DummyIcon
|
|
216
|
+
|
|
217
|
+
@staticmethod
|
|
218
|
+
def getResources():
|
|
219
|
+
return None
|
|
220
|
+
|
|
221
|
+
@staticmethod
|
|
222
|
+
def getPackageName():
|
|
223
|
+
return None # TODO get package name from buildozer.spec file
|
|
224
|
+
|
|
225
|
+
#Now writing Knowledge from errors
|
|
226
|
+
# notify.(int, Builder.build()) # must be int
|
android_notify/an_utils.py
CHANGED
|
@@ -1,18 +1,129 @@
|
|
|
1
|
-
"""Collection of useful functions"""
|
|
2
|
-
|
|
3
|
-
import inspect
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
1
|
+
"""Collection of useful functions"""
|
|
2
|
+
|
|
3
|
+
import inspect, os, re, traceback
|
|
4
|
+
from .config import autoclass
|
|
5
|
+
from .an_types import Importance
|
|
6
|
+
from .config import (
|
|
7
|
+
get_python_activity_context, app_storage_path,ON_ANDROID,
|
|
8
|
+
BitmapFactory, BuildVersion, Bundle,
|
|
9
|
+
NotificationManagerCompat,NotificationCompat
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
if ON_ANDROID:
|
|
13
|
+
Color = autoclass('android.graphics.Color')
|
|
14
|
+
else:
|
|
15
|
+
from .an_types import Color
|
|
16
|
+
|
|
17
|
+
def can_accept_arguments(func, *args, **kwargs):
|
|
18
|
+
try:
|
|
19
|
+
sig = inspect.signature(func)
|
|
20
|
+
sig.bind(*args, **kwargs)
|
|
21
|
+
return True
|
|
22
|
+
except TypeError:
|
|
23
|
+
return False
|
|
24
|
+
|
|
25
|
+
if ON_ANDROID:
|
|
26
|
+
context = get_python_activity_context()
|
|
27
|
+
else:
|
|
28
|
+
context = None
|
|
29
|
+
|
|
30
|
+
def get_android_importance(importance: Importance):
|
|
31
|
+
"""
|
|
32
|
+
Returns Android Importance Values
|
|
33
|
+
:param importance: ['urgent','high','medium','low','none']
|
|
34
|
+
:return: Android equivalent int or empty str
|
|
35
|
+
"""
|
|
36
|
+
if not ON_ANDROID:
|
|
37
|
+
return None
|
|
38
|
+
value = ''
|
|
39
|
+
if importance == 'urgent':
|
|
40
|
+
value = NotificationCompat.PRIORITY_HIGH if BuildVersion.SDK_INT <= 25 else NotificationManagerCompat.IMPORTANCE_HIGH
|
|
41
|
+
elif importance == 'high':
|
|
42
|
+
value = NotificationCompat.PRIORITY_DEFAULT if BuildVersion.SDK_INT <= 25 else NotificationManagerCompat.IMPORTANCE_DEFAULT
|
|
43
|
+
elif importance == 'medium':
|
|
44
|
+
value = NotificationCompat.PRIORITY_LOW if BuildVersion.SDK_INT <= 25 else NotificationManagerCompat.IMPORTANCE_LOW
|
|
45
|
+
elif importance == 'low':
|
|
46
|
+
value = NotificationCompat.PRIORITY_MIN if BuildVersion.SDK_INT <= 25 else NotificationManagerCompat.IMPORTANCE_MIN
|
|
47
|
+
elif importance == 'none':
|
|
48
|
+
value = '' if BuildVersion.SDK_INT <= 25 else NotificationManagerCompat.IMPORTANCE_NONE
|
|
49
|
+
|
|
50
|
+
return value
|
|
51
|
+
# side-note 'medium' = NotificationCompat.PRIORITY_LOW and 'low' = NotificationCompat.PRIORITY_MIN # weird but from docs
|
|
52
|
+
|
|
53
|
+
def generate_channel_id(channel_name: str) -> str:
|
|
54
|
+
"""
|
|
55
|
+
Generate a readable and consistent channel ID from a channel name.
|
|
56
|
+
|
|
57
|
+
Args:
|
|
58
|
+
channel_name (str): The name of the notification channel.
|
|
59
|
+
|
|
60
|
+
Returns:
|
|
61
|
+
str: A sanitized channel ID.
|
|
62
|
+
"""
|
|
63
|
+
# Normalize the channel name
|
|
64
|
+
channel_id = channel_name.strip().lower()
|
|
65
|
+
# Replace spaces and special characters with underscores
|
|
66
|
+
channel_id = re.sub(r'[^a-z0-9]+', '_', channel_id)
|
|
67
|
+
# Remove leading/trailing underscores
|
|
68
|
+
channel_id = channel_id.strip('_')
|
|
69
|
+
return channel_id[:50]
|
|
70
|
+
|
|
71
|
+
def get_img_from_path(relative_path):
|
|
72
|
+
app_folder = os.path.join(app_storage_path(), 'app')
|
|
73
|
+
output_path = os.path.join(app_folder, relative_path)
|
|
74
|
+
if not os.path.exists(output_path):
|
|
75
|
+
print(f"\nImage not found at path: {app_folder}, (Local images gotten from App Path)")
|
|
76
|
+
try:
|
|
77
|
+
print("- These are the existing files in your app Folder:")
|
|
78
|
+
print('[' + ', '.join(os.listdir(app_folder)) + ']')
|
|
79
|
+
except Exception as could_not_get_files_in_path_error:
|
|
80
|
+
print('Exception: ', could_not_get_files_in_path_error)
|
|
81
|
+
print("Couldn't get Files in App Folder")
|
|
82
|
+
return None
|
|
83
|
+
# TODO test with a badly written Image and catch error
|
|
84
|
+
Uri = autoclass('android.net.Uri')
|
|
85
|
+
uri = Uri.parse(f"file://{output_path}")
|
|
86
|
+
return BitmapFactory.decodeStream(context.getContentResolver().openInputStream(uri))
|
|
87
|
+
|
|
88
|
+
def setLayoutText(layout, id, text, color):
|
|
89
|
+
# checked if self.title_color available before entering method
|
|
90
|
+
if id and text:
|
|
91
|
+
layout.setTextViewText(id, text)
|
|
92
|
+
if color:
|
|
93
|
+
layout.setTextColor(id, Color.parseColor(color))
|
|
94
|
+
|
|
95
|
+
def get_bitmap_from_url(url, callback, logs):
|
|
96
|
+
"""Gets Bitmap from url
|
|
97
|
+
|
|
98
|
+
Args:
|
|
99
|
+
:param url: img url
|
|
100
|
+
:param callback: function to be called after thread done, callback receives bitmap data as argument
|
|
101
|
+
:param logs:
|
|
102
|
+
"""
|
|
103
|
+
if logs:
|
|
104
|
+
print("getting Bitmap from URL---")
|
|
105
|
+
try:
|
|
106
|
+
URL = autoclass('java.net.URL')
|
|
107
|
+
url = URL(url)
|
|
108
|
+
connection = url.openConnection()
|
|
109
|
+
connection.connect()
|
|
110
|
+
input_stream = connection.getInputStream()
|
|
111
|
+
bitmap = BitmapFactory.decodeStream(input_stream)
|
|
112
|
+
input_stream.close()
|
|
113
|
+
if bitmap:
|
|
114
|
+
callback(bitmap)
|
|
115
|
+
else:
|
|
116
|
+
print('Error No Bitmap for small icon ------------')
|
|
117
|
+
except Exception as extracting_bitmap_frm_URL_error:
|
|
118
|
+
callback(None)
|
|
119
|
+
# TODO get all types of JAVA Error that can fail here
|
|
120
|
+
print('Error Type ', extracting_bitmap_frm_URL_error)
|
|
121
|
+
print('Failed to get Bitmap from URL ', traceback.format_exc())
|
|
122
|
+
|
|
123
|
+
def add_data_to_intent(intent, title):
|
|
124
|
+
"""Persist Some data to notification object for later use"""
|
|
125
|
+
bundle = Bundle()
|
|
126
|
+
bundle.putString("title", title or 'Title Placeholder')
|
|
127
|
+
# bundle.putInt("notify_id", self.__id)
|
|
128
|
+
bundle.putInt("notify_id", 101)
|
|
129
|
+
intent.putExtras(bundle)
|