Hello I've made an app with a receiver to listen to incoming calls ,
My problem is that when i close (swipe off from list of apps) the receiver doesn't work anymore.
first thing i tried is the receiver itself defined in the android manifest like so:
<receiver
android:name=".demo.CallReceiver"
android:exported="true"
android:enabled="true">
<intent-filter android:priority="999">
<action android:name="android.intent.action.PHONE_STATE"/>
</intent-filter>
<intent-filter android:priority="999">
<action android:name="android.intent.action.NEW_OUTGOING_CALL" />
</intent-filter>
</receiver>
This works only when the app is open or in the background .
I looked online and saw this - https://stackoverflow.com/a/46889335/7079340
so i made a Service of my own like so (in the manifest):
<service android:name=".Service.CallService" android:enabled="true"
android:exported="false"> <intent-filter>
<action android:name="com.package.name.IRemoteConnection" />
</intent-filter>
</service>
and the class :
public class CallService extends Service {
private static BroadcastReceiver m_Receiver;
#Override
public IBinder onBind(Intent arg0)
{
Log.e("SERVICELOG","bind");
return null;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.e("SERVICELOG","start command");
return START_STICKY;
}
#Override
public void onCreate()
{
Log.e("SERVICELOG","create");
Receiver();
}
#Override
public void onDestroy()
{
Log.e("SERVICELOG","destroy");
try{
unregisterReceiver(m_Receiver);}catch (Exception e){
Log.e("SERVICELOG"," "+e.getMessage());
}
}
private void Receiver()
{
m_Receiver = new CallReceiver();
}
}
Started it in the oncreate of my Splashscreen and it prints the logs and it works !
Any way to make this work without a service I'm afraid of battery issues and so on ? Thanks !
Android has had many changes from Marshmallow and above about apps that implicitly listener for broadcast in their manifest. The reasoning for this is because several apps would register for broadcast and a new process would be spun up for all the registered app's broadcast receiver to run in (very expensive) thus causing battery drain. What made this worse, was that users have no control over this behavior because the broadcast receiver couldn't be unregistered. To fix this, the engineering team behind Android, only allows for a select few broadcast to be implicitly registered. One is the Device Boot broadcast intent. By stopping apps from implicitly registering Broadcast's, apps have to be manually launched by the user to listen for intents they'd like to be notified of. This prevent several unnecessary apps from waking up to attempt to handle intent.
As for your concern, about "battery issues" I would recommend to you to use the preferred pattern of explicitly registering a BroadcastReciever in your Service and just performance tune to get your code to be as performant as possible. Services are definitely not free, but they aren't automatically heavy objects just by having one started and running; plus they do exist for this exact purpose. Just remember to not do unnecessary work in your service and you should be off to the right start.
When you close your app, it goes directly to onDestroy method. In your service code, you implemented this method. So, in your method you did stop your service programmatically. In short, you must remove it.
Related
I've an app that starts itself if the phone is booted. A user told me his phone is used by two people, one of them is using my app and one not.
So I need some event to listen to when the user is switched, so that I can start my apps service if the correct user is using the phone. Anything I can use for that?
Edit
I'm listening to the boot event with a broadcast receiver registered in the manifest, so I know what this is. But I could not find anything suitable for switching users on a device
You need to look for something called BroadcastReciever in android. They are used to capture events such as camera click, phone booting up, screen unlocked etc... These events have a callback called onReceive where you can implement your login.
It's quite easy and you can Google it.
In your manifest:
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
In your application element (be sure to use a fully-qualified [or relative] class name for your BroadcastReceiver):
<receiver android:name="com.example.MyBroadcastReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
In MyBroadcastReceiver.java:
package com.example;
public class MyBroadcastReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Intent startServiceIntent = new Intent(context, MyService.class);
context.startService(startServiceIntent);
}
}
I've got an Android app that needs to detect when a USB peripheral is attached or detached. It works fine when the peripheral is first attached, but I don't receive any notification (i.e., I don't receive an Intent whose action is ACTION_USB_DEVICE_DETACHED) when it is subsequently detached.
Here's the relevant part of my AndroidManifest.xml:
<activity android:name=".LauncherActivity">
<intent-filter>
<action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" />
<action android:name="android.hardware.usb.action.USB_DEVICE_DETACHED" />
</intent-filter>
<meta-data android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" android:resource="#xml/device_filter" />
<meta-data android:name="android.hardware.usb.action.USB_DEVICE_DETACHED" android:resource="#xml/device_filter" />
</activity>
It may also be worth noting that LauncherActivity only exists to start a Service when the device is attached, and to stop the service when it is detached. In either case, LauncherActivity always finishes itself immediately. All of this occurs in LauncherActivity.onCreate.
Any ideas?
USB_DEVICE_DETACHED is a broadcast intent, thus you may want to declare the BroadcastReceiver in manifest with the appropriate intent-filter for detached action, also with meta-data attached.
Same goes for USB_ACCESSORY_DETACHED, for who is interested.
Summary:
USB_XXX_ATTACHED is an activity intent
USB_XXX_DETACHED is a broadcast intent
(where XXX = DEVICE | ACCESSORY)
See: http://developer.android.com/guide/components/intents-filters.html
"There is no overlap within these messaging systems: Broadcast intents are delivered only to broadcast receivers, never to activities or services"
Try with USB_STATE as below.
It will fire both attached and detatched to same receiver and in receiver you can identify whether is was attached or detatched event.
IntentFilter filter = new IntentFilter();
filter.addAction("android.hardware.usb.action.USB_STATE");
Receiver:
public class USBReceiver extends BroadcastReceiver {
public void onReceive(Context context, Intent intent) {
if (intent.getExtras().getBoolean("connected")) {
//do your stuff
}
}
}
So, I never got the ACTION_USB_DEVICE_DETACHED Intent to go to LauncherActivity; I don't know what the deal is there, probably something I don't properly understand about intent filters or the Activity lifecycle callbacks.
The solution I ended up using comes from the post linked by Pratik. I basically took everything about USB_DEVICE_DETACHED out of AndroidManifest.xml. Then, in the onCreate method of the Service, I registered a BroadcastReceiver like this:
#Override
public void onCreate() {
detachReceiver = new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
if(intent.getAction().equals(UsbManager.ACTION_USB_DEVICE_DETACHED))
stopSelf();
}
};
IntentFilter filter = new IntentFilter();
filter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED);
registerReceiver(detachReceiver, filter);
}
A little clunky, and I'm still curious why just adding USB_DEVICE_DETACHED to the <intent-filter> of LauncherActivity wasn't working, but it does what I need.
The fix that worked for me was to unregister in onPause() and register again in onResume():
#Override
public void onPause()
{
super.onPause();
stopIOManager();
if(m_UsbReceiver!=null) unregisterReceiver(m_UsbReceiver);
}
#Override
public void onResume()
{
super.onResume();
startIOManager();
IntentFilter filter = new IntentFilter(ACTION_USB_PERMISSION);
filter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED);
filter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED);
registerReceiver(m_UsbReceiver, filter);
}
However, although the app seems to always receive the DETACH event, once in a while it doesn't get the ATTACHED event. I then need to plug and and unplug the USB connector, and it usually works after one or two attempts. I've put the blame of this strange behaviour on the OS, without certainty.
For what it's worth, I had this same issue because the activity is paused then resumed when the device is disconnected.
Since the receiver is unregistered in the OnPause() method just prior to receiving the ACTION_USB_DEVICE_DETACHED, your app never gets notified.
I am working with Android.
I have an app I am working on uses an Activity to setup specific user input values that are then used by a service to provide alerts based on those values. Doing the research I determined how I could get the app to start up when the phone boots, however, what I really want is to have the service start but not have the app load to the screen. Currently the entire app loads to the screen when I turn on the device and then I have to exit out of it.
I have downloaded similar programs that have interfaces for settings but otherwise run in the background. How is that done?
First you have to create a receiver:
public class BootCompletedReceiver extends BroadcastReceiver {
final static String TAG = "BootCompletedReceiver";
#Override
public void onReceive(Context context, Intent arg1) {
Log.w(TAG, "starting service...");
context.startService(new Intent(context, YourService.class));
}
}
Then add permission to your AndroidManifest.xml:
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
and register intent receiver:
<receiver android:name=".BootCompletedReceiver" >
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
After this is done, your application (Application class) will run along with services, but no Activities.
Ah, and don't put your application on SD card (APP2SD or something like that), because it has to reside in the main memory to be available right after the boot is completed.
I have application with BroadcastReceiver which listens to SD card mount/unmount, like:
public class ExternalDatabaseRemovingBroadcastReceiver extends BroadcastReceiver
{
private static final String TAG= ExternalDatabaseRemovingBroadcastReceiver.class.getName();
public ExternalDatabaseRemovingBroadcastReceiver()
{
super();
}
#Override
public void onReceive(Context context, Intent intent)
{
if(Me.DEBUG)
Log.d(TAG, "SD card mount/unmount broadcast=" + intent.getAction());
if(intent.getAction()==null)
return;
if(Intent.ACTION_MEDIA_UNMOUNTED.equalsIgnoreCase(intent.getAction()) ||
Intent.ACTION_MEDIA_EJECT.equalsIgnoreCase(intent.getAction()) ||
Intent.ACTION_MEDIA_SHARED.equalsIgnoreCase(intent.getAction()))
{
//blah-blah
}
}
}
Broadcast is declared in AndroidManifest as:
<receiver android:enabled="true"
android:exported="true"
android:name=".ExternalDatabaseRemovingBroadcastReceiver">
<intent-filter>
<action android:name="android.intent.action.MEDIA_MOUNTED"/>
<action android:name="android.intent.action.MEDIA_UNMOUNTED"/>
<action android:name="android.intent.action.MEDIA_SHARED"/>
<data android:scheme="file"/>
</intent-filter>
</receiver>
And now my problem. During device launch (either real or emulator) - my application unintentionally runs. I mean ActivityManager self runs it reporting:
11-22 08:56:52.239: INFO/ActivityManager(61): Start proc ru.ivanovpv.cellbox.android for broadcast ru.ivanovpv.cellbox.android/.ExternalDatabaseRemovingBroadcastReceiver: pid=288 uid=10034 gids={1015}
Please explain what's goin on? And how to avoid application self running?
It does sound like the system is responding to the SD card mounting at boot. You could whitelist access to starting this BroadcastReceiver by removing android:exported="true" or changing it to false, and enabling the use of <permission>. I don't know what your end goal of this is, so that may not be the best course of action.
It seems to me that upon booting the device, the SD card is mounted as well, which triggers your intent filter. If you don't want this 'initial' mount to be registered by your app, you can perhaps ignore mounts that happen during the first x seconds of uptime. That may not be the most elegant solution, though...
Edit: Now that I understand barmaley's original intent, the solution is much simpler. Intent-filters in the Android manifest are meant to start your application when something external happens. If you only want to react to (un)mounts while your application is already running, just register your broadcastreceiver programmatically in Application.create and unregister it in Application.destroy using Context.registerReceiver and Context.unregisterReceiver respectively.
As my code looks today, I'm periodically sending a alarm(?) using AlarmManager that is received by AlarmReceiver extends BroadcastReceiver which in turn starts a Service. The Service do some updating and ends with a stopSelf(). IMO this is the best way of periodically perfom a task without constantly having a Service running. Correct?
The issue with this code is however that the whole chain of events is initiated onSharedPreferenceChanged(). I (initially) thought this was a good idea since the whole updating thing is enabled by the user in SharedPreferences.
I've now come to the conclusion that this is in fact not very good and that I need to initiate the AlarmManager/AlarmReceiver/Service/whatever both onPreferenceChange but also on boot.
I've done some searching but everyone seems to want to start the Service on boot. As I see it, I just need to initiate the AlarmManager which will then start the Service (when needed and only periodically).
Please help me with, first of all, sorting this out and secondly coding it!
Thanks in advance!
Then, create and register a BroadcastReceiver where you will do the AlarmManager stuff:
public class YourBootReceiver extends BroadcastReceiver{
#Override
public void onReceive(Context context, Intent intent) {
// do the AlarmManager here
}
}
Then, on your manifest:
<application>
... other stuff
<receiver android:name=".YourBootReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"/>
</intent-filter>
</receiver>
</application>
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />