@lucent-lang/lucent 0.0.4 → 0.1.0
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.
- package/README.md +97 -0
- package/app.plugin.js +46 -10
- package/dist/{bench-CR3E7e2e.js → bench-B555m0wU.js} +10 -6
- package/dist/{build-CAIrRSYA.js → build-UZ5Zm5m6.js} +1 -1
- package/dist/{build-check-QozuNJm5.js → build-check-Dmf8_yiw.js} +28 -13
- package/dist/{check-1SCmYlqA.js → check-D-H9WLG4.js} +1 -1
- package/dist/cli.js +62 -13
- package/dist/{codes-CIuwXt1S.js → codes-D-6LxUqD.js} +163 -7
- package/dist/compiler.js +4 -4
- package/dist/{dashboard-B-NzjjaN.js → dashboard-C8iF5-6T.js} +2 -2
- package/dist/{dev-BYWMP7oC.js → dev-B3lSx5CF.js} +62 -23
- package/dist/{diagnostic-BM9sXmwl.js → diagnostic-BvwLZQS1.js} +3 -1
- package/dist/{doctor-C0PrfWT6.js → doctor-CTAX3euc.js} +1 -1
- package/dist/{doctor-DdR4JnVc.js → doctor-CrgOAV5L.js} +1 -1
- package/dist/{explain--vV1oaRl.js → explain-Byit07F-.js} +1 -1
- package/dist/{packages-HyskKIsY.js → packages-CSmKeaAh.js} +2 -33
- package/dist/pipeline-BBY0df_2.js +700 -0
- package/dist/project-BLhWwnns.js +342 -0
- package/dist/sdk-coverage-DFGiDOzR.js +113 -0
- package/dist/sdk-diff-DpPYWzrl.js +109 -0
- package/dist/sdk-lock-QOxEFD1o.js +38 -0
- package/dist/{sdk-prefetch-mo04JDZV.js → sdk-prefetch-DX6ztlPN.js} +2 -2
- package/dist/{sdk-search-Iv4K3-8a.js → sdk-search-BShBL1LL.js} +2 -2
- package/dist/{sdk-show-CkrdlqlP.js → sdk-show-B3bI-0M7.js} +5 -4
- package/dist/sdk-usage-ljX5bTuZ.js +95 -0
- package/dist/src-DlICbK91.js +39682 -0
- package/dist/trace-Bi4hkmPx.js +139 -0
- package/lib/core.cjs +368 -1
- package/lib/sdk/android.d.ts +54 -1
- package/lib/sdk/compose.d.ts +94 -0
- package/lib/sdk/compose.schemas.json.gz +0 -0
- package/lib/sdk/core.d.ts +201 -0
- package/lib/sdk/ios.d.ts +75 -2
- package/lib/sdk/ui.d.ts +129 -0
- package/metro/transformer.cjs +11 -2
- package/package.json +2 -1
- package/runtime/cpp/lucent/abort.cpp +59 -11
- package/runtime/cpp/lucent/abort.h +22 -6
- package/runtime/cpp/lucent/array.h +12 -2
- package/runtime/cpp/lucent/async.h +101 -21
- package/runtime/cpp/lucent/bigint.cpp +898 -0
- package/runtime/cpp/lucent/bigint.h +302 -0
- package/runtime/cpp/lucent/buffer.cpp +228 -0
- package/runtime/cpp/lucent/buffer.h +328 -0
- package/runtime/cpp/lucent/builtins.cpp +4 -1
- package/runtime/cpp/lucent/bytes.h +21 -2
- package/runtime/cpp/lucent/callback.cpp +29 -0
- package/runtime/cpp/lucent/callback.h +230 -0
- package/runtime/cpp/lucent/compute.cpp +370 -0
- package/runtime/cpp/lucent/compute.h +455 -0
- package/runtime/cpp/lucent/conversions.h +111 -0
- package/runtime/cpp/lucent/core.h +16 -5
- package/runtime/cpp/lucent/equality.h +107 -28
- package/runtime/cpp/lucent/execution.cpp +416 -0
- package/runtime/cpp/lucent/execution.h +270 -0
- package/runtime/cpp/lucent/extension.cpp +97 -0
- package/runtime/cpp/lucent/extension.h +210 -0
- package/runtime/cpp/lucent/helpers.h +12 -5
- package/runtime/cpp/lucent/items.h +52 -0
- package/runtime/cpp/lucent/jserror.cpp +9 -0
- package/runtime/cpp/lucent/jserror.h +11 -0
- package/runtime/cpp/lucent/jsi/convert.cpp +138 -0
- package/runtime/cpp/lucent/jsi/convert.h +128 -59
- package/runtime/cpp/lucent/jsi/host.cpp +213 -35
- package/runtime/cpp/lucent/jsi/host.h +116 -16
- package/runtime/cpp/lucent/json.h +19 -1
- package/runtime/cpp/lucent/lifecycle.cpp +260 -0
- package/runtime/cpp/lucent/lifecycle.h +124 -0
- package/runtime/cpp/lucent/lucent.h +7 -0
- package/runtime/cpp/lucent/map.h +41 -7
- package/runtime/cpp/lucent/native.cpp +35 -47
- package/runtime/cpp/lucent/native.h +116 -26
- package/runtime/cpp/lucent/number.cpp +38 -38
- package/runtime/cpp/lucent/number.h +33 -0
- package/runtime/cpp/lucent/operation.h +65 -0
- package/runtime/cpp/lucent/ops.h +14 -6
- package/runtime/cpp/lucent/platform/android.cpp +223 -10
- package/runtime/cpp/lucent/platform/android.h +227 -4
- package/runtime/cpp/lucent/platform/android_activity.cpp +258 -0
- package/runtime/cpp/lucent/platform/android_requests.h +140 -0
- package/runtime/cpp/lucent/platform/compose.h +256 -0
- package/runtime/cpp/lucent/platform/ios.h +325 -25
- package/runtime/cpp/lucent/platform/ios.mm +1 -14
- package/runtime/cpp/lucent/platform/ios_ui.h +192 -0
- package/runtime/cpp/lucent/platform/ios_ui.mm +454 -0
- package/runtime/cpp/lucent/platform/swiftui.h +190 -0
- package/runtime/cpp/lucent/presentation.cpp +11 -0
- package/runtime/cpp/lucent/presentation.h +109 -0
- package/runtime/cpp/lucent/reactive.cpp +406 -0
- package/runtime/cpp/lucent/reactive.h +566 -0
- package/runtime/cpp/lucent/report.cpp +40 -0
- package/runtime/cpp/lucent/report.h +16 -0
- package/runtime/cpp/lucent/resource.cpp +83 -0
- package/runtime/cpp/lucent/resource.h +75 -0
- package/runtime/cpp/lucent/scheduler.cpp +38 -124
- package/runtime/cpp/lucent/scheduler.h +102 -83
- package/runtime/cpp/lucent/scope.cpp +308 -0
- package/runtime/cpp/lucent/scope.h +452 -0
- package/runtime/cpp/lucent/sizing.h +263 -0
- package/runtime/cpp/lucent/slots.h +231 -0
- package/runtime/cpp/lucent/trace.cpp +539 -0
- package/runtime/cpp/lucent/trace.h +251 -0
- package/runtime/cpp/lucent/transport.cpp +9 -0
- package/runtime/cpp/lucent/transport.h +341 -0
- package/runtime/cpp/lucent/view.cpp +123 -0
- package/runtime/cpp/lucent/view.h +157 -0
- package/runtime/cpp/rn/LucentComponentView.h +121 -0
- package/runtime/cpp/rn/LucentComponentView.mm +479 -0
- package/runtime/cpp/rn/LucentModule.cpp +37 -7
- package/runtime/cpp/rn/LucentModule.h +3 -0
- package/runtime/cpp/rn/LucentViewChildren.h +80 -0
- package/runtime/cpp/rn/LucentViewChildren.mm +211 -0
- package/runtime/cpp/rn/LucentViewRegistry.h +62 -0
- package/runtime/cpp/rn/LucentViewRequests.cpp +91 -0
- package/runtime/cpp/rn/LucentViewRequests.h +104 -0
- package/runtime/cpp/rn/LucentViewSizing.h +345 -0
- package/runtime/cpp/rn/LucentViewSlots.h +242 -0
- package/runtime/cpp/rn/LucentViewValues.h +125 -0
- package/runtime/cpp/rn/LucentViews.h +440 -0
- package/runtime/cpp/rn/LucentViewsAndroid.cpp +439 -0
- package/runtime/cpp/rn/LucentViewsAndroid.h +138 -0
- package/runtime/cpp/third_party/dragonbox/LICENSE-Boost +23 -0
- package/runtime/cpp/third_party/dragonbox/README.md +16 -0
- package/runtime/cpp/third_party/dragonbox/dragonbox.h +4440 -0
- package/runtime/js/index.d.ts +11 -1
- package/runtime/js/index.js +84 -8
- package/runtime/js/views.d.ts +38 -0
- package/runtime/js/views.js +226 -0
- package/runtime/native/android/CMakeLists.txt +4 -1
- package/runtime/native/android/src/compose/java/dev/lucent/compose/CompositionRules.kt +44 -0
- package/runtime/native/android/src/compose/java/dev/lucent/compose/LucentComposition.kt +161 -0
- package/runtime/native/android/src/main/java/dev/lucent/LucentActivities.java +292 -0
- package/runtime/native/android/src/main/java/dev/lucent/LucentChildren.java +174 -0
- package/runtime/native/android/src/main/java/dev/lucent/LucentHostView.java +232 -0
- package/runtime/native/android/src/main/java/dev/lucent/LucentInitializer.java +46 -0
- package/runtime/native/android/src/main/java/dev/lucent/LucentPackage.java +4 -3
- package/runtime/native/android/src/main/java/dev/lucent/LucentRequestActivity.java +92 -0
- package/runtime/native/android/src/main/java/dev/lucent/LucentSlotView.java +102 -0
- package/runtime/native/android/src/main/java/dev/lucent/LucentViewManager.java +149 -0
- package/runtime/native/android/src/main/java/dev/lucent/LucentViewManagers.java +16 -0
- package/runtime/native/android/src/main/java/dev/lucent/LucentViews.java +44 -0
- package/runtime/test/jsi/harness.cpp +9 -0
- package/schemas/build.schema.json +96 -1
- package/schemas/check.schema.json +56 -1
- package/schemas/lucent.schema.json +495 -9
- package/schemas/sdk-diff.schema.json +70 -0
- package/schemas/sdk-lock.schema.json +67 -0
- package/ts-plugin/index.js +77 -4
- package/dist/pipeline-CH7-2cxu.js +0 -232
- package/dist/project-D8tEet0k.js +0 -150
- package/dist/sdk-coverage-BoW_3Efd.js +0 -64
- package/dist/src-BD7f1_aZ.js +0 -11493
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
// Generated by Lucent. Do not edit.
|
|
2
|
+
package dev.lucent;
|
|
3
|
+
|
|
4
|
+
import android.app.Activity;
|
|
5
|
+
import android.app.Application;
|
|
6
|
+
import android.content.Intent;
|
|
7
|
+
import android.os.Bundle;
|
|
8
|
+
import android.os.Handler;
|
|
9
|
+
import android.os.Looper;
|
|
10
|
+
import android.util.Log;
|
|
11
|
+
import java.lang.ref.WeakReference;
|
|
12
|
+
import java.lang.reflect.InvocationHandler;
|
|
13
|
+
import java.lang.reflect.Method;
|
|
14
|
+
import java.lang.reflect.Proxy;
|
|
15
|
+
import java.util.ArrayDeque;
|
|
16
|
+
import java.util.HashMap;
|
|
17
|
+
import java.util.Map;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* The app's Activities as Lucent code sees them: the current one, their
|
|
21
|
+
* lifecycle events, and requests for an activity result or a permission.
|
|
22
|
+
*
|
|
23
|
+
* Activities are held weakly: Lucent never keeps one alive. A request runs
|
|
24
|
+
* in its own translucent Activity (LucentRequestActivity), so it works
|
|
25
|
+
* whatever the app's Activity is, and survives the app's Activity being
|
|
26
|
+
* recreated while the request is out. Permission requests run one at a
|
|
27
|
+
* time, as Android requires. Everything here runs on the main thread except
|
|
28
|
+
* current(), which any thread may call.
|
|
29
|
+
*/
|
|
30
|
+
public final class LucentActivities implements Application.ActivityLifecycleCallbacks {
|
|
31
|
+
/** Lifecycle events, as native code numbers them. */
|
|
32
|
+
static final int CREATED = 0, STARTED = 1, RESUMED = 2, PAUSED = 3, STOPPED = 4, DESTROYED = 5, NEW_INTENT = 6;
|
|
33
|
+
|
|
34
|
+
/** Request kinds. */
|
|
35
|
+
static final int RESULT = 0, PERMISSIONS = 1;
|
|
36
|
+
|
|
37
|
+
private static final String TAG = "Lucent";
|
|
38
|
+
private static final Handler main = new Handler(Looper.getMainLooper());
|
|
39
|
+
private static volatile boolean installed;
|
|
40
|
+
private static volatile WeakReference<Activity> current = new WeakReference<>(null);
|
|
41
|
+
/** Whether native code listens to lifecycle events. */
|
|
42
|
+
private static volatile boolean listening;
|
|
43
|
+
|
|
44
|
+
/** Requests by id, and the request Activity of each while it runs. */
|
|
45
|
+
private static final Map<Long, Request> requests = new HashMap<>();
|
|
46
|
+
/** Permission requests waiting for the one in flight. */
|
|
47
|
+
private static final ArrayDeque<Request> waiting = new ArrayDeque<>();
|
|
48
|
+
private static Request askingPermissions;
|
|
49
|
+
|
|
50
|
+
static final class Request {
|
|
51
|
+
final long id;
|
|
52
|
+
final int kind;
|
|
53
|
+
final Intent intent;
|
|
54
|
+
final String[] permissions;
|
|
55
|
+
WeakReference<LucentRequestActivity> activity = new WeakReference<>(null);
|
|
56
|
+
|
|
57
|
+
Request(long id, int kind, Intent intent, String[] permissions) {
|
|
58
|
+
this.id = id;
|
|
59
|
+
this.kind = kind;
|
|
60
|
+
this.intent = intent;
|
|
61
|
+
this.permissions = permissions;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
private LucentActivities() {}
|
|
66
|
+
|
|
67
|
+
/** Called once per process, before any Activity (LucentInitializer), or on first use. */
|
|
68
|
+
public static synchronized void install(Application app) {
|
|
69
|
+
if (installed) return;
|
|
70
|
+
|
|
71
|
+
app.registerActivityLifecycleCallbacks(new LucentActivities());
|
|
72
|
+
installed = true;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/** The Activity in front: the last one created, started or resumed and not destroyed. Null if none. */
|
|
76
|
+
public static Activity current() {
|
|
77
|
+
return current.get();
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
public static void listen(boolean on) {
|
|
81
|
+
listening = on;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// --- requests, from native code -------------------------------------------------
|
|
85
|
+
|
|
86
|
+
public static void startForResult(long id, Intent intent) {
|
|
87
|
+
main.post(() -> launch(new Request(id, RESULT, intent, null)));
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
public static void requestPermissions(long id, String[] permissions) {
|
|
91
|
+
main.post(() -> {
|
|
92
|
+
Request r = new Request(id, PERMISSIONS, null, permissions);
|
|
93
|
+
|
|
94
|
+
if (askingPermissions != null) {
|
|
95
|
+
waiting.add(r);
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
askingPermissions = r;
|
|
100
|
+
launch(r);
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Forgets a request: its answer, if it still comes, is dropped. Closes
|
|
106
|
+
* the Activity it started when Android lets the app (not a dialog of
|
|
107
|
+
* another app or of the system).
|
|
108
|
+
*/
|
|
109
|
+
public static void forget(long id) {
|
|
110
|
+
main.post(() -> {
|
|
111
|
+
Request r = requests.remove(id);
|
|
112
|
+
if (r == null) {
|
|
113
|
+
waiting.removeIf(w -> w.id == id);
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
Log.d(TAG, "request " + id + " forgotten: a later answer is dropped");
|
|
118
|
+
|
|
119
|
+
LucentRequestActivity a = r.activity.get();
|
|
120
|
+
if (a != null) a.abandon();
|
|
121
|
+
|
|
122
|
+
next(r);
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
private static void launch(Request r) {
|
|
127
|
+
Activity from = current.get();
|
|
128
|
+
|
|
129
|
+
if (from == null || from.isFinishing() || from.isDestroyed()) {
|
|
130
|
+
onFailed(r.id, "ERR_NO_ACTIVITY", "There is no Activity in front to ask from");
|
|
131
|
+
next(r);
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
requests.put(r.id, r);
|
|
136
|
+
Log.d(TAG, "request " + r.id + " started from " + from.getLocalClassName());
|
|
137
|
+
from.startActivity(LucentRequestActivity.intent(from, r.id));
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/** The request Activity for `id` was created: the request, unless it was forgotten meanwhile. */
|
|
141
|
+
static Request attach(long id, LucentRequestActivity activity) {
|
|
142
|
+
Request r = requests.get(id);
|
|
143
|
+
if (r != null) r.activity = new WeakReference<>(activity);
|
|
144
|
+
return r;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
static void answered(Request r, int resultCode, Intent data) {
|
|
148
|
+
if (!settle(r, "result " + resultCode)) return;
|
|
149
|
+
|
|
150
|
+
onResult(r.id, resultCode, data);
|
|
151
|
+
next(r);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
static void answered(Request r, String[] permissions, int[] grants) {
|
|
155
|
+
if (!settle(r, grants.length + " permission answers")) return;
|
|
156
|
+
|
|
157
|
+
onPermissions(r.id, permissions, grants);
|
|
158
|
+
next(r);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/** The request could not be made, or its Activity finished without an answer. */
|
|
162
|
+
static void failed(Request r, String code, String message) {
|
|
163
|
+
if (!settle(r, code)) return;
|
|
164
|
+
|
|
165
|
+
onFailed(r.id, code, message);
|
|
166
|
+
next(r);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/** Takes `r` for its answer: false, and the answer dropped, if it was forgotten meanwhile. */
|
|
170
|
+
private static boolean settle(Request r, String answer) {
|
|
171
|
+
if (requests.remove(r.id) == null) {
|
|
172
|
+
Log.d(TAG, "request " + r.id + ": dropped a late answer (" + answer + ")");
|
|
173
|
+
return false;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
Log.d(TAG, "request " + r.id + " answered: " + answer);
|
|
177
|
+
return true;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/** After a permission request, the next one. */
|
|
181
|
+
private static void next(Request done) {
|
|
182
|
+
if (done != askingPermissions) return;
|
|
183
|
+
|
|
184
|
+
askingPermissions = waiting.poll();
|
|
185
|
+
if (askingPermissions != null) launch(askingPermissions);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// --- the lifecycle -------------------------------------------------------------
|
|
189
|
+
|
|
190
|
+
private static boolean tracked(Activity a) {
|
|
191
|
+
// The request Activity is Lucent's own: it is never "the current Activity".
|
|
192
|
+
return !(a instanceof LucentRequestActivity);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
private static void event(int kind, Activity a, Intent intent) {
|
|
196
|
+
if (listening) onEvent(kind, a, intent);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
@Override
|
|
200
|
+
public void onActivityCreated(Activity a, Bundle saved) {
|
|
201
|
+
if (!tracked(a)) return;
|
|
202
|
+
|
|
203
|
+
current = new WeakReference<>(a);
|
|
204
|
+
newIntents(a);
|
|
205
|
+
event(CREATED, a, null);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
@Override
|
|
209
|
+
public void onActivityStarted(Activity a) {
|
|
210
|
+
if (!tracked(a)) return;
|
|
211
|
+
|
|
212
|
+
current = new WeakReference<>(a);
|
|
213
|
+
event(STARTED, a, null);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
@Override
|
|
217
|
+
public void onActivityResumed(Activity a) {
|
|
218
|
+
if (!tracked(a)) return;
|
|
219
|
+
|
|
220
|
+
current = new WeakReference<>(a);
|
|
221
|
+
event(RESUMED, a, null);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
@Override
|
|
225
|
+
public void onActivityPaused(Activity a) {
|
|
226
|
+
if (tracked(a)) event(PAUSED, a, null);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
@Override
|
|
230
|
+
public void onActivityStopped(Activity a) {
|
|
231
|
+
if (tracked(a)) event(STOPPED, a, null);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
@Override
|
|
235
|
+
public void onActivitySaveInstanceState(Activity a, Bundle out) {}
|
|
236
|
+
|
|
237
|
+
@Override
|
|
238
|
+
public void onActivityDestroyed(Activity a) {
|
|
239
|
+
if (!tracked(a)) return;
|
|
240
|
+
|
|
241
|
+
if (current.get() == a) current = new WeakReference<>(null);
|
|
242
|
+
event(DESTROYED, a, null);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* New intents reach an Activity in onNewIntent, which no callback reports:
|
|
247
|
+
* AndroidX activities (ComponentActivity, React Native's) take listeners,
|
|
248
|
+
* found by name so that Lucent's Java needs no AndroidX.
|
|
249
|
+
*/
|
|
250
|
+
private static void newIntents(Activity a) {
|
|
251
|
+
Method add;
|
|
252
|
+
Class<?> consumer;
|
|
253
|
+
try {
|
|
254
|
+
consumer = Class.forName("androidx.core.util.Consumer", false, a.getClassLoader());
|
|
255
|
+
add = a.getClass().getMethod("addOnNewIntentListener", consumer);
|
|
256
|
+
} catch (ReflectiveOperationException e) {
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
WeakReference<Activity> held = new WeakReference<>(a);
|
|
261
|
+
InvocationHandler onIntent = (proxy, m, args) -> {
|
|
262
|
+
switch (m.getName()) {
|
|
263
|
+
case "accept":
|
|
264
|
+
Activity at = held.get();
|
|
265
|
+
if (at != null) event(NEW_INTENT, at, (Intent) args[0]);
|
|
266
|
+
return null;
|
|
267
|
+
case "equals":
|
|
268
|
+
return proxy == args[0];
|
|
269
|
+
case "hashCode":
|
|
270
|
+
return System.identityHashCode(proxy);
|
|
271
|
+
default:
|
|
272
|
+
return "Lucent new intent listener";
|
|
273
|
+
}
|
|
274
|
+
};
|
|
275
|
+
|
|
276
|
+
try {
|
|
277
|
+
add.invoke(a, Proxy.newProxyInstance(a.getClassLoader(), new Class<?>[] {consumer}, onIntent));
|
|
278
|
+
} catch (ReflectiveOperationException e) {
|
|
279
|
+
// No listener: new intents go unreported for this Activity.
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// --- to native code ------------------------------------------------------------
|
|
284
|
+
|
|
285
|
+
private static native void onResult(long id, int resultCode, Intent data);
|
|
286
|
+
|
|
287
|
+
private static native void onPermissions(long id, String[] permissions, int[] grants);
|
|
288
|
+
|
|
289
|
+
private static native void onFailed(long id, String code, String message);
|
|
290
|
+
|
|
291
|
+
private static native void onEvent(int kind, Activity activity, Intent intent);
|
|
292
|
+
}
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
// Generated by Lucent. Do not edit.
|
|
2
|
+
package dev.lucent;
|
|
3
|
+
|
|
4
|
+
import java.util.ArrayList;
|
|
5
|
+
import java.util.List;
|
|
6
|
+
import java.util.function.Consumer;
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* One host view's React children, in React Native's order, and the slot of
|
|
10
|
+
* its current mount (lucent:ui's `slot`). Main thread.
|
|
11
|
+
*
|
|
12
|
+
* <p>Who owns what: React Native owns each child (it creates, updates, lays
|
|
13
|
+
* out and destroys it, and says where it goes: added or removed at an index
|
|
14
|
+
* of the component's children). The host owns the slot and the children's
|
|
15
|
+
* place in it: it makes a slot for each mount of a component taking
|
|
16
|
+
* children, puts every child React Native adds in it in React Native's
|
|
17
|
+
* order, takes out the ones React Native removes, and does nothing else to
|
|
18
|
+
* them. The setup owns the slot's place in the view it returns.
|
|
19
|
+
*
|
|
20
|
+
* <p>Plain Java over {@link Slots}, so its rules run anywhere: the shell
|
|
21
|
+
* ({@link LucentHostView}) gives it Android's views.
|
|
22
|
+
*
|
|
23
|
+
* @param <V> a view
|
|
24
|
+
* @param <S> a slot
|
|
25
|
+
*/
|
|
26
|
+
final class LucentChildren<V, S> {
|
|
27
|
+
/** What the host does to its views and slots. */
|
|
28
|
+
interface Slots<V, S> {
|
|
29
|
+
/** A new, empty slot. */
|
|
30
|
+
S make();
|
|
31
|
+
|
|
32
|
+
/** The slot `child` is in (the current mount's or an earlier one's), or null. */
|
|
33
|
+
S slotOf(V child);
|
|
34
|
+
|
|
35
|
+
/** Whether `child` is in any view. */
|
|
36
|
+
boolean hasParent(V child);
|
|
37
|
+
|
|
38
|
+
/** Puts `child` in `slot` just below `next`, a child already in it, or above every child without one. */
|
|
39
|
+
void insert(S slot, V child, V next);
|
|
40
|
+
|
|
41
|
+
/** Takes `child` out of `slot`. */
|
|
42
|
+
void remove(S slot, V child);
|
|
43
|
+
|
|
44
|
+
/** Whether `slot` is in `content`, or is it. */
|
|
45
|
+
boolean within(S slot, V content);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
private final Slots<V, S> slots;
|
|
49
|
+
private final String name;
|
|
50
|
+
private final boolean takesChildren;
|
|
51
|
+
private final Consumer<String> report;
|
|
52
|
+
private final List<V> children = new ArrayList<>();
|
|
53
|
+
private S slot;
|
|
54
|
+
/** Whether children given to a component taking none were reported. */
|
|
55
|
+
private boolean refused;
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* The children of a view of the component registered as `name`, which takes React children if
|
|
59
|
+
* `takesChildren`; what goes wrong goes to `report`.
|
|
60
|
+
*/
|
|
61
|
+
LucentChildren(Slots<V, S> slots, String name, boolean takesChildren, Consumer<String> report) {
|
|
62
|
+
this.slots = slots;
|
|
63
|
+
this.name = name;
|
|
64
|
+
this.takesChildren = takesChildren;
|
|
65
|
+
this.report = report;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/** The current mount's slot, or null (no mount, or no children taken). */
|
|
69
|
+
S slot() {
|
|
70
|
+
return slot;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
int count() {
|
|
74
|
+
return children.size();
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** React Native's child at `index`, or null. */
|
|
78
|
+
V childAt(int index) {
|
|
79
|
+
return index >= 0 && index < children.size() ? children.get(index) : null;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/** React Native adds `child` at `index` of the component's children. */
|
|
83
|
+
void add(V child, int index) {
|
|
84
|
+
int count = children.size();
|
|
85
|
+
|
|
86
|
+
if (index < 0 || index > count) {
|
|
87
|
+
report.accept("[lucent] " + name + ": React Native added a child at " + index + " of " + count);
|
|
88
|
+
index = index < 0 ? 0 : count;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
children.add(index, child);
|
|
92
|
+
|
|
93
|
+
if (!takesChildren && !refused) {
|
|
94
|
+
refused = true;
|
|
95
|
+
report.accept(
|
|
96
|
+
"[lucent] "
|
|
97
|
+
+ name
|
|
98
|
+
+ " takes no React children: React Native gave it "
|
|
99
|
+
+ children.size()
|
|
100
|
+
+ ", which it does not show");
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
place(child);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/** React Native removes the child at `index` of the component's children. */
|
|
107
|
+
void remove(int index) {
|
|
108
|
+
if (index < 0 || index >= children.size()) {
|
|
109
|
+
report.accept("[lucent] " + name + ": React Native removed a child at " + index + " of " + children.size());
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
V child = children.remove(index);
|
|
114
|
+
// Out of the slot it was put in (an earlier mount's, if React Native kept it past a recycle).
|
|
115
|
+
S in = slots.slotOf(child);
|
|
116
|
+
|
|
117
|
+
if (in != null) slots.remove(in, child);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/** A mount starts: a new slot holding every child, if the component takes them; the slot. */
|
|
121
|
+
S startMount() {
|
|
122
|
+
slot = takesChildren ? slots.make() : null;
|
|
123
|
+
|
|
124
|
+
for (V child : children) place(child);
|
|
125
|
+
|
|
126
|
+
return slot;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/** The mount's setup returned `content`: its slot should be in it. */
|
|
130
|
+
void mountedContent(V content) {
|
|
131
|
+
if (slot != null && content != null && !slots.within(slot, content))
|
|
132
|
+
report.accept(
|
|
133
|
+
"[lucent] "
|
|
134
|
+
+ name
|
|
135
|
+
+ "'s setup did not put its slot in the view it returned: its React children do not show");
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/** The mount ends (the view is recycled): its slot goes. */
|
|
139
|
+
void endMount() {
|
|
140
|
+
// React Native removes a view's children before it recycles the view: any left stay where they are.
|
|
141
|
+
if (!children.isEmpty())
|
|
142
|
+
report.accept(
|
|
143
|
+
"[lucent] "
|
|
144
|
+
+ name
|
|
145
|
+
+ " was recycled holding "
|
|
146
|
+
+ children.size()
|
|
147
|
+
+ " React children: they stay where React Native left them");
|
|
148
|
+
|
|
149
|
+
children.clear();
|
|
150
|
+
slot = null;
|
|
151
|
+
refused = false;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/** Puts `child` in the slot, below the next child there: React Native's order. */
|
|
155
|
+
private void place(V child) {
|
|
156
|
+
if (slot == null || slots.slotOf(child) == slot) return;
|
|
157
|
+
|
|
158
|
+
if (slots.hasParent(child)) {
|
|
159
|
+
report.accept("[lucent] " + name + ": a React child is in another view: React Native keeps it there");
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
for (int i = children.indexOf(child) + 1; i < children.size(); i++) {
|
|
164
|
+
V next = children.get(i);
|
|
165
|
+
|
|
166
|
+
if (slots.slotOf(next) == slot) {
|
|
167
|
+
slots.insert(slot, child, next);
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
slots.insert(slot, child, null);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
// Generated by Lucent. Do not edit.
|
|
2
|
+
package dev.lucent;
|
|
3
|
+
|
|
4
|
+
import android.content.Context;
|
|
5
|
+
import android.view.View;
|
|
6
|
+
import android.view.ViewGroup;
|
|
7
|
+
import android.widget.FrameLayout;
|
|
8
|
+
import com.facebook.react.modules.i18nmanager.I18nUtil;
|
|
9
|
+
import java.util.Arrays;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* The view React Native mounts for a Lucent component: a shell holding the
|
|
13
|
+
* view the component's setup returns, filling its content box (Yoga's
|
|
14
|
+
* border and padding are the shell's padding). React Native lays the
|
|
15
|
+
* shell out; the shell lays its content out, which React Native's layout
|
|
16
|
+
* would not, and measures it for the C++ host when the shadow tree needs
|
|
17
|
+
* its size. The React children React Native adds to the shell (through its
|
|
18
|
+
* manager) never go in it: they go in the slot of a component taking them
|
|
19
|
+
* ({@link LucentChildren}), whose place the shell passes on to the C++
|
|
20
|
+
* host, for Yoga to lay them out in it.
|
|
21
|
+
*/
|
|
22
|
+
public final class LucentHostView extends FrameLayout {
|
|
23
|
+
/** The C++ host (LucentViews), 0 while none. */
|
|
24
|
+
long host;
|
|
25
|
+
|
|
26
|
+
/** React Native's children of the component, and the current mount's slot for them. */
|
|
27
|
+
final LucentChildren<View, LucentSlotView> children;
|
|
28
|
+
|
|
29
|
+
private boolean relayoutPosted;
|
|
30
|
+
|
|
31
|
+
/** The slot's place and the content box last passed on (pixels), and the direction: unchanged ones are not. */
|
|
32
|
+
private final int[] placed = new int[9];
|
|
33
|
+
|
|
34
|
+
private final Runnable relayout =
|
|
35
|
+
() -> {
|
|
36
|
+
relayoutPosted = false;
|
|
37
|
+
measure(
|
|
38
|
+
MeasureSpec.makeMeasureSpec(getWidth(), MeasureSpec.EXACTLY),
|
|
39
|
+
MeasureSpec.makeMeasureSpec(getHeight(), MeasureSpec.EXACTLY));
|
|
40
|
+
layout(getLeft(), getTop(), getRight(), getBottom());
|
|
41
|
+
|
|
42
|
+
// The content may have changed size though no Lucent code ran (the
|
|
43
|
+
// host hears of that itself): measured if the shadow tree needs its size.
|
|
44
|
+
if (host != 0) LucentViews.contentChanged(host);
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
/** A shell for the component registered as `name`, which takes React children if `takesChildren`. */
|
|
48
|
+
public LucentHostView(Context context, String name, boolean takesChildren) {
|
|
49
|
+
super(context);
|
|
50
|
+
children = new LucentChildren<>(new Slots(context, this), name, takesChildren, LucentViews::report);
|
|
51
|
+
|
|
52
|
+
// What the content draws outside its box shows (a scaled Compose body),
|
|
53
|
+
// as a React Native view's children do by default.
|
|
54
|
+
setClipChildren(false);
|
|
55
|
+
setClipToPadding(false);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
@Override
|
|
59
|
+
protected void onAttachedToWindow() {
|
|
60
|
+
super.onAttachedToWindow();
|
|
61
|
+
|
|
62
|
+
if (host != 0) LucentViews.attach(host);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** Shows `content`, the view the component's setup returned (the C++ host calls it). */
|
|
66
|
+
void setContent(View content) {
|
|
67
|
+
if (getChildCount() == 1 && getChildAt(0) == content) return;
|
|
68
|
+
|
|
69
|
+
removeAllViews();
|
|
70
|
+
|
|
71
|
+
if (content == null) return;
|
|
72
|
+
|
|
73
|
+
children.mountedContent(content);
|
|
74
|
+
|
|
75
|
+
if (content.getParent() instanceof ViewGroup) ((ViewGroup) content.getParent()).removeView(content);
|
|
76
|
+
|
|
77
|
+
addView(content, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* A new slot for the React children of the mount starting, holding every
|
|
82
|
+
* child (the C++ host calls it), or null if the component takes none.
|
|
83
|
+
*/
|
|
84
|
+
ViewGroup makeSlot() {
|
|
85
|
+
// The mount's C++ host has been told nothing yet.
|
|
86
|
+
Arrays.fill(placed, Integer.MIN_VALUE);
|
|
87
|
+
|
|
88
|
+
return children.startMount();
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* The content's size in density-independent pixels, up to `maxWidth` ×
|
|
93
|
+
* `maxHeight` (infinite: no bound), or null without content or while it
|
|
94
|
+
* is in no window. The C++ host calls it on the main thread.
|
|
95
|
+
*/
|
|
96
|
+
float[] measureContent(float maxWidth, float maxHeight) {
|
|
97
|
+
if (getChildCount() != 1) return null;
|
|
98
|
+
|
|
99
|
+
View content = getChildAt(0);
|
|
100
|
+
|
|
101
|
+
// Out of a window, a view may not be able to measure what it shows (a
|
|
102
|
+
// ComposeView composes with its window's recomposer, and throws before
|
|
103
|
+
// it has one): the shell's next layout, once the content is attached,
|
|
104
|
+
// measures it (the relayout backstop).
|
|
105
|
+
if (!content.isAttachedToWindow()) {
|
|
106
|
+
requestLayout();
|
|
107
|
+
return null;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
float density = density();
|
|
111
|
+
|
|
112
|
+
// A view caches its measurement by spec: a change that asked for no
|
|
113
|
+
// layout (a native one) would get the old size back, from the content
|
|
114
|
+
// or from any view inside it (a ComposeView's composition view).
|
|
115
|
+
forceLayoutTree(content);
|
|
116
|
+
content.measure(bound(maxWidth, density), bound(maxHeight, density));
|
|
117
|
+
|
|
118
|
+
float[] size = {content.getMeasuredWidth() / density, content.getMeasuredHeight() / density};
|
|
119
|
+
|
|
120
|
+
// Back to the size it is laid out at: the next layout pass may keep it.
|
|
121
|
+
content.measure(
|
|
122
|
+
MeasureSpec.makeMeasureSpec(content.getWidth(), MeasureSpec.EXACTLY),
|
|
123
|
+
MeasureSpec.makeMeasureSpec(content.getHeight(), MeasureSpec.EXACTLY));
|
|
124
|
+
|
|
125
|
+
return size;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* The slot is at `x`, `y` (`width` × `height`) in the shell, in pixels:
|
|
130
|
+
* passed on to the C++ host with the content box, unless nothing changed.
|
|
131
|
+
*/
|
|
132
|
+
void placeSlot(int x, int y, int width, int height) {
|
|
133
|
+
boolean rtl = getLayoutDirection() == LAYOUT_DIRECTION_RTL;
|
|
134
|
+
int left = getPaddingLeft();
|
|
135
|
+
int top = getPaddingTop();
|
|
136
|
+
int[] now = {
|
|
137
|
+
x,
|
|
138
|
+
y,
|
|
139
|
+
width,
|
|
140
|
+
height,
|
|
141
|
+
left,
|
|
142
|
+
top,
|
|
143
|
+
getWidth() - left - getPaddingRight(),
|
|
144
|
+
getHeight() - top - getPaddingBottom(),
|
|
145
|
+
rtl ? 1 : 0
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
if (host == 0 || Arrays.equals(now, placed)) return;
|
|
149
|
+
|
|
150
|
+
System.arraycopy(now, 0, placed, 0, now.length);
|
|
151
|
+
|
|
152
|
+
// React Native's layout reads a right-to-left style's left and right as start and end if it swaps them.
|
|
153
|
+
boolean swapped = rtl && I18nUtil.getInstance().doLeftAndRightSwapInRTL(getContext());
|
|
154
|
+
|
|
155
|
+
LucentViews.slotPlaced(host, Arrays.copyOf(now, 8), rtl, swapped);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
private static void forceLayoutTree(View view) {
|
|
159
|
+
view.forceLayout();
|
|
160
|
+
|
|
161
|
+
if (view instanceof ViewGroup) {
|
|
162
|
+
ViewGroup group = (ViewGroup) view;
|
|
163
|
+
|
|
164
|
+
for (int i = 0; i < group.getChildCount(); i++) forceLayoutTree(group.getChildAt(i));
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/** Pixels per density-independent pixel: the scale layout rounds to. */
|
|
169
|
+
float density() {
|
|
170
|
+
return getResources().getDisplayMetrics().density;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
private static int bound(float max, float density) {
|
|
174
|
+
return Float.isInfinite(max)
|
|
175
|
+
? MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)
|
|
176
|
+
: MeasureSpec.makeMeasureSpec((int) Math.floor(max * density), MeasureSpec.AT_MOST);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
@Override
|
|
180
|
+
public void requestLayout() {
|
|
181
|
+
super.requestLayout();
|
|
182
|
+
|
|
183
|
+
// Called while constructing, before the field is set.
|
|
184
|
+
if (relayout != null && !relayoutPosted) {
|
|
185
|
+
relayoutPosted = true;
|
|
186
|
+
post(relayout);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/** Android's views and slots, as LucentChildren handles them. */
|
|
191
|
+
private static final class Slots implements LucentChildren.Slots<View, LucentSlotView> {
|
|
192
|
+
private final Context context;
|
|
193
|
+
private final LucentHostView host;
|
|
194
|
+
|
|
195
|
+
Slots(Context context, LucentHostView host) {
|
|
196
|
+
this.context = context;
|
|
197
|
+
this.host = host;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
@Override
|
|
201
|
+
public LucentSlotView make() {
|
|
202
|
+
return new LucentSlotView(context, host, host::placeSlot);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
@Override
|
|
206
|
+
public LucentSlotView slotOf(View child) {
|
|
207
|
+
return child.getParent() instanceof LucentSlotView ? (LucentSlotView) child.getParent() : null;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
@Override
|
|
211
|
+
public boolean hasParent(View child) {
|
|
212
|
+
return child.getParent() != null;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
@Override
|
|
216
|
+
public void insert(LucentSlotView slot, View child, View next) {
|
|
217
|
+
slot.addView(child, next == null ? -1 : slot.indexOfChild(next));
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
@Override
|
|
221
|
+
public void remove(LucentSlotView slot, View child) {
|
|
222
|
+
slot.removeView(child);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
@Override
|
|
226
|
+
public boolean within(LucentSlotView slot, View content) {
|
|
227
|
+
for (Object v = slot; v instanceof View; v = ((View) v).getParent()) if (v == content) return true;
|
|
228
|
+
|
|
229
|
+
return false;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|