Why my ContentObserver is not being called when I send an SMS? - android

I am trying to detect when a SMS is sent. I have searched on the web and on StackOverflow and all seems to be the same solution.
I have a simple Activity that starts a Service and in the service I am trying to detect when a SMS is sent:
MainActivity.java
import android.os.Bundle;
import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.util.Log;
import android.view.Menu;
public class MainActivity extends Activity {
Intent serviceIntent;
private static MyReceiver mServiceReceiver;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
#Override
protected void onPause() {
Log.i("Status","Pause");
unregisterReceiver(mServiceReceiver);
super.onPause();
}
#Override
protected void onResume() {
Log.i("Status","Resume");
// Inicio el Servicio
serviceIntent = new Intent(MainActivity.this, TrackerService.class);
startService(serviceIntent);
// Registro el broadcast del Service para obtener los datos
mServiceReceiver = new MyReceiver();
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(TrackerService.mAction);
registerReceiver(mServiceReceiver, intentFilter);
super.onResume();
}
/**
* Receiver del Service, aqui se obtienen los datos que envia el Service
*/
private class MyReceiver extends BroadcastReceiver{
#Override
public void onReceive(Context arg0, Intent arg1) {
Log.i("ServiceReceiver", "onReceive()");
}
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
}
TrackerService.java
import android.app.Service;
import android.content.ContentResolver;
import android.content.Intent;
import android.database.ContentObserver;
import android.database.Cursor;
import android.net.Uri;
import android.os.Handler;
import android.os.IBinder;
import android.util.Log;
public class TrackerService extends Service{
// Nombre del service
public static final String mAction = "SMSTracker";
ContentResolver content;
ContentResolver contentResolver;
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i("Status","Service Start");
// ContentResolver para obtener los SMS salientes
contentResolver = this.getContentResolver();
contentResolver.registerContentObserver(Uri.parse("content://sms/out"), true, new mObserver(new Handler()));
return super.onStartCommand(intent, flags, startId);
}
/**
* Observer que obtiene los SMS salientes
*/
class mObserver extends ContentObserver {
public mObserver(Handler handler) {
super(handler);
}
#Override
public void onChange(boolean selfChange) {
super.onChange(selfChange);
Log.i("Status","onChange");
Uri uriSMS = Uri.parse("content://sms/out");
Cursor cur = contentResolver.query(uriSMS, null, null, null, null);
Log.i("SMS", "Columns: " + cur.getColumnNames());
cur.moveToNext();
String smsText = cur.getString(cur.getColumnIndex("body"));
Log.i("SMS", "SMS Lenght: " + smsText.length());
}
}
#Override
public void onDestroy() {
super.onDestroy();
Log.i("Status","Service Destroy");
}
#Override
public IBinder onBind(Intent intent) {
Log.i("Status","Service Bind");
return null;
}
}
Manifest
<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="15" />
<uses-permission android:name="android.permission.RECEIVE_SMS"></uses-permission>
<uses-permission android:name="android.permission.READ_SMS"></uses-permission>
<application
android:icon="#drawable/ic_launcher"
android:label="#string/app_name"
android:theme="#style/AppTheme" >
<activity
android:name=".MainActivity"
android:label="#string/title_activity_main" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service android:name=".TrackerService" />
</application>
According to my LogCat the service is started and I can see it in running services in my Android phone onChange() method inside my service is never called when I send an sms. Is something I am missing ?

If you replace content://sms/out by content://sms/, you'll see that the ContentObserver is not called when the SMS is sent but when one is received. Here is the code of my observer. It only enters onChange(boolean) when receiving a SMS. The protocol is "0" at that time.
So, I am struck at the same point as you :(
class SmsProviderObserver extends ContentObserver {
public SmsProviderObserver(Handler handler) {
super(handler);
}
#Override
public void onChange(boolean selfChange) {
super.onChange(selfChange);
Uri uriSms = Uri.parse("content://sms/");
ContentResolver cr = getContentResolver();
Cursor cur = cr.query(uriSms, null, null, null, null);
// this will make it point to the first record, which is the last
// SMS sent
if (!cur.moveToNext()) {
return; // weird!
}
String protocol = cur.getString(cur.getColumnIndex("protocol"));
if (protocol == null) {
// send
Log.i("SMS", "SMS SEND");
int threadId = cur.getInt(cur.getColumnIndex("thread_id"));
Log.i("SMS", "SMS SEND ID = " + threadId);
Cursor c = cr.query(
Uri.parse("content://sms/outbox/" + threadId), null,
null, null, null);
c.moveToNext();
int p = cur.getInt(cur.getColumnIndex("person"));
Log.i("SMS", "SMS SEND person= " + p);
// getContentResolver().delete(Uri.parse("content://sms/conversations/"
// + threadId), null, null);
} else {
// receive
Log.i("SMS", "SMS RECIEVE");
int threadIdIn = cur.getInt(cur.getColumnIndex("thread_id"));
cr.delete(
Uri.parse("content://sms/conversations/" + threadIdIn),
null, null);
}
}
}
And the code registering the observer:
ContentResolver contentResolver = getContentResolver();
mSmsObserverHandler = new Handler();
mSmsProviderObserver = new SmsProviderObserver(mSmsObserverHandler);
contentResolver.registerContentObserver(Uri.parse("content://sms/"), true, mSmsProviderObserver);

Ok, seems there is no way, donĀ“t know if it is my error on an Android bug but I got it working:
if( (type == 2 || type == 1) && (!lastID.contentEquals(cur.getString(cur.getColumnIndex("_id")))) ){
String protocol = cur.getString(cur.getColumnIndex("protocol"));
lastID = cur.getString(cur.getColumnIndex("_id"));
// Mensaje enviado
if(protocol == null){
Log.i("SMSStatus", "SMS Sent");
}
// Mensaje recibido
else{
Log.i("SMSStatus", "SMS received");
}
}
When an SMS is received it is type = 1, when send an SMS and its type 6, 4 and finally when it is sent is type = 2. Be carefull because sometimes the onChange method is called lots of times no matters if it is only one SMS so you should detect if it is the same SMS by checking the _id parameter.

Related

Broadcast receiver works differently on different Version of Android (4.1.1 & 4.2.2)

I have a problem that when I run my Android App which has Broadcast Receiver for WiFi. it perform differently on different version of Android OS (Like 4.1.1 and 4.2.2).
When I run it on 4.1.1 it works perfect like Broadcast receiver receive the Broad cast when Wifi state changed.(on Disconnect wifi broadcast receive I have calculate Total time for wifi connection and store it in database).
but when I run it on 4.2.2 , Broadcast Receiver calls twice when Wifi is disconnected so at that time values(i.e time for wifi connection) store in database twice.
So I need to know why this happened?Why Broadcast Receiver calls twice at the time of wifi disconnect? I need code such that it works same for all Android Versions.
Here It is my code.
Android Menifest File
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.evowifiservice"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="8" android:maxSdkVersion="17" android:targetSdkVersion="17"/>
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<application
android:allowBackup="true"
android:icon="#drawable/ic_launcher"
android:label="#string/app_name"
android:theme="#style/AppTheme" >
<activity
android:name="com.example.evowifiservice.MainActivity"
android:label="#string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<receiver android:name=".WiFiReceiver" >
<intent-filter>
<action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
<action android:name="android.net.wifi.STATE_CHANGE" />
</intent-filter>
<!--
<intent-filter>
<action android:name="android.net.wifi.WIFI_STATE_CHANGED" />
</intent-filter>
-->
</receiver>
<service android:name=".FirstService" >
</service>
</application>
</manifest>
CODE FOR Receiver which extends Broadcast Receiver(execute twice when wifi is dissconnected)
package com.example.evowifiservice;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;
import android.content.BroadcastReceiver;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.util.Log;
import android.widget.Toast;
public class WiFiReceiver extends BroadcastReceiver {
SharedPreferences sharedpref;
SharedPreferences.Editor editor;
public static String PREFS_NAME = "WifiList";
WifiInfo connectionInfo;
Calendar calendar;
String strSSID;
ArrayList<String> arySSID;
#Override
public void onReceive(Context context, Intent intent) {
// TODO Auto-generated method stub
try {
MainActivity.mDatabase = context.openOrCreateDatabase(
"WifiDetails.db", SQLiteDatabase.CREATE_IF_NECESSARY, null);
MainActivity.mDatabase.setVersion(1);
MainActivity.mDatabase.setLocale(Locale.getDefault());
MainActivity.mDatabase.setLockingEnabled(true);
arySSID = new ArrayList<String>();
Cursor c = MainActivity.mDatabase.query("tbl_SSID", null, null, null, null,null, null);
arySSID.clear();
c.moveToFirst();
while (!c.isAfterLast()) {
arySSID.add(c.getString(1));
c.moveToNext();
}
Log.d("ArySSID","Array : "+arySSID+" Size "+arySSID.size());
} catch (Exception e) {
// TODO: handle exception
}
sharedpref = context.getSharedPreferences(PREFS_NAME, 0);
if (intent.getAction().equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
NetworkInfo networkInfo = intent
.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
if (networkInfo.isConnected()) {
// Wifi is connected
WifiManager wifiManager = (WifiManager) context
.getSystemService(Context.WIFI_SERVICE);
wifiManager.getConfiguredNetworks();
connectionInfo = wifiManager.getConnectionInfo();
strSSID = connectionInfo.getSSID();
Log.d("Currently Connected with", "" + strSSID);
// Retrieve the values
String comparestr=strSSID.replaceAll("^\"|\"$", "");
editor = sharedpref.edit();
editor.putString("CurrentSSID", "" + comparestr);
editor.commit();
if (arySSID.contains(comparestr)) {
Log.d("CONTAINSSSS", "CONTAINSSSS");
Date date = new Date();
long timeInMili = date.getTime();
editor.putLong("ConnectedTimeMili", timeInMili);
editor.commit();
Log.d("Connected Time", "" + timeInMili);
SimpleDateFormat df1 = new SimpleDateFormat("dd/MM/yyyy");
String connectedDateText = df1.format(date);
Log.d("Connected Date", connectedDateText);
SimpleDateFormat df2 = new SimpleDateFormat("HH:mm:ss");
String connectedTimeText = df2.format(date);
Log.d("Connected Time", connectedTimeText);
Toast.makeText(
context,
"You are Connected with Evosys Organization # "
+ "" + connectedTimeText, Toast.LENGTH_SHORT).show();
ContentValues myval = new ContentValues();
myval.put("SSID", "" + comparestr);
myval.put("Status", "CONNECTED");
myval.put("Date", connectedDateText);
myval.put("Time", connectedTimeText);
MainActivity.mDatabase.insert("tbl_WifiDet", null, myval);
Toast.makeText(context,
"Insert while Connected Successfully",
Toast.LENGTH_LONG).show();
}
Log.d("Inetify",
"Wifi is connected: " + String.valueOf(networkInfo));
}
} else if (intent.getAction().equals(
ConnectivityManager.CONNECTIVITY_ACTION)) {
NetworkInfo networkInfo = intent
.getParcelableExtra(ConnectivityManager.EXTRA_NETWORK_INFO);
if (networkInfo.getType() == ConnectivityManager.TYPE_WIFI
&& !networkInfo.isConnected()) {
// Wifi is disconnected
strSSID = sharedpref.getString("CurrentSSID", "");
Log.d("Currently DisConnected with", "-----" + strSSID);
String comparestr=strSSID.replaceAll("^\"|\"$", "");
if (arySSID.contains(comparestr)) {
Date date = new Date();
long dctimemili = date.getTime();
Log.d("DCConnected Time", "" + dctimemili);
SimpleDateFormat df1 = new SimpleDateFormat("dd/MM/yyyy");
String DcdateText = df1.format(date);
Log.d("Disconnected Date", DcdateText);
SimpleDateFormat df2 = new SimpleDateFormat("HH:mm:ss");
String DctimeText = df2.format(date);
Log.d("Disconnected Time", DctimeText);
long ctimemili = sharedpref.getLong("ConnectedTimeMili", 0);
long diff = dctimemili - ctimemili;
Log.d("MILI SECOND You are connected upto", "" + diff);
// int seconds = (int) (diff / 1000) % 60 ;
// int minutes = (int) ((diff / (1000*60)) % 60);
// int hours = (int) ((diff / (1000*60*60)) % 24);
int mHour = (int) (diff / (1000*60*60));
int mMin = (int) ((diff % (1000*60*60)) / (1000*60));
int mSec = (int) (((diff % (1000*60*60)) % (1000*60)) / 1000);
String connectionTime = mHour + ":" + mMin + ":" + mSec;
Log.d("You are connected upto", "" + connectionTime);
ContentValues myval = new ContentValues();
myval.put("SSID", "" + comparestr);
myval.put("Status", "DISCONNECTED");
myval.put("Date", DcdateText);
myval.put("Time", DctimeText);
myval.put("Total_Connection_Time", diff);
MainActivity.mDatabase.insert("tbl_WifiDet", null, myval);
Toast.makeText(context, "Insert while D/C Successfully",
Toast.LENGTH_LONG).show();
Toast.makeText(context, "Wifi is disconnected.",
Toast.LENGTH_LONG).show();
Log.d("Inetify",
"Wifi is disconnected: "
+ String.valueOf(networkInfo));
}
}
}
}
}
Main Activity
package com.example.evowifiservice;
import java.util.ArrayList;
import java.util.Locale;
import android.app.Activity;
import android.app.Dialog;
import android.app.ProgressDialog;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
public class MainActivity extends Activity {
SharedPreferences sharedpref;
SharedPreferences.Editor editor;
public static String PREFS_NAME = "WifiList";
static SQLiteDatabase mDatabase;
ListView list_wifiDet;
Button btn_wifiDetView,btn_delete_records,btn_tot_con_time;
ArrayList<String> arySSID,aryConStatus,aryDate,aryTime;
ArrayAdapter<String> adpt;
static final int CUSTOM_DIALOG_ID1 = 1;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btn_wifiDetView=(Button) findViewById(R.id.btn_wifiDetView);
btn_delete_records=(Button) findViewById(R.id.btn_delete_records);
btn_tot_con_time=(Button) findViewById(R.id.btn_tot_con_time);
list_wifiDet=(ListView) findViewById(R.id.list_wifiDet);
try
{
mDatabase=openOrCreateDatabase("WifiDetails.db",SQLiteDatabase.CREATE_IF_NECESSARY, null);
mDatabase.setVersion(1);
mDatabase.setLocale(Locale.getDefault());
mDatabase.setLockingEnabled(true);
String s="Create table tbl_WifiDet(Id INTEGER PRIMARY KEY AUTOINCREMENT,SSID TEXT,Status TEXT,Date TEXT,Time TEXT,Total_Connection_Time INTEGER)";
mDatabase.execSQL(s);
String s1="Create table tbl_SSID(Id INTEGER PRIMARY KEY AUTOINCREMENT,SSID TEXT)";
mDatabase.execSQL(s1);
ContentValues myval = new ContentValues();
myval.put("SSID", "evosys1");
mDatabase.insert("tbl_SSID", null, myval);
myval.put("SSID", "evosys2");
mDatabase.insert("tbl_SSID", null, myval);
myval.put("SSID", "ROUTE-999");
mDatabase.insert("tbl_SSID", null, myval);
//
Toast.makeText(getApplicationContext(), "Insert SSID List Successfully",
Toast.LENGTH_LONG).show();
} catch (Exception e) {
// TODO: handle exception
}
}
#Override
protected void onStart() {
// TODO Auto-generated method stub
super.onStart();
sharedpref = getSharedPreferences(PREFS_NAME, 0);
Log.d("Starting Service", "in MainActivity");
startService(new Intent(this,FirstService.class));
btn_wifiDetView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
Cursor c = mDatabase.query("tbl_WifiDet", null, null, null, null,null, null);
c.moveToFirst();
arySSID = new ArrayList<String>();
aryConStatus = new ArrayList<String>();
aryDate = new ArrayList<String>();
aryTime = new ArrayList<String>();
arySSID.clear();
aryConStatus.clear();
aryDate.clear();
aryTime.clear();
while (!c.isAfterLast()) {
arySSID.add(c.getString(1));
aryConStatus.add(c.getString(2));
aryDate.add(c.getString(3));
aryTime.add(c.getString(4));
c.moveToNext();
}
showDialog(CUSTOM_DIALOG_ID1);
adpt = new MyCustomBaseAdapteradptWifiDet(MainActivity.this, R.layout.wifi_list,arySSID,aryConStatus,aryDate,aryTime);
list_wifiDet.setAdapter(adpt);
}
});
btn_tot_con_time.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
// String[] columns = new String[]{ "sum(Total_Connection_Time) as " + "Total_Connection_Time" };
Cursor c = mDatabase.query("tbl_WifiDet", null, null, null, null,null, null);
c.moveToLast();
long contime=c.getLong(5);
Log.d("Chking Time", "-----"+contime);
c.moveToFirst();
long result = 0;
while(!c.isAfterLast()){
Log.d("SUMMM", ""+result+"+"+c.getLong(5));
result=(result + c.getLong(5));
Log.d("Answer:", "--"+result);
c.moveToNext();
}
Log.d("Toatal Con Time", ""+result);
int mHour = (int) (result / (1000*60*60));
int mMin = (int) ((result % (1000*60*60)) / (1000*60));
int mSec = (int) (((result % (1000*60*60)) % (1000*60)) / 1000);
String connectionTime = mHour + ":" + mMin + ":" + mSec;
Log.d("Formated Toatal Con Time", "" + connectionTime);
Toast.makeText(getApplicationContext(),"Connection Duration :: "+connectionTime, Toast.LENGTH_LONG).show();
}
});
btn_delete_records.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
new MyTask().execute();
}
});
}
public class MyTask extends AsyncTask<Void, Void, Void> {
ProgressDialog dialog = new ProgressDialog(MainActivity.this);
#Override
protected void onPreExecute() {
// update the UI immediately after the task is executed
dialog.setMessage("Please wait...");
dialog.setCancelable(false);
dialog.show();
super.onPreExecute();
}
#Override
protected Void doInBackground(Void... arg0) {
// TODO Auto-generated method stub
mDatabase.delete("tbl_WifiDet",null,null);
return null;
}
#Override
protected void onPostExecute(Void result) {
// TODO Auto-generated method stub
super.onPostExecute(result);
try {
dialog.dismiss();
} catch (Exception e) {
// TODO: handle exception
}
Toast.makeText(getApplicationContext(), "Record Delete Successfully",Toast.LENGTH_LONG).show();
}
}
#Override
protected Dialog onCreateDialog(int id) {
Dialog dialog = null;
switch (id) {
case CUSTOM_DIALOG_ID1:
dialog = new Dialog(MainActivity.this);
dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
dialog.getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
dialog.setContentView(R.layout.wifi_list);
list_wifiDet = (ListView) dialog.findViewById(R.id.list_wifiDet);
break;
}
return dialog;
}
class MyCustomBaseAdapteradptWifiDet extends ArrayAdapter<String>
{
public MyCustomBaseAdapteradptWifiDet(Context context, int textViewResourceId,ArrayList<String> object, ArrayList<String> aryConStatus, ArrayList<String> aryDate, ArrayList<String> aryTime)
{
super(context, textViewResourceId, object);
}
#Override
public View getView(int position, View convertView, ViewGroup parent)
{
LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View v = inflater.inflate(R.layout.wifi_list_cell, null);
final TextView lblSSID,lblStatus,lblDate,lblTime;
lblSSID = (TextView) v.findViewById(R.id.view_ssid);
lblStatus = (TextView) v.findViewById(R.id.view_connectionStatus);
lblDate = (TextView) v.findViewById(R.id.view_Date);
lblTime = (TextView) v.findViewById(R.id.view_Time);
lblSSID.append(arySSID.get(position));
lblStatus.append(aryConStatus.get(position));
lblDate.append(aryDate.get(position));
lblTime.append(aryTime.get(position));
return v;
}
}
}
If you want this BroadcastReciever to work for change in states (connected / disconnected) of wi-fi only, then below is your solution:
Instead of listening for actions - android.net.conn.CONNECTIVITY_CHANGE & android.net.wifi.STATE_CHANGE, you should listen for the broadcast of only android.net.wifi.WIFI_STATE_CHANGED
Modify <receiver> tag in your manifest.xml file as:
<receiver android:name=".WiFiReceiver" >
<intent-filter>
<action android:name="android.net.wifi.WIFI_STATE_CHANGED" />
</intent-filter>
</receiver>
In your onReceive() method of BroadcastReceiver, handle the wi-fi state changes as below:
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) {
int newWifiState = intent.getIntExtra(
WifiManager.EXTRA_WIFI_STATE, WifiManager.WIFI_STATE_UNKNOWN);
switch(newWifiState){
case WifiManager.WIFI_STATE_ENABLED:
Toast.makeText(context, "Wi-fi is Connected", Toast.LENGTH_SHORT).show();
// Put your code here to perform actions when wi-fi is connected
break;
case WifiManager.WIFI_STATE_DISABLED:
Toast.makeText(context, "Wi-fi Disconnected", Toast.LENGTH_SHORT).show();
// Put your code here to perform actions when wi-fi is disconnected
break;
/* You can also handle cases for the states "Connecting" and "Disconnecting"
by switching for values WifiManager.WIFI_STATE_ENABLING and WifiManager.WIFI_STATE_DISABLING */
}
}
}
This works perfectly fine for me and code is executed only once when wi-fi is "Connected" or "Disconnected" :)
If you want to receive WiFi connection changes (not only disabled/enabled), you need to check only the android.net.wifi.STATE_CHANGE events (note, that it is not the same as android.net.wifi.WIFI_STATE_CHANGED, it is only sent when you disable or enable WiFi).
You can check if you're connected to a WiFi network for example by checking WifiManager.getConnectionInfo().getNetworkId(), if it's -1, then you are disconnected.
I tried it on my 4.1.2 device, it sent me the broadcasts only once.
public class WifiBroadcastReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
WifiManager wifiManager = (WifiManager) getSystemService(Context.WIFI_SERVICE);
int networkId = wifiManager.getConnectionInfo().getNetworkId();
boolean isConnected = networkId != -1;
if (isConnected)
{
Toast.makeText(context, "WiFi connected: " + wifiManager.getConnectionInfo().getSSID(), Toast.LENGTH_SHORT).show() ;
}
else
{
Toast.makeText(context, "WiFi disconnected.", Toast.LENGTH_SHORT).show() ;
}
}
}
If it sends more than 1 broadcasts on 4.2.2, you should get a bool value (myIsConnected in the example), which indicates if the last broadcast said you are connected, like this:
if (connected)
{
if (!myIsConnected)
{
myIsConnected = true ;
Toast.makeText(context, "WiFi connected: " + mWifiManager.getConnectionInfo().getSSID(), Toast.LENGTH_SHORT).show() ;
}
}
else
{
if (myIsConnected)
{
myIsConnected = false ;
Toast.makeText(context, "WiFi disconnected.", Toast.LENGTH_SHORT).show() ;
}
}
You need android.permission.ACCESS_WIFI_STATE in order to receive these broadcasts and access WifiManager.
Try checking SupplicantState of current Wifi connection

NotificationListenerService Implementation

I am trying to implement NotificationListnerService which is added in android 4.3 but I am not able to get the notification details.
My code are as below
public class MainActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(this);
mBuilder.setSmallIcon(R.drawable.ic_launcher);
mBuilder.setContentTitle("notification test");
mBuilder.setContentText("Notification text");
mBuilder.setAutoCancel(true);
Intent resultIntent = new Intent(this, ResultActivity.class);
TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);
// Adds the back stack for the Intent (but not the Intent itself)
stackBuilder.addParentStack(ResultActivity.class);
// Adds the Intent that starts the Activity to the top of the stack
stackBuilder.addNextIntent(resultIntent);
PendingIntent resultPendingIntent =
stackBuilder.getPendingIntent(
0,
PendingIntent.FLAG_UPDATE_CURRENT
);
mBuilder.setContentIntent(resultPendingIntent);
NotificationManager manager = (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);
manager.notify(1, mBuilder.build());
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
}
public class NotificationListenerTesting extends NotificationListenerService{
public static String TAG = "NotificationListenerTesting";
//private StatusBarNotification[] mStatusBarNotification;
#Override
public void onCreate(){
super.onCreate();
Log.d(TAG, "Inside on create");
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onNotificationPosted(StatusBarNotification sbn) {
TAG = "onNotificationPosted";
Log.d(TAG, "id = " + sbn.getId() + "Package Name" + sbn.getPackageName() +
"Post time = " + sbn.getPostTime() + "Tag = " + sbn.getTag());
}
#Override
public void onNotificationRemoved(StatusBarNotification sbn) {
TAG = "onNotificationRemoved";
Log.d(TAG, "id = " + sbn.getId() + "Package Name" + sbn.getPackageName() +
"Post time = " + sbn.getPostTime() + "Tag = " + sbn.getTag());
}
}
Android manifest file is
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.notificationtest"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="18" />
<application
android:allowBackup="true"
android:icon="#drawable/ic_launcher"
android:label="#string/app_name"
android:theme="#style/AppTheme" >
<activity
android:name="com.example.notificationtest.MainActivity"
android:label="#string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name="com.example.notificationtest.ResultActivity"></activity>
<service android:name="com.example.notificationtest.NotificationListenerTesting"
android:label="notification"
android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE">
<intent-filter>
<action android:name="android.service.notification.NotificationListenerService"/>
</intent-filter>
</service>
</application>
</manifest>
but after notification click or on notification post NotificationListenerService is not getting called , Whats wrong in this or did I miss somthing ? How to implement it ?
You need to grant access to your app to read notifications:
"Settings > Security > Notification access" and check your app.
Inside the NotificationListenerService you need a looper to communicate with GUI thread so you can create a broadcast to handle the GUI interaction.
Hope this example will help you.
At least one problem with your code is that your implementation of onBind()
There is no necessity to override this method. But if you must, then at least return the IBinder returned by the superclass.
#Override
public IBinder onBind(Intent intent) {
return super.onBind(intent);
}
It might be a bit late. But a few months ago I also struggled with making NotificationListenerService work.
Since then I`ve learned how to implement it and felt like building a implementation tutorial to help others who went through the same as me
If anyone is interested, check the project here:
https://github.com/Chagall/notification-listener-service-example
Hope it helps someone who is struggling with it.
I know its too late to answer the question , but as I was not able to find Settings > Sound and notifications -> Notification access, so I directly allow access of notifications to my app by firing this intent:
startActivity(new Intent(Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS));
I ran into the same problem and have found some clues for that.
NotificationListenerService may not be running unless you call bindService() to start it.
Sometimes it started automatically when you enabled "Notification access" and sometimes it's not.
I am doing the same thing as in GitHub, but still I am not going into the notification class.
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.IBinder;
import android.service.notification.NotificationListenerService;
import android.service.notification.StatusBarNotification;
import android.util.Log;
/**
* #author dinesh
*
*/
public class UserNotificationService extends NotificationListenerService {
private String TAG = "UserNotificationService";
UserNotificationServiceReceiver notfRcvr;
#Override
public void onNotificationRemoved(StatusBarNotification sbn) {
Log.i(TAG,"********** onNotificationRemoved");
Log.i(TAG,"ID :" + sbn.getId() + "\t" + sbn.getNotification().tickerText +"\t" + sbn.getPackageName());
Intent i = new Intent("de.tu.darmstadt.moodsense.services.Notification");
i.putExtra("notification event", "On notification removed");
sendBroadcast(i);
}
#Override
public IBinder onBind(Intent intent) {
return super.onBind(intent);
}
#Override
public void onNotificationPosted(StatusBarNotification sbn) {
Log.i(TAG,"********** onNotificationPosted");
Log.i(TAG,"ID :" + sbn.getId() + "\t" + sbn.getNotification().tickerText + "\t" + sbn.getPackageName());
Intent i = new Intent("de.tu.darmstadt.moodsense.services.Notification");
i.putExtra("notification event", "On notification posted");
sendBroadcast(i);
}
#Override
public void onCreate() {
super.onCreate();
notfRcvr = new UserNotificationServiceReceiver();
IntentFilter filter = new IntentFilter();
filter.addAction("de.tu.darmstadt.moodsense.services.Notification");
registerReceiver(notfRcvr, filter);
}
#Override
public void onDestroy() {
super.onDestroy();
unregisterReceiver(notfRcvr);
}
class UserNotificationServiceReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
// TODO Auto-generated method stub
if(intent.getStringExtra("command").equals("clearall")) {
UserNotificationService.this.cancelAllNotifications();
}
}
}
}
import java.lang.Thread.State;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import twitter4j.Status;
import twitter4j.TwitterException;
import de.tu.darmstadt.moodsense.R;
import de.tu.darmstadt.moodsense.app.UserMood;
import de.tu.darmstadt.moodsense.constants.Constants;
import de.tu.darmstadt.moodsense.util.MqttMoodClient;
import de.tu.darmstadt.moodsense.util.TwitterMoodUtils;
import de.tu.darmstadt.moodsense.util.TwitterUtils;
import android.app.AlarmManager;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.IBinder;
import android.os.SystemClock;
import android.preference.PreferenceManager;
import android.support.v4.app.NotificationCompat;
import android.util.Log;
/**
* #author dinesh
* Added for V1.1
* Code style based on : https://newcircle.com/s/post/1049/
* tutorial_services_part_1_android_bootcamp_series_2012
*
*/
public class UserMoodService extends Service{
static final String TAG = "UserMoodService";
public static boolean userMoodSet = false;
//declarations for twitter
private SharedPreferences prefs;
SharedPreferences userPref;
String userTwitterMood = "";
String worldTwitterMood = "";
String screenName, userName;
int m_counter;
long shortMinutes;
boolean m_enterMood;
int m_myMood;
int m_moodIntensity;
MqttMoodClient mqc;
TwitterMoodUtils tmu;
Calendar cal = Calendar.getInstance();
private static final int MY_NOTIFICATION_ID=1;
NotificationManager notificationManager;
Notification myNotification;
UserMoodNotificationReceiver usrMoodNotfnnRcvr;
public UserMoodService() {
// TODO Auto-generated constructor stub
mqc = new MqttMoodClient();
tmu = new TwitterMoodUtils();
}
public void reset() {
m_myMood = Constants.NUM_MOOD_TYPES;
m_moodIntensity = Constants.MILD;
m_enterMood = false;
m_counter = 0;
}
#Override
public IBinder onBind(Intent arg0) {
return null;
}
#Override
public void onTaskRemoved(Intent rootIntent) {
// TODO Auto-generated method stub
Intent restartService = new Intent(getApplicationContext(),this.getClass());
restartService.setPackage(getPackageName());
PendingIntent restartServicePI = PendingIntent.getService(getApplicationContext(),
1, restartService, PendingIntent.FLAG_ONE_SHOT);
AlarmManager alarmService = (AlarmManager)getApplicationContext().getSystemService(Context.ALARM_SERVICE);
alarmService.set(AlarmManager.ELAPSED_REALTIME, SystemClock.elapsedRealtime() +100, restartServicePI);
}
/** (non-Javadoc)
* #see android.app.Service#onCreate()
*/
#Override
public void onCreate() {
Log.d(TAG, "OnCreation");
//super.onCreate();
usrMoodNotfnnRcvr = new UserMoodNotificationReceiver();
IntentFilter filter = new IntentFilter();
filter.addAction("Notofication Obj");
registerReceiver(usrMoodNotfnnRcvr, filter);
}
/** (non-Javadoc)
* #see android.app.Service#onStartCommand(android.content.Intent, int, int)
*/
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG, "OnStartCommand");
try {
ConnectivityManager cm =
(ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo netInfo = cm.getActiveNetworkInfo();
if (netInfo != null && netInfo.isConnectedOrConnecting()) {
Log.d(TAG,"Twitter loop enter");
//Check the user's mood on twitter
computeMoodOnTwitter();
if(userMoodSet) {
Log.d(TAG, "user's twitter mood" + userTwitterMood);
} /*else {
Log.d(TAG, "user mood not set, world mood computation started");
//If user's mood is not set then check for world's mood
}*/
}
} catch(Exception e) {
e.printStackTrace();
}
return START_STICKY;
}
private void computeMoodOnTwitter() {
// TODO Auto-generated method stub
reset();
this.prefs = PreferenceManager.getDefaultSharedPreferences(this);
Thread twitterThread;
twitterThread = new Thread() {
public void run() {
//userMoodSet = false;
Log.d(TAG, "User mood is :: "+ userMoodSet);
/*try {
String usrNme = TwitterUtils.getUserName(prefs).toString();
List<Status> statuses = TwitterUtils.getHomeTimeline(prefs);
for(int i=0; i < Constants.NUM_MOOD_TYPES; i++) {
for (int j =0 ; j < Constants.NUM_MOOD_TYPES; j++)
{
for (twitter4j.Status status : statuses) {
//Check if the status is from the user and it matches our mood strings
if(status.getText().contains(Constants.searchStrings[i][j])
&& (status.getUser().getScreenName().equals(usrNme))) {
Date date = status.getCreatedAt();
long Minutes = tmu.getMinuteDifference(cal.getTime(), date);
if((Constants.sdf.format(date).equals(Constants.sdf.format(cal.getTime())))) {
//Increment counter for each tweet
Log.d(TAG, "User has a status");
userMoodSet = true;
m_counter++;
//track time for the first tweet
if(m_counter == 1) {
shortMinutes = Minutes;
m_moodIntensity = computeMoodIntensity(i,j);
m_myMood = i;
Log.d(TAG, "intensity + mood" + m_moodIntensity +","+ m_myMood);
Log.d(TAG,"SocialMood:: mymood- " + Constants.moodIntensityNames[m_moodIntensity]+
" "+ Constants.moodNames[m_myMood]);
Log.d(TAG, "SocialMood:: status-"+status.getText());
} else //counter more than 1 //track time for the later tweets
{ //take latest tweet only if logged minutes is shorter than earlier minutes
if(Minutes < shortMinutes) {
shortMinutes = Minutes;
Log.d(TAG, "Called compute mood_intensity :: "+ m_counter);
m_moodIntensity = computeMoodIntensity(i,j);
m_myMood = i;
}
}
}
}
}
}
}
} catch(TwitterException te) {
userMoodSet = false;
Log.d(TAG, "Unable to process twitter get requests "+te.getErrorCode()+ " "+ te.getErrorMessage());
} catch (Exception e) {
// TODO Auto-generated catch block
Log.d(TAG,"Error msg");
e.printStackTrace();
}*/
try {
stopThread(this);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
};
twitterThread.start();
}
public int computeMoodIntensity(int m_detect, int m_type) {
// TODO Auto-generated method stub
for(int j=0; j < Constants.m_extreme.length; j++) {
if(m_type == Constants.m_extreme[m_detect][j])
return Constants.EXTREME;
}
for(int j=0; j < Constants.m_considerable.length; j++) {
if(m_type == Constants.m_considerable[m_detect][j])
return Constants.CONSIDERABLE;
}
return Constants.MILD;
}
private String userStatusToMood(int myMood) {
// TODO Auto-generated method stub
String userMood = Constants.userNoTwitter;
if(m_myMood >= Constants.NUM_MOOD_TYPES) {
m_enterMood = true;
Log.d(TAG, userMood);
//Unreachable code - maybe we need to delete this ?? QNS
/*Intent i = new Intent(UserMoodService.this,UserMood.class);
i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(i);*/
}
else {
userMood = "User mood is "+ Constants.moodNames[m_myMood];
userTwitterMood = Constants.moodIntensityNames[m_moodIntensity]
+" "+Constants.moodNames[m_myMood];
Log.d(TAG, "Updated user mood is "+userTwitterMood);
//call MQTT
MqttMoodClient mqc = new MqttMoodClient();
mqc.setupMqttClient();
mqc.sendMessage(userTwitterMood);
}
return userMood;
}
private void stopThread(Thread theThread) throws Exception {
// method to stop the worker thread once the process needed to do has been completed
Log.d(TAG,"userMoodSet :: "+ userMoodSet);
if (theThread != null)
{
theThread = null;
Log.d(TAG, "Execution complete inside stop thread");
if(userMoodSet)
userStatusToMood(m_myMood);
}
if(!userMoodSet) {
Log.d(TAG, "In world thread");
//Call world Service
//WorldMoodService worldService = new WorldMoodService();
//worldService.computeWorldMood(this);
//show notification!!
/**
* V1.1
* #author dinesh
* Code adapted from : http://android-er.blogspot.de/2013/06/
* start-activity-once-notification-clicked.html
*/
Intent myIntent = new Intent(UserMoodService.this, UserMood.class);
PendingIntent pendingIntent = PendingIntent.getActivity(
UserMoodService.this,
0,
myIntent,
Intent.FLAG_ACTIVITY_NEW_TASK);
myNotification = new NotificationCompat.Builder(UserMoodService.this)
.setContentTitle("MoodSense notification")
.setContentText("Please enter mood to play music as per your mood")
.setTicker("Please enter mood to play music as per your mood")
.setWhen(System.currentTimeMillis())
.setContentIntent(pendingIntent)
.setDefaults(Notification.DEFAULT_SOUND)
.setAutoCancel(true)
.setSmallIcon(R.drawable.app_icon)
.build();
notificationManager =
(NotificationManager)UserMoodService.this.
getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(MY_NOTIFICATION_ID, myNotification);
} else if (userMoodSet) {
Intent i = new Intent("de.tu.darmstadt.moodsense.services.Notification");
i.putExtra("command", "clear all");
sendBroadcast(i);
}
}
public class UserMoodNotificationReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
String temp = intent.getStringExtra("notification event");
}
}
/** (non-Javadoc)
* #see android.app.Service#onDestroy()
*/
#Override
public void onDestroy() {
Log.d(TAG, "OnDeletion");
super.onDestroy();
}
}
Update 2020 Kotlin And AndroidX
package com.hiteshsahu.notificationlistener.notification
import android.app.Activity
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.service.notification.NotificationListenerService
import android.service.notification.StatusBarNotification
import android.util.Log
import androidx.localbroadcastmanager.content.LocalBroadcastManager
class CustomNotificationListenerService : NotificationListenerService() {
private var commandFromUIReceiver: CommandFromUIReceiver? = null
override fun onCreate() {
super.onCreate()
// Register broadcast from UI
commandFromUIReceiver = CommandFromUIReceiver()
val filter = IntentFilter()
filter.addAction(READ_COMMAND_ACTION)
registerReceiver(commandFromUIReceiver, filter)
}
/**
* New Notn Added Callback
*/
override fun onNotificationPosted(newNotification: StatusBarNotification) {
Log.i(
TAG,
"-------- onNotificationPosted(): " + "ID :" + newNotification.id + "\t" + newNotification.notification.tickerText + "\t" + newNotification.packageName
)
sendResultOnUI("onNotificationPosted :" + newNotification.packageName + "\n")
}
/**
* Notn Removed callback
*/
override fun onNotificationRemoved(removedNotification: StatusBarNotification) {
Log.i(
TAG,
"-------- onNotificationRemoved() :" + "ID :" + removedNotification.id + "\t" + removedNotification.notification.tickerText + "\t" + removedNotification.packageName
)
sendResultOnUI("onNotificationRemoved: " + removedNotification.packageName + "\n")
}
internal inner class CommandFromUIReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
if (intent.getStringExtra(COMMAND_KEY) == CLEAR_NOTIFICATIONS)
// remove Notns
cancelAllNotifications()
else if (intent.getStringExtra(COMMAND_KEY) == GET_ACTIVE_NOTIFICATIONS)
// Read Notns
fetchCurrentNotifications()
}
}
/**
* Fetch list of Active Notns
*/
private fun fetchCurrentNotifications() {
sendResultOnUI("===== Notification List START ====")
val activeNotnCount = this#CustomNotificationListenerService.activeNotifications.size
if (activeNotnCount > 0) {
for (count in 0..activeNotnCount) {
val sbn = this#CustomNotificationListenerService.activeNotifications[count]
sendResultOnUI("#" + count.toString() + " Package: " + sbn.packageName + "\n")
}
} else {
sendResultOnUI("No active Notn found")
}
sendResultOnUI("===== Notification List END====")
}
// sendMessage success result on UI
private fun sendResultOnUI(result: String?) {
val resultIntent = Intent(UPDATE_UI_ACTION)
resultIntent.putExtra(RESULT_KEY, Activity.RESULT_OK)
resultIntent.putExtra(RESULT_VALUE, result)
LocalBroadcastManager.getInstance(this).sendBroadcast(resultIntent)
}
override fun onDestroy() {
super.onDestroy()
unregisterReceiver(commandFromUIReceiver)
}
companion object {
const val TAG = "NotificationListener"
//Update UI action
const val UPDATE_UI_ACTION = "ACTION_UPDATE_UI"
const val READ_COMMAND_ACTION = "ACTION_READ_COMMAND"
// Bundle Key Value Pair
const val RESULT_KEY = "readResultKey"
const val RESULT_VALUE = "readResultValue"
//Actions sent from UI
const val COMMAND_KEY = "READ_COMMAND"
const val CLEAR_NOTIFICATIONS = "clearall"
const val GET_ACTIVE_NOTIFICATIONS = "list"
}
}
Complete Project
https://github.com/hiteshsahu/Android-Notification-Demo
The notification that you build does not have "tickerText." I have found that if the notification does not have tickerText, onNotificationPosted does not get called.
In your code add mBuilder.setTicker( "your text here" ).
onNotificationPosted should now be called assuming that the rest of the NotificationListenerService set up is copacetic.

NFC Intent Filter - Sending Message Non-Main Activity

I am sure this is simple but I cannot figure it out. All I am trying to do is send a message via NFC. The code I have work perfectly if I am sending it to the main activity, but I don't know how to send it to a different activity. I have looked over both the NFC and Intent Filter articles on the Android Developer pages but am still not sure exactly how to do this. I am trying to send it to a NFC activity, I will post my manifest and NFC class below.
Manifest:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.justbaumdev.tagsense"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="16"
android:targetSdkVersion="15" />
<uses-permission android:name="android.permission.NFC" />
<uses-feature android:name="android.hardware.nfc" />
<application
android:icon="#drawable/ic_launcher"
android:label="#string/app_name"
android:theme="#android:style/Theme.Holo" >
<activity
android:name=".TagSense"
android:label="#string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name="com.justbaumdev.tagsense.NFC" android:exported="false">
<intent-filter>
<action android:name="android.nfc.action.NDEF_DISCOVERED" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="application/com.justbaumdev.tagsense" />
</intent-filter>
</activity>
</application>
</manifest>
NFC Class:
package com.justbaumdev.tagsense;
import org.json.JSONArray;
import org.json.JSONException;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.net.wifi.WifiManager;
import android.nfc.NdefMessage;
import android.nfc.NdefRecord;
import android.nfc.NfcAdapter;
import android.nfc.NfcAdapter.CreateNdefMessageCallback;
import android.nfc.NfcAdapter.OnNdefPushCompleteCallback;
import android.nfc.NfcEvent;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.Parcelable;
import android.widget.Toast;
public class NFC extends Activity implements CreateNdefMessageCallback, OnNdefPushCompleteCallback {
private NfcAdapter mNfcAdapter;
private static final int MESSAGE_SENT = 1;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.nfc);
mNfcAdapter = NfcAdapter.getDefaultAdapter(this); // Check for available NFC Adapter
if (mNfcAdapter == null)
{
Toast.makeText(this, "NFC is not available", Toast.LENGTH_LONG).show();
finish();
return;
}
else
{
mNfcAdapter.setNdefPushMessageCallback(this, this); // Register callback to set NDEF message
mNfcAdapter.setOnNdefPushCompleteCallback(this, this); // Register callback to listen for message-sent success
}
}
#Override
public void onResume() {
super.onResume();
// Check to see that the Activity started due to an Android Beam
if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(getIntent().getAction())) {
processIntent(getIntent());
}
}
#Override
public void onNewIntent(Intent intent) {
// onResume gets called after this to handle the intent
setIntent(intent);
}
#Override
public NdefMessage createNdefMessage(NfcEvent event) {
WifiManager wm = (WifiManager) getSystemService(Context.WIFI_SERVICE);
String mac = wm.getConnectionInfo().getMacAddress();
String newMac = mac.substring(0, 2);
mac = mac.substring(2);
int hex = Integer.parseInt(newMac, 16) + 0x2;
newMac = Integer.toHexString(hex);
String text = newMac + mac;
NdefMessage msg = new NdefMessage(NdefRecord.createMime(
"application/com.justbaumdev.tagsense", text.getBytes()));
return msg;
}
/**
* Implementation for the OnNdefPushCompleteCallback interface
*/
#Override
public void onNdefPushComplete(NfcEvent arg0) {
// A handler is needed to send messages to the activity when this
// callback occurs, because it happens from a binder thread
mHandler.obtainMessage(MESSAGE_SENT).sendToTarget();
}
/** This handler receives a message from onNdefPushComplete */
private final Handler mHandler = new Handler() {
#Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MESSAGE_SENT:
Toast.makeText(getApplicationContext(), "Message sent!", Toast.LENGTH_LONG).show();
break;
}
}
};
/**
* Parses the NDEF Message from the intent and prints to the TextView
*/
//TODO Currently overwrites any previously saved mac addresses. Get FB ID as value. Auto end activity.
void processIntent(Intent intent) {
Parcelable[] rawMsgs = intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES);
// only one message sent during the beam
NdefMessage msg = (NdefMessage) rawMsgs[0];
// record 0 contains the MIME type, record 1 is the AAR, if present
//textView.setText(new String(msg.getRecords()[0].getPayload()));
String payload = new String(msg.getRecords()[0].getPayload());
Toast.makeText(this, new String(msg.getRecords()[0].getPayload()), Toast.LENGTH_LONG).show();
SharedPreferences appData = getSharedPreferences("appData", Context.MODE_PRIVATE);
SharedPreferences.Editor editor = appData.edit();
String addresses = appData.getString("mac_address", null);
if(addresses==null)
{
JSONArray addressArray = new JSONArray();
addressArray.put(payload);
addresses = addressArray.toString();
}
else
{
try {
if(!addresses.contains(payload))
{
JSONArray addressArray = new JSONArray(addresses);
addressArray.put(payload);
addresses = addressArray.toString();
}
} catch (JSONException e) {
Toast.makeText(this, "Error adding new friend. Please try again.", Toast.LENGTH_SHORT).show();
}
}
editor.putString("mac_address", addresses);
editor.commit();
}
}
Thanks for your help.
Remove the attribute android:exported="false". See also https://stackoverflow.com/a/12719621/1202968

Why is the ContentObserver called multiple times?

I have following ContentObserver implementation for receiving and writing SMS, but it is called multiple times.
Code:
public class SMSObserverActivity extends Activity {
protected MyContentObserver observer = null;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
String url = "content://mms-sms/";
Uri uri = Uri.parse(url);
observer = new MyContentObserver(new Handler());
getContentResolver().registerContentObserver(uri, true, observer);
}
#Override
protected void onDestroy(){
super.onDestroy();
getContentResolver().unregisterContentObserver(observer);
}
class MyContentObserver extends ContentObserver {
ContentValues values = new ContentValues();
Handler handler;
public MyContentObserver(Handler handler){
super(handler);
this.handler = handler;
}
#Override
public boolean deliverSelfNotifications(){
return false;
}
#Override
public void onChange(boolean arg0){
super.onChange(arg0);
Log.v("SMS", "Notification on SMS observer");
values.put("status", 5);
Message msg = new Message();
msg.obj = "xxxxxxxxxx";
int threadId = 0;
handler.sendMessage(msg);
Uri uriSMSURI = Uri.parse("content://sms/");
Cursor cur =
getContentResolver().query(uriSMSURI, null, null, null,
null);
cur.moveToNext();
Log.e("sms", cur.getString(4)+" "+cur.getString(11));
}
}
}
Manifest:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.test"
android:versionCode="1"
android:versionName="1.0">
<uses-sdk android:minSdkVersion="8" />
<uses-permission android:name="android.permission.READ_SMS"></uses-permission>
<uses-permission android:name="android.permission.WRITE_SMS"></uses-permission>
<application android:icon="#drawable/icon" android:label="#string/app_name">
<activity android:name=".SMSObserverActivity"
android:label="#string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
Why is it called multiple times?
EDIT:
There was the idea that the problem is caused by the missing unregisterContentObserver, but it makes no difference.
This is occurring because you are registering your content observer for the entire SMS database. So your content observer gets notified each time a table entry in the database gets updated.
In this case when a message is sent for example around 7 tables entries get updated so your content observer gets notified 7 times.
Since I'm only interested if a message is sent I've changed to only observe the queued messages and this means my observer always gets notified exactly three times so I have implemented code to protect against that.
There are likely to be some other issues such as multi recipient or multi part messages but the basics work so far.
To avoid sending multiple sms by content observer try this
public class SmsObserver extends ContentObserver {
SharedPreferences trackMeData;
private Context context;
private static int initialPos;
private static final String TAG = "SMSContentObserver";
private static final Uri uriSMS = Uri.parse("content://sms/sent");
public SmsObserver(Handler handler, Context ctx) {
super(handler);
context = ctx;
trackMeData = context.getSharedPreferences("LockedSIM", 0);
initialPos = getLastMsgId();
}
#Override
public void onChange(boolean selfChange) {
super.onChange(selfChange);
queryLastSentSMS();
}
public int getLastMsgId() {
Cursor cur = context.getContentResolver().query(uriSMS, null, null, null, null);
cur.moveToFirst();
int lastMsgId = cur.getInt(cur.getColumnIndex("_id"));
Log.i(TAG, "Last sent message id: " + String.valueOf(lastMsgId));
return lastMsgId;
}
protected void queryLastSentSMS() {
new Thread(new Runnable() {
#Override
public void run() {
Cursor cur =
context.getContentResolver().query(uriSMS, null, null, null, null);
if (cur.moveToNext()) {
try {
String body = cur.getString(cur.getColumnIndex("body"));
if (initialPos != getLastMsgId()) {
String receiver = cur.getString(cur.getColumnIndex("address"));
Log.i("account", myDeviceId);
Log.i("date", day + "-" + month + "-" + year + " "
+ hour + ":" + minute + ":" + seconde);
Log.i("sender", myTelephoneNumber);
Log.i("receiver", receiver );
// Then, set initialPos to the current position.
initialPos = getLastMsgId();
sendsmstoph(receiver, body);
}
} catch (Exception e) {
// Treat exception here
}
}
cur.close();
}
}).start();
}
If you want to have your observer enabled only when the activity is in active state, I advise you to move registerContentObserver() and unregisterContentObserver() to methods onResume() and onPause() respectively. onDestroy() may not be called if your application exits, but onPause() is guaranteed to be.

Block outgoing SMS by contentObserver

I want to block SMS by contentObserver. For that I want to get the phone number of the SMS first. What do I do to get the number? This is the code that I have, just counting the number of SMS.
package com.SMSObserver4;
import android.app.Activity;
import android.database.ContentObserver;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.provider.Contacts;
import android.provider.Contacts.People.Phones;
public class SMSObserver4 extends Activity {
/** Called when the activity is first created. */
private static final String Address = null;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
setReceiver();
}
private SmsSentCounter smsSentObserver = new SmsSentCounter(new Handler());
private int sms_sent_counter = 0;
private void setReceiver() {
this.getContentResolver().registerContentObserver(
Uri.parse("content://sms"), true, smsSentObserver);
}
class SmsSentCounter extends ContentObserver {
public SmsSentCounter(Handler handler) {
super(handler);
// TODO Auto-generated constructor stub
}
#Override
public void onChange(boolean selfChange) {
// TODO Auto-generated method stub
try{
System.out.println ("Calling onChange new");
super.onChange(selfChange);
Cursor sms_sent_cursor = SMSObserver4.this.managedQuery(Uri
.parse("content://sms"), null, "type=?",
new String[] { "2" }, null);
if (sms_sent_cursor != null) {
if (sms_sent_cursor.moveToFirst()) {
sms_sent_counter++;
System.out.println("test" + sms_sent_counter);
}
}
Uri phoneUri = Uri.withAppendedPath(Contacts.Phones.CONTENT_FILTER_URL, Address);
if (phoneUri != null) {
Cursor phoneCursor = getContentResolver().query(phoneUri, new String[] {Phones._ID, Contacts.Phones.PERSON_ID}, null, null, null);
if (phoneCursor.moveToFirst()) {
long person = phoneCursor.getLong(1); // this is the person ID you need
}
}
}catch(Exception e)
{}
}
}
}
I have done a lot of tests and I found this to be impossible. That's because when the messaging application inserts a new record in the SMS content provider, it is already trying to send the SMS. So, even if you detect the SMS in the content://sms/outbox URI, it will be too late to stop it. In fact, there's no way to stop it... it all depends on the SMS application, which you can't interrupt.
Nope, you cant. Observer will comeinto affect after dataset has been modified, by that time sms will already be on the way for delivery. Infact by any means you cant block outgoing sms.

Categories

Resources