diff --git a/astrid/AndroidManifest.xml b/astrid/AndroidManifest.xml
index f21d2e1ac..b51db00b3 100644
--- a/astrid/AndroidManifest.xml
+++ b/astrid/AndroidManifest.xml
@@ -32,6 +32,8 @@
+
+
@@ -198,6 +200,12 @@
+
+
+
+
+
+
@@ -270,6 +278,10 @@
+
+
+
diff --git a/astrid/plugin-src/com/todoroo/astrid/calls/MissedCallActivity.java b/astrid/plugin-src/com/todoroo/astrid/calls/MissedCallActivity.java
new file mode 100644
index 000000000..69e737379
--- /dev/null
+++ b/astrid/plugin-src/com/todoroo/astrid/calls/MissedCallActivity.java
@@ -0,0 +1,195 @@
+package com.todoroo.astrid.calls;
+
+import java.io.InputStream;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.content.ContentUris;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.res.Resources;
+import android.graphics.BitmapFactory;
+import android.net.Uri;
+import android.os.Bundle;
+import android.provider.ContactsContract;
+import android.text.TextUtils;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import com.timsu.astrid.R;
+import com.todoroo.andlib.service.Autowired;
+import com.todoroo.andlib.service.DependencyInjectionService;
+import com.todoroo.andlib.utility.AndroidUtilities;
+import com.todoroo.andlib.utility.DialogUtilities;
+import com.todoroo.andlib.utility.Preferences;
+import com.todoroo.astrid.data.Task;
+import com.todoroo.astrid.reminders.NotificationFragment.SnoozeDialog;
+import com.todoroo.astrid.reminders.Notifications;
+import com.todoroo.astrid.reminders.SnoozeCallback;
+import com.todoroo.astrid.service.TaskService;
+import com.todoroo.astrid.service.ThemeService;
+
+public class MissedCallActivity extends Activity {
+
+ public static final String EXTRA_NUMBER = "number"; //$NON-NLS-1$
+ public static final String EXTRA_NAME = "name"; //$NON-NLS-1$
+ public static final String EXTRA_TIME = "time"; //$NON-NLS-1$
+ public static final String EXTRA_CONTACT_ID = "contactId"; //$NON-NLS-1$
+
+ private static final String PREF_IGNORE_PRESSES = "missedCallsIgnored"; //$NON-NLS-1$
+
+ // Prompt user to ignore all missed calls after this many ignore presses
+ private static final int IGNORE_PROMPT_COUNT = 3;
+
+ @Autowired private TaskService taskService;
+
+ private final OnClickListener dismissListener = new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ finish();
+ AndroidUtilities.callOverridePendingTransition(MissedCallActivity.this, 0, android.R.anim.fade_out);
+ }
+ };
+
+ private final OnClickListener ignoreListener = new OnClickListener() {
+ @Override
+ public void onClick(final View v) {
+ // Check for number of ignore presses
+ int ignorePresses = Preferences.getInt(PREF_IGNORE_PRESSES, 0);
+ ignorePresses++;
+ if (ignorePresses == IGNORE_PROMPT_COUNT) {
+ DialogUtilities.okCancelCustomDialog(MissedCallActivity.this,
+ getString(R.string.MCA_ignore_title),
+ getString(R.string.MCA_ignore_body),
+ R.string.MCA_ignore_all,
+ R.string.MCA_ignore_this,
+ 0,
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ Preferences.setBoolean(R.string.p_field_missed_calls, false);
+ dismissListener.onClick(v);
+ }
+ },
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ dismissListener.onClick(v);
+ }
+ });
+ } else {
+ dismissListener.onClick(v);
+ }
+ Preferences.setInt(PREF_IGNORE_PRESSES, ignorePresses);
+ }
+ };
+
+ private String name;
+ private String number;
+ private String timeString;
+
+ private TextView returnCallButton;
+ private TextView callLaterButton;
+ private TextView ignoreButton;
+ private View dismissButton;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ DependencyInjectionService.getInstance().inject(this);
+
+ setContentView(R.layout.missed_call_activity);
+
+ Intent intent = getIntent();
+
+ name = intent.getStringExtra(EXTRA_NAME);
+ number = intent.getStringExtra(EXTRA_NUMBER);
+ timeString = intent.getStringExtra(EXTRA_TIME);
+ long contactId = intent.getExtras().getLong(EXTRA_CONTACT_ID);
+
+ int color = ThemeService.getThemeColor();
+
+ returnCallButton = (TextView) findViewById(R.id.call_now);
+ callLaterButton = (TextView) findViewById(R.id.call_later);
+ ignoreButton = (TextView) findViewById(R.id.call_ignore);
+ dismissButton = findViewById(R.id.dismiss);
+ ((TextView) findViewById(R.id.reminder_title))
+ .setText(getString(R.string.MCA_title,
+ TextUtils.isEmpty(name) ? number : name, timeString));
+
+ ImageView pictureView = ((ImageView) findViewById(R.id.contact_picture));
+ if (contactId >= 0) {
+ Uri uri = ContentUris.withAppendedId(ContactsContract.Contacts.CONTENT_URI, contactId);
+ InputStream input = ContactsContract.Contacts.openContactPhotoInputStream(getContentResolver(), uri);
+ if (input == null)
+ return;
+ pictureView.setImageBitmap(BitmapFactory.decodeStream(input));
+ pictureView.setVisibility(View.VISIBLE);
+ }
+
+ Resources r = getResources();
+ returnCallButton.setBackgroundColor(r.getColor(color));
+ callLaterButton.setBackgroundColor(r.getColor(color));
+
+ addListeners();
+
+
+ if (!Preferences.getBoolean(R.string.p_rmd_nagging, true)) {
+ findViewById(R.id.missed_calls_speech_bubble).setVisibility(View.GONE);
+ } else {
+ TextView dialogView = (TextView) findViewById(R.id.reminder_message);
+ dialogView.setText(Notifications.getRandomReminder(getResources().getStringArray(R.array.MCA_dialog_speech_options)));
+ }
+ }
+
+ private void addListeners() {
+ ignoreButton.setOnClickListener(ignoreListener);
+ dismissButton.setOnClickListener(dismissListener);
+
+ returnCallButton.setOnClickListener(new OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ Intent call = new Intent(Intent.ACTION_CALL);
+ call.setData(Uri.parse("tel:" + number)); //$NON-NLS-1$
+ startActivity(call);
+ finish();
+ }
+ });
+
+ callLaterButton.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ final String taskTitle;
+ String dialogTitle;
+ if (TextUtils.isEmpty(name)) {
+ taskTitle = getString(R.string.MCA_task_title_no_name, number);
+ dialogTitle = getString(R.string.MCA_schedule_dialog_title, number);
+ } else {
+ taskTitle = getString(R.string.MCA_task_title_name, name, number);
+ dialogTitle = getString(R.string.MCA_schedule_dialog_title, name);
+ }
+ SnoozeDialog sd = new SnoozeDialog(MissedCallActivity.this, new SnoozeCallback() {
+ @Override
+ public void snoozeForTime(long time) {
+
+ Task newTask = new Task();
+ newTask.setValue(Task.TITLE, taskTitle);
+ newTask.setValue(Task.DUE_DATE, time);
+ taskService.save(newTask);
+
+ finish();
+ }
+ });
+ new AlertDialog.Builder(MissedCallActivity.this)
+ .setTitle(dialogTitle)
+ .setView(sd)
+ .setPositiveButton(android.R.string.ok, sd)
+ .setNegativeButton(android.R.string.cancel, null)
+ .show().setOwnerActivity(MissedCallActivity.this);
+ }
+ });
+ }
+}
diff --git a/astrid/plugin-src/com/todoroo/astrid/calls/PhoneStateChangedReceiver.java b/astrid/plugin-src/com/todoroo/astrid/calls/PhoneStateChangedReceiver.java
new file mode 100644
index 000000000..8f2e56a0e
--- /dev/null
+++ b/astrid/plugin-src/com/todoroo/astrid/calls/PhoneStateChangedReceiver.java
@@ -0,0 +1,132 @@
+package com.todoroo.astrid.calls;
+
+import java.util.Date;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.database.Cursor;
+import android.net.Uri;
+import android.provider.CallLog.Calls;
+import android.provider.ContactsContract;
+import android.telephony.TelephonyManager;
+import android.text.TextUtils;
+
+import com.timsu.astrid.R;
+import com.todoroo.andlib.utility.AndroidUtilities;
+import com.todoroo.andlib.utility.DateUtilities;
+import com.todoroo.andlib.utility.Preferences;
+
+@SuppressWarnings("nls")
+public class PhoneStateChangedReceiver extends BroadcastReceiver {
+
+ private static final String PREF_LAST_INCOMING_NUMBER = "last_incoming_number";
+
+ private static final long WAIT_BEFORE_READ_LOG = 3000L;
+
+ @Override
+ public void onReceive(final Context context, Intent intent) {
+ if (!Preferences.getBoolean(R.string.p_field_missed_calls, true)) {
+ Preferences.clear(PREF_LAST_INCOMING_NUMBER);
+ return;
+ }
+
+ String state = intent.getStringExtra(TelephonyManager.EXTRA_STATE);
+
+ if (TelephonyManager.EXTRA_STATE_RINGING.equals(state)) {
+ String number = digitsOnly(intent.getStringExtra(TelephonyManager.EXTRA_INCOMING_NUMBER));
+ if (TextUtils.isEmpty(number))
+ return;
+
+ Preferences.setString(PREF_LAST_INCOMING_NUMBER, number);
+ } else if (TelephonyManager.EXTRA_STATE_IDLE.equals(state)) {
+ final String lastNumber = Preferences.getStringValue(PREF_LAST_INCOMING_NUMBER);
+ if (TextUtils.isEmpty(lastNumber)) {
+ return;
+ }
+
+ Preferences.clear(PREF_LAST_INCOMING_NUMBER);
+
+ new Thread() {
+ @Override
+ public void run() {
+ AndroidUtilities.sleepDeep(WAIT_BEFORE_READ_LOG);
+ Cursor calls = context.getContentResolver().query(
+ Calls.CONTENT_URI,
+ null,
+ Calls.TYPE + " = ? AND " + Calls.NEW + " = ?",
+ new String[] { Integer.toString(Calls.MISSED_TYPE), "1" },
+ Calls.DATE + " DESC"
+ );
+ try {
+ if (calls.moveToFirst()) {
+ int numberIndex = calls.getColumnIndex(Calls.NUMBER);
+ String number = calls.getString(numberIndex);
+
+ // Sanity check for phone number match
+ // in case the phone logs haven't updated for some reaosn
+ if (!lastNumber.equals(digitsOnly(number))) {
+ return;
+ }
+
+ // If a lot of time has passed since the most recent missed call, ignore
+ // It could be the same person calling you back before you call them back,
+ // but if you answer this time, the missed call will still be in the database
+ // and will be processed again.
+ int dateIndex = calls.getColumnIndex(Calls.DATE);
+ long date = calls.getLong(dateIndex);
+ if (DateUtilities.now() - date > 2 * DateUtilities.ONE_MINUTE) {
+ return;
+ }
+
+ int nameIndex = calls.getColumnIndex(Calls.CACHED_NAME);
+ String name = calls.getString(nameIndex);
+
+ int timeIndex = calls.getColumnIndex(Calls.DATE);
+ long time = calls.getLong(timeIndex);
+ String timeString = DateUtilities.getTimeString(context, new Date(time));
+
+ long contactId = getContactIdFromNumber(context, number);
+
+ Intent missedCallIntent = new Intent(context, MissedCallActivity.class);
+ missedCallIntent.putExtra(MissedCallActivity.EXTRA_NUMBER, number);
+ missedCallIntent.putExtra(MissedCallActivity.EXTRA_NAME, name);
+ missedCallIntent.putExtra(MissedCallActivity.EXTRA_TIME, timeString);
+ missedCallIntent.putExtra(MissedCallActivity.EXTRA_CONTACT_ID, contactId);
+ missedCallIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
+ context.startActivity(missedCallIntent);
+ }
+ } finally {
+ calls.close();
+ }
+ }
+ }.start();
+ }
+ }
+
+ private String digitsOnly(String number) {
+ StringBuilder builder = new StringBuilder();
+ for (int i = 0; i < number.length(); i++) {
+ char c = number.charAt(i);
+ if (Character.isDigit(c))
+ builder.append(c);
+ }
+ return builder.toString();
+ }
+
+ private long getContactIdFromNumber(Context context, String number) {
+ Uri contactUri = Uri.withAppendedPath(ContactsContract.PhoneLookup.CONTENT_FILTER_URI, Uri.encode(number));
+ Cursor c = context.getContentResolver().query(contactUri, new String[] { ContactsContract.PhoneLookup._ID }, null, null, null);
+
+ try {
+ if (c.moveToFirst()) {
+ long id = c.getLong(c.getColumnIndex(ContactsContract.PhoneLookup._ID));
+ return id;
+ }
+ } finally {
+ c.close();
+ }
+ return -1;
+ }
+
+}
diff --git a/astrid/plugin-src/com/todoroo/astrid/reminders/Notifications.java b/astrid/plugin-src/com/todoroo/astrid/reminders/Notifications.java
index 64cb2cfc5..2cf63655c 100644
--- a/astrid/plugin-src/com/todoroo/astrid/reminders/Notifications.java
+++ b/astrid/plugin-src/com/todoroo/astrid/reminders/Notifications.java
@@ -128,7 +128,7 @@ public class Notifications extends BroadcastReceiver {
// --- notification creation
/** @return a random reminder string */
- static String getRandomReminder(String[] reminders) {
+ public static String getRandomReminder(String[] reminders) {
int next = ReminderService.random.nextInt(reminders.length);
String reminder = reminders[next];
return reminder;
diff --git a/astrid/res/drawable-hdpi/close_clear_cancel.png b/astrid/res/drawable-hdpi/close_clear_cancel.png
new file mode 100644
index 000000000..778c7f081
Binary files /dev/null and b/astrid/res/drawable-hdpi/close_clear_cancel.png differ
diff --git a/astrid/res/drawable/close_clear_cancel.png b/astrid/res/drawable/close_clear_cancel.png
new file mode 100644
index 000000000..778c7f081
Binary files /dev/null and b/astrid/res/drawable/close_clear_cancel.png differ
diff --git a/astrid/res/layout-land/astrid_reminder_view.xml b/astrid/res/layout-land/astrid_reminder_view.xml
index d946248e0..8e13547f5 100644
--- a/astrid/res/layout-land/astrid_reminder_view.xml
+++ b/astrid/res/layout-land/astrid_reminder_view.xml
@@ -53,7 +53,7 @@
android:scaleType="fitCenter"
android:layout_gravity="right"
android:layout_marginBottom="10dip"
- android:src="@android:drawable/ic_menu_close_clear_cancel"/>
+ android:src="@drawable/close_clear_cancel"/>