diff options
author | calderwoodra <calderwoodra@google.com> | 2018-01-31 17:05:48 -0800 |
---|---|---|
committer | Copybara-Service <copybara-piper@google.com> | 2018-01-31 20:29:23 -0800 |
commit | f1c6988bddc52f9b37b2359d1668faf8681953b0 (patch) | |
tree | c3fb1dbdb86a0c451c99721160b246f87a020f46 /java | |
parent | ef5b8668e881cb29b8b386e5d1ea93527e3a9a52 (diff) |
Implemented missed call/voicemail notifications in NUI.
This CL shows and updates the missed call/voicemail count in the bottom nav. It
also clears the call log notifications when the user either leaves the call log
fragment or closes the activity after 3 seconds of browsing the call log.
Bug: 72525595
Test: tba
PiperOrigin-RevId: 184071058
Change-Id: I540c9e3d815e022b5e311cb02a3995e241b8a984
Diffstat (limited to 'java')
-rw-r--r-- | java/com/android/dialer/app/calllog/CallLogAdapter.java | 16 | ||||
-rw-r--r-- | java/com/android/dialer/main/impl/BottomNavBar.java | 49 | ||||
-rw-r--r-- | java/com/android/dialer/main/impl/MainActivity.java | 159 |
3 files changed, 205 insertions, 19 deletions
diff --git a/java/com/android/dialer/app/calllog/CallLogAdapter.java b/java/com/android/dialer/app/calllog/CallLogAdapter.java index 9e1d4a7b9..b8ec168f6 100644 --- a/java/com/android/dialer/app/calllog/CallLogAdapter.java +++ b/java/com/android/dialer/app/calllog/CallLogAdapter.java @@ -54,8 +54,8 @@ 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; import com.android.dialer.app.calllog.calllogcache.CallLogCache; import com.android.dialer.app.contactinfo.ContactInfoCache; @@ -68,6 +68,7 @@ import com.android.dialer.callintent.CallIntentBuilder; import com.android.dialer.calllogutils.CallbackActionHelper.CallbackAction; import com.android.dialer.calllogutils.PhoneCallDetails; import com.android.dialer.common.Assert; +import com.android.dialer.common.FragmentUtils.FragmentUtilListener; import com.android.dialer.common.LogUtil; import com.android.dialer.common.concurrent.AsyncTaskExecutor; import com.android.dialer.common.concurrent.AsyncTaskExecutors; @@ -379,7 +380,18 @@ public class CallLogAdapter extends GroupingListAdapter if (viewHolder.callType == CallLog.Calls.MISSED_TYPE) { CallLogAsyncTaskUtil.markCallAsRead(activity, viewHolder.callIds); if (activityType == ACTIVITY_TYPE_DIALTACTS) { - ((DialtactsActivity) v.getContext()).updateTabUnreadCounts(); + if (v.getContext() instanceof CallLogFragmentListener) { + ((CallLogFragmentListener) v.getContext()).updateTabUnreadCounts(); + } else if (v.getContext() instanceof FragmentUtilListener) { + // 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) v.getContext()) + .getImpl(CallLogFragmentListener.class) + .updateTabUnreadCounts(); + } else { + throw Assert.createIllegalStateFailException( + "View parent does not implement CallLogFragmentListener"); + } } } expandViewHolderActions(viewHolder); diff --git a/java/com/android/dialer/main/impl/BottomNavBar.java b/java/com/android/dialer/main/impl/BottomNavBar.java index a4ddc0652..6d43ee29d 100644 --- a/java/com/android/dialer/main/impl/BottomNavBar.java +++ b/java/com/android/dialer/main/impl/BottomNavBar.java @@ -22,8 +22,11 @@ import android.support.annotation.Nullable; import android.util.AttributeSet; import android.view.View; import android.widget.LinearLayout; +import com.android.dialer.common.Assert; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.ArrayList; +import java.util.List; /** Dialer Bottom Nav Bar for {@link MainActivity}. */ final class BottomNavBar extends LinearLayout { @@ -32,22 +35,23 @@ final class BottomNavBar extends LinearLayout { @Retention(RetentionPolicy.SOURCE) @IntDef({ TabIndex.SPEED_DIAL, - TabIndex.HISTORY, + TabIndex.CALL_LOG, TabIndex.CONTACTS, TabIndex.VOICEMAIL, }) public @interface TabIndex { int SPEED_DIAL = 0; - int HISTORY = 1; + int CALL_LOG = 1; int CONTACTS = 2; int VOICEMAIL = 3; } + private final List<OnBottomNavTabSelectedListener> listeners = new ArrayList<>(); + private BottomNavItem speedDial; private BottomNavItem callLog; private BottomNavItem contacts; private BottomNavItem voicemail; - private OnBottomNavTabSelectedListener listener; private @TabIndex int selectedTab; public BottomNavBar(Context context, @Nullable AttributeSet attrs) { @@ -71,25 +75,25 @@ final class BottomNavBar extends LinearLayout { v -> { selectedTab = TabIndex.SPEED_DIAL; setSelected(speedDial); - listener.onSpeedDialSelected(); + updateListeners(selectedTab); }); callLog.setOnClickListener( v -> { - selectedTab = TabIndex.HISTORY; + selectedTab = TabIndex.CALL_LOG; setSelected(callLog); - listener.onCallLogSelected(); + updateListeners(selectedTab); }); contacts.setOnClickListener( v -> { selectedTab = TabIndex.CONTACTS; setSelected(contacts); - listener.onContactsSelected(); + updateListeners(selectedTab); }); voicemail.setOnClickListener( v -> { selectedTab = TabIndex.VOICEMAIL; setSelected(voicemail); - listener.onVoicemailSelected(); + updateListeners(selectedTab); }); } @@ -108,7 +112,7 @@ final class BottomNavBar extends LinearLayout { void selectTab(@TabIndex int tab) { if (tab == TabIndex.SPEED_DIAL) { speedDial.performClick(); - } else if (tab == TabIndex.HISTORY) { + } else if (tab == TabIndex.CALL_LOG) { callLog.performClick(); } else if (tab == TabIndex.CONTACTS) { contacts.performClick(); @@ -122,7 +126,7 @@ final class BottomNavBar extends LinearLayout { void setNotificationCount(@TabIndex int tab, int count) { if (tab == TabIndex.SPEED_DIAL) { speedDial.setNotificationCount(count); - } else if (tab == TabIndex.HISTORY) { + } else if (tab == TabIndex.CALL_LOG) { callLog.setNotificationCount(count); } else if (tab == TabIndex.CONTACTS) { contacts.setNotificationCount(count); @@ -133,8 +137,29 @@ final class BottomNavBar extends LinearLayout { } } - void setOnTabSelectedListener(OnBottomNavTabSelectedListener listener) { - this.listener = listener; + void addOnTabSelectedListener(OnBottomNavTabSelectedListener listener) { + listeners.add(listener); + } + + private void updateListeners(@TabIndex int tabIndex) { + for (OnBottomNavTabSelectedListener listener : listeners) { + switch (tabIndex) { + case TabIndex.SPEED_DIAL: + listener.onSpeedDialSelected(); + break; + case TabIndex.CALL_LOG: + listener.onCallLogSelected(); + break; + case TabIndex.CONTACTS: + listener.onContactsSelected(); + break; + case TabIndex.VOICEMAIL: + listener.onVoicemailSelected(); + break; + default: + throw Assert.createIllegalStateFailException("Invalid tab: " + tabIndex); + } + } } public int getSelectedTab() { diff --git a/java/com/android/dialer/main/impl/MainActivity.java b/java/com/android/dialer/main/impl/MainActivity.java index 57cc684e3..b94f07588 100644 --- a/java/com/android/dialer/main/impl/MainActivity.java +++ b/java/com/android/dialer/main/impl/MainActivity.java @@ -16,10 +16,14 @@ package com.android.dialer.main.impl; +import android.app.Activity; import android.app.Fragment; import android.app.FragmentManager; +import android.app.KeyguardManager; +import android.content.ContentResolver; import android.content.Context; import android.content.Intent; +import android.database.Cursor; import android.net.Uri; import android.os.Bundle; import android.provider.CallLog.Calls; @@ -33,6 +37,7 @@ import com.android.contacts.common.list.OnPhoneNumberPickerActionListener; import com.android.dialer.app.calllog.CallLogAdapter; import com.android.dialer.app.calllog.CallLogFragment; import com.android.dialer.app.calllog.CallLogFragment.CallLogFragmentListener; +import com.android.dialer.app.calllog.CallLogNotificationsService; import com.android.dialer.app.list.DragDropController; import com.android.dialer.app.list.OldSpeedDialFragment; import com.android.dialer.app.list.OnDragDropListener; @@ -52,6 +57,7 @@ import com.android.dialer.constants.ActivityRequestCodes; import com.android.dialer.contactsfragment.ContactsFragment; import com.android.dialer.contactsfragment.ContactsFragment.Header; import com.android.dialer.contactsfragment.ContactsFragment.OnContactSelectedListener; +import com.android.dialer.database.CallLogQueryHandler; import com.android.dialer.database.Database; import com.android.dialer.dialpadview.DialpadFragment; import com.android.dialer.dialpadview.DialpadFragment.DialpadListener; @@ -62,6 +68,7 @@ import com.android.dialer.interactions.PhoneNumberInteraction.DisambigDialogDism import com.android.dialer.interactions.PhoneNumberInteraction.InteractionErrorCode; import com.android.dialer.interactions.PhoneNumberInteraction.InteractionErrorListener; import com.android.dialer.main.impl.BottomNavBar.OnBottomNavTabSelectedListener; +import com.android.dialer.main.impl.BottomNavBar.TabIndex; import com.android.dialer.main.impl.toolbar.MainToolbar; import com.android.dialer.postcall.PostCall; import com.android.dialer.precall.PreCall; @@ -73,6 +80,7 @@ import com.android.dialer.util.DialerUtils; import com.android.dialer.util.TransactionSafeActivity; import com.android.dialer.voicemail.listui.NewVoicemailFragment; import com.google.common.util.concurrent.ListenableFuture; +import java.util.concurrent.TimeUnit; /** This is the main activity for dialer. It hosts favorites, call log, search, dialpad, etc... */ // TODO(calderwoodra): Do not extend TransactionSafeActivity after new SpeedDial is launched @@ -147,7 +155,11 @@ public final class MainActivity extends TransactionSafeActivity MainBottomNavBarBottomNavTabListener bottomNavTabListener = new MainBottomNavBarBottomNavTabListener( this, getFragmentManager(), getSupportFragmentManager()); - bottomNav.setOnTabSelectedListener(bottomNavTabListener); + bottomNav.addOnTabSelectedListener(bottomNavTabListener); + + callLogFragmentListener = + new MainCallLogFragmentListener(this, getContentResolver(), bottomNav); + bottomNav.addOnTabSelectedListener(callLogFragmentListener); searchController = new MainSearchController(this, bottomNav, fab, toolbar); toolbar.setSearchBarListener(searchController); @@ -158,7 +170,7 @@ public final class MainActivity extends TransactionSafeActivity callLogAdapterOnActionModeStateChangedListener = new MainCallLogAdapterOnActionModeStateChangedListener(); callLogHostInterface = new MainCallLogHost(searchController, fab); - callLogFragmentListener = new MainCallLogFragmentListener(); + onListFragmentScrolledListener = new MainOnListFragmentScrolledListener(snackbarContainer); onPhoneNumberPickerActionListener = new MainOnPhoneNumberPickerActionListener(this); oldSpeedDialFragmentHostInterface = @@ -179,6 +191,7 @@ public final class MainActivity extends TransactionSafeActivity @Override protected void onResume() { super.onResume(); + callLogFragmentListener.onActivityResume(); // Start the thread that updates the smart dial database if the activity is recreated with a // language change. boolean forceUpdate = !CompatUtils.getLocale(this).getISO3Language().equals(savedLanguageCode); @@ -186,6 +199,13 @@ public final class MainActivity extends TransactionSafeActivity showPostCallPrompt(); } + @Override + protected void onStop() { + super.onStop(); + callLogFragmentListener.onActivityStop( + isChangingConfigurations(), getSystemService(KeyguardManager.class).isKeyguardLocked()); + } + private void showPostCallPrompt() { if (TelecomUtil.isInManagedCall(this)) { // No prompt to show if the user is in a call @@ -417,18 +437,147 @@ public final class MainActivity extends TransactionSafeActivity } } - /** @see CallLogFragmentListener */ - private static final class MainCallLogFragmentListener implements CallLogFragmentListener { + /** + * Handles the logic for callbacks from: + * + * <ul> + * <li>{@link CallLogFragment} + * <li>{@link CallLogQueryHandler} + * <li>{@link BottomNavBar} + * </ul> + * + * This mainly entails: + * + * <ul> + * <li>Handling querying for missed calls/unread voicemails. + * <li>Displaying a badge to the user in the bottom nav when there are missed calls/unread + * voicemails present. + * <li>Marking missed calls as read when appropriate. See {@link + * #markMissedCallsAsReadAndRemoveNotification()} + * <li>TODO(calderwoodra): multiselect + * <li>TODO(calderwoodra): voicemail status + * </ul> + * + * @see CallLogFragmentListener + * @see CallLogQueryHandler.Listener + * @see OnBottomNavTabSelectedListener + */ + private static final class MainCallLogFragmentListener + implements CallLogFragmentListener, + CallLogQueryHandler.Listener, + OnBottomNavTabSelectedListener { + + private final CallLogQueryHandler callLogQueryHandler; + private final BottomNavBar bottomNavBar; + private final Context context; + + private @TabIndex int currentTab = TabIndex.SPEED_DIAL; + private long timeSelected = -1; + private boolean activityIsAlive; + + MainCallLogFragmentListener( + Context context, ContentResolver contentResolver, BottomNavBar bottomNavBar) { + callLogQueryHandler = new CallLogQueryHandler(context, contentResolver, this); + this.bottomNavBar = bottomNavBar; + this.context = context; + } @Override public void updateTabUnreadCounts() { - // TODO(a bug): implement unread counts + callLogQueryHandler.fetchMissedCallsUnreadCount(); + callLogQueryHandler.fetchVoicemailUnreadCount(); } @Override public void showMultiSelectRemoveView(boolean show) { // TODO(a bug): handle multiselect mode } + + @Override + public void onVoicemailStatusFetched(Cursor statusCursor) { + // TODO(calderwoodra): handle this when voicemail is implemented + } + + @Override + public void onVoicemailUnreadCountFetched(Cursor cursor) { + if (activityIsAlive) { + bottomNavBar.setNotificationCount(TabIndex.VOICEMAIL, cursor.getCount()); + } + cursor.close(); + } + + @Override + public void onMissedCallsUnreadCountFetched(Cursor cursor) { + if (activityIsAlive) { + bottomNavBar.setNotificationCount(TabIndex.CALL_LOG, cursor.getCount()); + } + cursor.close(); + } + + @Override + public boolean onCallsFetched(Cursor combinedCursor) { + // Return false; did not take ownership of cursor + return false; + } + + @Override + public void onSpeedDialSelected() { + setCurrentTab(TabIndex.SPEED_DIAL); + } + + @Override + public void onCallLogSelected() { + setCurrentTab(TabIndex.CALL_LOG); + } + + @Override + public void onContactsSelected() { + setCurrentTab(TabIndex.CONTACTS); + } + + @Override + public void onVoicemailSelected() { + setCurrentTab(TabIndex.VOICEMAIL); + } + + private void markMissedCallsAsReadAndRemoveNotification() { + callLogQueryHandler.markMissedCallsAsRead(); + CallLogNotificationsService.cancelAllMissedCalls(context); + } + + private void setCurrentTab(@TabIndex int tabIndex) { + if (currentTab == TabIndex.CALL_LOG && tabIndex != TabIndex.CALL_LOG) { + markMissedCallsAsReadAndRemoveNotification(); + } + currentTab = tabIndex; + timeSelected = System.currentTimeMillis(); + } + + public void onActivityResume() { + activityIsAlive = true; + callLogQueryHandler.fetchVoicemailStatus(); + callLogQueryHandler.fetchMissedCallsUnreadCount(); + // Reset the tab on resume to restart the timer + setCurrentTab(bottomNavBar.getSelectedTab()); + } + + /** Should be called when {@link Activity#onStop()} is called. */ + public void onActivityStop(boolean changingConfigurations, boolean keyguardLocked) { + activityIsAlive = false; + if (viewedCallLogTabPastTimeThreshold() && !changingConfigurations && !keyguardLocked) { + markMissedCallsAsReadAndRemoveNotification(); + } + } + + /** + * Returns true if the user has been (and still is) on the history tab for long than the + * threshold. + */ + private boolean viewedCallLogTabPastTimeThreshold() { + return currentTab == TabIndex.CALL_LOG + && timeSelected != -1 + && System.currentTimeMillis() - timeSelected > TimeUnit.SECONDS.toMillis(3); + } } /** @see OnListFragmentScrolledListener */ |