I am writing an app that connects to an Arduino bluetooth device. The goal is for the Android user to receive a push notification if the phone leaves the range of the Arduino. This should occur regardless of whether the app is in the foreground or not. To do this, I am currently using a BroadcastReceiver in the Android Manifest. However, I am not receiving any such notifications.
Here is the Receiver class that implements BroadcastReceiver:
public class Receiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
String action = intent.getAction();
if (action.equals(BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED)) {
if (adapter.getState() == BluetoothAdapter.STATE_OFF) {
pushNotification(context);
}
}
}
public void pushNotification(Context context) {
NotificationCompat.Builder builder = new NotificationCompat.Builder(context);
builder.setSmallIcon(R.mipmap.ic_launcher);
builder.setAutoCancel(true);
builder.setLargeIcon(BitmapFactory.decodeResource(context.getResources(), R.mipmap.ic_launcher));
builder.setContentTitle("This is a notification!");
builder.setContentText("This is the notification text!");
builder.setSubText("This is the notification subtext!");
NotificationManager notificationManager = (NotificationManager) context.getSystemService(context.NOTIFICATION_SERVICE);
notificationManager.notify(1, builder.build());
}
}
And the AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.BLUETOOTH"/>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
<uses-feature android:name="android.hardware.bluetooth_le" android:required="true"/>
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:supportsRtl="true"
android:theme="#style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<receiver
android:name=".Receiver"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="android.bluetooth.adapter.action.STATE_CHANGED" />
</intent-filter>
</receiver>
</application>
</manifest>
I'm fairly certain that the problem lies in the constants I am using in my logic. However, I'm not sure which ones I should use. As it is, the receiver is activated when ANY state change occurs to the Android Bluetooth, but I only want a notification when the connection is lost, which might not have anything to do with a state change of the Android Bluetooth receiver.
What should I do to make sure pushNotification() is called under these conditions?
You have register wrong intent.
<action android:name="android.bluetooth.adapter.action.STATE_CHANGED" />
This intent only indicate wheather the bluetooth is on and off.If you want to receive bluetooth devices connect state , you should use follow actions:
<action android:name="android.bluetooth.device.action.ACL_DISCONNECTED" />
<action android:name="android.bluetooth.device.action.ACL_CONNECTED" />
In you onRecive method:
if(TextUtils.equals(action,BluetoothDevice.ACTION_ACL_DISCONNECTED)) {
BluetoothDevice device = intent.getExtras()
.getParcelable(BluetoothDevice.EXTRA_DEVICE);
if (isYourDevice(device)) {
// to push your notification
}
}
Related
I am trying to listen for timezone changes even when my Android app is closed.
What I tried:
I found a intent action for it. It is TIME_ZONE_CHANGED. However it
is a protected intent that can only sent by system as the
documentation says and also it probably doesn't allow to make it
implicit broadcast.
I tried AlarmManager, but I couldn't find exact timezone change
events.
I used schedululeAtFixedRate in a thread in an app service. It worked perfectly.
But I don't want it to listen to every hour changes, I want only time zone timezone changes, as I mentioned above.
Edit:
MainActivity
public static final String CHANNEL_ID = "49";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
createNotificationChannel();
startService(new Intent(this,AppService.class));
}
private void createNotificationChannel() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
CharSequence c_name = getString(R.string.notification_channel_name);
String c_desc = getString(R.string.notification_channel_description);
int importance = NotificationManager.IMPORTANCE_DEFAULT;
NotificationChannel notificationChannel = new NotificationChannel(CHANNEL_ID,c_name,importance);
notificationChannel.setDescription(c_desc);
NotificationManager manager = getSystemService(NotificationManager.class);
manager.createNotificationChannel(notificationChannel);
}
}
Manifest
<?xml version="1.0" encoding="utf-8"?>
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:roundIcon="#mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="#style/AppTheme"
android:usesCleartextTraffic="true">
<receiver
android:name=".TimeChangedReceiver"
android:enabled="true"
android:exported="true">
<intent-filter >
<action android:name="android.intent.action.TIMEZONE_CHANGED"/>
</intent-filter>
</receiver>
<service
android:name=".AppService"
android:enabled="true"
android:exported="true" />
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
Receiver Class
package com.example.gridview;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import androidx.core.app.NotificationCompat;
import androidx.core.app.NotificationManagerCompat;
public class TimeChangedReceiver extends BroadcastReceiver {
private static int NOTIFICATION_ID = 1;
#Override
public void onReceive(Context context, Intent intent) {
// TODO: This method is called when the BroadcastReceiver is receiving
// an Intent broadcast.
StringBuilder sb = new StringBuilder("Timezone is changed
YAY!,executed for "+NOTIFICATION_ID+" times.");
if (intent.getAction().equals(Intent.ACTION_TIMEZONE_CHANGED)) {
sb.append("\n Status: Ok.");
}
NotificationCompat.Builder builder = new
NotificationCompat.Builder(context,MainActivity.CHANNEL_ID)
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
.setContentText(sb.toString())
.setSmallIcon(R.drawable.ic_launcher_background)
.setContentTitle("Hey!");
NotificationManagerCompat notificationManager =
NotificationManagerCompat.from(context);
notificationManager.notify(NOTIFICATION_ID,builder.build());
NOTIFICATION_ID++;
}
}
You should register a manifest receiver for "android.intent.action.TIMEZONE_CHANGED" (AKA Intent.ACTION_TIMEZONE_CHANGED). It is one of the implicit broadcast exceptions that is not subject to the restrictions on registering manifest receivers in Android 8.0+.
A manifest receiver like this is going to be the only way to detect this type of event when your app is not running.
For some reason my Firebase notifications are not showing in the foreground. They work fine in the background however, and I followed what Anroid Studio told me to add as well as this tutorial example on github
Judging by the Run window, it is receiving the notification but not sending in foreground
Here is my service code for sending the notification
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.media.RingtoneManager;
import android.net.Uri;
import android.os.Build;
import android.os.IBinder;
import android.support.v4.app.NotificationCompat;
import android.util.Log;
import com.google.firebase.messaging.FirebaseMessagingService;
import com.google.firebase.messaging.RemoteMessage;
import static android.support.constraint.Constraints.TAG;
public class FirebaseNotificationsRecieverService extends FirebaseMessagingService {
private static final String TAG = FirebaseNotificationsRecieverService.class.getName();
public FirebaseNotificationsRecieverService() {
}
#Override
public void onMessageReceived(RemoteMessage remoteMessage) {
// ...
System.out.println("RECIEVED");
// TODO(developer): Handle FCM messages here.
// Not getting messages here? See why this may be: (there was a link here)
Log.d(TAG, "From: " + remoteMessage.getFrom());
// Check if message contains a data payload.
if (remoteMessage.getData().size() > 0) {
Log.d(TAG, "Message data payload: " + remoteMessage.getData());
if (/* Check if data needs to be processed by long running job */ true) {
// For long-running tasks (10 seconds or more) use Firebase Job Dispatcher.
//scheduleJob();
} else {
// Handle message within 10 seconds
handleNow();
}
}
// Check if message contains a notification payload.
if (remoteMessage.getNotification() != null) {
Log.d(TAG, "Message Notification Body: " + remoteMessage.getNotification().getBody());
}
sendNotification(remoteMessage.getNotification().getBody());
// Also if you intend on generating your own notifications as a result of a received FCM
// message, here is where that should be initiated. See sendNotification method below.
}
private void handleNow() {
Log.d(TAG, "Short lived task is done.");
}
private void sendNotification(String messageBody) {
Intent intent = new Intent(this, MainActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0 /* Request code */, intent,
PendingIntent.FLAG_ONE_SHOT);
String channelId = getString(R.string.default_notification_channel_id);
Uri defaultSoundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
NotificationCompat.Builder notificationBuilder =
new NotificationCompat.Builder(this, channelId)
.setSmallIcon(R.drawable.wlmaclogo)
.setContentTitle(getString(R.string.fcm_message))
.setContentText(messageBody)
.setAutoCancel(true)
.setSound(defaultSoundUri)
.setContentIntent(pendingIntent);
NotificationManager notificationManager =
(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
// Since android Oreo notification channel is needed.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel channel = new NotificationChannel(channelId,
"Channel human readable title",
NotificationManager.IMPORTANCE_DEFAULT);
notificationManager.createNotificationChannel(channel);
}
notificationManager.notify(0 /* ID of notification */, notificationBuilder.build());
}
}
Manifest:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.starenkysoftware.macapp">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:roundIcon="#mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="#style/AppTheme"
android:usesCleartextTraffic="true">
<service
android:name=".FirebaseNotificationsRecieverService"
android:enabled="true"
android:exported="true"></service>
<service android:name="com.google.firebase.messaging.FirebaseMessagingService">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
Replace the manifest as below:-
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.starenkysoftware.macapp">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:roundIcon="#mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="#style/AppTheme"
android:usesCleartextTraffic="true">
<service android:name=".FirebaseNotificationsRecieverService">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
The intent filter com.google.firebase.MESSAGING_EVENT should be placed in your service, which you are using to get the notifications.
Remember to remove the existing messaging service from the AndroidManifest.xml. If you leave the FireBaseMessagingService in the Manifest. your newly defined service doesn't get picked up.
E.g. remove the following:
<service
android:name="com.google.firebase.messaging.FirebaseMessagingService">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>
and add the following:
<service
android:name=".MyFirebaseNotificationService">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>
I'm trying to develop android app for that can record phone calls. So, in the initial step, I've to see if BroadcastReceiver is getting fired or not.
I've added permissions, receiver tag in AndroidManifest file. I'm testing on OnePlus X. Activity is gets started but BroadcastReceiver doesn't get fired when I get call. What's going wrong here?
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.myapp">
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:supportsRtl="true"
android:theme="#style/AppTheme">
<activity
android:name="com.example.myapp.MainActivity"
android:label="#string/app_name"
android:screenOrientation="portrait">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<receiver android:name=".PhoneStateReceiver">
<intent-filter>
<action android:name="android.intent.action.PHONE_STATE" />
</intent-filter>
</receiver>
</application>
</manifest>
PhoneStateReceiver.Java
package com.example.myapp;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.telephony.TelephonyManager;
import android.widget.Toast;
public class PhoneStateReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
try {
System.out.println("Receiver start");
String state = intent.getStringExtra(TelephonyManager.EXTRA_STATE);
String incomingNumber = intent.getStringExtra(TelephonyManager.EXTRA_INCOMING_NUMBER);
if(state.equals(TelephonyManager.EXTRA_STATE_RINGING)){
Toast.makeText(context,"Incoming Call State",Toast.LENGTH_SHORT).show();
Toast.makeText(context,"Ringing State Number is -"+incomingNumber,Toast.LENGTH_SHORT).show();
}
if ((state.equals(TelephonyManager.EXTRA_STATE_OFFHOOK))){
Toast.makeText(context,"Call Received State",Toast.LENGTH_SHORT).show();
}
if (state.equals(TelephonyManager.EXTRA_STATE_IDLE)){
Toast.makeText(context,"Call Idle State",Toast.LENGTH_SHORT).show();
}
}
catch (Exception e){
e.printStackTrace();
}
}
}
The permission READ_PHONE_STATE is a dangerous permission, if you are on a marshmallow device you must request runtime permission else your broadcast receiver will not work neither it will throw an error.
That is the most likely the cause of issue from your code because you have correctly registered in the Intent filter other than that there is nothing wrong as it is just a broadcast receiver and should work, i.e get called by the Android system.
hi i have used the following code in receiver class
public class AlarmReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Log.e("the time is right","yay!");
Intent i = new Intent(context, AlarmServie.class);
context.startService(i);
}
}
here i the code which i used in service class
public class AlarmServie extends Service {
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onStart(Intent intent, int startId) {
super.onStart(intent, startId);
Log.e("onStart:","came" );
/* NotificationManager notifyman = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
Intent main_activity = new Intent(this.getApplicationContext(), MainActivity.class);
PendingIntent o = PendingIntent.getActivity(this, 0, main_activity, 0);
/*Notification noti = new Notification.Builder(this)
.setContentTitle("Reminder to Pill")
.setContentText("Click for info")
.setAutoCancel(true)
.setContentIntent(o)
.build();*/
NotificationManager nm = (NotificationManager) this.getApplicationContext().getSystemService(NOTIFICATION_SERVICE);
Intent in = new Intent(this.getApplicationContext(), MainActivity.class);
PendingIntent pending = PendingIntent.getactivity(this, 0, in, 0);
NotificationCompat.Builder mBuilder =new NotificationCompat.Builder(AlarmServie.this);
mBuilder.setContentTitle("Pill Reminder");
mBuilder.setContentText("CLick here to View");
//mBuilder.setSound(sound);
TaskStackBuilder ts=TaskStackBuilder.create(this);
ts.addParentStack(MainActivity.class);
nm.notify(9999,mBuilder.build());
}}
i created this code to get notification.when i run the app the receiver class is triggered but it is not moving to service class to invoke notification.can anyone say whats wrong in the code or say me how to get notification using broadcast receiver and service in a detailed tutorial
here is the manifest file and say me where to put the code exactly
<uses-permission android:name="android.permission.VIBRATE"/>
<uses-permission android:name="android.permission.WAKE_LOCK"/>
<uses-permission android:name="com.android.alarm.permission.SET_ALARM"/>
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:supportsRtl="true"
android:theme="#style/AppTheme">
<activity
android:name=".MainActivity"
android:label="#string/app_name"
android:theme="#style/AppTheme.NoActionBar">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<receiver android:name=".AlarmReceiver"></receiver>
<activity android:name=".Set"></activity><!-- ATTENTION: This was auto-generated to add Google Play services to your project for
App Indexing. See https://g.co/AppIndexing/AndroidStudio for more information. -->
<meta-data
android:name="com.google.android.gms.version"
android:value="#integer/google_play_services_version" />
</application>
</manifest>
please explain in detail what the problem in code.please tell exactly where to put the service tag
You must declare service in manifest also.
Intent to start service is sent to system then system tries to find right application component to handle this particular intent using information passed in manifest files.
It should be enough, but I'm not sure if you put service in the default package.
<application>
....
<service android:name=".AlarmServie"/>
....
</application>
Your Manifest should look something like this
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:supportsRtl="true"
android:theme="#style/AppTheme">
<activity
android:name=".MainActivity"
android:label="#string/app_name"
android:theme="#style/AppTheme.NoActionBar">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
//here is your service
<service android:name=".AlarmServie" android:enabled="true"/>
<receiver android:name=".AlarmReceiver"></receiver>
<activity android:name=".Set"></activity><!-- ATTENTION: This was auto-generated to add Google Play services to your project for
App Indexing. See https://g.co/AppIndexing/AndroidStudio for more information. -->
<meta-data
android:name="com.google.android.gms.version"
android:value="#integer/google_play_services_version" />
</application>
I am trying to catch a bluetooth device disconnection intent filter.
I added a log to the onReceive but it never reaches it and is not displayed in the logcat.
I suspect that the problem is with my manifest.xml configuration:
manifest:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.company"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="10"
android:targetSdkVersion="16" />
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<application
android:icon="#drawable/ic_launcher"
android:label="#string/app_name"
android:theme="#style/AppTheme" >
<receiver android:name="com.company.MyReceiver" android:enabled="true" android:exported="true">
<intent-filter>
<category android:name="android.intent.category.DEFAULT" />
<action android:name="android.bluetooth.device.action.ACL_DISCONNECT_REQUESTED" />
<action android:name="android.bluetooth.adapter.action.CONNECTION_STATE_CHANGED" />
<action android:name="android.bluetooth.adapter.action.DISCOVERY_STARTED" />
</intent-filter>
</receiver>
<activity
android:name=".BTActivity"
android:label="BTActivity" >
</activity>
</application>
</manifest>
MyReceiver extends BroadcastReceiver:
#Override
public void onReceive(Context context, Intent intent) {
Log.i("got-in", "got-in-");
// String action = intent.getAction();
BluetoothDevice device = intent
.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
Log.i("disconnect", device.getName());
Intent i = new Intent(context, BTActivity.class);
Bundle b = new Bundle();
b.putString("deviceName", device.getName());
intent.putExtras(b); // Put your id to your next Intent
context.startActivity(i);
// finish();
}
Use this code for receiving a disconnect (when Bluetooth is still turned on):
<intent-filter> <action android:name="android.bluetooth.device.action.ACL_CONNECTED" />
<action android:name="android.bluetooth.device.action.ACL_DISCONNECT_REQUESTED" />
<action android:name="android.bluetooth.device.action.ACL_DISCONNECTED" />
</intent-filter>
Also you have to register a service that then registers a broadcast receiver for Bluetooth status change, so your app knows when Bluetooth was turned off and therefore discards all active devices, as you won't receive a ACL_DISCONNECT when Bluetooth is simply turned off.
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
IntentFilter filter = new IntentFilter();
filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
registerReceiver(bluetoothTurnedOnOff, filter);
return START_STICKY;
}
private final BroadcastReceiver bluetoothTurnedOnOff = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
if (intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, -1) == BluetoothAdapter.STATE_OFF) {
[...]
For new users. it will works . Your manifest file looks
<receiver android:name="(package name).BluetoothReceiver" >
<intent-filter>
<action android:name="android.bluetooth.device.action.ACL_CONNECTED" />
<action android:name="android.bluetooth.device.action.ACL_DISCONNECTED" />
</intent-filter>
</receiver>
and your receiver will look like
else if (intent.getAction().equals(`enter code here`
BluetoothDevice.ACTION_ACL_DISCONNECTED)) {
// connection lost
Try removing the <category .../> from the intent-filter and try again.
Try android.bluetooth.device.action.ACL_DISCONNECTED action in intent filter. It should solve your problem.