I will send step count:
Foreground Service -> Broadcast Receiver -> Fragment
Since I need to keep tracking step count on background and when app is off, I've created two Broadcast Receiver, one for notification to keep showing step count on background and one for fragment UI which will be unregistered on Destroy.
So this process is for the latter.
Foreground Service
: I send "stepCount" value to broadcast.
Intent().also { intent ->
intent.setAction(ACTION_STEP_COUNTER_NOTIFICATION)
intent.putExtra("stepCount", "$todayTotalStepCount")
sendBroadcast(intent)
}
BroadcastReceiver
open class StepCountBroadcastReceiver : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
super.onReceive(context, intent)
if(intent!!.action == ACTION_STEP_COUNTER_NOTIFICATION) {
var intent = Intent()
var stepCount = intent.getStringExtra("stepCount")
var sendMyDiary = Intent(context!!, MyDiaryFragment::class.java)
sendMyDiary.putExtra("stepCount", stepCount)
context.startActivity(sendMyDiary)
}
}
}
when custom Action is triggered, this will get 'stepCount' from Service and here I will send it to Fragment named MyDiaryFragment.
So I send it by using Intent().putExtra.
Fragment
private val stepCountBroadcastReceiver: BroadcastReceiver = StepCountBroadcastReceiver()
val filter = IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION).apply {
addAction(ACTION_STEP_COUNTER_NOTIFICATION)
}
LocalBroadcastManager.getInstance(requireActivity()).registerReceiver(stepCountBroadcastReceiver, filter)
Here is my question.
I register this StepCountBroadcastReceiver in onCreate.
But I don't know how to get stepCount coming from broadcast in Fragment whenever Broadcastreceiver is called.
It that same process like just adding this below line at the bottom?
var stepCount = requireActivity().intent.getStringExtra("stepCount")
val filter = IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION).apply {
addAction(ACTION_STEP_COUNTER_NOTIFICATION)
}
LocalBroadcastManager.getInstance(requireActivity()).registerReceiver(stepCountBroadcastReceiver, filter)
var stepCount = requireActivity().intent.getStringExtra("stepCount")
In your broadcast receiver, add this.To Send a LocalBroadCast
override fun onReceive(context: Context?, intent: Intent?) {
super.onReceive(context, intent)
if(intent?.action == ACTION_STEP_COUNTER_NOTIFICATION) {
var stepCount = intent.getStringExtra("stepCount")
var localBroadCastIntent = Intent(LOCAL_BROADCAST_KEY)
localBroadCastIntent.putExtra("stepCount",stepCount?:"")
Handler(Looper.getMainLooper()).post {
LocalBroadcastManager.getInstance(this).sendBroadcast(intent)
}
}
}
const val LOCAL_BROADCAST_KEY = "step_counter_local_broadcast"
In your Fragment Call this register function.And in onCreateView and make sure to call unregisterReceiver in ondestroyview of Fragment.
private fun registerBroadCastReceiver() {
LocalBroadcastManager.getInstance(applicationContext).registerReceiver(
receiver,
IntentFilter(LOCAL_BROADCAST_KEY)
)
}
}
Add this BroadCast Receiver,in Fragment Class globally.
private var receiver: BroadcastReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
intent?.let {
val stepCount = it.getStringExtra("stepCount")
}
}
}
Note:
Here Flow will be like this.
Service -> BroadCast Receiver -> LocalBroadCastReceiver ->
UpdateFragment UI.
You can also directly call this localbroadcast from service based on your usecase. So it will be like below.
Service-> LocalBroadCastReceiver -> UpdateFragment UI.
So only when fragment is alive, the local broadcast receiver will receive messages and update ui.
Related
I want to receive an Android Shutdown Event in my app to revert a config change when I start the app. For that, I have register a context registered receiver in my main Activity onCreate as shown below
override fun onCreate(..)
{
...
val bootIntentFilter = IntentFilter(Intent.ACTION_SHUTDOWN);
registerReceiver(shutDownReceiver, bootIntentFilter)
}
private val shutDownReceiver = object : BroadcastReceiver()
{
override fun onReceive(context: Context?, intent: Intent?) {
if(Intent.ACTION_SHUTDOWN == intent?.action)
{
Log.e(TAG, "Shutdown Called")
//Run your logic
}
}
}
and I also added the following permission in manifest.xml
I still don't receive the broadcast is there anything I am missing? target API is 29
Any information to help is really appreciated.
Thanks,
Asma
I want to pass data from service to activity from another app. I already did activity to service exactly same as Bound Services using Messenger in developer.android.com. However they didn't do service to activity in Kotlin, and I didn't see similar solution in Kotlin. There are java solutions but they didn't work for me. Probably Messenger or Broadcast should work but I want to know how.
I think activity should have IncomingHandler and service should have onServiceConnected so can send message like mService?.send(msg) , but I didn't find how to bind them.
Update
Service
val intent = Intent("TO_SALE_APP")
intent.putExtra("RESPONSE_CODE", responseCode)
intent.putExtra("STATUS", status)
intent.putExtra("PAYMENT_TYPE", paymentType)
LocalBroadcastManager.getInstance(this.applicationContext).sendBroadcast(intent)
Update
Activity
private val mMessageReceiver: BroadcastReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent) {
val responseCode = intent.getStringExtra("RESPONSE_CODE")
val status = intent.getIntExtra("STATUS",0)
val paymentType = intent.getIntExtra("PAYMENT_TYPE",0)
Log.d("myTAG","data receive on activity!")
}
}
override fun onCreate(savedInstanceState: Bundle?) {
...
LocalBroadcastManager.getInstance(this).registerReceiver(
mMessageReceiver, IntentFilter("TO_SALE_APP")
)
}
Yes you can send data from Services to Activity
I had done it by sending Location Coordinated from Background Service class to Activity Periodically
In the service, class add this code
val intent = Intent("GPSLocationUpdates")
intent.putExtra("latitude", location.latitude.toString())
intent.putExtra("longitude", location.longitude.toString())
LocalBroadcastManager.getInstance(this#LocationService).sendBroadcast(intent)
In your Activity receives this data by , write this before onCreate
private val mMessageReceiver: BroadcastReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent) {
// Get extra data included in the Intent
val latitude = intent.getStringExtra("latitude")
val longitude = intent.getStringExtra("longitude")
}
}
and in the onCreate method mention this
LocalBroadcastManager.getInstance(this).registerReceiver(
mMessageReceiver, IntentFilter("GPSLocationUpdates")
);
NOTE: the EventListener GPSLocationUpdates should be the same in Service and Activity
I have a broadcast receiver for wifi scan results as a data source and I'd like to make it in coroutine way. I found an answer for suspend function here:
https://stackoverflow.com/a/53520496/5938671
suspend fun getCurrentScanResult(): List<ScanResult> =
suspendCancellableCoroutine { cont ->
//define broadcast reciever
val wifiScanReceiver = object : BroadcastReceiver() {
override fun onReceive(c: Context, intent: Intent) {
if (intent.action?.equals(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION) == true) {
context.unregisterReceiver(this)
cont.resume(wifiManager.scanResults)
}
}
}
//setup cancellation action on the continuation
cont.invokeOnCancellation {
context.unregisterReceiver(wifiScanReceiver)
}
//register broadcast reciever
context.registerReceiver(wifiScanReceiver, IntentFilter(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION))
//kick off scanning to eventually receive the broadcast
wifiManager.startScan()
}
This is fine for signle emit, but if I want to get results while scanning is going then I'll get crash because cont.resume() could be called only once. Then I decided to try Flow. And here is my code:
suspend fun getCurrentScanResult(): Flow<List<ScanResult>> =
flow{
val wifiScanReceiver = object : BroadcastReceiver() {
override fun onReceive(c: Context, intent: Intent) {
if (intent.action?.equals(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION) == true) {
//context.unregisterReceiver(this)
emit(wifiManager.scanResults)
}
}
}
//setup cancellation action on the continuation
//register broadcast reciever
context.registerReceiver(wifiScanReceiver, IntentFilter(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION))
//kick off scanning to eventually receive the broadcast
wifiManager.startScan()
}
But now Android Stuidio says Suspension functions can be called only within coroutine body for function emit(wifiManager.scanResults) Is there a way to use Flow here?
Please take a look at the callback flow which is specifically designed for this use case. Something like this will do the job:
callbackFlow {
val receiver = object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent) {
if (intent.action == WifiManager.SCAN_RESULTS_AVAILABLE_ACTION) {
sendBlocking(wifiManager.scanResults) // or non-blocking offer()
}
}
}
context.registerReceiver(receiver, intentFilter)
awaitClose {
context.unregisterReceiver(receiver)
}
}
You also might want to share this flow with e.g. shareIn operator to avoid registering a new receiver for each flow subscriber.
I want to use my BroadcastReceiver as sender of data into my activity. For this reason I'm using LocalBroadcastManager. This manager is used to register and unregister my receiver. Problem is that Context in onReceive method is different than Context in onStart and onStop method.
I need to pass activity context into my BroadcastReceiver or instance of LocalBroadcastManager initialized inside Activity. Because my receiver is not receiving any data.
Maybe it is not fault of this manager context but I don't know why it doesnt work since I implemented this manager.
class GPSReceiver: BroadcastReceiver(){
companion object{
const val GPS_PAYLOAD = "gps_payload"
}
override fun onReceive(context: Context, intent: Intent) {
try {
val locationManager = context.getSystemService(Context.LOCATION_SERVICE) as LocationManager
val int = Intent(GPS_PAYLOAD)
if (locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)) {
int.putExtra(GPS_PAYLOAD, true)
} else {
int.putExtra(GPS_PAYLOAD, false)
}
LocalBroadcastManager.getInstance(context).sendBroadcast(int)
} catch (ex: Exception) {
}
}
}
Registering receiver inside Activity:
private val gpsStatusReceiver = object: BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
App.log("isGpsEnabled: onReceive")
val gpsStatus = intent?.extras?.getBoolean(GPS_PAYLOAD)
if (gpsStatus != null) {
if (gpsStatus){
App.log("isGpsEnabled: true")
hideGpsSnackbar()
} else {
App.log("isGpsEnabled: false")
showGpsSnackbar()
}
} else {
App.log("isGpsEnabled: null")
}
}
}
override fun onStart() {
super.onStart()
LocalBroadcastManager.getInstance(this).apply {
val filter = IntentFilter()
filter.apply {
addAction("android.location.PROVIDERS_CHANGED")
addAction(GPS_PAYLOAD)
}
registerReceiver(gpsStatusReceiver, filter)
}
}
I have seen your code. So there is not issue with context, but in the approach.
Your are registering your reciever with the same strings in which you are getting you data inside the Reciever.
So Send Your broadcast from Fragment/Activity
Send BroadCast Like
private fun sendSuccessfulCheckoutEvent() {
val intent = Intent("successful_checkout_event")
intent.putExtra("cartID", cartId)
LocalBroadcastManager.getInstance(this).sendBroadcast(intent)
}
And Listen it in Activity/Fragment like this
1) Create broadcast Reciever
private val checkoutDoneReciever : BroadcastReceiver = object : BroadcastReceiver(){
override fun onReceive(context: Context?, intent: Intent?) {
val cartNumbers = intent.getIntExtra("cartID", 0)
Log.d("receiver", "Got message: $cartNumbers.toString()")
}
}
2) Register it in onCreate()/onStart()
LocalBroadcastManager.getInstance(this).registerReceiver(cartUpdatedReceiver,IntentFilter("successful_checkout_event"))
3) Unregister it in onDestroy()
LocalBroadcastManager.getInstance(this).unregisterReceiver(cartUpdatedReceiver)
How to use register and create a Broadcast Receiver in Android in Kotlin. Any advice...
In Java, you can create it by declaring it as a Broadcast Receiver. But in Kotlin I am not able to find Broadcast Receiver ...well if it is there then how to use it.
you can do it in the following way
Create a broadcast receiver object in your activity class
val broadCastReceiver = object : BroadcastReceiver() {
override fun onReceive(contxt: Context?, intent: Intent?) {
when (intent?.action) {
BROADCAST_DEFAULT_ALBUM_CHANGED -> handleAlbumChanged()
BROADCAST_CHANGE_TYPE_CHANGED -> handleChangeTypeChanged()
}
}
}
Register broadcast receiver in onCreate() function of your activity
LocalBroadcastManager.getInstance(this)
.registerReceiver(broadCastReceiver, IntentFilter(BROADCAST_DEFAULT_ALBUM_CHANGED))
unregister it in ondestroy function of your activity
LocalBroadcastManager.getInstance(this)
.unregisterReceiver(broadCastReceiver)
Anonymous class syntax in Kotlin is like this:
val receiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
}
}
I've created a BroadcastReceiver Kotlin extension, which you can copy/paste anywhere.
It doesn't do much more than what is already mentioned, but it reduces some of the boilerplate. 😀
Using this extension, you should register/unregister like so:
private lateinit var myReceiver: BroadcastReceiver
override fun onStart() {
super.onStart()
myReceiver = registerReceiver(IntentFilter(BROADCAST_SOMETHING_HAPPENED)) { intent ->
when (intent?.action) {
BROADCAST_SOMETHING_HAPPENED -> handleSomethingHappened()
}
}
}
override fun onStop() {
super.onStop()
unregisterReceiver(myReceiver)
}