I'm writing an app that checks external storage state. For monitoring the state I use BroadcastReceiver and I've noticed, that onReceive is called multiple times (eg.: 3x for unmount and 1x for mount) for one event (eg.: unplug usb).
Here is my code:
public class MainActivity extends ActionBarActivity {
final static String TAG = "BroadcastReceiverDemo";
private IntentFilter filter;
private BroadcastReceiver receiver;
private void registerReceiver() {
filter = new IntentFilter();
filter.addAction(Intent.ACTION_MEDIA_MOUNTED);
filter.addAction(Intent.ACTION_MEDIA_UNMOUNTED);
filter.addAction(Intent.ACTION_MEDIA_REMOVED);
filter.addAction(Intent.ACTION_MEDIA_SHARED);
filter.addDataScheme("file");
filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
receiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
String broadcast;
String state = Environment.getExternalStorageState();
Log.d(TAG, "State: " + state);
if (Environment.MEDIA_MOUNTED.equals(state) && !Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
broadcast = "External storage aviable";
} else {
broadcast = "External storage NOT aviable";
}
String msg = intent.getAction() + " received broadcast: " + broadcast;
Log.d(TAG, msg);
}
};
this.registerReceiver(receiver, filter);
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
registerReceiver();
}
#Override
protected void finalize() throws Throwable {
if (receiver != null) {
this.unregisterReceiver(receiver);
}
super.finalize();
}
}
Q: Why is onReceive called so many times and how to make it call only once?
Thanks for any help.
Related
I was reading some post but none have a good answer for me.
So, which is the best way to pass data from broadcast to activity without restart the activity?
Actually I'm using this.
SMSListener:
public class SmsListener extends BroadcastReceiver {
private OnSmsReceivedListener listener = null;
#Override
public void onReceive(Context context, Intent intent) {
try {
if (Telephony.Sms.Intents.SMS_RECEIVED_ACTION.equals(intent.getAction())) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
for (SmsMessage smsMessage : Telephony.Sms.Intents.getMessagesFromIntent(intent)) {
String messageBody = smsMessage.getMessageBody();
String phoneNumber = smsMessage.getDisplayOriginatingAddress();
if (listener != null) {
listener.onSmsReceived(phoneNumber, messageBody);
}
}
}
}
} catch (Exception e)
{
Log.e("Error", "Some some");
}
}
public void setOnSmsReceivedListener(Context context) {
this.listener = (OnSmsReceivedListener) context;
}
}
OnSmsReceivedListener:
public interface OnSmsReceivedListener {
void onSmsReceived(String sender, String message);
}
Activity:
public class MainActivity extends AppCompatActivity implements OnSmsReceivedListener {
private SmsListener receiver;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.test);
/***********/
receiver = new SmsListener();
receiver.setOnSmsReceivedListener(this);
}
#Override
public void onSmsReceived(String sender, String message) {
Log.e("Test", "Sender: "+sender+" - Message: "+message);
}
}
Another of my questions is why I never get log "Test" in my activity. Is like listener is always null, why?
You should add at the end of onCreate()
final String SMS_RECEIVED_ACTION = "android.provider.Telephony.SMS_RECEIVED";
IntentFilter intentFilter = new IntentFilter(SMS_RECEIVED_ACTION);
registerReceiver(receiver, intentFilter);
and in the onPause()
unregisterReceiver(receiver);
add also the following permission on AndroidManifest.xml
<uses-permission android:name="android.permission.RECEIVE_SMS"/>
As we know that, when we use any menu in sim toolkit it send command to mobile network in ussd or sms format.
I need that sms or ussd to record and show it to me in android application.
I am calling the library service which i got in main activity like this
public class MainActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Intent srvIntent = new Intent(this, CDUSSDService.class);
startService(srvIntent);
}
}
USSD Service Class is like that:
public class CDUSSDService extends Service{
private String TAG = CDUSSDService.class.getSimpleName();
private boolean mActive = true;//false //we will only activate this "USSD listener" when we want it
BroadcastReceiver receiver = new BroadcastReceiver()
{
#Override
public void onReceive(Context context, Intent intent)
{
if(intent.getAction().equals(Intent.ACTION_INSERT))
{
//activity wishes to listen to USSD returns, so activate this
mActive = true;
Log.d(TAG, "activate ussd listener");
showtoast(""+"activate ussd listener");
}
else if(intent.getAction().equals(Intent.ACTION_DELETE))
{
mActive = false;
Log.d(TAG, "deactivate ussd listener");
showtoast(""+"DeActivate ussd listener");
}
}
};
private final IExtendedNetworkService.Stub mBinder = new IExtendedNetworkService.Stub ()
{
public void clearMmiString() throws RemoteException
{
Log.d(TAG, "called clear");
showtoast("called clear.");
}
public void setMmiString(String number) throws RemoteException
{
Log.d (TAG, "setMmiString:" + number);
showtoast("setMmiString:"+number);
}
public CharSequence getMmiRunningText() throws RemoteException
{
if(mActive == true)
{
return null;
}
return "USSD Running";
}
public CharSequence getUserMessage(CharSequence text)
throws RemoteException {
Log.d(TAG, "get user message " + text);
showtoast("GET Usr Message:"+text);
if(mActive == false){
//listener is still inactive, so return whatever we got
Log.d(TAG, "inactive " + text);
showtoast("inactive:"+text);
return text;
}
//listener is active, so broadcast data and suppress it from default behavior
//build data to send with intent for activity, format URI as per RFC 2396
Uri ussdDataUri = new Uri.Builder()
.scheme(getBaseContext().getString(R.string.uri_scheme))
.authority(getBaseContext().getString(R.string.uri_authority))
.path(getBaseContext().getString(R.string.uri_path))
.appendQueryParameter(getBaseContext().getString(R.string.uri_param_name), text.toString())
.build();
sendBroadcast(new Intent(Intent.ACTION_GET_CONTENT, ussdDataUri));
mActive = false;
return null;
}
};
#Override
public IBinder onBind(Intent intent) {
Log.i(TAG, "called onbind");
//the insert/delete intents will be fired by activity to activate/deactivate listener since service cannot be stopped
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_INSERT);
filter.addAction(Intent.ACTION_DELETE);
filter.addDataScheme(getBaseContext().getString(R.string.uri_scheme));
filter.addDataAuthority(getBaseContext().getString(R.string.uri_authority), null);
filter.addDataPath(getBaseContext().getString(R.string.uri_path), PatternMatcher.PATTERN_LITERAL);
registerReceiver(receiver, filter);
return mBinder;
}
public void showtoast(String str)
{
Toast.makeText(getApplicationContext(), str, Toast.LENGTH_LONG).show();
}
}
I have created MainActivity inside the UssdLibray structure is like that in the below picture:
you can do this with the help of this library https://github.com/alaasalman/ussdinterceptor
I'm using a USSDInterceptor service to get the USSD responce in my application. Here is the service:
package com.codedemigod.services;
import com.android.internal.telephony.IExtendedNetworkService;
public class CDUSSDService extends Service{
private String TAG = CDUSSDService.class.getSimpleName();
private boolean mActive = false; //we will only activate this "USSD listener" when we want it
BroadcastReceiver receiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
if(intent.getAction().equals(Intent.ACTION_INSERT)){
//activity wishes to listen to USSD returns, so activate this
mActive = true;
Log.d(TAG, "activate ussd listener");
}
else if(intent.getAction().equals(Intent.ACTION_DELETE)){
mActive = false;
Log.d(TAG, "deactivate ussd listener");
}
}
};
private final IExtendedNetworkService.Stub mBinder = new IExtendedNetworkService.Stub () {
public void clearMmiString() throws RemoteException {
Log.d(TAG, "called clear");
}
public void setMmiString(String number) throws RemoteException {
Log.d (TAG, "setMmiString:" + number);
}
public CharSequence getMmiRunningText() throws RemoteException {
if(mActive == true){
return null;
}
return "USSD Running";
}
public CharSequence getUserMessage(CharSequence text)
throws RemoteException {
Log.d(TAG, "get user message " + text);
//getApplicationContext().sendBroadcast(new Intent("ussdMsg"));
if(mActive == false){
//listener is still inactive, so return whatever we got
Log.d(TAG, "inactive " + text);
return text;
}
//listener is active, so broadcast data and suppress it from default behavior
//build data to send with intent for activity, format URI as per RFC 2396
Uri ussdDataUri = new Uri.Builder()
.scheme(getBaseContext().getString(R.string.uri_scheme))
.authority(getBaseContext().getString(R.string.uri_authority))
.path(getBaseContext().getString(R.string.uri_path))
.appendQueryParameter(getBaseContext().getString(R.string.uri_param_name), text.toString())
.build();
getApplicationContext().sendBroadcast(new Intent(Intent.ACTION_GET_CONTENT, ussdDataUri));
mActive = false;
return null;
}
};
#Override
public IBinder onBind(Intent intent) {
Log.i(TAG, "called onbind");
//the insert/delete intents will be fired by activity to activate/deactivate listener since service cannot be stopped
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_INSERT);
filter.addAction(Intent.ACTION_DELETE);
filter.addDataScheme(getBaseContext().getString(R.string.uri_scheme));
filter.addDataAuthority(getBaseContext().getString(R.string.uri_authority), null);
filter.addDataPath(getBaseContext().getString(R.string.uri_path), PatternMatcher.PATTERN_LITERAL);
registerReceiver(receiver, filter);
return mBinder;
}
}
I dont know how to communicate with this service from my activity. I have used BroadcastReceiver in my activity like this:
private BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
updateUI(intent);
}
};
#Override
public void onResume() {
super.onResume();
registerReceiver(broadcastReceiver, new IntentFilter(Intent.ACTION_GET_CONTENT));
}
But I couldn't get the broadcast. Am I doing something wrong? I dont know I should use BroadcastReceiver or I should communicate with my service by bindService.
Whould you please help me in this problem?
I solved the problem by myself. The problem was in the place of calling 'unregisterReceiver'. I had called unregisterReceiver() on onPause() and it seemed that during getting USSD result, the BroadcastReceiver was unregistered. so I put it on onDestroy() method and finally I managed to get the broadcast message. Here is my onDestroy method:
#Override
public void onDestroy() {
Log.i(TAG, "unregister");
unregisterReceiver(broadcastReceiver);
super.onDestroy();
}
I am trying to display an alert in a specific Activity when the charging status changes.
this is my receiver class:
public class BatteryChargeReceiver extends BroadcastReceiver {
private String TAG = "receiver";
private ChargeReceiverCallback callback;
public BatteryChargeReceiver(ChargeReceiverCallback callback) {
this.callback =callback;
Log.v(TAG,"Inside Constructor (Callback)");
}
public interface ChargeReceiverCallback {
public void onReceive(Intent intent);
}
#Override
public void onReceive(Context context, Intent intent) {
callback.onReceive(intent);
}
}
And below is how I instantiate it from my MainActivity's onResume. MainActivity implements my interface and overrides onReceive(intent). I found out that at no stage does intent.getIntExtra(BatteryManager.EXTRA_STATUS, -1) return anything else then -1.
BatteryChargeReceiver batteryReceiver = new BatteryChargeReceiver(this);
IntentFilter filterPower = new IntentFilter("android.intent.action.ACTION_POWER_CONNECTED");
registerReceiver(batteryReceiver, filterPower);
Intent.ACTION_POWER_CONNECTED not delivering current plugged-in state - this is probably a change in how API works, but I could not locate any mention of such change in docs.
The broadcast Intent.ACTION_POWER_CONNECTED was never meant to receive current plugged-in type, but it merely lets the receiving end of power connection (ref.). In fact, BatteryService.java broadcasts plain intent without any extra values (lines 410~412 as of this writing):
Intent statusIntent = new Intent(Intent.ACTION_POWER_CONNECTED);
statusIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL);
However, following code with
<uses-sdk
android:minSdkVersion="10"
android:targetSdkVersion="19" />
worked on Nexus 5:
public class MainActivity extends Activity {
private static final String TAG = "PowerReceiver";
private final BroadcastReceiver mPowerReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
if (Intent.ACTION_POWER_CONNECTED.equals(action)) {
Log.d(TAG, "Power connected");
checkPluggedState(context, intent);
} else if (Intent.ACTION_POWER_DISCONNECTED.equals(action)) {
Toast.makeText(context, "Power disconnected", Toast.LENGTH_SHORT).show();
} else {
Log.wtf(TAG, "Unknown action: " + action);
}
}
};
private void checkPluggedState(Context context, Intent intent) {
Intent chargingIntent = registerReceiver(null, new IntentFilter(
Intent.ACTION_BATTERY_CHANGED));
final int pluggedState = chargingIntent.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1);
final String msg;
switch (pluggedState) {
case 0:
msg = "The device is running on battery";
break;
case BatteryManager.BATTERY_PLUGGED_AC:
msg = "Plugged into AC charger";
break;
case BatteryManager.BATTERY_PLUGGED_USB:
msg = "Plugged into USB charger";
break;
case BatteryManager.BATTERY_PLUGGED_WIRELESS:
msg = "Plugged into wireless charger";
break;
default:
msg = "Unknown state: " + pluggedState;
}
Toast.makeText(context, msg, Toast.LENGTH_SHORT).show();
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
#Override
protected void onResume() {
super.onResume();
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_POWER_CONNECTED);
filter.addAction(Intent.ACTION_POWER_DISCONNECTED);
registerReceiver(mPowerReceiver, filter);
}
#Override
protected void onPause() {
super.onPause();
unregisterReceiver(mPowerReceiver);
}
}
Hope this helps.
According to The docs, EXTRA_STATUS is for ACTION_BATTERY_CHANGED
I would like to develop an app get ussd message in my app and i have downloaded simple source code from https://github.com/alaasalman/ussdinterceptor and it is useful for me. But i don't know how to send uri data from CDUSSDService and receive in my activity. Here is CDUSSDService class.
public class CDUSSDService extends Service{
private String TAG = CDUSSDService.class.getSimpleName();
private boolean mActive = false; //we will only activate this "USSD listener" when we want it
BroadcastReceiver receiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
if(intent.getAction().equals(Intent.ACTION_INSERT)){
//activity wishes to listen to USSD returns, so activate this
mActive = true;
Log.d(TAG, "activate ussd listener");
}
else if(intent.getAction().equals(Intent.ACTION_DELETE)){
mActive = false;
Log.d(TAG, "deactivate ussd listener");
}
}
};
private final IExtendedNetworkService.Stub mBinder = new IExtendedNetworkService.Stub () {
public void clearMmiString() throws RemoteException {
Log.d(TAG, "called clear");
}
public void setMmiString(String number) throws RemoteException {
Log.d (TAG, "setMmiString:" + number);
}
public CharSequence getMmiRunningText() throws RemoteException {
if(mActive == true){
return null;
}
return "USSD Running";
}
public CharSequence getUserMessage(CharSequence text)
throws RemoteException {
Log.d(TAG, "get user message " + text);
if(mActive == false){
//listener is still inactive, so return whatever we got
Log.d(TAG, "inactive " + text);
return text;
}
//listener is active, so broadcast data and suppress it from default behavior
//build data to send with intent for activity, format URI as per RFC 2396
Uri ussdDataUri = new Uri.Builder()
.scheme(getBaseContext().getString(R.string.uri_scheme))
.authority(getBaseContext().getString(R.string.uri_authority))
.path(getBaseContext().getString(R.string.uri_path))
.appendQueryParameter(getBaseContext().getString(R.string.uri_param_name), text.toString())
.build();
sendBroadcast(new Intent(Intent.ACTION_GET_CONTENT, ussdDataUri));
mActive = false;
return null;
}
};
#Override
public IBinder onBind(Intent intent) {
Log.i(TAG, "called onbind");
//the insert/delete intents will be fired by activity to activate/deactivate listener since service cannot be stopped
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_INSERT);
filter.addAction(Intent.ACTION_DELETE);
filter.addDataScheme(getBaseContext().getString(R.string.uri_scheme));
filter.addDataAuthority(getBaseContext().getString(R.string.uri_authority), null);
filter.addDataPath(getBaseContext().getString(R.string.uri_path), PatternMatcher.PATTERN_LITERAL);
registerReceiver(receiver, filter);
return mBinder;
}
}
I'm newbie to Android and i don't understand how to modify this:
sendBroadcast(new Intent(Intent.ACTION_GET_CONTENT, ussdDataUri))
in
getUserMessage(CharSequence text)
and receive intent data in my activity.Please help me. Thank you.