استخدام مكتبة تطبيقات 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 الذي تستخدمه تطبيقات التنقّل من نقطة إلى أخرى لمشاركة مَعلمات ScreenManager الخاصة بالتنقّل وغيرها من الأحداث المتعلقة بالتنقّل مع المضيف.

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

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

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

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

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

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

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

  • 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 لمزيد من المعلومات عن إنشاء هذه النطاقات. يمكنك الاطّلاع على نمط النص السريع باستخدام 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 يتوفّر هذا الخيار منذ مستوى واجهة برمجة التطبيقات لتطبيق السيارة.
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 وجلسة

الشكل 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. احرص على ملء جميع المعلومات المطلوبة في نموذج المشكلة.

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

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