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

تتيح لك مكتبة تطبيقات Android for Cars إمكانية نقل تطبيقات التنقّل ونقاط الاهتمام وتطبيقات إنترنت الأشياء (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 على النحو الموضَّح في القسم السابق:

  • androidx.car.app.category.NAVIGATION: تطبيق يقدم اتجاهات التنقل خطوة بخطوة. اطلع على إنشاء تطبيقات تنقل للسيارات للحصول على وثائق إضافية حول هذه الفئة.
  • androidx.car.app.category.POI: تطبيق يوفّر وظائف ذات صلة بالعثور على نقاط الاهتمام مثل مواقف السيارات ومحطات الشحن ومحطات الوقود. اطلع على إنشاء تطبيقات نقاط الاهتمام للسيارات للحصول على مستندات إضافية حول هذه الفئة.
  • androidx.car.app.category.IOT: تطبيق يتيح للمستخدمين اتخاذ الإجراءات ذات الصلة على الأجهزة المتصلة من داخل السيارة. اطلع على إنشاء تطبيقات إنترنت الأشياء للسيارات للحصول على مستندات إضافية حول هذه الفئة.

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

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

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

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

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

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

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

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

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

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

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

مستوى واجهة برمجة تطبيقات Car App API

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

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

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

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

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

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

Kotlin

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

Java

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

يكون مثيل Session مسؤولاً عن عرض المثيل Screen لاستخدام أول مرة يتم فيها تشغيل التطبيق:

Kotlin

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

Java

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

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

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

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

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

Kotlin

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

Java

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

حصة CarContext

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Kotlin

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

Java

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

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

Kotlin

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

Java

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

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

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

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

Kotlin

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

Java

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

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

Kotlin

val gridItemLimit = manager.getContentLimit(ConstraintManager.CONTENT_LIMIT_TYPE_GRID)

Java

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

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

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

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

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

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

Kotlin

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

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

Java

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

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

يجب توفير InputCallback لـ InputSignInMethod Builder.

Kotlin

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

Java

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

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

Kotlin

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

Java

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

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

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

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

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

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

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

الشروط

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

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

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

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

الطرق الخصائص أذونات Android Auto أذونات نظام التشغيل Android Automotive
fetchModel الصنع والطراز والسنة android.car.permission.CAR_INFO
fetchEnergyProfile أنواع وصلات المركبات الكهربائية وأنواع الوقود com.google.android.gms.permission.CAR_FUEL android.car.permission.CAR_INFO
addTollListener
removeTollListener
حالة بطاقة تحصيل رسوم العبور ونوع بطاقة تحصيل رسوم العبور
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
addSpeedListener
removeSpeedListener
سرعة العرض الأولية وسرعة العرض (على شاشة العرض العنقودي في السيارة) com.google.android.gms.permission.CAR_SPEED android.car.permission.CAR_SPEED،
android.car.permission.READ_CAR_DISPLAY_UNITS
addMileageListener
removeMileageListener
مسافة عدّاد المسافات com.google.android.gms.permission.CAR_MILEAGE لا تتوفّر هذه البيانات على نظام التشغيل Android Automotive للتطبيقات المثبَّتة من "متجر Play".

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

Kotlin

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

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

carInfo.addEnergyLevelListener(carContext.mainExecutor, listener)
…
// Unregister the listener when you no longer need updates
carInfo.removeEnergyLevelListener(listener)

Java

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

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

carInfo.addEnergyLevelListener(getCarContext().getMainExecutor(), listener);
…
// Unregister the listener when you no longer need updates
carInfo.removeEnergyLevelListener(listener);

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

أجهزة استشعار السيارة

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

Kotlin

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

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

carSensors.addCompassListener(CarSensors.UPDATE_RATE_NORMAL, carContext.mainExecutor, listener)
…
// Unregister the listener when you no longer need updates
carSensors.removeCompassListener(listener)

Java

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

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

carSensors.addCompassListener(CarSensors.UPDATE_RATE_NORMAL, getCarContext().getMainExecutor(),
    listener);
…
// Unregister the listener when you no longer need updates
carSensors.removeCompassListener(listener);

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

الاختبار

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

مراحل نشاط CarAppService والجلسة والشاشة

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

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

الشكل 1. مراحل نشاط Session

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

دورة حياة الشاشة

الشكل 2. مراحل نشاط Screen

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

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

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

إذن بالتسجيل

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

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

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

تسجيل الصوت

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

Kotlin

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

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

Java

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

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

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

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

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

Kotlin

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

Java

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

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

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

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

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

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

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

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

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

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

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