/*
* 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 static android.support.v4.view.ViewPager.SCROLL_STATE_SETTLING;
import android.app.Fragment;
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.v4.view.ViewPager.OnPageChangeListener;
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.CallLogNotificationsService;
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.common.LogUtil;
import com.android.dialer.database.CallLogQueryHandler;
import com.android.dialer.database.CallLogQueryHandler.Listener;
import com.android.dialer.logging.DialerImpression;
import com.android.dialer.logging.Logger;
import com.android.dialer.logging.ScreenEvent;
import com.android.dialer.logging.UiAction;
import com.android.dialer.performancereport.PerformanceReport;
import com.android.dialer.speeddial.SpeedDialFragment;
import com.android.dialer.util.PermissionsUtil;
import com.android.dialer.voicemailstatus.VisualVoicemailEnabledChecker;
import com.android.dialer.voicemailstatus.VoicemailStatusHelper;
import java.util.ArrayList;
/**
* Fragment that is used as the main screen of the Dialer.
*
*
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 OnPageChangeListener, Listener {
private static final String TAG = "ListsFragment";
private DialerViewPager viewPager;
private ViewPagerTabs viewPagerTabs;
private DialtactsPagerAdapter adapter;
private RemoveView removeView;
private View removeViewContent;
private Fragment currentPage;
private SharedPreferences prefs;
private boolean hasFetchedVoicemailStatus;
private boolean showVoicemailTabAfterVoicemailStatusIsFetched;
private final ArrayList onPageChangeListeners = new ArrayList<>();
/** The position of the currently selected tab. */
private int tabIndex = DialtactsPagerAdapter.TAB_INDEX_SPEED_DIAL;
private boolean paused;
private CallLogQueryHandler callLogQueryHandler;
private UiAction.Type[] actionTypeList;
private final DialerImpression.Type[] swipeImpressionList =
new DialerImpression.Type[DialtactsPagerAdapter.TAB_COUNT_WITH_VOICEMAIL];
private final DialerImpression.Type[] clickImpressionList =
new DialerImpression.Type[DialtactsPagerAdapter.TAB_COUNT_WITH_VOICEMAIL];
// Only for detecting page selected by swiping or clicking.
private boolean onPageScrolledBeforeScrollStateSettling;
private final ContentObserver voicemailStatusObserver =
new ContentObserver(new Handler()) {
@Override
public void onChange(boolean selfChange) {
super.onChange(selfChange);
callLogQueryHandler.fetchVoicemailStatus();
}
};
@Override
public void onCreate(Bundle savedInstanceState) {
LogUtil.d("ListsFragment.onCreate", null);
Trace.beginSection(TAG + " onCreate");
super.onCreate(savedInstanceState);
prefs = PreferenceManager.getDefaultSharedPreferences(getActivity().getApplicationContext());
Trace.endSection();
}
@Override
public void onResume() {
LogUtil.enterBlock("ListsFragment.onResume");
Trace.beginSection(TAG + " onResume");
super.onResume();
paused = false;
if (getUserVisibleHint()) {
sendScreenViewForCurrentPosition();
}
// Fetch voicemail status to determine if we should show the voicemail tab.
callLogQueryHandler =
new CallLogQueryHandler(getActivity(), getActivity().getContentResolver(), this);
callLogQueryHandler.fetchVoicemailStatus();
callLogQueryHandler.fetchMissedCallsUnreadCount();
Trace.endSection();
currentPage = adapter.getItem(viewPager.getCurrentItem());
}
@Override
public void onPause() {
LogUtil.enterBlock("ListsFragment.onPause");
super.onPause();
paused = true;
}
@Override
public void onDestroyView() {
super.onDestroyView();
viewPager.removeOnPageChangeListener(this);
}
@Override
public View onCreateView(
LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
LogUtil.enterBlock("ListsFragment.onCreateView");
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");
actionTypeList = new UiAction.Type[DialtactsPagerAdapter.TAB_COUNT_WITH_VOICEMAIL];
actionTypeList[DialtactsPagerAdapter.TAB_INDEX_SPEED_DIAL] =
UiAction.Type.CHANGE_TAB_TO_FAVORITE;
actionTypeList[DialtactsPagerAdapter.TAB_INDEX_HISTORY] = UiAction.Type.CHANGE_TAB_TO_CALL_LOG;
actionTypeList[DialtactsPagerAdapter.TAB_INDEX_ALL_CONTACTS] =
UiAction.Type.CHANGE_TAB_TO_CONTACTS;
actionTypeList[DialtactsPagerAdapter.TAB_INDEX_VOICEMAIL] =
UiAction.Type.CHANGE_TAB_TO_VOICEMAIL;
swipeImpressionList[DialtactsPagerAdapter.TAB_INDEX_SPEED_DIAL] =
DialerImpression.Type.SWITCH_TAB_TO_FAVORITE_BY_SWIPE;
swipeImpressionList[DialtactsPagerAdapter.TAB_INDEX_HISTORY] =
DialerImpression.Type.SWITCH_TAB_TO_CALL_LOG_BY_SWIPE;
swipeImpressionList[DialtactsPagerAdapter.TAB_INDEX_ALL_CONTACTS] =
DialerImpression.Type.SWITCH_TAB_TO_CONTACTS_BY_SWIPE;
swipeImpressionList[DialtactsPagerAdapter.TAB_INDEX_VOICEMAIL] =
DialerImpression.Type.SWITCH_TAB_TO_VOICEMAIL_BY_SWIPE;
clickImpressionList[DialtactsPagerAdapter.TAB_INDEX_SPEED_DIAL] =
DialerImpression.Type.SWITCH_TAB_TO_FAVORITE_BY_CLICK;
clickImpressionList[DialtactsPagerAdapter.TAB_INDEX_HISTORY] =
DialerImpression.Type.SWITCH_TAB_TO_CALL_LOG_BY_CLICK;
clickImpressionList[DialtactsPagerAdapter.TAB_INDEX_ALL_CONTACTS] =
DialerImpression.Type.SWITCH_TAB_TO_CONTACTS_BY_CLICK;
clickImpressionList[DialtactsPagerAdapter.TAB_INDEX_VOICEMAIL] =
DialerImpression.Type.SWITCH_TAB_TO_VOICEMAIL_BY_CLICK;
String[] tabTitles = new String[DialtactsPagerAdapter.TAB_COUNT_WITH_VOICEMAIL];
tabTitles[DialtactsPagerAdapter.TAB_INDEX_SPEED_DIAL] =
getResources().getString(R.string.tab_speed_dial);
tabTitles[DialtactsPagerAdapter.TAB_INDEX_HISTORY] =
getResources().getString(R.string.tab_history);
tabTitles[DialtactsPagerAdapter.TAB_INDEX_ALL_CONTACTS] =
getResources().getString(R.string.tab_all_contacts);
tabTitles[DialtactsPagerAdapter.TAB_INDEX_VOICEMAIL] =
getResources().getString(R.string.tab_voicemail);
int[] tabIcons = new int[DialtactsPagerAdapter.TAB_COUNT_WITH_VOICEMAIL];
tabIcons[DialtactsPagerAdapter.TAB_INDEX_SPEED_DIAL] = R.drawable.quantum_ic_grade_white_24;
tabIcons[DialtactsPagerAdapter.TAB_INDEX_HISTORY] = R.drawable.quantum_ic_schedule_white_24;
tabIcons[DialtactsPagerAdapter.TAB_INDEX_ALL_CONTACTS] = R.drawable.quantum_ic_people_white_24;
tabIcons[DialtactsPagerAdapter.TAB_INDEX_VOICEMAIL] = R.drawable.quantum_ic_voicemail_white_24;
viewPager = (DialerViewPager) parentView.findViewById(R.id.lists_pager);
adapter =
new DialtactsPagerAdapter(
getContext(),
getChildFragmentManager(),
tabTitles,
prefs.getBoolean(
VisualVoicemailEnabledChecker.PREF_KEY_HAS_ACTIVE_VOICEMAIL_PROVIDER, false));
viewPager.setAdapter(adapter);
// This is deliberate. See cl/172018946 for the app startup implications of using 1 here
// versus loading more fragments upfront.
viewPager.setOffscreenPageLimit(1);
viewPager.addOnPageChangeListener(this);
showTab(DialtactsPagerAdapter.TAB_INDEX_SPEED_DIAL);
viewPagerTabs = (ViewPagerTabs) parentView.findViewById(R.id.lists_pager_header);
viewPagerTabs.configureTabIcons(tabIcons);
viewPagerTabs.setViewPager(viewPager);
addOnPageChangeListener(viewPagerTabs);
removeView = (RemoveView) parentView.findViewById(R.id.remove_view);
removeViewContent = parentView.findViewById(R.id.remove_view_content);
if (PermissionsUtil.hasReadVoicemailPermissions(getContext())
&& PermissionsUtil.hasAddVoicemailPermissions(getContext())) {
getActivity()
.getContentResolver()
.registerContentObserver(
VoicemailContract.Status.CONTENT_URI, true, voicemailStatusObserver);
} else {
LogUtil.w("ListsFragment.onCreateView", "no voicemail read permissions");
}
Trace.endSection();
Trace.endSection();
return parentView;
}
@Override
public void onDestroy() {
getActivity().getContentResolver().unregisterContentObserver(voicemailStatusObserver);
super.onDestroy();
}
public void addOnPageChangeListener(OnPageChangeListener onPageChangeListener) {
if (!onPageChangeListeners.contains(onPageChangeListener)) {
onPageChangeListeners.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 show the speed dial tab and try to show the
* voicemail tab after the voicemail status has been fetched.
*/
public void showTab(int index) {
if (index == DialtactsPagerAdapter.TAB_INDEX_VOICEMAIL) {
if (adapter.hasActiveVoicemailProvider()) {
viewPager.setCurrentItem(adapter.getRtlPosition(DialtactsPagerAdapter.TAB_INDEX_VOICEMAIL));
} else if (!hasFetchedVoicemailStatus) {
// Try to show the voicemail tab after the voicemail status returns.
showVoicemailTabAfterVoicemailStatusIsFetched = true;
}
} else if (index < getTabCount()) {
viewPager.setCurrentItem(adapter.getRtlPosition(index));
}
}
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
// onPageScrolled(0, 0, 0) is called when app launch. And we should ignore it.
// It's also called when swipe right from first tab, but we don't care.
if (positionOffsetPixels != 0) {
onPageScrolledBeforeScrollStateSettling = true;
}
tabIndex = adapter.getRtlPosition(position);
final int count = onPageChangeListeners.size();
for (int i = 0; i < count; i++) {
onPageChangeListeners.get(i).onPageScrolled(position, positionOffset, positionOffsetPixels);
}
}
@Override
public void onPageSelected(int position) {
// onPageScrollStateChanged(SCROLL_STATE_SETTLING) must be called before this.
// If onPageScrolled() is called before that, the page is selected by swiping;
// otherwise the page is selected by clicking.
if (onPageScrolledBeforeScrollStateSettling) {
Logger.get(getContext()).logImpression(swipeImpressionList[position]);
onPageScrolledBeforeScrollStateSettling = false;
} else {
Logger.get(getContext()).logImpression(clickImpressionList[position]);
}
PerformanceReport.recordClick(actionTypeList[position]);
LogUtil.i("ListsFragment.onPageSelected", "position: %d", position);
tabIndex = adapter.getRtlPosition(position);
// Show the tab which has been selected instead.
showVoicemailTabAfterVoicemailStatusIsFetched = false;
final int count = onPageChangeListeners.size();
for (int i = 0; i < count; i++) {
onPageChangeListeners.get(i).onPageSelected(position);
}
sendScreenViewForCurrentPosition();
if (currentPage instanceof CallLogFragment) {
((CallLogFragment) currentPage).onNotVisible();
}
currentPage = adapter.getItem(position);
if (currentPage instanceof CallLogFragment) {
((CallLogFragment) currentPage).onVisible();
}
}
@Override
public void onPageScrollStateChanged(int state) {
if (state != SCROLL_STATE_SETTLING) {
onPageScrolledBeforeScrollStateSettling = false;
}
final int count = onPageChangeListeners.size();
for (int i = 0; i < count; i++) {
onPageChangeListeners.get(i).onPageScrollStateChanged(state);
}
}
@Override
public void onVoicemailStatusFetched(Cursor statusCursor) {
hasFetchedVoicemailStatus = true;
if (getActivity() == null || paused) {
return;
}
VoicemailStatusCorruptionHandler.maybeFixVoicemailStatus(
getContext(), statusCursor, Source.Activity);
// Update hasActiveVoicemailProvider, which controls the number of tabs displayed.
boolean hasActiveVoicemailProvider =
VoicemailStatusHelper.getNumberActivityVoicemailSources(statusCursor) > 0;
if (hasActiveVoicemailProvider != adapter.hasActiveVoicemailProvider()) {
adapter.setHasActiveVoicemailProvider(hasActiveVoicemailProvider);
adapter.notifyDataSetChanged();
if (hasActiveVoicemailProvider) {
Logger.get(getContext()).logImpression(DialerImpression.Type.VVM_TAB_VISIBLE);
viewPagerTabs.updateTab(DialtactsPagerAdapter.TAB_INDEX_VOICEMAIL);
} else {
viewPagerTabs.removeTab(DialtactsPagerAdapter.TAB_INDEX_VOICEMAIL);
adapter.removeVoicemailFragment(getChildFragmentManager());
}
prefs
.edit()
.putBoolean(
VisualVoicemailEnabledChecker.PREF_KEY_HAS_ACTIVE_VOICEMAIL_PROVIDER,
hasActiveVoicemailProvider)
.apply();
}
if (hasActiveVoicemailProvider) {
callLogQueryHandler.fetchVoicemailUnreadCount();
}
if (adapter.hasActiveVoicemailProvider() && showVoicemailTabAfterVoicemailStatusIsFetched) {
showVoicemailTabAfterVoicemailStatusIsFetched = false;
showTab(DialtactsPagerAdapter.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();
}
viewPagerTabs.setUnreadCount(count, DialtactsPagerAdapter.TAB_INDEX_VOICEMAIL);
viewPagerTabs.updateTab(DialtactsPagerAdapter.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();
}
viewPagerTabs.setUnreadCount(count, DialtactsPagerAdapter.TAB_INDEX_HISTORY);
viewPagerTabs.updateTab(DialtactsPagerAdapter.TAB_INDEX_HISTORY);
}
@Override
public boolean onCallsFetched(Cursor statusCursor) {
// Return false; did not take ownership of cursor
return false;
}
public int getCurrentTabIndex() {
return tabIndex;
}
public boolean shouldShowFab() {
// If the VVM TOS is visible, don't show the fab
if (currentPage instanceof VisualVoicemailCallLogFragment
&& ((VisualVoicemailCallLogFragment) currentPage).isModalAlertVisible()) {
return false;
}
return true;
}
/**
* 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 (callLogQueryHandler != null) {
callLogQueryHandler.fetchMissedCallsUnreadCount();
if (adapter.hasActiveVoicemailProvider()) {
callLogQueryHandler.fetchVoicemailUnreadCount();
}
}
}
/** External method to mark all missed calls as read. */
public void markMissedCallsAsReadAndRemoveNotifications() {
if (callLogQueryHandler != null) {
callLogQueryHandler.markMissedCallsAsRead();
CallLogNotificationsService.cancelAllMissedCalls(getContext());
}
}
public void showRemoveView(boolean show) {
removeViewContent.setVisibility(show ? View.VISIBLE : View.GONE);
removeView.setAlpha(show ? 0 : 1);
removeView.animate().alpha(show ? 1 : 0).start();
}
public void showMultiSelectRemoveView(boolean show) {
viewPagerTabs.setVisibility(show ? View.GONE : View.VISIBLE);
viewPager.setEnableSwipingPages(!show);
}
public boolean hasFrequents() {
Fragment page =
adapter.getItem(adapter.getRtlPosition(DialtactsPagerAdapter.TAB_INDEX_SPEED_DIAL));
return page instanceof OldSpeedDialFragment
? ((OldSpeedDialFragment) page).hasFrequents()
: ((SpeedDialFragment) page).hasFrequents();
}
public RemoveView getRemoveView() {
return removeView;
}
public int getTabCount() {
return adapter.getCount();
}
public void sendScreenViewForCurrentPosition() {
if (!isResumed()) {
return;
}
ScreenEvent.Type screenType;
switch (getCurrentTabIndex()) {
case DialtactsPagerAdapter.TAB_INDEX_SPEED_DIAL:
screenType = ScreenEvent.Type.SPEED_DIAL;
break;
case DialtactsPagerAdapter.TAB_INDEX_HISTORY:
screenType = ScreenEvent.Type.CALL_LOG;
break;
case DialtactsPagerAdapter.TAB_INDEX_ALL_CONTACTS:
screenType = ScreenEvent.Type.ALL_CONTACTS;
break;
case DialtactsPagerAdapter.TAB_INDEX_VOICEMAIL:
screenType = ScreenEvent.Type.VOICEMAIL_LOG;
break;
default:
return;
}
Logger.get(getActivity()).logScreenView(screenType, getActivity());
}
}