استخدام مكتبة تطبيقات Android للسيارات

تتيح لك مكتبة تطبيقات Android للسيارات استخدام تطبيقات الملاحة أو نقاط الاهتمام أو إنترنت الأشياء أو الطقس في السيارة. ويتم ذلك من خلال توفير مجموعة من النماذج المصمَّمة لتلبية معايير تشتيت انتباه السائق، والاهتمام بالتفاصيل، مثل تنوّع عوامل شاشة السيارة وطرق الإدخال.

يقدّم هذا الدليل نظرة عامة على الميزات والمفاهيم الرئيسية للمكتبة، كما يوضّح لك خطوات إعداد تطبيق أساسي.

قبل البدء

  1. راجِع صفحات تصميم مناسب لقيادة السيارات التي تتناول Car App Library
  2. راجِع المصطلحات والمفاهيم الرئيسية في القسم التالي.
  3. تعرَّف على واجهة مستخدم نظام Android Auto وتصميم نظام التشغيل Android Automotive.
  4. راجِع ملاحظات الإصدار.
  5. راجِع العيّنات.

المصطلحات والمفاهيم الرئيسية

النماذج والقوالب
يتم تمثيل واجهة المستخدم من خلال رسم بياني لعناصر النموذج التي يمكن ترتيبها معًا بطرق مختلفة، حسب ما يسمح به النموذج الذي تنتمي إليه. النماذج هي مجموعة فرعية من النماذج التي يمكن أن تعمل كعنصر جذري في تلك الرسوم البيانية. تتضمّن النماذج المعلومات التي سيتم عرضها للمستخدم في شكل نصوص وصور، بالإضافة إلى سمات لتحديد جوانب المظهر المرئي لهذه المعلومات، مثل ألوان النصوص أو أحجام الصور. يحوّل التطبيق المضيف النماذج إلى طرق عرض مصمَّمة لتلبية معايير تشتيت انتباه السائق، ويتولّى معالجة التفاصيل، مثل مجموعة عوامل شاشة السيارة وطرق الإدخال.
استضِف أصدقاءك وعائلتك
المضيف هو مكوّن الخلفية الذي ينفّذ الوظائف التي توفّرها واجهات برمجة التطبيقات في المكتبة، ما يتيح تشغيل تطبيقك في السيارة. تتراوح مسؤوليات المضيف بين استكشاف تطبيقك وإدارة مراحل نشاطه وتحويل نماذجك إلى طرق عرض وإرسال إشعارات إلى تطبيقك بشأن تفاعلات المستخدمين. على الأجهزة الجوّالة، يتم تنفيذ هذا المضيف من خلال Android Auto. على نظام التشغيل Android Automotive، يتم تثبيت هذا المضيف كتطبيق نظام.
قيود النماذج
تفرض النماذج المختلفة قيودًا على محتوى نماذجها. على سبيل المثال، تتضمّن قوالب القوائم حدودًا لعدد العناصر التي يمكن عرضها للمستخدم. تتضمّن النماذج أيضًا قيودًا على طريقة ربطها لتحديد مسار مهمة. على سبيل المثال، يمكن للتطبيق إرسال ما يصل إلى خمسة نماذج إلى حزمة الشاشة. لمزيد من التفاصيل، يُرجى الاطّلاع على قيود النماذج.
Screen
Screen هي فئة توفّرها المكتبة وتستخدمها التطبيقات لإدارة واجهة المستخدم المعروضة للمستخدم. يحتوي Screen على دورة حياة ويوفر الآلية التي تتيح للتطبيق إرسال النموذج لعرضه عندما تكون الشاشة مرئية. يمكن أيضًا إرسال مثيلات Screen واستردادها من حزمة Screen، ما يضمن التزامها بقيود تسلسل النماذج.
CarAppService
CarAppService هو فئة Service مجرّدة يجب أن ينفّذها تطبيقك ويصدّرها ليتمكّن المضيف من اكتشافها وإدارتها. CarAppService في تطبيقك مسؤول عن التحقّق من إمكانية الوثوق باتصال المضيف باستخدام createHostValidator، ثم توفير مثيلات Session لكل اتصال باستخدام onCreateSession.
Session

Session هي فئة مجرّدة يجب أن ينفّذها تطبيقك وأن يعرضها باستخدام CarAppService.onCreateSession. وهو بمثابة نقطة الدخول لعرض المعلومات على شاشة السيارة. ويحتوي على دورة حياة تحدّد الحالة الحالية لتطبيقك على شاشة السيارة، مثلاً عندما يكون تطبيقك مرئيًا أو مخفيًا.

عند بدء Session، مثلاً عند تشغيل التطبيق للمرة الأولى، يطلب المضيف عرض Screen الأولي باستخدام طريقة onCreateScreen.

تثبيت "مكتبة تطبيقات السيارة"

يمكنك الاطّلاع على صفحة إصدار مكتبة Jetpack للحصول على تعليمات حول كيفية إضافة المكتبة إلى تطبيقك.

ضبط ملفات بيان التطبيق

قبل إنشاء تطبيق سيارة، عليك ضبط ملفات البيان الخاصة بتطبيقك على النحو التالي.

تعريف CarAppService

يتصل المضيف بتطبيقك من خلال عملية تنفيذ CarAppService. عليك تعريف هذه الخدمة في ملف البيان للسماح للتطبيق المضيف باكتشاف تطبيقك والاتصال به.

عليك أيضًا الإفصاح عن فئة تطبيقك في العنصر <category> ضمن فلتر الأهداف الخاص بتطبيقك. اطّلِع على قائمة فئات التطبيقات المتوافقة لمعرفة القيم المسموح بها لهذا العنصر.

يوضّح مقتطف الرمز التالي كيفية تعريف خدمة تطبيق سيارة لتطبيق خاص بنقاط الاهتمام في ملف البيان:

<application>
    ...
   <service
       ...
        android:name=".MyCarAppService"
        android:exported="true">
      <intent-filter>
        <action android:name="androidx.car.app.CarAppService"/>
        <category android:name="androidx.car.app.category.POI"/>
      </intent-filter>
    </service>

    ...
<application>

فئات التطبيقات المتوافقة

حدِّد فئة تطبيقك من خلال إضافة قيمة واحدة أو أكثر من قيم الفئات التالية إلى فلتر الأهداف عند تحديد CarAppService كما هو موضّح في القسم السابق:

يمكنك الاطّلاع على جودة تطبيقات Android للسيارات للحصول على أوصاف تفصيلية لكل فئة ومعايير انضمام التطبيقات إليها.

تحديد اسم التطبيق ورمزه

عليك تحديد اسم ورمز للتطبيق يمكن أن يستخدمهما المضيف لتمثيل تطبيقك في واجهة مستخدم النظام.

يمكنك تحديد اسم التطبيق ورمزه المستخدَمَين لتمثيل تطبيقك باستخدام السمتَين label وicon ضمن CarAppService:

...
<service
   android:name=".MyCarAppService"
   android:exported="true"
   android:label="@string/my_app_name"
   android:icon="@drawable/my_app_icon">
   ...
</service>
...

إذا لم يتم تحديد التصنيف أو الرمز في العنصر <service>، سيعود المضيف إلى القيم المحددة للعنصر <application>.

ضبط مظهر مخصّص

لضبط تصميم مخصّص لتطبيق السيارة، أضِف عنصر <meta-data> في ملف البيان على النحو التالي:

<meta-data
    android:name="androidx.car.app.theme"
    android:resource="@style/MyCarAppTheme />

بعد ذلك، عليك تعريف مصدر النمط لضبط السمات التالية لمظهر تطبيقك المخصّص للسيارات:

<resources>
  <style name="MyCarAppTheme">
    <item name="carColorPrimary">@layout/my_primary_car_color</item>
    <item name="carColorPrimaryDark">@layout/my_primary_dark_car_color</item>
    <item name="carColorSecondary">@layout/my_secondary_car_color</item>
    <item name="carColorSecondaryDark">@layout/my_secondary_dark_car_color</item>
    <item name="carPermissionActivityLayout">@layout/my_custom_background</item>
  </style>
</resources>

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

تحدّد &quot;مكتبة تطبيقات السيارات&quot; مستويات واجهة برمجة التطبيقات الخاصة بها حتى تتمكّن من معرفة ميزات المكتبة التي يتيحها مضيف النماذج في السيارة. لاسترداد أعلى مستوى لواجهة برمجة تطبيقات Car App API متوافق مع تطبيق مضيف، استخدِم الطريقة getCarAppApiLevel().

عليك الإفصاح عن الحد الأدنى لمستوى Car App API الذي يتيحه تطبيقك في ملف AndroidManifest.xml:

<manifest ...>
    <application ...>
        <meta-data
            android:name="androidx.car.app.minCarApiLevel"
            android:value="1"/>
    </application>
</manifest>

راجِع مستندات التعليق التوضيحي RequiresCarApi للحصول على تفاصيل حول كيفية الحفاظ على التوافق مع الإصدارات القديمة وتحديد الحد الأدنى لمستوى واجهة برمجة التطبيقات المطلوب لاستخدام إحدى الميزات. للاطّلاع على تعريف لمستوى واجهة برمجة التطبيقات المطلوب لاستخدام ميزة معيّنة في &quot;مكتبة تطبيقات السيارات&quot;، راجِع مستندات المرجع الخاصة CarAppApiLevels.

إنشاء CarAppService وSession

يجب أن يوسّع تطبيقك الفئة CarAppService وأن ينفّذ طريقتها onCreateSession التي تعرض مثيلاً من Session يتوافق مع الاتصال الحالي بالمضيف:

Kotlin

class HelloWorldService : CarAppService() {
    ...
    override fun onCreateSession(): Session {
        return HelloWorldSession()
    }
    ...
}

Java

public final class HelloWorldService extends CarAppService {
    ...
    @Override
    @NonNull
    public Session onCreateSession() {
        return new HelloWorldSession();
    }
    ...
}

يكون عنصر Session مسؤولاً عن عرض عنصر Screen عند بدء تشغيل التطبيق للمرة الأولى:

Kotlin

class HelloWorldSession : Session() {
    ...
    override fun onCreateScreen(intent: Intent): Screen {
        return HelloWorldScreen(carContext)
    }
    ...
}

Java

public final class HelloWorldSession extends Session {
    ...
    @Override
    @NonNull
    public Screen onCreateScreen(@NonNull Intent intent) {
        return new HelloWorldScreen(getCarContext());
    }
    ...
}

للتعامل مع السيناريوهات التي يحتاج فيها تطبيق السيارة إلى البدء من شاشة ليست الشاشة الرئيسية أو صفحة الوصول في تطبيقك، مثل التعامل مع الروابط لصفحات في التطبيق، يمكنك إعداد مسبق لمجموعة من الشاشات السابقة باستخدام ScreenManager.push قبل الرجوع من onCreateScreen. تتيح عملية التعبئة المسبقة للمستخدمين الرجوع إلى الشاشات السابقة من الشاشة الأولى التي يعرضها تطبيقك.

إنشاء شاشة البدء

يمكنك إنشاء الشاشات التي يعرضها تطبيقك من خلال تحديد الفئات التي توسّع الفئة Screen وتنفيذ الطريقة onGetTemplate الخاصة بها، والتي تعرض مثيلاً من Template يمثّل حالة واجهة المستخدم التي سيتم عرضها على شاشة السيارة.

يوضّح المقتطف التالي كيفية تعريف Screen يستخدم نموذج PaneTemplate لعرض السلسلة البسيطة "Hello world!":

Kotlin

class HelloWorldScreen(carContext: CarContext) : Screen(carContext) {
    override fun onGetTemplate(): Template {
        val row = Row.Builder().setTitle("Hello world!").build()
        val pane = Pane.Builder().addRow(row).build()
        return PaneTemplate.Builder(pane)
            .setHeaderAction(Action.APP_ICON)
            .build()
    }
}

Java

public class HelloWorldScreen extends Screen {
    @NonNull
    @Override
    public Template onGetTemplate() {
        Row row = new Row.Builder().setTitle("Hello world!").build();
        Pane pane = new Pane.Builder().addRow(row).build();
        return new PaneTemplate.Builder(pane)
            .setHeaderAction(Action.APP_ICON)
            .build();
    }
}

فئة CarContext

الفئة CarContext هي فئة فرعية ContextWrapper يمكن الوصول إليها من خلال مثيلات Session وScreen. توفّر هذه المكتبة إمكانية الوصول إلى خدمات السيارة، مثل ScreenManager لإدارة حزمة الشاشة وAppManager للوظائف العامة المتعلقة بالتطبيقات، مثل الوصول إلى العنصر Surface من أجل رسم الخرائط وNavigationManager الذي تستخدمه تطبيقات التنقّل خطوة بخطوة لإرسال بيانات التنقّل الوصفية وغيرها من الأحداث المتعلقة بالتنقّل إلى التطبيق المضيف.

يمكنك الاطّلاع على الوصول إلى نماذج التنقّل للحصول على قائمة شاملة بوظائف المكتبة المتاحة لتطبيقات التنقّل.

توفّر CarContext أيضًا وظائف أخرى، مثل السماح لك بتحميل موارد قابلة للرسم باستخدام الإعدادات من شاشة السيارة، وبدء تطبيق في السيارة باستخدام الأهداف، وتحديد ما إذا كان يجب أن يعرض تطبيقك الخريطة في المظهر الداكن.

تنفيذ التنقّل بين الشاشات

تعرض التطبيقات غالبًا عددًا من الشاشات المختلفة، وقد تستخدم كل شاشة قوالب مختلفة يمكن للمستخدم التنقّل بينها أثناء تفاعله مع الواجهة المعروضة على الشاشة.

تقدّم الفئة ScreenManager حزمة شاشات يمكنك استخدامها لعرض الشاشات التي يمكن إغلاقها تلقائيًا عندما ينقر المستخدم على زر الرجوع في شاشة السيارة أو يستخدم زر الرجوع المتاح في بعض السيارات.

يوضّح المقتطف التالي كيفية إضافة إجراء رجوع إلى نموذج رسالة، بالإضافة إلى إجراء يعرض شاشة جديدة عند اختيارها من قِبل المستخدم:

Kotlin

val template = MessageTemplate.Builder("Hello world!")
    .setHeaderAction(Action.BACK)
    .addAction(
        Action.Builder()
            .setTitle("Next screen")
            .setOnClickListener { screenManager.push(NextScreen(carContext)) }
            .build())
    .build()

Java

MessageTemplate template = new MessageTemplate.Builder("Hello world!")
    .setHeaderAction(Action.BACK)
    .addAction(
        new Action.Builder()
            .setTitle("Next screen")
            .setOnClickListener(
                () -> getScreenManager().push(new NextScreen(getCarContext())))
            .build())
    .build();

العنصر Action.BACK هو Action عادي يستدعي ScreenManager.pop تلقائيًا. يمكن تجاهل هذا السلوك باستخدام مثيل OnBackPressedDispatcher المتاح من CarContext.

للمساعدة في ضمان أمان استخدام التطبيق أثناء القيادة، يمكن أن يبلغ الحد الأقصى لعمق حزمة الشاشة خمس شاشات. يمكنك الاطّلاع على قسم القيود المفروضة على النماذج للحصول على مزيد من التفاصيل.

إعادة تحميل محتوى نموذج

يمكن لتطبيقك طلب إبطال محتوى Screen من خلال استدعاء الطريقة Screen.invalidate. يُجري المضيف بعد ذلك عملية استدعاء إلى طريقة Screen.onGetTemplate في تطبيقك لاسترداد النموذج مع المحتوى الجديد.

عند إعادة تحميل Screen، من المهم معرفة المحتوى المحدّد في النموذج الذي يمكن تعديله حتى لا يحتسب المضيف النموذج الجديد ضمن حصة النماذج. يمكنك الاطّلاع على قسم قيود النماذج لمزيد من التفاصيل.

لقد نصحناك بتنظيم شاشاتك بحيث يكون هناك تطابق واحد إلى واحد بين Screen ونوع النموذج الذي يعرضه من خلال عملية تنفيذ onGetTemplate.

رسم الخرائط

يمكن لتطبيقات التنقّل ونقاط الاهتمام والطقس التي تستخدم النماذج التالية رسم الخرائط من خلال الوصول إلى Surface.

لاستخدام النماذج التالية، يجب أن يتضمّن تطبيقك أحد الأذونات ذات الصلة في العنصر <uses-permission> ضمن ملف AndroidManifest.xml.

نموذج إذن النموذج إرشادات الفئة
NavigationTemplate androidx.car.app.NAVIGATION_TEMPLATES التنقل
MapWithContentTemplate androidx.car.app.NAVIGATION_TEMPLATES أو
androidx.car.app.MAP_TEMPLATES
التنقّل، نقاط الاهتمام، الطقس
MapTemplate (تم إيقافها نهائيًا) androidx.car.app.NAVIGATION_TEMPLATES التنقل
PlaceListNavigationTemplate (تم إيقافها نهائيًا) androidx.car.app.NAVIGATION_TEMPLATES التنقل
RoutePreviewNavigationTemplate (تم إيقافها نهائيًا) androidx.car.app.NAVIGATION_TEMPLATES التنقل

تضمين إذن العرض على السطح

بالإضافة إلى الإذن المطلوب للنموذج الذي يستخدمه تطبيقك، يجب أن يدرج تطبيقك الإذن androidx.car.app.ACCESS_SURFACE في ملف AndroidManifest.xml الخاص به للوصول إلى مساحة العرض:

<manifest ...>
  ...
  <uses-permission android:name="androidx.car.app.ACCESS_SURFACE" />
  ...
</manifest>

الوصول إلى مساحة العرض

للوصول إلى Surface الذي يوفّره المضيف، عليك تنفيذ SurfaceCallback وتقديم هذا التنفيذ إلى خدمة السيارة AppManager. يتم تمرير Surface الحالي إلى SurfaceCallback في المَعلمة SurfaceContainer ضمن عمليات الاسترجاع onSurfaceAvailable() وonSurfaceDestroyed().

Kotlin

carContext.getCarService(AppManager::class.java).setSurfaceCallback(surfaceCallback)

Java

carContext.getCarService(AppManager.class).setSurfaceCallback(surfaceCallback);

فهم مساحة العرض المرئية على السطح

يمكن للمضيف رسم عناصر واجهة المستخدم للنماذج فوق الخريطة. يُبلغ المضيف عن مساحة السطح التي يُضمن عدم حجبها وأنّها مرئية بالكامل للمستخدم من خلال استدعاء الطريقة SurfaceCallback.onVisibleAreaChanged. بالإضافة إلى ذلك، للحدّ من عدد التغييرات، يستدعي المضيف طريقة SurfaceCallback.onStableAreaChanged باستخدام أصغر مستطيل، والذي يظهر دائمًا استنادًا إلى النموذج الحالي.

على سبيل المثال، عندما يستخدم تطبيق خرائط NavigationTemplate مع شريط إجراءات في الأعلى، يمكن لشريط الإجراءات أن يخفي نفسه عندما لا يتفاعل المستخدم مع الشاشة لفترة من الوقت لتوفير مساحة أكبر للخريطة. في هذه الحالة، هناك دالة ردّ الاتصال إلى onStableAreaChanged وonVisibleAreaChanged مع المستطيل نفسه. عند إخفاء شريط الإجراءات، يتم استدعاء onVisibleAreaChanged فقط مع المساحة الأكبر. إذا تفاعل المستخدم مع الشاشة، سيتم استدعاء onVisibleAreaChanged مرة أخرى فقط مع المستطيل الأول.

توفير المظهر الداكن

يجب أن تعيد التطبيقات رسم الخريطة على مثيل Surface باستخدام ألوان داكنة مناسبة عندما يحدّد المضيف أنّ الظروف تستدعي ذلك، كما هو موضّح في جودة تطبيقات Android للسيارات.

لتحديد ما إذا كان سيتم رسم خريطة داكنة، يمكنك استخدام طريقة CarContext.isDarkMode. عندما تتغيّر حالة المظهر الداكن، ستتلقّى طلبًا إلى Session.onCarConfigurationChanged.

رسم الخرائط على شاشة العرض

بالإضافة إلى رسم الخرائط على الشاشة الرئيسية، يمكن لتطبيقات التنقّل أيضًا رسم الخرائط على شاشة مجموعة العدادات خلف عجلة القيادة. اطّلِع على الرسم على شاشة العرض المجمّعة للحصول على إرشادات إضافية.

السماح للمستخدمين بالتفاعل مع الخريطة

عند استخدام النماذج التالية، يمكنك إتاحة تفاعل المستخدمين مع الخرائط التي ترسمها، مثل السماح لهم بالاطّلاع على أجزاء مختلفة من الخريطة من خلال التكبير والتصغير والتحريك.

نموذج التفاعل متاح منذ مستوى Car App API
NavigationTemplate 2
PlaceListNavigationTemplate (متوقّف نهائيًا) 4
RoutePreviewNavigationTemplate (متوقّف نهائيًا) 4
MapTemplate (متوقّف نهائيًا) ‫5 (مقدمة عن النموذج)
MapWithContentTemplate ‫7 (مقدمة عن النموذج)

تنفيذ عمليات معاودة الاتصال المتعلقة بالتفاعل

تحتوي واجهة SurfaceCallback على العديد من طرق معاودة الاتصال التي يمكنك تنفيذها لإضافة تفاعلية إلى الخرائط التي تم إنشاؤها باستخدام النماذج في القسم السابق:

التفاعل طريقة واحدة (SurfaceCallback) متوافق منذ مستوى Car App API
نقر onClick 5
استخدام إصبعين للتصغير onScale 2
السحب بلمسة واحدة onScroll 2
نقل المحتوى بلمسة واحدة onFling 2
النقر مرتين onScale (مع تحديد عامل التحجيم من خلال المضيف النموذجي) 2
الدفع الدائري في وضع العرض الشامل onScroll (مع عامل المسافة الذي يحدّده المضيف الرئيسي) 2

إضافة شريط إجراءات خريطة

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

لتلقّي عمليات ردّ الاتصال التفاعلية الخاصة بالخريطة، يجب إضافة زر Action.PAN في شريط إجراءات الخريطة. عندما يضغط المستخدم على زر التحريك، يدخل الجهاز المضيف في وضع التحريك، كما هو موضّح في القسم التالي.

إذا حذف تطبيقك الزر Action.PAN في شريط إجراءات الخريطة، لن يتلقّى إدخالات المستخدم من الطريقتين SurfaceCallback، وسيخرج المضيف من أي وضع تحريك تم تفعيله سابقًا.

على شاشة تعمل باللمس، لا يظهر زر التحريك.

التعرّف على وضع العرض الشامل

في وضع التحريك، يحوّل مضيف النموذج إدخال المستخدم من أجهزة الإدخال غير اللمسية، مثل وحدات التحكّم الدورانية ولوحات اللمس، إلى طرق SurfaceCallback المناسبة. استجِب لإجراء المستخدم للدخول إلى وضع العرض الشامل أو الخروج منه باستخدام الطريقة setPanModeListener في NavigationTemplate.Builder. يمكن للمضيف إخفاء عناصر أخرى من واجهة المستخدم في النموذج أثناء استخدام وضع التحريك.

التفاعل مع المستخدم

يمكن لتطبيقك التفاعل مع المستخدم باستخدام أنماط مشابهة لتطبيق الأجهزة الجوّالة.

التعامل مع بيانات المستخدم

يمكن لتطبيقك الاستجابة لإدخالات المستخدمين من خلال تمرير أدوات المعالجة المناسبة إلى النماذج التي تتوافق معها. يوضّح المقتطف التالي كيفية إنشاء نموذج Action يضبط OnClickListener الذي يُعيد الاتصال بطريقة محدّدة في رمز تطبيقك:

Kotlin

val action = Action.Builder()
    .setTitle("Navigate")
    .setOnClickListener(::onClickNavigate)
    .build()

Java

Action action = new Action.Builder()
    .setTitle("Navigate")
    .setOnClickListener(this::onClickNavigate)
    .build();

يمكن أن تبدأ الطريقة onClickNavigate بعد ذلك تطبيق التنقّل التلقائي للسيارة باستخدام الطريقة CarContext.startCarApp:

Kotlin

private fun onClickNavigate() {
    val intent = Intent(CarContext.ACTION_NAVIGATE, Uri.parse("geo:0,0?q=" + address))
    carContext.startCarApp(intent)
}

Java

private void onClickNavigate() {
    Intent intent = new Intent(CarContext.ACTION_NAVIGATE, Uri.parse("geo:0,0?q=" + address));
    getCarContext().startCarApp(intent);
}

لمزيد من التفاصيل حول كيفية بدء تشغيل التطبيقات، بما في ذلك تنسيق الغرض ACTION_NAVIGATE، راجِع القسم بدء تشغيل تطبيق على السيارة باستخدام غرض.

لا يُسمح بتنفيذ بعض الإجراءات، مثل تلك التي تتطلّب توجيه المستخدم لمواصلة التفاعل على أجهزته الجوّالة، إلا عندما تكون السيارة متوقفة. يمكنك استخدام ParkedOnlyOnClickListener لتنفيذ هذه الإجراءات. إذا لم تكن السيارة متوقفة، يعرض الجهاز المضيف إشارة للمستخدم بأنّه لا يُسمح بتنفيذ الإجراء في هذه الحالة. إذا كانت السيارة متوقفة، سيتم تنفيذ الرمز البرمجي بشكل طبيعي. تعرض المقتطفة التالية كيفية استخدام ParkedOnlyOnClickListener لفتح شاشة الإعدادات على الجهاز الجوّال:

Kotlin

val row = Row.Builder()
    .setTitle("Open Settings")
    .setOnClickListener(ParkedOnlyOnClickListener.create(::openSettingsOnPhone))
    .build()

Java

Row row = new Row.Builder()
    .setTitle("Open Settings")
    .setOnClickListener(ParkedOnlyOnClickListener.create(this::openSettingsOnPhone))
    .build();

عرض الإشعارات

لا تظهر الإشعارات المُرسَلة إلى الجهاز الجوّال على شاشة السيارة إلا إذا تم توسيعها باستخدام CarAppExtender. يمكن ضبط بعض سمات الإشعارات، مثل عنوان المحتوى والنص والرمز والإجراءات، في CarAppExtender، ما يؤدي إلى تجاهل سمات الإشعارات عند ظهورها على شاشة السيارة.

يوضّح المقتطف التالي كيفية إرسال إشعار إلى شاشة السيارة يعرض عنوانًا مختلفًا عن العنوان المعروض على الجهاز الجوّال:

Kotlin

val notification = NotificationCompat.Builder(context, NOTIFICATION_CHANNEL_ID)
    .setContentTitle(titleOnThePhone)
    .extend(
        CarAppExtender.Builder()
            .setContentTitle(titleOnTheCar)
            ...
            .build())
    .build()

Java

Notification notification = new NotificationCompat.Builder(context, NOTIFICATION_CHANNEL_ID)
    .setContentTitle(titleOnThePhone)
    .extend(
        new CarAppExtender.Builder()
            .setContentTitle(titleOnTheCar)
            ...
            .build())
    .build();

يمكن أن تؤثر الإشعارات في الأجزاء التالية من واجهة المستخدم:

  • قد يتم عرض إشعار تنبيه للمستخدم.
  • يمكن إضافة إدخال في مركز الإشعارات، مع شارة مرئية في الشريط بشكل اختياري.
  • بالنسبة إلى تطبيقات الملاحة، قد يظهر الإشعار في أداة شريط التنقّل كما هو موضّح في الإشعارات خطوة بخطوة.

يمكنك اختيار طريقة ضبط إشعارات تطبيقك للتأثير في كل عنصر من عناصر واجهة المستخدم هذه باستخدام أولوية الإشعار، كما هو موضّح في مستندات CarAppExtender.

إذا تم استدعاء NotificationCompat.Builder.setOnlyAlertOnce بالقيمة true، سيتم عرض إشعار ذي أولوية عالية كإشعار HUN مرة واحدة فقط.

لمزيد من المعلومات حول كيفية تصميم إشعارات تطبيق السيارة، راجِع دليل Google Design for Driving حول الإشعارات.

عرض الإشعارات المنبثقة

يمكن لتطبيقك عرض إشعار مؤقت باستخدام CarToast كما هو موضّح في المقتطف التالي:

Kotlin

CarToast.makeText(carContext, "Hello!", CarToast.LENGTH_SHORT).show()

Java

CarToast.makeText(getCarContext(), "Hello!", CarToast.LENGTH_SHORT).show();

طلب الأذونات

إذا كان تطبيقك يحتاج إلى الوصول إلى بيانات أو إجراءات محظورة، مثل الموقع الجغرافي، تسري القواعد العادية لأذونات Android. لطلب الحصول على إذن، يمكنك استخدام طريقة CarContext.requestPermissions().

تتمثّل فائدة استخدام CarContext.requestPermissions() بدلاً من واجهات برمجة التطبيقات العادية في Android في أنّك لست بحاجة إلى تشغيل Activity لإنشاء مربّع حوار الأذونات. بالإضافة إلى ذلك، يمكنك استخدام الرمز البرمجي نفسه على كل من Android Auto وAndroid Automotive OS، بدلاً من الاضطرار إلى إنشاء مسارات تعتمد على النظام الأساسي.

تنسيق مربّع حوار الأذونات على Android Auto

في Android Auto، سيظهر مربّع حوار الأذونات للمستخدم على الهاتف. لن تظهر أي خلفية تلقائيًا خلف مربع الحوار. لضبط خلفية مخصّصة، عليك تعريف مظهر تطبيق السيارة في ملف AndroidManifest.xml وضبط السمة carPermissionActivityLayout لمظهر تطبيق السيارة.

<meta-data
    android:name="androidx.car.app.theme"
    android:resource="@style/MyCarAppTheme />

بعد ذلك، اضبط السمة carPermissionActivityLayout لمظهر تطبيق السيارة:

<resources>
  <style name="MyCarAppTheme">
    <item name="carPermissionActivityLayout">@layout/my_custom_background</item>
  </style>
</resources>

بدء تطبيق سيارة باستخدام هدف

يمكنك استدعاء الطريقة CarContext.startCarApp لتنفيذ أحد الإجراءات التالية:

  • افتح تطبيق "برنامج الاتصال" لإجراء مكالمة هاتفية.
  • ابدأ التنقّل بشكل مفصّل إلى موقع جغرافي باستخدام تطبيق التنقّل التلقائي بالسيارة.
  • ابدأ تطبيقك الخاص باستخدام هدف.

يوضّح المثال التالي كيفية إنشاء إشعار يتضمّن إجراءً يفتح تطبيقك مع شاشة تعرض تفاصيل حجز مكان لصف السيارة. يمكنك توسيع مثيل الإشعار باستخدام رسالة Intent تتضمّن محتوى يحتوي على PendingIntent يغلّف رسالة Intent صريحة لإجراء تطبيقك:

Kotlin

val notification = notificationBuilder
    ...
    .extend(
        CarAppExtender.Builder()
            .setContentIntent(
                PendingIntent.getBroadcast(
                    context,
                    ACTION_VIEW_PARKING_RESERVATION.hashCode(),
                    Intent(ACTION_VIEW_PARKING_RESERVATION)
                        .setComponent(ComponentName(context, MyNotificationReceiver::class.java)),
                    0))
            .build())

Java

Notification notification = notificationBuilder
    ...
    .extend(
        new CarAppExtender.Builder()
            .setContentIntent(
                PendingIntent.getBroadcast(
                    context,
                    ACTION_VIEW_PARKING_RESERVATION.hashCode(),
                    new Intent(ACTION_VIEW_PARKING_RESERVATION)
                        .setComponent(new ComponentName(context, MyNotificationReceiver.class)),
                    0))
            .build());

يجب أن يوضّح تطبيقك أيضًا BroadcastReceiver يتم استدعاؤه لمعالجة الغرض عندما يختار المستخدم الإجراء في واجهة الإشعارات ويستدعي CarContext.startCarApp مع غرض يتضمّن معرّف الموارد المنتظم للبيانات:

Kotlin

class MyNotificationReceiver : BroadcastReceiver() {
    override fun onReceive(context: Context, intent: Intent) {
        val intentAction = intent.action
        if (ACTION_VIEW_PARKING_RESERVATION == intentAction) {
            CarContext.startCarApp(
                intent,
                Intent(Intent.ACTION_VIEW)
                    .setComponent(ComponentName(context, MyCarAppService::class.java))
                    .setData(Uri.fromParts(MY_URI_SCHEME, MY_URI_HOST, intentAction)))
        }
    }
}

Java

public class MyNotificationReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        String intentAction = intent.getAction();
        if (ACTION_VIEW_PARKING_RESERVATION.equals(intentAction)) {
            CarContext.startCarApp(
                intent,
                new Intent(Intent.ACTION_VIEW)
                    .setComponent(new ComponentName(context, MyCarAppService.class))
                    .setData(Uri.fromParts(MY_URI_SCHEME, MY_URI_HOST, intentAction)));
        }
    }
}

أخيرًا، تعالج طريقة Session.onNewIntent في تطبيقك هذا الغرض عن طريق عرض شاشة حجز مكان انتظار السيارات في الحزمة، إذا لم تكن معروضة في المقدّمة:

Kotlin

override fun onNewIntent(intent: Intent) {
    val screenManager = carContext.getCarService(ScreenManager::class.java)
    val uri = intent.data
    if (uri != null
        && MY_URI_SCHEME == uri.scheme
        && MY_URI_HOST == uri.schemeSpecificPart
        && ACTION_VIEW_PARKING_RESERVATION == uri.fragment
    ) {
        val top = screenManager.top
        if (top !is ParkingReservationScreen) {
            screenManager.push(ParkingReservationScreen(carContext))
        }
    }
}

Java

@Override
public void onNewIntent(@NonNull Intent intent) {
    ScreenManager screenManager = getCarContext().getCarService(ScreenManager.class);
    Uri uri = intent.getData();
    if (uri != null
        && MY_URI_SCHEME.equals(uri.getScheme())
        && MY_URI_HOST.equals(uri.getSchemeSpecificPart())
        && ACTION_VIEW_PARKING_RESERVATION.equals(uri.getFragment())
    ) {
        Screen top = screenManager.getTop();
        if (!(top instanceof ParkingReservationScreen)) {
            screenManager.push(new ParkingReservationScreen(getCarContext()));
        }
    }
}

راجِع قسم عرض الإشعارات للحصول على مزيد من المعلومات حول كيفية التعامل مع الإشعارات في تطبيق السيارة.

قيود النماذج

يحدّد المضيف عدد النماذج التي سيتم عرضها لمهمة معيّنة بما لا يزيد عن خمسة نماذج، ويجب أن يكون النموذج الأخير أحد الأنواع التالية:

يُرجى العلم أنّ هذا الحدّ ينطبق على عدد النماذج وليس على عدد مثيلات Screen في الحزمة. على سبيل المثال، إذا أرسل تطبيق نموذجين أثناء عرض الشاشة (أ) ثم عرض الشاشة (ب)، يمكنه الآن إرسال ثلاثة نماذج أخرى. بدلاً من ذلك، إذا كانت كل شاشة منظَّمة لإرسال نموذج واحد، يمكن للتطبيق إرسال خمسة مثيلات شاشة إلى حزمة ScreenManager.

هناك حالات خاصة لهذه القيود، وهي عمليات إعادة تحميل النموذج والرجوع وإعادة الضبط.

إعادة تحميل النماذج

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

عمليات الخلفية

لتفعيل التدفقات الفرعية ضمن مهمة، يرصد المضيف متى يعرض التطبيق Screen من حزمة ScreenManager ويعدّل الحصة المتبقية استنادًا إلى عدد النماذج التي يتراجع التطبيق عنها.

على سبيل المثال، إذا أرسل التطبيق نموذجين أثناء عرض الشاشة (أ)، ثم عرض الشاشة (ب) وأرسل نموذجين آخرين، سيتبقى للتطبيق حصة واحدة. إذا عاد التطبيق بعد ذلك إلى الشاشة (أ)، يعيد المضيف ضبط الحصة إلى ثلاث، لأنّ التطبيق قد عاد خطوتين إلى الوراء.

يُرجى العِلم أنّه عند الرجوع إلى شاشة سابقة، يجب أن يرسل التطبيق نموذجًا من النوع نفسه الذي أرسلته الشاشة آخر مرة. سيؤدي إرسال أي نوع آخر من النماذج إلى حدوث خطأ. ومع ذلك، طالما ظل النوع كما هو أثناء عملية الرجوع، يمكن للتطبيق تعديل محتوى النموذج بحرية بدون التأثير في الحصة.

عمليات إعادة الضبط

تتضمّن بعض النماذج دلالات خاصة تشير إلى نهاية مهمة. على سبيل المثال، NavigationTemplate هي طريقة عرض من المتوقّع أن تبقى على الشاشة ويتم تحديثها بتعليمات جديدة للمستخدم. عندما يصل إلى أحد هذه النماذج، يعيد المضيف ضبط حصة النموذج، ويتعامل مع هذا النموذج كما لو كان الخطوة الأولى في مهمة جديدة. يسمح هذا للتطبيق ببدء مهمة جديدة. راجِع مستندات النماذج الفردية لمعرفة النماذج التي تؤدي إلى إعادة الضبط على الجهاز المضيف.

إذا تلقّى المضيف إشارة لبدء التطبيق من إجراء إشعار أو من مشغّل التطبيق، تتم إعادة ضبط الحصة أيضًا. تتيح هذه الآلية للتطبيق بدء مسار مهام جديد من الإشعارات، ويسري ذلك حتى إذا كان التطبيق مرتبطًا ومفتوحًا في المقدّمة.

راجِع قسم عرض الإشعارات لمزيد من التفاصيل حول كيفية عرض إشعارات تطبيقك على شاشة السيارة. راجِع قسم بدء تطبيق سيارة باستخدام intent للحصول على معلومات حول كيفية بدء تطبيقك من خلال إجراء إشعار.

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

يمكنك تحديد ما إذا كان تطبيقك يعمل على Android Auto أو Android Automotive OS باستخدام واجهة برمجة التطبيقات CarConnection لاسترداد معلومات الاتصال في وقت التشغيل.

على سبيل المثال، في Session ضمن تطبيق السيارة، يمكنك ضبط قيمة أولية لـ CarConnection والاشتراك في تلقّي تحديثات LiveData:

Kotlin

CarConnection(carContext).type.observe(this, ::onConnectionStateUpdated)

Java

new CarConnection(getCarContext()).getType().observe(this, this::onConnectionStateUpdated);

في أداة المراقبة، يمكنك بعد ذلك التفاعل مع التغييرات في حالة الاتصال:

Kotlin

fun onConnectionStateUpdated(connectionState: Int) {
  val message = when(connectionState) {
    CarConnection.CONNECTION_TYPE_NOT_CONNECTED -> "Not connected to a head unit"
    CarConnection.CONNECTION_TYPE_NATIVE -> "Connected to Android Automotive OS"
    CarConnection.CONNECTION_TYPE_PROJECTION -> "Connected to Android Auto"
    else -> "Unknown car connection type"
  }
  CarToast.makeText(carContext, message, CarToast.LENGTH_SHORT).show()
}

Java

private void onConnectionStateUpdated(int connectionState) {
  String message;
  switch(connectionState) {
    case CarConnection.CONNECTION_TYPE_NOT_CONNECTED:
      message = "Not connected to a head unit";
      break;
    case CarConnection.CONNECTION_TYPE_NATIVE:
      message = "Connected to Android Automotive OS";
      break;
    case CarConnection.CONNECTION_TYPE_PROJECTION:
      message = "Connected to Android Auto";
      break;
    default:
      message = "Unknown car connection type";
      break;
  }
  CarToast.makeText(getCarContext(), message, CarToast.LENGTH_SHORT).show();
}

Constraints API

قد تسمح السيارات المختلفة بعرض عدد مختلف من مثيلات Item للمستخدم في المرة الواحدة. استخدِم ConstraintManager للتحقّق من حد المحتوى في وقت التشغيل وضبط عدد العناصر المناسب في النماذج.

ابدأ بالحصول على ConstraintManager من CarContext:

Kotlin

val manager = carContext.getCarService(ConstraintManager::class.java)

Java

ConstraintManager manager = getCarContext().getCarService(ConstraintManager.class);

يمكنك بعد ذلك طلب البحث عن عنصر ConstraintManager الذي تم استرجاعه للحصول على الحدّ الأقصى المسموح به للمحتوى ذي الصلة. على سبيل المثال، للحصول على عدد العناصر التي يمكن عرضها في شبكة، استخدِم الدالة getContentLimit مع CONTENT_LIMIT_TYPE_GRID:

Kotlin

val gridItemLimit = manager.getContentLimit(ConstraintManager.CONTENT_LIMIT_TYPE_GRID)

Java

int gridItemLimit = manager.getContentLimit(ConstraintManager.CONTENT_LIMIT_TYPE_GRID);

إضافة مسار تسجيل دخول

إذا كان تطبيقك يوفّر للمستخدمين تجربة تسجيل الدخول، يمكنك استخدام نماذج مثل SignInTemplate وLongMessageTemplate مع مستوى 2 من Car App API أو أعلى للتعامل مع تسجيل الدخول إلى تطبيقك على وحدة رأس السيارة.

لإنشاء SignInTemplate، حدِّد SignInMethod. تتيح &quot;مكتبة تطبيقات السيارات&quot; حاليًا طرق تسجيل الدخول التالية:

  • InputSignInMethod لتسجيل الدخول باستخدام اسم المستخدم وكلمة المرور
  • PinSignInMethod لتسجيل الدخول باستخدام رقم التعريف الشخصي، حيث يربط المستخدم حسابه من هاتفه باستخدام رقم التعريف الشخصي المعروض على وحدة السيارة الرئيسية.
  • ProviderSignInMethod لتسجيل الدخول باستخدام موفِّر خدمة، مثل تسجيل الدخول باستخدام حساب Google وOne Tap.
  • QRCodeSignInMethod لتسجيل الدخول باستخدام رمز الاستجابة السريعة، حيث يمسح المستخدم رمز الاستجابة السريعة ضوئيًا لإكمال عملية تسجيل الدخول على هاتفه. تتوفّر هذه الميزة مع الإصدار 4 من Car API والإصدارات الأحدث.

على سبيل المثال، لتنفيذ نموذج يجمع كلمة مرور المستخدم، ابدأ بإنشاء InputCallback لمعالجة مدخلات المستخدم والتحقّق من صحتها:

Kotlin

val callback = object : InputCallback {
    override fun onInputSubmitted(text: String) {
        // You will receive this callback when the user presses Enter on the keyboard.
    }

    override fun onInputTextChanged(text: String) {
        // You will receive this callback as the user is typing. The update
        // frequency is determined by the host.
    }
}

Java

InputCallback callback = new InputCallback() {
    @Override
    public void onInputSubmitted(@NonNull String text) {
        // You will receive this callback when the user presses Enter on the keyboard.
    }

    @Override
    public void onInputTextChanged(@NonNull String text) {
        // You will receive this callback as the user is typing. The update
        // frequency is determined by the host.
    }
};

يجب إدخال InputCallback لـ InputSignInMethod Builder.

Kotlin

val passwordInput = InputSignInMethod.Builder(callback)
    .setHint("Password")
    .setInputType(InputSignInMethod.INPUT_TYPE_PASSWORD)
    ...
    .build()

Java

InputSignInMethod passwordInput = new InputSignInMethod.Builder(callback)
    .setHint("Password")
    .setInputType(InputSignInMethod.INPUT_TYPE_PASSWORD)
    ...
    .build();

أخيرًا، استخدِم InputSignInMethod الجديد لإنشاء SignInTemplate.

Kotlin

SignInTemplate.Builder(passwordInput)
    .setTitle("Sign in with username and password")
    .setInstructions("Enter your password")
    .setHeaderAction(Action.BACK)
    ...
    .build()

Java

new SignInTemplate.Builder(passwordInput)
    .setTitle("Sign in with username and password")
    .setInstructions("Enter your password")
    .setHeaderAction(Action.BACK)
    ...
    .build();

استخدام AccountManager

يجب أن تستخدم التطبيقات التي تعمل بنظام التشغيل Android Automotive والتي تتطلّب مصادقة AccountManager للأسباب التالية:

  • تجربة مستخدم أفضل وسهولة إدارة الحسابات: يمكن للمستخدمين إدارة جميع حساباتهم بسهولة من قائمة الحسابات في إعدادات النظام، بما في ذلك تسجيل الدخول وتسجيل الخروج.
  • تجارب "الضيف": بما أنّ السيارات هي أجهزة مشترَكة، يمكن للمصنّعين الأصليين للأجهزة تفعيل تجارب "الضيف" في السيارة، حيث لا يمكن إضافة حسابات.

إضافة صيغ مختلفة لسلسلة النص

قد تعرض أحجام شاشات السيارات المختلفة كميات مختلفة من النص. باستخدام Car App API المستوى 2 والإصدارات الأحدث، يمكنك تحديد عدة صيغ لسلسلة نصية لتناسب الشاشة على أفضل وجه. لمعرفة الأماكن التي يتم فيها قبول صيغ النص، ابحث عن النماذج والمكوّنات التي تتضمّن CarText.

يمكنك إضافة صيغ مختلفة لسلسلة نصية إلى CarText باستخدام طريقة CarText.Builder.addVariant():

Kotlin

val itemTitle = CarText.Builder("This is a very long string")
    .addVariant("Shorter string")
    ...
    .build()

Java

CarText itemTitle = new CarText.Builder("This is a very long string")
    .addVariant("Shorter string")
    ...
    .build();

يمكنك بعد ذلك استخدام هذا CarText، مثلاً كنص أساسي في GridItem.

Kotlin

GridItem.Builder()
    .addTitle(itemTitle)
    ...
    .build()

Java

new GridItem.Builder()
    .addTitle(itemTitle)
    ...
    build();

أضِف السلاسل بالترتيب من الأكثر تفضيلاً إلى الأقل تفضيلاً، مثلاً من الأطول إلى الأقصر. يختار المضيف السلسلة المناسبة للطول استنادًا إلى مقدار المساحة المتوفّرة على شاشة السيارة.

إضافة CarIcons مضمّنة للصفوف

يمكنك إضافة رموز مضمّنة مع النص لتحسين المظهر المرئي لتطبيقك باستخدام CarIconSpan. راجِع مستندات CarIconSpan.create للحصول على مزيد من المعلومات حول إنشاء هذه النطاقات. يمكنك الاطّلاع على تنسيق نص Spantastic باستخدام النطاقات للحصول على نظرة عامة حول طريقة عمل تنسيق النص باستخدام النطاقات.

Kotlin

  
val rating = SpannableString("Rating: 4.5 stars")
rating.setSpan(
    CarIconSpan.create(
        // Create a CarIcon with an image of four and a half stars
        CarIcon.Builder(...).build(),
        // Align the CarIcon to the baseline of the text
        CarIconSpan.ALIGN_BASELINE
    ),
    // The start index of the span (index of the character '4')
    8,
    // The end index of the span (index of the last 's' in "stars")
    16,
    Spanned.SPAN_INCLUSIVE_INCLUSIVE
)

val row = Row.Builder()
    ...
    .addText(rating)
    .build()
  
  

Java

  
SpannableString rating = new SpannableString("Rating: 4.5 stars");
rating.setSpan(
        CarIconSpan.create(
                // Create a CarIcon with an image of four and a half stars
                new CarIcon.Builder(...).build(),
                // Align the CarIcon to the baseline of the text
                CarIconSpan.ALIGN_BASELINE
        ),
        // The start index of the span (index of the character '4')
        8,
        // The end index of the span (index of the last 's' in "stars")
        16,
        Spanned.SPAN_INCLUSIVE_INCLUSIVE
);
Row row = new Row.Builder()
        ...
        .addText(rating)
        .build();
  
  

Car Hardware APIs

بدءًا من المستوى 3 من Car App API، تتضمّن Car App Library واجهات برمجة تطبيقات يمكنك استخدامها للوصول إلى خصائص المركبة وأجهزة الاستشعار.

المتطلبات

لاستخدام واجهات برمجة التطبيقات مع Android Auto، ابدأ بإضافة تبعية إلى androidx.car.app:app-projected في ملف build.gradle لوحدة Android Auto. بالنسبة إلى نظام التشغيل Android Automotive، أضِف تبعية إلى androidx.car.app:app-automotive في ملف build.gradle لوحدة Android Automotive OS.

بالإضافة إلى ذلك، عليك في ملف AndroidManifest.xml تضمين بيان بالأذونات ذات الصلة المطلوبة لطلب بيانات السيارة التي تريد استخدامها. يُرجى العِلم أنّه يجب أن يمنحك المستخدم هذه الأذونات أيضًا. يمكنك استخدام الرمز البرمجي نفسه على كل من Android Auto وAndroid Automotive OS، بدلاً من الاضطرار إلى إنشاء مسارات تعتمد على النظام الأساسي. ومع ذلك، تختلف الأذونات المطلوبة.

CarInfo

يوضّح هذا الجدول الخصائص التي تعرضها واجهات برمجة التطبيقات CarInfo والأذونات التي عليك طلبها لاستخدامها:

الطرق الخصائص أذونات Android Auto أذونات نظام التشغيل Android Automotive متوافق منذ مستوى Car App API
fetchModel الصنع والطراز والسنة android.car.permission.CAR_INFO 3
fetchEnergyProfile أنواع وصلات المركبات الكهربائية وأنواع الوقود com.google.android.gms.permission.CAR_FUEL android.car.permission.CAR_INFO 3
fetchExteriorDimensions

لا تتوفّر هذه البيانات إلا على بعض المركبات التي تعمل بنظام التشغيل Android Automotive والتي تستخدم الإصدار 30 أو الإصدارات الأحدث من واجهة برمجة التطبيقات

الأبعاد الخارجية لا ينطبق android.car.permission.CAR_INFO 7
addTollListener
removeTollListener
حالة بطاقة تحصيل رسوم العبور ونوعها 3
addEnergyLevelListener
removeEnergyLevelListener
مستوى شحن البطارية ومستوى الوقود ومستوى الوقود منخفض والمدى المتبقي com.google.android.gms.permission.CAR_FUEL android.car.permission.CAR_ENERGY،
android.car.permission.CAR_ENERGY_PORTS،
android.car.permission.READ_CAR_DISPLAY_UNITS
3
addSpeedListener
removeSpeedListener
السرعة الأولية، وسرعة العرض (المعروضة على شاشة مجموعة العدادات في السيارة) com.google.android.gms.permission.CAR_SPEED android.car.permission.CAR_SPEED،
android.car.permission.READ_CAR_DISPLAY_UNITS
3
addMileageListener
removeMileageListener

تحذير: اسم طريقة getOdometerMeters في الفئة Mileage غير دقيق، وهي تعرض كيلومترات وليس أمتارًا.

مسافة عدّاد المسافات com.google.android.gms.permission.CAR_MILEAGE لا تتوفّر هذه البيانات على نظام التشغيل Android Automotive للتطبيقات المثبَّتة من "متجر Play". 3

على سبيل المثال، للحصول على النطاق المتبقي، أنشئ مثيلاً من الكائن CarInfo، ثم أنشئ OnCarDataAvailableListener وسجِّله:

Kotlin

val carInfo = carContext.getCarService(CarHardwareManager::class.java).carInfo

val listener = OnCarDataAvailableListener<EnergyLevel> { data ->
    if (data.rangeRemainingMeters.status == CarValue.STATUS_SUCCESS) {
      val rangeRemaining = data.rangeRemainingMeters.value
    } else {
      // Handle error
    }
  }

carInfo.addEnergyLevelListener(carContext.mainExecutor, listener)

// Unregister the listener when you no longer need updates
carInfo.removeEnergyLevelListener(listener)

Java

CarInfo carInfo = getCarContext().getCarService(CarHardwareManager.class).getCarInfo();

OnCarDataAvailableListener<EnergyLevel> listener = (data) -> {
  if(data.getRangeRemainingMeters().getStatus() == CarValue.STATUS_SUCCESS) {
    float rangeRemaining = data.getRangeRemainingMeters().getValue();
  } else {
    // Handle error
  }
};

carInfo.addEnergyLevelListener(getCarContext().getMainExecutor(), listener);

// Unregister the listener when you no longer need updates
carInfo.removeEnergyLevelListener(listener);

لا تفترض أنّ البيانات من السيارة متاحة في جميع الأوقات. في حال حدوث خطأ، تحقَّق من الحالة الخاصة بالقيمة التي طلبتها لمعرفة سبب تعذّر استرداد البيانات المطلوبة. راجِع المستندات المرجعية لمعرفة تعريف فئة CarInfo الكامل.

CarSensors

يتيح لك الصف CarSensors الوصول إلى بيانات مقياس التسارع والجيروسكوب والبوصلة والموقع الجغرافي الخاصة بالمركبة. وقد يعتمد مدى توفّر هذه القيم على الشركة المصنّعة للجهاز الأصلي. يكون تنسيق البيانات الواردة من مقياس التسارع والجيروسكوب والبوصلة هو نفسه التنسيق الذي تحصل عليه من واجهة برمجة التطبيقات SensorManager. على سبيل المثال، للتحقّق من اتجاه المركبة:

Kotlin

val carSensors = carContext.getCarService(CarHardwareManager::class.java).carSensors

val listener = OnCarDataAvailableListener<Compass> { data ->
    if (data.orientations.status == CarValue.STATUS_SUCCESS) {
      val orientation = data.orientations.value
    } else {
      // Data not available, handle error
    }
  }

carSensors.addCompassListener(CarSensors.UPDATE_RATE_NORMAL, carContext.mainExecutor, listener)

// Unregister the listener when you no longer need updates
carSensors.removeCompassListener(listener)

Java

CarSensors carSensors = getCarContext().getCarService(CarHardwareManager.class).getCarSensors();

OnCarDataAvailableListener<Compass> listener = (data) -> {
  if (data.getOrientations().getStatus() == CarValue.STATUS_SUCCESS) {
    List<Float> orientations = data.getOrientations().getValue();
  } else {
    // Data not available, handle error
  }
};

carSensors.addCompassListener(CarSensors.UPDATE_RATE_NORMAL, getCarContext().getMainExecutor(),
    listener);

// Unregister the listener when you no longer need updates
carSensors.removeCompassListener(listener);

للوصول إلى بيانات الموقع الجغرافي من السيارة، عليك أيضًا الإفصاح عن إذن android.permission.ACCESS_FINE_LOCATION وطلبه.

الاختبار

لمحاكاة بيانات المستشعر عند الاختبار على Android Auto، يُرجى الرجوع إلى قسمَي أجهزة الاستشعار وإعدادات أجهزة الاستشعار في دليل &quot;وحدة رأسية على الكمبيوتر&quot;. لمحاكاة بيانات المستشعر عند الاختبار على نظام التشغيل Android Automotive، يُرجى الرجوع إلى قسم محاكاة حالة الأجهزة في دليل محاكي Android Automotive.

دورات حياة CarAppService والجلسة والشاشة

تنفّذ الفئتان Session وScreen الواجهة LifecycleOwner. أثناء تفاعل المستخدم مع التطبيق، يتم استدعاء عمليات معاودة الاتصال الخاصة بدورة حياة العنصرَين Session وScreen، كما هو موضّح في المخططات التالية.

دورات حياة CarAppService وSession

الشكل 1 Session دورة الحياة

للاطّلاع على التفاصيل الكاملة، راجِع مستندات طريقة Session.getLifecycle.

مراحل نشاط الشاشة

الشكل 2 Screen دورة الحياة

لمعرفة التفاصيل الكاملة، يُرجى الاطّلاع على مستندات طريقة Screen.getLifecycle.

التسجيل من ميكروفون السيارة

باستخدام CarAppService في تطبيقك وواجهة برمجة التطبيقات CarAudioRecord، يمكنك منح تطبيقك إذن الوصول إلى ميكروفون السيارة. على المستخدمين منح تطبيقك الإذن بالوصول إلى ميكروفون السيارة. يمكن لتطبيقك تسجيل مدخلات المستخدم ومعالجتها داخل التطبيق.

إذن التسجيل

قبل تسجيل أي صوت، عليك أولاً توضيح إذن التسجيل في AndroidManifest.xml وطلب أن يمنحه المستخدم.

<manifest ...>
   ...
   <uses-permission android:name="android.permission.RECORD_AUDIO" />
   ...
</manifest>

يجب طلب الإذن بالتسجيل في وقت التشغيل. راجِع قسم طلب الأذونات للحصول على تفاصيل حول كيفية طلب إذن في تطبيق السيارة.

تسجيل الصوت

بعد أن يمنح المستخدم الإذن بالتسجيل، يمكنك تسجيل الصوت ومعالجة التسجيل.

Kotlin

val carAudioRecord = CarAudioRecord.create(carContext)
        carAudioRecord.startRecording()

        val data = ByteArray(CarAudioRecord.AUDIO_CONTENT_BUFFER_SIZE)
        while(carAudioRecord.read(data, 0, CarAudioRecord.AUDIO_CONTENT_BUFFER_SIZE) >= 0) {
            // Use data array
            // Potentially call carAudioRecord.stopRecording() if your processing finds end of speech
        }
        carAudioRecord.stopRecording()
 

Java

CarAudioRecord carAudioRecord = CarAudioRecord.create(getCarContext());
        carAudioRecord.startRecording();

        byte[] data = new byte[CarAudioRecord.AUDIO_CONTENT_BUFFER_SIZE];
        while (carAudioRecord.read(data, 0, CarAudioRecord.AUDIO_CONTENT_BUFFER_SIZE) >= 0) {
            // Use data array
            // Potentially call carAudioRecord.stopRecording() if your processing finds end of speech
        }
        carAudioRecord.stopRecording();
 

التركيز على الصوت

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

في ما يلي مثال على كيفية الحصول على تركيز الصوت:

Kotlin

 
val carAudioRecord = CarAudioRecord.create(carContext)
        
        // Take audio focus so that user's media is not recorded
        val audioAttributes = AudioAttributes.Builder()
            .setContentType(AudioAttributes.CONTENT_TYPE_SPEECH)
            // Use the most appropriate usage type for your use case
            .setUsage(AudioAttributes.USAGE_ASSISTANCE_NAVIGATION_GUIDANCE)
            .build()
        
        val audioFocusRequest =
            AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE)
                .setAudioAttributes(audioAttributes)
                .setOnAudioFocusChangeListener { state: Int ->
                    if (state == AudioManager.AUDIOFOCUS_LOSS) {
                        // Stop recording if audio focus is lost
                        carAudioRecord.stopRecording()
                    }
                }
                .build()
        
        if (carContext.getSystemService(AudioManager::class.java)
                .requestAudioFocus(audioFocusRequest)
            != AudioManager.AUDIOFOCUS_REQUEST_GRANTED
        ) {
            // Don't record if the focus isn't granted
            return
        }
        
        carAudioRecord.startRecording()
        // Process the audio and abandon the AudioFocusRequest when done

Java

CarAudioRecord carAudioRecord = CarAudioRecord.create(getCarContext());
        // Take audio focus so that user's media is not recorded
        AudioAttributes audioAttributes =
                new AudioAttributes.Builder()
                        .setContentType(AudioAttributes.CONTENT_TYPE_SPEECH)
                        // Use the most appropriate usage type for your use case
                        .setUsage(AudioAttributes.USAGE_ASSISTANCE_NAVIGATION_GUIDANCE)
                        .build();

        AudioFocusRequest audioFocusRequest =
                new AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE)
                        .setAudioAttributes(audioAttributes)
                        .setOnAudioFocusChangeListener(state -> {
                            if (state == AudioManager.AUDIOFOCUS_LOSS) {
                                // Stop recording if audio focus is lost
                                carAudioRecord.stopRecording();
                            }
                        })
                        .build();

        if (getCarContext().getSystemService(AudioManager.class).requestAudioFocus(audioFocusRequest)
                != AUDIOFOCUS_REQUEST_GRANTED) {
            // Don't record if the focus isn't granted
            return;
        }

        carAudioRecord.startRecording();
        // Process the audio and abandon the AudioFocusRequest when done
 

مكتبة الاختبارات

توفّر مكتبة الاختبار في Android for Cars فئات مساعدة يمكنك استخدامها للتحقّق من صحة سلوك تطبيقك في بيئة اختبار. على سبيل المثال، يتيح لك SessionController محاكاة عملية ربط بالمضيف والتأكّد من إنشاء Screen و Template بشكل صحيح وعرضهما.

راجِع العينات للاطّلاع على أمثلة حول الاستخدام.

الإبلاغ عن مشكلة في "مكتبة تطبيقات Android للسيارات"

إذا واجهت مشكلة في المكتبة، يُرجى الإبلاغ عنها باستخدام أداة تتبُّع المشاكل في Google. احرص على ملء جميع المعلومات المطلوبة في نموذج المشكلة.

إنشاء مشكلة جديدة

قبل تسجيل مشكلة جديدة، يُرجى التحقّق مما إذا كانت مُدرَجة في ملاحظات الإصدار الخاصة بالمكتبة أو تم الإبلاغ عنها في قائمة المشاكل. يمكنك الاشتراك في المشاكل والتصويت عليها من خلال النقر على النجمة الخاصة بمشكلة في أداة التتبُّع. لمزيد من المعلومات، اطّلِع على مقالة الاشتراك في مشكلة.