| package com.android.dialer.list; |
| |
| import android.animation.LayoutTransition; |
| import android.app.ActionBar; |
| import android.app.Fragment; |
| import android.app.FragmentManager; |
| import android.app.LoaderManager; |
| import android.content.Context; |
| import android.content.CursorLoader; |
| import android.content.Loader; |
| import android.content.SharedPreferences; |
| import android.database.Cursor; |
| import android.net.Uri; |
| import android.os.Bundle; |
| import android.provider.CallLog; |
| import android.support.v13.app.FragmentPagerAdapter; |
| import android.support.v4.view.ViewPager; |
| import android.support.v4.view.ViewPager.OnPageChangeListener; |
| import android.util.Log; |
| import android.view.LayoutInflater; |
| import android.view.View; |
| import android.view.ViewGroup; |
| import android.widget.ListView; |
| |
| import com.android.contacts.common.GeoUtil; |
| import com.android.contacts.common.list.ViewPagerTabs; |
| import com.android.dialer.DialtactsActivity; |
| import com.android.dialer.R; |
| import com.android.dialer.calllog.CallLogAdapter; |
| import com.android.dialer.calllog.CallLogFragment; |
| import com.android.dialer.calllog.CallLogQuery; |
| import com.android.dialer.calllog.CallLogQueryHandler; |
| import com.android.dialer.calllog.ContactInfoHelper; |
| import com.android.dialer.list.ShortcutCardsAdapter.SwipeableShortcutCard; |
| import com.android.dialer.util.DialerUtils; |
| import com.android.dialer.widget.OverlappingPaneLayout; |
| import com.android.dialer.widget.OverlappingPaneLayout.PanelSlideListener; |
| import com.android.dialerbind.analytics.AnalyticsFragment; |
| import com.android.dialerbind.ObjectFactory; |
| |
| import java.util.ArrayList; |
| |
| /** |
| * Fragment that is used as the main screen of the Dialer. |
| * |
| * Contains a ViewPager that contains various contact lists like the Speed Dial list and the |
| * All Contacts list. This will also eventually contain the logic that allows sliding the |
| * ViewPager containing the lists up above the shortcut cards and pin it against the top of the |
| * screen. |
| */ |
| public class ListsFragment extends AnalyticsFragment implements CallLogQueryHandler.Listener, |
| CallLogAdapter.CallFetcher, ViewPager.OnPageChangeListener { |
| |
| private static final boolean DEBUG = DialtactsActivity.DEBUG; |
| private static final String TAG = "ListsFragment"; |
| |
| public static final int TAB_INDEX_SPEED_DIAL = 0; |
| public static final int TAB_INDEX_RECENTS = 1; |
| public static final int TAB_INDEX_ALL_CONTACTS = 2; |
| |
| public static final int TAB_INDEX_COUNT = 3; |
| |
| private static final int MAX_RECENTS_ENTRIES = 20; |
| // Oldest recents entry to display is 2 weeks old. |
| private static final long OLDEST_RECENTS_DATE = 1000L * 60 * 60 * 24 * 14; |
| |
| private static final String KEY_LAST_DISMISSED_CALL_SHORTCUT_DATE = |
| "key_last_dismissed_call_shortcut_date"; |
| |
| public static final float REMOVE_VIEW_SHOWN_ALPHA = 0.5f; |
| public static final float REMOVE_VIEW_HIDDEN_ALPHA = 1; |
| |
| // Used with LoaderManager |
| private static int MISSED_CALL_LOADER = 1; |
| |
| public interface HostInterface { |
| public void showCallHistory(); |
| public int getActionBarHeight(); |
| public void setActionBarHideOffset(int offset); |
| } |
| |
| private ActionBar mActionBar; |
| private ViewPager mViewPager; |
| private ViewPagerTabs mViewPagerTabs; |
| private ViewPagerAdapter mViewPagerAdapter; |
| private ListView mShortcutCardsListView; |
| private RemoveView mRemoveView; |
| private View mRemoveViewContent; |
| private SpeedDialFragment mSpeedDialFragment; |
| private CallLogFragment mRecentsFragment; |
| private AllContactsFragment mAllContactsFragment; |
| private ArrayList<OnPageChangeListener> mOnPageChangeListeners = |
| new ArrayList<OnPageChangeListener>(); |
| |
| private String[] mTabTitles; |
| |
| private ShortcutCardsAdapter mMergedAdapter; |
| private CallLogAdapter mCallLogAdapter; |
| private CallLogQueryHandler mCallLogQueryHandler; |
| |
| private boolean mIsPanelOpen = true; |
| |
| /** |
| * Call shortcuts older than this date (persisted in shared preferences) will not show up in |
| * at the top of the screen |
| */ |
| private long mLastCallShortcutDate = 0; |
| |
| /** |
| * The date of the current call shortcut that is showing on screen. |
| */ |
| private long mCurrentCallShortcutDate = 0; |
| |
| private class MissedCallLogLoaderListener implements LoaderManager.LoaderCallbacks<Cursor> { |
| |
| @Override |
| public Loader<Cursor> onCreateLoader(int id, Bundle args) { |
| final Uri uri = CallLog.Calls.CONTENT_URI; |
| final String[] projection = new String[] {CallLog.Calls.TYPE}; |
| final String selection = CallLog.Calls.TYPE + " = " + CallLog.Calls.MISSED_TYPE + |
| " AND " + CallLog.Calls.IS_READ + " = 0"; |
| return new CursorLoader(getActivity(), uri, projection, selection, null, null); |
| } |
| |
| @Override |
| public void onLoadFinished(Loader<Cursor> cursorLoader, Cursor data) { |
| mCallLogAdapter.setMissedCalls(data); |
| } |
| |
| @Override |
| public void onLoaderReset(Loader<Cursor> cursorLoader) { |
| } |
| } |
| |
| private PanelSlideListener mPanelSlideListener = new PanelSlideListener() { |
| @Override |
| public void onPanelSlide(View panel, float slideOffset) { |
| // For every 1 percent that the panel is slid upwards, clip 1.5 percent from each edge |
| // of the shortcut card, to achieve the animated effect of the shortcut card |
| // rapidly shrinking and disappearing from view when the panel is slid upwards. |
| // slideOffset is 1 when the shortcut card is fully exposed, and 0 when completely |
| // hidden. |
| float ratioCardHidden = (1 - slideOffset) * 1.5f; |
| if (mShortcutCardsListView.getChildCount() > 0) { |
| final SwipeableShortcutCard v = |
| (SwipeableShortcutCard) mShortcutCardsListView.getChildAt(0); |
| v.clipCard(ratioCardHidden); |
| } |
| |
| if (mActionBar != null) { |
| // Amount of available space that is not being hidden by the bottom pane |
| final int topPaneHeight = (int) (slideOffset * mShortcutCardsListView.getHeight()); |
| |
| final int availableActionBarHeight = |
| Math.min(mActionBar.getHeight(), topPaneHeight); |
| ((HostInterface) getActivity()).setActionBarHideOffset( |
| mActionBar.getHeight() - availableActionBarHeight); |
| |
| if (!mActionBar.isShowing()) { |
| mActionBar.show(); |
| } |
| } |
| } |
| |
| @Override |
| public void onPanelOpened(View panel) { |
| if (DEBUG) { |
| Log.d(TAG, "onPanelOpened"); |
| } |
| mIsPanelOpen = true; |
| } |
| |
| @Override |
| public void onPanelClosed(View panel) { |
| if (DEBUG) { |
| Log.d(TAG, "onPanelClosed"); |
| } |
| mIsPanelOpen = false; |
| } |
| }; |
| |
| public class ViewPagerAdapter extends FragmentPagerAdapter { |
| public ViewPagerAdapter(FragmentManager fm) { |
| super(fm); |
| } |
| |
| @Override |
| public Fragment getItem(int position) { |
| switch (getRtlPosition(position)) { |
| case TAB_INDEX_SPEED_DIAL: |
| mSpeedDialFragment = new SpeedDialFragment(); |
| return mSpeedDialFragment; |
| case TAB_INDEX_RECENTS: |
| mRecentsFragment = new CallLogFragment(CallLogQueryHandler.CALL_TYPE_ALL, |
| MAX_RECENTS_ENTRIES, System.currentTimeMillis() - OLDEST_RECENTS_DATE); |
| mRecentsFragment.setHasFooterView(true); |
| return mRecentsFragment; |
| case TAB_INDEX_ALL_CONTACTS: |
| mAllContactsFragment = new AllContactsFragment(); |
| return mAllContactsFragment; |
| } |
| throw new IllegalStateException("No fragment at position " + position); |
| } |
| |
| @Override |
| public int getCount() { |
| return TAB_INDEX_COUNT; |
| } |
| |
| @Override |
| public CharSequence getPageTitle(int position) { |
| return mTabTitles[position]; |
| } |
| } |
| |
| @Override |
| public void onCreate(Bundle savedInstanceState) { |
| super.onCreate(savedInstanceState); |
| |
| mCallLogQueryHandler = new CallLogQueryHandler(getActivity().getContentResolver(), |
| this, 1); |
| final String currentCountryIso = GeoUtil.getCurrentCountryIso(getActivity()); |
| mCallLogAdapter = ObjectFactory.newCallLogAdapter(getActivity(), this, |
| new ContactInfoHelper(getActivity(), currentCountryIso), null, null, false); |
| |
| mMergedAdapter = new ShortcutCardsAdapter(getActivity(), this, mCallLogAdapter); |
| } |
| |
| @Override |
| public void onStart() { |
| super.onStart(); |
| getLoaderManager().initLoader(MISSED_CALL_LOADER, null, new MissedCallLogLoaderListener()); |
| } |
| |
| @Override |
| public void onResume() { |
| super.onResume(); |
| final SharedPreferences prefs = getActivity().getSharedPreferences( |
| DialtactsActivity.SHARED_PREFS_NAME, Context.MODE_PRIVATE); |
| mLastCallShortcutDate = prefs.getLong(KEY_LAST_DISMISSED_CALL_SHORTCUT_DATE, 0); |
| mActionBar = getActivity().getActionBar(); |
| fetchCalls(); |
| mCallLogAdapter.setLoading(true); |
| } |
| |
| @Override |
| public void onPause() { |
| // Wipe the cache to refresh the call shortcut item. This is not that expensive because |
| // it only contains one item. |
| mCallLogAdapter.invalidateCache(); |
| super.onPause(); |
| } |
| |
| @Override |
| public View onCreateView(LayoutInflater inflater, ViewGroup container, |
| Bundle savedInstanceState) { |
| final View parentView = inflater.inflate(R.layout.lists_fragment, container, false); |
| mViewPager = (ViewPager) parentView.findViewById(R.id.lists_pager); |
| mViewPagerAdapter = new ViewPagerAdapter(getChildFragmentManager()); |
| mViewPager.setAdapter(mViewPagerAdapter); |
| mViewPager.setOffscreenPageLimit(2); |
| mViewPager.setOnPageChangeListener(this); |
| mViewPager.setCurrentItem(getRtlPosition(TAB_INDEX_SPEED_DIAL)); |
| |
| mTabTitles = new String[TAB_INDEX_COUNT]; |
| mTabTitles[TAB_INDEX_SPEED_DIAL] = getResources().getString(R.string.tab_speed_dial); |
| mTabTitles[TAB_INDEX_RECENTS] = getResources().getString(R.string.tab_recents); |
| mTabTitles[TAB_INDEX_ALL_CONTACTS] = getResources().getString(R.string.tab_all_contacts); |
| |
| mViewPagerTabs = (ViewPagerTabs) parentView.findViewById(R.id.lists_pager_header); |
| mViewPagerTabs.setViewPager(mViewPager); |
| addOnPageChangeListener(mViewPagerTabs); |
| |
| mShortcutCardsListView = (ListView) parentView.findViewById(R.id.shortcut_card_list); |
| mShortcutCardsListView.setAdapter(mMergedAdapter); |
| |
| mRemoveView = (RemoveView) parentView.findViewById(R.id.remove_view); |
| mRemoveViewContent = parentView.findViewById(R.id.remove_view_content); |
| |
| setupPaneLayout((OverlappingPaneLayout) parentView); |
| |
| return parentView; |
| } |
| |
| @Override |
| public void onVoicemailStatusFetched(Cursor statusCursor) { |
| // no-op |
| } |
| |
| @Override |
| public void onCallsFetched(Cursor cursor) { |
| mCallLogAdapter.setLoading(false); |
| |
| // Save the date of the most recent call log item |
| if (cursor != null && cursor.moveToFirst()) { |
| mCurrentCallShortcutDate = cursor.getLong(CallLogQuery.DATE); |
| } |
| |
| mCallLogAdapter.changeCursor(cursor); |
| mMergedAdapter.notifyDataSetChanged(); |
| } |
| |
| @Override |
| public void fetchCalls() { |
| mCallLogQueryHandler.fetchCalls(CallLogQueryHandler.CALL_TYPE_ALL, mLastCallShortcutDate); |
| } |
| |
| public void dismissShortcut(View view) { |
| mLastCallShortcutDate = mCurrentCallShortcutDate; |
| final SharedPreferences prefs = view.getContext().getSharedPreferences( |
| DialtactsActivity.SHARED_PREFS_NAME, Context.MODE_PRIVATE); |
| prefs.edit().putLong(KEY_LAST_DISMISSED_CALL_SHORTCUT_DATE, mLastCallShortcutDate) |
| .apply(); |
| fetchCalls(); |
| } |
| |
| public void addOnPageChangeListener(OnPageChangeListener onPageChangeListener) { |
| if (!mOnPageChangeListeners.contains(onPageChangeListener)) { |
| mOnPageChangeListeners.add(onPageChangeListener); |
| } |
| } |
| |
| @Override |
| public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { |
| final int count = mOnPageChangeListeners.size(); |
| for (int i = 0; i < count; i++) { |
| mOnPageChangeListeners.get(i).onPageScrolled(position, positionOffset, |
| positionOffsetPixels); |
| } |
| } |
| |
| @Override |
| public void onPageSelected(int position) { |
| if (position == TAB_INDEX_SPEED_DIAL) { |
| mSpeedDialFragment.sendScreenView(); |
| } else if (position == TAB_INDEX_RECENTS) { |
| mRecentsFragment.sendScreenView(); |
| } else if (position == TAB_INDEX_ALL_CONTACTS) { |
| mAllContactsFragment.sendScreenView(); |
| } |
| final int count = mOnPageChangeListeners.size(); |
| for (int i = 0; i < count; i++) { |
| mOnPageChangeListeners.get(i).onPageSelected(position); |
| } |
| } |
| |
| @Override |
| public void onPageScrollStateChanged(int state) { |
| final int count = mOnPageChangeListeners.size(); |
| for (int i = 0; i < count; i++) { |
| mOnPageChangeListeners.get(i).onPageScrollStateChanged(state); |
| } |
| } |
| |
| public void showRemoveView(boolean show) { |
| mRemoveViewContent.setVisibility(show ? View.VISIBLE : View.GONE); |
| mRemoveView.setAlpha(show ? 0 : 1); |
| mRemoveView.animate().alpha(show ? 1 : 0).start(); |
| |
| if (mShortcutCardsListView.getChildCount() > 0) { |
| View v = mShortcutCardsListView.getChildAt(0); |
| v.animate().withLayer() |
| .alpha(show ? REMOVE_VIEW_SHOWN_ALPHA : REMOVE_VIEW_HIDDEN_ALPHA) |
| .start(); |
| } |
| } |
| |
| public boolean shouldShowActionBar() { |
| return mIsPanelOpen && mActionBar != null; |
| } |
| |
| public boolean isPaneOpen() { |
| return mIsPanelOpen; |
| } |
| |
| private void setupPaneLayout(OverlappingPaneLayout paneLayout) { |
| // TODO: Remove the notion of a capturable view. The entire view be slideable, once |
| // the framework better supports nested scrolling. |
| paneLayout.setCapturableView(mViewPagerTabs); |
| paneLayout.openPane(); |
| paneLayout.setPanelSlideListener(mPanelSlideListener); |
| paneLayout.setIntermediatePinnedOffset( |
| ((HostInterface) getActivity()).getActionBarHeight()); |
| |
| LayoutTransition transition = paneLayout.getLayoutTransition(); |
| // Turns on animations for all types of layout changes so that they occur for |
| // height changes. |
| transition.enableTransitionType(LayoutTransition.CHANGING); |
| } |
| |
| public SpeedDialFragment getSpeedDialFragment() { |
| return mSpeedDialFragment; |
| } |
| |
| public RemoveView getRemoveView() { |
| return mRemoveView; |
| } |
| |
| public int getRtlPosition(int position) { |
| if (DialerUtils.isRtl()) { |
| return TAB_INDEX_COUNT - 1 - position; |
| } |
| return position; |
| } |
| } |