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))
Related
Installing App Manually
val install = Intent(Intent.ACTION_VIEW)
install.setDataAndType(
uri,
APP_INSTALL_PATH
)
// install.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
install.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
install.putExtra(Intent.EXTRA_NOT_UNKNOWN_SOURCE, true)
}
startActivity(install)
Handling Broadcast Receiver
val intentFilter = IntentFilter()
intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED)
intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED)
intentFilter.addAction(Intent.ACTION_PACKAGE_FULLY_REMOVED)
intentFilter.addAction(Intent.ACTION_PACKAGE_INSTALL)
intentFilter.addAction(Intent.ACTION_PACKAGE_REPLACED)
intentFilter.addAction(Intent.ACTION_MY_PACKAGE_REPLACED)
intentFilter.addDataScheme("package")
registerReceiver(restartAppReceiver, intentFilter)
private val restartAppReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
Toast.makeText(this#LoginActivity, getString(R.string.sign_out), Toast.LENGTH_LONG)
.show()
//start activity
val i = Intent()
Log.i("App_started", "Yes")
i.setClassName(packageName, packageName+".screen.activity.LoginActivity")
i.flags = Intent.FLAG_ACTIVITY_NEW_TASK
context?.startActivity(i)
}
}
Problem Facing
Here I'm not able to receive anything on receiver when app gets installed manually. I need to open the app automatically when it gets installed manually without any action from users end.
thats simple: you can't. user must open it manually. starting Android 10 you can't start any Activity from BroadcastReceiver. besides that: most of your IntentFilter entries are no-op (why are you calling addDataScheme)
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 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()
}
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
How can I determine whether a headset is plugged into an Android device or not?
You can use the broadcast receiver.
So, You might write this code in "AndroidManifest.xml"
<receiver android:name="com.juno.brheadset.HeadsetStateReceiver">
<intent-filter>
<action android:name="android.intent.action.HEADSET_PLUG"/>
</intent-filter>
</receiver>-->
But, This doesn't work. When OS send this "HEADSET_PLUG" intent, OS set the flag "Intent.FLAG_RECEIVER_REGISTERED_ONLY" So, You should write the code like below in Activity or Service class instead of "AndroidManifest" things.
public class BRHeadsetActivity extends Activity {
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
IntentFilter receiverFilter = new IntentFilter(Intent.ACTION_HEADSET_PLUG);
HeadsetStateReceiver receiver = new HeadsetStateReceiver();
registerReceiver( receiver, receiverFilter );
}
I hope this article help you. Bye!
This is the part of "HeadsetObserver.java", Android SDK Source.
private final void sendIntent(int headset, int headsetState, int prevHeadsetState, String headsetName) {
if ((headsetState & headset) != (prevHeadsetState & headset)) {
// Pack up the values and broadcast them to everyone
Intent intent = new Intent(Intent.ACTION_HEADSET_PLUG);
**intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);**
int state = 0;
int microphone = 0;
if ((headset & HEADSETS_WITH_MIC) != 0) {
microphone = 1;
}
if ((headsetState & headset) != 0) {
state = 1;
}
intent.putExtra("state", state);
intent.putExtra("name", headsetName);
intent.putExtra("microphone", microphone);
if (LOG) Slog.v(TAG, "Intent.ACTION_HEADSET_PLUG: state: "+state+" name: "+headsetName+" mic: "+microphone);
// TODO: Should we require a permission?
ActivityManagerNative.broadcastStickyIntent(intent, null);
}
}
When you say "headset", do you mean "wired headset"? If so, there's an intent to detect whether or not one is being plugged or unplugged: ACTION_HEADSET_PLUG.
To check the status, you can use AudioManager.isWiredHeadsetOn(), although that may return false if there is also a bluetooth headset, and audio is routed to that instead.
AudioManager.isWiredHeadsetOn() always return false because it requires user-permission MODIFY_AUDIO_SETTINGS.
I spent several days while found answer. There are no info about this in official documentation. And this bug already registered in BugTracker.
This should help you : http://developer.android.com/reference/android/content/Intent.html#ACTION_HEADSET_PLUG
You can create this kind of receiver class (Kotlin with Flow) in your project:
class HeadsetPlugReceiver : BroadcastReceiver() {
private val _isPlugged = MutableStateFlow<Boolean>(false)
val isPlugged: StateFlow<Boolean> = _isPlugged.asStateFlow()
override fun onReceive(context: Context, intent: Intent) {
context.appComponent.inject(this)
val action = intent.action
Log.i(TAG, "onReceive: $action")
when (action) {
Intent.ACTION_HEADSET_PLUG -> sendEvent(intent)
else -> checkStateOff(intent)
}
}
private fun checkStateOff(intent: Intent) {
Log.i(TAG, "onReceive: the local Bluetooth adapter is off")
}
private fun sendEvent(intent: Intent) {
val isPlugged = intent.getIntExtra(HEADSET_STATE, 0) == 1
Log.i(TAG, "sendEvent: $isPlugged")
_isPlugged.value = isPlugged
}
private companion object {
private const val TAG = "HeadsetPlugReceiver"
// Headset constant
private const val HEADSET_STATE = "state"
}
}
then register this receiver in some class with context:
val headsetReceiver = HeadsetPlugReceiver()
val headsetFilter = IntentFilter(Intent.ACTION_HEADSET_PLUG)
context.registerReceiver(headsetReceiver, headsetFilter)
then collect isPlugged and you get the state of your wire headset connecting state
PS: don't forget to unregister your receiver when it no needs context.unregisterReceiver(headsetReceiver)
First , create receiver in your manifest:
<receiver android:name="com.yourapplication.BroadcastReceiver">
<intent-filter>
<action android:name="android.intent.action.HEADSET_PLUG"/>
</intent-filter>
</receiver>
don't forget to change com.yourapplication according to your project name
Create two variables in the head of your activity :
private BroadcastReceiver mReceiver ;
boolean Microphone_Plugged_in = false;
Define your receiver inside onCreate of your activity :
mReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
int iii=2;
if (Intent.ACTION_HEADSET_PLUG.equals(action)) {
iii=intent.getIntExtra("state", -1);
if(Integer.valueOf(iii)==0){
Microphone_Plugged_in = false;
Toast.makeText(getApplicationContext(),"microphone not plugged in",Toast.LENGTH_LONG).show();
}if(Integer.valueOf(iii)==1){
Microphone_Plugged_in = true;
Toast.makeText(getApplicationContext(),"microphone plugged in",Toast.LENGTH_LONG).show();
}
}
}};
IntentFilter receiverFilter = new IntentFilter(Intent.ACTION_HEADSET_PLUG);
registerReceiver( mReceiver, receiverFilter );
add onResume and onStope :
#Override
protected void onResume() {
super.onResume();
IntentFilter filter = new IntentFilter(Intent.ACTION_HEADSET_PLUG);
getApplicationContext().registerReceiver(mReceiver, filter);
}
#Override
protected void onStop() {
super.onStop();
getApplicationContext().unregisterReceiver(mReceiver);
}
To add to the other answers, from Android documentation:
Warning: Limit how many broadcast receivers you set in your app.
Having too many broadcast receivers can affect your app's performance
and the battery life of users' devices. For more information about
APIs you can use instead of the BroadcastReceiver class for scheduling
background work, see Background Optimizations.
https://developer.android.com/guide/topics/manifest/receiver-element
Which means that you should create a small number of broadcast receivers as possible, to prevent memory issues with your app.
I will suggest using a singleton class with this receiver. In Kotlin:
class HeadsetReceiver private constructor(): BroadcastReceiver() {
// instances
var callback: HeadsetReceiverCallback? = null
//region singleton
private object HOLDER {
val INSTANCE = HeadsetReceiver()
}
companion object {
val instance: HeadsetReceiver by lazy { HOLDER.INSTANCE }
}
//endregion
override fun onReceive(context: Context, intent: Intent) {
if (intent.action == Intent.ACTION_HEADSET_PLUG) {
if(intent.getIntExtra("state", -1) == 0) {
callback?.onHeadsetDisconnected()
} else {
callback?.onHeadsetConnected()
}
}
}
fun register(context: Context) {
val receiverFilter = IntentFilter(Intent.ACTION_HEADSET_PLUG)
context.registerReceiver(this, receiverFilter)
}
fun unregister(context: Context) {
context.unregisterReceiver(this)
callback = null
}
interface HeadsetReceiverCallback {
fun onHeadsetConnected()
fun onHeadsetDisconnected()
}
}
Register:
HeadsetReceiver.instance.register(context)
HeadsetReceiver.instance.callback = object : HeadsetReceiver.HeadsetReceiverCallback {
override fun onHeadsetConnected() {
println("connected!")
}
override fun onHeadsetDisconnected() {
println("disonnected!")
}
}
Unregister:
HeadsetReceiver.instance.unregister(context)