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

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

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

قبل البدء

  1. راجِع صفحات التصميم المناسب لقيادة السيارات التي تتناول "مكتبة تطبيقات السيارات"
  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 يتوافق مع المضيف، استخدِم الأسلوب getCarAppApiLevel().

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

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

اطّلِع على مستندات التعليق التوضيحي RequiresCarApi لمعرفة تفاصيل عن كيفية الحفاظ على التوافق مع الإصدارات القديمة وتحديد الحد الأدنى لمستوى واجهة برمجة التطبيقات المطلوب لاستخدام إحدى الميزات. للاطّلاع على تعريف لمستوى IDE المطلوب لاستخدام ميزة معيّنة من "مكتبة تطبيقات السيارات"، يُرجى الاطّلاع على مستندات IDE المرجعية الخاصة ب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 لكي يعرض سلسلة "مرحبًا بك" بسيطة:

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:

النموذج إذن النموذج إرشادات حول الفئات
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 (introduction of template)

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

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

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

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

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

لتلقّي طلبات إعادة الاتصال للتفاعل في الخريطة، يجب إضافة زر 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_NAVIGATEintent، يُرجى الاطّلاع على القسم بدء تطبيق سيارة باستخدام 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 باستخدام نية تتضمّن عنوان URL للبيانات:

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 إلى عرض يُفترض أن يظل معروضًا على الشاشة وأن يتم تعديله من خلال إضافة تعليمات جديدة تتعلّق بالاتّجاهات للمستخدم. وعندما يصل إلى أحد هذين النموذجَين، يُعيد المضيف ضبط حصة النموذج، ويتعامل مع هذا النموذج كما لو كان هو الخطوة الأولى لمهمة جديدة. ويسمح ذلك للتطبيق ببدء مهمة جديدة. اطّلِع على مستندات النماذج الفردية لمعرفة النماذج التي تؤدي إلى إعادة الضبط على المضيف.

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

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

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

يمكنك تحديد ما إذا كان تطبيقك يعمل على Android Auto أو Android Automotive OS باستخدام واجهة برمجة التطبيقات CarConnection API ل retrieving retrieve information of connection at runtime.

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

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

  • 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 تنسيق النص باستخدام Spans للحصول على نظرة عامة حول آلية عمل تنسيق النص باستخدام 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 API، تتضمّن "مكتبة تطبيقات السيارات" واجهات برمجة تطبيقات يمكنك استخدامها للوصول إلى خصائص المركبات وأجهزة الاستشعار.

المتطلبات

لاستخدام واجهات برمجة التطبيقات مع 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

يوضّح هذا الجدول السمات التي تعرضها واجهات برمجة تطبيقات 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
المسافة المُسجَّلة في عدّاد المسافات 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);

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

CarSensors

تتيح لك فئة 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

تنفِّذ فئتَا Session و Screen واجهة LifecycleOwner. أثناء تفاعل المستخدم مع التطبيق، يتمّ استدعاء وظائف callback الخاصة بمراحل نشاط 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 للسيارات" klassen مساعدة يمكنك استخدامها للتحقّق من سلوك تطبيقك في بيئة اختبار. على سبيل المثال، يتيح لك الإجراء SessionController محاكاة اتصال بالمضيف والتأكّد من إنشاء Screen و Template الصحيحَين و إعادتهما.

يُرجى الرجوع إلى عيّنات لمعرفة أمثلة على الاستخدام.

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

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

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

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