blob: 465c5791146c1066056bc607c9ca562d50ca43b3 [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;
29import android.database.Cursor;
30import android.net.Uri;
31import android.os.Bundle;
Eric Erfaniand8046e52017-04-06 09:41:50 -070032import android.os.SystemClock;
Eric Erfanianccca3152017-02-22 16:32:36 -080033import android.os.Trace;
34import android.provider.CallLog.Calls;
calderwoodra73b51d52017-12-08 20:52:56 -080035import android.provider.ContactsContract.QuickContact;
Eric Erfanianccca3152017-02-22 16:32:36 -080036import android.speech.RecognizerIntent;
37import android.support.annotation.MainThread;
38import android.support.annotation.NonNull;
39import android.support.annotation.VisibleForTesting;
40import android.support.design.widget.CoordinatorLayout;
Eric Erfanianfc37b022017-03-21 10:11:17 -070041import android.support.design.widget.FloatingActionButton;
calderwoodra6007f522017-10-09 16:59:04 -070042import android.support.design.widget.FloatingActionButton.OnVisibilityChangedListener;
Eric Erfanianccca3152017-02-22 16:32:36 -080043import android.support.design.widget.Snackbar;
44import android.support.v4.app.ActivityCompat;
45import android.support.v4.view.ViewPager;
46import android.support.v7.app.ActionBar;
47import android.telecom.PhoneAccount;
48import android.text.Editable;
49import android.text.TextUtils;
50import android.text.TextWatcher;
51import android.view.DragEvent;
52import android.view.Gravity;
Eric Erfanianccca3152017-02-22 16:32:36 -080053import android.view.Menu;
54import android.view.MenuItem;
55import android.view.MotionEvent;
56import android.view.View;
57import android.view.View.OnDragListener;
Eric Erfanianccca3152017-02-22 16:32:36 -080058import android.view.animation.Animation;
59import android.view.animation.AnimationUtils;
60import android.widget.AbsListView.OnScrollListener;
61import android.widget.EditText;
62import android.widget.ImageButton;
calderwoodra73b51d52017-12-08 20:52:56 -080063import android.widget.ImageView;
Eric Erfanianccca3152017-02-22 16:32:36 -080064import android.widget.PopupMenu;
65import android.widget.TextView;
66import android.widget.Toast;
67import com.android.contacts.common.dialog.ClearFrequentsDialog;
68import com.android.contacts.common.list.OnPhoneNumberPickerActionListener;
69import com.android.contacts.common.list.PhoneNumberListAdapter;
Eric Erfanianccca3152017-02-22 16:32:36 -080070import com.android.contacts.common.list.PhoneNumberPickerFragment.CursorReranker;
71import com.android.contacts.common.list.PhoneNumberPickerFragment.OnLoadFinishedListener;
Eric Erfanianccca3152017-02-22 16:32:36 -080072import com.android.dialer.animation.AnimUtils;
73import com.android.dialer.animation.AnimationListenerAdapter;
Eric Erfaniand5e47f62017-03-15 14:41:07 -070074import com.android.dialer.app.calllog.CallLogActivity;
Eric Erfanian2ca43182017-08-31 06:57:16 -070075import com.android.dialer.app.calllog.CallLogAdapter;
Eric Erfanianccca3152017-02-22 16:32:36 -080076import com.android.dialer.app.calllog.CallLogFragment;
77import com.android.dialer.app.calllog.CallLogNotificationsService;
Eric Erfanian2ca43182017-08-31 06:57:16 -070078import com.android.dialer.app.calllog.IntentProvider;
Eric Erfaniand8046e52017-04-06 09:41:50 -070079import com.android.dialer.app.list.DialtactsPagerAdapter;
Eric Erfanian2ca43182017-08-31 06:57:16 -070080import com.android.dialer.app.list.DialtactsPagerAdapter.TabIndex;
Eric Erfanianccca3152017-02-22 16:32:36 -080081import com.android.dialer.app.list.DragDropController;
82import com.android.dialer.app.list.ListsFragment;
Eric Erfaniand8046e52017-04-06 09:41:50 -070083import com.android.dialer.app.list.OldSpeedDialFragment;
Eric Erfanianccca3152017-02-22 16:32:36 -080084import com.android.dialer.app.list.OnDragDropListener;
85import com.android.dialer.app.list.OnListFragmentScrolledListener;
86import com.android.dialer.app.list.PhoneFavoriteSquareTileView;
87import com.android.dialer.app.list.RegularSearchFragment;
88import com.android.dialer.app.list.SearchFragment;
89import com.android.dialer.app.list.SmartDialSearchFragment;
Eric Erfanianccca3152017-02-22 16:32:36 -080090import com.android.dialer.app.settings.DialerSettingsActivity;
91import com.android.dialer.app.widget.ActionBarController;
92import com.android.dialer.app.widget.SearchEditTextLayout;
Eric Erfaniand5e47f62017-03-15 14:41:07 -070093import com.android.dialer.callcomposer.CallComposerActivity;
Eric Erfanian2ca43182017-08-31 06:57:16 -070094import com.android.dialer.calldetails.CallDetailsActivity;
95import com.android.dialer.callintent.CallInitiationType;
Eric Erfanianccca3152017-02-22 16:32:36 -080096import com.android.dialer.callintent.CallIntentBuilder;
Eric Erfanian8369df02017-05-03 10:27:13 -070097import com.android.dialer.callintent.CallSpecificAppData;
Eric Erfanianccca3152017-02-22 16:32:36 -080098import com.android.dialer.common.Assert;
99import com.android.dialer.common.LogUtil;
calderwoodra9d197d02017-11-15 15:41:54 -0800100import com.android.dialer.common.UiUtil;
calderwoodra97d0b5e2018-01-26 12:11:26 -0800101import com.android.dialer.common.concurrent.DialerExecutorComponent;
calderwoodra6007f522017-10-09 16:59:04 -0700102import com.android.dialer.common.concurrent.ThreadUtil;
linyuhc9b35812017-12-28 09:58:34 -0800103import com.android.dialer.compat.CompatUtils;
Eric Erfanian2ca43182017-08-31 06:57:16 -0700104import com.android.dialer.configprovider.ConfigProviderBindings;
105import com.android.dialer.constants.ActivityRequestCodes;
Android Dialer3f4a3192017-09-20 17:59:44 -0700106import com.android.dialer.contactsfragment.ContactsFragment;
calderwoodra73b51d52017-12-08 20:52:56 -0800107import com.android.dialer.contactsfragment.ContactsFragment.OnContactSelectedListener;
Eric Erfanianccca3152017-02-22 16:32:36 -0800108import com.android.dialer.database.Database;
109import com.android.dialer.database.DialerDatabaseHelper;
Eric Erfanian2ca43182017-08-31 06:57:16 -0700110import com.android.dialer.dialpadview.DialpadFragment;
111import com.android.dialer.dialpadview.DialpadFragment.DialpadListener;
112import com.android.dialer.dialpadview.DialpadFragment.LastOutgoingCallCallback;
roldenburg19c6c252017-12-13 15:58:01 -0800113import com.android.dialer.duo.DuoComponent;
Eric Erfanianccca3152017-02-22 16:32:36 -0800114import com.android.dialer.interactions.PhoneNumberInteraction;
115import com.android.dialer.interactions.PhoneNumberInteraction.InteractionErrorCode;
Eric Erfanian8369df02017-05-03 10:27:13 -0700116import com.android.dialer.logging.DialerImpression;
calderwoodra73b51d52017-12-08 20:52:56 -0800117import com.android.dialer.logging.InteractionEvent;
Eric Erfanianccca3152017-02-22 16:32:36 -0800118import com.android.dialer.logging.Logger;
Eric Erfanian8369df02017-05-03 10:27:13 -0700119import com.android.dialer.logging.ScreenEvent;
Eric Erfanian2ca43182017-08-31 06:57:16 -0700120import com.android.dialer.logging.UiAction;
zachh7a96dc72018-02-20 22:16:03 -0800121import com.android.dialer.metrics.Metrics;
122import com.android.dialer.metrics.MetricsComponent;
Eric Erfanianccca3152017-02-22 16:32:36 -0800123import com.android.dialer.p13n.inference.P13nRanking;
124import com.android.dialer.p13n.inference.protocol.P13nRanker;
125import com.android.dialer.p13n.inference.protocol.P13nRanker.P13nRefreshCompleteListener;
126import com.android.dialer.p13n.logging.P13nLogger;
127import com.android.dialer.p13n.logging.P13nLogging;
Eric Erfanian2ca43182017-08-31 06:57:16 -0700128import com.android.dialer.performancereport.PerformanceReport;
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700129import com.android.dialer.postcall.PostCall;
twyen0efc8402017-11-07 15:39:15 -0800130import com.android.dialer.precall.PreCall;
Eric Erfanianccca3152017-02-22 16:32:36 -0800131import com.android.dialer.proguard.UsedByReflection;
Eric Erfanian2ca43182017-08-31 06:57:16 -0700132import com.android.dialer.searchfragment.list.NewSearchFragment;
calderwoodra32257fd2017-10-25 15:01:37 -0700133import com.android.dialer.searchfragment.list.NewSearchFragment.SearchFragmentListener;
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700134import com.android.dialer.simulator.Simulator;
135import com.android.dialer.simulator.SimulatorComponent;
linyuhbf4bb052017-12-21 15:42:00 -0800136import com.android.dialer.smartdial.util.SmartDialNameMatcher;
137import com.android.dialer.smartdial.util.SmartDialPrefix;
linyuh168d09c2017-10-26 17:04:30 -0700138import com.android.dialer.storage.StorageComponent;
Eric Erfanianccca3152017-02-22 16:32:36 -0800139import com.android.dialer.telecom.TelecomUtil;
140import com.android.dialer.util.DialerUtils;
Eric Erfanianccca3152017-02-22 16:32:36 -0800141import com.android.dialer.util.PermissionsUtil;
142import com.android.dialer.util.TouchPointManager;
143import com.android.dialer.util.TransactionSafeActivity;
144import com.android.dialer.util.ViewUtil;
calderwoodra6007f522017-10-09 16:59:04 -0700145import com.android.dialer.widget.FloatingActionButtonController;
twyen891fe0f2017-10-17 14:59:15 -0700146import com.google.common.base.Optional;
Eric Erfanianccca3152017-02-22 16:32:36 -0800147import java.util.ArrayList;
148import java.util.Arrays;
149import java.util.List;
150import java.util.Locale;
Eric Erfaniand8046e52017-04-06 09:41:50 -0700151import java.util.concurrent.TimeUnit;
Eric Erfanianccca3152017-02-22 16:32:36 -0800152
153/** The dialer tab's title is 'phone', a more common name (see strings.xml). */
154@UsedByReflection(value = "AndroidManifest-app.xml")
155public class DialtactsActivity extends TransactionSafeActivity
156 implements View.OnClickListener,
157 DialpadFragment.OnDialpadQueryChangedListener,
158 OnListFragmentScrolledListener,
159 CallLogFragment.HostInterface,
Eric Erfanian2ca43182017-08-31 06:57:16 -0700160 CallLogAdapter.OnActionModeStateChangedListener,
Android Dialer3f4a3192017-09-20 17:59:44 -0700161 ContactsFragment.OnContactsListScrolledListener,
Eric Erfanianccca3152017-02-22 16:32:36 -0800162 DialpadFragment.HostInterface,
Eric Erfaniand8046e52017-04-06 09:41:50 -0700163 OldSpeedDialFragment.HostInterface,
Eric Erfanianccca3152017-02-22 16:32:36 -0800164 SearchFragment.HostInterface,
165 OnDragDropListener,
166 OnPhoneNumberPickerActionListener,
167 PopupMenu.OnMenuItemClickListener,
168 ViewPager.OnPageChangeListener,
169 ActionBarController.ActivityUi,
170 PhoneNumberInteraction.InteractionErrorListener,
171 PhoneNumberInteraction.DisambigDialogDismissedListener,
Eric Erfanian2ca43182017-08-31 06:57:16 -0700172 ActivityCompat.OnRequestPermissionsResultCallback,
calderwoodrac3770122017-10-13 16:36:17 -0700173 DialpadListener,
calderwoodra73b51d52017-12-08 20:52:56 -0800174 SearchFragmentListener,
175 OnContactSelectedListener {
Eric Erfanianccca3152017-02-22 16:32:36 -0800176
177 public static final boolean DEBUG = false;
178 @VisibleForTesting public static final String TAG_DIALPAD_FRAGMENT = "dialpad";
179 private static final String ACTION_SHOW_TAB = "ACTION_SHOW_TAB";
180 @VisibleForTesting public static final String EXTRA_SHOW_TAB = "EXTRA_SHOW_TAB";
181 public static final String EXTRA_CLEAR_NEW_VOICEMAILS = "EXTRA_CLEAR_NEW_VOICEMAILS";
Eric Erfanian2ca43182017-08-31 06:57:16 -0700182 private static final String KEY_LAST_TAB = "last_tab";
Eric Erfanianccca3152017-02-22 16:32:36 -0800183 private static final String TAG = "DialtactsActivity";
184 private static final String KEY_IN_REGULAR_SEARCH_UI = "in_regular_search_ui";
185 private static final String KEY_IN_DIALPAD_SEARCH_UI = "in_dialpad_search_ui";
calderwoodra50715262017-09-08 09:20:08 -0700186 private static final String KEY_IN_NEW_SEARCH_UI = "in_new_search_ui";
Eric Erfanianccca3152017-02-22 16:32:36 -0800187 private static final String KEY_SEARCH_QUERY = "search_query";
calderwoodra9d197d02017-11-15 15:41:54 -0800188 private static final String KEY_DIALPAD_QUERY = "dialpad_query";
Eric Erfanianccca3152017-02-22 16:32:36 -0800189 private static final String KEY_FIRST_LAUNCH = "first_launch";
linyuhc9b35812017-12-28 09:58:34 -0800190 private static final String KEY_SAVED_LANGUAGE_CODE = "saved_language_code";
Eric Erfanianccca3152017-02-22 16:32:36 -0800191 private static final String KEY_WAS_CONFIGURATION_CHANGE = "was_configuration_change";
192 private static final String KEY_IS_DIALPAD_SHOWN = "is_dialpad_shown";
calderwoodra218fff32017-09-19 11:20:31 -0700193 private static final String KEY_FAB_VISIBLE = "fab_visible";
Eric Erfanian2ca43182017-08-31 06:57:16 -0700194 private static final String TAG_NEW_SEARCH_FRAGMENT = "new_search";
Eric Erfanianccca3152017-02-22 16:32:36 -0800195 private static final String TAG_REGULAR_SEARCH_FRAGMENT = "search";
196 private static final String TAG_SMARTDIAL_SEARCH_FRAGMENT = "smartdial";
197 private static final String TAG_FAVORITES_FRAGMENT = "favorites";
198 /** Just for backward compatibility. Should behave as same as {@link Intent#ACTION_DIAL}. */
199 private static final String ACTION_TOUCH_DIALER = "com.android.phone.action.TOUCH_DIALER";
200
Eric Erfanianccca3152017-02-22 16:32:36 -0800201 private static final int FAB_SCALE_IN_DELAY_MS = 300;
Eric Erfaniand8046e52017-04-06 09:41:50 -0700202
203 /**
204 * Minimum time the history tab must have been selected for it to be marked as seen in onStop()
205 */
206 private static final long HISTORY_TAB_SEEN_TIMEOUT = TimeUnit.SECONDS.toMillis(3);
207
linyuh183cb712017-12-27 17:02:37 -0800208 private static Optional<Boolean> voiceSearchEnabledForTest = Optional.absent();
twyen891fe0f2017-10-17 14:59:15 -0700209
Eric Erfanianccca3152017-02-22 16:32:36 -0800210 /** Fragment containing the dialpad that slides into view */
linyuh183cb712017-12-27 17:02:37 -0800211 protected DialpadFragment dialpadFragment;
Eric Erfanianccca3152017-02-22 16:32:36 -0800212
Android Dialer3f4a3192017-09-20 17:59:44 -0700213 /** Root layout of DialtactsActivity */
linyuh183cb712017-12-27 17:02:37 -0800214 private CoordinatorLayout parentLayout;
Eric Erfanianccca3152017-02-22 16:32:36 -0800215 /** Fragment for searching phone numbers using the alphanumeric keyboard. */
linyuh183cb712017-12-27 17:02:37 -0800216 private RegularSearchFragment regularSearchFragment;
Eric Erfanianccca3152017-02-22 16:32:36 -0800217
218 /** Fragment for searching phone numbers using the dialpad. */
linyuh183cb712017-12-27 17:02:37 -0800219 private SmartDialSearchFragment smartDialSearchFragment;
Eric Erfanianccca3152017-02-22 16:32:36 -0800220
Eric Erfanian2ca43182017-08-31 06:57:16 -0700221 /** new Fragment for search phone numbers using the keyboard and the dialpad. */
linyuh183cb712017-12-27 17:02:37 -0800222 private NewSearchFragment newSearchFragment;
Eric Erfanian2ca43182017-08-31 06:57:16 -0700223
Eric Erfanianccca3152017-02-22 16:32:36 -0800224 /** Animation that slides in. */
linyuh183cb712017-12-27 17:02:37 -0800225 private Animation slideIn;
Eric Erfanianccca3152017-02-22 16:32:36 -0800226
227 /** Animation that slides out. */
linyuh183cb712017-12-27 17:02:37 -0800228 private Animation slideOut;
Eric Erfanianccca3152017-02-22 16:32:36 -0800229 /** Fragment containing the speed dial list, call history list, and all contacts list. */
linyuh183cb712017-12-27 17:02:37 -0800230 private ListsFragment listsFragment;
Eric Erfanianccca3152017-02-22 16:32:36 -0800231 /**
232 * Tracks whether onSaveInstanceState has been called. If true, no fragment transactions can be
233 * commited.
234 */
linyuh183cb712017-12-27 17:02:37 -0800235 private boolean stateSaved;
Eric Erfanianccca3152017-02-22 16:32:36 -0800236
linyuh183cb712017-12-27 17:02:37 -0800237 private boolean isKeyboardOpen;
238 private boolean inNewSearch;
239 private boolean isRestarting;
240 private boolean inDialpadSearch;
241 private boolean inRegularSearch;
242 private boolean clearSearchOnPause;
243 private boolean isDialpadShown;
Eric Erfanianccca3152017-02-22 16:32:36 -0800244 /** Whether or not the device is in landscape orientation. */
linyuh183cb712017-12-27 17:02:37 -0800245 private boolean isLandscape;
Eric Erfanianccca3152017-02-22 16:32:36 -0800246 /** True if the dialpad is only temporarily showing due to being in call */
linyuh183cb712017-12-27 17:02:37 -0800247 private boolean inCallDialpadUp;
Eric Erfanianccca3152017-02-22 16:32:36 -0800248 /** True when this activity has been launched for the first time. */
linyuh183cb712017-12-27 17:02:37 -0800249 private boolean firstLaunch;
Eric Erfanianccca3152017-02-22 16:32:36 -0800250 /**
251 * Search query to be applied to the SearchView in the ActionBar once onCreateOptionsMenu has been
252 * called.
253 */
linyuh183cb712017-12-27 17:02:37 -0800254 private String pendingSearchViewQuery;
Eric Erfanianccca3152017-02-22 16:32:36 -0800255
linyuh183cb712017-12-27 17:02:37 -0800256 private PopupMenu overflowMenu;
257 private EditText searchView;
258 private SearchEditTextLayout searchEditTextLayout;
259 private View voiceSearchButton;
260 private String searchQuery;
261 private String dialpadQuery;
262 private DialerDatabaseHelper dialerDatabaseHelper;
263 private DragDropController dragDropController;
264 private ActionBarController actionBarController;
265 private FloatingActionButtonController floatingActionButtonController;
linyuhc9b35812017-12-28 09:58:34 -0800266 private String savedLanguageCode;
linyuh183cb712017-12-27 17:02:37 -0800267 private boolean wasConfigurationChange;
Eric Erfaniand8046e52017-04-06 09:41:50 -0700268 private long timeTabSelected;
Eric Erfanianccca3152017-02-22 16:32:36 -0800269
linyuh183cb712017-12-27 17:02:37 -0800270 private P13nLogger p13nLogger;
271 private P13nRanker p13nRanker;
Eric Erfanian2ca43182017-08-31 06:57:16 -0700272 public boolean isMultiSelectModeEnabled;
273
274 private boolean isLastTabEnabled;
Eric Erfanianccca3152017-02-22 16:32:36 -0800275
linyuh183cb712017-12-27 17:02:37 -0800276 AnimationListenerAdapter slideInListener =
Eric Erfanianccca3152017-02-22 16:32:36 -0800277 new AnimationListenerAdapter() {
278 @Override
279 public void onAnimationEnd(Animation animation) {
280 maybeEnterSearchUi();
281 }
282 };
283 /** Listener for after slide out animation completes on dialer fragment. */
linyuh183cb712017-12-27 17:02:37 -0800284 AnimationListenerAdapter slideOutListener =
Eric Erfanianccca3152017-02-22 16:32:36 -0800285 new AnimationListenerAdapter() {
286 @Override
287 public void onAnimationEnd(Animation animation) {
288 commitDialpadFragmentHide();
289 }
290 };
291 /** Listener used to send search queries to the phone search fragment. */
linyuh183cb712017-12-27 17:02:37 -0800292 private final TextWatcher phoneSearchQueryTextListener =
Eric Erfanianccca3152017-02-22 16:32:36 -0800293 new TextWatcher() {
294 @Override
295 public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
296
297 @Override
298 public void onTextChanged(CharSequence s, int start, int before, int count) {
299 final String newText = s.toString();
linyuh183cb712017-12-27 17:02:37 -0800300 if (newText.equals(searchQuery)) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800301 // If the query hasn't changed (perhaps due to activity being destroyed
302 // and restored, or user launching the same DIAL intent twice), then there is
303 // no need to do anything here.
304 return;
305 }
Eric Erfanian2ca43182017-08-31 06:57:16 -0700306
307 if (count != 0) {
308 PerformanceReport.recordClick(UiAction.Type.TEXT_CHANGE_WITH_INPUT);
309 }
310
wangqie9f94c72017-11-10 11:51:50 -0800311 LogUtil.v("DialtactsActivity.onTextChanged", "called with new query: " + newText);
linyuh183cb712017-12-27 17:02:37 -0800312 LogUtil.v("DialtactsActivity.onTextChanged", "previous query: " + searchQuery);
313 searchQuery = newText;
Eric Erfanianccca3152017-02-22 16:32:36 -0800314
Eric Erfanian2ca43182017-08-31 06:57:16 -0700315 // TODO(calderwoodra): show p13n when newText is empty.
Eric Erfanianccca3152017-02-22 16:32:36 -0800316 // Show search fragment only when the query string is changed to non-empty text.
317 if (!TextUtils.isEmpty(newText)) {
318 // Call enterSearchUi only if we are switching search modes, or showing a search
319 // fragment for the first time.
320 final boolean sameSearchMode =
linyuh183cb712017-12-27 17:02:37 -0800321 (isDialpadShown && inDialpadSearch) || (!isDialpadShown && inRegularSearch);
Eric Erfanianccca3152017-02-22 16:32:36 -0800322 if (!sameSearchMode) {
linyuh183cb712017-12-27 17:02:37 -0800323 enterSearchUi(isDialpadShown, searchQuery, true /* animate */);
Eric Erfanianccca3152017-02-22 16:32:36 -0800324 }
325 }
326
linyuh183cb712017-12-27 17:02:37 -0800327 if (smartDialSearchFragment != null && smartDialSearchFragment.isVisible()) {
328 smartDialSearchFragment.setQueryString(searchQuery);
329 } else if (regularSearchFragment != null && regularSearchFragment.isVisible()) {
330 regularSearchFragment.setQueryString(searchQuery);
331 } else if (newSearchFragment != null && newSearchFragment.isVisible()) {
332 newSearchFragment.setQuery(searchQuery, getCallInitiationType());
Eric Erfanianccca3152017-02-22 16:32:36 -0800333 }
334 }
335
336 @Override
337 public void afterTextChanged(Editable s) {}
338 };
339 /** Open the search UI when the user clicks on the search box. */
linyuh183cb712017-12-27 17:02:37 -0800340 private final View.OnClickListener searchViewOnClickListener =
Eric Erfanianccca3152017-02-22 16:32:36 -0800341 new View.OnClickListener() {
342 @Override
343 public void onClick(View v) {
344 if (!isInSearchUi()) {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700345 PerformanceReport.recordClick(UiAction.Type.OPEN_SEARCH);
linyuh183cb712017-12-27 17:02:37 -0800346 actionBarController.onSearchBoxTapped();
Eric Erfanianccca3152017-02-22 16:32:36 -0800347 enterSearchUi(
linyuh183cb712017-12-27 17:02:37 -0800348 false /* smartDialSearch */, searchView.getText().toString(), true /* animate */);
Eric Erfanianccca3152017-02-22 16:32:36 -0800349 }
350 }
351 };
352
linyuh183cb712017-12-27 17:02:37 -0800353 private int actionBarHeight;
354 private int previouslySelectedTabIndex;
Eric Erfanian2ca43182017-08-31 06:57:16 -0700355
Eric Erfanianccca3152017-02-22 16:32:36 -0800356 /**
357 * The text returned from a voice search query. Set in {@link #onActivityResult} and used in
358 * {@link #onResume()} to populate the search box.
359 */
linyuh183cb712017-12-27 17:02:37 -0800360 private String voiceSearchQuery;
Eric Erfanianccca3152017-02-22 16:32:36 -0800361
362 /**
363 * @param tab the TAB_INDEX_* constant in {@link ListsFragment}
364 * @return A intent that will open the DialtactsActivity into the specified tab. The intent for
365 * each tab will be unique.
366 */
367 public static Intent getShowTabIntent(Context context, int tab) {
368 Intent intent = new Intent(context, DialtactsActivity.class);
369 intent.setAction(ACTION_SHOW_TAB);
370 intent.putExtra(DialtactsActivity.EXTRA_SHOW_TAB, tab);
371 intent.setData(
372 new Uri.Builder()
373 .scheme("intent")
374 .authority(context.getPackageName())
375 .appendPath(TAG)
376 .appendQueryParameter(DialtactsActivity.EXTRA_SHOW_TAB, String.valueOf(tab))
377 .build());
378
379 return intent;
380 }
381
382 @Override
383 public boolean dispatchTouchEvent(MotionEvent ev) {
384 if (ev.getAction() == MotionEvent.ACTION_DOWN) {
385 TouchPointManager.getInstance().setPoint((int) ev.getRawX(), (int) ev.getRawY());
386 }
387 return super.dispatchTouchEvent(ev);
388 }
389
390 @Override
391 protected void onCreate(Bundle savedInstanceState) {
392 Trace.beginSection(TAG + " onCreate");
zachh6050ec22017-09-21 16:02:35 -0700393 LogUtil.enterBlock("DialtactsActivity.onCreate");
Eric Erfanianccca3152017-02-22 16:32:36 -0800394 super.onCreate(savedInstanceState);
395
linyuh183cb712017-12-27 17:02:37 -0800396 firstLaunch = true;
Eric Erfanian2ca43182017-08-31 06:57:16 -0700397 isLastTabEnabled = ConfigProviderBindings.get(this).getBoolean("last_tab_enabled", false);
Eric Erfanianccca3152017-02-22 16:32:36 -0800398
399 final Resources resources = getResources();
linyuh183cb712017-12-27 17:02:37 -0800400 actionBarHeight = resources.getDimensionPixelSize(R.dimen.action_bar_height_large);
Eric Erfanianccca3152017-02-22 16:32:36 -0800401
402 Trace.beginSection(TAG + " setContentView");
403 setContentView(R.layout.dialtacts_activity);
404 Trace.endSection();
405 getWindow().setBackgroundDrawable(null);
406
407 Trace.beginSection(TAG + " setup Views");
408 final ActionBar actionBar = getActionBarSafely();
409 actionBar.setCustomView(R.layout.search_edittext);
410 actionBar.setDisplayShowCustomEnabled(true);
411 actionBar.setBackgroundDrawable(null);
412
linyuh183cb712017-12-27 17:02:37 -0800413 searchEditTextLayout = actionBar.getCustomView().findViewById(R.id.search_view_container);
Eric Erfanianccca3152017-02-22 16:32:36 -0800414
linyuh183cb712017-12-27 17:02:37 -0800415 actionBarController = new ActionBarController(this, searchEditTextLayout);
Eric Erfanianccca3152017-02-22 16:32:36 -0800416
linyuh183cb712017-12-27 17:02:37 -0800417 searchView = searchEditTextLayout.findViewById(R.id.search_view);
418 searchView.addTextChangedListener(phoneSearchQueryTextListener);
419 searchView.setHint(getSearchBoxHint());
calderwoodra50715262017-09-08 09:20:08 -0700420
linyuh183cb712017-12-27 17:02:37 -0800421 voiceSearchButton = searchEditTextLayout.findViewById(R.id.voice_search_button);
422 searchEditTextLayout
Eric Erfanian10b34a52017-05-04 08:23:17 -0700423 .findViewById(R.id.search_box_collapsed)
linyuh183cb712017-12-27 17:02:37 -0800424 .setOnClickListener(searchViewOnClickListener);
425 searchEditTextLayout
calderwoodra50715262017-09-08 09:20:08 -0700426 .findViewById(R.id.search_back_button)
427 .setOnClickListener(v -> exitSearchUi());
Eric Erfanianccca3152017-02-22 16:32:36 -0800428
linyuh183cb712017-12-27 17:02:37 -0800429 isLandscape =
Eric Erfanianccca3152017-02-22 16:32:36 -0800430 getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE;
linyuh183cb712017-12-27 17:02:37 -0800431 previouslySelectedTabIndex = DialtactsPagerAdapter.TAB_INDEX_SPEED_DIAL;
Eric Erfanian2ca43182017-08-31 06:57:16 -0700432 FloatingActionButton floatingActionButton = findViewById(R.id.floating_action_button);
Eric Erfanianccca3152017-02-22 16:32:36 -0800433 floatingActionButton.setOnClickListener(this);
linyuh183cb712017-12-27 17:02:37 -0800434 floatingActionButtonController = new FloatingActionButtonController(this, floatingActionButton);
Eric Erfanianccca3152017-02-22 16:32:36 -0800435
436 ImageButton optionsMenuButton =
linyuh183cb712017-12-27 17:02:37 -0800437 searchEditTextLayout.findViewById(R.id.dialtacts_options_menu_button);
Eric Erfanianccca3152017-02-22 16:32:36 -0800438 optionsMenuButton.setOnClickListener(this);
linyuh183cb712017-12-27 17:02:37 -0800439 overflowMenu = buildOptionsMenu(optionsMenuButton);
440 optionsMenuButton.setOnTouchListener(overflowMenu.getDragToOpenListener());
Eric Erfanianccca3152017-02-22 16:32:36 -0800441
442 // Add the favorites fragment but only if savedInstanceState is null. Otherwise the
443 // fragment manager is responsible for recreating it.
444 if (savedInstanceState == null) {
445 getFragmentManager()
446 .beginTransaction()
447 .add(R.id.dialtacts_frame, new ListsFragment(), TAG_FAVORITES_FRAGMENT)
448 .commit();
449 } else {
linyuh183cb712017-12-27 17:02:37 -0800450 searchQuery = savedInstanceState.getString(KEY_SEARCH_QUERY);
451 dialpadQuery = savedInstanceState.getString(KEY_DIALPAD_QUERY);
452 inRegularSearch = savedInstanceState.getBoolean(KEY_IN_REGULAR_SEARCH_UI);
453 inDialpadSearch = savedInstanceState.getBoolean(KEY_IN_DIALPAD_SEARCH_UI);
454 inNewSearch = savedInstanceState.getBoolean(KEY_IN_NEW_SEARCH_UI);
455 firstLaunch = savedInstanceState.getBoolean(KEY_FIRST_LAUNCH);
linyuhc9b35812017-12-28 09:58:34 -0800456 savedLanguageCode = savedInstanceState.getString(KEY_SAVED_LANGUAGE_CODE);
linyuh183cb712017-12-27 17:02:37 -0800457 wasConfigurationChange = savedInstanceState.getBoolean(KEY_WAS_CONFIGURATION_CHANGE);
458 isDialpadShown = savedInstanceState.getBoolean(KEY_IS_DIALPAD_SHOWN);
459 floatingActionButtonController.setVisible(savedInstanceState.getBoolean(KEY_FAB_VISIBLE));
460 actionBarController.restoreInstanceState(savedInstanceState);
Eric Erfanianccca3152017-02-22 16:32:36 -0800461 }
462
463 final boolean isLayoutRtl = ViewUtil.isRtl();
linyuh183cb712017-12-27 17:02:37 -0800464 if (isLandscape) {
465 slideIn =
Eric Erfanianccca3152017-02-22 16:32:36 -0800466 AnimationUtils.loadAnimation(
467 this, isLayoutRtl ? R.anim.dialpad_slide_in_left : R.anim.dialpad_slide_in_right);
linyuh183cb712017-12-27 17:02:37 -0800468 slideOut =
Eric Erfanianccca3152017-02-22 16:32:36 -0800469 AnimationUtils.loadAnimation(
470 this, isLayoutRtl ? R.anim.dialpad_slide_out_left : R.anim.dialpad_slide_out_right);
471 } else {
linyuh183cb712017-12-27 17:02:37 -0800472 slideIn = AnimationUtils.loadAnimation(this, R.anim.dialpad_slide_in_bottom);
473 slideOut = AnimationUtils.loadAnimation(this, R.anim.dialpad_slide_out_bottom);
Eric Erfanianccca3152017-02-22 16:32:36 -0800474 }
475
linyuh183cb712017-12-27 17:02:37 -0800476 slideIn.setInterpolator(AnimUtils.EASE_IN);
477 slideOut.setInterpolator(AnimUtils.EASE_OUT);
Eric Erfanianccca3152017-02-22 16:32:36 -0800478
linyuh183cb712017-12-27 17:02:37 -0800479 slideIn.setAnimationListener(slideInListener);
480 slideOut.setAnimationListener(slideOutListener);
Eric Erfanianccca3152017-02-22 16:32:36 -0800481
linyuh183cb712017-12-27 17:02:37 -0800482 parentLayout = (CoordinatorLayout) findViewById(R.id.dialtacts_mainlayout);
483 parentLayout.setOnDragListener(new LayoutOnDragListener());
Eric Erfanianfc37b022017-03-21 10:11:17 -0700484 ViewUtil.doOnGlobalLayout(
485 floatingActionButton,
486 view -> {
linyuh183cb712017-12-27 17:02:37 -0800487 int screenWidth = parentLayout.getWidth();
488 floatingActionButtonController.setScreenWidth(screenWidth);
489 floatingActionButtonController.align(getFabAlignment(), false /* animate */);
Eric Erfanianfc37b022017-03-21 10:11:17 -0700490 });
Eric Erfanianccca3152017-02-22 16:32:36 -0800491
492 Trace.endSection();
493
494 Trace.beginSection(TAG + " initialize smart dialing");
linyuh183cb712017-12-27 17:02:37 -0800495 dialerDatabaseHelper = Database.get(this).getDatabaseHelper(this);
Eric Erfanianccca3152017-02-22 16:32:36 -0800496 SmartDialPrefix.initializeNanpSettings(this);
497 Trace.endSection();
498
linyuh183cb712017-12-27 17:02:37 -0800499 p13nLogger = P13nLogging.get(getApplicationContext());
500 p13nRanker = P13nRanking.get(getApplicationContext());
Eric Erfanianccca3152017-02-22 16:32:36 -0800501 Trace.endSection();
calderwoodra71eae262017-11-30 13:19:42 -0800502
503 // Update the new search fragment to the correct position and the ActionBar's visibility.
504 if (ConfigProviderBindings.get(this).getBoolean("enable_new_search_fragment", false)) {
505 updateSearchFragmentPosition();
506 }
Eric Erfanianccca3152017-02-22 16:32:36 -0800507 }
508
509 @NonNull
510 private ActionBar getActionBarSafely() {
511 return Assert.isNotNull(getSupportActionBar());
512 }
513
514 @Override
515 protected void onResume() {
zachhcc2cde02017-08-31 12:34:18 -0700516 LogUtil.enterBlock("DialtactsActivity.onResume");
Eric Erfanianccca3152017-02-22 16:32:36 -0800517 Trace.beginSection(TAG + " onResume");
518 super.onResume();
519
Eric Erfanian2ca43182017-08-31 06:57:16 -0700520 // Some calls may not be recorded (eg. from quick contact),
521 // so we should restart recording after these calls. (Recorded call is stopped)
522 PostCall.restartPerformanceRecordingIfARecentCallExist(this);
523 if (!PerformanceReport.isRecording()) {
524 PerformanceReport.startRecording();
525 }
526
linyuh183cb712017-12-27 17:02:37 -0800527 stateSaved = false;
528 if (firstLaunch) {
zachhcc2cde02017-08-31 12:34:18 -0700529 LogUtil.i("DialtactsActivity.onResume", "mFirstLaunch true, displaying fragment");
Eric Erfanianccca3152017-02-22 16:32:36 -0800530 displayFragment(getIntent());
linyuh183cb712017-12-27 17:02:37 -0800531 } else if (!phoneIsInUse() && inCallDialpadUp) {
zachhcc2cde02017-08-31 12:34:18 -0700532 LogUtil.i("DialtactsActivity.onResume", "phone not in use, hiding dialpad fragment");
Eric Erfanianccca3152017-02-22 16:32:36 -0800533 hideDialpadFragment(false, true);
linyuh183cb712017-12-27 17:02:37 -0800534 inCallDialpadUp = false;
535 } else if (isDialpadShown) {
zachhcc2cde02017-08-31 12:34:18 -0700536 LogUtil.i("DialtactsActivity.onResume", "showing dialpad on resume");
Eric Erfanianccca3152017-02-22 16:32:36 -0800537 showDialpadFragment(false);
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700538 } else {
linyuh183cb712017-12-27 17:02:37 -0800539 PostCall.promptUserForMessageIfNecessary(this, parentLayout);
Eric Erfanianccca3152017-02-22 16:32:36 -0800540 }
541
twyenbd73e9d2017-09-26 16:06:32 -0700542 // On M the fragment manager does not restore the hidden state of a fragment from
543 // savedInstanceState so it must be hidden again.
linyuh183cb712017-12-27 17:02:37 -0800544 if (!isDialpadShown && dialpadFragment != null && !dialpadFragment.isHidden()) {
twyenbd73e9d2017-09-26 16:06:32 -0700545 LogUtil.i(
546 "DialtactsActivity.onResume", "mDialpadFragment attached but not hidden, forcing hide");
linyuh183cb712017-12-27 17:02:37 -0800547 getFragmentManager().beginTransaction().hide(dialpadFragment).commit();
twyenbd73e9d2017-09-26 16:06:32 -0700548 }
549
Eric Erfanianccca3152017-02-22 16:32:36 -0800550 // If there was a voice query result returned in the {@link #onActivityResult} callback, it
551 // will have been stashed in mVoiceSearchQuery since the search results fragment cannot be
552 // shown until onResume has completed. Active the search UI and set the search term now.
linyuh183cb712017-12-27 17:02:37 -0800553 if (!TextUtils.isEmpty(voiceSearchQuery)) {
554 actionBarController.onSearchBoxTapped();
555 searchView.setText(voiceSearchQuery);
556 voiceSearchQuery = null;
Eric Erfanianccca3152017-02-22 16:32:36 -0800557 }
558
linyuh183cb712017-12-27 17:02:37 -0800559 if (isRestarting) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800560 // This is only called when the activity goes from resumed -> paused -> resumed, so it
561 // will not cause an extra view to be sent out on rotation
linyuh183cb712017-12-27 17:02:37 -0800562 if (isDialpadShown) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800563 Logger.get(this).logScreenView(ScreenEvent.Type.DIALPAD, this);
564 }
linyuh183cb712017-12-27 17:02:37 -0800565 isRestarting = false;
Eric Erfanianccca3152017-02-22 16:32:36 -0800566 }
567
568 prepareVoiceSearchButton();
linyuhc9b35812017-12-28 09:58:34 -0800569
570 // Start the thread that updates the smart dial database if
571 // (1) the activity is not recreated with a new configuration, or
572 // (2) the activity is recreated with a new configuration but the change is a language change.
573 boolean isLanguageChanged =
574 !CompatUtils.getLocale(this).getISO3Language().equals(savedLanguageCode);
575 if (!wasConfigurationChange || isLanguageChanged) {
576 dialerDatabaseHelper.startSmartDialUpdateThread(/* forceUpdate = */ isLanguageChanged);
Eric Erfanianccca3152017-02-22 16:32:36 -0800577 }
linyuhc9b35812017-12-28 09:58:34 -0800578
linyuh183cb712017-12-27 17:02:37 -0800579 if (isDialpadShown) {
580 floatingActionButtonController.scaleOut();
zachh6050ec22017-09-21 16:02:35 -0700581 } else {
linyuh183cb712017-12-27 17:02:37 -0800582 floatingActionButtonController.align(getFabAlignment(), false /* animate */);
zachh6050ec22017-09-21 16:02:35 -0700583 }
Eric Erfanianccca3152017-02-22 16:32:36 -0800584
linyuh183cb712017-12-27 17:02:37 -0800585 if (firstLaunch) {
Eric Erfanian10b34a52017-05-04 08:23:17 -0700586 // Only process the Intent the first time onResume() is called after receiving it
587 if (Calls.CONTENT_TYPE.equals(getIntent().getType())) {
588 // Externally specified extras take precedence to EXTRA_SHOW_TAB, which is only
589 // used internally.
590 final Bundle extras = getIntent().getExtras();
591 if (extras != null && extras.getInt(Calls.EXTRA_CALL_TYPE_FILTER) == Calls.VOICEMAIL_TYPE) {
linyuh183cb712017-12-27 17:02:37 -0800592 listsFragment.showTab(DialtactsPagerAdapter.TAB_INDEX_VOICEMAIL);
Eric Erfanian10b34a52017-05-04 08:23:17 -0700593 Logger.get(this).logImpression(DialerImpression.Type.VVM_NOTIFICATION_CLICKED);
594 } else {
linyuh183cb712017-12-27 17:02:37 -0800595 listsFragment.showTab(DialtactsPagerAdapter.TAB_INDEX_HISTORY);
Eric Erfanian10b34a52017-05-04 08:23:17 -0700596 }
597 } else if (getIntent().hasExtra(EXTRA_SHOW_TAB)) {
598 int index =
599 getIntent().getIntExtra(EXTRA_SHOW_TAB, DialtactsPagerAdapter.TAB_INDEX_SPEED_DIAL);
linyuh183cb712017-12-27 17:02:37 -0800600 if (index < listsFragment.getTabCount()) {
Eric Erfanian10b34a52017-05-04 08:23:17 -0700601 // Hide dialpad since this is an explicit intent to show a specific tab, which is coming
602 // from missed call or voicemail notification.
603 hideDialpadFragment(false, false);
604 exitSearchUi();
linyuh183cb712017-12-27 17:02:37 -0800605 listsFragment.showTab(index);
Eric Erfanian10b34a52017-05-04 08:23:17 -0700606 }
Eric Erfanianccca3152017-02-22 16:32:36 -0800607 }
Eric Erfanian10b34a52017-05-04 08:23:17 -0700608
609 if (getIntent().getBooleanExtra(EXTRA_CLEAR_NEW_VOICEMAILS, false)) {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700610 LogUtil.i("DialtactsActivity.onResume", "clearing all new voicemails");
611 CallLogNotificationsService.markAllNewVoicemailsAsOld(this);
Eric Erfanianccca3152017-02-22 16:32:36 -0800612 }
weijiaxu650e7cc2017-10-31 12:38:54 -0700613 // add 1 sec delay to get memory snapshot so that dialer wont react slowly on resume.
614 ThreadUtil.postDelayedOnUiThread(
weijiaxuc950a9b2017-11-06 16:39:04 -0800615 () ->
zachh7a96dc72018-02-20 22:16:03 -0800616 MetricsComponent.get(this)
617 .metrics()
618 .recordMemory(Metrics.DIALTACTS_ON_RESUME_MEMORY_EVENT_NAME),
weijiaxu650e7cc2017-10-31 12:38:54 -0700619 1000);
Eric Erfanianccca3152017-02-22 16:32:36 -0800620 }
621
linyuh183cb712017-12-27 17:02:37 -0800622 firstLaunch = false;
Eric Erfanianccca3152017-02-22 16:32:36 -0800623
624 setSearchBoxHint();
Eric Erfanian10b34a52017-05-04 08:23:17 -0700625 timeTabSelected = SystemClock.elapsedRealtime();
Eric Erfanianccca3152017-02-22 16:32:36 -0800626
linyuh183cb712017-12-27 17:02:37 -0800627 p13nLogger.reset();
628 p13nRanker.refresh(
Eric Erfanianccca3152017-02-22 16:32:36 -0800629 new P13nRefreshCompleteListener() {
630 @Override
631 public void onP13nRefreshComplete() {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700632 // TODO(strongarm): make zero-query search results visible
Eric Erfanianccca3152017-02-22 16:32:36 -0800633 }
634 });
635 Trace.endSection();
636 }
637
638 @Override
639 protected void onRestart() {
640 super.onRestart();
linyuh183cb712017-12-27 17:02:37 -0800641 isRestarting = true;
Eric Erfanianccca3152017-02-22 16:32:36 -0800642 }
643
644 @Override
645 protected void onPause() {
linyuh183cb712017-12-27 17:02:37 -0800646 if (clearSearchOnPause) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800647 hideDialpadAndSearchUi();
linyuh183cb712017-12-27 17:02:37 -0800648 clearSearchOnPause = false;
Eric Erfanianccca3152017-02-22 16:32:36 -0800649 }
linyuh183cb712017-12-27 17:02:37 -0800650 if (slideOut.hasStarted() && !slideOut.hasEnded()) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800651 commitDialpadFragmentHide();
652 }
653 super.onPause();
654 }
655
656 @Override
Eric Erfaniand8046e52017-04-06 09:41:50 -0700657 protected void onStop() {
658 super.onStop();
659 boolean timeoutElapsed =
660 SystemClock.elapsedRealtime() - timeTabSelected >= HISTORY_TAB_SEEN_TIMEOUT;
661 boolean isOnHistoryTab =
linyuh183cb712017-12-27 17:02:37 -0800662 listsFragment.getCurrentTabIndex() == DialtactsPagerAdapter.TAB_INDEX_HISTORY;
Eric Erfanian10b34a52017-05-04 08:23:17 -0700663 if (isOnHistoryTab
664 && timeoutElapsed
665 && !isChangingConfigurations()
666 && !getSystemService(KeyguardManager.class).isKeyguardLocked()) {
linyuh183cb712017-12-27 17:02:37 -0800667 listsFragment.markMissedCallsAsReadAndRemoveNotifications();
Eric Erfaniand8046e52017-04-06 09:41:50 -0700668 }
linyuh168d09c2017-10-26 17:04:30 -0700669 StorageComponent.get(this)
670 .unencryptedSharedPrefs()
Eric Erfanian2ca43182017-08-31 06:57:16 -0700671 .edit()
linyuh183cb712017-12-27 17:02:37 -0800672 .putInt(KEY_LAST_TAB, listsFragment.getCurrentTabIndex())
Eric Erfanian2ca43182017-08-31 06:57:16 -0700673 .apply();
Eric Erfaniand8046e52017-04-06 09:41:50 -0700674 }
675
676 @Override
Eric Erfanianccca3152017-02-22 16:32:36 -0800677 protected void onSaveInstanceState(Bundle outState) {
zachh6050ec22017-09-21 16:02:35 -0700678 LogUtil.enterBlock("DialtactsActivity.onSaveInstanceState");
Eric Erfanianccca3152017-02-22 16:32:36 -0800679 super.onSaveInstanceState(outState);
linyuh183cb712017-12-27 17:02:37 -0800680 outState.putString(KEY_SEARCH_QUERY, searchQuery);
681 outState.putString(KEY_DIALPAD_QUERY, dialpadQuery);
linyuhc9b35812017-12-28 09:58:34 -0800682 outState.putString(KEY_SAVED_LANGUAGE_CODE, CompatUtils.getLocale(this).getISO3Language());
linyuh183cb712017-12-27 17:02:37 -0800683 outState.putBoolean(KEY_IN_REGULAR_SEARCH_UI, inRegularSearch);
684 outState.putBoolean(KEY_IN_DIALPAD_SEARCH_UI, inDialpadSearch);
685 outState.putBoolean(KEY_IN_NEW_SEARCH_UI, inNewSearch);
686 outState.putBoolean(KEY_FIRST_LAUNCH, firstLaunch);
687 outState.putBoolean(KEY_IS_DIALPAD_SHOWN, isDialpadShown);
688 outState.putBoolean(KEY_FAB_VISIBLE, floatingActionButtonController.isVisible());
Eric Erfanianccca3152017-02-22 16:32:36 -0800689 outState.putBoolean(KEY_WAS_CONFIGURATION_CHANGE, isChangingConfigurations());
linyuh183cb712017-12-27 17:02:37 -0800690 actionBarController.saveInstanceState(outState);
691 stateSaved = true;
Eric Erfanianccca3152017-02-22 16:32:36 -0800692 }
693
694 @Override
695 public void onAttachFragment(final Fragment fragment) {
zachhcc2cde02017-08-31 12:34:18 -0700696 LogUtil.i("DialtactsActivity.onAttachFragment", "fragment: %s", fragment);
Eric Erfanianccca3152017-02-22 16:32:36 -0800697 if (fragment instanceof DialpadFragment) {
linyuh183cb712017-12-27 17:02:37 -0800698 dialpadFragment = (DialpadFragment) fragment;
Eric Erfanianccca3152017-02-22 16:32:36 -0800699 } else if (fragment instanceof SmartDialSearchFragment) {
linyuh183cb712017-12-27 17:02:37 -0800700 smartDialSearchFragment = (SmartDialSearchFragment) fragment;
701 smartDialSearchFragment.setOnPhoneNumberPickerActionListener(this);
702 if (!TextUtils.isEmpty(dialpadQuery)) {
703 smartDialSearchFragment.setAddToContactNumber(dialpadQuery);
Eric Erfanianccca3152017-02-22 16:32:36 -0800704 }
705 } else if (fragment instanceof SearchFragment) {
linyuh183cb712017-12-27 17:02:37 -0800706 regularSearchFragment = (RegularSearchFragment) fragment;
707 regularSearchFragment.setOnPhoneNumberPickerActionListener(this);
Eric Erfanianccca3152017-02-22 16:32:36 -0800708 } else if (fragment instanceof ListsFragment) {
linyuh183cb712017-12-27 17:02:37 -0800709 listsFragment = (ListsFragment) fragment;
710 listsFragment.addOnPageChangeListener(this);
Eric Erfanian2ca43182017-08-31 06:57:16 -0700711 } else if (fragment instanceof NewSearchFragment) {
linyuh183cb712017-12-27 17:02:37 -0800712 newSearchFragment = (NewSearchFragment) fragment;
Eric Erfanian2ca43182017-08-31 06:57:16 -0700713 updateSearchFragmentPosition();
Eric Erfanianccca3152017-02-22 16:32:36 -0800714 }
715 if (fragment instanceof SearchFragment) {
716 final SearchFragment searchFragment = (SearchFragment) fragment;
717 searchFragment.setReranker(
718 new CursorReranker() {
719 @Override
720 @MainThread
721 public Cursor rerankCursor(Cursor data) {
722 Assert.isMainThread();
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700723 String queryString = searchFragment.getQueryString();
linyuh183cb712017-12-27 17:02:37 -0800724 return p13nRanker.rankCursor(data, queryString == null ? 0 : queryString.length());
Eric Erfanianccca3152017-02-22 16:32:36 -0800725 }
726 });
727 searchFragment.addOnLoadFinishedListener(
728 new OnLoadFinishedListener() {
729 @Override
730 public void onLoadFinished() {
linyuh183cb712017-12-27 17:02:37 -0800731 p13nLogger.onSearchQuery(
Eric Erfanianccca3152017-02-22 16:32:36 -0800732 searchFragment.getQueryString(),
733 (PhoneNumberListAdapter) searchFragment.getAdapter());
734 }
735 });
736 }
737 }
738
739 protected void handleMenuSettings() {
740 final Intent intent = new Intent(this, DialerSettingsActivity.class);
741 startActivity(intent);
742 }
743
Android Dialer3f891692017-10-25 11:43:41 -0700744 public boolean isListsFragmentVisible() {
linyuh183cb712017-12-27 17:02:37 -0800745 return listsFragment.getUserVisibleHint();
Android Dialer3f4a3192017-09-20 17:59:44 -0700746 }
747
Eric Erfanianccca3152017-02-22 16:32:36 -0800748 @Override
749 public void onClick(View view) {
750 int resId = view.getId();
751 if (resId == R.id.floating_action_button) {
linyuh183cb712017-12-27 17:02:37 -0800752 if (!isDialpadShown) {
yueg49df7f62017-09-12 15:15:27 -0700753 LogUtil.i(
754 "DialtactsActivity.onClick", "floating action button clicked, going to show dialpad");
Eric Erfanian2ca43182017-08-31 06:57:16 -0700755 PerformanceReport.recordClick(UiAction.Type.OPEN_DIALPAD);
linyuh183cb712017-12-27 17:02:37 -0800756 inCallDialpadUp = false;
Eric Erfanianccca3152017-02-22 16:32:36 -0800757 showDialpadFragment(true);
Eric Erfanian90508232017-03-24 09:31:16 -0700758 PostCall.closePrompt();
yueg49df7f62017-09-12 15:15:27 -0700759 } else {
760 LogUtil.i(
761 "DialtactsActivity.onClick",
762 "floating action button clicked, but dialpad is already showing");
Eric Erfanianccca3152017-02-22 16:32:36 -0800763 }
764 } else if (resId == R.id.voice_search_button) {
765 try {
766 startActivityForResult(
767 new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH),
Eric Erfanian2ca43182017-08-31 06:57:16 -0700768 ActivityRequestCodes.DIALTACTS_VOICE_SEARCH);
Eric Erfanianccca3152017-02-22 16:32:36 -0800769 } catch (ActivityNotFoundException e) {
770 Toast.makeText(
771 DialtactsActivity.this, R.string.voice_search_not_available, Toast.LENGTH_SHORT)
772 .show();
773 }
774 } else if (resId == R.id.dialtacts_options_menu_button) {
linyuh183cb712017-12-27 17:02:37 -0800775 overflowMenu.show();
Eric Erfanianccca3152017-02-22 16:32:36 -0800776 } else {
777 Assert.fail("Unexpected onClick event from " + view);
778 }
779 }
780
781 @Override
782 public boolean onMenuItemClick(MenuItem item) {
783 if (!isSafeToCommitTransactions()) {
784 return true;
785 }
786
787 int resId = item.getItemId();
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700788 if (resId == R.id.menu_history) {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700789 PerformanceReport.recordClick(UiAction.Type.OPEN_CALL_HISTORY);
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700790 final Intent intent = new Intent(this, CallLogActivity.class);
791 startActivity(intent);
Eric Erfanianccca3152017-02-22 16:32:36 -0800792 } else if (resId == R.id.menu_clear_frequents) {
793 ClearFrequentsDialog.show(getFragmentManager());
794 Logger.get(this).logScreenView(ScreenEvent.Type.CLEAR_FREQUENTS, this);
795 return true;
796 } else if (resId == R.id.menu_call_settings) {
797 handleMenuSettings();
798 Logger.get(this).logScreenView(ScreenEvent.Type.SETTINGS, this);
799 return true;
Eric Erfanian2ca43182017-08-31 06:57:16 -0700800 } else if (resId == R.id.menu_new_ui_launcher_shortcut) {
calderwoodra9f16e172018-01-29 19:18:14 -0800801 MainComponent.createNewUiLauncherShortcut(this);
Eric Erfanian2ca43182017-08-31 06:57:16 -0700802 return true;
Eric Erfanianccca3152017-02-22 16:32:36 -0800803 }
804 return false;
805 }
806
807 @Override
808 protected void onActivityResult(int requestCode, int resultCode, Intent data) {
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700809 LogUtil.i(
810 "DialtactsActivity.onActivityResult",
811 "requestCode:%d, resultCode:%d",
812 requestCode,
813 resultCode);
Eric Erfanian2ca43182017-08-31 06:57:16 -0700814 if (requestCode == ActivityRequestCodes.DIALTACTS_VOICE_SEARCH) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800815 if (resultCode == RESULT_OK) {
816 final ArrayList<String> matches =
817 data.getStringArrayListExtra(RecognizerIntent.EXTRA_RESULTS);
818 if (matches.size() > 0) {
linyuh183cb712017-12-27 17:02:37 -0800819 voiceSearchQuery = matches.get(0);
Eric Erfanianccca3152017-02-22 16:32:36 -0800820 } else {
821 LogUtil.i("DialtactsActivity.onActivityResult", "voice search - nothing heard");
822 }
823 } else {
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700824 LogUtil.e("DialtactsActivity.onActivityResult", "voice search failed");
Eric Erfanianccca3152017-02-22 16:32:36 -0800825 }
Eric Erfanian2ca43182017-08-31 06:57:16 -0700826 } else if (requestCode == ActivityRequestCodes.DIALTACTS_CALL_COMPOSER) {
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700827 if (resultCode == RESULT_FIRST_USER) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800828 LogUtil.i(
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700829 "DialtactsActivity.onActivityResult", "returned from call composer, error occurred");
Eric Erfanianccca3152017-02-22 16:32:36 -0800830 String message =
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700831 getString(
832 R.string.call_composer_connection_failed,
833 data.getStringExtra(CallComposerActivity.KEY_CONTACT_NAME));
linyuh183cb712017-12-27 17:02:37 -0800834 Snackbar.make(parentLayout, message, Snackbar.LENGTH_LONG).show();
Eric Erfanianccca3152017-02-22 16:32:36 -0800835 } else {
836 LogUtil.i("DialtactsActivity.onActivityResult", "returned from call composer, no error");
837 }
Eric Erfanian2ca43182017-08-31 06:57:16 -0700838 } else if (requestCode == ActivityRequestCodes.DIALTACTS_CALL_DETAILS) {
839 if (resultCode == RESULT_OK
840 && data != null
841 && data.getBooleanExtra(CallDetailsActivity.EXTRA_HAS_ENRICHED_CALL_DATA, false)) {
842 String number = data.getStringExtra(CallDetailsActivity.EXTRA_PHONE_NUMBER);
843 int snackbarDurationMillis = 5_000;
linyuh183cb712017-12-27 17:02:37 -0800844 Snackbar.make(parentLayout, getString(R.string.ec_data_deleted), snackbarDurationMillis)
Eric Erfanian2ca43182017-08-31 06:57:16 -0700845 .setAction(
846 R.string.view_conversation,
847 v -> startActivity(IntentProvider.getSendSmsIntentProvider(number).getIntent(this)))
848 .setActionTextColor(getResources().getColor(R.color.dialer_snackbar_action_text_color))
849 .show();
850 }
roldenburg19c6c252017-12-13 15:58:01 -0800851 } else if (requestCode == ActivityRequestCodes.DIALTACTS_DUO) {
852 // We just returned from starting Duo for a task. Reload our reachability data since it
853 // may have changed after a user finished activating Duo.
854 DuoComponent.get(this).getDuo().reloadReachability(this);
Eric Erfanianccca3152017-02-22 16:32:36 -0800855 }
856 super.onActivityResult(requestCode, resultCode, data);
857 }
858
859 /**
860 * Update the number of unread voicemails (potentially other tabs) displayed next to the tab icon.
861 */
862 public void updateTabUnreadCounts() {
linyuh183cb712017-12-27 17:02:37 -0800863 listsFragment.updateTabUnreadCounts();
Eric Erfanianccca3152017-02-22 16:32:36 -0800864 }
865
866 /**
867 * Initiates a fragment transaction to show the dialpad fragment. Animations and other visual
868 * updates are handled by a callback which is invoked after the dialpad fragment is shown.
869 *
870 * @see #onDialpadShown
871 */
872 private void showDialpadFragment(boolean animate) {
zachhcc2cde02017-08-31 12:34:18 -0700873 LogUtil.i("DialtactActivity.showDialpadFragment", "animate: %b", animate);
linyuh183cb712017-12-27 17:02:37 -0800874 if (isDialpadShown) {
zachhcc2cde02017-08-31 12:34:18 -0700875 LogUtil.i("DialtactsActivity.showDialpadFragment", "dialpad already shown");
876 return;
877 }
linyuh183cb712017-12-27 17:02:37 -0800878 if (stateSaved) {
zachhcc2cde02017-08-31 12:34:18 -0700879 LogUtil.i("DialtactsActivity.showDialpadFragment", "state already saved");
Eric Erfanianccca3152017-02-22 16:32:36 -0800880 return;
881 }
linyuh183cb712017-12-27 17:02:37 -0800882 isDialpadShown = true;
Eric Erfanianccca3152017-02-22 16:32:36 -0800883
linyuh183cb712017-12-27 17:02:37 -0800884 listsFragment.setUserVisibleHint(false);
Eric Erfanianccca3152017-02-22 16:32:36 -0800885
886 final FragmentTransaction ft = getFragmentManager().beginTransaction();
linyuh183cb712017-12-27 17:02:37 -0800887 if (dialpadFragment == null) {
888 dialpadFragment = new DialpadFragment();
889 ft.add(R.id.dialtacts_container, dialpadFragment, TAG_DIALPAD_FRAGMENT);
Eric Erfanianccca3152017-02-22 16:32:36 -0800890 } else {
linyuh183cb712017-12-27 17:02:37 -0800891 ft.show(dialpadFragment);
Eric Erfanianccca3152017-02-22 16:32:36 -0800892 }
893
linyuh183cb712017-12-27 17:02:37 -0800894 dialpadFragment.setAnimate(animate);
Eric Erfanianccca3152017-02-22 16:32:36 -0800895 Logger.get(this).logScreenView(ScreenEvent.Type.DIALPAD, this);
896 ft.commit();
897
898 if (animate) {
linyuh183cb712017-12-27 17:02:37 -0800899 floatingActionButtonController.scaleOut();
Eric Erfanian2ca43182017-08-31 06:57:16 -0700900 maybeEnterSearchUi();
Eric Erfanianccca3152017-02-22 16:32:36 -0800901 } else {
linyuh183cb712017-12-27 17:02:37 -0800902 floatingActionButtonController.scaleOut();
Eric Erfanianccca3152017-02-22 16:32:36 -0800903 maybeEnterSearchUi();
904 }
linyuh183cb712017-12-27 17:02:37 -0800905 actionBarController.onDialpadUp();
Eric Erfanianccca3152017-02-22 16:32:36 -0800906
linyuh183cb712017-12-27 17:02:37 -0800907 Assert.isNotNull(listsFragment.getView()).animate().alpha(0).withLayer();
Eric Erfanianccca3152017-02-22 16:32:36 -0800908
Eric Erfanian2ca43182017-08-31 06:57:16 -0700909 // adjust the title, so the user will know where we're at when the activity start/resumes.
Eric Erfanianccca3152017-02-22 16:32:36 -0800910 setTitle(R.string.launcherDialpadActivityLabel);
911 }
912
Eric Erfanian2ca43182017-08-31 06:57:16 -0700913 @Override
914 public void getLastOutgoingCall(LastOutgoingCallCallback callback) {
calderwoodra97d0b5e2018-01-26 12:11:26 -0800915 DialerExecutorComponent.get(this)
916 .dialerExecutorFactory()
917 .createUiTaskBuilder(
918 getFragmentManager(), "Query last phone number", Calls::getLastOutgoingCall)
919 .onSuccess(output -> callback.lastOutgoingCall(output))
920 .build()
921 .executeParallel(this);
Eric Erfanian2ca43182017-08-31 06:57:16 -0700922 }
923
Eric Erfanianccca3152017-02-22 16:32:36 -0800924 /** Callback from child DialpadFragment when the dialpad is shown. */
Eric Erfanian2ca43182017-08-31 06:57:16 -0700925 @Override
Eric Erfanianccca3152017-02-22 16:32:36 -0800926 public void onDialpadShown() {
zachhcc2cde02017-08-31 12:34:18 -0700927 LogUtil.enterBlock("DialtactsActivity.onDialpadShown");
linyuh183cb712017-12-27 17:02:37 -0800928 Assert.isNotNull(dialpadFragment);
929 if (dialpadFragment.getAnimate()) {
930 Assert.isNotNull(dialpadFragment.getView()).startAnimation(slideIn);
Eric Erfanianccca3152017-02-22 16:32:36 -0800931 } else {
linyuh183cb712017-12-27 17:02:37 -0800932 dialpadFragment.setYFraction(0);
Eric Erfanianccca3152017-02-22 16:32:36 -0800933 }
934
935 updateSearchFragmentPosition();
936 }
937
calderwoodra50715262017-09-08 09:20:08 -0700938 @Override
939 public void onCallPlacedFromDialpad() {
linyuh183cb712017-12-27 17:02:37 -0800940 clearSearchOnPause = true;
calderwoodra50715262017-09-08 09:20:08 -0700941 }
942
Android Dialer3f4a3192017-09-20 17:59:44 -0700943 @Override
Android Dialere8d77522017-10-16 11:33:58 -0700944 public void onContactsListScrolled(boolean isDragging) {
Android Dialer3f4a3192017-09-20 17:59:44 -0700945 // intentionally empty.
946 }
947
Eric Erfanianccca3152017-02-22 16:32:36 -0800948 /**
949 * Initiates animations and other visual updates to hide the dialpad. The fragment is hidden in a
950 * callback after the hide animation ends.
951 *
952 * @see #commitDialpadFragmentHide
953 */
calderwoodra50715262017-09-08 09:20:08 -0700954 private void hideDialpadFragment(boolean animate, boolean clearDialpad) {
zachhcc2cde02017-08-31 12:34:18 -0700955 LogUtil.enterBlock("DialtactsActivity.hideDialpadFragment");
linyuh183cb712017-12-27 17:02:37 -0800956 if (dialpadFragment == null || dialpadFragment.getView() == null) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800957 return;
958 }
959 if (clearDialpad) {
960 // Temporarily disable accessibility when we clear the dialpad, since it should be
961 // invisible and should not announce anything.
linyuh183cb712017-12-27 17:02:37 -0800962 dialpadFragment
Eric Erfanianccca3152017-02-22 16:32:36 -0800963 .getDigitsWidget()
964 .setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO);
linyuh183cb712017-12-27 17:02:37 -0800965 dialpadFragment.clearDialpad();
966 dialpadFragment
Eric Erfanianccca3152017-02-22 16:32:36 -0800967 .getDigitsWidget()
968 .setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_AUTO);
969 }
linyuh183cb712017-12-27 17:02:37 -0800970 if (!isDialpadShown) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800971 return;
972 }
linyuh183cb712017-12-27 17:02:37 -0800973 isDialpadShown = false;
974 dialpadFragment.setAnimate(animate);
975 listsFragment.setUserVisibleHint(true);
976 listsFragment.sendScreenViewForCurrentPosition();
Eric Erfanianccca3152017-02-22 16:32:36 -0800977
978 updateSearchFragmentPosition();
979
linyuh183cb712017-12-27 17:02:37 -0800980 floatingActionButtonController.align(getFabAlignment(), animate);
Eric Erfanianccca3152017-02-22 16:32:36 -0800981 if (animate) {
linyuh183cb712017-12-27 17:02:37 -0800982 dialpadFragment.getView().startAnimation(slideOut);
Eric Erfanianccca3152017-02-22 16:32:36 -0800983 } else {
984 commitDialpadFragmentHide();
985 }
986
linyuh183cb712017-12-27 17:02:37 -0800987 actionBarController.onDialpadDown();
Eric Erfanianccca3152017-02-22 16:32:36 -0800988
Eric Erfanian2ca43182017-08-31 06:57:16 -0700989 // reset the title to normal.
Eric Erfanianccca3152017-02-22 16:32:36 -0800990 setTitle(R.string.launcherActivityLabel);
991 }
992
993 /** Finishes hiding the dialpad fragment after any animations are completed. */
994 private void commitDialpadFragmentHide() {
linyuh183cb712017-12-27 17:02:37 -0800995 if (!stateSaved && dialpadFragment != null && !dialpadFragment.isHidden() && !isDestroyed()) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800996 final FragmentTransaction ft = getFragmentManager().beginTransaction();
linyuh183cb712017-12-27 17:02:37 -0800997 ft.hide(dialpadFragment);
Eric Erfanianccca3152017-02-22 16:32:36 -0800998 ft.commit();
999 }
linyuh183cb712017-12-27 17:02:37 -08001000 floatingActionButtonController.scaleIn();
Eric Erfanianccca3152017-02-22 16:32:36 -08001001 }
1002
1003 private void updateSearchFragmentPosition() {
1004 SearchFragment fragment = null;
linyuh183cb712017-12-27 17:02:37 -08001005 if (smartDialSearchFragment != null) {
1006 fragment = smartDialSearchFragment;
1007 } else if (regularSearchFragment != null) {
1008 fragment = regularSearchFragment;
Eric Erfanianccca3152017-02-22 16:32:36 -08001009 }
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001010 LogUtil.d(
1011 "DialtactsActivity.updateSearchFragmentPosition",
1012 "fragment: %s, isVisible: %b",
1013 fragment,
1014 fragment != null && fragment.isVisible());
1015 if (fragment != null) {
1016 // We need to force animation here even when fragment is not visible since it might not be
1017 // visible immediately after screen orientation change and dialpad height would not be
1018 // available immediately which is required to update position. By forcing an animation,
1019 // position will be updated after a delay by when the dialpad height would be available.
Eric Erfanianccca3152017-02-22 16:32:36 -08001020 fragment.updatePosition(true /* animate */);
linyuh183cb712017-12-27 17:02:37 -08001021 } else if (newSearchFragment != null) {
Eric Erfanian2ca43182017-08-31 06:57:16 -07001022 int animationDuration = getResources().getInteger(R.integer.dialpad_slide_in_duration);
1023 int actionbarHeight = getResources().getDimensionPixelSize(R.dimen.action_bar_height_large);
1024 int shadowHeight = getResources().getDrawable(R.drawable.search_shadow).getIntrinsicHeight();
1025 int start = isDialpadShown() ? actionbarHeight - shadowHeight : 0;
1026 int end = isDialpadShown() ? 0 : actionbarHeight - shadowHeight;
linyuh183cb712017-12-27 17:02:37 -08001027 newSearchFragment.animatePosition(start, end, animationDuration);
Eric Erfanianccca3152017-02-22 16:32:36 -08001028 }
1029 }
1030
1031 @Override
1032 public boolean isInSearchUi() {
linyuh183cb712017-12-27 17:02:37 -08001033 return inDialpadSearch || inRegularSearch || inNewSearch;
Eric Erfanianccca3152017-02-22 16:32:36 -08001034 }
1035
1036 @Override
1037 public boolean hasSearchQuery() {
linyuh183cb712017-12-27 17:02:37 -08001038 return !TextUtils.isEmpty(searchQuery);
Eric Erfanianccca3152017-02-22 16:32:36 -08001039 }
1040
Eric Erfanianccca3152017-02-22 16:32:36 -08001041 private void setNotInSearchUi() {
linyuh183cb712017-12-27 17:02:37 -08001042 inDialpadSearch = false;
1043 inRegularSearch = false;
1044 inNewSearch = false;
Eric Erfanianccca3152017-02-22 16:32:36 -08001045 }
1046
1047 private void hideDialpadAndSearchUi() {
linyuh183cb712017-12-27 17:02:37 -08001048 if (isDialpadShown) {
Eric Erfanianccca3152017-02-22 16:32:36 -08001049 hideDialpadFragment(false, true);
Eric Erfanianccca3152017-02-22 16:32:36 -08001050 }
calderwoodrae51ca382017-11-21 10:54:36 -08001051 exitSearchUi();
Eric Erfanianccca3152017-02-22 16:32:36 -08001052 }
1053
1054 private void prepareVoiceSearchButton() {
linyuh183cb712017-12-27 17:02:37 -08001055 searchEditTextLayout.setVoiceSearchEnabled(isVoiceSearchEnabled());
1056 voiceSearchButton.setOnClickListener(this);
twyen891fe0f2017-10-17 14:59:15 -07001057 }
1058
1059 private boolean isVoiceSearchEnabled() {
linyuh183cb712017-12-27 17:02:37 -08001060 if (voiceSearchEnabledForTest.isPresent()) {
1061 return voiceSearchEnabledForTest.get();
Eric Erfanianccca3152017-02-22 16:32:36 -08001062 }
twyen891fe0f2017-10-17 14:59:15 -07001063 return canIntentBeHandled(new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH));
Eric Erfanianccca3152017-02-22 16:32:36 -08001064 }
1065
1066 public boolean isNearbyPlacesSearchEnabled() {
1067 return false;
1068 }
1069
1070 protected int getSearchBoxHint() {
1071 return R.string.dialer_hint_find_contact;
1072 }
1073
1074 /** Sets the hint text for the contacts search box */
1075 private void setSearchBoxHint() {
linyuh183cb712017-12-27 17:02:37 -08001076 ((TextView) searchEditTextLayout.findViewById(R.id.search_box_start_search))
Eric Erfanianccca3152017-02-22 16:32:36 -08001077 .setHint(getSearchBoxHint());
1078 }
1079
1080 protected OptionsPopupMenu buildOptionsMenu(View invoker) {
1081 final OptionsPopupMenu popupMenu = new OptionsPopupMenu(this, invoker);
1082 popupMenu.inflate(R.menu.dialtacts_options);
1083 popupMenu.setOnMenuItemClickListener(this);
1084 return popupMenu;
1085 }
1086
1087 @Override
1088 public boolean onCreateOptionsMenu(Menu menu) {
linyuh183cb712017-12-27 17:02:37 -08001089 if (pendingSearchViewQuery != null) {
1090 searchView.setText(pendingSearchViewQuery);
1091 pendingSearchViewQuery = null;
Eric Erfanianccca3152017-02-22 16:32:36 -08001092 }
linyuh183cb712017-12-27 17:02:37 -08001093 if (actionBarController != null) {
1094 actionBarController.restoreActionBarOffset();
Eric Erfanianccca3152017-02-22 16:32:36 -08001095 }
1096 return false;
1097 }
1098
1099 /**
1100 * Returns true if the intent is due to hitting the green send key (hardware call button:
1101 * KEYCODE_CALL) while in a call.
1102 *
1103 * @param intent the intent that launched this activity
1104 * @return true if the intent is due to hitting the green send key while in a call
1105 */
1106 private boolean isSendKeyWhileInCall(Intent intent) {
1107 // If there is a call in progress and the user launched the dialer by hitting the call
1108 // button, go straight to the in-call screen.
1109 final boolean callKey = Intent.ACTION_CALL_BUTTON.equals(intent.getAction());
1110
1111 // When KEYCODE_CALL event is handled it dispatches an intent with the ACTION_CALL_BUTTON.
1112 // Besides of checking the intent action, we must check if the phone is really during a
1113 // call in order to decide whether to ignore the event or continue to display the activity.
1114 if (callKey && phoneIsInUse()) {
1115 TelecomUtil.showInCallScreen(this, false);
1116 return true;
1117 }
1118
1119 return false;
1120 }
1121
1122 /**
1123 * Sets the current tab based on the intent's request type
1124 *
1125 * @param intent Intent that contains information about which tab should be selected
1126 */
1127 private void displayFragment(Intent intent) {
1128 // If we got here by hitting send and we're in call forward along to the in-call activity
1129 if (isSendKeyWhileInCall(intent)) {
1130 finish();
1131 return;
1132 }
1133
zachhcc2cde02017-08-31 12:34:18 -07001134 boolean showDialpadChooser =
Eric Erfanianccca3152017-02-22 16:32:36 -08001135 !ACTION_SHOW_TAB.equals(intent.getAction())
1136 && phoneIsInUse()
1137 && !DialpadFragment.isAddCallMode(intent);
zachhcc2cde02017-08-31 12:34:18 -07001138 boolean isDialIntent = intent.getData() != null && isDialIntent(intent);
calderwoodra48cce922017-09-14 14:28:46 -07001139 boolean isAddCallIntent = DialpadFragment.isAddCallMode(intent);
1140 if (showDialpadChooser || isDialIntent || isAddCallIntent) {
zachhcc2cde02017-08-31 12:34:18 -07001141 LogUtil.i(
1142 "DialtactsActivity.displayFragment",
calderwoodra48cce922017-09-14 14:28:46 -07001143 "show dialpad fragment (showDialpadChooser: %b, isDialIntent: %b, isAddCallIntent: %b)",
zachhcc2cde02017-08-31 12:34:18 -07001144 showDialpadChooser,
calderwoodra48cce922017-09-14 14:28:46 -07001145 isDialIntent,
1146 isAddCallIntent);
Eric Erfanianccca3152017-02-22 16:32:36 -08001147 showDialpadFragment(false);
linyuh183cb712017-12-27 17:02:37 -08001148 dialpadFragment.setStartedFromNewIntent(true);
1149 if (showDialpadChooser && !dialpadFragment.isVisible()) {
1150 inCallDialpadUp = true;
Eric Erfanianccca3152017-02-22 16:32:36 -08001151 }
Eric Erfanian2ca43182017-08-31 06:57:16 -07001152 } else if (isLastTabEnabled) {
1153 @TabIndex
1154 int tabIndex =
linyuh168d09c2017-10-26 17:04:30 -07001155 StorageComponent.get(this)
1156 .unencryptedSharedPrefs()
Eric Erfanian2ca43182017-08-31 06:57:16 -07001157 .getInt(KEY_LAST_TAB, DialtactsPagerAdapter.TAB_INDEX_SPEED_DIAL);
1158 // If voicemail tab is saved and its availability changes, we still move to the voicemail tab
1159 // but it is quickly removed and shown the contacts tab.
linyuh183cb712017-12-27 17:02:37 -08001160 if (listsFragment != null) {
1161 listsFragment.showTab(tabIndex);
Eric Erfanian2ca43182017-08-31 06:57:16 -07001162 PerformanceReport.setStartingTabIndex(tabIndex);
1163 } else {
1164 PerformanceReport.setStartingTabIndex(DialtactsPagerAdapter.TAB_INDEX_SPEED_DIAL);
1165 }
Eric Erfanianccca3152017-02-22 16:32:36 -08001166 }
1167 }
1168
1169 @Override
1170 public void onNewIntent(Intent newIntent) {
zachhcc2cde02017-08-31 12:34:18 -07001171 LogUtil.enterBlock("DialtactsActivity.onNewIntent");
Eric Erfanianccca3152017-02-22 16:32:36 -08001172 setIntent(newIntent);
linyuh183cb712017-12-27 17:02:37 -08001173 firstLaunch = true;
Eric Erfanianccca3152017-02-22 16:32:36 -08001174
linyuh183cb712017-12-27 17:02:37 -08001175 stateSaved = false;
Eric Erfanianccca3152017-02-22 16:32:36 -08001176 displayFragment(newIntent);
1177
1178 invalidateOptionsMenu();
1179 }
1180
1181 /** Returns true if the given intent contains a phone number to populate the dialer with */
1182 private boolean isDialIntent(Intent intent) {
1183 final String action = intent.getAction();
1184 if (Intent.ACTION_DIAL.equals(action) || ACTION_TOUCH_DIALER.equals(action)) {
1185 return true;
1186 }
1187 if (Intent.ACTION_VIEW.equals(action)) {
1188 final Uri data = intent.getData();
1189 if (data != null && PhoneAccount.SCHEME_TEL.equals(data.getScheme())) {
1190 return true;
1191 }
1192 }
1193 return false;
1194 }
1195
1196 /** Shows the search fragment */
1197 private void enterSearchUi(boolean smartDialSearch, String query, boolean animate) {
zachhcc2cde02017-08-31 12:34:18 -07001198 LogUtil.i("DialtactsActivity.enterSearchUi", "smart dial: %b", smartDialSearch);
linyuh183cb712017-12-27 17:02:37 -08001199 if (stateSaved || getFragmentManager().isDestroyed()) {
Eric Erfanianccca3152017-02-22 16:32:36 -08001200 // Weird race condition where fragment is doing work after the activity is destroyed
Eric Erfanian938468d2017-10-24 14:05:52 -07001201 // due to talkback being on (a bug). Just return since we can't do any
Eric Erfanianccca3152017-02-22 16:32:36 -08001202 // constructive here.
zachhcc2cde02017-08-31 12:34:18 -07001203 LogUtil.i(
1204 "DialtactsActivity.enterSearchUi",
1205 "not entering search UI (mStateSaved: %b, isDestroyed: %b)",
linyuh183cb712017-12-27 17:02:37 -08001206 stateSaved,
zachhcc2cde02017-08-31 12:34:18 -07001207 getFragmentManager().isDestroyed());
Eric Erfanianccca3152017-02-22 16:32:36 -08001208 return;
1209 }
1210
Eric Erfanianccca3152017-02-22 16:32:36 -08001211 final FragmentTransaction transaction = getFragmentManager().beginTransaction();
linyuh183cb712017-12-27 17:02:37 -08001212 if (inDialpadSearch && smartDialSearchFragment != null) {
1213 transaction.remove(smartDialSearchFragment);
1214 } else if (inRegularSearch && regularSearchFragment != null) {
1215 transaction.remove(regularSearchFragment);
Eric Erfanianccca3152017-02-22 16:32:36 -08001216 }
1217
1218 final String tag;
linyuh183cb712017-12-27 17:02:37 -08001219 inDialpadSearch = false;
1220 inRegularSearch = false;
1221 inNewSearch = false;
Eric Erfanian2ca43182017-08-31 06:57:16 -07001222 boolean useNewSearch =
1223 ConfigProviderBindings.get(this).getBoolean("enable_new_search_fragment", false);
1224 if (useNewSearch) {
1225 tag = TAG_NEW_SEARCH_FRAGMENT;
linyuh183cb712017-12-27 17:02:37 -08001226 inNewSearch = true;
Eric Erfanian2ca43182017-08-31 06:57:16 -07001227 } else if (smartDialSearch) {
Eric Erfanianccca3152017-02-22 16:32:36 -08001228 tag = TAG_SMARTDIAL_SEARCH_FRAGMENT;
linyuh183cb712017-12-27 17:02:37 -08001229 inDialpadSearch = true;
Eric Erfanianccca3152017-02-22 16:32:36 -08001230 } else {
1231 tag = TAG_REGULAR_SEARCH_FRAGMENT;
linyuh183cb712017-12-27 17:02:37 -08001232 inRegularSearch = true;
Eric Erfanianccca3152017-02-22 16:32:36 -08001233 }
Eric Erfanianccca3152017-02-22 16:32:36 -08001234
linyuh183cb712017-12-27 17:02:37 -08001235 floatingActionButtonController.scaleOut();
Eric Erfanianccca3152017-02-22 16:32:36 -08001236
Eric Erfanianccca3152017-02-22 16:32:36 -08001237 if (animate) {
1238 transaction.setCustomAnimations(android.R.animator.fade_in, 0);
1239 } else {
1240 transaction.setTransition(FragmentTransaction.TRANSIT_NONE);
1241 }
Eric Erfanian2ca43182017-08-31 06:57:16 -07001242
1243 Fragment fragment = getFragmentManager().findFragmentByTag(tag);
Eric Erfanianccca3152017-02-22 16:32:36 -08001244 if (fragment == null) {
Eric Erfanian2ca43182017-08-31 06:57:16 -07001245 if (useNewSearch) {
calderwoodra218fff32017-09-19 11:20:31 -07001246 fragment = NewSearchFragment.newInstance(!isDialpadShown());
Eric Erfanian2ca43182017-08-31 06:57:16 -07001247 } else if (smartDialSearch) {
Eric Erfanianccca3152017-02-22 16:32:36 -08001248 fragment = new SmartDialSearchFragment();
1249 } else {
1250 fragment = Bindings.getLegacy(this).newRegularSearchFragment();
Eric Erfanian2ca43182017-08-31 06:57:16 -07001251 ((SearchFragment) fragment)
1252 .setOnTouchListener(
1253 (v, event) -> {
1254 // Show the FAB when the user touches the lists fragment and the soft
1255 // keyboard is hidden.
1256 hideDialpadFragment(true, false);
1257 v.performClick();
1258 return false;
1259 });
Eric Erfanianccca3152017-02-22 16:32:36 -08001260 }
1261 transaction.add(R.id.dialtacts_frame, fragment, tag);
1262 } else {
Eric Erfanian2ca43182017-08-31 06:57:16 -07001263 // TODO(calderwoodra): if this is a transition from dialpad to searchbar, animate fragment
1264 // down, and vice versa. Perhaps just add a coordinator behavior with the search bar.
Eric Erfanianccca3152017-02-22 16:32:36 -08001265 transaction.show(fragment);
1266 }
Eric Erfanian2ca43182017-08-31 06:57:16 -07001267
Eric Erfanianccca3152017-02-22 16:32:36 -08001268 // DialtactsActivity will provide the options menu
1269 fragment.setHasOptionsMenu(false);
Eric Erfanian2ca43182017-08-31 06:57:16 -07001270
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001271 // Will show empty list if P13nRanker is not enabled. Else, re-ranked list by the ranker.
Eric Erfanian2ca43182017-08-31 06:57:16 -07001272 if (!useNewSearch) {
1273 ((SearchFragment) fragment)
linyuh183cb712017-12-27 17:02:37 -08001274 .setShowEmptyListForNullQuery(p13nRanker.shouldShowEmptyListForNullQuery());
Eric Erfanian2ca43182017-08-31 06:57:16 -07001275 } else {
1276 // TODO(calderwoodra): add p13n ranker to new search.
1277 }
1278
1279 if (!smartDialSearch && !useNewSearch) {
1280 ((SearchFragment) fragment).setQueryString(query);
1281 } else if (useNewSearch) {
calderwoodraaf3cc172017-09-01 23:28:21 -07001282 ((NewSearchFragment) fragment).setQuery(query, getCallInitiationType());
Eric Erfanianccca3152017-02-22 16:32:36 -08001283 }
1284 transaction.commit();
1285
1286 if (animate) {
linyuh183cb712017-12-27 17:02:37 -08001287 Assert.isNotNull(listsFragment.getView()).animate().alpha(0).withLayer();
Eric Erfanianccca3152017-02-22 16:32:36 -08001288 }
linyuh183cb712017-12-27 17:02:37 -08001289 listsFragment.setUserVisibleHint(false);
Eric Erfanianccca3152017-02-22 16:32:36 -08001290
1291 if (smartDialSearch) {
1292 Logger.get(this).logScreenView(ScreenEvent.Type.SMART_DIAL_SEARCH, this);
1293 } else {
1294 Logger.get(this).logScreenView(ScreenEvent.Type.REGULAR_SEARCH, this);
1295 }
1296 }
1297
1298 /** Hides the search fragment */
1299 private void exitSearchUi() {
zachhcc2cde02017-08-31 12:34:18 -07001300 LogUtil.enterBlock("DialtactsActivity.exitSearchUi");
1301
Eric Erfanianccca3152017-02-22 16:32:36 -08001302 // See related bug in enterSearchUI();
linyuh183cb712017-12-27 17:02:37 -08001303 if (getFragmentManager().isDestroyed() || stateSaved) {
Eric Erfanianccca3152017-02-22 16:32:36 -08001304 return;
1305 }
1306
linyuh183cb712017-12-27 17:02:37 -08001307 searchView.setText(null);
Eric Erfanianccca3152017-02-22 16:32:36 -08001308
linyuh183cb712017-12-27 17:02:37 -08001309 if (dialpadFragment != null) {
1310 dialpadFragment.clearDialpad();
Eric Erfanianccca3152017-02-22 16:32:36 -08001311 }
1312
1313 setNotInSearchUi();
1314
calderwoodra6007f522017-10-09 16:59:04 -07001315 // There are four states the fab can be in:
1316 // - Not visible and should remain not visible (do nothing)
1317 // - Not visible (move then show the fab)
1318 // - Visible, in the correct position (do nothing)
1319 // - Visible, in the wrong position (hide, move, then show the fab)
linyuh183cb712017-12-27 17:02:37 -08001320 if (floatingActionButtonController.isVisible()
calderwoodra6007f522017-10-09 16:59:04 -07001321 && getFabAlignment() != FloatingActionButtonController.ALIGN_END) {
linyuh183cb712017-12-27 17:02:37 -08001322 floatingActionButtonController.scaleOut(
calderwoodra6007f522017-10-09 16:59:04 -07001323 new OnVisibilityChangedListener() {
1324 @Override
1325 public void onHidden(FloatingActionButton floatingActionButton) {
1326 super.onHidden(floatingActionButton);
1327 onPageScrolled(
linyuh183cb712017-12-27 17:02:37 -08001328 listsFragment.getCurrentTabIndex(), 0 /* offset */, 0 /* pixelOffset */);
1329 floatingActionButtonController.scaleIn();
calderwoodra6007f522017-10-09 16:59:04 -07001330 }
1331 });
linyuh183cb712017-12-27 17:02:37 -08001332 } else if (!floatingActionButtonController.isVisible() && listsFragment.shouldShowFab()) {
1333 onPageScrolled(listsFragment.getCurrentTabIndex(), 0 /* offset */, 0 /* pixelOffset */);
calderwoodra6007f522017-10-09 16:59:04 -07001334 ThreadUtil.getUiThreadHandler()
linyuh183cb712017-12-27 17:02:37 -08001335 .postDelayed(() -> floatingActionButtonController.scaleIn(), FAB_SCALE_IN_DELAY_MS);
Eric Erfanianccca3152017-02-22 16:32:36 -08001336 }
Eric Erfanianccca3152017-02-22 16:32:36 -08001337
1338 final FragmentTransaction transaction = getFragmentManager().beginTransaction();
linyuh183cb712017-12-27 17:02:37 -08001339 if (smartDialSearchFragment != null) {
1340 transaction.remove(smartDialSearchFragment);
Eric Erfanianccca3152017-02-22 16:32:36 -08001341 }
linyuh183cb712017-12-27 17:02:37 -08001342 if (regularSearchFragment != null) {
1343 transaction.remove(regularSearchFragment);
Eric Erfanianccca3152017-02-22 16:32:36 -08001344 }
linyuh183cb712017-12-27 17:02:37 -08001345 if (newSearchFragment != null) {
1346 transaction.remove(newSearchFragment);
Eric Erfanian2ca43182017-08-31 06:57:16 -07001347 }
Eric Erfanianccca3152017-02-22 16:32:36 -08001348 transaction.commit();
1349
linyuh183cb712017-12-27 17:02:37 -08001350 Assert.isNotNull(listsFragment.getView()).animate().alpha(1).withLayer();
Eric Erfanianccca3152017-02-22 16:32:36 -08001351
linyuh183cb712017-12-27 17:02:37 -08001352 if (dialpadFragment == null || !dialpadFragment.isVisible()) {
Eric Erfanianccca3152017-02-22 16:32:36 -08001353 // If the dialpad fragment wasn't previously visible, then send a screen view because
1354 // we are exiting regular search. Otherwise, the screen view will be sent by
1355 // {@link #hideDialpadFragment}.
linyuh183cb712017-12-27 17:02:37 -08001356 listsFragment.sendScreenViewForCurrentPosition();
1357 listsFragment.setUserVisibleHint(true);
Eric Erfanianccca3152017-02-22 16:32:36 -08001358 }
linyuh183cb712017-12-27 17:02:37 -08001359 onPageSelected(listsFragment.getCurrentTabIndex());
Eric Erfanianccca3152017-02-22 16:32:36 -08001360
linyuh183cb712017-12-27 17:02:37 -08001361 actionBarController.onSearchUiExited();
Eric Erfanianccca3152017-02-22 16:32:36 -08001362 }
1363
1364 @Override
1365 public void onBackPressed() {
Eric Erfanian2ca43182017-08-31 06:57:16 -07001366 PerformanceReport.recordClick(UiAction.Type.PRESS_ANDROID_BACK_BUTTON);
1367
linyuh183cb712017-12-27 17:02:37 -08001368 if (stateSaved) {
Eric Erfanianccca3152017-02-22 16:32:36 -08001369 return;
1370 }
linyuh183cb712017-12-27 17:02:37 -08001371 if (isDialpadShown) {
calderwoodraed4e11d2017-09-05 18:24:47 -07001372 hideDialpadFragment(true, false);
linyuh183cb712017-12-27 17:02:37 -08001373 if (TextUtils.isEmpty(dialpadQuery)) {
calderwoodraecb04562017-09-16 07:34:05 -07001374 exitSearchUi();
1375 }
calderwoodraed4e11d2017-09-05 18:24:47 -07001376 } else if (isInSearchUi()) {
linyuh183cb712017-12-27 17:02:37 -08001377 if (isKeyboardOpen) {
1378 DialerUtils.hideInputMethod(parentLayout);
calderwoodra50715262017-09-08 09:20:08 -07001379 PerformanceReport.recordClick(UiAction.Type.HIDE_KEYBOARD_IN_SEARCH);
1380 } else {
1381 exitSearchUi();
1382 }
Eric Erfanianccca3152017-02-22 16:32:36 -08001383 } else {
1384 super.onBackPressed();
1385 }
1386 }
1387
calderwoodra50715262017-09-08 09:20:08 -07001388 @Override
1389 public void onConfigurationChanged(Configuration configuration) {
1390 super.onConfigurationChanged(configuration);
1391 // Checks whether a hardware keyboard is available
1392 if (configuration.hardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_NO) {
linyuh183cb712017-12-27 17:02:37 -08001393 isKeyboardOpen = true;
calderwoodra50715262017-09-08 09:20:08 -07001394 } else if (configuration.hardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_YES) {
linyuh183cb712017-12-27 17:02:37 -08001395 isKeyboardOpen = false;
calderwoodra50715262017-09-08 09:20:08 -07001396 }
1397 }
1398
Eric Erfanianccca3152017-02-22 16:32:36 -08001399 private void maybeEnterSearchUi() {
1400 if (!isInSearchUi()) {
linyuh183cb712017-12-27 17:02:37 -08001401 enterSearchUi(true /* isSmartDial */, searchQuery, false);
Eric Erfanianccca3152017-02-22 16:32:36 -08001402 }
1403 }
1404
Eric Erfanianccca3152017-02-22 16:32:36 -08001405 @Override
1406 public void onDialpadQueryChanged(String query) {
linyuh183cb712017-12-27 17:02:37 -08001407 dialpadQuery = query;
1408 if (smartDialSearchFragment != null) {
1409 smartDialSearchFragment.setAddToContactNumber(query);
Eric Erfanianccca3152017-02-22 16:32:36 -08001410 }
linyuh183cb712017-12-27 17:02:37 -08001411 if (newSearchFragment != null) {
1412 newSearchFragment.setRawNumber(query);
wangqie9f94c72017-11-10 11:51:50 -08001413 }
Eric Erfanianccca3152017-02-22 16:32:36 -08001414 final String normalizedQuery =
linyuhab146532017-12-19 11:28:51 -08001415 SmartDialNameMatcher.normalizeNumber(/* context = */ this, query);
Eric Erfanianccca3152017-02-22 16:32:36 -08001416
linyuh183cb712017-12-27 17:02:37 -08001417 if (!TextUtils.equals(searchView.getText(), normalizedQuery)) {
Eric Erfanianccca3152017-02-22 16:32:36 -08001418 if (DEBUG) {
1419 LogUtil.v("DialtactsActivity.onDialpadQueryChanged", "new query: " + query);
1420 }
linyuh183cb712017-12-27 17:02:37 -08001421 if (dialpadFragment == null || !dialpadFragment.isVisible()) {
Eric Erfanianccca3152017-02-22 16:32:36 -08001422 // This callback can happen if the dialpad fragment is recreated because of
1423 // activity destruction. In that case, don't update the search view because
1424 // that would bring the user back to the search fragment regardless of the
1425 // previous state of the application. Instead, just return here and let the
1426 // fragment manager correctly figure out whatever fragment was last displayed.
1427 if (!TextUtils.isEmpty(normalizedQuery)) {
linyuh183cb712017-12-27 17:02:37 -08001428 pendingSearchViewQuery = normalizedQuery;
Eric Erfanianccca3152017-02-22 16:32:36 -08001429 }
1430 return;
1431 }
linyuh183cb712017-12-27 17:02:37 -08001432 searchView.setText(normalizedQuery);
Eric Erfanianccca3152017-02-22 16:32:36 -08001433 }
1434
1435 try {
linyuh183cb712017-12-27 17:02:37 -08001436 if (dialpadFragment != null && dialpadFragment.isVisible()) {
1437 dialpadFragment.process_quote_emergency_unquote(normalizedQuery);
Eric Erfanianccca3152017-02-22 16:32:36 -08001438 }
1439 } catch (Exception ignored) {
1440 // Skip any exceptions for this piece of code
1441 }
1442 }
1443
1444 @Override
1445 public boolean onDialpadSpacerTouchWithEmptyQuery() {
linyuh183cb712017-12-27 17:02:37 -08001446 if (inDialpadSearch
1447 && smartDialSearchFragment != null
1448 && !smartDialSearchFragment.isShowingPermissionRequest()) {
Eric Erfanian2ca43182017-08-31 06:57:16 -07001449 PerformanceReport.recordClick(UiAction.Type.CLOSE_DIALPAD);
Eric Erfanianccca3152017-02-22 16:32:36 -08001450 hideDialpadFragment(true /* animate */, true /* clearDialpad */);
1451 return true;
1452 }
1453 return false;
1454 }
1455
1456 @Override
calderwoodra76cf7e12018-02-28 17:10:18 -08001457 public boolean shouldShowDialpadChooser() {
1458 // Show the dialpad chooser if we're in a call
1459 return true;
1460 }
1461
1462 @Override
calderwoodra70a4b092018-01-17 13:17:27 -08001463 public void onSearchListTouch() {
linyuh183cb712017-12-27 17:02:37 -08001464 if (isDialpadShown) {
calderwoodra9d197d02017-11-15 15:41:54 -08001465 PerformanceReport.recordClick(UiAction.Type.CLOSE_DIALPAD);
1466 hideDialpadFragment(true, false);
linyuh183cb712017-12-27 17:02:37 -08001467 if (TextUtils.isEmpty(dialpadQuery)) {
calderwoodra9d197d02017-11-15 15:41:54 -08001468 exitSearchUi();
1469 }
calderwoodra9d197d02017-11-15 15:41:54 -08001470 } else {
linyuh183cb712017-12-27 17:02:37 -08001471 UiUtil.hideKeyboardFrom(this, searchEditTextLayout);
calderwoodra9d197d02017-11-15 15:41:54 -08001472 }
calderwoodra9d197d02017-11-15 15:41:54 -08001473 }
1474
1475 @Override
Eric Erfanianccca3152017-02-22 16:32:36 -08001476 public void onListFragmentScrollStateChange(int scrollState) {
Eric Erfanian2ca43182017-08-31 06:57:16 -07001477 PerformanceReport.recordScrollStateChange(scrollState);
Eric Erfanianccca3152017-02-22 16:32:36 -08001478 if (scrollState == OnScrollListener.SCROLL_STATE_TOUCH_SCROLL) {
1479 hideDialpadFragment(true, false);
linyuh183cb712017-12-27 17:02:37 -08001480 DialerUtils.hideInputMethod(parentLayout);
Eric Erfanianccca3152017-02-22 16:32:36 -08001481 }
1482 }
1483
1484 @Override
1485 public void onListFragmentScroll(int firstVisibleItem, int visibleItemCount, int totalItemCount) {
1486 // TODO: No-op for now. This should eventually show/hide the actionBar based on
1487 // interactions with the ListsFragments.
1488 }
1489
1490 private boolean phoneIsInUse() {
wangqifff6fcf2017-08-31 15:30:58 -07001491 return TelecomUtil.isInManagedCall(this);
Eric Erfanianccca3152017-02-22 16:32:36 -08001492 }
1493
1494 private boolean canIntentBeHandled(Intent intent) {
1495 final PackageManager packageManager = getPackageManager();
1496 final List<ResolveInfo> resolveInfo =
1497 packageManager.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
1498 return resolveInfo != null && resolveInfo.size() > 0;
1499 }
1500
1501 /** Called when the user has long-pressed a contact tile to start a drag operation. */
1502 @Override
1503 public void onDragStarted(int x, int y, PhoneFavoriteSquareTileView view) {
linyuh183cb712017-12-27 17:02:37 -08001504 listsFragment.showRemoveView(true);
Eric Erfanianccca3152017-02-22 16:32:36 -08001505 }
1506
1507 @Override
1508 public void onDragHovered(int x, int y, PhoneFavoriteSquareTileView view) {}
1509
1510 /** Called when the user has released a contact tile after long-pressing it. */
1511 @Override
1512 public void onDragFinished(int x, int y) {
linyuh183cb712017-12-27 17:02:37 -08001513 listsFragment.showRemoveView(false);
Eric Erfanianccca3152017-02-22 16:32:36 -08001514 }
1515
1516 @Override
1517 public void onDroppedOnRemove() {}
1518
calderwoodra37d05a22018-01-30 14:50:44 -08001519 @Override
1520 public ImageView getDragShadowOverlay() {
1521 return findViewById(R.id.contact_tile_drag_shadow_overlay);
1522 }
1523
calderwoodraebc59e22018-02-24 02:33:40 -08001524 @Override
1525 public void setHasFrequents(boolean hasFrequents) {
1526 // No-op
1527 }
1528
Eric Erfanianccca3152017-02-22 16:32:36 -08001529 /**
1530 * Allows the SpeedDialFragment to attach the drag controller to mRemoveViewContainer once it has
1531 * been attached to the activity.
1532 */
1533 @Override
1534 public void setDragDropController(DragDropController dragController) {
linyuh183cb712017-12-27 17:02:37 -08001535 dragDropController = dragController;
1536 listsFragment.getRemoveView().setDragDropController(dragController);
Eric Erfanianccca3152017-02-22 16:32:36 -08001537 }
1538
Eric Erfaniand8046e52017-04-06 09:41:50 -07001539 /** Implemented to satisfy {@link OldSpeedDialFragment.HostInterface} */
Eric Erfanianccca3152017-02-22 16:32:36 -08001540 @Override
1541 public void showAllContactsTab() {
linyuh183cb712017-12-27 17:02:37 -08001542 if (listsFragment != null) {
1543 listsFragment.showTab(DialtactsPagerAdapter.TAB_INDEX_ALL_CONTACTS);
Eric Erfanianccca3152017-02-22 16:32:36 -08001544 }
1545 }
1546
1547 /** Implemented to satisfy {@link CallLogFragment.HostInterface} */
1548 @Override
1549 public void showDialpad() {
1550 showDialpadFragment(true);
1551 }
1552
1553 @Override
1554 public void enableFloatingButton(boolean enabled) {
1555 LogUtil.d("DialtactsActivity.enableFloatingButton", "enable: %b", enabled);
1556 // Floating button shouldn't be enabled when dialpad is shown.
1557 if (!isDialpadShown() || !enabled) {
linyuh183cb712017-12-27 17:02:37 -08001558 floatingActionButtonController.setVisible(enabled);
Eric Erfanianccca3152017-02-22 16:32:36 -08001559 }
1560 }
1561
1562 @Override
1563 public void onPickDataUri(
1564 Uri dataUri, boolean isVideoCall, CallSpecificAppData callSpecificAppData) {
linyuh183cb712017-12-27 17:02:37 -08001565 clearSearchOnPause = true;
Eric Erfanianccca3152017-02-22 16:32:36 -08001566 PhoneNumberInteraction.startInteractionForPhoneCall(
1567 DialtactsActivity.this, dataUri, isVideoCall, callSpecificAppData);
1568 }
1569
1570 @Override
1571 public void onPickPhoneNumber(
1572 String phoneNumber, boolean isVideoCall, CallSpecificAppData callSpecificAppData) {
1573 if (phoneNumber == null) {
1574 // Invalid phone number, but let the call go through so that InCallUI can show
1575 // an error message.
1576 phoneNumber = "";
1577 }
twyen0efc8402017-11-07 15:39:15 -08001578 PreCall.start(
1579 this,
erfanian04ac93d2017-10-09 15:12:22 -07001580 new CallIntentBuilder(phoneNumber, callSpecificAppData)
1581 .setIsVideoCall(isVideoCall)
twyen0efc8402017-11-07 15:39:15 -08001582 .setAllowAssistedDial(callSpecificAppData.getAllowAssistedDialing()));
Eric Erfanian2ca43182017-08-31 06:57:16 -07001583
linyuh183cb712017-12-27 17:02:37 -08001584 clearSearchOnPause = true;
Eric Erfanianccca3152017-02-22 16:32:36 -08001585 }
1586
1587 @Override
1588 public void onHomeInActionBarSelected() {
1589 exitSearchUi();
1590 }
1591
1592 @Override
1593 public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
calderwoodra98736472017-11-30 15:21:47 -08001594 // FAB does not move with the new favorites UI
1595 if (newFavoritesIsEnabled()) {
1596 return;
1597 }
linyuh183cb712017-12-27 17:02:37 -08001598 int tabIndex = listsFragment.getCurrentTabIndex();
Eric Erfanianccca3152017-02-22 16:32:36 -08001599
1600 // Scroll the button from center to end when moving from the Speed Dial to Call History tab.
1601 // In RTL, scroll when the current tab is Call History instead, since the order of the tabs
1602 // is reversed and the ViewPager returns the left tab position during scroll.
1603 boolean isRtl = ViewUtil.isRtl();
linyuh183cb712017-12-27 17:02:37 -08001604 if (!isRtl && tabIndex == DialtactsPagerAdapter.TAB_INDEX_SPEED_DIAL && !isLandscape) {
1605 floatingActionButtonController.onPageScrolled(positionOffset);
1606 } else if (isRtl && tabIndex == DialtactsPagerAdapter.TAB_INDEX_HISTORY && !isLandscape) {
1607 floatingActionButtonController.onPageScrolled(1 - positionOffset);
Eric Erfaniand8046e52017-04-06 09:41:50 -07001608 } else if (tabIndex != DialtactsPagerAdapter.TAB_INDEX_SPEED_DIAL) {
linyuh183cb712017-12-27 17:02:37 -08001609 floatingActionButtonController.onPageScrolled(1);
Eric Erfanianccca3152017-02-22 16:32:36 -08001610 }
1611 }
1612
1613 @Override
1614 public void onPageSelected(int position) {
1615 updateMissedCalls();
linyuh183cb712017-12-27 17:02:37 -08001616 int tabIndex = listsFragment.getCurrentTabIndex();
1617 if (tabIndex != previouslySelectedTabIndex) {
1618 floatingActionButtonController.scaleIn();
calderwoodra6007f522017-10-09 16:59:04 -07001619 }
Android Dialerb7dba5a2017-11-07 21:22:15 -08001620 LogUtil.i("DialtactsActivity.onPageSelected", "tabIndex: %d", tabIndex);
linyuh183cb712017-12-27 17:02:37 -08001621 previouslySelectedTabIndex = tabIndex;
Eric Erfaniand8046e52017-04-06 09:41:50 -07001622 timeTabSelected = SystemClock.elapsedRealtime();
Eric Erfanianccca3152017-02-22 16:32:36 -08001623 }
1624
1625 @Override
1626 public void onPageScrollStateChanged(int state) {}
1627
1628 @Override
1629 public boolean isActionBarShowing() {
linyuh183cb712017-12-27 17:02:37 -08001630 return actionBarController.isActionBarShowing();
Eric Erfanianccca3152017-02-22 16:32:36 -08001631 }
1632
1633 @Override
Eric Erfanianccca3152017-02-22 16:32:36 -08001634 public boolean isDialpadShown() {
linyuh183cb712017-12-27 17:02:37 -08001635 return isDialpadShown;
Eric Erfanianccca3152017-02-22 16:32:36 -08001636 }
1637
1638 @Override
1639 public int getDialpadHeight() {
linyuh183cb712017-12-27 17:02:37 -08001640 if (dialpadFragment != null) {
1641 return dialpadFragment.getDialpadHeight();
Eric Erfanianccca3152017-02-22 16:32:36 -08001642 }
1643 return 0;
1644 }
1645
1646 @Override
Eric Erfanianccca3152017-02-22 16:32:36 -08001647 public void setActionBarHideOffset(int offset) {
1648 getActionBarSafely().setHideOffset(offset);
1649 }
1650
1651 @Override
1652 public int getActionBarHeight() {
linyuh183cb712017-12-27 17:02:37 -08001653 return actionBarHeight;
Eric Erfanianccca3152017-02-22 16:32:36 -08001654 }
1655
calderwoodra6007f522017-10-09 16:59:04 -07001656 @VisibleForTesting
1657 public int getFabAlignment() {
calderwoodra98736472017-11-30 15:21:47 -08001658 if (!newFavoritesIsEnabled()
linyuh183cb712017-12-27 17:02:37 -08001659 && !isLandscape
Eric Erfanianccca3152017-02-22 16:32:36 -08001660 && !isInSearchUi()
linyuh183cb712017-12-27 17:02:37 -08001661 && listsFragment.getCurrentTabIndex() == DialtactsPagerAdapter.TAB_INDEX_SPEED_DIAL) {
Eric Erfanianccca3152017-02-22 16:32:36 -08001662 return FloatingActionButtonController.ALIGN_MIDDLE;
1663 }
1664 return FloatingActionButtonController.ALIGN_END;
1665 }
1666
1667 private void updateMissedCalls() {
linyuh183cb712017-12-27 17:02:37 -08001668 if (previouslySelectedTabIndex == DialtactsPagerAdapter.TAB_INDEX_HISTORY) {
1669 listsFragment.markMissedCallsAsReadAndRemoveNotifications();
Eric Erfanianccca3152017-02-22 16:32:36 -08001670 }
1671 }
1672
1673 @Override
1674 public void onDisambigDialogDismissed() {
1675 // Don't do anything; the app will remain open with favorites tiles displayed.
1676 }
1677
1678 @Override
1679 public void interactionError(@InteractionErrorCode int interactionErrorCode) {
1680 switch (interactionErrorCode) {
1681 case InteractionErrorCode.USER_LEAVING_ACTIVITY:
1682 // This is expected to happen if the user exits the activity before the interaction occurs.
1683 return;
1684 case InteractionErrorCode.CONTACT_NOT_FOUND:
1685 case InteractionErrorCode.CONTACT_HAS_NO_NUMBER:
1686 case InteractionErrorCode.OTHER_ERROR:
1687 default:
1688 // All other error codes are unexpected. For example, it should be impossible to start an
1689 // interaction with an invalid contact from the Dialtacts activity.
1690 Assert.fail("PhoneNumberInteraction error: " + interactionErrorCode);
1691 }
1692 }
1693
1694 @Override
1695 public void onRequestPermissionsResult(
1696 int requestCode, String[] permissions, int[] grantResults) {
1697 // This should never happen; it should be impossible to start an interaction without the
1698 // contacts permission from the Dialtacts activity.
1699 Assert.fail(
1700 String.format(
1701 Locale.US,
1702 "Permissions requested unexpectedly: %d/%s/%s",
1703 requestCode,
1704 Arrays.toString(permissions),
1705 Arrays.toString(grantResults)));
1706 }
1707
Eric Erfanian2ca43182017-08-31 06:57:16 -07001708 @Override
1709 public void onActionModeStateChanged(boolean isEnabled) {
1710 isMultiSelectModeEnabled = isEnabled;
1711 }
1712
1713 @Override
1714 public boolean isActionModeStateEnabled() {
1715 return isMultiSelectModeEnabled;
1716 }
1717
1718 private CallInitiationType.Type getCallInitiationType() {
linyuh183cb712017-12-27 17:02:37 -08001719 return isDialpadShown
Eric Erfanian2ca43182017-08-31 06:57:16 -07001720 ? CallInitiationType.Type.DIALPAD
1721 : CallInitiationType.Type.REGULAR_SEARCH;
1722 }
1723
calderwoodrac3770122017-10-13 16:36:17 -07001724 @Override
calderwoodrae51ca382017-11-21 10:54:36 -08001725 public void onCallPlacedFromSearch() {
linyuh183cb712017-12-27 17:02:37 -08001726 DialerUtils.hideInputMethod(parentLayout);
1727 clearSearchOnPause = true;
calderwoodra32257fd2017-10-25 15:01:37 -07001728 }
1729
calderwoodraf53ba7b2018-03-12 18:30:32 -07001730 @Override
1731 public void requestingPermission() {}
1732
Android Dialerb7dba5a2017-11-07 21:22:15 -08001733 protected int getPreviouslySelectedTabIndex() {
linyuh183cb712017-12-27 17:02:37 -08001734 return previouslySelectedTabIndex;
Android Dialerb7dba5a2017-11-07 21:22:15 -08001735 }
1736
calderwoodra73b51d52017-12-08 20:52:56 -08001737 @Override
1738 public void onContactSelected(ImageView photo, Uri contactUri, long contactId) {
1739 Logger.get(this)
1740 .logInteraction(InteractionEvent.Type.OPEN_QUICK_CONTACT_FROM_CONTACTS_FRAGMENT_ITEM);
1741 QuickContact.showQuickContact(
1742 this, photo, contactUri, QuickContact.MODE_LARGE, null /* excludeMimes */);
1743 }
1744
Eric Erfanian2ca43182017-08-31 06:57:16 -07001745 /** Popup menu accessible from the search bar */
Eric Erfanianccca3152017-02-22 16:32:36 -08001746 protected class OptionsPopupMenu extends PopupMenu {
1747
1748 public OptionsPopupMenu(Context context, View anchor) {
1749 super(context, anchor, Gravity.END);
1750 }
1751
1752 @Override
1753 public void show() {
Eric Erfaniand8046e52017-04-06 09:41:50 -07001754 Menu menu = getMenu();
1755 MenuItem clearFrequents = menu.findItem(R.id.menu_clear_frequents);
Eric Erfanianccca3152017-02-22 16:32:36 -08001756 clearFrequents.setVisible(
Eric Erfanianc857f902017-05-15 14:05:33 -07001757 PermissionsUtil.hasContactsReadPermissions(DialtactsActivity.this)
linyuh183cb712017-12-27 17:02:37 -08001758 && listsFragment != null
1759 && listsFragment.hasFrequents());
Eric Erfanianccca3152017-02-22 16:32:36 -08001760
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001761 menu.findItem(R.id.menu_history)
Eric Erfanianccca3152017-02-22 16:32:36 -08001762 .setVisible(PermissionsUtil.hasPhonePermissions(DialtactsActivity.this));
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001763
1764 Context context = DialtactsActivity.this.getApplicationContext();
1765 MenuItem simulatorMenuItem = menu.findItem(R.id.menu_simulator_submenu);
1766 Simulator simulator = SimulatorComponent.get(context).getSimulator();
1767 if (simulator.shouldShow()) {
1768 simulatorMenuItem.setVisible(true);
weijiaxu6b91ad32017-12-14 15:10:31 -08001769 simulatorMenuItem.setActionProvider(simulator.getActionProvider(DialtactsActivity.this));
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001770 } else {
1771 simulatorMenuItem.setVisible(false);
1772 }
1773
Eric Erfanian2ca43182017-08-31 06:57:16 -07001774 menu.findItem(R.id.menu_new_ui_launcher_shortcut)
calderwoodra9f16e172018-01-29 19:18:14 -08001775 .setVisible(MainComponent.isNewUiEnabled(context));
Eric Erfanian2ca43182017-08-31 06:57:16 -07001776
Eric Erfanianccca3152017-02-22 16:32:36 -08001777 super.show();
1778 }
1779 }
1780
1781 /**
1782 * Listener that listens to drag events and sends their x and y coordinates to a {@link
1783 * DragDropController}.
1784 */
1785 private class LayoutOnDragListener implements OnDragListener {
1786
1787 @Override
1788 public boolean onDrag(View v, DragEvent event) {
1789 if (event.getAction() == DragEvent.ACTION_DRAG_LOCATION) {
linyuh183cb712017-12-27 17:02:37 -08001790 dragDropController.handleDragHovered(v, (int) event.getX(), (int) event.getY());
Eric Erfanianccca3152017-02-22 16:32:36 -08001791 }
1792 return true;
1793 }
1794 }
twyen891fe0f2017-10-17 14:59:15 -07001795
1796 @VisibleForTesting
1797 static void setVoiceSearchEnabledForTest(Optional<Boolean> enabled) {
linyuh183cb712017-12-27 17:02:37 -08001798 voiceSearchEnabledForTest = enabled;
twyen891fe0f2017-10-17 14:59:15 -07001799 }
calderwoodra98736472017-11-30 15:21:47 -08001800
1801 private boolean newFavoritesIsEnabled() {
1802 return ConfigProviderBindings.get(this).getBoolean("enable_new_favorites_tab", false);
1803 }
Eric Erfanianccca3152017-02-22 16:32:36 -08001804}