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

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

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

قبل البدء

  1. مراجعة صفحات Design for Drive التي تغطي مكتبة تطبيقات السيارات
  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>

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

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

يُرجى توضيح الحد الأدنى لمستوى واجهة برمجة التطبيقات لتطبيق السيارة الذي يتيحه تطبيقك في ملف AndroidManifest.xml:

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

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

إنشاء CarAppService والجلسة

يحتاج تطبيقك إلى توسيع فئة 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" أيضًا وظائف أخرى، مثل السماح لك بتحميل الموارد القابلة للرسم باستخدام الإعدادات التي ضبطتها من شاشة السيارة، وتشغيل تطبيق في السيارة باستخدام عناصر intent، والإشارة إلى ما إذا كان يجب أن يعرض تطبيقك خريطته في المظهر الداكن.

تنفيذ التنقل على الشاشة

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

توفّر الفئة 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:

النموذج إذن النموذج إرشادات متعلّقة بالفئة
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.

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

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

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

تنفيذ استدعاءات التفاعل

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

التفاعل طريقة واحدة (SurfaceCallback) يتوفّر هذا الخيار منذ مستوى واجهة برمجة التطبيقات لتطبيق السيارة.
النقر 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 intent، يمكنك الاطّلاع على قسم بدء تطبيق سيارة من خلال intent.

لا يُسمح ببعض الإجراءات، مثل تلك التي تتطلب توجيه المستخدم لمتابعة التفاعل على أجهزته الجوّالة، إلا عند ركن السيارة. يمكنك استخدام 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();

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

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

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

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

للحصول على مزيد من المعلومات حول كيفية تصميم إشعارات تطبيق السيارة، اطّلع على دليل "تصميم Google للقيادة" حول الإشعارات.

عرض الخبز المحمص

يمكن لتطبيقك عرض إشعار منبثق باستخدام 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، بدلاً من الحاجة إلى إنشاء مسارات تعتمد على النظام الأساسي.

ضبط نمط مربّع حوار الأذونات على 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 لتنفيذ أحد الإجراءات التالية:

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

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 بقصد تضمين معرّف الموارد المنتظم (URI) للبيانات:

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 في الحزمة. على سبيل المثال، إذا أرسل أحد التطبيقات نموذجين أثناء وجوده في الشاشة A، ثم دفع الشاشة B، فيمكنه الآن إرسال ثلاثة نماذج أخرى. بدلاً من ذلك، إذا تم تنظيم كل شاشة لإرسال نموذج واحد، يمكن للتطبيق إرسال خمس مثيلات للشاشة إلى حزمة ScreenManager.

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

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

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

عمليات الرجوع

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

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

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

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

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

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

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

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

يمكنك تحديد ما إذا كان تطبيقك يعمل على Android Auto أو Android Automotive من خلال استخدام واجهة برمجة تطبيقات 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();
}

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

قد تتيح السيارات المختلفة عرض عدد مختلف من مثيلات 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. تتيح مكتبة تطبيقات سيارة السيارة حاليًا طرق تسجيل الدخول التالية:

  • InputSignInMethod لتسجيل الدخول باسم المستخدم أو كلمة المرور
  • PinSignInMethod لتسجيل الدخول باستخدام رقم التعريف الشخصي، حيث يربط المستخدم حسابه من هاتفه باستخدام رقم تعريف شخصي يظهر على الوحدة الرئيسية.
  • ProviderSignInMethod لتسجيل الدخول إلى مقدّم الخدمة، مثل تسجيل الدخول بحساب Google ونقرة واحدة
  • 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();

استخدام مدير الحساب

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

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

إضافة صيغ السلسلة النصية

قد تعرض أحجام شاشات السيارة المختلفة كميات مختلفة من النص. من خلال المستوى 2 والأعلى من واجهة برمجة تطبيقات Car App API، يمكنك تحديد صيغ متعددة لسلسلة نصية لتناسب الشاشة على أفضل نحو. للتعرّف على الأماكن التي تقبل فيها خيارات المنتجات النصية، ابحث عن النماذج والمكوّنات التي تحمل رمز 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 لمزيد من المعلومات حول إنشاء هذه النطاقات. يمكنك الاطّلاع على نمط النص السريع باستخدام Spans للحصول على نظرة عامة حول طريقة عمل نمط النص باستخدام الامتدادات.

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();
  
  

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

بدءًا من المستوى 3 من واجهة برمجة تطبيقات 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.

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

معلومات السيارة

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

الطرق الخصائص أذونات Android Auto أذونات نظام التشغيل Android Automotive يتوفّر هذا الخيار منذ مستوى واجهة برمجة التطبيقات لتطبيق السيارة.
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 من واجهة برمجة التطبيقات (API) أو الإصدارات الأحدث.

الأبعاد الخارجية لا ينطبق 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
مسافة عدّاد المسافات 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 إمكانية الوصول إلى مقياس التسارع والجيروسكوب والبوصلة وبيانات الموقع الجغرافي للمركبة. وقد يعتمد مدى توفُّر هذه القيم على المصنّع الأصلي للجهاز. إنّ تنسيق البيانات من مقياس التسارع والجيروسكوب والبوصلة هو نفسه الذي تستخدمه في SensorManager API. على سبيل المثال، للتحقق من عنوان المركبة:

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، يمكنك الاطّلاع على قسمَي أجهزة الاستشعار وضبط أجهزة الاستشعار في دليل الوحدة الرئيسية للكمبيوتر المكتبي. لمحاكاة بيانات جهاز الاستشعار عند إجراء اختبار على نظام التشغيل Android Automotive، يمكنك الرجوع إلى قسم محاكاة حالة الجهاز في دليل محاكي نظام التشغيل Android Automotive.

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

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

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

الشكل 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. تأكَّد من ملء جميع المعلومات المطلوبة في نموذج المشكلة.

إنشاء عدد جديد

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