I would like to act at one activity from another one. Lets call them Sender and Receiver. So, in Receiver I have registered receiver:
receiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
val command = intent.getIntExtra("action_id", -1)
val bundle = intent.extras
for (key in bundle!!.keySet()) {
val value = bundle[key]
Timber.i(String.format("%s %s (%s)", key,
value.toString(), value!!.javaClass.name))
}
}
}
registerReceiver(receiver, IntentFilter("command"))
And I send data from `Sender activity before closing it:
backToMess.setOnClickListener {
dialog.dismiss()
val intent = Intent()
intent.action = "command"
intent.putExtra("action_id", 1)
intent.putExtra("m_id", intent.getIntExtra("message_id", 0))
intent.putExtra("list_type", intent.getIntExtra("list_type", 0))
intent.putExtra("list_pos", intent.getIntExtra("list_pos", 0))
sendBroadcast(intent)
finishAndRemoveTask()
}
Data which I send from Sender is: 1,9238,1,12 and data which I receive at Receiver: 1,0,0,0 I don't understand why it happens. Maybe I can't send several extras or what?
I send from Sender is: 1,9238,1,12
val intent = Intent()
intent.getIntExtra("message_id", 0)
you are reading data from an object that you just created which is "0".
try
backToMess.setOnClickListener {
dialog.dismiss()
val broadcastIntent = Intent()
broadcastIntent.action = "command"
broadcastIntent.putExtra("action_id", 1)
broadcastIntent.putExtra("m_id", intent.getIntExtra("message_id", 0))
broadcastIntent.putExtra("list_type", intent.getIntExtra("list_type", 0))
broadcastIntent.putExtra("list_pos", intent.getIntExtra("list_pos", 0))
sendBroadcast(broadcastIntent)
finishAndRemoveTask()
}
Related
I am using a broadcast receiver to exchange data between a service and the MainActivity, but the receiver doesn't receive anything. Does anyone know what's the problem here?
Receiver code:
private var broadcastReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
val extras = intent.extras
if(extras != null){
when(extras.getString("message")){
"lock_phone" -> devicePolicyManager.lockNow()
"block_touch" -> devicePolicyManager.setKeyguardDisabled(adminComponent,true)
"unblock_touch" -> devicePolicyManager.setKeyguardDisabled(adminComponent,false)
}
}
}
}
Sender code:
class WatchService : WearableListenerService() {
override fun onMessageReceived(messageEvent: MessageEvent) {
if (messageEvent.path == "/command") {
val message = String(messageEvent.data)
val messageIntent = Intent()
messageIntent.action = Intent.ACTION_SEND
messageIntent.putExtra("message", message)
LocalBroadcastManager.getInstance(this).sendBroadcast(messageIntent)
} else {
super.onMessageReceived(messageEvent)
}
}
}
Edit:
I register the receiver in onCreate using
LocalBroadcastManager.getInstance(this).registerReceiver(broadcastReceiver, IntentFilter())
Thank you for helping me!
You've registered your BroadcastReceiver with an empty IntentFilter.
When you register your BroadcastReceiver, you need to provide an IntentFilter that tells Android what broadcast Intents should be delivered to your BroadcastReceiver. Since your Service is sending a broadcast Intent with ACTION_SEND, you can set up an IntentFilter that will trigger on ACTION_SEND, like this:
LocalBroadcastManager.getInstance(this)
.registerReceiver(broadcastReceiver, IntentFilter(Intent.ACTION_SEND))
I'm making a widget for my WebView app, and it's got a list of buttons on it. Currently, It's firing an intent whenever their pressed. In that intent, I'm putting some string extra's, but when the onNewIntent receives the intent, the value for the extra is NULL. So I'm stuck on receiving the actual string extra.
Here's the code on my list provider:
override fun getViewAt(positionIndexNum: Int): RemoteViews {
........
val extrasObj = Bundle()
extrasObj.putString("shortcutUrl", listViewUrlArr[positionIndexNum]) // I've tried hardcoding this part and it still returns null.
extrasObj.putString("shortcutJs", listViewJsArr[positionIndexNum])
extrasObj.putString("shortcutId", listViewIdArr[positionIndexNum])
val fillInIntentObj = Intent()
fillInIntentObj.putExtras(extrasObj)
viewObj.setOnClickFillInIntent(listViewItemId, fillInIntentObj)
return viewObj
}
Here's the code from the onNewIntent function:
override fun onNewIntent(intentObj: Intent) {
super.onNewIntent(intentObj)
val bundle = intentObj.extras
if (bundle != null) {
for (key in bundle.keySet()) {
Log.e("TAG", key + " : " + if (bundle[key] != null) bundle[key] else "NULL")
}
}
.....
}
That outputs in the logcat:
shortcutUrl : NULL
shortcutId : NULL
shortcutJs : NULL
I've also tried: intentObj.getStringExtra("shortcutId") which still returns NULL
EDIT:
I also have this PendingIntent code in the updateAppWidget function:
val clickIntent = Intent(contextObj, MainActivity::class.java)
val clickPI = PendingIntent.getActivity(contextObj, 0,
clickIntent, PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT);
viewsObj.setPendingIntentTemplate(R.id.widget_list, clickPI)
I finally found a fix for this, I'm not sure how it really works but I changed:
PendingIntent.FLAG_IMMUTABLE
to
PendingIntent.FLAG_MUTABLE
in the PendingIntent. Hopefully this helps someone else!
Here is a full answer which I posted on another question:
Application widget with bundle?
Read the Intent in MyActivity:
private fun readIntent() {
val intentExtras: Bundle? = intent.extras
intentExtras?.let {
val intentMessage: String? = intentExtras.getString(APPWIDGET_INTENT_MESSAGE)
println(intentMessage)
}
}
Here is a method in Kotlin to get pending intent to open your activity
fun getPendingIntentMyActivity(context: Context, message: String): PendingIntent {
val intent = Intent(context, MyActivity::class.java)
intent.action = APPWIDGET_INTENT
intent.data = Uri.parse(intent.toUri(Intent.URI_INTENT_SCHEME))
val extras = Bundle().apply {
putString(APPWIDGET_INTENT, APPWIDGET_OPEN_APP)
putString(APPWIDGET_INTENT_MESSAGE, message)
}
intent.putExtras(extras)
return PendingIntent.getActivity(context, 0, intent, FLAG_UPDATE_CURRENT or FLAG_IMMUTABLE)
}
Then set it in the Widget
remoteViews.setOnClickPendingIntent(R.id.rootView, getPendingIntentMyActivity(context, "Hello World")
Broadcast receiver class inside the service
inner class ServiceNotificationReceiver : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
val action = intent!!.action
Util.log("action from foreground services")
when (action) {
FOREGROUND_NEXT -> {
next()
}
FOREGROUND_PREVIOUS -> {
prevoius()
}
FOREGROUND_PLAY_PAUSE -> {
if (exoPlayer.isPlaying) {
pause()
} else {
play()
}
}
FOREGROUND_STOP -> {
stopSelf()
}
}
}
}
I am registering it inside the onCreate() of the service like so
serviceNotificationListener = ServiceNotificationReceiver()
val intentfliter = IntentFilter().apply {
addAction(FOREGROUND_PLAY_PAUSE)
addAction(FOREGROUND_PREVIOUS)
addAction(FOREGROUND_NEXT)
addAction(FOREGROUND_STOP)
}
this.registerReceiver(serviceNotificationListener, intentfliter)
The pending intent
playintent = Intent(this, ServiceNotificationReceiver::class.java).setAction(
FOREGROUND_PLAY_PAUSE
)
playpendingIntent =
PendingIntent.getBroadcast(this, 0, playintent, PendingIntent.FLAG_UPDATE_CURRENT)
I am adding it as an action inside the notification builder like so
addAction(
com.google.android.exoplayer2.R.drawable.exo_icon_previous,
"Previous",
previouspendingIntent
)
However the clicks are not registering inside the service. I cannot add it in the manifest due to some complexity in the app and this is the only way. So what could be the issue. Is it the flags or something else.
You're setting your intent target component to yourpackage.YourService.ServiceNotificationReceiver which is not registered in manifest, system will not be able to resolve it and nothing is executed.
Modify your intent to target only your apps package then your receiver will be able to match it:
playintent = Intent().setPackage(this.packageName).setAction(FOREGROUND_PLAY_PAUSE)
I am coding a simple app that measures all available sensors of the android device (Wifi, BT, etc). One of them is the user activity (via ActivityRecognition API), but I can't make it works properly.
I code a class to do everything related to user activity. I want to get only 4 states and one attribute to store the current one:
var VEHICLE = "vehicle"
var WALKING = "walking"
var STILL = "still"
var UNKNOWN = "unknown"
private var current: String? = null
It also includes a BroadcastReceiver object to handle activity transitions:
private var recognitionHandler = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
if (ActivityRecognitionResult.hasResult(intent)) {
val result = ActivityRecognitionResult.extractResult(intent)
val activity = result.mostProbableActivity
current = when(activity.type) {
DetectedActivity.IN_VEHICLE,
DetectedActivity.ON_BICYCLE -> VEHICLE
DetectedActivity.WALKING,
DetectedActivity.RUNNING -> WALKING
DetectedActivity.STILL -> STILL
else -> UNKNOWN
}
}
}
}
The class also have two methods to define the intent and request:
private fun createIntent() : PendingIntent {
val intent = Intent(context, recognitionHandler.javaClass)
val pendingIntent = PendingIntent.getBroadcast(context, 0, intent, 0)
context.registerReceiver(recognitionHandler, IntentFilter())
return pendingIntent
}
private fun createRequest() : ActivityTransitionRequest {
val types = listOf(
DetectedActivity.IN_VEHICLE,
DetectedActivity.WALKING,
DetectedActivity.RUNNING,
DetectedActivity.ON_BICYCLE,
DetectedActivity.STILL
)
val transitions = mutableListOf<ActivityTransition>()
types.forEach { activity ->
transitions.add(
ActivityTransition.Builder()
.setActivityType(activity)
.setActivityTransition(ActivityTransition.ACTIVITY_TRANSITION_ENTER)
.build()
)
}
return ActivityTransitionRequest(transitions)
}
And also one to start listening:
override fun start(onResult: (res: String?) -> Unit) {
// ...
intent = createIntent()
val request = createRequest()
ActivityRecognition.getClient(context)
.requestActivityTransitionUpdates(request, intent)
.addOnSuccessListener {
Log.d("UserActivity Service info", "listening...")
}
.addOnFailureListener { e ->
Log.d("UserActivity Service error", e.toString())
}
// ...
}
The problem is that the current attribute is always null. I think I have some issues with intent or handler registration, but I have no idea where.
Does someone have any comments? :)
Thanks!
This is your problem. In this code from createIntent():
val intent = Intent(context, recognitionHandler.javaClass)
val pendingIntent = PendingIntent.getBroadcast(context, 0, intent, 0)
context.registerReceiver(recognitionHandler, IntentFilter())
return pendingIntent
You return a PendingIntent that you use in the call to requestActivityTransitionUpdates(). However, that PendingIntent refers to a dynamically created inner class (your BroadcastReceiver) and Android cannot instantiate that class.
You also additionally call registerReceiver(), however you pass an empty IntentFilter in that call so the registered BroadcastReceiver is never called.
To fix the problem, you can either provide a correctIntentFilter that matches your PendingIntent OR you can refactor your BroadcastReceiver into a proper class (not a private inner class) and make sure that you've added the BroadcastReceiver to your manifest and make it publicly available (exported="true").
Here's an example of how to do this using a BroadcastReceiver:
https://steemit.com/utopian-io/#betheleyo/implementing-android-s-new-activity-recognition-transition-api
I'm sending a local notification with a cancel button and when I click it, I want to cancel my intent service but the event is not getting called.
I can cancel the intent service using an activity, but when is from a notification, it is not working.
My BroadcastReceiver (the Log is showing).
class EventsReceiver: BroadcastReceiver(){
var onEvent = {}
override fun onReceive(context: Context?, intent: Intent?) {
Log.i("EventsReceiver", "onReceive")
when(intent?.action){
Constants.ACTION_EVENT_CANCEL -> {
Log.i("EventsReceiver", "cancel")
onEvent()
}
}
}
}
The onReceive is getting called when I click the notification button but when I call the onEvent(), the cancelTrip() function is not getting called.
My Intent Service:
override fun onCreate() {
super.onCreate()
notificationHelper = NotificationHelper(this)
receiver = EventsReceiver ()
receiver.onEvent = {
cancelTrip()
}
LocalBroadcastManager.getInstance(this).registerReceiver(receiver, IntentFilter(Constants.ACTION_EVENT_CANCEL))
}
The activity function that cancels the intent service:
private fun cancel(){
val intent = Intent()
intent.setAction(Constants.ACTION_EVENT_CANCEL)
LocalBroadcastManager.getInstance(this).sendBroadcast(intent)
}
How I create my local notification:
val notif = NotificationCompat.Builder(ctx,NOTIFICATION_CHANNEL_ID)
notif.setContentTitle("My App")
notif.setContentText("Canceled trip")
notif.setSmallIcon(R.drawable.ic_launcher_foreground)
val notifIntent = Intent(ctx, MenuActivity::class.java)
val pendingIntent = PendingIntent.getActivity(ctx,0,notifIntent,0)
notif.setContentIntent(pendingIntent)
val cancelIntent = Intent(ctx, EventsReceiver::class.java)
val cancelPendingIntent = PendingIntent.getBroadcast(ctx,0,cancelIntent,PendingIntent.FLAG_CANCEL_CURRENT)
notif.addAction(R.mipmap.ic_launcher, ctx.getString(R.string.cancel_trip), cancelPendingIntent)
service.notify(NOTIFICATION_CANCEL_ID, notif.build())
val cancelIntent = Intent(ctx, EventsReceiver::class.java)
It's because you didn't specify the action (while creating the notification) so in here when(intent?.action)
you have different action then Constants.ACTION_EVENT_CANCEL.
try using this:
val cancelIntent = Intent(Constants.ACTION_EVENT_CANCEL)
or
val cancelIntent = Intent(ctx, EventsReceiver::class.java)
cancelIntent.action = Constants.ACTION_EVENT_CANCEL