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

تتيح لك مكتبة تطبيقات Android للسيارات إمكانية استخدام تطبيقات التنقل ونقاط الاهتمام وإنترنت الأشياء (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

تحدِّد "مكتبة تطبيقات السيارة" مستويات واجهة برمجة التطبيقات الخاصة بها حتى تتمكّن من معرفة ميزات المكتبة التي يوفّرها مضيف النماذج على أي مركبة. لاسترداد أعلى مستوى لواجهة برمجة تطبيقات 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 وsession

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

Kotlin

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

Java

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

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

Kotlin

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

Java

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

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

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

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

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

Kotlin

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

Java

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

فئة CarContext

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

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

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

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

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

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

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

Kotlin

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

Java

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

الكائن Action.BACK هو Action عادي يستدعي ScreenManager.pop تلقائيًا. يمكن إلغاء هذا السلوك باستخدام مثيل OnBackPressedDispatcher المتوفّر في CarContext.

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

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

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

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

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

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

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

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

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

Kotlin

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

Java

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

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

Kotlin

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

Java

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

لمزيد من التفاصيل عن كيفية بدء تشغيل التطبيقات، بما في ذلك تنسيق ACTION_NAVIGATE الغرض، يُرجى الاطّلاع على قسم بدء تشغيل تطبيق سيارة يتضمّن هدفاً.

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

Kotlin

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

Java

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

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

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

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

Kotlin

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

Java

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

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

  • قد يتم عرض إشعار تنبيه (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().

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

تشغيل تطبيق سيارة هدف

يمكنك استدعاء الطريقة 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 API لاسترداد معلومات الاتصال أثناء وقت التشغيل.

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

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

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

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

Kotlin

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

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

Java

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

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

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

Kotlin

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

Java

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

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

Kotlin

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

Java

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

استخدام AccountManager

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

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

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

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

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

Kotlin

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

Java

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

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

Kotlin

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

Java

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

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

إضافة رموز CarIcon مُضمَّنة للصفوف

يمكنك استخدام 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

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

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

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