craft-native 0.0.172 → 0.0.174
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/dist/android/src/index.d.ts +2 -0
- package/dist/android/src/index.js +4 -1
- package/dist/android/templates/AndroidManifest.xml.template +2 -0
- package/dist/android/templates/CraftNativeShell.kt.template +50 -0
- package/dist/android/templates/MainActivityNative.kt.template +441 -27
- package/dist/android/templates/build.gradle.kts.app.template +3 -0
- package/dist/cli.js +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.js +1 -1
- package/dist/ios/src/index.d.ts +2 -0
- package/dist/ios/src/index.js +1 -0
- package/dist/ios/templates/CraftApp.swift +11 -0
- package/dist/ios/templates/CraftNativeActions.swift +174 -0
- package/dist/ios/templates/CraftNativeFlatList.swift +1 -1
- package/dist/ios/templates/CraftNativeScreen.swift +443 -38
- package/dist/macos/src/index.d.ts +16 -0
- package/dist/macos/src/index.js +75 -0
- package/dist/macos/templates/CraftMacApplication.swift +342 -0
- package/dist/macos/templates/CraftMacRenderer.swift +481 -0
- package/dist/macos/templates/CraftMacRuntime.swift +266 -0
- package/dist/macos/templates/CraftNativeMutation.swift +390 -0
- package/dist/macos/templates/native_layout.zig +1651 -0
- package/package.json +9 -1
|
@@ -52,6 +52,8 @@ export interface CraftAndroidConfig {
|
|
|
52
52
|
trustedOrigins?: string[];
|
|
53
53
|
appIconPath?: string;
|
|
54
54
|
googleServicesFile?: string;
|
|
55
|
+
/** Google Maps Android SDK key used by native `Map` screens. */
|
|
56
|
+
mapsApiKey?: string;
|
|
55
57
|
devServerURL?: string;
|
|
56
58
|
hasBundledFallback?: boolean;
|
|
57
59
|
/** Android API level, at least ANDROID_MIN_SDK (26). */
|
|
@@ -271,6 +271,9 @@ function validateAndroidConfig(config) {
|
|
|
271
271
|
if (config.backgroundColorDark !== undefined && !config.backgroundColorDark.match(/^#[\dA-F]{3,4}$|^#[\dA-F]{6}$|^#[\dA-F]{8}$/i)) {
|
|
272
272
|
throw new Error(`Invalid Android dark background color: ${config.backgroundColorDark}`);
|
|
273
273
|
}
|
|
274
|
+
if (config.mapsApiKey !== undefined && !config.mapsApiKey.trim()) {
|
|
275
|
+
throw new Error("Android mapsApiKey must not be empty");
|
|
276
|
+
}
|
|
274
277
|
for (const [name, value] of [
|
|
275
278
|
["versionCode", config.versionCode],
|
|
276
279
|
["minSdk", config.minSdk],
|
|
@@ -570,7 +573,7 @@ async function init(options) {
|
|
|
570
573
|
const healthTemplate = readFileSync(join(TEMPLATES_DIR, config.enableHealthConnect ? "CraftHealthConnect.kt.template" : "CraftHealthConnectStub.kt.template"), "utf-8");
|
|
571
574
|
writeFileSync(join(output, "app/src/main/java", packagePath, "CraftHealthConnect.kt"), healthTemplate.replace(/\{\{PACKAGE_NAME\}\}/g, finalPackageName));
|
|
572
575
|
const manifestTemplate = readFileSync(join(TEMPLATES_DIR, "AndroidManifest.xml.template"), "utf-8");
|
|
573
|
-
const manifest = manifestTemplate.replace(/\{\{PACKAGE_NAME\}\}/g, finalPackageName).replace(/\{\{APP_NAME\}\}/g, name).replace(/\{\{PERMISSIONS\}\}/g, renderAndroidPermissions(config)).replace(/\{\{USES_CLEARTEXT\}\}/g, config.devServerURL?.startsWith("http://") || config.enableLocalNetwork ? "true" : "false").replace(/\{\{DEEP_LINK_INTENT_FILTERS\}\}/g, renderAndroidDeepLinks(config)).replace(/\{\{BACKGROUND_SERVICE\}\}/g, config.enableBackgroundLocation ? ' <service android:name="com.craft.runtime.LocationRecordingService" android:exported="false" android:foregroundServiceType="location" android:stopWithTask="false" />' : "").replace(/\{\{NATIVE_NOTIFICATION_RECEIVER\}\}/g, config.renderer === "native" ? renderNativeShellManifest(config) : "").replace(/\{\{HEALTH_CONNECT_QUERIES\}\}/g, config.enableHealthConnect ? ` <queries>
|
|
576
|
+
const manifest = manifestTemplate.replace(/\{\{PACKAGE_NAME\}\}/g, finalPackageName).replace(/\{\{APP_NAME\}\}/g, name).replace(/\{\{PERMISSIONS\}\}/g, renderAndroidPermissions(config)).replace(/\{\{USES_CLEARTEXT\}\}/g, config.devServerURL?.startsWith("http://") || config.enableLocalNetwork ? "true" : "false").replace(/\{\{DEEP_LINK_INTENT_FILTERS\}\}/g, renderAndroidDeepLinks(config)).replace(/\{\{BACKGROUND_SERVICE\}\}/g, config.enableBackgroundLocation ? ' <service android:name="com.craft.runtime.LocationRecordingService" android:exported="false" android:foregroundServiceType="location" android:stopWithTask="false" />' : "").replace(/\{\{NATIVE_NOTIFICATION_RECEIVER\}\}/g, config.renderer === "native" ? renderNativeShellManifest(config) : "").replace(/\{\{GOOGLE_MAPS_API_KEY\}\}/g, config.mapsApiKey ? ` <meta-data android:name="com.google.android.geo.API_KEY" android:value="${escapeXml(config.mapsApiKey)}" />` : "").replace(/\{\{HEALTH_CONNECT_QUERIES\}\}/g, config.enableHealthConnect ? ` <queries>
|
|
574
577
|
<package android:name="com.google.android.apps.healthdata" />
|
|
575
578
|
</queries>` : "").replace(/\{\{HEALTH_CONNECT_RATIONALE\}\}/g, config.enableHealthConnect ? ` <intent-filter>
|
|
576
579
|
<action android:name="androidx.health.ACTION_SHOW_PERMISSIONS_RATIONALE" />
|
|
@@ -13,8 +13,10 @@ import android.text.TextUtils
|
|
|
13
13
|
import android.util.Log
|
|
14
14
|
import android.util.TypedValue
|
|
15
15
|
import android.view.Gravity
|
|
16
|
+
import android.view.MotionEvent
|
|
16
17
|
import android.view.View
|
|
17
18
|
import android.view.ViewGroup
|
|
19
|
+
import android.view.ViewConfiguration
|
|
18
20
|
import android.widget.FrameLayout
|
|
19
21
|
import android.widget.ImageView
|
|
20
22
|
import android.widget.LinearLayout
|
|
@@ -576,10 +578,19 @@ internal class CraftNativeChrome(context: Context, private val tabs: List<CraftN
|
|
|
576
578
|
private val tabViews = mutableListOf<Pair<TextView, TextView>>()
|
|
577
579
|
private val density = resources.displayMetrics.density
|
|
578
580
|
private var selectedTab = 0
|
|
581
|
+
private var swipeTracking = false
|
|
582
|
+
private var swipeIntercepted = false
|
|
583
|
+
private var swipeDownX = 0f
|
|
584
|
+
private var swipeDownY = 0f
|
|
585
|
+
private val swipeSlop = ViewConfiguration.get(context).scaledTouchSlop
|
|
579
586
|
|
|
580
587
|
var onBack: (() -> Unit)? = null
|
|
581
588
|
var onButton: ((String) -> Unit)? = null
|
|
582
589
|
var onTab: ((Int) -> Unit)? = null
|
|
590
|
+
var onTabSwipeStart: ((Float, Float, Float) -> Boolean)? = null
|
|
591
|
+
var onTabSwipeMove: ((Float) -> Unit)? = null
|
|
592
|
+
var onTabSwipeEnd: ((Float, Boolean) -> Unit)? = null
|
|
593
|
+
var tabSwipeEnabled = true
|
|
583
594
|
|
|
584
595
|
init {
|
|
585
596
|
orientation = VERTICAL
|
|
@@ -640,6 +651,44 @@ internal class CraftNativeChrome(context: Context, private val tabs: List<CraftN
|
|
|
640
651
|
refreshColors()
|
|
641
652
|
}
|
|
642
653
|
|
|
654
|
+
override fun onInterceptTouchEvent(event: MotionEvent): Boolean {
|
|
655
|
+
when (event.actionMasked) {
|
|
656
|
+
MotionEvent.ACTION_DOWN -> {
|
|
657
|
+
swipeTracking = tabs.size > 1 && tabSwipeEnabled && tabBar.visibility == View.VISIBLE && event.y < tabBar.top
|
|
658
|
+
swipeIntercepted = false
|
|
659
|
+
swipeDownX = event.x
|
|
660
|
+
swipeDownY = event.y
|
|
661
|
+
}
|
|
662
|
+
MotionEvent.ACTION_MOVE -> if (swipeTracking && !swipeIntercepted) {
|
|
663
|
+
val dx = event.x - swipeDownX
|
|
664
|
+
val dy = event.y - swipeDownY
|
|
665
|
+
if (kotlin.math.abs(dx) > swipeSlop && kotlin.math.abs(dx) > kotlin.math.abs(dy)) {
|
|
666
|
+
swipeTracking = false
|
|
667
|
+
swipeIntercepted = onTabSwipeStart?.invoke(dx, event.x, event.y) == true
|
|
668
|
+
return swipeIntercepted
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
MotionEvent.ACTION_CANCEL, MotionEvent.ACTION_UP -> swipeTracking = false
|
|
672
|
+
}
|
|
673
|
+
return swipeIntercepted
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
override fun onTouchEvent(event: MotionEvent): Boolean {
|
|
677
|
+
if (!swipeIntercepted) return super.onTouchEvent(event)
|
|
678
|
+
when (event.actionMasked) {
|
|
679
|
+
MotionEvent.ACTION_MOVE -> onTabSwipeMove?.invoke(event.x - swipeDownX)
|
|
680
|
+
MotionEvent.ACTION_UP -> {
|
|
681
|
+
onTabSwipeEnd?.invoke(event.x - swipeDownX, false)
|
|
682
|
+
swipeIntercepted = false
|
|
683
|
+
}
|
|
684
|
+
MotionEvent.ACTION_CANCEL -> {
|
|
685
|
+
onTabSwipeEnd?.invoke(event.x - swipeDownX, true)
|
|
686
|
+
swipeIntercepted = false
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
return true
|
|
690
|
+
}
|
|
691
|
+
|
|
643
692
|
/** Draws `options` (a screen's `setOptions`) for a screen `depth` deep in its stack. */
|
|
644
693
|
fun apply(options: JSONObject, depth: Int, fallbackTitle: String?) {
|
|
645
694
|
val hidden = options.optBoolean("hidden", false)
|
|
@@ -659,6 +708,7 @@ internal class CraftNativeChrome(context: Context, private val tabs: List<CraftN
|
|
|
659
708
|
}
|
|
660
709
|
val tabBarHidden = options.optBoolean("tabBarHidden", false) || depth > 0 && options.optBoolean("tabBarHiddenWhenPushed", false)
|
|
661
710
|
tabBar.visibility = if (tabs.isEmpty() || tabBarHidden) View.GONE else View.VISIBLE
|
|
711
|
+
tabSwipeEnabled = options.optBoolean("tabSwipeEnabled", true)
|
|
662
712
|
}
|
|
663
713
|
|
|
664
714
|
fun selectTab(index: Int) {
|