summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--InCallUI/src/com/android/incallui/StatusBarNotifier.java4
-rw-r--r--InCallUI/src/com/android/incallui/ringtone/DialerRingtoneManager.java13
-rw-r--r--res/drawable-hdpi/ic_archive_white_24dp.pngbin0 -> 247 bytes
-rw-r--r--res/drawable-mdpi/ic_archive_white_24dp.pngbin0 -> 181 bytes
-rw-r--r--res/drawable-xhdpi/ic_archive_white_24dp.pngbin0 -> 267 bytes
-rw-r--r--res/drawable-xxhdpi/ic_archive_white_24dp.pngbin0 -> 390 bytes
-rw-r--r--res/drawable-xxxhdpi/ic_archive_white_24dp.pngbin0 -> 489 bytes
-rw-r--r--res/layout/phone_favorite_tile_view.xml6
-rw-r--r--res/layout/voicemail_playback_layout.xml11
-rw-r--r--res/menu/dialtacts_options.xml3
-rw-r--r--res/values/strings.xml27
-rw-r--r--src/com/android/dialer/DialtactsActivity.java16
-rw-r--r--src/com/android/dialer/calllog/CallLogAdapter.java10
-rw-r--r--src/com/android/dialer/calllog/CallLogAsyncTaskUtil.java63
-rw-r--r--src/com/android/dialer/calllog/CallLogFragment.java39
-rw-r--r--src/com/android/dialer/calllog/CallLogQueryHandler.java43
-rw-r--r--src/com/android/dialer/calllog/PhoneCallDetailsHelper.java32
-rw-r--r--src/com/android/dialer/list/ListsFragment.java40
-rw-r--r--src/com/android/dialer/voicemail/VisualVoicemailEnabledChecker.java5
-rw-r--r--src/com/android/dialer/voicemail/VoicemailPlaybackPresenter.java7
-rw-r--r--tests/src/com/android/dialer/calllog/PhoneCallDetailsHelperTest.java38
21 files changed, 312 insertions, 45 deletions
diff --git a/InCallUI/src/com/android/incallui/StatusBarNotifier.java b/InCallUI/src/com/android/incallui/StatusBarNotifier.java
index 1a4ec0935..315c0d0ae 100644
--- a/InCallUI/src/com/android/incallui/StatusBarNotifier.java
+++ b/InCallUI/src/com/android/incallui/StatusBarNotifier.java
@@ -315,7 +315,9 @@ public class StatusBarNotifier implements InCallPresenter.InCallStateListener,
audioAttributes.setContentType(AudioAttributes.CONTENT_TYPE_MUSIC);
audioAttributes.setUsage(AudioAttributes.USAGE_NOTIFICATION_RINGTONE);
notification.audioAttributes = audioAttributes.build();
- notification.vibrate = VIBRATE_PATTERN;
+ if (mDialerRingtoneManager.shouldVibrate(mContext.getContentResolver())) {
+ notification.vibrate = VIBRATE_PATTERN;
+ }
}
if (mDialerRingtoneManager.shouldPlayCallWaitingTone(callState)) {
Log.v(this, "Playing call waiting tone");
diff --git a/InCallUI/src/com/android/incallui/ringtone/DialerRingtoneManager.java b/InCallUI/src/com/android/incallui/ringtone/DialerRingtoneManager.java
index 29d3d9de3..39844e5a2 100644
--- a/InCallUI/src/com/android/incallui/ringtone/DialerRingtoneManager.java
+++ b/InCallUI/src/com/android/incallui/ringtone/DialerRingtoneManager.java
@@ -18,7 +18,9 @@ package com.android.incallui.ringtone;
import com.google.common.base.Preconditions;
+import android.content.ContentResolver;
import android.net.Uri;
+import android.provider.Settings;
import android.support.annotation.Nullable;
import com.android.contacts.common.compat.CompatUtils;
@@ -70,6 +72,17 @@ public class DialerRingtoneManager {
}
/**
+ * Determines if an incoming call should vibrate as well as ring.
+ *
+ * @param resolver {@link ContentResolver} used to look up the
+ * {@link Settings.System#VIBRATE_WHEN_RINGING} setting.
+ * @return {@code true} if the call should vibrate, {@code false} otherwise.
+ */
+ public boolean shouldVibrate(ContentResolver resolver) {
+ return Settings.System.getInt(resolver, Settings.System.VIBRATE_WHEN_RINGING, 0) != 0;
+ }
+
+ /**
* The incoming callState is never set as {@link State#CALL_WAITING} because
* {@link Call#translateState(int)} doesn't account for that case, check for it here
*/
diff --git a/res/drawable-hdpi/ic_archive_white_24dp.png b/res/drawable-hdpi/ic_archive_white_24dp.png
new file mode 100644
index 000000000..bb72e890f
--- /dev/null
+++ b/res/drawable-hdpi/ic_archive_white_24dp.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_archive_white_24dp.png b/res/drawable-mdpi/ic_archive_white_24dp.png
new file mode 100644
index 000000000..f6aa3f966
--- /dev/null
+++ b/res/drawable-mdpi/ic_archive_white_24dp.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_archive_white_24dp.png b/res/drawable-xhdpi/ic_archive_white_24dp.png
new file mode 100644
index 000000000..3513bd9fe
--- /dev/null
+++ b/res/drawable-xhdpi/ic_archive_white_24dp.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_archive_white_24dp.png b/res/drawable-xxhdpi/ic_archive_white_24dp.png
new file mode 100644
index 000000000..00e04e42b
--- /dev/null
+++ b/res/drawable-xxhdpi/ic_archive_white_24dp.png
Binary files differ
diff --git a/res/drawable-xxxhdpi/ic_archive_white_24dp.png b/res/drawable-xxxhdpi/ic_archive_white_24dp.png
new file mode 100644
index 000000000..34cd3fd80
--- /dev/null
+++ b/res/drawable-xxxhdpi/ic_archive_white_24dp.png
Binary files differ
diff --git a/res/layout/phone_favorite_tile_view.xml b/res/layout/phone_favorite_tile_view.xml
index 8b00fba61..aa82ca0dc 100644
--- a/res/layout/phone_favorite_tile_view.xml
+++ b/res/layout/phone_favorite_tile_view.xml
@@ -24,7 +24,8 @@
android:id="@+id/contact_favorite_card"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:focusable="true" >
+ android:focusable="true"
+ android:nextFocusRight="@+id/contact_tile_secondary_button">
<com.android.contacts.common.widget.LayoutSuppressingImageView
android:id="@+id/contact_tile_image"
@@ -102,8 +103,7 @@
android:id="@+id/contact_tile_push_state"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:focusable="true"
- android:nextFocusRight="@+id/contact_tile_secondary_button"
+ android:importantForAccessibility="no"
android:background="@drawable/item_background_material_dark" />
<ImageButton
diff --git a/res/layout/voicemail_playback_layout.xml b/res/layout/voicemail_playback_layout.xml
index bb7fe342a..64a68bdd2 100644
--- a/res/layout/voicemail_playback_layout.xml
+++ b/res/layout/voicemail_playback_layout.xml
@@ -97,6 +97,17 @@
android:tint="@color/voicemail_icon_tint"
android:contentDescription="@string/call_log_trash_voicemail" />
+ <Space
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:layout_weight="1" />
+
+ <ImageButton android:id="@+id/archive_voicemail"
+ style="@style/VoicemailPlaybackLayoutButtonStyle"
+ android:src="@drawable/ic_archive_white_24dp"
+ android:tint="@color/voicemail_icon_tint"
+ android:contentDescription="@string/call_log_archive_voicemail" />
+
</LinearLayout>
</LinearLayout>
diff --git a/res/menu/dialtacts_options.xml b/res/menu/dialtacts_options.xml
index 8a9e25f7f..8e31026a7 100644
--- a/res/menu/dialtacts_options.xml
+++ b/res/menu/dialtacts_options.xml
@@ -20,6 +20,9 @@
android:icon="@drawable/ic_menu_history_lt"
android:title="@string/action_menu_call_history_description" />
<item
+ android:id="@+id/menu_archive"
+ android:title="@string/voicemail_archive_activity_title" />
+ <item
android:id="@+id/menu_import_export"
android:title="@string/menu_import_export" />
<item
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 6d2bd5ef5..734dde899 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -84,12 +84,21 @@
<!-- Menu item used to delete a voicemail. [CHAR LIMIT=30] -->
<string name="call_log_trash_voicemail">Delete voicemail</string>
+ <!-- Menu item used to archive a voicemail. [CHAR LIMIT=30] -->
+ <string name="call_log_archive_voicemail">Archive voicemail</string>
+
<!-- Text for snackbar to undo a voicemail delete. [CHAR LIMIT=30] -->
<string name="snackbar_voicemail_deleted">Voicemail deleted</string>
+ <!-- Text for snackbar to undo a voicemail archive. [CHAR LIMIT=30] -->
+ <string name="snackbar_voicemail_archived">Voicemail archived</string>
+
<!-- Text for undo button in snackbar for voicemail deletion. [CHAR LIMIT=10] -->
<string name="snackbar_voicemail_deleted_undo">UNDO</string>
+ <!-- Text for going to archive button in snackbar for voicemail archive. [CHAR LIMIT=10] -->
+ <string name="snackbar_voicemail_archived_goto">GOTO ARCHIVE</string>
+
<!-- Title of the confirmation dialog for clearing the call log. [CHAR LIMIT=37] -->
<string name="clearCallLogConfirmation_title">Clear call history?</string>
@@ -158,6 +167,9 @@
<!-- Message to display whilst we are waiting for the content to be fetched. [CHAR LIMIT=40] -->
<string name="voicemail_fetching_content">Loading voicemail\u2026</string>
+ <!-- Message to display whilst we are waiting for the content to be archived. [CHAR LIMIT=40] -->
+ <string name="voicemail_archiving_content">Archiving voicemail\u2026</string>
+
<!-- Message to display if we fail to get content within a suitable time period. [CHAR LIMIT=40] -->
<string name="voicemail_fetching_timout">Couldn\'t load voicemail</string>
@@ -455,6 +467,12 @@
The date will be replaced by 'Today' for voicemails created on the current day. For example: Today at 2:49 PM -->
<string name="voicemailCallLogDateTimeFormat"><xliff:g id="date" example="Jul 25, 2014">%1$s</xliff:g> at <xliff:g id="time" example="2:49 PM">%2$s</xliff:g></string>
+ <!-- Format for duration of voicemails which are displayed when viewing voicemail logs. For example "01:22" -->
+ <string name="voicemailDurationFormat"><xliff:g id="minutes" example="10">%1$02d</xliff:g>:<xliff:g id="seconds" example="20">%2$02d</xliff:g></string>
+
+ <!-- A format string used for displaying the date, time and duration for a voicemail call log. For example: Jul 25, 2014 at 2:49 PM • 00:34 -->
+ <string name="voicemailCallLogDateTimeFormatWithDuration"><xliff:g id="dateAndTime" example="Jul 25, 2014 at 2:49PM">%1$s</xliff:g> \u2022 <xliff:g id="duration" example="01:22">%2$s</xliff:g></string>
+
<!-- Dialog message which is shown when the user tries to make a phone call
to prohibited phone numbers [CHAR LIMIT=NONE] -->
<string name="dialog_phone_call_prohibited_message" msgid="4313552620858880999">Can\'t call this number</string>
@@ -525,12 +543,18 @@
<!-- Text displayed when the list of voicemails is empty -->
<string name="call_log_voicemail_empty">Your voicemail inbox is empty.</string>
+ <!-- Text displayed when the list of voicemail archives is empty -->
+ <string name="voicemail_archive_empty">Your voicemail archive is empty.</string>
+
<!-- Menu option to show favorite contacts only -->
<string name="show_favorites_only">Show favorites only</string>
<!-- Title of activity that displays a list of all calls -->
<string name="call_log_activity_title">Call History</string>
+ <!-- Title of activity that displays a list of all archived voicemails -->
+ <string name="voicemail_archive_activity_title">Voicemail Archive</string>
+
<!-- Title for the call log tab containing the list of all voicemails and calls
[CHAR LIMIT=30] -->
<string name="call_log_all_title">All</string>
@@ -911,6 +935,9 @@
<!-- Error toast message for when send to voicemail import fails. [CHAR LIMIT=40] -->
<string name="send_to_voicemail_import_failed">Import failed</string>
+ <!-- Error toast message for when voicemail archive fails. [CHAR LIMIT=40] -->
+ <string name="voicemail_archive_failed">Failed to archive voicemail.</string>
+
<!-- String describing the delete icon on a blocked number list item.
When tapped, it will show a dialog confirming the unblocking of the number.
[CHAR LIMIT=NONE]-->
diff --git a/src/com/android/dialer/DialtactsActivity.java b/src/com/android/dialer/DialtactsActivity.java
index d50748303..d12cf24df 100644
--- a/src/com/android/dialer/DialtactsActivity.java
+++ b/src/com/android/dialer/DialtactsActivity.java
@@ -240,6 +240,7 @@ public class DialtactsActivity extends TransactionSafeActivity implements View.O
private FloatingActionButtonController mFloatingActionButtonController;
private int mActionBarHeight;
+ private int mPreviouslySelectedTabIndex;
/**
* The text returned from a voice search query. Set in {@link #onActivityResult} and used in
@@ -423,7 +424,7 @@ public class DialtactsActivity extends TransactionSafeActivity implements View.O
mIsLandscape = getResources().getConfiguration().orientation
== Configuration.ORIENTATION_LANDSCAPE;
-
+ mPreviouslySelectedTabIndex = ListsFragment.TAB_INDEX_SPEED_DIAL;
final View floatingActionButtonContainer = findViewById(
R.id.floating_action_button_container);
ImageButton floatingActionButton = (ImageButton) findViewById(R.id.floating_action_button);
@@ -567,6 +568,11 @@ public class DialtactsActivity extends TransactionSafeActivity implements View.O
@Override
protected void onPause() {
+ // Only clear missed calls if the pause was not triggered by an orientation change
+ // (or any other confirguration change)
+ if (!isChangingConfigurations()) {
+ updateMissedCalls();
+ }
if (mClearSearchOnPause) {
hideDialpadAndSearchUi();
mClearSearchOnPause = false;
@@ -1328,7 +1334,9 @@ public class DialtactsActivity extends TransactionSafeActivity implements View.O
@Override
public void onPageSelected(int position) {
+ updateMissedCalls();
int tabIndex = mListsFragment.getCurrentTabIndex();
+ mPreviouslySelectedTabIndex = tabIndex;
if (tabIndex == ListsFragment.TAB_INDEX_ALL_CONTACTS) {
mFloatingActionButtonController.changeIcon(
getResources().getDrawable(R.drawable.ic_person_add_24dp),
@@ -1389,4 +1397,10 @@ public class DialtactsActivity extends TransactionSafeActivity implements View.O
}
return FloatingActionButtonController.ALIGN_END;
}
+
+ private void updateMissedCalls() {
+ if (mPreviouslySelectedTabIndex == ListsFragment.TAB_INDEX_HISTORY) {
+ mListsFragment.markMissedCallsAsReadAndRemoveNotifications();
+ }
+ }
}
diff --git a/src/com/android/dialer/calllog/CallLogAdapter.java b/src/com/android/dialer/calllog/CallLogAdapter.java
index 372c7bd9b..6f96ee529 100644
--- a/src/com/android/dialer/calllog/CallLogAdapter.java
+++ b/src/com/android/dialer/calllog/CallLogAdapter.java
@@ -16,6 +16,7 @@
package com.android.dialer.calllog;
+import com.android.dialer.DialtactsActivity;
import com.google.common.annotations.VisibleForTesting;
import android.content.Context;
@@ -169,6 +170,12 @@ public class CallLogAdapter extends GroupingListAdapter
mCurrentlyExpandedPosition = RecyclerView.NO_POSITION;
mCurrentlyExpandedRowId = NO_EXPANDED_LIST_ITEM;
} else {
+ if (viewHolder.callType == CallLog.Calls.MISSED_TYPE) {
+ CallLogAsyncTaskUtil.markCallAsRead(mContext, viewHolder.callIds);
+ if (!mIsCallLogActivity) {
+ ((DialtactsActivity) v.getContext()).updateTabUnreadCounts();
+ }
+ }
expandViewHolderActions(viewHolder);
}
@@ -506,7 +513,8 @@ public class CallLogAdapter extends GroupingListAdapter
details.features = getCallFeatures(c, count);
details.geocode = c.getString(CallLogQuery.GEOCODED_LOCATION);
details.transcription = c.getString(CallLogQuery.TRANSCRIPTION);
- if (details.callTypes[0] == CallLog.Calls.VOICEMAIL_TYPE) {
+ if (details.callTypes[0] == CallLog.Calls.VOICEMAIL_TYPE ||
+ details.callTypes[0] == CallLog.Calls.MISSED_TYPE) {
details.isRead = c.getInt(CallLogQuery.IS_READ) == 1;
}
diff --git a/src/com/android/dialer/calllog/CallLogAsyncTaskUtil.java b/src/com/android/dialer/calllog/CallLogAsyncTaskUtil.java
index bb7bdbd0a..982591814 100644
--- a/src/com/android/dialer/calllog/CallLogAsyncTaskUtil.java
+++ b/src/com/android/dialer/calllog/CallLogAsyncTaskUtil.java
@@ -29,6 +29,7 @@ import android.text.TextUtils;
import android.util.Log;
import com.android.contacts.common.GeoUtil;
+import com.android.contacts.common.util.PermissionsUtil;
import com.android.dialer.DialtactsActivity;
import com.android.dialer.PhoneCallDetails;
import com.android.dialer.util.AppCompatConstants;
@@ -51,7 +52,9 @@ public class CallLogAsyncTaskUtil {
DELETE_CALL,
DELETE_BLOCKED_CALL,
MARK_VOICEMAIL_READ,
+ MARK_CALL_READ,
GET_CALL_DETAILS,
+ UPDATE_DURATION
}
private static final class CallDetailQuery {
@@ -348,8 +351,6 @@ public class CallLogAsyncTaskUtil {
Intent intent = new Intent(context, CallLogNotificationsService.class);
intent.setAction(CallLogNotificationsService.ACTION_MARK_NEW_VOICEMAILS_AS_OLD);
context.startService(intent);
-
- ((DialtactsActivity) context).updateTabUnreadCounts();
return null;
}
});
@@ -379,6 +380,64 @@ public class CallLogAsyncTaskUtil {
});
}
+ public static void markCallAsRead(final Context context, final long[] callIds) {
+ if (!PermissionsUtil.hasPhonePermissions(context)) {
+ return;
+ }
+ if (sAsyncTaskExecutor == null) {
+ initTaskExecutor();
+ }
+
+ sAsyncTaskExecutor.submit(Tasks.MARK_CALL_READ, new AsyncTask<Void, Void, Void>() {
+ @Override
+ public Void doInBackground(Void... params) {
+
+ StringBuilder where = new StringBuilder();
+ where.append(CallLog.Calls.TYPE).append(" = ").append(CallLog.Calls.MISSED_TYPE);
+ where.append(" AND ");
+
+ Long[] callIdLongs = new Long[callIds.length];
+ for (int i = 0; i < callIds.length; i++) {
+ callIdLongs[i] = callIds[i];
+ }
+ where.append(CallLog.Calls._ID).append(
+ " IN (" + TextUtils.join(",", callIdLongs) + ")");
+
+ ContentValues values = new ContentValues(1);
+ values.put(CallLog.Calls.IS_READ, "1");
+ context.getContentResolver().update(
+ CallLog.Calls.CONTENT_URI, values, where.toString(), null);
+ return null;
+ }
+ });
+ }
+
+ /**
+ * Updates the duration of a voicemail call log entry.
+ */
+ public static void updateVoicemailDuration(
+ final Context context,
+ final Uri voicemailUri,
+ final int duration) {
+ if (!PermissionsUtil.hasPhonePermissions(context)) {
+ return;
+ }
+
+ if (sAsyncTaskExecutor == null) {
+ initTaskExecutor();
+ }
+
+ sAsyncTaskExecutor.submit(Tasks.UPDATE_DURATION, new AsyncTask<Void, Void, Void>() {
+ @Override
+ public Void doInBackground(Void... params) {
+ ContentValues values = new ContentValues(1);
+ values.put(CallLog.Calls.DURATION, duration);
+ context.getContentResolver().update(voicemailUri, values, null, null);
+ return null;
+ }
+ });
+ }
+
@VisibleForTesting
public static void resetForTest() {
sAsyncTaskExecutor = null;
diff --git a/src/com/android/dialer/calllog/CallLogFragment.java b/src/com/android/dialer/calllog/CallLogFragment.java
index 41ff7d311..9cd1359c7 100644
--- a/src/com/android/dialer/calllog/CallLogFragment.java
+++ b/src/com/android/dialer/calllog/CallLogFragment.java
@@ -41,6 +41,7 @@ import android.view.ViewGroup;
import com.android.contacts.common.GeoUtil;
import com.android.contacts.common.util.PermissionsUtil;
import com.android.dialer.R;
+import com.android.dialer.list.ListsFragment;
import com.android.dialer.util.EmptyLoader;
import com.android.dialer.voicemail.VoicemailPlaybackPresenter;
import com.android.dialer.widget.EmptyContentView;
@@ -66,6 +67,7 @@ public class CallLogFragment extends Fragment implements CallLogQueryHandler.Lis
private static final String KEY_FILTER_TYPE = "filter_type";
private static final String KEY_LOG_LIMIT = "log_limit";
private static final String KEY_DATE_LIMIT = "date_limit";
+ private static final String KEY_IS_CALL_LOG_ACTIVITY = "is_call_log_activity";
// No limit specified for the number of logs to show; use the CallLogQueryHandler's default.
private static final int NO_LOG_LIMIT = -1;
@@ -193,6 +195,7 @@ public class CallLogFragment extends Fragment implements CallLogQueryHandler.Lis
mCallTypeFilter = state.getInt(KEY_FILTER_TYPE, mCallTypeFilter);
mLogLimit = state.getInt(KEY_LOG_LIMIT, mLogLimit);
mDateLimit = state.getLong(KEY_DATE_LIMIT, mDateLimit);
+ mIsCallLogActivity = state.getBoolean(KEY_IS_CALL_LOG_ACTIVITY, mIsCallLogActivity);
}
final Activity activity = getActivity();
@@ -278,6 +281,9 @@ public class CallLogFragment extends Fragment implements CallLogQueryHandler.Lis
public void onVoicemailUnreadCountFetched(Cursor cursor) {}
@Override
+ public void onMissedCallsUnreadCountFetched(Cursor cursor) {}
+
+ @Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedState) {
View view = inflater.inflate(R.layout.call_log_fragment, container, false);
setupView(view, null);
@@ -351,7 +357,7 @@ public class CallLogFragment extends Fragment implements CallLogQueryHandler.Lis
@Override
public void onStop() {
- updateOnTransition(false /* onEntry */);
+ updateOnTransition();
super.onStop();
}
@@ -371,6 +377,7 @@ public class CallLogFragment extends Fragment implements CallLogQueryHandler.Lis
outState.putInt(KEY_FILTER_TYPE, mCallTypeFilter);
outState.putInt(KEY_LOG_LIMIT, mLogLimit);
outState.putLong(KEY_DATE_LIMIT, mDateLimit);
+ outState.putBoolean(KEY_IS_CALL_LOG_ACTIVITY, mIsCallLogActivity);
mAdapter.onSaveInstanceState(outState);
}
@@ -378,6 +385,9 @@ public class CallLogFragment extends Fragment implements CallLogQueryHandler.Lis
@Override
public void fetchCalls() {
mCallLogQueryHandler.fetchCalls(mCallTypeFilter, mDateLimit);
+ if (!mIsCallLogActivity) {
+ ((ListsFragment) getParentFragment()).updateTabUnreadCounts();
+ }
}
private void updateEmptyMessage(int filterType) {
@@ -425,7 +435,7 @@ public class CallLogFragment extends Fragment implements CallLogQueryHandler.Lis
if (mMenuVisible != menuVisible) {
mMenuVisible = menuVisible;
if (!menuVisible) {
- updateOnTransition(false /* onEntry */);
+ updateOnTransition();
} else if (isResumed()) {
refreshData();
}
@@ -443,8 +453,8 @@ public class CallLogFragment extends Fragment implements CallLogQueryHandler.Lis
fetchCalls();
mCallLogQueryHandler.fetchVoicemailStatus();
-
- updateOnTransition(true /* onEntry */);
+ mCallLogQueryHandler.fetchMissedCallsUnreadCount();
+ updateOnTransition();
mRefreshDataRequired = false;
} else {
// Refresh the display of the existing data to update the timestamp text descriptions.
@@ -453,28 +463,17 @@ public class CallLogFragment extends Fragment implements CallLogQueryHandler.Lis
}
/**
- * Updates the call data and notification state on entering or leaving the call log tab.
- *
- * If we are leaving the call log tab, mark all the missed calls as read.
+ * Updates the voicemail notification state.
*
* TODO: Move to CallLogActivity
*/
- private void updateOnTransition(boolean onEntry) {
+ private void updateOnTransition() {
// We don't want to update any call data when keyguard is on because the user has likely not
// seen the new calls yet.
// This might be called before onCreate() and thus we need to check null explicitly.
- if (mKeyguardManager != null && !mKeyguardManager.inKeyguardRestrictedInputMode()) {
- // On either of the transitions we update the missed call and voicemail notifications.
- // While exiting we additionally consume all missed calls (by marking them as read).
- mCallLogQueryHandler.markNewCallsAsOld();
- if (!onEntry) {
- mCallLogQueryHandler.markMissedCallsAsRead();
- }
- if (mCallTypeFilter == Calls.VOICEMAIL_TYPE) {
- CallLogNotificationsHelper.updateVoicemailNotifications(getActivity());
- } else {
- CallLogNotificationsHelper.removeMissedCallNotifications(getActivity());
- }
+ if (mKeyguardManager != null && !mKeyguardManager.inKeyguardRestrictedInputMode()
+ && mCallTypeFilter == Calls.VOICEMAIL_TYPE) {
+ CallLogNotificationsHelper.updateVoicemailNotifications(getActivity());
}
}
diff --git a/src/com/android/dialer/calllog/CallLogQueryHandler.java b/src/com/android/dialer/calllog/CallLogQueryHandler.java
index 4cb835bda..9ff74cfbc 100644
--- a/src/com/android/dialer/calllog/CallLogQueryHandler.java
+++ b/src/com/android/dialer/calllog/CallLogQueryHandler.java
@@ -62,6 +62,8 @@ public class CallLogQueryHandler extends NoNullCursorAsyncQueryHandler {
private static final int QUERY_VOICEMAIL_STATUS_TOKEN = 57;
/** The token for the query to fetch the number of unread voicemails. */
private static final int QUERY_VOICEMAIL_UNREAD_COUNT_TOKEN = 58;
+ /** The token for the query to fetch the number of missed calls. */
+ private static final int QUERY_MISSED_CALLS_UNREAD_COUNT_TOKEN = 59;
private final int mLogLimit;
@@ -225,19 +227,25 @@ public class CallLogQueryHandler extends NoNullCursorAsyncQueryHandler {
if (!PermissionsUtil.hasPhonePermissions(mContext)) {
return;
}
- // Mark all "new" calls as not new anymore.
- StringBuilder where = new StringBuilder();
- where.append(Calls.IS_READ).append(" = 0");
- where.append(" AND ");
- where.append(Calls.TYPE).append(" = ").append(Calls.MISSED_TYPE);
ContentValues values = new ContentValues(1);
values.put(Calls.IS_READ, "1");
startUpdate(UPDATE_MARK_MISSED_CALL_AS_READ_TOKEN, null, Calls.CONTENT_URI, values,
- where.toString(), null);
+ getUnreadMissedCallsQuery(), null);
}
+ /** Fetch all missed calls received since last time the tab was opened. */
+ public void fetchMissedCallsUnreadCount() {
+ if (!PermissionsUtil.hasPhonePermissions(mContext)) {
+ return;
+ }
+
+ startQuery(QUERY_MISSED_CALLS_UNREAD_COUNT_TOKEN, null, Calls.CONTENT_URI,
+ new String[]{Calls._ID}, getUnreadMissedCallsQuery(), null, null);
+ }
+
+
@Override
protected synchronized void onNotNullableQueryComplete(int token, Object cookie,
Cursor cursor) {
@@ -253,6 +261,8 @@ public class CallLogQueryHandler extends NoNullCursorAsyncQueryHandler {
updateVoicemailStatus(cursor);
} else if (token == QUERY_VOICEMAIL_UNREAD_COUNT_TOKEN) {
updateVoicemailUnreadCount(cursor);
+ } else if (token == QUERY_MISSED_CALLS_UNREAD_COUNT_TOKEN) {
+ updateMissedCallsUnreadCount(cursor);
} else {
Log.w(TAG, "Unknown query completed: ignoring: " + token);
}
@@ -276,6 +286,17 @@ public class CallLogQueryHandler extends NoNullCursorAsyncQueryHandler {
}
+ /**
+ * @return Query string to get all unread missed calls.
+ */
+ private String getUnreadMissedCallsQuery() {
+ StringBuilder where = new StringBuilder();
+ where.append(Calls.IS_READ).append(" = 0");
+ where.append(" AND ");
+ where.append(Calls.TYPE).append(" = ").append(Calls.MISSED_TYPE);
+ return where.toString();
+ }
+
private void updateVoicemailStatus(Cursor statusCursor) {
final Listener listener = mListener.get();
if (listener != null) {
@@ -290,6 +311,13 @@ public class CallLogQueryHandler extends NoNullCursorAsyncQueryHandler {
}
}
+ private void updateMissedCallsUnreadCount(Cursor statusCursor) {
+ final Listener listener = mListener.get();
+ if (listener != null) {
+ listener.onMissedCallsUnreadCountFetched(statusCursor);
+ }
+ }
+
/** Listener to completion of various queries. */
public interface Listener {
/** Called when {@link CallLogQueryHandler#fetchVoicemailStatus()} completes. */
@@ -298,6 +326,9 @@ public class CallLogQueryHandler extends NoNullCursorAsyncQueryHandler {
/** Called when {@link CallLogQueryHandler#fetchVoicemailUnreadCount()} completes. */
void onVoicemailUnreadCountFetched(Cursor cursor);
+ /** Called when {@link CallLogQueryHandler#fetchMissedCallsUnreadCount()} completes. */
+ void onMissedCallsUnreadCountFetched(Cursor cursor);
+
/**
* Called when {@link CallLogQueryHandler#fetchCalls(int)} complete.
* Returns true if takes ownership of cursor.
diff --git a/src/com/android/dialer/calllog/PhoneCallDetailsHelper.java b/src/com/android/dialer/calllog/PhoneCallDetailsHelper.java
index e6b850872..7b149e24e 100644
--- a/src/com/android/dialer/calllog/PhoneCallDetailsHelper.java
+++ b/src/com/android/dialer/calllog/PhoneCallDetailsHelper.java
@@ -40,6 +40,7 @@ import com.android.dialer.util.DialerUtils;
import java.util.ArrayList;
import java.util.Calendar;
+import java.util.concurrent.TimeUnit;
/**
* Helper class to fill in the views in {@link PhoneCallDetailsViews}.
@@ -110,10 +111,8 @@ public class PhoneCallDetailsHelper {
callCount = null;
}
- CharSequence callLocationAndDate = getCallLocationAndDate(details);
-
- // Set the call count, location and date.
- setCallCountAndDate(views, callCount, callLocationAndDate);
+ // Set the call count, location, date and if voicemail, set the duration.
+ setDetailText(views, callCount, details);
// Set the account label if it exists.
String accountLabel = mCallLogCache.getAccountLabel(details.accountHandle);
@@ -314,10 +313,11 @@ public class PhoneCallDetailsHelper {
}
}
- /** Sets the call count and date. */
- private void setCallCountAndDate(PhoneCallDetailsViews views, Integer callCount,
- CharSequence dateText) {
+ /** Sets the call count, date, and if it is a voicemail, sets the duration. */
+ private void setDetailText(PhoneCallDetailsViews views, Integer callCount,
+ PhoneCallDetails details) {
// Combine the count (if present) and the date.
+ CharSequence dateText = getCallLocationAndDate(details);
final CharSequence text;
if (callCount != null) {
text = mResources.getString(
@@ -326,6 +326,22 @@ public class PhoneCallDetailsHelper {
text = dateText;
}
- views.callLocationAndDate.setText(text);
+ if (details.callTypes[0] == Calls.VOICEMAIL_TYPE && details.duration > 0) {
+ views.callLocationAndDate.setText(mResources.getString(
+ R.string.voicemailCallLogDateTimeFormatWithDuration, text,
+ getVoicemailDuration(details)));
+ } else {
+ views.callLocationAndDate.setText(text);
+ }
+
+ }
+
+ private String getVoicemailDuration(PhoneCallDetails details) {
+ long minutes = TimeUnit.SECONDS.toMinutes(details.duration);
+ long seconds = details.duration - TimeUnit.MINUTES.toSeconds(minutes);
+ if (minutes > 99) {
+ minutes = 99;
+ }
+ return mResources.getString(R.string.voicemailDurationFormat, minutes, seconds);
}
}
diff --git a/src/com/android/dialer/list/ListsFragment.java b/src/com/android/dialer/list/ListsFragment.java
index d5caa1a2e..5b7c950bf 100644
--- a/src/com/android/dialer/list/ListsFragment.java
+++ b/src/com/android/dialer/list/ListsFragment.java
@@ -36,6 +36,7 @@ import com.android.contacts.common.list.ViewPagerTabs;
import com.android.dialer.DialtactsActivity;
import com.android.dialer.R;
import com.android.dialer.calllog.CallLogFragment;
+import com.android.dialer.calllog.CallLogNotificationsHelper;
import com.android.dialer.calllog.CallLogQueryHandler;
import com.android.dialer.calllog.VisualVoicemailCallLogFragment;
import com.android.dialer.logging.Logger;
@@ -192,6 +193,7 @@ public class ListsFragment extends Fragment
mCallLogQueryHandler =
new CallLogQueryHandler(getActivity(), getActivity().getContentResolver(), this);
mCallLogQueryHandler.fetchVoicemailStatus();
+ mCallLogQueryHandler.fetchMissedCallsUnreadCount();
Trace.endSection();
}
@@ -347,6 +349,23 @@ public class ListsFragment extends Fragment
}
@Override
+ public void onMissedCallsUnreadCountFetched(Cursor cursor) {
+ if (getActivity() == null || getActivity().isFinishing() || cursor == null) {
+ return;
+ }
+
+ int count = 0;
+ try {
+ count = cursor.getCount();
+ } finally {
+ cursor.close();
+ }
+
+ mViewPagerTabs.setUnreadCount(count, TAB_INDEX_HISTORY);
+ mViewPagerTabs.updateTab(TAB_INDEX_HISTORY);
+ }
+
+ @Override
public boolean onCallsFetched(Cursor statusCursor) {
// Return false; did not take ownership of cursor
return false;
@@ -358,14 +377,29 @@ public class ListsFragment extends Fragment
/**
* External method to update unread count because the unread count changes when the user
- * expands a voicemail in the call log.
+ * expands a voicemail in the call log or when the user expands an unread call in the call
+ * history tab.
*/
public void updateTabUnreadCounts() {
- if (mHasActiveVoicemailProvider && mCallLogQueryHandler != null) {
- mCallLogQueryHandler.fetchVoicemailUnreadCount();
+ if (mCallLogQueryHandler != null) {
+ mCallLogQueryHandler.fetchMissedCallsUnreadCount();
+ if (mHasActiveVoicemailProvider) {
+ mCallLogQueryHandler.fetchVoicemailUnreadCount();
+ }
}
}
+ /**
+ * External method to mark all missed calls as read.
+ */
+ public void markMissedCallsAsReadAndRemoveNotifications() {
+ if (mCallLogQueryHandler != null) {
+ mCallLogQueryHandler.markMissedCallsAsRead();
+ CallLogNotificationsHelper.removeMissedCallNotifications(getActivity());
+ }
+ }
+
+
public void showRemoveView(boolean show) {
mRemoveViewContent.setVisibility(show ? View.VISIBLE : View.GONE);
mRemoveView.setAlpha(show ? 0 : 1);
diff --git a/src/com/android/dialer/voicemail/VisualVoicemailEnabledChecker.java b/src/com/android/dialer/voicemail/VisualVoicemailEnabledChecker.java
index 3134b1486..80a0368bd 100644
--- a/src/com/android/dialer/voicemail/VisualVoicemailEnabledChecker.java
+++ b/src/com/android/dialer/voicemail/VisualVoicemailEnabledChecker.java
@@ -91,6 +91,11 @@ public class VisualVoicemailEnabledChecker implements CallLogQueryHandler.Listen
}
@Override
+ public void onMissedCallsUnreadCountFetched(Cursor cursor) {
+ // Do nothing
+ }
+
+ @Override
public boolean onCallsFetched(Cursor combinedCursor) {
// Do nothing
return false;
diff --git a/src/com/android/dialer/voicemail/VoicemailPlaybackPresenter.java b/src/com/android/dialer/voicemail/VoicemailPlaybackPresenter.java
index 93ff00270..2bb4de932 100644
--- a/src/com/android/dialer/voicemail/VoicemailPlaybackPresenter.java
+++ b/src/com/android/dialer/voicemail/VoicemailPlaybackPresenter.java
@@ -36,6 +36,7 @@ import android.view.WindowManager.LayoutParams;
import android.widget.SeekBar;
import com.android.dialer.R;
+import com.android.dialer.calllog.CallLogAsyncTaskUtil;
import com.android.dialer.util.AsyncTaskExecutor;
import com.android.dialer.util.AsyncTaskExecutors;
import com.android.common.io.MoreCloseables;
@@ -557,6 +558,12 @@ public class VoicemailPlaybackPresenter implements MediaPlayer.OnPreparedListene
Log.d(TAG, "onPrepared");
mIsPrepared = true;
+ // Update the duration in the database if it was not previously retrieved
+ if (mDuration.get() == 0) {
+ CallLogAsyncTaskUtil.updateVoicemailDuration(mContext, mVoicemailUri,
+ mMediaPlayer.getDuration() / 1000);
+ }
+
mDuration.set(mMediaPlayer.getDuration());
Log.d(TAG, "onPrepared: mPosition=" + mPosition);
diff --git a/tests/src/com/android/dialer/calllog/PhoneCallDetailsHelperTest.java b/tests/src/com/android/dialer/calllog/PhoneCallDetailsHelperTest.java
index b6202b904..c0d1203d9 100644
--- a/tests/src/com/android/dialer/calllog/PhoneCallDetailsHelperTest.java
+++ b/tests/src/com/android/dialer/calllog/PhoneCallDetailsHelperTest.java
@@ -34,6 +34,8 @@ import com.android.dialer.util.LocaleTestUtils;
import java.util.GregorianCalendar;
import java.util.Locale;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
/**
* Unit tests for {@link PhoneCallDetailsHelper}.
@@ -141,9 +143,29 @@ public class PhoneCallDetailsHelperTest extends AndroidTestCase {
public void testVoicemailLocationNotShownWithDate() {
setVoicemailPhoneCallDetailsWithDate(TEST_DATE);
+ assertLocationAndDateExactEquals("Jun 3 at 1:00 PM • 99:20");
+ }
+
+ public void testVoicemailDuration() {
+ setVoicemailPhoneCallDetailsWithDuration(100);
+ assertDurationExactEquals("01:40");
+ }
+
+ public void testVoicemailDuration_Capped() {
+ setVoicemailPhoneCallDetailsWithDuration(TEST_DURATION);
+ assertDurationExactEquals("99:20");
+ }
+
+ public void testVoicemailDuration_Zero() {
+ setVoicemailPhoneCallDetailsWithDuration(0);
assertLocationAndDateExactEquals("Jun 3 at 1:00 PM");
}
+ public void testVoicemailDuration_EvenMinute() {
+ setVoicemailPhoneCallDetailsWithDuration(60);
+ assertDurationExactEquals("01:00");
+ }
+
/** Asserts that a char sequence is actually a Spanned corresponding to the expected HTML. */
private void assertEqualsHtml(String expectedHtml, CharSequence actualText) {
// In order to contain HTML, the text should actually be a Spanned.
@@ -346,6 +368,14 @@ public class PhoneCallDetailsHelperTest extends AndroidTestCase {
assertEquals(text, mViews.callLocationAndDate.getText());
}
+ /** Asserts that the duration is exactly as included in the location and date text field. */
+ private void assertDurationExactEquals(String text) {
+ Matcher matcher = Pattern.compile("(.*) (\\u2022) (\\d{2}:\\d{2})").matcher(
+ mViews.callLocationAndDate.getText());
+ assertEquals(true, matcher.matches());
+ assertEquals(text, matcher.group(3));
+ }
+
/** Asserts that the video icon is shown. */
private void assertIsVideoCall(boolean isVideoCall) {
assertEquals(isVideoCall, mViews.callTypeIcons.isVideoShown());
@@ -407,6 +437,14 @@ public class PhoneCallDetailsHelperTest extends AndroidTestCase {
mHelper.setPhoneCallDetails(mViews, details);
}
+ /** Sets the voice mail details with default values and the given duration. */
+ private void setVoicemailPhoneCallDetailsWithDuration(long duration) {
+ PhoneCallDetails details = getPhoneCallDetails();
+ details.duration = duration;
+ details.callTypes = new int[] {Calls.VOICEMAIL_TYPE};
+ mHelper.setPhoneCallDetails(mViews, details);
+ }
+
/** Sets the phone call details with default values and the given call types using icons. */
private void setPhoneCallDetailsWithCallTypeIcons(int... callTypes) {
PhoneCallDetails details = getPhoneCallDetails();