@blotch/angular-lynx-native 0.0.0-placeholder → 22.2.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 +39 -2
- package/dist/android/build.d.ts +28 -0
- package/dist/android/build.d.ts.map +1 -0
- package/dist/android/build.js +100 -0
- package/dist/android/project.d.ts +20 -0
- package/dist/android/project.d.ts.map +1 -0
- package/dist/android/project.js +84 -0
- package/dist/eject.d.ts +18 -0
- package/dist/eject.d.ts.map +1 -0
- package/dist/eject.js +68 -0
- package/dist/generate.d.ts +60 -0
- package/dist/generate.d.ts.map +1 -0
- package/dist/generate.js +111 -0
- package/dist/index.d.ts +41 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +72 -0
- package/dist/ios/build.d.ts +32 -0
- package/dist/ios/build.d.ts.map +1 -0
- package/dist/ios/build.js +149 -0
- package/dist/ios/frameworks.d.ts +31 -0
- package/dist/ios/frameworks.d.ts.map +1 -0
- package/dist/ios/frameworks.js +99 -0
- package/dist/ios/project.d.ts +11 -0
- package/dist/ios/project.d.ts.map +1 -0
- package/dist/ios/project.js +73 -0
- package/dist/options.d.ts +95 -0
- package/dist/options.d.ts.map +1 -0
- package/dist/options.js +112 -0
- package/dist/permissions.d.ts +17 -0
- package/dist/permissions.d.ts.map +1 -0
- package/dist/permissions.js +38 -0
- package/dist/png.d.ts +10 -0
- package/dist/png.d.ts.map +1 -0
- package/dist/png.js +24 -0
- package/dist/process.d.ts +17 -0
- package/dist/process.d.ts.map +1 -0
- package/dist/process.js +67 -0
- package/dist/xml.d.ts +10 -0
- package/dist/xml.d.ts.map +1 -0
- package/dist/xml.js +32 -0
- package/package.json +58 -3
- package/templates/android/README.md +42 -0
- package/templates/android/angular-lynx.properties +7 -0
- package/templates/android/app/build.gradle.kts +140 -0
- package/templates/android/app/proguard-rules.pro +65 -0
- package/templates/android/app/src/main/AndroidManifest.xml +36 -0
- package/templates/android/app/src/main/assets/.gitkeep +0 -0
- package/templates/android/app/src/main/java/dev/angularlynx/shell/BundleAssets.kt +45 -0
- package/templates/android/app/src/main/java/dev/angularlynx/shell/MainActivity.kt +96 -0
- package/templates/android/app/src/main/java/dev/angularlynx/shell/ResourceFetchers.kt +115 -0
- package/templates/android/app/src/main/java/dev/angularlynx/shell/ShellApplication.kt +39 -0
- package/templates/android/app/src/main/java/dev/angularlynx/shell/ShellModule.kt +40 -0
- package/templates/android/app/src/main/res/drawable/ic_launcher_background.xml +4 -0
- package/templates/android/app/src/main/res/drawable/ic_launcher_foreground.xml +10 -0
- package/templates/android/app/src/main/res/mipmap/ic_launcher.xml +6 -0
- package/templates/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml +6 -0
- package/templates/android/app/src/main/res/values/themes.xml +13 -0
- package/templates/android/app/src/main/res/values-night/themes.xml +8 -0
- package/templates/android/build.gradle.kts +5 -0
- package/templates/android/gradle/wrapper/gradle-wrapper.jar +0 -0
- package/templates/android/gradle/wrapper/gradle-wrapper.properties +10 -0
- package/templates/android/gradle.properties +7 -0
- package/templates/android/gradlew +248 -0
- package/templates/android/gradlew.bat +82 -0
- package/templates/android/settings.gradle.kts +28 -0
- package/templates/ios/App/AppDelegate.swift +51 -0
- package/templates/ios/App/Assets.xcassets/AppIcon.appiconset/AppIcon.png +0 -0
- package/templates/ios/App/Assets.xcassets/AppIcon.appiconset/Contents.json +14 -0
- package/templates/ios/App/Assets.xcassets/Contents.json +6 -0
- package/templates/ios/App/Assets.xcassets/LaunchBackground.colorset/Contents.json +20 -0
- package/templates/ios/App/BundleResourceFetcher.swift +136 -0
- package/templates/ios/App/Info.plist +48 -0
- package/templates/ios/App/LynxViewController.swift +68 -0
- package/templates/ios/App.xcodeproj/project.pbxproj +328 -0
- package/templates/ios/App.xcodeproj/project.xcworkspace/contents.xcworkspacedata +7 -0
- package/templates/ios/App.xcodeproj/xcshareddata/xcschemes/App.xcscheme +76 -0
- package/templates/ios/Config/App.xcconfig +9 -0
- package/templates/ios/LynxBundle/.gitkeep +0 -0
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# Lynx's keep rules, from Lynx's integration guide
|
|
2
|
+
# (https://lynxjs.org/guide/start/integrate-with-existing-apps). Only used once
|
|
3
|
+
# isMinifyEnabled is turned on in build.gradle.kts.
|
|
4
|
+
# LYNX START
|
|
5
|
+
# use @Keep to annotate retained classes.
|
|
6
|
+
-dontwarn android.support.annotation.Keep
|
|
7
|
+
-keep @android.support.annotation.Keep class **
|
|
8
|
+
-keep @android.support.annotation.Keep class ** {
|
|
9
|
+
@android.support.annotation.Keep <fields>;
|
|
10
|
+
@android.support.annotation.Keep <methods>;
|
|
11
|
+
}
|
|
12
|
+
-dontwarn androidx.annotation.Keep
|
|
13
|
+
-keep @androidx.annotation.Keep class **
|
|
14
|
+
-keep @androidx.annotation.Keep class ** {
|
|
15
|
+
@androidx.annotation.Keep <fields>;
|
|
16
|
+
@androidx.annotation.Keep <methods>;
|
|
17
|
+
}
|
|
18
|
+
# native method call
|
|
19
|
+
-keepclasseswithmembers,includedescriptorclasses class * {
|
|
20
|
+
native <methods>;
|
|
21
|
+
}
|
|
22
|
+
-keepclasseswithmembers class * {
|
|
23
|
+
@com.lynx.tasm.base.CalledByNative <methods>;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
# to customize a module, you need to keep the class name and the method annotated as LynxMethod.
|
|
27
|
+
-keepclasseswithmembers class * {
|
|
28
|
+
@com.lynx.jsbridge.LynxMethod <methods>;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
-keepclassmembers class * {
|
|
32
|
+
@com.lynx.tasm.behavior.LynxProp <methods>;
|
|
33
|
+
@com.lynx.tasm.behavior.LynxPropGroup <methods>;
|
|
34
|
+
@com.lynx.tasm.behavior.LynxUIMethod <methods>;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
-keepclassmembers class com.lynx.tasm.behavior.ui.UIGroup {
|
|
38
|
+
public boolean needCustomLayout();
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
# in case R8 compiler may remove mLoader in bytecode.
|
|
42
|
+
# as mLoader is not used in java and passed as a WeakRef in JNI.
|
|
43
|
+
-keepclassmembers class com.lynx.tasm.LynxTemplateRender {
|
|
44
|
+
private com.lynx.tasm.core.resource.LynxResourceLoader mLoader;
|
|
45
|
+
private com.lynx.tasm.core.resource.LynxResourceLoader mResourceLoader;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
# the automatically generated setter classes use the class names of LynxBaseUI and ShadowNode and their subclasses.
|
|
49
|
+
-keep class com.lynx.tasm.behavior.ui.LynxBaseUI
|
|
50
|
+
-keep class com.lynx.tasm.behavior.shadow.ShadowNode
|
|
51
|
+
-keep class com.lynx.jsbridge.LynxModule { *; }
|
|
52
|
+
-keep class * extends com.lynx.tasm.behavior.ui.LynxBaseUI
|
|
53
|
+
-keep class * extends com.lynx.tasm.behavior.shadow.ShadowNode
|
|
54
|
+
-keep class * extends com.lynx.jsbridge.LynxModule { *; }
|
|
55
|
+
-keep class * extends com.lynx.jsbridge.LynxContextModule
|
|
56
|
+
-keep class * implements com.lynx.tasm.behavior.utils.Settable
|
|
57
|
+
-keep class * implements com.lynx.tasm.behavior.utils.LynxUISetter
|
|
58
|
+
-keep class * implements com.lynx.tasm.behavior.utils.LynxUIMethodInvoker
|
|
59
|
+
-keep class com.lynx.tasm.rendernode.compat.**{
|
|
60
|
+
*;
|
|
61
|
+
}
|
|
62
|
+
-keep class com.lynx.tasm.rendernode.compat.RenderNodeFactory{
|
|
63
|
+
*;
|
|
64
|
+
}
|
|
65
|
+
# LYNX END
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="utf-8"?>
|
|
2
|
+
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
|
3
|
+
|
|
4
|
+
<!-- fetch() and XMLHttpRequest go through Lynx's HTTP service. -->
|
|
5
|
+
<uses-permission android:name="android.permission.INTERNET" />
|
|
6
|
+
<!-- angular-lynx:permissions -->
|
|
7
|
+
|
|
8
|
+
<application
|
|
9
|
+
android:name=".ShellApplication"
|
|
10
|
+
android:allowBackup="true"
|
|
11
|
+
android:icon="@mipmap/ic_launcher"
|
|
12
|
+
android:label="@string/app_name"
|
|
13
|
+
android:supportsRtl="true"
|
|
14
|
+
android:theme="@style/Theme.AngularLynxShell">
|
|
15
|
+
<!--
|
|
16
|
+
One activity holding one LynxView; Angular Router does all navigation
|
|
17
|
+
inside it. configChanges keeps the activity (and the running Angular
|
|
18
|
+
app) alive on rotation, resizing and dark-mode switches instead of
|
|
19
|
+
restarting it; the LynxView re-lays itself out.
|
|
20
|
+
-->
|
|
21
|
+
<activity
|
|
22
|
+
android:name=".MainActivity"
|
|
23
|
+
android:configChanges="orientation|screenSize|screenLayout|smallestScreenSize|keyboard|keyboardHidden|navigation|uiMode"
|
|
24
|
+
android:exported="true"
|
|
25
|
+
android:launchMode="singleTask"
|
|
26
|
+
android:screenOrientation="${screenOrientation}"
|
|
27
|
+
android:windowSoftInputMode="adjustResize">
|
|
28
|
+
<intent-filter>
|
|
29
|
+
<action android:name="android.intent.action.MAIN" />
|
|
30
|
+
<category android:name="android.intent.category.LAUNCHER" />
|
|
31
|
+
</intent-filter>
|
|
32
|
+
<!-- angular-lynx:deep-links -->
|
|
33
|
+
</activity>
|
|
34
|
+
</application>
|
|
35
|
+
|
|
36
|
+
</manifest>
|
|
File without changes
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
package dev.angularlynx.shell
|
|
2
|
+
|
|
3
|
+
import android.content.Context
|
|
4
|
+
import android.net.Uri
|
|
5
|
+
import java.io.IOException
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* The app's dist/ folder is copied into the APK's assets/ (by the AngularLynx
|
|
9
|
+
* generator), so every bundle and static file the Angular app references is an
|
|
10
|
+
* asset. These helpers turn the URLs Lynx hands us into asset paths.
|
|
11
|
+
*/
|
|
12
|
+
object BundleAssets {
|
|
13
|
+
/** The bundle the activity renders. */
|
|
14
|
+
const val MAIN_BUNDLE = "main.lynx.bundle"
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Maps a URL from the bundle to a path inside assets/, or null when it
|
|
18
|
+
* points somewhere else (http(s), data:, content:...).
|
|
19
|
+
*
|
|
20
|
+
* Rspeedy's default asset prefix is "/", so bundled files arrive as
|
|
21
|
+
* "/static/image/x.png" or, for lazy bundles, "/async/x.lynx.bundle".
|
|
22
|
+
* "asset:///" (Fresco's scheme) and plain relative paths also work, so
|
|
23
|
+
* the same bundle runs whatever prefix it was built with.
|
|
24
|
+
*/
|
|
25
|
+
fun assetPath(url: String): String? {
|
|
26
|
+
val path = when {
|
|
27
|
+
url.startsWith("asset:///") -> url.removePrefix("asset:///")
|
|
28
|
+
url.startsWith("file:///android_asset/") -> url.removePrefix("file:///android_asset/")
|
|
29
|
+
Uri.parse(url).scheme == null -> url.removePrefix("./").trimStart('/')
|
|
30
|
+
else -> return null
|
|
31
|
+
}
|
|
32
|
+
// Drop the query and fragment; assets are plain files.
|
|
33
|
+
return path.substringBefore('?').substringBefore('#').ifEmpty { null }
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
fun read(context: Context, path: String): ByteArray =
|
|
37
|
+
context.assets.open(path).use { it.readBytes() }
|
|
38
|
+
|
|
39
|
+
fun exists(context: Context, path: String): Boolean = try {
|
|
40
|
+
context.assets.open(path).close()
|
|
41
|
+
true
|
|
42
|
+
} catch (_: IOException) {
|
|
43
|
+
false
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
package dev.angularlynx.shell
|
|
2
|
+
|
|
3
|
+
import android.os.Bundle
|
|
4
|
+
import android.util.Log
|
|
5
|
+
import android.widget.FrameLayout
|
|
6
|
+
import androidx.activity.ComponentActivity
|
|
7
|
+
import androidx.activity.OnBackPressedCallback
|
|
8
|
+
import androidx.activity.enableEdgeToEdge
|
|
9
|
+
import androidx.core.view.ViewCompat
|
|
10
|
+
import androidx.core.view.WindowInsetsCompat
|
|
11
|
+
import com.lynx.react.bridge.JavaOnlyArray
|
|
12
|
+
import com.lynx.tasm.LynxBooleanOption
|
|
13
|
+
import com.lynx.tasm.LynxError
|
|
14
|
+
import com.lynx.tasm.LynxView
|
|
15
|
+
import com.lynx.tasm.LynxViewBuilder
|
|
16
|
+
import com.lynx.tasm.LynxViewClient
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* The whole app: one full-screen LynxView running the Angular app. Angular
|
|
20
|
+
* Router navigates inside it, so there is no native navigation stack.
|
|
21
|
+
*/
|
|
22
|
+
class MainActivity : ComponentActivity(), ShellModule.Host {
|
|
23
|
+
private lateinit var lynxView: LynxView
|
|
24
|
+
|
|
25
|
+
// Disabled until the Angular app enables it (see ShellModule), so by
|
|
26
|
+
// default back leaves the app like any other root screen.
|
|
27
|
+
private val backHandler = object : OnBackPressedCallback(false) {
|
|
28
|
+
override fun handleOnBackPressed() {
|
|
29
|
+
lynxView.sendGlobalEvent(ShellModule.BACK_EVENT, JavaOnlyArray())
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
override fun onCreate(savedInstanceState: Bundle?) {
|
|
34
|
+
// Android 15+ always draws apps edge to edge; opting in on older
|
|
35
|
+
// versions too keeps the layout the same everywhere.
|
|
36
|
+
enableEdgeToEdge()
|
|
37
|
+
super.onCreate(savedInstanceState)
|
|
38
|
+
|
|
39
|
+
lynxView = LynxViewBuilder().apply {
|
|
40
|
+
setTemplateProvider(AssetTemplateProvider(this@MainActivity))
|
|
41
|
+
setTemplateResourceFetcher(AssetTemplateResourceFetcher(this@MainActivity))
|
|
42
|
+
setMediaResourceFetcher(AssetMediaResourceFetcher())
|
|
43
|
+
setEnableGenericResourceFetcher(LynxBooleanOption.TRUE)
|
|
44
|
+
setGenericResourceFetcher(AssetGenericResourceFetcher(this@MainActivity))
|
|
45
|
+
registerModule(ShellModule.NAME, ShellModule::class.java, this@MainActivity)
|
|
46
|
+
}.build(this)
|
|
47
|
+
lynxView.addLynxViewClient(object : LynxViewClient() {
|
|
48
|
+
override fun onReceivedError(error: LynxError?) {
|
|
49
|
+
Log.e(TAG, "Lynx error: ${error?.msg}")
|
|
50
|
+
}
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
// Keep the app inside the safe area (status bar, navigation bar and
|
|
54
|
+
// camera cutout) until the runtime exposes the insets to CSS.
|
|
55
|
+
val container = FrameLayout(this)
|
|
56
|
+
container.addView(lynxView, FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT)
|
|
57
|
+
ViewCompat.setOnApplyWindowInsetsListener(container) { view, insets ->
|
|
58
|
+
val safe = insets.getInsets(
|
|
59
|
+
WindowInsetsCompat.Type.systemBars() or
|
|
60
|
+
WindowInsetsCompat.Type.displayCutout() or
|
|
61
|
+
WindowInsetsCompat.Type.ime(),
|
|
62
|
+
)
|
|
63
|
+
view.setPadding(safe.left, safe.top, safe.right, safe.bottom)
|
|
64
|
+
WindowInsetsCompat.CONSUMED
|
|
65
|
+
}
|
|
66
|
+
setContentView(container)
|
|
67
|
+
|
|
68
|
+
onBackPressedDispatcher.addCallback(this, backHandler)
|
|
69
|
+
lynxView.renderTemplateUrl(BundleAssets.MAIN_BUNDLE, "")
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
override fun setBackHandlerEnabled(enabled: Boolean) {
|
|
73
|
+
backHandler.isEnabled = enabled
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Lynx pauses timers and animations in the background and fires the
|
|
77
|
+
// page's visibility events from these.
|
|
78
|
+
override fun onResume() {
|
|
79
|
+
super.onResume()
|
|
80
|
+
lynxView.onEnterForeground()
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
override fun onPause() {
|
|
84
|
+
lynxView.onEnterBackground()
|
|
85
|
+
super.onPause()
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
override fun onDestroy() {
|
|
89
|
+
lynxView.destroy()
|
|
90
|
+
super.onDestroy()
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
private companion object {
|
|
94
|
+
const val TAG = "AngularLynxShell"
|
|
95
|
+
}
|
|
96
|
+
}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
package dev.angularlynx.shell
|
|
2
|
+
|
|
3
|
+
import android.content.Context
|
|
4
|
+
import com.lynx.tasm.core.LynxThreadPool
|
|
5
|
+
import com.lynx.tasm.provider.AbsTemplateProvider
|
|
6
|
+
import com.lynx.tasm.resourceprovider.LynxResourceCallback
|
|
7
|
+
import com.lynx.tasm.resourceprovider.LynxResourceRequest
|
|
8
|
+
import com.lynx.tasm.resourceprovider.LynxResourceResponse
|
|
9
|
+
import com.lynx.tasm.resourceprovider.generic.LynxGenericResourceFetcher
|
|
10
|
+
import com.lynx.tasm.resourceprovider.media.LynxMediaResourceFetcher
|
|
11
|
+
import com.lynx.tasm.resourceprovider.template.LynxTemplateResourceFetcher
|
|
12
|
+
import com.lynx.tasm.resourceprovider.template.TemplateProviderResult
|
|
13
|
+
import okhttp3.OkHttpClient
|
|
14
|
+
import okhttp3.Request
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Lynx can't load anything by itself: the host app supplies loaders for
|
|
18
|
+
* bundles, images and other resources. Ours read the copy of dist/ inside the
|
|
19
|
+
* APK and fall back to HTTP for absolute http(s) URLs (an API, a CDN, or a dev
|
|
20
|
+
* server).
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
private val http by lazy { OkHttpClient() }
|
|
24
|
+
|
|
25
|
+
// LynxResourceResponse.onFailed is declared without a type parameter, so
|
|
26
|
+
// Kotlin sees a raw type; this gives it the one each callback expects.
|
|
27
|
+
@Suppress("UNCHECKED_CAST")
|
|
28
|
+
private fun <T> failed(error: Throwable): LynxResourceResponse<T> =
|
|
29
|
+
LynxResourceResponse.onFailed(error) as LynxResourceResponse<T>
|
|
30
|
+
|
|
31
|
+
/** Reads [url] from assets/ or over HTTP, off the UI thread. */
|
|
32
|
+
private fun load(
|
|
33
|
+
context: Context,
|
|
34
|
+
url: String,
|
|
35
|
+
onSuccess: (ByteArray) -> Unit,
|
|
36
|
+
onFailure: (Throwable) -> Unit,
|
|
37
|
+
) {
|
|
38
|
+
LynxThreadPool.getBriefIOExecutor().execute {
|
|
39
|
+
try {
|
|
40
|
+
val asset = BundleAssets.assetPath(url)
|
|
41
|
+
val bytes = if (asset != null) {
|
|
42
|
+
BundleAssets.read(context, asset)
|
|
43
|
+
} else {
|
|
44
|
+
http.newCall(Request.Builder().url(url).build()).execute().use { response ->
|
|
45
|
+
check(response.isSuccessful) { "HTTP ${response.code} for $url" }
|
|
46
|
+
response.body!!.bytes()
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
onSuccess(bytes)
|
|
50
|
+
} catch (error: Throwable) {
|
|
51
|
+
onFailure(Exception("Couldn't load $url: ${error.message}", error))
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** Loads the bundle passed to LynxView.renderTemplateUrl (the main bundle). */
|
|
57
|
+
class AssetTemplateProvider(context: Context) : AbsTemplateProvider() {
|
|
58
|
+
private val context = context.applicationContext
|
|
59
|
+
|
|
60
|
+
override fun loadTemplate(url: String, callback: Callback) =
|
|
61
|
+
load(context, url, callback::onSuccess) { callback.onFailed(it.message) }
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Loads every other bundle: lazy route chunks and dynamic components, which
|
|
66
|
+
* the main bundle requests by URL at runtime.
|
|
67
|
+
*/
|
|
68
|
+
class AssetTemplateResourceFetcher(context: Context) : LynxTemplateResourceFetcher() {
|
|
69
|
+
private val context = context.applicationContext
|
|
70
|
+
|
|
71
|
+
override fun fetchTemplate(
|
|
72
|
+
request: LynxResourceRequest,
|
|
73
|
+
callback: LynxResourceCallback<TemplateProviderResult>,
|
|
74
|
+
) = load(
|
|
75
|
+
context,
|
|
76
|
+
request.url,
|
|
77
|
+
{ callback.onResponse(LynxResourceResponse.onSuccess(TemplateProviderResult.fromBinary(it))) },
|
|
78
|
+
{ callback.onResponse(failed(it)) },
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
// AngularLynx doesn't use Lynx's server-side rendering data.
|
|
82
|
+
override fun fetchSSRData(request: LynxResourceRequest, callback: LynxResourceCallback<ByteArray>) =
|
|
83
|
+
callback.onResponse(failed(UnsupportedOperationException("No SSR data")))
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Images: rather than loading them, Lynx asks where they live and hands the
|
|
88
|
+
* answer to Fresco. Bundled images become asset:/// URIs, which Fresco reads
|
|
89
|
+
* from the APK; anything else (https:, data:) passes through unchanged.
|
|
90
|
+
*/
|
|
91
|
+
class AssetMediaResourceFetcher : LynxMediaResourceFetcher() {
|
|
92
|
+
override fun shouldRedirectUrl(request: LynxResourceRequest): String {
|
|
93
|
+
val asset = BundleAssets.assetPath(request.url) ?: return request.url
|
|
94
|
+
return "asset:///$asset"
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/** Fonts, Lottie files and other resources the bundle loads by URL. */
|
|
99
|
+
class AssetGenericResourceFetcher(context: Context) : LynxGenericResourceFetcher() {
|
|
100
|
+
private val context = context.applicationContext
|
|
101
|
+
|
|
102
|
+
override fun fetchResource(request: LynxResourceRequest, callback: LynxResourceCallback<ByteArray>) =
|
|
103
|
+
load(
|
|
104
|
+
context,
|
|
105
|
+
request.url,
|
|
106
|
+
{ callback.onResponse(LynxResourceResponse.onSuccess(it)) },
|
|
107
|
+
{ callback.onResponse(failed(it)) },
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
// Lynx asks for a file path only for resources it hands to native code
|
|
111
|
+
// that reads files; assets inside an APK have no path, so it falls back
|
|
112
|
+
// to fetchResource.
|
|
113
|
+
override fun fetchResourcePath(request: LynxResourceRequest, callback: LynxResourceCallback<String>) =
|
|
114
|
+
callback.onResponse(failed(UnsupportedOperationException("No file paths for assets")))
|
|
115
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
package dev.angularlynx.shell
|
|
2
|
+
|
|
3
|
+
import android.app.Application
|
|
4
|
+
import com.facebook.drawee.backends.pipeline.Fresco
|
|
5
|
+
import com.facebook.imagepipeline.core.ImagePipelineConfig
|
|
6
|
+
import com.facebook.imagepipeline.memory.PoolConfig
|
|
7
|
+
import com.facebook.imagepipeline.memory.PoolFactory
|
|
8
|
+
import com.lynx.service.http.LynxHttpService
|
|
9
|
+
import com.lynx.service.image.LynxImageService
|
|
10
|
+
import com.lynx.service.log.LynxLogService
|
|
11
|
+
import com.lynx.tasm.LynxEnv
|
|
12
|
+
import com.lynx.tasm.service.LynxServiceCenter
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Starts Lynx once per process. Lynx requires its services and LynxEnv to be
|
|
16
|
+
* set up before the first LynxView is built, and Application.onCreate is the
|
|
17
|
+
* one place guaranteed to run first.
|
|
18
|
+
*/
|
|
19
|
+
class ShellApplication : Application() {
|
|
20
|
+
override fun onCreate() {
|
|
21
|
+
super.onCreate()
|
|
22
|
+
|
|
23
|
+
// LynxImageService draws <image> through Fresco, which must be
|
|
24
|
+
// initialized first. Fresco also understands asset:/// URIs natively,
|
|
25
|
+
// which is how bundled images are loaded (see AssetMediaResourceFetcher).
|
|
26
|
+
val pools = PoolFactory(PoolConfig.newBuilder().build())
|
|
27
|
+
Fresco.initialize(
|
|
28
|
+
this,
|
|
29
|
+
ImagePipelineConfig.newBuilder(this).setPoolFactory(pools).build(),
|
|
30
|
+
)
|
|
31
|
+
LynxServiceCenter.inst().registerService(LynxImageService.getInstance())
|
|
32
|
+
LynxServiceCenter.inst().registerService(LynxLogService)
|
|
33
|
+
LynxServiceCenter.inst().registerService(LynxHttpService)
|
|
34
|
+
|
|
35
|
+
// No global template provider or custom elements: MainActivity passes
|
|
36
|
+
// its loaders to the LynxView it builds.
|
|
37
|
+
LynxEnv.inst().init(this, null, null, null)
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
package dev.angularlynx.shell
|
|
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
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Native module the AngularLynx runtime calls as
|
|
11
|
+
* `NativeModules.AngularLynxShell` (from the background thread, where
|
|
12
|
+
* NativeModules lives).
|
|
13
|
+
*
|
|
14
|
+
* The system back gesture is synchronous: Android needs to know before the
|
|
15
|
+
* gesture starts whether the app will handle it (predictive back shows a
|
|
16
|
+
* different animation either way). The Angular app can't answer in time, so it
|
|
17
|
+
* tells us ahead of time instead: it enables the back handler while the Router
|
|
18
|
+
* has somewhere to go back to. While enabled, back presses become an
|
|
19
|
+
* `angularLynxBack` global event; while disabled (the default, and whenever the
|
|
20
|
+
* runtime doesn't know about this module) Android's default back behavior
|
|
21
|
+
* applies, which leaves the app.
|
|
22
|
+
*/
|
|
23
|
+
class ShellModule(context: Context, param: Any?) : LynxModule(context, param) {
|
|
24
|
+
private val host = param as Host
|
|
25
|
+
private val main = Handler(Looper.getMainLooper())
|
|
26
|
+
|
|
27
|
+
interface Host {
|
|
28
|
+
fun setBackHandlerEnabled(enabled: Boolean)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
@LynxMethod
|
|
32
|
+
fun setBackHandlerEnabled(enabled: Boolean) {
|
|
33
|
+
main.post { host.setBackHandlerEnabled(enabled) }
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
companion object {
|
|
37
|
+
const val NAME = "AngularLynxShell"
|
|
38
|
+
const val BACK_EVENT = "angularLynxBack"
|
|
39
|
+
}
|
|
40
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="utf-8"?>
|
|
2
|
+
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
|
3
|
+
android:width="108dp"
|
|
4
|
+
android:height="108dp"
|
|
5
|
+
android:viewportWidth="108"
|
|
6
|
+
android:viewportHeight="108">
|
|
7
|
+
<path
|
|
8
|
+
android:fillColor="#FFFFFF"
|
|
9
|
+
android:pathData="M54,30 L76,78 L68,78 L63,66 L45,66 L40,78 L32,78 Z M48,59 L60,59 L54,44 Z" />
|
|
10
|
+
</vector>
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="utf-8"?>
|
|
2
|
+
<!-- Android 7 (API 24-25) has no adaptive icons, so it gets the two layers flattened. -->
|
|
3
|
+
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
|
4
|
+
<item android:drawable="@drawable/ic_launcher_background" />
|
|
5
|
+
<item android:drawable="@drawable/ic_launcher_foreground" />
|
|
6
|
+
</layer-list>
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="utf-8"?>
|
|
2
|
+
<!-- Placeholder launcher icon; the generator replaces it with the app's icon. -->
|
|
3
|
+
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
|
4
|
+
<background android:drawable="@drawable/ic_launcher_background" />
|
|
5
|
+
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
|
6
|
+
</adaptive-icon>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="utf-8"?>
|
|
2
|
+
<resources>
|
|
3
|
+
<!--
|
|
4
|
+
No action bar: the Angular app draws the whole screen. The window
|
|
5
|
+
background shows before the first Lynx frame, so it matches the default
|
|
6
|
+
light/dark page background instead of flashing.
|
|
7
|
+
-->
|
|
8
|
+
<style name="Theme.AngularLynxShell" parent="android:Theme.Material.Light.NoActionBar">
|
|
9
|
+
<item name="android:windowBackground">@android:color/white</item>
|
|
10
|
+
<item name="android:windowLightStatusBar">true</item>
|
|
11
|
+
<item name="android:windowLightNavigationBar">true</item>
|
|
12
|
+
</style>
|
|
13
|
+
</resources>
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="utf-8"?>
|
|
2
|
+
<resources>
|
|
3
|
+
<style name="Theme.AngularLynxShell" parent="android:Theme.Material.NoActionBar">
|
|
4
|
+
<item name="android:windowBackground">@android:color/black</item>
|
|
5
|
+
<item name="android:windowLightStatusBar">false</item>
|
|
6
|
+
<item name="android:windowLightNavigationBar">false</item>
|
|
7
|
+
</style>
|
|
8
|
+
</resources>
|
|
Binary file
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
distributionBase=GRADLE_USER_HOME
|
|
2
|
+
distributionPath=wrapper/dists
|
|
3
|
+
distributionSha256Sum=9c0f7faeeb306cb14e4279a3e084ca6b596894089a0638e68a07c945a32c9e14
|
|
4
|
+
distributionUrl=https\://services.gradle.org/distributions/gradle-9.6.1-bin.zip
|
|
5
|
+
networkTimeout=10000
|
|
6
|
+
retries=0
|
|
7
|
+
retryBackOffMs=500
|
|
8
|
+
validateDistributionUrl=true
|
|
9
|
+
zipStoreBase=GRADLE_USER_HOME
|
|
10
|
+
zipStorePath=wrapper/dists
|