summaryrefslogtreecommitdiff
path: root/java/com/android
diff options
context:
space:
mode:
authorTreehugger Robot <treehugger-gerrit@google.com>2018-02-15 18:08:21 +0000
committerGerrit Code Review <noreply-gerritcodereview@google.com>2018-02-15 18:08:21 +0000
commitc54ce2658988ca36ca3dfab00daefca4dcfed3b2 (patch)
treefb432a423f670969da57a4900b5ff7dcc35d2f8c /java/com/android
parent70f98d3997c42c7d4e3b3e3920659aca78820d14 (diff)
parent39009b4ad73d5017295b30fb18a77224195f06af (diff)
Merge changes Ib360d3bc,Iae40d0ab,I486f7b1a,I709a1e30
* changes: Mark calls as read in new call log. Restored work profile contacts to Dialer search. Clicking on a missed call in the call log no longer crashes the app. Add bottom sheet options for blocked and/or spam numbers in the new call log.
Diffstat (limited to 'java/com/android')
-rw-r--r--java/com/android/contacts/common/compat/DirectoryCompat.java4
-rw-r--r--java/com/android/dialer/app/calllog/CallLogAdapter.java8
-rw-r--r--java/com/android/dialer/app/calllog/CallLogNotificationsService.java13
-rw-r--r--java/com/android/dialer/app/calllog/MissedCallNotifier.java59
-rw-r--r--java/com/android/dialer/calllog/CallLogComponent.java2
-rw-r--r--java/com/android/dialer/calllog/ClearMissedCalls.java166
-rw-r--r--java/com/android/dialer/calllog/ui/NewCallLogFragment.java31
-rw-r--r--java/com/android/dialer/calllog/ui/menu/Modules.java39
-rw-r--r--java/com/android/dialer/calllog/ui/menu/NewCallLogMenu.java29
-rw-r--r--java/com/android/dialer/common/concurrent/DefaultFutureCallback.java44
-rw-r--r--java/com/android/dialer/common/concurrent/UiThreadExecutor.java2
-rw-r--r--java/com/android/dialer/contactactions/IntentModule.java2
-rw-r--r--java/com/android/dialer/contactactions/SharedModules.java156
-rw-r--r--java/com/android/dialer/contactactions/res/drawable-xxxhdpi/ic_unblock.pngbin0 -> 1034 bytes
-rw-r--r--java/com/android/dialer/contactactions/res/values/strings.xml22
-rw-r--r--java/com/android/dialer/main/impl/NewMainActivityPeer.java28
-rw-r--r--java/com/android/dialer/main/impl/bottomnav/BottomNavBar.java1
-rw-r--r--java/com/android/dialer/notification/missedcalls/MissedCallConstants.java36
-rw-r--r--java/com/android/dialer/notification/missedcalls/MissedCallNotificationCanceller.java48
-rw-r--r--java/com/android/dialer/notification/missedcalls/MissedCallNotificationTags.java29
-rw-r--r--java/com/android/dialer/searchfragment/common/res/layout/search_contact_row.xml11
-rw-r--r--java/com/android/dialer/searchfragment/remote/RemoteContactViewHolder.java8
-rw-r--r--java/com/android/dialer/searchfragment/remote/RemoteContactsCursor.java8
-rw-r--r--java/com/android/dialer/searchfragment/remote/RemoteContactsCursorLoader.java21
-rw-r--r--java/com/android/dialer/simulator/impl/SimulatorSimCallManager.java19
-rw-r--r--java/com/android/dialer/theme/res/values/strings.xml3
-rw-r--r--java/com/android/dialer/voicemail/listui/menu/Modules.java15
27 files changed, 684 insertions, 120 deletions
diff --git a/java/com/android/contacts/common/compat/DirectoryCompat.java b/java/com/android/contacts/common/compat/DirectoryCompat.java
index 85f4a4202..e67087659 100644
--- a/java/com/android/contacts/common/compat/DirectoryCompat.java
+++ b/java/com/android/contacts/common/compat/DirectoryCompat.java
@@ -48,4 +48,8 @@ public class DirectoryCompat {
public static boolean isEnterpriseDirectoryId(long directoryId) {
return VERSION.SDK_INT >= VERSION_CODES.N && Directory.isEnterpriseDirectoryId(directoryId);
}
+
+ public static boolean isOnlyEnterpriseDirectoryId(long directoryId) {
+ return isEnterpriseDirectoryId(directoryId) && !isRemoteDirectoryId(directoryId);
+ }
}
diff --git a/java/com/android/dialer/app/calllog/CallLogAdapter.java b/java/com/android/dialer/app/calllog/CallLogAdapter.java
index 51df70219..baca590b5 100644
--- a/java/com/android/dialer/app/calllog/CallLogAdapter.java
+++ b/java/com/android/dialer/app/calllog/CallLogAdapter.java
@@ -54,6 +54,7 @@ import android.view.ViewGroup;
import com.android.contacts.common.ContactsUtils;
import com.android.contacts.common.compat.PhoneNumberUtilsCompat;
import com.android.contacts.common.preference.ContactsPreferences;
+import com.android.dialer.app.DialtactsActivity;
import com.android.dialer.app.R;
import com.android.dialer.app.calllog.CallLogFragment.CallLogFragmentListener;
import com.android.dialer.app.calllog.CallLogGroupBuilder.GroupCreator;
@@ -381,9 +382,7 @@ public class CallLogAdapter extends GroupingListAdapter
if (viewHolder.callType == CallLog.Calls.MISSED_TYPE) {
CallLogAsyncTaskUtil.markCallAsRead(activity, viewHolder.callIds);
if (activityType == ACTIVITY_TYPE_DIALTACTS) {
- if (v.getContext() instanceof CallLogFragmentListener) {
- ((CallLogFragmentListener) v.getContext()).updateTabUnreadCounts();
- } else if (v.getContext() instanceof MainActivityPeer.PeerSupplier) {
+ if (v.getContext() instanceof MainActivityPeer.PeerSupplier) {
// This is really bad, but we must do this to prevent a dependency cycle, enforce
// best practices in new code, and avoid refactoring DialtactsActivity.
((FragmentUtilListener)
@@ -391,8 +390,7 @@ public class CallLogAdapter extends GroupingListAdapter
.getImpl(CallLogFragmentListener.class)
.updateTabUnreadCounts();
} else {
- throw Assert.createIllegalStateFailException(
- "View parent does not implement CallLogFragmentListener");
+ ((DialtactsActivity) v.getContext()).updateTabUnreadCounts();
}
}
}
diff --git a/java/com/android/dialer/app/calllog/CallLogNotificationsService.java b/java/com/android/dialer/app/calllog/CallLogNotificationsService.java
index 5949141f1..10e30ff72 100644
--- a/java/com/android/dialer/app/calllog/CallLogNotificationsService.java
+++ b/java/com/android/dialer/app/calllog/CallLogNotificationsService.java
@@ -31,6 +31,7 @@ import com.android.dialer.common.Assert;
import com.android.dialer.common.LogUtil;
import com.android.dialer.common.concurrent.DialerExecutor.Worker;
import com.android.dialer.common.concurrent.DialerExecutorComponent;
+import com.android.dialer.notification.missedcalls.MissedCallNotificationCanceller;
import com.android.dialer.telecom.TelecomUtil;
import com.android.dialer.util.PermissionsUtil;
@@ -87,14 +88,6 @@ public class CallLogNotificationsService extends IntentService {
context.startService(serviceIntent);
}
- public static void markSingleNewVoicemailAsOld(Context context, @Nullable Uri voicemailUri) {
- LogUtil.enterBlock("CallLogNotificationsService.markSingleNewVoicemailAsOld");
- Intent serviceIntent = new Intent(context, CallLogNotificationsService.class);
- serviceIntent.setAction(CallLogNotificationsService.ACTION_MARK_SINGLE_NEW_VOICEMAIL_AS_OLD);
- serviceIntent.setData(voicemailUri);
- context.startService(serviceIntent);
- }
-
public static void cancelAllMissedCalls(Context context) {
LogUtil.enterBlock("CallLogNotificationsService.cancelAllMissedCalls");
DialerExecutorComponent.get(context)
@@ -175,7 +168,7 @@ public class CallLogNotificationsService extends IntentService {
case ACTION_CANCEL_SINGLE_MISSED_CALL:
Uri callUri = intent.getData();
CallLogNotificationsQueryHelper.markSingleMissedCallInCallLogAsRead(this, callUri);
- MissedCallNotifier.cancelSingleMissedCallNotification(this, callUri);
+ MissedCallNotificationCanceller.cancelSingle(this, callUri);
TelecomUtil.cancelMissedCallsNotification(this);
break;
case ACTION_CALL_BACK_FROM_MISSED_CALL_NOTIFICATION:
@@ -196,7 +189,7 @@ public class CallLogNotificationsService extends IntentService {
LogUtil.enterBlock("CallLogNotificationsService.cancelAllMissedCallsBackground");
Assert.isWorkerThread();
CallLogNotificationsQueryHelper.markAllMissedCallsInCallLogAsRead(context);
- MissedCallNotifier.cancelAllMissedCallNotifications(context);
+ MissedCallNotificationCanceller.cancelAll(context);
TelecomUtil.cancelMissedCallsNotification(context);
}
diff --git a/java/com/android/dialer/app/calllog/MissedCallNotifier.java b/java/com/android/dialer/app/calllog/MissedCallNotifier.java
index 417f8f0f9..14bbdfa56 100644
--- a/java/com/android/dialer/app/calllog/MissedCallNotifier.java
+++ b/java/com/android/dialer/app/calllog/MissedCallNotifier.java
@@ -58,7 +58,9 @@ import com.android.dialer.duo.DuoConstants;
import com.android.dialer.enrichedcall.FuzzyPhoneNumberMatcher;
import com.android.dialer.notification.DialerNotificationManager;
import com.android.dialer.notification.NotificationChannelId;
-import com.android.dialer.notification.NotificationManagerUtils;
+import com.android.dialer.notification.missedcalls.MissedCallConstants;
+import com.android.dialer.notification.missedcalls.MissedCallNotificationCanceller;
+import com.android.dialer.notification.missedcalls.MissedCallNotificationTags;
import com.android.dialer.phonenumbercache.ContactInfo;
import com.android.dialer.phonenumberutil.PhoneNumberHelper;
import com.android.dialer.precall.PreCall;
@@ -71,18 +73,6 @@ import java.util.Set;
/** Creates a notification for calls that the user missed (neither answered nor rejected). */
public class MissedCallNotifier implements Worker<Pair<Integer, String>, Void> {
- /** Prefix used to generate a unique tag for each missed call notification. */
- private static final String NOTIFICATION_TAG_PREFIX = "MissedCall_";
- /** Common ID for all missed call notifications. */
- private static final int NOTIFICATION_ID = 1;
- /** Tag for the group summary notification. */
- private static final String GROUP_SUMMARY_NOTIFICATION_TAG = "GroupSummary_MissedCall";
- /**
- * Key used to associate all missed call notifications and the summary as belonging to a single
- * group.
- */
- private static final String GROUP_KEY = "MissedCallGroup";
-
private final Context context;
private final CallLogNotificationsQueryHelper callLogNotificationsQueryHelper;
@@ -126,7 +116,7 @@ public class MissedCallNotifier implements Worker<Pair<Integer, String>, Void> {
if ((newCalls != null && newCalls.isEmpty()) || count == 0) {
// No calls to notify about: clear the notification.
CallLogNotificationsQueryHelper.markAllMissedCallsInCallLogAsRead(context);
- cancelAllMissedCallNotifications(context);
+ MissedCallNotificationCanceller.cancelAll(context);
return;
}
@@ -226,7 +216,10 @@ public class MissedCallNotifier implements Worker<Pair<Integer, String>, Void> {
LogUtil.i("MissedCallNotifier.updateMissedCallNotification", "adding missed call notification");
DialerNotificationManager.notify(
- context, GROUP_SUMMARY_NOTIFICATION_TAG, NOTIFICATION_ID, notification);
+ context,
+ MissedCallConstants.GROUP_SUMMARY_NOTIFICATION_TAG,
+ MissedCallConstants.NOTIFICATION_ID,
+ notification);
if (useCallList) {
// Do not repost active notifications to prevent erasing post call notes.
@@ -240,7 +233,10 @@ public class MissedCallNotifier implements Worker<Pair<Integer, String>, Void> {
String callTag = getNotificationTagForCall(call);
if (!activeTags.contains(callTag)) {
DialerNotificationManager.notify(
- context, callTag, NOTIFICATION_ID, getNotificationForCall(call, null));
+ context,
+ callTag,
+ MissedCallConstants.NOTIFICATION_ID,
+ getNotificationForCall(call, null));
}
}
}
@@ -286,29 +282,8 @@ public class MissedCallNotifier implements Worker<Pair<Integer, String>, Void> {
}
}
- public static void cancelAllMissedCallNotifications(@NonNull Context context) {
- NotificationManagerUtils.cancelAllInGroup(context, GROUP_KEY);
- }
-
- public static void cancelSingleMissedCallNotification(
- @NonNull Context context, @Nullable Uri callUri) {
- if (callUri == null) {
- LogUtil.e(
- "MissedCallNotifier.cancelSingleMissedCallNotification",
- "unable to cancel notification, uri is null");
- return;
- }
- // This will also dismiss the group summary if there are no more missed call notifications.
- DialerNotificationManager.cancel(
- context, getNotificationTagForCallUri(callUri), NOTIFICATION_ID);
- }
-
private static String getNotificationTagForCall(@NonNull NewCall call) {
- return getNotificationTagForCallUri(call.callsUri);
- }
-
- private static String getNotificationTagForCallUri(@NonNull Uri callUri) {
- return NOTIFICATION_TAG_PREFIX + callUri;
+ return MissedCallNotificationTags.getNotificationTagForCallUri(call.callsUri);
}
@WorkerThread
@@ -324,7 +299,7 @@ public class MissedCallNotifier implements Worker<Pair<Integer, String>, Void> {
DialerNotificationManager.notify(
context,
getNotificationTagForCall(call),
- NOTIFICATION_ID,
+ MissedCallConstants.NOTIFICATION_ID,
getNotificationForCall(call, note));
return;
}
@@ -408,7 +383,7 @@ public class MissedCallNotifier implements Worker<Pair<Integer, String>, Void> {
private Notification.Builder createNotificationBuilder() {
return new Notification.Builder(context)
- .setGroup(GROUP_KEY)
+ .setGroup(MissedCallConstants.GROUP_KEY)
.setSmallIcon(android.R.drawable.stat_notify_missed_call)
.setColor(context.getResources().getColor(R.color.dialer_theme_color, null))
.setAutoCancel(true)
@@ -437,7 +412,7 @@ public class MissedCallNotifier implements Worker<Pair<Integer, String>, Void> {
public void callBackFromMissedCall(String number, Uri callUri) {
closeSystemDialogs(context);
CallLogNotificationsQueryHelper.markSingleMissedCallInCallLogAsRead(context, callUri);
- cancelSingleMissedCallNotification(context, callUri);
+ MissedCallNotificationCanceller.cancelSingle(context, callUri);
DialerUtils.startActivityWithErrorToast(
context,
PreCall.getIntent(
@@ -450,7 +425,7 @@ public class MissedCallNotifier implements Worker<Pair<Integer, String>, Void> {
public void sendSmsFromMissedCall(String number, Uri callUri) {
closeSystemDialogs(context);
CallLogNotificationsQueryHelper.markSingleMissedCallInCallLogAsRead(context, callUri);
- cancelSingleMissedCallNotification(context, callUri);
+ MissedCallNotificationCanceller.cancelSingle(context, callUri);
DialerUtils.startActivityWithErrorToast(
context, IntentUtil.getSendSmsIntent(number).setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
}
diff --git a/java/com/android/dialer/calllog/CallLogComponent.java b/java/com/android/dialer/calllog/CallLogComponent.java
index c7db2a1b8..bb5bfee2a 100644
--- a/java/com/android/dialer/calllog/CallLogComponent.java
+++ b/java/com/android/dialer/calllog/CallLogComponent.java
@@ -27,6 +27,8 @@ public abstract class CallLogComponent {
public abstract RefreshAnnotatedCallLogWorker getRefreshAnnotatedCallLogWorker();
+ public abstract ClearMissedCalls getClearMissedCalls();
+
public static CallLogComponent get(Context context) {
return ((HasComponent) ((HasRootComponent) context.getApplicationContext()).component())
.callLogComponent();
diff --git a/java/com/android/dialer/calllog/ClearMissedCalls.java b/java/com/android/dialer/calllog/ClearMissedCalls.java
new file mode 100644
index 000000000..d216e7b88
--- /dev/null
+++ b/java/com/android/dialer/calllog/ClearMissedCalls.java
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dialer.calllog;
+
+import android.annotation.SuppressLint;
+import android.content.ContentValues;
+import android.content.Context;
+import android.net.Uri;
+import android.provider.CallLog.Calls;
+import android.support.v4.os.UserManagerCompat;
+import com.android.dialer.common.LogUtil;
+import com.android.dialer.common.concurrent.Annotations.BackgroundExecutor;
+import com.android.dialer.common.concurrent.Annotations.Ui;
+import com.android.dialer.common.database.Selection;
+import com.android.dialer.inject.ApplicationContext;
+import com.android.dialer.notification.missedcalls.MissedCallNotificationCanceller;
+import com.android.dialer.util.PermissionsUtil;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.ListeningExecutorService;
+import com.google.common.util.concurrent.MoreExecutors;
+import java.util.Collection;
+import javax.inject.Inject;
+
+/**
+ * Clears missed calls. This includes cancelling notifications and updating the "NEW" status in the
+ * system call log.
+ */
+public final class ClearMissedCalls {
+
+ private final Context appContext;
+ private final ListeningExecutorService backgroundExecutor;
+ private final ListeningExecutorService uiThreadExecutor;
+
+ @Inject
+ ClearMissedCalls(
+ @ApplicationContext Context appContext,
+ @BackgroundExecutor ListeningExecutorService backgroundExecutor,
+ @Ui ListeningExecutorService uiThreadExecutor) {
+ this.appContext = appContext;
+ this.backgroundExecutor = backgroundExecutor;
+ this.uiThreadExecutor = uiThreadExecutor;
+ }
+
+ /**
+ * Cancels all missed call notifications and marks all "new" missed calls in the system call log
+ * as "not new".
+ */
+ public ListenableFuture<Void> clearAll() {
+ ListenableFuture<Void> markNewFuture = markNotNew(ImmutableSet.of());
+ ListenableFuture<Void> cancelNotificationsFuture =
+ uiThreadExecutor.submit(
+ () -> {
+ MissedCallNotificationCanceller.cancelAll(appContext);
+ return null;
+ });
+
+ // Note on this usage of whenAllComplete:
+ // -The returned future completes when all sub-futures complete (whether they fail or not)
+ // -The returned future fails if any sub-future fails
+ return Futures.whenAllComplete(markNewFuture, cancelNotificationsFuture)
+ .call(
+ () -> {
+ // Calling get() is necessary to propagate failures.
+ markNewFuture.get();
+ cancelNotificationsFuture.get();
+ return null;
+ },
+ MoreExecutors.directExecutor());
+ }
+
+ /**
+ * For the provided set of IDs from the system call log, cancels their missed call notifications
+ * and marks them "not new".
+ *
+ * @param ids IDs from the system call log (see {@link Calls#_ID}}.
+ */
+ public ListenableFuture<Void> clearBySystemCallLogId(Collection<Long> ids) {
+ ListenableFuture<Void> markNewFuture = markNotNew(ids);
+ ListenableFuture<Void> cancelNotificationsFuture =
+ uiThreadExecutor.submit(
+ () -> {
+ for (long id : ids) {
+ Uri callUri = Calls.CONTENT_URI.buildUpon().appendPath(Long.toString(id)).build();
+ MissedCallNotificationCanceller.cancelSingle(appContext, callUri);
+ }
+ return null;
+ });
+
+ // Note on this usage of whenAllComplete:
+ // -The returned future completes when all sub-futures complete (whether they fail or not)
+ // -The returned future fails if any sub-future fails
+ return Futures.whenAllComplete(markNewFuture, cancelNotificationsFuture)
+ .call(
+ () -> {
+ // Calling get() is necessary to propagate failures.
+ markNewFuture.get();
+ cancelNotificationsFuture.get();
+ return null;
+ },
+ MoreExecutors.directExecutor());
+ }
+
+ /**
+ * Marks all provided system call log IDs as not new, or if the provided collection is empty,
+ * marks all calls as not new.
+ */
+ @SuppressLint("MissingPermission")
+ private ListenableFuture<Void> markNotNew(Collection<Long> ids) {
+ return backgroundExecutor.submit(
+ () -> {
+ if (!UserManagerCompat.isUserUnlocked(appContext)) {
+ LogUtil.e("ClearMissedCalls.markNotNew", "locked");
+ return null;
+ }
+ if (!PermissionsUtil.hasCallLogWritePermissions(appContext)) {
+ LogUtil.e("ClearMissedCalls.markNotNew", "no permission");
+ return null;
+ }
+
+ ContentValues values = new ContentValues();
+ values.put(Calls.NEW, 0);
+
+ Selection.Builder selectionBuilder =
+ Selection.builder()
+ .and(Selection.column(Calls.NEW).is("=", 1))
+ .and(Selection.column(Calls.TYPE).is("=", Calls.MISSED_TYPE));
+ if (!ids.isEmpty()) {
+ selectionBuilder.and(Selection.column(Calls._ID).in(toStrings(ids)));
+ }
+ Selection selection = selectionBuilder.build();
+ appContext
+ .getContentResolver()
+ .update(
+ Calls.CONTENT_URI,
+ values,
+ selection.getSelection(),
+ selection.getSelectionArgs());
+ return null;
+ });
+ }
+
+ private static String[] toStrings(Collection<Long> longs) {
+ String[] strings = new String[longs.size()];
+ int i = 0;
+ for (long value : longs) {
+ strings[i++] = Long.toString(value);
+ }
+ return strings;
+ }
+}
diff --git a/java/com/android/dialer/calllog/ui/NewCallLogFragment.java b/java/com/android/dialer/calllog/ui/NewCallLogFragment.java
index 10f75ef07..5e676f072 100644
--- a/java/com/android/dialer/calllog/ui/NewCallLogFragment.java
+++ b/java/com/android/dialer/calllog/ui/NewCallLogFragment.java
@@ -18,6 +18,7 @@ package com.android.dialer.calllog.ui;
import android.database.Cursor;
import android.os.Bundle;
import android.support.annotation.Nullable;
+import android.support.annotation.VisibleForTesting;
import android.support.v4.app.Fragment;
import android.support.v4.app.LoaderManager.LoaderCallbacks;
import android.support.v4.content.Loader;
@@ -31,10 +32,14 @@ import com.android.dialer.calllog.CallLogFramework;
import com.android.dialer.calllog.CallLogFramework.CallLogUi;
import com.android.dialer.calllog.RefreshAnnotatedCallLogWorker;
import com.android.dialer.common.LogUtil;
+import com.android.dialer.common.concurrent.DefaultFutureCallback;
import com.android.dialer.common.concurrent.DialerExecutorComponent;
import com.android.dialer.common.concurrent.ThreadUtil;
import com.android.dialer.common.concurrent.UiListener;
+import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.MoreExecutors;
+import java.util.concurrent.TimeUnit;
/** The "new" call log fragment implementation, which is built on top of the annotated call log. */
public final class NewCallLogFragment extends Fragment
@@ -46,13 +51,19 @@ public final class NewCallLogFragment extends Fragment
* the simulator, using this value results in ~6 refresh cycles (on a release build) to write 120
* call log entries.
*/
- private static final long WAIT_MILLIS = 100L;
+ private static final long REFRESH_ANNOTATED_CALL_LOG_WAIT_MILLIS = 100L;
+
+ @VisibleForTesting
+ static final long MARK_ALL_CALLS_READ_WAIT_MILLIS = TimeUnit.SECONDS.toMillis(3);
private RefreshAnnotatedCallLogWorker refreshAnnotatedCallLogWorker;
private UiListener<Void> refreshAnnotatedCallLogListener;
private RecyclerView recyclerView;
@Nullable private Runnable refreshAnnotatedCallLogRunnable;
+ private boolean shouldMarkCallsRead = false;
+ private final Runnable setShouldMarkCallsReadTrue = () -> shouldMarkCallsRead = true;
+
public NewCallLogFragment() {
LogUtil.enterBlock("NewCallLogFragment.NewCallLogFragment");
}
@@ -103,6 +114,13 @@ public final class NewCallLogFragment extends Fragment
((NewCallLogAdapter) recyclerView.getAdapter()).clearCache();
recyclerView.getAdapter().notifyDataSetChanged();
}
+
+ // We shouldn't mark the calls as read immediately when the 3 second timer expires because we
+ // don't want to disrupt the UI; instead we set a bit indicating to mark them read when the user
+ // leaves the fragment (in onPause).
+ shouldMarkCallsRead = false;
+ ThreadUtil.getUiThreadHandler()
+ .postDelayed(setShouldMarkCallsReadTrue, MARK_ALL_CALLS_READ_WAIT_MILLIS);
}
@Override
@@ -113,9 +131,17 @@ public final class NewCallLogFragment extends Fragment
// This is pending work that we don't actually need to follow through with.
ThreadUtil.getUiThreadHandler().removeCallbacks(refreshAnnotatedCallLogRunnable);
+ ThreadUtil.getUiThreadHandler().removeCallbacks(setShouldMarkCallsReadTrue);
CallLogFramework callLogFramework = CallLogComponent.get(getContext()).callLogFramework();
callLogFramework.detachUi();
+
+ if (shouldMarkCallsRead) {
+ Futures.addCallback(
+ CallLogComponent.get(getContext()).getClearMissedCalls().clearAll(),
+ new DefaultFutureCallback<>(),
+ MoreExecutors.directExecutor());
+ }
}
@Override
@@ -159,7 +185,8 @@ public final class NewCallLogFragment extends Fragment
throw new RuntimeException(throwable);
});
};
- ThreadUtil.getUiThreadHandler().postDelayed(refreshAnnotatedCallLogRunnable, WAIT_MILLIS);
+ ThreadUtil.getUiThreadHandler()
+ .postDelayed(refreshAnnotatedCallLogRunnable, REFRESH_ANNOTATED_CALL_LOG_WAIT_MILLIS);
}
@Override
diff --git a/java/com/android/dialer/calllog/ui/menu/Modules.java b/java/com/android/dialer/calllog/ui/menu/Modules.java
index d59155810..c85a9fddd 100644
--- a/java/com/android/dialer/calllog/ui/menu/Modules.java
+++ b/java/com/android/dialer/calllog/ui/menu/Modules.java
@@ -52,15 +52,8 @@ final class Modules {
if (canPlaceCalls) {
addModuleForVideoOrAudioCall(context, modules, row, normalizedNumber);
-
- SharedModules.maybeAddModuleForAddingToContacts(
- context,
- modules,
- row.number(),
- row.numberAttributes().getName(),
- row.numberAttributes().getLookupUri());
-
- SharedModules.maybeAddModuleForSendingTextMessage(context, modules, normalizedNumber);
+ SharedModules.maybeAddModuleForSendingTextMessage(
+ context, modules, normalizedNumber, row.numberAttributes().getIsBlocked());
}
if (!modules.isEmpty()) {
@@ -68,10 +61,23 @@ final class Modules {
}
- // TODO(zachh): Module for blocking/unblocking spam.
// TODO(zachh): Module for CallComposer.
if (canPlaceCalls) {
+ SharedModules.maybeAddModuleForAddingToContacts(
+ context,
+ modules,
+ row.number(),
+ row.numberAttributes().getName(),
+ row.numberAttributes().getLookupUri(),
+ row.numberAttributes().getIsBlocked(),
+ row.numberAttributes().getIsSpam());
+ SharedModules.addModulesHandlingBlockedOrSpamNumber(
+ context,
+ modules,
+ normalizedNumber,
+ row.numberAttributes().getIsBlocked(),
+ row.numberAttributes().getIsSpam());
SharedModules.maybeAddModuleForCopyingNumber(context, modules, normalizedNumber);
}
@@ -89,10 +95,23 @@ final class Modules {
List<ContactActionModule> modules,
CoalescedRow row,
String normalizedNumber) {
+ // If a number is blocked, skip this menu item.
+ if (row.numberAttributes().getIsBlocked()) {
+ return;
+ }
+
PhoneAccountHandle phoneAccountHandle =
TelecomUtil.composePhoneAccountHandle(
row.phoneAccountComponentName(), row.phoneAccountId());
+ // For a spam number, only audio calls are allowed.
+ if (row.numberAttributes().getIsSpam()) {
+ modules.add(
+ IntentModule.newCallModule(
+ context, normalizedNumber, phoneAccountHandle, CallInitiationType.Type.CALL_LOG));
+ return;
+ }
+
if ((row.features() & Calls.FEATURES_VIDEO) == Calls.FEATURES_VIDEO) {
// Add an audio call item for video calls. Clicking the top entry on the bottom sheet will
// trigger a video call.
diff --git a/java/com/android/dialer/calllog/ui/menu/NewCallLogMenu.java b/java/com/android/dialer/calllog/ui/menu/NewCallLogMenu.java
index 81c05135f..02724e628 100644
--- a/java/com/android/dialer/calllog/ui/menu/NewCallLogMenu.java
+++ b/java/com/android/dialer/calllog/ui/menu/NewCallLogMenu.java
@@ -17,10 +17,15 @@
package com.android.dialer.calllog.ui.menu;
import android.content.Context;
+import android.provider.CallLog.Calls;
import android.view.View;
+import com.android.dialer.calllog.CallLogComponent;
import com.android.dialer.calllog.model.CoalescedRow;
+import com.android.dialer.common.concurrent.DefaultFutureCallback;
import com.android.dialer.contactactions.ContactActionBottomSheet;
import com.android.dialer.glidephotomanager.GlidePhotoManager;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.MoreExecutors;
/** Handles configuration of the bottom sheet menus for call log entries. */
public final class NewCallLogMenu {
@@ -28,11 +33,23 @@ public final class NewCallLogMenu {
/** Creates and returns the OnClickListener which opens the menu for the provided row. */
public static View.OnClickListener createOnClickListener(
Context context, CoalescedRow row, GlidePhotoManager glidePhotoManager) {
- return (view) ->
- ContactActionBottomSheet.show(
- context,
- PrimaryAction.fromRow(context, row),
- Modules.fromRow(context, row),
- glidePhotoManager);
+ return view -> {
+ ContactActionBottomSheet.show(
+ context,
+ PrimaryAction.fromRow(context, row),
+ Modules.fromRow(context, row),
+ glidePhotoManager);
+
+ // If the user opens the bottom sheet for a new call, clear the notifications and make the row
+ // not bold immediately. To do this, mark all of the calls in group as not new.
+ if (row.isNew() && row.callType() == Calls.MISSED_TYPE) {
+ Futures.addCallback(
+ CallLogComponent.get(context)
+ .getClearMissedCalls()
+ .clearBySystemCallLogId(row.coalescedIds().getCoalescedIdList()),
+ new DefaultFutureCallback<>(),
+ MoreExecutors.directExecutor());
+ }
+ };
}
}
diff --git a/java/com/android/dialer/common/concurrent/DefaultFutureCallback.java b/java/com/android/dialer/common/concurrent/DefaultFutureCallback.java
new file mode 100644
index 000000000..93ad0faf0
--- /dev/null
+++ b/java/com/android/dialer/common/concurrent/DefaultFutureCallback.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.dialer.common.concurrent;
+
+import com.google.common.util.concurrent.FutureCallback;
+import com.google.common.util.concurrent.MoreExecutors;
+
+/**
+ * Returns a {@link FutureCallback} which does nothing on success and crashes the application on
+ * failure.
+ *
+ * <p>You generally shouldn't use this for futures which should be tied to UI, for those use {@link
+ * UiListener}.
+ *
+ * <p>Can be safely used with {@link MoreExecutors#directExecutor()}
+ */
+public final class DefaultFutureCallback<T> implements FutureCallback<T> {
+
+ @Override
+ public void onSuccess(T unused) {}
+
+ @Override
+ public void onFailure(Throwable throwable) {
+ ThreadUtil.getUiThreadHandler()
+ .post(
+ () -> {
+ throw new RuntimeException(throwable);
+ });
+ }
+}
diff --git a/java/com/android/dialer/common/concurrent/UiThreadExecutor.java b/java/com/android/dialer/common/concurrent/UiThreadExecutor.java
index ec51ed334..8378d69ce 100644
--- a/java/com/android/dialer/common/concurrent/UiThreadExecutor.java
+++ b/java/com/android/dialer/common/concurrent/UiThreadExecutor.java
@@ -31,7 +31,7 @@ import javax.inject.Inject;
public class UiThreadExecutor extends AbstractListeningExecutorService {
@Inject
- UiThreadExecutor() {}
+ public UiThreadExecutor() {}
@Override
public void shutdown() {
diff --git a/java/com/android/dialer/contactactions/IntentModule.java b/java/com/android/dialer/contactactions/IntentModule.java
index aa7fd25a6..9a345c669 100644
--- a/java/com/android/dialer/contactactions/IntentModule.java
+++ b/java/com/android/dialer/contactactions/IntentModule.java
@@ -72,7 +72,7 @@ public class IntentModule implements ContactActionModule {
context,
new CallIntentBuilder(number, initiationType)
.setPhoneAccountHandle(phoneAccountHandle)),
- R.string.call,
+ R.string.voice_call,
R.drawable.quantum_ic_call_white_24);
}
diff --git a/java/com/android/dialer/contactactions/SharedModules.java b/java/com/android/dialer/contactactions/SharedModules.java
index 7e72863aa..6d97fcb61 100644
--- a/java/com/android/dialer/contactactions/SharedModules.java
+++ b/java/com/android/dialer/contactactions/SharedModules.java
@@ -20,14 +20,15 @@ import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.provider.ContactsContract;
-import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.text.TextUtils;
+import android.widget.Toast;
import com.android.dialer.DialerPhoneNumber;
import com.android.dialer.clipboard.ClipboardUtils;
import com.android.dialer.util.IntentUtil;
import com.android.dialer.util.UriUtils;
import java.util.List;
+import java.util.Locale;
/**
* Modules for the bottom sheet that are shared between NewVoicemailFragment and NewCallLogFragment
@@ -37,10 +38,15 @@ public class SharedModules {
public static void maybeAddModuleForAddingToContacts(
Context context,
List<ContactActionModule> modules,
- @NonNull DialerPhoneNumber number,
- @Nullable String name,
- @Nullable String lookupUri) {
- // TODO(zachh): Only show this for non-spam/blocked numbers.
+ DialerPhoneNumber number,
+ String name,
+ String lookupUri,
+ boolean isBlocked,
+ boolean isSpam) {
+ // Skip showing the menu item for a spam/blocked number.
+ if (isBlocked || isSpam) {
+ return;
+ }
// Skip showing the menu item for existing contacts.
if (isExistingContact(lookupUri)) {
@@ -83,22 +89,148 @@ public class SharedModules {
}
public static void maybeAddModuleForSendingTextMessage(
- Context context, List<ContactActionModule> modules, String originalNumber) {
+ Context context,
+ List<ContactActionModule> modules,
+ String normalizedNumber,
+ boolean isBlocked) {
+ // Don't show the option to send a text message if the number is blocked.
+ if (isBlocked) {
+ return;
+ }
+
// TODO(zachh): There are some conditions where this module should not be shown; consider
- // voicemail, business numbers, blocked numbers, spam numbers, etc.
- if (!TextUtils.isEmpty(originalNumber)) {
+ // voicemail, business numbers, etc.
+
+ if (!TextUtils.isEmpty(normalizedNumber)) {
modules.add(
new IntentModule(
context,
- IntentUtil.getSendSmsIntent(originalNumber),
+ IntentUtil.getSendSmsIntent(normalizedNumber),
R.string.send_a_message,
R.drawable.quantum_ic_message_vd_theme_24));
}
}
+ public static void addModulesHandlingBlockedOrSpamNumber(
+ Context context,
+ List<ContactActionModule> modules,
+ String normalizedNumber,
+ boolean isBlocked,
+ boolean isSpam) {
+ // For a spam number, add two options:
+ // (1) "Not spam" and "Block", or
+ // (2) "Not spam" and "Unblock".
+ if (isSpam) {
+ addModuleForMarkingNumberAsNonSpam(context, modules, normalizedNumber);
+ addModuleForBlockingOrUnblockingNumber(context, modules, normalizedNumber, isBlocked);
+ return;
+ }
+
+ // For a blocked non-spam number, add "Unblock" option.
+ if (isBlocked) {
+ addModuleForBlockingOrUnblockingNumber(context, modules, normalizedNumber, isBlocked);
+ return;
+ }
+
+ // For a number that is neither a spam number nor blocked, add "Block/Report spam" option.
+ addModuleForBlockingNumberAndOptionallyReportingSpam(context, modules, normalizedNumber);
+ }
+
+ private static void addModuleForMarkingNumberAsNonSpam(
+ Context context, List<ContactActionModule> modules, String normalizedNumber) {
+ modules.add(
+ new ContactActionModule() {
+ @Override
+ public int getStringId() {
+ return R.string.not_spam;
+ }
+
+ @Override
+ public int getDrawableId() {
+ return R.drawable.quantum_ic_report_off_vd_theme_24;
+ }
+
+ @Override
+ public boolean onClick() {
+ // TODO(a bug): implement this method.
+ Toast.makeText(
+ context,
+ String.format(Locale.ENGLISH, "TODO: Report %s as non-spam", normalizedNumber),
+ Toast.LENGTH_SHORT)
+ .show();
+ return true; // Close the bottom sheet.
+ }
+ });
+ }
+
+ private static void addModuleForBlockingOrUnblockingNumber(
+ Context context,
+ List<ContactActionModule> modules,
+ String normalizedNumber,
+ boolean isBlocked) {
+ modules.add(
+ new ContactActionModule() {
+ @Override
+ public int getStringId() {
+ return isBlocked ? R.string.unblock_number : R.string.block_number;
+ }
+
+ @Override
+ public int getDrawableId() {
+ return isBlocked
+ ? R.drawable.ic_unblock // TODO(a bug): use a vector icon
+ : R.drawable.quantum_ic_block_vd_theme_24;
+ }
+
+ @Override
+ public boolean onClick() {
+ // TODO(a bug): implement this method.
+ Toast.makeText(
+ context,
+ String.format(
+ Locale.ENGLISH,
+ "TODO: " + (isBlocked ? "Unblock " : "Block ") + " number %s.",
+ normalizedNumber),
+ Toast.LENGTH_SHORT)
+ .show();
+ return true; // Close the bottom sheet.
+ }
+ });
+ }
+
+ private static void addModuleForBlockingNumberAndOptionallyReportingSpam(
+ Context context, List<ContactActionModule> modules, String normalizedNumber) {
+ modules.add(
+ new ContactActionModule() {
+ @Override
+ public int getStringId() {
+ return R.string.block_and_optionally_report_spam;
+ }
+
+ @Override
+ public int getDrawableId() {
+ return R.drawable.quantum_ic_block_vd_theme_24;
+ }
+
+ @Override
+ public boolean onClick() {
+ // TODO(a bug): implement this method.
+ Toast.makeText(
+ context,
+ String.format(
+ Locale.ENGLISH,
+ "TODO: Block and optionally report as spam %s.",
+ normalizedNumber),
+ Toast.LENGTH_SHORT)
+ .show();
+ return true; // Close the bottom sheet.
+ }
+ });
+ }
+
public static void maybeAddModuleForCopyingNumber(
- Context context, List<ContactActionModule> modules, String originalNumber) {
- if (TextUtils.isEmpty(originalNumber)) {
+ Context context, List<ContactActionModule> modules, String normalizedNumber) {
+ if (TextUtils.isEmpty(normalizedNumber)) {
return;
}
modules.add(
@@ -115,7 +247,7 @@ public class SharedModules {
@Override
public boolean onClick() {
- ClipboardUtils.copyText(context, null, originalNumber, true);
+ ClipboardUtils.copyText(context, null, normalizedNumber, true);
return false;
}
});
diff --git a/java/com/android/dialer/contactactions/res/drawable-xxxhdpi/ic_unblock.png b/java/com/android/dialer/contactactions/res/drawable-xxxhdpi/ic_unblock.png
new file mode 100644
index 000000000..01551e2fc
--- /dev/null
+++ b/java/com/android/dialer/contactactions/res/drawable-xxxhdpi/ic_unblock.png
Binary files differ
diff --git a/java/com/android/dialer/contactactions/res/values/strings.xml b/java/com/android/dialer/contactactions/res/values/strings.xml
index 0e953a56d..4d598a930 100644
--- a/java/com/android/dialer/contactactions/res/values/strings.xml
+++ b/java/com/android/dialer/contactactions/res/values/strings.xml
@@ -16,13 +16,31 @@
-->
<resources>
+ <!-- Option shown in call log/voicemail menu to make a voice call [CHAR LIMIT=30] -->
+ <string name="voice_call">Voice call</string>
+
+ <!-- Option shown in a call log/voicemail menu to make a video call [CHAR LIMIT=30] -->
+ <string name="video_call">Video call</string>
+
<!-- Option shown in call log menu/voicemail to add the phone number from an entry to an existing contact
(also provides option to create a new contact from the number). [CHAR LIMIT=30] -->
- <string name="add_to_contacts">Add to contacts</string>
+ <string name="add_to_contacts">Add contact</string>
<!-- Options shown in call log/voicemail menu to send a SMS to the number represented by the call log/voicemailentry.
[CHAR LIMIT=30] -->
- <string name="send_a_message">Send a message</string>
+ <string name="send_a_message">Message</string>
+
+ <!-- Options shown in call log/voicemail menu to mark a number as non-spam. [CHAR LIMIT=30] -->
+ <string name="not_spam">Not spam</string>
+
+ <!-- Options shown in a call log/voicemail menu to block a number. [CHAR LIMIT=30] -->
+ <string name="block_number">Block</string>
+
+ <!-- Options shown in a call log/voicemail menu to unblock a number. [CHAR LIMIT=30] -->
+ <string name="unblock_number">Unblock</string>
+
+ <!-- Options shown in a call log/voicemail menu to block a number and/or report it as spam. [CHAR LIMIT=30] -->
+ <string name="block_and_optionally_report_spam">Block/Report spam</string>
<!-- Option displayed in call log/voicemail menu to copy phone number. [CHAR LIMIT=30] -->
<string name="copy_number">Copy number</string>
diff --git a/java/com/android/dialer/main/impl/NewMainActivityPeer.java b/java/com/android/dialer/main/impl/NewMainActivityPeer.java
index 789648928..0a85667a1 100644
--- a/java/com/android/dialer/main/impl/NewMainActivityPeer.java
+++ b/java/com/android/dialer/main/impl/NewMainActivityPeer.java
@@ -16,16 +16,22 @@
package com.android.dialer.main.impl;
+import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
+import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
+import com.android.dialer.calllog.CallLogComponent;
import com.android.dialer.calllog.ui.NewCallLogFragment;
+import com.android.dialer.common.concurrent.DefaultFutureCallback;
import com.android.dialer.main.MainActivityPeer;
import com.android.dialer.main.impl.bottomnav.BottomNavBar;
import com.android.dialer.main.impl.bottomnav.BottomNavBar.OnBottomNavTabSelectedListener;
import com.android.dialer.main.impl.bottomnav.BottomNavBar.TabIndex;
import com.android.dialer.voicemail.listui.NewVoicemailFragment;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.MoreExecutors;
/** MainActivityPeer that implements the new fragments. */
public class NewMainActivityPeer implements MainActivityPeer {
@@ -40,7 +46,8 @@ public class NewMainActivityPeer implements MainActivityPeer {
public void onActivityCreate(Bundle saveInstanceState) {
mainActivity.setContentView(R.layout.main_activity);
MainBottomNavBarBottomNavTabListener bottomNavBarBottomNavTabListener =
- new MainBottomNavBarBottomNavTabListener(mainActivity.getSupportFragmentManager());
+ new MainBottomNavBarBottomNavTabListener(
+ mainActivity.getSupportFragmentManager(), mainActivity.getApplicationContext());
BottomNavBar bottomNav = mainActivity.findViewById(R.id.bottom_nav_bar);
bottomNav.addOnTabSelectedListener(bottomNavBarBottomNavTabListener);
bottomNav.selectTab(TabIndex.SPEED_DIAL);
@@ -77,9 +84,12 @@ public class NewMainActivityPeer implements MainActivityPeer {
private static final String VOICEMAIL_TAG = "voicemail";
private final FragmentManager supportFragmentManager;
+ private final Context appContext;
- private MainBottomNavBarBottomNavTabListener(FragmentManager supportFragmentManager) {
+ private MainBottomNavBarBottomNavTabListener(
+ FragmentManager supportFragmentManager, Context appContext) {
this.supportFragmentManager = supportFragmentManager;
+ this.appContext = appContext;
}
@Override
@@ -126,8 +136,18 @@ public class NewMainActivityPeer implements MainActivityPeer {
private void hideAllFragments() {
FragmentTransaction supportTransaction = supportFragmentManager.beginTransaction();
- if (supportFragmentManager.findFragmentByTag(CALL_LOG_TAG) != null) {
- supportTransaction.hide(supportFragmentManager.findFragmentByTag(CALL_LOG_TAG));
+ Fragment callLogFragment = supportFragmentManager.findFragmentByTag(CALL_LOG_TAG);
+ if (callLogFragment != null) {
+ if (callLogFragment.isVisible()) {
+ // If the user taps any bottom nav button and the call log is showing, immediately cancel
+ // missed calls (unbold them and clear their notifications).
+ Futures.addCallback(
+ // TODO(zachh): Use dagger to create Peer and MainBottomNavBarBottomNavTabListener.
+ CallLogComponent.get(appContext).getClearMissedCalls().clearAll(),
+ new DefaultFutureCallback<>(),
+ MoreExecutors.directExecutor());
+ }
+ supportTransaction.hide(callLogFragment);
}
if (supportFragmentManager.findFragmentByTag(VOICEMAIL_TAG) != null) {
supportTransaction.hide(supportFragmentManager.findFragmentByTag(VOICEMAIL_TAG));
diff --git a/java/com/android/dialer/main/impl/bottomnav/BottomNavBar.java b/java/com/android/dialer/main/impl/bottomnav/BottomNavBar.java
index a580aee68..2945e39a9 100644
--- a/java/com/android/dialer/main/impl/bottomnav/BottomNavBar.java
+++ b/java/com/android/dialer/main/impl/bottomnav/BottomNavBar.java
@@ -162,6 +162,7 @@ public final class BottomNavBar extends LinearLayout {
}
}
+ @TabIndex
public int getSelectedTab() {
return selectedTab;
}
diff --git a/java/com/android/dialer/notification/missedcalls/MissedCallConstants.java b/java/com/android/dialer/notification/missedcalls/MissedCallConstants.java
new file mode 100644
index 000000000..8553ea58e
--- /dev/null
+++ b/java/com/android/dialer/notification/missedcalls/MissedCallConstants.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dialer.notification.missedcalls;
+
+/** Constants related to missed call notifications. */
+public final class MissedCallConstants {
+
+ /** Prefix used to generate a unique tag for each missed call notification. */
+ public static final String NOTIFICATION_TAG_PREFIX = "MissedCall_";
+
+ /** Common ID for all missed call notifications. */
+ public static final int NOTIFICATION_ID = 1;
+
+ /** Tag for the group summary notification. */
+ public static final String GROUP_SUMMARY_NOTIFICATION_TAG = "GroupSummary_MissedCall";
+
+ /**
+ * Key used to associate all missed call notifications and the summary as belonging to a single
+ * group.
+ */
+ public static final String GROUP_KEY = "MissedCallGroup";
+}
diff --git a/java/com/android/dialer/notification/missedcalls/MissedCallNotificationCanceller.java b/java/com/android/dialer/notification/missedcalls/MissedCallNotificationCanceller.java
new file mode 100644
index 000000000..8798c2127
--- /dev/null
+++ b/java/com/android/dialer/notification/missedcalls/MissedCallNotificationCanceller.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.dialer.notification.missedcalls;
+
+import android.content.Context;
+import android.net.Uri;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import com.android.dialer.common.LogUtil;
+import com.android.dialer.notification.DialerNotificationManager;
+import com.android.dialer.notification.NotificationManagerUtils;
+
+/** Cancels missed calls notifications. */
+public final class MissedCallNotificationCanceller {
+
+ /** Cancels all missed call notifications. */
+ public static void cancelAll(@NonNull Context context) {
+ NotificationManagerUtils.cancelAllInGroup(context, MissedCallConstants.GROUP_KEY);
+ }
+
+ /** Cancels a missed call notification for a single call. */
+ public static void cancelSingle(@NonNull Context context, @Nullable Uri callUri) {
+ if (callUri == null) {
+ LogUtil.e(
+ "MissedCallNotificationCanceller.cancelSingle",
+ "unable to cancel notification, uri is null");
+ return;
+ }
+ // This will also dismiss the group summary if there are no more missed call notifications.
+ DialerNotificationManager.cancel(
+ context,
+ MissedCallNotificationTags.getNotificationTagForCallUri(callUri),
+ MissedCallConstants.NOTIFICATION_ID);
+ }
+}
diff --git a/java/com/android/dialer/notification/missedcalls/MissedCallNotificationTags.java b/java/com/android/dialer/notification/missedcalls/MissedCallNotificationTags.java
new file mode 100644
index 000000000..64f28eeec
--- /dev/null
+++ b/java/com/android/dialer/notification/missedcalls/MissedCallNotificationTags.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dialer.notification.missedcalls;
+
+import android.net.Uri;
+import android.support.annotation.NonNull;
+
+/** Static methods related to missed call notification tags. */
+public final class MissedCallNotificationTags {
+
+ /** Gets the notification tag for a single call. */
+ public static String getNotificationTagForCallUri(@NonNull Uri callUri) {
+ return MissedCallConstants.NOTIFICATION_TAG_PREFIX + callUri;
+ }
+}
diff --git a/java/com/android/dialer/searchfragment/common/res/layout/search_contact_row.xml b/java/com/android/dialer/searchfragment/common/res/layout/search_contact_row.xml
index 407207a83..9be7fa046 100644
--- a/java/com/android/dialer/searchfragment/common/res/layout/search_contact_row.xml
+++ b/java/com/android/dialer/searchfragment/common/res/layout/search_contact_row.xml
@@ -65,4 +65,15 @@
android:tint="@color/dialer_secondary_text_color"
android:visibility="gone"
android:scaleType="center"/>
+
+ <ImageView
+ android:id="@+id/work_icon"
+ android:layout_width="@dimen/search_row_height"
+ android:layout_height="@dimen/search_row_height"
+ android:layout_alignParentEnd="true"
+ android:layout_centerVertical="true"
+ android:padding="@dimen/call_to_action_padding"
+ android:src="@drawable/ic_work_profile"
+ android:scaleType="centerInside"
+ android:visibility="gone"/>
</RelativeLayout> \ No newline at end of file
diff --git a/java/com/android/dialer/searchfragment/remote/RemoteContactViewHolder.java b/java/com/android/dialer/searchfragment/remote/RemoteContactViewHolder.java
index 9d369003d..4be96fe58 100644
--- a/java/com/android/dialer/searchfragment/remote/RemoteContactViewHolder.java
+++ b/java/com/android/dialer/searchfragment/remote/RemoteContactViewHolder.java
@@ -26,8 +26,10 @@ import android.provider.ContactsContract.Contacts;
import android.support.v7.widget.RecyclerView;
import android.text.TextUtils;
import android.view.View;
+import android.widget.ImageView;
import android.widget.QuickContactBadge;
import android.widget.TextView;
+import com.android.contacts.common.compat.DirectoryCompat;
import com.android.dialer.callintent.CallInitiationType;
import com.android.dialer.callintent.CallIntentBuilder;
import com.android.dialer.contactphoto.ContactPhotoManager;
@@ -46,6 +48,7 @@ public final class RemoteContactViewHolder extends RecyclerView.ViewHolder
private final TextView nameView;
private final TextView numberView;
private final QuickContactBadge photo;
+ private final ImageView workBadge;
private String number;
@@ -55,6 +58,7 @@ public final class RemoteContactViewHolder extends RecyclerView.ViewHolder
photo = view.findViewById(R.id.photo);
nameView = view.findViewById(R.id.primary);
numberView = view.findViewById(R.id.secondary);
+ workBadge = view.findViewById(R.id.work_icon);
context = view.getContext();
}
@@ -74,6 +78,10 @@ public final class RemoteContactViewHolder extends RecyclerView.ViewHolder
nameView.setText(QueryBoldingUtil.getNameWithQueryBolded(query, name, context));
numberView.setText(QueryBoldingUtil.getNameWithQueryBolded(query, secondaryInfo, context));
+ workBadge.setVisibility(
+ DirectoryCompat.isOnlyEnterpriseDirectoryId(cursor.getDirectoryId())
+ ? View.VISIBLE
+ : View.GONE);
if (shouldShowPhoto(cursor)) {
nameView.setVisibility(View.VISIBLE);
diff --git a/java/com/android/dialer/searchfragment/remote/RemoteContactsCursor.java b/java/com/android/dialer/searchfragment/remote/RemoteContactsCursor.java
index 9510443b9..653c67041 100644
--- a/java/com/android/dialer/searchfragment/remote/RemoteContactsCursor.java
+++ b/java/com/android/dialer/searchfragment/remote/RemoteContactsCursor.java
@@ -22,6 +22,7 @@ import android.database.MatrixCursor;
import android.database.MergeCursor;
import android.support.annotation.Nullable;
import android.support.annotation.VisibleForTesting;
+import com.android.contacts.common.compat.DirectoryCompat;
import com.android.dialer.common.Assert;
import com.android.dialer.searchfragment.common.SearchCursor;
import com.android.dialer.searchfragment.directories.DirectoriesCursorLoader;
@@ -101,7 +102,12 @@ public final class RemoteContactsCursor extends MergeCursor implements SearchCur
private static MatrixCursor createHeaderCursor(Context context, String name, long id) {
MatrixCursor headerCursor = new MatrixCursor(PROJECTION, 1);
- headerCursor.addRow(new Object[] {context.getString(R.string.directory, name), id});
+ if (DirectoryCompat.isOnlyEnterpriseDirectoryId(id)) {
+ headerCursor.addRow(
+ new Object[] {context.getString(R.string.directory_search_label_work), id});
+ } else {
+ headerCursor.addRow(new Object[] {context.getString(R.string.directory, name), id});
+ }
return headerCursor;
}
diff --git a/java/com/android/dialer/searchfragment/remote/RemoteContactsCursorLoader.java b/java/com/android/dialer/searchfragment/remote/RemoteContactsCursorLoader.java
index 9feeb7e99..cf495e49c 100644
--- a/java/com/android/dialer/searchfragment/remote/RemoteContactsCursorLoader.java
+++ b/java/com/android/dialer/searchfragment/remote/RemoteContactsCursorLoader.java
@@ -27,6 +27,7 @@ import android.provider.ContactsContract;
import android.provider.ContactsContract.CommonDataKinds.Phone;
import android.support.annotation.NonNull;
import android.support.annotation.VisibleForTesting;
+import com.android.contacts.common.compat.DirectoryCompat;
import com.android.dialer.searchfragment.common.Projections;
import com.android.dialer.searchfragment.directories.DirectoriesCursorLoader.Directory;
import java.util.ArrayList;
@@ -71,7 +72,14 @@ public final class RemoteContactsCursorLoader extends CursorLoader {
Directory directory = directories.get(i);
// Filter out local directories
- if (!isRemoteDirectory(directory.getId())) {
+ if (!DirectoryCompat.isRemoteDirectoryId(directory.getId())
+ && !DirectoryCompat.isEnterpriseDirectoryId(directory.getId())) {
+ cursors[i] = null;
+ continue;
+ }
+
+ // Filter out invisible directories
+ if (DirectoryCompat.isInvisibleDirectory(directory.getId())) {
cursors[i] = null;
continue;
}
@@ -93,17 +101,6 @@ public final class RemoteContactsCursorLoader extends CursorLoader {
return RemoteContactsCursor.newInstance(getContext(), cursors, directories);
}
- private static boolean isRemoteDirectory(long directoryId) {
- return VERSION.SDK_INT >= VERSION_CODES.N
- ? ContactsContract.Directory.isRemoteDirectoryId(directoryId)
- : (directoryId != ContactsContract.Directory.DEFAULT
- && directoryId != ContactsContract.Directory.LOCAL_INVISIBLE
- // Directory.ENTERPRISE_DEFAULT is the default work profile directory for locally stored
- // contacts
- && directoryId != ContactsContract.Directory.ENTERPRISE_DEFAULT
- && directoryId != ContactsContract.Directory.ENTERPRISE_LOCAL_INVISIBLE);
- }
-
private MatrixCursor createMatrixCursorFilteringNullNumbers(Cursor cursor) {
if (cursor == null) {
return null;
diff --git a/java/com/android/dialer/simulator/impl/SimulatorSimCallManager.java b/java/com/android/dialer/simulator/impl/SimulatorSimCallManager.java
index 00899fd69..f28393c0c 100644
--- a/java/com/android/dialer/simulator/impl/SimulatorSimCallManager.java
+++ b/java/com/android/dialer/simulator/impl/SimulatorSimCallManager.java
@@ -29,6 +29,7 @@ import android.telecom.TelecomManager;
import android.telephony.TelephonyManager;
import com.android.dialer.common.Assert;
import com.android.dialer.common.LogUtil;
+import com.android.dialer.strictmode.StrictModeUtils;
import java.util.Arrays;
import java.util.List;
import java.util.Random;
@@ -53,17 +54,23 @@ public class SimulatorSimCallManager {
static void register(@NonNull Context context) {
LogUtil.enterBlock("SimulatorSimCallManager.register");
Assert.isNotNull(context);
- TelecomManager telecomManager = context.getSystemService(TelecomManager.class);
- telecomManager.registerPhoneAccount(buildSimCallManagerAccount(context));
- telecomManager.registerPhoneAccount(buildVideoProviderAccount(context));
+ StrictModeUtils.bypass(
+ () -> {
+ TelecomManager telecomManager = context.getSystemService(TelecomManager.class);
+ telecomManager.registerPhoneAccount(buildSimCallManagerAccount(context));
+ telecomManager.registerPhoneAccount(buildVideoProviderAccount(context));
+ });
}
static void unregister(@NonNull Context context) {
LogUtil.enterBlock("SimulatorSimCallManager.unregister");
Assert.isNotNull(context);
- TelecomManager telecomManager = context.getSystemService(TelecomManager.class);
- telecomManager.unregisterPhoneAccount(getSimCallManagerHandle(context));
- telecomManager.unregisterPhoneAccount(getVideoProviderHandle(context));
+ StrictModeUtils.bypass(
+ () -> {
+ TelecomManager telecomManager = context.getSystemService(TelecomManager.class);
+ telecomManager.unregisterPhoneAccount(getSimCallManagerHandle(context));
+ telecomManager.unregisterPhoneAccount(getVideoProviderHandle(context));
+ });
}
@NonNull
diff --git a/java/com/android/dialer/theme/res/values/strings.xml b/java/com/android/dialer/theme/res/values/strings.xml
index 74cabadf7..a14693f51 100644
--- a/java/com/android/dialer/theme/res/values/strings.xml
+++ b/java/com/android/dialer/theme/res/values/strings.xml
@@ -30,9 +30,6 @@
used in the Launcher icon. -->
<string name="launcherActivityLabel">Phone</string>
- <!-- text on a button, Video call, as in to place a video call. -->
- <string name="video_call">Video call</string>
-
<!-- Label shown on the 'positive' button for the dialog. Indicates that the call will proceed -->
<string name="call">Call</string>
</resources>
diff --git a/java/com/android/dialer/voicemail/listui/menu/Modules.java b/java/com/android/dialer/voicemail/listui/menu/Modules.java
index 665031a1f..7254ad651 100644
--- a/java/com/android/dialer/voicemail/listui/menu/Modules.java
+++ b/java/com/android/dialer/voicemail/listui/menu/Modules.java
@@ -41,16 +41,25 @@ final class Modules {
modules,
voicemailEntry.number(),
voicemailEntry.numberAttributes().getName(),
- voicemailEntry.numberAttributes().getLookupUri());
+ voicemailEntry.numberAttributes().getLookupUri(),
+ voicemailEntry.numberAttributes().getIsBlocked(),
+ voicemailEntry.numberAttributes().getIsSpam());
String normalizedNumber = voicemailEntry.number().getNormalizedNumber();
- SharedModules.maybeAddModuleForSendingTextMessage(context, modules, normalizedNumber);
+ SharedModules.maybeAddModuleForSendingTextMessage(
+ context, modules, normalizedNumber, voicemailEntry.numberAttributes().getIsBlocked());
if (!modules.isEmpty()) {
modules.add(new DividerModule());
}
- // TODO(zachh): Module for blocking/unblocking spam.
+ SharedModules.addModulesHandlingBlockedOrSpamNumber(
+ context,
+ modules,
+ normalizedNumber,
+ voicemailEntry.numberAttributes().getIsBlocked(),
+ voicemailEntry.numberAttributes().getIsSpam());
+
// TODO(zachh): Module for CallComposer.
SharedModules.maybeAddModuleForCopyingNumber(context, modules, normalizedNumber);