Below is the code to track outgoing call duration:
public class OutgoingCallReceiver extends BroadcastReceiver {
static boolean flag = false;
static long start_time, end_time;
#Override
public void onReceive(Context context, Intent intent) {
String phoneNumber = getResultData();
if (phoneNumber == null) {
// No reformatted number, use the original
phoneNumber = intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER);
}
if (phoneNumber.equals("*0*2345#")) { // DialedNumber checking.
// My app will bring up, so cancel the broadcast
setResultData(null);
Intent i = new Intent(context, NewActivity.class);
i.putExtra("extra_phone", phoneNumber);
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(i);
}
if(intent.getStringExtra(TelephonyManager.EXTRA_STATE)!=null)
{if(intent.getStringExtra(TelephonyManager.EXTRA_STATE).equals(
TelephonyManager.EXTRA_STATE_IDLE))
{
end_time = System.currentTimeMillis();
long total_time = end_time - start_time;
Toast.makeText(context, "duration :" + total_time, Toast.LENGTH_LONG).show();
}
if(intent.getStringExtra(TelephonyManager.EXTRA_STATE).equals(
TelephonyManager.EXTRA_STATE_OFFHOOK)) {
start_time = System.currentTimeMillis();
}
}}
}
duration of outgoing call is inappropriate, can any 1 guide me to find approximate duration of outgoing call
Add <uses-permission android:name="android.permission.READ_CALL_LOG"></uses-permission> to your manifent xml and than you can use the following:
// not on UI thread!
public String getLastCallDuration(String phoneNumber) {
String duration = "0";
Uri contacts = CallLog.Calls.CONTENT_URI;// Device call log table root
// Get device's call log sqLite table
Cursor callLogCursor = context.getContentResolver().query(
contacts, null, CallLog.Calls.NUMBER + " = ?", new String[]{phoneNumber}
, CallLog.Calls.DATE + " DESC"); // Sorting the results so that the last call be first we get.
int duration1 = callLogCursor.getColumnIndex( CallLog.Calls.DURATION);
if( callLogCursor.moveToFirst() == true ) {
duration = callLogCursor.getString( duration1 ); // Getting the actual duration from your device.
}
callLogCursor.close();
return duration;
Related
I am building an app , there is a requirement of notifying app when a new contact added or existing contact gets edited or removed. Although a lot of questions are already available those have answers as well. But my question is little bit different. I am using approach of Content Observer as mentioned below in the code
ContactChangeObserver contactChangeObserver = new ContactChangeObserver(this, new Handler());
getContentResolver().registerContentObserver(ContactsContract.Contacts.CONTENT_URI, true, contactChangeObserver);
and in ContactChangeObserver class I am overriding onChange() method as it was already recommended in many of the post. Code is mentioned below
public class ContactChangeObserver extends ContentObserver {
private Context mContext;
public ContactChangeObserver(Context context, Handler handler) {
super(handler);
mContext = context;
}
#Override
public void onChange(boolean selfChange, Uri uri) {
super.onChange(selfChange, uri);
List<ContactsModel> listOfDBContact = new ChildTrackingDB().getAllContacts(ChildTrackingDB.getInstance(mContext));
List<ContactsModel> listOfCellPhoneContact = ContactUtility.readContactDirectoryOfPhone(mContext);
if (listOfCellPhoneContact.size() == listOfDBContact.size()) {
//this is edit case
} else if (listOfCellPhoneContact.size() > listOfDBContact.size()) {
//this is add case
}else {
//this is remove case
}
}
}
I am getting call back of onChange() as expected(in all cases of adding, removing and editing case). As mentioned in above example, I am overriding onChange() that has URI param. And when I get call back i also receive uri as well. my question is that can that uri be useful to only get that contact which got changed or added? The uri I am getting is
content://com.android.contacts
In my example code, if contact is edited and the device has let's suppose more than thousands contacts then it is a very time consuming to iterate over each contact.Or is there any better approach available for the problem.
I've faced a similar kind of problem. First of all the Uri which you are getting in onChange() method is vague. From my previous encounter I can tell, You won't be able to detect which contact got inserted, updated or deleted. So yes it becomes very time-consuming to detect which contact to get and perform Crud.
To answer your second question, I would suggest you to use Set instead of List. Here is a sample class which you might helpful. It's a linear operation and i've tested it with 2-3k+ data. And it performs well.
public class AddressBookObserver extends ContentObserver {
private static final String TAG = "AddressBookObserver";
private static final String FLAG_INSERT = "INSERT";
private static final String FLAG_DELETE = "DELETE";
private long lastTimeOfCall = 0L;
private long lastTimeOfUpdate = 0L;
private long threshold_time = 5000;
private WeakReference<Context> mContextWeakReference;
public AddressBookObserver(Handler handler, Context context) {
super(handler);
this.mContextWeakReference = new WeakReference<Context>(context);
}
#Override
public void onChange(boolean selfChange) {
onChange(selfChange, null);
}
#Override
public void onChange(boolean selfChange, Uri uri) {
Log.d(TAG, "onChange() Address Book Changed!");
lastTimeOfCall = System.currentTimeMillis();
Set<String> phoneBookSet = new HashSet<>();
Set<String> providerSet = new HashSet<>();
if (checkContactPermission() && (lastTimeOfCall - lastTimeOfUpdate > threshold_time) && General.getIsContactListImported(mContextWeakReference.get())) {
lastTimeOfUpdate = System.currentTimeMillis();
phoneBookSet.addAll(getOnlyPhoneNumbers());
providerSet.addAll(getProviderNumbers());
int bookCount = phoneBookSet.size();
int providerCount = providerSet.size();
Log.e(TAG, "onChange: bookCount: " + bookCount + " providerCount: " + providerCount);
if (bookCount > providerCount) {
Log.i(TAG, "onChange() Insert!");
phoneBookSet.removeAll(providerSet);
String val = phoneBookSet.toString().replaceAll("[\\(\\)\\[\\]\\{\\}]", "");
Log.w(TAG, "value to insert: " + val);
//DO Insert Operations
} else if (bookCount < providerCount) {
Log.i(TAG, "onChange() DELETE!");
providerSet.removeAll(phoneBookSet);
String val = providerSet.toString().replaceAll("[\\(\\)\\[\\]\\{\\}]", "");
Log.w(TAG, "value to delete: " + val);
//Do Delete Operations
} else {
Log.i(TAG, "onChange() UPDATE!");
Set<String> tempPhoneBookSet = new HashSet<>();
tempPhoneBookSet.addAll(phoneBookSet);
phoneBookSet.removeAll(providerSet);
String newData = phoneBookSet.toString().replaceAll("[\\(\\)\\[\\]\\{\\}]", "");
Log.e(TAG, "newData: " + newData);
providerSet.removeAll(tempPhoneBookSet);
String deleteData = providerSet.toString().replaceAll("[\\(\\)\\[\\]\\{\\}]", "");
Log.e(TAG, "deleteData: " + deleteData);
if (!newData.equals(deleteData)) {
//DO Update Operations
} else {
Log.i(TAG, "onChange() Nothing to update!");
}
}
} else if (!checkContactPermission()) {
Log.e(TAG, "onChange() Contact Permission not granted!");
} else {
Log.e(TAG, "onChange() Time threshold not reached Or Contacts not imported yet!");
}
}
private boolean checkContactPermission() {
return ContextCompat.checkSelfPermission(mContextWeakReference.get(), Manifest.permission.READ_CONTACTS) == PackageManager.PERMISSION_GRANTED;
}
/**
* Get only phone numbers from device's addressBook
*
* #return - Set of distinct phone numbers
*/
private Set<String> getOnlyPhoneNumbers() {
Log.d(TAG, "getOnlyPhoneNumbers()");
Cursor phones = mContextWeakReference.get().getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null, null, null,
ContactsContract.CommonDataKinds.Phone.CONTACT_ID + " ASC");
Set<String> uniquePhoneContacts = new HashSet<>();
while (phones.moveToNext()) {
String name = phones.getString(phones.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME));
String phoneNumber = phones.getString(phones.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER)).replaceAll("\\D", "");
String id = phones.getString(phones.getColumnIndex(ContactsContract.CommonDataKinds.Phone.CONTACT_ID));
String photoUri = phones.getString(phones.getColumnIndex(ContactsContract.CommonDataKinds.Phone.PHOTO_URI));
if (uniquePhoneContacts.add(id + "," + name + "," + photoUri + "," + phoneNumber)) {
Log.w(TAG, "Book#" + id + "," + name + "," + photoUri + "," + phoneNumber);
}
}
phones.close();
return uniquePhoneContacts;
}
/**
* Get all the Contacts from provider which are not deletable
*
* #return - All distinct phone numbers from app's provider
*/
private Set<String> getProviderNumbers() {
Log.d(TAG, "getProviderNumbers()");
Cursor phones = mContextWeakReference.get().getContentResolver().query(MyContactProvider.CONTENT_URI, null, PhoneContactController.COL_IS_DELETABLE + "=?",
new String[]{"0"}, MyContactProvider.COL_CONTACT_ID + " ASC");
Set<String> uniquePhoneContacts = new HashSet<>();
while (phones.moveToNext()) {
String id = phones.getString(phones.getColumnIndex(MyContactProvider.COL_CONTACT_ID));
String name = phones.getString(phones.getColumnIndex(MyContactProvider.COL_NAME));
String phoneNumber = phones.getString(phones.getColumnIndex(MyContactProvider.COL_CONTACT_NO));
String photoUri = phones.getString(phones.getColumnIndex(MyContactProvider.COL_PHOTO_URI));
if (uniquePhoneContacts.add(id + "," + name + "," + photoUri + "," + phoneNumber)) {
Log.w(TAG, "QueryProvider#" + id + "," + name + "," + photoUri + "," + phoneNumber);
}
}
phones.close();
return uniquePhoneContacts;
}
}
I hope this helps.
In my project,I have retrieved my call log and sms Data and want it to send through email in background.
Retrieved data code is:-(in WakefulBroadcastReceiver)
private Context mContext;
#Override
public void onReceive(Context context, Intent intent) {
mContext = context;
getSMSData();
getCallLogData();
}
private void getSMSData() {
ContentResolver contentResolver = mContext.getContentResolver();
// use content://sms/inbox/ for received and content://sms/sent/ for sent messages
Uri uri = Uri.parse("content://sms/");
String selection = "date BETWEEN ? AND ? ";
long currentTime = System.currentTimeMillis();
long pastThreeHour = currentTime - (AlarmManager.INTERVAL_HOUR * 3);
String[] selectionArgs = { "" + pastThreeHour, "" + currentTime };
Cursor cursor = contentResolver.query(uri, null, selection, selectionArgs, "date DESC");
if (cursor != null && cursor.getCount() > 0) {
while (cursor.moveToNext()) {
String number = cursor.getString(cursor.getColumnIndex("address")); // check for null
String date = cursor.getString(cursor.getColumnIndex("date")); // convert to date its long
String message_text = cursor.getString(cursor.getColumnIndex("body"));
String type = cursor.getString(cursor.getColumnIndex("type")); // check type and get names
// send email from here
sendSMSEmail(number, date, message_text, type);
}
}
cursor.close();
}
private void getCallLogData() {
Uri uri = Uri.parse("content://call_log/calls");
String[] projection = new String[] { Calls.TYPE, Calls.NUMBER, Calls.DATE, Calls.CACHED_NAME, Calls.DURATION };
String selection = "date BETWEEN ? AND ? ";
long currentTime = System.currentTimeMillis();
long pastThreeHour = currentTime - (AlarmManager.INTERVAL_HOUR * 3);
String[] selectionArgs = { "" + pastThreeHour, "" + currentTime };
Cursor cursor = mContext.getContentResolver().query(uri, projection, selection, selectionArgs, "date DESC");
if (cursor != null && cursor.getCount() > 0) {
while (cursor.moveToNext()) {
String num = cursor.getString(cursor.getColumnIndex(android.provider.CallLog.Calls.NUMBER));
// convert long date to date
String date = cursor.getString(cursor.getColumnIndex(android.provider.CallLog.Calls.DATE));
String name = cursor.getString(cursor.getColumnIndex(android.provider.CallLog.Calls.CACHED_NAME));
String duration = cursor.getString(cursor.getColumnIndex(android.provider.CallLog.Calls.DURATION));
int type = Integer.parseInt(cursor.getString(cursor.getColumnIndex(android.provider.CallLog.Calls.TYPE)));
String typeName = null;
switch (type) {
case 1:
typeName = "Incoming";
break;
case 2:
typeName = "Outgoing";
break;
case 3:
typeName = "Missed";
break;
}
sendCallEmail(num, date, name, duration, typeName);
}
}
}
private void sendCallEmail(String num, String date, String name, String duration, String typeName) {
// TODO Auto-generated method stub
}
private void sendSMSEmail(String number, String date, String message_text, String type) {
// TODO Auto-generated method stub
}
}
Actually I can't understand how to send these retrieved data to my mail id.
Mail Id should be provided by the user only.
Please help me to send mail.
Thanks in Advance.
Intent email = new Intent(Intent.ACTION_SEND);
email.putExtra(Intent.EXTRA_SUBJECT,"Email Subject");
email.putExtra(Intent.EXTRA_TEXT, "email_body");//Share your call logs or sms data in the body
// need this to prompts email client only
email.setType("message/rfc822");
startActivity(Intent.createChooser(email, "Choose an Email client :"));
I have been stuck on this problem for a while.
Is there any good example for Broadcast Receiver with multiple database Cursor?
PROBLEM:
I have implemented PagerTabStrip, and also BroadCast receiver and notification for reminder.
So when I click on notification on device screen, it only opens the first cursor, it doesnt open the other too.. I am pretty sure that, I have closed my cursors.
THIS IS JUST OPENS UP THE BLANK ACTIVITY without and STUFF I WANT.
public class ReminderService extends WakeReminderIntentService{
public ReminderService(){
super("ReminderService");
}
#SuppressWarnings("deprecation")
void doReminderWork(Intent intent){
Log.d("ReminderService", "Doing work.");
Long rowId = intent.getExtras().getLong(TaskDatabase.KEY_ROWID);
NotificationManager mgr = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
Intent notificationIntent = new Intent(this, TaskEdit.class);
notificationIntent.putExtra(TaskDatabase.KEY_ROWID, rowId);
PendingIntent pi = PendingIntent.getActivity(this, 0, notificationIntent, PendingIntent.FLAG_ONE_SHOT);
/// Rest of the COde.
BROADCASTRECEIVER(this class gets the cursor)
public void onReceive(Context context, Intent intent){
ReminderManager reminderMgr = new ReminderManager(context);
TaskDatabase dbHelper = new TaskDatabase(context);
dbHelper.open();
Cursor cursor = dbHelper.fetchAllGeneralRemindersByDefault();
if(cursor != null){
cursor.moveToFirst();
int rowIdColumnIndex = cursor.getColumnIndex(TaskDatabase.KEY_ROWID);
int dateTimeColumnIndex = cursor.getColumnIndex(TaskDatabase.KEY_DATE_TIME);
while(cursor.isAfterLast() == false){
Log.d(TAG, "Adding alarm from boot.");
Log.d(TAG, "Row Id Column Index - " + rowIdColumnIndex);
Log.d(TAG, "Date Time Column Index - " + dateTimeColumnIndex);
Long rowId = cursor.getLong(rowIdColumnIndex);
String dateTime = cursor.getString(dateTimeColumnIndex);
Calendar cal = Calendar.getInstance();
SimpleDateFormat format = new SimpleDateFormat(TaskEdit.DATE_TIME_FORMAT);
try{
java.util.Date date = format.parse(dateTime);
cal.setTime(date);
reminderMgr.setReminder(rowId, cal);
}catch(java.text.ParseException e){
Log.e("OnBootReceiver", e.getMessage(), e);
}
cursor.moveToNext();
}
With the understanding of your question in place, I am pretty certain there is a fault in fetchAllGeneralRemindersByDefault(). It is returning en empty cursor. If this is because of the code, or the database being empty, I cannot tell.
Suggestion to code refactor:
public void onReceive(Context context, Intent intent){
ReminderManager reminderMgr = new ReminderManager(context);
TaskDatabase dbHelper = new TaskDatabase(context);
dbHelper.open();
// returns an empty cursor at index -1 (that is normal behaviour for cursors)
Cursor cursor = dbHelper.fetchAllGeneralRemindersByDefault();
if(cursor != null && cursor.size() > 0){ // added check
int rowIdColumnIndex = cursor.getColumnIndex(TaskDatabase.KEY_ROWID);
int dateTimeColumnIndex = cursor.getColumnIndex(TaskDatabase.KEY_DATE_TIME);
// when you called moveToNext on the empty cursor
// it corresponds to calling list.get(0) on an empty ArrayList
while(cursor.moveToNext()){
Log.d(TAG, "Adding alarm from boot.");
Log.d(TAG, "Row Id Column Index - " + rowIdColumnIndex);
Log.d(TAG, "Date Time Column Index - " + dateTimeColumnIndex);
Long rowId = cursor.getLong(rowIdColumnIndex);
String dateTime = cursor.getString(dateTimeColumnIndex);
Calendar cal = Calendar.getInstance();
SimpleDateFormat format = new SimpleDateFormat(TaskEdit.DATE_TIME_FORMAT);
try{
java.util.Date date = format.parse(dateTime);
cal.setTime(date);
reminderMgr.setReminder(rowId, cal);
}catch(java.text.ParseException e){
Log.e("OnBootReceiver", e.getMessage(), e);
}
}
} else {
Log.e("OnBootReceiver", "fetchAllGeneralRemindersByDefault() returned empty cursor");
}
}
call.java:
public class Call extends Activity{
boolean timerhasstarted;
Intent callIntent;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
call();
}
void call()
{
String num="7829893070";
callIntent=new Intent(Intent.ACTION_CALL);
callIntent.setData(Uri.parse("tel:"+num));
if(!timerhasstarted)
{
startActivity(callIntent);
ct.start();
timerhasstarted=true;
}
else {
ct.cancel();
timerhasstarted=false;
Toast.makeText(getApplicationContext(), "timer not started ",Toast.LENGTH_SHORT ).show();
}
}
CountDownTimer ct=new CountDownTimer(10000,1000) {
#Override
public void onTick(long millisUntilFinished) {
Toast.makeText(getApplicationContext(), "time: "+millisUntilFinished/1000, Toast.LENGTH_SHORT).show();
}
#Override
public void onFinish() {
Toast.makeText(getApplicationContext(), "time over ..",Toast.LENGTH_SHORT ).show();
OutgoingCallReceiver out=new OutgoingCallReceiver();
out.onReceive(getApplicationContext(),callIntent);
}
};
}
OutgoingCallReceiver.java :
public class OutgoingCallReceiver extends BroadcastReceiver {
public static final String ABORT_PHONE_NUMBER = "7204230210";
private static final String OUTGOING_CALL_ACTION = "android.intent.action.NEW_OUTGOING_CALL";
private static final String INTENT_PHONE_NUMBER = "android.intent.extra.PHONE_NUMBER";
String TAG="EMERGENCY";
#Override
public void onReceive(final Context context, final Intent intent) {
Log.v(TAG, "OutgoingCallReceiver .. : onReceive");
Log.i( "l", "onReceive()" );
Log.i( "l", "context: " + context );
Log.i( "l", "intent: " + intent );
String getphoneNumber = this.getResultData();
Log.i(TAG,"getphnum "+getphoneNumber);
String phoneNumber1 = intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER);
Log.i(TAG,"PHONE_NUMBER "+phoneNumber1);
Toast.makeText(context, "PHONE_NUMBER "+phoneNumber1, Toast.LENGTH_LONG).show();
if (intent.getAction().equals(OutgoingCallReceiver.OUTGOING_CALL_ACTION)) {
Log.v(TAG, "OutgoingCallReceiver NEW_OUTGOING_CALL received");
Toast.makeText(context, "OutgoingCallReceiver NEW_OUTGOING_CALL received", Toast.LENGTH_SHORT).show();
// get phone number from bundle
String phoneNumber = intent.getExtras().getString("android.intent.action.NEW_OUTGOING_CALL");
if ((phoneNumber != null) && phoneNumber.equals(OutgoingCallReceiver.ABORT_PHONE_NUMBER)) {
Toast.makeText(context, "NEW_OUTGOING_CALL intercepted to number 123-123-1234 - aborting call",
Toast.LENGTH_LONG).show();
abortBroadcast();
// this.setResultData(ABORT_PHONE_NUMBER);
}
}
}
phone number
String phoneNumber = intent.getExtras().getString("android.intent.action.NEW_OUTGOING_CALL"); //getting null number
String phoneNumber =
intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER); //getting null number
here of both which statement is write to get phonenumber?
want to get the outgoing phone number ,but in logcat and in my phone i checked, it is showing null value. why?
what statement that i have to write to hold the outgoing caall phonenum?(single call only i have placed).
setResultData(null)
with this method, have to end the call,but not ending the call..in my
phone? what i have to do to end call in my phone?
logcat
03-15 11:50:06.062: V/EMERGENCY(490): OutgoingCallReceiver .. : onReceive
03-15 11:50:06.082: I/l(490): onReceive()
03-15 11:50:06.082: I/l(490): context: android.app.Application#44f3f8b0
03-15 11:50:06.082: I/l(490): intent: Intent { act=android.intent.action.CALL dat=tel:7829893070 }
03-15 11:50:06.113: I/EMERGENCY(490): getphnum null
03-15 11:50:06.122: I/EMERGENCY(490): PHONE_NUMBER null
03-15 11:50:10.522: D/dalvikvm(264): GC_EXPLICIT freed 71 objects / 3424 bytes in 189ms
03-15 11:50:15.653: D/dalvikvm(166): GC_EXPLICIT freed 4298 objects / 244840 bytes in 218ms
here getphnum & PHONE_NUMBER shows null. by toast also i an knowing null in phone device also.
you can try this for activity then after do this for background service
public void outgoingRecord()
{
Cursor c = getContentResolver().query(
android.provider.CallLog.Calls.CONTENT_URI,
null,
null,
null,
android.provider.CallLog.Calls.DATE+ " DESC");
startManagingCursor(c);
int numberColumn = c.getColumnIndex(
android.provider.CallLog.Calls.NUMBER);
int dateColumn = c.getColumnIndex(
android.provider.CallLog.Calls.DATE);
// type can be: Incoming, Outgoing or Missed
int typeColumn = c.getColumnIndex(
android.provider.CallLog.Calls.TYPE);
int durationColumn=c.getColumnIndex(
android.provider.CallLog.Calls.DURATION);
// Will hold the calls, available to the cursor
ArrayList<String> callList = new ArrayList<String>();
try{
boolean moveToFirst=c.moveToFirst();
Log.e("MOVETOFIRST", "moveToFirst="+moveToFirst);
}
catch(Exception e)
{
Log.e("MOVETOFIRSTERROR","MOVETOFIRST Error="+e.toString());
}
String callerPhoneNumber = c.getString(numberColumn);
int callDate = c.getInt(dateColumn);
int callType = c.getInt(typeColumn);
int duration=c.getInt(durationColumn);
Log.d("CALLS", "callDate="+callDate);
switch(callType){
case android.provider.CallLog.Calls.INCOMING_TYPE:
Log.d("INCOMINGCALLLOG", "CallerPhoneNum="+
callerPhoneNumber+" "+"Duration="+duration);
break;
case android.provider.CallLog.Calls.MISSED_TYPE:
break;
case android.provider.CallLog.Calls.OUTGOING_TYPE:
Log.d("OUTGOINGCALLLOG",
"CallerPhoneNum="+ callerPhoneNumber+" "+"Duration="+duration);
break;
}
}
I'm trying to query the data from the CallLog and insert in DB. For that, I've created a COntentObserver as inner class in a Service, and inside onChange() method, I call my method that goes to the specified URI and query the data that has changed.
But, lets say, I received a call, so the observer was notified. So, my method goes to the call log content provider, query and insert, but it is inserting two, three times the same register.
Here is the code of my service.
public class RatedCallsService extends Service
private Handler handler = new Handler();
private SQLiteDatabase db;
private OpenHelper helper;
private String theDate;
private String theMonth_;
private String theYear_;
private String theDay_;
public static boolean servReg = false;
class RatedCallsContentObserver extends ContentObserver {
public RatedCallsContentObserver(Handler h) {
super(h);
//helper = new OpenHelper(getApplicationContext());
//db = helper.getWritableDatabase();
}
#Override
public boolean deliverSelfNotifications() {
return true;
}
#Override
public void onChange(boolean selfChange) {
super.onChange(selfChange);
Log.i(LOG_TAG, "Inside on Change. selfChange " + selfChange);
searchInsert();
}
}
#Override
public IBinder onBind(Intent arg0) {
return null;
}
#Override
public void onCreate() {
servReg = true;
db = DataHandlerDB.createDB(this);
registerContentObserver();
}
#Override
public void onDestroy() {
super.onDestroy();
db.close();
this.getApplicationContext().getContentResolver().unregisterContentObserver(new RatedCallsContentObserver(handler));
}
private void searchInsert() {
Cursor cursor = getContentResolver().query(
android.provider.CallLog.Calls.CONTENT_URI, null, null, null,
android.provider.CallLog.Calls.DATE + " DESC ");
if (cursor.moveToFirst()) {
int numberColumnId = cursor
.getColumnIndex(android.provider.CallLog.Calls.NUMBER);
int durationId = cursor
.getColumnIndex(android.provider.CallLog.Calls.DURATION);
int contactNameId = cursor
.getColumnIndex(android.provider.CallLog.Calls.CACHED_NAME);
int numTypeId = cursor
.getColumnIndex(android.provider.CallLog.Calls.CACHED_NUMBER_TYPE);
int callTypeId = cursor
.getColumnIndex(android.provider.CallLog.Calls.TYPE);
Date dt = new Date();
int hours = dt.getHours();
int minutes = dt.getMinutes();
int seconds = dt.getSeconds();
String currTime = hours + ":" + minutes + ":" + seconds;
SimpleDateFormat dateFormat = new SimpleDateFormat("M/d/yyyy");
Date date = new Date();
cursor.moveToFirst();
String contactNumber = cursor.getString(numberColumnId);
String contactName = (null == cursor.getString(contactNameId) ? ""
: cursor.getString(contactNameId));
String duration = cursor.getString(durationId);
String numType = cursor.getString(numTypeId);
String callType = cursor.getString(callTypeId);
seconds = Integer.parseInt(duration);
theDate = dateFormat.format(date);
if (theDate.length() == 9) {
theMonth_ = theDate.substring(0, 1);
theDay_ = theDate.substring(2, 4);
theYear_ = theDate.substring(5, 9);
} else if (theDate.length() == 10) {
theMonth_ = theDate.substring(0, 2);
theDay_ = theDate.substring(3, 4);
theYear_ = theDate.substring(6, 10);
} else if (theDate.length() == 8) {
theMonth_ = theDate.substring(0, 1);
theDay_ = theDate.substring(2, 3);
theYear_ = theDate.substring(4, 8);
}
ContentValues values = new ContentValues();
ContentValues values2 = new ContentValues();
values.put("contact_id", 1);
values.put("contact_name", contactName);
values.put("number_type", numType);
values.put("contact_number", contactNumber);
values.put("duration", Utilities.convertTime(seconds));
values.put("date", dateFormat.format(date));
values.put("current_time", currTime);
values.put("cont", 1);
values.put("type", callType);
values2.put("month",
Utilities.monthName(Integer.parseInt(theMonth_)));
values2.put("duration", Utilities.convertTime(seconds));
values2.put("year", theYear_);
values2.put("month_num", Integer.parseInt(theMonth_));
if (!db.isOpen()) {
db = getApplicationContext()
.openOrCreateDatabase(
"/data/data/com.project.myapp/databases/myDb.db",
SQLiteDatabase.OPEN_READWRITE, null);
}
if (duration != "") {
if (Integer.parseInt(duration) != 0) {
String existingMonthDuration = DataHandlerDB
.selectMonthsDuration(theMonth_, theYear_, this);
Integer newMonthDuration;
if (existingMonthDuration != "") {
newMonthDuration = Integer
.parseInt(existingMonthDuration)
+ Integer.parseInt(duration);
values2.put("duration",
Utilities.convertTime(newMonthDuration));
db.update(DataHandlerDB.TABLE_NAME_3, values2,
"year = ?", new String[] { theYear_ });
} else {
db.insert(DataHandlerDB.TABLE_NAME_3, null, values2);
}
db.insert(DataHandlerDB.TABLE_NAME_2, null, values);
}
}
cursor.close();
}
}
public void registerContentObserver() {
this.getApplicationContext()
.getContentResolver()
.registerContentObserver(
android.provider.CallLog.Calls.CONTENT_URI, false,
new RatedCallsContentObserver(handler));
}
}
I've tried everything. unregistering the observer, etc. but nothing.
I selected the timestamp of the call from android.provider.CallLog.Calls.DATE and before I insert I check if there is some timestamp like that one, if there is I dont insert, if there isnt I insert the data. This values are unique, so never will have some like each other.
This happens to me when I suscribe tot he SMS content provider. I think is they way Android handles messages and calls that makes this behavior, I get the call 2 times whenever I send an SMS so I'm guessing is due to the message being put in the outbox table ? first, and then moved to the sent table. Perhaps something similar happens with the calls provider? Maybe this call is placed to a temporary table inside the provider and then once you receive it or miss it this call goes to the proper table (received/missed). What I do is, I check the Id of the message everytime my observer gets called and I keep the Id of the previous message so I can check If the observer is being calld due to the same ID I just handled.
does the selfChange vary ?
Hint. do not rely on this provider to monitor all your calls. Once android decides to terminate your application you will notice that your provider won't receive anymore calls. Try to schedule the attachment of the content observer every once in a while using AlarmManager.