Im trying to set a continuos broadcast from Kotlin to dart using an EventChannel, following this example: GITHUB REPO it uses an IntentFilter in order to notify the battery status.
What im trying to do is to return each second if possible a value that's stored on a sharedPreference, but it doesn't matter if I set the Intent to stream with action_send, flutter doesn't receive the value.
this is the current code.
private lateinit var streamChannel: EventChannel
private lateinit var statusValueStateChangeReceiver: BroadcastReceiver
override fun configureFlutterEngine(#NonNull flutterEngine: FlutterEngine) {
streamChannel = EventChannel(
flutterEngine.dartExecutor.binaryMessenger, "continuous.stream.boolean/statusValue")
streamChannel.setStreamHandler(this)
}
override fun onListen(arguments: Any?, events: EventChannel.EventSink?) {
statusValueStateChangeReceiver = createStatusValueStateChangeReceiver(events);
applicationContext.registerReceiver(
statusValueStateChangeReceiver, IntentFilter(Intent.ACTION_SEND));
}
override fun onCancel(arguments: Any?) {
applicationContext.unregisterReceiver(statusValueStateChangeReceiver);
}
private fun createStatusValueStateChangeReceiver(events: EventChannel.EventSink?): BroadcastReceiver {
return object : BroadcastReceiver() {
override fun onReceive(contxt: Context?, intent: Intent?) {
events?.success(intent?.let { MyClass().getStatusValue() })
}
}
}
how can I keep sending the data to dart in a persistent way? thanks in advance.
Related
I want to create a plugin to use in my project. I wonder that how I can call or setup it for invoking from flutter application without specific it in pubspec.yaml (because I use some other packages which also use their owned plugins, if I specify my plugin inside pubspec.yaml, those plugins do not work) or separating to another package.
Here is my current code:
class DemoPlugin : FlutterPlugin, MethodCallHandler, ActivityAware {
private lateinit var channel: MethodChannel
private lateinit var context: Context
private lateinit var activity: Activity
override fun onMethodCall(call: MethodCall, result: Result) {
when (call.method) {
"demo" -> {
result.success("Demo")
}
}
}
override fun onAttachedToEngine(#NonNull flutterPluginBinding: FlutterPluginBinding) {
context = flutterPluginBinding.applicationContext
channel = MethodChannel(flutterPluginBinding.binaryMessenger, channelName)
channel.setMethodCallHandler(this)
}
override fun onDetachedFromEngine(binding: FlutterPluginBinding) {
channel.setMethodCallHandler(null)
}
override fun onAttachedToActivity(binding: ActivityPluginBinding) {
activity = binding.activity
}
override fun onDetachedFromActivityForConfigChanges() {}
override fun onReattachedToActivityForConfigChanges(binding: ActivityPluginBinding) {}
override fun onDetachedFromActivity() {}
}
According to the docs of the FlutterPlugin Class you can do this in the MainActivity Class.
class MainActivity: FlutterActivity() {
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
GeneratedPluginRegistrant.registerWith(flutterEngine)
flutterEngine.plugins.add(DemoPlugin()) // add the plugin
}
}
Any idea on how can to send and receive data between Application module to Androidauto. Also how can i put events/listener in Androidauto module if i received data from the application.
With BroadcastRecevier.
Use DefaultLifecycleObserver on your Screen or Session
class CarHomeScreen(carContext: CarContext) : Screen(carContext), DefaultLifecycleObserver {
private val someBroadcastReceiver = SomeBroadcastReceiver()
init {
lifecycle.addObserver(this)
}
override fun onGetTemplate(): Template {}
override fun onCreate(owner: LifecycleOwner) {
carContext.registerReceiver(someBroadcastReceiver, IntentFilter)
super.onCreate(owner)
}
override fun onDestroy(owner: LifecycleOwner) {
lifecycle.removeObserver(this)
carContext.unregisterReceiver(someBroadcastReceiver)
super.onDestroy(owner)
}
}
How about Local Broadcast Receiver?
I'm writing tests to verify the reception of the transmitting receivers but for some reason, the receiver is never registered or the intent is never sent.
I guess there should be a problem with the Context but, no luck yet finding it.
This is the BroadcastFactory.kt:
object BroadcastFactory {
private lateinit var intent: Intent
fun build(
action: String,
flag: Int? = null,
): BroadcastFactory {
intent = Intent().apply {
this.action = action
this.flags = flag ?: 0
}
return this
}
fun send(
context: Context
): Intent {
context.sendBroadcast(intent)
return intent
}
}
And this is the test file BroadcastTest.kt:
#RunWith(AndroidJUnit4::class)
#SmallTest
class BroadcastTest {
lateinit var intents: MutableList<Intent>
lateinit var latch: CountDownLatch
private lateinit var receiver: BroadcastReceiverTester
inner class BroadcastReceiverTester : BroadcastReceiver() {
override fun onReceive(p0: Context?, intent: Intent?) {
intent?.let {
intents.add(it)
latch.countDown()
}
}
}
private val context: Context = getInstrumentation().targetContext
#Before
fun setUp() {
intents = mutableListOf()
latch = CountDownLatch(1)
receiver = BroadcastReceiverTester()
LocalBroadcastManager.getInstance(context).registerReceiver(
receiver,
IntentFilter.create(
Constants.ACTION, "text/plain"
)
)
}
#Test
fun testBroadcastReception() {
BroadcastFactory
.build(Constants.ACTION, Constants.FLAG)
.send(context)
// assert broadcast reception (NOT WORKING)
latch.await(10, TimeUnit.SECONDS)
assertThat(intents.size).isEqualTo(1)
}
#After
fun tearDown() {
LocalBroadcastManager.getInstance(context).unregisterReceiver(receiver)
}
}
I'm using a CountDownLatch to wait 10 seconds for the receiver, plus, its value can be asserted. Besides, I set a list of Intents to check the number of registrations/receptions.
There is something I'm missing here? Different context provider? Robolectric runner?
Thanks
Is solved it by changing the receiver with this:
context.registerReceiver(
receiver,
IntentFilter(
Constants.ACTION
)
)
Thanks to #selvin and #mike-m for the help!
I want to use MutableSharedFlow in the Service class, but I'm not sure how to stop subscribing when Service ends. How to implement the MutableSharedFlow function in service or any other function available to listen to stream data?
To use a Flow in an android Service class we need a CoroutineScope instance to handle launching coroutines and cancellations. Please see the following code with my comments:
class CoroutineService : Service() {
private val scope = CoroutineScope(Dispatchers.IO)
private val flow = MutableSharedFlow<String>(extraBufferCapacity = 64)
override fun onCreate() {
super.onCreate()
// collect data emitted by the Flow
flow.onEach {
// Handle data
}.launchIn(scope)
}
override fun onStartCommand(#Nullable intent: Intent?, flags: Int, startId: Int): Int {
scope.launch {
// retrieve data from Intent and send it to Flow
val messageFromIntent = intent?.let { it.extras?.getString("KEY_MESSAGE")} ?: ""
flow.emit(messageFromIntent)
}
return START_STICKY
}
override fun onBind(intent: Intent?): IBinder? = null
override fun onDestroy() {
scope.cancel() // cancel CoroutineScope and all launched coroutines
}
}
I need to scan for a list of wifi access points in my android app. I've done it in the past using java, but I'm having trouble getting my kotlin code to work.
My code:
var resultList = ArrayList<ScanResult>()
lateinit var wifiManager: WifiManager
val broadcastReceiver = object : BroadcastReceiver() {
override fun onReceive(contxt: Context?, intent: Intent?) {
resultList = wifiManager.scanResults as ArrayList<ScanResult>
Log.d("TESTING", "onReceive Called")
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
wifiManager = this.applicationContext.getSystemService(Context.WIFI_SERVICE) as WifiManager
}
override fun onGridTileClicked(x: Int, y: Int) {
startScanning()
}
fun startScanning() {
registerReceiver(broadcastReceiver, IntentFilter(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION))
Handler().postDelayed({
stopScanning()
}, 10000)
}
fun stopScanning() {
unregisterReceiver(broadcastReceiver)
val axisList = ArrayList<Axis>()
for (result in resultList) {
axisList.add(Axis(result.BSSID, result.level))
}
Log.d("TESTING", axisList.toString())
}
The onReceive() function is never called, and I have the ACCESS_FINE_LOCATION and ACCESS_WIFI_STATE both declared in the manifest, so I'm not sure what I'm doing wrong. I'm sure I'm missing something obvious, but some help would be appreciated. Thanks!
You forgot to start scanning. Add wifiManager.startScan() call in your startScanning method.