first commit

This commit is contained in:
Hamza-Ayed
2026-06-09 08:40:31 +03:00
commit d8901e1a87
3161 changed files with 536187 additions and 0 deletions

View File

@@ -0,0 +1,188 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<!-- ===== Permissions ===== -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_LOCATION" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_TYPE_REMOTE_MESSAGING" />
<uses-permission android:name="android.permission.USE_BIOMETRIC" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.USE_FULL_SCREEN_INTENT" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.WRITE_CONTACTS" />
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
<uses-permission android:name="android.permission.QUICKBOOT_POWERON" />
<uses-permission android:name="com.htc.intent.action.QUICKBOOT_POWERON" />
<uses-permission android:name="android.permission.PICTURE_IN_PICTURE" />
<uses-feature android:name="android.hardware.camera" />
<uses-feature android:name="android.hardware.camera.autofocus" />
<application android:name="${applicationName}" android:icon="@mipmap/launcher_icon"
android:label="@string/label" android:enableOnBackInvokedCallback="true"
android:allowBackup="false" android:fullBackupContent="false"
android:networkSecurityConfig="@xml/network_security_config"
android:usesCleartextTraffic="false" android:theme="@style/LaunchTheme">
<!-- Flutter embedding v2 -->
<meta-data android:name="flutterEmbedding" android:value="2" />
<!-- تحديد نقطة دخول خلفية (للـ overlay / background executor) -->
<!-- <meta-data
android:name="io.flutter.embedding.android.BackgroundExecutor.DART_ENTRYPOINT"
android:value="overlayMain" />
<meta-data
android:name="io.flutter.embedding.android.BackgroundExecutor.DART_LIBRARY_URI"
android:value="main.dart" /> -->
<!-- خرائط + إشعارات فFirebase (قناة افتراضية) -->
<meta-data
android:name="com.google.android.geo.API_KEY"
android:value="${mapsApiKey}" />
<meta-data android:name="com.google.firebase.messaging.default_notification_channel_id"
android:value="@string/default_notification_channel_id" />
<meta-data android:name="io.flutter.embedding.android.EnableImpeller" android:value="false" />
<!-- Main Activity -->
<activity android:name=".MainActivity"
android:configChanges="orientation|keyboardHidden|screenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:exported="true" android:hardwareAccelerated="true" android:launchMode="singleTask"
android:theme="@style/LaunchTheme" android:windowSoftInputMode="adjustResize">
<meta-data android:name="io.flutter.embedding.android.NormalTheme"
android:resource="@style/NormalTheme" />
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<!-- Deep Link: siroapp://... -->
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="siroapp" />
</intent-filter>
<!-- Navigation Intents -->
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="geo" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="google.navigation" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="vnd.android.cursor.item/map" />
</intent-filter>
</activity>
<!-- أنشطة ومكوّنات إضافية -->
<activity android:name="com.yalantis.ucrop.UCropActivity" android:screenOrientation="portrait"
android:theme="@style/Theme.AppCompat.Light.NoActionBar" />
<!-- خدماتك الخاصة -->
<service
android:name="id.flutter.flutter_background_service.BackgroundService"
android:foregroundServiceType="location"
android:enabled="true"
android:exported="true"
/>
<service
android:name="com.siro.siro_driver.trip_overlay_plugin.TripOverlayService"
android:exported="false"
android:foregroundServiceType="specialUse" />
<service android:name=".MyFirebaseMessagingService" android:exported="false" />
<service android:name=".LocationUpdatesService" android:exported="false"
android:foregroundServiceType="location" />
<!-- خدمة Firebase الرسمية لاستقبال رسائل FCM -->
<service android:name="com.google.firebase.messaging.FirebaseMessagingService"
android:exported="false" tools:replace="android:exported">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>
<!-- خدمة overlay للمكتبة الأولى (إن كنت تستخدمها) -->
<!-- <service
android:name="com.phan_tech.flutter_overlay_apps.OverlayService"
android:exported="false" /> -->
<service
android:name="com.trip_overlay.TripOverlayService"
android:exported="false"
android:foregroundServiceType="specialUse"
android:stopWithTask="false" />
<receiver
android:name="com.trip_overlay.TripOverlayReceiver"
android:exported="false">
<intent-filter>
<action android:name="com.trip_overlay.SHOW_OVERLAY" />
</intent-filter>
</receiver>
<service android:name="flutter.overlay.window.flutter_overlay_window.OverlayService"
android:exported="false" android:foregroundServiceType="specialUse" />
<!-- خدمة overlay الخاصة بمكتبة flutter_overlay_window -->
<!-- استقبال توكن/رسائل قديمة (توافقية) -->
<receiver android:name="com.google.firebase.iid.FirebaseInstanceIdReceiver"
android:exported="true" android:permission="com.google.android.c2dm.permission.SEND">
<intent-filter>
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
<category android:name="com.siro.siro_driver" />
</intent-filter>
</receiver>
<!-- خدمة الفقاعة الخاصة بك -->
<service android:name="com.dsaved.bubblehead.bubble.BubbleHeadService" android:enabled="true"
android:exported="false">
<intent-filter>
<action android:name="intent.bring.app.to.foreground" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</service>
<!-- Notif schedulers -->
<receiver android:name="com.dexterous.flutterlocalnotifications.ScheduledNotificationReceiver"
android:exported="false" />
<receiver
android:name="com.dexterous.flutterlocalnotifications.ScheduledNotificationBootReceiver"
android:exported="false">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
<action android:name="android.intent.action.MY_PACKAGE_REPLACED" />
<action android:name="android.intent.action.QUICKBOOT_POWERON" />
<action android:name="com.htc.intent.action.QUICKBOOT_POWERON" />
</intent-filter>
</receiver>
<!-- مستقبل برودكاست خاص بك -->
<receiver android:name=".YourBroadcastReceiver" android:exported="false" />
<!-- Android Auto Support -->
<meta-data
android:name="androidx.car.app.minCarAppApiLevel"
android:value="1" />
<meta-data
android:name="com.google.android.gms.car.application"
android:resource="@xml/automotive_app_desc" />
<service
android:name=".MyCarAppService"
android:exported="true">
<intent-filter>
<action android:name="androidx.car.app.CarAppService" />
<category android:name="androidx.car.app.category.NAVIGATION" />
</intent-filter>
</service>
</application>
<uses-permission android:name="android.permission.INTERNET" />
</manifest>

View File

@@ -0,0 +1,11 @@
cmake_minimum_required(VERSION 3.10.2) # 3.10.2 is fine, but no need to go as high as 3.31.5
project(intaleq_driver) # Good
# Add your C++ source file(s) to create a SHARED library.
add_library(native-lib SHARED native-lib.cpp)
# Find the Android log library.
find_library(log-lib log)
# Link your library against the log library. This is essential for debugging.
target_link_libraries(native-lib ${log-lib})

View File

@@ -0,0 +1,187 @@
#include <jni.h>
#include <string>
#include <fstream>
#include <unistd.h>
#include <dirent.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <android/log.h>
#include <pthread.h>
#include <dlfcn.h>
#include <link.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/ptrace.h> // Add this line
#define LOG_TAG "NativeLib"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
// Function to check for common root binaries
bool isRooted()
{
std::string paths[] = {
"/system/app/Superuser.apk",
"/system/xbin/su",
"/system/bin/su",
"/system/bin/magisk",
"/system/xbin/magisk",
"/sbin/magisk"};
for (const auto &path : paths)
{
std::ifstream file(path);
if (file.good())
{
return true;
}
}
return false;
}
// Function to check for the presence of files or directories commonly associated with Frida.
bool checkFridaFiles()
{
std::string fridaFiles[] = {
"/data/local/tmp/re.frida.server", // Common Frida server path
"/data/local/tmp/frida-server",
"/usr/lib/libfrida-gadget.so", // Frida gadget (injected library)
"/usr/lib64/libfrida-gadget.so",
"/data/local/re.frida.server",
};
for (const auto &path : fridaFiles)
{
if (access(path.c_str(), F_OK) != -1)
{
LOGE("Frida file detected: %s", path.c_str());
return true;
}
}
return false;
}
// Checks for open ports commonly used by Frida. This is less reliable, as ports can be changed.
bool checkFridaPorts()
{
int sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock == -1)
{
return false; // Couldn't create socket, not a strong indicator.
}
sockaddr_in sa;
memset(&sa, 0, sizeof(sa));
sa.sin_family = AF_INET;
sa.sin_port = htons(27042); // Default Frida port
inet_pton(AF_INET, "127.0.0.1", &sa.sin_addr);
if (connect(sock, (struct sockaddr *)&sa, sizeof(sa)) != -1)
{
LOGE("Frida default port (27042) is open.");
close(sock);
return true;
}
close(sock);
return false;
}
// Check the maps file of the current process for any suspicious entries.
bool checkMaps()
{
std::ifstream mapsFile("/proc/self/maps");
std::string line;
if (mapsFile.is_open())
{
while (std::getline(mapsFile, line))
{
// Look for lines that indicate injected libraries, especially Frida.
if (line.find("frida") != std::string::npos ||
line.find("gum-js-") != std::string::npos)
{ // Gum is Frida's JavaScript engine
LOGE("Suspicious entry in /proc/self/maps: %s", line.c_str());
return true;
}
}
mapsFile.close();
}
else
{
LOGE("Could not open /proc/self/maps");
return false;
}
return false;
}
// Check loaded modules.
bool checkLoadedModules()
{
bool found = false;
dl_iterate_phdr([](struct dl_phdr_info *info, size_t size, void *data)
{
bool *found_ptr = static_cast<bool *>(data);
if (std::string(info->dlpi_name).find("frida") != std::string::npos)
{
LOGE("Frida module detected: %s", info->dlpi_name);
*found_ptr = true;
return 1; // Stop iterating
}
return 0; // Continue iterating
},
&found);
return found;
}
// This is a simple ptrace check. More sophisticated checks are possible (and necessary for robust detection).
// bool checkPtrace() {
// // Attempt to ptrace ourselves. If another process is already tracing us, this will fail.
// if (ptrace(PTRACE_TRACEME, 0, 1, 0) < 0) {
// LOGE("ptrace failed. Debugger or tracer detected.");
// return true; // Likely being traced
// }
// // Detach. If attached, need to detach to not interfere.
// ptrace(PTRACE_DETACH, 0, 0, 0);
// return false;
//}
extern "C" JNIEXPORT jboolean JNICALL
Java_com_siro_siro_1driver_RootDetection_isNativeRooted(JNIEnv *env, jobject /* this */)
{
if (isRooted())
{
return JNI_TRUE;
}
if (checkFridaFiles())
{
return JNI_TRUE;
}
if (checkFridaPorts())
{
return JNI_TRUE;
}
if (checkMaps())
{
return JNI_TRUE;
}
if (checkLoadedModules())
{
return JNI_TRUE;
}
// if (checkPtrace()) {
// return JNI_TRUE;
// }
return JNI_FALSE;
}

View File

@@ -0,0 +1,44 @@
package com.siro.siro_driver
import android.os.Handler
import android.os.Looper
/**
* كائن مشترك (Singleton) يحمل بيانات التوجيه في الوقت الحقيقي.
* يتم تحديثه من فلاتر عبر MethodChannel في MainActivity،
* ويتم قراءته من MyCarScreen و MapPresentation لعرض البيانات على شاشة السيارة.
*/
object CarNavigationData {
// --- بيانات الموقع ---
var currentLat: Double = 0.0
var currentLng: Double = 0.0
var currentBearing: Double = 0.0
// --- بيانات التوجيه ---
var currentInstruction: String = "في انتظار بدء الرحلة..."
var distanceToNextStepMeters: Double = 0.0
var totalDistanceRemainingMeters: Double = 0.0
var estimatedTimeRemainingSeconds: Double = 0.0
var maneuverType: Int = 0 // يطابق قيم Maneuver.TYPE_* من Car App Library
// --- حالة التوجيه ---
var isNavigating: Boolean = false
var currentSpeed: Double = 0.0 // km/h
// --- نظام المستمعين ---
private val listeners = mutableListOf<() -> Unit>()
fun addListener(listener: () -> Unit) {
listeners.add(listener)
}
fun removeListener(listener: () -> Unit) {
listeners.remove(listener)
}
fun notifyListeners() {
Handler(Looper.getMainLooper()).post {
listeners.forEach { it.invoke() }
}
}
}

View File

@@ -0,0 +1,209 @@
package com.siro.siro_driver
import android.app.AlertDialog
import android.content.Intent
import android.os.Bundle
import android.util.Log
import android.widget.LinearLayout
import android.widget.ProgressBar
import android.widget.TextView
import androidx.core.view.setPadding
import com.scottyab.rootbeer.RootBeer
import io.flutter.embedding.android.FlutterFragmentActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodChannel
import java.io.File
import java.util.Timer
import kotlin.concurrent.schedule
class MainActivity : FlutterFragmentActivity() {
private val SECURITY_CHANNEL = "com.siro.siro_driver/security"
private val APP_CONTROL_CHANNEL = "com.siro.siro_driver/app_control"
private var appControlChannel: MethodChannel? = null
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
appControlChannel =
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, APP_CONTROL_CHANNEL)
// Channel for security checks (isRooted)
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, SECURITY_CHANNEL)
.setMethodCallHandler { call, result ->
when (call.method) {
"isNativeRooted" -> result.success(isDeviceCompromised())
else -> result.notImplemented()
}
}
// Channel for app control (bringing to foreground)
appControlChannel?.setMethodCallHandler { call, result ->
when (call.method) {
"bringToForeground" -> {
val intent =
Intent(this, MainActivity::class.java).apply {
action = Intent.ACTION_MAIN
addCategory(Intent.CATEGORY_LAUNCHER)
addFlags(
Intent.FLAG_ACTIVITY_NEW_TASK or
Intent.FLAG_ACTIVITY_CLEAR_TOP or
Intent.FLAG_ACTIVITY_SINGLE_TOP
)
}
try {
startActivity(intent)
result.success(true)
} catch (e: Exception) {
result.error("ACTIVITY_START_FAILED", e.message, e.stackTraceToString())
}
}
else -> result.notImplemented()
}
}
// ✅ Channel for Android Auto Navigation Updates
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, "com.siro.siro_driver/car_navigation")
.setMethodCallHandler { call, result ->
when (call.method) {
"updateNavState" -> {
// تحديث شامل لجميع بيانات التوجيه دفعة واحدة
CarNavigationData.currentLat = call.argument<Double>("lat") ?: CarNavigationData.currentLat
CarNavigationData.currentLng = call.argument<Double>("lng") ?: CarNavigationData.currentLng
CarNavigationData.currentBearing = call.argument<Double>("bearing") ?: CarNavigationData.currentBearing
CarNavigationData.currentSpeed = call.argument<Double>("speed") ?: CarNavigationData.currentSpeed
CarNavigationData.currentInstruction = call.argument<String>("instruction") ?: CarNavigationData.currentInstruction
CarNavigationData.distanceToNextStepMeters = call.argument<Double>("distanceToStep") ?: CarNavigationData.distanceToNextStepMeters
CarNavigationData.totalDistanceRemainingMeters = call.argument<Double>("totalDistance") ?: CarNavigationData.totalDistanceRemainingMeters
CarNavigationData.estimatedTimeRemainingSeconds = call.argument<Double>("eta") ?: CarNavigationData.estimatedTimeRemainingSeconds
CarNavigationData.maneuverType = call.argument<Int>("maneuver") ?: CarNavigationData.maneuverType
CarNavigationData.isNavigating = call.argument<Boolean>("isNavigating") ?: CarNavigationData.isNavigating
CarNavigationData.notifyListeners()
result.success(true)
}
"updateLocation" -> {
CarNavigationData.currentLat = call.argument<Double>("lat") ?: 0.0
CarNavigationData.currentLng = call.argument<Double>("lng") ?: 0.0
CarNavigationData.currentBearing = call.argument<Double>("bearing") ?: CarNavigationData.currentBearing
CarNavigationData.currentSpeed = call.argument<Double>("speed") ?: CarNavigationData.currentSpeed
CarNavigationData.notifyListeners()
result.success(true)
}
"updateInstruction" -> {
CarNavigationData.currentInstruction = call.argument<String>("instruction") ?: ""
CarNavigationData.maneuverType = call.argument<Int>("maneuver") ?: 0
CarNavigationData.distanceToNextStepMeters = call.argument<Double>("distanceToStep") ?: 0.0
CarNavigationData.notifyListeners()
result.success(true)
}
"stopNavigation" -> {
CarNavigationData.isNavigating = false
CarNavigationData.currentInstruction = "تمت الرحلة بنجاح!"
CarNavigationData.notifyListeners()
result.success(true)
}
else -> result.notImplemented()
}
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
if (isDeviceCompromised()) {
showSecurityWarningDialog()
}
// ✅ فحص هل التطبيق فتح بسبب زر "قبول" في النافذة
checkIntentForOverlayAccept(intent)
}
override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent)
setIntent(intent)
// ✅ فحص النية (Intent) عند فتح التطبيق من الخلفية
checkIntentForOverlayAccept(intent)
}
// 🔥 هذه الدالة السحرية التي تلتقط زر القبول وترسله للتطبيق الرئيسي 🔥
private fun checkIntentForOverlayAccept(intent: Intent) {
val acceptedTripId = intent.getStringExtra("acceptedTripId")
if (acceptedTripId != null) {
Log.d("MainActivity", "✅ Trip accepted via Native Intent: $acceptedTripId")
appControlChannel?.invokeMethod("onOverlayTripAccepted", acceptedTripId)
intent.removeExtra("acceptedTripId") // مسح النية لكي لا تتكرر
}
}
// --- بقية كود الحماية الخاص بك ---
private fun isDeviceCompromised(): Boolean {
return try {
val isRootedByRootBeer = RootBeer(this).isRooted
isRootedByRootBeer
} catch (e: Exception) {
true
}
}
private fun showSecurityWarningDialog() {
var secondsRemaining = 10
val progressBar =
ProgressBar(this, null, android.R.attr.progressBarStyleHorizontal).apply {
max = 10
progress = 10
}
val textView =
TextView(this).apply {
text = getString(R.string.security_warning_message)
textAlignment = TextView.TEXT_ALIGNMENT_CENTER
}
val layout =
LinearLayout(this).apply {
orientation = LinearLayout.VERTICAL
setPadding(48)
addView(textView)
addView(progressBar)
}
val dialog =
AlertDialog.Builder(this)
.setTitle(getString(R.string.security_warning_title))
.setView(layout)
.setCancelable(false)
.create()
dialog.show()
val timer = Timer()
timer.schedule(0, 1000) {
secondsRemaining--
runOnUiThread {
progressBar.progress = secondsRemaining
if (secondsRemaining <= 0) {
timer.cancel()
dialog.dismiss()
clearAppDataAndExit()
}
}
}
}
private fun clearAppDataAndExit() {
try {
Runtime.getRuntime().exec("pm clear $packageName")
} catch (e: Exception) {
clearCache()
clearAppData()
}
finishAffinity()
System.exit(0)
}
private fun clearCache() {
deleteDir(cacheDir)
deleteDir(externalCacheDir)
}
private fun clearAppData() {}
private fun deleteDir(dir: File?): Boolean {
if (dir != null && dir.isDirectory) {
dir.list()?.forEach { deleteDir(File(dir, it)) }
}
return dir?.delete() ?: false
}
}

View File

@@ -0,0 +1,119 @@
package com.siro.siro_driver
import android.app.Presentation
import android.content.Context
import android.os.Bundle
import android.view.Display
import android.view.ViewGroup
import android.widget.FrameLayout
import org.maplibre.android.MapLibre
import org.maplibre.android.camera.CameraPosition
import org.maplibre.android.camera.CameraUpdateFactory
import org.maplibre.android.geometry.LatLng
import org.maplibre.android.maps.MapView
import org.maplibre.android.maps.MapLibreMap
import org.maplibre.android.maps.Style
/**
* شاشة عرض وهمية (Presentation) تُرسم على VirtualDisplay الخاص بشاشة السيارة.
* تستخدم MapLibre Native SDK لعرض الخريطة بنفس ستايل تطبيق انطلق.
*/
class MapPresentation(outerContext: Context, display: Display) : Presentation(outerContext, display) {
lateinit var mapView: MapView
private var mapboxMap: MapLibreMap? = null
private var isMapReady = false
private val locationListener: () -> Unit = {
updateCameraPosition()
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
MapLibre.getInstance(context)
val root = FrameLayout(context)
root.layoutParams = ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT
)
mapView = MapView(context)
mapView.layoutParams = FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.MATCH_PARENT
)
root.addView(mapView)
setContentView(root)
mapView.onCreate(savedInstanceState)
mapView.getMapAsync { map ->
mapboxMap = map
// تحميل ستايل خرائط انطلق من أصول فلاتر
val styleUrl = "asset://flutter_assets/assets/style.json"
map.setStyle(Style.Builder().fromUri(styleUrl)) {
isMapReady = true
updateCameraPosition()
}
// إعدادات مناسبة لشاشة السيارة
map.uiSettings.isCompassEnabled = false
map.uiSettings.isLogoEnabled = false
map.uiSettings.isAttributionEnabled = false
}
// الاستماع لتحديثات الموقع القادمة من فلاتر
CarNavigationData.addListener(locationListener)
}
private fun updateCameraPosition() {
if (!isMapReady || mapboxMap == null) return
val lat = CarNavigationData.currentLat
val lng = CarNavigationData.currentLng
val bearing = CarNavigationData.currentBearing
val speed = CarNavigationData.currentSpeed
if (lat == 0.0 && lng == 0.0) return
// حساب الزوم المناسب بناءً على السرعة (نفس المنطق في فلاتر)
val zoom = when {
speed < 15 -> 17.0
speed < 40 -> 16.5
speed < 70 -> 15.5
speed < 100 -> 15.0
else -> 14.0
}
// حساب الميل (Tilt) بناءً على السرعة لتأثير ثلاثي الأبعاد
val tilt = when {
speed < 10 -> 0.0
speed < 40 -> 40.0
else -> 55.0
}
val position = CameraPosition.Builder()
.target(LatLng(lat, lng))
.zoom(zoom)
.bearing(bearing)
.tilt(tilt)
.build()
mapboxMap?.animateCamera(
CameraUpdateFactory.newCameraPosition(position),
1000 // انتقال سلس خلال ثانية
)
}
override fun onStart() { super.onStart(); mapView.onStart() }
override fun onStop() { super.onStop(); mapView.onStop() }
fun onResume() { mapView.onResume() }
fun onPause() { mapView.onPause() }
fun onDestroy() {
CarNavigationData.removeListener(locationListener)
mapView.onDestroy()
}
}

View File

@@ -0,0 +1,44 @@
package com.siro.siro_driver
import android.app.Application
import android.content.Intent
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.embedding.engine.dart.DartExecutor
import io.flutter.plugin.common.MethodChannel
class MyApplication : Application() {
companion object {
lateinit var instance: MyApplication
private set
val flutterEngine: FlutterEngine by lazy {
FlutterEngine(instance).apply {
dartExecutor.executeDartEntrypoint(DartExecutor.DartEntrypoint.createDefault())
}
}
}
override fun onCreate() {
super.onCreate()
instance = this
MethodChannel(
flutterEngine.dartExecutor.binaryMessenger,
"com.siro.siro_driver/app_lifecycle"
)
.setMethodCallHandler { call, result ->
if (call.method == "bringAppToForeground") {
bringAppToForeground()
result.success(null)
} else {
result.notImplemented()
}
}
}
private fun bringAppToForeground() {
val intent = Intent(applicationContext, MainActivity::class.java)
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_REORDER_TO_FRONT)
startActivity(intent)
}
}

View File

@@ -0,0 +1,24 @@
package com.siro.siro_driver
import android.content.pm.ApplicationInfo
import androidx.car.app.CarAppService
import androidx.car.app.Session
import androidx.car.app.validation.HostValidator
class MyCarAppService : CarAppService() {
override fun createHostValidator(): HostValidator {
// في وضع التطوير: نسمح لجميع المستضيفين (DHU + أي تطبيق)
// في وضع الإنتاج: نسمح فقط لتطبيقات Android Auto و Google الرسمية
return if (applicationInfo.flags and ApplicationInfo.FLAG_DEBUGGABLE != 0) {
HostValidator.ALLOW_ALL_HOSTS_VALIDATOR
} else {
HostValidator.Builder(applicationContext)
.addAllowedHosts(androidx.car.app.R.array.hosts_allowlist_sample)
.build()
}
}
override fun onCreateSession(): Session {
return MyCarSession()
}
}

View File

@@ -0,0 +1,80 @@
package com.siro.siro_driver
import androidx.car.app.CarContext
import androidx.car.app.Screen
import androidx.car.app.model.Action
import androidx.car.app.model.CarColor
import androidx.car.app.model.CarIcon
import androidx.car.app.model.Distance
import androidx.car.app.model.MessageTemplate
import androidx.car.app.model.Template
import androidx.car.app.navigation.model.Maneuver
import androidx.car.app.navigation.model.NavigationTemplate
import androidx.car.app.navigation.model.RoutingInfo
import androidx.car.app.navigation.model.Step
class MyCarScreen(carContext: CarContext) : Screen(carContext) {
init {
CarNavigationData.addListener {
invalidate()
}
}
override fun onGetTemplate(): Template {
// إذا لم يكن التوجيه نشطاً بعد، نعرض شاشة ترحيبية
if (!CarNavigationData.isNavigating) {
return MessageTemplate.Builder("مرحباً بك في انطلق درايفر\nبانتظار بدء رحلة جديدة...")
.setTitle("Intaleq Driver")
.setHeaderAction(Action.APP_ICON)
.build()
}
// --- بناء معلومات التوجيه (Turn-by-Turn) ---
val maneuverType = mapIntaleqManeuverToCarManeuver(CarNavigationData.maneuverType)
val maneuver = Maneuver.Builder(maneuverType).build()
val step = Step.Builder(CarNavigationData.currentInstruction)
.setManeuver(maneuver)
.build()
val distanceToStep = Distance.create(
CarNavigationData.distanceToNextStepMeters,
Distance.UNIT_METERS
)
val routingInfo = RoutingInfo.Builder()
.setCurrentStep(step, distanceToStep)
.build()
// --- بناء قالب التوجيه ---
return NavigationTemplate.Builder()
.setNavigationInfo(routingInfo)
.setActionStrip(
androidx.car.app.model.ActionStrip.Builder()
.addAction(Action.APP_ICON)
.build()
)
.setBackgroundColor(CarColor.PRIMARY)
.build()
}
/**
* تحويل أكواد الانعطاف الخاصة بتطبيق انطلق (NavigationController.currentManeuverModifier)
* إلى أكواد Maneuver الرسمية من مكتبة Android for Cars.
*/
private fun mapIntaleqManeuverToCarManeuver(intaleqCode: Int): Int {
return when (intaleqCode) {
0 -> Maneuver.TYPE_STRAIGHT // مستقيم
2 -> Maneuver.TYPE_TURN_NORMAL_RIGHT // يمين
3 -> Maneuver.TYPE_TURN_SLIGHT_RIGHT // يمين خفيف
-2 -> Maneuver.TYPE_TURN_NORMAL_LEFT // يسار
-1 -> Maneuver.TYPE_TURN_SLIGHT_LEFT // يسار خفيف
4 -> Maneuver.TYPE_DESTINATION // وصلت
6 -> Maneuver.TYPE_ROUNDABOUT_ENTER_AND_EXIT_CW // دوار
7 -> Maneuver.TYPE_KEEP_RIGHT // ابق يمين
-7 -> Maneuver.TYPE_KEEP_LEFT // ابق يسار
else -> Maneuver.TYPE_UNKNOWN
}
}
}

View File

@@ -0,0 +1,69 @@
package com.siro.siro_driver
import android.content.Intent
import android.hardware.display.DisplayManager
import android.hardware.display.VirtualDisplay
import android.os.Handler
import android.os.Looper
import androidx.car.app.AppManager
import androidx.car.app.Screen
import androidx.car.app.Session
import androidx.car.app.SurfaceCallback
import androidx.car.app.SurfaceContainer
import androidx.lifecycle.DefaultLifecycleObserver
import androidx.lifecycle.LifecycleOwner
class MyCarSession : Session(), DefaultLifecycleObserver {
private var virtualDisplay: VirtualDisplay? = null
private var presentation: MapPresentation? = null
override fun onCreateScreen(intent: Intent): Screen {
lifecycle.addObserver(this)
val appManager = carContext.getCarService(AppManager::class.java)
appManager.setSurfaceCallback(object : SurfaceCallback {
override fun onSurfaceAvailable(surfaceContainer: SurfaceContainer) {
val surface = surfaceContainer.surface ?: return
val width = surfaceContainer.width
val height = surfaceContainer.height
val dpi = surfaceContainer.dpi
val displayManager = carContext.getSystemService(DisplayManager::class.java)
virtualDisplay = displayManager.createVirtualDisplay(
"CarAppMapDisplay",
width,
height,
dpi,
surface,
DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY
)
virtualDisplay?.display?.let { display ->
Handler(Looper.getMainLooper()).post {
presentation = MapPresentation(carContext, display)
presentation?.show()
}
}
}
override fun onVisibleAreaChanged(visibleArea: android.graphics.Rect) {
// تحديث المساحة المرئية إذا لزم الأمر
}
override fun onSurfaceDestroyed(surfaceContainer: SurfaceContainer) {
Handler(Looper.getMainLooper()).post {
presentation?.dismiss()
presentation = null
}
virtualDisplay?.release()
virtualDisplay = null
}
})
return MyCarScreen(carContext)
}
override fun onResume(owner: LifecycleOwner) { presentation?.onResume() }
override fun onPause(owner: LifecycleOwner) { presentation?.onPause() }
override fun onDestroy(owner: LifecycleOwner) { presentation?.onDestroy() }
}

View File

@@ -0,0 +1,9 @@
package com.siro.siro_driver
object RootDetection {
init {
System.loadLibrary("native-lib") // Load the native library
}
external fun isNativeRooted(): Boolean // Declare the external function
}

View File

@@ -0,0 +1,104 @@
import android.content.Context
import android.util.Base64
import android.util.Log
import com.google.android.gms.safetynet.SafetyNet
import java.io.IOException
import java.security.GeneralSecurityException
import java.security.SecureRandom
import org.json.JSONObject
object SafetyNetCheck {
private const val TAG = "SafetyNetCheck"
fun checkSafetyNet(context: Context, apiKey: String, callback: (Boolean) -> Unit) {
// Generate a nonce. A good nonce is large, random, and used only once.
val nonce = generateNonce()
SafetyNet.getClient(context)
.attest(nonce, apiKey)
.addOnSuccessListener { response ->
// Success! Now, *verify* the response.
val jwsResult = response.jwsResult
if (jwsResult != null) {
try {
val isSafe = SafetyNetResponseVerifier.verify(jwsResult)
Log.d(TAG, "SafetyNet verification result: $isSafe")
callback(isSafe) // Now passing a *verified* result.
} catch (e: Exception) {
Log.e(TAG, "Error verifying SafetyNet response: ${e.message}", e)
callback(false) // Treat verification errors as failures.
}
} else {
Log.e(TAG, "SafetyNet jwsResult is null")
callback(false) // Null result is a failure.
}
}
.addOnFailureListener { e ->
Log.e(TAG, "SafetyNet attest API call failed: ${e.message}", e)
callback(false) // API call failure.
}
}
// Helper function to generate a nonce.
private fun generateNonce(): ByteArray {
val byteGenerator = SecureRandom()
val nonce = ByteArray(32)
byteGenerator.nextBytes(nonce)
return nonce
}
}
// Helper class to verify the SafetyNet response.
object SafetyNetResponseVerifier {
private const val TAG = "SafetyNetVerifier"
// This method *must* be implemented on a *backend server* for real security.
// This is just a *simplified example* for demonstration purposes and is
// *not* suitable for production without a backend check.
@Throws(GeneralSecurityException::class, IOException::class)
fun verify(jwsResult: String): Boolean {
// 1. Parse the JWS: Split into header, payload, and signature.
val parts = jwsResult.split(".")
if (parts.size != 3) {
Log.e(TAG, "Invalid JWS format")
return false // Invalid JWS format
}
val header = parts[0]
val payload = parts[1]
val signature = parts[2]
// 2. Decode the payload (it's Base64 encoded).
val decodedPayload = Base64.decode(payload, Base64.DEFAULT)
val payloadJson = JSONObject(String(decodedPayload))
// 3. Check the ctsProfileMatch and basicIntegrity.
val ctsProfileMatch = payloadJson.optBoolean("ctsProfileMatch", false)
val basicIntegrity = payloadJson.optBoolean("basicIntegrity", false)
Log.d(TAG, "ctsProfileMatch: $ctsProfileMatch, basicIntegrity: $basicIntegrity")
// 4. **CRITICAL: In a real application, you *must* send the JWS to your
// backend server for verification. The server should use the
// Google SafetyNet API (or a library that wraps it) to verify
// the signature and check the fields. This prevents attackers
// from tampering with the response on the device.**
//
// // Example (pseudo-code) of what the backend check would do:
// // GoogleCredential credential = ...;
// // SafetyNet safetyNet = new SafetyNet.Builder(httpTransport, jsonFactory)
// // .setApplicationName("YourAppName")
// // .setHttpRequestInitializer(credential)
// // .build();
// // SafetyNetApi.VerifyJwsRequest request = safetyNet.safetynet().verifyJws(jwsResult);
// // SafetyNetApi.VerifyJwsResponse response = request.execute();
// // return response.isValidSignature() && response.getCtsProfileMatch() &&
// response.getBasicIntegrity();
// 5. For this *example* (without a backend), we'll just check the fields.
// This is *NOT SECURE* for production!
return ctsProfileMatch && basicIntegrity
}
}

View File

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Modify this file to customize your launch splash screen -->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="?android:colorBackground" />
<!-- You can insert your own image assets here -->
<!-- <item>
<bitmap
android:gravity="center"
android:src="@mipmap/launch_image" />
</item> -->
</layer-list>

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.8 KiB

View File

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Modify this file to customize your launch splash screen -->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@android:color/white" />
<!-- You can insert your own image assets here -->
<!-- <item>
<bitmap
android:gravity="center"
android:src="@mipmap/launch_image" />
</item> -->
</layer-list>

Binary file not shown.

After

Width:  |  Height:  |  Size: 544 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 442 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 721 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,8 @@
<resources>
<string name="security_warning_title">تحذير أمني</string>
<string name="security_warning_message">تم اكتشاف مشكلة أمنية أو تعديل على هذا الجهاز. لا يمكن
تشغيل التطبيق على هذا الجهاز.</string>
<string name="exit_button">إغلاق التطبيق</string>
<string name="device_secure">الجهاز آمن. الاستمرار بشكل طبيعي.</string>
<string name="label">انطلق درايفر</string>
</resources>

View File

@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is on -->
<style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
<!-- Show a splash screen on the activity. Automatically removed when
the Flutter engine draws its first frame -->
<item name="android:windowBackground">@drawable/launch_background</item>
</style>
<!-- Theme applied to the Android Window as soon as the process has started.
This theme determines the color of the Android Window while your
Flutter UI initializes, as well as behind your Flutter UI while its
running.
This Theme is only used starting with V2 of Flutter's Android embedding. -->
<style name="NormalTheme" parent="@android:style/Theme.Black.NoTitleBar">
<item name="android:windowBackground">?android:colorBackground</item>
</style>
</resources>

View File

@@ -0,0 +1,21 @@
<resources>
<string name="app_name">My App</string>
<!-- <string name="default_notification_channel_id">ride_channel</string> -->
<!-- <string name="default_notification_channel_id">default_channel</string> -->
<string name="default_notification_channel_id">high_importance_channel</string>
<string name="api_key">e</string>
<string name="security_warning_title">Security Warning</string>
<string name="api_key_safety">AIzaSyB04YNW3LbvmQ5lX1t2bOwEU18-</string>
<string name="label">Siro Driver</string>
<string name="security_warning_message">A security issue or modification has been detected on
this device. The app cannot run on this device.</string>
<string name="exit_button">Exit App</string>
<string name="device_secure">Device is secure. Proceeding normally.</string>
<!-- <string name="default_notification_channel_id">driver_service_channel</string> -->
<string name="location_service_channel_id">location_service_channel</string>
<string name="geolocator_channel_id">geolocator_channel</string>
</resources>

View File

@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is off -->
<style name="LaunchTheme" parent="@android:style/Theme.Light.NoTitleBar">
<!-- Show a splash screen on the activity. Automatically removed when
the Flutter engine draws its first frame -->
<item name="android:windowBackground">@drawable/launch_background</item>
</style>
<!-- Theme applied to the Android Window as soon as the process has started.
This theme determines the color of the Android Window while your
Flutter UI initializes, as well as behind your Flutter UI while its
running.
This Theme is only used starting with V2 of Flutter's Android embedding. -->
<style name="NormalTheme" parent="@android:style/Theme.Light.NoTitleBar">
<item name="android:windowBackground">?android:colorBackground</item>
</style>
</resources>

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<automotiveApp>
<uses name="navigation" />
</automotiveApp>

View File

@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<base-config cleartextTrafficPermitted="false">
<trust-anchors>
<certificates src="system" />
</trust-anchors>
</base-config>
<domain-config cleartextTrafficPermitted="false">
<domain includeSubdomains="true">intaleq.xyz</domain>
<pin-set expiration="2027-01-01">
<!-- <pin digest="SHA-256">pXmP2hTQLxDEvlTVmP5N7xpiA32sycBsxB6hBFT2uL4=</pin> -->
<pin digest="SHA-256">XJXX7XthMj5VlSHfvo1q73sY7orJ9Wle0X4avj0/Vwo=</pin>
<pin digest="SHA-256">C5+lpZ7tcVwmwQIMcRtPbsQtWLABXhQzejna0wHESsl=</pin>
</pin-set>
</domain-config>
</network-security-config>