बाहरी डिवाइसों को कंट्रोल करना

Android 11 और इसके बाद के वर्शन में, डिवाइसों को तुरंत ऐक्सेस करने की सुविधा मिलती है. इसकी मदद से उपयोगकर्ता, लाइट, थर्मोस्टैट, और कैमरे जैसे बाहरी डिवाइसों को तुरंत देख और कंट्रोल कर सकते हैं. इसके लिए, उन्हें डिफ़ॉल्ट लॉन्चर से तीन बार इंटरैक्ट करना होगा. डिवाइस बनाने वाली कंपनी यह तय करती है कि किस लॉन्चर का इस्तेमाल करना है. डिवाइस एग्रीगेटर—उदाहरण के लिए, Google Home—और तीसरे पक्ष के वेंडर ऐप्लिकेशन, इस स्पेस में डिसप्ले के लिए डिवाइस उपलब्ध करा सकते हैं. इस पेज पर बताया गया है कि इस स्पेस में डिवाइस कंट्रोल कैसे दिखाए जाएं और उन्हें कंट्रोल करने वाले ऐप्लिकेशन से कैसे लिंक किया जाए.

पहली इमेज. Android के यूज़र इंटरफ़ेस (यूआई) में डिवाइस कंट्रोल करने की जगह.

यह सुविधा जोड़ने के लिए, ControlsProviderService बनाएं और उसका एलान करें. पहले से तय किए गए कंट्रोल टाइप के आधार पर, अपने ऐप्लिकेशन के लिए कंट्रोल बनाएं. इसके बाद, इन कंट्रोल के लिए पब्लिशर बनाएं.

बेहतर यूज़र इंटरफ़ेस

डिवाइस, डिवाइस कंट्रोल में टेंप्लेट वाले विजेट के तौर पर दिखते हैं. डिवाइस कंट्रोल करने के लिए पांच विजेट उपलब्ध हैं. इन्हें इस इमेज में दिखाया गया है:

डिवाइस कंट्रोल के लिए विजेट टॉगल करें
टॉगल करें
स्लाइडर विजेट की मदद से टॉगल करना
स्लाइडर के साथ टॉगल करें
डिवाइस कंट्रोल के लिए रेंज स्लाइडर विजेट
रेंज (इसे चालू या बंद नहीं किया जा सकता)
स्टेटलेस टॉगल कंट्रोल विजेट
Stateless टॉगल
बंद स्थिति में तापमान पैनल विजेट
Temperature panel (closed)
दूसरी इमेज. टेंप्लेट वाले विजेट का कलेक्शन.

किसी विजेट को दबाकर रखने से, आपको ऐप्लिकेशन के ज़्यादा कंट्रोल मिलते हैं. हर विजेट पर आइकॉन और रंग को पसंद के मुताबिक बनाया जा सकता है. हालांकि, उपयोगकर्ता को बेहतर अनुभव देने के लिए, डिफ़ॉल्ट आइकॉन और रंग का इस्तेमाल करें. ऐसा तब करें, जब डिफ़ॉल्ट सेट डिवाइस से मेल खाता हो.

खुली हुई स्थिति में तापमान पैनल का विजेट
तीसरी इमेज. तापमान पैनल विजेट खोलें.

सेवा बनाना

इस सेक्शन में, 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() {
    // ...
}

कंट्रोल का सही टाइप चुनना

एपीआई, कंट्रोल बनाने के लिए बिल्डर के तरीके उपलब्ध कराता है. बिल्डर को भरने के लिए, यह तय करें कि आपको किस डिवाइस को कंट्रोल करना है और उपयोगकर्ता उससे कैसे इंटरैक्ट करेगा. यह तरीका अपनाएं:

  1. उस डिवाइस का टाइप चुनें जिसे कंट्रोल करता है. DeviceTypes क्लास, काम करने वाले सभी डिवाइसों की गिनती करती है. इस टाइप का इस्तेमाल, यूज़र इंटरफ़ेस (यूआई) में डिवाइस के आइकॉन और रंगों का पता लगाने के लिए किया जाता है.
  2. इससे कंट्रोल से जुड़े यूज़र इंटरफ़ेस (यूआई) के टेक्स्ट वाले अन्य एलिमेंट के साथ-साथ, उपयोगकर्ता को दिखने वाले नाम और डिवाइस की जगह की जानकारी का पता चलता है. उदाहरण के लिए, रसोई.
  3. उपयोगकर्ता के इंटरैक्शन को बेहतर बनाने के लिए, सबसे अच्छा टेंप्लेट चुनें. ऐप्लिकेशन से कंट्रोल को 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 की ज़रूरी शर्तें पूरी की जा सकती हैं. इसके लिए, यह तरीका अपनाया जाता है:

  1. Flow बनाता है.
  2. एक सेकंड इंतज़ार करता है.
  3. यह कुकी, स्मार्ट लाइट की स्थिति को बनाती है और उसे चालू करती है.
  4. एक और सेकंड इंतज़ार करता है.
  5. यह कुकी, थर्मोस्टैट की स्थिति को बनाती और भेजती है.

कार्रवाइयों को मैनेज करना

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

ऐप्लिकेशन खोलें और डिवाइस कंट्रोल मेन्यू ऐक्सेस करें. इसके बाद, लाइट और थर्मोस्टैट कंट्रोल देखें.

इस कुकी से, लाइट और थर्मोस्टैट को दिखाने की सुविधा कंट्रोल की जाती है
छठी इमेज. लाइट और थर्मोस्टैट को कंट्रोल करने की सुविधा.