blob: 3d61f7371befe1ae1659becc888e8fb5178ea7ff [file] [log] [blame]
Eric Erfanianccca3152017-02-22 16:32:36 -08001/*
2 * Copyright (C) 2013 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License
15 */
16
17package com.android.dialer.app;
18
19import android.app.Fragment;
20import android.app.FragmentTransaction;
Eric Erfanian10b34a52017-05-04 08:23:17 -070021import android.app.KeyguardManager;
Eric Erfanianccca3152017-02-22 16:32:36 -080022import android.content.ActivityNotFoundException;
23import android.content.Context;
24import android.content.Intent;
25import android.content.pm.PackageManager;
26import android.content.pm.ResolveInfo;
27import android.content.res.Configuration;
28import android.content.res.Resources;
Eric Erfanianccca3152017-02-22 16:32:36 -080029import android.net.Uri;
30import android.os.Bundle;
Eric Erfaniand8046e52017-04-06 09:41:50 -070031import android.os.SystemClock;
Eric Erfanianccca3152017-02-22 16:32:36 -080032import android.os.Trace;
33import android.provider.CallLog.Calls;
calderwoodra73b51d52017-12-08 20:52:56 -080034import android.provider.ContactsContract.QuickContact;
Eric Erfanianccca3152017-02-22 16:32:36 -080035import android.speech.RecognizerIntent;
Eric Erfanianccca3152017-02-22 16:32:36 -080036import android.support.annotation.NonNull;
37import android.support.annotation.VisibleForTesting;
38import android.support.design.widget.CoordinatorLayout;
Eric Erfanianfc37b022017-03-21 10:11:17 -070039import android.support.design.widget.FloatingActionButton;
calderwoodra6007f522017-10-09 16:59:04 -070040import android.support.design.widget.FloatingActionButton.OnVisibilityChangedListener;
Eric Erfanianccca3152017-02-22 16:32:36 -080041import android.support.design.widget.Snackbar;
42import android.support.v4.app.ActivityCompat;
43import android.support.v4.view.ViewPager;
44import android.support.v7.app.ActionBar;
45import android.telecom.PhoneAccount;
46import android.text.Editable;
47import android.text.TextUtils;
48import android.text.TextWatcher;
49import android.view.DragEvent;
50import android.view.Gravity;
Eric Erfanianccca3152017-02-22 16:32:36 -080051import android.view.Menu;
52import android.view.MenuItem;
53import android.view.MotionEvent;
54import android.view.View;
55import android.view.View.OnDragListener;
Eric Erfanianccca3152017-02-22 16:32:36 -080056import android.view.animation.Animation;
57import android.view.animation.AnimationUtils;
58import android.widget.AbsListView.OnScrollListener;
59import android.widget.EditText;
60import android.widget.ImageButton;
calderwoodra73b51d52017-12-08 20:52:56 -080061import android.widget.ImageView;
Eric Erfanianccca3152017-02-22 16:32:36 -080062import android.widget.PopupMenu;
63import android.widget.TextView;
64import android.widget.Toast;
65import com.android.contacts.common.dialog.ClearFrequentsDialog;
66import com.android.contacts.common.list.OnPhoneNumberPickerActionListener;
Eric Erfanianccca3152017-02-22 16:32:36 -080067import com.android.dialer.animation.AnimUtils;
68import com.android.dialer.animation.AnimationListenerAdapter;
Eric Erfaniand5e47f62017-03-15 14:41:07 -070069import com.android.dialer.app.calllog.CallLogActivity;
Eric Erfanian2ca43182017-08-31 06:57:16 -070070import com.android.dialer.app.calllog.CallLogAdapter;
Eric Erfanianccca3152017-02-22 16:32:36 -080071import com.android.dialer.app.calllog.CallLogFragment;
72import com.android.dialer.app.calllog.CallLogNotificationsService;
Eric Erfanian2ca43182017-08-31 06:57:16 -070073import com.android.dialer.app.calllog.IntentProvider;
Eric Erfaniand8046e52017-04-06 09:41:50 -070074import com.android.dialer.app.list.DialtactsPagerAdapter;
Eric Erfanian2ca43182017-08-31 06:57:16 -070075import com.android.dialer.app.list.DialtactsPagerAdapter.TabIndex;
Eric Erfanianccca3152017-02-22 16:32:36 -080076import com.android.dialer.app.list.DragDropController;
77import com.android.dialer.app.list.ListsFragment;
Eric Erfaniand8046e52017-04-06 09:41:50 -070078import com.android.dialer.app.list.OldSpeedDialFragment;
Eric Erfanianccca3152017-02-22 16:32:36 -080079import com.android.dialer.app.list.OnDragDropListener;
80import com.android.dialer.app.list.OnListFragmentScrolledListener;
81import com.android.dialer.app.list.PhoneFavoriteSquareTileView;
Eric Erfanianccca3152017-02-22 16:32:36 -080082import com.android.dialer.app.settings.DialerSettingsActivity;
83import com.android.dialer.app.widget.ActionBarController;
84import com.android.dialer.app.widget.SearchEditTextLayout;
Eric Erfaniand5e47f62017-03-15 14:41:07 -070085import com.android.dialer.callcomposer.CallComposerActivity;
linyuh66428472018-03-14 15:21:01 -070086import com.android.dialer.calldetails.OldCallDetailsActivity;
Eric Erfanian2ca43182017-08-31 06:57:16 -070087import com.android.dialer.callintent.CallInitiationType;
Eric Erfanianccca3152017-02-22 16:32:36 -080088import com.android.dialer.callintent.CallIntentBuilder;
Eric Erfanian8369df02017-05-03 10:27:13 -070089import com.android.dialer.callintent.CallSpecificAppData;
Eric Erfanianccca3152017-02-22 16:32:36 -080090import com.android.dialer.common.Assert;
91import com.android.dialer.common.LogUtil;
calderwoodra9d197d02017-11-15 15:41:54 -080092import com.android.dialer.common.UiUtil;
calderwoodra97d0b5e2018-01-26 12:11:26 -080093import com.android.dialer.common.concurrent.DialerExecutorComponent;
calderwoodra6007f522017-10-09 16:59:04 -070094import com.android.dialer.common.concurrent.ThreadUtil;
linyuhc9b35812017-12-28 09:58:34 -080095import com.android.dialer.compat.CompatUtils;
Eric Erfanian2ca43182017-08-31 06:57:16 -070096import com.android.dialer.configprovider.ConfigProviderBindings;
97import com.android.dialer.constants.ActivityRequestCodes;
Android Dialer3f4a3192017-09-20 17:59:44 -070098import com.android.dialer.contactsfragment.ContactsFragment;
calderwoodra73b51d52017-12-08 20:52:56 -080099import com.android.dialer.contactsfragment.ContactsFragment.OnContactSelectedListener;
Eric Erfanianccca3152017-02-22 16:32:36 -0800100import com.android.dialer.database.Database;
101import com.android.dialer.database.DialerDatabaseHelper;
Eric Erfanian2ca43182017-08-31 06:57:16 -0700102import com.android.dialer.dialpadview.DialpadFragment;
103import com.android.dialer.dialpadview.DialpadFragment.DialpadListener;
104import com.android.dialer.dialpadview.DialpadFragment.LastOutgoingCallCallback;
roldenburg19c6c252017-12-13 15:58:01 -0800105import com.android.dialer.duo.DuoComponent;
Eric Erfanianccca3152017-02-22 16:32:36 -0800106import com.android.dialer.interactions.PhoneNumberInteraction;
107import com.android.dialer.interactions.PhoneNumberInteraction.InteractionErrorCode;
Eric Erfanian8369df02017-05-03 10:27:13 -0700108import com.android.dialer.logging.DialerImpression;
calderwoodra73b51d52017-12-08 20:52:56 -0800109import com.android.dialer.logging.InteractionEvent;
Eric Erfanianccca3152017-02-22 16:32:36 -0800110import com.android.dialer.logging.Logger;
Eric Erfanian8369df02017-05-03 10:27:13 -0700111import com.android.dialer.logging.ScreenEvent;
Eric Erfanian2ca43182017-08-31 06:57:16 -0700112import com.android.dialer.logging.UiAction;
zachh7a96dc72018-02-20 22:16:03 -0800113import com.android.dialer.metrics.Metrics;
114import com.android.dialer.metrics.MetricsComponent;
Eric Erfanian2ca43182017-08-31 06:57:16 -0700115import com.android.dialer.performancereport.PerformanceReport;
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700116import com.android.dialer.postcall.PostCall;
twyen0efc8402017-11-07 15:39:15 -0800117import com.android.dialer.precall.PreCall;
Eric Erfanianccca3152017-02-22 16:32:36 -0800118import com.android.dialer.proguard.UsedByReflection;
Eric Erfanian2ca43182017-08-31 06:57:16 -0700119import com.android.dialer.searchfragment.list.NewSearchFragment;
calderwoodra32257fd2017-10-25 15:01:37 -0700120import com.android.dialer.searchfragment.list.NewSearchFragment.SearchFragmentListener;
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700121import com.android.dialer.simulator.Simulator;
122import com.android.dialer.simulator.SimulatorComponent;
linyuhbf4bb052017-12-21 15:42:00 -0800123import com.android.dialer.smartdial.util.SmartDialNameMatcher;
124import com.android.dialer.smartdial.util.SmartDialPrefix;
linyuh168d09c2017-10-26 17:04:30 -0700125import com.android.dialer.storage.StorageComponent;
Eric Erfanianccca3152017-02-22 16:32:36 -0800126import com.android.dialer.telecom.TelecomUtil;
127import com.android.dialer.util.DialerUtils;
Eric Erfanianccca3152017-02-22 16:32:36 -0800128import com.android.dialer.util.PermissionsUtil;
129import com.android.dialer.util.TouchPointManager;
130import com.android.dialer.util.TransactionSafeActivity;
131import com.android.dialer.util.ViewUtil;
calderwoodra6007f522017-10-09 16:59:04 -0700132import com.android.dialer.widget.FloatingActionButtonController;
twyen891fe0f2017-10-17 14:59:15 -0700133import com.google.common.base.Optional;
Eric Erfanianccca3152017-02-22 16:32:36 -0800134import java.util.ArrayList;
135import java.util.Arrays;
136import java.util.List;
137import java.util.Locale;
Eric Erfaniand8046e52017-04-06 09:41:50 -0700138import java.util.concurrent.TimeUnit;
Eric Erfanianccca3152017-02-22 16:32:36 -0800139
140/** The dialer tab's title is 'phone', a more common name (see strings.xml). */
141@UsedByReflection(value = "AndroidManifest-app.xml")
142public class DialtactsActivity extends TransactionSafeActivity
143 implements View.OnClickListener,
144 DialpadFragment.OnDialpadQueryChangedListener,
145 OnListFragmentScrolledListener,
146 CallLogFragment.HostInterface,
Eric Erfanian2ca43182017-08-31 06:57:16 -0700147 CallLogAdapter.OnActionModeStateChangedListener,
Android Dialer3f4a3192017-09-20 17:59:44 -0700148 ContactsFragment.OnContactsListScrolledListener,
Eric Erfanianccca3152017-02-22 16:32:36 -0800149 DialpadFragment.HostInterface,
Eric Erfaniand8046e52017-04-06 09:41:50 -0700150 OldSpeedDialFragment.HostInterface,
Eric Erfanianccca3152017-02-22 16:32:36 -0800151 OnDragDropListener,
152 OnPhoneNumberPickerActionListener,
153 PopupMenu.OnMenuItemClickListener,
154 ViewPager.OnPageChangeListener,
155 ActionBarController.ActivityUi,
156 PhoneNumberInteraction.InteractionErrorListener,
157 PhoneNumberInteraction.DisambigDialogDismissedListener,
Eric Erfanian2ca43182017-08-31 06:57:16 -0700158 ActivityCompat.OnRequestPermissionsResultCallback,
calderwoodrac3770122017-10-13 16:36:17 -0700159 DialpadListener,
calderwoodra73b51d52017-12-08 20:52:56 -0800160 SearchFragmentListener,
161 OnContactSelectedListener {
Eric Erfanianccca3152017-02-22 16:32:36 -0800162
163 public static final boolean DEBUG = false;
164 @VisibleForTesting public static final String TAG_DIALPAD_FRAGMENT = "dialpad";
165 private static final String ACTION_SHOW_TAB = "ACTION_SHOW_TAB";
166 @VisibleForTesting public static final String EXTRA_SHOW_TAB = "EXTRA_SHOW_TAB";
167 public static final String EXTRA_CLEAR_NEW_VOICEMAILS = "EXTRA_CLEAR_NEW_VOICEMAILS";
Eric Erfanian2ca43182017-08-31 06:57:16 -0700168 private static final String KEY_LAST_TAB = "last_tab";
Eric Erfanianccca3152017-02-22 16:32:36 -0800169 private static final String TAG = "DialtactsActivity";
170 private static final String KEY_IN_REGULAR_SEARCH_UI = "in_regular_search_ui";
171 private static final String KEY_IN_DIALPAD_SEARCH_UI = "in_dialpad_search_ui";
calderwoodra50715262017-09-08 09:20:08 -0700172 private static final String KEY_IN_NEW_SEARCH_UI = "in_new_search_ui";
Eric Erfanianccca3152017-02-22 16:32:36 -0800173 private static final String KEY_SEARCH_QUERY = "search_query";
calderwoodra9d197d02017-11-15 15:41:54 -0800174 private static final String KEY_DIALPAD_QUERY = "dialpad_query";
Eric Erfanianccca3152017-02-22 16:32:36 -0800175 private static final String KEY_FIRST_LAUNCH = "first_launch";
linyuhc9b35812017-12-28 09:58:34 -0800176 private static final String KEY_SAVED_LANGUAGE_CODE = "saved_language_code";
Eric Erfanianccca3152017-02-22 16:32:36 -0800177 private static final String KEY_WAS_CONFIGURATION_CHANGE = "was_configuration_change";
178 private static final String KEY_IS_DIALPAD_SHOWN = "is_dialpad_shown";
calderwoodra218fff32017-09-19 11:20:31 -0700179 private static final String KEY_FAB_VISIBLE = "fab_visible";
Eric Erfanian2ca43182017-08-31 06:57:16 -0700180 private static final String TAG_NEW_SEARCH_FRAGMENT = "new_search";
Eric Erfanianccca3152017-02-22 16:32:36 -0800181 private static final String TAG_FAVORITES_FRAGMENT = "favorites";
182 /** Just for backward compatibility. Should behave as same as {@link Intent#ACTION_DIAL}. */
183 private static final String ACTION_TOUCH_DIALER = "com.android.phone.action.TOUCH_DIALER";
184
Eric Erfanianccca3152017-02-22 16:32:36 -0800185 private static final int FAB_SCALE_IN_DELAY_MS = 300;
Eric Erfaniand8046e52017-04-06 09:41:50 -0700186
187 /**
188 * Minimum time the history tab must have been selected for it to be marked as seen in onStop()
189 */
190 private static final long HISTORY_TAB_SEEN_TIMEOUT = TimeUnit.SECONDS.toMillis(3);
191
linyuh183cb712017-12-27 17:02:37 -0800192 private static Optional<Boolean> voiceSearchEnabledForTest = Optional.absent();
twyen891fe0f2017-10-17 14:59:15 -0700193
Eric Erfanianccca3152017-02-22 16:32:36 -0800194 /** Fragment containing the dialpad that slides into view */
linyuh183cb712017-12-27 17:02:37 -0800195 protected DialpadFragment dialpadFragment;
Eric Erfanianccca3152017-02-22 16:32:36 -0800196
Android Dialer3f4a3192017-09-20 17:59:44 -0700197 /** Root layout of DialtactsActivity */
linyuh183cb712017-12-27 17:02:37 -0800198 private CoordinatorLayout parentLayout;
Eric Erfanianccca3152017-02-22 16:32:36 -0800199
Eric Erfanian2ca43182017-08-31 06:57:16 -0700200 /** new Fragment for search phone numbers using the keyboard and the dialpad. */
linyuh183cb712017-12-27 17:02:37 -0800201 private NewSearchFragment newSearchFragment;
Eric Erfanian2ca43182017-08-31 06:57:16 -0700202
Eric Erfanianccca3152017-02-22 16:32:36 -0800203 /** Animation that slides in. */
linyuh183cb712017-12-27 17:02:37 -0800204 private Animation slideIn;
Eric Erfanianccca3152017-02-22 16:32:36 -0800205
206 /** Animation that slides out. */
linyuh183cb712017-12-27 17:02:37 -0800207 private Animation slideOut;
Eric Erfanianccca3152017-02-22 16:32:36 -0800208 /** Fragment containing the speed dial list, call history list, and all contacts list. */
linyuh183cb712017-12-27 17:02:37 -0800209 private ListsFragment listsFragment;
Eric Erfanianccca3152017-02-22 16:32:36 -0800210 /**
211 * Tracks whether onSaveInstanceState has been called. If true, no fragment transactions can be
212 * commited.
213 */
linyuh183cb712017-12-27 17:02:37 -0800214 private boolean stateSaved;
Eric Erfanianccca3152017-02-22 16:32:36 -0800215
linyuh183cb712017-12-27 17:02:37 -0800216 private boolean isKeyboardOpen;
217 private boolean inNewSearch;
218 private boolean isRestarting;
219 private boolean inDialpadSearch;
220 private boolean inRegularSearch;
221 private boolean clearSearchOnPause;
222 private boolean isDialpadShown;
Eric Erfanianccca3152017-02-22 16:32:36 -0800223 /** Whether or not the device is in landscape orientation. */
linyuh183cb712017-12-27 17:02:37 -0800224 private boolean isLandscape;
Eric Erfanianccca3152017-02-22 16:32:36 -0800225 /** True if the dialpad is only temporarily showing due to being in call */
linyuh183cb712017-12-27 17:02:37 -0800226 private boolean inCallDialpadUp;
Eric Erfanianccca3152017-02-22 16:32:36 -0800227 /** True when this activity has been launched for the first time. */
linyuh183cb712017-12-27 17:02:37 -0800228 private boolean firstLaunch;
Eric Erfanianccca3152017-02-22 16:32:36 -0800229 /**
230 * Search query to be applied to the SearchView in the ActionBar once onCreateOptionsMenu has been
231 * called.
232 */
linyuh183cb712017-12-27 17:02:37 -0800233 private String pendingSearchViewQuery;
Eric Erfanianccca3152017-02-22 16:32:36 -0800234
linyuh183cb712017-12-27 17:02:37 -0800235 private PopupMenu overflowMenu;
236 private EditText searchView;
237 private SearchEditTextLayout searchEditTextLayout;
238 private View voiceSearchButton;
239 private String searchQuery;
240 private String dialpadQuery;
241 private DialerDatabaseHelper dialerDatabaseHelper;
242 private DragDropController dragDropController;
243 private ActionBarController actionBarController;
244 private FloatingActionButtonController floatingActionButtonController;
linyuhc9b35812017-12-28 09:58:34 -0800245 private String savedLanguageCode;
linyuh183cb712017-12-27 17:02:37 -0800246 private boolean wasConfigurationChange;
Eric Erfaniand8046e52017-04-06 09:41:50 -0700247 private long timeTabSelected;
Eric Erfanianccca3152017-02-22 16:32:36 -0800248
Eric Erfanian2ca43182017-08-31 06:57:16 -0700249 public boolean isMultiSelectModeEnabled;
250
251 private boolean isLastTabEnabled;
Eric Erfanianccca3152017-02-22 16:32:36 -0800252
linyuh183cb712017-12-27 17:02:37 -0800253 AnimationListenerAdapter slideInListener =
Eric Erfanianccca3152017-02-22 16:32:36 -0800254 new AnimationListenerAdapter() {
255 @Override
256 public void onAnimationEnd(Animation animation) {
257 maybeEnterSearchUi();
258 }
259 };
260 /** Listener for after slide out animation completes on dialer fragment. */
linyuh183cb712017-12-27 17:02:37 -0800261 AnimationListenerAdapter slideOutListener =
Eric Erfanianccca3152017-02-22 16:32:36 -0800262 new AnimationListenerAdapter() {
263 @Override
264 public void onAnimationEnd(Animation animation) {
265 commitDialpadFragmentHide();
266 }
267 };
268 /** Listener used to send search queries to the phone search fragment. */
linyuh183cb712017-12-27 17:02:37 -0800269 private final TextWatcher phoneSearchQueryTextListener =
Eric Erfanianccca3152017-02-22 16:32:36 -0800270 new TextWatcher() {
271 @Override
272 public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
273
274 @Override
275 public void onTextChanged(CharSequence s, int start, int before, int count) {
276 final String newText = s.toString();
linyuh183cb712017-12-27 17:02:37 -0800277 if (newText.equals(searchQuery)) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800278 // If the query hasn't changed (perhaps due to activity being destroyed
279 // and restored, or user launching the same DIAL intent twice), then there is
280 // no need to do anything here.
281 return;
282 }
Eric Erfanian2ca43182017-08-31 06:57:16 -0700283
284 if (count != 0) {
285 PerformanceReport.recordClick(UiAction.Type.TEXT_CHANGE_WITH_INPUT);
286 }
287
wangqie9f94c72017-11-10 11:51:50 -0800288 LogUtil.v("DialtactsActivity.onTextChanged", "called with new query: " + newText);
linyuh183cb712017-12-27 17:02:37 -0800289 LogUtil.v("DialtactsActivity.onTextChanged", "previous query: " + searchQuery);
290 searchQuery = newText;
Eric Erfanianccca3152017-02-22 16:32:36 -0800291
292 // Show search fragment only when the query string is changed to non-empty text.
293 if (!TextUtils.isEmpty(newText)) {
294 // Call enterSearchUi only if we are switching search modes, or showing a search
295 // fragment for the first time.
296 final boolean sameSearchMode =
linyuh183cb712017-12-27 17:02:37 -0800297 (isDialpadShown && inDialpadSearch) || (!isDialpadShown && inRegularSearch);
Eric Erfanianccca3152017-02-22 16:32:36 -0800298 if (!sameSearchMode) {
linyuh183cb712017-12-27 17:02:37 -0800299 enterSearchUi(isDialpadShown, searchQuery, true /* animate */);
Eric Erfanianccca3152017-02-22 16:32:36 -0800300 }
301 }
302
calderwoodra58d0b2a2018-03-21 16:57:10 -0700303 if (newSearchFragment != null && newSearchFragment.isVisible()) {
linyuh183cb712017-12-27 17:02:37 -0800304 newSearchFragment.setQuery(searchQuery, getCallInitiationType());
Eric Erfanianccca3152017-02-22 16:32:36 -0800305 }
306 }
307
308 @Override
309 public void afterTextChanged(Editable s) {}
310 };
311 /** Open the search UI when the user clicks on the search box. */
linyuh183cb712017-12-27 17:02:37 -0800312 private final View.OnClickListener searchViewOnClickListener =
Eric Erfanianccca3152017-02-22 16:32:36 -0800313 new View.OnClickListener() {
314 @Override
315 public void onClick(View v) {
316 if (!isInSearchUi()) {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700317 PerformanceReport.recordClick(UiAction.Type.OPEN_SEARCH);
linyuh183cb712017-12-27 17:02:37 -0800318 actionBarController.onSearchBoxTapped();
Eric Erfanianccca3152017-02-22 16:32:36 -0800319 enterSearchUi(
linyuh183cb712017-12-27 17:02:37 -0800320 false /* smartDialSearch */, searchView.getText().toString(), true /* animate */);
Eric Erfanianccca3152017-02-22 16:32:36 -0800321 }
322 }
323 };
324
linyuh183cb712017-12-27 17:02:37 -0800325 private int actionBarHeight;
326 private int previouslySelectedTabIndex;
Eric Erfanian2ca43182017-08-31 06:57:16 -0700327
Eric Erfanianccca3152017-02-22 16:32:36 -0800328 /**
329 * The text returned from a voice search query. Set in {@link #onActivityResult} and used in
330 * {@link #onResume()} to populate the search box.
331 */
linyuh183cb712017-12-27 17:02:37 -0800332 private String voiceSearchQuery;
Eric Erfanianccca3152017-02-22 16:32:36 -0800333
334 /**
335 * @param tab the TAB_INDEX_* constant in {@link ListsFragment}
336 * @return A intent that will open the DialtactsActivity into the specified tab. The intent for
337 * each tab will be unique.
338 */
339 public static Intent getShowTabIntent(Context context, int tab) {
340 Intent intent = new Intent(context, DialtactsActivity.class);
341 intent.setAction(ACTION_SHOW_TAB);
342 intent.putExtra(DialtactsActivity.EXTRA_SHOW_TAB, tab);
343 intent.setData(
344 new Uri.Builder()
345 .scheme("intent")
346 .authority(context.getPackageName())
347 .appendPath(TAG)
348 .appendQueryParameter(DialtactsActivity.EXTRA_SHOW_TAB, String.valueOf(tab))
349 .build());
350
351 return intent;
352 }
353
354 @Override
355 public boolean dispatchTouchEvent(MotionEvent ev) {
356 if (ev.getAction() == MotionEvent.ACTION_DOWN) {
357 TouchPointManager.getInstance().setPoint((int) ev.getRawX(), (int) ev.getRawY());
358 }
359 return super.dispatchTouchEvent(ev);
360 }
361
362 @Override
363 protected void onCreate(Bundle savedInstanceState) {
364 Trace.beginSection(TAG + " onCreate");
zachh6050ec22017-09-21 16:02:35 -0700365 LogUtil.enterBlock("DialtactsActivity.onCreate");
Eric Erfanianccca3152017-02-22 16:32:36 -0800366 super.onCreate(savedInstanceState);
367
linyuh183cb712017-12-27 17:02:37 -0800368 firstLaunch = true;
Eric Erfanian2ca43182017-08-31 06:57:16 -0700369 isLastTabEnabled = ConfigProviderBindings.get(this).getBoolean("last_tab_enabled", false);
Eric Erfanianccca3152017-02-22 16:32:36 -0800370
371 final Resources resources = getResources();
linyuh183cb712017-12-27 17:02:37 -0800372 actionBarHeight = resources.getDimensionPixelSize(R.dimen.action_bar_height_large);
Eric Erfanianccca3152017-02-22 16:32:36 -0800373
374 Trace.beginSection(TAG + " setContentView");
375 setContentView(R.layout.dialtacts_activity);
376 Trace.endSection();
377 getWindow().setBackgroundDrawable(null);
378
379 Trace.beginSection(TAG + " setup Views");
380 final ActionBar actionBar = getActionBarSafely();
381 actionBar.setCustomView(R.layout.search_edittext);
382 actionBar.setDisplayShowCustomEnabled(true);
383 actionBar.setBackgroundDrawable(null);
384
linyuh183cb712017-12-27 17:02:37 -0800385 searchEditTextLayout = actionBar.getCustomView().findViewById(R.id.search_view_container);
Eric Erfanianccca3152017-02-22 16:32:36 -0800386
linyuh183cb712017-12-27 17:02:37 -0800387 actionBarController = new ActionBarController(this, searchEditTextLayout);
Eric Erfanianccca3152017-02-22 16:32:36 -0800388
linyuh183cb712017-12-27 17:02:37 -0800389 searchView = searchEditTextLayout.findViewById(R.id.search_view);
390 searchView.addTextChangedListener(phoneSearchQueryTextListener);
391 searchView.setHint(getSearchBoxHint());
calderwoodra50715262017-09-08 09:20:08 -0700392
linyuh183cb712017-12-27 17:02:37 -0800393 voiceSearchButton = searchEditTextLayout.findViewById(R.id.voice_search_button);
394 searchEditTextLayout
Eric Erfanian10b34a52017-05-04 08:23:17 -0700395 .findViewById(R.id.search_box_collapsed)
linyuh183cb712017-12-27 17:02:37 -0800396 .setOnClickListener(searchViewOnClickListener);
397 searchEditTextLayout
calderwoodra50715262017-09-08 09:20:08 -0700398 .findViewById(R.id.search_back_button)
399 .setOnClickListener(v -> exitSearchUi());
Eric Erfanianccca3152017-02-22 16:32:36 -0800400
linyuh183cb712017-12-27 17:02:37 -0800401 isLandscape =
Eric Erfanianccca3152017-02-22 16:32:36 -0800402 getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE;
linyuh183cb712017-12-27 17:02:37 -0800403 previouslySelectedTabIndex = DialtactsPagerAdapter.TAB_INDEX_SPEED_DIAL;
Eric Erfanian2ca43182017-08-31 06:57:16 -0700404 FloatingActionButton floatingActionButton = findViewById(R.id.floating_action_button);
Eric Erfanianccca3152017-02-22 16:32:36 -0800405 floatingActionButton.setOnClickListener(this);
linyuh183cb712017-12-27 17:02:37 -0800406 floatingActionButtonController = new FloatingActionButtonController(this, floatingActionButton);
Eric Erfanianccca3152017-02-22 16:32:36 -0800407
408 ImageButton optionsMenuButton =
linyuh183cb712017-12-27 17:02:37 -0800409 searchEditTextLayout.findViewById(R.id.dialtacts_options_menu_button);
Eric Erfanianccca3152017-02-22 16:32:36 -0800410 optionsMenuButton.setOnClickListener(this);
linyuh183cb712017-12-27 17:02:37 -0800411 overflowMenu = buildOptionsMenu(optionsMenuButton);
412 optionsMenuButton.setOnTouchListener(overflowMenu.getDragToOpenListener());
Eric Erfanianccca3152017-02-22 16:32:36 -0800413
414 // Add the favorites fragment but only if savedInstanceState is null. Otherwise the
415 // fragment manager is responsible for recreating it.
416 if (savedInstanceState == null) {
417 getFragmentManager()
418 .beginTransaction()
419 .add(R.id.dialtacts_frame, new ListsFragment(), TAG_FAVORITES_FRAGMENT)
420 .commit();
421 } else {
linyuh183cb712017-12-27 17:02:37 -0800422 searchQuery = savedInstanceState.getString(KEY_SEARCH_QUERY);
423 dialpadQuery = savedInstanceState.getString(KEY_DIALPAD_QUERY);
424 inRegularSearch = savedInstanceState.getBoolean(KEY_IN_REGULAR_SEARCH_UI);
425 inDialpadSearch = savedInstanceState.getBoolean(KEY_IN_DIALPAD_SEARCH_UI);
426 inNewSearch = savedInstanceState.getBoolean(KEY_IN_NEW_SEARCH_UI);
427 firstLaunch = savedInstanceState.getBoolean(KEY_FIRST_LAUNCH);
linyuhc9b35812017-12-28 09:58:34 -0800428 savedLanguageCode = savedInstanceState.getString(KEY_SAVED_LANGUAGE_CODE);
linyuh183cb712017-12-27 17:02:37 -0800429 wasConfigurationChange = savedInstanceState.getBoolean(KEY_WAS_CONFIGURATION_CHANGE);
430 isDialpadShown = savedInstanceState.getBoolean(KEY_IS_DIALPAD_SHOWN);
431 floatingActionButtonController.setVisible(savedInstanceState.getBoolean(KEY_FAB_VISIBLE));
432 actionBarController.restoreInstanceState(savedInstanceState);
Eric Erfanianccca3152017-02-22 16:32:36 -0800433 }
434
435 final boolean isLayoutRtl = ViewUtil.isRtl();
linyuh183cb712017-12-27 17:02:37 -0800436 if (isLandscape) {
437 slideIn =
Eric Erfanianccca3152017-02-22 16:32:36 -0800438 AnimationUtils.loadAnimation(
439 this, isLayoutRtl ? R.anim.dialpad_slide_in_left : R.anim.dialpad_slide_in_right);
linyuh183cb712017-12-27 17:02:37 -0800440 slideOut =
Eric Erfanianccca3152017-02-22 16:32:36 -0800441 AnimationUtils.loadAnimation(
442 this, isLayoutRtl ? R.anim.dialpad_slide_out_left : R.anim.dialpad_slide_out_right);
443 } else {
linyuh183cb712017-12-27 17:02:37 -0800444 slideIn = AnimationUtils.loadAnimation(this, R.anim.dialpad_slide_in_bottom);
445 slideOut = AnimationUtils.loadAnimation(this, R.anim.dialpad_slide_out_bottom);
Eric Erfanianccca3152017-02-22 16:32:36 -0800446 }
447
linyuh183cb712017-12-27 17:02:37 -0800448 slideIn.setInterpolator(AnimUtils.EASE_IN);
449 slideOut.setInterpolator(AnimUtils.EASE_OUT);
Eric Erfanianccca3152017-02-22 16:32:36 -0800450
linyuh183cb712017-12-27 17:02:37 -0800451 slideIn.setAnimationListener(slideInListener);
452 slideOut.setAnimationListener(slideOutListener);
Eric Erfanianccca3152017-02-22 16:32:36 -0800453
linyuh183cb712017-12-27 17:02:37 -0800454 parentLayout = (CoordinatorLayout) findViewById(R.id.dialtacts_mainlayout);
455 parentLayout.setOnDragListener(new LayoutOnDragListener());
Eric Erfanianfc37b022017-03-21 10:11:17 -0700456 ViewUtil.doOnGlobalLayout(
457 floatingActionButton,
458 view -> {
linyuh183cb712017-12-27 17:02:37 -0800459 int screenWidth = parentLayout.getWidth();
460 floatingActionButtonController.setScreenWidth(screenWidth);
461 floatingActionButtonController.align(getFabAlignment(), false /* animate */);
Eric Erfanianfc37b022017-03-21 10:11:17 -0700462 });
Eric Erfanianccca3152017-02-22 16:32:36 -0800463
464 Trace.endSection();
465
466 Trace.beginSection(TAG + " initialize smart dialing");
linyuh183cb712017-12-27 17:02:37 -0800467 dialerDatabaseHelper = Database.get(this).getDatabaseHelper(this);
Eric Erfanianccca3152017-02-22 16:32:36 -0800468 SmartDialPrefix.initializeNanpSettings(this);
469 Trace.endSection();
470
Eric Erfanianccca3152017-02-22 16:32:36 -0800471 Trace.endSection();
calderwoodra71eae262017-11-30 13:19:42 -0800472
calderwoodra58d0b2a2018-03-21 16:57:10 -0700473 updateSearchFragmentPosition();
Eric Erfanianccca3152017-02-22 16:32:36 -0800474 }
475
476 @NonNull
477 private ActionBar getActionBarSafely() {
478 return Assert.isNotNull(getSupportActionBar());
479 }
480
481 @Override
482 protected void onResume() {
zachhcc2cde02017-08-31 12:34:18 -0700483 LogUtil.enterBlock("DialtactsActivity.onResume");
Eric Erfanianccca3152017-02-22 16:32:36 -0800484 Trace.beginSection(TAG + " onResume");
485 super.onResume();
486
Eric Erfanian2ca43182017-08-31 06:57:16 -0700487 // Some calls may not be recorded (eg. from quick contact),
488 // so we should restart recording after these calls. (Recorded call is stopped)
489 PostCall.restartPerformanceRecordingIfARecentCallExist(this);
490 if (!PerformanceReport.isRecording()) {
491 PerformanceReport.startRecording();
492 }
493
linyuh183cb712017-12-27 17:02:37 -0800494 stateSaved = false;
495 if (firstLaunch) {
zachhcc2cde02017-08-31 12:34:18 -0700496 LogUtil.i("DialtactsActivity.onResume", "mFirstLaunch true, displaying fragment");
Eric Erfanianccca3152017-02-22 16:32:36 -0800497 displayFragment(getIntent());
linyuh183cb712017-12-27 17:02:37 -0800498 } else if (!phoneIsInUse() && inCallDialpadUp) {
zachhcc2cde02017-08-31 12:34:18 -0700499 LogUtil.i("DialtactsActivity.onResume", "phone not in use, hiding dialpad fragment");
Eric Erfanianccca3152017-02-22 16:32:36 -0800500 hideDialpadFragment(false, true);
linyuh183cb712017-12-27 17:02:37 -0800501 inCallDialpadUp = false;
502 } else if (isDialpadShown) {
zachhcc2cde02017-08-31 12:34:18 -0700503 LogUtil.i("DialtactsActivity.onResume", "showing dialpad on resume");
Eric Erfanianccca3152017-02-22 16:32:36 -0800504 showDialpadFragment(false);
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700505 } else {
linyuh183cb712017-12-27 17:02:37 -0800506 PostCall.promptUserForMessageIfNecessary(this, parentLayout);
Eric Erfanianccca3152017-02-22 16:32:36 -0800507 }
508
twyenbd73e9d2017-09-26 16:06:32 -0700509 // On M the fragment manager does not restore the hidden state of a fragment from
510 // savedInstanceState so it must be hidden again.
linyuh183cb712017-12-27 17:02:37 -0800511 if (!isDialpadShown && dialpadFragment != null && !dialpadFragment.isHidden()) {
twyenbd73e9d2017-09-26 16:06:32 -0700512 LogUtil.i(
513 "DialtactsActivity.onResume", "mDialpadFragment attached but not hidden, forcing hide");
linyuh183cb712017-12-27 17:02:37 -0800514 getFragmentManager().beginTransaction().hide(dialpadFragment).commit();
twyenbd73e9d2017-09-26 16:06:32 -0700515 }
516
Eric Erfanianccca3152017-02-22 16:32:36 -0800517 // If there was a voice query result returned in the {@link #onActivityResult} callback, it
518 // will have been stashed in mVoiceSearchQuery since the search results fragment cannot be
519 // shown until onResume has completed. Active the search UI and set the search term now.
linyuh183cb712017-12-27 17:02:37 -0800520 if (!TextUtils.isEmpty(voiceSearchQuery)) {
521 actionBarController.onSearchBoxTapped();
522 searchView.setText(voiceSearchQuery);
523 voiceSearchQuery = null;
Eric Erfanianccca3152017-02-22 16:32:36 -0800524 }
525
linyuh183cb712017-12-27 17:02:37 -0800526 if (isRestarting) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800527 // This is only called when the activity goes from resumed -> paused -> resumed, so it
528 // will not cause an extra view to be sent out on rotation
linyuh183cb712017-12-27 17:02:37 -0800529 if (isDialpadShown) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800530 Logger.get(this).logScreenView(ScreenEvent.Type.DIALPAD, this);
531 }
linyuh183cb712017-12-27 17:02:37 -0800532 isRestarting = false;
Eric Erfanianccca3152017-02-22 16:32:36 -0800533 }
534
535 prepareVoiceSearchButton();
linyuhc9b35812017-12-28 09:58:34 -0800536
537 // Start the thread that updates the smart dial database if
538 // (1) the activity is not recreated with a new configuration, or
539 // (2) the activity is recreated with a new configuration but the change is a language change.
540 boolean isLanguageChanged =
541 !CompatUtils.getLocale(this).getISO3Language().equals(savedLanguageCode);
542 if (!wasConfigurationChange || isLanguageChanged) {
543 dialerDatabaseHelper.startSmartDialUpdateThread(/* forceUpdate = */ isLanguageChanged);
Eric Erfanianccca3152017-02-22 16:32:36 -0800544 }
linyuhc9b35812017-12-28 09:58:34 -0800545
linyuh183cb712017-12-27 17:02:37 -0800546 if (isDialpadShown) {
547 floatingActionButtonController.scaleOut();
zachh6050ec22017-09-21 16:02:35 -0700548 } else {
linyuh183cb712017-12-27 17:02:37 -0800549 floatingActionButtonController.align(getFabAlignment(), false /* animate */);
zachh6050ec22017-09-21 16:02:35 -0700550 }
Eric Erfanianccca3152017-02-22 16:32:36 -0800551
linyuh183cb712017-12-27 17:02:37 -0800552 if (firstLaunch) {
Eric Erfanian10b34a52017-05-04 08:23:17 -0700553 // Only process the Intent the first time onResume() is called after receiving it
554 if (Calls.CONTENT_TYPE.equals(getIntent().getType())) {
555 // Externally specified extras take precedence to EXTRA_SHOW_TAB, which is only
556 // used internally.
557 final Bundle extras = getIntent().getExtras();
558 if (extras != null && extras.getInt(Calls.EXTRA_CALL_TYPE_FILTER) == Calls.VOICEMAIL_TYPE) {
linyuh183cb712017-12-27 17:02:37 -0800559 listsFragment.showTab(DialtactsPagerAdapter.TAB_INDEX_VOICEMAIL);
Eric Erfanian10b34a52017-05-04 08:23:17 -0700560 Logger.get(this).logImpression(DialerImpression.Type.VVM_NOTIFICATION_CLICKED);
561 } else {
linyuh183cb712017-12-27 17:02:37 -0800562 listsFragment.showTab(DialtactsPagerAdapter.TAB_INDEX_HISTORY);
Eric Erfanian10b34a52017-05-04 08:23:17 -0700563 }
564 } else if (getIntent().hasExtra(EXTRA_SHOW_TAB)) {
565 int index =
566 getIntent().getIntExtra(EXTRA_SHOW_TAB, DialtactsPagerAdapter.TAB_INDEX_SPEED_DIAL);
linyuh183cb712017-12-27 17:02:37 -0800567 if (index < listsFragment.getTabCount()) {
Eric Erfanian10b34a52017-05-04 08:23:17 -0700568 // Hide dialpad since this is an explicit intent to show a specific tab, which is coming
569 // from missed call or voicemail notification.
570 hideDialpadFragment(false, false);
571 exitSearchUi();
linyuh183cb712017-12-27 17:02:37 -0800572 listsFragment.showTab(index);
Eric Erfanian10b34a52017-05-04 08:23:17 -0700573 }
Eric Erfanianccca3152017-02-22 16:32:36 -0800574 }
Eric Erfanian10b34a52017-05-04 08:23:17 -0700575
576 if (getIntent().getBooleanExtra(EXTRA_CLEAR_NEW_VOICEMAILS, false)) {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700577 LogUtil.i("DialtactsActivity.onResume", "clearing all new voicemails");
578 CallLogNotificationsService.markAllNewVoicemailsAsOld(this);
Eric Erfanianccca3152017-02-22 16:32:36 -0800579 }
weijiaxu650e7cc2017-10-31 12:38:54 -0700580 // add 1 sec delay to get memory snapshot so that dialer wont react slowly on resume.
581 ThreadUtil.postDelayedOnUiThread(
weijiaxuc950a9b2017-11-06 16:39:04 -0800582 () ->
zachh7a96dc72018-02-20 22:16:03 -0800583 MetricsComponent.get(this)
584 .metrics()
585 .recordMemory(Metrics.DIALTACTS_ON_RESUME_MEMORY_EVENT_NAME),
weijiaxu650e7cc2017-10-31 12:38:54 -0700586 1000);
Eric Erfanianccca3152017-02-22 16:32:36 -0800587 }
588
linyuh183cb712017-12-27 17:02:37 -0800589 firstLaunch = false;
Eric Erfanianccca3152017-02-22 16:32:36 -0800590
591 setSearchBoxHint();
Eric Erfanian10b34a52017-05-04 08:23:17 -0700592 timeTabSelected = SystemClock.elapsedRealtime();
Eric Erfanianccca3152017-02-22 16:32:36 -0800593
Eric Erfanianccca3152017-02-22 16:32:36 -0800594 Trace.endSection();
595 }
596
597 @Override
598 protected void onRestart() {
599 super.onRestart();
linyuh183cb712017-12-27 17:02:37 -0800600 isRestarting = true;
Eric Erfanianccca3152017-02-22 16:32:36 -0800601 }
602
603 @Override
604 protected void onPause() {
linyuh183cb712017-12-27 17:02:37 -0800605 if (clearSearchOnPause) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800606 hideDialpadAndSearchUi();
linyuh183cb712017-12-27 17:02:37 -0800607 clearSearchOnPause = false;
Eric Erfanianccca3152017-02-22 16:32:36 -0800608 }
linyuh183cb712017-12-27 17:02:37 -0800609 if (slideOut.hasStarted() && !slideOut.hasEnded()) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800610 commitDialpadFragmentHide();
611 }
612 super.onPause();
613 }
614
615 @Override
Eric Erfaniand8046e52017-04-06 09:41:50 -0700616 protected void onStop() {
617 super.onStop();
618 boolean timeoutElapsed =
619 SystemClock.elapsedRealtime() - timeTabSelected >= HISTORY_TAB_SEEN_TIMEOUT;
620 boolean isOnHistoryTab =
linyuh183cb712017-12-27 17:02:37 -0800621 listsFragment.getCurrentTabIndex() == DialtactsPagerAdapter.TAB_INDEX_HISTORY;
Eric Erfanian10b34a52017-05-04 08:23:17 -0700622 if (isOnHistoryTab
623 && timeoutElapsed
624 && !isChangingConfigurations()
625 && !getSystemService(KeyguardManager.class).isKeyguardLocked()) {
linyuh183cb712017-12-27 17:02:37 -0800626 listsFragment.markMissedCallsAsReadAndRemoveNotifications();
Eric Erfaniand8046e52017-04-06 09:41:50 -0700627 }
linyuh168d09c2017-10-26 17:04:30 -0700628 StorageComponent.get(this)
629 .unencryptedSharedPrefs()
Eric Erfanian2ca43182017-08-31 06:57:16 -0700630 .edit()
linyuh183cb712017-12-27 17:02:37 -0800631 .putInt(KEY_LAST_TAB, listsFragment.getCurrentTabIndex())
Eric Erfanian2ca43182017-08-31 06:57:16 -0700632 .apply();
Eric Erfaniand8046e52017-04-06 09:41:50 -0700633 }
634
635 @Override
Eric Erfanianccca3152017-02-22 16:32:36 -0800636 protected void onSaveInstanceState(Bundle outState) {
zachh6050ec22017-09-21 16:02:35 -0700637 LogUtil.enterBlock("DialtactsActivity.onSaveInstanceState");
Eric Erfanianccca3152017-02-22 16:32:36 -0800638 super.onSaveInstanceState(outState);
linyuh183cb712017-12-27 17:02:37 -0800639 outState.putString(KEY_SEARCH_QUERY, searchQuery);
640 outState.putString(KEY_DIALPAD_QUERY, dialpadQuery);
linyuhc9b35812017-12-28 09:58:34 -0800641 outState.putString(KEY_SAVED_LANGUAGE_CODE, CompatUtils.getLocale(this).getISO3Language());
linyuh183cb712017-12-27 17:02:37 -0800642 outState.putBoolean(KEY_IN_REGULAR_SEARCH_UI, inRegularSearch);
643 outState.putBoolean(KEY_IN_DIALPAD_SEARCH_UI, inDialpadSearch);
644 outState.putBoolean(KEY_IN_NEW_SEARCH_UI, inNewSearch);
645 outState.putBoolean(KEY_FIRST_LAUNCH, firstLaunch);
646 outState.putBoolean(KEY_IS_DIALPAD_SHOWN, isDialpadShown);
647 outState.putBoolean(KEY_FAB_VISIBLE, floatingActionButtonController.isVisible());
Eric Erfanianccca3152017-02-22 16:32:36 -0800648 outState.putBoolean(KEY_WAS_CONFIGURATION_CHANGE, isChangingConfigurations());
linyuh183cb712017-12-27 17:02:37 -0800649 actionBarController.saveInstanceState(outState);
650 stateSaved = true;
Eric Erfanianccca3152017-02-22 16:32:36 -0800651 }
652
653 @Override
654 public void onAttachFragment(final Fragment fragment) {
zachhcc2cde02017-08-31 12:34:18 -0700655 LogUtil.i("DialtactsActivity.onAttachFragment", "fragment: %s", fragment);
Eric Erfanianccca3152017-02-22 16:32:36 -0800656 if (fragment instanceof DialpadFragment) {
linyuh183cb712017-12-27 17:02:37 -0800657 dialpadFragment = (DialpadFragment) fragment;
Eric Erfanianccca3152017-02-22 16:32:36 -0800658 } else if (fragment instanceof ListsFragment) {
linyuh183cb712017-12-27 17:02:37 -0800659 listsFragment = (ListsFragment) fragment;
660 listsFragment.addOnPageChangeListener(this);
Eric Erfanian2ca43182017-08-31 06:57:16 -0700661 } else if (fragment instanceof NewSearchFragment) {
linyuh183cb712017-12-27 17:02:37 -0800662 newSearchFragment = (NewSearchFragment) fragment;
Eric Erfanian2ca43182017-08-31 06:57:16 -0700663 updateSearchFragmentPosition();
Eric Erfanianccca3152017-02-22 16:32:36 -0800664 }
Eric Erfanianccca3152017-02-22 16:32:36 -0800665 }
666
667 protected void handleMenuSettings() {
668 final Intent intent = new Intent(this, DialerSettingsActivity.class);
669 startActivity(intent);
670 }
671
Android Dialer3f891692017-10-25 11:43:41 -0700672 public boolean isListsFragmentVisible() {
linyuh183cb712017-12-27 17:02:37 -0800673 return listsFragment.getUserVisibleHint();
Android Dialer3f4a3192017-09-20 17:59:44 -0700674 }
675
Eric Erfanianccca3152017-02-22 16:32:36 -0800676 @Override
677 public void onClick(View view) {
678 int resId = view.getId();
679 if (resId == R.id.floating_action_button) {
linyuh183cb712017-12-27 17:02:37 -0800680 if (!isDialpadShown) {
yueg49df7f62017-09-12 15:15:27 -0700681 LogUtil.i(
682 "DialtactsActivity.onClick", "floating action button clicked, going to show dialpad");
Eric Erfanian2ca43182017-08-31 06:57:16 -0700683 PerformanceReport.recordClick(UiAction.Type.OPEN_DIALPAD);
linyuh183cb712017-12-27 17:02:37 -0800684 inCallDialpadUp = false;
Eric Erfanianccca3152017-02-22 16:32:36 -0800685 showDialpadFragment(true);
Eric Erfanian90508232017-03-24 09:31:16 -0700686 PostCall.closePrompt();
yueg49df7f62017-09-12 15:15:27 -0700687 } else {
688 LogUtil.i(
689 "DialtactsActivity.onClick",
690 "floating action button clicked, but dialpad is already showing");
Eric Erfanianccca3152017-02-22 16:32:36 -0800691 }
692 } else if (resId == R.id.voice_search_button) {
693 try {
694 startActivityForResult(
695 new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH),
Eric Erfanian2ca43182017-08-31 06:57:16 -0700696 ActivityRequestCodes.DIALTACTS_VOICE_SEARCH);
Eric Erfanianccca3152017-02-22 16:32:36 -0800697 } catch (ActivityNotFoundException e) {
698 Toast.makeText(
699 DialtactsActivity.this, R.string.voice_search_not_available, Toast.LENGTH_SHORT)
700 .show();
701 }
702 } else if (resId == R.id.dialtacts_options_menu_button) {
linyuh183cb712017-12-27 17:02:37 -0800703 overflowMenu.show();
Eric Erfanianccca3152017-02-22 16:32:36 -0800704 } else {
705 Assert.fail("Unexpected onClick event from " + view);
706 }
707 }
708
709 @Override
710 public boolean onMenuItemClick(MenuItem item) {
711 if (!isSafeToCommitTransactions()) {
712 return true;
713 }
714
715 int resId = item.getItemId();
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700716 if (resId == R.id.menu_history) {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700717 PerformanceReport.recordClick(UiAction.Type.OPEN_CALL_HISTORY);
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700718 final Intent intent = new Intent(this, CallLogActivity.class);
719 startActivity(intent);
Eric Erfanianccca3152017-02-22 16:32:36 -0800720 } else if (resId == R.id.menu_clear_frequents) {
721 ClearFrequentsDialog.show(getFragmentManager());
722 Logger.get(this).logScreenView(ScreenEvent.Type.CLEAR_FREQUENTS, this);
723 return true;
724 } else if (resId == R.id.menu_call_settings) {
725 handleMenuSettings();
726 Logger.get(this).logScreenView(ScreenEvent.Type.SETTINGS, this);
727 return true;
728 }
729 return false;
730 }
731
732 @Override
733 protected void onActivityResult(int requestCode, int resultCode, Intent data) {
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700734 LogUtil.i(
735 "DialtactsActivity.onActivityResult",
736 "requestCode:%d, resultCode:%d",
737 requestCode,
738 resultCode);
Eric Erfanian2ca43182017-08-31 06:57:16 -0700739 if (requestCode == ActivityRequestCodes.DIALTACTS_VOICE_SEARCH) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800740 if (resultCode == RESULT_OK) {
741 final ArrayList<String> matches =
742 data.getStringArrayListExtra(RecognizerIntent.EXTRA_RESULTS);
743 if (matches.size() > 0) {
linyuh183cb712017-12-27 17:02:37 -0800744 voiceSearchQuery = matches.get(0);
Eric Erfanianccca3152017-02-22 16:32:36 -0800745 } else {
746 LogUtil.i("DialtactsActivity.onActivityResult", "voice search - nothing heard");
747 }
748 } else {
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700749 LogUtil.e("DialtactsActivity.onActivityResult", "voice search failed");
Eric Erfanianccca3152017-02-22 16:32:36 -0800750 }
Eric Erfanian2ca43182017-08-31 06:57:16 -0700751 } else if (requestCode == ActivityRequestCodes.DIALTACTS_CALL_COMPOSER) {
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700752 if (resultCode == RESULT_FIRST_USER) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800753 LogUtil.i(
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700754 "DialtactsActivity.onActivityResult", "returned from call composer, error occurred");
Eric Erfanianccca3152017-02-22 16:32:36 -0800755 String message =
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700756 getString(
757 R.string.call_composer_connection_failed,
758 data.getStringExtra(CallComposerActivity.KEY_CONTACT_NAME));
linyuh183cb712017-12-27 17:02:37 -0800759 Snackbar.make(parentLayout, message, Snackbar.LENGTH_LONG).show();
Eric Erfanianccca3152017-02-22 16:32:36 -0800760 } else {
761 LogUtil.i("DialtactsActivity.onActivityResult", "returned from call composer, no error");
762 }
Eric Erfanian2ca43182017-08-31 06:57:16 -0700763 } else if (requestCode == ActivityRequestCodes.DIALTACTS_CALL_DETAILS) {
764 if (resultCode == RESULT_OK
765 && data != null
linyuh66428472018-03-14 15:21:01 -0700766 && data.getBooleanExtra(OldCallDetailsActivity.EXTRA_HAS_ENRICHED_CALL_DATA, false)) {
767 String number = data.getStringExtra(OldCallDetailsActivity.EXTRA_PHONE_NUMBER);
Eric Erfanian2ca43182017-08-31 06:57:16 -0700768 int snackbarDurationMillis = 5_000;
linyuh183cb712017-12-27 17:02:37 -0800769 Snackbar.make(parentLayout, getString(R.string.ec_data_deleted), snackbarDurationMillis)
Eric Erfanian2ca43182017-08-31 06:57:16 -0700770 .setAction(
771 R.string.view_conversation,
772 v -> startActivity(IntentProvider.getSendSmsIntentProvider(number).getIntent(this)))
773 .setActionTextColor(getResources().getColor(R.color.dialer_snackbar_action_text_color))
774 .show();
775 }
roldenburg19c6c252017-12-13 15:58:01 -0800776 } else if (requestCode == ActivityRequestCodes.DIALTACTS_DUO) {
777 // We just returned from starting Duo for a task. Reload our reachability data since it
778 // may have changed after a user finished activating Duo.
779 DuoComponent.get(this).getDuo().reloadReachability(this);
Eric Erfanianccca3152017-02-22 16:32:36 -0800780 }
781 super.onActivityResult(requestCode, resultCode, data);
782 }
783
784 /**
785 * Update the number of unread voicemails (potentially other tabs) displayed next to the tab icon.
786 */
787 public void updateTabUnreadCounts() {
linyuh183cb712017-12-27 17:02:37 -0800788 listsFragment.updateTabUnreadCounts();
Eric Erfanianccca3152017-02-22 16:32:36 -0800789 }
790
791 /**
792 * Initiates a fragment transaction to show the dialpad fragment. Animations and other visual
793 * updates are handled by a callback which is invoked after the dialpad fragment is shown.
794 *
795 * @see #onDialpadShown
796 */
797 private void showDialpadFragment(boolean animate) {
zachhcc2cde02017-08-31 12:34:18 -0700798 LogUtil.i("DialtactActivity.showDialpadFragment", "animate: %b", animate);
linyuh183cb712017-12-27 17:02:37 -0800799 if (isDialpadShown) {
zachhcc2cde02017-08-31 12:34:18 -0700800 LogUtil.i("DialtactsActivity.showDialpadFragment", "dialpad already shown");
801 return;
802 }
linyuh183cb712017-12-27 17:02:37 -0800803 if (stateSaved) {
zachhcc2cde02017-08-31 12:34:18 -0700804 LogUtil.i("DialtactsActivity.showDialpadFragment", "state already saved");
Eric Erfanianccca3152017-02-22 16:32:36 -0800805 return;
806 }
linyuh183cb712017-12-27 17:02:37 -0800807 isDialpadShown = true;
Eric Erfanianccca3152017-02-22 16:32:36 -0800808
linyuh183cb712017-12-27 17:02:37 -0800809 listsFragment.setUserVisibleHint(false);
Eric Erfanianccca3152017-02-22 16:32:36 -0800810
811 final FragmentTransaction ft = getFragmentManager().beginTransaction();
linyuh183cb712017-12-27 17:02:37 -0800812 if (dialpadFragment == null) {
813 dialpadFragment = new DialpadFragment();
814 ft.add(R.id.dialtacts_container, dialpadFragment, TAG_DIALPAD_FRAGMENT);
Eric Erfanianccca3152017-02-22 16:32:36 -0800815 } else {
linyuh183cb712017-12-27 17:02:37 -0800816 ft.show(dialpadFragment);
Eric Erfanianccca3152017-02-22 16:32:36 -0800817 }
818
linyuh183cb712017-12-27 17:02:37 -0800819 dialpadFragment.setAnimate(animate);
Eric Erfanianccca3152017-02-22 16:32:36 -0800820 Logger.get(this).logScreenView(ScreenEvent.Type.DIALPAD, this);
821 ft.commit();
822
823 if (animate) {
linyuh183cb712017-12-27 17:02:37 -0800824 floatingActionButtonController.scaleOut();
Eric Erfanian2ca43182017-08-31 06:57:16 -0700825 maybeEnterSearchUi();
Eric Erfanianccca3152017-02-22 16:32:36 -0800826 } else {
linyuh183cb712017-12-27 17:02:37 -0800827 floatingActionButtonController.scaleOut();
Eric Erfanianccca3152017-02-22 16:32:36 -0800828 maybeEnterSearchUi();
829 }
linyuh183cb712017-12-27 17:02:37 -0800830 actionBarController.onDialpadUp();
Eric Erfanianccca3152017-02-22 16:32:36 -0800831
linyuh183cb712017-12-27 17:02:37 -0800832 Assert.isNotNull(listsFragment.getView()).animate().alpha(0).withLayer();
Eric Erfanianccca3152017-02-22 16:32:36 -0800833
Eric Erfanian2ca43182017-08-31 06:57:16 -0700834 // adjust the title, so the user will know where we're at when the activity start/resumes.
Eric Erfanianccca3152017-02-22 16:32:36 -0800835 setTitle(R.string.launcherDialpadActivityLabel);
836 }
837
Eric Erfanian2ca43182017-08-31 06:57:16 -0700838 @Override
839 public void getLastOutgoingCall(LastOutgoingCallCallback callback) {
calderwoodra97d0b5e2018-01-26 12:11:26 -0800840 DialerExecutorComponent.get(this)
841 .dialerExecutorFactory()
842 .createUiTaskBuilder(
843 getFragmentManager(), "Query last phone number", Calls::getLastOutgoingCall)
844 .onSuccess(output -> callback.lastOutgoingCall(output))
845 .build()
846 .executeParallel(this);
Eric Erfanian2ca43182017-08-31 06:57:16 -0700847 }
848
Eric Erfanianccca3152017-02-22 16:32:36 -0800849 /** Callback from child DialpadFragment when the dialpad is shown. */
Eric Erfanian2ca43182017-08-31 06:57:16 -0700850 @Override
Eric Erfanianccca3152017-02-22 16:32:36 -0800851 public void onDialpadShown() {
zachhcc2cde02017-08-31 12:34:18 -0700852 LogUtil.enterBlock("DialtactsActivity.onDialpadShown");
linyuh183cb712017-12-27 17:02:37 -0800853 Assert.isNotNull(dialpadFragment);
854 if (dialpadFragment.getAnimate()) {
855 Assert.isNotNull(dialpadFragment.getView()).startAnimation(slideIn);
Eric Erfanianccca3152017-02-22 16:32:36 -0800856 } else {
linyuh183cb712017-12-27 17:02:37 -0800857 dialpadFragment.setYFraction(0);
Eric Erfanianccca3152017-02-22 16:32:36 -0800858 }
859
860 updateSearchFragmentPosition();
861 }
862
calderwoodra50715262017-09-08 09:20:08 -0700863 @Override
864 public void onCallPlacedFromDialpad() {
linyuh183cb712017-12-27 17:02:37 -0800865 clearSearchOnPause = true;
calderwoodra50715262017-09-08 09:20:08 -0700866 }
867
Android Dialer3f4a3192017-09-20 17:59:44 -0700868 @Override
Android Dialere8d77522017-10-16 11:33:58 -0700869 public void onContactsListScrolled(boolean isDragging) {
Android Dialer3f4a3192017-09-20 17:59:44 -0700870 // intentionally empty.
871 }
872
Eric Erfanianccca3152017-02-22 16:32:36 -0800873 /**
874 * Initiates animations and other visual updates to hide the dialpad. The fragment is hidden in a
875 * callback after the hide animation ends.
876 *
877 * @see #commitDialpadFragmentHide
878 */
calderwoodra50715262017-09-08 09:20:08 -0700879 private void hideDialpadFragment(boolean animate, boolean clearDialpad) {
zachhcc2cde02017-08-31 12:34:18 -0700880 LogUtil.enterBlock("DialtactsActivity.hideDialpadFragment");
linyuh183cb712017-12-27 17:02:37 -0800881 if (dialpadFragment == null || dialpadFragment.getView() == null) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800882 return;
883 }
884 if (clearDialpad) {
885 // Temporarily disable accessibility when we clear the dialpad, since it should be
886 // invisible and should not announce anything.
linyuh183cb712017-12-27 17:02:37 -0800887 dialpadFragment
Eric Erfanianccca3152017-02-22 16:32:36 -0800888 .getDigitsWidget()
889 .setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO);
linyuh183cb712017-12-27 17:02:37 -0800890 dialpadFragment.clearDialpad();
891 dialpadFragment
Eric Erfanianccca3152017-02-22 16:32:36 -0800892 .getDigitsWidget()
893 .setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_AUTO);
894 }
linyuh183cb712017-12-27 17:02:37 -0800895 if (!isDialpadShown) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800896 return;
897 }
linyuh183cb712017-12-27 17:02:37 -0800898 isDialpadShown = false;
899 dialpadFragment.setAnimate(animate);
900 listsFragment.setUserVisibleHint(true);
901 listsFragment.sendScreenViewForCurrentPosition();
Eric Erfanianccca3152017-02-22 16:32:36 -0800902
903 updateSearchFragmentPosition();
904
linyuh183cb712017-12-27 17:02:37 -0800905 floatingActionButtonController.align(getFabAlignment(), animate);
Eric Erfanianccca3152017-02-22 16:32:36 -0800906 if (animate) {
linyuh183cb712017-12-27 17:02:37 -0800907 dialpadFragment.getView().startAnimation(slideOut);
Eric Erfanianccca3152017-02-22 16:32:36 -0800908 } else {
909 commitDialpadFragmentHide();
910 }
911
linyuh183cb712017-12-27 17:02:37 -0800912 actionBarController.onDialpadDown();
Eric Erfanianccca3152017-02-22 16:32:36 -0800913
Eric Erfanian2ca43182017-08-31 06:57:16 -0700914 // reset the title to normal.
Eric Erfanianccca3152017-02-22 16:32:36 -0800915 setTitle(R.string.launcherActivityLabel);
916 }
917
918 /** Finishes hiding the dialpad fragment after any animations are completed. */
919 private void commitDialpadFragmentHide() {
linyuh183cb712017-12-27 17:02:37 -0800920 if (!stateSaved && dialpadFragment != null && !dialpadFragment.isHidden() && !isDestroyed()) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800921 final FragmentTransaction ft = getFragmentManager().beginTransaction();
linyuh183cb712017-12-27 17:02:37 -0800922 ft.hide(dialpadFragment);
Eric Erfanianccca3152017-02-22 16:32:36 -0800923 ft.commit();
924 }
linyuh183cb712017-12-27 17:02:37 -0800925 floatingActionButtonController.scaleIn();
Eric Erfanianccca3152017-02-22 16:32:36 -0800926 }
927
928 private void updateSearchFragmentPosition() {
calderwoodra58d0b2a2018-03-21 16:57:10 -0700929 if (newSearchFragment != null) {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700930 int animationDuration = getResources().getInteger(R.integer.dialpad_slide_in_duration);
931 int actionbarHeight = getResources().getDimensionPixelSize(R.dimen.action_bar_height_large);
932 int shadowHeight = getResources().getDrawable(R.drawable.search_shadow).getIntrinsicHeight();
933 int start = isDialpadShown() ? actionbarHeight - shadowHeight : 0;
934 int end = isDialpadShown() ? 0 : actionbarHeight - shadowHeight;
linyuh183cb712017-12-27 17:02:37 -0800935 newSearchFragment.animatePosition(start, end, animationDuration);
Eric Erfanianccca3152017-02-22 16:32:36 -0800936 }
937 }
938
939 @Override
940 public boolean isInSearchUi() {
linyuh183cb712017-12-27 17:02:37 -0800941 return inDialpadSearch || inRegularSearch || inNewSearch;
Eric Erfanianccca3152017-02-22 16:32:36 -0800942 }
943
944 @Override
945 public boolean hasSearchQuery() {
linyuh183cb712017-12-27 17:02:37 -0800946 return !TextUtils.isEmpty(searchQuery);
Eric Erfanianccca3152017-02-22 16:32:36 -0800947 }
948
Eric Erfanianccca3152017-02-22 16:32:36 -0800949 private void setNotInSearchUi() {
linyuh183cb712017-12-27 17:02:37 -0800950 inDialpadSearch = false;
951 inRegularSearch = false;
952 inNewSearch = false;
Eric Erfanianccca3152017-02-22 16:32:36 -0800953 }
954
955 private void hideDialpadAndSearchUi() {
linyuh183cb712017-12-27 17:02:37 -0800956 if (isDialpadShown) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800957 hideDialpadFragment(false, true);
Eric Erfanianccca3152017-02-22 16:32:36 -0800958 }
calderwoodrae51ca382017-11-21 10:54:36 -0800959 exitSearchUi();
Eric Erfanianccca3152017-02-22 16:32:36 -0800960 }
961
962 private void prepareVoiceSearchButton() {
linyuh183cb712017-12-27 17:02:37 -0800963 searchEditTextLayout.setVoiceSearchEnabled(isVoiceSearchEnabled());
964 voiceSearchButton.setOnClickListener(this);
twyen891fe0f2017-10-17 14:59:15 -0700965 }
966
967 private boolean isVoiceSearchEnabled() {
linyuh183cb712017-12-27 17:02:37 -0800968 if (voiceSearchEnabledForTest.isPresent()) {
969 return voiceSearchEnabledForTest.get();
Eric Erfanianccca3152017-02-22 16:32:36 -0800970 }
twyen891fe0f2017-10-17 14:59:15 -0700971 return canIntentBeHandled(new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH));
Eric Erfanianccca3152017-02-22 16:32:36 -0800972 }
973
974 public boolean isNearbyPlacesSearchEnabled() {
975 return false;
976 }
977
978 protected int getSearchBoxHint() {
979 return R.string.dialer_hint_find_contact;
980 }
981
982 /** Sets the hint text for the contacts search box */
983 private void setSearchBoxHint() {
linyuh183cb712017-12-27 17:02:37 -0800984 ((TextView) searchEditTextLayout.findViewById(R.id.search_box_start_search))
Eric Erfanianccca3152017-02-22 16:32:36 -0800985 .setHint(getSearchBoxHint());
986 }
987
988 protected OptionsPopupMenu buildOptionsMenu(View invoker) {
989 final OptionsPopupMenu popupMenu = new OptionsPopupMenu(this, invoker);
990 popupMenu.inflate(R.menu.dialtacts_options);
991 popupMenu.setOnMenuItemClickListener(this);
992 return popupMenu;
993 }
994
995 @Override
996 public boolean onCreateOptionsMenu(Menu menu) {
linyuh183cb712017-12-27 17:02:37 -0800997 if (pendingSearchViewQuery != null) {
998 searchView.setText(pendingSearchViewQuery);
999 pendingSearchViewQuery = null;
Eric Erfanianccca3152017-02-22 16:32:36 -08001000 }
linyuh183cb712017-12-27 17:02:37 -08001001 if (actionBarController != null) {
1002 actionBarController.restoreActionBarOffset();
Eric Erfanianccca3152017-02-22 16:32:36 -08001003 }
1004 return false;
1005 }
1006
1007 /**
1008 * Returns true if the intent is due to hitting the green send key (hardware call button:
1009 * KEYCODE_CALL) while in a call.
1010 *
1011 * @param intent the intent that launched this activity
1012 * @return true if the intent is due to hitting the green send key while in a call
1013 */
1014 private boolean isSendKeyWhileInCall(Intent intent) {
1015 // If there is a call in progress and the user launched the dialer by hitting the call
1016 // button, go straight to the in-call screen.
1017 final boolean callKey = Intent.ACTION_CALL_BUTTON.equals(intent.getAction());
1018
1019 // When KEYCODE_CALL event is handled it dispatches an intent with the ACTION_CALL_BUTTON.
1020 // Besides of checking the intent action, we must check if the phone is really during a
1021 // call in order to decide whether to ignore the event or continue to display the activity.
1022 if (callKey && phoneIsInUse()) {
1023 TelecomUtil.showInCallScreen(this, false);
1024 return true;
1025 }
1026
1027 return false;
1028 }
1029
1030 /**
1031 * Sets the current tab based on the intent's request type
1032 *
1033 * @param intent Intent that contains information about which tab should be selected
1034 */
1035 private void displayFragment(Intent intent) {
1036 // If we got here by hitting send and we're in call forward along to the in-call activity
1037 if (isSendKeyWhileInCall(intent)) {
1038 finish();
1039 return;
1040 }
1041
zachhcc2cde02017-08-31 12:34:18 -07001042 boolean showDialpadChooser =
Eric Erfanianccca3152017-02-22 16:32:36 -08001043 !ACTION_SHOW_TAB.equals(intent.getAction())
1044 && phoneIsInUse()
1045 && !DialpadFragment.isAddCallMode(intent);
zachhcc2cde02017-08-31 12:34:18 -07001046 boolean isDialIntent = intent.getData() != null && isDialIntent(intent);
calderwoodra48cce922017-09-14 14:28:46 -07001047 boolean isAddCallIntent = DialpadFragment.isAddCallMode(intent);
1048 if (showDialpadChooser || isDialIntent || isAddCallIntent) {
zachhcc2cde02017-08-31 12:34:18 -07001049 LogUtil.i(
1050 "DialtactsActivity.displayFragment",
calderwoodra48cce922017-09-14 14:28:46 -07001051 "show dialpad fragment (showDialpadChooser: %b, isDialIntent: %b, isAddCallIntent: %b)",
zachhcc2cde02017-08-31 12:34:18 -07001052 showDialpadChooser,
calderwoodra48cce922017-09-14 14:28:46 -07001053 isDialIntent,
1054 isAddCallIntent);
Eric Erfanianccca3152017-02-22 16:32:36 -08001055 showDialpadFragment(false);
linyuh183cb712017-12-27 17:02:37 -08001056 dialpadFragment.setStartedFromNewIntent(true);
1057 if (showDialpadChooser && !dialpadFragment.isVisible()) {
1058 inCallDialpadUp = true;
Eric Erfanianccca3152017-02-22 16:32:36 -08001059 }
Eric Erfanian2ca43182017-08-31 06:57:16 -07001060 } else if (isLastTabEnabled) {
1061 @TabIndex
1062 int tabIndex =
linyuh168d09c2017-10-26 17:04:30 -07001063 StorageComponent.get(this)
1064 .unencryptedSharedPrefs()
Eric Erfanian2ca43182017-08-31 06:57:16 -07001065 .getInt(KEY_LAST_TAB, DialtactsPagerAdapter.TAB_INDEX_SPEED_DIAL);
1066 // If voicemail tab is saved and its availability changes, we still move to the voicemail tab
1067 // but it is quickly removed and shown the contacts tab.
linyuh183cb712017-12-27 17:02:37 -08001068 if (listsFragment != null) {
1069 listsFragment.showTab(tabIndex);
Eric Erfanian2ca43182017-08-31 06:57:16 -07001070 PerformanceReport.setStartingTabIndex(tabIndex);
1071 } else {
1072 PerformanceReport.setStartingTabIndex(DialtactsPagerAdapter.TAB_INDEX_SPEED_DIAL);
1073 }
Eric Erfanianccca3152017-02-22 16:32:36 -08001074 }
1075 }
1076
1077 @Override
1078 public void onNewIntent(Intent newIntent) {
zachhcc2cde02017-08-31 12:34:18 -07001079 LogUtil.enterBlock("DialtactsActivity.onNewIntent");
Eric Erfanianccca3152017-02-22 16:32:36 -08001080 setIntent(newIntent);
linyuh183cb712017-12-27 17:02:37 -08001081 firstLaunch = true;
Eric Erfanianccca3152017-02-22 16:32:36 -08001082
linyuh183cb712017-12-27 17:02:37 -08001083 stateSaved = false;
Eric Erfanianccca3152017-02-22 16:32:36 -08001084 displayFragment(newIntent);
1085
1086 invalidateOptionsMenu();
1087 }
1088
1089 /** Returns true if the given intent contains a phone number to populate the dialer with */
1090 private boolean isDialIntent(Intent intent) {
1091 final String action = intent.getAction();
1092 if (Intent.ACTION_DIAL.equals(action) || ACTION_TOUCH_DIALER.equals(action)) {
1093 return true;
1094 }
1095 if (Intent.ACTION_VIEW.equals(action)) {
1096 final Uri data = intent.getData();
1097 if (data != null && PhoneAccount.SCHEME_TEL.equals(data.getScheme())) {
1098 return true;
1099 }
1100 }
1101 return false;
1102 }
1103
1104 /** Shows the search fragment */
1105 private void enterSearchUi(boolean smartDialSearch, String query, boolean animate) {
zachhcc2cde02017-08-31 12:34:18 -07001106 LogUtil.i("DialtactsActivity.enterSearchUi", "smart dial: %b", smartDialSearch);
linyuh183cb712017-12-27 17:02:37 -08001107 if (stateSaved || getFragmentManager().isDestroyed()) {
Eric Erfanianccca3152017-02-22 16:32:36 -08001108 // Weird race condition where fragment is doing work after the activity is destroyed
Eric Erfanian938468d2017-10-24 14:05:52 -07001109 // due to talkback being on (a bug). Just return since we can't do any
Eric Erfanianccca3152017-02-22 16:32:36 -08001110 // constructive here.
zachhcc2cde02017-08-31 12:34:18 -07001111 LogUtil.i(
1112 "DialtactsActivity.enterSearchUi",
1113 "not entering search UI (mStateSaved: %b, isDestroyed: %b)",
linyuh183cb712017-12-27 17:02:37 -08001114 stateSaved,
zachhcc2cde02017-08-31 12:34:18 -07001115 getFragmentManager().isDestroyed());
Eric Erfanianccca3152017-02-22 16:32:36 -08001116 return;
1117 }
1118
calderwoodra58d0b2a2018-03-21 16:57:10 -07001119 FragmentTransaction transaction = getFragmentManager().beginTransaction();
1120 String tag = TAG_NEW_SEARCH_FRAGMENT;
1121 inNewSearch = true;
Eric Erfanianccca3152017-02-22 16:32:36 -08001122
linyuh183cb712017-12-27 17:02:37 -08001123 floatingActionButtonController.scaleOut();
Eric Erfanianccca3152017-02-22 16:32:36 -08001124
Eric Erfanianccca3152017-02-22 16:32:36 -08001125 if (animate) {
1126 transaction.setCustomAnimations(android.R.animator.fade_in, 0);
1127 } else {
1128 transaction.setTransition(FragmentTransaction.TRANSIT_NONE);
1129 }
Eric Erfanian2ca43182017-08-31 06:57:16 -07001130
calderwoodra58d0b2a2018-03-21 16:57:10 -07001131 NewSearchFragment fragment = (NewSearchFragment) getFragmentManager().findFragmentByTag(tag);
Eric Erfanianccca3152017-02-22 16:32:36 -08001132 if (fragment == null) {
calderwoodra58d0b2a2018-03-21 16:57:10 -07001133 fragment = NewSearchFragment.newInstance(!isDialpadShown());
Eric Erfanianccca3152017-02-22 16:32:36 -08001134 transaction.add(R.id.dialtacts_frame, fragment, tag);
1135 } else {
1136 transaction.show(fragment);
1137 }
Eric Erfanian2ca43182017-08-31 06:57:16 -07001138
Eric Erfanianccca3152017-02-22 16:32:36 -08001139 // DialtactsActivity will provide the options menu
1140 fragment.setHasOptionsMenu(false);
calderwoodra58d0b2a2018-03-21 16:57:10 -07001141 fragment.setQuery(query, getCallInitiationType());
Eric Erfanianccca3152017-02-22 16:32:36 -08001142 transaction.commit();
1143
1144 if (animate) {
linyuh183cb712017-12-27 17:02:37 -08001145 Assert.isNotNull(listsFragment.getView()).animate().alpha(0).withLayer();
Eric Erfanianccca3152017-02-22 16:32:36 -08001146 }
linyuh183cb712017-12-27 17:02:37 -08001147 listsFragment.setUserVisibleHint(false);
Eric Erfanianccca3152017-02-22 16:32:36 -08001148 }
1149
1150 /** Hides the search fragment */
1151 private void exitSearchUi() {
zachhcc2cde02017-08-31 12:34:18 -07001152 LogUtil.enterBlock("DialtactsActivity.exitSearchUi");
1153
Eric Erfanianccca3152017-02-22 16:32:36 -08001154 // See related bug in enterSearchUI();
linyuh183cb712017-12-27 17:02:37 -08001155 if (getFragmentManager().isDestroyed() || stateSaved) {
Eric Erfanianccca3152017-02-22 16:32:36 -08001156 return;
1157 }
1158
linyuh183cb712017-12-27 17:02:37 -08001159 searchView.setText(null);
Eric Erfanianccca3152017-02-22 16:32:36 -08001160
linyuh183cb712017-12-27 17:02:37 -08001161 if (dialpadFragment != null) {
1162 dialpadFragment.clearDialpad();
Eric Erfanianccca3152017-02-22 16:32:36 -08001163 }
1164
1165 setNotInSearchUi();
1166
calderwoodra6007f522017-10-09 16:59:04 -07001167 // There are four states the fab can be in:
1168 // - Not visible and should remain not visible (do nothing)
1169 // - Not visible (move then show the fab)
1170 // - Visible, in the correct position (do nothing)
1171 // - Visible, in the wrong position (hide, move, then show the fab)
linyuh183cb712017-12-27 17:02:37 -08001172 if (floatingActionButtonController.isVisible()
calderwoodra6007f522017-10-09 16:59:04 -07001173 && getFabAlignment() != FloatingActionButtonController.ALIGN_END) {
linyuh183cb712017-12-27 17:02:37 -08001174 floatingActionButtonController.scaleOut(
calderwoodra6007f522017-10-09 16:59:04 -07001175 new OnVisibilityChangedListener() {
1176 @Override
1177 public void onHidden(FloatingActionButton floatingActionButton) {
1178 super.onHidden(floatingActionButton);
1179 onPageScrolled(
linyuh183cb712017-12-27 17:02:37 -08001180 listsFragment.getCurrentTabIndex(), 0 /* offset */, 0 /* pixelOffset */);
1181 floatingActionButtonController.scaleIn();
calderwoodra6007f522017-10-09 16:59:04 -07001182 }
1183 });
linyuh183cb712017-12-27 17:02:37 -08001184 } else if (!floatingActionButtonController.isVisible() && listsFragment.shouldShowFab()) {
1185 onPageScrolled(listsFragment.getCurrentTabIndex(), 0 /* offset */, 0 /* pixelOffset */);
calderwoodra6007f522017-10-09 16:59:04 -07001186 ThreadUtil.getUiThreadHandler()
linyuh183cb712017-12-27 17:02:37 -08001187 .postDelayed(() -> floatingActionButtonController.scaleIn(), FAB_SCALE_IN_DELAY_MS);
Eric Erfanianccca3152017-02-22 16:32:36 -08001188 }
Eric Erfanianccca3152017-02-22 16:32:36 -08001189
1190 final FragmentTransaction transaction = getFragmentManager().beginTransaction();
linyuh183cb712017-12-27 17:02:37 -08001191 if (newSearchFragment != null) {
1192 transaction.remove(newSearchFragment);
Eric Erfanian2ca43182017-08-31 06:57:16 -07001193 }
Eric Erfanianccca3152017-02-22 16:32:36 -08001194 transaction.commit();
1195
linyuh183cb712017-12-27 17:02:37 -08001196 Assert.isNotNull(listsFragment.getView()).animate().alpha(1).withLayer();
Eric Erfanianccca3152017-02-22 16:32:36 -08001197
linyuh183cb712017-12-27 17:02:37 -08001198 if (dialpadFragment == null || !dialpadFragment.isVisible()) {
Eric Erfanianccca3152017-02-22 16:32:36 -08001199 // If the dialpad fragment wasn't previously visible, then send a screen view because
1200 // we are exiting regular search. Otherwise, the screen view will be sent by
1201 // {@link #hideDialpadFragment}.
linyuh183cb712017-12-27 17:02:37 -08001202 listsFragment.sendScreenViewForCurrentPosition();
1203 listsFragment.setUserVisibleHint(true);
Eric Erfanianccca3152017-02-22 16:32:36 -08001204 }
linyuh183cb712017-12-27 17:02:37 -08001205 onPageSelected(listsFragment.getCurrentTabIndex());
Eric Erfanianccca3152017-02-22 16:32:36 -08001206
linyuh183cb712017-12-27 17:02:37 -08001207 actionBarController.onSearchUiExited();
Eric Erfanianccca3152017-02-22 16:32:36 -08001208 }
1209
1210 @Override
1211 public void onBackPressed() {
Eric Erfanian2ca43182017-08-31 06:57:16 -07001212 PerformanceReport.recordClick(UiAction.Type.PRESS_ANDROID_BACK_BUTTON);
1213
linyuh183cb712017-12-27 17:02:37 -08001214 if (stateSaved) {
Eric Erfanianccca3152017-02-22 16:32:36 -08001215 return;
1216 }
linyuh183cb712017-12-27 17:02:37 -08001217 if (isDialpadShown) {
calderwoodraed4e11d2017-09-05 18:24:47 -07001218 hideDialpadFragment(true, false);
linyuh183cb712017-12-27 17:02:37 -08001219 if (TextUtils.isEmpty(dialpadQuery)) {
calderwoodraecb04562017-09-16 07:34:05 -07001220 exitSearchUi();
1221 }
calderwoodraed4e11d2017-09-05 18:24:47 -07001222 } else if (isInSearchUi()) {
linyuh183cb712017-12-27 17:02:37 -08001223 if (isKeyboardOpen) {
1224 DialerUtils.hideInputMethod(parentLayout);
calderwoodra50715262017-09-08 09:20:08 -07001225 PerformanceReport.recordClick(UiAction.Type.HIDE_KEYBOARD_IN_SEARCH);
1226 } else {
1227 exitSearchUi();
1228 }
Eric Erfanianccca3152017-02-22 16:32:36 -08001229 } else {
1230 super.onBackPressed();
1231 }
1232 }
1233
calderwoodra50715262017-09-08 09:20:08 -07001234 @Override
1235 public void onConfigurationChanged(Configuration configuration) {
1236 super.onConfigurationChanged(configuration);
1237 // Checks whether a hardware keyboard is available
1238 if (configuration.hardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_NO) {
linyuh183cb712017-12-27 17:02:37 -08001239 isKeyboardOpen = true;
calderwoodra50715262017-09-08 09:20:08 -07001240 } else if (configuration.hardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_YES) {
linyuh183cb712017-12-27 17:02:37 -08001241 isKeyboardOpen = false;
calderwoodra50715262017-09-08 09:20:08 -07001242 }
1243 }
1244
Eric Erfanianccca3152017-02-22 16:32:36 -08001245 private void maybeEnterSearchUi() {
1246 if (!isInSearchUi()) {
linyuh183cb712017-12-27 17:02:37 -08001247 enterSearchUi(true /* isSmartDial */, searchQuery, false);
Eric Erfanianccca3152017-02-22 16:32:36 -08001248 }
1249 }
1250
Eric Erfanianccca3152017-02-22 16:32:36 -08001251 @Override
1252 public void onDialpadQueryChanged(String query) {
linyuh183cb712017-12-27 17:02:37 -08001253 dialpadQuery = query;
linyuh183cb712017-12-27 17:02:37 -08001254 if (newSearchFragment != null) {
1255 newSearchFragment.setRawNumber(query);
wangqie9f94c72017-11-10 11:51:50 -08001256 }
Eric Erfanianccca3152017-02-22 16:32:36 -08001257 final String normalizedQuery =
linyuhab146532017-12-19 11:28:51 -08001258 SmartDialNameMatcher.normalizeNumber(/* context = */ this, query);
Eric Erfanianccca3152017-02-22 16:32:36 -08001259
linyuh183cb712017-12-27 17:02:37 -08001260 if (!TextUtils.equals(searchView.getText(), normalizedQuery)) {
Eric Erfanianccca3152017-02-22 16:32:36 -08001261 if (DEBUG) {
1262 LogUtil.v("DialtactsActivity.onDialpadQueryChanged", "new query: " + query);
1263 }
linyuh183cb712017-12-27 17:02:37 -08001264 if (dialpadFragment == null || !dialpadFragment.isVisible()) {
Eric Erfanianccca3152017-02-22 16:32:36 -08001265 // This callback can happen if the dialpad fragment is recreated because of
1266 // activity destruction. In that case, don't update the search view because
1267 // that would bring the user back to the search fragment regardless of the
1268 // previous state of the application. Instead, just return here and let the
1269 // fragment manager correctly figure out whatever fragment was last displayed.
1270 if (!TextUtils.isEmpty(normalizedQuery)) {
linyuh183cb712017-12-27 17:02:37 -08001271 pendingSearchViewQuery = normalizedQuery;
Eric Erfanianccca3152017-02-22 16:32:36 -08001272 }
1273 return;
1274 }
linyuh183cb712017-12-27 17:02:37 -08001275 searchView.setText(normalizedQuery);
Eric Erfanianccca3152017-02-22 16:32:36 -08001276 }
1277
1278 try {
linyuh183cb712017-12-27 17:02:37 -08001279 if (dialpadFragment != null && dialpadFragment.isVisible()) {
1280 dialpadFragment.process_quote_emergency_unquote(normalizedQuery);
Eric Erfanianccca3152017-02-22 16:32:36 -08001281 }
1282 } catch (Exception ignored) {
1283 // Skip any exceptions for this piece of code
1284 }
1285 }
1286
1287 @Override
1288 public boolean onDialpadSpacerTouchWithEmptyQuery() {
Eric Erfanianccca3152017-02-22 16:32:36 -08001289 return false;
1290 }
1291
1292 @Override
calderwoodra76cf7e12018-02-28 17:10:18 -08001293 public boolean shouldShowDialpadChooser() {
1294 // Show the dialpad chooser if we're in a call
1295 return true;
1296 }
1297
1298 @Override
calderwoodra70a4b092018-01-17 13:17:27 -08001299 public void onSearchListTouch() {
linyuh183cb712017-12-27 17:02:37 -08001300 if (isDialpadShown) {
calderwoodra9d197d02017-11-15 15:41:54 -08001301 PerformanceReport.recordClick(UiAction.Type.CLOSE_DIALPAD);
1302 hideDialpadFragment(true, false);
linyuh183cb712017-12-27 17:02:37 -08001303 if (TextUtils.isEmpty(dialpadQuery)) {
calderwoodra9d197d02017-11-15 15:41:54 -08001304 exitSearchUi();
1305 }
calderwoodra9d197d02017-11-15 15:41:54 -08001306 } else {
linyuh183cb712017-12-27 17:02:37 -08001307 UiUtil.hideKeyboardFrom(this, searchEditTextLayout);
calderwoodra9d197d02017-11-15 15:41:54 -08001308 }
calderwoodra9d197d02017-11-15 15:41:54 -08001309 }
1310
1311 @Override
Eric Erfanianccca3152017-02-22 16:32:36 -08001312 public void onListFragmentScrollStateChange(int scrollState) {
Eric Erfanian2ca43182017-08-31 06:57:16 -07001313 PerformanceReport.recordScrollStateChange(scrollState);
Eric Erfanianccca3152017-02-22 16:32:36 -08001314 if (scrollState == OnScrollListener.SCROLL_STATE_TOUCH_SCROLL) {
1315 hideDialpadFragment(true, false);
linyuh183cb712017-12-27 17:02:37 -08001316 DialerUtils.hideInputMethod(parentLayout);
Eric Erfanianccca3152017-02-22 16:32:36 -08001317 }
1318 }
1319
1320 @Override
1321 public void onListFragmentScroll(int firstVisibleItem, int visibleItemCount, int totalItemCount) {
1322 // TODO: No-op for now. This should eventually show/hide the actionBar based on
1323 // interactions with the ListsFragments.
1324 }
1325
1326 private boolean phoneIsInUse() {
wangqifff6fcf2017-08-31 15:30:58 -07001327 return TelecomUtil.isInManagedCall(this);
Eric Erfanianccca3152017-02-22 16:32:36 -08001328 }
1329
1330 private boolean canIntentBeHandled(Intent intent) {
1331 final PackageManager packageManager = getPackageManager();
1332 final List<ResolveInfo> resolveInfo =
1333 packageManager.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
1334 return resolveInfo != null && resolveInfo.size() > 0;
1335 }
1336
1337 /** Called when the user has long-pressed a contact tile to start a drag operation. */
1338 @Override
1339 public void onDragStarted(int x, int y, PhoneFavoriteSquareTileView view) {
linyuh183cb712017-12-27 17:02:37 -08001340 listsFragment.showRemoveView(true);
Eric Erfanianccca3152017-02-22 16:32:36 -08001341 }
1342
1343 @Override
1344 public void onDragHovered(int x, int y, PhoneFavoriteSquareTileView view) {}
1345
1346 /** Called when the user has released a contact tile after long-pressing it. */
1347 @Override
1348 public void onDragFinished(int x, int y) {
linyuh183cb712017-12-27 17:02:37 -08001349 listsFragment.showRemoveView(false);
Eric Erfanianccca3152017-02-22 16:32:36 -08001350 }
1351
1352 @Override
1353 public void onDroppedOnRemove() {}
1354
calderwoodra37d05a22018-01-30 14:50:44 -08001355 @Override
1356 public ImageView getDragShadowOverlay() {
1357 return findViewById(R.id.contact_tile_drag_shadow_overlay);
1358 }
1359
calderwoodraebc59e22018-02-24 02:33:40 -08001360 @Override
1361 public void setHasFrequents(boolean hasFrequents) {
1362 // No-op
1363 }
1364
Eric Erfanianccca3152017-02-22 16:32:36 -08001365 /**
1366 * Allows the SpeedDialFragment to attach the drag controller to mRemoveViewContainer once it has
1367 * been attached to the activity.
1368 */
1369 @Override
1370 public void setDragDropController(DragDropController dragController) {
linyuh183cb712017-12-27 17:02:37 -08001371 dragDropController = dragController;
1372 listsFragment.getRemoveView().setDragDropController(dragController);
Eric Erfanianccca3152017-02-22 16:32:36 -08001373 }
1374
Eric Erfaniand8046e52017-04-06 09:41:50 -07001375 /** Implemented to satisfy {@link OldSpeedDialFragment.HostInterface} */
Eric Erfanianccca3152017-02-22 16:32:36 -08001376 @Override
1377 public void showAllContactsTab() {
linyuh183cb712017-12-27 17:02:37 -08001378 if (listsFragment != null) {
1379 listsFragment.showTab(DialtactsPagerAdapter.TAB_INDEX_ALL_CONTACTS);
Eric Erfanianccca3152017-02-22 16:32:36 -08001380 }
1381 }
1382
1383 /** Implemented to satisfy {@link CallLogFragment.HostInterface} */
1384 @Override
1385 public void showDialpad() {
1386 showDialpadFragment(true);
1387 }
1388
1389 @Override
1390 public void enableFloatingButton(boolean enabled) {
1391 LogUtil.d("DialtactsActivity.enableFloatingButton", "enable: %b", enabled);
1392 // Floating button shouldn't be enabled when dialpad is shown.
1393 if (!isDialpadShown() || !enabled) {
linyuh183cb712017-12-27 17:02:37 -08001394 floatingActionButtonController.setVisible(enabled);
Eric Erfanianccca3152017-02-22 16:32:36 -08001395 }
1396 }
1397
1398 @Override
1399 public void onPickDataUri(
1400 Uri dataUri, boolean isVideoCall, CallSpecificAppData callSpecificAppData) {
linyuh183cb712017-12-27 17:02:37 -08001401 clearSearchOnPause = true;
Eric Erfanianccca3152017-02-22 16:32:36 -08001402 PhoneNumberInteraction.startInteractionForPhoneCall(
1403 DialtactsActivity.this, dataUri, isVideoCall, callSpecificAppData);
1404 }
1405
1406 @Override
1407 public void onPickPhoneNumber(
1408 String phoneNumber, boolean isVideoCall, CallSpecificAppData callSpecificAppData) {
1409 if (phoneNumber == null) {
1410 // Invalid phone number, but let the call go through so that InCallUI can show
1411 // an error message.
1412 phoneNumber = "";
1413 }
twyen0efc8402017-11-07 15:39:15 -08001414 PreCall.start(
1415 this,
erfanian04ac93d2017-10-09 15:12:22 -07001416 new CallIntentBuilder(phoneNumber, callSpecificAppData)
1417 .setIsVideoCall(isVideoCall)
twyen0efc8402017-11-07 15:39:15 -08001418 .setAllowAssistedDial(callSpecificAppData.getAllowAssistedDialing()));
Eric Erfanian2ca43182017-08-31 06:57:16 -07001419
linyuh183cb712017-12-27 17:02:37 -08001420 clearSearchOnPause = true;
Eric Erfanianccca3152017-02-22 16:32:36 -08001421 }
1422
1423 @Override
1424 public void onHomeInActionBarSelected() {
1425 exitSearchUi();
1426 }
1427
1428 @Override
1429 public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
calderwoodra98736472017-11-30 15:21:47 -08001430 // FAB does not move with the new favorites UI
1431 if (newFavoritesIsEnabled()) {
1432 return;
1433 }
linyuh183cb712017-12-27 17:02:37 -08001434 int tabIndex = listsFragment.getCurrentTabIndex();
Eric Erfanianccca3152017-02-22 16:32:36 -08001435
1436 // Scroll the button from center to end when moving from the Speed Dial to Call History tab.
1437 // In RTL, scroll when the current tab is Call History instead, since the order of the tabs
1438 // is reversed and the ViewPager returns the left tab position during scroll.
1439 boolean isRtl = ViewUtil.isRtl();
linyuh183cb712017-12-27 17:02:37 -08001440 if (!isRtl && tabIndex == DialtactsPagerAdapter.TAB_INDEX_SPEED_DIAL && !isLandscape) {
1441 floatingActionButtonController.onPageScrolled(positionOffset);
1442 } else if (isRtl && tabIndex == DialtactsPagerAdapter.TAB_INDEX_HISTORY && !isLandscape) {
1443 floatingActionButtonController.onPageScrolled(1 - positionOffset);
Eric Erfaniand8046e52017-04-06 09:41:50 -07001444 } else if (tabIndex != DialtactsPagerAdapter.TAB_INDEX_SPEED_DIAL) {
linyuh183cb712017-12-27 17:02:37 -08001445 floatingActionButtonController.onPageScrolled(1);
Eric Erfanianccca3152017-02-22 16:32:36 -08001446 }
1447 }
1448
1449 @Override
1450 public void onPageSelected(int position) {
1451 updateMissedCalls();
linyuh183cb712017-12-27 17:02:37 -08001452 int tabIndex = listsFragment.getCurrentTabIndex();
1453 if (tabIndex != previouslySelectedTabIndex) {
1454 floatingActionButtonController.scaleIn();
calderwoodra6007f522017-10-09 16:59:04 -07001455 }
Android Dialerb7dba5a2017-11-07 21:22:15 -08001456 LogUtil.i("DialtactsActivity.onPageSelected", "tabIndex: %d", tabIndex);
linyuh183cb712017-12-27 17:02:37 -08001457 previouslySelectedTabIndex = tabIndex;
Eric Erfaniand8046e52017-04-06 09:41:50 -07001458 timeTabSelected = SystemClock.elapsedRealtime();
Eric Erfanianccca3152017-02-22 16:32:36 -08001459 }
1460
1461 @Override
1462 public void onPageScrollStateChanged(int state) {}
1463
Eric Erfanianccca3152017-02-22 16:32:36 -08001464 public boolean isActionBarShowing() {
linyuh183cb712017-12-27 17:02:37 -08001465 return actionBarController.isActionBarShowing();
Eric Erfanianccca3152017-02-22 16:32:36 -08001466 }
1467
Eric Erfanianccca3152017-02-22 16:32:36 -08001468 public boolean isDialpadShown() {
linyuh183cb712017-12-27 17:02:37 -08001469 return isDialpadShown;
Eric Erfanianccca3152017-02-22 16:32:36 -08001470 }
1471
1472 @Override
Eric Erfanianccca3152017-02-22 16:32:36 -08001473 public void setActionBarHideOffset(int offset) {
1474 getActionBarSafely().setHideOffset(offset);
1475 }
1476
1477 @Override
1478 public int getActionBarHeight() {
linyuh183cb712017-12-27 17:02:37 -08001479 return actionBarHeight;
Eric Erfanianccca3152017-02-22 16:32:36 -08001480 }
1481
calderwoodra6007f522017-10-09 16:59:04 -07001482 @VisibleForTesting
1483 public int getFabAlignment() {
calderwoodra98736472017-11-30 15:21:47 -08001484 if (!newFavoritesIsEnabled()
linyuh183cb712017-12-27 17:02:37 -08001485 && !isLandscape
Eric Erfanianccca3152017-02-22 16:32:36 -08001486 && !isInSearchUi()
linyuh183cb712017-12-27 17:02:37 -08001487 && listsFragment.getCurrentTabIndex() == DialtactsPagerAdapter.TAB_INDEX_SPEED_DIAL) {
Eric Erfanianccca3152017-02-22 16:32:36 -08001488 return FloatingActionButtonController.ALIGN_MIDDLE;
1489 }
1490 return FloatingActionButtonController.ALIGN_END;
1491 }
1492
1493 private void updateMissedCalls() {
linyuh183cb712017-12-27 17:02:37 -08001494 if (previouslySelectedTabIndex == DialtactsPagerAdapter.TAB_INDEX_HISTORY) {
1495 listsFragment.markMissedCallsAsReadAndRemoveNotifications();
Eric Erfanianccca3152017-02-22 16:32:36 -08001496 }
1497 }
1498
1499 @Override
1500 public void onDisambigDialogDismissed() {
1501 // Don't do anything; the app will remain open with favorites tiles displayed.
1502 }
1503
1504 @Override
1505 public void interactionError(@InteractionErrorCode int interactionErrorCode) {
1506 switch (interactionErrorCode) {
1507 case InteractionErrorCode.USER_LEAVING_ACTIVITY:
1508 // This is expected to happen if the user exits the activity before the interaction occurs.
1509 return;
1510 case InteractionErrorCode.CONTACT_NOT_FOUND:
1511 case InteractionErrorCode.CONTACT_HAS_NO_NUMBER:
1512 case InteractionErrorCode.OTHER_ERROR:
1513 default:
1514 // All other error codes are unexpected. For example, it should be impossible to start an
1515 // interaction with an invalid contact from the Dialtacts activity.
1516 Assert.fail("PhoneNumberInteraction error: " + interactionErrorCode);
1517 }
1518 }
1519
1520 @Override
1521 public void onRequestPermissionsResult(
1522 int requestCode, String[] permissions, int[] grantResults) {
1523 // This should never happen; it should be impossible to start an interaction without the
1524 // contacts permission from the Dialtacts activity.
1525 Assert.fail(
1526 String.format(
1527 Locale.US,
1528 "Permissions requested unexpectedly: %d/%s/%s",
1529 requestCode,
1530 Arrays.toString(permissions),
1531 Arrays.toString(grantResults)));
1532 }
1533
Eric Erfanian2ca43182017-08-31 06:57:16 -07001534 @Override
1535 public void onActionModeStateChanged(boolean isEnabled) {
1536 isMultiSelectModeEnabled = isEnabled;
1537 }
1538
1539 @Override
1540 public boolean isActionModeStateEnabled() {
1541 return isMultiSelectModeEnabled;
1542 }
1543
1544 private CallInitiationType.Type getCallInitiationType() {
linyuh183cb712017-12-27 17:02:37 -08001545 return isDialpadShown
Eric Erfanian2ca43182017-08-31 06:57:16 -07001546 ? CallInitiationType.Type.DIALPAD
1547 : CallInitiationType.Type.REGULAR_SEARCH;
1548 }
1549
calderwoodrac3770122017-10-13 16:36:17 -07001550 @Override
calderwoodrae51ca382017-11-21 10:54:36 -08001551 public void onCallPlacedFromSearch() {
linyuh183cb712017-12-27 17:02:37 -08001552 DialerUtils.hideInputMethod(parentLayout);
1553 clearSearchOnPause = true;
calderwoodra32257fd2017-10-25 15:01:37 -07001554 }
1555
calderwoodraf53ba7b2018-03-12 18:30:32 -07001556 @Override
1557 public void requestingPermission() {}
1558
Android Dialerb7dba5a2017-11-07 21:22:15 -08001559 protected int getPreviouslySelectedTabIndex() {
linyuh183cb712017-12-27 17:02:37 -08001560 return previouslySelectedTabIndex;
Android Dialerb7dba5a2017-11-07 21:22:15 -08001561 }
1562
calderwoodra73b51d52017-12-08 20:52:56 -08001563 @Override
1564 public void onContactSelected(ImageView photo, Uri contactUri, long contactId) {
1565 Logger.get(this)
1566 .logInteraction(InteractionEvent.Type.OPEN_QUICK_CONTACT_FROM_CONTACTS_FRAGMENT_ITEM);
1567 QuickContact.showQuickContact(
1568 this, photo, contactUri, QuickContact.MODE_LARGE, null /* excludeMimes */);
1569 }
1570
Eric Erfanian2ca43182017-08-31 06:57:16 -07001571 /** Popup menu accessible from the search bar */
Eric Erfanianccca3152017-02-22 16:32:36 -08001572 protected class OptionsPopupMenu extends PopupMenu {
1573
1574 public OptionsPopupMenu(Context context, View anchor) {
1575 super(context, anchor, Gravity.END);
1576 }
1577
1578 @Override
1579 public void show() {
Eric Erfaniand8046e52017-04-06 09:41:50 -07001580 Menu menu = getMenu();
1581 MenuItem clearFrequents = menu.findItem(R.id.menu_clear_frequents);
Eric Erfanianccca3152017-02-22 16:32:36 -08001582 clearFrequents.setVisible(
Eric Erfanianc857f902017-05-15 14:05:33 -07001583 PermissionsUtil.hasContactsReadPermissions(DialtactsActivity.this)
linyuh183cb712017-12-27 17:02:37 -08001584 && listsFragment != null
1585 && listsFragment.hasFrequents());
Eric Erfanianccca3152017-02-22 16:32:36 -08001586
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001587 menu.findItem(R.id.menu_history)
Eric Erfanianccca3152017-02-22 16:32:36 -08001588 .setVisible(PermissionsUtil.hasPhonePermissions(DialtactsActivity.this));
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001589
1590 Context context = DialtactsActivity.this.getApplicationContext();
1591 MenuItem simulatorMenuItem = menu.findItem(R.id.menu_simulator_submenu);
1592 Simulator simulator = SimulatorComponent.get(context).getSimulator();
1593 if (simulator.shouldShow()) {
1594 simulatorMenuItem.setVisible(true);
weijiaxu6b91ad32017-12-14 15:10:31 -08001595 simulatorMenuItem.setActionProvider(simulator.getActionProvider(DialtactsActivity.this));
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001596 } else {
1597 simulatorMenuItem.setVisible(false);
1598 }
Eric Erfanianccca3152017-02-22 16:32:36 -08001599 super.show();
1600 }
1601 }
1602
1603 /**
1604 * Listener that listens to drag events and sends their x and y coordinates to a {@link
1605 * DragDropController}.
1606 */
1607 private class LayoutOnDragListener implements OnDragListener {
1608
1609 @Override
1610 public boolean onDrag(View v, DragEvent event) {
1611 if (event.getAction() == DragEvent.ACTION_DRAG_LOCATION) {
linyuh183cb712017-12-27 17:02:37 -08001612 dragDropController.handleDragHovered(v, (int) event.getX(), (int) event.getY());
Eric Erfanianccca3152017-02-22 16:32:36 -08001613 }
1614 return true;
1615 }
1616 }
twyen891fe0f2017-10-17 14:59:15 -07001617
1618 @VisibleForTesting
1619 static void setVoiceSearchEnabledForTest(Optional<Boolean> enabled) {
linyuh183cb712017-12-27 17:02:37 -08001620 voiceSearchEnabledForTest = enabled;
twyen891fe0f2017-10-17 14:59:15 -07001621 }
calderwoodra98736472017-11-30 15:21:47 -08001622
1623 private boolean newFavoritesIsEnabled() {
1624 return ConfigProviderBindings.get(this).getBoolean("enable_new_favorites_tab", false);
1625 }
Eric Erfanianccca3152017-02-22 16:32:36 -08001626}