pydrud 1.0.0__tar.gz

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 (76) hide show
  1. pydrud-1.0.0/LICENSE +21 -0
  2. pydrud-1.0.0/PKG-INFO +502 -0
  3. pydrud-1.0.0/README.md +469 -0
  4. pydrud-1.0.0/pydrud/__init__.py +61 -0
  5. pydrud-1.0.0/pydrud/android/templates/android/AndroidManifest.xml.j2 +24 -0
  6. pydrud-1.0.0/pydrud/android/templates/android/BridgeService.java.j2 +173 -0
  7. pydrud-1.0.0/pydrud/android/templates/android/EventDispatcher.java.j2 +71 -0
  8. pydrud-1.0.0/pydrud/android/templates/android/MainActivity.java.j2 +93 -0
  9. pydrud-1.0.0/pydrud/android/templates/android/ViewFactory.java.j2 +565 -0
  10. pydrud-1.0.0/pydrud/android/templates/android/app/build.gradle.kts.j2 +59 -0
  11. pydrud-1.0.0/pydrud/android/templates/android/build.gradle.kts.j2 +5 -0
  12. pydrud-1.0.0/pydrud/android/templates/android/gradle/wrapper/gradle-wrapper.properties.j2 +7 -0
  13. pydrud-1.0.0/pydrud/android/templates/android/gradle.properties.j2 +6 -0
  14. pydrud-1.0.0/pydrud/android/templates/android/gradlew.bat.j2 +19 -0
  15. pydrud-1.0.0/pydrud/android/templates/android/gradlew.j2 +72 -0
  16. pydrud-1.0.0/pydrud/android/templates/android/local.properties.j2 +2 -0
  17. pydrud-1.0.0/pydrud/android/templates/android/settings.gradle.kts.j2 +20 -0
  18. pydrud-1.0.0/pydrud/android/templates/android/themes.xml.j2 +12 -0
  19. pydrud-1.0.0/pydrud/android/templates/dot.gitignore.j2 +26 -0
  20. pydrud-1.0.0/pydrud/android/templates/pydrud.yaml.j2 +8 -0
  21. pydrud-1.0.0/pydrud/android/templates/python/app.py.j2 +1 -0
  22. pydrud-1.0.0/pydrud/android/templates/python/main.py.j2 +328 -0
  23. pydrud-1.0.0/pydrud/android/templates/python/pydrud_config.py.j2 +14 -0
  24. pydrud-1.0.0/pydrud/android/templates/python/run.py.j2 +16 -0
  25. pydrud-1.0.0/pydrud/android/templates/python/setup.py.j2 +11 -0
  26. pydrud-1.0.0/pydrud/android/templates/res/mipmap-anydpi-v26/ic_launcher.xml +5 -0
  27. pydrud-1.0.0/pydrud/android/templates/res/mipmap-hdpi/ic_launcher.png +0 -0
  28. pydrud-1.0.0/pydrud/android/templates/res/mipmap-hdpi/ic_launcher_adaptive_back.png +0 -0
  29. pydrud-1.0.0/pydrud/android/templates/res/mipmap-hdpi/ic_launcher_adaptive_fore.png +0 -0
  30. pydrud-1.0.0/pydrud/android/templates/res/mipmap-mdpi/ic_launcher.png +0 -0
  31. pydrud-1.0.0/pydrud/android/templates/res/mipmap-mdpi/ic_launcher_adaptive_back.png +0 -0
  32. pydrud-1.0.0/pydrud/android/templates/res/mipmap-mdpi/ic_launcher_adaptive_fore.png +0 -0
  33. pydrud-1.0.0/pydrud/android/templates/res/mipmap-xhdpi/ic_launcher.png +0 -0
  34. pydrud-1.0.0/pydrud/android/templates/res/mipmap-xhdpi/ic_launcher_adaptive_back.png +0 -0
  35. pydrud-1.0.0/pydrud/android/templates/res/mipmap-xhdpi/ic_launcher_adaptive_fore.png +0 -0
  36. pydrud-1.0.0/pydrud/android/templates/res/mipmap-xxhdpi/ic_launcher.png +0 -0
  37. pydrud-1.0.0/pydrud/android/templates/res/mipmap-xxhdpi/ic_launcher_adaptive_back.png +0 -0
  38. pydrud-1.0.0/pydrud/android/templates/res/mipmap-xxhdpi/ic_launcher_adaptive_fore.png +0 -0
  39. pydrud-1.0.0/pydrud/android/templates/res/mipmap-xxxhdpi/ic_launcher.png +0 -0
  40. pydrud-1.0.0/pydrud/android/templates/res/mipmap-xxxhdpi/ic_launcher_adaptive_back.png +0 -0
  41. pydrud-1.0.0/pydrud/android/templates/res/mipmap-xxxhdpi/ic_launcher_adaptive_fore.png +0 -0
  42. pydrud-1.0.0/pydrud/commands/__init__.py +1 -0
  43. pydrud-1.0.0/pydrud/commands/builder.py +440 -0
  44. pydrud-1.0.0/pydrud/commands/cli.py +131 -0
  45. pydrud-1.0.0/pydrud/commands/doctor.py +162 -0
  46. pydrud-1.0.0/pydrud/commands/project.py +282 -0
  47. pydrud-1.0.0/pydrud/core/__init__.py +1 -0
  48. pydrud-1.0.0/pydrud/core/bridge.py +66 -0
  49. pydrud-1.0.0/pydrud/core/diff.py +151 -0
  50. pydrud-1.0.0/pydrud/core/events.py +68 -0
  51. pydrud-1.0.0/pydrud/core/responsive.py +116 -0
  52. pydrud-1.0.0/pydrud/core/state.py +96 -0
  53. pydrud-1.0.0/pydrud/main.py +289 -0
  54. pydrud-1.0.0/pydrud/utils/__init__.py +1 -0
  55. pydrud-1.0.0/pydrud/utils/colors.py +40 -0
  56. pydrud-1.0.0/pydrud/widgets/__init__.py +28 -0
  57. pydrud-1.0.0/pydrud/widgets/base.py +131 -0
  58. pydrud-1.0.0/pydrud/widgets/basic.py +308 -0
  59. pydrud-1.0.0/pydrud/widgets/layout.py +182 -0
  60. pydrud-1.0.0/pydrud/widgets/styling.py +304 -0
  61. pydrud-1.0.0/pydrud.egg-info/PKG-INFO +502 -0
  62. pydrud-1.0.0/pydrud.egg-info/SOURCES.txt +74 -0
  63. pydrud-1.0.0/pydrud.egg-info/dependency_links.txt +1 -0
  64. pydrud-1.0.0/pydrud.egg-info/entry_points.txt +2 -0
  65. pydrud-1.0.0/pydrud.egg-info/requires.txt +3 -0
  66. pydrud-1.0.0/pydrud.egg-info/top_level.txt +1 -0
  67. pydrud-1.0.0/pyproject.toml +57 -0
  68. pydrud-1.0.0/setup.cfg +4 -0
  69. pydrud-1.0.0/tests/test_app.py +109 -0
  70. pydrud-1.0.0/tests/test_bridge.py +50 -0
  71. pydrud-1.0.0/tests/test_diff.py +104 -0
  72. pydrud-1.0.0/tests/test_events.py +62 -0
  73. pydrud-1.0.0/tests/test_responsive.py +88 -0
  74. pydrud-1.0.0/tests/test_state.py +87 -0
  75. pydrud-1.0.0/tests/test_styling.py +137 -0
  76. pydrud-1.0.0/tests/test_widgets.py +188 -0
pydrud-1.0.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Pydrud Contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
pydrud-1.0.0/PKG-INFO ADDED
@@ -0,0 +1,502 @@
1
+ Metadata-Version: 2.4
2
+ Name: pydrud
3
+ Version: 1.0.0
4
+ Summary: A lightweight Python framework for building native Android apps โ€” Flutter-inspired, radically simpler.
5
+ Author: Pydrud Contributors
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/MurShidM01/Pydrud
8
+ Project-URL: Repository, https://github.com/MurShidM01/Pydrud
9
+ Project-URL: Documentation, https://github.com/MurShidM01/Pydrud#readme
10
+ Project-URL: Bug Tracker, https://github.com/MurShidM01/Pydrud/issues
11
+ Keywords: android,framework,ui,mobile,python,native,app-development
12
+ Classifier: Development Status :: 3 - Alpha
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3 :: Only
17
+ Classifier: Programming Language :: Python :: 3.10
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Programming Language :: Java
21
+ Classifier: Topic :: Software Development :: Build Tools
22
+ Classifier: Topic :: Software Development :: Code Generators
23
+ Classifier: Topic :: Software Development :: User Interfaces
24
+ Classifier: Framework :: Setuptools Plugin
25
+ Classifier: Natural Language :: English
26
+ Requires-Python: >=3.10
27
+ Description-Content-Type: text/markdown
28
+ License-File: LICENSE
29
+ Requires-Dist: click>=8.0
30
+ Requires-Dist: Jinja2>=3.0
31
+ Requires-Dist: Pillow>=10.0
32
+ Dynamic: license-file
33
+
34
+ <p align="center">
35
+ <img src="https://img.shields.io/pypi/v/pydrud?color=6366F1&style=flat-square" alt="PyPI" />
36
+ <img src="https://img.shields.io/pypi/pyversions/pydrud?style=flat-square" alt="Python" />
37
+ <img src="https://img.shields.io/badge/android-native-brightgreen?style=flat-square" alt="Android Native" />
38
+ <img src="https://img.shields.io/github/license/MurShidM01/Pydrud?style=flat-square" alt="License" />
39
+ <img src="https://img.shields.io/pypi/dm/pydrud?style=flat-square" alt="Downloads" />
40
+ </p>
41
+
42
+ <h1 align="center">โšก Pydrud</h1>
43
+
44
+ <p align="center">
45
+ <strong>Build native Android apps with Python.<br>
46
+ No XML layouts. No Kotlin UI code. Just Python.</strong>
47
+ </p>
48
+
49
+ <p align="center">
50
+ A Flutter-inspired framework that converts declarative Python widget trees<br>
51
+ into <strong>real native Android Views</strong> at runtime โ€” via Chaquopy + a lightweight TCP bridge.
52
+ </p>
53
+
54
+ <br>
55
+
56
+ ---
57
+
58
+ ## โœจ Quick Start
59
+
60
+ ```bash
61
+ pip install pydrud # Install the framework
62
+ pydrud init my_app --org com.example # Scaffold a new project
63
+ cd my_app
64
+ pydrud run # Build โ†’ install โ†’ launch
65
+ ```
66
+
67
+ **First APK in ~2 minutes.** Connect your Android device via USB (or ADB over TCP), and `pydrud run` does the rest.
68
+
69
+ <br>
70
+
71
+ ---
72
+
73
+ ## ๐Ÿ“ฑ What You Get
74
+
75
+ | | |
76
+ |---|---|
77
+ | **๐Ÿงฑ Declarative UI** | Compose screens with Python โ€” `Container`, `Column`, `Row`, `Text`, `Button`, and more |
78
+ | **โšก Reactive State** | `State<T>` auto-triggers UI re-renders on value change |
79
+ | **๐ŸŽจ Full Styling** | Colors, padding, margin, borders, fonts, elevation, alignment |
80
+ | **๐Ÿ“ฒ Native Rendering** | Every widget becomes a real Android `View` โ€” not a WebView or canvas |
81
+ | **๐Ÿ›  CLI Toolchain** | `init` / `build` / `run` / `clean` / `doctor` โ€” one command to ship |
82
+ | **๐Ÿ“ Responsive** | `Responsive.text()` auto-scales UI to any screen size |
83
+ | **๐ŸŒ‰ TCP Bridge** | Clean NDJSON protocol over port 8595 โ€” debuggable with any JSON tool |
84
+
85
+ <br>
86
+
87
+ ---
88
+
89
+ ## ๐Ÿ“ Example App
90
+
91
+ ```python
92
+ from pydrud import App, Text, Container, Column, Row, Button, Center
93
+ from pydrud import State, EdgeInsets, Alignment
94
+
95
+ counter = State(0)
96
+
97
+ def main(page):
98
+ page.title = "Pydrud Demo"
99
+ page.bgcolor = "#FFF9FAFB"
100
+
101
+ def on_pressed(data):
102
+ counter.value += 1
103
+ page.update()
104
+
105
+ page.add(
106
+ Column(scroll=True, children=[
107
+
108
+ Container(
109
+ bg="#FF6366F1", padding=EdgeInsets.symmetric(horizontal=20, vertical=16),
110
+ child=Text("Pydrud", size=20, weight=700, color="#FFFFFFFF"),
111
+ ),
112
+
113
+ Spacer(expand=2),
114
+ Center(child=Text(str(counter.value), size=72, weight=200, color="#FF6366F1")),
115
+ Center(child=Text("Tap the button", size=14, color="#FF9CA3AF")),
116
+ Spacer(expand=3),
117
+
118
+ Center(
119
+ child=Button(
120
+ "+", bg_color="#FF6366F1", color="#FFFFFFFF",
121
+ style={"borderRadius": 28, "width": 56, "height": 56},
122
+ ).on_click(on_pressed),
123
+ ),
124
+ Spacer(expand=1),
125
+
126
+ ]),
127
+ )
128
+
129
+ app = App(target=main)
130
+ app.run()
131
+ ```
132
+
133
+ > ๐Ÿ’ก **State lives outside `main()`** so it persists across re-renders.
134
+ > Update โ†’ `page.update()` โ†’ tree rebuilds โ†’ Android re-renders.
135
+
136
+ <br>
137
+
138
+ ---
139
+
140
+ ## ๐Ÿ”ง Installation
141
+
142
+ ### From PyPI (recommended)
143
+
144
+ ```bash
145
+ pip install pydrud
146
+ ```
147
+
148
+ ### From source
149
+
150
+ ```bash
151
+ git clone https://github.com/MurShidM01/Pydrud.git
152
+ cd Pydrud
153
+ pip install -e .
154
+ ```
155
+
156
+ ### Corporate / proxy networks
157
+
158
+ ```bash
159
+ export http_proxy=http://proxy:port
160
+ export https_proxy=http://proxy:port
161
+ pydrud build # proxy forwarded to Gradle automatically
162
+ ```
163
+
164
+ <br>
165
+
166
+ ---
167
+
168
+ ## ๐Ÿงฑ Widget Reference
169
+
170
+ ### Layout
171
+
172
+ | Widget | Description | Key Props |
173
+ |--------|-------------|-----------|
174
+ | `Container` | Box with padding, margin, bg, border-radius | `child`, `padding`, `margin`, `bg`, `border_radius`, `width`, `height`, `alignment`, `expand` |
175
+ | `Column` | Vertical flex layout | `children`, `spacing`, `horizontal_alignment`, `scroll`, `expand` |
176
+ | `Row` | Horizontal flex layout | `children`, `spacing`, `vertical_alignment`, `expand` |
177
+ | `Center` | Centres its child | `child`, `expand` |
178
+ | `Spacer` | Flexible empty space | `expand` (default 1) |
179
+ | `Divider` | Horizontal / vertical line | `color`, `thickness` |
180
+
181
+ ### Basic
182
+
183
+ | Widget | Description | Key Props |
184
+ |--------|-------------|-----------|
185
+ | `Text` | Readable text | `value`, `size`, `color`, `weight` (100โ€“900), `italic`, `text_align` |
186
+ | `Button` | Clickable button | `text`, `variant` (filled / outlined / text), `icon`, `bg_color`, `color`, `disabled` |
187
+ | `TextField` | Text input | `value`, `hint`, `multiline`, `password` |
188
+ | `Image` | Display image | `src` (asset or URL), `fit` |
189
+ | `Icon` | Material icon | `name` (star, home, search, โ€ฆ), `size`, `color` |
190
+ | `Checkbox` | Checkable box | `label`, `checked` |
191
+ | `Switch` | Toggle switch | `label`, `active` |
192
+
193
+ ### Events
194
+
195
+ All interactive widgets support callback chaining:
196
+
197
+ ```python
198
+ widget.on_click(callback) # callback(data) โ†’ data is a dict
199
+ widget.on_change(callback) # TextField, Checkbox, Switch
200
+ widget.on_submit(callback) # TextField (IME action)
201
+ widget.on_focus(callback) # Focus gain / loss
202
+ ```
203
+
204
+ ### Styling
205
+
206
+ Use the `Style` builder or pass dicts inline:
207
+
208
+ ```python
209
+ Style().bg("#FFFFFF").padding(EdgeInsets.all(16)).border_radius(8).elevation(4).build()
210
+ ```
211
+
212
+ **All style properties:**
213
+
214
+ | Prop | Type | Example |
215
+ |------|------|---------|
216
+ | `bg` | Hex color | `"#FF6366F1"` |
217
+ | `opacity` | float 0โ€“1 | `0.5` |
218
+ | `width` / `height` | int or `"match"` | `200` or `"match"` |
219
+ | `padding` / `margin` | `EdgeInsets` | `EdgeInsets.all(16)` |
220
+ | `borderRadius` | int | `12` |
221
+ | `font.size` | int (sp) | `16` |
222
+ | `font.color` | Hex color | `"#FF1F2937"` |
223
+ | `font.weight` | int 100โ€“900 | `700` |
224
+ | `font.italic` | bool | `true` |
225
+ | `alignment` | `Alignment` constant | `Alignment.center` |
226
+ | `elevation` | int (dp) | `4` |
227
+ | `visible` | bool | `true` / `false` |
228
+ | `tooltip` | string | `"Save changes"` |
229
+
230
+ <br>
231
+
232
+ ---
233
+
234
+ ## ๐Ÿ“ Responsive Scaling
235
+
236
+ Pydrud includes a built-in responsive system that **auto-scales** your UI to the device screen width.
237
+
238
+ ```python
239
+ from pydrud import Responsive
240
+
241
+ Responsive.text(16) # Font size โ€” scales proportionally
242
+ Responsive.w(48) # Width โ€” scales proportionally
243
+ Responsive.h(48) # Height
244
+ Responsive.padding(24) # Padding / margin
245
+ Responsive.spacing(12) # Gaps between widgets
246
+ Responsive.radius(12) # Border radius
247
+ Responsive.icon(24) # Icon size
248
+ ```
249
+
250
+ **How it works:**
251
+
252
+ 1. Android sends the screen dimensions (width, height, density) to Python via the bridge `"ready"` event
253
+ 2. Python's `Responsive` class computes a scale factor: `screen_width_dp / 360`
254
+ 3. All sizes are multiplied by this factor โ€” on a 720dp tablet, everything is **2ร— larger**
255
+ 4. Baseline `360dp` matches the typical phone width
256
+
257
+ > โœ… No device info available? Defaults to factor `1.0` (no scaling) โ€” safe fallback.
258
+
259
+ <br>
260
+
261
+ ---
262
+
263
+ ## ๐Ÿ— Architecture
264
+
265
+ ```
266
+ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
267
+ โ”‚ Python Layer โ”‚
268
+ โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚
269
+ โ”‚ โ”‚Widgets โ”‚ โ”‚ EventDispatcher โ”‚ โ”‚
270
+ โ”‚ โ”‚ Tree โ”œโ”€โ”€โ”ค (user callbacks) โ”‚ โ”‚
271
+ โ”‚ โ””โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚
272
+ โ”‚ โ”‚ โ”‚ โ”‚
273
+ โ”‚ โ”Œโ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚
274
+ โ”‚ โ”‚ TreeDiff โ†’ Patches โ†’ Bridge.encodeโ”‚ โ”‚
275
+ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚
276
+ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
277
+ โ”‚ TCP / NDJSON :8595
278
+ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
279
+ โ”‚ Android Layer โ”‚ โ”‚
280
+ โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚
281
+ โ”‚ โ”‚ BridgeService (TCP server) โ”‚ โ”‚
282
+ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚
283
+ โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚
284
+ โ”‚ โ”‚ ViewFactory โ†’ native Views โ”‚ โ”‚
285
+ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚
286
+ โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚
287
+ โ”‚ โ”‚ EventDispatcher โ†’ Python โ”‚ โ”‚
288
+ โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚
289
+ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
290
+ ```
291
+
292
+ ### Data Flow
293
+
294
+ 1. **`App(target=main)`** โ€” calls your builder โ†’ produces a Widget tree
295
+ 2. **`page.update()`** โ€” rebuilds the tree โ†’ sends full render command to Android
296
+ 3. **`BridgeService`** โ€” receives the JSON tree โ†’ `ViewFactory` creates native Views
297
+ 4. **User taps a Button** โ€” Java sends `{"type":"click","key":"..."}` over the socket
298
+ 5. **Python EventDispatcher** โ€” routes the event to your callback
299
+ 6. **Callback mutates `State`** โ€” triggers `page.update()` โ†’ goto step 2
300
+
301
+ > ๐Ÿ”„ For V1, every state change sends a **full tree re-render**. Incremental patch support is planned for V2.
302
+
303
+ <br>
304
+
305
+ ---
306
+
307
+ ## ๐Ÿ›  CLI Reference
308
+
309
+ | Command | Description |
310
+ |---------|-------------|
311
+ | `pydrud init <name>` | Create a new project |
312
+ | `pydrud init <name> --org com.example` | With custom package |
313
+ | `pydrud build` | Build debug APK |
314
+ | `pydrud build --release` | Build release APK |
315
+ | `pydrud run` | Build + install + launch + live logcat |
316
+ | `pydrud run --device <id>` | Target specific device |
317
+ | `pydrud clean` | Clean build artifacts |
318
+ | `pydrud doctor` | Check environment requirements |
319
+
320
+ ### Environment
321
+
322
+ ```bash
323
+ pydrud doctor
324
+
325
+ # Expected output:
326
+ # โœ” Python โ‰ฅ 3.10 โ€” 3.12.3
327
+ # โœ” Java 17+ โ€” OpenJDK 17
328
+ # โœ” Android SDK โ€” /path/to/sdk (API 35)
329
+ # โœ” Gradle โ€” gradlew wrapper found
330
+ # โœ” ADB โ€” Android Debug Bridge 2.x
331
+ ```
332
+
333
+ <br>
334
+
335
+ ---
336
+
337
+ ## โš™๏ธ Requirements
338
+
339
+ ### Development machine
340
+
341
+ | Tool | Version | Notes |
342
+ |------|---------|-------|
343
+ | Python | โ‰ฅ 3.10, โ‰ค 3.12 | 3.12 recommended for Chaquopy compatibility |
344
+ | Java (JDK) | 17+ | OpenJDK 17 LTS recommended |
345
+ | Android SDK | API 33+ | Set `ANDROID_HOME` or `ANDROID_SDK_ROOT` |
346
+ | Android NDK | r29+ | Required for Chaquopy native libraries |
347
+ | Gradle | 8.x | Bundled via wrapper in generated projects |
348
+
349
+ ### Android device
350
+
351
+ | Requirement | Notes |
352
+ |-------------|-------|
353
+ | API 24+ (Android 7.0+) | Minimum supported |
354
+ | ARM64 / x86_64 | Both supported |
355
+ | USB Debugging enabled | Or ADB over TCP |
356
+
357
+ ### Setting up Android SDK
358
+
359
+ <details>
360
+ <summary><b>Windows</b></summary>
361
+
362
+ ```powershell
363
+ # Set environment variables
364
+ [System.Environment]::SetEnvironmentVariable('ANDROID_HOME', 'C:\Android\Sdk', 'User')
365
+ [System.Environment]::SetEnvironmentVariable('ANDROID_SDK_ROOT', 'C:\Android\Sdk', 'User')
366
+ ```
367
+
368
+ Restart your terminal and verify:
369
+ ```powershell
370
+ pydrud doctor
371
+ ```
372
+ </details>
373
+
374
+ <details>
375
+ <summary><b>Linux / macOS</b></summary>
376
+
377
+ ```bash
378
+ export ANDROID_HOME=$HOME/Android/Sdk
379
+ export ANDROID_SDK_ROOT=$HOME/Android/Sdk
380
+ ```
381
+
382
+ Add to your `~/.bashrc` or `~/.zshrc`:
383
+ ```bash
384
+ echo 'export ANDROID_HOME=$HOME/Android/Sdk' >> ~/.bashrc
385
+ echo 'export ANDROID_SDK_ROOT=$HOME/Android/Sdk' >> ~/.bashrc
386
+ ```
387
+ </details>
388
+
389
+ <br>
390
+
391
+ ---
392
+
393
+ ## ๐Ÿงช Development
394
+
395
+ ```bash
396
+ git clone https://github.com/MurShidM01/Pydrud.git
397
+ cd Pydrud
398
+ pip install -e ".[dev]"
399
+ python -m pytest tests/ -v
400
+ ```
401
+
402
+ Current test count: **96 tests** (widgets, state, diffing, events, styling, bridge, responsive).
403
+
404
+ ### Project Structure
405
+
406
+ ```
407
+ pydrud/
408
+ โ”œโ”€โ”€ pydrud/
409
+ โ”‚ โ”œโ”€โ”€ main.py # App + Page + bridge event loop
410
+ โ”‚ โ”œโ”€โ”€ widgets/
411
+ โ”‚ โ”‚ โ”œโ”€โ”€ base.py # Widget base class
412
+ โ”‚ โ”‚ โ”œโ”€โ”€ layout.py # Container, Column, Row, Center, Spacer, Divider
413
+ โ”‚ โ”‚ โ”œโ”€โ”€ basic.py # Text, Button, TextField, Image, Icon, Checkbox, Switch
414
+ โ”‚ โ”‚ โ””โ”€โ”€ styling.py # Style, EdgeInsets, Alignment, FontStyle
415
+ โ”‚ โ”œโ”€โ”€ core/
416
+ โ”‚ โ”‚ โ”œโ”€โ”€ state.py # State[T], ReactiveDict
417
+ โ”‚ โ”‚ โ”œโ”€โ”€ diff.py # TreeDiff โ†’ Patch list
418
+ โ”‚ โ”‚ โ”œโ”€โ”€ events.py # Event dispatcher
419
+ โ”‚ โ”‚ โ”œโ”€โ”€ bridge.py # Protocol encoding / decoding
420
+ โ”‚ โ”‚ โ””โ”€โ”€ responsive.py # Responsive scaling
421
+ โ”‚ โ”œโ”€โ”€ commands/
422
+ โ”‚ โ”‚ โ”œโ”€โ”€ cli.py # Click CLI
423
+ โ”‚ โ”‚ โ”œโ”€โ”€ project.py # Jinja2 project scaffold
424
+ โ”‚ โ”‚ โ”œโ”€โ”€ builder.py # Gradle + ADB
425
+ โ”‚ โ”‚ โ””โ”€โ”€ doctor.py # Environment check
426
+ โ”‚ โ””โ”€โ”€ android/templates/ # Jinja2 โ†’ Android project
427
+ โ”œโ”€โ”€ tests/ # pytest test suite
428
+ โ”œโ”€โ”€ pyproject.toml
429
+ โ””โ”€โ”€ README.md
430
+ ```
431
+
432
+ ### Adding a New Widget
433
+
434
+ 1. Create Python class in `pydrud/widgets/` (extend `Widget`, set `_widget_type`, implement `_serialise_props()`)
435
+ 2. Export in `pydrud/widgets/__init__.py` and `pydrud/__init__.py`
436
+ 3. Add a creator method in `pydrud/android/templates/android/ViewFactory.java.j2`
437
+ 4. Add style handling in `applyStyle()`
438
+ 5. Write tests in `tests/`
439
+ 6. Run `python -m pytest tests/ -v`
440
+
441
+ <br>
442
+
443
+ ---
444
+
445
+ ## ๐Ÿ“ฆ Publishing to PyPI
446
+
447
+ ```bash
448
+ # Install build tools
449
+ pip install build twine
450
+
451
+ # Build source distribution and wheel
452
+ python -m build
453
+
454
+ # Upload to PyPI
455
+ twine upload dist/*
456
+ ```
457
+
458
+ > ๐Ÿ’ก Update the version number in `pyproject.toml` before publishing.
459
+
460
+ <br>
461
+
462
+ ---
463
+
464
+ ## ๐Ÿ—บ Roadmap
465
+
466
+ | Version | Focus |
467
+ |---------|-------|
468
+ | **V1** โœ… | Core widgets, state, diffing, CLI, APK generation, responsive scaling |
469
+ | **V2** ๐Ÿ”œ | Hot reload, Material theme, ListView / GridView, Navigation, Animations, Snackbar / Dialog |
470
+ | **V3** ๐Ÿš€ | Canvas / CustomPaint, Camera / GPS, Plugins, Database (SQLite), i18n |
471
+ | **V4** ๐ŸŒ | iOS backend (SwiftUI), Web (WASM), macOS desktop |
472
+
473
+ <br>
474
+
475
+ ---
476
+
477
+ ## ๐Ÿค Contributing
478
+
479
+ Contributions are welcome! Please feel free to submit a Pull Request.
480
+
481
+ 1. Fork the repository
482
+ 2. Create a feature branch (`git checkout -b feature/amazing-feature`)
483
+ 3. Commit your changes (`git commit -am 'Add amazing feature'`)
484
+ 4. Push (`git push origin feature/amazing-feature`)
485
+ 5. Open a Pull Request
486
+
487
+ <br>
488
+
489
+ ---
490
+
491
+ ## ๐Ÿ“„ License
492
+
493
+ MIT ยฉ Pydrud Contributors. See [LICENSE](LICENSE) for details.
494
+
495
+ <br>
496
+
497
+ ---
498
+
499
+ <p align="center">
500
+ <strong>Pydrud</strong> โ€” <em>Pythonic. Native. Simple.</em><br>
501
+ Built with โค๏ธ for Python developers who love native mobile.
502
+ </p>