/* * Copyright (C) 2018 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.main.impl; import android.annotation.SuppressLint; import android.app.Fragment; import android.app.FragmentManager; import android.app.FragmentTransaction; import android.app.KeyguardManager; import android.content.BroadcastReceiver; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.database.ContentObserver; import android.database.Cursor; import android.net.Uri; import android.os.Bundle; import android.os.Handler; import android.provider.CallLog.Calls; import android.provider.ContactsContract.QuickContact; import android.provider.VoicemailContract; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.design.widget.FloatingActionButton; import android.support.design.widget.Snackbar; import android.support.v4.content.ContextCompat; import android.support.v4.content.LocalBroadcastManager; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.Toolbar; import android.telecom.PhoneAccount; import android.telecom.PhoneAccountHandle; import android.telephony.TelephonyManager; import android.text.TextUtils; import android.view.DragEvent; import android.view.View; import android.widget.ImageView; import com.android.contacts.common.list.OnPhoneNumberPickerActionListener; import com.android.dialer.animation.AnimUtils; import com.android.dialer.app.DialtactsActivity; import com.android.dialer.app.calllog.CallLogAdapter; import com.android.dialer.app.calllog.CallLogFragment; import com.android.dialer.app.calllog.CallLogFragment.CallLogFragmentListener; import com.android.dialer.app.calllog.CallLogNotificationsService; import com.android.dialer.app.calllog.IntentProvider; import com.android.dialer.app.calllog.VisualVoicemailCallLogFragment; import com.android.dialer.app.list.DragDropController; import com.android.dialer.app.list.OldSpeedDialFragment; import com.android.dialer.app.list.OnDragDropListener; import com.android.dialer.app.list.OnListFragmentScrolledListener; import com.android.dialer.app.list.PhoneFavoriteSquareTileView; import com.android.dialer.app.list.RemoveView; import com.android.dialer.callcomposer.CallComposerActivity; import com.android.dialer.calldetails.OldCallDetailsActivity; import com.android.dialer.callintent.CallIntentBuilder; import com.android.dialer.callintent.CallSpecificAppData; import com.android.dialer.calllog.CallLogComponent; import com.android.dialer.calllog.config.CallLogConfigComponent; import com.android.dialer.calllog.ui.NewCallLogFragment; import com.android.dialer.common.FragmentUtils.FragmentUtilListener; import com.android.dialer.common.LogUtil; import com.android.dialer.common.concurrent.DefaultFutureCallback; import com.android.dialer.common.concurrent.DialerExecutorComponent; import com.android.dialer.common.concurrent.ThreadUtil; import com.android.dialer.common.concurrent.UiListener; import com.android.dialer.configprovider.ConfigProviderBindings; import com.android.dialer.constants.ActivityRequestCodes; import com.android.dialer.contactsfragment.ContactsFragment; import com.android.dialer.contactsfragment.ContactsFragment.Header; import com.android.dialer.contactsfragment.ContactsFragment.OnContactSelectedListener; import com.android.dialer.database.CallLogQueryHandler; import com.android.dialer.database.Database; import com.android.dialer.dialpadview.DialpadFragment; import com.android.dialer.dialpadview.DialpadFragment.DialpadListener; import com.android.dialer.dialpadview.DialpadFragment.LastOutgoingCallCallback; import com.android.dialer.dialpadview.DialpadFragment.OnDialpadQueryChangedListener; import com.android.dialer.duo.DuoComponent; import com.android.dialer.i18n.LocaleUtils; import com.android.dialer.interactions.PhoneNumberInteraction; import com.android.dialer.logging.DialerImpression; import com.android.dialer.logging.Logger; import com.android.dialer.logging.ScreenEvent; import com.android.dialer.main.MainActivityPeer; import com.android.dialer.main.impl.bottomnav.BottomNavBar; import com.android.dialer.main.impl.bottomnav.BottomNavBar.OnBottomNavTabSelectedListener; import com.android.dialer.main.impl.bottomnav.BottomNavBar.TabIndex; import com.android.dialer.main.impl.bottomnav.MissedCallCountObserver; import com.android.dialer.main.impl.toolbar.MainToolbar; import com.android.dialer.metrics.Metrics; import com.android.dialer.metrics.MetricsComponent; import com.android.dialer.postcall.PostCall; import com.android.dialer.precall.PreCall; import com.android.dialer.searchfragment.list.NewSearchFragment.SearchFragmentListener; import com.android.dialer.smartdial.util.SmartDialPrefix; import com.android.dialer.storage.StorageComponent; import com.android.dialer.telecom.TelecomUtil; import com.android.dialer.util.DialerUtils; import com.android.dialer.util.PermissionsUtil; import com.android.dialer.util.TransactionSafeActivity; import com.android.dialer.voicemail.listui.error.VoicemailStatusCorruptionHandler; import com.android.dialer.voicemail.listui.error.VoicemailStatusCorruptionHandler.Source; import com.android.dialer.voicemailstatus.VisualVoicemailEnabledChecker; import com.android.dialer.voicemailstatus.VoicemailStatusHelper; import com.android.voicemail.VoicemailComponent; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.MoreExecutors; import java.util.Locale; import java.util.concurrent.TimeUnit; /** * OldMainActivityPeer which implements all of the old fragments we know and love <3 * *
TODO(calderwoodra): Deprecate this class when we launch NewmainActivityPeer.
*/
public class OldMainActivityPeer implements MainActivityPeer, FragmentUtilListener {
private static final String KEY_SAVED_LANGUAGE_CODE = "saved_language_code";
private static final String KEY_CURRENT_TAB = "current_tab";
private static final String KEY_LAST_TAB = "last_tab";
/** Action and extra to let the activity know which tab to open up to. */
private static final String ACTION_SHOW_TAB = "ACTION_SHOW_TAB";
private static final String EXTRA_SHOW_TAB = "EXTRA_SHOW_TAB";
// TODO(calderwoodra): change to AppCompatActivity once new speed dial ships
private final TransactionSafeActivity activity;
private final BroadcastReceiver disableNewCallLogReceiver =
new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (bottomNavTabListener == null) {
return;
}
/*
* Remove the NewCallLogFragment if it is currently attached. If this is not done, user
* interaction with the fragment could cause call log framework state to be unexpectedly
* written. For example scrolling could cause the AnnotatedCallLog to be read (which would
* trigger database creation).
*/
bottomNavTabListener.disableNewCallLogFragment();
}
};
// Contacts
private MainOnContactSelectedListener onContactSelectedListener;
// Dialpad and Search
private MainDialpadFragmentHost dialpadFragmentHostInterface;
private MainSearchController searchController;
private MainOnDialpadQueryChangedListener onDialpadQueryChangedListener;
private MainDialpadListener dialpadListener;
private MainSearchFragmentListener searchFragmentListener;
// Action Mode
private MainCallLogAdapterOnActionModeStateChangedListener
callLogAdapterOnActionModeStateChangedListener;
// Call Log
private MainCallLogHost callLogHostInterface;
private MainCallLogFragmentListener callLogFragmentListener;
private MainOnListFragmentScrolledListener onListFragmentScrolledListener;
// Speed Dial
private MainOnPhoneNumberPickerActionListener onPhoneNumberPickerActionListener;
private MainOldSpeedDialFragmentHost oldSpeedDialFragmentHost;
/** Language the device was in last time {@link #onSaveInstanceState(Bundle)} was called. */
private String savedLanguageCode;
private LastTabController lastTabController;
private BottomNavBar bottomNav;
private MainBottomNavBarBottomNavTabListener bottomNavTabListener;
private View snackbarContainer;
private MissedCallCountObserver missedCallCountObserver;
private UiListener TODO(calderwoodra, uabdullah): Rethink the logic for showing/hiding the FAB when new
* voicemail is ready.
*/
private static final class MainBottomNavBarBottomNavTabListener
implements OnBottomNavTabSelectedListener {
private static final String SPEED_DIAL_TAG = "speed_dial";
private static final String CALL_LOG_TAG = "call_log";
private static final String CONTACTS_TAG = "contacts";
private static final String VOICEMAIL_TAG = "voicemail";
private final AppCompatActivity activity;
private final FragmentManager fragmentManager;
private final android.support.v4.app.FragmentManager supportFragmentManager;
private final FloatingActionButton fab;
@TabIndex private int selectedTab = -1;
private MainBottomNavBarBottomNavTabListener(
AppCompatActivity activity,
FragmentManager fragmentManager,
android.support.v4.app.FragmentManager supportFragmentManager,
FloatingActionButton fab) {
this.activity = activity;
this.fragmentManager = fragmentManager;
this.supportFragmentManager = supportFragmentManager;
this.fab = fab;
}
@Override
public void onSpeedDialSelected() {
LogUtil.enterBlock("MainBottomNavBarBottomNavTabListener.onSpeedDialSelected");
if (selectedTab == TabIndex.SPEED_DIAL) {
return;
}
Logger.get(activity).logScreenView(ScreenEvent.Type.MAIN_SPEED_DIAL, activity);
selectedTab = TabIndex.SPEED_DIAL;
Fragment fragment = fragmentManager.findFragmentByTag(SPEED_DIAL_TAG);
showFragment(fragment == null ? new OldSpeedDialFragment() : fragment, SPEED_DIAL_TAG);
fab.show();
}
@Override
public void onCallLogSelected() {
LogUtil.enterBlock("MainBottomNavBarBottomNavTabListener.onCallLogSelected");
if (selectedTab == TabIndex.CALL_LOG) {
return;
}
Logger.get(activity).logScreenView(ScreenEvent.Type.MAIN_CALL_LOG, activity);
selectedTab = TabIndex.CALL_LOG;
if (CallLogConfigComponent.get(activity).callLogConfig().isNewCallLogFragmentEnabled()) {
android.support.v4.app.Fragment supportFragment =
supportFragmentManager.findFragmentByTag(CALL_LOG_TAG);
showSupportFragment(
supportFragment == null ? new NewCallLogFragment() : supportFragment, CALL_LOG_TAG);
} else {
Fragment fragment = fragmentManager.findFragmentByTag(CALL_LOG_TAG);
showFragment(fragment == null ? new CallLogFragment() : fragment, CALL_LOG_TAG);
}
fab.show();
}
void disableNewCallLogFragment() {
LogUtil.i("MainBottomNavBarBottomNavTabListener.disableNewCallLogFragment", "disabled");
android.support.v4.app.Fragment supportFragment =
supportFragmentManager.findFragmentByTag(CALL_LOG_TAG);
if (supportFragment != null) {
supportFragmentManager.beginTransaction().remove(supportFragment).commit();
// If the NewCallLogFragment was showing, immediately show the old call log fragment
// instead.
if (selectedTab == TabIndex.CALL_LOG) {
LogUtil.i(
"MainBottomNavBarBottomNavTabListener.disableNewCallLogFragment", "showing old");
Fragment fragment = fragmentManager.findFragmentByTag(CALL_LOG_TAG);
showFragment(fragment == null ? new CallLogFragment() : fragment, CALL_LOG_TAG);
}
}
}
void ensureCorrectCallLogShown() {
android.support.v4.app.Fragment supportFragment =
supportFragmentManager.findFragmentByTag(CALL_LOG_TAG);
if (supportFragment != null
&& !CallLogConfigComponent.get(activity).callLogConfig().isNewCallLogFragmentEnabled()) {
LogUtil.i("MainBottomNavBarBottomNavTabListener.ensureCorrectCallLogShown", "disabling");
disableNewCallLogFragment();
}
}
boolean newCallLogFragmentActive() {
return supportFragmentManager.findFragmentByTag(CALL_LOG_TAG) != null
|| (fragmentManager.findFragmentByTag(CALL_LOG_TAG) == null
&& CallLogConfigComponent.get(activity)
.callLogConfig()
.isNewCallLogFragmentEnabled());
}
@Override
public void onContactsSelected() {
LogUtil.enterBlock("MainBottomNavBarBottomNavTabListener.onContactsSelected");
if (selectedTab == TabIndex.CONTACTS) {
return;
}
Logger.get(activity).logScreenView(ScreenEvent.Type.MAIN_CONTACTS, activity);
selectedTab = TabIndex.CONTACTS;
Fragment fragment = fragmentManager.findFragmentByTag(CONTACTS_TAG);
showFragment(
fragment == null ? ContactsFragment.newInstance(Header.ADD_CONTACT) : fragment,
CONTACTS_TAG);
fab.show();
}
@Override
public void onVoicemailSelected() {
LogUtil.enterBlock("MainBottomNavBarBottomNavTabListener.onVoicemailSelected");
if (selectedTab == TabIndex.VOICEMAIL) {
return;
}
Logger.get(activity).logScreenView(ScreenEvent.Type.MAIN_VOICEMAIL, activity);
selectedTab = TabIndex.VOICEMAIL;
VisualVoicemailCallLogFragment fragment =
(VisualVoicemailCallLogFragment) fragmentManager.findFragmentByTag(VOICEMAIL_TAG);
if (fragment == null) {
fragment = new VisualVoicemailCallLogFragment();
}
showFragment(fragment, VOICEMAIL_TAG);
fragment.setUserVisibleHint(true);
fragment.onVisible();
}
private void showFragment(@NonNull Fragment fragment, String tag) {
showFragment(fragment, null, tag);
}
/**
* Shows the passed in fragment and hides all of the others in one transaction.
*
* Exactly one of fragment or supportFragment should be provided.
*
* Executes all fragment shows/hides in one transaction with no conflicting transactions
* (like showing and hiding the same fragment in the same transaction). See a bug.
*
* Special care should be taken to avoid calling this method several times in a short window
* as it can lead to fragments overlapping.
*/
private void showFragment(
@Nullable Fragment fragment,
@Nullable android.support.v4.app.Fragment supportFragment,
String tag) {
LogUtil.enterBlock("MainBottomNavBarBottomNavTabListener.showFragment");
Fragment speedDial = fragmentManager.findFragmentByTag(SPEED_DIAL_TAG);
Fragment oldCallLog = fragmentManager.findFragmentByTag(CALL_LOG_TAG);
Fragment contacts = fragmentManager.findFragmentByTag(CONTACTS_TAG);
Fragment voicemail = fragmentManager.findFragmentByTag(VOICEMAIL_TAG);
FragmentTransaction transaction = fragmentManager.beginTransaction();
boolean fragmentShown = showIfEqualElseHide(transaction, fragment, speedDial);
fragmentShown |= showIfEqualElseHide(transaction, fragment, oldCallLog);
fragmentShown |= showIfEqualElseHide(transaction, fragment, contacts);
fragmentShown |= showIfEqualElseHide(transaction, fragment, voicemail);
if (!fragmentShown && fragment != null) {
LogUtil.i(
"MainBottomNavBarBottomNavTabListener.showFragment", "Not added yet: " + fragment);
transaction.add(R.id.fragment_container, fragment, tag);
}
transaction.commit();
// Handle support fragments.
// TODO(calderwoodra): Handle other new fragments.
android.support.v4.app.Fragment newCallLog =
supportFragmentManager.findFragmentByTag(CALL_LOG_TAG);
android.support.v4.app.FragmentTransaction supportTransaction =
supportFragmentManager.beginTransaction();
boolean supportFragmentShown =
showIfEqualElseHideSupport(supportTransaction, supportFragment, newCallLog);
if (!supportFragmentShown && supportFragment != null) {
LogUtil.i(
"MainBottomNavBarBottomNavTabListener.showFragment",
"Not added yet: " + supportFragment);
supportTransaction.add(R.id.fragment_container, supportFragment, tag);
}
supportTransaction.commit();
}
private void showSupportFragment(
@NonNull android.support.v4.app.Fragment supportFragment, String tag) {
showFragment(null, supportFragment, tag);
}
/**
* @param fragment1 will be shown if equal to {@code fragment2}
* @param fragment2 will be hidden if unequal to {@code fragment1}
* @return {@code true} if {@code fragment1} was shown
*/
private boolean showIfEqualElseHide(
FragmentTransaction transaction, Fragment fragment1, Fragment fragment2) {
boolean shown = false;
if (fragment1 != null && fragment1.equals(fragment2)) {
transaction.show(fragment1);
shown = true;
} else if (fragment2 != null) {
if (fragment2 instanceof VisualVoicemailCallLogFragment) {
fragment2.setUserVisibleHint(false);
((VisualVoicemailCallLogFragment) fragment2).onNotVisible();
}
transaction.hide(fragment2);
}
return shown;
}
/**
* @param supportFragment1 will be shown if equal to {@code fragment2}
* @param supportFragment2 will be hidden if unequal to {@code fragment1}
* @return {@code true} if {@code fragment1} was shown
*/
private boolean showIfEqualElseHideSupport(
android.support.v4.app.FragmentTransaction supportTransaction,
android.support.v4.app.Fragment supportFragment1,
android.support.v4.app.Fragment supportFragment2) {
boolean shown = false;
if (supportFragment1 != null && supportFragment1.equals(supportFragment2)) {
supportTransaction.show(supportFragment1);
shown = true;
} else if (supportFragment2 != null) {
supportTransaction.hide(supportFragment2);
}
return shown;
}
}
private static final class LastTabController {
private final Context context;
private final BottomNavBar bottomNavBar;
private final boolean isEnabled;
private final boolean canShowVoicemailTab;
LastTabController(Context context, BottomNavBar bottomNavBar, boolean canShowVoicemailTab) {
this.context = context;
this.bottomNavBar = bottomNavBar;
isEnabled = ConfigProviderBindings.get(context).getBoolean("last_tab_enabled", false);
this.canShowVoicemailTab = canShowVoicemailTab;
}
/** Sets the last tab if the feature is enabled, otherwise defaults to speed dial. */
@TabIndex
int getLastTab() {
@TabIndex int tabIndex = TabIndex.SPEED_DIAL;
if (isEnabled) {
tabIndex =
StorageComponent.get(context)
.unencryptedSharedPrefs()
.getInt(KEY_LAST_TAB, TabIndex.SPEED_DIAL);
}
// If the voicemail tab cannot be shown, default to showing speed dial
if (tabIndex == TabIndex.VOICEMAIL && !canShowVoicemailTab) {
tabIndex = TabIndex.SPEED_DIAL;
}
return tabIndex;
}
void onActivityStop() {
StorageComponent.get(context)
.unencryptedSharedPrefs()
.edit()
.putInt(KEY_LAST_TAB, bottomNavBar.getSelectedTab())
.apply();
}
}
}
*
*
* This mainly entails:
*
*
*
*
* @see CallLogFragmentListener
* @see CallLogQueryHandler.Listener
* @see OnBottomNavTabSelectedListener
*/
private static final class MainCallLogFragmentListener
implements CallLogFragmentListener,
CallLogQueryHandler.Listener,
OnBottomNavTabSelectedListener {
private final CallLogQueryHandler callLogQueryHandler;
private final Context context;
private final BottomNavBar bottomNavBar;
private final Toolbar toolbar;
private final MainBottomNavBarBottomNavTabListener bottomNavTabListener;
private @TabIndex int currentTab = TabIndex.SPEED_DIAL;
private long timeSelected = -1;
private boolean activityIsAlive;
private final ContentObserver voicemailStatusObserver =
new ContentObserver(new Handler()) {
@Override
public void onChange(boolean selfChange) {
super.onChange(selfChange);
callLogQueryHandler.fetchVoicemailStatus();
}
};
MainCallLogFragmentListener(
Context context,
ContentResolver contentResolver,
BottomNavBar bottomNavBar,
Toolbar toolbar,
MainBottomNavBarBottomNavTabListener bottomNavTabListener) {
callLogQueryHandler = new CallLogQueryHandler(context, contentResolver, this);
this.context = context;
this.bottomNavBar = bottomNavBar;
this.toolbar = toolbar;
this.bottomNavTabListener = bottomNavTabListener;
}
private void registerVoicemailStatusContentObserver(Context context) {
if (PermissionsUtil.hasReadVoicemailPermissions(context)
&& PermissionsUtil.hasAddVoicemailPermissions(context)) {
context
.getContentResolver()
.registerContentObserver(
VoicemailContract.Status.CONTENT_URI, true, voicemailStatusObserver);
} else {
LogUtil.w(
"MainCallLogFragmentListener.registerVoicemailStatusContentObserver",
"no voicemail read/add permissions");
}
}
@Override
public void updateTabUnreadCounts() {
callLogQueryHandler.fetchMissedCallsUnreadCount();
callLogQueryHandler.fetchVoicemailUnreadCount();
}
@Override
public void showMultiSelectRemoveView(boolean show) {
bottomNavBar.setVisibility(show ? View.GONE : View.VISIBLE);
toolbar.setVisibility(show ? View.GONE : View.VISIBLE);
}
@Override
public void onVoicemailStatusFetched(Cursor statusCursor) {
LogUtil.i("OldMainActivityPeer.MainCallLogFragmentListener", "onVoicemailStatusFetched");
VoicemailStatusCorruptionHandler.maybeFixVoicemailStatus(
context, statusCursor, Source.Activity);
// Update hasActiveVoicemailProvider, which controls the number of tabs displayed.
int numberOfActiveVoicemailSources =
VoicemailStatusHelper.getNumberActivityVoicemailSources(statusCursor);
boolean hasActiveVoicemailProvider = numberOfActiveVoicemailSources > 0;
LogUtil.i(
"OldMainActivityPeer.onVoicemailStatusFetched",
String.format(
Locale.US,
"hasActiveVoicemailProvider:%b, number of active voicemail sources:%d",
hasActiveVoicemailProvider,
numberOfActiveVoicemailSources));
if (hasActiveVoicemailProvider) {
Logger.get(context).logImpression(DialerImpression.Type.MAIN_VVM_TAB_VISIBLE);
bottomNavBar.showVoicemail(true);
callLogQueryHandler.fetchVoicemailUnreadCount();
} else {
bottomNavBar.showVoicemail(false);
}
StorageComponent.get(context)
.unencryptedSharedPrefs()
.edit()
.putBoolean(
VisualVoicemailEnabledChecker.PREF_KEY_HAS_ACTIVE_VOICEMAIL_PROVIDER,
hasActiveVoicemailProvider)
.apply();
// TODO(uabdullah): Check if we need to force move to the VM tab (e.g in the event of
// clicking a vm notification and a status wasn't yet fetched).
}
@Override
public void onVoicemailUnreadCountFetched(Cursor cursor) {
if (activityIsAlive) {
bottomNavBar.setNotificationCount(TabIndex.VOICEMAIL, cursor.getCount());
}
cursor.close();
}
@Override
public void onMissedCallsUnreadCountFetched(Cursor cursor) {
if (activityIsAlive) {
bottomNavBar.setNotificationCount(TabIndex.CALL_LOG, cursor.getCount());
}
cursor.close();
}
@Override
public boolean onCallsFetched(Cursor combinedCursor) {
// Return false; did not take ownership of cursor
return false;
}
@Override
public void onSpeedDialSelected() {
setCurrentTab(TabIndex.SPEED_DIAL);
}
@Override
public void onCallLogSelected() {
setCurrentTab(TabIndex.CALL_LOG);
}
@Override
public void onContactsSelected() {
setCurrentTab(TabIndex.CONTACTS);
}
@Override
public void onVoicemailSelected() {
setCurrentTab(TabIndex.VOICEMAIL);
}
private void markMissedCallsAsReadAndRemoveNotification() {
if (bottomNavTabListener.newCallLogFragmentActive()) {
Futures.addCallback(
CallLogComponent.get(context).getClearMissedCalls().clearAll(),
new DefaultFutureCallback<>(),
MoreExecutors.directExecutor());
} else {
callLogQueryHandler.markMissedCallsAsRead();
CallLogNotificationsService.cancelAllMissedCalls(context);
}
}
private void setCurrentTab(@TabIndex int tabIndex) {
if (currentTab == TabIndex.CALL_LOG && tabIndex != TabIndex.CALL_LOG) {
markMissedCallsAsReadAndRemoveNotification();
}
currentTab = tabIndex;
timeSelected = System.currentTimeMillis();
}
public void onActivityResume() {
activityIsAlive = true;
registerVoicemailStatusContentObserver(context);
callLogQueryHandler.fetchVoicemailStatus();
if (!bottomNavTabListener.newCallLogFragmentActive()) {
callLogQueryHandler.fetchMissedCallsUnreadCount();
}
// Reset the tab on resume to restart the timer
setCurrentTab(bottomNavBar.getSelectedTab());
}
/** Should be called when {@link AppCompatActivity#onStop()} is called. */
public void onActivityStop(boolean changingConfigurations, boolean keyguardLocked) {
context.getContentResolver().unregisterContentObserver(voicemailStatusObserver);
activityIsAlive = false;
// The new call log fragment handles this on its own.
if (!bottomNavTabListener.newCallLogFragmentActive()
&& viewedCallLogTabPastTimeThreshold()
&& !changingConfigurations
&& !keyguardLocked) {
markMissedCallsAsReadAndRemoveNotification();
}
}
/**
* Returns true if the user has been (and still is) on the history tab for long than the
* threshold.
*/
private boolean viewedCallLogTabPastTimeThreshold() {
return currentTab == TabIndex.CALL_LOG
&& timeSelected != -1
&& System.currentTimeMillis() - timeSelected > TimeUnit.SECONDS.toMillis(3);
}
}
/** @see OnListFragmentScrolledListener */
private static final class MainOnListFragmentScrolledListener
implements OnListFragmentScrolledListener {
private final View parentLayout;
MainOnListFragmentScrolledListener(View parentLayout) {
this.parentLayout = parentLayout;
}
@Override
public void onListFragmentScrollStateChange(int scrollState) {
DialerUtils.hideInputMethod(parentLayout);
}
@Override
public void onListFragmentScroll(
int firstVisibleItem, int visibleItemCount, int totalItemCount) {
// TODO: No-op for now. This should eventually show/hide the actionBar based on
// interactions with the ListsFragments.
}
}
/** @see OnPhoneNumberPickerActionListener */
private static final class MainOnPhoneNumberPickerActionListener
implements OnPhoneNumberPickerActionListener {
private final TransactionSafeActivity activity;
MainOnPhoneNumberPickerActionListener(TransactionSafeActivity activity) {
this.activity = activity;
}
@Override
public void onPickDataUri(
Uri dataUri, boolean isVideoCall, CallSpecificAppData callSpecificAppData) {
PhoneNumberInteraction.startInteractionForPhoneCall(
activity, dataUri, isVideoCall, callSpecificAppData);
}
@Override
public void onPickPhoneNumber(
String phoneNumber, boolean isVideoCall, CallSpecificAppData callSpecificAppData) {
if (phoneNumber == null) {
// Invalid phone number, but let the call go through so that InCallUI can show
// an error message.
phoneNumber = "";
}
PreCall.start(
activity,
new CallIntentBuilder(phoneNumber, callSpecificAppData)
.setIsVideoCall(isVideoCall)
.setAllowAssistedDial(callSpecificAppData.getAllowAssistedDialing()));
}
@Override
public void onHomeInActionBarSelected() {
// TODO(calderwoodra): investigate if we need to exit search here
// PhoneNumberPickerFragment#onOptionsItemSelected
}
}
/**
* Handles the callbacks for {@link OldSpeedDialFragment} and drag/drop logic for drag to remove.
*
* @see OldSpeedDialFragment.HostInterface
* @see OnDragDropListener
*/
private static final class MainOldSpeedDialFragmentHost
implements OldSpeedDialFragment.HostInterface, OnDragDropListener {
private final Context context;
private final View rootLayout;
private final BottomNavBar bottomNavBar;
private final ImageView dragShadowOverlay;
private final RemoveView removeView;
private final View removeViewContent;
private final View searchViewContainer;
private final MainToolbar toolbar;
MainOldSpeedDialFragmentHost(
Context context,
View rootLayout,
BottomNavBar bottomNavBar,
ImageView dragShadowOverlay,
RemoveView removeView,
View searchViewContainer,
MainToolbar toolbar) {
this.context = context;
this.rootLayout = rootLayout;
this.bottomNavBar = bottomNavBar;
this.dragShadowOverlay = dragShadowOverlay;
this.removeView = removeView;
this.searchViewContainer = searchViewContainer;
this.toolbar = toolbar;
removeViewContent = removeView.findViewById(R.id.remove_view_content);
}
@Override
public void setDragDropController(DragDropController dragDropController) {
removeView.setDragDropController(dragDropController);
rootLayout.setOnDragListener(
(v, event) -> {
if (event.getAction() == DragEvent.ACTION_DRAG_LOCATION) {
dragDropController.handleDragHovered(v, (int) event.getX(), (int) event.getY());
}
return true;
});
}
@Override
public void showAllContactsTab() {
bottomNavBar.selectTab(TabIndex.CONTACTS);
Logger.get(context).logImpression(DialerImpression.Type.MAIN_OPEN_WITH_TAB_CONTACTS);
}
@Override
public ImageView getDragShadowOverlay() {
return dragShadowOverlay;
}
@Override
public void setHasFrequents(boolean hasFrequents) {
toolbar.showClearFrequents(hasFrequents);
}
@Override
public void onDragStarted(int x, int y, PhoneFavoriteSquareTileView view) {
showRemoveView(true);
}
@Override
public void onDragHovered(int x, int y, PhoneFavoriteSquareTileView view) {}
@Override
public void onDragFinished(int x, int y) {
showRemoveView(false);
}
@Override
public void onDroppedOnRemove() {}
private void showRemoveView(boolean show) {
if (show) {
AnimUtils.crossFadeViews(removeViewContent, searchViewContainer, 300);
} else {
AnimUtils.crossFadeViews(searchViewContainer, removeViewContent, 300);
}
}
}
/**
* Implementation of {@link OnBottomNavTabSelectedListener} that handles logic for showing each of
* the main tabs and FAB.
*
*