pythonnative 0.4.0__py3-none-any.whl → 0.6.0__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.
Files changed (52) hide show
  1. pythonnative/__init__.py +94 -66
  2. pythonnative/cli/pn.py +153 -24
  3. pythonnative/components.py +563 -0
  4. pythonnative/element.py +53 -0
  5. pythonnative/hooks.py +287 -0
  6. pythonnative/hot_reload.py +143 -0
  7. pythonnative/native_modules/__init__.py +19 -0
  8. pythonnative/native_modules/camera.py +105 -0
  9. pythonnative/native_modules/file_system.py +131 -0
  10. pythonnative/native_modules/location.py +61 -0
  11. pythonnative/native_modules/notifications.py +151 -0
  12. pythonnative/native_views.py +1334 -0
  13. pythonnative/page.py +320 -247
  14. pythonnative/reconciler.py +262 -0
  15. pythonnative/style.py +115 -0
  16. pythonnative/templates/android_template/app/build.gradle +2 -7
  17. pythonnative/templates/android_template/app/src/main/java/com/pythonnative/android_template/PageFragment.kt +2 -1
  18. pythonnative/templates/android_template/build.gradle +1 -1
  19. pythonnative/utils.py +21 -29
  20. {pythonnative-0.4.0.dist-info → pythonnative-0.6.0.dist-info}/METADATA +20 -19
  21. {pythonnative-0.4.0.dist-info → pythonnative-0.6.0.dist-info}/RECORD +25 -40
  22. pythonnative/activity_indicator_view.py +0 -71
  23. pythonnative/button.py +0 -113
  24. pythonnative/collection_view.py +0 -0
  25. pythonnative/date_picker.py +0 -76
  26. pythonnative/image_view.py +0 -78
  27. pythonnative/label.py +0 -133
  28. pythonnative/list_view.py +0 -76
  29. pythonnative/material_activity_indicator_view.py +0 -71
  30. pythonnative/material_bottom_navigation_view.py +0 -0
  31. pythonnative/material_button.py +0 -69
  32. pythonnative/material_date_picker.py +0 -87
  33. pythonnative/material_progress_view.py +0 -70
  34. pythonnative/material_search_bar.py +0 -69
  35. pythonnative/material_switch.py +0 -69
  36. pythonnative/material_time_picker.py +0 -76
  37. pythonnative/material_toolbar.py +0 -0
  38. pythonnative/picker_view.py +0 -69
  39. pythonnative/progress_view.py +0 -70
  40. pythonnative/scroll_view.py +0 -101
  41. pythonnative/search_bar.py +0 -69
  42. pythonnative/stack_view.py +0 -199
  43. pythonnative/switch.py +0 -68
  44. pythonnative/text_field.py +0 -132
  45. pythonnative/text_view.py +0 -135
  46. pythonnative/time_picker.py +0 -77
  47. pythonnative/view.py +0 -173
  48. pythonnative/web_view.py +0 -60
  49. {pythonnative-0.4.0.dist-info → pythonnative-0.6.0.dist-info}/WHEEL +0 -0
  50. {pythonnative-0.4.0.dist-info → pythonnative-0.6.0.dist-info}/entry_points.txt +0 -0
  51. {pythonnative-0.4.0.dist-info → pythonnative-0.6.0.dist-info}/licenses/LICENSE +0 -0
  52. {pythonnative-0.4.0.dist-info → pythonnative-0.6.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,61 @@
1
+ """Cross-platform location / GPS access.
2
+
3
+ Provides methods for requesting the current device location.
4
+ Uses Android's ``LocationManager`` or iOS's ``CLLocationManager``.
5
+ """
6
+
7
+ from typing import Any, Callable, Optional, Tuple
8
+
9
+ from ..utils import IS_ANDROID
10
+
11
+
12
+ class Location:
13
+ """GPS / Location services interface."""
14
+
15
+ @staticmethod
16
+ def get_current(
17
+ on_result: Optional[Callable[[Optional[Tuple[float, float]]], None]] = None,
18
+ **options: Any,
19
+ ) -> None:
20
+ """Request the current location.
21
+
22
+ Parameters
23
+ ----------
24
+ on_result:
25
+ ``((lat, lon) | None) -> None`` called with coordinates or
26
+ ``None`` if location is unavailable.
27
+ """
28
+ if IS_ANDROID:
29
+ Location._android_get(on_result, **options)
30
+ else:
31
+ Location._ios_get(on_result, **options)
32
+
33
+ @staticmethod
34
+ def _android_get(on_result: Optional[Callable] = None, **options: Any) -> None:
35
+ try:
36
+ from java import jclass
37
+
38
+ from ..utils import get_android_context
39
+
40
+ ctx = get_android_context()
41
+ lm = ctx.getSystemService(jclass("android.content.Context").LOCATION_SERVICE)
42
+ loc = lm.getLastKnownLocation("gps")
43
+ if loc and on_result:
44
+ on_result((loc.getLatitude(), loc.getLongitude()))
45
+ elif on_result:
46
+ on_result(None)
47
+ except Exception:
48
+ if on_result:
49
+ on_result(None)
50
+
51
+ @staticmethod
52
+ def _ios_get(on_result: Optional[Callable] = None, **options: Any) -> None:
53
+ try:
54
+ from rubicon.objc import ObjCClass
55
+
56
+ lm = ObjCClass("CLLocationManager").alloc().init()
57
+ lm.requestWhenInUseAuthorization()
58
+ lm.startUpdatingLocation()
59
+ except Exception:
60
+ if on_result:
61
+ on_result(None)
@@ -0,0 +1,151 @@
1
+ """Cross-platform local notifications.
2
+
3
+ Provides methods for scheduling and cancelling local push notifications.
4
+ Uses Android's ``NotificationManager`` or iOS's ``UNUserNotificationCenter``.
5
+ """
6
+
7
+ from typing import Any, Callable, Optional
8
+
9
+ from ..utils import IS_ANDROID
10
+
11
+
12
+ class Notifications:
13
+ """Local notification interface."""
14
+
15
+ @staticmethod
16
+ def request_permission(on_result: Optional[Callable[[bool], None]] = None) -> None:
17
+ """Request notification permission from the user.
18
+
19
+ Parameters
20
+ ----------
21
+ on_result:
22
+ ``(granted: bool) -> None`` called with the permission result.
23
+ """
24
+ if IS_ANDROID:
25
+ if on_result:
26
+ on_result(True)
27
+ else:
28
+ Notifications._ios_request_permission(on_result)
29
+
30
+ @staticmethod
31
+ def schedule(
32
+ title: str,
33
+ body: str = "",
34
+ delay_seconds: float = 0,
35
+ identifier: str = "default",
36
+ **options: Any,
37
+ ) -> None:
38
+ """Schedule a local notification.
39
+
40
+ Parameters
41
+ ----------
42
+ title:
43
+ Notification title.
44
+ body:
45
+ Notification body text.
46
+ delay_seconds:
47
+ Seconds from now until delivery (0 = immediate).
48
+ identifier:
49
+ Unique ID for this notification (for cancellation).
50
+ """
51
+ if IS_ANDROID:
52
+ Notifications._android_schedule(title, body, delay_seconds, identifier, **options)
53
+ else:
54
+ Notifications._ios_schedule(title, body, delay_seconds, identifier, **options)
55
+
56
+ @staticmethod
57
+ def cancel(identifier: str = "default") -> None:
58
+ """Cancel a pending notification by its identifier."""
59
+ if IS_ANDROID:
60
+ Notifications._android_cancel(identifier)
61
+ else:
62
+ Notifications._ios_cancel(identifier)
63
+
64
+ # -- Android ---------------------------------------------------------
65
+
66
+ @staticmethod
67
+ def _android_schedule(title: str, body: str, delay_seconds: float, identifier: str, **options: Any) -> None:
68
+ try:
69
+ from java import jclass
70
+
71
+ from ..utils import get_android_context
72
+
73
+ ctx = get_android_context()
74
+ nm = ctx.getSystemService(jclass("android.content.Context").NOTIFICATION_SERVICE)
75
+ channel_id = "pn_default"
76
+ NotificationChannel = jclass("android.app.NotificationChannel")
77
+ channel = NotificationChannel(channel_id, "PythonNative", 3) # IMPORTANCE_DEFAULT
78
+ nm.createNotificationChannel(channel)
79
+
80
+ Builder = jclass("android.app.Notification$Builder")
81
+ builder = Builder(ctx, channel_id)
82
+ builder.setContentTitle(title)
83
+ builder.setContentText(body)
84
+ builder.setSmallIcon(jclass("android.R$drawable").ic_dialog_info)
85
+ nm.notify(abs(hash(identifier)) % (2**31), builder.build())
86
+ except Exception:
87
+ pass
88
+
89
+ @staticmethod
90
+ def _android_cancel(identifier: str) -> None:
91
+ try:
92
+ from java import jclass
93
+
94
+ from ..utils import get_android_context
95
+
96
+ ctx = get_android_context()
97
+ nm = ctx.getSystemService(jclass("android.content.Context").NOTIFICATION_SERVICE)
98
+ nm.cancel(abs(hash(identifier)) % (2**31))
99
+ except Exception:
100
+ pass
101
+
102
+ # -- iOS -------------------------------------------------------------
103
+
104
+ @staticmethod
105
+ def _ios_request_permission(on_result: Optional[Callable[[bool], None]] = None) -> None:
106
+ try:
107
+ from rubicon.objc import ObjCClass
108
+
109
+ center = ObjCClass("UNUserNotificationCenter").currentNotificationCenter()
110
+ center.requestAuthorizationWithOptions_completionHandler_(0x07, None)
111
+ if on_result:
112
+ on_result(True)
113
+ except Exception:
114
+ if on_result:
115
+ on_result(False)
116
+
117
+ @staticmethod
118
+ def _ios_schedule(title: str, body: str, delay_seconds: float, identifier: str, **options: Any) -> None:
119
+ try:
120
+ from rubicon.objc import ObjCClass
121
+
122
+ content = ObjCClass("UNMutableNotificationContent").alloc().init()
123
+ content.setTitle_(title)
124
+ content.setBody_(body)
125
+
126
+ if delay_seconds > 0:
127
+ trigger = ObjCClass("UNTimeIntervalNotificationTrigger").triggerWithTimeInterval_repeats_(
128
+ delay_seconds, False
129
+ )
130
+ else:
131
+ trigger = ObjCClass("UNTimeIntervalNotificationTrigger").triggerWithTimeInterval_repeats_(1, False)
132
+
133
+ request = ObjCClass("UNNotificationRequest").requestWithIdentifier_content_trigger_(
134
+ identifier, content, trigger
135
+ )
136
+ center = ObjCClass("UNUserNotificationCenter").currentNotificationCenter()
137
+ center.addNotificationRequest_withCompletionHandler_(request, None)
138
+ except Exception:
139
+ pass
140
+
141
+ @staticmethod
142
+ def _ios_cancel(identifier: str) -> None:
143
+ try:
144
+ from rubicon.objc import ObjCClass
145
+
146
+ center = ObjCClass("UNUserNotificationCenter").currentNotificationCenter()
147
+ NSArray = ObjCClass("NSArray")
148
+ arr = NSArray.arrayWithObject_(identifier)
149
+ center.removePendingNotificationRequestsWithIdentifiers_(arr)
150
+ except Exception:
151
+ pass