در اندروید ۱۱ و بالاتر، ویژگی کنترلهای دستگاه با دسترسی سریع (Quick Access Device Controls) به کاربران امکان میدهد تا به سرعت دستگاههای خارجی مانند چراغها، ترموستاتها و دوربینها را از طریق یک لانچر پیشفرض و تنها با سه تعامل مشاهده و کنترل کنند. تولیدکننده اصلی دستگاه انتخاب میکند که از کدام لانچر استفاده شود. تجمیعکنندههای دستگاه - به عنوان مثال، Google Home - و برنامههای فروشندگان شخص ثالث میتوانند دستگاههایی را برای نمایش در این فضا ارائه دهند. این صفحه به شما نشان میدهد که چگونه کنترلهای دستگاه را در این فضا نمایش دهید و آنها را به برنامه کنترل خود پیوند دهید.
برای افزودن این پشتیبانی، یک ControlsProviderService ایجاد و اعلان کنید. کنترلهایی را که برنامه شما پشتیبانی میکند بر اساس انواع کنترلهای از پیش تعریف شده ایجاد کنید و سپس ناشرانی برای این کنترلها ایجاد کنید.
رابط کاربری
دستگاهها در بخش کنترلهای دستگاه به صورت ویجتهای قالببندیشده نمایش داده میشوند. پنج ویجت کنترل دستگاه در دسترس هستند، همانطور که در شکل زیر نشان داده شده است:
![]() | ![]() | ![]() |
![]() | ![]() |
لمس و نگه داشتن یک ویجت شما را برای کنترل عمیقتر به برنامه میبرد. میتوانید آیکون و رنگ هر ویجت را سفارشی کنید، اما برای بهترین تجربه کاربری، اگر تنظیمات پیشفرض با دستگاه شما مطابقت دارد، از آیکون و رنگ پیشفرض استفاده کنید.

سرویس را ایجاد کنید
این بخش نحوه ایجاد ControlsProviderService را نشان میدهد. این سرویس به رابط کاربری سیستم اندروید میگوید که برنامه شما شامل کنترلهای دستگاه است که باید در ناحیه کنترلهای دستگاه رابط کاربری اندروید نمایش داده شوند.
API ControlsProviderService آشنایی با جریانهای واکنشی (reactive streams) را که در پروژه گیتهاب Reactive Streams تعریف و در رابطهای Flow جاوا ۹ پیادهسازی شدهاند، فرض میکند. این API بر اساس مفاهیم زیر بنا شده است:
- ناشر: برنامه شما ناشر است.
- مشترک: رابط کاربری سیستم، مشترک است و میتواند تعدادی کنترل از ناشر درخواست کند.
- اشتراک: بازه زمانی که ناشر میتواند بهروزرسانیها را به رابط کاربری سیستم ارسال کند. ناشر یا مشترک میتوانند این پنجره را ببندند.
اعلام سرویس
برنامه شما باید یک سرویس - مانند MyCustomControlService - را در مانیفست برنامه خود اعلام کند.
سرویس باید شامل یک فیلتر intent برای ControlsProviderService باشد. این فیلتر به برنامهها اجازه میدهد تا کنترلهایی را به رابط کاربری سیستم اضافه کنند.
شما همچنین به یک label نیاز دارید که در کنترلهای رابط کاربری سیستم نمایش داده شود.
مثال زیر نحوه تعریف یک سرویس را نشان میدهد:
<service
android:name="MyCustomControlService"
android:label="My Custom Controls"
android:permission="android.permission.BIND_CONTROLS"
android:exported="true"
>
<intent-filter>
<action android:name="android.service.controls.ControlsProviderService" />
</intent-filter>
</service>
در مرحله بعد، یک فایل کاتلین جدید با نام MyCustomControlService.kt ایجاد کنید و آن را ControlsProviderService ارثبری کنید:
class MyCustomControlService : ControlsProviderService() { // ... }
نوع کنترل صحیح را انتخاب کنید
API متدهای سازنده را برای ایجاد کنترلها ارائه میدهد. برای پر کردن سازنده، دستگاهی را که میخواهید کنترل کنید و نحوه تعامل کاربر با آن را تعیین کنید. مراحل زیر را انجام دهید:
- نوع دستگاهی که کنترل نشان میدهد را انتخاب کنید. کلاس
DeviceTypesیک شمارش از تمام دستگاههای پشتیبانی شده است. این نوع برای تعیین آیکونها و رنگهای دستگاه در رابط کاربری استفاده میشود. - نام کاربری، مکان دستگاه - مثلاً آشپزخانه - و سایر عناصر متنی رابط کاربری مرتبط با کنترل را تعیین کنید.
- بهترین الگو را برای پشتیبانی از تعامل کاربر انتخاب کنید. کنترلها از برنامه یک
ControlTemplateاختصاص داده میشوند. این الگو مستقیماً وضعیت کنترل و همچنین روشهای ورودی موجود - یعنیControlAction- را به کاربر نشان میدهد. جدول زیر برخی از الگوهای موجود و اقداماتی که پشتیبانی میکنند را شرح میدهد:
| الگو | اکشن | توضیحات |
ControlTemplate.getNoTemplateObject() | None | برنامه ممکن است از این برای انتقال اطلاعات در مورد کنترل استفاده کند، اما کاربر نمیتواند با آن تعامل داشته باشد. |
ToggleTemplate | BooleanAction | کنترلی را نشان میدهد که میتواند بین حالتهای فعال و غیرفعال تغییر کند. شیء BooleanAction حاوی فیلدی است که وقتی کاربر روی کنترل ضربه میزند، برای نشان دادن حالت جدید درخواستی تغییر میکند. |
RangeTemplate | FloatAction | یک ویجت اسلایدر با مقادیر حداقل، حداکثر و گام مشخص شده را نشان میدهد. هنگامی که کاربر با اسلایدر تعامل میکند، یک شیء FloatAction جدید با مقدار بهروزرسانی شده به برنامه ارسال میکند. |
ToggleRangeTemplate | BooleanAction , FloatAction | این الگو ترکیبی از ToggleTemplate و RangeTemplate است. این الگو از رویدادهای لمسی و همچنین یک اسلایدر، مانند کنترل چراغهای کمنور، پشتیبانی میکند. |
TemperatureControlTemplate | ModeAction , BooleanAction , FloatAction | این الگو علاوه بر کپسولهسازی اقدامات قبلی، به کاربر اجازه میدهد حالتی مانند گرما، سرما، گرما/خنک، سازگار با محیط زیست یا خاموش را تنظیم کند. |
StatelessTemplate | CommandAction | برای نشان دادن کنترلی استفاده میشود که قابلیت لمس را فراهم میکند اما وضعیت آن قابل تعیین نیست، مانند کنترل تلویزیون مادون قرمز. میتوانید از این الگو برای تعریف یک روال یا ماکرو استفاده کنید که مجموعهای از تغییرات کنترل و وضعیت است. |
با استفاده از این اطلاعات، میتوانید کنترل را ایجاد کنید:
- وقتی وضعیت کنترل ناشناخته است، از کلاس سازنده
Control.StatelessBuilderاستفاده کنید. - وقتی وضعیت کنترل مشخص است، از کلاس سازنده
Control.StatefulBuilderاستفاده کنید.
برای مثال، برای کنترل یک لامپ هوشمند و یک ترموستات، ثابتهای زیر را به MyCustomControlService خود اضافه کنید:
private const val LIGHT_ID = 1234 private const val LIGHT_TITLE = "My fancy light" private const val LIGHT_TYPE = DeviceTypes.TYPE_LIGHT private const val THERMOSTAT_ID = 5678 private const val THERMOSTAT_TITLE = "My fancy thermostat" private const val THERMOSTAT_TYPE = DeviceTypes.TYPE_THERMOSTAT class MyCustomControlService : ControlsProviderService() { // ... }
ایجاد ناشران برای کنترلها
پس از ایجاد کنترل، به یک ناشر نیاز دارد. ناشر، رابط کاربری سیستم را از وجود کنترل مطلع میکند. کلاس ControlsProviderService دارای دو متد ناشر است که باید در کد برنامه خود آنها را بازنویسی کنید:
-
createPublisherForAllAvailable: یکPublisherبرای تمام کنترلهای موجود در برنامه شما ایجاد میکند.Control.StatelessBuilderبرای ساخت اشیاءControlبرای این ناشر استفاده کنید. -
createPublisherFor: یکPublisherبرای لیستی از کنترلهای داده شده، همانطور که توسط شناسههای رشتهای آنها مشخص شدهاند، ایجاد میکند. برای ساخت این اشیاءControlControl.StatefulBuilderاستفاده کنید، زیرا ناشر باید به هر کنترل یک وضعیت (state) اختصاص دهد.
ناشر را ایجاد کنید
وقتی برنامه شما برای اولین بار کنترلها را در رابط کاربری سیستم منتشر میکند، برنامه از وضعیت هر کنترل اطلاعی ندارد. دریافت وضعیت میتواند یک عملیات زمانبر باشد که شامل مراحل زیادی در شبکه ارائهدهنده دستگاه میشود. از متد createPublisherForAllAvailable برای اعلام کنترلهای موجود به سیستم استفاده کنید. این متد از کلاس سازنده Control.StatelessBuilder استفاده میکند، زیرا وضعیت هر کنترل ناشناخته است.
وقتی کنترلها در رابط کاربری اندروید ظاهر شدند، کاربران میتوانند کنترلهای مورد علاقه خود را انتخاب کنند.
برای استفاده از کوروتینهای کاتلین برای ایجاد یک ControlsProviderService ، یک وابستگی جدید به build.gradle خود اضافه کنید:
گرووی
dependencies { implementation "org.jetbrains.kotlinx:kotlinx-coroutines-jdk9:1.6.4" }
کاتلین
dependencies { implementation("org.jetbrains.kotlinx:kotlinx-coroutines-jdk9:1.6.4") }
پس از همگامسازی فایلهای Gradle، قطعه کد زیر را به Service خود اضافه کنید تا createPublisherForAllAvailable پیادهسازی شود:
class MyCustomControlService : ControlsProviderService() { override fun createPublisherForAllAvailable(): Flow.Publisher<Control> = flowPublish { send(createStatelessControl(LIGHT_ID, LIGHT_TITLE, LIGHT_TYPE)) send(createStatelessControl(THERMOSTAT_ID, THERMOSTAT_TITLE, THERMOSTAT_TYPE)) } private fun createStatelessControl(id: Int, title: String, type: Int): Control { val intent = Intent(this, MainActivity::class.java) .putExtra(EXTRA_MESSAGE, title) .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) val action = PendingIntent.getActivity( this, id, intent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE, ) return Control.StatelessBuilder(id.toString(), action) .setTitle(title) .setDeviceType(type) .build() } override fun createPublisherFor(controlIds: List<String>): Flow.Publisher<Control> { TODO() } override fun performControlAction( controlId: String, action: ControlAction, consumer: Consumer<Int>, ) { TODO() } }
منوی سیستم را به پایین بکشید و دکمه کنترل دستگاه را که در شکل 4 نشان داده شده است، پیدا کنید:

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

حالا، متد createPublisherFor پیادهسازی کنید و موارد زیر را به Service خود اضافه کنید:
private val job = SupervisorJob() private val scope = CoroutineScope(Dispatchers.IO + job) private val controlFlows = mutableMapOf<String, MutableSharedFlow<Control>>() private var toggleState = false private var rangeState = 18f override fun createPublisherFor(controlIds: List<String>): Flow.Publisher<Control> { val flow = MutableSharedFlow<Control>(replay = 2, extraBufferCapacity = 2) controlIds.forEach { controlFlows[it] = flow } scope.launch { delay(1000) // Retrieving the toggle state. flow.tryEmit(createLight()) delay(1000) // Retrieving the range state. flow.tryEmit(createThermostat()) } return flow.asPublisher() } private fun createLight() = createStatefulControl( LIGHT_ID, LIGHT_TITLE, LIGHT_TYPE, toggleState, ToggleTemplate( LIGHT_ID.toString(), ControlButton( toggleState, toggleState.toString().uppercase(Locale.getDefault()), ), ), ) private fun createThermostat() = createStatefulControl( THERMOSTAT_ID, THERMOSTAT_TITLE, THERMOSTAT_TYPE, rangeState, RangeTemplate( THERMOSTAT_ID.toString(), 15f, 25f, rangeState, 0.1f, "%1.1f", ), ) private fun <T> createStatefulControl( id: Int, title: String, type: Int, state: T, template: ControlTemplate, ): Control { val intent = Intent(this, MainActivity::class.java) .putExtra(EXTRA_MESSAGE, "$title $state") .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) val action = PendingIntent.getActivity( this, id, intent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE, ) return Control.StatefulBuilder(id.toString(), action) .setTitle(title) .setDeviceType(type) .setStatus(Control.STATUS_OK) .setControlTemplate(template) .build() } override fun onDestroy() { super.onDestroy() job.cancel() }
در این مثال، متد createPublisherFor شامل یک پیادهسازی جعلی از کاری است که برنامه شما باید انجام دهد: برقراری ارتباط با دستگاه شما برای دریافت وضعیت آن و انتشار آن وضعیت به سیستم.
متد createPublisherFor از کوروتینها و جریانهای کاتلین برای برآورده کردن API مورد نیاز Reactive Streams با انجام موارد زیر استفاده میکند:
-
Flowایجاد میکند. - یک ثانیه صبر میکند.
- حالت نور هوشمند را ایجاد و منتشر میکند.
- یک ثانیه دیگر صبر میکند.
- وضعیت ترموستات را ایجاد و منتشر میکند.
رسیدگی به اقدامات
متد performControlAction زمانی که کاربر با یک کنترل منتشر شده تعامل میکند، سیگنال میدهد. نوع ControlAction ارسالی، اقدام مورد نظر را تعیین میکند. اقدام مناسب را برای کنترل داده شده انجام دهید و سپس وضعیت دستگاه را در رابط کاربری اندروید بهروزرسانی کنید.
برای تکمیل مثال، موارد زیر را به Service خود اضافه کنید:
override fun performControlAction( controlId: String, action: ControlAction, consumer: Consumer<Int>, ) { controlFlows[controlId]?.let { flow -> when (controlId) { LIGHT_ID.toString() -> { consumer.accept(ControlAction.RESPONSE_OK) if (action is BooleanAction) toggleState = action.newState flow.tryEmit(createLight()) } THERMOSTAT_ID.toString() -> { consumer.accept(ControlAction.RESPONSE_OK) if (action is FloatAction) rangeState = action.newValue flow.tryEmit(createThermostat()) } else -> consumer.accept(ControlAction.RESPONSE_FAIL) } } ?: consumer.accept(ControlAction.RESPONSE_FAIL) }
برنامه را اجرا کنید، به منوی کنترلهای دستگاه دسترسی پیدا کنید و کنترلهای نور و ترموستات خود را مشاهده کنید.

در اندروید ۱۱ و بالاتر، ویژگی کنترلهای دستگاه با دسترسی سریع (Quick Access Device Controls) به کاربران امکان میدهد تا به سرعت دستگاههای خارجی مانند چراغها، ترموستاتها و دوربینها را از طریق یک لانچر پیشفرض و تنها با سه تعامل مشاهده و کنترل کنند. تولیدکننده اصلی دستگاه انتخاب میکند که از کدام لانچر استفاده شود. تجمیعکنندههای دستگاه - به عنوان مثال، Google Home - و برنامههای فروشندگان شخص ثالث میتوانند دستگاههایی را برای نمایش در این فضا ارائه دهند. این صفحه به شما نشان میدهد که چگونه کنترلهای دستگاه را در این فضا نمایش دهید و آنها را به برنامه کنترل خود پیوند دهید.
برای افزودن این پشتیبانی، یک ControlsProviderService ایجاد و اعلان کنید. کنترلهایی را که برنامه شما پشتیبانی میکند بر اساس انواع کنترلهای از پیش تعریف شده ایجاد کنید و سپس ناشرانی برای این کنترلها ایجاد کنید.
رابط کاربری
دستگاهها در بخش کنترلهای دستگاه به صورت ویجتهای قالببندیشده نمایش داده میشوند. پنج ویجت کنترل دستگاه در دسترس هستند، همانطور که در شکل زیر نشان داده شده است:
![]() | ![]() | ![]() |
![]() | ![]() |
لمس و نگه داشتن یک ویجت شما را برای کنترل عمیقتر به برنامه میبرد. میتوانید آیکون و رنگ هر ویجت را سفارشی کنید، اما برای بهترین تجربه کاربری، اگر تنظیمات پیشفرض با دستگاه شما مطابقت دارد، از آیکون و رنگ پیشفرض استفاده کنید.

سرویس را ایجاد کنید
این بخش نحوه ایجاد ControlsProviderService را نشان میدهد. این سرویس به رابط کاربری سیستم اندروید میگوید که برنامه شما شامل کنترلهای دستگاه است که باید در ناحیه کنترلهای دستگاه رابط کاربری اندروید نمایش داده شوند.
API ControlsProviderService آشنایی با جریانهای واکنشی (reactive streams) را که در پروژه گیتهاب Reactive Streams تعریف و در رابطهای Flow جاوا ۹ پیادهسازی شدهاند، فرض میکند. این API بر اساس مفاهیم زیر بنا شده است:
- ناشر: برنامه شما ناشر است.
- مشترک: رابط کاربری سیستم، مشترک است و میتواند تعدادی کنترل از ناشر درخواست کند.
- اشتراک: بازه زمانی که ناشر میتواند بهروزرسانیها را به رابط کاربری سیستم ارسال کند. ناشر یا مشترک میتوانند این پنجره را ببندند.
اعلام سرویس
برنامه شما باید یک سرویس - مانند MyCustomControlService - را در مانیفست برنامه خود اعلام کند.
سرویس باید شامل یک فیلتر intent برای ControlsProviderService باشد. این فیلتر به برنامهها اجازه میدهد تا کنترلهایی را به رابط کاربری سیستم اضافه کنند.
شما همچنین به یک label نیاز دارید که در کنترلهای رابط کاربری سیستم نمایش داده شود.
مثال زیر نحوه تعریف یک سرویس را نشان میدهد:
<service
android:name="MyCustomControlService"
android:label="My Custom Controls"
android:permission="android.permission.BIND_CONTROLS"
android:exported="true"
>
<intent-filter>
<action android:name="android.service.controls.ControlsProviderService" />
</intent-filter>
</service>
در مرحله بعد، یک فایل کاتلین جدید با نام MyCustomControlService.kt ایجاد کنید و آن را ControlsProviderService ارثبری کنید:
class MyCustomControlService : ControlsProviderService() { // ... }
نوع کنترل صحیح را انتخاب کنید
API متدهای سازنده را برای ایجاد کنترلها ارائه میدهد. برای پر کردن سازنده، دستگاهی را که میخواهید کنترل کنید و نحوه تعامل کاربر با آن را تعیین کنید. مراحل زیر را انجام دهید:
- نوع دستگاهی که کنترل نشان میدهد را انتخاب کنید. کلاس
DeviceTypesیک شمارش از تمام دستگاههای پشتیبانی شده است. این نوع برای تعیین آیکونها و رنگهای دستگاه در رابط کاربری استفاده میشود. - نام کاربری، مکان دستگاه - مثلاً آشپزخانه - و سایر عناصر متنی رابط کاربری مرتبط با کنترل را تعیین کنید.
- بهترین الگو را برای پشتیبانی از تعامل کاربر انتخاب کنید. کنترلها از برنامه یک
ControlTemplateاختصاص داده میشوند. این الگو مستقیماً وضعیت کنترل و همچنین روشهای ورودی موجود - یعنیControlAction- را به کاربر نشان میدهد. جدول زیر برخی از الگوهای موجود و اقداماتی که پشتیبانی میکنند را شرح میدهد:
| الگو | اکشن | توضیحات |
ControlTemplate.getNoTemplateObject() | None | برنامه ممکن است از این برای انتقال اطلاعات در مورد کنترل استفاده کند، اما کاربر نمیتواند با آن تعامل داشته باشد. |
ToggleTemplate | BooleanAction | کنترلی را نشان میدهد که میتواند بین حالتهای فعال و غیرفعال تغییر کند. شیء BooleanAction حاوی فیلدی است که وقتی کاربر روی کنترل ضربه میزند، برای نشان دادن حالت جدید درخواستی تغییر میکند. |
RangeTemplate | FloatAction | یک ویجت اسلایدر با مقادیر حداقل، حداکثر و گام مشخص شده را نشان میدهد. هنگامی که کاربر با اسلایدر تعامل میکند، یک شیء FloatAction جدید با مقدار بهروزرسانی شده به برنامه ارسال میکند. |
ToggleRangeTemplate | BooleanAction , FloatAction | این الگو ترکیبی از ToggleTemplate و RangeTemplate است. این الگو از رویدادهای لمسی و همچنین یک اسلایدر، مانند کنترل چراغهای کمنور، پشتیبانی میکند. |
TemperatureControlTemplate | ModeAction , BooleanAction , FloatAction | این الگو علاوه بر کپسولهسازی اقدامات قبلی، به کاربر اجازه میدهد حالتی مانند گرما، سرما، گرما/خنک، سازگار با محیط زیست یا خاموش را تنظیم کند. |
StatelessTemplate | CommandAction | برای نشان دادن کنترلی استفاده میشود که قابلیت لمس را فراهم میکند اما وضعیت آن قابل تعیین نیست، مانند کنترل تلویزیون مادون قرمز. میتوانید از این الگو برای تعریف یک روال یا ماکرو استفاده کنید که مجموعهای از تغییرات کنترل و وضعیت است. |
با استفاده از این اطلاعات، میتوانید کنترل را ایجاد کنید:
- وقتی وضعیت کنترل ناشناخته است، از کلاس سازنده
Control.StatelessBuilderاستفاده کنید. - وقتی وضعیت کنترل مشخص است، از کلاس سازنده
Control.StatefulBuilderاستفاده کنید.
برای مثال، برای کنترل یک لامپ هوشمند و یک ترموستات، ثابتهای زیر را به MyCustomControlService خود اضافه کنید:
private const val LIGHT_ID = 1234 private const val LIGHT_TITLE = "My fancy light" private const val LIGHT_TYPE = DeviceTypes.TYPE_LIGHT private const val THERMOSTAT_ID = 5678 private const val THERMOSTAT_TITLE = "My fancy thermostat" private const val THERMOSTAT_TYPE = DeviceTypes.TYPE_THERMOSTAT class MyCustomControlService : ControlsProviderService() { // ... }
ایجاد ناشران برای کنترلها
پس از ایجاد کنترل، به یک ناشر نیاز دارد. ناشر، رابط کاربری سیستم را از وجود کنترل مطلع میکند. کلاس ControlsProviderService دارای دو متد ناشر است که باید در کد برنامه خود آنها را بازنویسی کنید:
-
createPublisherForAllAvailable: یکPublisherبرای تمام کنترلهای موجود در برنامه شما ایجاد میکند.Control.StatelessBuilderبرای ساخت اشیاءControlبرای این ناشر استفاده کنید. -
createPublisherFor: یکPublisherبرای لیستی از کنترلهای داده شده، همانطور که توسط شناسههای رشتهای آنها مشخص شدهاند، ایجاد میکند. برای ساخت این اشیاءControlControl.StatefulBuilderاستفاده کنید، زیرا ناشر باید به هر کنترل یک وضعیت (state) اختصاص دهد.
ناشر را ایجاد کنید
وقتی برنامه شما برای اولین بار کنترلها را در رابط کاربری سیستم منتشر میکند، برنامه از وضعیت هر کنترل اطلاعی ندارد. دریافت وضعیت میتواند یک عملیات زمانبر باشد که شامل مراحل زیادی در شبکه ارائهدهنده دستگاه میشود. از متد createPublisherForAllAvailable برای اعلام کنترلهای موجود به سیستم استفاده کنید. این متد از کلاس سازنده Control.StatelessBuilder استفاده میکند، زیرا وضعیت هر کنترل ناشناخته است.
وقتی کنترلها در رابط کاربری اندروید ظاهر شدند، کاربران میتوانند کنترلهای مورد علاقه خود را انتخاب کنند.
برای استفاده از کوروتینهای کاتلین برای ایجاد یک ControlsProviderService ، یک وابستگی جدید به build.gradle خود اضافه کنید:
گرووی
dependencies { implementation "org.jetbrains.kotlinx:kotlinx-coroutines-jdk9:1.6.4" }
کاتلین
dependencies { implementation("org.jetbrains.kotlinx:kotlinx-coroutines-jdk9:1.6.4") }
پس از همگامسازی فایلهای Gradle، قطعه کد زیر را به Service خود اضافه کنید تا createPublisherForAllAvailable پیادهسازی شود:
class MyCustomControlService : ControlsProviderService() { override fun createPublisherForAllAvailable(): Flow.Publisher<Control> = flowPublish { send(createStatelessControl(LIGHT_ID, LIGHT_TITLE, LIGHT_TYPE)) send(createStatelessControl(THERMOSTAT_ID, THERMOSTAT_TITLE, THERMOSTAT_TYPE)) } private fun createStatelessControl(id: Int, title: String, type: Int): Control { val intent = Intent(this, MainActivity::class.java) .putExtra(EXTRA_MESSAGE, title) .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) val action = PendingIntent.getActivity( this, id, intent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE, ) return Control.StatelessBuilder(id.toString(), action) .setTitle(title) .setDeviceType(type) .build() } override fun createPublisherFor(controlIds: List<String>): Flow.Publisher<Control> { TODO() } override fun performControlAction( controlId: String, action: ControlAction, consumer: Consumer<Int>, ) { TODO() } }
منوی سیستم را به پایین بکشید و دکمه کنترل دستگاه را که در شکل 4 نشان داده شده است، پیدا کنید:

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

حالا، متد createPublisherFor پیادهسازی کنید و موارد زیر را به Service خود اضافه کنید:
private val job = SupervisorJob() private val scope = CoroutineScope(Dispatchers.IO + job) private val controlFlows = mutableMapOf<String, MutableSharedFlow<Control>>() private var toggleState = false private var rangeState = 18f override fun createPublisherFor(controlIds: List<String>): Flow.Publisher<Control> { val flow = MutableSharedFlow<Control>(replay = 2, extraBufferCapacity = 2) controlIds.forEach { controlFlows[it] = flow } scope.launch { delay(1000) // Retrieving the toggle state. flow.tryEmit(createLight()) delay(1000) // Retrieving the range state. flow.tryEmit(createThermostat()) } return flow.asPublisher() } private fun createLight() = createStatefulControl( LIGHT_ID, LIGHT_TITLE, LIGHT_TYPE, toggleState, ToggleTemplate( LIGHT_ID.toString(), ControlButton( toggleState, toggleState.toString().uppercase(Locale.getDefault()), ), ), ) private fun createThermostat() = createStatefulControl( THERMOSTAT_ID, THERMOSTAT_TITLE, THERMOSTAT_TYPE, rangeState, RangeTemplate( THERMOSTAT_ID.toString(), 15f, 25f, rangeState, 0.1f, "%1.1f", ), ) private fun <T> createStatefulControl( id: Int, title: String, type: Int, state: T, template: ControlTemplate, ): Control { val intent = Intent(this, MainActivity::class.java) .putExtra(EXTRA_MESSAGE, "$title $state") .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) val action = PendingIntent.getActivity( this, id, intent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE, ) return Control.StatefulBuilder(id.toString(), action) .setTitle(title) .setDeviceType(type) .setStatus(Control.STATUS_OK) .setControlTemplate(template) .build() } override fun onDestroy() { super.onDestroy() job.cancel() }
در این مثال، متد createPublisherFor شامل یک پیادهسازی جعلی از کاری است که برنامه شما باید انجام دهد: برقراری ارتباط با دستگاه شما برای دریافت وضعیت آن و انتشار آن وضعیت به سیستم.
متد createPublisherFor از کوروتینها و جریانهای کاتلین برای برآورده کردن API مورد نیاز Reactive Streams با انجام موارد زیر استفاده میکند:
-
Flowایجاد میکند. - یک ثانیه صبر میکند.
- حالت نور هوشمند را ایجاد و منتشر میکند.
- یک ثانیه دیگر صبر میکند.
- وضعیت ترموستات را ایجاد و منتشر میکند.
رسیدگی به اقدامات
متد performControlAction زمانی که کاربر با یک کنترل منتشر شده تعامل میکند، سیگنال میدهد. نوع ControlAction ارسالی، اقدام مورد نظر را تعیین میکند. اقدام مناسب را برای کنترل داده شده انجام دهید و سپس وضعیت دستگاه را در رابط کاربری اندروید بهروزرسانی کنید.
برای تکمیل مثال، موارد زیر را به Service خود اضافه کنید:
override fun performControlAction( controlId: String, action: ControlAction, consumer: Consumer<Int>, ) { controlFlows[controlId]?.let { flow -> when (controlId) { LIGHT_ID.toString() -> { consumer.accept(ControlAction.RESPONSE_OK) if (action is BooleanAction) toggleState = action.newState flow.tryEmit(createLight()) } THERMOSTAT_ID.toString() -> { consumer.accept(ControlAction.RESPONSE_OK) if (action is FloatAction) rangeState = action.newValue flow.tryEmit(createThermostat()) } else -> consumer.accept(ControlAction.RESPONSE_FAIL) } } ?: consumer.accept(ControlAction.RESPONSE_FAIL) }
برنامه را اجرا کنید، به منوی کنترلهای دستگاه دسترسی پیدا کنید و کنترلهای نور و ترموستات خود را مشاهده کنید.





