summaryrefslogtreecommitdiff
path: root/java/com/android/dialer/app/list/ListsFragment.java
diff options
context:
space:
mode:
Diffstat (limited to 'java/com/android/dialer/app/list/ListsFragment.java')
-rw-r--r--java/com/android/dialer/app/list/ListsFragment.java587
1 files changed, 587 insertions, 0 deletions
diff --git a/java/com/android/dialer/app/list/ListsFragment.java b/java/com/android/dialer/app/list/ListsFragment.java
new file mode 100644
index 000000000..725ad3001
--- /dev/null
+++ b/java/com/android/dialer/app/list/ListsFragment.java
@@ -0,0 +1,587 @@
+/*
+ * Copyright (C) 2013 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.app.list;
+
+import android.app.Activity;
+import android.app.Fragment;
+import android.app.FragmentManager;
+import android.content.SharedPreferences;
+import android.database.ContentObserver;
+import android.database.Cursor;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Trace;
+import android.preference.PreferenceManager;
+import android.provider.VoicemailContract;
+import android.support.annotation.Nullable;
+import android.support.v13.app.FragmentPagerAdapter;
+import android.support.v4.view.ViewPager;
+import android.support.v4.view.ViewPager.OnPageChangeListener;
+import android.support.v7.app.ActionBar;
+import android.support.v7.app.AppCompatActivity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import com.android.contacts.common.list.ViewPagerTabs;
+import com.android.dialer.app.R;
+import com.android.dialer.app.calllog.CallLogFragment;
+import com.android.dialer.app.calllog.CallLogNotificationsHelper;
+import com.android.dialer.app.calllog.VisualVoicemailCallLogFragment;
+import com.android.dialer.app.voicemail.error.VoicemailStatusCorruptionHandler;
+import com.android.dialer.app.voicemail.error.VoicemailStatusCorruptionHandler.Source;
+import com.android.dialer.app.widget.ActionBarController;
+import com.android.dialer.common.LogUtil;
+import com.android.dialer.database.CallLogQueryHandler;
+import com.android.dialer.logging.Logger;
+import com.android.dialer.logging.nano.DialerImpression;
+import com.android.dialer.logging.nano.ScreenEvent;
+import com.android.dialer.util.ViewUtil;
+import com.android.dialer.voicemailstatus.VisualVoicemailEnabledChecker;
+import com.android.dialer.voicemailstatus.VoicemailStatusHelper;
+import com.android.dialer.voicemailstatus.VoicemailStatusHelperImpl;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Fragment that is used as the main screen of the Dialer.
+ *
+ * <p>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 search bar and pin it against the top of the screen.
+ */
+public class ListsFragment extends Fragment
+ implements ViewPager.OnPageChangeListener, CallLogQueryHandler.Listener {
+
+ /** Every fragment in the list show implement this interface. */
+ public interface ListsPage {
+
+ /**
+ * Called when the page is resumed, including selecting the page or activity resume. Note: This
+ * is called before the page fragment is attached to a activity.
+ *
+ * @param activity the activity hosting the ListFragment
+ */
+ void onPageResume(@Nullable Activity activity);
+
+ /**
+ * Called when the page is paused, including selecting another page or activity pause. Note:
+ * This is called after the page fragment is detached from a activity.
+ *
+ * @param activity the activity hosting the ListFragment
+ */
+ void onPagePause(@Nullable Activity activity);
+ }
+
+ public static final int TAB_INDEX_SPEED_DIAL = 0;
+ public static final int TAB_INDEX_HISTORY = 1;
+ public static final int TAB_INDEX_ALL_CONTACTS = 2;
+ public static final int TAB_INDEX_VOICEMAIL = 3;
+ public static final int TAB_COUNT_DEFAULT = 3;
+ public static final int TAB_COUNT_WITH_VOICEMAIL = 4;
+ private static final String TAG = "ListsFragment";
+ private ActionBar mActionBar;
+ private ViewPager mViewPager;
+ private ViewPagerTabs mViewPagerTabs;
+ private ViewPagerAdapter mViewPagerAdapter;
+ private RemoveView mRemoveView;
+ private View mRemoveViewContent;
+ private SpeedDialFragment mSpeedDialFragment;
+ private CallLogFragment mHistoryFragment;
+ private AllContactsFragment mAllContactsFragment;
+ private CallLogFragment mVoicemailFragment;
+ private ListsPage mCurrentPage;
+ private SharedPreferences mPrefs;
+ private boolean mHasActiveVoicemailProvider;
+ private boolean mHasFetchedVoicemailStatus;
+ private boolean mShowVoicemailTabAfterVoicemailStatusIsFetched;
+ private VoicemailStatusHelper mVoicemailStatusHelper;
+ private ArrayList<OnPageChangeListener> mOnPageChangeListeners =
+ new ArrayList<OnPageChangeListener>();
+ private String[] mTabTitles;
+ private int[] mTabIcons;
+ /** The position of the currently selected tab. */
+ private int mTabIndex = TAB_INDEX_SPEED_DIAL;
+
+ private CallLogQueryHandler mCallLogQueryHandler;
+
+ private final ContentObserver mVoicemailStatusObserver =
+ new ContentObserver(new Handler()) {
+ @Override
+ public void onChange(boolean selfChange) {
+ super.onChange(selfChange);
+ mCallLogQueryHandler.fetchVoicemailStatus();
+ }
+ };
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ LogUtil.d("ListsFragment.onCreate", null);
+ Trace.beginSection(TAG + " onCreate");
+ super.onCreate(savedInstanceState);
+
+ mVoicemailStatusHelper = new VoicemailStatusHelperImpl();
+ mHasFetchedVoicemailStatus = false;
+
+ mPrefs = PreferenceManager.getDefaultSharedPreferences(getActivity());
+ mHasActiveVoicemailProvider =
+ mPrefs.getBoolean(
+ VisualVoicemailEnabledChecker.PREF_KEY_HAS_ACTIVE_VOICEMAIL_PROVIDER, false);
+
+ Trace.endSection();
+ }
+
+ @Override
+ public void onResume() {
+ LogUtil.d("ListsFragment.onResume", null);
+ Trace.beginSection(TAG + " onResume");
+ super.onResume();
+
+ mActionBar = ((AppCompatActivity) getActivity()).getSupportActionBar();
+ if (getUserVisibleHint()) {
+ sendScreenViewForCurrentPosition();
+ }
+
+ // Fetch voicemail status to determine if we should show the voicemail tab.
+ mCallLogQueryHandler =
+ new CallLogQueryHandler(getActivity(), getActivity().getContentResolver(), this);
+ mCallLogQueryHandler.fetchVoicemailStatus();
+ mCallLogQueryHandler.fetchMissedCallsUnreadCount();
+ Trace.endSection();
+ mCurrentPage = getListsPage(mViewPager.getCurrentItem());
+ if (mCurrentPage != null) {
+ mCurrentPage.onPageResume(getActivity());
+ }
+ }
+
+ @Override
+ public void onPause() {
+ LogUtil.d("ListsFragment.onPause", null);
+ if (mCurrentPage != null) {
+ mCurrentPage.onPagePause(getActivity());
+ }
+ super.onPause();
+ }
+
+ @Override
+ public void onDestroyView() {
+ super.onDestroyView();
+ mViewPager.removeOnPageChangeListener(this);
+ }
+
+ @Override
+ public View onCreateView(
+ LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ LogUtil.d("ListsFragment.onCreateView", null);
+ Trace.beginSection(TAG + " onCreateView");
+ Trace.beginSection(TAG + " inflate view");
+ final View parentView = inflater.inflate(R.layout.lists_fragment, container, false);
+ Trace.endSection();
+ Trace.beginSection(TAG + " setup views");
+ mViewPager = (ViewPager) parentView.findViewById(R.id.lists_pager);
+ mViewPagerAdapter = new ViewPagerAdapter(getChildFragmentManager());
+ mViewPager.setAdapter(mViewPagerAdapter);
+ mViewPager.setOffscreenPageLimit(TAB_COUNT_WITH_VOICEMAIL - 1);
+ mViewPager.addOnPageChangeListener(this);
+ showTab(TAB_INDEX_SPEED_DIAL);
+
+ mTabTitles = new String[TAB_COUNT_WITH_VOICEMAIL];
+ mTabTitles[TAB_INDEX_SPEED_DIAL] = getResources().getString(R.string.tab_speed_dial);
+ mTabTitles[TAB_INDEX_HISTORY] = getResources().getString(R.string.tab_history);
+ mTabTitles[TAB_INDEX_ALL_CONTACTS] = getResources().getString(R.string.tab_all_contacts);
+ mTabTitles[TAB_INDEX_VOICEMAIL] = getResources().getString(R.string.tab_voicemail);
+
+ mTabIcons = new int[TAB_COUNT_WITH_VOICEMAIL];
+ mTabIcons[TAB_INDEX_SPEED_DIAL] = R.drawable.ic_grade_24dp;
+ mTabIcons[TAB_INDEX_HISTORY] = R.drawable.ic_schedule_24dp;
+ mTabIcons[TAB_INDEX_ALL_CONTACTS] = R.drawable.ic_people_24dp;
+ mTabIcons[TAB_INDEX_VOICEMAIL] = R.drawable.ic_voicemail_24dp;
+
+ mViewPagerTabs = (ViewPagerTabs) parentView.findViewById(R.id.lists_pager_header);
+ mViewPagerTabs.configureTabIcons(mTabIcons);
+ mViewPagerTabs.setViewPager(mViewPager);
+ addOnPageChangeListener(mViewPagerTabs);
+
+ mRemoveView = (RemoveView) parentView.findViewById(R.id.remove_view);
+ mRemoveViewContent = parentView.findViewById(R.id.remove_view_content);
+
+ getActivity()
+ .getContentResolver()
+ .registerContentObserver(
+ VoicemailContract.Status.CONTENT_URI, true, mVoicemailStatusObserver);
+
+ Trace.endSection();
+ Trace.endSection();
+ return parentView;
+ }
+
+ @Override
+ public void onDestroy() {
+ getActivity().getContentResolver().unregisterContentObserver(mVoicemailStatusObserver);
+ super.onDestroy();
+ }
+
+ public void addOnPageChangeListener(OnPageChangeListener onPageChangeListener) {
+ if (!mOnPageChangeListeners.contains(onPageChangeListener)) {
+ mOnPageChangeListeners.add(onPageChangeListener);
+ }
+ }
+
+ /**
+ * Shows the tab with the specified index. If the voicemail tab index is specified, but the
+ * voicemail status hasn't been fetched, it will try to show the tab after the voicemail status
+ * has been fetched.
+ */
+ public void showTab(int index) {
+ if (index == TAB_INDEX_VOICEMAIL) {
+ if (mHasActiveVoicemailProvider) {
+ Logger.get(getContext()).logImpression(DialerImpression.Type.VVM_TAB_VISIBLE);
+ mViewPager.setCurrentItem(getRtlPosition(TAB_INDEX_VOICEMAIL));
+ } else if (!mHasFetchedVoicemailStatus) {
+ // Try to show the voicemail tab after the voicemail status returns.
+ mShowVoicemailTabAfterVoicemailStatusIsFetched = true;
+ }
+ } else if (index < getTabCount()) {
+ mViewPager.setCurrentItem(getRtlPosition(index));
+ }
+ }
+
+ @Override
+ public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
+ mTabIndex = getRtlPosition(position);
+
+ 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) {
+ LogUtil.i("ListsFragment.onPageSelected", "position: %d", position);
+ mTabIndex = getRtlPosition(position);
+
+ // Show the tab which has been selected instead.
+ mShowVoicemailTabAfterVoicemailStatusIsFetched = false;
+
+ final int count = mOnPageChangeListeners.size();
+ for (int i = 0; i < count; i++) {
+ mOnPageChangeListeners.get(i).onPageSelected(position);
+ }
+ sendScreenViewForCurrentPosition();
+
+ if (mCurrentPage != null) {
+ mCurrentPage.onPagePause(getActivity());
+ }
+ mCurrentPage = getListsPage(position);
+ if (mCurrentPage != null) {
+ mCurrentPage.onPageResume(getActivity());
+ }
+ }
+
+ @Override
+ public void onPageScrollStateChanged(int state) {
+ final int count = mOnPageChangeListeners.size();
+ for (int i = 0; i < count; i++) {
+ mOnPageChangeListeners.get(i).onPageScrollStateChanged(state);
+ }
+ }
+
+ @Override
+ public void onVoicemailStatusFetched(Cursor statusCursor) {
+ mHasFetchedVoicemailStatus = true;
+
+ if (getActivity() == null || getActivity().isFinishing()) {
+ return;
+ }
+
+ VoicemailStatusCorruptionHandler.maybeFixVoicemailStatus(
+ getContext(), statusCursor, Source.Activity);
+
+ // Update mHasActiveVoicemailProvider, which controls the number of tabs displayed.
+ boolean hasActiveVoicemailProvider =
+ mVoicemailStatusHelper.getNumberActivityVoicemailSources(statusCursor) > 0;
+ if (hasActiveVoicemailProvider != mHasActiveVoicemailProvider) {
+ mHasActiveVoicemailProvider = hasActiveVoicemailProvider;
+ mViewPagerAdapter.notifyDataSetChanged();
+
+ if (hasActiveVoicemailProvider) {
+ mViewPagerTabs.updateTab(TAB_INDEX_VOICEMAIL);
+ } else {
+ mViewPagerTabs.removeTab(TAB_INDEX_VOICEMAIL);
+ removeVoicemailFragment();
+ }
+
+ mPrefs
+ .edit()
+ .putBoolean(
+ VisualVoicemailEnabledChecker.PREF_KEY_HAS_ACTIVE_VOICEMAIL_PROVIDER,
+ hasActiveVoicemailProvider)
+ .commit();
+ }
+
+ if (hasActiveVoicemailProvider) {
+ mCallLogQueryHandler.fetchVoicemailUnreadCount();
+ }
+
+ if (mHasActiveVoicemailProvider && mShowVoicemailTabAfterVoicemailStatusIsFetched) {
+ mShowVoicemailTabAfterVoicemailStatusIsFetched = false;
+ showTab(TAB_INDEX_VOICEMAIL);
+ }
+ }
+
+ @Override
+ public void onVoicemailUnreadCountFetched(Cursor cursor) {
+ if (getActivity() == null || getActivity().isFinishing() || cursor == null) {
+ return;
+ }
+
+ int count = 0;
+ try {
+ count = cursor.getCount();
+ } finally {
+ cursor.close();
+ }
+
+ mViewPagerTabs.setUnreadCount(count, TAB_INDEX_VOICEMAIL);
+ mViewPagerTabs.updateTab(TAB_INDEX_VOICEMAIL);
+ }
+
+ @Override
+ public void onMissedCallsUnreadCountFetched(Cursor cursor) {
+ if (getActivity() == null || getActivity().isFinishing() || cursor == null) {
+ return;
+ }
+
+ int count = 0;
+ try {
+ count = cursor.getCount();
+ } finally {
+ cursor.close();
+ }
+
+ mViewPagerTabs.setUnreadCount(count, TAB_INDEX_HISTORY);
+ mViewPagerTabs.updateTab(TAB_INDEX_HISTORY);
+ }
+
+ @Override
+ public boolean onCallsFetched(Cursor statusCursor) {
+ // Return false; did not take ownership of cursor
+ return false;
+ }
+
+ public int getCurrentTabIndex() {
+ return mTabIndex;
+ }
+
+ /**
+ * External method to update unread count because the unread count changes when the user expands a
+ * voicemail in the call log or when the user expands an unread call in the call history tab.
+ */
+ public void updateTabUnreadCounts() {
+ if (mCallLogQueryHandler != null) {
+ mCallLogQueryHandler.fetchMissedCallsUnreadCount();
+ if (mHasActiveVoicemailProvider) {
+ mCallLogQueryHandler.fetchVoicemailUnreadCount();
+ }
+ }
+ }
+
+ /** External method to mark all missed calls as read. */
+ public void markMissedCallsAsReadAndRemoveNotifications() {
+ if (mCallLogQueryHandler != null) {
+ mCallLogQueryHandler.markMissedCallsAsRead();
+ CallLogNotificationsHelper.removeMissedCallNotifications(getActivity());
+ }
+ }
+
+ public void showRemoveView(boolean show) {
+ mRemoveViewContent.setVisibility(show ? View.VISIBLE : View.GONE);
+ mRemoveView.setAlpha(show ? 0 : 1);
+ mRemoveView.animate().alpha(show ? 1 : 0).start();
+ }
+
+ public boolean shouldShowActionBar() {
+ // TODO: Update this based on scroll state.
+ return mActionBar != null;
+ }
+
+ public SpeedDialFragment getSpeedDialFragment() {
+ return mSpeedDialFragment;
+ }
+
+ public RemoveView getRemoveView() {
+ return mRemoveView;
+ }
+
+ public int getTabCount() {
+ return mViewPagerAdapter.getCount();
+ }
+
+ private int getRtlPosition(int position) {
+ if (ViewUtil.isRtl()) {
+ return mViewPagerAdapter.getCount() - 1 - position;
+ }
+ return position;
+ }
+
+ public void sendScreenViewForCurrentPosition() {
+ if (!isResumed()) {
+ return;
+ }
+
+ int screenType;
+ switch (getCurrentTabIndex()) {
+ case TAB_INDEX_SPEED_DIAL:
+ screenType = ScreenEvent.Type.SPEED_DIAL;
+ break;
+ case TAB_INDEX_HISTORY:
+ screenType = ScreenEvent.Type.CALL_LOG;
+ break;
+ case TAB_INDEX_ALL_CONTACTS:
+ screenType = ScreenEvent.Type.ALL_CONTACTS;
+ break;
+ case TAB_INDEX_VOICEMAIL:
+ screenType = ScreenEvent.Type.VOICEMAIL_LOG;
+ break;
+ default:
+ return;
+ }
+ Logger.get(getActivity()).logScreenView(screenType, getActivity());
+ }
+
+ private void removeVoicemailFragment() {
+ if (mVoicemailFragment != null) {
+ getChildFragmentManager()
+ .beginTransaction()
+ .remove(mVoicemailFragment)
+ .commitAllowingStateLoss();
+ mVoicemailFragment = null;
+ }
+ }
+
+ private ListsPage getListsPage(int position) {
+ switch (getRtlPosition(position)) {
+ case TAB_INDEX_SPEED_DIAL:
+ return mSpeedDialFragment;
+ case TAB_INDEX_HISTORY:
+ return mHistoryFragment;
+ case TAB_INDEX_ALL_CONTACTS:
+ return mAllContactsFragment;
+ case TAB_INDEX_VOICEMAIL:
+ return mVoicemailFragment;
+ }
+ throw new IllegalStateException("No fragment at position " + position);
+ }
+
+ public interface HostInterface {
+
+ ActionBarController getActionBarController();
+ }
+
+ public class ViewPagerAdapter extends FragmentPagerAdapter {
+
+ private final List<Fragment> mFragments = new ArrayList<>();
+
+ public ViewPagerAdapter(FragmentManager fm) {
+ super(fm);
+ for (int i = 0; i < TAB_COUNT_WITH_VOICEMAIL; i++) {
+ mFragments.add(null);
+ }
+ }
+
+ @Override
+ public long getItemId(int position) {
+ return getRtlPosition(position);
+ }
+
+ @Override
+ public Fragment getItem(int position) {
+ LogUtil.d("ViewPagerAdapter.getItem", "position: %d", position);
+ switch (getRtlPosition(position)) {
+ case TAB_INDEX_SPEED_DIAL:
+ if (mSpeedDialFragment == null) {
+ mSpeedDialFragment = new SpeedDialFragment();
+ }
+ return mSpeedDialFragment;
+ case TAB_INDEX_HISTORY:
+ if (mHistoryFragment == null) {
+ mHistoryFragment = new CallLogFragment();
+ }
+ return mHistoryFragment;
+ case TAB_INDEX_ALL_CONTACTS:
+ if (mAllContactsFragment == null) {
+ mAllContactsFragment = new AllContactsFragment();
+ }
+ return mAllContactsFragment;
+ case TAB_INDEX_VOICEMAIL:
+ if (mVoicemailFragment == null) {
+ mVoicemailFragment = new VisualVoicemailCallLogFragment();
+ LogUtil.v(
+ "ViewPagerAdapter.getItem",
+ "new VisualVoicemailCallLogFragment: %s",
+ mVoicemailFragment);
+ }
+ return mVoicemailFragment;
+ }
+ throw new IllegalStateException("No fragment at position " + position);
+ }
+
+ @Override
+ public Fragment instantiateItem(ViewGroup container, int position) {
+ LogUtil.d("ViewPagerAdapter.instantiateItem", "position: %d", position);
+ // On rotation the FragmentManager handles rotation. Therefore getItem() isn't called.
+ // Copy the fragments that the FragmentManager finds so that we can store them in
+ // instance variables for later.
+ final Fragment fragment = (Fragment) super.instantiateItem(container, position);
+ if (fragment instanceof SpeedDialFragment) {
+ mSpeedDialFragment = (SpeedDialFragment) fragment;
+ } else if (fragment instanceof CallLogFragment && position == TAB_INDEX_HISTORY) {
+ mHistoryFragment = (CallLogFragment) fragment;
+ } else if (fragment instanceof AllContactsFragment) {
+ mAllContactsFragment = (AllContactsFragment) fragment;
+ } else if (fragment instanceof CallLogFragment && position == TAB_INDEX_VOICEMAIL) {
+ mVoicemailFragment = (CallLogFragment) fragment;
+ LogUtil.v("ViewPagerAdapter.instantiateItem", mVoicemailFragment.toString());
+ }
+ mFragments.set(position, fragment);
+ return fragment;
+ }
+
+ /**
+ * When {@link android.support.v4.view.PagerAdapter#notifyDataSetChanged} is called, this method
+ * is called on all pages to determine whether they need to be recreated. When the voicemail tab
+ * is removed, the view needs to be recreated by returning POSITION_NONE. If
+ * notifyDataSetChanged is called for some other reason, the voicemail tab is recreated only if
+ * it is active. All other tabs do not need to be recreated and POSITION_UNCHANGED is returned.
+ */
+ @Override
+ public int getItemPosition(Object object) {
+ return !mHasActiveVoicemailProvider && mFragments.indexOf(object) == TAB_INDEX_VOICEMAIL
+ ? POSITION_NONE
+ : POSITION_UNCHANGED;
+ }
+
+ @Override
+ public int getCount() {
+ return mHasActiveVoicemailProvider ? TAB_COUNT_WITH_VOICEMAIL : TAB_COUNT_DEFAULT;
+ }
+
+ @Override
+ public CharSequence getPageTitle(int position) {
+ return mTabTitles[position];
+ }
+ }
+}