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