Dialer: support sub and call-type filters for msim call log
src/com/android/dialer/calllog/MSimCallLogFragment.java is copied
form the file src/com/android/dialer/calllog/CallLogFragment.java in the
commit b84df80a6f6fd663bb5dd6757d142a1a09fe7678
src/com/android/dialer/calllog/SpinnerContent.java is copied from
src/com/android/dialer/calllog/SpinnerContent.java in the commit
066b90ea588e102042434fcdcb7222ad295ac3c0
Change-Id: I1b0183fe376b9148bb534eb862e50f4dbdb93818
CRs-Fixed: 934138
diff --git a/src/com/android/dialer/calllog/MSimCallLogFragment.java b/src/com/android/dialer/calllog/MSimCallLogFragment.java
new file mode 100644
index 0000000..33525db
--- /dev/null
+++ b/src/com/android/dialer/calllog/MSimCallLogFragment.java
@@ -0,0 +1,703 @@
+/*
+ * Copyright (C) 2011 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.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
+import android.app.Activity;
+import android.app.DialogFragment;
+import android.app.KeyguardManager;
+import android.content.Context;
+import android.content.Intent;
+import android.database.ContentObserver;
+import android.database.Cursor;
+import android.graphics.Rect;
+import android.os.Bundle;
+import android.os.Handler;
+import android.provider.CallLog;
+import android.provider.CallLog.Calls;
+import android.provider.ContactsContract;
+import android.provider.VoicemailContract.Status;
+import android.util.MutableInt;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewTreeObserver;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup.LayoutParams;
+import android.widget.ListView;
+import android.widget.TextView;
+
+import com.android.contacts.common.GeoUtil;
+import com.android.contacts.common.util.ViewUtil;
+import com.android.dialer.R;
+import com.android.dialer.list.ListsFragment.HostInterface;
+import com.android.dialer.util.DialerUtils;
+import com.android.dialer.util.EmptyLoader;
+import com.android.dialer.voicemail.VoicemailStatusHelper;
+import com.android.dialer.voicemail.VoicemailStatusHelper.StatusMessage;
+import com.android.dialer.voicemail.VoicemailStatusHelperImpl;
+import com.android.dialerbind.ObjectFactory;
+import com.android.dialerbind.analytics.AnalyticsListFragment;
+
+import java.util.List;
+
+/**
+ * Displays a list of call log entries. To filter for a particular kind of call
+ * (all, missed or voicemails), specify it in the constructor.
+ */
+public class CallLogFragment extends AnalyticsListFragment
+ implements CallLogQueryHandler.Listener, CallLogAdapter.OnReportButtonClickListener,
+ CallLogAdapter.CallFetcher,
+ CallLogAdapter.CallItemExpandedListener {
+ private static final String TAG = "CallLogFragment";
+
+ private static final String REPORT_DIALOG_TAG = "report_dialog";
+ private String mReportDialogNumber;
+ private boolean mIsReportDialogShowing;
+
+ /**
+ * ID of the empty loader to defer other fragments.
+ */
+ private static final int EMPTY_LOADER_ID = 0;
+
+ private static final String KEY_FILTER_TYPE = "filter_type";
+ private static final String KEY_LOG_LIMIT = "log_limit";
+ private static final String KEY_DATE_LIMIT = "date_limit";
+ private static final String KEY_SHOW_FOOTER = "show_footer";
+ private static final String KEY_IS_REPORT_DIALOG_SHOWING = "is_report_dialog_showing";
+ private static final String KEY_REPORT_DIALOG_NUMBER = "report_dialog_number";
+
+ protected CallLogAdapter mAdapter;
+ protected CallLogQueryHandler mCallLogQueryHandler;
+ private boolean mScrollToTop;
+
+ /** Whether there is at least one voicemail source installed. */
+ protected boolean mVoicemailSourcesAvailable = false;
+
+ protected VoicemailStatusHelper mVoicemailStatusHelper;
+ protected View mStatusMessageView;
+ protected TextView mStatusMessageText;
+ protected TextView mStatusMessageAction;
+ private KeyguardManager mKeyguardManager;
+ private View mFooterView;
+
+ private boolean mEmptyLoaderRunning;
+ private boolean mCallLogFetched;
+ private boolean mVoicemailStatusFetched;
+
+ private float mExpandedItemTranslationZ;
+ private int mFadeInDuration;
+ private int mFadeInStartDelay;
+ private int mFadeOutDuration;
+ private int mExpandCollapseDuration;
+
+ private final Handler mHandler = new Handler();
+
+ private class CustomContentObserver extends ContentObserver {
+ public CustomContentObserver() {
+ super(mHandler);
+ }
+ @Override
+ public void onChange(boolean selfChange) {
+ mRefreshDataRequired = true;
+ }
+ }
+
+ // See issue 6363009
+ private final ContentObserver mCallLogObserver = new CustomContentObserver();
+ private final ContentObserver mContactsObserver = new CustomContentObserver();
+ private final ContentObserver mVoicemailStatusObserver = new CustomContentObserver();
+ private boolean mRefreshDataRequired = true;
+
+ // Exactly same variable is in Fragment as a package private.
+ private boolean mMenuVisible = true;
+
+ // Default to all calls.
+ protected int mCallTypeFilter = CallLogQueryHandler.CALL_TYPE_ALL;
+
+ // Log limit - if no limit is specified, then the default in {@link CallLogQueryHandler}
+ // will be used.
+ private int mLogLimit = -1;
+
+ // Date limit (in millis since epoch) - when non-zero, only calls which occurred on or after
+ // the date filter are included. If zero, no date-based filtering occurs.
+ private long mDateLimit = 0;
+
+ // Whether or not to show the Show call history footer view
+ private boolean mHasFooterView = false;
+
+ public CallLogFragment() {
+ this(CallLogQueryHandler.CALL_TYPE_ALL, -1);
+ }
+
+ public CallLogFragment(int filterType) {
+ this(filterType, -1);
+ }
+
+ public CallLogFragment(int filterType, int logLimit) {
+ super();
+ mCallTypeFilter = filterType;
+ mLogLimit = logLimit;
+ }
+
+ /**
+ * Creates a call log fragment, filtering to include only calls of the desired type, occurring
+ * after the specified date.
+ * @param filterType type of calls to include.
+ * @param dateLimit limits results to calls occurring on or after the specified date.
+ */
+ public CallLogFragment(int filterType, long dateLimit) {
+ this(filterType, -1, dateLimit);
+ }
+
+ /**
+ * Creates a call log fragment, filtering to include only calls of the desired type, occurring
+ * after the specified date. Also provides a means to limit the number of results returned.
+ * @param filterType type of calls to include.
+ * @param logLimit limits the number of results to return.
+ * @param dateLimit limits results to calls occurring on or after the specified date.
+ */
+ public CallLogFragment(int filterType, int logLimit, long dateLimit) {
+ this(filterType, logLimit);
+ mDateLimit = dateLimit;
+ }
+
+ @Override
+ public void onCreate(Bundle state) {
+ super.onCreate(state);
+ if (state != null) {
+ mCallTypeFilter = state.getInt(KEY_FILTER_TYPE, mCallTypeFilter);
+ mLogLimit = state.getInt(KEY_LOG_LIMIT, mLogLimit);
+ mDateLimit = state.getLong(KEY_DATE_LIMIT, mDateLimit);
+ mHasFooterView = state.getBoolean(KEY_SHOW_FOOTER, mHasFooterView);
+ mIsReportDialogShowing = state.getBoolean(KEY_IS_REPORT_DIALOG_SHOWING,
+ mIsReportDialogShowing);
+ mReportDialogNumber = state.getString(KEY_REPORT_DIALOG_NUMBER, mReportDialogNumber);
+ }
+
+ String currentCountryIso = GeoUtil.getCurrentCountryIso(getActivity());
+ mAdapter = ObjectFactory.newCallLogAdapter(getActivity(), this,
+ new ContactInfoHelper(getActivity(), currentCountryIso), this, this, true);
+ setListAdapter(mAdapter);
+ mCallLogQueryHandler = new CallLogQueryHandler(getActivity().getContentResolver(),
+ this, mLogLimit);
+ mKeyguardManager =
+ (KeyguardManager) getActivity().getSystemService(Context.KEYGUARD_SERVICE);
+ getActivity().getContentResolver().registerContentObserver(CallLog.CONTENT_URI, true,
+ mCallLogObserver);
+ getActivity().getContentResolver().registerContentObserver(
+ ContactsContract.Contacts.CONTENT_URI, true, mContactsObserver);
+ getActivity().getContentResolver().registerContentObserver(
+ Status.CONTENT_URI, true, mVoicemailStatusObserver);
+ setHasOptionsMenu(true);
+ updateCallList(mCallTypeFilter, mDateLimit);
+
+ mExpandedItemTranslationZ =
+ getResources().getDimension(R.dimen.call_log_expanded_translation_z);
+ mFadeInDuration = getResources().getInteger(R.integer.call_log_actions_fade_in_duration);
+ mFadeInStartDelay = getResources().getInteger(R.integer.call_log_actions_fade_start);
+ mFadeOutDuration = getResources().getInteger(R.integer.call_log_actions_fade_out_duration);
+ mExpandCollapseDuration = getResources().getInteger(
+ R.integer.call_log_expand_collapse_duration);
+
+ if (mIsReportDialogShowing) {
+ DialogFragment df = ObjectFactory.getReportDialogFragment(mReportDialogNumber);
+ if (df != null) {
+ df.setTargetFragment(this, 0);
+ df.show(getActivity().getFragmentManager(), REPORT_DIALOG_TAG);
+ }
+ }
+ }
+
+ /** Called by the CallLogQueryHandler when the list of calls has been fetched or updated. */
+ @Override
+ public boolean onCallsFetched(Cursor cursor) {
+ if (getActivity() == null || getActivity().isFinishing()) {
+ // Return false; we did not take ownership of the cursor
+ return false;
+ }
+ mAdapter.setLoading(false);
+ mAdapter.changeCursor(cursor);
+ // This will update the state of the "Clear call log" menu item.
+ getActivity().invalidateOptionsMenu();
+ if (mScrollToTop) {
+ final ListView listView = getListView();
+ // The smooth-scroll animation happens over a fixed time period.
+ // As a result, if it scrolls through a large portion of the list,
+ // each frame will jump so far from the previous one that the user
+ // will not experience the illusion of downward motion. Instead,
+ // if we're not already near the top of the list, we instantly jump
+ // near the top, and animate from there.
+ if (listView.getFirstVisiblePosition() > 5) {
+ listView.setSelection(5);
+ }
+ // Workaround for framework issue: the smooth-scroll doesn't
+ // occur if setSelection() is called immediately before.
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ if (getActivity() == null || getActivity().isFinishing()) {
+ return;
+ }
+ listView.smoothScrollToPosition(0);
+ }
+ });
+
+ mScrollToTop = false;
+ }
+ mCallLogFetched = true;
+ destroyEmptyLoaderIfAllDataFetched();
+ return true;
+ }
+
+ /**
+ * Called by {@link CallLogQueryHandler} after a successful query to voicemail status provider.
+ */
+ @Override
+ public void onVoicemailStatusFetched(Cursor statusCursor) {
+ if (getActivity() == null || getActivity().isFinishing()) {
+ return;
+ }
+ updateVoicemailStatusMessage(statusCursor);
+
+ int activeSources = mVoicemailStatusHelper.getNumberActivityVoicemailSources(statusCursor);
+ setVoicemailSourcesAvailable(activeSources != 0);
+ mVoicemailStatusFetched = true;
+ destroyEmptyLoaderIfAllDataFetched();
+ }
+
+ private void destroyEmptyLoaderIfAllDataFetched() {
+ if (mCallLogFetched && mVoicemailStatusFetched && mEmptyLoaderRunning) {
+ mEmptyLoaderRunning = false;
+ getLoaderManager().destroyLoader(EMPTY_LOADER_ID);
+ }
+ }
+
+ /** Sets whether there are any voicemail sources available in the platform. */
+ protected void setVoicemailSourcesAvailable(boolean voicemailSourcesAvailable) {
+ if (mVoicemailSourcesAvailable == voicemailSourcesAvailable) return;
+ mVoicemailSourcesAvailable = voicemailSourcesAvailable;
+
+ Activity activity = getActivity();
+ if (activity != null) {
+ // This is so that the options menu content is updated.
+ activity.invalidateOptionsMenu();
+ }
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedState) {
+ View view = inflater.inflate(R.layout.call_log_fragment, container, false);
+ mVoicemailStatusHelper = new VoicemailStatusHelperImpl();
+ mStatusMessageView = view.findViewById(R.id.voicemail_status);
+ mStatusMessageText = (TextView) view.findViewById(R.id.voicemail_status_message);
+ mStatusMessageAction = (TextView) view.findViewById(R.id.voicemail_status_action);
+ return view;
+ }
+
+ @Override
+ public void onViewCreated(View view, Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
+ getListView().setEmptyView(view.findViewById(R.id.empty_list_view));
+ getListView().setItemsCanFocus(true);
+ maybeAddFooterView();
+
+ updateEmptyMessage(mCallTypeFilter);
+ }
+
+ /**
+ * Based on the new intent, decide whether the list should be configured
+ * to scroll up to display the first item.
+ */
+ public void configureScreenFromIntent(Intent newIntent) {
+ // Typically, when switching to the call-log we want to show the user
+ // the same section of the list that they were most recently looking
+ // at. However, under some circumstances, we want to automatically
+ // scroll to the top of the list to present the newest call items.
+ // For example, immediately after a call is finished, we want to
+ // display information about that call.
+ mScrollToTop = Calls.CONTENT_TYPE.equals(newIntent.getType());
+ }
+
+ @Override
+ public void onStart() {
+ // Start the empty loader now to defer other fragments. We destroy it when both calllog
+ // and the voicemail status are fetched.
+ getLoaderManager().initLoader(EMPTY_LOADER_ID, null,
+ new EmptyLoader.Callback(getActivity()));
+ mEmptyLoaderRunning = true;
+ super.onStart();
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ refreshData();
+ }
+
+ private void updateVoicemailStatusMessage(Cursor statusCursor) {
+ List<StatusMessage> messages = mVoicemailStatusHelper.getStatusMessages(statusCursor);
+ if (messages.size() == 0) {
+ mStatusMessageView.setVisibility(View.GONE);
+ } else {
+ mStatusMessageView.setVisibility(View.VISIBLE);
+ // TODO: Change the code to show all messages. For now just pick the first message.
+ final StatusMessage message = messages.get(0);
+ if (message.showInCallLog()) {
+ mStatusMessageText.setText(message.callLogMessageId);
+ }
+ if (message.actionMessageId != -1) {
+ mStatusMessageAction.setText(message.actionMessageId);
+ }
+ if (message.actionUri != null) {
+ mStatusMessageAction.setVisibility(View.VISIBLE);
+ mStatusMessageAction.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ getActivity().startActivity(
+ new Intent(Intent.ACTION_VIEW, message.actionUri));
+ }
+ });
+ } else {
+ mStatusMessageAction.setVisibility(View.GONE);
+ }
+ }
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ // Kill the requests thread
+ mAdapter.stopRequestProcessing();
+ }
+
+ @Override
+ public void onStop() {
+ super.onStop();
+ updateOnExit();
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ mAdapter.stopRequestProcessing();
+ mAdapter.changeCursor(null);
+ getActivity().getContentResolver().unregisterContentObserver(mCallLogObserver);
+ getActivity().getContentResolver().unregisterContentObserver(mContactsObserver);
+ getActivity().getContentResolver().unregisterContentObserver(mVoicemailStatusObserver);
+ }
+
+ @Override
+ public void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ outState.putInt(KEY_FILTER_TYPE, mCallTypeFilter);
+ outState.putInt(KEY_LOG_LIMIT, mLogLimit);
+ outState.putLong(KEY_DATE_LIMIT, mDateLimit);
+ outState.putBoolean(KEY_SHOW_FOOTER, mHasFooterView);
+ outState.putBoolean(KEY_IS_REPORT_DIALOG_SHOWING, mIsReportDialogShowing);
+ outState.putString(KEY_REPORT_DIALOG_NUMBER, mReportDialogNumber);
+ }
+
+ @Override
+ public void fetchCalls() {
+ mCallLogQueryHandler.fetchCalls(mCallTypeFilter, mDateLimit);
+ }
+
+ public void startCallsQuery() {
+ mAdapter.setLoading(true);
+ mCallLogQueryHandler.fetchCalls(mCallTypeFilter, mDateLimit);
+ }
+
+ private void startVoicemailStatusQuery() {
+ mCallLogQueryHandler.fetchVoicemailStatus();
+ }
+
+ private void updateCallList(int filterType, long dateLimit) {
+ mCallLogQueryHandler.fetchCalls(filterType, dateLimit);
+ }
+
+ private void updateEmptyMessage(int filterType) {
+ final int messageId;
+ switch (filterType) {
+ case Calls.MISSED_TYPE:
+ messageId = R.string.recentMissed_empty;
+ break;
+ case Calls.VOICEMAIL_TYPE:
+ messageId = R.string.recentVoicemails_empty;
+ break;
+ case CallLogQueryHandler.CALL_TYPE_ALL:
+ messageId = R.string.recentCalls_empty;
+ break;
+ default:
+ throw new IllegalArgumentException("Unexpected filter type in CallLogFragment: "
+ + filterType);
+ }
+ DialerUtils.configureEmptyListView(
+ getListView().getEmptyView(), R.drawable.empty_call_log, messageId, getResources());
+ }
+
+ CallLogAdapter getAdapter() {
+ return mAdapter;
+ }
+
+ @Override
+ public void setMenuVisibility(boolean menuVisible) {
+ super.setMenuVisibility(menuVisible);
+ if (mMenuVisible != menuVisible) {
+ mMenuVisible = menuVisible;
+ if (!menuVisible) {
+ updateOnExit();
+ } else if (isResumed()) {
+ refreshData();
+ }
+ }
+ }
+
+ /** Requests updates to the data to be shown. */
+ private void refreshData() {
+ // Prevent unnecessary refresh.
+ if (mRefreshDataRequired) {
+ // Mark all entries in the contact info cache as out of date, so they will be looked up
+ // again once being shown.
+ mAdapter.invalidateCache();
+ startCallsQuery();
+ startVoicemailStatusQuery();
+ updateOnEntry();
+ mRefreshDataRequired = false;
+ }
+ }
+
+ /** Updates call data and notification state while leaving the call log tab. */
+ private void updateOnExit() {
+ updateOnTransition(false);
+ }
+
+ /** Updates call data and notification state while entering the call log tab. */
+ private void updateOnEntry() {
+ updateOnTransition(true);
+ }
+
+ // TODO: Move to CallLogActivity
+ private void updateOnTransition(boolean onEntry) {
+ // We don't want to update any call data when keyguard is on because the user has likely not
+ // seen the new calls yet.
+ // This might be called before onCreate() and thus we need to check null explicitly.
+ if (mKeyguardManager != null && !mKeyguardManager.inKeyguardRestrictedInputMode()) {
+ // On either of the transitions we update the missed call and voicemail notifications.
+ // While exiting we additionally consume all missed calls (by marking them as read).
+ mCallLogQueryHandler.markNewCallsAsOld();
+ if (!onEntry) {
+ mCallLogQueryHandler.markMissedCallsAsRead();
+ }
+ CallLogNotificationsHelper.removeMissedCallNotifications(getActivity());
+ CallLogNotificationsHelper.updateVoicemailNotifications(getActivity());
+ }
+ }
+
+ /**
+ * Enables/disables the showing of the view full call history footer
+ *
+ * @param hasFooterView Whether or not to show the footer
+ */
+ public void setHasFooterView(boolean hasFooterView) {
+ mHasFooterView = hasFooterView;
+ maybeAddFooterView();
+ }
+
+ /**
+ * Determine whether or not the footer view should be added to the listview. If getView()
+ * is null, which means onCreateView hasn't been called yet, defer the addition of the footer
+ * until onViewCreated has been called.
+ */
+ private void maybeAddFooterView() {
+ if (!mHasFooterView || getView() == null) {
+ return;
+ }
+
+ if (mFooterView == null) {
+ mFooterView = getActivity().getLayoutInflater().inflate(
+ R.layout.recents_list_footer, getListView(), false);
+ mFooterView.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ ((HostInterface) getActivity()).showCallHistory();
+ }
+ });
+ }
+
+ final ListView listView = getListView();
+ listView.removeFooterView(mFooterView);
+ listView.addFooterView(mFooterView);
+
+ ViewUtil.addBottomPaddingToListViewForFab(listView, getResources());
+ }
+
+ @Override
+ public void onItemExpanded(final CallLogListItemView view) {
+ final int startingHeight = view.getHeight();
+ final CallLogListItemViews viewHolder = (CallLogListItemViews) view.getTag();
+ final ViewTreeObserver observer = getListView().getViewTreeObserver();
+ observer.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
+ @Override
+ public boolean onPreDraw() {
+ // We don't want to continue getting called for every draw.
+ if (observer.isAlive()) {
+ observer.removeOnPreDrawListener(this);
+ }
+ // Calculate some values to help with the animation.
+ final int endingHeight = view.getHeight();
+ final int distance = Math.abs(endingHeight - startingHeight);
+ final int baseHeight = Math.min(endingHeight, startingHeight);
+ final boolean isExpand = endingHeight > startingHeight;
+
+ // Set the views back to the start state of the animation
+ view.getLayoutParams().height = startingHeight;
+ if (!isExpand) {
+ viewHolder.actionsView.setVisibility(View.VISIBLE);
+ }
+ CallLogAdapter.expandVoicemailTranscriptionView(viewHolder, !isExpand);
+
+ // Set up the fade effect for the action buttons.
+ if (isExpand) {
+ // Start the fade in after the expansion has partly completed, otherwise it
+ // will be mostly over before the expansion completes.
+ viewHolder.actionsView.setAlpha(0f);
+ viewHolder.actionsView.animate()
+ .alpha(1f)
+ .setStartDelay(mFadeInStartDelay)
+ .setDuration(mFadeInDuration)
+ .start();
+ } else {
+ viewHolder.actionsView.setAlpha(1f);
+ viewHolder.actionsView.animate()
+ .alpha(0f)
+ .setDuration(mFadeOutDuration)
+ .start();
+ }
+ view.requestLayout();
+
+ // Set up the animator to animate the expansion and shadow depth.
+ ValueAnimator animator = isExpand ? ValueAnimator.ofFloat(0f, 1f)
+ : ValueAnimator.ofFloat(1f, 0f);
+
+ // Figure out how much scrolling is needed to make the view fully visible.
+ final Rect localVisibleRect = new Rect();
+ view.getLocalVisibleRect(localVisibleRect);
+ final int scrollingNeeded = localVisibleRect.top > 0 ? -localVisibleRect.top
+ : view.getMeasuredHeight() - localVisibleRect.height();
+ final ListView listView = getListView();
+ animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+
+ private int mCurrentScroll = 0;
+
+ @Override
+ public void onAnimationUpdate(ValueAnimator animator) {
+ Float value = (Float) animator.getAnimatedValue();
+
+ // For each value from 0 to 1, animate the various parts of the layout.
+ view.getLayoutParams().height = (int) (value * distance + baseHeight);
+ float z = mExpandedItemTranslationZ * value;
+ viewHolder.callLogEntryView.setTranslationZ(z);
+ view.setTranslationZ(z); // WAR
+ view.requestLayout();
+
+ if (isExpand) {
+ if (listView != null) {
+ int scrollBy = (int) (value * scrollingNeeded) - mCurrentScroll;
+ listView.smoothScrollBy(scrollBy, /* duration = */ 0);
+ mCurrentScroll += scrollBy;
+ }
+ }
+ }
+ });
+ // Set everything to their final values when the animation's done.
+ animator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ view.getLayoutParams().height = LayoutParams.WRAP_CONTENT;
+
+ if (!isExpand) {
+ viewHolder.actionsView.setVisibility(View.GONE);
+ } else {
+ // This seems like it should be unnecessary, but without this, after
+ // navigating out of the activity and then back, the action view alpha
+ // is defaulting to the value (0) at the start of the expand animation.
+ viewHolder.actionsView.setAlpha(1);
+ }
+ CallLogAdapter.expandVoicemailTranscriptionView(viewHolder, isExpand);
+ }
+ });
+
+ animator.setDuration(mExpandCollapseDuration);
+ animator.start();
+
+ // Return false so this draw does not occur to prevent the final frame from
+ // being drawn for the single frame before the animations start.
+ return false;
+ }
+ });
+ }
+
+ /**
+ * Retrieves the call log view for the specified call Id. If the view is not currently
+ * visible, returns null.
+ *
+ * @param callId The call Id.
+ * @return The call log view.
+ */
+ @Override
+ public CallLogListItemView getViewForCallId(long callId) {
+ ListView listView = getListView();
+
+ int firstPosition = listView.getFirstVisiblePosition();
+ int lastPosition = listView.getLastVisiblePosition();
+
+ for (int position = 0; position <= lastPosition - firstPosition; position++) {
+ View view = listView.getChildAt(position);
+
+ if (view != null) {
+ final CallLogListItemViews viewHolder = (CallLogListItemViews) view.getTag();
+ if (viewHolder != null && viewHolder.rowId == callId) {
+ return (CallLogListItemView)view;
+ }
+ }
+ }
+
+ return null;
+ }
+
+ public void onBadDataReported(String number) {
+ mIsReportDialogShowing = false;
+ if (number == null) {
+ return;
+ }
+ mAdapter.onBadDataReported(number);
+ mAdapter.notifyDataSetChanged();
+ }
+
+ public void onReportButtonClick(String number) {
+ DialogFragment df = ObjectFactory.getReportDialogFragment(number);
+ if (df != null) {
+ df.setTargetFragment(this, 0);
+ df.show(getActivity().getFragmentManager(), REPORT_DIALOG_TAG);
+ mReportDialogNumber = number;
+ mIsReportDialogShowing = true;
+ }
+ }
+}
diff --git a/src/com/android/dialer/calllog/SpinnerContent.java b/src/com/android/dialer/calllog/SpinnerContent.java
new file mode 100644
index 0000000..614b594
--- /dev/null
+++ b/src/com/android/dialer/calllog/SpinnerContent.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2014 The CyanogenMod 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.content.Context;
+import android.provider.CallLog;
+import android.telephony.TelephonyManager;
+import android.text.TextUtils;
+import android.util.Log;
+import android.widget.Spinner;
+
+import com.android.contacts.common.MoreContactUtils;
+import com.android.dialer.R;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * To save the spinner content.
+ */
+public class SpinnerContent {
+ private static String TAG = SpinnerContent.class.getSimpleName();
+
+ public final int value;
+ public final String label;
+
+ // The index for call type spinner.
+ private static final int INDEX_CALL_TYPE_ALL = 0;
+ private static final int INDEX_CALL_TYPE_INCOMING = 1;
+ private static final int INDEX_CALL_TYPE_OUTGOING = 2;
+ private static final int INDEX_CALL_TYPE_MISSED = 3;
+ private static final int INDEX_CALL_TYPE_VOICEMAIL = 4;
+
+ public static void setSpinnerContentValue(Spinner spinner, int value) {
+ for (int i = 0, count = spinner.getCount(); i < count; i++) {
+ SpinnerContent sc = (SpinnerContent) spinner.getItemAtPosition(i);
+ if (sc.value == value) {
+ spinner.setSelection(i, true);
+ Log.i(TAG, "Set selection for spinner(" + sc + ") with the value: " + value);
+ return;
+ }
+ }
+ }
+
+ public SpinnerContent(int value, String label) {
+ this.value = value;
+ this.label = label;
+ }
+
+ @Override
+ public String toString() {
+ return label;
+ }
+
+ /**
+ * @return the spinner contents for the different sims (all, sim0, sim1 etc)
+ */
+ public static List<SpinnerContent> setupSubFilterContent(Context context) {
+ TelephonyManager telephonyManager =
+ (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
+ int count = telephonyManager.getPhoneCount();
+ // Update the filter sub content.
+ ArrayList<SpinnerContent> values = new ArrayList<SpinnerContent>(count + 1);
+ values.add(new SpinnerContent(CallLogQueryHandler.CALL_SUB_ALL,
+ context.getString(R.string.call_log_show_all_slots)));
+ for (int i = 0; i < count; i++) {
+ String subDisplayName = PhoneAccountUtils.getAccountLabel(context,
+ MoreContactUtils.getAccount(i));
+ if (!TextUtils.isEmpty(subDisplayName)) {
+ values.add(new SpinnerContent(i, subDisplayName));
+ }
+ }
+ return values;
+ }
+
+ /**
+ * @param voicemailAvailable true if voicemail should be included in the return values
+ * @return the spinner contents for the different call types (incoming, outgoing etc)
+ */
+ public static List<SpinnerContent> setupStatusFilterContent(Context context,
+ boolean voicemailAvailable) {
+ // Didn't show the voice mail item if not available.
+ int statusCount = voicemailAvailable ? 5 : 4;
+ ArrayList<SpinnerContent> values = new ArrayList<SpinnerContent>(statusCount);
+ for (int i = 0; i < statusCount; i++) {
+ int value = CallLogQueryHandler.CALL_TYPE_ALL;
+ String label = null;
+ switch (i) {
+ case INDEX_CALL_TYPE_ALL:
+ value = CallLogQueryHandler.CALL_TYPE_ALL;
+ label = context.getString(R.string.call_log_all_calls_header);
+ break;
+ case INDEX_CALL_TYPE_INCOMING:
+ value = CallLog.Calls.INCOMING_TYPE;
+ label = context.getString(R.string.call_log_incoming_header);
+ break;
+ case INDEX_CALL_TYPE_OUTGOING:
+ value = CallLog.Calls.OUTGOING_TYPE;
+ label = context.getString(R.string.call_log_outgoing_header);
+ break;
+ case INDEX_CALL_TYPE_MISSED:
+ value = CallLog.Calls.MISSED_TYPE;
+ label = context.getString(R.string.call_log_missed_header);
+ break;
+ case INDEX_CALL_TYPE_VOICEMAIL:
+ value = CallLog.Calls.VOICEMAIL_TYPE;
+ label = context.getString(R.string.call_log_voicemail_header);
+ break;
+ }
+ values.add(new SpinnerContent(value, label));
+ }
+ return values;
+ }
+}