android-notify 1.3__py3-none-any.whl → 1.60.6.dev0__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 +2 -1
- android_notify/__main__.py +24 -0
- android_notify/an_types.py +341 -0
- android_notify/an_utils.py +209 -0
- android_notify/base.py +97 -0
- android_notify/config.py +167 -0
- android_notify/core.py +171 -76
- android_notify/fallback-icons/flet-appicon.png +0 -0
- android_notify/fallback-icons/pydroid3-appicon.png +0 -0
- android_notify/styles.py +17 -7
- android_notify/sword.py +1142 -325
- android_notify-1.60.6.dev0.dist-info/METADATA +171 -0
- android_notify-1.60.6.dev0.dist-info/RECORD +24 -0
- {android_notify-1.3.dist-info → android_notify-1.60.6.dev0.dist-info}/WHEEL +1 -1
- android_notify-1.60.6.dev0.dist-info/entry_points.txt +2 -0
- {android_notify-1.3.dist-info → android_notify-1.60.6.dev0.dist-info}/top_level.txt +1 -0
- docs/examples/flet-working/src/core.py +221 -0
- docs/examples/flet-working/src/main.py +68 -0
- docs/tests/flet/adv/main.py +97 -0
- docs/tests/flet/adv/tests/__init__.py +0 -0
- docs/tests/flet/adv/tests/test_android_notify_full.py +199 -0
- docs/tests/flet/basic/src/core.py +221 -0
- docs/tests/flet/basic/src/main.py +112 -0
- docs/website/src/pages/data/laner_Sent.py +24 -0
- android_notify-1.3.dist-info/METADATA +0 -350
- android_notify-1.3.dist-info/RECORD +0 -9
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Comprehensive test suite for Android-Notify Tester (Laner.use_android_notify)
|
|
3
|
+
Covers all notification styles, callbacks, and behaviors.
|
|
4
|
+
"""
|
|
5
|
+
import unittest
|
|
6
|
+
import time
|
|
7
|
+
import traceback
|
|
8
|
+
|
|
9
|
+
from android_notify import Notification, NotificationStyles, NotificationHandler, send_notification
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class TestAndroidNotifyFull(unittest.TestCase):
|
|
13
|
+
def tearDown(self):
|
|
14
|
+
time.sleep(3)
|
|
15
|
+
|
|
16
|
+
def setUp(self):
|
|
17
|
+
max_int = 2_147_483_647
|
|
18
|
+
self.uid = int(time.time() * 1000) % max_int
|
|
19
|
+
|
|
20
|
+
def test_simple(self):
|
|
21
|
+
try:
|
|
22
|
+
n = Notification( id=self.uid,title="Simple", message="Simple message test")
|
|
23
|
+
n.send()
|
|
24
|
+
except Exception as e:
|
|
25
|
+
self.fail(f"Simple notification failed: {e}")
|
|
26
|
+
|
|
27
|
+
def test_progress(self):
|
|
28
|
+
try:
|
|
29
|
+
n = Notification( id=self.uid,title="Downloading...", message="0% downloaded", progress_current_value=0, progress_max_value=100)
|
|
30
|
+
n.send()
|
|
31
|
+
for i in range(0, 101, 10):
|
|
32
|
+
time.sleep(2)
|
|
33
|
+
n.updateProgressBar(i, f"{i}% done")
|
|
34
|
+
n.removeProgressBar(message="Done", title="Download Complete")
|
|
35
|
+
except Exception as e:
|
|
36
|
+
self.fail(f"Progress notification failed: {e}")
|
|
37
|
+
|
|
38
|
+
def test_big_picture(self):
|
|
39
|
+
try:
|
|
40
|
+
n = Notification( id=self.uid,title="Big Picture", message="Testing big picture")
|
|
41
|
+
n.setBigPicture("assets/icon.png")
|
|
42
|
+
n.send()
|
|
43
|
+
except Exception as e:
|
|
44
|
+
self.fail(f"Big picture failed: {e}")
|
|
45
|
+
|
|
46
|
+
def test_inbox(self):
|
|
47
|
+
try:
|
|
48
|
+
n = Notification( id=self.uid,title="Inbox", message="Inbox notification")
|
|
49
|
+
n.setLines(["Line 1", "Line 2", "Line 3"])
|
|
50
|
+
n.send()
|
|
51
|
+
except Exception as e:
|
|
52
|
+
self.fail(f"Inbox notification failed: {e}")
|
|
53
|
+
|
|
54
|
+
def test_inbox_from_file_message(self):
|
|
55
|
+
try:
|
|
56
|
+
lines = "Test1\nTest2\nTest3"
|
|
57
|
+
n = Notification( id=self.uid,title="Inbox File", message="File Inbox Test", lines_txt=lines)
|
|
58
|
+
n.send()
|
|
59
|
+
except Exception as e:
|
|
60
|
+
self.fail(f"Inbox from file failed: {e}")
|
|
61
|
+
|
|
62
|
+
def test_inbox_add_line(self):
|
|
63
|
+
try:
|
|
64
|
+
n = Notification( id=self.uid,title="Inbox AddLine", message="Testing addLine()")
|
|
65
|
+
n.addLine("First line")
|
|
66
|
+
n.addLine("Second line")
|
|
67
|
+
n.addLine("Third line")
|
|
68
|
+
n.send()
|
|
69
|
+
except Exception as e:
|
|
70
|
+
self.fail(f"Inbox addLine failed: {e}")
|
|
71
|
+
|
|
72
|
+
def test_large_icon(self):
|
|
73
|
+
try:
|
|
74
|
+
n = Notification( id=self.uid,title="Large Icon", message="Testing large icon")
|
|
75
|
+
n.setLargeIcon("assets/icon.png")
|
|
76
|
+
n.send()
|
|
77
|
+
except Exception as e:
|
|
78
|
+
self.fail(f"Large icon failed: {e}")
|
|
79
|
+
|
|
80
|
+
def test_big_text(self):
|
|
81
|
+
try:
|
|
82
|
+
n = Notification( id=self.uid,title="Big Text", message="Testing big text")
|
|
83
|
+
n.setBigText("Lorem Ipsum is dummy text for testing BigTextStyle display.")
|
|
84
|
+
n.send()
|
|
85
|
+
except Exception as e:
|
|
86
|
+
self.fail(f"Big text failed: {e}")
|
|
87
|
+
|
|
88
|
+
def test_buttons(self):
|
|
89
|
+
try:
|
|
90
|
+
n = Notification( id=self.uid,title="With Buttons", message="Testing action buttons")
|
|
91
|
+
n.addButton(text="Play", on_release=lambda: print("Playing"))
|
|
92
|
+
n.addButton(text="Pause", on_release=lambda: print("Paused"))
|
|
93
|
+
n.addButton(text="Stop", on_release=lambda: print("Stopped"))
|
|
94
|
+
n.send()
|
|
95
|
+
except Exception as e:
|
|
96
|
+
self.fail(f"Buttons failed: {e}")
|
|
97
|
+
|
|
98
|
+
def test_both_imgs(self):
|
|
99
|
+
try:
|
|
100
|
+
n = Notification( id=self.uid,title="Both Images", message="Testing both large & big picture")
|
|
101
|
+
n.setLargeIcon("assets/icon.png")
|
|
102
|
+
n.setBigPicture("assets/icon.png")
|
|
103
|
+
n.send()
|
|
104
|
+
except Exception as e:
|
|
105
|
+
self.fail(f"Both imgs failed: {e}")
|
|
106
|
+
|
|
107
|
+
def test_custom_icon(self):
|
|
108
|
+
try:
|
|
109
|
+
n = Notification( id=self.uid,title="Custom Icon", message="Testing custom app icon")
|
|
110
|
+
n.setSmallIcon("assets/icon.png")
|
|
111
|
+
n.send()
|
|
112
|
+
except Exception as e:
|
|
113
|
+
self.fail(f"Custom icon failed: {e}")
|
|
114
|
+
|
|
115
|
+
def test_title_message_update(self):
|
|
116
|
+
try:
|
|
117
|
+
n = Notification( id=self.uid,title="Old Title", message="Old Message")
|
|
118
|
+
n.send()
|
|
119
|
+
time.sleep(2)
|
|
120
|
+
n.updateTitle("New Title")
|
|
121
|
+
n.updateMessage("New Message")
|
|
122
|
+
except Exception as e:
|
|
123
|
+
self.fail(f"Update title/message failed: {e}")
|
|
124
|
+
|
|
125
|
+
def test_download_channel(self):
|
|
126
|
+
try:
|
|
127
|
+
n = Notification( id=self.uid,
|
|
128
|
+
title="Download Done",
|
|
129
|
+
message="Your file finished downloading.",
|
|
130
|
+
channel_name="Download Notifications",
|
|
131
|
+
channel_id="downloads_notifications"
|
|
132
|
+
)
|
|
133
|
+
n.send()
|
|
134
|
+
except Exception as e:
|
|
135
|
+
self.fail(f"Download channel failed: {e}")
|
|
136
|
+
|
|
137
|
+
def test_channel_generate_id(self):
|
|
138
|
+
try:
|
|
139
|
+
n = Notification( id=self.uid,
|
|
140
|
+
title="Generated Channel",
|
|
141
|
+
message="Channel creation test",
|
|
142
|
+
channel_name="Custom Channel"
|
|
143
|
+
)
|
|
144
|
+
n.send()
|
|
145
|
+
except Exception as e:
|
|
146
|
+
self.fail(f"Channel generating ID failed: {e}")
|
|
147
|
+
|
|
148
|
+
def test_custom_id(self):
|
|
149
|
+
try:
|
|
150
|
+
n = Notification( id=self.uid,title="Custom ID", message="Click to trigger handler", name="change_app_page")
|
|
151
|
+
n.send()
|
|
152
|
+
except Exception as e:
|
|
153
|
+
self.fail(f"Custom ID failed: {e}")
|
|
154
|
+
|
|
155
|
+
def test_callback(self):
|
|
156
|
+
try:
|
|
157
|
+
n = Notification( id=self.uid,title="With Callback", message="Tap to run callback", callback=lambda: print("Callback invoked"))
|
|
158
|
+
n.send()
|
|
159
|
+
except Exception as e:
|
|
160
|
+
self.fail(f"Callback failed: {e}")
|
|
161
|
+
|
|
162
|
+
def test_custom_channel_name(self):
|
|
163
|
+
try:
|
|
164
|
+
n = Notification( id=self.uid,title="Custom Channel", message="Testing custom name", channel_name="MyChannelName")
|
|
165
|
+
n.send()
|
|
166
|
+
except Exception as e:
|
|
167
|
+
self.fail(f"Custom channel name failed: {e}")
|
|
168
|
+
|
|
169
|
+
def test_cancel_all(self):
|
|
170
|
+
try:
|
|
171
|
+
Notification.cancelAll()
|
|
172
|
+
except Exception as e:
|
|
173
|
+
self.fail(f"Cancel all failed: {e}")
|
|
174
|
+
|
|
175
|
+
def test_create_channel_lifespan(self):
|
|
176
|
+
try:
|
|
177
|
+
n = Notification( id=self.uid,title="Check ID Lifespan", message="Testing channel id reuse")
|
|
178
|
+
cid = "test_channel_id"
|
|
179
|
+
n.createChannel(id=cid, name="Test Channel", description="Lifespan check")
|
|
180
|
+
n.send()
|
|
181
|
+
except Exception as e:
|
|
182
|
+
self.fail(f"Create channel lifespan failed: {e}")
|
|
183
|
+
|
|
184
|
+
def test_persistent(self):
|
|
185
|
+
try:
|
|
186
|
+
n = Notification( id=self.uid,title="Persistent", message="This notification should persist")
|
|
187
|
+
n.send(persistent=True)
|
|
188
|
+
except Exception as e:
|
|
189
|
+
self.fail(f"Persistent notification failed: {e}")
|
|
190
|
+
|
|
191
|
+
# def test_get_active_notifications(self):
|
|
192
|
+
# try:
|
|
193
|
+
# Notification( id=self.uid,).get_active_notifications()
|
|
194
|
+
# except Exception as e:
|
|
195
|
+
# self.fail(f"Get active notifications failed: {e}")
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
if __name__ == "__main__":
|
|
199
|
+
unittest.main(verbosity=2)
|
|
@@ -0,0 +1,221 @@
|
|
|
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
|
+
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import flet as ft
|
|
2
|
+
import os,traceback
|
|
3
|
+
|
|
4
|
+
try:
|
|
5
|
+
from core import send_notification
|
|
6
|
+
except Exception as e:
|
|
7
|
+
print("Not on android")
|
|
8
|
+
print("Error importing android_notify: {}".format(e))
|
|
9
|
+
|
|
10
|
+
md1 = """ """
|
|
11
|
+
i=0
|
|
12
|
+
|
|
13
|
+
def main(page: ft.Page):
|
|
14
|
+
page.scroll = ft.ScrollMode.ADAPTIVE
|
|
15
|
+
mdObj=ft.Markdown(
|
|
16
|
+
md1,
|
|
17
|
+
selectable=True,
|
|
18
|
+
extension_set=ft.MarkdownExtensionSet.GITHUB_WEB,
|
|
19
|
+
on_tap_link=lambda e: page.launch_url(e.data),
|
|
20
|
+
)
|
|
21
|
+
def handle_click(style):
|
|
22
|
+
if style == "big_text":
|
|
23
|
+
send_notification(
|
|
24
|
+
title="Big Text Example",
|
|
25
|
+
message="This uses the BigTextStyle!",
|
|
26
|
+
style="big_text",
|
|
27
|
+
big_text="This is an expanded notification showing how much text can fit here."
|
|
28
|
+
)
|
|
29
|
+
elif style == "big_picture":
|
|
30
|
+
send_notification(
|
|
31
|
+
title="Big Picture Example",
|
|
32
|
+
message="This uses the BigPictureStyle!",
|
|
33
|
+
big_picture_path="assets/splash_android.png"
|
|
34
|
+
)
|
|
35
|
+
elif style == "large_icon":
|
|
36
|
+
send_notification(
|
|
37
|
+
title="Large Icon Example",
|
|
38
|
+
message="This uses a Large Icon!",
|
|
39
|
+
large_icon_path="assets/icon.png"
|
|
40
|
+
)
|
|
41
|
+
elif style == "both_imgs":
|
|
42
|
+
send_notification(
|
|
43
|
+
title="Both 'Large Icon' and 'Big Picture' Example",
|
|
44
|
+
message="This uses a Both Imgs!",
|
|
45
|
+
big_picture_path="assets/splash_android.png",
|
|
46
|
+
large_icon_path="assets/icon.png"
|
|
47
|
+
)
|
|
48
|
+
elif style == "inbox":
|
|
49
|
+
send_notification(
|
|
50
|
+
title="Inbox Example",
|
|
51
|
+
message="This uses the InboxStyle!",
|
|
52
|
+
lines="Line 1\nLine 2\nLine 3"
|
|
53
|
+
)
|
|
54
|
+
else:
|
|
55
|
+
send_notification(
|
|
56
|
+
title="Simple Notification",
|
|
57
|
+
message="This is a normal notification!"
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
page.snack_bar = ft.SnackBar(ft.Text(f"Sent: {style or 'default'}"))
|
|
61
|
+
page.snack_bar.open = True
|
|
62
|
+
page.update()
|
|
63
|
+
|
|
64
|
+
def console(a):
|
|
65
|
+
global md1,i
|
|
66
|
+
i+=1
|
|
67
|
+
print('Testing print visibility: ',i,'\n')
|
|
68
|
+
try:
|
|
69
|
+
with open(os.getenv("FLET_APP_CONSOLE"), "r") as f:
|
|
70
|
+
md1 = f.read()
|
|
71
|
+
mdObj.value=md1
|
|
72
|
+
except Exception as e:
|
|
73
|
+
mdObj.value=f"{e}, hello readddd me i'm Error"
|
|
74
|
+
finally:
|
|
75
|
+
mdObj.update()
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def send_basic(e):
|
|
79
|
+
send_notification(title='Hello World',message='From android_notify',custom_app_icon_path=f'assets/icon.png')
|
|
80
|
+
|
|
81
|
+
def request_permission(e):
|
|
82
|
+
try:
|
|
83
|
+
from core import asks_permission_if_needed
|
|
84
|
+
asks_permission_if_needed()
|
|
85
|
+
except Exception as e:
|
|
86
|
+
print( f"pyjnius android_ import error: {traceback.format_exc()}" )
|
|
87
|
+
|
|
88
|
+
page.add(
|
|
89
|
+
ft.OutlinedButton(
|
|
90
|
+
"Request Permission if Needed",
|
|
91
|
+
on_click=request_permission,
|
|
92
|
+
), ft.OutlinedButton(
|
|
93
|
+
"Refresh Prints",
|
|
94
|
+
on_click=console,
|
|
95
|
+
)
|
|
96
|
+
)
|
|
97
|
+
page.add(
|
|
98
|
+
ft.Text("🧪 Android Notify Test", size=25, weight=ft.FontWeight.BOLD),
|
|
99
|
+
ft.Text("Click a button below to test the corresponding style.\n", size=16),
|
|
100
|
+
|
|
101
|
+
ft.ElevatedButton("🔔 Simple Notification", on_click=lambda e: handle_click(None)),
|
|
102
|
+
ft.ElevatedButton("🖼️ Big Picture Style", on_click=lambda e: handle_click("big_picture")),
|
|
103
|
+
ft.ElevatedButton("🧩 Large Icon Style", on_click=lambda e: handle_click("large_icon")),
|
|
104
|
+
ft.ElevatedButton("# Both imgs", on_click=lambda e: handle_click("both_imgs")),
|
|
105
|
+
ft.ElevatedButton("📝 Big Text Style", on_click=lambda e: handle_click("big_text")),
|
|
106
|
+
ft.ElevatedButton("📋 Inbox Style", on_click=lambda e: handle_click("inbox")),
|
|
107
|
+
)
|
|
108
|
+
btn = ft.ElevatedButton("Click me!", on_click=send_basic)
|
|
109
|
+
page.add(btn)
|
|
110
|
+
page.add(mdObj)
|
|
111
|
+
ft.app(main)
|
|
112
|
+
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import React, { useState, useEffect } from 'react';
|
|
2
|
+
|
|
3
|
+
const ResponsiveText: React.FC = () => {
|
|
4
|
+
const [fontSize, setFontSize] = useState<string>(getFontSize());
|
|
5
|
+
|
|
6
|
+
function getFontSize(): string {
|
|
7
|
+
return window.innerWidth < 600 ? '12px' : '16px';
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
useEffect(() => {
|
|
11
|
+
const handleResize = () => setFontSize(getFontSize());
|
|
12
|
+
|
|
13
|
+
window.addEventListener('resize', handleResize);
|
|
14
|
+
return () => window.removeEventListener('resize', handleResize);
|
|
15
|
+
}, []);
|
|
16
|
+
|
|
17
|
+
return (
|
|
18
|
+
<p style={{ fontSize }}>
|
|
19
|
+
This text adjusts size based on screen width!
|
|
20
|
+
</p>
|
|
21
|
+
);
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export default ResponsiveText;
|