Skip to content

Instantly share code, notes, and snippets.

@KieronQuinn
Created June 11, 2020 19:46
Show Gist options
  • Save KieronQuinn/c9950f3ee09e11f305ce16e7f48f03b8 to your computer and use it in GitHub Desktop.
Save KieronQuinn/c9950f3ee09e11f305ce16e7f48f03b8 to your computer and use it in GitHub Desktop.
Controls API Sample
package com.kieronquinn.app.controltest
import android.app.PendingIntent
import android.app.Service
import android.content.ComponentName
import android.content.Intent
import android.content.res.ColorStateList
import android.graphics.Color
import android.graphics.drawable.Icon
import android.os.Bundle
import android.os.IBinder
import android.provider.Settings
import android.service.controls.Control
import android.service.controls.ControlsProviderService
import android.service.controls.DeviceTypes
import android.service.controls.actions.BooleanAction
import android.service.controls.actions.ControlAction
import android.service.controls.actions.FloatAction
import android.service.controls.templates.*
import android.util.Log
import android.widget.Toast
import androidx.annotation.RequiresApi
import java.lang.reflect.Method
import java.util.concurrent.Flow
import java.util.function.Consumer
/*
Note: This was originally created before the API became public, so contains some hardcoded values rather than referring to the classes.
Please don't just copy paste this, use it to see how the API works.
*/
@RequiresApi(30)
class ControlTile : ControlsProviderService() {
private val screenBrightness: Int
get() = Settings.System.getInt(this.contentResolver, Settings.System.SCREEN_BRIGHTNESS)
private val controlList: List<Control>
get() {
val mutableList = ArrayList<Control>()
mutableList.add {
val control = Control.StatefulBuilder(
"Button",
PendingIntent.getActivity(
this,
0,
Intent(this, MainActivity::class.java),
PendingIntent.FLAG_CANCEL_CURRENT
)
)
control.setTitle("Switch Button")
control.setSubtitle("Test button")
control.setDeviceType(DeviceTypes.TYPE_GENERIC_ON_OFF)
control.setControlId("button")
control.setStatus(1)
val controlButton = ControlButton(true, "button")
control.setControlTemplate(ToggleTemplate("button", controlButton))
control.build()
}
mutableList.add {
val control = Control.StatefulBuilder(
"brightness",
PendingIntent.getActivity(
this,
0,
Intent(this, MainActivity::class.java),
PendingIntent.FLAG_CANCEL_CURRENT
)
)
control.setTitle("Brightness")
control.setSubtitle("Slider")
control.setDeviceType(DeviceTypes.TYPE_LIGHT)
control.setCustomIcon(Icon.createWithResource(this, R.drawable.ic_brightness))
control.setCustomColor(ColorStateList.valueOf(Color.RED))
control.setControlId("brightness")
control.setStatus(1)
val controlButton = ControlButton(true, "brightness")
control.setControlTemplate(
ToggleRangeTemplate(
"brightness",
controlButton,
RangeTemplate("range", 0f, 255f, screenBrightness.toFloat(), 5f, null)
)
)
control.build()
}
return mutableList
}
override fun createPublisherForAllAvailable(): Flow.Publisher<Control> {
return Flow.Publisher {
for (control in controlList) {
it.onNext(control)
}
it.onComplete()
}
}
override fun performControlAction(
controlId: String,
action: ControlAction,
consumer: Consumer<Int>
) {
if (controlId == "brightness") {
if (action is FloatAction) {
val value = action.newValue
Settings.System.putInt(
this.contentResolver,
Settings.System.SCREEN_BRIGHTNESS,
value.toInt()
)
return
} else if (action is BooleanAction) {
startActivity(Intent(Settings.ACTION_DISPLAY_SETTINGS).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK))
sendBroadcast(Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS))
}
}
Log.d(
"ControlTest",
"performControlAction $controlId ${action.toString()} ${consumer.toString()}"
)
Toast.makeText(this, "Clicked", Toast.LENGTH_LONG).show()
consumer.accept(ControlAction.RESPONSE_OK)
}
override fun createPublisherFor(controlIds: MutableList<String>): Flow.Publisher<Control> {
Log.d("ControlTest", "publisherFor ${controlIds.toString()}")
return Flow.Publisher {
for (control in controlList) {
if (controlIds.contains(control.controlId)) {
Log.d("ControlTest", "Found ${control.controlId}")
it.onSubscribe(object : Flow.Subscription {
override fun cancel() {
Log.d("ControlTest", "cancel")
}
override fun request(p0: Long) {
Log.d("ControlTest", "request $p0")
}
})
it.onNext(control)
}
}
}
}
private fun ArrayList<Control>.add(inline: () -> Control) {
this.add(inline.invoke())
}
}
@AndroidDeveloperLB
Copy link

I don't see long pressing on the tiles. Maybe on 0:19:
https://youtu.be/zhJbzt2Ts5E?t=19

But then it becomes black.
In any case, Tasker app seems to have the same issue, and as a result I also found this crash of the OS when developers don't use it right:
https://issuetracker.google.com/issues/169658959

@KieronQuinn
Copy link
Author

Yes, that is long press (there's no other interaction there that could launch anything). At that time it launched the activity in that windowed mode, but didn't work correctly, hence the black.

@AndroidDeveloperLB
Copy link

ok

@renattele
Copy link

Hi! In your example not working switching between on and off on switch button(background not switching). Do you know how to improve this issue?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment