Home | History | Annotate | Download | only in list
      1 /*
      2  * Copyright (C) 2013 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 package com.android.dialer.list;
     17 
     18 import android.app.Fragment;
     19 import android.app.FragmentManager;
     20 import android.content.SharedPreferences;
     21 import android.database.Cursor;
     22 import android.os.Bundle;
     23 import android.os.Trace;
     24 import android.preference.PreferenceManager;
     25 import android.provider.CallLog.Calls;
     26 import android.support.v13.app.FragmentPagerAdapter;
     27 import android.support.v4.view.ViewPager;
     28 import android.support.v4.view.ViewPager.OnPageChangeListener;
     29 import android.support.v7.app.ActionBar;
     30 import android.support.v7.app.AppCompatActivity;
     31 import android.view.LayoutInflater;
     32 import android.view.View;
     33 import android.view.ViewGroup;
     34 
     35 import com.android.contacts.common.list.ViewPagerTabs;
     36 import com.android.dialer.DialtactsActivity;
     37 import com.android.dialer.R;
     38 import com.android.dialer.calllog.CallLogFragment;
     39 import com.android.dialer.calllog.CallLogNotificationsHelper;
     40 import com.android.dialer.calllog.CallLogQueryHandler;
     41 import com.android.dialer.calllog.VisualVoicemailCallLogFragment;
     42 import com.android.dialer.logging.Logger;
     43 import com.android.dialer.logging.ScreenEvent;
     44 import com.android.dialer.util.DialerUtils;
     45 import com.android.dialer.voicemail.VisualVoicemailEnabledChecker;
     46 import com.android.dialer.voicemail.VoicemailStatusHelper;
     47 import com.android.dialer.voicemail.VoicemailStatusHelperImpl;
     48 import com.android.dialer.widget.ActionBarController;
     49 
     50 import java.util.ArrayList;
     51 import java.util.List;
     52 
     53 /**
     54  * Fragment that is used as the main screen of the Dialer.
     55  *
     56  * Contains a ViewPager that contains various contact lists like the Speed Dial list and the
     57  * All Contacts list. This will also eventually contain the logic that allows sliding the
     58  * ViewPager containing the lists up above the search bar and pin it against the top of the
     59  * screen.
     60  */
     61 public class ListsFragment extends Fragment
     62         implements ViewPager.OnPageChangeListener, CallLogQueryHandler.Listener {
     63 
     64     private static final boolean DEBUG = DialtactsActivity.DEBUG;
     65     private static final String TAG = "ListsFragment";
     66 
     67     public static final int TAB_INDEX_SPEED_DIAL = 0;
     68     public static final int TAB_INDEX_HISTORY = 1;
     69     public static final int TAB_INDEX_ALL_CONTACTS = 2;
     70     public static final int TAB_INDEX_VOICEMAIL = 3;
     71 
     72     public static final int TAB_COUNT_DEFAULT = 3;
     73     public static final int TAB_COUNT_WITH_VOICEMAIL = 4;
     74 
     75     public interface HostInterface {
     76         public ActionBarController getActionBarController();
     77     }
     78 
     79     private ActionBar mActionBar;
     80     private ViewPager mViewPager;
     81     private ViewPagerTabs mViewPagerTabs;
     82     private ViewPagerAdapter mViewPagerAdapter;
     83     private RemoveView mRemoveView;
     84     private View mRemoveViewContent;
     85 
     86     private SpeedDialFragment mSpeedDialFragment;
     87     private CallLogFragment mHistoryFragment;
     88     private AllContactsFragment mAllContactsFragment;
     89     private CallLogFragment mVoicemailFragment;
     90 
     91     private SharedPreferences mPrefs;
     92     private boolean mHasActiveVoicemailProvider;
     93     private boolean mHasFetchedVoicemailStatus;
     94     private boolean mShowVoicemailTabAfterVoicemailStatusIsFetched;
     95 
     96     private VoicemailStatusHelper mVoicemailStatusHelper;
     97     private ArrayList<OnPageChangeListener> mOnPageChangeListeners =
     98             new ArrayList<OnPageChangeListener>();
     99 
    100     private String[] mTabTitles;
    101     private int[] mTabIcons;
    102 
    103     /**
    104      * The position of the currently selected tab.
    105      */
    106     private int mTabIndex = TAB_INDEX_SPEED_DIAL;
    107     private CallLogQueryHandler mCallLogQueryHandler;
    108 
    109     public class ViewPagerAdapter extends FragmentPagerAdapter {
    110         private final List<Fragment> mFragments = new ArrayList<>();
    111 
    112         public ViewPagerAdapter(FragmentManager fm) {
    113             super(fm);
    114             for (int i = 0; i < TAB_COUNT_WITH_VOICEMAIL; i++) {
    115                 mFragments.add(null);
    116             }
    117         }
    118 
    119         @Override
    120         public long getItemId(int position) {
    121             return getRtlPosition(position);
    122         }
    123 
    124         @Override
    125         public Fragment getItem(int position) {
    126             switch (getRtlPosition(position)) {
    127                 case TAB_INDEX_SPEED_DIAL:
    128                     mSpeedDialFragment = new SpeedDialFragment();
    129                     return mSpeedDialFragment;
    130                 case TAB_INDEX_HISTORY:
    131                     mHistoryFragment = new CallLogFragment(CallLogQueryHandler.CALL_TYPE_ALL);
    132                     return mHistoryFragment;
    133                 case TAB_INDEX_ALL_CONTACTS:
    134                     mAllContactsFragment = new AllContactsFragment();
    135                     return mAllContactsFragment;
    136                 case TAB_INDEX_VOICEMAIL:
    137                     mVoicemailFragment = new VisualVoicemailCallLogFragment();
    138                     return mVoicemailFragment;
    139             }
    140             throw new IllegalStateException("No fragment at position " + position);
    141         }
    142 
    143         @Override
    144         public Fragment instantiateItem(ViewGroup container, int position) {
    145             // On rotation the FragmentManager handles rotation. Therefore getItem() isn't called.
    146             // Copy the fragments that the FragmentManager finds so that we can store them in
    147             // instance variables for later.
    148             final Fragment fragment =
    149                     (Fragment) super.instantiateItem(container, position);
    150             if (fragment instanceof SpeedDialFragment) {
    151                 mSpeedDialFragment = (SpeedDialFragment) fragment;
    152             } else if (fragment instanceof CallLogFragment && position == TAB_INDEX_HISTORY) {
    153                 mHistoryFragment = (CallLogFragment) fragment;
    154             } else if (fragment instanceof AllContactsFragment) {
    155                 mAllContactsFragment = (AllContactsFragment) fragment;
    156             } else if (fragment instanceof CallLogFragment && position == TAB_INDEX_VOICEMAIL) {
    157                 mVoicemailFragment = (CallLogFragment) fragment;
    158             }
    159             mFragments.set(position, fragment);
    160             return fragment;
    161         }
    162 
    163         /**
    164          * When {@link android.support.v4.view.PagerAdapter#notifyDataSetChanged} is called,
    165          * this method is called on all pages to determine whether they need to be recreated.
    166          * When the voicemail tab is removed, the view needs to be recreated by returning
    167          * POSITION_NONE. If notifyDataSetChanged is called for some other reason, the voicemail
    168          * tab is recreated only if it is active. All other tabs do not need to be recreated
    169          * and POSITION_UNCHANGED is returned.
    170          */
    171         @Override
    172         public int getItemPosition(Object object) {
    173             return !mHasActiveVoicemailProvider &&
    174                     mFragments.indexOf(object) == TAB_INDEX_VOICEMAIL ? POSITION_NONE :
    175                     POSITION_UNCHANGED;
    176         }
    177 
    178         @Override
    179         public int getCount() {
    180             return mHasActiveVoicemailProvider ? TAB_COUNT_WITH_VOICEMAIL : TAB_COUNT_DEFAULT;
    181         }
    182 
    183         @Override
    184         public CharSequence getPageTitle(int position) {
    185             return mTabTitles[position];
    186         }
    187     }
    188 
    189     @Override
    190     public void onCreate(Bundle savedInstanceState) {
    191         Trace.beginSection(TAG + " onCreate");
    192         super.onCreate(savedInstanceState);
    193 
    194         mVoicemailStatusHelper = new VoicemailStatusHelperImpl();
    195         mHasFetchedVoicemailStatus = false;
    196 
    197         mPrefs = PreferenceManager.getDefaultSharedPreferences(getActivity());
    198         mHasActiveVoicemailProvider = mPrefs.getBoolean(
    199                 VisualVoicemailEnabledChecker.PREF_KEY_HAS_ACTIVE_VOICEMAIL_PROVIDER, false);
    200 
    201         Trace.endSection();
    202     }
    203 
    204     @Override
    205     public void onResume() {
    206         Trace.beginSection(TAG + " onResume");
    207         super.onResume();
    208 
    209         mActionBar = ((AppCompatActivity) getActivity()).getSupportActionBar();
    210         if (getUserVisibleHint()) {
    211             sendScreenViewForCurrentPosition();
    212         }
    213 
    214         // Fetch voicemail status to determine if we should show the voicemail tab.
    215         mCallLogQueryHandler =
    216                 new CallLogQueryHandler(getActivity(), getActivity().getContentResolver(), this);
    217         mCallLogQueryHandler.fetchVoicemailStatus();
    218         mCallLogQueryHandler.fetchMissedCallsUnreadCount();
    219         Trace.endSection();
    220     }
    221 
    222     @Override
    223     public View onCreateView(LayoutInflater inflater, ViewGroup container,
    224             Bundle savedInstanceState) {
    225         Trace.beginSection(TAG + " onCreateView");
    226         Trace.beginSection(TAG + " inflate view");
    227         final View parentView = inflater.inflate(R.layout.lists_fragment, container, false);
    228         Trace.endSection();
    229         Trace.beginSection(TAG + " setup views");
    230         mViewPager = (ViewPager) parentView.findViewById(R.id.lists_pager);
    231         mViewPagerAdapter = new ViewPagerAdapter(getChildFragmentManager());
    232         mViewPager.setAdapter(mViewPagerAdapter);
    233         mViewPager.setOffscreenPageLimit(TAB_COUNT_WITH_VOICEMAIL - 1);
    234         mViewPager.setOnPageChangeListener(this);
    235         showTab(TAB_INDEX_SPEED_DIAL);
    236 
    237         mTabTitles = new String[TAB_COUNT_WITH_VOICEMAIL];
    238         mTabTitles[TAB_INDEX_SPEED_DIAL] = getResources().getString(R.string.tab_speed_dial);
    239         mTabTitles[TAB_INDEX_HISTORY] = getResources().getString(R.string.tab_history);
    240         mTabTitles[TAB_INDEX_ALL_CONTACTS] = getResources().getString(R.string.tab_all_contacts);
    241         mTabTitles[TAB_INDEX_VOICEMAIL] = getResources().getString(R.string.tab_voicemail);
    242 
    243         mTabIcons = new int[TAB_COUNT_WITH_VOICEMAIL];
    244         mTabIcons[TAB_INDEX_SPEED_DIAL] = R.drawable.ic_grade_24dp;
    245         mTabIcons[TAB_INDEX_HISTORY] = R.drawable.ic_schedule_24dp;
    246         mTabIcons[TAB_INDEX_ALL_CONTACTS] = R.drawable.ic_people_24dp;
    247         mTabIcons[TAB_INDEX_VOICEMAIL] = R.drawable.ic_voicemail_24dp;
    248 
    249         mViewPagerTabs = (ViewPagerTabs) parentView.findViewById(R.id.lists_pager_header);
    250         mViewPagerTabs.configureTabIcons(mTabIcons);
    251         mViewPagerTabs.setViewPager(mViewPager);
    252         addOnPageChangeListener(mViewPagerTabs);
    253 
    254         mRemoveView = (RemoveView) parentView.findViewById(R.id.remove_view);
    255         mRemoveViewContent = parentView.findViewById(R.id.remove_view_content);
    256 
    257         Trace.endSection();
    258         Trace.endSection();
    259         return parentView;
    260     }
    261 
    262     public void addOnPageChangeListener(OnPageChangeListener onPageChangeListener) {
    263         if (!mOnPageChangeListeners.contains(onPageChangeListener)) {
    264             mOnPageChangeListeners.add(onPageChangeListener);
    265         }
    266     }
    267 
    268     /**
    269      * Shows the tab with the specified index. If the voicemail tab index is specified, but the
    270      * voicemail status hasn't been fetched, it will try to show the tab after the voicemail status
    271      * has been fetched.
    272      */
    273     public void showTab(int index) {
    274         if (index == TAB_INDEX_VOICEMAIL) {
    275             if (mHasActiveVoicemailProvider) {
    276                 mViewPager.setCurrentItem(getRtlPosition(TAB_INDEX_VOICEMAIL));
    277             } else if (!mHasFetchedVoicemailStatus) {
    278                 // Try to show the voicemail tab after the voicemail status returns.
    279                 mShowVoicemailTabAfterVoicemailStatusIsFetched = true;
    280             }
    281         } else if (index < getTabCount()){
    282             mViewPager.setCurrentItem(getRtlPosition(index));
    283         }
    284     }
    285 
    286     @Override
    287     public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
    288         mTabIndex = getRtlPosition(position);
    289 
    290         final int count = mOnPageChangeListeners.size();
    291         for (int i = 0; i < count; i++) {
    292             mOnPageChangeListeners.get(i).onPageScrolled(position, positionOffset,
    293                     positionOffsetPixels);
    294         }
    295     }
    296 
    297     @Override
    298     public void onPageSelected(int position) {
    299         mTabIndex = getRtlPosition(position);
    300 
    301         // Show the tab which has been selected instead.
    302         mShowVoicemailTabAfterVoicemailStatusIsFetched = false;
    303 
    304         final int count = mOnPageChangeListeners.size();
    305         for (int i = 0; i < count; i++) {
    306             mOnPageChangeListeners.get(i).onPageSelected(position);
    307         }
    308         sendScreenViewForCurrentPosition();
    309     }
    310 
    311     @Override
    312     public void onPageScrollStateChanged(int state) {
    313         final int count = mOnPageChangeListeners.size();
    314         for (int i = 0; i < count; i++) {
    315             mOnPageChangeListeners.get(i).onPageScrollStateChanged(state);
    316         }
    317     }
    318 
    319     @Override
    320     public void onVoicemailStatusFetched(Cursor statusCursor) {
    321         mHasFetchedVoicemailStatus = true;
    322 
    323         if (getActivity() == null || getActivity().isFinishing()) {
    324             return;
    325         }
    326 
    327         // Update mHasActiveVoicemailProvider, which controls the number of tabs displayed.
    328         boolean hasActiveVoicemailProvider =
    329                 mVoicemailStatusHelper.getNumberActivityVoicemailSources(statusCursor) > 0;
    330         if (hasActiveVoicemailProvider != mHasActiveVoicemailProvider) {
    331             mHasActiveVoicemailProvider = hasActiveVoicemailProvider;
    332             mViewPagerAdapter.notifyDataSetChanged();
    333 
    334             if (hasActiveVoicemailProvider) {
    335                 mViewPagerTabs.updateTab(TAB_INDEX_VOICEMAIL);
    336             } else {
    337                 mViewPagerTabs.removeTab(TAB_INDEX_VOICEMAIL);
    338                 removeVoicemailFragment();
    339             }
    340 
    341             mPrefs.edit()
    342                   .putBoolean(VisualVoicemailEnabledChecker.PREF_KEY_HAS_ACTIVE_VOICEMAIL_PROVIDER,
    343                           hasActiveVoicemailProvider)
    344                   .commit();
    345         }
    346 
    347         if (hasActiveVoicemailProvider) {
    348             mCallLogQueryHandler.fetchVoicemailUnreadCount();
    349         }
    350 
    351         if (mHasActiveVoicemailProvider && mShowVoicemailTabAfterVoicemailStatusIsFetched) {
    352             mShowVoicemailTabAfterVoicemailStatusIsFetched = false;
    353             showTab(TAB_INDEX_VOICEMAIL);
    354         }
    355     }
    356 
    357     @Override
    358     public void onVoicemailUnreadCountFetched(Cursor cursor) {
    359         if (getActivity() == null || getActivity().isFinishing() || cursor == null) {
    360             return;
    361         }
    362 
    363         int count = 0;
    364         try {
    365             count = cursor.getCount();
    366         } finally {
    367             cursor.close();
    368         }
    369 
    370         mViewPagerTabs.setUnreadCount(count, TAB_INDEX_VOICEMAIL);
    371         mViewPagerTabs.updateTab(TAB_INDEX_VOICEMAIL);
    372     }
    373 
    374     @Override
    375     public void onMissedCallsUnreadCountFetched(Cursor cursor) {
    376         if (getActivity() == null || getActivity().isFinishing() || cursor == null) {
    377             return;
    378         }
    379 
    380         int count = 0;
    381         try {
    382             count = cursor.getCount();
    383         } finally {
    384             cursor.close();
    385         }
    386 
    387         mViewPagerTabs.setUnreadCount(count, TAB_INDEX_HISTORY);
    388         mViewPagerTabs.updateTab(TAB_INDEX_HISTORY);
    389     }
    390 
    391     @Override
    392     public boolean onCallsFetched(Cursor statusCursor) {
    393         // Return false; did not take ownership of cursor
    394         return false;
    395     }
    396 
    397     public int getCurrentTabIndex() {
    398         return mTabIndex;
    399     }
    400 
    401     /**
    402      * External method to update unread count because the unread count changes when the user
    403      * expands a voicemail in the call log or when the user expands an unread call in the call
    404      * history tab.
    405      */
    406     public void updateTabUnreadCounts() {
    407         if (mCallLogQueryHandler != null) {
    408             mCallLogQueryHandler.fetchMissedCallsUnreadCount();
    409             if (mHasActiveVoicemailProvider) {
    410                 mCallLogQueryHandler.fetchVoicemailUnreadCount();
    411             }
    412         }
    413     }
    414 
    415     /**
    416      * External method to mark all missed calls as read.
    417      */
    418     public void markMissedCallsAsReadAndRemoveNotifications() {
    419         if (mCallLogQueryHandler != null) {
    420             mCallLogQueryHandler.markMissedCallsAsRead();
    421             CallLogNotificationsHelper.removeMissedCallNotifications(getActivity());
    422         }
    423     }
    424 
    425 
    426     public void showRemoveView(boolean show) {
    427         mRemoveViewContent.setVisibility(show ? View.VISIBLE : View.GONE);
    428         mRemoveView.setAlpha(show ? 0 : 1);
    429         mRemoveView.animate().alpha(show ? 1 : 0).start();
    430     }
    431 
    432     public boolean shouldShowActionBar() {
    433         // TODO: Update this based on scroll state.
    434         return mActionBar != null;
    435     }
    436 
    437     public SpeedDialFragment getSpeedDialFragment() {
    438         return mSpeedDialFragment;
    439     }
    440 
    441     public RemoveView getRemoveView() {
    442         return mRemoveView;
    443     }
    444 
    445     public int getTabCount() {
    446         return mViewPagerAdapter.getCount();
    447     }
    448 
    449     private int getRtlPosition(int position) {
    450         if (DialerUtils.isRtl()) {
    451             return mViewPagerAdapter.getCount() - 1 - position;
    452         }
    453         return position;
    454     }
    455 
    456     public void sendScreenViewForCurrentPosition() {
    457         if (!isResumed()) {
    458             return;
    459         }
    460 
    461         int screenType;
    462         switch (getCurrentTabIndex()) {
    463             case TAB_INDEX_SPEED_DIAL:
    464                 screenType = ScreenEvent.SPEED_DIAL;
    465                 break;
    466             case TAB_INDEX_HISTORY:
    467                 screenType = ScreenEvent.CALL_LOG;
    468                 break;
    469             case TAB_INDEX_ALL_CONTACTS:
    470                 screenType = ScreenEvent.ALL_CONTACTS;
    471                 break;
    472             case TAB_INDEX_VOICEMAIL:
    473                 screenType = ScreenEvent.VOICEMAIL_LOG;
    474             default:
    475                 return;
    476         }
    477         Logger.logScreenView(screenType, getActivity());
    478     }
    479 
    480     private void removeVoicemailFragment() {
    481         if (mVoicemailFragment != null) {
    482             getChildFragmentManager().beginTransaction().remove(mVoicemailFragment)
    483                     .commitAllowingStateLoss();
    484             mVoicemailFragment = null;
    485         }
    486     }
    487 }
    488