Merge "Call-Blocking causing crash with incoming SIP call" into ub-contactsdialer-a-dev
diff --git a/proguard.flags b/proguard.flags
index 38d4050..185d5eb 100644
--- a/proguard.flags
+++ b/proguard.flags
@@ -12,4 +12,9 @@
@com.android.dialer.NeededForReflection *;
}
+# For design libraries
+-keep public class * extends android.support.design.widget.CoordinatorLayout$Behavior {
+ public <init>(android.content.Context, android.util.AttributeSet);
+}
+
-verbose
diff --git a/res/layout/dialtacts_activity.xml b/res/layout/dialtacts_activity.xml
index 0f1f2bb..782d4f3 100644
--- a/res/layout/dialtacts_activity.xml
+++ b/res/layout/dialtacts_activity.xml
@@ -13,8 +13,9 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<FrameLayout
+<android.support.design.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/dialtacts_mainlayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
@@ -43,7 +44,8 @@
android:layout_width="@dimen/floating_action_button_width"
android:layout_height="@dimen/floating_action_button_height"
android:layout_marginBottom="@dimen/floating_action_button_margin_bottom"
- android:layout_gravity="center_horizontal|bottom">
+ android:layout_gravity="center_horizontal|bottom"
+ app:layout_behavior="com.android.dialer.FloatingActionButtonBehavior">
<ImageButton
android:id="@+id/floating_action_button"
@@ -68,4 +70,4 @@
android:importantForAccessibility="no" />
</FrameLayout>
-</FrameLayout>
+</android.support.design.widget.CoordinatorLayout>
diff --git a/res/layout/voicemail_playback_layout.xml b/res/layout/voicemail_playback_layout.xml
index 555d201..54493f1 100644
--- a/res/layout/voicemail_playback_layout.xml
+++ b/res/layout/voicemail_playback_layout.xml
@@ -71,7 +71,7 @@
<ImageButton android:id="@+id/playback_speakerphone"
style="@style/VoicemailPlaybackLayoutButtonStyle"
- android:src="@drawable/ic_speakerphone_on"
+ android:src="@drawable/ic_volume_down_24dp"
android:tint="@color/voicemail_icon_tint"
android:contentDescription="@string/description_playback_speakerphone" />
diff --git a/src/com/android/dialer/CallDetailActivity.java b/src/com/android/dialer/CallDetailActivity.java
index 32eccc4..72a5012 100644
--- a/src/com/android/dialer/CallDetailActivity.java
+++ b/src/com/android/dialer/CallDetailActivity.java
@@ -16,32 +16,22 @@
package com.android.dialer;
-import android.app.Activity;
-import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
import android.net.Uri;
import android.os.Bundle;
-import android.os.PowerManager;
import android.provider.ContactsContract.CommonDataKinds.Phone;
-import android.provider.VoicemailContract.Voicemails;
-import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity;
-import android.telecom.PhoneAccount;
import android.telecom.PhoneAccountHandle;
-import android.telephony.TelephonyManager;
import android.text.BidiFormatter;
import android.text.TextDirectionHeuristics;
import android.text.TextUtils;
-import android.util.Log;
-import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
-import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.QuickContactBadge;
import android.widget.TextView;
@@ -49,7 +39,6 @@
import com.android.contacts.common.ContactPhotoManager;
import com.android.contacts.common.ContactPhotoManager.DefaultImageRequest;
-import com.android.contacts.common.util.PermissionsUtil;
import com.android.contacts.common.GeoUtil;
import com.android.contacts.common.CallUtil;
import com.android.contacts.common.util.UriUtils;
@@ -57,16 +46,12 @@
import com.android.dialer.calllog.CallLogAsyncTaskUtil.CallLogAsyncTaskListener;
import com.android.dialer.calllog.CallLogAsyncTaskUtil;
import com.android.dialer.calllog.CallTypeHelper;
-import com.android.dialer.calllog.ContactInfo;
import com.android.dialer.calllog.ContactInfoHelper;
import com.android.dialer.calllog.PhoneAccountUtils;
-import com.android.dialer.calllog.PhoneNumberDisplayUtil;
-import com.android.dialer.util.DialerUtils;
-import com.android.dialer.util.IntentUtil;
+import com.android.dialer.util.IntentUtil.CallIntentBuilder;
import com.android.dialer.util.PhoneNumberUtil;
import com.android.dialer.util.TelecomUtil;
-
-import java.util.List;
+import com.android.incallui.Call.LogState;
/**
* Displays the details of a specific call log entry.
@@ -252,11 +237,14 @@
mDefaultCountryIso = GeoUtil.getCurrentCountryIso(this);
mContactPhotoManager = ContactPhotoManager.getInstance(this);
- mCallButton = (View) findViewById(R.id.call_back_button);
+ mCallButton = findViewById(R.id.call_back_button);
mCallButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
- mContext.startActivity(IntentUtil.getCallIntent(mNumber));
+ mContext.startActivity(
+ new CallIntentBuilder(mNumber)
+ .setCallInitiationType(LogState.INITIATION_CALL_DETAILS)
+ .build());
}
});
diff --git a/src/com/android/dialer/DialtactsActivity.java b/src/com/android/dialer/DialtactsActivity.java
index 4ade04a..dbff276 100644
--- a/src/com/android/dialer/DialtactsActivity.java
+++ b/src/com/android/dialer/DialtactsActivity.java
@@ -30,6 +30,7 @@
import android.os.Trace;
import android.provider.CallLog.Calls;
import android.speech.RecognizerIntent;
+import android.support.design.widget.CoordinatorLayout;
import android.support.v4.view.ViewPager;
import android.support.v7.app.ActionBar;
import android.telecom.PhoneAccount;
@@ -52,7 +53,6 @@
import android.view.animation.AnimationUtils;
import android.widget.AbsListView.OnScrollListener;
import android.widget.EditText;
-import android.widget.FrameLayout;
import android.widget.ImageButton;
import android.widget.PopupMenu;
import android.widget.TextView;
@@ -83,11 +83,13 @@
import com.android.dialer.list.SpeedDialFragment;
import com.android.dialer.settings.DialerSettingsActivity;
import com.android.dialer.util.IntentUtil;
+import com.android.dialer.util.IntentUtil.CallIntentBuilder;
import com.android.dialer.util.DialerUtils;
import com.android.dialer.widget.ActionBarController;
import com.android.dialer.widget.SearchEditTextLayout;
import com.android.dialer.widget.SearchEditTextLayout.Callback;
import com.android.dialerbind.DatabaseHelperManager;
+import com.android.incallui.Call.LogState;
import com.android.phone.common.animation.AnimUtils;
import com.android.phone.common.animation.AnimationListenerAdapter;
@@ -118,10 +120,6 @@
public static final String SHARED_PREFS_NAME = "com.android.dialer_preferences";
- /** @see #getCallOrigin() */
- private static final String CALL_ORIGIN_DIALTACTS =
- "com.android.dialer.DialtactsActivity";
-
private static final String KEY_IN_REGULAR_SEARCH_UI = "in_regular_search_ui";
private static final String KEY_IN_DIALPAD_SEARCH_UI = "in_dialpad_search_ui";
private static final String KEY_SEARCH_QUERY = "search_query";
@@ -143,7 +141,7 @@
private static final int FAB_SCALE_IN_DELAY_MS = 300;
- private FrameLayout mParentLayout;
+ private CoordinatorLayout mParentLayout;
/**
* Fragment containing the dialpad that slides into view
@@ -468,7 +466,7 @@
mSlideIn.setAnimationListener(mSlideInListener);
mSlideOut.setAnimationListener(mSlideOutListener);
- mParentLayout = (FrameLayout) findViewById(R.id.dialtacts_mainlayout);
+ mParentLayout = (CoordinatorLayout) findViewById(R.id.dialtacts_mainlayout);
mParentLayout.setOnDragListener(new LayoutOnDragListener());
floatingActionButtonContainer.getViewTreeObserver().addOnGlobalLayoutListener(
new ViewTreeObserver.OnGlobalLayoutListener() {
@@ -536,13 +534,21 @@
mDialerDatabaseHelper.startSmartDialUpdateThread();
mFloatingActionButtonController.align(getFabAlignment(), false /* animate */);
- if (getIntent().hasExtra(EXTRA_SHOW_TAB)) {
+ if (Calls.CONTENT_TYPE.equals(getIntent().getType())) {
+ // Externally specified extras take precedence to EXTRA_SHOW_TAB, which is only
+ // used internally.
+ final Bundle extras = getIntent().getExtras();
+ if (extras != null
+ && extras.getInt(Calls.EXTRA_CALL_TYPE_FILTER) == Calls.VOICEMAIL_TYPE) {
+ mListsFragment.showTab(ListsFragment.TAB_INDEX_VOICEMAIL);
+ } else {
+ mListsFragment.showTab(ListsFragment.TAB_INDEX_HISTORY);
+ }
+ } else if (getIntent().hasExtra(EXTRA_SHOW_TAB)) {
int index = getIntent().getIntExtra(EXTRA_SHOW_TAB, ListsFragment.TAB_INDEX_SPEED_DIAL);
if (index < mListsFragment.getTabCount()) {
mListsFragment.showTab(index);
}
- } else if (Calls.CONTENT_TYPE.equals(getIntent().getType())) {
- mListsFragment.showTab(ListsFragment.TAB_INDEX_HISTORY);
}
setSearchBoxHint();
@@ -951,16 +957,6 @@
}
/**
- * Returns an appropriate call origin for this Activity. May return null when no call origin
- * should be used (e.g. when some 3rd party application launched the screen. Call origin is
- * for remembering the tab in which the user made a phone call, so the external app's DIAL
- * request should not be counted.)
- */
- public String getCallOrigin() {
- return !isDialIntent(getIntent()) ? CALL_ORIGIN_DIALTACTS : null;
- }
-
- /**
* Shows the search fragment
*/
private void enterSearchUi(boolean smartDialSearch, String query, boolean animate) {
@@ -1248,29 +1244,26 @@
}
@Override
- public void onPickPhoneNumberAction(Uri dataUri) {
- // Specify call-origin so that users will see the previous tab instead of
- // CallLog screen (search UI will be automatically exited).
- PhoneNumberInteraction.startInteractionForPhoneCall(
- DialtactsActivity.this, dataUri, getCallOrigin());
+ public void onPickPhoneNumberAction(Uri dataUri, int callInitiationType) {
mClearSearchOnPause = true;
+ PhoneNumberInteraction.startInteractionForPhoneCall(
+ DialtactsActivity.this, dataUri, callInitiationType);
}
@Override
- public void onCallNumberDirectly(String phoneNumber) {
- onCallNumberDirectly(phoneNumber, false /* isVideoCall */);
- }
-
- @Override
- public void onCallNumberDirectly(String phoneNumber, boolean isVideoCall) {
+ public void onCallNumberDirectly(String phoneNumber, boolean isVideoCall,
+ int callInitiationType) {
if (phoneNumber == null) {
// Invalid phone number, but let the call go through so that InCallUI can show
// an error message.
phoneNumber = "";
}
- Intent intent = isVideoCall ?
- IntentUtil.getVideoCallIntent(phoneNumber, getCallOrigin()) :
- IntentUtil.getCallIntent(phoneNumber, getCallOrigin());
+
+ final Intent intent = new CallIntentBuilder(phoneNumber)
+ .setIsVideoCall(isVideoCall)
+ .setCallInitiationType(callInitiationType)
+ .build();
+
DialerUtils.startActivityWithErrorToast(this, intent);
mClearSearchOnPause = true;
}
diff --git a/src/com/android/dialer/FloatingActionButtonBehavior.java b/src/com/android/dialer/FloatingActionButtonBehavior.java
new file mode 100644
index 0000000..8a407bd
--- /dev/null
+++ b/src/com/android/dialer/FloatingActionButtonBehavior.java
@@ -0,0 +1,50 @@
+/*
+ * 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;
+
+import android.content.Context;
+import android.support.design.widget.CoordinatorLayout;
+import android.support.design.widget.Snackbar.SnackbarLayout;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.FrameLayout;
+
+/**
+ * Implements custom behavior for the movement of the FAB in response to the Snackbar.
+ * Because we are not using the design framework FloatingActionButton widget, we need to manually
+ * implement the Material Design behavior of having the FAB translate upward and downward with
+ * the appearance and disappearance of a Snackbar.
+ */
+public class FloatingActionButtonBehavior extends CoordinatorLayout.Behavior<FrameLayout> {
+ public FloatingActionButtonBehavior(Context context, AttributeSet attrs) {
+ }
+
+ @Override
+ public boolean layoutDependsOn(CoordinatorLayout parent, FrameLayout child, View dependency) {
+ // This needs to return true to trigger the callback correctly.
+ return true;
+ }
+
+ @Override
+ public boolean onDependentViewChanged(CoordinatorLayout parent, FrameLayout child,
+ View dependency) {
+ if (dependency instanceof SnackbarLayout) {
+ float translationY = Math.min(0, dependency.getTranslationY() - dependency.getHeight());
+ child.setTranslationY(translationY);
+ }
+ return true;
+ }
+}
diff --git a/src/com/android/dialer/calllog/CallLogAsyncTaskUtil.java b/src/com/android/dialer/calllog/CallLogAsyncTaskUtil.java
index 5a62a7d..aa994d2 100644
--- a/src/com/android/dialer/calllog/CallLogAsyncTaskUtil.java
+++ b/src/com/android/dialer/calllog/CallLogAsyncTaskUtil.java
@@ -156,9 +156,13 @@
boolean isVoicemail = PhoneNumberUtil.isVoicemailNumber(context, accountHandle, number);
boolean shouldLookupNumber =
PhoneNumberUtil.canPlaceCallsTo(number, numberPresentation) && !isVoicemail;
- ContactInfo info = shouldLookupNumber
- ? contactInfoHelper.lookupNumber(number, countryIso)
- : ContactInfo.EMPTY;
+
+ ContactInfo info = ContactInfo.EMPTY;
+ if (shouldLookupNumber) {
+ ContactInfo lookupInfo = contactInfoHelper.lookupNumber(number, countryIso);
+ info = lookupInfo != null ? lookupInfo : ContactInfo.EMPTY;
+ }
+
PhoneCallDetails details = new PhoneCallDetails(
context, number, numberPresentation, info.formattedNumber, isVoicemail);
diff --git a/src/com/android/dialer/calllog/CallLogFragment.java b/src/com/android/dialer/calllog/CallLogFragment.java
index 8762f18..26e3965 100644
--- a/src/com/android/dialer/calllog/CallLogFragment.java
+++ b/src/com/android/dialer/calllog/CallLogFragment.java
@@ -34,6 +34,7 @@
import android.graphics.Rect;
import android.os.Bundle;
import android.os.Handler;
+import android.os.Message;
import android.provider.CallLog;
import android.provider.CallLog.Calls;
import android.provider.ContactsContract;
@@ -89,6 +90,10 @@
private static final int READ_CALL_LOG_PERMISSION_REQUEST_CODE = 1;
+ private static final int EVENT_UPDATE_DISPLAY = 1;
+
+ private static final long MILLIS_IN_MINUTE = 60 * 1000;
+
private RecyclerView mRecyclerView;
private LinearLayoutManager mLayoutManager;
private CallLogAdapter mAdapter;
@@ -106,6 +111,18 @@
private boolean mCallLogFetched;
private boolean mVoicemailStatusFetched;
+ private final Handler mDisplayUpdateHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case EVENT_UPDATE_DISPLAY:
+ refreshData();
+ rescheduleDisplayUpdate();
+ break;
+ }
+ }
+ };
+
private final Handler mHandler = new Handler();
private class CustomContentObserver extends ContentObserver {
@@ -343,10 +360,14 @@
mHasReadCallLogPermission = hasReadCallLogPermission;
refreshData();
mAdapter.startCache();
+
+ rescheduleDisplayUpdate();
}
@Override
public void onPause() {
+ cancelDisplayUpdate();
+
if (mVoicemailPlaybackPresenter != null) {
mVoicemailPlaybackPresenter.onPause();
}
@@ -517,4 +538,25 @@
}
}
}
+
+ /**
+ * Schedules an update to the relative call times (X mins ago).
+ */
+ private void rescheduleDisplayUpdate() {
+ if (!mDisplayUpdateHandler.hasMessages(EVENT_UPDATE_DISPLAY)) {
+ long time = System.currentTimeMillis();
+ // This value allows us to change the display relatively close to when the time changes
+ // from one minute to the next.
+ long millisUtilNextMinute = MILLIS_IN_MINUTE - (time % MILLIS_IN_MINUTE);
+ mDisplayUpdateHandler.sendEmptyMessageDelayed(
+ EVENT_UPDATE_DISPLAY, millisUtilNextMinute);
+ }
+ }
+
+ /**
+ * Cancels any pending update requests to update the relative call times (X mins ago).
+ */
+ private void cancelDisplayUpdate() {
+ mDisplayUpdateHandler.removeMessages(EVENT_UPDATE_DISPLAY);
+ }
}
diff --git a/src/com/android/dialer/calllog/IntentProvider.java b/src/com/android/dialer/calllog/IntentProvider.java
index a11d00b..773436b 100644
--- a/src/com/android/dialer/calllog/IntentProvider.java
+++ b/src/com/android/dialer/calllog/IntentProvider.java
@@ -21,17 +21,17 @@
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
-import android.provider.CallLog.Calls;
import android.provider.ContactsContract;
import android.telecom.PhoneAccountHandle;
+import com.android.contacts.common.CallUtil;
import com.android.contacts.common.model.Contact;
import com.android.contacts.common.model.ContactLoader;
import com.android.dialer.CallDetailActivity;
-import com.android.dialer.DialtactsActivity;
-import com.android.dialer.PhoneCallDetails;
import com.android.dialer.util.IntentUtil;
+import com.android.dialer.util.IntentUtil.CallIntentBuilder;
import com.android.dialer.util.TelecomUtil;
+import com.android.incallui.Call.LogState;
import java.util.ArrayList;
@@ -55,7 +55,10 @@
return new IntentProvider() {
@Override
public Intent getIntent(Context context) {
- return IntentUtil.getCallIntent(number, accountHandle);
+ return new CallIntentBuilder(number)
+ .setPhoneAccountHandle(accountHandle)
+ .setCallInitiationType(LogState.INITIATION_CALL_LOG)
+ .build();
}
};
}
@@ -69,7 +72,11 @@
return new IntentProvider() {
@Override
public Intent getIntent(Context context) {
- return IntentUtil.getVideoCallIntent(number, accountHandle);
+ return new CallIntentBuilder(number)
+ .setPhoneAccountHandle(accountHandle)
+ .setCallInitiationType(LogState.INITIATION_CALL_LOG)
+ .setIsVideoCall(true)
+ .build();
}
};
}
@@ -78,7 +85,9 @@
return new IntentProvider() {
@Override
public Intent getIntent(Context context) {
- return IntentUtil.getVoicemailIntent();
+ return new CallIntentBuilder(CallUtil.getVoicemailUri())
+ .setCallInitiationType(LogState.INITIATION_CALL_LOG)
+ .build();
}
};
}
diff --git a/src/com/android/dialer/dialpad/DialpadFragment.java b/src/com/android/dialer/dialpad/DialpadFragment.java
index 0bbf802..d2628da 100644
--- a/src/com/android/dialer/dialpad/DialpadFragment.java
+++ b/src/com/android/dialer/dialpad/DialpadFragment.java
@@ -81,7 +81,8 @@
import com.android.dialer.SpecialCharSequenceMgr;
import com.android.dialer.calllog.PhoneAccountUtils;
import com.android.dialer.util.DialerUtils;
-import com.android.dialer.util.IntentUtil;
+import com.android.dialer.util.IntentUtil.CallIntentBuilder;
+import com.android.incallui.Call.LogState;
import com.android.phone.common.CallLogAsync;
import com.android.phone.common.animation.AnimUtils;
import com.android.phone.common.dialpad.DialpadKeyButton;
@@ -1044,7 +1045,10 @@
}
public void callVoicemail() {
- DialerUtils.startActivityWithErrorToast(getActivity(), IntentUtil.getVoicemailIntent());
+ DialerUtils.startActivityWithErrorToast(getActivity(),
+ new CallIntentBuilder(CallUtil.getVoicemailUri())
+ .setCallInitiationType(LogState.INITIATION_DIALPAD)
+ .build());
hideAndClearDialpad(false);
}
@@ -1140,9 +1144,9 @@
// Clear the digits just in case.
clearDialpad();
} else {
- final Intent intent = IntentUtil.getCallIntent(number,
- (getActivity() instanceof DialtactsActivity ?
- ((DialtactsActivity) getActivity()).getCallOrigin() : null));
+ final Intent intent = new CallIntentBuilder(number).
+ setCallInitiationType(LogState.INITIATION_DIALPAD)
+ .build();
DialerUtils.startActivityWithErrorToast(getActivity(), intent);
hideAndClearDialpad(false);
}
@@ -1667,7 +1671,7 @@
}
private Intent newFlashIntent() {
- final Intent intent = IntentUtil.getCallIntent(EMPTY_NUMBER);
+ final Intent intent = new CallIntentBuilder(EMPTY_NUMBER).build();
intent.putExtra(EXTRA_SEND_EMPTY_FLASH, true);
return intent;
}
diff --git a/src/com/android/dialer/interactions/PhoneNumberInteraction.java b/src/com/android/dialer/interactions/PhoneNumberInteraction.java
index 6e218c0..96742fd 100644
--- a/src/com/android/dialer/interactions/PhoneNumberInteraction.java
+++ b/src/com/android/dialer/interactions/PhoneNumberInteraction.java
@@ -53,6 +53,8 @@
import com.android.dialer.TransactionSafeActivity;
import com.android.dialer.contact.ContactUpdateService;
import com.android.dialer.util.IntentUtil;
+import com.android.dialer.util.IntentUtil.CallIntentBuilder;
+import com.android.incallui.Call.LogState;
import com.android.dialer.util.DialerUtils;
import com.google.common.annotations.VisibleForTesting;
@@ -188,21 +190,20 @@
private static final String ARG_PHONE_LIST = "phoneList";
private static final String ARG_INTERACTION_TYPE = "interactionType";
- private static final String ARG_CALL_ORIGIN = "callOrigin";
+ private static final String ARG_CALL_INITIATION_TYPE = "callInitiation";
private int mInteractionType;
private ListAdapter mPhonesAdapter;
private List<PhoneItem> mPhoneList;
- private String mCallOrigin;
+ private int mCallInitiationType;
- public static void show(FragmentManager fragmentManager,
- ArrayList<PhoneItem> phoneList, int interactionType,
- String callOrigin) {
+ public static void show(FragmentManager fragmentManager, ArrayList<PhoneItem> phoneList,
+ int interactionType, int callInitiationType) {
PhoneDisambiguationDialogFragment fragment = new PhoneDisambiguationDialogFragment();
Bundle bundle = new Bundle();
bundle.putParcelableArrayList(ARG_PHONE_LIST, phoneList);
- bundle.putSerializable(ARG_INTERACTION_TYPE, interactionType);
- bundle.putString(ARG_CALL_ORIGIN, callOrigin);
+ bundle.putInt(ARG_INTERACTION_TYPE, interactionType);
+ bundle.putInt(ARG_CALL_INITIATION_TYPE, callInitiationType);
fragment.setArguments(bundle);
fragment.show(fragmentManager, TAG);
}
@@ -212,7 +213,7 @@
final Activity activity = getActivity();
mPhoneList = getArguments().getParcelableArrayList(ARG_PHONE_LIST);
mInteractionType = getArguments().getInt(ARG_INTERACTION_TYPE);
- mCallOrigin = getArguments().getString(ARG_CALL_ORIGIN);
+ mCallInitiationType = getArguments().getInt(ARG_CALL_INITIATION_TYPE);
mPhonesAdapter = new PhoneItemAdapter(activity, mPhoneList, mInteractionType);
final LayoutInflater inflater = activity.getLayoutInflater();
@@ -241,7 +242,7 @@
}
PhoneNumberInteraction.performAction(activity, phoneItem.phoneNumber,
- mInteractionType, mCallOrigin);
+ mInteractionType, mCallInitiationType);
} else {
dialog.dismiss();
}
@@ -280,7 +281,7 @@
private final OnDismissListener mDismissListener;
private final int mInteractionType;
- private final String mCallOrigin;
+ private final int mCallInitiationType;
private boolean mUseDefault;
private static final int UNKNOWN_CONTACT_ID = -1;
@@ -297,24 +298,25 @@
@VisibleForTesting
/* package */ PhoneNumberInteraction(Context context, int interactionType,
DialogInterface.OnDismissListener dismissListener) {
- this(context, interactionType, dismissListener, null);
+ this(context, interactionType, dismissListener, LogState.INITIATION_UNKNOWN);
}
private PhoneNumberInteraction(Context context, int interactionType,
- DialogInterface.OnDismissListener dismissListener, String callOrigin) {
+ DialogInterface.OnDismissListener dismissListener, int callInitiationType) {
mContext = context;
mInteractionType = interactionType;
mDismissListener = dismissListener;
- mCallOrigin = callOrigin;
+ mCallInitiationType = callInitiationType;
}
private void performAction(String phoneNumber) {
- PhoneNumberInteraction.performAction(mContext, phoneNumber, mInteractionType, mCallOrigin);
+ PhoneNumberInteraction.performAction(mContext, phoneNumber, mInteractionType,
+ mCallInitiationType);
}
private static void performAction(
Context context, String phoneNumber, int interactionType,
- String callOrigin) {
+ int callInitiationType) {
Intent intent;
switch (interactionType) {
case ContactDisplayUtils.INTERACTION_SMS:
@@ -322,7 +324,9 @@
Intent.ACTION_SENDTO, Uri.fromParts("sms", phoneNumber, null));
break;
default:
- intent = IntentUtil.getCallIntent(phoneNumber, callOrigin);
+ intent = new CallIntentBuilder(phoneNumber)
+ .setCallInitiationType(callInitiationType)
+ .build();
break;
}
DialerUtils.startActivityWithErrorToast(context, intent);
@@ -447,54 +451,15 @@
}
/**
- * Start call action using given contact Uri. If there are multiple candidates for the phone
- * call, dialog is automatically shown and the user is asked to choose one.
- *
* @param activity that is calling this interaction. This must be of type
* {@link TransactionSafeActivity} because we need to check on the activity state after the
* phone numbers have been queried for.
- * @param uri contact Uri (built from {@link Contacts#CONTENT_URI}) or data Uri
- * (built from {@link Data#CONTENT_URI}). Contact Uri may show the disambiguation dialog while
- * data Uri won't.
- */
- public static void startInteractionForPhoneCall(TransactionSafeActivity activity, Uri uri) {
- (new PhoneNumberInteraction(activity, ContactDisplayUtils.INTERACTION_CALL, null))
- .startInteraction(uri, true);
- }
-
- /**
- * Start call action using given contact Uri. If there are multiple candidates for the phone
- * call, dialog is automatically shown and the user is asked to choose one.
- *
- * @param activity that is calling this interaction. This must be of type
- * {@link TransactionSafeActivity} because we need to check on the activity state after the
- * phone numbers have been queried for.
- * @param uri contact Uri (built from {@link Contacts#CONTENT_URI}) or data Uri
- * (built from {@link Data#CONTENT_URI}). Contact Uri may show the disambiguation dialog while
- * data Uri won't.
- * @param useDefault Whether or not to use the primary(default) phone number. If true, the
- * primary phone number will always be used by default if one is available. If false, a
- * disambiguation dialog will be shown regardless of whether or not a primary phone number
- * is available.
+ * @param callInitiationType Indicates the UI affordance that was used to initiate the call.
*/
public static void startInteractionForPhoneCall(TransactionSafeActivity activity, Uri uri,
- boolean useDefault) {
- (new PhoneNumberInteraction(activity, ContactDisplayUtils.INTERACTION_CALL, null))
- .startInteraction(uri, useDefault);
- }
-
- /**
- * @param activity that is calling this interaction. This must be of type
- * {@link TransactionSafeActivity} because we need to check on the activity state after the
- * phone numbers have been queried for.
- * @param callOrigin If non null, {@link PhoneConstants#EXTRA_CALL_ORIGIN} will be
- * appended to the Intent initiating phone call. See comments in Phone package (PhoneApp)
- * for more detail.
- */
- public static void startInteractionForPhoneCall(TransactionSafeActivity activity, Uri uri,
- String callOrigin) {
- (new PhoneNumberInteraction(activity, ContactDisplayUtils.INTERACTION_CALL, null, callOrigin))
- .startInteraction(uri, true);
+ int callInitiationType) {
+ (new PhoneNumberInteraction(activity, ContactDisplayUtils.INTERACTION_CALL, null,
+ callInitiationType)).startInteraction(uri, true);
}
/**
@@ -521,7 +486,17 @@
@VisibleForTesting
/* package */ void showDisambiguationDialog(ArrayList<PhoneItem> phoneList) {
- PhoneDisambiguationDialogFragment.show(((Activity)mContext).getFragmentManager(),
- phoneList, mInteractionType, mCallOrigin);
+ final Activity activity = (Activity) mContext;
+ if (activity.isDestroyed()) {
+ // Check whether the activity is still running
+ return;
+ }
+ try {
+ PhoneDisambiguationDialogFragment.show(activity.getFragmentManager(),
+ phoneList, mInteractionType, mCallInitiationType);
+ } catch (IllegalStateException e) {
+ // ignore to be safe. Shouldn't happen because we checked the
+ // activity wasn't destroyed, but to be safe.
+ }
}
}
diff --git a/src/com/android/dialer/list/ListsFragment.java b/src/com/android/dialer/list/ListsFragment.java
index 09a4cb2..c80ab42 100644
--- a/src/com/android/dialer/list/ListsFragment.java
+++ b/src/com/android/dialer/list/ListsFragment.java
@@ -176,10 +176,6 @@
Trace.beginSection(TAG + " onCreate");
super.onCreate(savedInstanceState);
- Trace.beginSection(TAG + " getCurrentCountryIso");
- final String currentCountryIso = GeoUtil.getCurrentCountryIso(getActivity());
- Trace.endSection();
-
mVoicemailStatusHelper = new VoicemailStatusHelperImpl();
mHasFetchedVoicemailStatus = false;
@@ -265,7 +261,7 @@
// Try to show the voicemail tab after the voicemail status returns.
mShowVoicemailTabAfterVoicemailStatusIsFetched = true;
}
- } else {
+ } else if (index < getTabCount()){
mViewPager.setCurrentItem(getRtlPosition(index));
}
}
diff --git a/src/com/android/dialer/list/RegularSearchFragment.java b/src/com/android/dialer/list/RegularSearchFragment.java
index c715de8..ec771e8 100644
--- a/src/com/android/dialer/list/RegularSearchFragment.java
+++ b/src/com/android/dialer/list/RegularSearchFragment.java
@@ -15,7 +15,6 @@
*/
package com.android.dialer.list;
-import static android.Manifest.permission.ACCESS_FINE_LOCATION;
import static android.Manifest.permission.READ_CONTACTS;
import android.app.Activity;
@@ -28,7 +27,7 @@
import com.android.contacts.common.util.PermissionsUtil;
import com.android.contacts.commonbind.analytics.AnalyticsUtil;
import com.android.dialerbind.ObjectFactory;
-
+import com.android.incallui.Call.LogState;
import com.android.dialer.R;
import com.android.dialer.service.CachedNumberLookupService;
import com.android.dialer.widget.EmptyContentView;
@@ -119,4 +118,10 @@
}
}
}
+
+ @Override
+ protected int getCallInitiationType(boolean isRemoteDirectory) {
+ return isRemoteDirectory ? LogState.INITIATION_REMOTE_DIRECTORY
+ : LogState.INITIATION_REGULAR_SEARCH;
+ }
}
diff --git a/src/com/android/dialer/list/SearchFragment.java b/src/com/android/dialer/list/SearchFragment.java
index 315cfb9..26ed117 100644
--- a/src/com/android/dialer/list/SearchFragment.java
+++ b/src/com/android/dialer/list/SearchFragment.java
@@ -53,6 +53,7 @@
import com.android.dialer.util.DialerUtils;
import com.android.dialer.util.IntentUtil;
import com.android.dialer.widget.EmptyContentView;
+import com.android.incallui.Call.LogState;
import com.android.phone.common.animation.AnimUtils;
public class SearchFragment extends PhoneNumberPickerFragment {
@@ -250,7 +251,8 @@
number = adapter.getQueryString();
listener = getOnPhoneNumberPickerListener();
if (listener != null && !checkForProhibitedPhoneNumber(number)) {
- listener.onCallNumberDirectly(number);
+ listener.onCallNumberDirectly(number, false /* isVideoCall */,
+ getCallInitiationType(false /* isRemoteDirectory */));
}
break;
case DialerPhoneNumberListAdapter.SHORTCUT_CREATE_NEW_CONTACT:
@@ -275,7 +277,8 @@
number = adapter.getQueryString();
listener = getOnPhoneNumberPickerListener();
if (listener != null && !checkForProhibitedPhoneNumber(number)) {
- listener.onCallNumberDirectly(number, true /* isVideoCall */);
+ listener.onCallNumberDirectly(number, true /* isVideoCall */,
+ getCallInitiationType(false /* isRemoteDirectory */));
}
break;
}
diff --git a/src/com/android/dialer/list/SmartDialSearchFragment.java b/src/com/android/dialer/list/SmartDialSearchFragment.java
index 72d3abf..9aedfe8 100644
--- a/src/com/android/dialer/list/SmartDialSearchFragment.java
+++ b/src/com/android/dialer/list/SmartDialSearchFragment.java
@@ -30,6 +30,7 @@
import com.android.dialer.dialpad.SmartDialCursorLoader;
import com.android.dialer.R;
import com.android.dialer.widget.EmptyContentView;
+import com.android.incallui.Call.LogState;
import java.util.ArrayList;
@@ -116,6 +117,11 @@
}
}
+ @Override
+ protected int getCallInitiationType(boolean isRemoteDirectory) {
+ return LogState.INITIATION_SMART_DIAL;
+ }
+
public boolean isShowingPermissionRequest() {
return mEmptyView != null && mEmptyView.isShowingContent();
}
diff --git a/src/com/android/dialer/list/SpeedDialFragment.java b/src/com/android/dialer/list/SpeedDialFragment.java
index 324caef..7b72e45 100644
--- a/src/com/android/dialer/list/SpeedDialFragment.java
+++ b/src/com/android/dialer/list/SpeedDialFragment.java
@@ -54,6 +54,7 @@
import com.android.dialer.R;
import com.android.dialer.util.DialerUtils;
import com.android.dialer.widget.EmptyContentView;
+import com.android.incallui.Call.LogState;
import java.util.ArrayList;
import java.util.HashMap;
@@ -115,14 +116,16 @@
@Override
public void onContactSelected(Uri contactUri, Rect targetRect) {
if (mPhoneNumberPickerActionListener != null) {
- mPhoneNumberPickerActionListener.onPickPhoneNumberAction(contactUri);
+ mPhoneNumberPickerActionListener.onPickPhoneNumberAction(contactUri,
+ LogState.INITIATION_SPEED_DIAL);
}
}
@Override
public void onCallNumberDirectly(String phoneNumber) {
if (mPhoneNumberPickerActionListener != null) {
- mPhoneNumberPickerActionListener.onCallNumberDirectly(phoneNumber);
+ mPhoneNumberPickerActionListener.onCallNumberDirectly(phoneNumber,
+ false /* isVideoCall */, LogState.INITIATION_SPEED_DIAL);
}
}
diff --git a/src/com/android/dialer/logging/Logger.java b/src/com/android/dialer/logging/Logger.java
new file mode 100644
index 0000000..3007077
--- /dev/null
+++ b/src/com/android/dialer/logging/Logger.java
@@ -0,0 +1,58 @@
+/*
+ * 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.logging;
+
+import android.app.Activity;
+
+import com.android.dialerbind.ObjectFactory;
+import com.android.incallui.Call;
+
+public abstract class Logger {
+
+ public static Logger getInstance() {
+ return ObjectFactory.getLoggerInstance();
+ }
+
+ /**
+ * Logs a call event. PII like the call's number or caller details should never be logged.
+ *
+ * @param call to log.
+ */
+ public static void logCall(Call call) {
+ final Logger logger = getInstance();
+ if (logger != null) {
+ logger.logCallImpl(call);
+ }
+ }
+
+ /**
+ * Logs an event indicating that a screen/fragment was displayed.
+ *
+ * @param fragmentName of the displayed fragment.
+ * @param activity Parent activity of the fragment.
+ * @param tag Optional string used to provide additional information about the fragment.
+ */
+ public static void logScreenView(String fragmentName, Activity activity, String tag) {
+ final Logger logger = getInstance();
+ if (logger != null) {
+ logger.logScreenViewImpl(fragmentName, activity, tag);
+ }
+ }
+
+ public abstract void logCallImpl(Call call);
+ public abstract void logScreenViewImpl(String fragmentName, Activity activity, String tag);
+}
diff --git a/src/com/android/dialer/util/DialerUtils.java b/src/com/android/dialer/util/DialerUtils.java
index e25ada5..fbe14ba 100644
--- a/src/com/android/dialer/util/DialerUtils.java
+++ b/src/com/android/dialer/util/DialerUtils.java
@@ -81,7 +81,14 @@
// All dialer-initiated calls should pass the touch point to the InCallUI
Point touchPoint = TouchPointManager.getInstance().getPoint();
if (touchPoint.x != 0 || touchPoint.y != 0) {
- Bundle extras = new Bundle();
+ Bundle extras;
+ // Make sure to not accidentally clobber any existing extras
+ if (intent.hasExtra(TelecomManager.EXTRA_OUTGOING_CALL_EXTRAS)) {
+ extras = intent.getParcelableExtra(
+ TelecomManager.EXTRA_OUTGOING_CALL_EXTRAS);
+ } else {
+ extras = new Bundle();
+ }
extras.putParcelable(TouchPointManager.TOUCH_POINT, touchPoint);
intent.putExtra(TelecomManager.EXTRA_OUTGOING_CALL_EXTRAS, extras);
}
diff --git a/src/com/android/dialer/util/IntentUtil.java b/src/com/android/dialer/util/IntentUtil.java
index 2ce3bd1..5a4a80b 100644
--- a/src/com/android/dialer/util/IntentUtil.java
+++ b/src/com/android/dialer/util/IntentUtil.java
@@ -18,14 +18,13 @@
import android.content.Intent;
import android.net.Uri;
+import android.os.Bundle;
import android.provider.ContactsContract;
-import android.telecom.PhoneAccount;
import android.telecom.PhoneAccountHandle;
import android.telecom.TelecomManager;
import android.telecom.VideoProfile;
import com.android.contacts.common.CallUtil;
-import com.android.phone.common.PhoneConstants;
/**
* Utilities for creation of intents in Dialer, such as {@link Intent#ACTION_CALL}.
@@ -36,108 +35,65 @@
private static final String SMS_URI_PREFIX = "sms:";
private static final int NO_PHONE_TYPE = -1;
- /**
- * Return an Intent for making a phone call. Scheme (e.g. tel, sip) will be determined
- * automatically.
- */
- public static Intent getCallIntent(String number) {
- return getCallIntent(number, null, null);
+ public static final String EXTRA_CALL_INITIATION_TYPE
+ = "com.android.dialer.EXTRA_CALL_INITIATION_TYPE";
+
+ public static class CallIntentBuilder {
+ private Uri mUri;
+ private int mCallInitiationType;
+ private PhoneAccountHandle mPhoneAccountHandle;
+ private boolean mIsVideoCall = false;
+
+ public CallIntentBuilder(Uri uri) {
+ mUri = uri;
+ }
+
+ public CallIntentBuilder(String number) {
+ this(CallUtil.getCallUri(number));
+ }
+
+ public CallIntentBuilder setCallInitiationType(int initiationType) {
+ mCallInitiationType = initiationType;
+ return this;
+ }
+
+ public CallIntentBuilder setPhoneAccountHandle(PhoneAccountHandle accountHandle) {
+ mPhoneAccountHandle = accountHandle;
+ return this;
+ }
+
+ public CallIntentBuilder setIsVideoCall(boolean isVideoCall) {
+ mIsVideoCall = isVideoCall;
+ return this;
+ }
+
+ public Intent build() {
+ return getCallIntent(
+ mUri,
+ mPhoneAccountHandle,
+ mIsVideoCall ? VideoProfile.STATE_BIDIRECTIONAL : VideoProfile.STATE_AUDIO_ONLY,
+ mCallInitiationType);
+ }
}
/**
- * Return an Intent for making a phone call. A given Uri will be used as is (without any
- * sanity check).
- */
- public static Intent getCallIntent(Uri uri) {
- return getCallIntent(uri, null, null);
- }
-
- /**
- * A variant of {@link #getCallIntent(String)} but also accept a call origin.
- * For more information about call origin, see comments in Phone package (PhoneApp).
- */
- public static Intent getCallIntent(String number, String callOrigin) {
- return getCallIntent(CallUtil.getCallUri(number), callOrigin, null);
- }
-
- /**
- * A variant of {@link #getCallIntent(String)} but also include {@code Account}.
- */
- public static Intent getCallIntent(String number, PhoneAccountHandle accountHandle) {
- return getCallIntent(number, null, accountHandle);
- }
-
- /**
- * A variant of {@link #getCallIntent(android.net.Uri)} but also include {@code Account}.
- */
- public static Intent getCallIntent(Uri uri, PhoneAccountHandle accountHandle) {
- return getCallIntent(uri, null, accountHandle);
- }
-
- /**
- * A variant of {@link #getCallIntent(String, String)} but also include {@code Account}.
+ * Create a call intent that can be used to place a call.
+ *
+ * @param uri Address to place the call to.
+ * @param accountHandle {@link PhoneAccountHandle} to place the call with.
+ * @param videoState Initial video state of the call.
+ * @param callIntiationType The UI affordance the call was initiated by.
+ * @return Call intent with provided extras and data.
*/
public static Intent getCallIntent(
- String number, String callOrigin, PhoneAccountHandle accountHandle) {
- return getCallIntent(CallUtil.getCallUri(number), callOrigin, accountHandle);
- }
-
- /**
- * A variant of {@link #getCallIntent(android.net.Uri)} but also accept a call
- * origin and {@code Account}.
- * For more information about call origin, see comments in Phone package (PhoneApp).
- */
- public static Intent getCallIntent(
- Uri uri, String callOrigin, PhoneAccountHandle accountHandle) {
- return getCallIntent(uri, callOrigin, accountHandle,
- VideoProfile.STATE_AUDIO_ONLY);
- }
-
- /**
- * A variant of {@link #getCallIntent(String, String)} for starting a video call.
- */
- public static Intent getVideoCallIntent(String number, String callOrigin) {
- return getCallIntent(CallUtil.getCallUri(number), callOrigin, null,
- VideoProfile.STATE_BIDIRECTIONAL);
- }
-
- /**
- * A variant of {@link #getCallIntent(String, String, android.telecom.PhoneAccountHandle)} for
- * starting a video call.
- */
- public static Intent getVideoCallIntent(
- String number, String callOrigin, PhoneAccountHandle accountHandle) {
- return getCallIntent(CallUtil.getCallUri(number), callOrigin, accountHandle,
- VideoProfile.STATE_BIDIRECTIONAL);
- }
-
- /**
- * A variant of {@link #getCallIntent(String, String, android.telecom.PhoneAccountHandle)} for
- * starting a video call.
- */
- public static Intent getVideoCallIntent(String number, PhoneAccountHandle accountHandle) {
- return getVideoCallIntent(number, null, accountHandle);
- }
-
- /**
- * A variant of {@link #getCallIntent(android.net.Uri)} for calling Voicemail.
- */
- public static Intent getVoicemailIntent() {
- return getCallIntent(Uri.fromParts(PhoneAccount.SCHEME_VOICEMAIL, "", null));
- }
-
- /**
- * A variant of {@link #getCallIntent(android.net.Uri)} but also accept a call
- * origin and {@code Account} and {@code VideoCallProfile} state.
- * For more information about call origin, see comments in Phone package (PhoneApp).
- */
- public static Intent getCallIntent(
- Uri uri, String callOrigin, PhoneAccountHandle accountHandle, int videoState) {
+ Uri uri, PhoneAccountHandle accountHandle, int videoState, int callIntiationType) {
final Intent intent = new Intent(CALL_ACTION, uri);
intent.putExtra(TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE, videoState);
- if (callOrigin != null) {
- intent.putExtra(PhoneConstants.EXTRA_CALL_ORIGIN, callOrigin);
- }
+
+ final Bundle b = new Bundle();
+ b.putInt(EXTRA_CALL_INITIATION_TYPE, callIntiationType);
+ intent.putExtra(TelecomManager.EXTRA_OUTGOING_CALL_EXTRAS, b);
+
if (accountHandle != null) {
intent.putExtra(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, accountHandle);
}
diff --git a/src/com/android/dialer/voicemail/VoicemailAudioManager.java b/src/com/android/dialer/voicemail/VoicemailAudioManager.java
new file mode 100644
index 0000000..e64e180
--- /dev/null
+++ b/src/com/android/dialer/voicemail/VoicemailAudioManager.java
@@ -0,0 +1,73 @@
+/*
+ * 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.voicemail;
+
+import android.content.Context;
+import android.media.AudioManager;
+import android.media.AudioManager.OnAudioFocusChangeListener;
+import android.util.Log;
+
+import java.util.concurrent.RejectedExecutionException;
+
+/**
+ * This class manages all audio changes for voicemail playback.
+ */
+final class VoicemailAudioManager implements OnAudioFocusChangeListener {
+ private static final String TAG = VoicemailAudioManager.class.getSimpleName();
+
+ public static final int PLAYBACK_STREAM = AudioManager.STREAM_VOICE_CALL;
+
+ private AudioManager mAudioManager;
+ private VoicemailPlaybackPresenter mVoicemailPlaybackPresenter;
+
+ public VoicemailAudioManager(Context context,
+ VoicemailPlaybackPresenter voicemailPlaybackPresenter) {
+ mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
+ mVoicemailPlaybackPresenter = voicemailPlaybackPresenter;
+ }
+
+ public void requestAudioFocus() {
+ int result = mAudioManager.requestAudioFocus(
+ this,
+ PLAYBACK_STREAM,
+ AudioManager.AUDIOFOCUS_GAIN_TRANSIENT);
+ if (result != AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
+ throw new RejectedExecutionException("Could not capture audio focus.");
+ }
+ }
+
+ public void abandonAudioFocus() {
+ mAudioManager.abandonAudioFocus(this);
+ }
+
+ @Override
+ public void onAudioFocusChange(int focusChange) {
+ Log.d(TAG, "onAudioFocusChange: focusChange=" + focusChange);
+ mVoicemailPlaybackPresenter.onAudioFocusChange(focusChange == AudioManager.AUDIOFOCUS_GAIN);
+ }
+
+ public void turnOnSpeaker(boolean on) {
+ if (mAudioManager.isSpeakerphoneOn() != on) {
+ Log.i(TAG, "turning speaker phone on: " + on);
+ mAudioManager.setSpeakerphoneOn(on);
+ }
+ }
+
+ public boolean isSpeakerphoneOn() {
+ return mAudioManager.isSpeakerphoneOn();
+ }
+}
\ No newline at end of file
diff --git a/src/com/android/dialer/voicemail/VoicemailPlaybackLayout.java b/src/com/android/dialer/voicemail/VoicemailPlaybackLayout.java
index 69c075f..14c5473 100644
--- a/src/com/android/dialer/voicemail/VoicemailPlaybackLayout.java
+++ b/src/com/android/dialer/voicemail/VoicemailPlaybackLayout.java
@@ -42,6 +42,7 @@
import com.android.dialer.R;
import com.android.dialer.calllog.CallLogAsyncTaskUtil;
+import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Preconditions;
import java.util.concurrent.TimeUnit;
@@ -159,7 +160,7 @@
@Override
public void onClick(View v) {
if (mPresenter != null) {
- onSpeakerphoneOn(!mPresenter.isSpeakerphoneOn());
+ mPresenter.toggleSpeakerphone();
}
}
};
@@ -286,10 +287,6 @@
mStartStopButton.setImageResource(R.drawable.ic_pause);
- if (mPresenter != null) {
- onSpeakerphoneOn(mPresenter.isSpeakerphoneOn());
- }
-
if (mPositionUpdater != null) {
mPositionUpdater.stopUpdating();
mPositionUpdater = null;
@@ -321,10 +318,6 @@
}
public void onSpeakerphoneOn(boolean on) {
- if (mPresenter != null) {
- mPresenter.setSpeakerphoneOn(on);
- }
-
if (on) {
mPlaybackSpeakerphone.setImageResource(R.drawable.ic_volume_up_24dp);
// Speaker is now on, tapping button will turn it off.
@@ -373,7 +366,6 @@
@Override
public void disableUiElements() {
mStartStopButton.setEnabled(false);
- mPlaybackSpeakerphone.setEnabled(false);
mPlaybackSeek.setProgress(0);
mPlaybackSeek.setEnabled(false);
@@ -384,7 +376,6 @@
@Override
public void enableUiElements() {
mStartStopButton.setEnabled(true);
- mPlaybackSpeakerphone.setEnabled(true);
mPlaybackSeek.setEnabled(true);
mPositionText.setVisibility(View.VISIBLE);
@@ -421,4 +412,9 @@
}
return String.format("%02d:%02d", minutes, seconds);
}
+
+ @VisibleForTesting
+ public String getStateText() {
+ return mStateText.getText().toString();
+ }
}
diff --git a/src/com/android/dialer/voicemail/VoicemailPlaybackPresenter.java b/src/com/android/dialer/voicemail/VoicemailPlaybackPresenter.java
index 3f5a489..95622bf 100644
--- a/src/com/android/dialer/voicemail/VoicemailPlaybackPresenter.java
+++ b/src/com/android/dialer/voicemail/VoicemailPlaybackPresenter.java
@@ -23,7 +23,6 @@
import android.database.ContentObserver;
import android.database.Cursor;
import android.media.AudioManager;
-import android.media.AudioManager.OnAudioFocusChangeListener;
import android.media.MediaPlayer;
import android.net.Uri;
import android.os.AsyncTask;
@@ -60,7 +59,7 @@
* {@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}. This
+ * instance can be reused for different such layouts, using {@link #setPlaybackView}. 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
@@ -68,8 +67,7 @@
*/
@NotThreadSafe
@VisibleForTesting
-public class VoicemailPlaybackPresenter
- implements OnAudioFocusChangeListener, MediaPlayer.OnPreparedListener,
+public class VoicemailPlaybackPresenter implements MediaPlayer.OnPreparedListener,
MediaPlayer.OnCompletionListener, MediaPlayer.OnErrorListener {
private static final String TAG = VoicemailPlaybackPresenter.class.getSimpleName();
@@ -105,7 +103,6 @@
VoicemailContract.Voicemails.HAS_CONTENT,
};
- public static final int PLAYBACK_STREAM = AudioManager.STREAM_VOICE_CALL;
private static final int NUMBER_OF_THREADS_IN_POOL = 2;
// Time to wait for content to be fetched before timing out.
private static final long FETCH_CONTENT_TIMEOUT_MS = 20000;
@@ -120,6 +117,8 @@
// If present in the saved instance bundle, indicates where to set the playback slider.
private static final String CLIP_POSITION_KEY =
VoicemailPlaybackPresenter.class.getName() + ".CLIP_POSITION_KEY";
+ private static final String IS_SPEAKERPHONE_ON_KEY =
+ VoicemailPlaybackPresenter.class.getName() + ".IS_SPEAKER_PHONE_ON";
/**
* The most recently cached duration. We cache this since we don't want to keep requesting it
@@ -141,6 +140,7 @@
// MediaPlayer crashes on some method calls if not prepared but does not have a method which
// exposes its prepared state. Store this locally, so we can check and prevent crashes.
private boolean mIsPrepared;
+ private boolean mIsSpeakerphoneOn;
private boolean mShouldResumePlaybackAfterSeeking;
private int mInitialOrientation;
@@ -156,7 +156,7 @@
private FetchResultHandler mFetchResultHandler;
private Handler mHandler = new Handler();
private PowerManager.WakeLock mProximityWakeLock;
- private AudioManager mAudioManager;
+ private VoicemailAudioManager mVoicemailAudioManager;
private OnVoicemailDeletedListener mOnVoicemailDeletedListener;
@@ -185,7 +185,7 @@
private VoicemailPlaybackPresenter(Activity activity) {
Context context = activity.getApplicationContext();
mAsyncTaskExecutor = AsyncTaskExecutors.createAsyncTaskExecutor();
- mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
+ mVoicemailAudioManager = new VoicemailAudioManager(context, this);
PowerManager powerManager =
(PowerManager) context.getSystemService(Context.POWER_SERVICE);
@@ -203,7 +203,7 @@
mContext = activity;
mInitialOrientation = mContext.getResources().getConfiguration().orientation;
- mActivity.setVolumeControlStream(VoicemailPlaybackPresenter.PLAYBACK_STREAM);
+ mActivity.setVolumeControlStream(VoicemailAudioManager.PLAYBACK_STREAM);
if (savedInstanceState != null) {
// Restores playback state when activity is recreated, such as after rotation.
@@ -211,6 +211,7 @@
mIsPrepared = savedInstanceState.getBoolean(IS_PREPARED_KEY);
mPosition = savedInstanceState.getInt(CLIP_POSITION_KEY, 0);
mIsPlaying = savedInstanceState.getBoolean(IS_PLAYING_STATE_KEY, false);
+ mIsSpeakerphoneOn = savedInstanceState.getBoolean(IS_SPEAKERPHONE_ON_KEY, false);
}
if (mMediaPlayer == null) {
@@ -228,6 +229,7 @@
outState.putBoolean(IS_PREPARED_KEY, mIsPrepared);
outState.putInt(CLIP_POSITION_KEY, mView.getDesiredClipPosition());
outState.putBoolean(IS_PLAYING_STATE_KEY, mIsPlaying);
+ outState.putBoolean(IS_SPEAKERPHONE_ON_KEY, mIsSpeakerphoneOn);
}
}
@@ -239,16 +241,21 @@
mView = view;
mView.setPresenter(this, voicemailUri);
+ // Handles cases where the same entry is binded again when scrolling in list, or where
+ // the MediaPlayer was retained after an orientation change.
if (mMediaPlayer != null && mIsPrepared && voicemailUri.equals(mVoicemailUri)) {
- // Handles case where MediaPlayer was retained after an orientation change.
onPrepared(mMediaPlayer);
- mView.onSpeakerphoneOn(isSpeakerphoneOn());
} else {
if (!voicemailUri.equals(mVoicemailUri)) {
+ mVoicemailUri = voicemailUri;
mPosition = 0;
+ // Default to earpiece.
+ setSpeakerphoneOn(false);
+ } else {
+ // Update the view to the current speakerphone state.
+ mView.onSpeakerphoneOn(mIsSpeakerphoneOn);
}
- mVoicemailUri = voicemailUri;
mDuration.set(0);
if (startPlayingImmediately) {
@@ -258,9 +265,6 @@
mIsPlaying = startPlayingImmediately;
checkForContent();
}
-
- // Default to earpiece.
- mView.onSpeakerphoneOn(false);
}
}
@@ -496,7 +500,7 @@
mMediaPlayer.reset();
mMediaPlayer.setDataSource(mContext, mVoicemailUri);
- mMediaPlayer.setAudioStreamType(PLAYBACK_STREAM);
+ mMediaPlayer.setAudioStreamType(VoicemailAudioManager.PLAYBACK_STREAM);
mMediaPlayer.prepareAsync();
} catch (IOException e) {
handleError(e);
@@ -570,15 +574,22 @@
}
}
- @Override
- public void onAudioFocusChange(int focusChange) {
- 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 (!mIsPlaying && focusChange == AudioManager.AUDIOFOCUS_GAIN) {
+ /**
+ * Only play voicemail when audio focus is granted. When it is lost (usually by another
+ * application requesting focus), pause playback.
+ *
+ * @param gainedFocus {@code true} if the audio focus was gained, {@code} false otherwise.
+ */
+ public void onAudioFocusChange(boolean gainedFocus) {
+ if (mIsPlaying == gainedFocus) {
+ // Nothing new here, just exit.
+ return;
+ }
+
+ if (!mIsPlaying) {
resumePlayback();
+ } else {
+ pausePlayback();
}
}
@@ -595,7 +606,6 @@
// If we haven't downloaded the voicemail yet, attempt to download it.
checkForContent();
mIsPlaying = true;
-
return;
}
@@ -608,15 +618,10 @@
try {
// Grab audio focus.
- int result = mAudioManager.requestAudioFocus(
- this,
- PLAYBACK_STREAM,
- AudioManager.AUDIOFOCUS_GAIN_TRANSIENT);
- if (result != AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
- throw new RejectedExecutionException("Could not capture audio focus.");
- }
-
// Can throw RejectedExecutionException.
+ mVoicemailAudioManager.requestAudioFocus();
+
+ setSpeakerphoneOn(mIsSpeakerphoneOn);
mMediaPlayer.start();
} catch (RejectedExecutionException e) {
handleError(e);
@@ -625,11 +630,6 @@
Log.d(TAG, "Resumed playback at " + mPosition + ".");
mView.onPlaybackStarted(mDuration.get(), getScheduledExecutorServiceInstance());
- if (isSpeakerphoneOn()) {
- mActivity.getWindow().addFlags(LayoutParams.FLAG_KEEP_SCREEN_ON);
- } else {
- enableProximitySensor();
- }
}
/**
@@ -653,7 +653,8 @@
if (mView != null) {
mView.onPlaybackStopped();
}
- mAudioManager.abandonAudioFocus(this);
+
+ mVoicemailAudioManager.abandonAudioFocus();
if (mActivity != null) {
mActivity.getWindow().clearFlags(LayoutParams.FLAG_KEEP_SCREEN_ON);
@@ -681,7 +682,7 @@
}
private void enableProximitySensor() {
- if (mProximityWakeLock == null || isSpeakerphoneOn() || !mIsPrepared
+ if (mProximityWakeLock == null || mIsSpeakerphoneOn || !mIsPrepared
|| mMediaPlayer == null || !mMediaPlayer.isPlaying()) {
return;
}
@@ -707,24 +708,32 @@
}
}
- public void setSpeakerphoneOn(boolean on) {
- mAudioManager.setSpeakerphoneOn(on);
-
- if (on) {
- disableProximitySensor(false /* waitForFarState */);
- if (mIsPrepared && mMediaPlayer != null && mMediaPlayer.isPlaying()) {
- mActivity.getWindow().addFlags(LayoutParams.FLAG_KEEP_SCREEN_ON);
- }
- } else {
- enableProximitySensor();
- if (mActivity != null) {
- mActivity.getWindow().clearFlags(LayoutParams.FLAG_KEEP_SCREEN_ON);
- }
- }
+ public void toggleSpeakerphone() {
+ setSpeakerphoneOn(!mIsSpeakerphoneOn);
}
- public boolean isSpeakerphoneOn() {
- return mAudioManager.isSpeakerphoneOn();
+ private void setSpeakerphoneOn(boolean on) {
+ mView.onSpeakerphoneOn(on);
+ if (mIsSpeakerphoneOn == on) {
+ return;
+ }
+
+ mIsSpeakerphoneOn = on;
+ mVoicemailAudioManager.turnOnSpeaker(on);
+
+ if (mIsPlaying) {
+ if (on) {
+ disableProximitySensor(false /* waitForFarState */);
+ if (mIsPrepared && mMediaPlayer != null && mMediaPlayer.isPlaying()) {
+ mActivity.getWindow().addFlags(LayoutParams.FLAG_KEEP_SCREEN_ON);
+ }
+ } else {
+ enableProximitySensor();
+ if (mActivity != null) {
+ mActivity.getWindow().clearFlags(LayoutParams.FLAG_KEEP_SCREEN_ON);
+ }
+ }
+ }
}
public void setOnVoicemailDeletedListener(OnVoicemailDeletedListener listener) {
@@ -767,4 +776,14 @@
public boolean isPlaying() {
return mIsPlaying;
}
+
+ @VisibleForTesting
+ public boolean isSpeakerphoneOn() {
+ return mIsSpeakerphoneOn;
+ }
+
+ @VisibleForTesting
+ public void clearInstance() {
+ sInstance = null;
+ }
}
diff --git a/src/com/android/dialerbind/ObjectFactory.java b/src/com/android/dialerbind/ObjectFactory.java
index d67ada0..c64afe8 100644
--- a/src/com/android/dialerbind/ObjectFactory.java
+++ b/src/com/android/dialerbind/ObjectFactory.java
@@ -22,6 +22,7 @@
import com.android.dialer.calllog.CallLogAdapter;
import com.android.dialer.calllog.ContactInfoHelper;
+import com.android.dialer.logging.Logger;
import com.android.dialer.service.CachedNumberLookupService;
import com.android.dialer.voicemail.VoicemailPlaybackPresenter;
@@ -59,4 +60,9 @@
voicemailPlaybackPresenter,
isCallLogActivity);
}
+
+ public static Logger getLoggerInstance() {
+ // no-op
+ return null;
+ }
}
diff --git a/tests/src/com/android/dialer/calllog/CallLogAdapterTest.java b/tests/src/com/android/dialer/calllog/CallLogAdapterTest.java
index de6f198..83d098f 100644
--- a/tests/src/com/android/dialer/calllog/CallLogAdapterTest.java
+++ b/tests/src/com/android/dialer/calllog/CallLogAdapterTest.java
@@ -22,14 +22,16 @@
import android.database.MatrixCursor;
import android.net.Uri;
import android.provider.CallLog.Calls;
+import android.provider.ContactsContract.CommonDataKinds.Phone;
import android.provider.VoicemailContract;
import android.support.v7.widget.RecyclerView.ViewHolder;
+import android.telephony.PhoneNumberUtils;
import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.SmallTest;
import android.test.suitebuilder.annotation.MediumTest;
+import android.test.suitebuilder.annotation.LargeTest;
import android.text.TextUtils;
import android.view.View;
-import android.widget.LinearLayout;
import com.android.dialer.contactinfo.ContactInfoCache;
import com.android.dialer.contactinfo.ContactInfoCache.OnContactInfoChangedListener;
@@ -42,24 +44,32 @@
/**
* Unit tests for {@link CallLogAdapter}.
+ *
+ * adb shell am instrument \
+ * -e com.android.dialer.calllog.CallLogAdapterTest \
+ * -w com.android.dialer.tests/android.test.InstrumentationTestRunner
*/
-@SmallTest
public class CallLogAdapterTest extends AndroidTestCase {
+ private static final String EMPTY_STRING = "";
private static final int NO_VALUE_SET = -1;
+ private static final String TEST_CACHED_NAME = "name";
+ private static final String TEST_CACHED_NUMBER_LABEL = "label";
+ private static final int TEST_CACHED_NUMBER_TYPE = 1;
+ private static final String TEST_COUNTRY_ISO = "US";
+ private static final String TEST_DEFAULT_CUSTOM_LABEL = "myLabel";
+ private static final Uri TEST_LOOKUP_URI = Uri.parse("content://contacts/2");
+
+ private static final String TEST_NUMBER = "12125551000";
private static final String TEST_NUMBER_1 = "12345678";
private static final String TEST_NUMBER_2 = "87654321";
private static final String TEST_NUMBER_3 = "18273645";
- private static final String TEST_NAME = "name";
- private static final String TEST_NUMBER_LABEL = "label";
- private static final int TEST_NUMBER_TYPE = 1;
- private static final String TEST_COUNTRY_ISO = "US";
+ private static final String TEST_FORMATTED_NUMBER = "1 212-555-1000";
// The object under test.
private TestCallLogAdapter mAdapter;
private MatrixCursor mCursor;
- private int mCursorSize;
private View mView;
private CallLogListItemViewHolder mViewHolder;
@@ -88,117 +98,188 @@
};
mAdapter = new TestCallLogAdapter(getContext(), fakeCallFetcher, fakeContactInfoHelper);
+
// The cursor used in the tests to store the entries to display.
mCursor = new MatrixCursor(CallLogQuery._PROJECTION);
mCursor.moveToFirst();
+
// The views into which to store the data.
- mView = new LinearLayout(getContext());
mViewHolder = CallLogListItemViewHolder.createForTest(getContext());
- mView.setTag(mViewHolder);
}
@Override
protected void tearDown() throws Exception {
mAdapter = null;
mCursor = null;
- mView = null;
super.tearDown();
}
@MediumTest
public void testBindView_NumberOnlyNoCache() {
createCallLogEntry();
- mAdapter.changeCursor(mCursor);
+ mAdapter.changeCursor(mCursor);
mAdapter.onBindViewHolder(mViewHolder, 0);
- assertNameIs(mViewHolder, TEST_NUMBER_1);
+
+ assertNameIs(mViewHolder, TEST_NUMBER);
}
@MediumTest
public void testBindView_PrivateCall() {
createPrivateCallLogEntry();
- mAdapter.changeCursor(mCursor);
+ mAdapter.changeCursor(mCursor);
mAdapter.onBindViewHolder(mViewHolder, 0);
+
assertEquals(Calls.PRESENTATION_RESTRICTED, mViewHolder.numberPresentation);
+ assertNull(mViewHolder.primaryActionButtonView.getTag());
+ }
+
+ @MediumTest
+ public void testBindView_UnknownCall() {
+ createUnknownCallLogEntry();
+
+ mAdapter.changeCursor(mCursor);
+ mAdapter.onBindViewHolder(mViewHolder, 0);
+
+ assertEquals(Calls.PRESENTATION_UNKNOWN, mViewHolder.numberPresentation);
+ assertNull(mViewHolder.primaryActionButtonView.getTag());
}
@MediumTest
public void testBindView_WithoutQuickContactBadge() {
createCallLogEntry();
- mAdapter.changeCursor(mCursor);
+ mAdapter.changeCursor(mCursor);
mAdapter.onBindViewHolder(mViewHolder, 0);
+
assertFalse(mViewHolder.quickContactView.isEnabled());
}
@MediumTest
public void testBindView_CallButton() {
createCallLogEntry();
- mAdapter.changeCursor(mCursor);
+ mAdapter.changeCursor(mCursor);
mAdapter.onBindViewHolder(mViewHolder, 0);
// The primaryActionView tag is set when the ViewHolder is binded. If it is possible
// to place a call to the phone number, a call intent will have been created which
// starts a phone call to the entry's number.
- IntentProvider intentProvider =
- (IntentProvider) mViewHolder.primaryActionButtonView.getTag();
- Intent intent = intentProvider.getIntent(getContext());
- assertEquals(TestConstants.CALL_INTENT_ACTION, intent.getAction());
- assertEquals(Uri.parse("tel:" + TEST_NUMBER_1), intent.getData());
+ assertHasCallAction(mViewHolder);
}
@MediumTest
public void testBindView_VoicemailUri() {
createVoicemailCallLogEntry();
- mAdapter.changeCursor(mCursor);
+ mAdapter.changeCursor(mCursor);
mAdapter.onBindViewHolder(mViewHolder, 0);
+
assertEquals(Uri.parse(mViewHolder.voicemailUri),
ContentUris.withAppendedId(VoicemailContract.Voicemails.CONTENT_URI, 0));
+ assertNull(mViewHolder.primaryActionButtonView.getTag());
}
- public void testBindView_NoCallLogCacheNorMemoryCache_EnqueueRequest() {
- createCallLogEntry();
+ @MediumTest
+ public void testPresentationAfterRebindingViewHolders() {
+ final int increment = 10;
+ final int size = increment * 4;
- // Bind the views of a single row.
+ // Instantiate list of ViewHolders.
+ CallLogListItemViewHolder[] holders = new CallLogListItemViewHolder[size];
+ for (int i = 0; i < size; i++) {
+ holders[i] = CallLogListItemViewHolder.createForTest(getContext());
+ }
+
+ // Add first set of entries to the cursor.
+ for (int i = 0; i < increment; i++) {
+ createCallLogEntry();
+ createPrivateCallLogEntry();
+ createCallLogEntry();
+ createUnknownCallLogEntry();
+ }
+
mAdapter.changeCursor(mCursor);
- mAdapter.onBindViewHolder(mViewHolder, 0);
+
+ // Verify correct appearance for presentation.
+ for (int i = 0; i < size; i++) {
+ mAdapter.onBindViewHolder(holders[i], i);
+ if (holders[i].numberPresentation == Calls.PRESENTATION_ALLOWED) {
+ assertHasCallAction(holders[i]);
+ } else {
+ assertNull(holders[i].primaryActionButtonView.getTag());
+ assertEquals(holders[i].number, EMPTY_STRING);
+ }
+ }
+
+ // Append the rest of the entries to the cursor. Keep the first set of ViewHolders
+ // so they are updated and not buitl from scratch. This checks for bugs which may
+ // be evident only after the call log is updated.
+ for (int i = 0; i < increment; i++) {
+ createPrivateCallLogEntry();
+ createCallLogEntry();
+ createUnknownCallLogEntry();
+ createCallLogEntry();
+ }
+
+ mCursor.move(size);
+
+ // Verify correct appearnce for presentation.
+ for (int i = 0; i < size; i++) {
+ mAdapter.onBindViewHolder(holders[i], i + size);
+ if (holders[i].numberPresentation == Calls.PRESENTATION_ALLOWED) {
+ assertHasCallAction(holders[i]);
+ } else {
+ assertNull(holders[i].primaryActionButtonView.getTag());
+ assertEquals(holders[i].number, EMPTY_STRING);
+ }
+ }
+ }
+
+ @MediumTest
+ public void testBindView_NoCallLogCacheNorMemoryCache_EnqueueRequest() {
+ createCallLogEntry();
+
+ // Bind the views of a single row.
+ mAdapter.changeCursor(mCursor);
+ mAdapter.onBindViewHolder(mViewHolder, 0);
+
+ // There is one request for contact details.
+ assertEquals(1, mAdapter.getContactInfoCache().requests.size());
+
+ TestContactInfoCache.Request request = mAdapter.getContactInfoCache().requests.get(0);
+ // It is for the number we need to show.
+ assertEquals(TEST_NUMBER, request.number);
+ // It has the right country.
+ assertEquals(TEST_COUNTRY_ISO, request.countryIso);
+ // Since there is nothing in the cache, it is an immediate request.
+ assertTrue("should be immediate", request.immediate);
+ }
+
+ @MediumTest
+ public void testBindView_CallLogCacheButNoMemoryCache_EnqueueRequest() {
+ createCallLogEntryWithCachedValues(false);
+
+ // Bind the views of a single row.
+ mAdapter.changeCursor(mCursor);
+ mAdapter.onBindViewHolder(mViewHolder, 0);
// There is one request for contact details.
assertEquals(1, mAdapter.getContactInfoCache().requests.size());
TestContactInfoCache.Request request = mAdapter.getContactInfoCache().requests.get(0);
- // It is for the number we need to show.
- assertEquals(TEST_NUMBER_1, request.number);
- // It has the right country.
- assertEquals(TEST_COUNTRY_ISO, request.countryIso);
- // Since there is nothing in the cache, it is an immediate request.
- assertTrue("should be immediate", request.immediate);
- }
- public void testBindView_CallLogCacheButNoMemoryCache_EnqueueRequest() {
- mCursor.addRow(createCallLogEntryWithCachedValues());
-
- // Bind the views of a single row.
- mAdapter.changeCursor(mCursor);
- mAdapter.onBindViewHolder(mViewHolder, 0);
-
- // There is one request for contact details.
- assertEquals(1, mAdapter.getContactInfoCache().requests.size());
-
- TestContactInfoCache.Request request = mAdapter.getContactInfoCache().requests.get(0);
// The values passed to the request, match the ones in the call log cache.
- assertEquals(TEST_NAME, request.callLogInfo.name);
- assertEquals(1, request.callLogInfo.type);
- assertEquals(TEST_NUMBER_LABEL, request.callLogInfo.label);
+ assertEquals(TEST_CACHED_NAME, request.callLogInfo.name);
+ assertEquals(TEST_CACHED_NUMBER_TYPE, request.callLogInfo.type);
+ assertEquals(TEST_CACHED_NUMBER_LABEL, request.callLogInfo.label);
}
-
+ @MediumTest
public void testBindView_NoCallLogButMemoryCache_EnqueueRequest() {
createCallLogEntry();
- mAdapter.injectContactInfoForTest(TEST_NUMBER_1, TEST_COUNTRY_ISO, createContactInfo());
+ mAdapter.injectContactInfoForTest(TEST_NUMBER, TEST_COUNTRY_ISO, createContactInfo());
// Bind the views of a single row.
mAdapter.changeCursor(mCursor);
@@ -212,9 +293,9 @@
assertFalse("should not be immediate", request.immediate);
}
+ @MediumTest
public void testBindView_BothCallLogAndMemoryCache_NoEnqueueRequest() {
- mCursor.addRow(createCallLogEntryWithCachedValues());
- mAdapter.injectContactInfoForTest(TEST_NUMBER_1, TEST_COUNTRY_ISO, createContactInfo());
+ createCallLogEntryWithCachedValues(true);
// Bind the views of a single row.
mAdapter.changeCursor(mCursor);
@@ -224,13 +305,14 @@
assertEquals(0, mAdapter.getContactInfoCache().requests.size());
}
- public void testBindView_MismatchBetwenCallLogAndMemoryCache_EnqueueRequest() {
- mCursor.addRow(createCallLogEntryWithCachedValues());
+ @MediumTest
+ public void testBindView_MismatchBetweenCallLogAndMemoryCache_EnqueueRequest() {
+ createCallLogEntryWithCachedValues(false);
// Contact info contains a different name.
ContactInfo info = createContactInfo();
info.name = "new name";
- mAdapter.injectContactInfoForTest(TEST_NUMBER_1, TEST_COUNTRY_ISO, info);
+ mAdapter.injectContactInfoForTest(TEST_NUMBER, TEST_COUNTRY_ISO, info);
// Bind the views of a single row.
mAdapter.changeCursor(mCursor);
@@ -244,6 +326,98 @@
assertFalse("should not be immediate", request.immediate);
}
+ @MediumTest
+ public void testBindView_WithCachedName() {
+ createCallLogEntryWithCachedValues(
+ "John Doe",
+ Phone.TYPE_HOME,
+ TEST_CACHED_NUMBER_LABEL);
+
+ mAdapter.changeCursor(mCursor);
+ mAdapter.onBindViewHolder(mViewHolder, 0);
+
+ assertNameIs(mViewHolder, "John Doe");
+ assertLabel(mViewHolder, TEST_FORMATTED_NUMBER, getTypeLabel(Phone.TYPE_HOME));
+ }
+
+ @MediumTest
+ public void testBindView_UriNumber() {
+ createCallLogEntryWithCachedValues(
+ "sip:johndoe@gmail.com",
+ Calls.INCOMING_TYPE,
+ "John Doe",
+ Phone.TYPE_HOME,
+ TEST_DEFAULT_CUSTOM_LABEL,
+ EMPTY_STRING,
+ false /* inject */);
+
+ mAdapter.changeCursor(mCursor);
+ mAdapter.onBindViewHolder(mViewHolder, 0);
+
+ assertNameIs(mViewHolder, "John Doe");
+ assertLabel(mViewHolder, "sip:johndoe@gmail.com", "sip:johndoe@gmail.com");
+ }
+
+ @MediumTest
+ public void testBindView_HomeLabel() {
+ createCallLogEntryWithCachedValues(
+ "John Doe",
+ Phone.TYPE_HOME,
+ TEST_CACHED_NUMBER_LABEL);
+
+ mAdapter.changeCursor(mCursor);
+ mAdapter.onBindViewHolder(mViewHolder, 0);
+
+ assertNameIs(mViewHolder, "John Doe");
+ assertLabel(mViewHolder, TEST_FORMATTED_NUMBER, getTypeLabel(Phone.TYPE_HOME));
+ }
+
+ @MediumTest
+ public void testBindView_WorkLabel() {
+ createCallLogEntryWithCachedValues(
+ "John Doe",
+ Phone.TYPE_WORK,
+ TEST_CACHED_NUMBER_LABEL);
+
+ mAdapter.changeCursor(mCursor);
+ mAdapter.onBindViewHolder(mViewHolder, 0);
+
+ assertNameIs(mViewHolder, "John Doe");
+ assertLabel(mViewHolder, TEST_FORMATTED_NUMBER, getTypeLabel(Phone.TYPE_WORK));
+ }
+
+ @MediumTest
+ public void testBindView_CustomLabel() {
+ createCallLogEntryWithCachedValues(
+ "John Doe",
+ Phone.TYPE_CUSTOM,
+ TEST_DEFAULT_CUSTOM_LABEL);
+
+ mAdapter.changeCursor(mCursor);
+ mAdapter.onBindViewHolder(mViewHolder, 0);
+
+ assertNameIs(mViewHolder, "John Doe");
+ assertLabel(mViewHolder, TEST_FORMATTED_NUMBER, TEST_DEFAULT_CUSTOM_LABEL);
+ }
+
+ @MediumTest
+ public void testBindView_NumberOnlyDbCachedFormattedNumber() {
+ createCallLogEntryWithCachedValues(
+ TEST_NUMBER,
+ Calls.INCOMING_TYPE,
+ EMPTY_STRING,
+ TEST_CACHED_NUMBER_TYPE,
+ TEST_CACHED_NUMBER_LABEL,
+ TEST_FORMATTED_NUMBER,
+ false /* inject */);
+
+ mAdapter.changeCursor(mCursor);
+ mAdapter.onBindViewHolder(mViewHolder, 0);
+
+ assertNameIs(mViewHolder, TEST_FORMATTED_NUMBER);
+ }
+
+ @MediumTest
public void testBindVoicemailPromoCard() {
createCallLogEntry(TEST_NUMBER_1);
createCallLogEntry(TEST_NUMBER_1);
@@ -271,51 +445,106 @@
assertEquals(TEST_NUMBER_3, mViewHolder.number);
}
- /** Returns a contact info with default values. */
- private ContactInfo createContactInfo() {
- ContactInfo info = new ContactInfo();
- info.number = TEST_NUMBER_1;
- info.name = TEST_NAME;
- info.type = TEST_NUMBER_TYPE;
- info.label = TEST_NUMBER_LABEL;
- return info;
- }
-
- /** Returns a call log entry without cached values. */
private void createCallLogEntry() {
- createCallLogEntry(TEST_NUMBER_1);
+ createCallLogEntry(TEST_NUMBER);
}
- private void createCallLogEntry(String testNumber) {
- createCallLogEntry(testNumber, NO_VALUE_SET, NO_VALUE_SET, NO_VALUE_SET, NO_VALUE_SET);
+ private void createCallLogEntry(String testNumber) {
+ createCallLogEntry(testNumber, NO_VALUE_SET, NO_VALUE_SET);
}
private void createPrivateCallLogEntry() {
- createCallLogEntry("", Calls.PRESENTATION_RESTRICTED, NO_VALUE_SET, 0, Calls.INCOMING_TYPE);
+ createCallLogEntry(EMPTY_STRING, Calls.PRESENTATION_RESTRICTED, Calls.INCOMING_TYPE);
+ }
+
+ private void createUnknownCallLogEntry() {
+ createCallLogEntry(EMPTY_STRING, Calls.PRESENTATION_UNKNOWN, Calls.INCOMING_TYPE);
}
private void createVoicemailCallLogEntry() {
- createCallLogEntry(TEST_NUMBER_1, NO_VALUE_SET, NO_VALUE_SET, NO_VALUE_SET,
- Calls.VOICEMAIL_TYPE, true /* isVoicemail */);
+ createCallLogEntry(TEST_NUMBER, NO_VALUE_SET, Calls.VOICEMAIL_TYPE);
}
- private void createCallLogEntry(
- String number, int presentation, long date, int duration, int type) {
- createCallLogEntry(number, presentation, date, duration, type, false /* isVoicemail */);
+ private void createCallLogEntry(String number, int presentation, int type) {
+ Object[] values = getValues(number, presentation, type);
+ mCursor.addRow(values);
}
- private void createCallLogEntry(
+ private void createCallLogEntryWithCachedValues(boolean inject) {
+ createCallLogEntryWithCachedValues(
+ TEST_NUMBER,
+ NO_VALUE_SET,
+ TEST_CACHED_NAME,
+ TEST_CACHED_NUMBER_TYPE,
+ TEST_CACHED_NUMBER_LABEL,
+ EMPTY_STRING,
+ inject);
+ }
+
+ private void createCallLogEntryWithCachedValues(
+ String cachedName, int cachedNumberType, String cachedNumberLabel) {
+ createCallLogEntryWithCachedValues(
+ TEST_NUMBER,
+ NO_VALUE_SET,
+ cachedName,
+ cachedNumberType,
+ cachedNumberLabel,
+ EMPTY_STRING,
+ false /* inject */);
+ }
+
+ /**
+ * Inserts a new call log entry
+ *
+ * It includes the values for the cached contact associated with the number.
+ *
+ * @param number The phone number.
+ * @param type Either Call.OUTGOING_TYPE or Call.INCOMING_TYPE or Call.MISSED_TYPE.
+ * @param cachedName The name of the contact with this number
+ * @param cachedNumberType The type of the number, from the contact with this number.
+ * @param cachedNumberLabel The label of the number, from the contact with this number.
+ * @param cachedFormattedNumber The formatted number, from the contact with this number.
+ * @param inject Whether to inject the contact info into the adapter's ContactInfoCache.
+ */
+ private void createCallLogEntryWithCachedValues(
+ String number,
+ int type,
+ String cachedName,
+ int cachedNumberType,
+ String cachedNumberLabel,
+ String cachedFormattedNumber,
+ boolean inject) {
+ Object[] values = getValues(number, NO_VALUE_SET, type);
+ values[CallLogQuery.CACHED_NAME] = cachedName;
+ values[CallLogQuery.CACHED_NUMBER_TYPE] = cachedNumberType;
+ values[CallLogQuery.CACHED_NUMBER_LABEL] = cachedNumberLabel;
+ values[CallLogQuery.CACHED_FORMATTED_NUMBER] = cachedFormattedNumber;
+
+ mCursor.addRow(values);
+
+ if (inject) {
+ ContactInfo contactInfo =
+ createContactInfo(cachedName, cachedNumberType, cachedNumberLabel);
+ mAdapter.injectContactInfoForTest(number, TEST_COUNTRY_ISO, contactInfo);
+ }
+ }
+
+ /**
+ * @param number The phone number.
+ * @param presentation Number representing display rules for "allowed",
+ * "payphone", "restricted", or "unknown".
+ * @param date In millisec since epoch. Use NOW to use the current time.
+ */
+ private Object[] getValues(
String number,
int presentation,
- long date,
- int duration,
- int type,
- boolean isVoicemail) {
+ int type) {
Object[] values = CallLogQueryTestUtils.createTestValues();
- values[CallLogQuery.ID] = mCursorSize;
+ values[CallLogQuery.ID] = mCursor.getCount();
values[CallLogQuery.COUNTRY_ISO] = TEST_COUNTRY_ISO;
- values[CallLogQuery.DATE] = date != NO_VALUE_SET ? date : new Date().getTime();
+ values[CallLogQuery.DATE] = new Date().getTime();
+ values[CallLogQuery.DURATION] = mRandom.nextInt(10 * 60);
if (!TextUtils.isEmpty(number)) {
values[CallLogQuery.NUMBER] = number;
@@ -323,38 +552,68 @@
if (presentation != NO_VALUE_SET) {
values[CallLogQuery.NUMBER_PRESENTATION] = presentation;
}
- if (duration != NO_VALUE_SET) {
- values[CallLogQuery.DURATION] = (duration < 0) ? mRandom.nextInt(10 * 60) : duration;
- }
if (type != NO_VALUE_SET) {
values[CallLogQuery.CALL_TYPE] = type;
}
- if (isVoicemail) {
- values[CallLogQuery.VOICEMAIL_URI] =
- ContentUris.withAppendedId(VoicemailContract.Voicemails.CONTENT_URI, mCursorSize);
+ if (type == Calls.VOICEMAIL_TYPE) {
+ values[CallLogQuery.VOICEMAIL_URI] = ContentUris.withAppendedId(
+ VoicemailContract.Voicemails.CONTENT_URI, mCursor.getCount());
}
- mCursor.addRow(values);
- mCursorSize++;
- }
-
- // Returns a call log entry with a cached values.
- private Object[] createCallLogEntryWithCachedValues() {
- Object[] values = CallLogQueryTestUtils.createTestValues();
- values[CallLogQuery.NUMBER] = TEST_NUMBER_1;
- values[CallLogQuery.COUNTRY_ISO] = TEST_COUNTRY_ISO;
- values[CallLogQuery.CACHED_NAME] = TEST_NAME;
- values[CallLogQuery.CACHED_NUMBER_TYPE] = TEST_NUMBER_TYPE;
- values[CallLogQuery.CACHED_NUMBER_LABEL] = TEST_NUMBER_LABEL;
return values;
}
- // Asserts that the name text view is shown and contains the given text./
+ private ContactInfo createContactInfo() {
+ return createContactInfo(
+ TEST_CACHED_NAME,
+ TEST_CACHED_NUMBER_TYPE,
+ TEST_CACHED_NUMBER_LABEL);
+ }
+
+ /** Returns a contact info with default values. */
+ private ContactInfo createContactInfo(String name, int type, String label) {
+ ContactInfo info = new ContactInfo();
+ info.number = TEST_NUMBER;
+ info.name = name;
+ info.type = type;
+ info.label = label;
+ info.formattedNumber = TEST_FORMATTED_NUMBER;
+ info.normalizedNumber = TEST_NUMBER;
+ info.lookupUri = TEST_LOOKUP_URI;
+ return info;
+ }
+
+ // Asserts that the name text view is shown and contains the given text.
private void assertNameIs(CallLogListItemViewHolder viewHolder, String name) {
assertEquals(View.VISIBLE, viewHolder.phoneCallDetailsViews.nameView.getVisibility());
assertEquals(name, viewHolder.phoneCallDetailsViews.nameView.getText());
}
+ // Asserts that the label text view contains the given text.
+ private void assertLabel(
+ CallLogListItemViewHolder viewHolder, CharSequence number, CharSequence label) {
+ if (label != null) {
+ assertTrue(viewHolder.phoneCallDetailsViews.callLocationAndDate.getText()
+ .toString().contains(label));
+ }
+ }
+
+ private void assertHasCallAction(CallLogListItemViewHolder viewHolder) {
+ // The primaryActionView tag is set when the ViewHolder is binded. If it is possible
+ // to place a call to the phone number, a call intent will have been created which
+ // starts a phone call to the entry's number.
+ IntentProvider intentProvider =
+ (IntentProvider) viewHolder.primaryActionButtonView.getTag();
+ Intent intent = intentProvider.getIntent(getContext());
+ assertEquals(TestConstants.CALL_INTENT_ACTION, intent.getAction());
+ assertEquals(Uri.parse("tel:" + TEST_NUMBER), intent.getData());
+ }
+
+ /** Returns the label associated with a given phone type. */
+ private CharSequence getTypeLabel(int phoneType) {
+ return Phone.getTypeLabel(getContext().getResources(), phoneType, "");
+ }
+
/// Subclass of {@link CallLogAdapter} used in tests to intercept certain calls.
private static final class TestCallLogAdapter extends CallLogAdapter {
public TestCallLogAdapter(Context context, CallFetcher callFetcher,
diff --git a/tests/src/com/android/dialer/calllog/CallLogFragmentTest.java b/tests/src/com/android/dialer/calllog/CallLogFragmentTest.java
deleted file mode 100644
index aa4ad80..0000000
--- a/tests/src/com/android/dialer/calllog/CallLogFragmentTest.java
+++ /dev/null
@@ -1,545 +0,0 @@
-/*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.dialer.calllog;
-
-import android.app.FragmentManager;
-import android.app.FragmentTransaction;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.res.Resources;
-import android.database.MatrixCursor;
-import android.graphics.Bitmap;
-import android.graphics.drawable.BitmapDrawable;
-import android.net.Uri;
-import android.provider.CallLog.Calls;
-import android.provider.ContactsContract.CommonDataKinds.Phone;
-import android.provider.VoicemailContract;
-import android.telephony.PhoneNumberUtils;
-import android.telephony.TelephonyManager;
-import android.test.ActivityInstrumentationTestCase2;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.MediumTest;
-import android.util.Log;
-import android.view.View;
-import android.widget.FrameLayout;
-
-import com.android.contacts.common.test.FragmentTestActivity;
-import com.android.dialer.CallDetailActivity;
-import com.android.dialer.R;
-import com.android.dialer.util.TestConstants;
-
-import java.util.Date;
-import java.util.Formatter;
-import java.util.HashMap;
-import java.util.Random;
-
-/**
- * Tests for the contact call list activity.
- *
- * Running all tests:
- *
- * runtest contacts
- * or
- * adb shell am instrument \
- * -w com.android.dialer.tests/android.test.InstrumentationTestRunner
- */
-@LargeTest
-public class CallLogFragmentTest extends ActivityInstrumentationTestCase2<FragmentTestActivity> {
- private static final int RAND_DURATION = -1;
- private static final long NOW = -1L;
-
- /** A test value for the URI of a contact. */
- private static final Uri TEST_LOOKUP_URI = Uri.parse("content://contacts/2");
- /** A test value for the country ISO of the phone number in the call log. */
- private static final String TEST_COUNTRY_ISO = "US";
- /** A phone number to be used in tests. */
- private static final String TEST_NUMBER = "12125551000";
- /** The formatted version of {@link #TEST_NUMBER}. */
- private static final String TEST_FORMATTED_NUMBER = "1 212-555-1000";
-
- private static final String TEST_DEFAULT_CUSTOM_LABEL = "myLabel";
-
- /** The activity in which we are hosting the fragment. */
- private FragmentTestActivity mActivity;
- private CallLogFragment mFragment;
- private FrameLayout mParentView;
- /**
- * The adapter used by the fragment to build the rows in the call log. We use it with our own in
- * memory database.
- */
- private CallLogAdapter mAdapter;
- private String mVoicemail;
-
- // In memory array to hold the rows corresponding to the 'calls' table.
- private MatrixCursor mCursor;
- private int mIndex; // Of the next row.
-
- private Random mRnd;
-
- // An item in the call list. All the methods performing checks use it.
- private CallLogListItemViewHolder mItem;
-
- // The list of view holderss representing the data in the DB, in reverse order from the DB.
- private CallLogListItemViewHolder[] mList;
-
- public CallLogFragmentTest() {
- super(FragmentTestActivity.class);
- mIndex = 1;
- mRnd = new Random();
- }
-
- @Override
- public void setUp() {
- mActivity = getActivity();
- // Needed by the CallLogFragment.
- mActivity.setTheme(R.style.DialtactsTheme);
-
- // Create the fragment and load it into the activity.
- mFragment = new CallLogFragment();
- FragmentManager fragmentManager = mActivity.getFragmentManager();
- FragmentTransaction transaction = fragmentManager.beginTransaction();
- transaction.add(FragmentTestActivity.LAYOUT_ID, mFragment);
- transaction.commitAllowingStateLoss();
- // Wait for the fragment to be loaded.
- getInstrumentation().waitForIdleSync();
-
- final TelephonyManager telephonyManager =
- (TelephonyManager) mActivity.getSystemService(Context.TELEPHONY_SERVICE);
- mVoicemail = telephonyManager.getVoiceMailNumber();
- mAdapter = mFragment.getAdapter();
- // Do not process requests for details during tests. This would start a background thread,
- // which makes the tests flaky.
- mAdapter.disableRequestProcessingForTest();
- mAdapter.pauseCache();
- mParentView = new FrameLayout(mActivity);
- mCursor = new MatrixCursor(CallLogQuery._PROJECTION);
-
- getInstrumentation().runOnMainSync(new Runnable() {
- @Override
- public void run() {
- mAdapter.changeCursor(mCursor);
- }
- });
- getInstrumentation().waitForIdleSync();
- }
-
- /**
- * Checks that the call icon is not visible for private and
- * unknown numbers.
- * Use 2 passes, one where new viewHolder are created and one where
- * half of the total viewHolder are updated and the other half created.
- */
- @MediumTest
- public void testCallViewIsNotVisibleForPrivateAndUnknownNumbers() {
- final int SIZE = 50;
- mList = new CallLogListItemViewHolder[SIZE];
-
- // Insert the first batch of entries.
- mCursor.moveToFirst();
- insertRandomEntries(SIZE / 2);
- int startOfSecondBatch = mCursor.getPosition();
-
- buildViewListFromDb();
- checkCallStatus();
-
- // Append the rest of the entries. We keep the first set of
- // viewHolder around so they get updated and not built from
- // scratch, this exposes some bugs that are not there when the
- // call log is launched for the 1st time but show up when the
- // call log gets updated afterwards.
- mCursor.move(startOfSecondBatch);
- insertRandomEntries(SIZE / 2);
-
- buildViewListFromDb();
- checkCallStatus();
- }
-
- @MediumTest
- public void testBindView_NumberOnlyDbCachedFormattedNumber() {
- mCursor.moveToFirst();
- Object[] values = getValuesToInsert(TEST_NUMBER,
- Calls.PRESENTATION_ALLOWED, NOW, 0, Calls.INCOMING_TYPE);
- values[CallLogQuery.CACHED_FORMATTED_NUMBER] = TEST_FORMATTED_NUMBER;
- insertValues(values);
- CallLogListItemViewHolder viewHolder = (CallLogListItemViewHolder)
- mAdapter.onCreateViewHolder(mParentView, /* viewType */ 0);
- bindViewForTest(viewHolder);
-
- assertNameIs(viewHolder, TEST_FORMATTED_NUMBER);
- }
-
- @MediumTest
- public void testBindView_WithCachedName() {
- mCursor.moveToFirst();
- insertWithCachedValues(TEST_NUMBER, NOW, 0, Calls.INCOMING_TYPE,
- "John Doe", Phone.TYPE_HOME, TEST_DEFAULT_CUSTOM_LABEL);
- CallLogListItemViewHolder viewHolder = (CallLogListItemViewHolder)
- mAdapter.onCreateViewHolder(mParentView, /* viewType */ 0);
- bindViewForTest(viewHolder);
-
- assertNameIs(viewHolder, "John Doe");
- assertLabel(viewHolder, TEST_FORMATTED_NUMBER, getTypeLabel(Phone.TYPE_HOME));
- }
-
- @MediumTest
- public void testBindView_UriNumber() {
- mCursor.moveToFirst();
- insertWithCachedValues("sip:johndoe@gmail.com", NOW, 0, Calls.INCOMING_TYPE,
- "John Doe", Phone.TYPE_HOME, TEST_DEFAULT_CUSTOM_LABEL);
- CallLogListItemViewHolder viewHolder = (CallLogListItemViewHolder)
- mAdapter.onCreateViewHolder(mParentView, /* viewType */ 0);
- bindViewForTest(viewHolder);
-
- assertNameIs(viewHolder, "John Doe");
- assertLabel(viewHolder, "sip:johndoe@gmail.com", "sip:johndoe@gmail.com");
- }
-
- @MediumTest
- public void testBindView_HomeLabel() {
- mCursor.moveToFirst();
- insertWithCachedValues(TEST_NUMBER, NOW, 0, Calls.INCOMING_TYPE,
- "John Doe", Phone.TYPE_HOME, TEST_DEFAULT_CUSTOM_LABEL);
- CallLogListItemViewHolder viewHolder = (CallLogListItemViewHolder)
- mAdapter.onCreateViewHolder(mParentView, /* viewType */ 0);
- bindViewForTest(viewHolder);
-
- assertNameIs(viewHolder, "John Doe");
- assertLabel(viewHolder, TEST_FORMATTED_NUMBER, getTypeLabel(Phone.TYPE_HOME));
- }
-
- @MediumTest
- public void testBindView_WorkLabel() {
- mCursor.moveToFirst();
- insertWithCachedValues(TEST_NUMBER, NOW, 0, Calls.INCOMING_TYPE,
- "John Doe", Phone.TYPE_WORK, TEST_DEFAULT_CUSTOM_LABEL);
- CallLogListItemViewHolder viewHolder = (CallLogListItemViewHolder)
- mAdapter.onCreateViewHolder(mParentView, /* viewType */ 0);
- bindViewForTest(viewHolder);
-
- assertNameIs(viewHolder, "John Doe");
- assertLabel(viewHolder, TEST_FORMATTED_NUMBER, getTypeLabel(Phone.TYPE_WORK));
- }
-
- @MediumTest
- public void testBindView_CustomLabel() {
- mCursor.moveToFirst();
- String numberLabel = "My label";
- insertWithCachedValues(TEST_NUMBER, NOW, 0, Calls.INCOMING_TYPE,
- "John Doe", Phone.TYPE_CUSTOM, numberLabel);
- CallLogListItemViewHolder viewHolder = (CallLogListItemViewHolder)
- mAdapter.onCreateViewHolder(mParentView, /* viewType */ 0);
- bindViewForTest(viewHolder);
-
- assertNameIs(viewHolder, "John Doe");
- assertLabel(viewHolder, TEST_FORMATTED_NUMBER, numberLabel);
- }
-
-
- /** Returns the label associated with a given phone type. */
- private CharSequence getTypeLabel(int phoneType) {
- return Phone.getTypeLabel(getActivity().getResources(), phoneType, "");
- }
-
- //
- // HELPERS to check conditions on the DB/viewHolder
- //
- /**
- * Go over the viewHolder in the list and check to ensure that
- * callable numbers have an associated call intent, where numbers
- * which are not callable have a null intent.
- */
- private void checkCallStatus() {
- for (int i = 0; i < mList.length; i++) {
- if (null == mList[i]) {
- break;
- }
- mItem = (CallLogListItemViewHolder) mList[i];
- int presentation = getPhoneNumberPresentationForListEntry(i);
- if (presentation == Calls.PRESENTATION_RESTRICTED ||
- presentation == Calls.PRESENTATION_UNKNOWN) {
- //If number is not callable, the primary action view should have a null tag.
- assertNull(mItem.primaryActionButtonView.getTag());
- } else {
- //If the number is callable, the primary action view should have a non-null tag.
- assertNotNull(mItem.primaryActionButtonView.getTag());
-
- IntentProvider intentProvider =
- (IntentProvider) mItem.primaryActionButtonView.getTag();
- Intent callIntent = intentProvider.getIntent(mActivity);
-
- //The intent should be to make the call
- assertEquals(TestConstants.CALL_INTENT_ACTION, callIntent.getAction());
- }
- }
- }
-
- //
- // HELPERS to setup the tests.
- //
-
- /**
- * Get the Bitmap from the icons in the contacts package.
- */
- private Bitmap getBitmap(String resName) {
- Resources r = mActivity.getResources();
- int resid = r.getIdentifier(resName, "drawable",
- getInstrumentation().getTargetContext().getPackageName());
- BitmapDrawable d = (BitmapDrawable) r.getDrawable(resid);
- assertNotNull(d);
- return d.getBitmap();
- }
-
- //
- // HELPERS to build/update the call entries (viewHolder) from the DB.
- //
-
- /**
- * Read the DB and foreach call either update the existing view if
- * one exists already otherwise create one.
- * The list is build from a DESC view of the DB (last inserted entry is first).
- */
- private void buildViewListFromDb() {
- int i = 0;
- mCursor.moveToLast();
- while (!mCursor.isBeforeFirst()) {
- if (null == mList[i]) {
- mList[i] = (CallLogListItemViewHolder)
- mAdapter.onCreateViewHolder(mParentView, /* itemType */ 0);
- }
- // Bind to the proper position, despite iterating in reverse.
- bindViewForTest(mList[i], mCursor.getCount() - i - 1);
- mCursor.moveToPrevious();
- i++;
- }
- }
-
- /** Returns the number presentation associated with the given entry in {{@link #mList}. */
- private int getPhoneNumberPresentationForListEntry(int index) {
- // The entries are added backward, so count from the end of the cursor.
- mCursor.moveToPosition(mCursor.getCount() - index - 1);
- return mCursor.getInt(CallLogQuery.NUMBER_PRESENTATION);
- }
-
- //
- // HELPERS to insert numbers in the call log DB.
- //
-
- /**
- * Bind a call log entry view for testing purposes. Also inflates the action view stub so
- * unit tests can access the buttons contained within.
- *
- * @param view The current call log row.
- * @param position The position of the item.
- */
- private void bindViewForTest(final CallLogListItemViewHolder viewHolder, int position) {
- mAdapter.onBindViewHolder(viewHolder, position);
- getInstrumentation().runOnMainSync(new Runnable() {
- @Override
- public void run() {
- viewHolder.inflateActionViewStub();
- }
- });
- getInstrumentation().waitForIdleSync();
- }
-
- private void bindViewForTest(CallLogListItemViewHolder viewHolder) {
- bindViewForTest(viewHolder, /* position */ 0);
- }
-
- /**
- * Insert a certain number of random numbers in the DB. Makes sure
- * there is at least one private and one unknown number in the DB.
- * @param num Of entries to be inserted.
- */
- private void insertRandomEntries(int num) {
- if (num < 10) {
- throw new IllegalArgumentException("num should be >= 10");
- }
- boolean privateOrUnknownOrVm[];
- privateOrUnknownOrVm = insertRandomRange(0, num - 2);
-
- if (privateOrUnknownOrVm[0] && privateOrUnknownOrVm[1]) {
- insertRandomRange(num - 2, num);
- } else {
- insertPrivate(NOW, RAND_DURATION);
- insertUnknown(NOW, RAND_DURATION);
- }
- }
-
- /**
- * Insert a new call entry in the test DB.
- *
- * It includes the values for the cached contact associated with the number.
- *
- * @param number The phone number.
- * @param date In millisec since epoch. Use NOW to use the current time.
- * @param duration In seconds of the call. Use RAND_DURATION to pick a random one.
- * @param type Either Call.OUTGOING_TYPE or Call.INCOMING_TYPE or Call.MISSED_TYPE.
- * @param cachedName the name of the contact with this number
- * @param cachedNumberType the type of the number, from the contact with this number
- * @param cachedNumberLabel the label of the number, from the contact with this number
- */
- private void insertWithCachedValues(String number, long date, int duration, int type,
- String cachedName, int cachedNumberType, String cachedNumberLabel) {
- insert(number, Calls.PRESENTATION_ALLOWED, date, duration, type);
- ContactInfo contactInfo = new ContactInfo();
- contactInfo.lookupUri = TEST_LOOKUP_URI;
- contactInfo.name = cachedName;
- contactInfo.type = cachedNumberType;
- contactInfo.label = cachedNumberLabel;
- String formattedNumber = PhoneNumberUtils.formatNumber(number, TEST_COUNTRY_ISO);
- if (formattedNumber == null) {
- formattedNumber = number;
- }
- contactInfo.formattedNumber = formattedNumber;
- contactInfo.normalizedNumber = number;
- contactInfo.photoId = 0;
- mAdapter.injectContactInfoForTest(number, TEST_COUNTRY_ISO, contactInfo);
- }
-
- /**
- * Insert a new call entry in the test DB.
- * @param number The phone number.
- * @param presentation Number representing display rules for "allowed",
- * "payphone", "restricted", or "unknown".
- * @param date In millisec since epoch. Use NOW to use the current time.
- * @param duration In seconds of the call. Use RAND_DURATION to pick a random one.
- * @param type Either Call.OUTGOING_TYPE or Call.INCOMING_TYPE or Call.MISSED_TYPE.
- */
- private void insert(String number, int presentation, long date, int duration, int type) {
- insertValues(getValuesToInsert(number, presentation, date, duration, type));
- }
-
- /** Inserts the given values in the cursor. */
- private void insertValues(Object[] values) {
- mCursor.addRow(values);
- ++mIndex;
- }
-
- /**
- * Returns the values for a new call entry.
- *
- * @param number The phone number.
- * @param presentation Number representing display rules for "allowed",
- * "payphone", "restricted", or "unknown".
- * @param date In millisec since epoch. Use NOW to use the current time.
- * @param duration In seconds of the call. Use RAND_DURATION to pick a random one.
- * @param type Either Call.OUTGOING_TYPE or Call.INCOMING_TYPE or Call.MISSED_TYPE.
- */
- private Object[] getValuesToInsert(String number, int presentation,
- long date, int duration, int type) {
- Object[] values = CallLogQueryTestUtils.createTestValues();
- values[CallLogQuery.ID] = mIndex;
- values[CallLogQuery.NUMBER] = number;
- values[CallLogQuery.NUMBER_PRESENTATION] = presentation;
- values[CallLogQuery.DATE] = date == NOW ? new Date().getTime() : date;
- values[CallLogQuery.DURATION] = duration < 0 ? mRnd.nextInt(10 * 60) : duration;
- if (mVoicemail != null && mVoicemail.equals(number)) {
- assertEquals(Calls.OUTGOING_TYPE, type);
- }
- values[CallLogQuery.CALL_TYPE] = type;
- values[CallLogQuery.COUNTRY_ISO] = TEST_COUNTRY_ISO;
- return values;
- }
-
- /**
- * Insert a new private call entry in the test DB.
- * @param date In millisec since epoch. Use NOW to use the current time.
- * @param duration In seconds of the call. Use RAND_DURATION to pick a random one.
- */
- private void insertPrivate(long date, int duration) {
- insert("", Calls.PRESENTATION_RESTRICTED, date, duration, Calls.INCOMING_TYPE);
- }
-
- /**
- * Insert a new unknown call entry in the test DB.
- * @param date In millisec since epoch. Use NOW to use the current time.
- * @param duration In seconds of the call. Use RAND_DURATION to pick a random one.
- */
- private void insertUnknown(long date, int duration) {
- insert("", Calls.PRESENTATION_UNKNOWN, date, duration, Calls.INCOMING_TYPE);
- }
-
- /**
- * Insert a new call to voicemail entry in the test DB.
- * @param date In millisec since epoch. Use NOW to use the current time.
- * @param duration In seconds of the call. Use RAND_DURATION to pick a random one.
- */
- private void insertCalltoVoicemail(long date, int duration) {
- // mVoicemail may be null
- if (mVoicemail != null) {
- insert(mVoicemail, Calls.PRESENTATION_ALLOWED, date, duration, Calls.OUTGOING_TYPE);
- }
- }
-
- /**
- * Insert a range [start, end) of random numbers in the DB. For
- * each row, there is a 1/10 probability that the number will be
- * marked as PRIVATE or UNKNOWN or VOICEMAIL. For regular numbers, a number is
- * inserted, its last 4 digits will be the number of the iteration
- * in the range.
- * @param start Of the range.
- * @param end Of the range (excluded).
- * @return An array with 2 booleans [0 = private number, 1 =
- * unknown number, 2 = voicemail] to indicate if at least one
- * private or unknown or voicemail number has been inserted. Since
- * the numbers are random some tests may want to enforce the
- * insertion of such numbers.
- */
- // TODO: Should insert numbers with contact entries too.
- private boolean[] insertRandomRange(int start, int end) {
- boolean[] privateOrUnknownOrVm = new boolean[] {false, false, false};
-
- for (int i = start; i < end; i++ ) {
- int type = mRnd.nextInt(10);
-
- if (0 == type) {
- insertPrivate(NOW, RAND_DURATION);
- privateOrUnknownOrVm[0] = true;
- } else if (1 == type) {
- insertUnknown(NOW, RAND_DURATION);
- privateOrUnknownOrVm[1] = true;
- } else if (2 == type) {
- insertCalltoVoicemail(NOW, RAND_DURATION);
- privateOrUnknownOrVm[2] = true;
- } else {
- int inout = mRnd.nextBoolean() ? Calls.OUTGOING_TYPE : Calls.INCOMING_TYPE;
- final Formatter formatter = new Formatter();
- String number = formatter.format("1800123%04d", i).toString();
- formatter.close();
- insert(number, Calls.PRESENTATION_ALLOWED, NOW, RAND_DURATION, inout);
- }
- }
- return privateOrUnknownOrVm;
- }
-
- /** Asserts that the name text view is shown and contains the given text. */
- private void assertNameIs(CallLogListItemViewHolder viewHolder, String name) {
- assertEquals(View.VISIBLE, viewHolder.phoneCallDetailsViews.nameView.getVisibility());
- assertEquals(name, viewHolder.phoneCallDetailsViews.nameView.getText().toString());
- }
-
- /** Asserts that the label text view contains the given text. */
- private void assertLabel(CallLogListItemViewHolder viewHolder, CharSequence number,
- CharSequence label) {
- if (label != null) {
- assertTrue(viewHolder.phoneCallDetailsViews.callLocationAndDate.getText()
- .toString().contains(label));
- }
- }
-}
diff --git a/tests/src/com/android/dialer/calllog/CallLogGroupBuilderTest.java b/tests/src/com/android/dialer/calllog/CallLogGroupBuilderTest.java
index 95558bc..9bdcd40 100644
--- a/tests/src/com/android/dialer/calllog/CallLogGroupBuilderTest.java
+++ b/tests/src/com/android/dialer/calllog/CallLogGroupBuilderTest.java
@@ -66,14 +66,14 @@
public void testAddGroups_OneCall() {
addCallLogEntry(TEST_NUMBER1, Calls.INCOMING_TYPE);
mBuilder.addGroups(mCursor);
- assertEquals(0, mFakeGroupCreator.groups.size());
+ assertEquals(1, mFakeGroupCreator.groups.size());
}
public void testAddGroups_TwoCallsNotMatching() {
addCallLogEntry(TEST_NUMBER1, Calls.INCOMING_TYPE);
addCallLogEntry(TEST_NUMBER2, Calls.INCOMING_TYPE);
mBuilder.addGroups(mCursor);
- assertEquals(0, mFakeGroupCreator.groups.size());
+ assertEquals(2, mFakeGroupCreator.groups.size());
}
public void testAddGroups_ThreeCallsMatching() {
@@ -136,21 +136,25 @@
public void testAddGroups_Mixed() {
addMultipleCallLogEntries(TEST_NUMBER1,
- Calls.VOICEMAIL_TYPE, // Stand-alone
- Calls.INCOMING_TYPE, // Group 1: 1-4
+ Calls.VOICEMAIL_TYPE, // Group 1:Stand-alone
+ Calls.INCOMING_TYPE, // Group 2: 1-4
Calls.OUTGOING_TYPE,
Calls.MISSED_TYPE,
Calls.MISSED_TYPE,
- Calls.VOICEMAIL_TYPE, // Stand-alone
- Calls.INCOMING_TYPE, // Stand-alone
- Calls.VOICEMAIL_TYPE, // Stand-alone
- Calls.MISSED_TYPE, // Group 2: 8-10
+ Calls.VOICEMAIL_TYPE, // Group 3: Stand-alone
+ Calls.INCOMING_TYPE, // Group 4: Stand-alone
+ Calls.VOICEMAIL_TYPE, // Group 5: Stand-alone
+ Calls.MISSED_TYPE, // Group 6: 8-10
Calls.MISSED_TYPE,
Calls.OUTGOING_TYPE);
mBuilder.addGroups(mCursor);
- assertEquals(2, mFakeGroupCreator.groups.size());
- assertGroupIs(1, 4, mFakeGroupCreator.groups.get(0));
- assertGroupIs(8, 3, mFakeGroupCreator.groups.get(1));
+ assertEquals(6, mFakeGroupCreator.groups.size());
+ assertGroupIs(0, 1, mFakeGroupCreator.groups.get(0));
+ assertGroupIs(1, 4, mFakeGroupCreator.groups.get(1));
+ assertGroupIs(5, 1, mFakeGroupCreator.groups.get(2));
+ assertGroupIs(6, 1, mFakeGroupCreator.groups.get(3));
+ assertGroupIs(7, 1, mFakeGroupCreator.groups.get(4));
+ assertGroupIs(8, 3, mFakeGroupCreator.groups.get(5));
}
public void testEqualPhoneNumbers() {
@@ -237,7 +241,7 @@
clearFakeGroupCreator();
addMultipleCallLogEntries(TEST_NUMBER1, types);
mBuilder.addGroups(mCursor);
- assertEquals(0, mFakeGroupCreator.groups.size());
+ assertEquals(types.length, mFakeGroupCreator.groups.size());
}
/** Adds a set of calls with the given types, all from the same number, in the old section. */
diff --git a/tests/src/com/android/dialer/voicemail/VoicemailPlaybackTest.java b/tests/src/com/android/dialer/voicemail/VoicemailPlaybackTest.java
index b9c70d3..420a17c 100644
--- a/tests/src/com/android/dialer/voicemail/VoicemailPlaybackTest.java
+++ b/tests/src/com/android/dialer/voicemail/VoicemailPlaybackTest.java
@@ -17,6 +17,7 @@
package com.android.dialer.voicemail;
import static com.android.dialer.voicemail.VoicemailPlaybackPresenter.Tasks.CHECK_FOR_CONTENT;
+import static com.android.dialer.voicemail.VoicemailPlaybackPresenter.Tasks.CHECK_CONTENT_AFTER_CHANGE;
import android.app.Activity;
import android.content.ContentResolver;
@@ -31,7 +32,6 @@
import android.view.View;
import android.widget.TextView;
-import com.android.contacts.common.test.IntegrationTestUtils;
import com.android.dialer.R;
import com.android.dialer.calllog.CallLogActivity;
import com.android.dialer.util.AsyncTaskExecutors;
@@ -61,7 +61,6 @@
private VoicemailPlaybackLayout mLayout;
private Uri mVoicemailUri;
- private IntegrationTestUtils mTestUtils;
private LocaleTestUtils mLocaleTestUtils;
private FakeAsyncTaskExecutor mFakeAsyncTaskExecutor;
@@ -75,7 +74,6 @@
mFakeAsyncTaskExecutor = new FakeAsyncTaskExecutor(getInstrumentation());
AsyncTaskExecutors.setFactoryForTest(mFakeAsyncTaskExecutor.getFactory());
- mTestUtils = new IntegrationTestUtils(getInstrumentation());
// Some of the tests rely on the text - safest to force a specific locale.
mLocaleTestUtils = new LocaleTestUtils(getInstrumentation().getTargetContext());
@@ -95,56 +93,73 @@
mLocaleTestUtils.restoreLocale();
mLocaleTestUtils = null;
- mLayout = null;
- mPresenter = null;
- mTestUtils = null;
+ mPresenter.clearInstance();
AsyncTaskExecutors.setFactoryForTest(null);
+ mActivity = null;
+ mPresenter = null;
+ mLayout = null;
+
super.tearDown();
}
public void testFetchingVoicemail() throws Throwable {
- setUriForRealFileVoicemailEntry();
+ setUriForUnfetchedVoicemailEntry();
setPlaybackViewForPresenter();
- assertHasOneTextViewContaining("Loading voicemail");
+
+ getInstrumentation().runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ mPresenter.resumePlayback();
+ }
+ });
+ mFakeAsyncTaskExecutor.runTask(CHECK_FOR_CONTENT);
+ getInstrumentation().waitForIdleSync();
+
+ assertStateTextContains("Loading voicemail");
}
public void testWhenCheckForContentCompletes() throws Throwable {
setUriForRealFileVoicemailEntry();
setPlaybackViewForPresenter();
- // There is a background check that is testing to see if we have the content available.
- // Once that task completes, we shouldn't be showing the fetching message.
+ getInstrumentation().runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ mPresenter.resumePlayback();
+ }
+ });
mFakeAsyncTaskExecutor.runTask(CHECK_FOR_CONTENT);
getInstrumentation().waitForIdleSync();
- assertHasOneTextViewContaining("Buffering");
- assertHasZeroTextViewsContaining("Loading voicemail");
+ // Since the content is already fetched, don't show the loading message.
+ assertStateTextNotContains("Loading voicemail");
}
public void testInvalidVoicemailShowsErrorMessage() throws Throwable {
setUriForInvalidVoicemailEntry();
setPlaybackViewForPresenter();
+ getInstrumentation().runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ mPresenter.resumePlayback();
+ }
+ });
mFakeAsyncTaskExecutor.runTask(CHECK_FOR_CONTENT);
getInstrumentation().waitForIdleSync();
// The media player will have thrown an IOException since the file doesn't exist.
// This should have put a failed to play message on screen, buffering is gone.
- assertHasOneTextViewContaining("Couldn't play voicemail");
- assertHasZeroTextViewsContaining("Buffering");
+ assertStateTextContains("Couldn't play voicemail");
+ assertStateTextNotContains("Buffering");
}
public void testClickingSpeakerphoneButton() throws Throwable {
setUriForRealFileVoicemailEntry();
setPlaybackViewForPresenter();
- // Wait for check for content to complete.
- mFakeAsyncTaskExecutor.runTask(CHECK_FOR_CONTENT);
- getInstrumentation().waitForIdleSync();
-
- // Force the speakerphone to false to start.
- mPresenter.setSpeakerphoneOn(false);
+ // Check that the speakerphone is false to start.
assertFalse(mPresenter.isSpeakerphoneOn());
View speakerphoneButton = mLayout.findViewById(R.id.playback_speakerphone);
@@ -177,6 +192,18 @@
}
}
+ private void setUriForUnfetchedVoicemailEntry() {
+ assertNull(mVoicemailUri);
+ ContentValues values = new ContentValues();
+ values.put(VoicemailContract.Voicemails.DATE, String.valueOf(System.currentTimeMillis()));
+ values.put(VoicemailContract.Voicemails.NUMBER, CONTACT_NUMBER);
+ values.put(VoicemailContract.Voicemails.MIME_TYPE, MIME_TYPE);
+ values.put(VoicemailContract.Voicemails.HAS_CONTENT, 0);
+ String packageName = getInstrumentation().getTargetContext().getPackageName();
+ mVoicemailUri = getContentResolver().insert(
+ VoicemailContract.Voicemails.buildSourceUri(packageName), values);
+ }
+
private void setUriForInvalidVoicemailEntry() {
assertNull(mVoicemailUri);
ContentResolver contentResolver = getContentResolver();
@@ -205,18 +232,14 @@
}
}
- private void assertHasOneTextViewContaining(String text) throws Throwable {
+ private void assertStateTextContains(String text) throws Throwable {
assertNotNull(mLayout);
- List<TextView> views = mTestUtils.getTextViewsWithString(mLayout, text);
- assertEquals("There should have been one TextView with text '" + text + "' but found "
- + views, 1, views.size());
+ assertTrue(mLayout.getStateText().contains(text));
}
- private void assertHasZeroTextViewsContaining(String text) throws Throwable {
+ private void assertStateTextNotContains(String text) throws Throwable {
assertNotNull(mLayout);
- List<TextView> views = mTestUtils.getTextViewsWithString(mLayout, text);
- assertEquals("There should have been no TextViews with text '" + text + "' but found "
- + views, 0, views.size());
+ assertFalse(mLayout.getStateText().contains(text));
}
private ContentResolver getContentResolver() {