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 
     17 package com.android.dialer.app.list;
     18 
     19 import static com.android.dialer.app.list.DialtactsPagerAdapter.TAB_COUNT_WITH_VOICEMAIL;
     20 import static com.android.dialer.app.list.DialtactsPagerAdapter.TAB_INDEX_ALL_CONTACTS;
     21 import static com.android.dialer.app.list.DialtactsPagerAdapter.TAB_INDEX_HISTORY;
     22 import static com.android.dialer.app.list.DialtactsPagerAdapter.TAB_INDEX_SPEED_DIAL;
     23 import static com.android.dialer.app.list.DialtactsPagerAdapter.TAB_INDEX_VOICEMAIL;
     24 
     25 import android.app.Fragment;
     26 import android.content.SharedPreferences;
     27 import android.database.ContentObserver;
     28 import android.database.Cursor;
     29 import android.os.Bundle;
     30 import android.os.Handler;
     31 import android.os.Trace;
     32 import android.preference.PreferenceManager;
     33 import android.provider.VoicemailContract;
     34 import android.support.v4.view.ViewPager;
     35 import android.support.v4.view.ViewPager.OnPageChangeListener;
     36 import android.view.LayoutInflater;
     37 import android.view.View;
     38 import android.view.ViewGroup;
     39 import com.android.contacts.common.list.ViewPagerTabs;
     40 import com.android.dialer.app.R;
     41 import com.android.dialer.app.calllog.CallLogFragment;
     42 import com.android.dialer.app.calllog.CallLogNotificationsService;
     43 import com.android.dialer.app.voicemail.error.VoicemailStatusCorruptionHandler;
     44 import com.android.dialer.app.voicemail.error.VoicemailStatusCorruptionHandler.Source;
     45 import com.android.dialer.common.LogUtil;
     46 import com.android.dialer.database.CallLogQueryHandler;
     47 import com.android.dialer.logging.DialerImpression;
     48 import com.android.dialer.logging.Logger;
     49 import com.android.dialer.logging.ScreenEvent;
     50 import com.android.dialer.speeddial.SpeedDialFragment;
     51 import com.android.dialer.util.PermissionsUtil;
     52 import com.android.dialer.voicemailstatus.VisualVoicemailEnabledChecker;
     53 import com.android.dialer.voicemailstatus.VoicemailStatusHelper;
     54 import java.util.ArrayList;
     55 
     56 /**
     57  * Fragment that is used as the main screen of the Dialer.
     58  *
     59  * <p>Contains a ViewPager that contains various contact lists like the Speed Dial list and the All
     60  * Contacts list. This will also eventually contain the logic that allows sliding the ViewPager
     61  * containing the lists up above the search bar and pin it against the top of the screen.
     62  */
     63 public class ListsFragment extends Fragment
     64     implements ViewPager.OnPageChangeListener, CallLogQueryHandler.Listener {
     65 
     66   private static final String TAG = "ListsFragment";
     67   private ViewPager mViewPager;
     68   private ViewPagerTabs mViewPagerTabs;
     69   private DialtactsPagerAdapter mAdapter;
     70   private RemoveView mRemoveView;
     71   private View mRemoveViewContent;
     72   private Fragment mCurrentPage;
     73   private SharedPreferences mPrefs;
     74   private boolean mHasFetchedVoicemailStatus;
     75   private boolean mShowVoicemailTabAfterVoicemailStatusIsFetched;
     76   private VoicemailStatusHelper mVoicemailStatusHelper;
     77   private final ArrayList<OnPageChangeListener> mOnPageChangeListeners = new ArrayList<>();
     78   /** The position of the currently selected tab. */
     79   private int mTabIndex = TAB_INDEX_SPEED_DIAL;
     80   private boolean mPaused;
     81 
     82   private CallLogQueryHandler mCallLogQueryHandler;
     83 
     84   private final ContentObserver mVoicemailStatusObserver =
     85       new ContentObserver(new Handler()) {
     86         @Override
     87         public void onChange(boolean selfChange) {
     88           super.onChange(selfChange);
     89           mCallLogQueryHandler.fetchVoicemailStatus();
     90         }
     91       };
     92 
     93   @Override
     94   public void onCreate(Bundle savedInstanceState) {
     95     LogUtil.d("ListsFragment.onCreate", null);
     96     Trace.beginSection(TAG + " onCreate");
     97     super.onCreate(savedInstanceState);
     98     mVoicemailStatusHelper = new VoicemailStatusHelper();
     99     mPrefs = PreferenceManager.getDefaultSharedPreferences(getActivity());
    100     Trace.endSection();
    101   }
    102 
    103   @Override
    104   public void onResume() {
    105     LogUtil.d("ListsFragment.onResume", null);
    106     Trace.beginSection(TAG + " onResume");
    107     super.onResume();
    108 
    109     mPaused = false;
    110 
    111     if (getUserVisibleHint()) {
    112       sendScreenViewForCurrentPosition();
    113     }
    114 
    115     // Fetch voicemail status to determine if we should show the voicemail tab.
    116     mCallLogQueryHandler =
    117         new CallLogQueryHandler(getActivity(), getActivity().getContentResolver(), this);
    118     mCallLogQueryHandler.fetchVoicemailStatus();
    119     mCallLogQueryHandler.fetchMissedCallsUnreadCount();
    120     Trace.endSection();
    121     mCurrentPage = mAdapter.getItem(mViewPager.getCurrentItem());
    122     if (mCurrentPage instanceof CallLogFragment) {
    123       ((CallLogFragment) mCurrentPage).onVisible();
    124     }
    125   }
    126 
    127   @Override
    128   public void onPause() {
    129     LogUtil.d("ListsFragment.onPause", null);
    130     if (mCurrentPage instanceof CallLogFragment) {
    131       ((CallLogFragment) mCurrentPage).onNotVisible();
    132     }
    133     super.onPause();
    134 
    135     mPaused = true;
    136   }
    137 
    138   @Override
    139   public void onDestroyView() {
    140     super.onDestroyView();
    141     mViewPager.removeOnPageChangeListener(this);
    142   }
    143 
    144   @Override
    145   public View onCreateView(
    146       LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    147     LogUtil.d("ListsFragment.onCreateView", null);
    148     Trace.beginSection(TAG + " onCreateView");
    149     Trace.beginSection(TAG + " inflate view");
    150     final View parentView = inflater.inflate(R.layout.lists_fragment, container, false);
    151     Trace.endSection();
    152     Trace.beginSection(TAG + " setup views");
    153 
    154     String[] tabTitles = new String[TAB_COUNT_WITH_VOICEMAIL];
    155     tabTitles[TAB_INDEX_SPEED_DIAL] = getResources().getString(R.string.tab_speed_dial);
    156     tabTitles[TAB_INDEX_HISTORY] = getResources().getString(R.string.tab_history);
    157     tabTitles[TAB_INDEX_ALL_CONTACTS] = getResources().getString(R.string.tab_all_contacts);
    158     tabTitles[TAB_INDEX_VOICEMAIL] = getResources().getString(R.string.tab_voicemail);
    159 
    160     int[] tabIcons = new int[TAB_COUNT_WITH_VOICEMAIL];
    161     tabIcons[TAB_INDEX_SPEED_DIAL] = R.drawable.quantum_ic_grade_white_24;
    162     tabIcons[TAB_INDEX_HISTORY] = R.drawable.quantum_ic_schedule_white_24;
    163     tabIcons[TAB_INDEX_ALL_CONTACTS] = R.drawable.quantum_ic_people_white_24;
    164     tabIcons[TAB_INDEX_VOICEMAIL] = R.drawable.quantum_ic_voicemail_white_24;
    165 
    166     mViewPager = (ViewPager) parentView.findViewById(R.id.lists_pager);
    167     mAdapter =
    168         new DialtactsPagerAdapter(
    169             getContext(),
    170             getChildFragmentManager(),
    171             tabTitles,
    172             mPrefs.getBoolean(
    173                 VisualVoicemailEnabledChecker.PREF_KEY_HAS_ACTIVE_VOICEMAIL_PROVIDER, false));
    174     mViewPager.setAdapter(mAdapter);
    175     mViewPager.setOffscreenPageLimit(TAB_COUNT_WITH_VOICEMAIL - 1);
    176     mViewPager.addOnPageChangeListener(this);
    177     showTab(TAB_INDEX_SPEED_DIAL);
    178 
    179     mViewPagerTabs = (ViewPagerTabs) parentView.findViewById(R.id.lists_pager_header);
    180     mViewPagerTabs.configureTabIcons(tabIcons);
    181     mViewPagerTabs.setViewPager(mViewPager);
    182     addOnPageChangeListener(mViewPagerTabs);
    183 
    184     mRemoveView = (RemoveView) parentView.findViewById(R.id.remove_view);
    185     mRemoveViewContent = parentView.findViewById(R.id.remove_view_content);
    186 
    187     if (PermissionsUtil.hasReadVoicemailPermissions(getContext())
    188         && PermissionsUtil.hasAddVoicemailPermissions(getContext())) {
    189       getActivity()
    190           .getContentResolver()
    191           .registerContentObserver(
    192               VoicemailContract.Status.CONTENT_URI, true, mVoicemailStatusObserver);
    193     } else {
    194       LogUtil.w("ListsFragment.onCreateView", "no voicemail read/add permissions");
    195     }
    196 
    197     Trace.endSection();
    198     Trace.endSection();
    199     return parentView;
    200   }
    201 
    202   @Override
    203   public void onDestroy() {
    204     getActivity().getContentResolver().unregisterContentObserver(mVoicemailStatusObserver);
    205     super.onDestroy();
    206   }
    207 
    208   public void addOnPageChangeListener(OnPageChangeListener onPageChangeListener) {
    209     if (!mOnPageChangeListeners.contains(onPageChangeListener)) {
    210       mOnPageChangeListeners.add(onPageChangeListener);
    211     }
    212   }
    213 
    214   /**
    215    * Shows the tab with the specified index. If the voicemail tab index is specified, but the
    216    * voicemail status hasn't been fetched, it will try to show the tab after the voicemail status
    217    * has been fetched.
    218    */
    219   public void showTab(int index) {
    220     if (index == TAB_INDEX_VOICEMAIL) {
    221       if (mAdapter.hasActiveVoicemailProvider()) {
    222         mViewPager.setCurrentItem(mAdapter.getRtlPosition(TAB_INDEX_VOICEMAIL));
    223       } else if (!mHasFetchedVoicemailStatus) {
    224         // Try to show the voicemail tab after the voicemail status returns.
    225         mShowVoicemailTabAfterVoicemailStatusIsFetched = true;
    226       }
    227     } else if (index < getTabCount()) {
    228       mViewPager.setCurrentItem(mAdapter.getRtlPosition(index));
    229     }
    230   }
    231 
    232   @Override
    233   public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
    234     mTabIndex = mAdapter.getRtlPosition(position);
    235 
    236     final int count = mOnPageChangeListeners.size();
    237     for (int i = 0; i < count; i++) {
    238       mOnPageChangeListeners.get(i).onPageScrolled(position, positionOffset, positionOffsetPixels);
    239     }
    240   }
    241 
    242   @Override
    243   public void onPageSelected(int position) {
    244     LogUtil.i("ListsFragment.onPageSelected", "position: %d", position);
    245     mTabIndex = mAdapter.getRtlPosition(position);
    246 
    247     // Show the tab which has been selected instead.
    248     mShowVoicemailTabAfterVoicemailStatusIsFetched = false;
    249 
    250     final int count = mOnPageChangeListeners.size();
    251     for (int i = 0; i < count; i++) {
    252       mOnPageChangeListeners.get(i).onPageSelected(position);
    253     }
    254     sendScreenViewForCurrentPosition();
    255 
    256     if (mCurrentPage instanceof CallLogFragment) {
    257       ((CallLogFragment) mCurrentPage).onNotVisible();
    258     }
    259     mCurrentPage = mAdapter.getItem(position);
    260     if (mCurrentPage instanceof CallLogFragment) {
    261       ((CallLogFragment) mCurrentPage).onVisible();
    262     }
    263   }
    264 
    265   @Override
    266   public void onPageScrollStateChanged(int state) {
    267     final int count = mOnPageChangeListeners.size();
    268     for (int i = 0; i < count; i++) {
    269       mOnPageChangeListeners.get(i).onPageScrollStateChanged(state);
    270     }
    271   }
    272 
    273   @Override
    274   public void onVoicemailStatusFetched(Cursor statusCursor) {
    275     mHasFetchedVoicemailStatus = true;
    276 
    277     if (getActivity() == null || mPaused) {
    278       return;
    279     }
    280 
    281     VoicemailStatusCorruptionHandler.maybeFixVoicemailStatus(
    282         getContext(), statusCursor, Source.Activity);
    283 
    284     // Update hasActiveVoicemailProvider, which controls the number of tabs displayed.
    285     boolean hasActiveVoicemailProvider =
    286         mVoicemailStatusHelper.getNumberActivityVoicemailSources(statusCursor) > 0;
    287     if (hasActiveVoicemailProvider != mAdapter.hasActiveVoicemailProvider()) {
    288       mAdapter.setHasActiveVoicemailProvider(hasActiveVoicemailProvider);
    289       mAdapter.notifyDataSetChanged();
    290 
    291       if (hasActiveVoicemailProvider) {
    292         Logger.get(getContext()).logImpression(DialerImpression.Type.VVM_TAB_VISIBLE);
    293         mViewPagerTabs.updateTab(TAB_INDEX_VOICEMAIL);
    294       } else {
    295         mViewPagerTabs.removeTab(TAB_INDEX_VOICEMAIL);
    296         mAdapter.removeVoicemailFragment(getChildFragmentManager());
    297       }
    298 
    299       mPrefs
    300           .edit()
    301           .putBoolean(
    302               VisualVoicemailEnabledChecker.PREF_KEY_HAS_ACTIVE_VOICEMAIL_PROVIDER,
    303               hasActiveVoicemailProvider)
    304           .apply();
    305     }
    306 
    307     if (hasActiveVoicemailProvider) {
    308       mCallLogQueryHandler.fetchVoicemailUnreadCount();
    309     }
    310 
    311     if (mAdapter.hasActiveVoicemailProvider() && mShowVoicemailTabAfterVoicemailStatusIsFetched) {
    312       mShowVoicemailTabAfterVoicemailStatusIsFetched = false;
    313       showTab(TAB_INDEX_VOICEMAIL);
    314     }
    315   }
    316 
    317   @Override
    318   public void onVoicemailUnreadCountFetched(Cursor cursor) {
    319     if (getActivity() == null || getActivity().isFinishing() || cursor == null) {
    320       return;
    321     }
    322 
    323     int count = 0;
    324     try {
    325       count = cursor.getCount();
    326     } finally {
    327       cursor.close();
    328     }
    329 
    330     mViewPagerTabs.setUnreadCount(count, TAB_INDEX_VOICEMAIL);
    331     mViewPagerTabs.updateTab(TAB_INDEX_VOICEMAIL);
    332   }
    333 
    334   @Override
    335   public void onMissedCallsUnreadCountFetched(Cursor cursor) {
    336     if (getActivity() == null || getActivity().isFinishing() || cursor == null) {
    337       return;
    338     }
    339 
    340     int count = 0;
    341     try {
    342       count = cursor.getCount();
    343     } finally {
    344       cursor.close();
    345     }
    346 
    347     mViewPagerTabs.setUnreadCount(count, TAB_INDEX_HISTORY);
    348     mViewPagerTabs.updateTab(TAB_INDEX_HISTORY);
    349   }
    350 
    351   @Override
    352   public boolean onCallsFetched(Cursor statusCursor) {
    353     // Return false; did not take ownership of cursor
    354     return false;
    355   }
    356 
    357   public int getCurrentTabIndex() {
    358     return mTabIndex;
    359   }
    360 
    361   /**
    362    * External method to update unread count because the unread count changes when the user expands a
    363    * voicemail in the call log or when the user expands an unread call in the call history tab.
    364    */
    365   public void updateTabUnreadCounts() {
    366     if (mCallLogQueryHandler != null) {
    367       mCallLogQueryHandler.fetchMissedCallsUnreadCount();
    368       if (mAdapter.hasActiveVoicemailProvider()) {
    369         mCallLogQueryHandler.fetchVoicemailUnreadCount();
    370       }
    371     }
    372   }
    373 
    374   /** External method to mark all missed calls as read. */
    375   public void markMissedCallsAsReadAndRemoveNotifications() {
    376     if (mCallLogQueryHandler != null) {
    377       mCallLogQueryHandler.markMissedCallsAsRead();
    378       CallLogNotificationsService.markNewMissedCallsAsOld(getContext(), null);
    379     }
    380   }
    381 
    382   public void showRemoveView(boolean show) {
    383     mRemoveViewContent.setVisibility(show ? View.VISIBLE : View.GONE);
    384     mRemoveView.setAlpha(show ? 0 : 1);
    385     mRemoveView.animate().alpha(show ? 1 : 0).start();
    386   }
    387 
    388   public boolean hasFrequents() {
    389     Fragment page = mAdapter.getItem(mAdapter.getRtlPosition(TAB_INDEX_SPEED_DIAL));
    390     return page instanceof OldSpeedDialFragment
    391         ? ((OldSpeedDialFragment) page).hasFrequents()
    392         : ((SpeedDialFragment) page).hasFrequents();
    393   }
    394 
    395   public RemoveView getRemoveView() {
    396     return mRemoveView;
    397   }
    398 
    399   public int getTabCount() {
    400     return mAdapter.getCount();
    401   }
    402 
    403   public void sendScreenViewForCurrentPosition() {
    404     if (!isResumed()) {
    405       return;
    406     }
    407 
    408     ScreenEvent.Type screenType;
    409     switch (getCurrentTabIndex()) {
    410       case TAB_INDEX_SPEED_DIAL:
    411         screenType = ScreenEvent.Type.SPEED_DIAL;
    412         break;
    413       case TAB_INDEX_HISTORY:
    414         screenType = ScreenEvent.Type.CALL_LOG;
    415         break;
    416       case TAB_INDEX_ALL_CONTACTS:
    417         screenType = ScreenEvent.Type.ALL_CONTACTS;
    418         break;
    419       case TAB_INDEX_VOICEMAIL:
    420         screenType = ScreenEvent.Type.VOICEMAIL_LOG;
    421         break;
    422       default:
    423         return;
    424     }
    425     Logger.get(getActivity()).logScreenView(screenType, getActivity());
    426   }
    427 }
    428