chore: add build artifacts, map assets, and routing data

This commit is contained in:
Hamza-Ayed
2026-09-19 12:34:24 +03:00
parent aa2b9f131f
commit 43ad2e0ad4
327 changed files with 52421 additions and 1810 deletions
@@ -11,7 +11,7 @@
<application
android:label="tactical_app"
android:name="${applicationName}"
android:icon="@mipmap/ic_launcher">
android:icon="@mipmap/launcher_icon">
<activity
android:name=".MainActivity"
android:exported="true"
@@ -1,5 +1,20 @@
package com.intaleq.tactical_app
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
class MainActivity : FlutterActivity()
class MainActivity : FlutterActivity() {
private val valhallaBridge = ValhallaBridge(this)
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
valhallaBridge.register(flutterEngine)
}
override fun cleanUpFlutterEngine(flutterEngine: FlutterEngine) {
// تحرير الغراف المفتوح في الذاكرة عند تدمير محرك Flutter.
valhallaBridge.release()
super.cleanUpFlutterEngine(flutterEngine)
}
}
@@ -0,0 +1,141 @@
package com.intaleq.tactical_app
import android.content.Context
import android.util.Log
import com.squareup.moshi.Moshi
import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory
import com.valhalla.api.models.RouteRequest
import com.valhalla.api.models.RouteResponse
import com.valhalla.config.ValhallaConfigBuilder
import com.valhalla.valhalla.Valhalla
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodChannel
import java.io.File
/**
* جسر MethodChannel إلى محرك التوجيه Valhalla المحلي (valhalla-mobile).
*
* يدير حزمة توجيه الأردن (valhalla_tiles.tar + admins.sqlite) التي يُنزّلها
* التطبيق من السيرفر، ويبني محرك Valhalla واحداً ويعيد استخدامه لكل الطلبات.
* كل الحسابات تعمل 100% على الجهاز بدون أي اتصال شبكي.
*/
class ValhallaBridge(private val context: Context) {
companion object {
const val CHANNEL = "intaleq_tactical/valhalla"
private const val TAG = "ValhallaBridge"
}
private val moshi = Moshi.Builder().add(KotlinJsonAdapterFactory()).build()
/** المحرك مُعاد استخدامه — بناؤه مكلف لأنه يفتح tar الغراف بالكامل. */
private var engine: Valhalla? = null
private var engineDir: String? = null
fun register(flutterEngine: FlutterEngine) {
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL)
.setMethodCallHandler { call, result ->
when (call.method) {
// نلتقط Throwable وليس Exception فقط: فشل تحميل المكتبة الأصلية
// (UnsatisfiedLinkError مثلاً) هو Error وليس Exception — التقاطه
// يُظهر سبباً مقروءاً في Dart بدل انهيار التطبيق.
"ensureReady" -> try {
ensureReady(call.argument<String>("regionDir"))
result.success(true)
} catch (e: Throwable) {
Log.e(TAG, "ensureReady failed", e)
result.error("ENSURE_FAILED", describe(e), stackOf(e))
}
"route" -> try {
val requestJson = call.argument<String>("request")
?: return@setMethodCallHandler result.error("BAD_ARGS", "missing request", null)
result.success(route(requestJson))
} catch (e: Throwable) {
Log.e(TAG, "route failed", e)
result.error("ROUTE_FAILED", describe(e), stackOf(e))
}
"release" -> try {
release()
result.success(true)
} catch (e: Throwable) {
result.error("RELEASE_FAILED", describe(e), stackOf(e))
}
else -> result.notImplemented()
}
}
}
/** يبني المحرك لمجلد الحزمة إن لم يكن جاهزاً، ويُعيد بناءه عند تغيّر المجلد. */
@Synchronized
private fun ensureReady(regionDirPath: String?) {
val dir = File(regionDirPath ?: File(context.filesDir, "routing/jordan").absolutePath)
if (!dir.exists()) {
throw IllegalStateException("routing package dir does not exist: " + dir.absolutePath)
}
val tilesTar = findFile(dir, "valhalla_tiles.tar")
?: throw IllegalStateException("valhalla_tiles.tar missing in " + dir.absolutePath)
if (engine != null && engineDir == dir.absolutePath) return
release()
val config = ValhallaConfigBuilder().withTileExtract(tilesTar.absolutePath).build()
engine = Valhalla(context, config)
engineDir = dir.absolutePath
Log.i(TAG, "Valhalla engine ready against " + tilesTar.absolutePath)
}
/**
* حساب مسار: يستقبل طلب Valhalla JSON خاماً من Dart، يفكّه عبر Moshi إلى
* RouteRequest النموذجي، ينفّذه محلياً ثم يعيد الاستجابة كنص JSON كامل
* (trip/legs/maneuvers/shape + summary مع ascend/descend).
*/
@Synchronized
private fun route(requestJson: String): String {
ensureReady(engineDir)
val activeEngine = engine ?: throw IllegalStateException("Valhalla engine not initialised")
val requestAdapter = moshi.adapter(RouteRequest::class.java)
val request = requestAdapter.fromJson(requestJson)
?: throw IllegalArgumentException("could not decode RouteRequest from Dart payload")
val response = activeEngine.route(request)
return when (response) {
is com.valhalla.valhalla.ValhallaResponse.Json ->
moshi.adapter(RouteResponse::class.java).toJson(response.jsonResponse)
else ->
throw IllegalArgumentException("only JSON route format is supported on-device")
}
}
/** تحرير المحرك الأصلي (يغلق tar الغراف المفتوح في الذاكرة). */
@Synchronized
fun release() {
try {
engine?.close()
} catch (e: Throwable) {
Log.w(TAG, "engine close failed", e)
}
engine = null
engineDir = null
}
/** بحث تكراري عن ملف داخل مجلد الحزمة (البنية قد تتغير بين إصدارات الحزمة). */
private fun findFile(dir: File, name: String): File? {
if (!dir.isDirectory) return null
dir.listFiles()?.forEach { child ->
if (child.isFile && child.name == name) return child
if (child.isDirectory) findFile(child, name)?.let { return it }
}
return null
}
private fun describe(e: Throwable): String {
val cause = e.cause?.let { " (${it.javaClass.simpleName}: ${it.message})" } ?: ""
return "${e.javaClass.simpleName}: ${e.message}$cause"
}
private fun stackOf(e: Throwable): String = Log.getStackTraceString(e)
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 170 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 304 KiB

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_launcher_background"/>
<foreground>
<inset
android:drawable="@drawable/ic_launcher_foreground"
android:inset="16%" />
</foreground>
</adaptive-icon>
Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="ic_launcher_background">#090E1A</color>
</resources>