mirror of https://github.com/tasks/tasks
Remove missed call functionality
This feature violates impending Google Play Developer policy change: https://android-developers.googleblog.com/2018/10/providing-safe-and-secure-experience.htmlpull/757/head
parent
bffd683e03
commit
4aab081f6c
@ -1,265 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2012 Todoroo Inc
|
|
||||||
*
|
|
||||||
* <p>See the file "LICENSE" for the full license governing this code.
|
|
||||||
*/
|
|
||||||
package com.todoroo.astrid.calls;
|
|
||||||
|
|
||||||
import static org.tasks.time.DateTimeUtils.currentTimeMillis;
|
|
||||||
|
|
||||||
import android.annotation.SuppressLint;
|
|
||||||
import android.app.PendingIntent;
|
|
||||||
import android.content.ContentUris;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.database.Cursor;
|
|
||||||
import android.graphics.Bitmap;
|
|
||||||
import android.graphics.BitmapFactory;
|
|
||||||
import android.net.Uri;
|
|
||||||
import android.provider.CallLog.Calls;
|
|
||||||
import android.provider.ContactsContract;
|
|
||||||
import android.support.v4.app.NotificationCompat;
|
|
||||||
import android.telephony.TelephonyManager;
|
|
||||||
import android.text.TextUtils;
|
|
||||||
import com.todoroo.andlib.utility.AndroidUtilities;
|
|
||||||
import com.todoroo.andlib.utility.DateUtilities;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import javax.inject.Inject;
|
|
||||||
import org.tasks.R;
|
|
||||||
import org.tasks.injection.BroadcastComponent;
|
|
||||||
import org.tasks.injection.ForApplication;
|
|
||||||
import org.tasks.injection.InjectingBroadcastReceiver;
|
|
||||||
import org.tasks.notifications.NotificationManager;
|
|
||||||
import org.tasks.preferences.PermissionChecker;
|
|
||||||
import org.tasks.preferences.Preferences;
|
|
||||||
import org.tasks.reminders.MissedCallActivity;
|
|
||||||
import timber.log.Timber;
|
|
||||||
|
|
||||||
public class PhoneStateChangedReceiver extends InjectingBroadcastReceiver {
|
|
||||||
|
|
||||||
private static final String PREF_LAST_INCOMING_NUMBER = "last_incoming_number";
|
|
||||||
|
|
||||||
private static final long WAIT_BEFORE_READ_LOG = 3000L;
|
|
||||||
|
|
||||||
@Inject Preferences preferences;
|
|
||||||
@Inject NotificationManager notificationManager;
|
|
||||||
@Inject PermissionChecker permissionChecker;
|
|
||||||
@Inject @ForApplication Context context;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onReceive(final Context context, Intent intent) {
|
|
||||||
super.onReceive(context, intent);
|
|
||||||
|
|
||||||
if (!intent.getAction().equals(TelephonyManager.ACTION_PHONE_STATE_CHANGED)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!preferences.fieldMissedPhoneCalls()) {
|
|
||||||
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;
|
|
||||||
try {
|
|
||||||
calls = getMissedCalls();
|
|
||||||
} catch (Exception e) { // Sometimes database is locked, retry once
|
|
||||||
Timber.e(e);
|
|
||||||
AndroidUtilities.sleepDeep(300L);
|
|
||||||
try {
|
|
||||||
calls = getMissedCalls();
|
|
||||||
} catch (Exception e2) {
|
|
||||||
Timber.e(e2);
|
|
||||||
calls = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
if (calls == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
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);
|
|
||||||
|
|
||||||
long contactId = getContactIdFromNumber(context, number);
|
|
||||||
|
|
||||||
triggerMissedCallNotification(name, number, contactId);
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
Timber.e(e);
|
|
||||||
} finally {
|
|
||||||
if (calls != null) {
|
|
||||||
calls.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}.start();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressLint("MissingPermission")
|
|
||||||
private Cursor getMissedCalls() {
|
|
||||||
if (permissionChecker.canAccessMissedCallPermissions()) {
|
|
||||||
//noinspection MissingPermission
|
|
||||||
return context
|
|
||||||
.getContentResolver()
|
|
||||||
.query(
|
|
||||||
Calls.CONTENT_URI,
|
|
||||||
new String[] {Calls.NUMBER, Calls.DATE, Calls.CACHED_NAME},
|
|
||||||
Calls.TYPE + " = ? AND " + Calls.NEW + " = ?",
|
|
||||||
new String[] {Integer.toString(Calls.MISSED_TYPE), "1"},
|
|
||||||
Calls.DATE + " DESC");
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void inject(BroadcastComponent component) {
|
|
||||||
component.inject(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
private String digitsOnly(String number) {
|
|
||||||
if (number == null) {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
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()) {
|
|
||||||
return c.getLong(c.getColumnIndex(ContactsContract.PhoneLookup._ID));
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
c.close();
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void triggerMissedCallNotification(
|
|
||||||
final String name, final String number, long contactId) {
|
|
||||||
final String title =
|
|
||||||
context.getString(R.string.missed_call, TextUtils.isEmpty(name) ? number : name);
|
|
||||||
|
|
||||||
Intent missedCallDialog = new Intent(context, MissedCallActivity.class);
|
|
||||||
missedCallDialog.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
|
||||||
missedCallDialog.putExtra(MissedCallActivity.EXTRA_NUMBER, number);
|
|
||||||
missedCallDialog.putExtra(MissedCallActivity.EXTRA_NAME, name);
|
|
||||||
missedCallDialog.putExtra(MissedCallActivity.EXTRA_TITLE, title);
|
|
||||||
|
|
||||||
NotificationCompat.Builder builder =
|
|
||||||
new NotificationCompat.Builder(context, NotificationManager.NOTIFICATION_CHANNEL_CALLS)
|
|
||||||
.setTicker(title)
|
|
||||||
.setContentTitle(title)
|
|
||||||
.setContentText(context.getString(R.string.app_name))
|
|
||||||
.setWhen(currentTimeMillis())
|
|
||||||
.setShowWhen(true)
|
|
||||||
.setSmallIcon(R.drawable.ic_check_white_24dp)
|
|
||||||
.setContentIntent(
|
|
||||||
PendingIntent.getActivity(
|
|
||||||
context,
|
|
||||||
missedCallDialog.hashCode(),
|
|
||||||
missedCallDialog,
|
|
||||||
PendingIntent.FLAG_UPDATE_CURRENT));
|
|
||||||
|
|
||||||
Bitmap contactImage = getContactImage(contactId);
|
|
||||||
if (contactImage != null) {
|
|
||||||
builder.setLargeIcon(contactImage);
|
|
||||||
}
|
|
||||||
|
|
||||||
Intent callNow = new Intent(context, MissedCallActivity.class);
|
|
||||||
callNow.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
|
||||||
callNow.putExtra(MissedCallActivity.EXTRA_NUMBER, number);
|
|
||||||
callNow.putExtra(MissedCallActivity.EXTRA_NAME, name);
|
|
||||||
callNow.putExtra(MissedCallActivity.EXTRA_TITLE, title);
|
|
||||||
callNow.putExtra(MissedCallActivity.EXTRA_CALL_NOW, true);
|
|
||||||
|
|
||||||
Intent callLater = new Intent(context, MissedCallActivity.class);
|
|
||||||
callLater.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
|
||||||
callLater.putExtra(MissedCallActivity.EXTRA_NUMBER, number);
|
|
||||||
callLater.putExtra(MissedCallActivity.EXTRA_NAME, name);
|
|
||||||
callLater.putExtra(MissedCallActivity.EXTRA_TITLE, title);
|
|
||||||
callLater.putExtra(MissedCallActivity.EXTRA_CALL_LATER, true);
|
|
||||||
builder
|
|
||||||
.addAction(
|
|
||||||
R.drawable.ic_phone_white_24dp,
|
|
||||||
context.getString(R.string.MCA_return_call),
|
|
||||||
PendingIntent.getActivity(
|
|
||||||
context, callNow.hashCode(), callNow, PendingIntent.FLAG_UPDATE_CURRENT))
|
|
||||||
.addAction(
|
|
||||||
R.drawable.ic_add_white_24dp,
|
|
||||||
context.getString(R.string.MCA_add_task),
|
|
||||||
PendingIntent.getActivity(
|
|
||||||
context, callLater.hashCode(), callLater, PendingIntent.FLAG_UPDATE_CURRENT));
|
|
||||||
|
|
||||||
notificationManager.notify(number.hashCode(), builder, true, false, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Bitmap getContactImage(long contactId) {
|
|
||||||
Bitmap b = null;
|
|
||||||
if (contactId >= 0) {
|
|
||||||
Uri uri = ContentUris.withAppendedId(ContactsContract.Contacts.CONTENT_URI, contactId);
|
|
||||||
InputStream input =
|
|
||||||
ContactsContract.Contacts.openContactPhotoInputStream(context.getContentResolver(), uri);
|
|
||||||
try {
|
|
||||||
b = BitmapFactory.decodeStream(input);
|
|
||||||
} catch (OutOfMemoryError e) {
|
|
||||||
Timber.e(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return b;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,104 +0,0 @@
|
|||||||
package org.tasks.reminders;
|
|
||||||
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.net.Uri;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.support.v4.app.FragmentManager;
|
|
||||||
import android.text.TextUtils;
|
|
||||||
import com.todoroo.astrid.data.Task;
|
|
||||||
import com.todoroo.astrid.service.TaskCreator;
|
|
||||||
import javax.inject.Inject;
|
|
||||||
import org.tasks.R;
|
|
||||||
import org.tasks.injection.ActivityComponent;
|
|
||||||
import org.tasks.injection.InjectingAppCompatActivity;
|
|
||||||
import org.tasks.intents.TaskIntents;
|
|
||||||
import org.tasks.notifications.NotificationManager;
|
|
||||||
|
|
||||||
public class MissedCallActivity extends InjectingAppCompatActivity
|
|
||||||
implements MissedCallDialog.MissedCallHandler {
|
|
||||||
|
|
||||||
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_TITLE = "extra_title";
|
|
||||||
public static final String EXTRA_CALL_NOW = "extra_call_now";
|
|
||||||
public static final String EXTRA_CALL_LATER = "extra_call_later";
|
|
||||||
private static final String FRAG_TAG_MISSED_CALL_FRAGMENT = "frag_tag_missed_call_fragment";
|
|
||||||
@Inject NotificationManager notificationManager;
|
|
||||||
@Inject TaskCreator taskCreator;
|
|
||||||
|
|
||||||
private String name;
|
|
||||||
private String number;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
|
||||||
super.onCreate(savedInstanceState);
|
|
||||||
|
|
||||||
setup(getIntent());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void inject(ActivityComponent component) {
|
|
||||||
component.inject(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onNewIntent(Intent intent) {
|
|
||||||
super.onNewIntent(intent);
|
|
||||||
|
|
||||||
setup(intent);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setup(Intent intent) {
|
|
||||||
name = intent.getStringExtra(EXTRA_NAME);
|
|
||||||
number = intent.getStringExtra(EXTRA_NUMBER);
|
|
||||||
|
|
||||||
if (intent.getBooleanExtra(EXTRA_CALL_NOW, false)) {
|
|
||||||
callNow();
|
|
||||||
} else if (intent.getBooleanExtra(EXTRA_CALL_LATER, false)) {
|
|
||||||
callLater();
|
|
||||||
} else {
|
|
||||||
FragmentManager fragmentManager = getSupportFragmentManager();
|
|
||||||
MissedCallDialog fragment =
|
|
||||||
(MissedCallDialog) fragmentManager.findFragmentByTag(FRAG_TAG_MISSED_CALL_FRAGMENT);
|
|
||||||
if (fragment == null) {
|
|
||||||
fragment = new MissedCallDialog();
|
|
||||||
fragment.show(fragmentManager, FRAG_TAG_MISSED_CALL_FRAGMENT);
|
|
||||||
}
|
|
||||||
fragment.setTitle(intent.getStringExtra(EXTRA_TITLE));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void dismiss() {
|
|
||||||
finish();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void callNow() {
|
|
||||||
Intent call = new Intent(Intent.ACTION_VIEW);
|
|
||||||
call.setData(Uri.parse("tel:" + number)); // $NON-NLS-1$
|
|
||||||
startActivity(call);
|
|
||||||
cancelNotificationAndFinish();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void callLater() {
|
|
||||||
String title =
|
|
||||||
TextUtils.isEmpty(name)
|
|
||||||
? getString(R.string.MCA_task_title_no_name, number)
|
|
||||||
: getString(R.string.MCA_task_title_name, name, number);
|
|
||||||
Task task = taskCreator.basicQuickAddTask(title);
|
|
||||||
TaskIntents.getEditTaskStack(this, null, task.getId()).startActivities();
|
|
||||||
cancelNotificationAndFinish();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void ignore() {
|
|
||||||
cancelNotificationAndFinish();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void cancelNotificationAndFinish() {
|
|
||||||
notificationManager.cancel(number.hashCode());
|
|
||||||
finish();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,82 +0,0 @@
|
|||||||
package org.tasks.reminders;
|
|
||||||
|
|
||||||
import static java.util.Arrays.asList;
|
|
||||||
|
|
||||||
import android.app.Dialog;
|
|
||||||
import android.content.DialogInterface;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.support.annotation.NonNull;
|
|
||||||
import java.util.List;
|
|
||||||
import javax.inject.Inject;
|
|
||||||
import org.tasks.R;
|
|
||||||
import org.tasks.dialogs.DialogBuilder;
|
|
||||||
import org.tasks.injection.DialogFragmentComponent;
|
|
||||||
import org.tasks.injection.InjectingDialogFragment;
|
|
||||||
|
|
||||||
public class MissedCallDialog extends InjectingDialogFragment {
|
|
||||||
|
|
||||||
@Inject DialogBuilder dialogBuilder;
|
|
||||||
private String title;
|
|
||||||
private MissedCallHandler handler;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void inject(DialogFragmentComponent component) {
|
|
||||||
component.inject(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
@Override
|
|
||||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
|
||||||
List<String> actions =
|
|
||||||
asList(
|
|
||||||
getString(R.string.MCA_return_call),
|
|
||||||
getString(R.string.MCA_add_task),
|
|
||||||
getString(R.string.MCA_ignore));
|
|
||||||
|
|
||||||
handler = (MissedCallHandler) getActivity();
|
|
||||||
|
|
||||||
return dialogBuilder
|
|
||||||
.newDialog()
|
|
||||||
.setTitle(title)
|
|
||||||
.setItems(
|
|
||||||
actions,
|
|
||||||
(dialog, which) -> {
|
|
||||||
switch (which) {
|
|
||||||
case 0:
|
|
||||||
handler.callNow();
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
handler.callLater();
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
handler.ignore();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.show();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onDismiss(DialogInterface dialog) {
|
|
||||||
handler.dismiss();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setTitle(String title) {
|
|
||||||
this.title = title;
|
|
||||||
Dialog dialog = getDialog();
|
|
||||||
if (dialog != null) {
|
|
||||||
dialog.setTitle(title);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public interface MissedCallHandler {
|
|
||||||
|
|
||||||
void callNow();
|
|
||||||
|
|
||||||
void callLater();
|
|
||||||
|
|
||||||
void ignore();
|
|
||||||
|
|
||||||
void dismiss();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Loading…
Reference in New Issue