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"/>