Android 11 और इसके बाद के वर्शन में, क्विक ऐक्सेस डिवाइस कंट्रोल की सुविधा की मदद से, उपयोगकर्ता लाइट, थर्मोस्टैट, और कैमरे जैसे बाहरी डिवाइसों को तुरंत देख और कंट्रोल कर सकते हैं. इसके लिए, उन्हें डिफ़ॉल्ट लॉन्चर से तीन इंटरैक्शन के अंदर उपयोगकर्ता के लिए उपलब्ध सुविधा का इस्तेमाल करना होगा. डिवाइस का ओईएम यह चुनता है कि किस लॉन्चर का इस्तेमाल करना है. डिवाइस एग्रीगेटर (जैसे, Google Home) और तीसरे पक्ष के वेंडर के ऐप्लिकेशन, इस स्पेस में दिखाने के लिए डिवाइस उपलब्ध करा सकते हैं. इस पेज पर, इस स्पेस में डिवाइस के कंट्रोल दिखाने और उन्हें कंट्रोल ऐप्लिकेशन से लिंक करने का तरीका बताया गया है.
यह सुविधा जोड़ने के लिए, ControlsProviderService बनाएं और उसे डिक्लेयर करें. पहले से तय किए गए कंट्रोल टाइप के आधार पर, वे कंट्रोल बनाएं जो आपके ऐप्लिकेशन के साथ काम करते हैं. इसके बाद, इन कंट्रोल के लिए पब्लिशर बनाएं.
बेहतर यूज़र इंटरफ़ेस
डिवाइसों को डिवाइस कंट्रोल में, टेंप्लेट वाले विजेट के तौर पर दिखाया जाता है. डिवाइस कंट्रोल करने वाले पांच विजेट उपलब्ध हैं, जैसा कि इस इमेज में दिखाया गया है:
|
|
|
|
|
किसी विजेट को दबाकर रखने से, आपको ऐप्लिकेशन पर ले जाया जाता है. इससे आपको ज़्यादा कंट्रोल मिलता है. हर विजेट पर आइकॉन और रंग को पसंद के मुताबिक बनाया जा सकता है. हालांकि, उपयोगकर्ता को बेहतर अनुभव देने के लिए, डिफ़ॉल्ट आइकॉन और रंग का इस्तेमाल करें. ऐसा तब करें, जब डिफ़ॉल्ट सेट, डिवाइस से मैच करता हो.
सेवा बनाएं
इस सेक्शन में, ControlsProviderService बनाने का तरीका बताया गया है. यह सेवा, Android सिस्टम के यूज़र इंटरफ़ेस (यूआई) को बताती है कि आपके ऐप्लिकेशन में डिवाइस कंट्रोल मौजूद हैं. इन्हें Android के यूज़र इंटरफ़ेस (यूआई) के डिवाइस कंट्रोल सेक्शन में दिखाना ज़रूरी है.
ControlsProviderService एपीआई के लिए, यह ज़रूरी है कि आपको रिएक्टिव स्ट्रीम के बारे में जानकारी हो. इसके बारे में रिएक्टिव स्ट्रीम 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.StatefulBuilderका इस्तेमाल करके, येControlऑब्जेक्ट बनाएं. ऐसा इसलिए, क्योंकि पब्लिशर को हर कंट्रोल के लिए एक स्थिति असाइन करनी होगी.
पब्लिशर बनाना
जब आपका ऐप्लिकेशन पहली बार सिस्टम यूज़र इंटरफ़ेस (यूआई) में कंट्रोल पब्लिश करता है, तो ऐप्लिकेशन को हर कंट्रोल की स्थिति के बारे में पता नहीं होता. स्टेटस की जानकारी पाने में समय लग सकता है. ऐसा इसलिए, क्योंकि डिवाइस की सेवा देने वाली कंपनी के नेटवर्क में कई हॉप शामिल होते हैं. सिस्टम को उपलब्ध कंट्रोल के बारे में बताने के लिए, createPublisherForAllAvailable तरीके का इस्तेमाल करें. यह तरीका, Control.StatelessBuilder बिल्डर क्लास का इस्तेमाल करता है, क्योंकि हर कंट्रोल की स्थिति के बारे में जानकारी नहीं होती.
Android के यूज़र इंटरफ़ेस (यूआई) में कंट्रोल दिखने के बाद, उपयोगकर्ता अपने पसंदीदा कंट्रोल चुन सकते हैं.
Kotlin कोरूटीन का इस्तेमाल करके ControlsProviderService बनाने के लिए, अपनी 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 फ़ाइलों को सिंक करने के बाद, createPublisherForAllAvailable को लागू करने के लिए, अपने Service में यह स्निपेट जोड़ें:
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 कोरूटीन और फ़्लो का इस्तेमाल करता है, ताकि ज़रूरी रिएक्टिव स्ट्रीम एपीआई को पूरा किया जा सके. इसके लिए, यह तरीका अपनाएं:
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) }
ऐप्लिकेशन खोलें और डिवाइस कंट्रोल मेन्यू को ऐक्सेस करें. इसके बाद, लाइट और थर्मोस्टैट के कंट्रोल देखें.