Instrument Cluster API

يمكنك استخدام Instrument Cluster API (إحدى واجهات برمجة التطبيقات في Android) لعرض تطبيقات التنقّل، بما في ذلك "خرائط Google"، على شاشة ثانوية في السيارة، مثل الشاشة الموجودة خلف عجلة القيادة على لوحة العدادات. توضّح هذه الصفحة كيفية إنشاء خدمة للتحكّم في هذه الشاشة الثانوية ودمج الخدمة مع CarService لكي تتمكّن تطبيقات التنقّل من عرض واجهة مستخدم.

المصطلحات

تُستخدَم المصطلحات التالية في هذه الصفحة.

‫CarInstrumentClusterManager
هي إحدى حالات CarManager التي تتيح للتطبيقات الخارجية تشغيل نشاط على مجموعة العدادات وتلقّي معاودة الاتصال عندما تكون مجموعة العدادات جاهزة لعرض الأنشطة.
‫CarManager
هي الصنف الأساسي لجميع المدراء الذين تستخدمهم التطبيقات الخارجية للتفاعل مع الخدمات الخاصة بالسيارة التي تنفّذها `CarService`.
‫CarService
هي خدمة "نظام Android الأساسي" التي توفّر التواصل بين التطبيقات الخارجية (بما في ذلك "خرائط Google") والميزات الخاصة بالسيارة، مثل الوصول إلى مجموعة العدادات.
الوجهة
هي الوجهة النهائية التي ستنتقل إليها المركبة.
الوقت المقدَّر للوصول (ETA)
هو الوقت المقدَّر للوصول إلى وجهة معيّنة.
الوحدة الرئيسية (HU)
هي وحدة الحوسبة الأساسية المضمّنة في السيارة. تشغّل الوحدة الرئيسية جميع رموز Android وتكون متصلة بالشاشة المركزية في السيارة.
مجموعة العدادات
هي شاشة ثانوية تقع خلف عجلة القيادة وبين عدادات السيارة. يمكن أن تكون هذه الشاشة وحدة حوسبة مستقلة متصلة بالوحدة الرئيسية من خلال الشبكة الداخلية للسيارة (CAN bus) أو شاشة ثانوية متصلة بالوحدة الرئيسية.
‫InstrumentClusterRenderingService
هي الفئة الأساسية للخدمة المستخدَمة للتفاعل مع شاشة مجموعة العدادات. على المصنّعين الأصليين للأجهزة (OEM) توفير إضافة لهذه الفئة تتفاعل مع الأجهزة الخاصة بالمصنّع الأصلي للجهاز.
تطبيق KitchenSink
هو تطبيق تجريبي مضمّن في Android Automotive.
مسار الجولة
هو مسار محدّد تنتقل فيه المركبة للوصول إلى وجهة معيّنة.
خدمة "سينغلتون"
هي خدمة Android تتضمّن السمة android:singleUser. في أي وقت، يتم تشغيل حالة واحدة على الأكثر من الخدمة على نظام Android.

المتطلبات الأساسية

قبل المتابعة، تأكَّد من توفّر العناصر التالية:

  • بيئة تطوير Android : لإعداد بيئة تطوير Android ، يُرجى الاطّلاع على متطلبات الإصدار.
  • تنزيل رمز المصدر لنظام Android : يمكنك الحصول على أحدث إصدار من رمز المصدر لنظام Android من فرع pi-car-release (أو إصدار أحدث) على https://android.googlesource.com.
  • الوحدة الرئيسية (HU) : يجب أن يكون لديك جهاز Android قادر على تشغيل Android 9 (أو إصدار أحدث). يجب أن يتضمّن هذا الجهاز شاشة خاصة به وأن يكون قادرًا على عرض الإصدارات الجديدة من Android على الشاشة.
  • مجموعة العدادات هي إحدى الحالات التالية:
    • شاشة ثانوية فعلية متصلة بالوحدة الرئيسية : إذا كان جهازك ونواة النظام يتيحان إدارة شاشات متعددة.
    • وحدة مستقلة : أي وحدة حوسبة متصلة بالوحدة الرئيسية من خلال اتصال شبكة، وقادرة على تلقّي وعرض بث فيديو على شاشتها الخاصة.
    • شاشة محاكاة : أثناء التطوير، يمكنك استخدام إحدى بيئات المحاكاة التالية:
      • شاشات ثانوية محاكاة : لتفعيل شاشة ثانوية محاكاة على أي توزيعة AOSP Android، انتقِل إلى إعدادات خيارات المطوّرين في تطبيق الإعدادات على النظام، ثم اختَر محاكاة الشاشات الثانوية. يعادل هذا الإعداد توصيل شاشة ثانوية فعلية، مع العلم أنّ هذه الشاشة تكون فوق الشاشة الأساسية.
      • مجموعة عدادات محاكاة : يوفّر محاكي Android المضمّن في AAOS خيارًا لعرض مجموعة عدادات باستخدام ClusterRenderingService.

بنية التكامل

مكوّنات التكامل

يتألف أي تكامل لواجهة برمجة التطبيقات Instrument Cluster API من المكوّنات الثلاثة التالية:

  • CarService
  • تطبيقات التنقّل
  • خدمة مجموعة العدادات الخاصة بالمصنّع الأصلي للجهاز

مكوّنات الدمج

‫CarService

تتوسّط CarService بين تطبيقات التنقّل والسيارة، ما يضمن تفعيل تطبيق تنقّل واحد فقط في أي وقت، وأنّ التطبيقات التي لديها إذن android.car.permission.CAR_INSTRUMENT_CLUSTER_CONTROL فقط هي التي يمكنها إرسال البيانات إلى السيارة.

تعمل CarService على تهيئة جميع الخدمات الخاصة بالسيارة وتوفّر إمكانية الوصول إلى هذه الخدمات من خلال سلسلة من المدراء. للتفاعل مع الخدمات، يمكن للتطبيقات التي يتم تشغيلها في السيارة الوصول إلى هؤلاء المدراء.

لتنفيذ مجموعة العدادات، على المصنّعين الأصليين للأجهزة في مجال السيارات إنشاء عملية تنفيذ مخصّصة لـ InstrumentClusterRendererService وتعديل ClusterRenderingService.

عند عرض مجموعة عدادات، تقرأ CarService مفتاح InstrumentClusterRendererService في ClusterRenderingService أثناء عملية التشغيل لتحديد موقع عملية تنفيذ InstrumentClusterService. في AOSP، يشير هذا الإدخال إلى خدمة عرض نموذج تنفيذ مجموعة العدادات لواجهة برمجة التطبيقات Navigation State API:

<string name="instrumentClusterRendererService">
android.car.cluster/.ClusterRenderingService
</string>

تتم تهيئة الخدمة المشار إليها في هذا الإدخال وربطها بـ CarService. عندما تطلب تطبيقات التنقّل، مثل "خرائط Google"، عنصر CarInstrumentClusterManager، توفّر CarService مديرًا يحدّث حالة مجموعة العدادات من InstrumentClusterRenderingService المرتبطة. (في هذه الحالة، يشير مصطلح مرتبطة إلى خدمات Android.)

خدمة مجموعة العدادات

على المصنّعين الأصليين للأجهزة إنشاء حزمة Android (APK) تحتوي على فئة فرعية من ClusterRenderingService.

تخدم هذه الفئة غرضَين:

  • توفّر واجهة بين Android وجهاز عرض مجموعة العدادات (وهو الغرض من هذه الصفحة).
  • تتلقّى وتعرض آخر المعلومات عن حالة التنقّل، مثل إرشادات التنقّل المفصّلة.

بالنسبة إلى الغرض الأول، يجب أن تهيئ عمليات تنفيذ InstrumentClusterRendererService الشاشة الثانوية المستخدَمة لعرض المعلومات على الشاشات في مقصورة السيارة، وأن تنقل هذه المعلومات إلى CarService من خلال استدعاء الطريقتَين InstrumentClusterRendererService.setClusterActivityOptions() و InstrumentClusterRendererService.setClusterActivityState().

بالنسبة إلى الوظيفة الثانية، يجب أن توفّر خدمة مجموعة العدادات عملية تنفيذ لواجهة ClusterRenderingService تتلقّى أحداث آخر المعلومات عن حالة التنقّل، والتي يتم ترميزها على أنّها eventType وبيانات الحدث المرمّزة في حزمة.

تسلسل التكامل

يوضّح المخطّط البياني التالي عملية تنفيذ حالة التنقّل التي تعرض آخر المعلومات:

تسلسل الدمج

في هذا الرسم التوضيحي، تشير الألوان إلى ما يلي:

  • الأصفر : CarService وCarNavigationStatusManager المقدَّمان من نظام Android الأساسي. لمزيد من المعلومات، يُرجى الاطّلاع على Car و CAR_NAVIGATION_SERVICE.
  • السماوي : InstrumentClusterRendererService نفّذه المصنّع الأصلي للجهاز.
  • الأرجواني : تطبيق التنقّل الذي نفّذته Google والمطوّرون الخارجيون.
  • الأخضر : CarAppFocusManager. لمزيد من المعلومات، يُرجى الاطّلاع على استخدام CarAppFocusManager API أدناه و CarAppFocusManager.

يتّبع تدفق معلومات "حالة التنقّل" التسلسل التالي:

  1. تهيئ CarService عنصر InstrumentClusterRenderingService.
  2. أثناء التهيئة، يحدّث InstrumentClusterRenderingService CarService بما يلي:
    1. خصائص شاشة مجموعة العدادات، مثل الحدود غير المحجوبة (يمكنك الاطّلاع على مزيد من التفاصيل حول الحدود غير المحجوبة لاحقًا).
    2. خيارات النشاط اللازمة لتشغيل الأنشطة داخل شاشة مجموعة العدادات. لمزيد من المعلومات، يُرجى الاطّلاع على ActivityOptions.
  3. تطبيق تنقّل (مثل "خرائط Google" لنظام Android Automotive أو أي تطبيق خرائط لديه الأذونات المطلوبة):
    1. يحصل على CarAppFocusManager باستخدام فئة Car من car-lib.
    2. قبل بدء الاتجاهات المفصّلة، يستدعي CarAppFocusManager.requestFocus() لتمرير CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION كالمَعلمة appType.
  4. تنقل CarAppFocusManager هذا الطلب إلى CarService. إذا تم منح الإذن، تفحص CarService حزمة تطبيق التنقّل وتحدّد موقع نشاط تم وضع علامة عليه بالفئة android.car.cluster.NAVIGATION.
  5. إذا تم العثور على النشاط، يستخدم تطبيق التنقّل ActivityOptions الذي أبلغت عنه InstrumentClusterRenderingService لتشغيل النشاط، ويتضمّن خصائص شاشة مجموعة العدادات كبيانات إضافية في الـ intent.

دمج واجهة برمجة التطبيقات

يجب أن تتّسم عملية تنفيذ InstrumentClusterRenderingService بما يلي:

  • يجب تحديدها كخدمة "سينغلتون" من خلال إضافة القيمة التالية إلى AndroidManifest.xml. هذا ضروري لضمان تشغيل نسخة واحدة من خدمة مجموعة العدادات، حتى أثناء التهيئة وتبديل المستخدم:
    android:singleUser="true"
  • يجب أن يكون لديك إذن النظام BIND_INSTRUMENT_CLUSTER_RENDERER_SERVICE. يضمن ذلك أنّ CarService لا تربط إلا خدمة عرض مجموعة العدادات المضمّنة كجزء من صورة نظام Android:
    <uses-permission android:name="android.car.permission.BIND_INSTRUMENT_CLUSTER_RENDERER_SERVICE"/>
    

تنفيذ InstrumentClusterRenderingService

لإنشاء الخدمة، اتّبِع الخطوات التالية:

  1. اكتب فئة تستند إلى ClusterRenderingService ، ثم أضِف إدخالاً مطابقًا إلى ملف AndroidManifest.xml تتحكّم هذه الفئة في شاشة مجموعة العدادات ويمكنها (اختياريًا) عرض بيانات Navigation State API.
  2. أثناء onCreate()، استخدِم هذه الخدمة لتهيئة التواصل مع جهاز العرض. تشمل الخيارات ما يلي:
    • تحديد الشاشة الثانوية التي سيتم استخدامها لمجموعة العدادات.
    • إنشاء شاشة افتراضية لكي يعرض تطبيق مجموعة العدادات الصورة المعروضة وينقلها إلى وحدة خارجية (باستخدام تنسيق بث فيديو، مثل H.264).
  3. عندما تكون الشاشة الموضّحة أعلاه جاهزة، يجب أن تستدعي هذه الخدمة InstrumentClusterRenderingService#setClusterActivityLaunchOptions() لتحديد ActivityOptions الدقيقة التي يجب استخدامها لعرض النشاط على مجموعة العدادات. استخدِم هذه المَعلمات:
    • category. ClusterRenderingService.
    • ActivityOptions. هي حالة ActivityOptions يمكن استخدامها لتشغيل نشاط في مجموعة العدادات. على سبيل المثال، من نموذج تنفيذ مجموعة العدادات على AOSP:
      getService().setClusterActivityLaunchOptions(
        CATEGORY_NAVIGATION,
        ActivityOptions.makeBasic()
            .setLaunchDisplayId(displayId));
  4. عندما تكون مجموعة العدادات جاهزة لعرض الأنشطة، يجب أن تستدعي هذه الخدمة InstrumentClusterRenderingService#setClusterActivityState(). استخدِم هذه المَعلمات:
    • category ClusterRenderingService
    • state حزمة تم إنشاؤها باستخدام ClusterRenderingService. احرص على توفير هذه البيانات:
      • visible تحدّد هذه المَعلمة مجموعة العدادات على أنّها مرئية وجاهزة لـ عرض المحتوى.
      • unobscuredBounds هو مستطيل يحدّد المنطقة داخل شاشة مجموعة العدادات التي يكون من الآمن عرض المحتوى فيها. على سبيل المثال، المناطق التي تغطيها المؤشرات والمقاييس.
  5. يمكنك إلغاء طريقة Service#dump() وعرض معلومات الحالة المفيدة لتحديد الأخطاء وحلّها (يُرجى الاطّلاع على dumpsys لمزيد من المعلومات).

نموذج تنفيذ InstrumentClusterRenderingService

يقدّم المثال التالي عملية تنفيذ InstrumentClusterRenderingService، التي تنشئ VirtualDisplay لعرض محتوى مجموعة العدادات على شاشة فعلية بعيدة.

بدلاً من ذلك، يمكن لهذا الرمز تمرير displayId لشاشة ثانوية فعلية متصلة بالوحدة الرئيسية، إذا كان من المعروف أنّها متوفّرة.

/**
* Sample {@link InstrumentClusterRenderingService} implementation
*/
public class SampleClusterServiceImpl extends InstrumentClusterRenderingService {
   // Used to retrieve or create displays
   private final DisplayManager mDisplayManager;
   // Unique identifier for the display to be used for instrument
   // cluster
   private final String mUniqueId = UUID.randomUUID().toString();
   // Format of the instrument cluster display
   private static final int DISPLAY_WIDTH = 1280;
   private static final int DISPLAY_HEIGHT = 720;
   private static final int DISPLAY_DPI = 320;
   // Area not covered by instruments
   private static final int DISPLAY_UNOBSCURED_LEFT = 40;
   private static final int DISPLAY_UNOBSCURED_TOP = 0;
   private static final int DISPLAY_UNOBSCURED_RIGHT = 1200;
   private static final int DISPLAY_UNOBSCURED_BOTTOM = 680;
   @Override
   public void onCreate() {
      super.onCreate();
      // Create a virtual display to render instrument cluster activities on
      mDisplayManager = getSystemService(DisplayManager.class);
      VirtualDisplay display = mDisplayManager.createVirtualDisplay(
          mUniqueId, DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_DPI, null,
          0 /* flags */, null, null);
      // Do any additional initialization (e.g.: start a video stream
      // based on this virtual display to present activities on a remote
      // display).
      onDisplayReady(display.getDisplay());
}
private void onDisplayReady(Display display) {
    // Report activity options that should be used to launch activities on
    // the instrument cluster.
    String category = CarInstrumentClusterManager.CATEGORY_NAVIGATION;
    ActionOptions options = ActivityOptions.makeBasic()
        .setLaunchDisplayId(display.getDisplayId());
    setClusterActivityOptions(category, options);
    // Report instrument cluster state.
    Rect unobscuredBounds = new Rect(DISPLAY_UNOBSCURED_LEFT,
        DISPLAY_UNOBSCURED_TOP, DISPLAY_UNOBSCURED_RIGHT,
        DISPLAY_UNOBSCURED_BOTTOM);
    boolean visible = true;
    ClusterActivityState state = ClusterActivityState.create(visible,
       unobscuredBounds);
    setClusterActivityState(category, options);
  }
}

استخدام CarAppFocusManager API

توفر CarAppFocusManager API طريقة باسم getAppTypeOwner()، ما يسمح لخدمة مجموعة العدادات التي يكتبها المصنّعون الأصليون للأجهزة بمعرفة تطبيق التنقّل الذي لديه تركيز التنقّل في أي وقت. يمكن للمصنّعين الأصليين للأجهزة استخدام الطريقة الحالية CarAppFocusManager#addFocusListener()، و ثم استخدام getAppTypeOwner() لمعرفة التطبيق الذي لديه التركيز. باستخدام هذه المعلومات، يمكن للمصنّعين الأصليين للأجهزة إجراء ما يلي:

  • تبديل النشاط المعروض في مجموعة العدادات إلى نشاط مجموعة العدادات الذي يوفّره تطبيق التنقّل الذي لديه التركيز.
  • يمكنهم رصد ما إذا كان تطبيق التنقّل الذي لديه التركيز يتضمّن نشاط مجموعة عدادات أم لا. إذا لم يكن لتطبيق التنقّل الذي لديه التركيز نشاط مجموعة عدادات (أو إذا كان هذا النشاط غير مفعَّل)، يمكن للمصنّعين الأصليين للأجهزة إرسال هذه الإشارة إلى وحدة DIM في السيارة ليتم تخطّي جانب التنقّل في مجموعة العدادات بالكامل.

يمكنك استخدام CarAppFocusManager لضبط تركيز التطبيق الحالي والاستماع إليه، مثل التنقّل النشط أو أمر صوتي. عادةً ما يتم تشغيل (أو تركيز) حالة واحدة فقط من هذا التطبيق في النظام.

استخدِم طريقة CarAppFocusManager#addFocusListener(..) للاستماع إلى التغييرات في تركيز التطبيق:

import android.car.CarAppFocusManager;

...

Car car = Car.createCar(this);
mAppFocusManager = (CarAppFocusManager)car.getCarManager(Car.APP_FOCUS_SERVICE);
mAppFocusManager.addFocusListener(this, CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION);

...

public void onAppFocusChanged(int appType, boolean active) {
    // Use the CarAppFocusManager#getAppTypeOwner(appType) method call
    // to retrieve a list of active package names
}

استخدِم طريقة CarAppFocusManager#getAppTypeOwner(..) لاسترداد أسماء حِزم المالك الحالي لنوع تطبيق معيّن محل التركيز. قد تعرض هذه الطريقة أكثر من اسم حزمة إذا كان المالك الحالي يستخدم ميزة android:sharedUserId.

import android.car.CarAppFocusManager;

...

Car car = Car.createCar(this);
mAppFocusManager = (CarAppFocusManager)car.getCarManager(Car.APP_FOCUS_SERVICE);
List<String> focusOwnerPackageNames = mAppFocusManager.getAppTypeOwner(
              CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION);

if (focusOwnerPackageNames == null || focusOwnerPackageNames.isEmpty()) {
        // No Navigation app has focus
        // OEM may choose to show their default cluster view
} else {
       // focusOwnerPackageNames
       // Use the PackageManager to retrieve the cluster activity for the package(s)
       // returned in focusOwnerPackageNames
}

...

تحديد تطبيقات النماذج

بالنسبة إلى تطبيقات التنقّل المستندة إلى النماذج التي تستخدم مكتبة تطبيقات السيارة، تعرض CarAppFocusManager#getAppTypeOwner() اسم حزمة المضيف (على سبيل المثال، com.google.android.apps.automotive.templates.host) لأنّ المضيف لديه تركيز النظام نيابةً عن تطبيق العميل.

لتحديد تطبيق العميل الذي يتنقّل، يمكن للمصنّعين الأصليين للأجهزة استخراج اسم الحزمة من حزمة حالة التنقّل التي يتم إرسالها مع CarNavigationStatusManager. يتم تخزين اسم الحزمة ضمن المفتاح active_app_package_name في الحزمة التي يتلقّاها NavigationRenderer#onNavigationStateChanged(Bundle):

// In your NavigationRenderer implementation
@Override
public void onNavigationStateChanged(Bundle bundle) {
    if (bundle.containsKey("active_app_package_name")) {
        String activeAppPackage = bundle.getString("active_app_package_name");
        // Use the package name to identify the navigating app (e.g., com.waze)
    }
}

الملحق: استخدام نموذج التطبيق

يوفّر AOSP نموذج تطبيق ينفّذ Navigation State API.

لتشغيل نموذج التطبيق هذا، اتّبِع الخطوات التالية:

  1. أنشئ Android Auto وثبّت ذاكرة ROM على وحدة رئيسية متوافقة. استخدِم تعليمات إنشاء Android وعرضه الخاصة بجهازك. للحصول على تعليمات، يُرجى الاطّلاع على استخدام لوحات مرجعية.
  2. وصِّل شاشة ثانوية فعلية بالوحدة الرئيسية (إذا كانت متوافقة) أو فعِّل الوحدة الرئيسية الثانوية الافتراضية:
    1. اختَر وضع المطوّرين في تطبيق "الإعدادات".
    2. انتقِل إلى الإعدادات > النظام > الإعدادات المتقدّمة > خيارات المطوّرين > محاكاة الشاشات الثانوية.
  3. أعِد تشغيل الوحدة الرئيسية.
  4. لتشغيل تطبيق KitchenSink، اتّبِع الخطوات التالية:
    1. افتح الدرج.
    2. انتقِل إلى مجموعة العدادات.
    3. انقر على بدء البيانات الوصفية.

يطلب KitchenSink تركيز NAVIGATION، ما يوجّه خدمة DirectRenderingCluster لعرض واجهة مستخدم محاكاة على مجموعة العدادات.