Android 11 ve sonraki sürümlerde, Hızlı Erişim Cihaz Kontrolleri özelliği, kullanıcıların ışıklar, termostatlar ve kameralar gibi harici cihazları varsayılan başlatıcıdan üç etkileşim içinde hızlı bir şekilde görüntülemesine ve kontrol etmesine olanak tanır. Cihaz OEM'si, hangi başlatıcının kullanılacağını seçer. Cihaz toplayıcılar (ör. Google Home) ve üçüncü taraf satıcı uygulamaları, bu alanda gösterilecek cihazlar sağlayabilir. Bu sayfada, cihaz kontrollerini bu alanda nasıl göstereceğiniz ve kontrol uygulamanıza nasıl bağlayacağınız açıklanmaktadır.
Bu desteği eklemek için ControlsProviderService oluşturup tanımlayın. Uygulamanızın desteklediği kontrolleri önceden tanımlanmış kontrol türlerine göre oluşturun ve ardından bu kontroller için yayıncılar oluşturun.
Kullanıcı arayüzü
Cihazlar, Cihaz denetimleri altında şablonlu widget'lar olarak gösterilir. Aşağıdaki şekilde gösterildiği gibi beş cihaz kontrol widget'ı kullanılabilir:
|
|
|
|
|
Bir widget'a dokunup basılı tuttuğunuzda daha ayrıntılı kontrol için uygulamaya yönlendirilirsiniz. Her widget'taki simgeyi ve rengi özelleştirebilirsiniz. Ancak en iyi kullanıcı deneyimi için varsayılan küme cihaza uyuyorsa varsayılan simgeyi ve rengi kullanın.
Hizmeti oluşturun
Bu bölümde, ControlsProviderService oluşturma işlemi açıklanmaktadır. Bu hizmet, Android sistem kullanıcı arayüzüne uygulamanızın, Android kullanıcı arayüzünün Cihaz kontrolleri alanında gösterilmesi gereken cihaz kontrolleri içerdiğini bildirir.
ControlsProviderService API, Reactive Streams GitHub projesinde tanımlandığı ve Java 9 Flow arayüzlerinde uygulandığı şekliyle reaktif akışlar hakkında bilgi sahibi olduğunuzu varsayar.
API, aşağıdaki kavramlara dayanır:
- Yayıncı: Uygulamanız yayıncıdır.
- Abone: Sistem kullanıcı arayüzü abonedir ve yayıncıdan bir dizi kontrol isteyebilir.
- Abonelik: Yayıncının, Sistem Arayüzüne güncelleme gönderebileceği zaman aralığı. Bu pencereyi yayıncı veya abone kapatabilir.
Hizmeti tanımlama
Uygulamanız, uygulama manifestinde MyCustomControlService gibi bir hizmet beyan etmelidir.
Hizmet, ControlsProviderService için bir intent filtresi içermelidir. Bu filtre, uygulamaların sistem kullanıcı arayüzüne kontroller eklemesine olanak tanır.
Ayrıca, sistem kullanıcı arayüzündeki kontrollerde gösterilen bir label de gerekir.
Aşağıdaki örnekte bir hizmetin nasıl beyan edileceği gösterilmektedir:
<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>
Ardından, MyCustomControlService.kt adlı yeni bir Kotlin dosyası oluşturun ve ControlsProviderService'yi genişletin:
class MyCustomControlService : ControlsProviderService() { // ... }
Doğru kontrol türünü seçme
API, kontrolleri oluşturmak için oluşturucu yöntemler sağlar. Oluşturucuyu doldurmak için kontrol etmek istediğiniz cihazı ve kullanıcının bu cihazla nasıl etkileşimde bulunduğunu belirleyin. Aşağıdaki adımları uygulayın:
- Kontrolün temsil ettiği cihaz türünü seçin.
DeviceTypessınıfı, desteklenen tüm cihazların numaralandırılmış listesidir. Tür, kullanıcı arayüzündeki cihazın simgelerini ve renklerini belirlemek için kullanılır. - Kullanıcıya gösterilen adı, cihaz konumunu (ör. mutfak) ve kontrolle ilişkili diğer kullanıcı arayüzü metin öğelerini belirleyin.
- Kullanıcı etkileşimini desteklemek için en iyi şablonu seçin. Kontrollere uygulamadan
ControlTemplateatanır. Bu şablon, kontrol durumunu ve kullanılabilir giriş yöntemlerini (yaniControlAction) doğrudan kullanıcıya gösterir. Aşağıdaki tabloda, mevcut şablonlardan bazıları ve destekledikleri işlemler özetlenmektedir:
| Şablon | İşlem | Açıklama |
ControlTemplate.getNoTemplateObject()
|
None
|
Uygulama, kontrolle ilgili bilgileri aktarmak için bunu kullanabilir ancak kullanıcı bununla etkileşimde bulunamaz. |
ToggleTemplate
|
BooleanAction
|
Etkin ve devre dışı durumları arasında geçiş yapılabilen bir kontrolü temsil eder. BooleanAction nesnesi, kullanıcı kontrolü tıkladığında istenen yeni durumu temsil etmek için değişen bir alan içerir.
|
RangeTemplate
|
FloatAction
|
Belirtilen minimum, maksimum ve adım değerlerine sahip bir kaydırma çubuğu widget'ını temsil eder. Kullanıcı kaydırma çubuğuyla etkileşimde bulunduğunda, güncellenmiş değerle birlikte uygulamaya yeni bir FloatAction nesnesi gönderin.
|
ToggleRangeTemplate
|
BooleanAction, FloatAction
|
Bu şablon, ToggleTemplate ve RangeTemplate kombinasyonudur. Dokunma etkinliklerinin yanı sıra, kısılabilir ışıkları kontrol etmek için kullanılan kaydırma çubuğu gibi öğeleri de destekler.
|
TemperatureControlTemplate
|
ModeAction, BooleanAction, FloatAction
|
Bu şablon, yukarıdaki işlemleri kapsamanın yanı sıra kullanıcının ısıtma, soğutma, ısıtma/soğutma, eko veya kapalı gibi bir mod ayarlamasına da olanak tanır. |
StatelessTemplate
|
CommandAction
|
Dokunma özelliği sağlayan ancak durumu belirlenemeyen bir kontrolü (ör. kızılötesi televizyon uzaktan kumandası) belirtmek için kullanılır. Bu şablonu, kontrol ve durum değişikliklerinin bir araya getirilmesiyle oluşan bir rutin veya makro tanımlamak için kullanabilirsiniz. |
Bu bilgilerle şu kontrolü oluşturabilirsiniz:
- Kontrolün durumu bilinmediğinde
Control.StatelessBuilderoluşturucu sınıfını kullanın. - Kontrolün durumu bilindiğinde
Control.StatefulBuilderoluşturucu sınıfını kullanın.
Örneğin, akıllı bir ampulü ve termostatı kontrol etmek için MyCustomControlService öğenize aşağıdaki sabitleri ekleyin:
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() { // ... }
Kontroller için yayıncı oluşturma
Kontrolü oluşturduktan sonra bir yayıncıya ihtiyacı vardır. Yayıncı, kontrolün varlığı hakkında sistem kullanıcı arayüzünü bilgilendirir. ControlsProviderService sınıfında, uygulama kodunuzda geçersiz kılmanız gereken iki yayıncı yöntemi vardır:
createPublisherForAllAvailable: Uygulamanızda bulunan tüm kontroller içinPublisheroluşturur. Bu yayıncı içinControlnesneleri oluşturmak üzereControl.StatelessBuilderkullanın.createPublisherFor: Dize tanımlayıcılarıyla tanımlanan belirli kontroller listesi içinPublisheroluşturur. Yayıncı her denetime bir durum atamak zorunda olduğundan buControlnesneleri oluşturmak içinControl.StatefulBuilderkullanın.
Yayıncıyı oluşturma
Uygulamanız, kontrolleri ilk kez sistem kullanıcı arayüzünde yayınladığında her kontrolün durumunu bilmez. Durumu almak, cihaz sağlayıcının ağında birçok atlama içeren zaman alıcı bir işlem olabilir. Sistemde kullanılabilen denetimleri tanıtmak için createPublisherForAllAvailable yöntemini kullanın. Bu yöntemde, her kontrolün durumu bilinmediğinden Control.StatelessBuilder oluşturucu sınıfı kullanılır.
Android kullanıcı arayüzünde kontroller göründükten sonra kullanıcılar favori kontrolleri seçebilir.
ControlsProviderService oluşturmak için Kotlin eş yordamlarını kullanmak üzere build.gradle dosyanıza yeni bir bağımlılık ekleyin:
Modern
dependencies { implementation "org.jetbrains.kotlinx:kotlinx-coroutines-jdk9:1.6.4" }
Kotlin
dependencies { implementation("org.jetbrains.kotlinx:kotlinx-coroutines-jdk9:1.6.4") }
Gradle dosyalarınızı senkronize ettikten sonra Service dosyanıza aşağıdaki snippet'i ekleyerek createPublisherForAllAvailable uygulayın:
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() } }
Sistem menüsünü aşağı kaydırın ve Şekil 4'te gösterilen Cihaz kontrolleri düğmesini bulun:
Cihaz kontrolleri'ne dokunduğunuzda uygulamanızı seçebileceğiniz ikinci bir ekrana yönlendirilirsiniz. Uygulamanızı seçtikten sonra, önceki snippet'in yeni kontrollerinizi gösteren özel bir sistem menüsünü nasıl oluşturduğunu görürsünüz (Şekil 5'te gösterildiği gibi):
Şimdi createPublisherFor yöntemini uygulayın ve Service öğenize aşağıdakileri ekleyin:
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() }
Bu örnekte, createPublisherFor yöntemi, uygulamanızın yapması gereken işlemlerin sahte bir uygulamasını içerir: durumunu almak için cihazınızla iletişim kurmak ve bu durumu sisteme göndermek.
createPublisherFor yöntemi, aşağıdaki işlemleri yaparak gerekli Reactive Streams API'yi karşılamak için Kotlin eşyordamlarını ve akışlarını kullanır:
Flowoluşturur.- Bir saniye bekler.
- Akıllı ışığın durumunu oluşturur ve yayar.
- Bir saniye daha bekler.
- Termostatın durumunu oluşturur ve yayınlar.
İşlemleri işleme
performControlAction yöntemi, kullanıcı yayınlanmış bir kontrolle etkileşimde bulunduğunda sinyal gönderir. Gönderilen ControlAction türü, işlemi belirler.
Belirtilen kontrol için uygun işlemi gerçekleştirin ve ardından Android kullanıcı arayüzünde cihazın durumunu güncelleyin.
Örneği tamamlamak için Service dosyanıza aşağıdakileri ekleyin:
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) }
Uygulamayı çalıştırın, Cihaz kontrolleri menüsüne erişin ve ışık ile termostat kontrollerinizi görün.