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/base.py CHANGED
@@ -1,90 +1,97 @@
1
- """Assists Notification Class with Args keeps subclass cleaner"""
2
- from dataclasses import dataclass, fields
3
- import difflib
4
- from .styles import NotificationStyles
5
-
6
- @dataclass
7
- class BaseNotification:
8
- """Encapsulation"""
9
-
10
- # Basic options
11
- title: str = ''
12
- message: str = ''
13
- style: str = 'simple'
14
-
15
- # Style-specific attributes
16
- big_picture_path: str = ''
17
- large_icon_path: str = ''
18
- progress_max_value: int = 100
19
- progress_current_value: float = 0.0 # Also Takes in Ints
20
- body: str = ''
21
- lines_txt: str = ''
22
-
23
- # Notification Functions
24
- name: str = ''
25
- callback: object = None
26
-
27
- # Advanced Options
28
- id: int = 0
29
- app_icon: str = 'Defaults to package app icon'
30
-
31
- channel_name: str = 'Default Channel'
32
- """User visible channel name"""
33
- channel_id: str = 'default_channel'
34
- """Used to reference notification channel"""
35
-
36
- silent: bool = False
37
- logs: bool = False
38
-
39
- def __init__(self, **kwargs):
40
- """Custom init to handle validation before dataclass assigns values"""
41
-
42
- # Validate provided arguments
43
- self.validate_args(kwargs)
44
-
45
- # Assign validated values using the normal dataclass behavior
46
- for field_ in fields(self):
47
- field_name = field_.name
48
- setattr(self, field_name, kwargs.get(field_name, getattr(self, field_name)))
49
-
50
- def validate_args(self, inputted_kwargs):
51
- """Check for unexpected arguments and suggest corrections before Python validation"""
52
- default_fields = {field.name : field.type for field in fields(self)} #{'title': <class 'str'>, 'message': <class 'str'>,...
53
- allowed_fields_keys = set(default_fields.keys())
54
-
55
- # Identify invalid arguments
56
- invalid_args = set(inputted_kwargs) - allowed_fields_keys
57
- if invalid_args:
58
- suggestions = []
59
- for arg in invalid_args:
60
- closest_match = difflib.get_close_matches(arg, allowed_fields_keys, n=1, cutoff=0.6)
61
- if closest_match:
62
- suggestions.append(f"* '{arg}' is invalid -> Did you mean '{closest_match[0]}'?")
63
- else:
64
- suggestions.append(f"* '{arg}' is not a valid argument.")
65
-
66
- suggestion_text = '\n'.join(suggestions)
67
- raise ValueError(f"Invalid arguments provided:\n{suggestion_text}")
68
-
69
- # Validating types
70
- for each_arg in inputted_kwargs.keys():
71
- expected_type = default_fields[each_arg]
72
- actual_value = inputted_kwargs[each_arg]
73
-
74
- # Allow both int and float for progress_current_value
75
- if each_arg == "progress_current_value":
76
- if not isinstance(actual_value, (int, float)):
77
- raise TypeError(f"Expected '{each_arg}' to be int or float, got {type(actual_value)} instead.")
78
- else:
79
- if not isinstance(actual_value, expected_type):
80
- raise TypeError(f"Expected '{each_arg}' to be {expected_type}, got {type(actual_value)} instead.")
81
-
82
- # Validate `style` values
83
- style_values = [value for key, value in vars(NotificationStyles).items() if not key.startswith("__")]
84
- if 'style' in inputted_kwargs and inputted_kwargs['style'] not in ['',*style_values]:
85
- inputted_style=inputted_kwargs['style']
86
- allowed_styles=', '.join(style_values)
87
- raise ValueError(
88
- f"Invalid style '{inputted_style}'. Allowed styles: {allowed_styles}"
89
- )
90
-
1
+ """Assists Notification Class with Args keeps subclass cleaner"""
2
+ from dataclasses import dataclass, fields
3
+ import difflib
4
+ from .styles import NotificationStyles
5
+ # For Dev when creating new attr use have to set type for validate_args to work
6
+
7
+ @dataclass
8
+ class BaseNotification:
9
+ """Encapsulation"""
10
+
11
+ # Basic options
12
+ title: str = ''
13
+ message: str = ''
14
+ style: str = 'simple'
15
+
16
+ # Style-specific attributes
17
+ big_picture_path: str = ''
18
+ large_icon_path: str = ''
19
+ progress_max_value: int = 0
20
+ progress_current_value: float = 0.0 # Also Takes in Ints
21
+ body: str = ''
22
+ lines_txt: str = ''
23
+
24
+ # Notification Functions
25
+ name: str = ''
26
+ callback: object = None
27
+
28
+ # Advanced Options
29
+ id: int = 0
30
+ app_icon: str = 'Defaults to package app icon'
31
+ sub_text: str=''
32
+
33
+ # Channel related
34
+ channel_name: str = 'Default Channel'
35
+ """User visible channel name"""
36
+ channel_id: str = 'default_channel'
37
+ """Used to reference notification channel"""
38
+
39
+ silent: bool = False
40
+ logs: bool = False
41
+
42
+ # Custom Notification Attrs
43
+ title_color: str = ''
44
+ message_color: str = ''
45
+
46
+ def __init__(self, **kwargs):
47
+ """Custom init to handle validation before dataclass assigns values"""
48
+
49
+ # Validate provided arguments
50
+ self.validate_args(kwargs)
51
+
52
+ # Assign validated values using the normal dataclass behavior
53
+ for field_ in fields(self):
54
+ field_name = field_.name
55
+ setattr(self, field_name, kwargs.get(field_name, getattr(self, field_name)))
56
+
57
+ def validate_args(self, inputted_kwargs):
58
+ """Check for unexpected arguments and suggest corrections before Python validation"""
59
+ default_fields = {field.name : field.type for field in fields(self)} #{'title': <class 'str'>, 'message': <class 'str'>,...
60
+ allowed_fields_keys = set(default_fields.keys())
61
+
62
+ # Identify invalid arguments
63
+ invalid_args = set(inputted_kwargs) - allowed_fields_keys
64
+ if invalid_args:
65
+ suggestions = []
66
+ for arg in invalid_args:
67
+ closest_match = difflib.get_close_matches(arg, allowed_fields_keys, n=1, cutoff=0.6)
68
+ if closest_match:
69
+ suggestions.append(f"* '{arg}' is invalid -> Did you mean '{closest_match[0]}'?")
70
+ else:
71
+ suggestions.append(f"* '{arg}' is not a valid argument.")
72
+
73
+ suggestion_text = '\n'.join(suggestions)
74
+ raise ValueError(f"Invalid arguments provided:\n{suggestion_text}")
75
+
76
+ # Validating types
77
+ for each_arg in inputted_kwargs.keys():
78
+ expected_type = default_fields[each_arg]
79
+ actual_value = inputted_kwargs[each_arg]
80
+
81
+ # Allow both int and float for progress_current_value
82
+ if each_arg == "progress_current_value":
83
+ if not isinstance(actual_value, (int, float)):
84
+ raise TypeError(f"Expected '{each_arg}' to be int or float, got {type(actual_value)} instead.")
85
+ else:
86
+ if not isinstance(actual_value, expected_type):
87
+ raise TypeError(f"Expected '{each_arg}' to be {expected_type}, got {type(actual_value)} instead.")
88
+
89
+ # Validate `style` values
90
+ style_values = [value for key, value in vars(NotificationStyles).items() if not key.startswith("__")]
91
+ if 'style' in inputted_kwargs and inputted_kwargs['style'] not in ['',*style_values]:
92
+ inputted_style=inputted_kwargs['style']
93
+ allowed_styles=', '.join(style_values)
94
+ raise ValueError(
95
+ f"Invalid style '{inputted_style}'. Allowed styles: {allowed_styles}"
96
+ )
97
+
@@ -0,0 +1,145 @@
1
+ import os, traceback
2
+
3
+ ON_ANDROID = False
4
+ __version__ = "1.60.0"
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 = config.JAVA_NAMESPACE
15
+ except (ImportError, AttributeError):
16
+ ACTIVITY_CLASS_NAME = 'org.kivy.android'
17
+ return ACTIVITY_CLASS_NAME
18
+
19
+
20
+ if os.getenv("MAIN_ACTIVITY_HOST_CLASS_NAME"):
21
+ from jnius import cast, autoclass
22
+ else:
23
+ # print('Not on Flet android env...\n')
24
+ try:
25
+ import kivy #TODO find var for kivy
26
+ from jnius import cast, autoclass
27
+ except Exception as e:
28
+ print('android-notify: No pjnius, not on android')
29
+ # So commandline still works if java isn't installed and get pyjinus import error
30
+ # print('Exception occured in __init__.py: ',e)
31
+ cast = lambda x: x
32
+ autoclass = lambda x: None
33
+ try:
34
+ # Android Imports
35
+
36
+ # Get the required Java classes needs to on android to import
37
+ Bundle = autoclass('android.os.Bundle')
38
+ String = autoclass('java.lang.String')
39
+ Intent = autoclass('android.content.Intent')
40
+ PendingIntent = autoclass('android.app.PendingIntent')
41
+ BitmapFactory = autoclass('android.graphics.BitmapFactory')
42
+ BuildVersion = autoclass('android.os.Build$VERSION')
43
+ NotificationManager = autoclass('android.app.NotificationManager')
44
+ NotificationChannel = autoclass('android.app.NotificationChannel')
45
+ RemoteViews = autoclass('android.widget.RemoteViews')
46
+
47
+ ON_ANDROID = RemoteViews
48
+ except Exception as e:
49
+ from .an_types import *
50
+ if hasattr(e,'name') and e.name != 'android' :
51
+ print('Exception: ',e)
52
+ print(traceback.format_exc())
53
+
54
+
55
+ if ON_ANDROID:
56
+ try:
57
+ NotificationManagerCompat = autoclass('androidx.core.app.NotificationManagerCompat')
58
+ NotificationCompat = autoclass('androidx.core.app.NotificationCompat')
59
+ IconCompat = autoclass('androidx.core.graphics.drawable.IconCompat')
60
+ Color = autoclass('android.graphics.Color')
61
+
62
+ # Notification Design
63
+ NotificationCompatBuilder = autoclass('androidx.core.app.NotificationCompat$Builder')
64
+ NotificationCompatBigTextStyle = autoclass('androidx.core.app.NotificationCompat$BigTextStyle')
65
+ NotificationCompatBigPictureStyle = autoclass('androidx.core.app.NotificationCompat$BigPictureStyle')
66
+ NotificationCompatInboxStyle = autoclass('androidx.core.app.NotificationCompat$InboxStyle')
67
+ NotificationCompatDecoratedCustomViewStyle = autoclass('androidx.core.app.NotificationCompat$DecoratedCustomViewStyle')
68
+
69
+ except Exception as dependencies_import_error:
70
+ print('dependencies_import_error: ',dependencies_import_error)
71
+ print("""
72
+ Dependency Error: Add the following in buildozer.spec:
73
+ * android.gradle_dependencies = androidx.core:core-ktx:1.15.0, androidx.core:core:1.6.0
74
+ * android.enable_androidx = True
75
+ * android.permissions = POST_NOTIFICATIONS
76
+ """)
77
+
78
+ from .an_types import *
79
+ else:
80
+ from .an_types import *
81
+
82
+ def from_service_file():
83
+ return 'PYTHON_SERVICE_ARGUMENT' in os.environ
84
+
85
+ run_on_ui_thread = None
86
+ if on_flet_app() or from_service_file() or not ON_ANDROID:
87
+ def run_on_ui_thread(func):
88
+ """Fallback for Developing on PC"""
89
+
90
+ def wrapper(*args, **kwargs):
91
+ # print("Simulating run on UI thread")
92
+ return func(*args, **kwargs)
93
+
94
+ return wrapper
95
+ else:# TODO find var for kivy
96
+ from android.runnable import run_on_ui_thread
97
+
98
+ def get_python_activity():
99
+ if not ON_ANDROID:
100
+ from .an_types import PythonActivity
101
+ return PythonActivity
102
+ ACTIVITY_CLASS_NAME = get_activity_class_name()
103
+ if from_service_file():
104
+ PythonActivity = autoclass(ACTIVITY_CLASS_NAME + '.PythonService')
105
+ elif on_flet_app():
106
+ PythonActivity = autoclass(ACTIVITY_CLASS_NAME)
107
+ else:
108
+ PythonActivity = autoclass(ACTIVITY_CLASS_NAME + '.PythonActivity')
109
+ return PythonActivity
110
+
111
+ def get_python_activity_context():
112
+ if not ON_ANDROID:
113
+ from .an_types import Context
114
+ return Context
115
+
116
+ PythonActivity = get_python_activity()
117
+ if from_service_file():
118
+ service = PythonActivity.mService
119
+ context = service.getApplication().getApplicationContext()
120
+ # context = PythonActivity.mService
121
+ else:
122
+ context = PythonActivity.mActivity
123
+ return context
124
+
125
+
126
+ if ON_ANDROID:
127
+ context = get_python_activity_context()
128
+ else:
129
+ context = None
130
+
131
+ def get_notification_manager():
132
+ if not ON_ANDROID:
133
+ return None
134
+ notification_service = context.getSystemService(context.NOTIFICATION_SERVICE)
135
+ return cast(NotificationManager, notification_service)
136
+
137
+ def app_storage_path():
138
+ if on_flet_app():
139
+ return os.path.join(context.getFilesDir().getAbsolutePath(), 'flet')
140
+ else:
141
+ try:
142
+ from android.storage import app_storage_path as kivy_app_storage_path # type: ignore
143
+ return kivy_app_storage_path()
144
+ except Exception as e:
145
+ return './' # TODO return file main.py path (not android)