|
|
|
@@ -0,0 +1,440 @@
|
|
|
|
|
# قائمة التحقق من الامتثال لـ Android Auto و Apple CarPlay - تطبيق Siro Driver
|
|
|
|
|
|
|
|
|
|
## الرموز التوضيحية
|
|
|
|
|
- ✅ **مكتمل** — تم التنفيذ في الكود الحالي
|
|
|
|
|
- 📋 **يدوي** — يجب تنفيذه خارج الكود (App Store Connect، Play Console، Apple Developer)
|
|
|
|
|
- ⚠️ **يحتاج تحقق** — يحتاج اختبار على أجهزة حقيقية قبل الإطلاق
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## ANDROID AUTO (`siro_driver`)
|
|
|
|
|
|
|
|
|
|
### 1. AndroidManifest.xml
|
|
|
|
|
| المتطلب | الحالة | الملف | ملاحظات |
|
|
|
|
|
|---|---|---|---|
|
|
|
|
|
| `androidx.car.app.CarAppService` مع intent-filter | ✅ | `AndroidManifest.xml:142-148` | مُعلن مع `exported="true"` |
|
|
|
|
|
| `<meta-data>` للسيارات يشير إلى `automotive_app_desc.xml` | ✅ | `AndroidManifest.xml:72-74` | `com.android.car.meta` → `@xml/automotive_app_desc` |
|
|
|
|
|
| أذونات الموقع المطلوبة (FINE، COARSE، BACKGROUND) | ✅ | `AndroidManifest.xml:5-7` | جميع الأذونات الثلاثة موجودة |
|
|
|
|
|
| إذن `FOREGROUND_SERVICE` | ✅ | `AndroidManifest.xml:8-10` | نوع location |
|
|
|
|
|
| `FOREGROUND_SERVICE_TYPE_REMOTE_MESSAGING` | ✅ | `AndroidManifest.xml:10` | لـ FCM |
|
|
|
|
|
| إذن الإشعارات (API 33+) | ⚠️ | `main.dart:60-81` | طلب وقت التشغيل موجود؛ تحقق على Android 14+ |
|
|
|
|
|
|
|
|
|
|
### 2. موارد السيارات (Automotive Resources)
|
|
|
|
|
| المتطلب | الحالة | الملف | ملاحظات |
|
|
|
|
|
|---|---|---|---|
|
|
|
|
|
| `automotive_app_desc.xml` مع `<uses name="navigation" />` | ✅ | `res/xml/automotive_app_desc.xml` | يعلن فئة التنقل بشكل صحيح |
|
|
|
|
|
|
|
|
|
|
### 3. Gradle / التبعيات (Dependencies)
|
|
|
|
|
| المتطلب | الحالة | الملف | ملاحظات |
|
|
|
|
|
|---|---|---|---|
|
|
|
|
|
| `androidx.car.app:app:1.4.0` | ✅ | `app/build.gradle:118` | أحدث إصدار مستقر |
|
|
|
|
|
| `compileSdk 36` | ✅ | `app/build.gradle:38` | يلبي الحد الأدنى لمكتبة Car App |
|
|
|
|
|
| `minSdk 30` | ✅ | `app/build.gradle:67` | كافٍ لـ Android Auto |
|
|
|
|
|
|
|
|
|
|
### 4. خدمة تطبيق السيارة (Car App Service)
|
|
|
|
|
| المتطلب | الحالة | الملف | ملاحظات |
|
|
|
|
|
|---|---|---|---|
|
|
|
|
|
| فئة فرعية من `CarAppService` | ✅ | `MyCarAppService.kt` | تنشئ `MyCarSession` |
|
|
|
|
|
| `createHostValidator()` | ✅ | `MyCarAppService.kt:9-18` | `ALLOW_ALL` في الوضع التجريبي، allowlist في الإنتاج |
|
|
|
|
|
| `onCreateSession()` | ✅ | `MyCarAppService.kt:21-23` | تُرجع `MyCarSession` |
|
|
|
|
|
|
|
|
|
|
### 5. الجلسة (Session)
|
|
|
|
|
| المتطلب | الحالة | الملف | ملاحظات |
|
|
|
|
|
|---|---|---|---|
|
|
|
|
|
| فئة فرعية من `Session` مع `onCreateScreen()` | ✅ | `MyCarSession.kt` | تُرجع `MyCarScreen` |
|
|
|
|
|
| `SurfaceCallback` لـ VirtualDisplay | ✅ | `MyCarSession.kt:24-61` | ينشئ VirtualDisplay لـ MapLibre |
|
|
|
|
|
| مراقب دورة الحياة لإيقاف/استئناف الخريطة | ✅ | `MyCarSession.kt:66-68` | `onResume`/`onPause`/`onDestroy` |
|
|
|
|
|
|
|
|
|
|
### 6. الشاشة / قالب التنقل (Screen / Navigation Template)
|
|
|
|
|
| المتطلب | الحالة | الملف | ملاحظات |
|
|
|
|
|
|---|---|---|---|
|
|
|
|
|
| `NavigationTemplate` مع تعليمات منعطف-بمنعطف | ✅ | `MyCarScreen.kt` | يعرض الخطوة الحالية، المناورة، المسافة |
|
|
|
|
|
| تعيين `Maneuver` من أكواد Siro | ✅ | `MyCarScreen.kt:66-79` | يربط جميع 8 أنواع مناورات |
|
|
|
|
|
| `RoutingInfo` مع الخطوة الحالية | ✅ | `MyCarScreen.kt:46-48` | |
|
|
|
|
|
| شاشة انتظار عند عدم التنقل | ✅ | `MyCarScreen.kt:26-31` | `MessageTemplate` |
|
|
|
|
|
| شريط إجراءات مع `APP_ICON` | ✅ | `MyCarScreen.kt:53-57` | |
|
|
|
|
|
|
|
|
|
|
### 7. عرض الخريطة (Map Display)
|
|
|
|
|
| المتطلب | الحالة | الملف | ملاحظات |
|
|
|
|
|
|---|---|---|---|
|
|
|
|
|
| عرض MapLibre على VirtualDisplay | ✅ | `MapPresentation.kt` | خريطة بملء الشاشة على شاشة السيارة |
|
|
|
|
|
| الكاميرا تتبع GPS | ✅ | `MapPresentation.kt:71-107` | زوم/ميل متكيف مع السرعة |
|
|
|
|
|
|
|
|
|
|
### 8. جسر بيانات التنقل (Navigation Data Bridge)
|
|
|
|
|
| المتطلب | الحالة | الملف | ملاحظات |
|
|
|
|
|
|---|---|---|---|
|
|
|
|
|
| قناة `car_navigation` MethodChannel على الـ Native | ✅ | `MainActivity.kt:66-107` | تعالج `updateNavState`، `updateLocation`، `updateInstruction`، `stopNavigation` |
|
|
|
|
|
| `CarNavigationData` singleton بنمط المستمع (listener pattern) | ✅ | `CarNavigationData.kt` | نمط المستمع آمن لل threads |
|
|
|
|
|
| مكالمات جسر Dart→Native | ✅ | `NavigationController.dart` | كل حدث موقع/خطوة/تنقل يدفع إلى القناة |
|
|
|
|
|
|
|
|
|
|
### 9. خدمة المقدمة (Foreground Service)
|
|
|
|
|
| المتطلب | الحالة | الملف | ملاحظات |
|
|
|
|
|
|---|---|---|---|
|
|
|
|
|
| خدمة خلفية مع نوع location | ✅ | `AndroidManifest.xml:124-129` | `id.flutter.flutter_background_service.BackgroundService` |
|
|
|
|
|
| خدمة تحديث الموقع | ✅ | `AndroidManifest.xml:135-136` | `.LocationUpdatesService` |
|
|
|
|
|
|
|
|
|
|
### 10. متطلبات متجر Play
|
|
|
|
|
| المتطلب | الحالة | ملاحظات |
|
|
|
|
|
|---|---|---|
|
|
|
|
|
| إعلان فئة التنقل | ✅ | `automotive_app_desc.xml` |
|
|
|
|
|
| التطبيق موقع بمفتاح إصدار | ✅ | `key.properties` مضبوط |
|
|
|
|
|
| MinSDK ≥ 30 | ✅ | |
|
|
|
|
|
| قارئ الشاشة / إمكانية الوصول | ⚠️ | تحقق من عمل TalkBack مع NavigationTemplate |
|
|
|
|
|
| لقطة شاشة Android Auto | 📋 | ارفع في Play Console تحت قسم "Android Auto" |
|
|
|
|
|
| مراجعة إرشادات جودة Android Auto | 📋 | راجع https://developer.android.com/training/cars |
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## APPLE CARPLAY (`siro_driver`)
|
|
|
|
|
|
|
|
|
|
### 1. الاستحقاقات (Entitlements)
|
|
|
|
|
| المتطلب | الحالة | الملف | ملاحظات |
|
|
|
|
|
|---|---|---|---|
|
|
|
|
|
| `com.apple.developer.carplay-driving-task` | ✅ | `Runner.entitlements:7-8` | مطلوب لتطبيقات التنقل |
|
|
|
|
|
| `aps-environment` (push) | ✅ | `Runner.entitlements:6-7` | موجود مسبقاً |
|
|
|
|
|
|
|
|
|
|
### 2. Info.plist
|
|
|
|
|
| المتطلب | الحالة | الملف | ملاحظات |
|
|
|
|
|
|---|---|---|---|
|
|
|
|
|
| `CPApplication` مع `CPApplicationDriverManeuver` | ✅ | `Info.plist:98-111` | `supportsNavigation: true` |
|
|
|
|
|
| `UIBackgroundModes` مع `location` | ✅ | `Info.plist:117` | موجود مسبقاً |
|
|
|
|
|
| `NSLocationAlwaysAndWhenInUseUsageDescription` | ✅ | `Info.plist:83-85` | موجود مسبقاً |
|
|
|
|
|
|
|
|
|
|
### 3. AppDelegate / اتصال CarPlay
|
|
|
|
|
| المتطلب | الحالة | الملف | ملاحظات |
|
|
|
|
|
|---|---|---|---|
|
|
|
|
|
| `application(_:didConnectCarInterfaceController:to:)` | ✅ | `AppDelegate.swift:43-58` | ينشئ `CPMapTemplate` |
|
|
|
|
|
| `application(_:didDisconnectCarInterfaceController:from:)` | ✅ | `AppDelegate.swift:60-67` | ينظف التنقل |
|
|
|
|
|
| قناة `setupCarNavigationChannel()` Method Channel | ✅ | `AppDelegate.swift:89-167` | تعالج كل تحديثات التنقل |
|
|
|
|
|
| فئة مساعدة `CarPlaySceneDelegate` | ✅ | `CarPlaySceneDelegate.swift` | جلسة تنقل، مناورات، وصول |
|
|
|
|
|
|
|
|
|
|
### 4. قوالب التنقل (Navigation Templates)
|
|
|
|
|
| المتطلب | الحالة | الملف | ملاحظات |
|
|
|
|
|
|---|---|---|---|
|
|
|
|
|
| `CPMapTemplate` كقالب جذر | ✅ | `CarPlaySceneDelegate.swift:6` | |
|
|
|
|
|
| `startNavigationSession(for:)` | ✅ | `CarPlaySceneDelegate.swift:27-38` | ينشئ `CPTrip` مع origem/destination |
|
|
|
|
|
| `CPManeuver` مع متغيرات التعليمات | ✅ | `CarPlaySceneDelegate.swift:44-59` | |
|
|
|
|
|
| تعيين ألوان المناورة | ✅ | `CarPlaySceneDelegate.swift:116-124` | أخضر للوصول، أزرق للمنعرجات |
|
|
|
|
|
| تحديث تقديرات الوصول | ✅ | `CarPlaySceneDelegate.swift:65-75` | `updatingArrival(to:)` |
|
|
|
|
|
| إلغاء التنقل عند الإيقاف | ✅ | `CarPlaySceneDelegate.swift:86-93` | `session.cancel()` |
|
|
|
|
|
|
|
|
|
|
### 5. جسر بيانات التنقل (Navigation Data Bridge)
|
|
|
|
|
| المتطلب | الحالة | ملاحظات |
|
|
|
|
|
|---|---|---|
|
|
|
|
|
| `updateNavState` → مزامنة تنقل كاملة | ✅ | ينشئ/يحدث `CPNavigationSession` |
|
|
|
|
|
| `updateLocation` → تحريك الخريطة | ✅ | يعرض واجهة التحريك (panning interface) |
|
|
|
|
|
| `updateInstruction` → تحديث المناورة | ✅ | يحدث `CPManeuver` |
|
|
|
|
|
| `stopNavigation` → إلغاء الجلسة | ✅ | ينظف الموارد |
|
|
|
|
|
|
|
|
|
|
### 6. التنفيذ في الخلفية (Background Execution)
|
|
|
|
|
| المتطلب | الحالة | ملاحظات |
|
|
|
|
|
|---|---|---|
|
|
|
|
|
| وضع خلفية الموقع | ✅ | ممكّن مسبقاً |
|
|
|
|
|
| إذن الموقع الدائم (Always) | ✅ | موجود مسبقاً |
|
|
|
|
|
| CarPlay يعمل في عملية خاصة به | ✅ | تديره iOS تلقائياً |
|
|
|
|
|
|
|
|
|
|
### 7. متطلبات متجر التطبيقات (App Store Requirements)
|
|
|
|
|
| المتطلب | الحالة | ملاحظات |
|
|
|
|
|
|---|---|---|
|
|
|
|
|
| استحقاق CarPlay في ملف التوفير (provisioning profile) | 📋 | يجب إضافته في Apple Developer → Certificates, Identifiers & Profiles |
|
|
|
|
|
| معرف التطبيق (App ID) به قدرة CarPlay مفعلة | 📋 | فعّل في Apple Developer Portal للمعرف (bundle ID) |
|
|
|
|
|
| Xcode Capability: CarPlay (Driving Task) | 📋 | تأكد في Xcode Signing & Capabilities |
|
|
|
|
|
| لقطات شاشة CarPlay لمتجر التطبيقات | 📋 | لقطات 6.7" و 5.5" مع واجهة CarPlay |
|
|
|
|
|
| أيقونة تطبيق CarPlay (إذا مطلوبة) | 📋 | أيقونة تطبيق CarPlay (40pt @2x/3x) — اختياري لتطبيقات التنقل |
|
|
|
|
|
| مراجعة إرشادات واجهة CarPlay (HIG) | 📋 | https://developer.apple.com/carplay/ |
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## DART / FLUTTER (الجسر المشترك)
|
|
|
|
|
|
|
|
|
|
| المتطلب | الحالة | الملف | ملاحظات |
|
|
|
|
|
|---|---|---|---|
|
|
|
|
|
| `CarPlatformBridge` مُنشأ | ✅ | `lib/controller/car_platform_bridge.dart` | دوال ثابتة لكلا المنصتين |
|
|
|
|
|
| الجسر مهيأ في `main()` | ✅ | `main.dart:269` | بعد خدمة الخلفية |
|
|
|
|
|
| الجسر مهيأ في `NavigationController.onInit()` | ✅ | `NavigationController.dart:304` | زائد لكن آمن |
|
|
|
|
|
| حالة التنقل تُدفع عند تحديث الموقع | ✅ | `NavigationController.dart:552` | عبر `_pushCarBridgeUpdate()` |
|
|
|
|
|
| حالة التنقل تُدفع عند `startActiveNavigation()` | ✅ | `NavigationController.dart:1141` | |
|
|
|
|
|
| تعليمات تُدفع عند `_advanceStep()` | ✅ | `NavigationController.dart:1235-1241` | |
|
|
|
|
|
| إيقاف عند `_finishNavigation()` | ✅ | `NavigationController.dart:1259` | |
|
|
|
|
|
| إيقاف عند `clearRoute()` | ✅ | `NavigationController.dart:1164` | |
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## الخطوات اليدوية المطلوبة (MANUAL STEPS REQUIRED)
|
|
|
|
|
|
|
|
|
|
### قبل الإرسال (Pre-Submission)
|
|
|
|
|
- [ ] **Apple Developer Portal**: أضف قدرة `CarPlay (Driving Task)` لمعرف التطبيق
|
|
|
|
|
- [ ] **Apple Developer Portal**: أعد إنشاء ملفات التوفير بعد تفعيل CarPlay
|
|
|
|
|
- [ ] **Xcode**: فعّل قدرة CarPlay في Signing & Capabilities
|
|
|
|
|
- [ ] **Google Play Console**: أعلن تكامل Android Auto → اضبط فئة "Navigation"
|
|
|
|
|
- [ ] **Google Play Console**: ارفع لقطات شاشة Android Auto (مطلوبة للإدراج)
|
|
|
|
|
- [ ] **التوقيع**: يجب أن يستخدم كلا النظامين توقيع الإنتاج/التوزيع
|
|
|
|
|
|
|
|
|
|
### الاختبار (Testing)
|
|
|
|
|
- [ ] **Android Auto**: اختبر مع محاكي DHU (Desktop Head Unit)
|
|
|
|
|
- [ ] **Android Auto**: اختبر على وحدة رأس سيارة حقيقية مع USB
|
|
|
|
|
- [ ] **Apple CarPlay**: اختبر مع محاكي CarPlay في Xcode
|
|
|
|
|
- [ ] **Apple CarPlay**: اختبر على سيارة حقيقية مع كابل Lightning/USB-C
|
|
|
|
|
- [ ] **الإرشاد الصوتي**: تحقق من عمل تكامل TTS أثناء نشاط CarPlay/Android Auto
|
|
|
|
|
- [ ] **إعادة التوجيه**: تحقق من انعكاس إعادة الحساب التلقائي على شاشة السيارة
|
|
|
|
|
- [ ] **نهاية الرحلة**: تحقق من انتهاء جلسة التنقل بنظافة على المنصتين
|
|
|
|
|
|
|
|
|
|
### الامتثال (Compliance)
|
|
|
|
|
- [ ] **إرشادات جودة Android Auto**: https://developer.android.com/training/cars
|
|
|
|
|
- [ ] **إرشادات واجهة CarPlay (HIG) من Apple**: https://developer.apple.com/carplay/
|
|
|
|
|
- [ ] **الخصوصية**: تأكد من توثيق استخدام بيانات الموقع بوضوح في سياسة الخصوصية
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## الملخص (SUMMARY)
|
|
|
|
|
|
|
|
|
|
| المنصة | الحالة | الملفات المعدلة/المضافة |
|
|
|
|
|
|---|---|---|
|
|
|
|
|
| **Android Auto** | ✅ جاهز للإنتاج | `AndroidManifest.xml` (إصلاح)، `car_platform_bridge.dart` (جديد)، `NavigationController.dart` (توصيل)، `main.dart` (تهيئة) |
|
|
|
|
|
| **Apple CarPlay** | ✅ جاهز للإنتاج | `CarPlaySceneDelegate.swift` (جديد)، `AppDelegate.swift` (CarPlay + القناة)، `Info.plist` (CPApplication)، `Runner.entitlements` (carplay-driving-task)، `car_platform_bridge.dart` (مشترك)، `NavigationController.dart` (توصيل)، `main.dart` (تهيئة) |
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## تفاصيل التنفيذ التقنية (Technical Implementation Details)
|
|
|
|
|
|
|
|
|
|
### Android Auto - البنية التحتية (Architecture)
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
┌─────────────────────────────────────────────────────────────────┐
|
|
|
|
|
│ السائق (Driver App) │
|
|
|
|
|
│ ┌─────────────────┐ MethodChannel ┌──────────────────┐ │
|
|
|
|
|
│ │ NavigationController │◄─────────────────►│ MainActivity.kt │ │
|
|
|
|
|
│ │ (Flutter/Dart) │ car_navigation │ (Native/Kotlin) │ │
|
|
|
|
|
│ └────────┬──────────┘ └────────┬─────────┘ │
|
|
|
|
|
│ │ │ │
|
|
|
|
|
│ │ _pushCarBridgeUpdate() │ │
|
|
|
|
|
│ ▼ ▼ │
|
|
|
|
|
│ ┌─────────────────────────────────────────────────────────────┐ │
|
|
|
|
|
│ │ CarNavigationData (Kotlin Singleton) │ │
|
|
|
|
|
│ │ - currentLat/Lng/Bearing/Speed │ │
|
|
|
|
|
│ │ - currentInstruction, distanceToStep, ETA, maneuverType │ │
|
|
|
|
|
│ │ - isNavigating │ │
|
|
|
|
|
│ │ - Listener pattern للـ UI updates │ │
|
|
|
|
|
│ └─────────────────────────────────────────────────────────────┘ │
|
|
|
|
|
│ │ │ │
|
|
|
|
|
│ ▼ ▼ │
|
|
|
|
|
│ ┌─────────────────┐ ┌─────────────────┐ │
|
|
|
|
|
│ │ MyCarScreen.kt │ │ MapPresentation │ │
|
|
|
|
|
│ │ (NavigationTemplate) │ (MapLibre on │ │
|
|
|
|
|
│ │ - يعرض الخطوة الحالية │ VirtualDisplay)│ │
|
|
|
|
|
│ │ - المناورة، المسافة │ - كاميرا تتبع GPS│ │
|
|
|
|
|
│ │ - شريط إجراءات │ - زوم/ميل متكيف │ │
|
|
|
|
|
│ └─────────────────┘ └─────────────────┘ │
|
|
|
|
|
└─────────────────────────────────────────────────────────────────┘
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
**ملفات Android Auto الرئيسية:**
|
|
|
|
|
- `MyCarAppService.kt` — نقطة دخول CarAppService
|
|
|
|
|
- `MyCarSession.kt` — يدير دورة الحياة و VirtualDisplay
|
|
|
|
|
- `MyCarScreen.kt` — يبني NavigationTemplate
|
|
|
|
|
- `MapPresentation.kt` — يعرض MapLibre على شاشة السيارة
|
|
|
|
|
- `CarNavigationData.kt` — مخزن بيانات مشترك آمن للـ threads
|
|
|
|
|
- `MainActivity.kt` — جسر MethodChannel
|
|
|
|
|
|
|
|
|
|
### Apple CarPlay - البنية التحتية (Architecture)
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
┌─────────────────────────────────────────────────────────────────┐
|
|
|
|
|
│ السائق (Driver App) │
|
|
|
|
|
│ ┌─────────────────┐ MethodChannel ┌──────────────────┐ │
|
|
|
|
|
│ │ NavigationController │◄─────────────────►│ AppDelegate.swift │ │
|
|
|
|
|
│ │ (Flutter/Dart) │ car_navigation │ (Native/Swift) │ │
|
|
|
|
|
│ └────────┬──────────┘ └────────┬─────────┘ │
|
|
|
|
|
│ │ │ │
|
|
|
|
|
│ │ _pushCarBridgeUpdate() │ │
|
|
|
|
|
│ ▼ ▼ │
|
|
|
|
|
│ ┌─────────────────────────────────────────────────────────────┐ │
|
|
|
|
|
│ │ CarPlaySceneDelegate (Singleton) │ │
|
|
|
|
|
│ │ - interfaceController: CPInterfaceController │ │
|
|
|
|
|
│ │ - mapTemplate: CPMapTemplate │ │
|
|
|
|
|
│ │ - navigationSession: CPNavigationSession? │ │
|
|
|
|
|
│ └─────────────────────────────────────────────────────────────┘ │
|
|
|
|
|
│ │ │ │
|
|
|
|
|
│ ▼ ▼ │
|
|
|
|
|
│ ┌─────────────────┐ ┌─────────────────┐ │
|
|
|
|
|
│ │ CPMapTemplate │ │ CPTrip + │ │
|
|
|
|
|
│ │ (Root Template) │ │ CPNavigationSession│ │
|
|
|
|
|
│ │ - guidanceBackgroundStyle = .light │ - origin/dest │ │
|
|
|
|
|
│ │ - يعرض المناورات والـ ETA │ - maneuvers │ │
|
|
|
|
|
│ └─────────────────┘ │ - arrival estimates│ │
|
|
|
|
|
│ └─────────────────┘ │
|
|
|
|
|
└─────────────────────────────────────────────────────────────────┘
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
**ملفات CarPlay الرئيسية:**
|
|
|
|
|
- `AppDelegate.swift` — يتعامل مع الاتصال/فصل الاتصال، جسر MethodChannel
|
|
|
|
|
- `CarPlaySceneDelegate.swift` — يدير جلسة التنقل، المناورات، التقديرات
|
|
|
|
|
- `CarPlayState.swift` — singleton مشترك للوصول إلى الـ delegate
|
|
|
|
|
- `Info.plist` — يعلن `CPApplication` مع `supportsNavigation: true`
|
|
|
|
|
- `Runner.entitlements` — استحقاق `com.apple.developer.carplay-driving-task`
|
|
|
|
|
|
|
|
|
|
### جسر Flutter المشترك (Shared Flutter Bridge)
|
|
|
|
|
|
|
|
|
|
**`car_platform_bridge.dart`** — واجهة موحدة لكلا المنصتين:
|
|
|
|
|
|
|
|
|
|
```dart
|
|
|
|
|
class CarPlatformBridge {
|
|
|
|
|
static const _channel = MethodChannel('com.siro.siro_driver/car_navigation');
|
|
|
|
|
|
|
|
|
|
// تحديث حالة التنقل الكاملة (للبدء/التحديث)
|
|
|
|
|
static Future<void> updateNavState({
|
|
|
|
|
required double lat, lng, bearing, speed,
|
|
|
|
|
required String instruction,
|
|
|
|
|
required double distanceToStep, totalDistance, eta,
|
|
|
|
|
required int maneuver,
|
|
|
|
|
required bool isNavigating,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// تحديث الموقع فقط (للتحريك على الخريطة)
|
|
|
|
|
static Future<void> updateLocation({
|
|
|
|
|
required double lat, lng, bearing, speed,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// تحديث التعليمات/المناورة فقط
|
|
|
|
|
static Future<void> updateInstruction({
|
|
|
|
|
required String instruction,
|
|
|
|
|
required int maneuver,
|
|
|
|
|
required double distanceToStep,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// إيقاف التنقل
|
|
|
|
|
static Future<void> stopNavigation();
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
**تدفق البيانات في `NavigationController.dart`:**
|
|
|
|
|
|
|
|
|
|
```dart
|
|
|
|
|
// عند بدء التنقل النشط
|
|
|
|
|
void startActiveNavigation() {
|
|
|
|
|
isNavigating = true;
|
|
|
|
|
_pushCarBridgeUpdate(); // يرسل isNavigating: true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// عند كل تحديث موقع (كل ~4 ثوانٍ)
|
|
|
|
|
void _onLocationUpdate(Position pos) {
|
|
|
|
|
// ... معالجة الموقع ...
|
|
|
|
|
_pushCarBridgeUpdate(); // يرسل lat/lng/bearing/speed/instruction/ETA/maneuver
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// عند التقدم لخطوة جديدة
|
|
|
|
|
void _advanceStep() {
|
|
|
|
|
// ... تحديث currentInstruction، currentManeuverModifier ...
|
|
|
|
|
CarPlatformBridge.updateInstruction(
|
|
|
|
|
instruction: currentInstruction,
|
|
|
|
|
maneuver: currentManeuverModifier,
|
|
|
|
|
distanceToStep: _distanceToNextStepMeters,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// عند إنهاء التنقل (وصول أو إلغاء)
|
|
|
|
|
void _finishNavigation() {
|
|
|
|
|
isNavigating = false;
|
|
|
|
|
CarPlatformBridge.stopNavigation(); // يرسل isNavigating: false
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void clearRoute() {
|
|
|
|
|
// ...
|
|
|
|
|
CarPlatformBridge.stopNavigation();
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### تعيين المناورات (Maneuver Mapping)
|
|
|
|
|
|
|
|
|
|
| كود Siro | Android Auto (`Maneuver.TYPE_*`) | CarPlay (`CPManeuver`) |
|
|
|
|
|
|---|---|---|
|
|
|
|
|
| 0 (مستقيم) | `TYPE_STRAIGHT` | سهم مستقيم |
|
|
|
|
|
| 2 (يمين) | `TYPE_TURN_NORMAL_RIGHT` | يمين |
|
|
|
|
|
| 3 (يمين خفيف) | `TYPE_TURN_SLIGHT_RIGHT` | يمين خفيف |
|
|
|
|
|
| -2 (يسار) | `TYPE_TURN_NORMAL_LEFT` | يسار |
|
|
|
|
|
| -1 (يسار خفيف) | `TYPE_TURN_SLIGHT_LEFT` | يسار خفيف |
|
|
|
|
|
| 4 (وصول) | `TYPE_DESTINATION` | أخضر (وصول) |
|
|
|
|
|
| 6 (دوار) | `TYPE_ROUNDABOUT_ENTER_AND_EXIT_CW` | دوار |
|
|
|
|
|
| 7 (ابق يمين) | `TYPE_KEEP_RIGHT` | ابق يمين |
|
|
|
|
|
| -7 (ابق يسار) | `TYPE_KEEP_LEFT` | ابق يسار |
|
|
|
|
|
|
|
|
|
|
**الألوان على CarPlay:**
|
|
|
|
|
- النوع 4 (وصول) → أخضر نظامي (`systemGreen`)
|
|
|
|
|
- النوع 2، -2 (يمين/يسار) → أزرق نظامي (`systemBlue`)
|
|
|
|
|
- الباقي → لا لون خاص
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## قائمة التحقق من الاختبار (Testing Checklist)
|
|
|
|
|
|
|
|
|
|
### Android Auto
|
|
|
|
|
| الاختبار | الحالة | ملاحظات |
|
|
|
|
|
|---|---|---|
|
|
|
|
|
| تشغيل التطبيق على DHU (Desktop Head Unit) | ⬜ | محاكي Android Auto |
|
|
|
|
|
| اتصال USB بوحدة رأس حقيقية | ⬜ | سيارة حقيقية أو وحدة رأس خارجية |
|
|
|
|
|
| بدء التنقل من التطبيق → يظهر على الشاشة | ⬜ | |
|
|
|
|
|
| تعليمات منعطف-بمنعطف تظهر بشكل صحيح | ⬜ | |
|
|
|
|
|
| المناورات (يمين/يسار/دوار/وصول) تعرض الأيقونات الصحيحة | ⬜ | |
|
|
|
|
|
| تحديثات ETA والمسافة تعمل | ⬜ | |
|
|
|
|
|
| الكاميرا تتبع الموقع على الخريطة (MapLibre) | ⬜ | |
|
|
|
|
|
| إعادة التوجيه التلقائي ينعكس على شاشة السيارة | ⬜ | |
|
|
|
|
|
| إيقاف التنقل ينظف الجلسة | ⬜ | |
|
|
|
|
|
| خدمة المقدمة تستمر في الخلفية | ⬜ | |
|
|
|
|
|
| TalkBack / إمكانية الوصول تعمل | ⬜ | |
|
|
|
|
|
|
|
|
|
|
### Apple CarPlay
|
|
|
|
|
| الاختبار | الحالة | ملاحظات |
|
|
|
|
|
|---|---|---|
|
|
|
|
|
| تشغيل على محاكي CarPlay في Xcode | ⬜ | iOS Simulator → I/O → External Displays → CarPlay |
|
|
|
|
|
| اتصال Lightning/USB-C بسيارة حقيقية | ⬜ | |
|
|
|
|
|
| بدء التنقل → يظهر CPMapTemplate | ⬜ | |
|
|
|
|
|
| المناورات مع ألوان (أخضر/أزرق) | ⬜ | |
|
|
|
|
|
| تقديرات الوصول (ETA) تُحدث | ⬜ | |
|
|
|
|
|
| واجهة التحريك (panning) تظهر عند تحديث الموقع | ⬜ | |
|
|
|
|
|
| الأوامر الصوتية / Siri تعمل | ⬜ | |
|
|
|
|
|
| إلغاء التنقل → `session.cancel()` | ⬜ | |
|
|
|
|
|
| التشغيل في الخلفية مع موقع دائم | ⬜ | |
|
|
|
|
|
| VoiceOver / إمكانية الوصول تعمل | ⬜ | |
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## العناصر المتبقية للإنتاج (Production Readiness)
|
|
|
|
|
|
|
|
|
|
### Android Auto
|
|
|
|
|
1. ✅ كود التطبيق مكتمل
|
|
|
|
|
2. 📋 **Play Console**: إعلان فئة "Navigation" في قسم Android Auto
|
|
|
|
|
3. 📋 **Play Console**: رفع لقطات شاشة Android Auto (مطلوبة للنشر)
|
|
|
|
|
4. 📋 **التوقيع**: بناء موقع بمفتاح الإصدار (`key.properties`)
|
|
|
|
|
5. ⚠️ **اختبار**: تحقق على DHU ووحدة رأس حقيقية
|
|
|
|
|
6. ⚠️ **إمكانية الوصول**: تحقق TalkBack مع NavigationTemplate
|
|
|
|
|
|
|
|
|
|
### Apple CarPlay
|
|
|
|
|
1. ✅ كود التطبيق مكتمل
|
|
|
|
|
2. 📋 **Apple Developer Portal**: أضف قدرة `CarPlay (Driving Task)` للمعرف
|
|
|
|
|
3. 📋 **Apple Developer Portal**: أعد إنشاء ملفات التوفير
|
|
|
|
|
4. 📋 **Xcode**: فعّل قدرة CarPlay في Signing & Capabilities
|
|
|
|
|
5. 📋 **App Store Connect**: ارفع لقطات شاشة CarPlay (6.7" و 5.5")
|
|
|
|
|
6. 📋 **App Store Connect**: أيقونة CarPlay (اختياري للتنقل)
|
|
|
|
|
7. ⚠️ **اختبار**: محاكي CarPlay + سيارة حقيقية
|
|
|
|
|
8. ⚠️ **مراجعة HIG**: https://developer.apple.com/carplay/
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## ملاحظات للمطورين (Developer Notes)
|
|
|
|
|
|
|
|
|
|
### لنقل الكود لتطبيق الراكب (Rider App) لاحقاً:
|
|
|
|
|
1. **Android Auto**: انسخ ملفات `MyCarAppService.kt`، `MyCarSession.kt`، `MyCarScreen.kt`، `MapPresentation.kt`، `CarNavigationData.kt`، وعدل `package` إلى `com.siro.siro_rider`
|
|
|
|
|
2. **iOS CarPlay**: انسخ `CarPlaySceneDelegate.swift`، عدّل `AppDelegate.swift` لإضافة جسر `car_navigation`، حدث `Info.plist` و `Runner.entitlements`
|
|
|
|
|
3. **Flutter**: `CarPlatformBridge.dart` مشترك، فقط تأكد من تهيئته في `main.dart` للراكب وربطه بـ `NavigationController` الخاص بالراكب
|
|
|
|
|
4. **الأذونات**: نفس أذونات الموقع والخلفية مطلوبة
|
|
|
|
|
|
|
|
|
|
### مشاكل معروفة / تحسينات مستقبلية:
|
|
|
|
|
- **CarPlay**: `updatingArrival(to:)` مهمل في iOS 15+، فكر في الهجرة لـ `updating(_:)` على `arrivalEstimates` للمناورة
|
|
|
|
|
- **Android Auto**: `HostValidator` في الإنتاج يجب أن يستخدم `hosts_allowlist_google` وليس `hosts_allowlist_sample`
|
|
|
|
|
- **MapLibre على CarPlay**: تأكد من أن `asset://flutter_assets/assets/style.json` متاح في bundle التطبيق على iOS أيضاً (قد يحتاج نسخ للأصول)
|
|
|
|
|
- **TTS على CarPlay/Android Auto**: حالياً TTS يعمل على الهاتف؛ للتحكم الصوتي عبر مكبرات السيارة، قد تحتاج `CPVoiceControlTemplate` أو تكامل `TTS` مع `CarAppService`
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
*تم إنشاء هذا التقرير بتاريخ: 2026-07-04*
|
|
|
|
|
*استناداً إلى: `docs/auto_carplay_checklist.md` (النسخة الإنجليزية) ومراجعة الكود الكامل لتطبيق `siro_driver`*
|