From c18ca0fba4375134107c6e71dab9bd0f85a2c968 Mon Sep 17 00:00:00 2001 From: mdooley Date: Thu, 7 Sep 2017 17:15:59 -0700 Subject: Updating voicemail notifications This is mostly just a revived version of cl/158901400. It adds a job service that is triggered by changes to the voicemail database. The job updates voicemail notifications, as necessary. video of notification update: https://drive.google.com/open?id=0B9o_KvtLkcuId1ptNk1EbGotWFU Bug: 37340510,27535759 Test: manual and unit test PiperOrigin-RevId: 167934550 Change-Id: I36f03c0769645f7a0cb478172171f1079eca2108 --- java/com/android/dialer/app/AndroidManifest.xml | 5 ++ .../calllog/CallLogNotificationsQueryHelper.java | 28 +++++++ .../app/calllog/VisualVoicemailNotifier.java | 4 +- .../app/calllog/VisualVoicemailUpdateTask.java | 58 ++++++++++++-- .../calllog/VoicemailNotificationJobService.java | 89 ++++++++++++++++++++++ .../dialer/app/calllog/VoicemailQueryHandler.java | 10 ++- 6 files changed, 183 insertions(+), 11 deletions(-) create mode 100644 java/com/android/dialer/app/calllog/VoicemailNotificationJobService.java (limited to 'java/com/android/dialer/app') diff --git a/java/com/android/dialer/app/AndroidManifest.xml b/java/com/android/dialer/app/AndroidManifest.xml index 4200082a6..2ef5dad14 100644 --- a/java/com/android/dialer/app/AndroidManifest.xml +++ b/java/com/android/dialer/app/AndroidManifest.xml @@ -103,6 +103,11 @@ android:name="com.android.dialer.app.calllog.CallLogNotificationsService" /> + + diff --git a/java/com/android/dialer/app/calllog/CallLogNotificationsQueryHelper.java b/java/com/android/dialer/app/calllog/CallLogNotificationsQueryHelper.java index 649a6639d..2f8b1f476 100644 --- a/java/com/android/dialer/app/calllog/CallLogNotificationsQueryHelper.java +++ b/java/com/android/dialer/app/calllog/CallLogNotificationsQueryHelper.java @@ -136,6 +136,10 @@ public class CallLogNotificationsQueryHelper { return new DefaultNewCallsQuery(context.getApplicationContext(), contentResolver); } + NewCallsQuery getNewCallsQuery() { + return mNewCallsQuery; + } + /** * Get all voicemails with the "new" flag set to 1. * @@ -219,6 +223,10 @@ public class CallLogNotificationsQueryHelper { /** Returns the new calls of a certain type for which a notification should be generated. */ @Nullable List query(int type); + + /** Returns a {@link NewCall} pointed by the {@code callsUri} */ + @Nullable + NewCall query(Uri callsUri); } /** Information about a new voicemail. */ @@ -346,6 +354,26 @@ public class CallLogNotificationsQueryHelper { } } + @Nullable + @Override + public NewCall query(Uri callsUri) { + if (!PermissionsUtil.hasPermission(mContext, Manifest.permission.READ_CALL_LOG)) { + LogUtil.w( + "CallLogNotificationsQueryHelper.DefaultNewCallsQuery.query", + "No READ_CALL_LOG permission, returning null for calls lookup."); + return null; + } + try (Cursor cursor = mContentResolver.query(callsUri, PROJECTION, null, null, null)) { + if (cursor == null) { + return null; + } + if (!cursor.moveToFirst()) { + return null; + } + return createNewCallsFromCursor(cursor); + } + } + /** Returns an instance of {@link NewCall} created by using the values of the cursor. */ private NewCall createNewCallsFromCursor(Cursor cursor) { String voicemailUriString = cursor.getString(VOICEMAIL_URI_COLUMN_INDEX); diff --git a/java/com/android/dialer/app/calllog/VisualVoicemailNotifier.java b/java/com/android/dialer/app/calllog/VisualVoicemailNotifier.java index 27963491d..ceae3d38e 100644 --- a/java/com/android/dialer/app/calllog/VisualVoicemailNotifier.java +++ b/java/com/android/dialer/app/calllog/VisualVoicemailNotifier.java @@ -54,9 +54,9 @@ final class VisualVoicemailNotifier { /** Prefix used to generate a unique tag for each voicemail notification. */ private static final String NOTIFICATION_TAG_PREFIX = "VisualVoicemail_"; /** Common ID for all voicemail notifications. */ - private static final int NOTIFICATION_ID = 1; + static final int NOTIFICATION_ID = 1; /** Tag for the group summary notification. */ - private static final String GROUP_SUMMARY_NOTIFICATION_TAG = "GroupSummary_VisualVoicemail"; + static final String GROUP_SUMMARY_NOTIFICATION_TAG = "GroupSummary_VisualVoicemail"; /** * Key used to associate all voicemail notifications and the summary as belonging to a single * group. diff --git a/java/com/android/dialer/app/calllog/VisualVoicemailUpdateTask.java b/java/com/android/dialer/app/calllog/VisualVoicemailUpdateTask.java index d6601be36..219ad676d 100644 --- a/java/com/android/dialer/app/calllog/VisualVoicemailUpdateTask.java +++ b/java/com/android/dialer/app/calllog/VisualVoicemailUpdateTask.java @@ -17,6 +17,8 @@ package com.android.dialer.app.calllog; import android.content.Context; +import android.net.Uri; +import android.service.notification.StatusBarNotification; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.annotation.WorkerThread; @@ -30,6 +32,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.DialerExecutors; +import com.android.dialer.notification.DialerNotificationManager; import com.android.dialer.phonenumbercache.ContactInfo; import com.android.dialer.telecom.TelecomUtil; import java.util.ArrayList; @@ -57,13 +60,20 @@ class VisualVoicemailUpdateTask implements Worker newCalls = queryHelper.getNewVoicemails(); - if (newCalls == null) { + List voicemailsToNotify = queryHelper.getNewVoicemails(); + if (voicemailsToNotify == null) { + // Query failed, just return return; } - newCalls = filterBlockedNumbers(context, queryHandler, newCalls); - if (newCalls.isEmpty()) { + + voicemailsToNotify.addAll(getAndUpdateVoicemailsWithExistingNotification(context, queryHelper)); + voicemailsToNotify = filterBlockedNumbers(context, queryHandler, voicemailsToNotify); + if (voicemailsToNotify.isEmpty()) { + LogUtil.i("VisualVoicemailUpdateTask.updateNotification", "no voicemails to notify about"); + VisualVoicemailNotifier.cancelAllVoicemailNotifications(context); + VoicemailNotificationJobService.cancelJob(context); return; } @@ -73,7 +83,7 @@ class VisualVoicemailUpdateTask implements Worker contactInfos = new ArrayMap<>(); - for (NewCall newCall : newCalls) { + for (NewCall newCall : voicemailsToNotify) { if (!contactInfos.containsKey(newCall.number)) { ContactInfo contactInfo = queryHelper.getContactInfo( @@ -90,7 +100,43 @@ class VisualVoicemailUpdateTask implements Worker getAndUpdateVoicemailsWithExistingNotification( + Context context, CallLogNotificationsQueryHelper queryHelper) { + Assert.isWorkerThread(); + List result = new ArrayList<>(); + for (StatusBarNotification notification : + DialerNotificationManager.getActiveNotifications(context)) { + if (notification.getId() != VisualVoicemailNotifier.NOTIFICATION_ID) { + continue; + } + if (TextUtils.equals( + notification.getTag(), VisualVoicemailNotifier.GROUP_SUMMARY_NOTIFICATION_TAG)) { + // Group header + continue; + } + NewCall existingCall = queryHelper.getNewCallsQuery().query(Uri.parse(notification.getTag())); + if (existingCall != null) { + result.add(existingCall); + } else { + LogUtil.i( + "VisualVoicemailUpdateTask.getVoicemailsWithExistingNotification", + "voicemail deleted, removing notification"); + DialerNotificationManager.cancel(context, notification.getTag(), notification.getId()); + } + } + return result; } @WorkerThread diff --git a/java/com/android/dialer/app/calllog/VoicemailNotificationJobService.java b/java/com/android/dialer/app/calllog/VoicemailNotificationJobService.java new file mode 100644 index 000000000..ba61601ae --- /dev/null +++ b/java/com/android/dialer/app/calllog/VoicemailNotificationJobService.java @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2017 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.app.calllog; + +import android.app.job.JobInfo; +import android.app.job.JobParameters; +import android.app.job.JobScheduler; +import android.app.job.JobService; +import android.content.ComponentName; +import android.content.Context; +import android.os.Build; +import android.provider.VoicemailContract; +import com.android.dialer.common.LogUtil; +import com.android.dialer.constants.ScheduledJobIds; + +/** Monitors voicemail provider changes to update active notifications. */ +public class VoicemailNotificationJobService extends JobService { + + private static JobInfo jobInfo; + + /** + * Start monitoring the provider. The provider should be monitored whenever a visual voicemail + * notification is visible. + */ + public static void scheduleJob(Context context) { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) { + LogUtil.i("VoicemailNotificationJobService.scheduleJob", "not supported"); + } else { + context.getSystemService(JobScheduler.class).schedule(getJobInfo(context)); + LogUtil.i("VoicemailNotificationJobService.scheduleJob", "job scheduled"); + } + } + + /** + * Stop monitoring the provider. The provider should not be monitored when visual voicemail + * notification is cleared. + */ + public static void cancelJob(Context context) { + context.getSystemService(JobScheduler.class).cancel(ScheduledJobIds.VVM_NOTIFICATION_JOB); + LogUtil.i("VoicemailNotificationJobService.scheduleJob", "job canceled"); + } + + @Override + public boolean onStartJob(JobParameters params) { + LogUtil.i("VoicemailNotificationJobService.onStartJob", "updating notification"); + VisualVoicemailUpdateTask.scheduleTask( + this, + () -> { + jobFinished(params, false); + }); + return true; // Running in background + } + + @Override + public boolean onStopJob(JobParameters params) { + return false; + } + + private static JobInfo getJobInfo(Context context) { + if (jobInfo == null) { + jobInfo = + new JobInfo.Builder( + ScheduledJobIds.VVM_NOTIFICATION_JOB, + new ComponentName(context, VoicemailNotificationJobService.class)) + .addTriggerContentUri( + new JobInfo.TriggerContentUri( + VoicemailContract.Voicemails.CONTENT_URI, + JobInfo.TriggerContentUri.FLAG_NOTIFY_FOR_DESCENDANTS)) + .setTriggerContentMaxDelay(0) + .build(); + } + + return jobInfo; + } +} diff --git a/java/com/android/dialer/app/calllog/VoicemailQueryHandler.java b/java/com/android/dialer/app/calllog/VoicemailQueryHandler.java index 2fbebdd30..169d0fd35 100644 --- a/java/com/android/dialer/app/calllog/VoicemailQueryHandler.java +++ b/java/com/android/dialer/app/calllog/VoicemailQueryHandler.java @@ -45,7 +45,8 @@ public class VoicemailQueryHandler extends AsyncQueryHandler { public static void markAllNewVoicemailsAsRead(final @NonNull Context context) { ThreadUtil.postOnUiThread( () -> { - new VoicemailQueryHandler(context.getContentResolver()).markNewVoicemailsAsOld(null); + new VoicemailQueryHandler(context.getContentResolver()) + .markNewVoicemailsAsOld(context, null); }); } @@ -59,12 +60,12 @@ public class VoicemailQueryHandler extends AsyncQueryHandler { ThreadUtil.postOnUiThread( () -> { new VoicemailQueryHandler(context.getContentResolver()) - .markNewVoicemailsAsOld(voicemailUri); + .markNewVoicemailsAsOld(context, voicemailUri); }); } /** Updates all new voicemails to mark them as old. */ - private void markNewVoicemailsAsOld(@Nullable Uri voicemailUri) { + private void markNewVoicemailsAsOld(Context context, @Nullable Uri voicemailUri) { // Mark all "new" voicemails as not new anymore. StringBuilder where = new StringBuilder(); where.append(Calls.NEW); @@ -88,5 +89,8 @@ public class VoicemailQueryHandler extends AsyncQueryHandler { voicemailUri == null ? new String[] {Integer.toString(Calls.VOICEMAIL_TYPE)} : new String[] {Integer.toString(Calls.VOICEMAIL_TYPE), voicemailUri.toString()}); + + // No more notifications, stop monitoring the voicemail provider + VoicemailNotificationJobService.cancelJob(context); } } -- cgit v1.2.3