Merge "Show transcriptions in voicemail notifications." into mnc-dev
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index eef8ed6..86bf4ff 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -286,6 +286,7 @@
<service android:name="com.android.incallui.InCallServiceImpl"
android:permission="android.permission.BIND_INCALL_SERVICE" >
+ <meta-data android:name="android.telecom.IN_CALL_SERVICE_UI" android:value="true" />
<intent-filter>
<action android:name="android.telecom.InCallService"/>
</intent-filter>
diff --git a/res/layout/voicemail_promo_card.xml b/res/layout/voicemail_promo_card.xml
new file mode 100644
index 0000000..103fa30
--- /dev/null
+++ b/res/layout/voicemail_promo_card.xml
@@ -0,0 +1,99 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2015 Google Inc. All Rights Reserved. -->
+
+<android.support.v7.widget.CardView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:card_view="http://schemas.android.com/apk/res-auto"
+ android:id="@+id/promo_card"
+ style="@style/CallLogCardStyle"
+ android:orientation="vertical"
+ android:gravity="center_vertical"
+ card_view:cardBackgroundColor="@color/visual_voicemail_promo_card_background">
+
+ <LinearLayout
+ android:id="@+id/promo_card_content"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingStart="@dimen/promo_card_start_padding"
+ android:paddingEnd="@dimen/promo_card_main_padding"
+ android:paddingTop="@dimen/promo_card_top_padding"
+ android:paddingBottom="@dimen/promo_card_main_padding"
+ android:orientation="horizontal"
+ android:gravity="top">
+
+ <ImageView
+ android:id="@+id/promo_card_icon"
+ android:layout_width="@dimen/promo_card_icon_size"
+ android:layout_height="@dimen/promo_card_icon_size"
+ android:layout_gravity="top"
+ android:src="@drawable/ic_voicemail_24dp"/>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="@dimen/promo_card_main_padding"
+ android:orientation="vertical"
+ android:gravity="center_vertical">
+
+ <TextView
+ android:id="@+id/promo_card_header"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="@dimen/promo_card_title_padding"
+ android:layout_gravity="center_vertical"
+ android:textColor="@color/background_dialer_white"
+ android:textSize="@dimen/call_log_primary_text_size"
+ android:textStyle="bold"
+ android:text="@string/visual_voicemail_title"
+ android:singleLine="false"/>
+
+ <TextView
+ android:id="@+id/promo_card_details"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textColor="@color/background_dialer_white"
+ android:textSize="@dimen/call_log_secondary_text_size"
+ android:text="@string/visual_voicemail_text"
+ android:lineSpacingExtra="@dimen/promo_card_line_spacing"
+ android:singleLine="false"/>
+ </LinearLayout>
+ </LinearLayout>
+
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="1dp"
+ android:background="@color/visual_voicemail_promo_card_divider"/>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingEnd="@dimen/promo_card_action_end_padding"
+ android:paddingTop="@dimen/promo_card_action_vertical_padding"
+ android:paddingBottom="@dimen/promo_card_action_vertical_padding"
+ android:orientation="horizontal"
+ android:gravity="end">
+
+ <TextView
+ android:id="@+id/settings_action"
+ style="@style/PromoCardActionStyle"
+ android:background="?android:attr/selectableItemBackground"
+ android:text="@string/visual_voicemail_settings"
+ android:nextFocusLeft="@+id/promo_card"
+ android:nextFocusRight="@+id/ok_action"
+ android:paddingEnd="@dimen/promo_card_action_between_padding"/>
+
+ <TextView
+ android:id="@+id/ok_action"
+ style="@style/PromoCardActionStyle"
+ android:background="?android:attr/selectableItemBackground"
+ android:text="@android:string/ok"
+ android:nextFocusLeft="@+id/settings_action"
+ android:nextFocusRight="@+id/promo_card"/>
+ </LinearLayout>
+ </LinearLayout>
+</android.support.v7.widget.CardView>
diff --git a/res/values/colors.xml b/res/values/colors.xml
index f83c328..c3b0fb5 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -36,6 +36,11 @@
<!-- Color of the text describing an unconsumed voicemail. -->
<color name="call_log_voicemail_highlight_color">#33b5e5</color>
+ <!-- Background color of visual voicemail promo card. -->
+ <color name="visual_voicemail_promo_card_background">#673ab7</color>
+ <color name="visual_voicemail_promo_card_divider">#7d57c1</color>
+ <color name="promo_card_text">#ffffff</color>
+
<!-- Tint of the recent card phone icon; 30% black -->
<color name="call_log_list_item_primary_action_icon_tint">#4d000000</color>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index bcde855..206b447 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -134,4 +134,15 @@
<dimen name="preference_summary_line_spacing_extra">4dp</dimen>
<dimen name="call_log_list_item_primary_action_dimen">36dp</dimen>
+
+ <!-- Dimensions for promo cards -->
+ <dimen name="promo_card_icon_size">24dp</dimen>
+ <dimen name="promo_card_start_padding">16dp</dimen>
+ <dimen name="promo_card_top_padding">21dp</dimen>
+ <dimen name="promo_card_main_padding">24dp</dimen>
+ <dimen name="promo_card_title_padding">12dp</dimen>
+ <dimen name="promo_card_action_vertical_padding">4dp</dimen>
+ <dimen name="promo_card_action_end_padding">4dp</dimen>
+ <dimen name="promo_card_action_between_padding">11dp</dimen>
+ <dimen name="promo_card_line_spacing">4dp</dimen>
</resources>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index dab5c6a..7d5d42f 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -218,4 +218,18 @@
<item name="cardCornerRadius">2dp</item>
<item name="cardBackgroundColor">@color/background_dialer_call_log_list_item</item>
</style>
+
+ <style name="PromoCardActionStyle">
+ <item name="android:layout_width">wrap_content</item>
+ <item name="android:layout_height">@dimen/call_log_action_height</item>
+ <item name="android:gravity">end|center_vertical</item>
+ <item name="android:paddingStart">@dimen/call_log_action_horizontal_padding</item>
+ <item name="android:paddingEnd">@dimen/call_log_action_horizontal_padding</item>
+ <item name="android:textColor">@color/promo_card_text</item>
+ <item name="android:textSize">@dimen/call_log_list_item_actions_text_size</item>
+ <item name="android:fontFamily">"sans-serif-medium"</item>
+ <item name="android:focusable">true</item>
+ <item name="android:singleLine">true</item>
+ <item name="android:textAllCaps">true</item>
+ </style>
</resources>
diff --git a/src/com/android/dialer/DialtactsActivity.java b/src/com/android/dialer/DialtactsActivity.java
index fc0f1fb..b77e910 100644
--- a/src/com/android/dialer/DialtactsActivity.java
+++ b/src/com/android/dialer/DialtactsActivity.java
@@ -133,6 +133,7 @@
* Just for backward compatibility. Should behave as same as {@link Intent#ACTION_DIAL}.
*/
private static final String ACTION_TOUCH_DIALER = "com.android.phone.action.TOUCH_DIALER";
+ public static final String EXTRA_SHOW_TAB = "EXTRA_SHOW_TAB";
private static final int ACTIVITY_REQUEST_CODE_VOICE_SEARCH = 1;
@@ -539,6 +540,7 @@
}
mIsRestarting = false;
}
+
prepareVoiceSearchButton();
mDialerDatabaseHelper.startSmartDialUpdateThread();
mFloatingActionButtonController.align(getFabAlignment(), false /* animate */);
@@ -903,6 +905,11 @@
mStateSaved = false;
displayFragment(newIntent);
+ if (newIntent.hasExtra(EXTRA_SHOW_TAB)) {
+ mListsFragment.showTab(
+ getIntent().getIntExtra(EXTRA_SHOW_TAB, mListsFragment.TAB_INDEX_SPEED_DIAL));
+ }
+
invalidateOptionsMenu();
}
diff --git a/src/com/android/dialer/PhoneCallDetails.java b/src/com/android/dialer/PhoneCallDetails.java
index add6315..403c4e8 100644
--- a/src/com/android/dialer/PhoneCallDetails.java
+++ b/src/com/android/dialer/PhoneCallDetails.java
@@ -19,12 +19,9 @@
import com.android.dialer.calllog.PhoneNumberDisplayUtil;
import android.content.Context;
-import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.provider.CallLog.Calls;
-import android.provider.ContactsContract.CommonDataKinds.Phone;
import android.telecom.PhoneAccountHandle;
-import android.text.TextUtils;
/**
* The details of a phone call to be shown in the UI.
@@ -87,10 +84,19 @@
// Voicemail transcription
public String transcription;
+ // The display string for the number.
public String displayNumber;
+
+ // Whether the contact number is a voicemail number.
public boolean isVoicemail;
/**
+ * If this is a voicemail, whether the message is read. For other types of calls, this defaults
+ * to {@code true}.
+ */
+ public boolean isRead = true;
+
+ /**
* Constructor with required fields for the details of a call with a number associated with a
* contact.
*/
@@ -104,7 +110,6 @@
this.numberPresentation = numberPresentation;
this.formattedNumber = formattedNumber;
this.isVoicemail = isVoicemail;
-
this.displayNumber = PhoneNumberDisplayUtil.getDisplayNumber(
context,
this.number,
diff --git a/src/com/android/dialer/PhoneCallDetailsHelper.java b/src/com/android/dialer/PhoneCallDetailsHelper.java
index 672a1c8..2dc0810 100644
--- a/src/com/android/dialer/PhoneCallDetailsHelper.java
+++ b/src/com/android/dialer/PhoneCallDetailsHelper.java
@@ -18,6 +18,7 @@
import android.content.Context;
import android.content.res.Resources;
+import android.graphics.Typeface;
import android.graphics.drawable.Drawable;
import android.provider.CallLog;
import android.provider.CallLog.Calls;
@@ -140,6 +141,12 @@
views.voicemailTranscriptionView.setText(null);
views.voicemailTranscriptionView.setVisibility(View.GONE);
}
+
+ // Bold if not read
+ Typeface typeface = details.isRead ? Typeface.SANS_SERIF : Typeface.DEFAULT_BOLD;
+ views.nameView.setTypeface(typeface);
+ views.voicemailTranscriptionView.setTypeface(typeface);
+ views.callLocationAndDate.setTypeface(typeface);
}
/**
diff --git a/src/com/android/dialer/calllog/CallLogAdapter.java b/src/com/android/dialer/calllog/CallLogAdapter.java
index 4304002..cc3c5ba 100644
--- a/src/com/android/dialer/calllog/CallLogAdapter.java
+++ b/src/com/android/dialer/calllog/CallLogAdapter.java
@@ -18,15 +18,20 @@
import android.content.Context;
import android.content.Intent;
+import android.content.SharedPreferences;
import android.content.res.Resources;
import android.database.Cursor;
import android.net.Uri;
import android.support.v7.widget.RecyclerView;
import android.os.Bundle;
import android.os.Trace;
+import android.preference.PreferenceActivity;
+import android.preference.PreferenceManager;
+import android.provider.CallLog;
import android.support.v7.widget.RecyclerView.ViewHolder;
import android.telecom.PhoneAccountHandle;
import android.telephony.PhoneNumberUtils;
+import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
@@ -61,6 +66,21 @@
private static final int VIEW_TYPE_SHOW_CALL_HISTORY_LIST_ITEM = 10;
private static final int NO_EXPANDED_LIST_ITEM = -1;
+ private static final int VOICEMAIL_PROMO_CARD_POSITION = 0;
+ /**
+ * View type for voicemail promo card. Note: Numbering starts at 20 to avoid collision
+ * with {@link com.android.common.widget.GroupingListAdapter#ITEM_TYPE_IN_GROUP}, and
+ * {@link CallLogAdapter#VIEW_TYPE_SHOW_CALL_HISTORY_LIST_ITEM}.
+ */
+ private static final int VIEW_TYPE_VOICEMAIL_PROMO_CARD = 20;
+
+ /**
+ * The key for the show voicemail promo card preference which will determine whether the promo
+ * card was permanently dismissed or not.
+ */
+ private static final String SHOW_VOICEMAIL_PROMO_CARD = "show_voicemail_promo_card";
+ private static final boolean SHOW_VOICEMAIL_PROMO_CARD_DEFAULT = true;
+
protected final Context mContext;
private final ContactInfoHelper mContactInfoHelper;
private final VoicemailPlaybackPresenter mVoicemailPlaybackPresenter;
@@ -96,6 +116,10 @@
private boolean mLoading = true;
+ private SharedPreferences mPrefs;
+
+ private boolean mShowPromoCard = false;
+
/** Instance of helper class for managing views. */
private final CallLogListItemHelper mCallLogViewsHelper;
@@ -116,8 +140,10 @@
return;
}
- // Always reset the voicemail playback state on expand or collapse.
- mVoicemailPlaybackPresenter.reset();
+ if (mVoicemailPlaybackPresenter != null) {
+ // Always reset the voicemail playback state on expand or collapse.
+ mVoicemailPlaybackPresenter.reset();
+ }
if (viewHolder.getAdapterPosition() == mCurrentlyExpandedPosition) {
// Hide actions, if the clicked item is the expanded item.
@@ -132,6 +158,30 @@
}
};
+ /**
+ * Click handler used to dismiss the promo card when the user taps the "ok" button.
+ */
+ private final View.OnClickListener mOkActionListener = new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ dismissVoicemailPromoCard();
+ }
+ };
+
+ /**
+ * Click handler used to send the user to the voicemail settings screen and then dismiss the
+ * promo card.
+ */
+ private final View.OnClickListener mVoicemailSettingsActionListener =
+ new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ Intent intent = new Intent(TelephonyManager.ACTION_CONFIGURE_VOICEMAIL);
+ mContext.startActivity(intent);
+ dismissVoicemailPromoCard();
+ }
+ };
+
private void expandViewHolderActions(CallLogListItemViewHolder viewHolder) {
// If another item is expanded, notify it that it has changed. Its actions will be
// hidden when it is re-binded because we change mCurrentlyExpandedPosition below.
@@ -209,6 +259,8 @@
new PhoneCallDetailsHelper(mContext, resources, mPhoneNumberUtilsWrapper);
mCallLogViewsHelper = new CallLogListItemHelper(phoneCallDetailsHelper, resources);
mCallLogGroupBuilder = new CallLogGroupBuilder(this);
+ mPrefs = PreferenceManager.getDefaultSharedPreferences(context);
+ maybeShowVoicemailPromoCard();
}
public void onSaveInstanceState(Bundle outState) {
@@ -276,6 +328,8 @@
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if (viewType == VIEW_TYPE_SHOW_CALL_HISTORY_LIST_ITEM) {
return ShowCallHistoryViewHolder.create(mContext, parent);
+ } else if (viewType == VIEW_TYPE_VOICEMAIL_PROMO_CARD) {
+ return createVoicemailPromoCardViewHolder(parent);
}
return createCallLogEntryViewHolder(parent);
}
@@ -289,7 +343,6 @@
private ViewHolder createCallLogEntryViewHolder(ViewGroup parent) {
LayoutInflater inflater = LayoutInflater.from(mContext);
View view = inflater.inflate(R.layout.call_log_list_item, parent, false);
-
CallLogListItemViewHolder viewHolder = CallLogListItemViewHolder.create(
view,
mContext,
@@ -311,19 +364,52 @@
* TODO: This gets called 20-30 times when Dialer starts up for a single call log entry and
* should not. It invokes cross-process methods and the repeat execution can get costly.
*
- * @param callLogItemView the view corresponding to this entry
- * @param count the number of entries in the current item, greater than 1 if it is a group
+ * @param ViewHolder The view corresponding to this entry.
+ * @param position The position of the entry.
*/
public void onBindViewHolder(ViewHolder viewHolder, int position) {
- if (getItemViewType(position) == VIEW_TYPE_SHOW_CALL_HISTORY_LIST_ITEM) {
- return;
- }
Trace.beginSection("onBindViewHolder: " + position);
+
+ switch (getItemViewType(position)) {
+ case VIEW_TYPE_SHOW_CALL_HISTORY_LIST_ITEM:
+ break;
+ case VIEW_TYPE_VOICEMAIL_PROMO_CARD:
+ bindVoicemailPromoCardViewHolder(viewHolder);
+ break;
+ default:
+ bindCallLogListViewHolder(viewHolder, position);
+ break;
+ }
+
+ Trace.endSection();
+ }
+
+ /**
+ * Binds the promo card view holder.
+ *
+ * @param viewHolder The promo card view holder.
+ */
+ protected void bindVoicemailPromoCardViewHolder(ViewHolder viewHolder) {
+ PromoCardViewHolder promoCardViewHolder = (PromoCardViewHolder) viewHolder;
+
+ promoCardViewHolder.getSettingsTextView().setOnClickListener(
+ mVoicemailSettingsActionListener);
+ promoCardViewHolder.getOkTextView().setOnClickListener(mOkActionListener);
+ }
+
+ /**
+ * Binds the view holder for the call log list item view.
+ *
+ * @param viewHolder The call log list item view holder.
+ * @param position The position of the list item.
+ */
+
+ private void bindCallLogListViewHolder(ViewHolder viewHolder, int position) {
Cursor c = (Cursor) getItem(position);
if (c == null) {
- Trace.endSection();
return;
}
+
int count = getGroupSize(position);
final String number = c.getString(CallLogQuery.NUMBER);
@@ -358,6 +444,9 @@
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) {
+ details.isRead = c.getInt(CallLogQuery.IS_READ) == 1;
+ }
if (!c.isNull(CallLogQuery.DATA_USAGE)) {
details.dataUsage = c.getLong(CallLogQuery.DATA_USAGE);
@@ -423,22 +512,35 @@
mViewTreeObserver = views.rootView.getViewTreeObserver();
mViewTreeObserver.addOnPreDrawListener(this);
}
- Trace.endSection();
}
@Override
public int getItemCount() {
- return super.getItemCount() + (isShowingRecentsTab() ? 1 : 0);
+ return super.getItemCount() + ((isShowingRecentsTab() || mShowPromoCard) ? 1 : 0);
}
@Override
public int getItemViewType(int position) {
if (position == getItemCount() - 1 && isShowingRecentsTab()) {
return VIEW_TYPE_SHOW_CALL_HISTORY_LIST_ITEM;
+ } else if (position == VOICEMAIL_PROMO_CARD_POSITION && mShowPromoCard) {
+ return VIEW_TYPE_VOICEMAIL_PROMO_CARD;
}
return super.getItemViewType(position);
}
+ /**
+ * Retrieves an item at the specified position, taking into account the presence of a promo
+ * card.
+ *
+ * @param position The position to retrieve.
+ * @return The item at that position.
+ */
+ @Override
+ public Object getItem(int position) {
+ return super.getItem(position - (mShowPromoCard ? 1 : 0));
+ }
+
protected boolean isShowingRecentsTab() {
return mIsShowingRecentsTab;
}
@@ -592,4 +694,37 @@
return mContext.getResources().getString(R.string.call_log_header_other);
}
}
+
+ /**
+ * Determines if the voicemail promo card should be shown or not. The voicemail promo card will
+ * be shown as the first item in the voicemail tab.
+ */
+ private void maybeShowVoicemailPromoCard() {
+ boolean showPromoCard = mPrefs.getBoolean(SHOW_VOICEMAIL_PROMO_CARD,
+ SHOW_VOICEMAIL_PROMO_CARD_DEFAULT);
+ mShowPromoCard = (mVoicemailPlaybackPresenter != null) && showPromoCard;
+ }
+
+ /**
+ * Dismisses the voicemail promo card and refreshes the call log.
+ */
+ private void dismissVoicemailPromoCard() {
+ mPrefs.edit().putBoolean(SHOW_VOICEMAIL_PROMO_CARD, false).apply();
+ mShowPromoCard = false;
+ notifyItemRemoved(VOICEMAIL_PROMO_CARD_POSITION);
+ }
+
+ /**
+ * Creates the view holder for the voicemail promo card.
+ *
+ * @param parent The parent view.
+ * @return The {@link ViewHolder}.
+ */
+ protected ViewHolder createVoicemailPromoCardViewHolder(ViewGroup parent) {
+ LayoutInflater inflater = LayoutInflater.from(mContext);
+ View view = inflater.inflate(R.layout.voicemail_promo_card, parent, false);
+
+ PromoCardViewHolder viewHolder = PromoCardViewHolder.create(view);
+ return viewHolder;
+ }
}
diff --git a/src/com/android/dialer/calllog/CallLogFragment.java b/src/com/android/dialer/calllog/CallLogFragment.java
index 0f19f14..21ea97e 100644
--- a/src/com/android/dialer/calllog/CallLogFragment.java
+++ b/src/com/android/dialer/calllog/CallLogFragment.java
@@ -187,7 +187,10 @@
resolver.registerContentObserver(Status.CONTENT_URI, true, mVoicemailStatusObserver);
setHasOptionsMenu(true);
- mVoicemailPlaybackPresenter = new VoicemailPlaybackPresenter(activity, state);
+ if (mCallTypeFilter == Calls.VOICEMAIL_TYPE) {
+ mVoicemailPlaybackPresenter = VoicemailPlaybackPresenter
+ .getInstance(activity, state);
+ }
}
/** Called by the CallLogQueryHandler when the list of calls has been fetched or updated. */
@@ -321,23 +324,28 @@
@Override
public void onPause() {
- mVoicemailPlaybackPresenter.onPause(getActivity().isFinishing());
+ if (mVoicemailPlaybackPresenter != null) {
+ mVoicemailPlaybackPresenter.onPause();
+ }
mAdapter.pauseCache();
super.onPause();
}
@Override
public void onStop() {
- super.onStop();
-
updateOnTransition(false /* onEntry */);
+
+ super.onStop();
}
@Override
public void onDestroy() {
mAdapter.pauseCache();
mAdapter.changeCursor(null);
- mVoicemailPlaybackPresenter.onDestroy(getActivity().isFinishing());
+
+ if (mVoicemailPlaybackPresenter != null) {
+ mVoicemailPlaybackPresenter.onDestroy();
+ }
getActivity().getContentResolver().unregisterContentObserver(mCallLogObserver);
getActivity().getContentResolver().unregisterContentObserver(mContactsObserver);
@@ -353,7 +361,10 @@
outState.putLong(KEY_DATE_LIMIT, mDateLimit);
mAdapter.onSaveInstanceState(outState);
- mVoicemailPlaybackPresenter.onSaveInstanceState(outState);
+
+ if (mVoicemailPlaybackPresenter != null) {
+ mVoicemailPlaybackPresenter.onSaveInstanceState(outState);
+ }
}
@Override
diff --git a/src/com/android/dialer/calllog/DefaultVoicemailNotifier.java b/src/com/android/dialer/calllog/DefaultVoicemailNotifier.java
index abdc38f..3d6eb0b 100644
--- a/src/com/android/dialer/calllog/DefaultVoicemailNotifier.java
+++ b/src/com/android/dialer/calllog/DefaultVoicemailNotifier.java
@@ -32,8 +32,10 @@
import android.util.Log;
import com.android.common.io.MoreCloseables;
+import com.android.dialer.DialtactsActivity;
import com.android.dialer.R;
import com.android.dialer.calllog.PhoneAccountUtils;
+import com.android.dialer.list.ListsFragment;
import com.google.common.collect.Maps;
import java.util.Map;
@@ -179,10 +181,10 @@
final Intent contentIntent;
// Open the call log.
// TODO: Send to recents tab in Dialer instead.
- contentIntent = new Intent(Intent.ACTION_VIEW, Calls.CONTENT_URI);
- contentIntent.putExtra(Calls.EXTRA_CALL_TYPE_FILTER, Calls.VOICEMAIL_TYPE);
- notificationBuilder.setContentIntent(
- PendingIntent.getActivity(mContext, 0, contentIntent, 0));
+ contentIntent = new Intent(mContext, DialtactsActivity.class);
+ contentIntent.putExtra(DialtactsActivity.EXTRA_SHOW_TAB, ListsFragment.TAB_INDEX_VOICEMAIL);
+ notificationBuilder.setContentIntent(PendingIntent.getActivity(
+ mContext, 0, contentIntent, PendingIntent.FLAG_UPDATE_CURRENT));
// The text to show in the ticker, describing the new event.
if (callToNotify != null) {
diff --git a/src/com/android/dialer/calllog/PromoCardViewHolder.java b/src/com/android/dialer/calllog/PromoCardViewHolder.java
new file mode 100644
index 0000000..4c96027
--- /dev/null
+++ b/src/com/android/dialer/calllog/PromoCardViewHolder.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2015 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 com.android.dialer.R;
+
+import android.support.v7.widget.CardView;
+import android.support.v7.widget.RecyclerView;
+import android.view.View;
+
+/**
+ * View holder class for a promo card which will appear in the voicemail tab.
+ */
+public class PromoCardViewHolder extends RecyclerView.ViewHolder {
+ public static PromoCardViewHolder create(View rootView) {
+ return new PromoCardViewHolder(rootView);
+ }
+
+ /**
+ * The "Settings" button view.
+ */
+ private View mSettingsTextView;
+
+ /**
+ * The "Ok" button view.
+ */
+ private View mOkTextView;
+
+ /**
+ * Creates an instance of the {@link ViewHolder}.
+ *
+ * @param rootView The root view.
+ */
+ private PromoCardViewHolder(View rootView) {
+ super(rootView);
+
+ mSettingsTextView = rootView.findViewById(R.id.settings_action);
+ mOkTextView = rootView.findViewById(R.id.ok_action);
+ }
+
+ /**
+ * Retrieves the "Settings" button.
+ *
+ * @return The view.
+ */
+ public View getSettingsTextView() {
+ return mSettingsTextView;
+ }
+
+ /**
+ * Retrieves the "Ok" button.
+ *
+ * @return The view.
+ */
+ public View getOkTextView() {
+ return mOkTextView;
+ }
+}
diff --git a/src/com/android/dialer/dialpad/DialpadFragment.java b/src/com/android/dialer/dialpad/DialpadFragment.java
index 766175c..5b1e211 100644
--- a/src/com/android/dialer/dialpad/DialpadFragment.java
+++ b/src/com/android/dialer/dialpad/DialpadFragment.java
@@ -1643,12 +1643,17 @@
if (mAnimate) {
dialpadView.animateShow();
}
+ mFloatingActionButtonController.setVisible(false);
mFloatingActionButtonController.scaleIn(mAnimate ? mDialpadSlideInDuration : 0);
activity.onDialpadShown();
mDigits.requestFocus();
}
- if (hidden && mAnimate) {
- mFloatingActionButtonController.scaleOut();
+ if (hidden) {
+ if (mAnimate) {
+ mFloatingActionButtonController.scaleOut();
+ } else {
+ mFloatingActionButtonController.setVisible(false);
+ }
}
}
diff --git a/src/com/android/dialer/list/AllContactsFragment.java b/src/com/android/dialer/list/AllContactsFragment.java
index 71c6980..d34250b 100644
--- a/src/com/android/dialer/list/AllContactsFragment.java
+++ b/src/com/android/dialer/list/AllContactsFragment.java
@@ -75,7 +75,7 @@
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
super.onLoadFinished(loader, data);
- if (data.getCount() == 0) {
+ if (data == null || data.getCount() == 0) {
mEmptyListView.setVisibility(View.VISIBLE);
}
}
diff --git a/src/com/android/dialer/list/ListsFragment.java b/src/com/android/dialer/list/ListsFragment.java
index 0e3df52..e45da0c 100644
--- a/src/com/android/dialer/list/ListsFragment.java
+++ b/src/com/android/dialer/list/ListsFragment.java
@@ -84,6 +84,8 @@
private SharedPreferences mPrefs;
private boolean mHasActiveVoicemailProvider;
+ private boolean mHasFetchedVoicemailStatus;
+ private boolean mShowVoicemailTabAfterVoicemailStatusIsFetched;
private VoicemailStatusHelper mVoicemailStatusHelper;
private ArrayList<OnPageChangeListener> mOnPageChangeListeners =
@@ -167,6 +169,7 @@
Trace.endSection();
mVoicemailStatusHelper = new VoicemailStatusHelperImpl();
+ mHasFetchedVoicemailStatus = false;
mPrefs = PreferenceManager.getDefaultSharedPreferences(getActivity());
mHasActiveVoicemailProvider = mPrefs.getBoolean(
@@ -204,7 +207,7 @@
mViewPager.setAdapter(mViewPagerAdapter);
mViewPager.setOffscreenPageLimit(TAB_COUNT_WITH_VOICEMAIL - 1);
mViewPager.setOnPageChangeListener(this);
- mViewPager.setCurrentItem(getRtlPosition(TAB_INDEX_SPEED_DIAL));
+ showTab(TAB_INDEX_SPEED_DIAL);
mTabTitles = new String[TAB_COUNT_WITH_VOICEMAIL];
mTabTitles[TAB_INDEX_SPEED_DIAL] = getResources().getString(R.string.tab_speed_dial);
@@ -237,6 +240,24 @@
}
}
+ /**
+ * Shows the tab with the specified index. If the voicemail tab index is specified, but the
+ * voicemail status hasn't been fetched, it will try to show the tab after the voicemail status
+ * has been fetched.
+ */
+ public void showTab(int index) {
+ if (index == TAB_INDEX_VOICEMAIL) {
+ if (mHasActiveVoicemailProvider) {
+ mViewPager.setCurrentItem(getRtlPosition(TAB_INDEX_VOICEMAIL));
+ } else if (!mHasFetchedVoicemailStatus) {
+ // Try to show the voicemail tab after the voicemail status returns.
+ mShowVoicemailTabAfterVoicemailStatusIsFetched = true;
+ }
+ } else {
+ mViewPager.setCurrentItem(getRtlPosition(index));
+ }
+ }
+
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
mTabIndex = getRtlPosition(position);
@@ -252,6 +273,9 @@
public void onPageSelected(int position) {
mTabIndex = getRtlPosition(position);
+ // Show the tab which has been selected instead.
+ mShowVoicemailTabAfterVoicemailStatusIsFetched = false;
+
final int count = mOnPageChangeListeners.size();
for (int i = 0; i < count; i++) {
mOnPageChangeListeners.get(i).onPageSelected(position);
@@ -269,6 +293,8 @@
@Override
public void onVoicemailStatusFetched(Cursor statusCursor) {
+ mHasFetchedVoicemailStatus = true;
+
if (getActivity() == null || getActivity().isFinishing()) {
return;
}
@@ -285,6 +311,11 @@
.putBoolean(PREF_KEY_HAS_ACTIVE_VOICEMAIL_PROVIDER, hasActiveVoicemailProvider)
.commit();
}
+
+ if (mHasActiveVoicemailProvider && mShowVoicemailTabAfterVoicemailStatusIsFetched) {
+ mShowVoicemailTabAfterVoicemailStatusIsFetched = false;
+ showTab(TAB_INDEX_VOICEMAIL);
+ }
}
@Override
diff --git a/src/com/android/dialer/voicemail/VoicemailPlaybackLayout.java b/src/com/android/dialer/voicemail/VoicemailPlaybackLayout.java
index 73f4b3b..ca487db 100644
--- a/src/com/android/dialer/voicemail/VoicemailPlaybackLayout.java
+++ b/src/com/android/dialer/voicemail/VoicemailPlaybackLayout.java
@@ -50,8 +50,9 @@
import javax.annotation.concurrent.ThreadSafe;
/**
- * Displays and plays a single voicemail.
- * <p>
+ * Displays and plays a single voicemail. See {@link VoicemailPlaybackPresenter} for
+ * details on the voicemail playback implementation.
+ *
* This class is not thread-safe, it is thread-confined. All calls to all public
* methods on this class are expected to come from the main ui thread.
*/
@@ -178,12 +179,13 @@
if (mPresenter == null) {
return;
}
- CallLogAsyncTaskUtil.deleteVoicemail(mContext, mPresenter.getVoicemailUri(), null);
+ CallLogAsyncTaskUtil.deleteVoicemail(mContext, mVoicemailUri, null);
}
};
private Context mContext;
private VoicemailPlaybackPresenter mPresenter;
+ private Uri mVoicemailUri;
private boolean mIsPlaying = false;
@@ -209,8 +211,9 @@
}
@Override
- public void setPresenter(VoicemailPlaybackPresenter presenter) {
+ public void setPresenter(VoicemailPlaybackPresenter presenter, Uri voicemailUri) {
mPresenter = presenter;
+ mVoicemailUri = voicemailUri;
}
@Override
@@ -256,15 +259,13 @@
}
@Override
- public void onPlaybackError(Exception e) {
+ public void onPlaybackError() {
if (mPositionUpdater != null) {
mPositionUpdater.stopUpdating();
}
disableUiElements();
mPlaybackPosition.setText(getString(R.string.voicemail_playback_error));
-
- Log.e(TAG, "Could not play voicemail", e);
}
diff --git a/src/com/android/dialer/voicemail/VoicemailPlaybackPresenter.java b/src/com/android/dialer/voicemail/VoicemailPlaybackPresenter.java
index 60425e4..4cd8c4d 100644
--- a/src/com/android/dialer/voicemail/VoicemailPlaybackPresenter.java
+++ b/src/com/android/dialer/voicemail/VoicemailPlaybackPresenter.java
@@ -56,10 +56,13 @@
import javax.annotation.concurrent.ThreadSafe;
/**
- * Contains the controlling logic for a voicemail playback UI.
+ * Contains the controlling logic for a voicemail playback in the call log. It is closely coupled
+ * to assumptions about the behaviors and lifecycle of the call log, in particular in the
+ * {@link CallLogFragment} and {@link CallLogAdapter}.
* <p>
* This controls a single {@link com.android.dialer.voicemail.VoicemailPlaybackLayout}. A single
- * instance can be reused for different such layouts, using {@link #setVoicemailPlaybackView}.
+ * instance can be reused for different such layouts, using {@link #setVoicemailPlaybackView}. This
+ * is to facilitate reuse across different voicemail call log entries.
* <p>
* This class is not thread safe. The thread policy for this class is thread-confinement, all calls
* into this class from outside must be done from the main UI thread.
@@ -77,7 +80,7 @@
int getDesiredClipPosition();
void disableUiElements();
void enableUiElements();
- void onPlaybackError(Exception e);
+ void onPlaybackError();
void onPlaybackStarted(int duration, ScheduledExecutorService executorService);
void onPlaybackStopped();
void onSpeakerphoneOn(boolean on);
@@ -85,7 +88,7 @@
void setFetchContentTimeout();
void setIsBuffering();
void setIsFetchingContent();
- void setPresenter(VoicemailPlaybackPresenter presenter);
+ void setPresenter(VoicemailPlaybackPresenter presenter, Uri voicemailUri);
}
/** The enumeration of {@link AsyncTask} objects we use in this class. */
@@ -121,12 +124,14 @@
*/
private final AtomicInteger mDuration = new AtomicInteger(0);
+ private static VoicemailPlaybackPresenter sInstance;
+
private Activity mActivity;
private Context mContext;
private PlaybackView mView;
- private static MediaPlayer mMediaPlayer;
-
private Uri mVoicemailUri;
+
+ private MediaPlayer mMediaPlayer;
private int mPosition;
private boolean mIsPlaying;
// MediaPlayer crashes on some method calls if not prepared but does not have a method which
@@ -134,9 +139,10 @@
private boolean mIsPrepared;
private boolean mShouldResumePlaybackAfterSeeking;
+ private int mInitialOrientation;
// Used to run async tasks that need to interact with the UI.
- private final AsyncTaskExecutor mAsyncTaskExecutor;
+ private AsyncTaskExecutor mAsyncTaskExecutor;
private static ScheduledExecutorService mScheduledExecutorService;
/**
* Used to handle the result of a successful or time-out fetch result.
@@ -148,12 +154,49 @@
private PowerManager.WakeLock mProximityWakeLock;
private AudioManager mAudioManager;
- public VoicemailPlaybackPresenter(Activity activity, Bundle savedInstanceState) {
+ /**
+ * Obtain singleton instance of this class. Use a single instance to provide a consistent
+ * listener to the AudioManager when requesting and abandoning audio focus.
+ *
+ * Otherwise, after rotation the previous listener will still be active but a new listener
+ * will be provided to calls to the AudioManager, which is bad. For example, abandoning
+ * audio focus with the new listeners results in an AUDIO_FOCUS_GAIN callback to the
+ * previous listener, which is the opposite of the intended behavior.
+ */
+ public static VoicemailPlaybackPresenter getInstance(
+ Activity activity, Bundle savedInstanceState) {
+ if (sInstance == null) {
+ sInstance = new VoicemailPlaybackPresenter(activity);
+ }
+
+ sInstance.init(activity, savedInstanceState);
+ return sInstance;
+ }
+
+ /**
+ * Initialize variables which are activity-independent and state-independent.
+ */
+ private VoicemailPlaybackPresenter(Activity activity) {
+ Context context = activity.getApplicationContext();
+ mAsyncTaskExecutor = AsyncTaskExecutors.createAsyncTaskExecutor();
+ mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
+
+ PowerManager powerManager =
+ (PowerManager) context.getSystemService(Context.POWER_SERVICE);
+ if (powerManager.isWakeLockLevelSupported(PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK)) {
+ mProximityWakeLock = powerManager.newWakeLock(
+ PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK, TAG);
+ }
+ }
+
+ /**
+ * Update variables which are activity-dependent or state-dependent.
+ */
+ private void init(Activity activity, Bundle savedInstanceState) {
mActivity = activity;
mContext = activity;
- mAsyncTaskExecutor = AsyncTaskExecutors.createAsyncTaskExecutor();
- mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
+ mInitialOrientation = mContext.getResources().getConfiguration().orientation;
mActivity.setVolumeControlStream(VoicemailPlaybackPresenter.PLAYBACK_STREAM);
if (savedInstanceState != null) {
@@ -163,27 +206,58 @@
mPosition = savedInstanceState.getInt(CLIP_POSITION_KEY, 0);
mIsPlaying = savedInstanceState.getBoolean(IS_PLAYING_STATE_KEY, false);
}
-
- PowerManager powerManager =
- (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
- if (powerManager.isWakeLockLevelSupported(PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK)) {
- mProximityWakeLock = powerManager.newWakeLock(
- PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK, TAG);
- }
-
- // mMediaPlayer is static to enable seamless playback during rotation. If we do not create
- // a new MediaPlayer, we still need to update listeners to the current Presenter instance.
- if (mMediaPlayer == null) {
- mMediaPlayer = new MediaPlayer();
- mIsPrepared = false;
- }
- mMediaPlayer.setOnPreparedListener(this);
- mMediaPlayer.setOnErrorListener(this);
- mMediaPlayer.setOnCompletionListener(this);
}
+ /**
+ * Must be invoked when the parent Activity is saving it state.
+ */
+ public void onSaveInstanceState(Bundle outState) {
+ if (mView != null) {
+ outState.putParcelable(VOICEMAIL_URI_KEY, mVoicemailUri);
+ outState.putBoolean(IS_PREPARED_KEY, mIsPrepared);
+ outState.putInt(CLIP_POSITION_KEY, mView.getDesiredClipPosition());
+ outState.putBoolean(IS_PLAYING_STATE_KEY, mIsPlaying);
+ }
+ }
+
+ /**
+ * Specify the view which this presenter controls and the voicemail to prepare to play.
+ */
+ public void setPlaybackView(
+ PlaybackView view, Uri voicemailUri, boolean startPlayingImmediately) {
+ mView = view;
+ mView.setPresenter(this, voicemailUri);
+
+ if (mMediaPlayer != null && voicemailUri.equals(mVoicemailUri)) {
+ // Handles case where MediaPlayer was retained after an orientation change.
+ onPrepared(mMediaPlayer);
+ mView.onSpeakerphoneOn(isSpeakerphoneOn());
+ } else {
+ if (!voicemailUri.equals(mVoicemailUri)) {
+ mPosition = 0;
+ }
+
+ mVoicemailUri = voicemailUri;
+ mDuration.set(0);
+ mIsPlaying = startPlayingImmediately;
+
+ checkForContent();
+
+ // Default to earpiece.
+ mView.onSpeakerphoneOn(false);
+ }
+ }
+
+ /**
+ * Reset the presenter for playback.
+ */
public void reset() {
- pausePlayback();
+ if (mMediaPlayer != null) {
+ mMediaPlayer.release();
+ mMediaPlayer = null;
+ }
+
+ disableProximitySensor(false /* waitForFarState */);
mView = null;
mVoicemailUri = null;
@@ -195,49 +269,32 @@
}
/**
- * Specify the view which this presenter controls and the voicemail for playback.
+ * Must be invoked when the parent activity is paused.
*/
- public void setPlaybackView(
- PlaybackView view, Uri voicemailUri, boolean startPlayingImmediately) {
- mView = view;
- mView.setPresenter(this);
-
- if (mVoicemailUri != null && mVoicemailUri.equals(voicemailUri)) {
- // Handles rotation case where playback view is set for the same voicemail.
- if (mIsPrepared) {
- onPrepared(mMediaPlayer);
- } else {
- checkForContent();
- }
- mView.onSpeakerphoneOn(isSpeakerphoneOn());
- } else {
- mVoicemailUri = voicemailUri;
- mPosition = 0;
- mDuration.set(0);
- mIsPlaying = startPlayingImmediately;
-
- // Default to earpiece.
- mView.onSpeakerphoneOn(false);
-
- checkForContent();
+ public void onPause() {
+ if (mContext != null && mIsPrepared
+ && mInitialOrientation != mContext.getResources().getConfiguration().orientation) {
+ // If an orientation change triggers the pause, retain the MediaPlayer.
+ Log.d(TAG, "onPause: Orientation changed.");
+ return;
}
- }
- public void onPause(boolean isFinishing) {
- // Do not pause for orientation changes.
- if (mIsPrepared && mMediaPlayer.isPlaying() && isFinishing) {
- pausePlayback();
+ // Release the media player, otherwise there may be failures.
+ if (mMediaPlayer != null) {
+ mMediaPlayer.release();
+ mMediaPlayer = null;
}
disableProximitySensor(false /* waitForFarState */);
}
- public void onDestroy(boolean isFinishing) {
- // Do not release for orientation changes.
- if (mIsPrepared && isFinishing) {
- mMediaPlayer.release();
- mIsPrepared = false;
- }
+ /**
+ * Must be invoked when the parent activity is destroyed.
+ */
+ public void onDestroy() {
+ // Clear references to avoid leaks from the singleton instance.
+ mActivity = null;
+ mContext = null;
if (mScheduledExecutorService != null) {
mScheduledExecutorService.shutdown();
@@ -248,17 +305,6 @@
mFetchResultHandler.destroy();
mFetchResultHandler = null;
}
-
- disableProximitySensor(false /* waitForFarState */);
- }
-
- public void onSaveInstanceState(Bundle outState) {
- if (mView != null) {
- outState.putParcelable(VOICEMAIL_URI_KEY, mVoicemailUri);
- outState.putBoolean(IS_PREPARED_KEY, mIsPrepared);
- outState.putInt(CLIP_POSITION_KEY, mView.getDesiredClipPosition());
- outState.putBoolean(IS_PLAYING_STATE_KEY, mIsPlaying);
- }
}
/**
@@ -268,7 +314,7 @@
* voicemail we've been asked to play has any content available.
* <p>
* Notify the user that we are fetching the content, then check to see if the content field in
- * the DB is set. If set, we proceed to {@link #prepareToPlayContent()} method. If not set, make
+ * the DB is set. If set, we proceed to {@link #prepareContent()} method. If not set, make
* a request to fetch the content asynchronously via {@link #requestContent()}.
*/
private void checkForContent() {
@@ -282,7 +328,7 @@
@Override
public void onPostExecute(Boolean hasContent) {
if (hasContent) {
- prepareToPlayContent();
+ prepareContent();
} else {
requestContent();
}
@@ -314,7 +360,7 @@
* will trigger a broadcast to request that the content be downloaded. It will add a listener to
* the content resolver so that it will be notified when the has_content field changes. It will
* also set a timer. If the has_content field changes to true within the allowed time, we will
- * proceed to {@link #prepareToPlayContent()}. If the has_content field does not
+ * proceed to {@link #prepareContent()}. If the has_content field does not
* become true within the allowed time, we will update the ui to reflect the fact that content
* was not available.
*/
@@ -323,8 +369,7 @@
mFetchResultHandler.destroy();
}
- mFetchResultHandler = new FetchResultHandler(new Handler());
- mFetchResultHandler.registerContentObserver(mVoicemailUri);
+ mFetchResultHandler = new FetchResultHandler(new Handler(), mVoicemailUri);
// Send voicemail fetch request.
Intent intent = new Intent(VoicemailContract.ACTION_FETCH_VOICEMAIL, mVoicemailUri);
@@ -336,17 +381,13 @@
private AtomicBoolean mIsWaitingForResult = new AtomicBoolean(true);
private final Handler mFetchResultHandler;
- public FetchResultHandler(Handler handler) {
+ public FetchResultHandler(Handler handler, Uri voicemailUri) {
super(handler);
mFetchResultHandler = handler;
- }
- public void registerContentObserver(Uri voicemailUri) {
- if (mIsWaitingForResult.get()) {
- mContext.getContentResolver().registerContentObserver(
- voicemailUri, false, this);
- mFetchResultHandler.postDelayed(this, FETCH_CONTENT_TIMEOUT_MS);
- }
+ mContext.getContentResolver().registerContentObserver(
+ voicemailUri, false, this);
+ mFetchResultHandler.postDelayed(this, FETCH_CONTENT_TIMEOUT_MS);
}
/**
@@ -354,7 +395,7 @@
*/
@Override
public void run() {
- if (mIsWaitingForResult.getAndSet(false)) {
+ if (mIsWaitingForResult.getAndSet(false) && mContext != null) {
mContext.getContentResolver().unregisterContentObserver(this);
if (mView != null) {
mView.setFetchContentTimeout();
@@ -363,7 +404,7 @@
}
public void destroy() {
- if (mIsWaitingForResult.getAndSet(false)) {
+ if (mIsWaitingForResult.getAndSet(false) && mContext != null) {
mContext.getContentResolver().unregisterContentObserver(this);
mFetchResultHandler.removeCallbacks(this);
}
@@ -380,12 +421,10 @@
@Override
public void onPostExecute(Boolean hasContent) {
- if (hasContent) {
- if (mIsWaitingForResult.getAndSet(false)) {
- mContext.getContentResolver().unregisterContentObserver(
- FetchResultHandler.this);
- prepareToPlayContent();
- }
+ if (hasContent && mContext != null && mIsWaitingForResult.getAndSet(false)) {
+ mContext.getContentResolver().unregisterContentObserver(
+ FetchResultHandler.this);
+ prepareContent();
}
}
});
@@ -400,15 +439,27 @@
* media player. If preparation is successful, the media player will {@link #onPrepared()},
* and it will call {@link #onError()} otherwise.
*/
- private void prepareToPlayContent() {
+ private void prepareContent() {
if (mView == null) {
return;
}
- mIsPrepared = false;
+ Log.d(TAG, "prepareContent");
+
+ // Release the previous media player, otherwise there may be failures.
+ if (mMediaPlayer != null) {
+ mMediaPlayer.release();
+ mMediaPlayer = null;
+ }
mView.setIsBuffering();
+ mIsPrepared = false;
try {
+ mMediaPlayer = new MediaPlayer();
+ mMediaPlayer.setOnPreparedListener(this);
+ mMediaPlayer.setOnErrorListener(this);
+ mMediaPlayer.setOnCompletionListener(this);
+
mMediaPlayer.reset();
mMediaPlayer.setDataSource(mContext, mVoicemailUri);
mMediaPlayer.setAudioStreamType(PLAYBACK_STREAM);
@@ -426,12 +477,15 @@
if (mView == null) {
return;
}
+ Log.d(TAG, "onPrepared");
mIsPrepared = true;
mDuration.set(mMediaPlayer.getDuration());
mView.enableUiElements();
+ Log.d(TAG, "onPrepared: mPosition=" + mPosition);
mView.setClipPosition(mPosition, mDuration.get());
+ mMediaPlayer.seekTo(mPosition);
if (mIsPlaying) {
resumePlayback();
@@ -451,12 +505,15 @@
}
private void handleError(Exception e) {
+ Log.d(TAG, "handleError: Could not play voicemail " + e);
+
if (mIsPrepared) {
mMediaPlayer.release();
+ mMediaPlayer = null;
mIsPrepared = false;
}
- mView.onPlaybackError(e);
+ mView.onPlaybackError();
mPosition = 0;
mIsPlaying = false;
@@ -476,15 +533,12 @@
@Override
public void onAudioFocusChange(int focusChange) {
- if (!mIsPrepared) {
- return;
- }
-
- boolean lostFocus = focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT ||
- focusChange == AudioManager.AUDIOFOCUS_LOSS;
- if (mMediaPlayer.isPlaying() && lostFocus) {
+ Log.d(TAG, "onAudioFocusChange: focusChange=" + focusChange);
+ boolean lostFocus = focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT
+ || focusChange == AudioManager.AUDIOFOCUS_LOSS;
+ if (mIsPlaying && focusChange == AudioManager.AUDIOFOCUS_LOSS) {
pausePlayback();
- } else if (!mMediaPlayer.isPlaying() && focusChange == AudioManager.AUDIOFOCUS_GAIN) {
+ } else if (!mIsPlaying && focusChange == AudioManager.AUDIOFOCUS_GAIN) {
resumePlayback();
}
}
@@ -506,25 +560,25 @@
mMediaPlayer.seekTo(mPosition);
try {
- // Grab audio focus here
+ // Grab audio focus.
int result = mAudioManager.requestAudioFocus(
- VoicemailPlaybackPresenter.this,
+ this,
PLAYBACK_STREAM,
AudioManager.AUDIOFOCUS_GAIN_TRANSIENT);
-
if (result != AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
throw new RejectedExecutionException("Could not capture audio focus.");
}
- // Can throw RejectedExecutionException
+ // Can throw RejectedExecutionException.
mMediaPlayer.start();
} catch (RejectedExecutionException e) {
handleError(e);
}
}
- enableProximitySensor();
+ Log.d(TAG, "Resumed playback at " + mPosition + ".");
mView.onPlaybackStarted(mDuration.get(), getScheduledExecutorServiceInstance());
+ enableProximitySensor();
}
/**
@@ -535,17 +589,17 @@
return;
}
- mPosition = mMediaPlayer.getCurrentPosition();
mIsPlaying = false;
if (mMediaPlayer.isPlaying()) {
mMediaPlayer.pause();
}
- mAudioManager.abandonAudioFocus(this);
- mView.onPlaybackStopped();
+ mPosition = mMediaPlayer.getCurrentPosition();
+ Log.d(TAG, "Paused playback at " + mPosition + ".");
- // Always disable the proximity sensor on stop.
+ mView.onPlaybackStopped();
+ mAudioManager.abandonAudioFocus(this);
disableProximitySensor(true /* waitForFarState */);
}
@@ -567,6 +621,9 @@
}
private void enableProximitySensor() {
+ // Disable until proximity sensor behavior in onPause is fixed: b/21932251.
+
+ /*
if (mProximityWakeLock == null || isSpeakerphoneOn() || !mIsPrepared
|| !mMediaPlayer.isPlaying()) {
return;
@@ -578,6 +635,7 @@
} else {
Log.i(TAG, "Proximity wake lock already acquired");
}
+ */
}
private void disableProximitySensor(boolean waitForFarState) {
@@ -606,12 +664,8 @@
return mAudioManager.isSpeakerphoneOn();
}
- public Uri getVoicemailUri() {
- return mVoicemailUri;
- }
-
public int getMediaPlayerPosition() {
- return mIsPrepared ? mMediaPlayer.getCurrentPosition() : 0;
+ return mIsPrepared && mMediaPlayer != null ? mMediaPlayer.getCurrentPosition() : 0;
}
private static synchronized ScheduledExecutorService getScheduledExecutorServiceInstance() {
diff --git a/tests/src/com/android/dialer/voicemail/VoicemailPlaybackTest.java b/tests/src/com/android/dialer/voicemail/VoicemailPlaybackTest.java
index ea341a3..58b4f55 100644
--- a/tests/src/com/android/dialer/voicemail/VoicemailPlaybackTest.java
+++ b/tests/src/com/android/dialer/voicemail/VoicemailPlaybackTest.java
@@ -85,7 +85,7 @@
mLayout = new VoicemailPlaybackLayout(mActivity);
mLayout.onFinishInflate();
- mPresenter = new VoicemailPlaybackPresenter(mActivity, null);
+ mPresenter = VoicemailPlaybackPresenter.getInstance(mActivity, null);
}
@Override