از کتابخانه برنامه Android for Cars استفاده کنید

کتابخانه برنامه Android for Cars به ​​شما امکان می‌دهد برنامه ناوبری ، نقطه مورد علاقه (POI) ، اینترنت اشیا (IOT) یا برنامه آب و هوا را به ماشین بیاورید. این کار را با ارائه مجموعه‌ای از الگوهای طراحی شده برای مطابقت با استانداردهای حواس‌پرتی راننده و مراقبت از جزئیاتی مانند انواع فاکتورهای صفحه نمایش خودرو و روش‌های ورودی انجام می‌دهد.

این راهنما نمای کلی از ویژگی‌ها و مفاهیم کلیدی کتابخانه را ارائه می‌کند و شما را در فرآیند راه‌اندازی یک برنامه اولیه راهنمایی می‌کند.

قبل از شروع

  1. صفحات طراحی برای رانندگی را که کتابخانه برنامه ماشین را پوشش می دهد، مرور کنید
  2. اصطلاحات و مفاهیم کلیدی را در بخش زیر مرور کنید.
  3. با رابط کاربری Android Auto System و طراحی سیستم عامل Android Automotive آشنا شوید.
  4. یادداشت های انتشار را مرور کنید.
  5. نمونه ها را مرور کنید.

اصطلاحات و مفاهیم کلیدی

مدل ها و قالب ها
رابط کاربری با نموداری از اشیاء مدل نشان داده می‌شود که می‌توانند به روش‌های مختلف با هم مرتب شوند، همانطور که توسط قالبی که به آن تعلق دارند مجاز است. قالب ها زیرمجموعه ای از مدل ها هستند که می توانند به عنوان ریشه در آن نمودارها عمل کنند. مدل‌ها شامل اطلاعاتی هستند که در قالب متن و تصویر به کاربر نمایش داده می‌شوند و همچنین ویژگی‌هایی برای پیکربندی جنبه‌های ظاهر بصری چنین اطلاعاتی - برای مثال رنگ‌های متن یا اندازه‌های تصویر. میزبان مدل ها را به نماهایی تبدیل می کند که برای مطابقت با استانداردهای حواس پرتی راننده طراحی شده اند و از جزئیاتی مانند انواع فاکتورهای صفحه نمایش خودرو و روش های ورودی مراقبت می کند.
میزبان
میزبان جزء پشتیبان است که عملکرد ارائه شده توسط APIهای کتابخانه را پیاده سازی می کند تا برنامه شما بتواند در ماشین اجرا شود. مسئولیت‌های میزبان از کشف برنامه شما و مدیریت چرخه عمر آن گرفته تا تبدیل مدل‌های شما به نماها و اطلاع‌رسانی به برنامه شما از تعاملات کاربر متغیر است. در دستگاه های تلفن همراه، این میزبان توسط Android Auto پیاده سازی شده است. در سیستم عامل Android Automotive، این میزبان به عنوان یک برنامه سیستمی نصب شده است.
محدودیت های قالب
قالب های مختلف محدودیت هایی را در محتوای مدل های خود اعمال می کنند. به عنوان مثال، قالب‌های فهرست محدودیت‌هایی در تعداد مواردی دارند که می‌توانند به کاربر ارائه شوند. الگوها همچنین محدودیت هایی در نحوه اتصال آنها برای شکل دادن به جریان یک کار دارند. به عنوان مثال، این برنامه تنها می تواند تا پنج الگو را به پشته صفحه نمایش دهد. برای جزئیات بیشتر به محدودیت های الگو مراجعه کنید.
Screen
Screen کلاسی است که توسط کتابخانه ارائه می شود که برنامه ها برای مدیریت رابط کاربری ارائه شده به کاربر پیاده سازی می کنند. یک Screen یک چرخه حیات دارد و مکانیزمی را برای برنامه برای ارسال الگو برای نمایش زمانی که صفحه نمایش قابل مشاهده است فراهم می کند. نمونه های Screen همچنین می توانند به پشته Screen داده شوند و از آن خارج شوند، که تضمین می کند آنها به محدودیت های جریان الگو پایبند هستند.
CarAppService
CarAppService یک کلاس Service انتزاعی است که برنامه شما باید آن را پیاده سازی و صادر کند تا توسط میزبان کشف و مدیریت شود. CarAppService برنامه شما مسئول تأیید اعتبار اتصال میزبان با استفاده از createHostValidator است و متعاقباً با استفاده از onCreateSession نمونه‌های Session را برای هر اتصال ارائه می‌کند.
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 خود را همانطور که در بخش قبل توضیح داده شد، با اضافه کردن یک یا چند مورد از مقادیر دسته زیر در فیلتر intent، دسته برنامه خود را اعلام کنید:

برای توضیحات دقیق هر دسته و معیارهای مربوط به برنامه‌ها ، کیفیت برنامه اندروید برای خودروها را ببینید.

نام و نماد برنامه را مشخص کنید

باید نام و نماد برنامه را مشخص کنید که میزبان بتواند از آن برای نشان دادن برنامه شما در رابط کاربری سیستم استفاده کند.

می‌توانید با استفاده از 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>

سطح API برنامه Car

Car App Library سطوح API خود را تعریف می‌کند تا بتوانید بدانید کدام ویژگی‌های کتابخانه توسط میزبان الگو در خودرو پشتیبانی می‌شود. برای بازیابی بالاترین سطح API Car App پشتیبانی شده توسط یک میزبان، از متد getCarAppApiLevel() استفاده کنید.

حداقل سطح API Car App را که توسط برنامه شما پشتیبانی می شود در فایل AndroidManifest.xml خود اعلام کنید:

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

برای جزئیات بیشتر در مورد نحوه حفظ سازگاری به عقب و اعلام حداقل سطح API مورد نیاز برای استفاده از یک ویژگی، به مستندات حاشیه نویسی RequiresCarApi مراجعه کنید. برای تعریف اینکه کدام سطح API برای استفاده از ویژگی خاصی از کتابخانه برنامه خودرو لازم است، مستندات مرجع CarAppApiLevels را بررسی کنید.

CarAppService و Session خود را ایجاد کنید

برنامه شما باید کلاس CarAppService را گسترش دهد و روش onCreateSession خود را پیاده سازی کند، که نمونه Session مربوط به اتصال فعلی به هاست را برمی گرداند:

کاتلین

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

جاوا

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

نمونه Session مسئول برگرداندن نمونه Screen برای استفاده از اولین بار شروع برنامه است:

کاتلین

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

جاوا

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

برای رسیدگی به سناریوهایی که برنامه ماشین شما باید از صفحه‌ای شروع شود که صفحه اصلی یا صفحه اصلی برنامه شما نیست، مانند مدیریت پیوندهای عمیق، می‌توانید قبل از بازگشت از onCreateScreen ، پشته‌ای از صفحه‌ها را با استفاده از ScreenManager.push پیش‌بینی کنید. Pre-seeding به کاربران امکان می دهد از اولین صفحه ای که برنامه شما نشان می دهد به صفحه های قبلی برگردند.

صفحه شروع خود را ایجاد کنید

شما صفحات نمایش داده شده توسط برنامه خود را با تعریف کلاس هایی ایجاد می کنید که کلاس Screen را گسترش می دهند و روش onGetTemplate آن را پیاده سازی می کند، که نمونه Template را نشان دهنده وضعیت رابط کاربری برای نمایش در صفحه ماشین برمی گرداند.

قطعه زیر نشان می دهد که چگونه می توان Screen اعلام کرد که از یک الگوی PaneTemplate برای نمایش یک "Hello world!" ساده استفاده می کند. رشته:

کاتلین

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

جاوا

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

کلاس CarContext

کلاس CarContext یک زیر کلاس ContextWrapper است که برای نمونه‌های Session و Screen شما قابل دسترسی است. این امکان دسترسی به خدمات خودرو، مانند ScreenManager برای مدیریت پشته صفحه را فراهم می کند. AppManager برای عملکردهای کلی مربوط به برنامه، مانند دسترسی به آبجکت Surface برای ترسیم نقشه ها . و NavigationManager که توسط برنامه های ناوبری گام به گام برای برقراری ارتباط فراداده ناوبری و سایر رویدادهای مربوط به ناوبری با میزبان استفاده می شود.

برای فهرست جامعی از عملکردهای کتابخانه در دسترس برنامه های ناوبری، به الگوهای پیمایش دسترسی پیدا کنید.

CarContext همچنین قابلیت‌های دیگری را ارائه می‌کند، مانند اینکه به شما اجازه می‌دهد منابع قابل ترسیم را با استفاده از پیکربندی از صفحه ماشین بارگیری کنید، برنامه‌ای را در ماشین با استفاده از intent راه‌اندازی کنید ، و نشان دهید که آیا برنامه شما باید نقشه‌اش را با تم تیره نمایش دهد یا خیر.

پیاده سازی ناوبری صفحه

برنامه‌ها اغلب صفحه‌های مختلفی را ارائه می‌دهند، که هر کدام احتمالاً از الگوهای مختلفی استفاده می‌کنند که کاربر می‌تواند در تعامل با رابط نمایش داده شده در صفحه، در میان آنها حرکت کند.

کلاس ScreenManager یک پشته صفحه نمایش را ارائه می دهد که می توانید از آن برای فشار دادن صفحه نمایش ها استفاده کنید که می تواند به طور خودکار هنگامی که کاربر دکمه برگشت را در صفحه ماشین انتخاب می کند یا از دکمه سخت افزاری برگشت موجود در برخی از اتومبیل ها استفاده می کند ظاهر شود.

قطعه زیر نشان می دهد که چگونه می توان یک اکشن برگشتی را به یک الگوی پیام اضافه کرد و همچنین عملکردی را نشان می دهد که هنگام انتخاب کاربر، صفحه جدیدی را فشار می دهد:

کاتلین

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

جاوا

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.invalidate درخواست کند محتوای یک Screen را باطل کند. میزبان متعاقباً با روش Screen.onGetTemplate برنامه شما تماس می گیرد تا الگو را با محتویات جدید بازیابی کند.

هنگام بازخوانی یک Screen ، مهم است که محتوای خاصی را در قالب درک کنید که می‌تواند به‌روزرسانی شود، بنابراین میزبان، الگوی جدید را در سهمیه الگو به حساب نمی‌آورد. برای جزئیات بیشتر به بخش محدودیت های الگو مراجعه کنید.

توصیه می‌کنیم صفحه‌های خود را طوری ساختار دهید که یک نگاشت یک به یک بین یک Screen و نوع قالبی که از طریق اجرای onGetTemplate آن برمی‌گرداند وجود داشته باشد.

ترسیم نقشه ها

برنامه‌های ناوبری، نقاط مورد علاقه (POI) و آب و هوا با استفاده از الگوهای زیر می‌توانند با دسترسی به یک Surface نقشه‌ها را ترسیم کنند.

برای استفاده از الگوهای زیر، برنامه شما باید یکی از مجوزهای مربوطه را در عنصر <uses-permission> در فایل AndroidManifest.xml خود داشته باشد.

الگو مجوز قالب راهنمایی دسته
NavigationTemplate androidx.car.app.NAVIGATION_TEMPLATES ناوبری
MapWithContentTemplate androidx.car.app.NAVIGATION_TEMPLATES یا
androidx.car.app.MAP_TEMPLATES
ناوبری ، POI ، آب و هوا
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() ارسال می‌شود.

کاتلین

carContext.getCarService(AppManager::class.java).setSurfaceCallback(surfaceCallback)

جاوا

carContext.getCarService(AppManager.class).setSurfaceCallback(surfaceCallback);

ناحیه قابل مشاهده سطح را درک کنید

میزبان می تواند عناصر رابط کاربری را برای الگوهای بالای نقشه بکشد. میزبان با فراخوانی متد SurfaceCallback.onVisibleAreaChanged ، ناحیه‌ای از سطح را که تضمین شده است بدون مانع و کاملاً برای کاربر قابل مشاهده است، ارتباط برقرار می‌کند. همچنین برای به حداقل رساندن تعداد تغییرات، میزبان متد SurfaceCallback.onStableAreaChanged را با کوچکترین مستطیل فراخوانی می کند که همیشه بر اساس الگوی فعلی قابل مشاهده است.

به عنوان مثال، هنگامی که یک برنامه ناوبری از NavigationTemplate با یک نوار اقدام در بالا استفاده می کند، زمانی که کاربر برای مدتی با صفحه تعامل نداشته باشد، نوار اقدام می تواند خود را پنهان کند تا فضای بیشتری برای نقشه ایجاد کند. در این حالت، یک تماس به onStableAreaChanged و onVisibleAreaChanged با همان مستطیل وجود دارد. وقتی نوار اقدام پنهان است، فقط onVisibleAreaChanged با ناحیه بزرگتر فراخوانی می شود. اگر کاربر با صفحه تعامل داشته باشد، دوباره فقط onVisibleAreaChanged با اولین مستطیل فراخوانی می شود.

پشتیبانی از تم تیره

برنامه‌ها باید نقشه خود را روی نمونه Surface با رنگ‌های تیره مناسب، زمانی که میزبان تعیین می‌کند، آن‌طور که در کیفیت برنامه Android برای خودروها توضیح داده شده است، دوباره ترسیم کنند.

برای تصمیم گیری در مورد ترسیم نقشه تاریک، می توانید از روش CarContext.isDarkMode استفاده کنید. هر زمان که وضعیت تم تیره تغییر کند، با Session.onCarConfigurationChanged تماسی دریافت می‌کنید.

به کاربران اجازه دهید با نقشه شما تعامل داشته باشند

هنگام استفاده از الگوهای زیر، می‌توانید برای تعامل کاربران با نقشه‌هایی که ترسیم می‌کنید، پشتیبانی اضافه کنید، مانند اینکه به آن‌ها اجازه دهید قسمت‌های مختلف نقشه را با بزرگ‌نمایی و جابجایی ببینند.

الگو تعامل از سطح API برنامه Car پشتیبانی می شود
NavigationTemplate 2
PlaceListNavigationTemplate ( منسوخ شده ) 4
RoutePreviewNavigationTemplate ( منسوخ شده ) 4
MapTemplate ( منسوخ شده ) 5 (معرفی قالب)
MapWithContentTemplate 7 (معرفی قالب)

اجرای تماس های تعاملی

رابط SurfaceCallback چندین روش پاسخ به تماس دارد که می‌توانید برای افزودن تعامل به نقشه‌های ساخته شده با الگوهای بخش قبل پیاده‌سازی کنید:

تعامل روش SurfaceCallback از سطح API برنامه Car پشتیبانی می شود
ضربه بزنید onClick 5
برای بزرگنمایی، خرج کردن onScale 2
کشیدن تک لمسی onScroll 2
پرتاب تک لمسی onFling 2
دو ضربه سریع بزنید onScale (با ضریب مقیاس تعیین شده توسط میزبان الگو) 2
حرکت چرخشی در حالت پان onScroll (با فاکتور فاصله تعیین شده توسط میزبان الگو) 2

یک نوار اقدام نقشه اضافه کنید

این الگوها می‌توانند یک نوار عملکرد نقشه برای اقدامات مرتبط با نقشه مانند بزرگنمایی و کوچک‌نمایی، نمایش مجدد قطب‌نما و سایر اقداماتی که برای نمایش انتخاب می‌کنید داشته باشند. نوار اکشن نقشه می‌تواند تا چهار دکمه فقط دارای نماد داشته باشد که می‌توانند بدون تأثیر بر عمق کار، به‌روزرسانی شوند. در حالت بیکار پنهان می شود و در حالت فعال دوباره ظاهر می شود.

برای دریافت تماس‌های تعاملی نقشه، باید یک دکمه Action.PAN را در نوار اقدام نقشه اضافه کنید. هنگامی که کاربر دکمه پان را فشار می دهد، میزبان وارد حالت پان می شود، همانطور که در قسمت زیر توضیح داده شده است.

اگر برنامه شما دکمه Action.PAN را در نوار اقدام نقشه حذف کند، ورودی کاربر را از روش‌های SurfaceCallback دریافت نمی‌کند و میزبان از حالت پان که قبلاً فعال شده است خارج می‌شود.

در صفحه لمسی، دکمه پان نمایش داده نمی شود.

حالت پان را درک کنید

در حالت پان، میزبان الگو ورودی های کاربر را از دستگاه های ورودی غیر لمسی، مانند کنترلرهای چرخشی و پد لمسی، به روش های SurfaceCallback مناسب ترجمه می کند. به اقدام کاربر برای ورود یا خروج از حالت پان با متد setPanModeListener در NavigationTemplate.Builder پاسخ دهید. وقتی کاربر در حالت پان است، میزبان می‌تواند سایر اجزای رابط کاربری را در قالب پنهان کند.

با کاربر تعامل داشته باشید

برنامه شما می تواند با استفاده از الگوهای مشابه یک برنامه تلفن همراه با کاربر تعامل داشته باشد.

کنترل ورودی کاربر

برنامه شما می‌تواند با ارسال شنوندگان مناسب به مدل‌هایی که از آنها پشتیبانی می‌کنند، به ورودی‌های کاربر پاسخ دهد. قطعه زیر نحوه ایجاد یک Action Model را نشان می دهد که یک OnClickListener را تنظیم می کند که متدی را که توسط کد برنامه شما تعریف شده است، فراخوانی می کند:

کاتلین

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

جاوا

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

سپس روش onClickNavigate می‌تواند برنامه پیش‌فرض ماشین ناوبری را با استفاده از روش CarContext.startCarApp راه‌اندازی کند:

کاتلین

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

جاوا

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

برای جزئیات بیشتر درباره نحوه راه‌اندازی برنامه‌ها، از جمله قالب هدف ACTION_NAVIGATE ، به بخش Start a car with a intent مراجعه کنید.

برخی از اقدامات، مانند اقداماتی که نیاز به هدایت کاربر برای ادامه تعامل در دستگاه های تلفن همراه خود دارند، تنها زمانی مجاز هستند که خودرو پارک شده باشد. برای اجرای این اقدامات می توانید از ParkedOnlyOnClickListener استفاده کنید. اگر ماشین پارک نشده باشد، میزبان نشانه ای را به کاربر نشان می دهد که در این مورد عمل مجاز نیست. اگر ماشین پارک شده باشد، کد به طور معمول اجرا می شود. قطعه زیر نحوه استفاده از ParkedOnlyOnClickListener را برای باز کردن صفحه تنظیمات در دستگاه تلفن همراه نشان می دهد:

کاتلین

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

جاوا

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

نمایش اعلان ها

اعلان‌های ارسال شده به دستگاه تلفن همراه تنها در صورتی روی صفحه ماشین نشان داده می‌شوند که با CarAppExtender گسترش داده شوند. برخی از ویژگی‌های اعلان، مانند عنوان محتوا، متن، نماد و کنش‌ها را می‌توان در CarAppExtender تنظیم کرد و ویژگی‌های اعلان را هنگامی که روی صفحه ماشین ظاهر می‌شوند لغو کرد.

قطعه زیر نحوه ارسال اعلان به صفحه ماشین را نشان می دهد که عنوانی متفاوت از عنوان نشان داده شده در دستگاه تلفن همراه را نشان می دهد:

کاتلین

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

جاوا

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

اعلان ها می توانند بخش های زیر از رابط کاربری را تحت تاثیر قرار دهند:

  • ممکن است یک اعلان heads-up (HUN) به کاربر نمایش داده شود.
  • ممکن است یک ورودی در مرکز اعلان اضافه شود، که به صورت اختیاری یک نشان قابل مشاهده در راه آهن است.
  • برای برنامه های ناوبری، اعلان ممکن است در ویجت راه آهن همانطور که در اعلان های مرحله به مرحله توضیح داده شده است، نمایش داده شود.

همانطور که در مستندات CarAppExtender توضیح داده شده است، می توانید نحوه پیکربندی اعلان های برنامه خود را برای تأثیرگذاری بر هر یک از آن عناصر رابط کاربری با استفاده از اولویت اعلان انتخاب کنید.

اگر NotificationCompat.Builder.setOnlyAlertOnce با مقدار true فراخوانی شود، یک اعلان با اولویت بالا تنها یک بار به صورت HUN نمایش داده می شود.

برای اطلاعات بیشتر در مورد نحوه طراحی اعلان‌های برنامه خودرو، به راهنمای Google Design for Driving درباره اعلان‌ها مراجعه کنید.

نان تست را نشان دهید

همانطور که در این قطعه نشان داده شده است، برنامه شما می تواند با استفاده از CarToast یک نان تست را نمایش دهد:

کاتلین

CarToast.makeText(carContext, "Hello!", CarToast.LENGTH_SHORT).show()

جاوا

CarToast.makeText(getCarContext(), "Hello!", CarToast.LENGTH_SHORT).show();

درخواست مجوزها

اگر برنامه شما نیاز به دسترسی به داده‌ها یا اقدامات محدود شده دارد (مثلاً مکان)، قوانین استاندارد مجوزهای Android اعمال می‌شود. برای درخواست مجوز، می توانید از متد CarContext.requestPermissions() استفاده کنید.

مزیت استفاده از CarContext.requestPermissions() ، بر خلاف استفاده از APIهای استاندارد Android ، این است که برای ایجاد گفتگوی مجوزها نیازی به راه اندازی Activity خود ندارید. علاوه بر این، می‌توانید از کد یکسانی در Android Auto و Android Automotive OS استفاده کنید، نه اینکه نیاز به ایجاد جریان‌های وابسته به پلتفرم داشته باشید.

به گفتگوی مجوزها در 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 است که یک هدف صریح را به عملکرد برنامه شما می بندد:

کاتلین

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

جاوا

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 داده فراخوانی می کند:

کاتلین

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)))
        }
    }
}

جاوا

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 در برنامه شما با فشار دادن صفحه رزرو پارکینگ در پشته، این هدف را کنترل می‌کند، در صورتی که قبلاً در بالای صفحه نیست:

کاتلین

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))
        }
    }
}

جاوا

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

برای اطلاعات بیشتر در مورد نحوه رسیدگی به اعلان‌ها برای برنامه ماشین، بخش نمایش اعلان‌ها را ببینید.

محدودیت های قالب

میزبان تعداد الگوهایی را برای نمایش برای یک کار معین به حداکثر 5 عدد محدود می کند که آخرین الگو باید یکی از انواع زیر باشد:

توجه داشته باشید که این محدودیت برای تعداد الگوها اعمال می شود و نه تعداد نمونه های Screen در پشته. به عنوان مثال، اگر یک برنامه در حالی که در صفحه A است، دو الگو ارسال کند و سپس صفحه B را فشار دهد، اکنون می تواند سه الگوی دیگر ارسال کند. متناوبا، اگر ساختار هر صفحه برای ارسال یک الگو باشد، برنامه می‌تواند پنج نمونه صفحه را در پشته ScreenManager قرار دهد.

موارد خاصی برای این محدودیت ها وجود دارد: بازخوانی قالب و عملیات بازگشت و بازنشانی.

الگو به روز می شود

برخی از به‌روزرسانی‌های محتوایی در محدودیت قالب حساب نمی‌شوند. به طور کلی، اگر یک برنامه یک الگوی جدید را که از همان نوع است و حاوی محتوای اصلی قالب قبلی است را فشار دهد، الگوی جدید در سهمیه حساب نمی‌شود. به عنوان مثال، به‌روزرسانی وضعیت جابجایی یک ردیف در یک ListTemplate در سهمیه حساب نمی‌شود. برای آشنایی بیشتر با انواع به‌روزرسانی‌های محتوا، به مستندات تک تک قالب‌ها مراجعه کنید.

عملیات پشت

برای فعال کردن جریان‌های فرعی در یک کار، میزبان تشخیص می‌دهد که یک برنامه در حال نمایش یک Screen از پشته ScreenManager است و سهمیه باقی‌مانده را بر اساس تعداد الگوهایی که برنامه به عقب برمی‌گردد، به‌روزرسانی می‌کند.

برای مثال، اگر برنامه در حالی که در صفحه A است، دو الگو ارسال کند، سپس صفحه B را فشار دهد و دو الگوی دیگر ارسال کند، برنامه یک سهمیه باقی مانده است. اگر برنامه به صفحه A بازگردد، میزبان سهمیه را به سه بازنشانی می‌کند، زیرا برنامه با دو الگو به عقب رفته است.

توجه داشته باشید که زمانی که یک برنامه به صفحه نمایش باز می گردد، باید یک الگو را ارسال کند که از همان نوع قالبی باشد که آخرین بار توسط آن صفحه ارسال شده است. ارسال هر نوع قالب دیگری باعث ایجاد خطا می شود. با این حال، تا زمانی که نوع آن در طول عملیات برگشت یکسان باقی بماند، یک برنامه می‌تواند آزادانه محتویات الگو را بدون تأثیر بر سهمیه تغییر دهد.

بازنشانی عملیات

برخی از الگوها دارای معنای خاصی هستند که به معنای پایان یک کار است. به عنوان مثال، NavigationTemplate یک نمای است که انتظار می رود روی صفحه باقی بماند و با دستورالعمل های گام به گام جدید برای مصرف کاربر به روز شود. هنگامی که به یکی از این الگوها می رسد، میزبان سهمیه الگو را بازنشانی می کند و با آن الگو به گونه ای رفتار می کند که گویی اولین مرحله از یک کار جدید است. این به برنامه اجازه می دهد تا یک کار جدید را شروع کند. به مستندات تک تک الگوها مراجعه کنید تا ببینید کدام یک بازنشانی را در میزبان ایجاد می کنند.

اگر میزبان قصد شروع برنامه را از طریق یک اقدام اعلان یا از راه‌انداز دریافت کند، سهمیه نیز بازنشانی می‌شود. این مکانیسم به برنامه اجازه می‌دهد تا یک کار جدید را از اعلان‌ها شروع کند، و حتی اگر برنامه‌ای از قبل محدود شده باشد و در پیش‌زمینه باشد، صادق است.

برای جزئیات بیشتر در مورد نحوه نمایش اعلان‌های برنامه خود در صفحه ماشین، به بخش نمایش اعلان‌ها مراجعه کنید. برای اطلاعات در مورد نحوه راه‌اندازی برنامه از طریق یک اقدام اعلان، به بخش Start a car with a intent مراجعه کنید.

اتصال API

با استفاده از CarConnection API برای بازیابی اطلاعات اتصال در زمان اجرا، می توانید تعیین کنید که آیا برنامه شما روی Android Auto یا Android Automotive OS اجرا می شود.

به عنوان مثال، در Session برنامه خودروی خود، CarConnection راه‌اندازی کنید و در به‌روزرسانی‌های LiveData مشترک شوید:

کاتلین

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

جاوا

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

در مشاهده گر، می توانید به تغییرات در وضعیت اتصال واکنش نشان دهید:

کاتلین

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

جاوا

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

محدودیت‌های API

ماشین‌های مختلف ممکن است اجازه دهند تعداد متفاوتی از نمونه‌های Item در یک زمان به کاربر نمایش داده شود. از ConstraintManager برای بررسی محدودیت محتوا در زمان اجرا استفاده کنید و تعداد مناسبی از موارد را در قالب‌های خود تنظیم کنید.

با گرفتن ConstraintManager از CarContext شروع کنید:

کاتلین

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

جاوا

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

سپس می توانید شی ConstraintManager بازیابی شده را برای محدودیت محتوای مربوطه پرس و جو کنید. به عنوان مثال، برای به دست آوردن تعداد مواردی که می توان در یک شبکه نمایش داد، getContentLimit با CONTENT_LIMIT_TYPE_GRID تماس بگیرید:

کاتلین

val gridItemLimit = manager.getContentLimit(ConstraintManager.CONTENT_LIMIT_TYPE_GRID)

جاوا

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

یک جریان ورود به سیستم اضافه کنید

اگر برنامه شما تجربه ورود به سیستم را برای کاربران ارائه می‌دهد، می‌توانید از الگوهایی مانند SignInTemplate و LongMessageTemplate با Car App API سطح 2 و بالاتر برای ورود به برنامه خود در قسمت اصلی خودرو استفاده کنید.

برای ایجاد یک SignInTemplate ، یک SignInMethod تعریف کنید. کتابخانه برنامه خودرو در حال حاضر از روش‌های ورود به سیستم زیر پشتیبانی می‌کند:

  • InputSignInMethod برای ورود نام کاربری/رمز عبور.
  • PinSignInMethod برای ورود به سیستم پین، که در آن کاربر با استفاده از یک پین نمایش داده شده روی واحد اصلی، حساب خود را از تلفن خود پیوند می دهد.
  • ProviderSignInMethod برای ورود به سیستم ارائه دهنده، مانند Google Sign-In و One Tap .
  • QRCodeSignInMethod برای ورود به سیستم کد QR، جایی که کاربر یک کد QR را اسکن می کند تا ورود به سیستم را در تلفن خود کامل کند. این با Car API سطح 4 و بالاتر در دسترس است.

به عنوان مثال، برای پیاده سازی قالبی که رمز عبور کاربر را جمع آوری می کند، با ایجاد یک InputCallback برای پردازش و اعتبارسنجی ورودی کاربر شروع کنید:

کاتلین

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.
    }
}

جاوا

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 مورد نیاز است.

کاتلین

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

جاوا

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

در نهایت، از InputSignInMethod جدید خود برای ایجاد یک SignInTemplate استفاده کنید.

کاتلین

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

جاوا

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

از AccountManager استفاده کنید

برنامه‌های سیستم‌عامل Android Automotive که دارای احراز هویت هستند، به دلایل زیر باید از AccountManager استفاده کنند:

  • تجربه کاربری بهتر و سهولت مدیریت حساب : کاربران می توانند به راحتی تمام حساب های خود را از منوی حساب ها در تنظیمات سیستم مدیریت کنند، از جمله ورود به سیستم و خروج از سیستم.
  • تجربه‌های «مهمان» : از آنجایی که خودروها دستگاه‌های مشترک هستند، OEM‌ها می‌توانند تجربه‌های مهمان را در خودرو فعال کنند، جایی که نمی‌توان حساب‌ها را اضافه کرد.

انواع رشته متن را اضافه کنید

اندازه های مختلف صفحه نمایش ماشین ممکن است مقادیر متفاوتی از متن را نشان دهد. با Car App API سطح 2 و بالاتر، می‌توانید انواع مختلفی از یک رشته متنی را مشخص کنید تا به بهترین نحو با صفحه نمایش مطابقت داشته باشد. برای دیدن مکان‌هایی که انواع متن پذیرفته می‌شوند، به دنبال الگوها و مؤلفه‌هایی باشید که CarText می‌گیرند.

می توانید انواع رشته متنی را با متد CarText.Builder.addVariant() به CarText اضافه کنید:

کاتلین

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

جاوا

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

سپس می توانید از این CarText - برای مثال، به عنوان متن اصلی GridItem استفاده کنید.

کاتلین

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

جاوا

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

رشته ها را به ترتیب از بیشترین به کمترین ترجیح اضافه کنید - برای مثال، از طولانی ترین به کوتاه ترین. میزبان رشته با طول مناسب را بسته به میزان فضای موجود روی صفحه ماشین انتخاب می کند.

CarIcon های درون خطی را برای ردیف ها اضافه کنید

با استفاده از CarIconSpan می‌توانید نمادهایی را به صورت متنی اضافه کنید تا جذابیت بصری برنامه خود را تقویت کنید. برای اطلاعات بیشتر در مورد ایجاد این دهانه ها به مستندات CarIconSpan.create مراجعه کنید. برای یک نمای کلی از نحوه عملکرد استایل نوشتار با دهانه، استایل‌سازی متن Spantastic با Spans را ببینید.

کاتلین

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

جاوا

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

API های سخت افزار خودرو

با شروع API سطح 3 Car App، کتابخانه برنامه Car دارای APIهایی است که می توانید از آنها برای دسترسی به ویژگی ها و حسگرهای خودرو استفاده کنید.

الزامات

برای استفاده از 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 OS استفاده کنید، نه اینکه مجبور باشید جریان‌های وابسته به پلتفرم ایجاد کنید. با این حال، مجوزهای مورد نیاز متفاوت است.

CarInfo

این جدول ویژگی های ظاهر شده توسط API های CarInfo و مجوزهایی را که برای استفاده از آنها باید درخواست کنید، توضیح می دهد:

روش ها خواص مجوزهای Android Auto مجوزهای سیستم عامل Android Automotive از سطح API برنامه Car پشتیبانی می شود
fetchModel ساخت، مدل، سال android.car.permission.CAR_INFO 3
fetchEnergyProfile انواع کانکتور EV، انواع سوخت com.google.android.gms.permission.CAR_FUEL android.car.permission.CAR_INFO 3
fetchExteriorDimensions

این داده‌ها فقط در برخی از خودروهای دارای سیستم عامل Android Automotive دارای API 30 یا بالاتر موجود است

ابعاد بیرونی N/A 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

اخطار: متد getOdometerMeters از کلاس Mileage به اشتباه نامگذاری شده است و کیلومترها را برمی گرداند نه مترها.

فاصله کیلومتر شمار com.google.android.gms.permission.CAR_MILEAGE این داده‌ها در سیستم عامل Android Automotive برای برنامه‌های نصب‌شده از فروشگاه Play در دسترس نیست. 3

به عنوان مثال، برای بدست آوردن محدوده باقیمانده، یک شی CarInfo نمونه سازی کنید، سپس یک OnCarDataAvailableListener ایجاد و ثبت کنید:

کاتلین

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)

جاوا

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 به ​​شما امکان دسترسی به شتاب سنج، ژیروسکوپ، قطب نما و داده های موقعیت مکانی خودرو را می دهد. در دسترس بودن این مقادیر ممکن است به OEM بستگی داشته باشد. فرمت داده های حاصل از شتاب سنج ، ژیروسکوپ و قطب نما همان است که از API SensorManager دریافت می کنید. به عنوان مثال ، برای بررسی عنوان وسیله نقلیه:

کاتلین

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)

جاوا

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 Automobile ، به بخش Emulate Hardware State از راهنمای Emulator OS Android Automobile Os مراجعه کنید.

چرخه های حیات Carappservice ، Session و Screen

کلاس های Session و Screen رابط LifecycleOwner پیاده سازی می کنند. از آنجا که کاربر با برنامه در تعامل است ، همانطور که در نمودارهای زیر توضیح داده شده است ، از تماس های چرخه عمر Session و Screen اشیاء استفاده می شود.

چرخه های حیات یک سرویس دهنده و یک جلسه

شکل 1 . چرخه حیات Session .

برای جزئیات کامل ، به مستندات روش Session.getLifecycle مراجعه کنید.

چرخه عمر یک صفحه نمایش

شکل 2 . چرخه عمر Screen .

برای جزئیات کامل ، به مستندات روش Screen.getLifecycle مراجعه کنید.

ضبط از میکروفون ماشین

با استفاده از CarAppService برنامه و API CarAudioRecord ، می توانید برنامه خود را به میکروفون ماشین کاربر دسترسی دهید. کاربران برای دسترسی به میکروفون خودرو باید به برنامه شما اجازه دهند. برنامه شما می تواند ورودی کاربر را در برنامه شما ضبط و پردازش کند.

اجازه ضبط

قبل از ضبط هرگونه صدا ، ابتدا باید اجازه ضبط در AndroidManifest.xml خود را اعلام کنید و درخواست کنید که کاربر آن را اعطا کند.

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

شما باید در زمان اجرا مجوز ضبط را درخواست کنید. برای جزئیات بیشتر در مورد نحوه درخواست مجوز در برنامه ماشین خود ، به بخش مجوزهای درخواست مراجعه کنید.

ضبط صدا

بعد از اینکه کاربر اجازه ضبط را می دهد ، می توانید صدا را ضبط کرده و ضبط را پردازش کنید.

کاتلین

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

جاوا

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

تمرکز صوتی

هنگام ضبط از میکروفون خودرو ، ابتدا فوکوس صوتی را به دست می آورید تا اطمینان حاصل شود که هر رسانه در حال انجام است. اگر تمرکز صوتی را از دست دادید ، ضبط را متوقف کنید.

در اینجا نمونه ای از نحوه دستیابی به تمرکز صوتی آورده شده است:

کاتلین

 
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

جاوا

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 صحیح ایجاد و بازگردانده شده است.

برای نمونه های استفاده به نمونه ها مراجعه کنید.

گزارش یک اندرویدی برای شماره کتابخانه برنامه خودروها

اگر مشکلی با کتابخانه پیدا کردید ، آن را با استفاده از ردیاب شماره Google گزارش دهید. حتماً تمام اطلاعات درخواستی را در الگوی شماره پر کنید.

یک مسئله جدید ایجاد کنید

قبل از تشکیل مسئله جدید ، لطفاً بررسی کنید که آیا در یادداشت های انتشار کتابخانه ذکر شده است یا در لیست شماره ها گزارش شده است. با کلیک بر روی ستاره برای مسئله در ردیاب می توانید مشترک شوید و به موضوعات رای دهید. برای اطلاعات بیشتر ، به عضویت در یک موضوع مراجعه کنید.

،

کتابخانه برنامه Android for Cars به ​​شما امکان می دهد ناوبری ، نقطه مورد علاقه (POI) ، اینترنت اشیاء (IoT) یا برنامه هواشناسی را به ماشین بیاورید. این کار را با ارائه مجموعه ای از الگوهای طراحی شده برای رعایت استانداردهای حواس پرتی راننده و مراقبت از جزئیاتی مانند انواع فاکتورهای صفحه نمایش خودرو و روشهای ورودی انجام می دهد.

این راهنما مروری بر ویژگی ها و مفاهیم کلیدی کتابخانه ارائه می دهد و شما را از طریق فرآیند تنظیم یک برنامه اساسی طی می کند.

قبل از شروع

  1. طرح صفحات رانندگی پوشش کتابخانه برنامه خودرو را مرور کنید
  2. اصطلاحات و مفاهیم کلیدی را در بخش زیر مرور کنید.
  3. خود را با طراحی سیستم عامل Android Auto UI و Android Automobile Os آشنا کنید.
  4. یادداشت های نسخه را مرور کنید.
  5. نمونه ها را مرور کنید.

اصطلاحات و مفاهیم کلیدی

مدل ها و الگوها
رابط کاربری توسط نمودار از اشیاء مدل که می توانند به روش های مختلف با هم چیده شوند ، نشان داده شده است ، همانطور که توسط الگوی متعلق به آنها مجاز است. قالب ها زیر مجموعه ای از مدل ها هستند که می توانند به عنوان ریشه در آن نمودارها عمل کنند. مدل ها شامل اطلاعاتی هستند که به شکل متن و تصاویر به کاربر نمایش داده می شوند و همچنین ویژگی هایی برای پیکربندی جنبه های ظاهر بصری چنین اطلاعاتی - برای مثال ، رنگ های متن یا اندازه تصویر. میزبان مدل ها را به نمایی که برای رعایت استانداردهای حواس پرتی راننده طراحی شده اند تبدیل می کند و از جزئیات مانند انواع فاکتورهای صفحه نمایش خودرو و روشهای ورودی مراقبت می کند.
میزبان
میزبان مؤلفه باطن است که عملکردی ارائه شده توسط API های کتابخانه را پیاده سازی می کند تا برنامه شما بتواند در ماشین اجرا شود. مسئولیت های میزبان از کشف برنامه شما و مدیریت چرخه عمر آن تا تبدیل مدل های خود به نمایش و اطلاع برنامه تعامل کاربر است. در دستگاه های تلفن همراه ، این میزبان توسط Android Auto اجرا می شود. در سیستم عامل Android Automobile ، این میزبان به عنوان یک برنامه سیستم نصب شده است.
محدودیت های الگو
الگوهای مختلف محدودیت هایی را در محتوای مدل های خود اعمال می کنند. به عنوان مثال ، الگوهای لیست محدودیت هایی در تعداد مواردی دارند که می توانند به کاربر ارائه شوند. الگوهای همچنین محدودیت هایی در نحوه اتصال آنها برای تشکیل جریان یک کار دارند. به عنوان مثال ، برنامه فقط می تواند حداکثر پنج قالب را به پشته صفحه منتقل کند. برای اطلاعات بیشتر به محدودیت های الگو مراجعه کنید.
Screen
Screen یک کلاس ارائه شده توسط کتابخانه است که برنامه ها برای مدیریت رابط کاربری ارائه شده به کاربر پیاده سازی می کنند. یک Screen دارای چرخه عمر است و مکانیسم برنامه را برای ارسال الگوی برای نمایش در هنگام نمایش صفحه فراهم می کند. نمونه های Screen همچنین می توانند به یک پشته صفحه نمایش و از یک پشته Screen داده شوند و این امر را تضمین می کند که آنها به محدودیت های جریان الگوی پایبند هستند.
CarAppService
CarAppService یک کلاس Service انتزاعی است که برنامه شما باید توسط میزبان کشف و صادرات کند. CarAppService برنامه شما وظیفه تأیید این را دارد که به یک اتصال میزبان با استفاده از createHostValidator و متعاقباً ارائه نمونه های Session برای هر اتصال با استفاده از onCreateSession می توان اعتماد کرد.
Session

Session یک کلاس انتزاعی است که برنامه شما باید با استفاده از CarAppService.onCreateSession اجرا و بازگردد. این به عنوان نقطه ورود برای نمایش اطلاعات در صفحه ماشین است. این یک چرخه حیات دارد که وضعیت فعلی برنامه شما را در صفحه ماشین آگاه می کند ، مانند زمانی که برنامه شما قابل مشاهده یا پنهان است.

هنگامی که یک Session آغاز شد ، مانند زمان راه اندازی برنامه ، میزبان درخواست می کند تا Screen اولیه با استفاده از روش onCreateScreen نمایش داده شود.

کتابخانه برنامه ماشین را نصب کنید

برای راهنمایی در مورد نحوه اضافه کردن کتابخانه به برنامه خود ، به صفحه انتشار کتابخانه JetPack مراجعه کنید.

پرونده های مانیفست برنامه خود را پیکربندی کنید

قبل از اینکه بتوانید برنامه اتومبیل خود را ایجاد کنید ، پرونده های مانیفست برنامه خود را به شرح زیر پیکربندی کنید.

سرویس کار خود را اعلام کنید

میزبان از طریق اجرای 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 را برای خودروها مشاهده کنید.

نام و نماد برنامه را مشخص کنید

شما باید یک نام برنامه و نماد را که میزبان می تواند از آن استفاده کند تا برنامه شما را در سیستم UI سیستم استفاده کند ، مشخص کنید.

می توانید نام برنامه و نماد را که برای نمایش برنامه خود با استفاده از ویژگی های 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>

سطح API برنامه ماشین

کتابخانه برنامه Car سطح API خود را تعریف می کند تا بتوانید بدانید که کدام ویژگی های کتابخانه توسط میزبان الگوی روی یک وسیله نقلیه پشتیبانی می شود. برای بازیابی بالاترین سطح API برنامه اتومبیل پشتیبانی شده توسط یک میزبان ، از روش getCarAppApiLevel() استفاده کنید.

حداقل سطح API برنامه اتومبیل را که توسط برنامه خود در پرونده AndroidManifest.xml پشتیبانی می شود ، اعلام کنید:

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

برای جزئیات بیشتر در مورد نحوه حفظ سازگاری به عقب و اعلام حداقل سطح API مورد نیاز برای استفاده از یک ویژگی ، به اسناد مربوط به حاشیه نویسی RequiresCarApi مراجعه کنید. برای تعریفی که سطح API برای استفاده از ویژگی خاصی از کتابخانه برنامه خودرو مورد نیاز است ، مستندات مرجع را برای CarAppApiLevels بررسی کنید.

سرویس و جلسه خود را ایجاد کنید

برنامه شما باید کلاس CarAppService را گسترش داده و روش onCreateSession خود را اجرا کند ، که یک نمونه Session مربوط به اتصال فعلی به میزبان را برمی گرداند:

کاتلین

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

جاوا

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

نمونه Session مسئول بازگشت نمونه Screen برای استفاده از اولین بار است که برنامه شروع می شود:

کاتلین

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

جاوا

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

برای رسیدگی به سناریوهایی که برنامه ماشین شما باید از صفحه نمایش شروع کند که صفحه اصلی برنامه شما نیست ، مانند دستیابی به پیوندهای عمیق ، می توانید قبل از بازگشت از onCreateScreen ، یک صفحه پشتی از صفحه نمایش را با استفاده از ScreenManager.push از قبل بفرستید. قبل از بذر به کاربران امکان می دهد از اولین صفحه ای که برنامه شما در حال نمایش است به صفحه های قبلی برگردند.

صفحه شروع خود را ایجاد کنید

شما صفحه نمایش های نمایش داده شده توسط برنامه خود را با تعریف کلاس هایی که کلاس Screen را گسترش می دهند و روش onGetTemplate آن را اجرا می کنند ، ایجاد می کنید ، که نمونه Template را نشان می دهد که وضعیت UI را برای نمایش در صفحه نمایش خودرو نشان می دهد.

قطعه زیر نشان می دهد که چگونه می توان Screen اعلام کرد که از یک الگوی PaneTemplate استفاده می کند تا یک "سلام جهان!" رشته:

کاتلین

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

جاوا

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 یک پشته صفحه نمایش را فراهم می کند که می توانید برای فشار دادن صفحه نمایش هایی که می توانند به طور خودکار ظاهر شوند ، هنگام انتخاب کاربر یک دکمه پشتی را در صفحه ماشین یا از دکمه پشت سخت افزار موجود در برخی از اتومبیل ها استفاده کنید.

قطعه زیر نشان می دهد که چگونه می توان یک عمل پشتی را به یک الگوی پیام و همچنین عملی اضافه کرد که هنگام انتخاب توسط کاربر ، یک صفحه جدید را تحت فشار قرار می دهد:

کاتلین

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

جاوا

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 Object یک Action استاندارد است که به طور خودکار ScreenManager.pop فراخوانی می کند. این رفتار می تواند با استفاده از نمونه OnBackPressedDispatcher موجود از CarContext نادیده گرفته شود.

برای کمک به اطمینان از استفاده از برنامه در هنگام رانندگی ، پشته صفحه می تواند حداکثر عمق پنج صفحه را داشته باشد. برای اطلاعات بیشتر به بخش محدودیت های الگو مراجعه کنید.

محتوای یک الگوی را تازه کنید

برنامه شما می تواند با فراخوانی روش Screen.invalidate ، محتوای یک Screen را باطل کند. میزبان متعاقباً برای بازیابی الگوی با محتوای جدید ، به روش Screen.onGetTemplate شما باز می گردد.

هنگام طراوت یک Screen ، درک محتوای خاص موجود در الگوی که می تواند به روز شود ، مهم است تا میزبان الگوی جدید را در برابر سهمیه الگو بشمارد. برای اطلاعات بیشتر به بخش محدودیت های الگو مراجعه کنید.

ما توصیه می کنیم که صفحه های خود را ساختار دهید ، بنابراین یک نقشه برداری یک به یک بین Screen و نوع الگویی که از طریق اجرای onGetTemplate خود باز می گردد وجود دارد.

نقشه ها

ناوبری ، نقطه مورد علاقه (POI) و برنامه های آب و هوایی با استفاده از الگوهای زیر می توانند با دسترسی به یک Surface نقشه ها را ترسیم کنند.

برای استفاده از الگوهای زیر ، برنامه شما باید یکی از مجوزهای مربوطه اعلام شده در یک عنصر <uses-permission> در پرونده AndroidManifest.xml خود را داشته باشد.

الگو مجوز الگوی راهنمایی
NavigationTemplate androidx.car.app.NAVIGATION_TEMPLATES ناوبری
MapWithContentTemplate androidx.car.app.NAVIGATION_TEMPLATES یا
androidx.car.app.MAP_TEMPLATES
ناوبری ، POI ، آب و هوا
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 فعلی در پارامتر SurfaceContainer از تماسهای برگشتی onSurfaceAvailable() و onSurfaceDestroyed() به SurfaceCallback شما منتقل می شود.

کاتلین

carContext.getCarService(AppManager::class.java).setSurfaceCallback(surfaceCallback)

جاوا

carContext.getCarService(AppManager.class).setSurfaceCallback(surfaceCallback);

منطقه قابل مشاهده سطح را درک کنید

میزبان می تواند عناصر رابط کاربری را برای الگوهای موجود در بالای نقشه ترسیم کند. میزبان با فراخوانی با استفاده از روش SurfaceCallback.onVisibleAreaChanged ، مساحت سطح را که تضمین می شود بدون مانع و کاملاً قابل مشاهده برای کاربر ارتباط برقرار می کند ، ارتباط برقرار می کند. همچنین ، برای به حداقل رساندن تعداد تغییرات ، میزبان روش SurfaceCallback.onStableAreaChanged را با کوچکترین مستطیل فراخوانی می کند ، که همیشه بر اساس الگوی فعلی قابل مشاهده است.

به عنوان مثال ، هنگامی که یک برنامه ناوبری از NavigationTemplate با یک نوار اکشن در بالا استفاده می کند ، نوار عمل می تواند خود را پنهان کند وقتی کاربر مدتی با صفحه تعامل نداشته باشد تا فضای بیشتری برای نقشه ایجاد کند. در این حالت ، یک تماس تلفنی برای onStableAreaChanged و onVisibleAreaChanged با همان مستطیل وجود دارد. هنگامی که نوار اکشن پنهان است ، فقط onVisibleAreaChanged با منطقه بزرگتر فراخوانی می شود. اگر کاربر با صفحه تعامل داشته باشد ، دوباره فقط onVisibleAreaChanged با اولین مستطیل فراخوانی می شود.

حمایت از موضوع تاریک

برنامه ها باید نقشه خود را بر روی نمونه Surface با رنگ های تیره مناسب مجدداً تنظیم کنند ، هنگامی که میزبان شرایط را تعیین می کند ، آن را ضمانت می کند ، همانطور که در کیفیت برنامه Android برای اتومبیل ها توضیح داده شده است.

برای تصمیم گیری در مورد ترسیم نقشه تاریک ، می توانید از روش CarContext.isDarkMode استفاده کنید. هر وقت وضعیت موضوع تاریک تغییر می کند ، شما یک تماس به Session.onCarConfigurationChanged را دریافت می کنید.

به کاربران اجازه دهید با نقشه شما ارتباط برقرار کنند

هنگام استفاده از الگوهای زیر ، می توانید پشتیبانی را برای کاربران اضافه کنید تا با نقشه هایی که شما ترسیم می کنید در تعامل باشند ، مانند این که به آنها اجازه دهید قسمت های مختلف نقشه را با بزرگنمایی و پوشیدن مشاهده کنند.

الگو تعامل از سطح API برنامه اتومبیل پشتیبانی می شود
NavigationTemplate 2
PlaceListNavigationTemplate ( مستهلک ) 4
RoutePreviewNavigationTemplate ( مستهلک ) 4
MapTemplate ( مستهلک ) 5 (معرفی الگو)
MapWithContentTemplate 7 (معرفی الگو)

پاسخ های تعاملی را اجرا کنید

رابط SurfaceCallback دارای چندین روش پاسخ به تماس است که می توانید برای اضافه کردن تعامل به نقشه های ساخته شده با الگوهای موجود در بخش قبلی ، استفاده کنید:

تعامل روش SurfaceCallback از سطح API برنامه اتومبیل پشتیبانی می شود
ضربه بزنید onClick 5
برای بزرگنمایی، خرج کردن onScale 2
کشیدن تک لمسی onScroll 2
یک لمس تک لمسی onFling 2
دو ضربه سریع بزنید onScale (با فاکتور مقیاس تعیین شده توسط میزبان الگو) 2
گنگ دوار در حالت تابه onScroll (با ضریب فاصله تعیین شده توسط میزبان الگو) 2

نوار عمل نقشه اضافه کنید

این الگوها می توانند یک نوار اکشن نقشه برای اقدامات مرتبط با نقشه مانند بزرگنمایی در داخل و خارج ، اخیر ، نمایش قطب نما و سایر اقداماتی که برای نمایش انتخاب می کنید داشته باشد. نوار عمل نقشه می تواند حداکثر چهار دکمه فقط نماد داشته باشد که بدون تأثیرگذاری بر عمق کار می توانند تازه شوند. در حین حالت بیکار پنهان می شود و دوباره در حالت فعال ظاهر می شود.

برای دریافت مکالمه های تعامل MAP ، باید یک دکمه Action.PAN را در نوار عملکرد نقشه اضافه کنید. هنگامی که کاربر دکمه PAN را فشار می دهد ، میزبان همانطور که در بخش زیر توضیح داده شده است ، وارد حالت PAN می شود.

اگر برنامه شما از دکمه Action.PAN در نوار عمل نقشه خارج شده است ، ورودی کاربر را از روش های SurfaceCallback دریافت نمی کند ، و میزبان از هر حالت PAN فعال شده قبلی خارج می شود.

در صفحه لمسی ، دکمه PAN نمایش داده نمی شود.

حالت تابه را درک کنید

در حالت PAN ، میزبان الگوی ورودی کاربر را از دستگاه های ورودی غیر لمسی مانند کنترل کننده های چرخشی و لمسی به روشهای مناسب SurfaceCallback ترجمه می کند. به عمل کاربر برای وارد کردن یا خروج از حالت PAN با روش setPanModeListener در NavigationTemplate.Builder پاسخ دهید. میزبان می تواند سایر اجزای UI را در الگوی پنهان کند در حالی که کاربر در حالت PAN است.

تعامل با کاربر

برنامه شما می تواند با استفاده از الگوهای مشابه با یک برنامه تلفن همراه با کاربر ارتباط برقرار کند.

ورودی کاربر را اداره کنید

برنامه شما می تواند با انتقال شنوندگان مناسب به مدلهایی که از آنها پشتیبانی می کنند ، به ورودی کاربر پاسخ دهند. قطعه زیر نحوه ایجاد یک مدل Action را نشان می دهد که یک OnClickListener تنظیم می کند که به روشی تعریف شده توسط کد برنامه شما باز می گردد:

کاتلین

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

جاوا

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

روش onClickNavigate می تواند برنامه پیش فرض Navigation Car را با استفاده از روش CarContext.startCarApp شروع کند:

کاتلین

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

جاوا

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

برای اطلاعات بیشتر در مورد نحوه شروع برنامه ها ، از جمله قالب هدف ACTION_NAVIGATE ، برنامه Start a Car را با یک بخش هدف مشاهده کنید.

برخی از اقدامات ، مانند مواردی که نیاز به کارگردانی کاربر برای ادامه تعامل در دستگاه های تلفن همراه خود دارند ، فقط در هنگام پارک کردن ماشین مجاز است. برای اجرای این اقدامات می توانید از ParkedOnlyOnClickListener استفاده کنید. اگر ماشین پارک نشده باشد ، میزبان نشانه ای را برای کاربر نشان می دهد که در این مورد این عمل مجاز نیست. اگر ماشین پارک شده باشد ، کد به طور عادی اجرا می شود. قطعه زیر نحوه استفاده از ParkedOnlyOnClickListener را برای باز کردن صفحه تنظیمات روی دستگاه تلفن همراه نشان می دهد:

کاتلین

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

جاوا

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

نمایش اعلان ها

اعلان های ارسال شده به دستگاه تلفن همراه فقط در صورت گسترش با یک CarAppExtender ، روی صفحه نمایش خودرو نشان داده می شوند. برخی از ویژگی های اعلان ، مانند عنوان محتوا ، متن ، نماد و اقدامات را می توان در CarAppExtender تنظیم کرد و ویژگی های اعلان را هنگام ظاهر شدن در صفحه خودرو بر عهده داشت.

قطعه زیر نحوه ارسال اعلان را به صفحه نمایش اتومبیل نشان می دهد که عنوانی متفاوت از نمونه ای که در دستگاه تلفن همراه نشان داده شده است نشان می دهد:

کاتلین

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

جاوا

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

اعلان ها می توانند قسمتهای زیر رابط کاربری را تحت تأثیر قرار دهند:

  • اعلان Heads-Up (HUN) ممکن است به کاربر نمایش داده شود.
  • ورود به مرکز اعلان ممکن است به صورت اختیاری با نشان قابل مشاهده در راه آهن اضافه شود.
  • برای برنامه های ناوبری ، اعلان ممکن است در ویجت ریلی نمایش داده شود ، همانطور که در اعلان های نوبت به نوبه خود توضیح داده شده است.

شما می توانید نحوه پیکربندی اعلان های برنامه خود را برای تأثیرگذاری بر هر یک از آن عناصر رابط کاربری با استفاده از اولویت اعلان ، همانطور که در مستندات CarAppExtender توضیح داده شده است ، انتخاب کنید.

اگر NotificationCompat.Builder.setOnlyAlertOnce با مقدار true فراخوانی شود ، یک اعلان با اولویت بالا فقط یک بار به عنوان یک هون نمایش می دهد.

برای اطلاعات بیشتر در مورد نحوه طراحی اعلان های برنامه ماشین خود ، به راهنمای Google برای راهنمای رانندگی در مورد اعلان ها مراجعه کنید.

نان تست ها را نشان دهید

برنامه شما می تواند با استفاده از CarToast همانطور که در این قطعه نشان داده شده است ، نان تست را نشان دهد:

کاتلین

CarToast.makeText(carContext, "Hello!", CarToast.LENGTH_SHORT).show()

جاوا

CarToast.makeText(getCarContext(), "Hello!", CarToast.LENGTH_SHORT).show();

درخواست مجوزها

اگر برنامه شما نیاز به دسترسی به داده ها یا اقدامات محدود - برای مثال ، مکان - قوانین استاندارد مجوزهای اندرویدی دارد. برای درخواست مجوز ، می توانید از روش CarContext.requestPermissions() استفاده کنید.

فایده استفاده از CarContext.requestPermissions() ، بر خلاف استفاده از API های استاندارد Android ، این است که برای ایجاد گفتگوی مجوزها نیازی به Activity خود ندارید. علاوه بر این ، شما می توانید به جای ایجاد جریان وابسته به پلتفرم ، از همان کد Android Auto و Android Automobile Out استفاده کنید.

گفتگوی مجوزها را در 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 ، قصد صریح عمل برنامه خود را گسترش می دهید:

کاتلین

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

جاوا

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 فراخوانی می کند:

کاتلین

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)))
        }
    }
}

جاوا

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 در برنامه شما با فشار دادن صفحه رزرو پارکینگ روی پشته ، این هدف را انجام می دهد ، اگر قبلاً در بالای صفحه قرار نگرفته است:

کاتلین

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))
        }
    }
}

جاوا

@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 است ، تشخیص می دهد و سهمیه باقی مانده را بر اساس تعداد الگوهایی که برنامه به عقب می رود ، به روز می کند.

به عنوان مثال ، اگر برنامه دو الگوی را در هنگام صفحه A ارسال کند ، صفحه B را فشار می دهد و دو الگوی دیگر را ارسال می کند ، برنامه یک سهمیه باقی مانده است. اگر برنامه پس از آن به صفحه نمایش A برگردد ، میزبان سهمیه را به سه بازگرداند ، زیرا این برنامه توسط دو قالب به عقب رفته است.

توجه داشته باشید که ، هنگام بازگشت به صفحه ، یک برنامه باید الگویی را ارسال کند که از همان نوع آخرین ارسال شده توسط آن صفحه باشد. ارسال هر نوع الگوی دیگر باعث ایجاد خطایی می شود. با این حال ، تا زمانی که نوع در حین عملکرد عقب یکسان باشد ، یک برنامه می تواند آزادانه محتوای الگوی را بدون تأثیرگذاری بر سهمیه اصلاح کند.

بازنشانی عملیات

برخی از الگوهای خاص دارای معناشناسی ویژه ای هستند که نشانگر پایان یک کار است. به عنوان مثال ، NavigationTemplate منظره ای است که انتظار می رود روی صفحه بماند و با دستورالعمل های جدید نوبت به نوبه خود برای مصرف کاربر ، تازه شود. هنگامی که به یکی از این الگوها رسید ، میزبان سهمیه الگوی را تنظیم می کند و آن الگو را درمان می کند که گویی این اولین قدم از یک کار جدید است. این به برنامه اجازه می دهد تا یک کار جدید را آغاز کند. به مستندات الگوهای فردی مراجعه کنید تا ببینید کدام یک از آنها باعث تنظیم مجدد میزبان می شوند.

اگر میزبان قصد شروع برنامه را از یک اقدام اعلان یا از پرتابگر دریافت کند ، سهمیه نیز تنظیم مجدد می شود. این مکانیسم به یک برنامه اجازه می دهد تا جریان کار جدیدی را از اعلان ها آغاز کند ، و حتی اگر یک برنامه قبلاً محدود باشد و در پیش زمینه باشد ، صادق است.

برای اطلاعات بیشتر در مورد نحوه نمایش اعلان های برنامه خود در صفحه اتومبیل ، به بخش اعلان های نمایشگر مراجعه کنید. برای اطلاعات در مورد نحوه شروع برنامه خود از یک اقدام اعلان ، برنامه Start a Car را با یک بخش قصد مشاهده کنید.

API اتصال

می توانید با استفاده از API CarConnection برای بازیابی اطلاعات اتصال در زمان اجرا ، تعیین کنید که آیا برنامه شما در سیستم عامل Android Auto یا Android Automobile OS کار می کند.

به عنوان مثال ، در Session برنامه خودرو خود ، یک CarConnection آغاز کنید و در به روزرسانی های LiveData مشترک شوید:

کاتلین

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

جاوا

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

در Observer ، سپس می توانید نسبت به تغییرات در حالت اتصال واکنش نشان دهید:

کاتلین

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

جاوا

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

محدودیت های API

اتومبیل های مختلف ممکن است امکان نمایش تعداد متفاوتی از موارد Item را در یک زمان به کاربر نشان دهند. برای بررسی حد محتوا در زمان اجرا از ConstraintManager استفاده کنید و تعداد مناسب موارد را در قالب های خود تنظیم کنید.

با دریافت یک ConstraintManager از CarContext شروع کنید:

کاتلین

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

جاوا

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

سپس می توانید برای محدودیت محتوای مربوطه ، از شیء ConstraintManager بازیابی شده استفاده کنید. به عنوان مثال ، برای به دست آوردن تعداد مواردی که می توانند در یک شبکه نمایش داده شوند ، با CONTENT_LIMIT_TYPE_GRID با getContentLimit تماس بگیرید:

کاتلین

val gridItemLimit = manager.getContentLimit(ConstraintManager.CONTENT_LIMIT_TYPE_GRID)

جاوا

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

یک جریان ورود به سیستم اضافه کنید

اگر برنامه شما یک تجربه امضا شده را برای کاربران ارائه می دهد ، می توانید از قالب هایی مانند SignInTemplate و LongMessageTemplate با برنامه اتومبیل API سطح 2 و بالاتر استفاده کنید تا وارد سیستم خود شوید.

برای ایجاد یک SignInTemplate ، یک SignInMethod را تعریف کنید. کتابخانه برنامه اتومبیل در حال حاضر از روشهای ورود به سیستم پشتیبانی می کند:

  • InputSignInMethod برای ورود به سیستم نام کاربری/رمز عبور.
  • PinSignInMethod برای ورود به سیستم پین ، جایی که کاربر با استفاده از پین نمایش داده شده در واحد سر ، حساب خود را از تلفن خود پیوند می دهد.
  • ProviderSignInMethod برای ورود به سیستم ارائه دهنده ، مانند Google Sign Sign و One Tap .
  • QRCodeSignInMethod برای ورود به سیستم QR ، جایی که کاربر برای تکمیل ورود به سیستم ، کد QR را اسکن می کند. این با API سطح 4 و بالاتر در دسترس است.

به عنوان مثال ، برای پیاده سازی الگویی که رمز عبور کاربر را جمع می کند ، با ایجاد یک InputCallback برای پردازش و اعتبارسنجی ورودی کاربر شروع کنید:

کاتلین

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.
    }
}

جاوا

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.
    }
};

برای Builder InputSignInMethod یک InputCallback لازم است.

کاتلین

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

جاوا

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

در آخر ، از InputSignInMethod جدید خود استفاده کنید تا یک SignInTemplate ایجاد کنید.

کاتلین

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

جاوا

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

از حساب کاربری استفاده کنید

برنامه های سیستم عامل Android Automobile که دارای احراز هویت هستند باید به دلایل زیر از AccountManager استفاده کنند:

  • UX بهتر و سهولت در مدیریت حساب : کاربران می توانند به راحتی تمام حساب های خود را از منوی حساب ها در تنظیمات سیستم ، از جمله ورود به سیستم و ورود به سیستم ، مدیریت کنند.
  • تجربیات "مهمان" : از آنجا که اتومبیل ها دستگاه های مشترک هستند ، OEM ها می توانند تجربیات مهمان را در وسیله نقلیه فعال کنند ، جایی که نمی توان حساب ها را اضافه کرد.

انواع رشته های متن را اضافه کنید

اندازه های مختلف صفحه نمایش اتومبیل ممکن است مقادیر مختلفی از متن را نشان دهد. با استفاده از API API API سطح 2 و بالاتر ، می توانید انواع مختلفی از یک رشته متن را مشخص کنید تا به بهترین وجه متناسب با صفحه باشد. برای دیدن اینکه انواع متن در آن پذیرفته شده است ، به دنبال الگوها و مؤلفه هایی باشید که یک CarText می گیرند.

You can add text string variants to a CarText with the CarText.Builder.addVariant() method:

کاتلین

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

جاوا

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

You can then use this CarText —for example, as the primary text of a GridItem .

کاتلین

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

جاوا

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

Add strings in order from most to least preferred—for example, from longest to shortest. The host picks the appropriate-length string depending on the amount of space available on the car screen.

Add inline CarIcons for rows

You can add icons inline with text to enrich your app's visual appeal using CarIconSpan . See the documentation for CarIconSpan.create for more information on creating these spans. See Spantastic text styling with Spans for an overview of how text styling with spans work.

کاتلین

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

جاوا

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

Car Hardware APIs

Starting with Car App API level 3, the Car App Library has APIs that you can use to access vehicle properties and sensors.

الزامات

To use the APIs with Android Auto, start by adding a dependency on androidx.car.app:app-projected to the build.gradle file for your Android Auto module. For Android Automotive OS, add a dependency on androidx.car.app:app-automotive to the build.gradle file for your Android Automotive OS module.

Additionally, in your AndroidManifest.xml file, you need to declare the relevant permissions needed to request the car data you want to use. Note that these permissions must also be granted to you by the user. You can use the same code on both Android Auto and Android Automotive OS, rather than having to create platform-dependent flows. However, the permissions needed are different.

CarInfo

This table describes the properties surfaced by the CarInfo APIs and the permissions you need to request to use them:

روش ها خواص Android Auto Permissions Android Automotive OS Permissions Supported since Car App API level
fetchModel Make, model, year android.car.permission.CAR_INFO 3
fetchEnergyProfile EV connector types, fuel types com.google.android.gms.permission.CAR_FUEL android.car.permission.CAR_INFO 3
fetchExteriorDimensions

This data is only available on some Android Automotive OS vehicles running API 30 or higher

ابعاد بیرونی N/A android.car.permission.CAR_INFO 7
addTollListener
removeTollListener
Toll card state, toll card type 3
addEnergyLevelListener
removeEnergyLevelListener
Battery level, fuel level, fuel level low, range remaining 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
Raw speed, display speed (shown on car's cluster display) com.google.android.gms.permission.CAR_SPEED android.car.permission.CAR_SPEED ,
android.car.permission.READ_CAR_DISPLAY_UNITS
3
addMileageListener
removeMileageListener

Warning: the getOdometerMeters method of the Mileage class is inaccurately named and returns kilometers, not meters.

Odometer distance com.google.android.gms.permission.CAR_MILEAGE This data is not available on Android Automotive OS to apps installed from the Play Store. 3

For example, to get the remaining range, instantiate a CarInfo object, then create and register an OnCarDataAvailableListener :

کاتلین

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)

جاوا

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

Don't assume that the data from the car is available at all times. If you get an error, check the status of the value you requested to better understand why the data you requested could not be retrieved. Refer to the reference documentation for the full CarInfo class definition.

CarSensors

The CarSensors class gives you access to the vehicle's accelerometer, gyroscope, compass, and location data. The availability of these values may depend on the OEM. The format for the data from the accelerometer, gyroscope, and compass is the same as you would get from the SensorManager API . For example, to check the vehicle's heading:

کاتلین

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)

جاوا

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

To access location data from the car, you also need to declare and request the android.permission.ACCESS_FINE_LOCATION permission.

تست کردن

To simulate sensor data when testing on Android Auto, refer to the Sensors and Sensor configuration sections of the Desktop Head Unit guide. To simulate sensor data when testing on Android Automotive OS, refer to the Emulate hardware state section of the Android Automotive OS emulator guide.

The CarAppService, Session and Screen lifecycles

The Session and Screen classes implement the LifecycleOwner interface. As the user interacts with the app, your Session and Screen objects' lifecycle callbacks are invoked, as described in the following diagrams.

The lifecycles of a CarAppService and a Session

شکل 1 . The Session lifecycle.

For full details, see the documentation for the Session.getLifecycle method.

The lifecycle of a Screen

شکل 2 . The Screen lifecycle.

For full details, see the documentation for the Screen.getLifecycle method.

Record from the car microphone

Using your app's CarAppService and the CarAudioRecord API, you can give your app access to the user's car microphone. Users need to give your app permission to access the car microphone. Your app can record and process the user's input within your app.

Permission to record

Before recording any audio, you must first declare the permission to record in your AndroidManifest.xml and request that the user grant it.

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

You need to request the permission to record at runtime. See the Request permissions section for details on how to request a permission in your car app.

ضبط صدا

After the user gives permission to record, you can record the audio and process the recording.

کاتلین

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

جاوا

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

Audio focus

When recording from the car microphone, first acquire audio focus to ensure that any ongoing media is stopped. If you lose audio focus, stop recording.

Here is an example of how to acquire audio focus:

کاتلین

 
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

جاوا

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
 

Testing Library

The Android for Cars Testing Library provides auxiliary classes that you can use to validate your app's behavior in a test environment. For example, the SessionController lets you simulate a connection to the host and verify that the correct Screen and Template are created and returned.

Refer to the Samples for usage examples.

Report an Android for Cars App Library issue

If you find an issue with the library, report it using the Google Issue Tracker . Be sure to fill out all the requested information in the issue template.

Create a new issue

Before filing a new issue, please check whether it is listed in the library's release notes or reported in the issues list. You can subscribe and vote for issues by clicking the star for an issue in the tracker. For more information, see Subscribing to an Issue .

،

The Android for Cars App Library lets you bring your navigation , point of interest (POI) , internet of things (IOT) , or weather app to the car. It does so by providing a set of templates designed to meet driver distraction standards and taking care of details like the variety of car screen factors and input modalities.

This guide provides an overview of the library's key features and concepts and walks you through the process of setting up a basic app.

قبل از شروع

  1. Review the Design for Driving pages covering the Car App Library
  2. Review the key terms and concepts in the following section.
  3. Familiarize yourself with the Android Auto System UI and Android Automotive OS design .
  4. Review the Release Notes .
  5. Review the Samples .

اصطلاحات و مفاهیم کلیدی

Models and Templates
The user interface is represented by a graph of model objects that can be arranged together in different ways, as allowed by the template they belong to. Templates are a subset of the models that can act as a root in those graphs. Models include the information to be displayed to the user in the form of text and images as well as attributes to configure aspects of the visual appearance of such information—for example, text colors or image sizes. The host converts the models to views that are designed to meet driver distraction standards and takes care of details like the variety of car screen factors and input modalities.
میزبان
The host is the backend component that implements the functionality offered by the library's APIs so your app can run in the car. The responsibilities of the host range from discovering your app and managing its lifecycle to converting your models into views and notifying your app of user interactions. On mobile devices, this host is implemented by Android Auto. On Android Automotive OS, this host is installed as a system app.
Template restrictions
Different templates enforce restrictions in the content of their models. For example, list templates have limits on the number of items that can be presented to the user. Templates also have restrictions in the way they can be connected to form the flow of a task. For example, the app can only push up to five templates to the screen stack. See Template restrictions for more details.
Screen
Screen is a class provided by the library that apps implement to manage the user interface presented to the user. A Screen has a lifecycle and provides the mechanism for the app to send the template to display when the screen is visible. Screen instances can also be pushed and popped to and from a Screen stack , which ensures they adhere to the template flow restrictions .
CarAppService
CarAppService is an abstract Service class that your app must implement and export to be discovered and managed by the host. Your app's CarAppService is responsible for validating that a host connection can be trusted using createHostValidator and subsequently providing Session instances for each connection using onCreateSession .
Session

Session is an abstract class that your app must implement and return using CarAppService.onCreateSession . It serves as the entry point to display information on the car screen. It has a lifecycle that informs the current state of your app on the car screen, such as when your app is visible or hidden.

When a Session is started, such as when the app is first launched, the host requests for the initial Screen to display using the onCreateScreen method.

Install the Car App Library

See the Jetpack library release page for instructions on how to add the library to your app.

Configure your app's manifest files

Before you can create your car app, configure your app's manifest files as follows.

Declare your CarAppService

The host connects to your app through your CarAppService implementation. You declare this service in your manifest to let the host discover and connect to your app.

You also need to declare your app's category in the <category> element of your app's intent filter. See the list of supported app categories for the values allowed for this element.

The following code snippet shows how to declare a car app service for a point of interest app in your manifest:

<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>

Supported app categories

Declare your app's category by adding one or more of the following category values in the intent filter when you declare your CarAppService as described in the preceding section :

  • androidx.car.app.category.NAVIGATION : an app that provides turn-by-turn navigation directions. See Build navigation apps for cars .
  • androidx.car.app.category.POI : an app that provides functionality relevant to finding points of interest such as parking spots, charging stations, and gas stations. See Build point of interest apps for cars .
  • androidx.car.app.category.IOT : an app that enables users to take relevant actions on connected devices from within the car. See Build internet of things apps for cars .
  • androidx.car.app.category.WEATHER : an app that lets users see relevant weather information related to their current location or along their route. See Build weather apps for cars .

See Android app quality for cars for detailed descriptions of each category and criteria for apps to belong to them.

Specify the app name and icon

You need to specify an app name and icon that the host can use to represent your app in the system UI.

You can specify the app name and icon that is used to represent your app using the label and icon attributes of your CarAppService :

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

If the label or icon are not declared in the <service> element, the host falls back to the values specified for the <application> element.

Set a custom theme

To set a custom theme for your car app, add a <meta-data> element in your manifest file, as follows:

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

Then, declare your style resource to set the following attributes for your custom car app theme:

<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 level

The Car App Library defines its own API levels so that you can know which library features are supported by the template host on a vehicle. To retrieve the highest Car App API Level supported by a host, use the getCarAppApiLevel() method.

Declare the minimum Car App API Level supported by your app in your AndroidManifest.xml file:

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

See the documentation for the RequiresCarApi annotation for details on how to maintain backward compatibility and declare the minimum API level required to use a feature. For a definition of which API level is required to use a certain feature of the Car App Library, check the reference documentation for CarAppApiLevels .

Create your CarAppService and Session

Your app needs to extend the CarAppService class and implement its onCreateSession method, which returns a Session instance corresponding to the current connection to the host:

کاتلین

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

جاوا

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

The Session instance is responsible for returning the Screen instance to use the first time the app is started:

کاتلین

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

جاوا

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

To handle scenarios where your car app needs to start from a screen that is not the home or landing screen of your app, such as handling deep links, you can pre-seed a back stack of screens using ScreenManager.push before returning from onCreateScreen . Pre-seeding allows users to navigate back to previous screens from the first screen that your app is showing.

Create your start screen

You create the screens displayed by your app by defining classes that extend the Screen class and implementing its onGetTemplate method, which returns the Template instance representing the state of the UI to display in the car screen.

The following snippet shows how to declare a Screen that uses a PaneTemplate template to display a simple “Hello world!” string:

کاتلین

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

جاوا

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

The CarContext class

The CarContext class is a ContextWrapper subclass accessible to your Session and Screen instances. It provides access to car services, such as the ScreenManager for managing the screen stack ; the AppManager for general app-related functionality, such as accessing the Surface object for drawing maps ; and the NavigationManager used by turn-by-turn navigation apps to communicate navigation metadata and other navigation-related events with the host.

See Access the navigation templates for a comprehensive list of library functionality available to navigation apps.

CarContext also offers other functionality, such as letting you load drawable resources using the configuration from the car screen, starting an app in the car using intents, and signaling whether your app should display its map in dark theme .

Implement screen navigation

Apps often present a number of different screens, each possibly using different templates the user can navigate through as they interact with the interface displayed in the screen.

The ScreenManager class provides a screen stack you can use to push screens that can be popped automatically when the user selects a back button in the car screen or uses the hardware back button available in some cars.

The following snippet shows how to add a back action to a message template as well as an action that pushes a new screen when selected by the user:

کاتلین

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

جاوا

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

The Action.BACK object is a standard Action that automatically invokes ScreenManager.pop . This behavior can be overridden by using the OnBackPressedDispatcher instance available from the CarContext .

To help ensure the app is safe to use while driving, the screen stack can have a maximum depth of five screens. See the Template restrictions section for more details.

Refresh the contents of a template

Your app can request the content of a Screen to be invalidated by calling the Screen.invalidate method. The host subsequently calls back into your app's Screen.onGetTemplate method to retrieve the template with the new contents.

When refreshing a Screen , it is important to understand the specific content in the template that can be updated so the host does not count the new template against the template quota. See the Template restrictions section for more details.

We recommended that you structure your screens so there is a one-to-one mapping between a Screen and the type of template it returns through its onGetTemplate implementation.

Draw maps

Navigation, point of interest (POI), and weather apps using the following templates can draw maps by accessing a Surface .

To use the following templates, your app must have one of the corresponding permissions declared in a <uses-permission> element in its AndroidManifest.xml file.

الگو Template permission Category guidance
NavigationTemplate androidx.car.app.NAVIGATION_TEMPLATES ناوبری
MapWithContentTemplate androidx.car.app.NAVIGATION_TEMPLATES OR
androidx.car.app.MAP_TEMPLATES
Navigation , POI , Weather
MapTemplate ( deprecated ) androidx.car.app.NAVIGATION_TEMPLATES ناوبری
PlaceListNavigationTemplate ( deprecated ) androidx.car.app.NAVIGATION_TEMPLATES ناوبری
RoutePreviewNavigationTemplate ( deprecated ) androidx.car.app.NAVIGATION_TEMPLATES ناوبری

Declare the surface permission

In addition to the permission required for the template that you app is using, your app must declare the androidx.car.app.ACCESS_SURFACE permission in its AndroidManifest.xml file to get access to the surface:

<manifest ...>
  ...
  <uses-permission android:name="androidx.car.app.ACCESS_SURFACE" />
  ...
</manifest>

Access the surface

To access the Surface that the host provides, you must implement a SurfaceCallback and provide that implementation to the AppManager car service. The current Surface is passed to your SurfaceCallback in the SurfaceContainer parameter of the onSurfaceAvailable() and onSurfaceDestroyed() callbacks.

کاتلین

carContext.getCarService(AppManager::class.java).setSurfaceCallback(surfaceCallback)

جاوا

carContext.getCarService(AppManager.class).setSurfaceCallback(surfaceCallback);

Understand the surface's visible area

The host can draw user interface elements for the templates on top of the map. The host communicates the area of the surface that is guaranteed to be unobstructed and fully visible to the user by calling the SurfaceCallback.onVisibleAreaChanged method. Also, to minimize the number of changes, the host calls the SurfaceCallback.onStableAreaChanged method with the smallest rectangle, which is always visible based on the current template.

For example, when a navigation app uses the NavigationTemplate with an action strip on top, the action strip can hide itself when the user has not interacted with the screen for a while to make more space for the map. In this case, there is a callback to onStableAreaChanged and onVisibleAreaChanged with the same rectangle. When the action strip is hidden, only onVisibleAreaChanged is called with the larger area. If the user interacts with the screen, then again only onVisibleAreaChanged is called with the first rectangle.

Support dark theme

Apps must redraw their map onto the Surface instance with the proper dark colors when the host determines conditions warrant it, as described in Android app quality for cars .

To decide whether to draw a dark map, you can use the CarContext.isDarkMode method. Whenever the dark theme status changes, you receive a call to Session.onCarConfigurationChanged .

Let users interact with your map

When using the following templates, you can add support for users to interact with the maps you draw, such as letting them see different parts of a map by zooming and panning.

الگو Interactivity supported since Car App API Level
NavigationTemplate 2
PlaceListNavigationTemplate ( deprecated ) 4
RoutePreviewNavigationTemplate ( deprecated ) 4
MapTemplate ( deprecated ) 5 (introduction of template)
MapWithContentTemplate 7 (introduction of template)

Implement interactivity callbacks

The SurfaceCallback interface has several callback methods you can implement to add interactivity to maps built with the templates in the preceding section:

تعامل SurfaceCallback method Supported since Car App API level
ضربه بزنید onClick 5
برای بزرگنمایی، خرج کردن onScale 2
Single-touch drag onScroll 2
Single-touch fling onFling 2
دو ضربه سریع بزنید onScale (with scale factor determined by template host) 2
Rotary nudge in pan mode onScroll (with distance factor determined by template host) 2

Add a map action strip

These templates can have a map action strip for map-related actions such as zooming in and out, recentering, displaying a compass, and other actions you choose to display. The map action strip can have up to four icon-only buttons that can be refreshed without impacting task depth. It hides during idle state and reappears on active state.

To receive map interactivity callbacks , you must add an Action.PAN button in the map action strip. When the user presses the pan button, the host enters pan mode, as described in the following section.

If your app omits the Action.PAN button in the map action strip, it doesn't receive user input from the SurfaceCallback methods, and the host exits any previously activated pan mode.

On a touchscreen, the pan button is not displayed.

Understand pan mode

In pan mode, the template host translates user input from non-touch input devices, such as rotary controllers and touchpads, into the appropriate SurfaceCallback methods. Respond to the user action to enter or exit pan mode with the setPanModeListener method in the NavigationTemplate.Builder . The host can hide other UI components in the template while the user is in pan mode.

Interact with the user

Your app can interact with the user using patterns similar to a mobile app.

Handle user input

Your app can respond to user input by passing the appropriate listeners to the models that support them. The following snippet shows how to create an Action model that sets an OnClickListener that calls back to a method defined by your app's code:

کاتلین

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

جاوا

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

The onClickNavigate method can then start the default navigation car app by using the CarContext.startCarApp method:

کاتلین

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

جاوا

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

For more details on how to start apps, including the format of the ACTION_NAVIGATE intent, see the Start a car app with an intent section.

Some actions, such as those that require directing the user to continue the interaction on their mobile devices, are only allowed when the car is parked. You can use the ParkedOnlyOnClickListener to implement those actions. If the car is not parked, the host displays an indication to the user that the action is not allowed in this case. If the car is parked, the code executes normally. The following snippet shows how to use the ParkedOnlyOnClickListener to open a settings screen on the mobile device:

کاتلین

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

جاوا

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

نمایش اعلان ها

Notifications sent to the mobile device only show up on the car screen if they are extended with a CarAppExtender . Some notification attributes, such as content title, text, icon, and actions, can be set in the CarAppExtender , overriding the notification's attributes when they appear on the car screen.

The following snippet shows how to send a notification to the car screen that displays a different title than the one shown on the mobile device:

کاتلین

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

جاوا

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

Notifications can affect the following parts of the user interface:

  • A heads-up notification (HUN) may be displayed to the user.
  • An entry in the notification center may be added, optionally with a badge visible in the rail.
  • For navigation apps, the notification may be displayed in the rail widget as described in Turn-by-turn notifications .

You can choose how to configure your app's notifications to affect each of those user interface elements by using the notification's priority, as described in the CarAppExtender documentation.

If NotificationCompat.Builder.setOnlyAlertOnce is called with a value of true , a high-priority notification displays as a HUN only once.

For more information on how to design your car app's notifications, see the Google Design for Driving guide about Notifications .

Show toasts

Your app can display a toast using CarToast as shown in this snippet:

کاتلین

CarToast.makeText(carContext, "Hello!", CarToast.LENGTH_SHORT).show()

جاوا

CarToast.makeText(getCarContext(), "Hello!", CarToast.LENGTH_SHORT).show();

درخواست مجوزها

If your app needs access to restricted data or actions—for example, location—the standard rules of Android permissions apply. To request a permission, you can use the CarContext.requestPermissions() method.

The benefit of using CarContext.requestPermissions() , as opposed to using standard Android APIs , is that you don't need to launch your own Activity to create the permissions dialog. Moreover, you can use the same code on both Android Auto and Android Automotive OS, rather than having to create platform-dependent flows.

Style the permissions dialog on Android Auto

On Android Auto, the permissions dialog for the user will appear on the phone. By default, there will be no background behind the dialog. To set a custom background, declare a car app theme in your AndroidManifest.xml file and set the carPermissionActivityLayout attribute for your car app theme.

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

Then, set the carPermissionActivityLayout attribute for your car app theme:

<resources>
  <style name="MyCarAppTheme">
    <item name="carPermissionActivityLayout">@layout/my_custom_background</item>
  </style>
</resources>

Start a car app with an intent

You can call the CarContext.startCarApp method to perform one of the following actions:

  • Open the dialer to make a phone call.
  • Start turn-by-turn navigation to a location with the default navigation car app .
  • Start your own app with an intent.

The following example shows how to create a notification with an action that opens your app with a screen that shows the details of a parking reservation. You extend the notification instance with a content intent that contains a PendingIntent wrapping an explicit intent to your app's action:

کاتلین

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

جاوا

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

Your app must also declare a BroadcastReceiver that is invoked to process the intent when the user selects the action in the notification interface and invokes CarContext.startCarApp with an intent including the data URI:

کاتلین

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)))
        }
    }
}

جاوا

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

Finally, the Session.onNewIntent method in your app handles this intent by pushing the parking reservation screen on the stack, if it's not already on top:

کاتلین

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))
        }
    }
}

جاوا

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

See the Display notifications section for more information on how to handle notifications for the car app.

Template restrictions

The host limits the number of templates to display for a given task to a maximum of five, of which the last template must be one of the following types:

Note that this limit applies to the number of templates and not the number of Screen instances in the stack. For example, if an app sends two templates while in screen A and then pushes screen B, it can now send three more templates. Alternatively, if each screen is structured to send a single template, then the app can push five screen instances onto the ScreenManager stack.

There are special cases to these restrictions: template refreshes and back and reset operations.

Template refreshes

Certain content updates are not counted toward the template limit. In general, if an app pushes a new template that is of the same type and contains the same main content as the previous template, the new template is not counted against the quota. For example, updating the toggle state of a row in a ListTemplate does not count against the quota. See the documentation of individual templates to learn more about what types of content updates can be considered a refresh.

Back operations

To enable sub-flows within a task, the host detects when an app is popping a Screen from the ScreenManager stack and updates the remaining quota based on the number of templates that the app is going backward by.

For example, if the app sends two templates while in screen A, then pushes screen B and sends two more templates, the app has one quota remaining. If the app then pops back to screen A, the host resets the quota to three, because the app has gone backward by two templates.

Note that, when popping back to a screen, an app must send a template that is of the same type as the one last sent by that screen. Sending any other template type causes an error. However, as long as the type remains the same during a back operation, an app can freely modify the contents of the template without affecting the quota.

Reset operations

Certain templates have special semantics that signify the end of a task. For example, the NavigationTemplate is a view that is expected to stay on the screen and be refreshed with new turn-by-turn instructions for the user's consumption. When it reaches one of these templates, the host resets the template quota, treating that template as if it is the first step of a new task. This allows the app to begin a new task. See the documentation of individual templates to see which ones trigger a reset on the host.

If the host receives an intent to start the app from a notification action or from the launcher, the quota is also reset. This mechanism lets an app begin a new task flow from notifications, and it holds true even if an app is already bound and in the foreground.

See the Display notifications section for more details on how to display your app's notifications in the car screen. See the Start a car app with an intent section for information on how to start your app from a notification action.

Connection API

You can determine whether your app is running on Android Auto or Android Automotive OS by using the CarConnection API to retrieve connection information at runtime.

For example, in your car app's Session , initialize a CarConnection and subscribe to LiveData updates:

کاتلین

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

جاوا

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

In the observer, you can then react to changes in the connection state:

کاتلین

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

جاوا

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

Different cars may allow for a different number of Item instances to be displayed to the user at a time. Use the ConstraintManager to check the content limit at runtime and set the appropriate number of items in your templates.

Start by getting a ConstraintManager from the CarContext :

کاتلین

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

جاوا

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

You can then query the retrieved ConstraintManager object for the relevant content limit. For example, to get the number of items that can be displayed in a grid, call getContentLimit with CONTENT_LIMIT_TYPE_GRID :

کاتلین

val gridItemLimit = manager.getContentLimit(ConstraintManager.CONTENT_LIMIT_TYPE_GRID)

جاوا

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

Add a sign-in flow

If your app offers a signed-in experience for users, you can use templates like the SignInTemplate and LongMessageTemplate with Car App API level 2 and above to handle signing in to your app on the car's head unit.

To create a SignInTemplate , define a SignInMethod . The Car App Library currently supports the following sign-in methods:

For example, to implement a template that collects the user's password, start by creating an InputCallback to process and validate user input:

کاتلین

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.
    }
}

جاوا

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.
    }
};

An InputCallback is required for the InputSignInMethod Builder .

کاتلین

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

جاوا

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

Finally, use your new InputSignInMethod to create a SignInTemplate .

کاتلین

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

جاوا

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

Use AccountManager

Android Automotive OS apps that have authentication must use AccountManager for the following reasons:

  • Better UX and ease of account management : Users can easily manage all their accounts from the accounts menu in the system settings, including sign-in and sign-out.
  • "Guest" experiences : Because cars are shared devices, OEMs can enable guest experiences in the vehicle, where accounts cannot be added.

Add text string variants

Different car screen sizes may show different amounts of text. With Car App API level 2 and above, you can specify multiple variants of a text string to best fit the screen. To see where text variants are accepted, look for templates and components that take a CarText .

You can add text string variants to a CarText with the CarText.Builder.addVariant() method:

کاتلین

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

جاوا

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

You can then use this CarText —for example, as the primary text of a GridItem .

کاتلین

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

جاوا

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

Add strings in order from most to least preferred—for example, from longest to shortest. The host picks the appropriate-length string depending on the amount of space available on the car screen.

Add inline CarIcons for rows

You can add icons inline with text to enrich your app's visual appeal using CarIconSpan . See the documentation for CarIconSpan.create for more information on creating these spans. See Spantastic text styling with Spans for an overview of how text styling with spans work.

کاتلین

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

جاوا

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

Car Hardware APIs

Starting with Car App API level 3, the Car App Library has APIs that you can use to access vehicle properties and sensors.

الزامات

To use the APIs with Android Auto, start by adding a dependency on androidx.car.app:app-projected to the build.gradle file for your Android Auto module. For Android Automotive OS, add a dependency on androidx.car.app:app-automotive to the build.gradle file for your Android Automotive OS module.

Additionally, in your AndroidManifest.xml file, you need to declare the relevant permissions needed to request the car data you want to use. Note that these permissions must also be granted to you by the user. You can use the same code on both Android Auto and Android Automotive OS, rather than having to create platform-dependent flows. However, the permissions needed are different.

CarInfo

This table describes the properties surfaced by the CarInfo APIs and the permissions you need to request to use them:

روش ها خواص Android Auto Permissions Android Automotive OS Permissions Supported since Car App API level
fetchModel Make, model, year android.car.permission.CAR_INFO 3
fetchEnergyProfile EV connector types, fuel types com.google.android.gms.permission.CAR_FUEL android.car.permission.CAR_INFO 3
fetchExteriorDimensions

This data is only available on some Android Automotive OS vehicles running API 30 or higher

ابعاد بیرونی N/A android.car.permission.CAR_INFO 7
addTollListener
removeTollListener
Toll card state, toll card type 3
addEnergyLevelListener
removeEnergyLevelListener
Battery level, fuel level, fuel level low, range remaining 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
Raw speed, display speed (shown on car's cluster display) com.google.android.gms.permission.CAR_SPEED android.car.permission.CAR_SPEED ,
android.car.permission.READ_CAR_DISPLAY_UNITS
3
addMileageListener
removeMileageListener

Warning: the getOdometerMeters method of the Mileage class is inaccurately named and returns kilometers, not meters.

Odometer distance com.google.android.gms.permission.CAR_MILEAGE This data is not available on Android Automotive OS to apps installed from the Play Store. 3

For example, to get the remaining range, instantiate a CarInfo object, then create and register an OnCarDataAvailableListener :

کاتلین

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)

جاوا

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

Don't assume that the data from the car is available at all times. If you get an error, check the status of the value you requested to better understand why the data you requested could not be retrieved. Refer to the reference documentation for the full CarInfo class definition.

CarSensors

The CarSensors class gives you access to the vehicle's accelerometer, gyroscope, compass, and location data. The availability of these values may depend on the OEM. The format for the data from the accelerometer, gyroscope, and compass is the same as you would get from the SensorManager API . For example, to check the vehicle's heading:

کاتلین

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)

جاوا

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

To access location data from the car, you also need to declare and request the android.permission.ACCESS_FINE_LOCATION permission.

تست کردن

To simulate sensor data when testing on Android Auto, refer to the Sensors and Sensor configuration sections of the Desktop Head Unit guide. To simulate sensor data when testing on Android Automotive OS, refer to the Emulate hardware state section of the Android Automotive OS emulator guide.

The CarAppService, Session and Screen lifecycles

The Session and Screen classes implement the LifecycleOwner interface. As the user interacts with the app, your Session and Screen objects' lifecycle callbacks are invoked, as described in the following diagrams.

The lifecycles of a CarAppService and a Session

شکل 1 . The Session lifecycle.

For full details, see the documentation for the Session.getLifecycle method.

The lifecycle of a Screen

شکل 2 . The Screen lifecycle.

For full details, see the documentation for the Screen.getLifecycle method.

Record from the car microphone

Using your app's CarAppService and the CarAudioRecord API, you can give your app access to the user's car microphone. Users need to give your app permission to access the car microphone. Your app can record and process the user's input within your app.

Permission to record

Before recording any audio, you must first declare the permission to record in your AndroidManifest.xml and request that the user grant it.

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

You need to request the permission to record at runtime. See the Request permissions section for details on how to request a permission in your car app.

ضبط صدا

After the user gives permission to record, you can record the audio and process the recording.

کاتلین

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

جاوا

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

Audio focus

When recording from the car microphone, first acquire audio focus to ensure that any ongoing media is stopped. If you lose audio focus, stop recording.

Here is an example of how to acquire audio focus:

کاتلین

 
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

جاوا

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
 

Testing Library

The Android for Cars Testing Library provides auxiliary classes that you can use to validate your app's behavior in a test environment. For example, the SessionController lets you simulate a connection to the host and verify that the correct Screen and Template are created and returned.

Refer to the Samples for usage examples.

Report an Android for Cars App Library issue

If you find an issue with the library, report it using the Google Issue Tracker . Be sure to fill out all the requested information in the issue template.

Create a new issue

Before filing a new issue, please check whether it is listed in the library's release notes or reported in the issues list. You can subscribe and vote for issues by clicking the star for an issue in the tracker. For more information, see Subscribing to an Issue .

،

The Android for Cars App Library lets you bring your navigation , point of interest (POI) , internet of things (IOT) , or weather app to the car. It does so by providing a set of templates designed to meet driver distraction standards and taking care of details like the variety of car screen factors and input modalities.

This guide provides an overview of the library's key features and concepts and walks you through the process of setting up a basic app.

قبل از شروع

  1. Review the Design for Driving pages covering the Car App Library
  2. Review the key terms and concepts in the following section.
  3. Familiarize yourself with the Android Auto System UI and Android Automotive OS design .
  4. Review the Release Notes .
  5. Review the Samples .

اصطلاحات و مفاهیم کلیدی

Models and Templates
The user interface is represented by a graph of model objects that can be arranged together in different ways, as allowed by the template they belong to. Templates are a subset of the models that can act as a root in those graphs. Models include the information to be displayed to the user in the form of text and images as well as attributes to configure aspects of the visual appearance of such information—for example, text colors or image sizes. The host converts the models to views that are designed to meet driver distraction standards and takes care of details like the variety of car screen factors and input modalities.
میزبان
The host is the backend component that implements the functionality offered by the library's APIs so your app can run in the car. The responsibilities of the host range from discovering your app and managing its lifecycle to converting your models into views and notifying your app of user interactions. On mobile devices, this host is implemented by Android Auto. On Android Automotive OS, this host is installed as a system app.
Template restrictions
Different templates enforce restrictions in the content of their models. For example, list templates have limits on the number of items that can be presented to the user. Templates also have restrictions in the way they can be connected to form the flow of a task. For example, the app can only push up to five templates to the screen stack. See Template restrictions for more details.
Screen
Screen is a class provided by the library that apps implement to manage the user interface presented to the user. A Screen has a lifecycle and provides the mechanism for the app to send the template to display when the screen is visible. Screen instances can also be pushed and popped to and from a Screen stack , which ensures they adhere to the template flow restrictions .
CarAppService
CarAppService is an abstract Service class that your app must implement and export to be discovered and managed by the host. Your app's CarAppService is responsible for validating that a host connection can be trusted using createHostValidator and subsequently providing Session instances for each connection using onCreateSession .
Session

Session is an abstract class that your app must implement and return using CarAppService.onCreateSession . It serves as the entry point to display information on the car screen. It has a lifecycle that informs the current state of your app on the car screen, such as when your app is visible or hidden.

When a Session is started, such as when the app is first launched, the host requests for the initial Screen to display using the onCreateScreen method.

Install the Car App Library

See the Jetpack library release page for instructions on how to add the library to your app.

Configure your app's manifest files

Before you can create your car app, configure your app's manifest files as follows.

Declare your CarAppService

The host connects to your app through your CarAppService implementation. You declare this service in your manifest to let the host discover and connect to your app.

You also need to declare your app's category in the <category> element of your app's intent filter. See the list of supported app categories for the values allowed for this element.

The following code snippet shows how to declare a car app service for a point of interest app in your manifest:

<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>

Supported app categories

Declare your app's category by adding one or more of the following category values in the intent filter when you declare your CarAppService as described in the preceding section :

  • androidx.car.app.category.NAVIGATION : an app that provides turn-by-turn navigation directions. See Build navigation apps for cars .
  • androidx.car.app.category.POI : an app that provides functionality relevant to finding points of interest such as parking spots, charging stations, and gas stations. See Build point of interest apps for cars .
  • androidx.car.app.category.IOT : an app that enables users to take relevant actions on connected devices from within the car. See Build internet of things apps for cars .
  • androidx.car.app.category.WEATHER : an app that lets users see relevant weather information related to their current location or along their route. See Build weather apps for cars .

See Android app quality for cars for detailed descriptions of each category and criteria for apps to belong to them.

Specify the app name and icon

You need to specify an app name and icon that the host can use to represent your app in the system UI.

You can specify the app name and icon that is used to represent your app using the label and icon attributes of your CarAppService :

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

If the label or icon are not declared in the <service> element, the host falls back to the values specified for the <application> element.

Set a custom theme

To set a custom theme for your car app, add a <meta-data> element in your manifest file, as follows:

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

Then, declare your style resource to set the following attributes for your custom car app theme:

<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 level

The Car App Library defines its own API levels so that you can know which library features are supported by the template host on a vehicle. To retrieve the highest Car App API Level supported by a host, use the getCarAppApiLevel() method.

Declare the minimum Car App API Level supported by your app in your AndroidManifest.xml file:

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

See the documentation for the RequiresCarApi annotation for details on how to maintain backward compatibility and declare the minimum API level required to use a feature. For a definition of which API level is required to use a certain feature of the Car App Library, check the reference documentation for CarAppApiLevels .

Create your CarAppService and Session

Your app needs to extend the CarAppService class and implement its onCreateSession method, which returns a Session instance corresponding to the current connection to the host:

کاتلین

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

جاوا

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

The Session instance is responsible for returning the Screen instance to use the first time the app is started:

کاتلین

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

جاوا

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

To handle scenarios where your car app needs to start from a screen that is not the home or landing screen of your app, such as handling deep links, you can pre-seed a back stack of screens using ScreenManager.push before returning from onCreateScreen . Pre-seeding allows users to navigate back to previous screens from the first screen that your app is showing.

Create your start screen

You create the screens displayed by your app by defining classes that extend the Screen class and implementing its onGetTemplate method, which returns the Template instance representing the state of the UI to display in the car screen.

The following snippet shows how to declare a Screen that uses a PaneTemplate template to display a simple “Hello world!” string:

کاتلین

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

جاوا

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

The CarContext class

The CarContext class is a ContextWrapper subclass accessible to your Session and Screen instances. It provides access to car services, such as the ScreenManager for managing the screen stack ; the AppManager for general app-related functionality, such as accessing the Surface object for drawing maps ; and the NavigationManager used by turn-by-turn navigation apps to communicate navigation metadata and other navigation-related events with the host.

See Access the navigation templates for a comprehensive list of library functionality available to navigation apps.

CarContext also offers other functionality, such as letting you load drawable resources using the configuration from the car screen, starting an app in the car using intents, and signaling whether your app should display its map in dark theme .

Implement screen navigation

Apps often present a number of different screens, each possibly using different templates the user can navigate through as they interact with the interface displayed in the screen.

The ScreenManager class provides a screen stack you can use to push screens that can be popped automatically when the user selects a back button in the car screen or uses the hardware back button available in some cars.

The following snippet shows how to add a back action to a message template as well as an action that pushes a new screen when selected by the user:

کاتلین

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

جاوا

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

The Action.BACK object is a standard Action that automatically invokes ScreenManager.pop . This behavior can be overridden by using the OnBackPressedDispatcher instance available from the CarContext .

To help ensure the app is safe to use while driving, the screen stack can have a maximum depth of five screens. See the Template restrictions section for more details.

Refresh the contents of a template

Your app can request the content of a Screen to be invalidated by calling the Screen.invalidate method. The host subsequently calls back into your app's Screen.onGetTemplate method to retrieve the template with the new contents.

When refreshing a Screen , it is important to understand the specific content in the template that can be updated so the host does not count the new template against the template quota. See the Template restrictions section for more details.

We recommended that you structure your screens so there is a one-to-one mapping between a Screen and the type of template it returns through its onGetTemplate implementation.

Draw maps

Navigation, point of interest (POI), and weather apps using the following templates can draw maps by accessing a Surface .

To use the following templates, your app must have one of the corresponding permissions declared in a <uses-permission> element in its AndroidManifest.xml file.

الگو Template permission Category guidance
NavigationTemplate androidx.car.app.NAVIGATION_TEMPLATES ناوبری
MapWithContentTemplate androidx.car.app.NAVIGATION_TEMPLATES OR
androidx.car.app.MAP_TEMPLATES
Navigation , POI , Weather
MapTemplate ( deprecated ) androidx.car.app.NAVIGATION_TEMPLATES ناوبری
PlaceListNavigationTemplate ( deprecated ) androidx.car.app.NAVIGATION_TEMPLATES ناوبری
RoutePreviewNavigationTemplate ( deprecated ) androidx.car.app.NAVIGATION_TEMPLATES ناوبری

Declare the surface permission

In addition to the permission required for the template that you app is using, your app must declare the androidx.car.app.ACCESS_SURFACE permission in its AndroidManifest.xml file to get access to the surface:

<manifest ...>
  ...
  <uses-permission android:name="androidx.car.app.ACCESS_SURFACE" />
  ...
</manifest>

Access the surface

To access the Surface that the host provides, you must implement a SurfaceCallback and provide that implementation to the AppManager car service. The current Surface is passed to your SurfaceCallback in the SurfaceContainer parameter of the onSurfaceAvailable() and onSurfaceDestroyed() callbacks.

کاتلین

carContext.getCarService(AppManager::class.java).setSurfaceCallback(surfaceCallback)

جاوا

carContext.getCarService(AppManager.class).setSurfaceCallback(surfaceCallback);

Understand the surface's visible area

The host can draw user interface elements for the templates on top of the map. The host communicates the area of the surface that is guaranteed to be unobstructed and fully visible to the user by calling the SurfaceCallback.onVisibleAreaChanged method. Also, to minimize the number of changes, the host calls the SurfaceCallback.onStableAreaChanged method with the smallest rectangle, which is always visible based on the current template.

For example, when a navigation app uses the NavigationTemplate with an action strip on top, the action strip can hide itself when the user has not interacted with the screen for a while to make more space for the map. In this case, there is a callback to onStableAreaChanged and onVisibleAreaChanged with the same rectangle. When the action strip is hidden, only onVisibleAreaChanged is called with the larger area. If the user interacts with the screen, then again only onVisibleAreaChanged is called with the first rectangle.

Support dark theme

Apps must redraw their map onto the Surface instance with the proper dark colors when the host determines conditions warrant it, as described in Android app quality for cars .

To decide whether to draw a dark map, you can use the CarContext.isDarkMode method. Whenever the dark theme status changes, you receive a call to Session.onCarConfigurationChanged .

Let users interact with your map

When using the following templates, you can add support for users to interact with the maps you draw, such as letting them see different parts of a map by zooming and panning.

الگو Interactivity supported since Car App API Level
NavigationTemplate 2
PlaceListNavigationTemplate ( deprecated ) 4
RoutePreviewNavigationTemplate ( deprecated ) 4
MapTemplate ( deprecated ) 5 (introduction of template)
MapWithContentTemplate 7 (introduction of template)

Implement interactivity callbacks

The SurfaceCallback interface has several callback methods you can implement to add interactivity to maps built with the templates in the preceding section:

تعامل SurfaceCallback method Supported since Car App API level
ضربه بزنید onClick 5
برای بزرگنمایی، خرج کردن onScale 2
Single-touch drag onScroll 2
Single-touch fling onFling 2
دو ضربه سریع بزنید onScale (with scale factor determined by template host) 2
Rotary nudge in pan mode onScroll (with distance factor determined by template host) 2

Add a map action strip

These templates can have a map action strip for map-related actions such as zooming in and out, recentering, displaying a compass, and other actions you choose to display. The map action strip can have up to four icon-only buttons that can be refreshed without impacting task depth. It hides during idle state and reappears on active state.

To receive map interactivity callbacks , you must add an Action.PAN button in the map action strip. When the user presses the pan button, the host enters pan mode, as described in the following section.

If your app omits the Action.PAN button in the map action strip, it doesn't receive user input from the SurfaceCallback methods, and the host exits any previously activated pan mode.

On a touchscreen, the pan button is not displayed.

Understand pan mode

In pan mode, the template host translates user input from non-touch input devices, such as rotary controllers and touchpads, into the appropriate SurfaceCallback methods. Respond to the user action to enter or exit pan mode with the setPanModeListener method in the NavigationTemplate.Builder . The host can hide other UI components in the template while the user is in pan mode.

Interact with the user

Your app can interact with the user using patterns similar to a mobile app.

Handle user input

Your app can respond to user input by passing the appropriate listeners to the models that support them. The following snippet shows how to create an Action model that sets an OnClickListener that calls back to a method defined by your app's code:

کاتلین

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

جاوا

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

The onClickNavigate method can then start the default navigation car app by using the CarContext.startCarApp method:

کاتلین

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

جاوا

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

For more details on how to start apps, including the format of the ACTION_NAVIGATE intent, see the Start a car app with an intent section.

Some actions, such as those that require directing the user to continue the interaction on their mobile devices, are only allowed when the car is parked. You can use the ParkedOnlyOnClickListener to implement those actions. If the car is not parked, the host displays an indication to the user that the action is not allowed in this case. If the car is parked, the code executes normally. The following snippet shows how to use the ParkedOnlyOnClickListener to open a settings screen on the mobile device:

کاتلین

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

جاوا

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

نمایش اعلان ها

Notifications sent to the mobile device only show up on the car screen if they are extended with a CarAppExtender . Some notification attributes, such as content title, text, icon, and actions, can be set in the CarAppExtender , overriding the notification's attributes when they appear on the car screen.

The following snippet shows how to send a notification to the car screen that displays a different title than the one shown on the mobile device:

کاتلین

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

جاوا

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

Notifications can affect the following parts of the user interface:

  • A heads-up notification (HUN) may be displayed to the user.
  • An entry in the notification center may be added, optionally with a badge visible in the rail.
  • For navigation apps, the notification may be displayed in the rail widget as described in Turn-by-turn notifications .

You can choose how to configure your app's notifications to affect each of those user interface elements by using the notification's priority, as described in the CarAppExtender documentation.

If NotificationCompat.Builder.setOnlyAlertOnce is called with a value of true , a high-priority notification displays as a HUN only once.

For more information on how to design your car app's notifications, see the Google Design for Driving guide about Notifications .

Show toasts

Your app can display a toast using CarToast as shown in this snippet:

کاتلین

CarToast.makeText(carContext, "Hello!", CarToast.LENGTH_SHORT).show()

جاوا

CarToast.makeText(getCarContext(), "Hello!", CarToast.LENGTH_SHORT).show();

درخواست مجوزها

If your app needs access to restricted data or actions—for example, location—the standard rules of Android permissions apply. To request a permission, you can use the CarContext.requestPermissions() method.

The benefit of using CarContext.requestPermissions() , as opposed to using standard Android APIs , is that you don't need to launch your own Activity to create the permissions dialog. Moreover, you can use the same code on both Android Auto and Android Automotive OS, rather than having to create platform-dependent flows.

Style the permissions dialog on Android Auto

On Android Auto, the permissions dialog for the user will appear on the phone. By default, there will be no background behind the dialog. To set a custom background, declare a car app theme in your AndroidManifest.xml file and set the carPermissionActivityLayout attribute for your car app theme.

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

Then, set the carPermissionActivityLayout attribute for your car app theme:

<resources>
  <style name="MyCarAppTheme">
    <item name="carPermissionActivityLayout">@layout/my_custom_background</item>
  </style>
</resources>

Start a car app with an intent

You can call the CarContext.startCarApp method to perform one of the following actions:

  • Open the dialer to make a phone call.
  • Start turn-by-turn navigation to a location with the default navigation car app .
  • Start your own app with an intent.

The following example shows how to create a notification with an action that opens your app with a screen that shows the details of a parking reservation. You extend the notification instance with a content intent that contains a PendingIntent wrapping an explicit intent to your app's action:

کاتلین

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

جاوا

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

Your app must also declare a BroadcastReceiver that is invoked to process the intent when the user selects the action in the notification interface and invokes CarContext.startCarApp with an intent including the data URI:

کاتلین

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)))
        }
    }
}

جاوا

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

Finally, the Session.onNewIntent method in your app handles this intent by pushing the parking reservation screen on the stack, if it's not already on top:

کاتلین

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))
        }
    }
}

جاوا

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

See the Display notifications section for more information on how to handle notifications for the car app.

Template restrictions

The host limits the number of templates to display for a given task to a maximum of five, of which the last template must be one of the following types:

Note that this limit applies to the number of templates and not the number of Screen instances in the stack. For example, if an app sends two templates while in screen A and then pushes screen B, it can now send three more templates. Alternatively, if each screen is structured to send a single template, then the app can push five screen instances onto the ScreenManager stack.

There are special cases to these restrictions: template refreshes and back and reset operations.

Template refreshes

Certain content updates are not counted toward the template limit. In general, if an app pushes a new template that is of the same type and contains the same main content as the previous template, the new template is not counted against the quota. For example, updating the toggle state of a row in a ListTemplate does not count against the quota. See the documentation of individual templates to learn more about what types of content updates can be considered a refresh.

Back operations

To enable sub-flows within a task, the host detects when an app is popping a Screen from the ScreenManager stack and updates the remaining quota based on the number of templates that the app is going backward by.

For example, if the app sends two templates while in screen A, then pushes screen B and sends two more templates, the app has one quota remaining. If the app then pops back to screen A, the host resets the quota to three, because the app has gone backward by two templates.

Note that, when popping back to a screen, an app must send a template that is of the same type as the one last sent by that screen. Sending any other template type causes an error. However, as long as the type remains the same during a back operation, an app can freely modify the contents of the template without affecting the quota.

Reset operations

Certain templates have special semantics that signify the end of a task. For example, the NavigationTemplate is a view that is expected to stay on the screen and be refreshed with new turn-by-turn instructions for the user's consumption. When it reaches one of these templates, the host resets the template quota, treating that template as if it is the first step of a new task. This allows the app to begin a new task. See the documentation of individual templates to see which ones trigger a reset on the host.

If the host receives an intent to start the app from a notification action or from the launcher, the quota is also reset. This mechanism lets an app begin a new task flow from notifications, and it holds true even if an app is already bound and in the foreground.

See the Display notifications section for more details on how to display your app's notifications in the car screen. See the Start a car app with an intent section for information on how to start your app from a notification action.

Connection API

You can determine whether your app is running on Android Auto or Android Automotive OS by using the CarConnection API to retrieve connection information at runtime.

For example, in your car app's Session , initialize a CarConnection and subscribe to LiveData updates:

کاتلین

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

جاوا

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

In the observer, you can then react to changes in the connection state:

کاتلین

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

جاوا

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

Different cars may allow for a different number of Item instances to be displayed to the user at a time. Use the ConstraintManager to check the content limit at runtime and set the appropriate number of items in your templates.

Start by getting a ConstraintManager from the CarContext :

کاتلین

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

جاوا

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

You can then query the retrieved ConstraintManager object for the relevant content limit. For example, to get the number of items that can be displayed in a grid, call getContentLimit with CONTENT_LIMIT_TYPE_GRID :

کاتلین

val gridItemLimit = manager.getContentLimit(ConstraintManager.CONTENT_LIMIT_TYPE_GRID)

جاوا

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

Add a sign-in flow

If your app offers a signed-in experience for users, you can use templates like the SignInTemplate and LongMessageTemplate with Car App API level 2 and above to handle signing in to your app on the car's head unit.

To create a SignInTemplate , define a SignInMethod . The Car App Library currently supports the following sign-in methods:

For example, to implement a template that collects the user's password, start by creating an InputCallback to process and validate user input:

کاتلین

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.
    }
}

جاوا

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.
    }
};

An InputCallback is required for the InputSignInMethod Builder .

کاتلین

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

جاوا

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

Finally, use your new InputSignInMethod to create a SignInTemplate .

کاتلین

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

جاوا

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

Use AccountManager

Android Automotive OS apps that have authentication must use AccountManager for the following reasons:

  • Better UX and ease of account management : Users can easily manage all their accounts from the accounts menu in the system settings, including sign-in and sign-out.
  • "Guest" experiences : Because cars are shared devices, OEMs can enable guest experiences in the vehicle, where accounts cannot be added.

Add text string variants

Different car screen sizes may show different amounts of text. With Car App API level 2 and above, you can specify multiple variants of a text string to best fit the screen. To see where text variants are accepted, look for templates and components that take a CarText .

You can add text string variants to a CarText with the CarText.Builder.addVariant() method:

کاتلین

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

جاوا

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

You can then use this CarText —for example, as the primary text of a GridItem .

کاتلین

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

جاوا

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

Add strings in order from most to least preferred—for example, from longest to shortest. The host picks the appropriate-length string depending on the amount of space available on the car screen.

Add inline CarIcons for rows

You can add icons inline with text to enrich your app's visual appeal using CarIconSpan . See the documentation for CarIconSpan.create for more information on creating these spans. See Spantastic text styling with Spans for an overview of how text styling with spans work.

کاتلین

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

جاوا

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

Car Hardware APIs

Starting with Car App API level 3, the Car App Library has APIs that you can use to access vehicle properties and sensors.

الزامات

To use the APIs with Android Auto, start by adding a dependency on androidx.car.app:app-projected to the build.gradle file for your Android Auto module. For Android Automotive OS, add a dependency on androidx.car.app:app-automotive to the build.gradle file for your Android Automotive OS module.

Additionally, in your AndroidManifest.xml file, you need to declare the relevant permissions needed to request the car data you want to use. Note that these permissions must also be granted to you by the user. You can use the same code on both Android Auto and Android Automotive OS, rather than having to create platform-dependent flows. However, the permissions needed are different.

CarInfo

This table describes the properties surfaced by the CarInfo APIs and the permissions you need to request to use them:

روش ها خواص Android Auto Permissions Android Automotive OS Permissions Supported since Car App API level
fetchModel Make, model, year android.car.permission.CAR_INFO 3
fetchEnergyProfile EV connector types, fuel types com.google.android.gms.permission.CAR_FUEL android.car.permission.CAR_INFO 3
fetchExteriorDimensions

This data is only available on some Android Automotive OS vehicles running API 30 or higher

ابعاد بیرونی N/A android.car.permission.CAR_INFO 7
addTollListener
removeTollListener
Toll card state, toll card type 3
addEnergyLevelListener
removeEnergyLevelListener
Battery level, fuel level, fuel level low, range remaining 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
Raw speed, display speed (shown on car's cluster display) com.google.android.gms.permission.CAR_SPEED android.car.permission.CAR_SPEED ,
android.car.permission.READ_CAR_DISPLAY_UNITS
3
addMileageListener
removeMileageListener

Warning: the getOdometerMeters method of the Mileage class is inaccurately named and returns kilometers, not meters.

Odometer distance com.google.android.gms.permission.CAR_MILEAGE This data is not available on Android Automotive OS to apps installed from the Play Store. 3

For example, to get the remaining range, instantiate a CarInfo object, then create and register an OnCarDataAvailableListener :

کاتلین

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)

جاوا

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

Don't assume that the data from the car is available at all times. If you get an error, check the status of the value you requested to better understand why the data you requested could not be retrieved. Refer to the reference documentation for the full CarInfo class definition.

CarSensors

The CarSensors class gives you access to the vehicle's accelerometer, gyroscope, compass, and location data. The availability of these values may depend on the OEM. The format for the data from the accelerometer, gyroscope, and compass is the same as you would get from the SensorManager API . For example, to check the vehicle's heading:

کاتلین

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)

جاوا

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

To access location data from the car, you also need to declare and request the android.permission.ACCESS_FINE_LOCATION permission.

تست کردن

To simulate sensor data when testing on Android Auto, refer to the Sensors and Sensor configuration sections of the Desktop Head Unit guide. To simulate sensor data when testing on Android Automotive OS, refer to the Emulate hardware state section of the Android Automotive OS emulator guide.

The CarAppService, Session and Screen lifecycles

The Session and Screen classes implement the LifecycleOwner interface. As the user interacts with the app, your Session and Screen objects' lifecycle callbacks are invoked, as described in the following diagrams.

The lifecycles of a CarAppService and a Session

شکل 1 . The Session lifecycle.

For full details, see the documentation for the Session.getLifecycle method.

The lifecycle of a Screen

شکل 2 . The Screen lifecycle.

For full details, see the documentation for the Screen.getLifecycle method.

Record from the car microphone

Using your app's CarAppService and the CarAudioRecord API, you can give your app access to the user's car microphone. Users need to give your app permission to access the car microphone. Your app can record and process the user's input within your app.

Permission to record

Before recording any audio, you must first declare the permission to record in your AndroidManifest.xml and request that the user grant it.

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

You need to request the permission to record at runtime. See the Request permissions section for details on how to request a permission in your car app.

ضبط صدا

After the user gives permission to record, you can record the audio and process the recording.

کاتلین

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

جاوا

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

Audio focus

When recording from the car microphone, first acquire audio focus to ensure that any ongoing media is stopped. If you lose audio focus, stop recording.

Here is an example of how to acquire audio focus:

کاتلین

 
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

جاوا

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
 

Testing Library

The Android for Cars Testing Library provides auxiliary classes that you can use to validate your app's behavior in a test environment. For example, the SessionController lets you simulate a connection to the host and verify that the correct Screen and Template are created and returned.

Refer to the Samples for usage examples.

Report an Android for Cars App Library issue

If you find an issue with the library, report it using the Google Issue Tracker . Be sure to fill out all the requested information in the issue template.

Create a new issue

Before filing a new issue, please check whether it is listed in the library's release notes or reported in the issues list. You can subscribe and vote for issues by clicking the star for an issue in the tracker. For more information, see Subscribing to an Issue .