From 3a398a5adf0feae1f6dafe629a6c7cc4a0a968b4 Mon Sep 17 00:00:00 2001 From: calderwoodra Date: Fri, 2 Feb 2018 12:22:13 -0800 Subject: Split MainActivity into two peers, old and new to keep logic isolated. This change updates MainActivity to defer lifecycle callbacks and other relevant methods to which ever peer is active, old or new. The old peer implements the logic for the old fragments. The new peer implements the logic for the new fragments. MainActivity implements the logic that is shared or common between the two like last tab, post call, search, dialpad, ect. Bug: 72525324 Test: pending PiperOrigin-RevId: 184317828 Change-Id: Ie73733f2a3837c9d63e54cf5b142984633340731 --- java/com/android/dialer/common/FragmentUtils.java | 6 + java/com/android/dialer/main/MainActivityPeer.java | 44 ++ .../com/android/dialer/main/impl/MainActivity.java | 807 +------------------- .../dialer/main/impl/NewMainActivityPeer.java | 137 ++++ .../dialer/main/impl/OldMainActivityPeer.java | 815 +++++++++++++++++++++ 5 files changed, 1020 insertions(+), 789 deletions(-) create mode 100644 java/com/android/dialer/main/MainActivityPeer.java create mode 100644 java/com/android/dialer/main/impl/NewMainActivityPeer.java create mode 100644 java/com/android/dialer/main/impl/OldMainActivityPeer.java diff --git a/java/com/android/dialer/common/FragmentUtils.java b/java/com/android/dialer/common/FragmentUtils.java index 947a9b20a..c07d9a799 100644 --- a/java/com/android/dialer/common/FragmentUtils.java +++ b/java/com/android/dialer/common/FragmentUtils.java @@ -21,6 +21,7 @@ import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.annotation.VisibleForTesting; import android.support.v4.app.Fragment; +import com.android.dialer.main.MainActivityPeer; /** Utility methods for working with Fragments */ public class FragmentUtils { @@ -89,6 +90,11 @@ public class FragmentUtils { @SuppressWarnings("unchecked") // Casts are checked using runtime methods T parent = ((FragmentUtilListener) fragment.getActivity()).getImpl(callbackInterface); return parent; + } else if (fragment.getActivity() instanceof MainActivityPeer.PeerSupplier) { + MainActivityPeer peer = ((MainActivityPeer.PeerSupplier) fragment.getActivity()).getPeer(); + if (peer instanceof FragmentUtilListener) { + return ((FragmentUtilListener) peer).getImpl(callbackInterface); + } } return null; } diff --git a/java/com/android/dialer/main/MainActivityPeer.java b/java/com/android/dialer/main/MainActivityPeer.java new file mode 100644 index 000000000..6457b607b --- /dev/null +++ b/java/com/android/dialer/main/MainActivityPeer.java @@ -0,0 +1,44 @@ +/* + * 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; + +import android.content.Intent; +import android.os.Bundle; + +/** Interface for peers of MainActivity. */ +public interface MainActivityPeer { + + void onActivityCreate(Bundle saveInstanceState); + + void onActivityResume(); + + void onActivityStop(); + + void onNewIntent(Intent intent); + + void onActivityResult(int requestCode, int resultCode, Intent data); + + void onSaveInstanceState(Bundle bundle); + + boolean onBackPressed(); + + /** Supplies the MainActivityPeer */ + interface PeerSupplier { + + MainActivityPeer getPeer(); + } +} diff --git a/java/com/android/dialer/main/impl/MainActivity.java b/java/com/android/dialer/main/impl/MainActivity.java index ca9d47843..1a79fbab3 100644 --- a/java/com/android/dialer/main/impl/MainActivity.java +++ b/java/com/android/dialer/main/impl/MainActivity.java @@ -16,113 +16,27 @@ package com.android.dialer.main.impl; -import android.app.Activity; -import android.app.Fragment; -import android.app.FragmentManager; -import android.app.KeyguardManager; -import android.content.ContentResolver; import android.content.Context; import android.content.Intent; -import android.database.Cursor; -import android.net.Uri; import android.os.Bundle; -import android.provider.CallLog.Calls; -import android.provider.ContactsContract.QuickContact; -import android.support.annotation.Nullable; -import android.support.design.widget.FloatingActionButton; -import android.support.v4.app.FragmentTransaction; -import android.view.View; -import android.widget.ImageView; -import com.android.contacts.common.list.OnPhoneNumberPickerActionListener; -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.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.callintent.CallIntentBuilder; -import com.android.dialer.callintent.CallSpecificAppData; -import com.android.dialer.calllog.ui.NewCallLogFragment; import com.android.dialer.common.Assert; -import com.android.dialer.common.FragmentUtils.FragmentUtilListener; import com.android.dialer.common.LogUtil; -import com.android.dialer.common.concurrent.DialerExecutorComponent; -import com.android.dialer.common.concurrent.UiListener; -import com.android.dialer.compat.CompatUtils; -import com.android.dialer.configprovider.ConfigProviderBindings; import com.android.dialer.configprovider.ConfigProviderComponent; -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.interactions.PhoneNumberInteraction; import com.android.dialer.interactions.PhoneNumberInteraction.DisambigDialogDismissedListener; import com.android.dialer.interactions.PhoneNumberInteraction.InteractionErrorCode; import com.android.dialer.interactions.PhoneNumberInteraction.InteractionErrorListener; -import com.android.dialer.main.impl.BottomNavBar.OnBottomNavTabSelectedListener; -import com.android.dialer.main.impl.BottomNavBar.TabIndex; -import com.android.dialer.main.impl.toolbar.MainToolbar; -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.speeddial.SpeedDialFragment; -import com.android.dialer.storage.StorageComponent; -import com.android.dialer.telecom.TelecomUtil; -import com.android.dialer.util.DialerUtils; +import com.android.dialer.main.MainActivityPeer; import com.android.dialer.util.TransactionSafeActivity; -import com.android.dialer.voicemail.listui.NewVoicemailFragment; -import com.google.common.util.concurrent.ListenableFuture; -import java.util.concurrent.TimeUnit; /** This is the main activity for dialer. It hosts favorites, call log, search, dialpad, etc... */ // TODO(calderwoodra): Do not extend TransactionSafeActivity after new SpeedDial is launched public final class MainActivity extends TransactionSafeActivity - implements FragmentUtilListener, + implements MainActivityPeer.PeerSupplier, // TODO(calderwoodra): remove these 2 interfaces when we migrate to new speed dial fragment InteractionErrorListener, DisambigDialogDismissedListener { - 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"; - - private final MainOnContactSelectedListener onContactSelectedListener = - new MainOnContactSelectedListener(this); - private final MainDialpadFragmentHost dialpadFragmentHostInterface = - new MainDialpadFragmentHost(); - - private MainSearchController searchController; - private MainOnDialpadQueryChangedListener onDialpadQueryChangedListener; - private MainDialpadListener dialpadListener; - private MainSearchFragmentListener searchFragmentListener; - private MainCallLogAdapterOnActionModeStateChangedListener - callLogAdapterOnActionModeStateChangedListener; - private MainCallLogHost callLogHostInterface; - private MainCallLogFragmentListener callLogFragmentListener; - private MainOnListFragmentScrolledListener onListFragmentScrolledListener; - private MainOnPhoneNumberPickerActionListener onPhoneNumberPickerActionListener; - private MainOldSpeedDialFragmentHostInterface oldSpeedDialFragmentHostInterface; - private MainOnDragDropListener onDragDropListener; - - /** Language the device was in last time {@link #onSaveInstanceState(Bundle)} was called. */ - private String savedLanguageCode; - - private LastTabController lastTabController; - - private BottomNavBar bottomNav; - private View snackbarContainer; - private UiListener getLastOutgoingCallListener; + private MainActivityPeer activePeer; /** * @param context Context of the application package implementing MainActivity class. @@ -138,164 +52,54 @@ public final class MainActivity extends TransactionSafeActivity protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); LogUtil.enterBlock("MainActivity.onCreate"); - setContentView(R.layout.main_activity); - initUiListeners(); - initLayout(savedInstanceState); - SmartDialPrefix.initializeNanpSettings(this); - } - - private void initUiListeners() { - getLastOutgoingCallListener = - DialerExecutorComponent.get(this) - .createUiListener(getFragmentManager(), "Query last phone number"); - } - - private void initLayout(Bundle savedInstanceState) { - snackbarContainer = findViewById(R.id.coordinator_layout); - - FloatingActionButton fab = findViewById(R.id.fab); - fab.setOnClickListener(v -> searchController.showDialpad(true)); - - MainToolbar toolbar = findViewById(R.id.toolbar); - setSupportActionBar(findViewById(R.id.toolbar)); - - bottomNav = findViewById(R.id.bottom_nav_bar); - MainBottomNavBarBottomNavTabListener bottomNavTabListener = - new MainBottomNavBarBottomNavTabListener( - this, getFragmentManager(), getSupportFragmentManager()); - bottomNav.addOnTabSelectedListener(bottomNavTabListener); - - callLogFragmentListener = - new MainCallLogFragmentListener(this, getContentResolver(), bottomNav); - bottomNav.addOnTabSelectedListener(callLogFragmentListener); - - searchController = new MainSearchController(this, bottomNav, fab, toolbar); - toolbar.setSearchBarListener(searchController); - - onDialpadQueryChangedListener = new MainOnDialpadQueryChangedListener(searchController); - dialpadListener = new MainDialpadListener(this, searchController, getLastOutgoingCallListener); - searchFragmentListener = new MainSearchFragmentListener(searchController); - callLogAdapterOnActionModeStateChangedListener = - new MainCallLogAdapterOnActionModeStateChangedListener(); - callLogHostInterface = new MainCallLogHost(searchController, fab); - - onListFragmentScrolledListener = new MainOnListFragmentScrolledListener(snackbarContainer); - onPhoneNumberPickerActionListener = new MainOnPhoneNumberPickerActionListener(this); - oldSpeedDialFragmentHostInterface = - new MainOldSpeedDialFragmentHostInterface( - bottomNavTabListener, findViewById(R.id.contact_tile_drag_shadow_overlay)); - onDragDropListener = new MainOnDragDropListener(); - - lastTabController = new LastTabController(this, bottomNav); - - // Restore our view state if needed, else initialize as if the app opened for the first time - if (savedInstanceState != null) { - savedLanguageCode = savedInstanceState.getString(KEY_SAVED_LANGUAGE_CODE); - searchController.onRestoreInstanceState(savedInstanceState); - bottomNav.selectTab(savedInstanceState.getInt(KEY_CURRENT_TAB)); + if (ConfigProviderComponent.get(this) + .getConfigProvider() + .getBoolean("nui_peer_enabled", false)) { + activePeer = new NewMainActivityPeer(this); } else { - lastTabController.selectLastTab(); + activePeer = new OldMainActivityPeer(this); } + activePeer.onActivityCreate(savedInstanceState); } @Override protected void onNewIntent(Intent intent) { super.onNewIntent(intent); - lastTabController.selectLastTab(); + activePeer.onNewIntent(intent); } @Override protected void onResume() { super.onResume(); - callLogFragmentListener.onActivityResume(); - // Start the thread that updates the smart dial database if the activity is recreated with a - // language change. - boolean forceUpdate = !CompatUtils.getLocale(this).getISO3Language().equals(savedLanguageCode); - Database.get(this).getDatabaseHelper(this).startSmartDialUpdateThread(forceUpdate); - showPostCallPrompt(); + activePeer.onActivityResume(); } @Override protected void onStop() { super.onStop(); - lastTabController.onActivityStop(); - callLogFragmentListener.onActivityStop( - isChangingConfigurations(), getSystemService(KeyguardManager.class).isKeyguardLocked()); - } - - private void showPostCallPrompt() { - if (TelecomUtil.isInManagedCall(this)) { - // No prompt to show if the user is in a call - return; - } - - if (searchController.isInSearch()) { - // Don't show the prompt if we're in the search ui - return; - } - - PostCall.promptUserForMessageIfNecessary(this, snackbarContainer); + activePeer.onActivityStop(); } @Override protected void onSaveInstanceState(Bundle bundle) { super.onSaveInstanceState(bundle); - bundle.putString(KEY_SAVED_LANGUAGE_CODE, CompatUtils.getLocale(this).getISO3Language()); - bundle.putInt(KEY_CURRENT_TAB, bottomNav.getSelectedTab()); - searchController.onSaveInstanceState(bundle); + activePeer.onSaveInstanceState(bundle); } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); - if (requestCode == ActivityRequestCodes.DIALTACTS_VOICE_SEARCH) { - searchController.onVoiceResults(resultCode, data); - } else { - LogUtil.e("MainActivity.onActivityResult", "Unknown request code: " + requestCode); - } + activePeer.onActivityResult(requestCode, resultCode, data); } @Override public void onBackPressed() { - if (searchController.onBackPressed()) { + if (activePeer.onBackPressed()) { return; } super.onBackPressed(); } - @Nullable - @Override - @SuppressWarnings("unchecked") // Casts are checked using runtime methods - public T getImpl(Class callbackInterface) { - if (callbackInterface.isInstance(onContactSelectedListener)) { - return (T) onContactSelectedListener; - } else if (callbackInterface.isInstance(onDialpadQueryChangedListener)) { - return (T) onDialpadQueryChangedListener; - } else if (callbackInterface.isInstance(dialpadListener)) { - return (T) dialpadListener; - } else if (callbackInterface.isInstance(dialpadFragmentHostInterface)) { - return (T) dialpadFragmentHostInterface; - } else if (callbackInterface.isInstance(searchFragmentListener)) { - return (T) searchFragmentListener; - } else if (callbackInterface.isInstance(callLogAdapterOnActionModeStateChangedListener)) { - return (T) callLogAdapterOnActionModeStateChangedListener; - } else if (callbackInterface.isInstance(callLogHostInterface)) { - return (T) callLogHostInterface; - } else if (callbackInterface.isInstance(callLogFragmentListener)) { - return (T) callLogFragmentListener; - } else if (callbackInterface.isInstance(onListFragmentScrolledListener)) { - return (T) onListFragmentScrolledListener; - } else if (callbackInterface.isInstance(onPhoneNumberPickerActionListener)) { - return (T) onPhoneNumberPickerActionListener; - } else if (callbackInterface.isInstance(oldSpeedDialFragmentHostInterface)) { - return (T) oldSpeedDialFragmentHostInterface; - } else if (callbackInterface.isInstance(onDragDropListener)) { - return (T) onDragDropListener; - } else { - return null; - } - } - @Override public void interactionError(@InteractionErrorCode int interactionErrorCode) { switch (interactionErrorCode) { @@ -318,583 +122,8 @@ public final class MainActivity extends TransactionSafeActivity // Don't do anything; the app will remain open with favorites tiles displayed. } - /** @see OnContactSelectedListener */ - private static final class MainOnContactSelectedListener implements OnContactSelectedListener { - - private final Context context; - - MainOnContactSelectedListener(Context context) { - this.context = context; - } - - @Override - public void onContactSelected(ImageView photo, Uri contactUri, long contactId) { - // TODO(calderwoodra): Add impression logging - QuickContact.showQuickContact( - context, photo, contactUri, QuickContact.MODE_LARGE, null /* excludeMimes */); - } - } - - /** @see OnDialpadQueryChangedListener */ - private static final class MainOnDialpadQueryChangedListener - implements OnDialpadQueryChangedListener { - - private final MainSearchController searchController; - - MainOnDialpadQueryChangedListener(MainSearchController searchController) { - this.searchController = searchController; - } - - @Override - public void onDialpadQueryChanged(String query) { - searchController.onDialpadQueryChanged(query); - } - } - - /** @see DialpadListener */ - private static final class MainDialpadListener implements DialpadListener { - - private final MainSearchController searchController; - private final Context context; - private final UiListener listener; - - MainDialpadListener( - Context context, MainSearchController searchController, UiListener uiListener) { - this.context = context; - this.searchController = searchController; - this.listener = uiListener; - } - - @Override - public void getLastOutgoingCall(LastOutgoingCallCallback callback) { - ListenableFuture listenableFuture = - DialerExecutorComponent.get(context) - .backgroundExecutor() - .submit(() -> Calls.getLastOutgoingCall(context)); - listener.listen(context, listenableFuture, callback::lastOutgoingCall, throwable -> {}); - } - - @Override - public void onDialpadShown() { - searchController.onDialpadShown(); - } - - @Override - public void onCallPlacedFromDialpad() { - // TODO(calderwoodra): logging - } - } - - /** @see SearchFragmentListener */ - private static final class MainSearchFragmentListener implements SearchFragmentListener { - - private final MainSearchController searchController; - - MainSearchFragmentListener(MainSearchController searchController) { - this.searchController = searchController; - } - - @Override - public void onSearchListTouch() { - searchController.onSearchListTouch(); - } - - @Override - public void onCallPlacedFromSearch() { - // TODO(calderwoodra): logging - } - } - - /** @see DialpadFragment.HostInterface */ - private static final class MainDialpadFragmentHost implements DialpadFragment.HostInterface { - - @Override - public boolean onDialpadSpacerTouchWithEmptyQuery() { - // No-op, just let the clicks fall through to the search list - return false; - } - } - - /** @see CallLogAdapter.OnActionModeStateChangedListener */ - // TODO(a bug): handle multiselect mode - private static final class MainCallLogAdapterOnActionModeStateChangedListener - implements CallLogAdapter.OnActionModeStateChangedListener { - - @Override - public void onActionModeStateChanged(boolean isEnabled) {} - - @Override - public boolean isActionModeStateEnabled() { - return false; - } - } - - /** @see CallLogFragment.HostInterface */ - private static final class MainCallLogHost implements CallLogFragment.HostInterface { - - private final MainSearchController searchController; - private final FloatingActionButton fab; - - MainCallLogHost(MainSearchController searchController, FloatingActionButton fab) { - this.searchController = searchController; - this.fab = fab; - } - - @Override - public void showDialpad() { - searchController.showDialpad(true); - } - - @Override - public void enableFloatingButton(boolean enabled) { - if (enabled) { - fab.show(); - } else { - fab.hide(); - } - } - } - - /** - * Handles the logic for callbacks from: - * - *
    - *
  • {@link CallLogFragment} - *
  • {@link CallLogQueryHandler} - *
  • {@link BottomNavBar} - *
- * - * This mainly entails: - * - *
    - *
  • Handling querying for missed calls/unread voicemails. - *
  • Displaying a badge to the user in the bottom nav when there are missed calls/unread - * voicemails present. - *
  • Marking missed calls as read when appropriate. See {@link - * #markMissedCallsAsReadAndRemoveNotification()} - *
  • TODO(calderwoodra): multiselect - *
  • TODO(calderwoodra): voicemail status - *
- * - * @see CallLogFragmentListener - * @see CallLogQueryHandler.Listener - * @see OnBottomNavTabSelectedListener - */ - private static final class MainCallLogFragmentListener - implements CallLogFragmentListener, - CallLogQueryHandler.Listener, - OnBottomNavTabSelectedListener { - - private final CallLogQueryHandler callLogQueryHandler; - private final BottomNavBar bottomNavBar; - private final Context context; - - private @TabIndex int currentTab = TabIndex.SPEED_DIAL; - private long timeSelected = -1; - private boolean activityIsAlive; - - MainCallLogFragmentListener( - Context context, ContentResolver contentResolver, BottomNavBar bottomNavBar) { - callLogQueryHandler = new CallLogQueryHandler(context, contentResolver, this); - this.bottomNavBar = bottomNavBar; - this.context = context; - } - - @Override - public void updateTabUnreadCounts() { - callLogQueryHandler.fetchMissedCallsUnreadCount(); - callLogQueryHandler.fetchVoicemailUnreadCount(); - } - - @Override - public void showMultiSelectRemoveView(boolean show) { - // TODO(a bug): handle multiselect mode - } - - @Override - public void onVoicemailStatusFetched(Cursor statusCursor) { - // TODO(calderwoodra): handle this when voicemail is implemented - } - - @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() { - 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; - callLogQueryHandler.fetchVoicemailStatus(); - callLogQueryHandler.fetchMissedCallsUnreadCount(); - // Reset the tab on resume to restart the timer - setCurrentTab(bottomNavBar.getSelectedTab()); - } - - /** Should be called when {@link Activity#onStop()} is called. */ - public void onActivityStop(boolean changingConfigurations, boolean keyguardLocked) { - activityIsAlive = false; - if (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 - } - } - - /** @see OldSpeedDialFragment.HostInterface */ - private static final class MainOldSpeedDialFragmentHostInterface - implements OldSpeedDialFragment.HostInterface { - - private final MainBottomNavBarBottomNavTabListener listener; - private final ImageView dragShadowOverlay; - - // TODO(calderwoodra): Use this for drag and drop - @SuppressWarnings("unused") - private DragDropController dragDropController; - - MainOldSpeedDialFragmentHostInterface( - MainBottomNavBarBottomNavTabListener listener, ImageView dragShadowOverlay) { - this.listener = listener; - this.dragShadowOverlay = dragShadowOverlay; - } - - @Override - public void setDragDropController(DragDropController dragDropController) { - this.dragDropController = dragDropController; - } - - @Override - public void showAllContactsTab() { - listener.onContactsSelected(); - } - - @Override - public ImageView getDragShadowOverlay() { - return dragShadowOverlay; - } - } - - /** @see com.android.dialer.app.list.OnDragDropListener */ - // TODO(calderwoodra): implement drag and drop - private static final class MainOnDragDropListener implements OnDragDropListener { - - @Override - public void onDragStarted(int x, int y, PhoneFavoriteSquareTileView view) {} - - @Override - public void onDragHovered(int x, int y, PhoneFavoriteSquareTileView view) {} - - @Override - public void onDragFinished(int x, int y) {} - - @Override - public void onDroppedOnRemove() {} - } - - /** - * Implementation of {@link OnBottomNavTabSelectedListener} that handles logic for showing each of - * the main tabs. - */ - 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 Context context; - private final FragmentManager fragmentManager; - private final android.support.v4.app.FragmentManager supportFragmentManager; - - private MainBottomNavBarBottomNavTabListener( - Context context, - FragmentManager fragmentManager, - android.support.v4.app.FragmentManager supportFragmentManager) { - this.context = context; - this.fragmentManager = fragmentManager; - this.supportFragmentManager = supportFragmentManager; - } - - @Override - public void onSpeedDialSelected() { - hideAllFragments(); - Fragment fragment = fragmentManager.findFragmentByTag(SPEED_DIAL_TAG); - if (fragment == null) { - if (ConfigProviderComponent.get(context) - .getConfigProvider() - .getBoolean("enable_new_favorites_tab", false)) { - fragment = SpeedDialFragment.newInstance(); - } else { - fragment = new OldSpeedDialFragment(); - } - fragmentManager - .beginTransaction() - .add(R.id.fragment_container, fragment, SPEED_DIAL_TAG) - .commit(); - } else { - fragmentManager.beginTransaction().show(fragment).commit(); - } - } - - @Override - public void onCallLogSelected() { - hideAllFragments(); - if (ConfigProviderComponent.get(context) - .getConfigProvider() - .getBoolean("enable_new_call_log", false)) { - NewCallLogFragment fragment = - (NewCallLogFragment) supportFragmentManager.findFragmentByTag(CALL_LOG_TAG); - if (fragment == null) { - supportFragmentManager - .beginTransaction() - .add(R.id.fragment_container, new NewCallLogFragment(), CALL_LOG_TAG) - .commit(); - } else { - supportFragmentManager.beginTransaction().show(fragment).commit(); - } - } else { - CallLogFragment fragment = - (CallLogFragment) fragmentManager.findFragmentByTag(CALL_LOG_TAG); - if (fragment == null) { - fragmentManager - .beginTransaction() - .add(R.id.fragment_container, new CallLogFragment(), CALL_LOG_TAG) - .commit(); - } else { - fragmentManager.beginTransaction().show(fragment).commit(); - } - } - } - - @Override - public void onContactsSelected() { - hideAllFragments(); - ContactsFragment fragment = - (ContactsFragment) fragmentManager.findFragmentByTag(CONTACTS_TAG); - if (fragment == null) { - fragmentManager - .beginTransaction() - .add( - R.id.fragment_container, - ContactsFragment.newInstance(Header.ADD_CONTACT), - CONTACTS_TAG) - .commit(); - } else { - fragmentManager.beginTransaction().show(fragment).commit(); - } - } - - @Override - public void onVoicemailSelected() { - hideAllFragments(); - if (ConfigProviderComponent.get(context) - .getConfigProvider() - .getBoolean("enable_new_voicemail_fragment", false)) { - NewVoicemailFragment fragment = - (NewVoicemailFragment) supportFragmentManager.findFragmentByTag(VOICEMAIL_TAG); - if (fragment == null) { - supportFragmentManager - .beginTransaction() - .add(R.id.fragment_container, new NewVoicemailFragment(), VOICEMAIL_TAG) - .commit(); - } else { - supportFragmentManager.beginTransaction().show(fragment).commit(); - } - } else { - VisualVoicemailCallLogFragment fragment = - (VisualVoicemailCallLogFragment) fragmentManager.findFragmentByTag(VOICEMAIL_TAG); - if (fragment == null) { - fragmentManager - .beginTransaction() - .add(R.id.fragment_container, new VisualVoicemailCallLogFragment(), VOICEMAIL_TAG) - .commit(); - } else { - fragmentManager.beginTransaction().show(fragment).commit(); - } - } - } - - private void hideAllFragments() { - FragmentTransaction supportTransaction = supportFragmentManager.beginTransaction(); - if (supportFragmentManager.findFragmentByTag(CALL_LOG_TAG) != null) { - // NewCallLogFragment - supportTransaction.hide(supportFragmentManager.findFragmentByTag(CALL_LOG_TAG)); - } - if (supportFragmentManager.findFragmentByTag(VOICEMAIL_TAG) != null) { - // NewVoicemailFragment - supportTransaction.hide(supportFragmentManager.findFragmentByTag(VOICEMAIL_TAG)); - } - supportTransaction.commit(); - - android.app.FragmentTransaction transaction = fragmentManager.beginTransaction(); - if (fragmentManager.findFragmentByTag(SPEED_DIAL_TAG) != null) { - transaction.hide(fragmentManager.findFragmentByTag(SPEED_DIAL_TAG)); - } - if (fragmentManager.findFragmentByTag(CALL_LOG_TAG) != null) { - // Old CallLogFragment - transaction.hide(fragmentManager.findFragmentByTag(CALL_LOG_TAG)); - } - if (fragmentManager.findFragmentByTag(CONTACTS_TAG) != null) { - transaction.hide(fragmentManager.findFragmentByTag(CONTACTS_TAG)); - } - if (fragmentManager.findFragmentByTag(VOICEMAIL_TAG) != null) { - // Old VisualVoicemailFragment - transaction.hide(fragmentManager.findFragmentByTag(VOICEMAIL_TAG)); - } - transaction.commit(); - } - } - - private static final class LastTabController { - - private final Context context; - private final BottomNavBar bottomNavBar; - private final boolean isEnabled; - - public LastTabController(Context context, BottomNavBar bottomNavBar) { - this.context = context; - this.bottomNavBar = bottomNavBar; - isEnabled = ConfigProviderBindings.get(context).getBoolean("last_tab_enabled", false); - } - - /** Sets the last tab if the feature is enabled, otherwise defaults to speed dial. */ - public void selectLastTab() { - @TabIndex int tabIndex = TabIndex.SPEED_DIAL; - if (isEnabled) { - tabIndex = - StorageComponent.get(context) - .unencryptedSharedPrefs() - .getInt(KEY_LAST_TAB, TabIndex.SPEED_DIAL); - } - bottomNavBar.selectTab(tabIndex); - } - - public void onActivityStop() { - StorageComponent.get(context) - .unencryptedSharedPrefs() - .edit() - .putInt(KEY_LAST_TAB, bottomNavBar.getSelectedTab()) - .apply(); - } + @Override + public MainActivityPeer getPeer() { + return activePeer; } } diff --git a/java/com/android/dialer/main/impl/NewMainActivityPeer.java b/java/com/android/dialer/main/impl/NewMainActivityPeer.java new file mode 100644 index 000000000..cc4c3e7fd --- /dev/null +++ b/java/com/android/dialer/main/impl/NewMainActivityPeer.java @@ -0,0 +1,137 @@ +/* + * 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.content.Intent; +import android.os.Bundle; +import android.support.v4.app.FragmentManager; +import android.support.v4.app.FragmentTransaction; +import com.android.dialer.calllog.ui.NewCallLogFragment; +import com.android.dialer.main.MainActivityPeer; +import com.android.dialer.main.impl.BottomNavBar.OnBottomNavTabSelectedListener; +import com.android.dialer.main.impl.BottomNavBar.TabIndex; +import com.android.dialer.voicemail.listui.NewVoicemailFragment; + +/** MainActivityPeer that implements the new fragments. */ +public class NewMainActivityPeer implements MainActivityPeer { + + private final MainActivity mainActivity; + + public NewMainActivityPeer(MainActivity mainActivity) { + this.mainActivity = mainActivity; + } + + @Override + public void onActivityCreate(Bundle saveInstanceState) { + mainActivity.setContentView(R.layout.main_activity); + MainBottomNavBarBottomNavTabListener bottomNavBarBottomNavTabListener = + new MainBottomNavBarBottomNavTabListener(mainActivity.getSupportFragmentManager()); + BottomNavBar bottomNav = mainActivity.findViewById(R.id.bottom_nav_bar); + bottomNav.addOnTabSelectedListener(bottomNavBarBottomNavTabListener); + bottomNav.selectTab(TabIndex.SPEED_DIAL); + } + + @Override + public void onActivityResume() {} + + @Override + public void onActivityStop() {} + + @Override + public void onNewIntent(Intent intent) {} + + @Override + public void onActivityResult(int requestCode, int resultCode, Intent data) {} + + @Override + public void onSaveInstanceState(Bundle bundle) {} + + @Override + public boolean onBackPressed() { + return false; + } + + /** + * Implementation of {@link OnBottomNavTabSelectedListener} that handles logic for showing each of + * the main tabs. + */ + private static final class MainBottomNavBarBottomNavTabListener + implements OnBottomNavTabSelectedListener { + + private static final String CALL_LOG_TAG = "call_log"; + private static final String VOICEMAIL_TAG = "voicemail"; + + private final FragmentManager supportFragmentManager; + + private MainBottomNavBarBottomNavTabListener(FragmentManager supportFragmentManager) { + this.supportFragmentManager = supportFragmentManager; + } + + @Override + public void onSpeedDialSelected() { + hideAllFragments(); + // TODO(calderwoodra): Implement SpeedDialFragment when FragmentUtils#getParent works + } + + @Override + public void onCallLogSelected() { + hideAllFragments(); + NewCallLogFragment fragment = + (NewCallLogFragment) supportFragmentManager.findFragmentByTag(CALL_LOG_TAG); + if (fragment == null) { + supportFragmentManager + .beginTransaction() + .add(R.id.fragment_container, new NewCallLogFragment(), CALL_LOG_TAG) + .commit(); + } else { + supportFragmentManager.beginTransaction().show(fragment).commit(); + } + } + + @Override + public void onContactsSelected() { + hideAllFragments(); + // TODO(calderwoodra): Implement ContactsFragment when FragmentUtils#getParent works + } + + @Override + public void onVoicemailSelected() { + hideAllFragments(); + NewVoicemailFragment fragment = + (NewVoicemailFragment) supportFragmentManager.findFragmentByTag(VOICEMAIL_TAG); + if (fragment == null) { + supportFragmentManager + .beginTransaction() + .add(R.id.fragment_container, new NewVoicemailFragment(), VOICEMAIL_TAG) + .commit(); + } else { + supportFragmentManager.beginTransaction().show(fragment).commit(); + } + } + + private void hideAllFragments() { + FragmentTransaction supportTransaction = supportFragmentManager.beginTransaction(); + if (supportFragmentManager.findFragmentByTag(CALL_LOG_TAG) != null) { + supportTransaction.hide(supportFragmentManager.findFragmentByTag(CALL_LOG_TAG)); + } + if (supportFragmentManager.findFragmentByTag(VOICEMAIL_TAG) != null) { + supportTransaction.hide(supportFragmentManager.findFragmentByTag(VOICEMAIL_TAG)); + } + supportTransaction.commit(); + } + } +} diff --git a/java/com/android/dialer/main/impl/OldMainActivityPeer.java b/java/com/android/dialer/main/impl/OldMainActivityPeer.java new file mode 100644 index 000000000..489de1a64 --- /dev/null +++ b/java/com/android/dialer/main/impl/OldMainActivityPeer.java @@ -0,0 +1,815 @@ +/* + * 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.app.Activity; +import android.app.Fragment; +import android.app.FragmentManager; +import android.app.KeyguardManager; +import android.content.ContentResolver; +import android.content.Context; +import android.content.Intent; +import android.database.Cursor; +import android.net.Uri; +import android.os.Bundle; +import android.provider.CallLog.Calls; +import android.provider.ContactsContract.QuickContact; +import android.support.annotation.Nullable; +import android.support.design.widget.FloatingActionButton; +import android.view.View; +import android.widget.ImageView; +import com.android.contacts.common.list.OnPhoneNumberPickerActionListener; +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.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.callintent.CallIntentBuilder; +import com.android.dialer.callintent.CallSpecificAppData; +import com.android.dialer.common.FragmentUtils.FragmentUtilListener; +import com.android.dialer.common.LogUtil; +import com.android.dialer.common.concurrent.DialerExecutorComponent; +import com.android.dialer.common.concurrent.UiListener; +import com.android.dialer.compat.CompatUtils; +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.interactions.PhoneNumberInteraction; +import com.android.dialer.main.MainActivityPeer; +import com.android.dialer.main.impl.BottomNavBar.OnBottomNavTabSelectedListener; +import com.android.dialer.main.impl.BottomNavBar.TabIndex; +import com.android.dialer.main.impl.toolbar.MainToolbar; +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.TransactionSafeActivity; +import com.google.common.util.concurrent.ListenableFuture; +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"; + + private final MainActivity mainActivity; + + // 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 MainOldSpeedDialFragmentHostInterface oldSpeedDialFragmentHostInterface; + private MainOnDragDropListener onDragDropListener; + + /** Language the device was in last time {@link #onSaveInstanceState(Bundle)} was called. */ + private String savedLanguageCode; + + private LastTabController lastTabController; + + private BottomNavBar bottomNav; + private View snackbarContainer; + private UiListener getLastOutgoingCallListener; + + public OldMainActivityPeer(MainActivity mainActivity) { + this.mainActivity = mainActivity; + } + + @Override + public void onActivityCreate(Bundle savedInstanceState) { + mainActivity.setContentView(R.layout.main_activity); + initUiListeners(); + initLayout(savedInstanceState); + SmartDialPrefix.initializeNanpSettings(mainActivity); + } + + private void initUiListeners() { + getLastOutgoingCallListener = + DialerExecutorComponent.get(mainActivity) + .createUiListener(mainActivity.getFragmentManager(), "Query last phone number"); + } + + private void initLayout(Bundle savedInstanceState) { + onContactSelectedListener = new MainOnContactSelectedListener(mainActivity); + dialpadFragmentHostInterface = new MainDialpadFragmentHost(); + + snackbarContainer = mainActivity.findViewById(R.id.coordinator_layout); + + FloatingActionButton fab = mainActivity.findViewById(R.id.fab); + fab.setOnClickListener(v -> searchController.showDialpad(true)); + + MainToolbar toolbar = mainActivity.findViewById(R.id.toolbar); + mainActivity.setSupportActionBar(mainActivity.findViewById(R.id.toolbar)); + + bottomNav = mainActivity.findViewById(R.id.bottom_nav_bar); + MainBottomNavBarBottomNavTabListener bottomNavTabListener = + new MainBottomNavBarBottomNavTabListener(mainActivity.getFragmentManager()); + bottomNav.addOnTabSelectedListener(bottomNavTabListener); + + callLogFragmentListener = + new MainCallLogFragmentListener(mainActivity, mainActivity.getContentResolver(), bottomNav); + bottomNav.addOnTabSelectedListener(callLogFragmentListener); + + searchController = new MainSearchController(mainActivity, bottomNav, fab, toolbar); + toolbar.setSearchBarListener(searchController); + + onDialpadQueryChangedListener = new MainOnDialpadQueryChangedListener(searchController); + dialpadListener = + new MainDialpadListener(mainActivity, searchController, getLastOutgoingCallListener); + searchFragmentListener = new MainSearchFragmentListener(searchController); + callLogAdapterOnActionModeStateChangedListener = + new MainCallLogAdapterOnActionModeStateChangedListener(); + callLogHostInterface = new MainCallLogHost(searchController, fab); + + onListFragmentScrolledListener = new MainOnListFragmentScrolledListener(snackbarContainer); + onPhoneNumberPickerActionListener = new MainOnPhoneNumberPickerActionListener(mainActivity); + oldSpeedDialFragmentHostInterface = + new MainOldSpeedDialFragmentHostInterface( + bottomNav, mainActivity.findViewById(R.id.contact_tile_drag_shadow_overlay)); + onDragDropListener = new MainOnDragDropListener(); + + lastTabController = new LastTabController(mainActivity, bottomNav); + + // Restore our view state if needed, else initialize as if the app opened for the first time + if (savedInstanceState != null) { + savedLanguageCode = savedInstanceState.getString(KEY_SAVED_LANGUAGE_CODE); + searchController.onRestoreInstanceState(savedInstanceState); + bottomNav.selectTab(savedInstanceState.getInt(KEY_CURRENT_TAB)); + } else { + lastTabController.selectLastTab(); + } + } + + @Override + public void onNewIntent(Intent intent) { + lastTabController.selectLastTab(); + } + + @Override + public void onActivityResume() { + callLogFragmentListener.onActivityResume(); + // Start the thread that updates the smart dial database if the activity is recreated with a + // language change. + boolean forceUpdate = + !CompatUtils.getLocale(mainActivity).getISO3Language().equals(savedLanguageCode); + Database.get(mainActivity) + .getDatabaseHelper(mainActivity) + .startSmartDialUpdateThread(forceUpdate); + showPostCallPrompt(); + } + + @Override + public void onActivityStop() { + lastTabController.onActivityStop(); + callLogFragmentListener.onActivityStop( + mainActivity.isChangingConfigurations(), + mainActivity.getSystemService(KeyguardManager.class).isKeyguardLocked()); + } + + private void showPostCallPrompt() { + if (TelecomUtil.isInManagedCall(mainActivity)) { + // No prompt to show if the user is in a call + return; + } + + if (searchController.isInSearch()) { + // Don't show the prompt if we're in the search ui + return; + } + + PostCall.promptUserForMessageIfNecessary(mainActivity, snackbarContainer); + } + + @Override + public void onSaveInstanceState(Bundle bundle) { + bundle.putString( + KEY_SAVED_LANGUAGE_CODE, CompatUtils.getLocale(mainActivity).getISO3Language()); + bundle.putInt(KEY_CURRENT_TAB, bottomNav.getSelectedTab()); + searchController.onSaveInstanceState(bundle); + } + + @Override + public void onActivityResult(int requestCode, int resultCode, Intent data) { + if (requestCode == ActivityRequestCodes.DIALTACTS_VOICE_SEARCH) { + searchController.onVoiceResults(resultCode, data); + } else { + LogUtil.e("MainActivity.onActivityResult", "Unknown request code: " + requestCode); + } + } + + @Override + public boolean onBackPressed() { + if (searchController.onBackPressed()) { + return true; + } + return false; + } + + @Nullable + @Override + @SuppressWarnings("unchecked") // Casts are checked using runtime methods + public T getImpl(Class callbackInterface) { + if (callbackInterface.isInstance(onContactSelectedListener)) { + return (T) onContactSelectedListener; + } else if (callbackInterface.isInstance(onDialpadQueryChangedListener)) { + return (T) onDialpadQueryChangedListener; + } else if (callbackInterface.isInstance(dialpadListener)) { + return (T) dialpadListener; + } else if (callbackInterface.isInstance(dialpadFragmentHostInterface)) { + return (T) dialpadFragmentHostInterface; + } else if (callbackInterface.isInstance(searchFragmentListener)) { + return (T) searchFragmentListener; + } else if (callbackInterface.isInstance(callLogAdapterOnActionModeStateChangedListener)) { + return (T) callLogAdapterOnActionModeStateChangedListener; + } else if (callbackInterface.isInstance(callLogHostInterface)) { + return (T) callLogHostInterface; + } else if (callbackInterface.isInstance(callLogFragmentListener)) { + return (T) callLogFragmentListener; + } else if (callbackInterface.isInstance(onListFragmentScrolledListener)) { + return (T) onListFragmentScrolledListener; + } else if (callbackInterface.isInstance(onPhoneNumberPickerActionListener)) { + return (T) onPhoneNumberPickerActionListener; + } else if (callbackInterface.isInstance(oldSpeedDialFragmentHostInterface)) { + return (T) oldSpeedDialFragmentHostInterface; + } else if (callbackInterface.isInstance(onDragDropListener)) { + return (T) onDragDropListener; + } else { + return null; + } + } + + /** @see OnContactSelectedListener */ + private static final class MainOnContactSelectedListener implements OnContactSelectedListener { + + private final Context context; + + MainOnContactSelectedListener(Context context) { + this.context = context; + } + + @Override + public void onContactSelected(ImageView photo, Uri contactUri, long contactId) { + // TODO(calderwoodra): Add impression logging + QuickContact.showQuickContact( + context, photo, contactUri, QuickContact.MODE_LARGE, null /* excludeMimes */); + } + } + + /** @see OnDialpadQueryChangedListener */ + private static final class MainOnDialpadQueryChangedListener + implements OnDialpadQueryChangedListener { + + private final MainSearchController searchController; + + MainOnDialpadQueryChangedListener(MainSearchController searchController) { + this.searchController = searchController; + } + + @Override + public void onDialpadQueryChanged(String query) { + searchController.onDialpadQueryChanged(query); + } + } + + /** @see DialpadListener */ + private static final class MainDialpadListener implements DialpadListener { + + private final MainSearchController searchController; + private final Context context; + private final UiListener listener; + + MainDialpadListener( + Context context, MainSearchController searchController, UiListener uiListener) { + this.context = context; + this.searchController = searchController; + this.listener = uiListener; + } + + @Override + public void getLastOutgoingCall(LastOutgoingCallCallback callback) { + ListenableFuture listenableFuture = + DialerExecutorComponent.get(context) + .backgroundExecutor() + .submit(() -> Calls.getLastOutgoingCall(context)); + listener.listen(context, listenableFuture, callback::lastOutgoingCall, throwable -> {}); + } + + @Override + public void onDialpadShown() { + searchController.onDialpadShown(); + } + + @Override + public void onCallPlacedFromDialpad() { + // TODO(calderwoodra): logging + } + } + + /** @see SearchFragmentListener */ + private static final class MainSearchFragmentListener implements SearchFragmentListener { + + private final MainSearchController searchController; + + MainSearchFragmentListener(MainSearchController searchController) { + this.searchController = searchController; + } + + @Override + public void onSearchListTouch() { + searchController.onSearchListTouch(); + } + + @Override + public void onCallPlacedFromSearch() { + // TODO(calderwoodra): logging + } + } + + /** @see DialpadFragment.HostInterface */ + private static final class MainDialpadFragmentHost implements DialpadFragment.HostInterface { + + @Override + public boolean onDialpadSpacerTouchWithEmptyQuery() { + // No-op, just let the clicks fall through to the search list + return false; + } + } + + /** @see CallLogAdapter.OnActionModeStateChangedListener */ + // TODO(a bug): handle multiselect mode + private static final class MainCallLogAdapterOnActionModeStateChangedListener + implements CallLogAdapter.OnActionModeStateChangedListener { + + @Override + public void onActionModeStateChanged(boolean isEnabled) {} + + @Override + public boolean isActionModeStateEnabled() { + return false; + } + } + + /** @see CallLogFragment.HostInterface */ + private static final class MainCallLogHost implements CallLogFragment.HostInterface { + + private final FloatingActionButton fab; + private final MainSearchController searchController; + + MainCallLogHost(MainSearchController searchController, FloatingActionButton fab) { + this.searchController = searchController; + this.fab = fab; + } + + @Override + public void showDialpad() { + searchController.showDialpad(true); + } + + @Override + public void enableFloatingButton(boolean enabled) { + if (enabled) { + fab.show(); + } else { + fab.hide(); + } + } + } + + /** + * Handles the logic for callbacks from: + * + *

    + *
  • {@link CallLogFragment} + *
  • {@link CallLogQueryHandler} + *
  • {@link BottomNavBar} + *
+ * + * This mainly entails: + * + *
    + *
  • Handling querying for missed calls/unread voicemails. + *
  • Displaying a badge to the user in the bottom nav when there are missed calls/unread + * voicemails present. + *
  • Marking missed calls as read when appropriate. See {@link + * #markMissedCallsAsReadAndRemoveNotification()} + *
  • TODO(calderwoodra): multiselect + *
  • TODO(calderwoodra): voicemail status + *
+ * + * @see CallLogFragmentListener + * @see CallLogQueryHandler.Listener + * @see OnBottomNavTabSelectedListener + */ + private static final class MainCallLogFragmentListener + implements CallLogFragmentListener, + CallLogQueryHandler.Listener, + OnBottomNavTabSelectedListener { + + private final CallLogQueryHandler callLogQueryHandler; + private final BottomNavBar bottomNavBar; + private final Context context; + + private @TabIndex int currentTab = TabIndex.SPEED_DIAL; + private long timeSelected = -1; + private boolean activityIsAlive; + + MainCallLogFragmentListener( + Context context, ContentResolver contentResolver, BottomNavBar bottomNavBar) { + callLogQueryHandler = new CallLogQueryHandler(context, contentResolver, this); + this.bottomNavBar = bottomNavBar; + this.context = context; + } + + @Override + public void updateTabUnreadCounts() { + callLogQueryHandler.fetchMissedCallsUnreadCount(); + callLogQueryHandler.fetchVoicemailUnreadCount(); + } + + @Override + public void showMultiSelectRemoveView(boolean show) { + // TODO(a bug): handle multiselect mode + } + + @Override + public void onVoicemailStatusFetched(Cursor statusCursor) { + // TODO(calderwoodra): handle this when voicemail is implemented + } + + @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() { + 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; + callLogQueryHandler.fetchVoicemailStatus(); + callLogQueryHandler.fetchMissedCallsUnreadCount(); + // Reset the tab on resume to restart the timer + setCurrentTab(bottomNavBar.getSelectedTab()); + } + + /** Should be called when {@link Activity#onStop()} is called. */ + public void onActivityStop(boolean changingConfigurations, boolean keyguardLocked) { + activityIsAlive = false; + if (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 + } + } + + /** @see OldSpeedDialFragment.HostInterface */ + private static final class MainOldSpeedDialFragmentHostInterface + implements OldSpeedDialFragment.HostInterface { + + private final BottomNavBar bottomNavBar; + private final ImageView dragShadowOverlay; + + // TODO(calderwoodra): Use this for drag and drop + @SuppressWarnings("unused") + private DragDropController dragDropController; + + MainOldSpeedDialFragmentHostInterface(BottomNavBar bottomNavBar, ImageView dragShadowOverlay) { + this.bottomNavBar = bottomNavBar; + this.dragShadowOverlay = dragShadowOverlay; + } + + @Override + public void setDragDropController(DragDropController dragDropController) { + this.dragDropController = dragDropController; + } + + @Override + public void showAllContactsTab() { + bottomNavBar.selectTab(TabIndex.CONTACTS); + } + + @Override + public ImageView getDragShadowOverlay() { + return dragShadowOverlay; + } + } + + /** @see com.android.dialer.app.list.OnDragDropListener */ + // TODO(calderwoodra): implement drag and drop + private static final class MainOnDragDropListener implements OnDragDropListener { + + @Override + public void onDragStarted(int x, int y, PhoneFavoriteSquareTileView view) {} + + @Override + public void onDragHovered(int x, int y, PhoneFavoriteSquareTileView view) {} + + @Override + public void onDragFinished(int x, int y) {} + + @Override + public void onDroppedOnRemove() {} + } + + /** + * Implementation of {@link OnBottomNavTabSelectedListener} that handles logic for showing each of + * the main tabs. + */ + 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 FragmentManager fragmentManager; + + private MainBottomNavBarBottomNavTabListener(FragmentManager fragmentManager) { + this.fragmentManager = fragmentManager; + } + + @Override + public void onSpeedDialSelected() { + hideAllFragments(); + Fragment fragment = fragmentManager.findFragmentByTag(SPEED_DIAL_TAG); + if (fragment == null) { + fragmentManager + .beginTransaction() + .add(R.id.fragment_container, new OldSpeedDialFragment(), SPEED_DIAL_TAG) + .commit(); + } else { + fragmentManager.beginTransaction().show(fragment).commit(); + } + } + + @Override + public void onCallLogSelected() { + hideAllFragments(); + CallLogFragment fragment = (CallLogFragment) fragmentManager.findFragmentByTag(CALL_LOG_TAG); + if (fragment == null) { + fragmentManager + .beginTransaction() + .add(R.id.fragment_container, new CallLogFragment(), CALL_LOG_TAG) + .commit(); + } else { + fragmentManager.beginTransaction().show(fragment).commit(); + } + } + + @Override + public void onContactsSelected() { + hideAllFragments(); + ContactsFragment fragment = + (ContactsFragment) fragmentManager.findFragmentByTag(CONTACTS_TAG); + if (fragment == null) { + fragmentManager + .beginTransaction() + .add( + R.id.fragment_container, + ContactsFragment.newInstance(Header.ADD_CONTACT), + CONTACTS_TAG) + .commit(); + } else { + fragmentManager.beginTransaction().show(fragment).commit(); + } + } + + @Override + public void onVoicemailSelected() { + hideAllFragments(); + VisualVoicemailCallLogFragment fragment = + (VisualVoicemailCallLogFragment) fragmentManager.findFragmentByTag(VOICEMAIL_TAG); + if (fragment == null) { + fragmentManager + .beginTransaction() + .add(R.id.fragment_container, new VisualVoicemailCallLogFragment(), VOICEMAIL_TAG) + .commit(); + } else { + fragmentManager.beginTransaction().show(fragment).commit(); + } + } + + private void hideAllFragments() { + android.app.FragmentTransaction transaction = fragmentManager.beginTransaction(); + if (fragmentManager.findFragmentByTag(SPEED_DIAL_TAG) != null) { + transaction.hide(fragmentManager.findFragmentByTag(SPEED_DIAL_TAG)); + } + if (fragmentManager.findFragmentByTag(CALL_LOG_TAG) != null) { + // Old CallLogFragment + transaction.hide(fragmentManager.findFragmentByTag(CALL_LOG_TAG)); + } + if (fragmentManager.findFragmentByTag(CONTACTS_TAG) != null) { + transaction.hide(fragmentManager.findFragmentByTag(CONTACTS_TAG)); + } + if (fragmentManager.findFragmentByTag(VOICEMAIL_TAG) != null) { + // Old VisualVoicemailFragment + transaction.hide(fragmentManager.findFragmentByTag(VOICEMAIL_TAG)); + } + transaction.commit(); + } + } + + private static final class LastTabController { + + private final Context context; + private final BottomNavBar bottomNavBar; + private final boolean isEnabled; + + LastTabController(Context context, BottomNavBar bottomNavBar) { + this.context = context; + this.bottomNavBar = bottomNavBar; + isEnabled = ConfigProviderBindings.get(context).getBoolean("last_tab_enabled", false); + } + + /** Sets the last tab if the feature is enabled, otherwise defaults to speed dial. */ + void selectLastTab() { + @TabIndex int tabIndex = TabIndex.SPEED_DIAL; + if (isEnabled) { + tabIndex = + StorageComponent.get(context) + .unencryptedSharedPrefs() + .getInt(KEY_LAST_TAB, TabIndex.SPEED_DIAL); + } + bottomNavBar.selectTab(tabIndex); + } + + void onActivityStop() { + StorageComponent.get(context) + .unencryptedSharedPrefs() + .edit() + .putInt(KEY_LAST_TAB, bottomNavBar.getSelectedTab()) + .apply(); + } + } +} -- cgit v1.2.3