I am trying to create an application that calls the sender of an SMS as soon as the smartphone receives an SMS.
This is my code:
public class SmsReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals("android.provider.Telephony.SMS_RECEIVED")){
// ... (Managing SMS)
Intent intent = new Intent(Intent.ACTION_CALL);
intent.setData(Uri.parse("tel:" + sender));
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent);
}
}
But it dials and calls the sender only when the application is in foreground, while I'd like it to work always. Using the debugger, the execution flows, but it is unable to start the ACTION_CALL activity somehow. Am I missing anything? Thank you very much in advance
Since Android 10, there are limitations on when/how background processes (Service, BroadcastReceiver) can launch activities. This is your problem.
See this guide for more details.
while I'd like it to work always
That is not an option on modern versions of Android. You cannot start an activity from the background, because you do not know what is going on in the foreground at the time. For example, if the user is relying on a navigation app for driving, taking over the foreground could cause the user to crash.
You could raise a high-priority Notification instead.
Related
(Before you mark this question duplicate, please note that I'm facing this problem in a specific API Level (API R), the app shows normal expected working behaviour on other API levels below R).
When running the app on Pixed 3a API R
I'm trying out the the Alarm Manager in android, by building a basic Alarm app. I have an AlarmPageActivity that I tried to start from my custom Broadcast Receiver (AlarmReceiver.java)
I have code in my MainActivity where I set the alarm using Alarm Manager. When the alarm goes off, the pending intent goes to the AlarmReceiver class where I try to start an activity using context.startActivity(context, alarmIntent).
The problem: [Before you go there, yes I have the appropriate flags required (Intent.FLAG_ACTIVITY_NEW_TASK)]. When I set a time for the alarm, and keep the app open, the AlarmPageActivity (the activity which is supposed to pop open when the alarm goes off) launches and the app executes normal behaviour. But when I set a time, and close the app (onDestroy()), the code follows through to AlarmReceiver.java (my Broadcast receiver), the ringtone starts playing, but my AlarmPageActivity doesn't show up (In fact, even it's onCreate() method does not execute). Neither does it show any error in the Log/run window. I tried changing everything from pending intent parameters, to adding new activity flags to the intent object, etc. but nothing worked! You can see below in the code that there is no syntax error that should be stopping the code from starting a new activity through broadcast receiver (when the user is outside the app).
I faced this issue for nearly 2 days. I read every article/post on the internet about starting an activity from a broadcast receiver class. But it wouldn't work, and I got so frustrated. Then MIRACULOUSLY, I thought why not try running the app on a different emulator. And guess what? it worked. sigh... I kept thinking I was doing something wrong or that my code was whack because I'm only so beginner-ish in android studio. But little did I know, that my code was correct all that time.
When running the app on Pixed 3a API 26
The app shows normal expected behaviour. Even after onDestroy(), the BroadCast receiver receives and launches the AlarmPageActivity, with the ringtone :)
My Question: This is the same code (I didn't change/add anything else) that was not working on the emulator device running API R, but works now in API 26. Can somebody explain why it wouldn't work? I would really appreciate. Or maybe my code wasn't compatible with API R in some way... who knows? Enlighten me!
MainActivity.java
protected void onCreate(Bundle savedInstanceState) {
...
other code...
...
// Alarm manager code ----------------------------------------
alarmMgr = (AlarmManager) getSystemService(ALARM_SERVICE);
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(System.currentTimeMillis());
calendar.set(Calendar.HOUR_OF_DAY, 0);
calendar.set(Calendar.MINUTE, 56);
calendar.set(Calendar.SECOND, 0);
long startTime = calendar.getTimeInMillis();
Intent alarmIntent = new Intent(this, AlarmReceiver.class);
final PendingIntent alarmPendingIntent = PendingIntent.getBroadcast(this, 0,
alarmIntent, 0);
alarmMgr.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, startTime, alarmPendingIntent);
}
AlarmReceiver.java
public class AlarmReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent){
// TODO: This method is called when the BroadcastReceiver is receiving
// an Intent broadcast.
Log.d("asdfasdf", "BroadcastReceiver (alarmReceiver) activity reached");
Intent alarmIntent = new Intent(context.getApplicationContext(), AlarmPageActivity.class);
alarmIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(alarmIntent);
alarmUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_ALARM);
// ringtone code ...
ringtone.play();
Log.d("asdfasdf", "Reached end of alarmReceiver ");
}
} // mind the wrong indenting here
AlarmPageActivity.java
has the default empty activity code...
And yes, I know I shouldn't run an activity from a Broadcast receiver as it shouldn't do heavy task that takes > 10s and everything, but I just wanted to see how to it would look like before improving on that code later.
I think it not work from android 10 and higher. Please read more detail about Restrictions on starting activities from the background. Try to request permisison SYSTEM_ALERT_WINDOW for this case
I am trying to update my homescreen widget when there is a Do not Disturb change. Because WidgetProvider is a BroadcastReceiver in itself, I thought it would be simple. But I am not seeing any Toast when I switch Dnd on or off. Does it mean that the WidgetProvider can only send broadcasts but not receive them?
Here is my code:
#Override
public void onReceive(Context context, Intent intent) {
super.onReceive(context, intent);
String action = intent.getAction();
assert action != null;
if (action.equals(NotificationManager.ACTION_INTERRUPTION_FILTER_CHANGED)) {
System.out.println("Success!");
}
}
You do not show how you are registering for that broadcast. If it is in the manifest, the broadcast that you are trying to receive is not listed on the implicit broadcast exceptions "whitelist", so you may not receive it on Android 8.0+. The only way that you would be able to receive that broadcast on Android 8.0+ is if you have a foreground service running all the time, and that has costs.
Also:
I am not familiar with that specific broadcast and so I am uncertain if it is sent on a DND change (though it certainly seems plausible)
You are not showing a Toast; instead, you are printing a message to stdout, which should get redirected to Logcat on Android
it kills background service, to solve your issue you should use foreground service.
my background sticky service is kill on oreo and heigher devices any solution for getting location on background service when Activity is on backound
It's because of Android Oreo behaviour changes for background execution. Suggested alternatives are
1) Foreground service.
Use this if you are ok displaying notification to the user when you are trying to retrieve the location using Location API. This would be reliable and is suggested in the documentation.
Sample app from the documentation :
LocationUpdatesForegroundService project on GitHub
An example of an app that allows the app to continue a user-initiated
action without requesting all-the-time access to background location.
References
https://developer.android.com/training/location/receive-location-updates
2) Work Manager
This approach would be less reliable as you will not be able to control when exactly this would be called but can be used if you don't want to show notification to the user at all.
You would not be able to run background services long running in Oreo as there are behavior changes, now Oreo to optimize system memory, battery etc, it kills background service, to solve your issue you should use foreground service.
Have a look at Background execution limits https://developer.android.com/about/versions/oreo/android-8.0-changes
A suggestion from me, if you can use FCM then go for it, because apps like WeChat, Facebook uses it, to deliver notifications and they don't face any problem.
Alternate solution which I have opted without FCM due to client requirement to run service that updates location in background. Here are steps below:-
You need to start your background service with showing notification in Oreo and above.
Them after that you need to keep in mind that after some time phone enters into Doze mode so you have to tackle that also
In Addition, Battery optimization must be disabled for the application too.
In Some custom ROM you need to manage the Auto-start permission to restart your service if the service is killed by the android.
And the most important part is, if the service is killed by the android system then send a Broadcast Message to the Broadcast receiver to restart your service one again
Hope you will do some more R&D work.
I have shared my experience and the process by which i have done the same to run the service in the background.
you must show notification for ForegroundService
public class ForegroundService extends Service {
public static final String CHANNEL_ID = "ForegroundServiceChannel";
#Override
public void onCreate() {
super.onCreate();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
String input = intent.getStringExtra("inputExtra");
createNotificationChannel();
Intent notificationIntent = new Intent(this, MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this,
0, notificationIntent, 0);
Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)
.setContentTitle("Foreground Service")
.setContentText(input)
.setSmallIcon(R.drawable.ic_stat_name)
.setContentIntent(pendingIntent)
.build();
startForeground(1, notification);
//do heavy work on a background thread
//stopSelf();
return START_NOT_STICKY;
}
#Override
public void onDestroy() {
super.onDestroy();
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
}
and add permission
<uses-permissionandroid:name=”android.permission.FOREGROUND_SERVICE” />
I need to detect when my app is being uninstalled. For that, I've seen logcat sends out UNINSTALL_PACKAGE intent, and I simply added it to existing Broadcast Receiver I have. But it just doesn't catch it, while other intent I'm listening to, TIME_TICK, works perfectly. Why?
Code currently used:
private IntentFilter intentFilter;
static {
intentFilter = new IntentFilter();
intentFilter.addAction(Intent.ACTION_TIME_TICK);
intentFilter.addAction(Intent.ACTION_UNINSTALL_PACKAGE);
}
private final BroadcastReceiver receiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
if (action.equals(Intent.ACTION_UNINSTALL_PACKAGE) {
Log.e("App", "Uninstalling");
} else if (action.equals(Intent.ACTION_TIME_TICK){
// this works
}
}
};
I need to detect when my app is being uninstalled
There is no supported way to do this, for obvious security reasons.
But it just doesn't catch it
ACTION_UNINSTALL_PACKAGE is an activity action. Nothing on an Android device should be sending it as a broadcast. Hence, you cannot listen to it via a BroadcastReceiver.
I 've seen logcat sends out UNINSTALL_PACKAGE intent, and I simply added it to existing Broadcast Reciever I have. But it just doesn't catch it, while other intent I'm listening to, TIME_TICK, works perfectly. Why?
Whenever your application is being uninstalled, Android OS kills all the components associated with that application as well free memory & resources. Uninstall broadcast receiver intent was to listen uninstall event of others app not for your app.
If you want to capture uninstall event of your app then there is no concept google provides, however you can do by observing data change in your file system where you need to keep monitoring changes in file system.
This solution is not going to work for all phones & different versions of Android.
Can't you just listen for android.intent.action.PACKAGE_REMOVED with your IntentFilter when you uninstall using ACTION_DELETE or ACTION_UNINSTALL_PACKAGE?
I'm currently trying to place a call in the background.
Therefore I call this in my Main-Activity:
Intent callIntent = new Intent(Intent.ACTION_CALL);
callIntent.setData(Uri.parse("tel:123456789"));
startActivity(callIntent);
I also wrote an Outgoing Call Receiver which changes the activity:
Intent myIntent = new Intent(context, DisplayCalcActivity.class);
myIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(myIntent);
The problem is now, that the call-screen is in the foreground. If I switch manually from the call screen to my app, the activity has changed as I wanted, but how is it possible to start the activity in the foreground, so that the call-screen won't be shown at all?
The Android SDK does not allow to make a call in "foreground" maybe you can do accessing to private members but this is not recommended. The only way to call is by dialer app.
Got the solution!
I used another Receiver to listen to "android.intent.action.PHONE_STATE".
The outgoing call, activates the receiver which starts the other activity.