Android 11 ve sonraki sürümlerde Hızlı Erişim Cihaz Kontrolleri özelliği kullanıcının lambalar, telefon numaraları, e-posta gibi harici cihazlar gibi harici cihazları cihazları ve kameraları, tek bir konumdan üç etkileşim içinde varsayılan başlatıcıdır. Cihaz OEM'si hangi başlatıcıyı kullanacağını seçer. Cihaz Google Home ve üçüncü taraf tedarikçi firma uygulamaları gibi bu alanda görüntülenecek cihazlar sağlayın. Bu sayfada, Google Haberler'de kontrol uygulamanıza bağlayabilirsiniz.
Bu desteği eklemek için bir ControlsProviderService
oluşturup beyan edin. 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. Beş cihaz denetimi widget'ları aşağıdaki şekilde gösterildiği gibi kullanılabilir:
|
|
|
|
|
Bir widget'a dokunup basılı tutarak daha ayrıntılı kontrol için uygulamaya gidebilirsiniz. Her widget'ın simgesini ve rengini özelleştirebilirsiniz ancak en iyi kullanıcı deneyimi için varsayılan ayar cihazla eşleşiyorsa varsayılan simgeyi ve rengi kullanın.
Hizmeti oluşturun
Bu bölümde, ControlsProviderService
'nin nasıl oluşturulacağı gösterilmektedir.
Bu hizmet, Android sistem kullanıcı arayüzüne uygulamanızın cihaz kontrolleri içerdiğini bildirir
bölümünde gösterilmesi gerekir.
ControlsProviderService
API, Reactive Streams GitHub projesinde tanımlanan ve Java 9 Flow arayüzlerinde uygulanan reaktif akışlara aşina olduğunuzu varsayar.
API aşağıdaki kavramlar temel alınarak oluşturulmuştur:
- Yayıncı: Yayıncı, uygulamanızdır.
- Abone: Sistem kullanıcı arayüzü, abonedir ve sayı isteyebilir. kontrol etmek için kullanır.
- Abonelik: Yayıncının sistem kullanıcı arayüzüne güncelleme gönderebileceği zaman aralığı. Bu pencereyi yayıncı veya abone kapatabilir.
Hizmeti beyan etme
Uygulamanız MyCustomControlService
gibi bir hizmeti
bildirmelidir.
Hizmet, ControlsProviderService
için bir intent filtresi içermelidir. Bu
filtre, uygulamaların sistem arayüzüne kontrol katkısında bulunmalarını sağlar.
Ayrıca, sistem kullanıcı arayüzündeki kontrol panellerinde gösterilen bir label
'e de ihtiyacınız vardır.
Aşağıdaki örnekte bir hizmetin nasıl tanımlanacağı 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:
Kotlin
class MyCustomControlService : ControlsProviderService() { ... }
Java
public class MyCustomJavaControlService extends ControlsProviderService { ... }
Doğru kontrol türünü seçme
API, kontrolleri oluşturmak için oluşturucu yöntemleri sunar. oluşturucu, kontrol etmek istediğiniz cihazı ve kullanıcının nasıl etkileşimde bulunacağını belirleyin gerçekleşebilir. Aşağıdaki adımları uygulayın:
- Kontrolün temsil ettiği cihaz türünü seçin. İlgili içeriği oluşturmak için kullanılan
DeviceTypes
sınıfı bir desteklenen tüm cihazların numaralandırması. Tür, renklerini ve simgelerini görebilirsiniz. - Kullanıcıya yönelik adı, cihaz konumunu (ör. mutfak) ve kontrolle ilişkili diğer kullanıcı arayüzü metin öğelerini belirleyin.
- Kullanıcı etkileşimini destekleyen en iyi şablonu seçin. Kontrollere
ControlTemplate
kaldırıyor. Bu şablon, doğrudan hem de kullanılabilir giriş yöntemlerini (yaniControlAction
. Aşağıdaki tabloda, mevcut şablonlardan bazıları ve destekledikleri işlemler özetlenmiştir:
Şablon | İşlem | Açıklama |
ControlTemplate.getNoTemplateObject()
|
None
|
Uygulama bunu kontrol, kontrol, kullanıcı ve uygulama ancak kullanıcı onunla etkileşim kuramaz. |
ToggleTemplate
|
BooleanAction
|
Etkin ve devre dışı durumları arasında geçiş yapılabilen bir kontrolü temsil eder. BooleanAction nesnesi, değişen bir alan içeriyor
olarak, kullanıcı denetime dokunduğunda istenen yeni durumu temsil eder.
|
RangeTemplate
|
FloatAction
|
Belirtilen minimum, maksimum ve adım değerlerine sahip bir kaydırma çubuğu widget'ını temsil eder. Kullanıcı, kaydırma çubuklarıyla etkileşime girdiğinde uygulamaya güncellenmiş değeri içeren yeni bir FloatAction nesnesi gönderin.
|
ToggleRangeTemplate
|
BooleanAction, FloatAction
|
Bu şablon, ToggleTemplate ve
RangeTemplate . Dokunma etkinliklerinin yanı sıra karartılabilir ışıkları kontrol etmek için kullanılan kaydırma çubuklarını da destekler.
|
TemperatureControlTemplate
|
ModeAction, BooleanAction, FloatAction
|
Bu şablon, önceki işlemleri kapsüllemenin yanı sıra Kullanıcı ısıtma, soğutma, ısıtma/soğutma, eko veya kapalı gibi bir mod ayarlar. |
StatelessTemplate
|
CommandAction
|
Dokunma yeteneği sağlayan ancak durumu ancak kızılötesi bir televizyon gibi belirlenemiyor. Kontrol ve durum değişikliklerinin bir toplamı olan rutin veya makro tanımlamak için bu şablonu kullanabilirsiniz. |
Bu bilgilerle kontrolü oluşturabilirsiniz:
- Şunu kullanın:
Control.StatelessBuilder
oluşturucu sınıfını kullanın. - Şunu kullanın:
Control.StatefulBuilder
oluşturucu sınıfını kullanmanız gerekir.
Örneğin, bir akıllı ampulü ve bir termostatı kontrol etmek için aşağıdakini ekleyin
MyCustomControlService
sabit değerleri:
Kotlin
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() { ... }
Java
public class MyCustomJavaControlService extends ControlsProviderService { private final int LIGHT_ID = 1337; private final String LIGHT_TITLE = "My fancy light"; private final int LIGHT_TYPE = DeviceTypes.TYPE_LIGHT; private final int THERMOSTAT_ID = 1338; private final String THERMOSTAT_TITLE = "My fancy thermostat"; private final int THERMOSTAT_TYPE = DeviceTypes.TYPE_THERMOSTAT; ... }
Kontroller için yayıncılar oluşturma
Kontrolü oluşturduktan sonra bir yayıncıya ihtiyacı vardır. Yayıncı, sistem kullanıcı arayüzünü kontrolün varlığı hakkında bilgilendirir. ControlsProviderService
sınıfı
uygulama kodunuzda geçersiz kılmanız gereken iki yayıncı yöntemi vardır:
createPublisherForAllAvailable()
: Uygulamanızdaki tüm kontroller için birPublisher
oluşturur. Bu yayıncı içinControl
nesneleri oluşturmak üzereControl.StatelessBuilder()
'i kullanın.createPublisherFor()
: Belirli denetimlerin listesi için dize tanımlayıcılarıyla tanımlanan birPublisher
oluşturur. Şunlar içinControl.StatefulBuilder
kullanın: buControl
nesnesini oluşturur, çünkü yayıncının her bir kontrole dokunun.
Yayıncıyı oluşturma
Uygulamanız, kontrolleri sistem kullanıcı arayüzüne ilk kez yayınladığında uygulama bunu bilmez.
durumu hakkında bilgi edinin. Durum bilgisine ulaşmak zaman alıcı bir işlem olabilir.
çok sayıda atlama olması gerekir. Mevcut denetimlerin sisteme tanıtılması için createPublisherForAllAvailable()
yöntemini kullanın. Her bir kontrolün durumu bilinmediğinden bu yöntemde Control.StatelessBuilder
oluşturucu sınıfı kullanılır.
Kontroller Android kullanıcı arayüzünde göründüğünde kullanıcı favori kontrolleri seçebilir.
ControlsProviderService
oluşturmak için Kotlin eş yordamlarını kullanmak istiyorsanız build.gradle
dosyanıza yeni bir bağımlılık ekleyin:
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 dosyalarınızı senkronize ettikten sonra createPublisherForAllAvailable()
özelliğini uygulamak için Service
dosyanıza aşağıdaki snippet'i ekleyin:
Kotlin
class MyCustomControlService : ControlsProviderService() { override fun createPublisherForAllAvailable(): Flow.Publisher= 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 ): Flow.Publisher { TODO() } override fun performControlAction( controlId: String, action: ControlAction, consumer: Consumer ) { TODO() } }
Java
public class MyCustomJavaControlService extends ControlsProviderService { private final int LIGHT_ID = 1337; private final String LIGHT_TITLE = "My fancy light"; private final int LIGHT_TYPE = DeviceTypes.TYPE_LIGHT; private final int THERMOSTAT_ID = 1338; private final String THERMOSTAT_TITLE = "My fancy thermostat"; private final int THERMOSTAT_TYPE = DeviceTypes.TYPE_THERMOSTAT; private boolean toggleState = false; private float rangeState = 18f; private final Map<String, ReplayProcessor> controlFlows = new HashMap<>(); @NonNull @Override public Flow.Publisher createPublisherForAllAvailable() { List controls = new ArrayList<>(); controls.add(createStatelessControl(LIGHT_ID, LIGHT_TITLE, LIGHT_TYPE)); controls.add(createStatelessControl(THERMOSTAT_ID, THERMOSTAT_TITLE, THERMOSTAT_TYPE)); return FlowAdapters.toFlowPublisher(Flowable.fromIterable(controls)); } @NonNull @Override public Flow.Publisher createPublisherFor(@NonNull List controlIds) { ReplayProcessor updatePublisher = ReplayProcessor.create(); controlIds.forEach(control -> { controlFlows.put(control, updatePublisher); updatePublisher.onNext(createLight()); updatePublisher.onNext(createThermostat()); }); return FlowAdapters.toFlowPublisher(updatePublisher); } }
Sistem menüsünü aşağı doğru kaydırın ve şurada gösterilen Cihaz denetimleri düğmesini bulun: Şekil 4:
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ü oluşturduğunu görürsünüz (Şekil 5'te gösterilmiştir):
Şimdi, aşağıdaki kodu uygulamanıza ekleyerek createPublisherFor()
yöntemini uygulayın:
Service
:
Kotlin
private val job = SupervisorJob() private val scope = CoroutineScope(Dispatchers.IO + job) private val controlFlows = mutableMapOf<String, MutableSharedFlow>() private var toggleState = false private var rangeState = 18f override fun createPublisherFor(controlIds: List ): Flow.Publisher { val flow = MutableSharedFlow (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 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() }
Java
@NonNull @Override public Flow.PublishercreatePublisherFor(@NonNull List controlIds) { ReplayProcessor updatePublisher = ReplayProcessor.create(); controlIds.forEach(control -> { controlFlows.put(control, updatePublisher); updatePublisher.onNext(createLight()); updatePublisher.onNext(createThermostat()); }); return FlowAdapters.toFlowPublisher(updatePublisher); } private Control createStatelessControl(int id, String title, int type) { Intent intent = new Intent(this, MainActivity.class) .putExtra(EXTRA_MESSAGE, title) .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); PendingIntent action = PendingIntent.getActivity( this, id, intent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE ); return new Control.StatelessBuilder(id + "", action) .setTitle(title) .setDeviceType(type) .build(); } private Control createLight() { return createStatefulControl( LIGHT_ID, LIGHT_TITLE, LIGHT_TYPE, toggleState, new ToggleTemplate( LIGHT_ID + "", new ControlButton( toggleState, String.valueOf(toggleState).toUpperCase(Locale.getDefault()) ) ) ); } private Control createThermostat() { return createStatefulControl( THERMOSTAT_ID, THERMOSTAT_TITLE, THERMOSTAT_TYPE, rangeState, new RangeTemplate( THERMOSTAT_ID + "", 15f, 25f, rangeState, 0.1f, "%1.1f" ) ); } private Control createStatefulControl(int id, String title, int type, T state, ControlTemplate template) { Intent intent = new Intent(this, MainActivity.class) .putExtra(EXTRA_MESSAGE, "$title $state") .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); PendingIntent action = PendingIntent.getActivity( this, id, intent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE ); return new Control.StatefulBuilder(id + "", action) .setTitle(title) .setDeviceType(type) .setStatus(Control.STATUS_OK) .setControlTemplate(template) .build(); }
Bu örnekte, createPublisherFor()
yöntemi sahte bir
uygulamanızla iletişim kurma:
alma ve bu durumu sisteme iletme.
createPublisherFor()
yöntemi, aşağıdaki koşulu karşılamak için Kotlin eş yordamlarını ve akışlarını kullanır
aşağıdaki işlemleri yaparak gerekli Reactive Streams API'sini isteyin:
- Bir
Flow
oluş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ının yayınlanmış bir kontrolle etkileşime geçtiğinde sinyal gönderir. Gönderilen ControlAction
türü, işlemi belirler.
Verilen denetim için uygun işlemi gerçekleştirip durumu güncelleyin
cihazın Android kullanıcı arayüzünde görebilirsiniz.
Örneği tamamlamak için Service
öğenize aşağıdakileri ekleyin:
Kotlin
override fun performControlAction( controlId: String, action: ControlAction, consumer: Consumer) { 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) }
Java
@Override public void performControlAction(@NonNull String controlId, @NonNull ControlAction action, @NonNull Consumerconsumer) { ReplayProcessor processor = controlFlows.get(controlId); if (processor == null) return; if (controlId.equals(LIGHT_ID + "")) { consumer.accept(ControlAction.RESPONSE_OK); if (action instanceof BooleanAction) toggleState = ((BooleanAction) action).getNewState(); processor.onNext(createLight()); } if (controlId.equals(THERMOSTAT_ID + "")) { consumer.accept(ControlAction.RESPONSE_OK); if (action instanceof FloatAction) rangeState = ((FloatAction) action).getNewValue() processor.onNext(createThermostat()); } }
Uygulamayı çalıştırın, Cihaz kontrolleri menüsüne erişin ve ışığınızı ve termostat kontrollerine dokunun.