ListsFragment.java revision 9e40502d2b5685b6996ecee7b3b3c812d5b06ec8
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 */
16package com.android.dialer.list;
17
18import android.animation.LayoutTransition;
19import android.app.Fragment;
20import android.app.FragmentManager;
21import android.content.Context;
22import android.content.SharedPreferences;
23import android.database.Cursor;
24import android.os.Bundle;
25import android.os.Trace;
26import android.preference.PreferenceManager;
27import android.provider.CallLog.Calls;
28import android.support.v13.app.FragmentPagerAdapter;
29import android.support.v4.view.ViewPager;
30import android.support.v4.view.ViewPager.OnPageChangeListener;
31import android.support.v7.app.ActionBar;
32import android.support.v7.app.AppCompatActivity;
33import android.util.Log;
34import android.view.LayoutInflater;
35import android.view.View;
36import android.view.ViewGroup;
37import android.widget.AbsListView;
38import android.widget.ListView;
39
40import com.android.contacts.common.GeoUtil;
41import com.android.contacts.common.list.ViewPagerTabs;
42import com.android.contacts.commonbind.analytics.AnalyticsUtil;
43import com.android.dialer.DialtactsActivity;
44import com.android.dialer.R;
45import com.android.dialer.calllog.CallLogFragment;
46import com.android.dialer.calllog.CallLogQueryHandler;
47import com.android.dialer.calllog.ContactInfoHelper;
48import com.android.dialer.util.DialerUtils;
49import com.android.dialer.voicemail.VoicemailStatusHelper;
50import com.android.dialer.voicemail.VoicemailStatusHelperImpl;
51import com.android.dialer.widget.ActionBarController;
52import com.android.dialerbind.ObjectFactory;
53
54import java.util.ArrayList;
55
56/**
57 * Fragment that is used as the main screen of the Dialer.
58 *
59 * Contains a ViewPager that contains various contact lists like the Speed Dial list and the
60 * All Contacts list. This will also eventually contain the logic that allows sliding the
61 * ViewPager containing the lists up above the search bar and pin it against the top of the
62 * screen.
63 */
64public class ListsFragment extends Fragment
65        implements ViewPager.OnPageChangeListener, CallLogQueryHandler.Listener {
66
67    private static final boolean DEBUG = DialtactsActivity.DEBUG;
68    private static final String TAG = "ListsFragment";
69
70    public static final int TAB_INDEX_SPEED_DIAL = 0;
71    public static final int TAB_INDEX_HISTORY = 1;
72    public static final int TAB_INDEX_ALL_CONTACTS = 2;
73    public static final int TAB_INDEX_VOICEMAIL = 3;
74
75    public static final int TAB_COUNT_DEFAULT = 3;
76    public static final int TAB_COUNT_WITH_VOICEMAIL = 4;
77
78    private static final String PREF_KEY_HAS_ACTIVE_VOICEMAIL_PROVIDER =
79            "has_active_voicemail_provider";
80
81    public interface HostInterface {
82        public ActionBarController getActionBarController();
83    }
84
85    private ActionBar mActionBar;
86    private ViewPager mViewPager;
87    private ViewPagerTabs mViewPagerTabs;
88    private ViewPagerAdapter mViewPagerAdapter;
89    private RemoveView mRemoveView;
90    private View mRemoveViewContent;
91
92    private SpeedDialFragment mSpeedDialFragment;
93    private CallLogFragment mHistoryFragment;
94    private AllContactsFragment mAllContactsFragment;
95    private CallLogFragment mVoicemailFragment;
96
97    private SharedPreferences mPrefs;
98    private boolean mHasActiveVoicemailProvider;
99    private boolean mHasFetchedVoicemailStatus;
100    private boolean mShowVoicemailTabAfterVoicemailStatusIsFetched;
101
102    private VoicemailStatusHelper mVoicemailStatusHelper;
103    private ArrayList<OnPageChangeListener> mOnPageChangeListeners =
104            new ArrayList<OnPageChangeListener>();
105
106    private String[] mTabTitles;
107    private int[] mTabIcons;
108
109    /**
110     * The position of the currently selected tab.
111     */
112    private int mTabIndex = TAB_INDEX_SPEED_DIAL;
113    private CallLogQueryHandler mCallLogQueryHandler;
114
115    public class ViewPagerAdapter extends FragmentPagerAdapter {
116        public ViewPagerAdapter(FragmentManager fm) {
117            super(fm);
118        }
119
120        @Override
121        public long getItemId(int position) {
122            return getRtlPosition(position);
123        }
124
125        @Override
126        public Fragment getItem(int position) {
127            switch (getRtlPosition(position)) {
128                case TAB_INDEX_SPEED_DIAL:
129                    mSpeedDialFragment = new SpeedDialFragment();
130                    return mSpeedDialFragment;
131                case TAB_INDEX_HISTORY:
132                    mHistoryFragment = new CallLogFragment(CallLogQueryHandler.CALL_TYPE_ALL);
133                    return mHistoryFragment;
134                case TAB_INDEX_ALL_CONTACTS:
135                    mAllContactsFragment = new AllContactsFragment();
136                    return mAllContactsFragment;
137                case TAB_INDEX_VOICEMAIL:
138                    mVoicemailFragment = new CallLogFragment(Calls.VOICEMAIL_TYPE);
139                    return mVoicemailFragment;
140            }
141            throw new IllegalStateException("No fragment at position " + position);
142        }
143
144        @Override
145        public Object instantiateItem(ViewGroup container, int position) {
146            // On rotation the FragmentManager handles rotation. Therefore getItem() isn't called.
147            // Copy the fragments that the FragmentManager finds so that we can store them in
148            // instance variables for later.
149            final Fragment fragment =
150                    (Fragment) super.instantiateItem(container, position);
151            if (fragment instanceof SpeedDialFragment) {
152                mSpeedDialFragment = (SpeedDialFragment) fragment;
153            } else if (fragment instanceof CallLogFragment && position == TAB_INDEX_HISTORY) {
154                mHistoryFragment = (CallLogFragment) fragment;
155            } else if (fragment instanceof AllContactsFragment) {
156                mAllContactsFragment = (AllContactsFragment) fragment;
157            } else if (fragment instanceof CallLogFragment && position == TAB_INDEX_VOICEMAIL) {
158                mVoicemailFragment = (CallLogFragment) fragment;
159            }
160            return fragment;
161        }
162
163        @Override
164        public int getCount() {
165            return mHasActiveVoicemailProvider ? TAB_COUNT_WITH_VOICEMAIL : TAB_COUNT_DEFAULT;
166        }
167
168        @Override
169        public CharSequence getPageTitle(int position) {
170            return mTabTitles[position];
171        }
172    }
173
174    @Override
175    public void onCreate(Bundle savedInstanceState) {
176        Trace.beginSection(TAG + " onCreate");
177        super.onCreate(savedInstanceState);
178
179        mVoicemailStatusHelper = new VoicemailStatusHelperImpl();
180        mHasFetchedVoicemailStatus = false;
181
182        mPrefs = PreferenceManager.getDefaultSharedPreferences(getActivity());
183        mHasActiveVoicemailProvider = mPrefs.getBoolean(
184                PREF_KEY_HAS_ACTIVE_VOICEMAIL_PROVIDER, false);
185
186        Trace.endSection();
187    }
188
189    @Override
190    public void onResume() {
191        Trace.beginSection(TAG + " onResume");
192        super.onResume();
193        mActionBar = ((AppCompatActivity) getActivity()).getSupportActionBar();
194        if (getUserVisibleHint()) {
195            sendScreenViewForCurrentPosition();
196        }
197
198        // Fetch voicemail status to determine if we should show the voicemail tab.
199        mCallLogQueryHandler =
200                new CallLogQueryHandler(getActivity(), getActivity().getContentResolver(), this);
201        mCallLogQueryHandler.fetchVoicemailStatus();
202        Trace.endSection();
203    }
204
205    @Override
206    public View onCreateView(LayoutInflater inflater, ViewGroup container,
207            Bundle savedInstanceState) {
208        Trace.beginSection(TAG + " onCreateView");
209        Trace.beginSection(TAG + " inflate view");
210        final View parentView = inflater.inflate(R.layout.lists_fragment, container, false);
211        Trace.endSection();
212        Trace.beginSection(TAG + " setup views");
213        mViewPager = (ViewPager) parentView.findViewById(R.id.lists_pager);
214        mViewPagerAdapter = new ViewPagerAdapter(getChildFragmentManager());
215        mViewPager.setAdapter(mViewPagerAdapter);
216        mViewPager.setOffscreenPageLimit(TAB_COUNT_WITH_VOICEMAIL - 1);
217        mViewPager.setOnPageChangeListener(this);
218        showTab(TAB_INDEX_SPEED_DIAL);
219
220        mTabTitles = new String[TAB_COUNT_WITH_VOICEMAIL];
221        mTabTitles[TAB_INDEX_SPEED_DIAL] = getResources().getString(R.string.tab_speed_dial);
222        mTabTitles[TAB_INDEX_HISTORY] = getResources().getString(R.string.tab_history);
223        mTabTitles[TAB_INDEX_ALL_CONTACTS] = getResources().getString(R.string.tab_all_contacts);
224        mTabTitles[TAB_INDEX_VOICEMAIL] = getResources().getString(R.string.tab_voicemail);
225
226        mTabIcons = new int[TAB_COUNT_WITH_VOICEMAIL];
227        mTabIcons[TAB_INDEX_SPEED_DIAL] = R.drawable.ic_grade_24dp;
228        mTabIcons[TAB_INDEX_HISTORY] = R.drawable.ic_schedule_24dp;
229        mTabIcons[TAB_INDEX_ALL_CONTACTS] = R.drawable.ic_people_24dp;
230        mTabIcons[TAB_INDEX_VOICEMAIL] = R.drawable.ic_voicemail_24dp;
231
232        mViewPagerTabs = (ViewPagerTabs) parentView.findViewById(R.id.lists_pager_header);
233        mViewPagerTabs.configureTabIcons(mTabIcons);
234        mViewPagerTabs.setViewPager(mViewPager);
235        addOnPageChangeListener(mViewPagerTabs);
236
237        mRemoveView = (RemoveView) parentView.findViewById(R.id.remove_view);
238        mRemoveViewContent = parentView.findViewById(R.id.remove_view_content);
239
240        Trace.endSection();
241        Trace.endSection();
242        return parentView;
243    }
244
245    public void addOnPageChangeListener(OnPageChangeListener onPageChangeListener) {
246        if (!mOnPageChangeListeners.contains(onPageChangeListener)) {
247            mOnPageChangeListeners.add(onPageChangeListener);
248        }
249    }
250
251    /**
252     * Shows the tab with the specified index. If the voicemail tab index is specified, but the
253     * voicemail status hasn't been fetched, it will try to show the tab after the voicemail status
254     * has been fetched.
255     */
256    public void showTab(int index) {
257        if (index == TAB_INDEX_VOICEMAIL) {
258            if (mHasActiveVoicemailProvider) {
259                mViewPager.setCurrentItem(getRtlPosition(TAB_INDEX_VOICEMAIL));
260            } else if (!mHasFetchedVoicemailStatus) {
261                // Try to show the voicemail tab after the voicemail status returns.
262                mShowVoicemailTabAfterVoicemailStatusIsFetched = true;
263            }
264        } else if (index < getTabCount()){
265            mViewPager.setCurrentItem(getRtlPosition(index));
266        }
267    }
268
269    @Override
270    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
271        mTabIndex = getRtlPosition(position);
272
273        final int count = mOnPageChangeListeners.size();
274        for (int i = 0; i < count; i++) {
275            mOnPageChangeListeners.get(i).onPageScrolled(position, positionOffset,
276                    positionOffsetPixels);
277        }
278    }
279
280    @Override
281    public void onPageSelected(int position) {
282        mTabIndex = getRtlPosition(position);
283
284        // Show the tab which has been selected instead.
285        mShowVoicemailTabAfterVoicemailStatusIsFetched = false;
286
287        final int count = mOnPageChangeListeners.size();
288        for (int i = 0; i < count; i++) {
289            mOnPageChangeListeners.get(i).onPageSelected(position);
290        }
291        sendScreenViewForCurrentPosition();
292    }
293
294    @Override
295    public void onPageScrollStateChanged(int state) {
296        final int count = mOnPageChangeListeners.size();
297        for (int i = 0; i < count; i++) {
298            mOnPageChangeListeners.get(i).onPageScrollStateChanged(state);
299        }
300    }
301
302    @Override
303    public void onVoicemailStatusFetched(Cursor statusCursor) {
304        mHasFetchedVoicemailStatus = true;
305
306        if (getActivity() == null || getActivity().isFinishing()) {
307            return;
308        }
309
310        // Update mHasActiveVoicemailProvider, which controls the number of tabs displayed.
311        boolean hasActiveVoicemailProvider =
312                mVoicemailStatusHelper.getNumberActivityVoicemailSources(statusCursor) > 0;
313        if (hasActiveVoicemailProvider != mHasActiveVoicemailProvider) {
314            mHasActiveVoicemailProvider = hasActiveVoicemailProvider;
315            mViewPagerAdapter.notifyDataSetChanged();
316            mViewPagerTabs.updateTab(TAB_INDEX_VOICEMAIL);
317
318            mPrefs.edit()
319                  .putBoolean(PREF_KEY_HAS_ACTIVE_VOICEMAIL_PROVIDER, hasActiveVoicemailProvider)
320                  .commit();
321        }
322
323        if (hasActiveVoicemailProvider) {
324            mCallLogQueryHandler.fetchVoicemailUnreadCount();
325        }
326
327        if (mHasActiveVoicemailProvider && mShowVoicemailTabAfterVoicemailStatusIsFetched) {
328            mShowVoicemailTabAfterVoicemailStatusIsFetched = false;
329            showTab(TAB_INDEX_VOICEMAIL);
330        }
331    }
332
333    @Override
334    public void onVoicemailUnreadCountFetched(Cursor cursor) {
335        if (getActivity() == null || getActivity().isFinishing() || cursor == null) {
336            return;
337        }
338
339        int count = 0;
340        try {
341            count = cursor.getCount();
342        } finally {
343            cursor.close();
344        }
345
346        mViewPagerTabs.setUnreadCount(count, TAB_INDEX_VOICEMAIL);
347        mViewPagerTabs.updateTab(TAB_INDEX_VOICEMAIL);
348    }
349
350    @Override
351    public boolean onCallsFetched(Cursor statusCursor) {
352        // Return false; did not take ownership of cursor
353        return false;
354    }
355
356    public int getCurrentTabIndex() {
357        return mTabIndex;
358    }
359
360    /**
361     * External method to update unread count because the unread count changes when the user
362     * expands a voicemail in the call log.
363     */
364    public void updateTabUnreadCounts() {
365        if (mHasActiveVoicemailProvider) {
366            mCallLogQueryHandler.fetchVoicemailUnreadCount();
367        }
368    }
369
370    public void showRemoveView(boolean show) {
371        mRemoveViewContent.setVisibility(show ? View.VISIBLE : View.GONE);
372        mRemoveView.setAlpha(show ? 0 : 1);
373        mRemoveView.animate().alpha(show ? 1 : 0).start();
374    }
375
376    public boolean shouldShowActionBar() {
377        // TODO: Update this based on scroll state.
378        return mActionBar != null;
379    }
380
381    public SpeedDialFragment getSpeedDialFragment() {
382        return mSpeedDialFragment;
383    }
384
385    public RemoveView getRemoveView() {
386        return mRemoveView;
387    }
388
389    public int getTabCount() {
390        return mViewPagerAdapter.getCount();
391    }
392
393    private int getRtlPosition(int position) {
394        if (DialerUtils.isRtl()) {
395            return mViewPagerAdapter.getCount() - 1 - position;
396        }
397        return position;
398    }
399
400    public void sendScreenViewForCurrentPosition() {
401        if (!isResumed()) {
402            return;
403        }
404
405        String fragmentName;
406        switch (getCurrentTabIndex()) {
407            case TAB_INDEX_SPEED_DIAL:
408                fragmentName = SpeedDialFragment.class.getSimpleName();
409                break;
410            case TAB_INDEX_HISTORY:
411                fragmentName = CallLogFragment.class.getSimpleName() + "#History";
412                break;
413            case TAB_INDEX_ALL_CONTACTS:
414                fragmentName = AllContactsFragment.class.getSimpleName();
415                break;
416            case TAB_INDEX_VOICEMAIL:
417                fragmentName = CallLogFragment.class.getSimpleName() + "#Voicemail";
418            default:
419                return;
420        }
421        AnalyticsUtil.sendScreenView(fragmentName, getActivity(), null);
422    }
423}
424