Android 11 और इसके बाद के वर्शन में, डिवाइसों को तुरंत ऐक्सेस करने की सुविधा मिलती है. इसकी मदद से उपयोगकर्ता, लाइट, थर्मोस्टैट, और कैमरे जैसे बाहरी डिवाइसों को तुरंत देख और कंट्रोल कर सकते हैं. इसके लिए, उन्हें डिफ़ॉल्ट लॉन्चर से तीन बार इंटरैक्ट करना होगा. डिवाइस बनाने वाली कंपनी यह तय करती है कि किस लॉन्चर का इस्तेमाल करना है. डिवाइस एग्रीगेटर—उदाहरण के लिए, Google Home—और तीसरे पक्ष के वेंडर ऐप्लिकेशन, इस स्पेस में डिसप्ले के लिए डिवाइस उपलब्ध करा सकते हैं. इस पेज पर बताया गया है कि इस स्पेस में डिवाइस कंट्रोल कैसे दिखाए जाएं और उन्हें कंट्रोल करने वाले ऐप्लिकेशन से कैसे लिंक किया जाए.
यह सुविधा जोड़ने के लिए, ControlsProviderService बनाएं और उसका एलान करें. पहले से तय किए गए कंट्रोल टाइप के आधार पर, अपने ऐप्लिकेशन के लिए कंट्रोल बनाएं. इसके बाद, इन कंट्रोल के लिए पब्लिशर बनाएं.
बेहतर यूज़र इंटरफ़ेस
डिवाइस, डिवाइस कंट्रोल में टेंप्लेट वाले विजेट के तौर पर दिखते हैं. डिवाइस कंट्रोल करने के लिए पांच विजेट उपलब्ध हैं. इन्हें इस इमेज में दिखाया गया है:
|
|
|
|
|
किसी विजेट को दबाकर रखने से, आपको ऐप्लिकेशन के ज़्यादा कंट्रोल मिलते हैं. हर विजेट पर आइकॉन और रंग को पसंद के मुताबिक बनाया जा सकता है. हालांकि, उपयोगकर्ता को बेहतर अनुभव देने के लिए, डिफ़ॉल्ट आइकॉन और रंग का इस्तेमाल करें. ऐसा तब करें, जब डिफ़ॉल्ट सेट डिवाइस से मेल खाता हो.
सेवा बनाना
इस सेक्शन में, ControlsProviderService बनाने का तरीका बताया गया है. यह सेवा, Android सिस्टम यूज़र इंटरफ़ेस (यूआई) को बताती है कि आपके ऐप्लिकेशन में डिवाइस कंट्रोल मौजूद हैं. इन्हें Android यूज़र इंटरफ़ेस (यूआई) के डिवाइस कंट्रोल सेक्शन में दिखाया जाना चाहिए.
ControlsProviderService एपीआई में यह माना गया है कि आपको रिएक्टिव स्ट्रीम के बारे में जानकारी है. इसके बारे में Reactive Streams GitHub प्रोजेक्ट में बताया गया है. साथ ही, इसे Java 9 Flow इंटरफ़ेस में लागू किया गया है.
एपीआई, इन कॉन्सेप्ट पर आधारित है:
- पब्लिशर: आपका ऐप्लिकेशन पब्लिशर है.
- सदस्य: सिस्टम यूज़र इंटरफ़ेस (यूआई) सदस्य होता है. यह पब्लिशर से कई कंट्रोल का अनुरोध कर सकता है.
- सदस्यता: वह समयावधि जिसके दौरान पब्लिशर, सिस्टम यूज़र इंटरफ़ेस (यूआई) को अपडेट भेज सकता है. पब्लिशर या सदस्य, दोनों में से कोई भी इस विंडो को बंद कर सकता है.
सेवा के बारे में जानकारी देना
आपके ऐप्लिकेशन को अपने मेनिफ़ेस्ट में, MyCustomControlService जैसी किसी सेवा के बारे में बताना होगा.
सेवा में 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 नाम की एक नई Kotlin फ़ाइल बनाएं और इसे ControlsProviderService के तौर पर एक्सटेंड करें:
class MyCustomControlService : ControlsProviderService() { // ... }
कंट्रोल का सही टाइप चुनना
एपीआई, कंट्रोल बनाने के लिए बिल्डर के तरीके उपलब्ध कराता है. बिल्डर को भरने के लिए, यह तय करें कि आपको किस डिवाइस को कंट्रोल करना है और उपयोगकर्ता उससे कैसे इंटरैक्ट करेगा. यह तरीका अपनाएं:
- उस डिवाइस का टाइप चुनें जिसे कंट्रोल करता है.
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ऑब्जेक्ट बनाने के लिए,Control.StatelessBuilderका इस्तेमाल करें.createPublisherFor: यह दिए गए कंट्रोल की सूची के लिएPublisherबनाता है. कंट्रोल की पहचान उनके स्ट्रिंग आइडेंटिफ़ायर से की जाती है. इनControlऑब्जेक्ट को बनाने के लिए,Control.StatefulBuilderका इस्तेमाल करें. ऐसा इसलिए, क्योंकि पब्लिशर को हर कंट्रोल के लिए एक स्थिति असाइन करनी होती है.
पब्लिशर बनाना
जब आपका ऐप्लिकेशन पहली बार सिस्टम यूज़र इंटरफ़ेस (यूआई) पर कंट्रोल पब्लिश करता है, तो ऐप्लिकेशन को हर कंट्रोल की स्थिति के बारे में पता नहीं होता. डिवाइस की स्थिति पता करने में समय लग सकता है. ऐसा इसलिए, क्योंकि डिवाइस-प्रोवाइडर के नेटवर्क में कई हॉप शामिल होते हैं. सिस्टम को उपलब्ध कंट्रोल के बारे में बताने के लिए, createPublisherForAllAvailable तरीके का इस्तेमाल करें. इस तरीके में Control.StatelessBuilder बिल्डर क्लास का इस्तेमाल किया जाता है, क्योंकि हर कंट्रोल की स्थिति के बारे में जानकारी नहीं होती.
Android के यूज़र इंटरफ़ेस (यूआई) में कंट्रोल दिखने के बाद, उपयोगकर्ता पसंदीदा कंट्रोल चुन सकते हैं.
ControlsProviderService बनाने के लिए Kotlin कोरूटीन का इस्तेमाल करने के लिए, अपनी build.gradle में नई डिपेंडेंसी जोड़ें:
Groovy
dependencies { implementation "org.jetbrains.kotlinx:kotlinx-coroutines-jdk9:1.6.4" }
Kotlin
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 तरीके में, Kotlin coroutines और फ़्लो का इस्तेमाल किया जाता है. इससे Reactive Streams API की ज़रूरी शर्तें पूरी की जा सकती हैं. इसके लिए, यह तरीका अपनाया जाता है:
Flowबनाता है.- एक सेकंड इंतज़ार करता है.
- यह कुकी, स्मार्ट लाइट की स्थिति को बनाती है और उसे चालू करती है.
- एक और सेकंड इंतज़ार करता है.
- यह कुकी, थर्मोस्टैट की स्थिति को बनाती और भेजती है.
कार्रवाइयों को मैनेज करना
performControlAction तरीका, यह सिग्नल देता है कि उपयोगकर्ता ने पब्लिश किए गए कंट्रोल के साथ इंटरैक्ट किया है. भेजे गए ControlAction के टाइप के आधार पर कार्रवाई तय होती है.
दिए गए कंट्रोल के लिए ज़रूरी कार्रवाई करें. इसके बाद, Android यूज़र इंटरफ़ेस (यूआई) में डिवाइस की स्थिति अपडेट करें.
उदाहरण को पूरा करने के लिए, 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) }
ऐप्लिकेशन खोलें और डिवाइस कंट्रोल मेन्यू ऐक्सेस करें. इसके बाद, लाइट और थर्मोस्टैट कंट्रोल देखें.