Bring app to front from foreground service - android

I have an app that enables people to send alarms to groups of other users and I have implemented support to trigger alarms from a BLE device. It all works as expected, except for when the app is in background.
I use a foreground service to listen for the device event and a local broadcast to trigger the actual alarm. The alarm get triggered as expected and all code runs (sending alarm to server, playing audio, etc) but the app stays in background.
I have tried several solutions but failed to come up with a working one. Either the answers are too old or they just do not work (as in unwanted results).
I want the exact same result as when tapping the foreground notification = resume the current activity without passing destroy.
I'm actually surprised how hard this seems to be, in my opinion it should be easy as "bringToFront()" since the activity is obviously running.
Any help would be much appreciated!
I don't think any of my code would help to understand my problem, but here's some anyways:
AlarmDeviceService.java - The service listening for BLE events:
private void sendAlarmTriggerBroadcastMessage(String deviceName){
Intent intent = new Intent();
intent.setAction(ALARM_DEVICE_TRIGGERED);
intent.putExtra(INTENT_EXTRA_SOURCE_NAME, deviceName);
LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(intent);
}
AlarmDeviceBroadcastReceiver.java:
public void onReceive(Context context, Intent intent){
String action = intent.getAction();
Log.d(TAG, "Received: "+ action);
if(mReceiver != null){
if(action.equals(AlarmDeviceService.ALARM_DEVICE_TRIGGERED)) {
mReceiver.onDeviceAlarmBroadcast();
}
else if(action.equals(AlarmDeviceService.ALARM_DEVICE_CONNECTED)){
String mac = intent.getStringExtra(AlarmDeviceService.INTENT_EXTRA_SOURCE_MAC);
mReceiver.onAlarmDeviceConnected(mac);
}
else if(action.equals(AlarmDeviceService.ALL_DEVICES_CONNECTED)){
mReceiver.onAllDevicesConnected();
}
}
}
AlarmActivity.kt - The activity that should be brought to front:
override fun onDeviceAlarmBroadcast(){
if (alarmUtilities?.alarmSentHandler?.currentSentAlarm == null) {
Log.d(LOG, "Doing it!")
triggerDefaultAlarm()
}else{
alarmUtilities?.alarmSentHandler?.cancelAlarm()
}
}
If my question is poorly put, please excuse me - it's my first post.

Related

Background operations when app is killed? (Android API 23+)

To learn something new, I'm developing an Android APP (min SDK version API 23 and Target SDK Version API 28) that allows me and my family to create and share a virtual shopping list through HTTP requests and JSON responses on a free Web. Everything works fine, but I want to add a feature: I would like to get notified when someone makes a change even when the app is killed or has never been launched. I know what the task could do to compare the changes made on the list and I also know that it is something to be done once every 5 minute (for example), but I don't know how to perform background operations when the app is no longer running and it has been killed from the recent tasks list. I gave the Service class a try, but when the app is killed it stops. So I looked for a solution and I found the BroadcastReceiver and made it able to receive a message whenever the Service stops in order to restart it. But from Android API 26 the BroadcastReceiver must be (I guess..) contex-registered.
So this is what I my main Activity does when the onCreate method is called:
ReceiverCall receiver = new ReceiverCall();
registerReceiver(receiver, new IntentFilter("com.dadex.familyapp.startServiceRequest"));
My ReceiverCall which extends the BroadcastReceiver Class:
public class ReceiverCall extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
try{
String action = intent.getAction();
if (action.equals("com.dadex.familyapp.startServiceRequest"))
context.startService(new Intent(context, CheckListService.class));
}
catch (Exception e){
Log.e("ERROR", e.getMessage());
}
}
}
And this is my CheckListService onDestroy method:
#Override
public void onDestroy() {
Intent intent = new Intent("com.dadex.familyapp.startServiceRequest");
sendBroadcast(intent);
}
It works fine when the app is launched, but as soon as I kill it, the receiver won't receive anything. So my question is: what is the best way to perform such background operations? Are there other classes I need to learn first? Thanks a lot!
You need a background service, with a notification to keep it alive.
startForegorund()
Search for startForegorund with notification and you will find what you need.

How to know whether an Android foreground service was started by OS or some user action?

I have a requirement to keep a service running (which fetches latest location updates) regardless of whether my Android app is running or closed.
I have finally used a notification based foreground service for this purpose, and it does this job well unless and until the app is closed.
So what happens is:
when the app is running on UI or in the background, the service perfectly fetches the location.
when the app is closed, i.e. swiped off from recent apps, the service gets restarted somehow and fails to fetch location updates.
Now, I want to understand whether that service is started by my application or by the Android OS. Is there any way to figure out?
I tried the following, but the problem is that intent isn't always null when started by service.
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i(Constants.TAG + this.getClass().getSimpleName(), "Inside onStartCommand");
if (null == intent) {
Log.w(Constants.TAG, "Service was stopped and automatically restarted by the system.");
BackgroundTaskHandler.startLocationMonitorServiceBySelf(this, 20 * 1000); // start after 20 sec
Log.w(Constants.TAG, "Stopped self. Restarting again via alarm.");
stopSelf();
} else {
// Other piece of code to setup location client
// and configure notification intent
}
return START_STICKY;
}
Instead of checking if the intent is null, you can be more specific and check if the intent contains an extra set by your activity.
In your activity:
Intent intent = new Intent(mContext, YourService.class);
intent.putExtra("IntentFlag", "MainActivity");
In your service:
if (intent.getStringExtra("IntentFlag").equals("MainActivity"))
{
//Service started by activity
}
Hope this helps!

Bluetooth receiver is not working in Android

I am working in an Android application to receive Bluetooth data from an Hardware Bluetooth device. I have seen all the posts of stack and also used sample app to design my code.
The problem is, my "Bluetooth receiver" is not working, whenever i send data from hardware device, it works for the first time but exactly 2nd time it always failed to receive the data notification.
public class BluetoothDataReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
ConfigClass.bluetoothDataReceive++;
if (ConfigClass.bluetoothDataReceive == 1) {
ConfigClass.showToast(context,
ConfigClass.MSG_RECEIVE_BLUETOOTH_DATA);
}else if (ConfigClass.bluetoothDataReceive ==2) {
ConfigClass.bluetoothDataReceive = 0;
}
}
}
Please help me out....I am struggling with this problem from a long time.
the BroadcastReceiver.onReceive() runs in different thread than your UI. UI is not thread safe. Doing Toast() within it is a bad idea, and supposedly leads to unexpected results...

stop android emulator call

I am working on an Android application, having functionality like voicemail.
I am using BroadcastReceiver to get dialing events.
I have to get the event "WHEN CALL IS UNANSWERED (not picked after few rings) FROM RECEIVER".
I will do some actions on caller end against this event.
I am using AVD emulator, and
I do call from one instance to another instance and it calls perfectly,
but the problem is: It continuously calls until I reject or accept the call.
This way I cannot detect that "CALL IS UNANSWERED AFTER A NUMBER OF RINGS".
So I want the Caller emulator to drop the call after a number of rings (if unanswered) like a normal phone.
I can do it (drop the call after some time) by writing some code, but I need the natural functionality of phone in the emulator.
Can anyone please guide me? Is there any settings in the emulator? Or something else?
The code is shown below in case it helps:
public class MyPhoneReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent)
{
Bundle extras = intent.getExtras();
if (extras != null)
{
String state = "my call state = " + extras.getString(TelephonyManager.EXTRA_STATE);
Log.w("DEBUG", state);
}
}
Hi i think this is impossible with reference to the link:
http://android.bigresource.com/Track/android-sr1t1eagx/
Regards

What is the correct way of handling the action NEW_OUTGOING_CALL while looping?

At the moment I am developing an application which catches the action NEW_OUTGOING_CALL with the help of a BroadcastReceiver. I am aborting the call by calling setResultData(null). After that I am showing the user a dialog which allows him to decide if he wants to use my application to rewrite its number. When the users decision has happened I am placing the new call depending on the decision. Now my broadcast receiver gets called up once again.
What is the correct way of getting to know that I have already processed the number? I got a working solution that uses a timestamp to guess if it could be already processed. Another solution would be to add a "+" at the end of the processed number.
These methods are working fine for my application being the only one catching the NEW_OUTGOING_CALL event. But what should I do when other applications (like Sipdroid or Google Voice) are also sitting there catching the NEW_OUTGOING_CALL broadcast aborting it and restarting it again? I don't see a possibility to get to know if we are still in the same "call flow" and if I already processed the number.
I would love to hear your ideas about this problem!
What API level are you working with? If it's >= 11, check out the new BroadcastReceiver.goAsync function that lets you extend the processing of the broadcast outside of the onReceive function of your receiver. This could bypass the need to loop altogether.
If, like me, you're stuck trying to do this before level 11, it is surprisingly tricky to do this elegantly. You may have done this as well, but I tried to include a "processed" flag as an extra in the ACTION_CALL intent that my code generated, hoping that it would somehow get included in the resulting ACTION_NEW_OUTGOING_CALL broadcast, but that sadly does not work.
The best solution I have been able to find is including a fragment in the URI for the ACTION_CALL intent that you generate. This fragment will be included for the resulting ACTION_NEW_OUTGOING_CALL broadcast, so your broadcast receiver can differentiate between the original call and the one that you generate, but it won't interfere with handlers that aren't looking for it.
Here's the basic code.
In your BroadcastReceiver for the ACTION_NEW_OUTGOING_CALL
public class YourBroadcastReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
// extract the fragment from the URI
String uriFragment = Uri.parse(
intent.getStringExtra("android.phone.extra.ORIGINAL_URI")).getFragment();
// if the fragment is missing or does not have your flag, it is new
if (uriFragment == null || !uriFragment.contains("your_flag")) {
// launch your activity, pass the phone number, etc.
// use getResultData to get the number in order to respect
// earlier broadcast receivers
...
// abort the broadcast
this.setResultData(null);
this.abortBroadcast();
}
// otherwise, your code is there, this call was triggered by you
else {
// unless you have a special need, you'll probably just let the broadcast
// go through here
// note that resultData ignores the fragment, so other receivers should
// be blissfully unaware of it
}
}
}
When the user first dials the number, the fragment will either be missing altogether or your flag won't be present, so you'll abort the broadcast and start your activity. In your activity, if you decide to place the call again, do something like the following:
startActivity(new Intent(Intent.ACTION_CALL,
Uri.parse("tel:" + modified_number + "#your_flag")));
The "your_flag" fragment will then be present in the subsequent NEW_OUTGOING_CALL broadcast and thus allow you to handle this case differently in your broadcast receiver.
The nice thing about this is the the fragment is completely ignored unless you look for it in the ORIGINAL_URI, so other broadcast receivers can continue to function. If you want to be really nice, you may want to look for an existing fragment and add your flag to it (perhaps with a comma separator).
I hope that helps. Good luck!
I don't see a possibility to get to
know if we are still in the same "call
flow" and if I already processed the
number.
Technically, you are not in the same "call flow" as placing a new call is asynchronous. You have to use hints (such as a timestamp) as you seem to be doing already.
If you are confident that other applications will not rewrite the number except to change the prefix or to add a suffix, you may want to add another "proximity check" hint to avoid false positives/negatives, but I'm afraid that's about all you can do.
The onReceive() method in Broadcast receiver receives an Intent as an argument.
Extract the Bundle from the Intent using Intent.getExtras().
This Bundle contains 3 key-value pairs as follows :
android.phone.extra.ALREADY_CALLED = null
android.intent.extra.PHONE_NUMBER = 98xxxxxx98
android.phone.extra.ORIGINAL_URI = tel:98xxxxxx98
98xxxxxx98 is the number dialled by the user.
When the onReceive() is called again, this number changes to 98xxxxxx98* or 0*
By checking for the asterisk(*) at the end of the dialled number, it can be inferred if the onReceive() method is called for the first time or the next subsequent times.
One of the answers would be to track the boolean extra in the intent. It is done in similar way by the Google Phone app. You can check this BroadcastReceiver here (look for alreadyCalled usage)
The other way would be just to pass that "rewritten" number from your broadcast to the next broadcast receiver down the road (can be any app, like Sipdroid, Google Voice, or custom VoIP app) without calling ACTION_CALL intent (this is why you get loop and you broadcast receiver called again) The following code is example of how I am handling call in my custom VoIP app. When I intercept NEW_OUTGOING_CALL in my broadcast receiver, I first check if there is internet connection. If phone is connected to internet I use custom defined intent action of my activity to place call through my VoIP app. If there is no internet connection, I just set original phone number to the broadcast receiver result data. This is used by the next broadcast receiver (probably default phone app, but doesn't have to be) in the flow to place a call.
public class BHTTalkerCallReceiver extends BroadcastReceiver {
private static final String TAG = "BHTTalkerCallReceiver";
#Override
public void onReceive(Context context, Intent intent) {
Log.d(TAG, "Broadcast successfull ... ");
// Extract phone number reformatted by previous receivers
String phoneNumber = getResultData();
if (phoneNumber == null) {
// No reformatted number, use the original
phoneNumber = intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER);
}
if (isNetworkAvailable(context)) { // Make sure this app handles call only if there is internet connection
// My app will bring up the call, so cancel the broadcast
setResultData(null);
// Start my app to bring up the call
Intent voipCallIntent = new Intent(context, TalkerActivity.class);
voipCallIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
voipCallIntent.putExtra(TalkerActivity.OUT_CALL_NUMBER, phoneNumber);
voipCallIntent.setAction(TalkerActivity.BHT_TALKER_OUT_CALL);
context.startActivity(voipCallIntent);
} else { //otherwise make a regular call...
// Forward phone data to standard phone call
setResultData(phoneNumber);
}
}
private boolean isNetworkAvailable(final Context context) {
final ConnectivityManager connectivityManager = ((ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE));
return connectivityManager.getActiveNetworkInfo() != null && connectivityManager.getActiveNetworkInfo().isConnected();
}
}

Categories

Resources