create-mithril-lynx 2.4.0 → 2.5.2
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 +152 -13
- package/package.json +1 -1
- package/src/index.js +812 -12
- package/templates/_shared/ts/package.json +1 -1
- package/templates/_shared/ts/src/rspeedy-env.d.ts +18 -0
- package/templates/android/app-scripts/android-prepare.mjs +53 -0
- package/templates/android/host/app/build.gradle.kts +4 -0
- package/templates/android/host/app/src/main/AndroidManifest.xml +1 -0
- package/templates/android/host/app/src/main/java/package-path/MainActivity.kt +34 -0
- package/templates/android/host/app/src/main/java/package-path/MithrilLynxNavigationModule.kt +28 -0
- package/templates/android/host/app/src/main/res/xml/config.xml +8 -0
- package/templates/android/host/app/src/main/res/xml/file_paths.xml +10 -0
- package/templates/android/host/app/src/main/res/xml/network_security_config.xml +9 -0
- package/templates/basic-activity/ts/src/app-bar.ts +3 -4
- package/templates/basic-activity/ts/src/background.ts +13 -1
- /package/templates/android/host/app/src/main/res/values/{themes.xml → styles.xml} +0 -0
|
@@ -1,3 +1,21 @@
|
|
|
1
1
|
/// <reference types="@lynx-js/rspeedy/client" />
|
|
2
2
|
/// <reference types="@lynx-js/types" />
|
|
3
3
|
/// <reference types="@lynx-js/type-element-api" />
|
|
4
|
+
|
|
5
|
+
declare module "@lynx-js/types" {
|
|
6
|
+
interface NativeModules {
|
|
7
|
+
/** Android host bridge used by mithril-lynx/route.listenBackButton(). */
|
|
8
|
+
MithrilLynxNavigationModule?: {
|
|
9
|
+
setCanGoBack(canGoBack: boolean): void;
|
|
10
|
+
};
|
|
11
|
+
LynxBatteryPlugin?: { getStatus(requestId: string): void };
|
|
12
|
+
LynxCameraPlugin?: { takePhoto(requestId: string): void };
|
|
13
|
+
LynxDevicePlugin?: { getInfo(requestId: string): void };
|
|
14
|
+
LynxGeolocationPlugin?: { getCurrentPosition(requestId: string, highAccuracy: boolean): void };
|
|
15
|
+
LynxNetworkPlugin?: { getInfo(requestId: string): void };
|
|
16
|
+
LynxVibrationPlugin?: {
|
|
17
|
+
vibrate(requestId: string, durationMs: number): void;
|
|
18
|
+
cancel(requestId: string): void;
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Copies the Android resources kept beside the JS source into the sibling
|
|
4
|
+
* Gradle host. Edit <project>/android/*.xml, then run:
|
|
5
|
+
*
|
|
6
|
+
* bun run android:prepare
|
|
7
|
+
*/
|
|
8
|
+
import fs from "node:fs";
|
|
9
|
+
import path from "node:path";
|
|
10
|
+
import { fileURLToPath } from "node:url";
|
|
11
|
+
|
|
12
|
+
const projectRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
|
13
|
+
const sourceDir = path.join(projectRoot, "android");
|
|
14
|
+
const androidDir = path.resolve(projectRoot, "{{ANDROID_REL_DIR}}");
|
|
15
|
+
|
|
16
|
+
const files = [
|
|
17
|
+
["AndroidManifest.xml", ["app", "src", "main", "AndroidManifest.xml"]],
|
|
18
|
+
["strings.xml", ["app", "src", "main", "res", "values", "strings.xml"]],
|
|
19
|
+
["styles.xml", ["app", "src", "main", "res", "values", "styles.xml"]],
|
|
20
|
+
["config.xml", ["app", "src", "main", "res", "xml", "config.xml"]],
|
|
21
|
+
["network_security_config.xml", ["app", "src", "main", "res", "xml", "network_security_config.xml"]],
|
|
22
|
+
["file_paths.xml", ["app", "src", "main", "res", "xml", "file_paths.xml"]],
|
|
23
|
+
];
|
|
24
|
+
|
|
25
|
+
function fail(message) {
|
|
26
|
+
console.error(`\n ✖ ${message}\n`);
|
|
27
|
+
process.exit(1);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (!fs.existsSync(path.join(androidDir, "settings.gradle.kts"))) {
|
|
31
|
+
fail(`Can't find the Android host at ${androidDir}.`);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
for (const [name, destination] of files) {
|
|
35
|
+
const source = path.join(sourceDir, name);
|
|
36
|
+
if (!fs.existsSync(source)) {
|
|
37
|
+
fail(`Missing ${path.relative(projectRoot, source)}. Restore it before preparing the Android host.`);
|
|
38
|
+
}
|
|
39
|
+
const target = path.join(androidDir, ...destination);
|
|
40
|
+
fs.mkdirSync(path.dirname(target), { recursive: true });
|
|
41
|
+
fs.copyFileSync(source, target);
|
|
42
|
+
console.log(` → ${path.relative(projectRoot, target)}`);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const mapsConfig = path.join(sourceDir, "maps.json");
|
|
46
|
+
if (fs.existsSync(mapsConfig)) {
|
|
47
|
+
const mapsTarget = path.join(androidDir, "app", "src", "main", "assets", "lynx_maps", "config.json");
|
|
48
|
+
fs.mkdirSync(path.dirname(mapsTarget), { recursive: true });
|
|
49
|
+
fs.copyFileSync(mapsConfig, mapsTarget);
|
|
50
|
+
console.log(` → ${path.relative(projectRoot, mapsTarget)}`);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
console.log("\n ✔ Android resources prepared.\n");
|
|
@@ -98,6 +98,10 @@ dependencies {
|
|
|
98
98
|
// gesture then never works).
|
|
99
99
|
implementation("androidx.viewpager2:viewpager2:1.1.0")
|
|
100
100
|
|
|
101
|
+
// <create-mithril-lynx:android-connectors>
|
|
102
|
+
{{ANDROID_PLUGIN_DEPENDENCIES}}
|
|
103
|
+
// </create-mithril-lynx:android-connectors>
|
|
104
|
+
|
|
101
105
|
implementation("androidx.appcompat:appcompat:1.7.0")
|
|
102
106
|
implementation("androidx.core:core-splashscreen:1.0.1")
|
|
103
107
|
}
|
|
@@ -1,14 +1,20 @@
|
|
|
1
1
|
package {{PACKAGE_NAME}}
|
|
2
2
|
|
|
3
3
|
import android.os.Bundle
|
|
4
|
+
// <create-mithril-lynx:connector-imports>
|
|
5
|
+
// </create-mithril-lynx:connector-imports>
|
|
6
|
+
import androidx.activity.OnBackPressedCallback
|
|
4
7
|
import androidx.appcompat.app.AppCompatActivity
|
|
5
8
|
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
|
|
9
|
+
import com.lynx.react.bridge.JavaOnlyArray
|
|
6
10
|
import com.lynx.tasm.LynxBooleanOption
|
|
7
11
|
import com.lynx.tasm.LynxViewBuilder
|
|
8
12
|
import com.lynx.tasm.ThreadStrategyForRendering
|
|
9
13
|
import com.lynx.xelement.XElementBehaviors
|
|
10
14
|
|
|
11
15
|
class MainActivity : AppCompatActivity() {
|
|
16
|
+
private var backCallback: OnBackPressedCallback? = null
|
|
17
|
+
|
|
12
18
|
override fun onCreate(savedInstanceState: Bundle?) {
|
|
13
19
|
// Must run BEFORE super.onCreate() — a Splash Screen API requirement.
|
|
14
20
|
installSplashScreen()
|
|
@@ -20,6 +26,11 @@ class MainActivity : AppCompatActivity() {
|
|
|
20
26
|
// keyboard. Remove this line (and the xelement dependencies) if you
|
|
21
27
|
// don't use them.
|
|
22
28
|
builder.addBehaviors(XElementBehaviors().create())
|
|
29
|
+
// Lets mithril-lynx/route tell Android when its in-memory history can
|
|
30
|
+
// handle Back. See MithrilLynxNavigationModule and the callback below.
|
|
31
|
+
builder.registerModule("MithrilLynxNavigationModule", MithrilLynxNavigationModule::class.java)
|
|
32
|
+
// <create-mithril-lynx:connector-registration>
|
|
33
|
+
// </create-mithril-lynx:connector-registration>
|
|
23
34
|
// lynx-family/lynx's own explorer/android registers a
|
|
24
35
|
// GenericResourceFetcher unconditionally (LynxViewShellActivity —
|
|
25
36
|
// "used inside LynxEngine for resource loading capabilities of
|
|
@@ -37,9 +48,32 @@ class MainActivity : AppCompatActivity() {
|
|
|
37
48
|
val lynxView = builder.build(this)
|
|
38
49
|
setContentView(lynxView)
|
|
39
50
|
|
|
51
|
+
// Disabled until route.listenBackButton() reports that JS has an
|
|
52
|
+
// in-app history entry. When disabled, Android's normal Back behavior
|
|
53
|
+
// remains in charge and closes the activity from the first screen.
|
|
54
|
+
val callback = object : OnBackPressedCallback(false) {
|
|
55
|
+
override fun handleOnBackPressed() {
|
|
56
|
+
lynxView.sendGlobalEvent("mithrilLynx:back", JavaOnlyArray())
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
onBackPressedDispatcher.addCallback(this, callback)
|
|
60
|
+
backCallback = callback
|
|
61
|
+
|
|
40
62
|
// The bundle lives in app/src/main/assets/main-thread.bundle, copied
|
|
41
63
|
// there by `npm run android` (scripts/android.mjs) from the JS
|
|
42
64
|
// project's dist/. The name has to match exactly.
|
|
43
65
|
lynxView.renderTemplateUrl("main-thread.bundle", "")
|
|
44
66
|
}
|
|
67
|
+
|
|
68
|
+
fun setCanGoBack(canGoBack: Boolean) {
|
|
69
|
+
backCallback?.isEnabled = canGoBack
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// <create-mithril-lynx:connector-callbacks>
|
|
73
|
+
// </create-mithril-lynx:connector-callbacks>
|
|
74
|
+
|
|
75
|
+
override fun onDestroy() {
|
|
76
|
+
backCallback = null
|
|
77
|
+
super.onDestroy()
|
|
78
|
+
}
|
|
45
79
|
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
package {{PACKAGE_NAME}}
|
|
2
|
+
|
|
3
|
+
import android.content.Context
|
|
4
|
+
import android.os.Handler
|
|
5
|
+
import android.os.Looper
|
|
6
|
+
import com.lynx.jsbridge.LynxMethod
|
|
7
|
+
import com.lynx.jsbridge.LynxModule
|
|
8
|
+
import com.lynx.tasm.behavior.LynxContext
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* JS -> Android half of mithril-lynx/route's system Back integration.
|
|
12
|
+
*
|
|
13
|
+
* MainActivity supplies the Android -> JS half with sendGlobalEvent(). Keeping
|
|
14
|
+
* the callback disabled when the route history is empty preserves Android's
|
|
15
|
+
* default behavior on the app's first screen, including predictive Back.
|
|
16
|
+
*/
|
|
17
|
+
class MithrilLynxNavigationModule(context: Context) : LynxModule(context) {
|
|
18
|
+
private val activity = (context as? LynxContext)?.activity as? MainActivity
|
|
19
|
+
|
|
20
|
+
@LynxMethod
|
|
21
|
+
fun setCanGoBack(canGoBack: Boolean) {
|
|
22
|
+
// Lynx invokes native modules from its JS thread; Android's callback
|
|
23
|
+
// belongs to the main thread.
|
|
24
|
+
Handler(Looper.getMainLooper()).post {
|
|
25
|
+
activity?.setCanGoBack(canGoBack)
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="utf-8"?>
|
|
2
|
+
<!--
|
|
3
|
+
Project-owned Android configuration entry point. Android does not consume a
|
|
4
|
+
generic config.xml by itself; use it for XML metadata that a manifest entry
|
|
5
|
+
or a native integration needs, keeping that configuration beside the other
|
|
6
|
+
editable Android files in <project>/android/.
|
|
7
|
+
-->
|
|
8
|
+
<config />
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="utf-8"?>
|
|
2
|
+
<!--
|
|
3
|
+
Ready for an androidx.core.content.FileProvider declaration in
|
|
4
|
+
AndroidManifest.xml when the app needs to share a file with another app.
|
|
5
|
+
-->
|
|
6
|
+
<paths>
|
|
7
|
+
<files-path name="files" path="." />
|
|
8
|
+
<cache-path name="cache" path="." />
|
|
9
|
+
<external-files-path name="external_files" path="." />
|
|
10
|
+
</paths>
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="utf-8"?>
|
|
2
|
+
<!--
|
|
3
|
+
The generated manifest references this resource. It deliberately matches the
|
|
4
|
+
previous host default (cleartext allowed) so existing local HTTP workflows do
|
|
5
|
+
not change. Restrict this before a production release when possible.
|
|
6
|
+
-->
|
|
7
|
+
<network-security-config>
|
|
8
|
+
<base-config cleartextTrafficPermitted="true" />
|
|
9
|
+
</network-security-config>
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import m from "mithril-runtime";
|
|
2
2
|
|
|
3
3
|
// A reusable app bar: a title, plus a back affordance when `onBack` is
|
|
4
|
-
// given. No m.route.Link involved for
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
// route.back() explicitly.
|
|
4
|
+
// given. No m.route.Link involved for this on-screen affordance: it calls
|
|
5
|
+
// route.back() explicitly. The generated Android host's system Back bridge
|
|
6
|
+
// reaches the same history separately through route.listenBackButton().
|
|
8
7
|
export function AppBar(title: string, onBack?: () => void) {
|
|
9
8
|
return m("view", { class: "AppBar" }, [
|
|
10
9
|
onBack != null ? m("text", { class: "AppBar-back", ontap: onBack }, "←") : null,
|
|
@@ -21,12 +21,24 @@ route("/", {
|
|
|
21
21
|
"/detail": DetailHost,
|
|
22
22
|
});
|
|
23
23
|
|
|
24
|
+
// The host generated by --android forwards the system Back button through a
|
|
25
|
+
// Lynx global event. The module is optional so this template still runs in
|
|
26
|
+
// LynxExplorer, or when generated without its native Android host.
|
|
27
|
+
route.listenBackButton({
|
|
28
|
+
onCanGoBackChange: (canGoBack) =>
|
|
29
|
+
NativeModules.MithrilLynxNavigationModule?.setCanGoBack(canGoBack),
|
|
30
|
+
});
|
|
31
|
+
|
|
24
32
|
declare const module: {
|
|
25
33
|
hot?: {
|
|
26
34
|
accept(path: string, callback: () => void): void;
|
|
27
35
|
};
|
|
28
36
|
};
|
|
29
|
-
|
|
37
|
+
// Overloads: HMR reloads the two screen modules; DEV font registration
|
|
38
|
+
// prepended by --with-font/--find-font requires .ttf paths as string URLs.
|
|
39
|
+
declare function require(id: "./screens/home.js"): typeof homeModule;
|
|
40
|
+
declare function require(id: "./screens/detail.js"): typeof detailModule;
|
|
41
|
+
declare function require(id: string): string;
|
|
30
42
|
|
|
31
43
|
if (module.hot) {
|
|
32
44
|
module.hot.accept("./screens/home.js", () => {
|
|
File without changes
|