blob: 05d7e0313d56b0895a2d36fbaff2abfa6a42c5d4 [file] [log] [blame]
Yorke Lee6b049122013-07-16 10:38:02 -07001/*
Yorke Leedfb2eee2013-06-26 18:24:32 -07002 * Copyright (C) 2013 The Android Open Source Project
Yorke Lee6b049122013-07-16 10:38:02 -07003 *
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;
18
Yorke Leedfb2eee2013-06-26 18:24:32 -070019import android.animation.Animator;
20import android.animation.Animator.AnimatorListener;
21import android.animation.AnimatorListenerAdapter;
Yorke Lee6b049122013-07-16 10:38:02 -070022import android.app.Activity;
23import android.app.backup.BackupManager;
24import android.app.Fragment;
25import android.app.FragmentManager;
26import android.app.FragmentTransaction;
27import android.content.ActivityNotFoundException;
28import android.content.Context;
29import android.content.Intent;
30import android.content.SharedPreferences;
Yorke Leed9999322013-07-19 09:22:03 -070031import android.content.res.Resources;
Yorke Lee6b049122013-07-16 10:38:02 -070032import android.net.Uri;
33import android.os.Bundle;
34import android.os.RemoteException;
35import android.os.ServiceManager;
Yorke Lee6b049122013-07-16 10:38:02 -070036import android.provider.CallLog.Calls;
Yorke Leedfb2eee2013-06-26 18:24:32 -070037import android.provider.ContactsContract;
Yorke Lee6b049122013-07-16 10:38:02 -070038import android.provider.ContactsContract.Contacts;
39import android.provider.ContactsContract.Intents.UI;
Yorke Leedfb2eee2013-06-26 18:24:32 -070040import android.provider.Settings;
Yorke Lee191df332013-07-30 17:21:28 -070041import android.speech.RecognizerIntent;
Yorke Lee80a05ed2013-07-26 17:05:04 -070042import android.telephony.TelephonyManager;
Yorke Leed9999322013-07-19 09:22:03 -070043import android.text.Editable;
Yorke Lee6b049122013-07-16 10:38:02 -070044import android.text.TextUtils;
Yorke Leed9999322013-07-19 09:22:03 -070045import android.text.TextWatcher;
Yorke Lee6b049122013-07-16 10:38:02 -070046import android.util.Log;
47import android.view.Menu;
Yorke Lee6b049122013-07-16 10:38:02 -070048import android.view.MenuItem;
Yorke Lee6b049122013-07-16 10:38:02 -070049import android.view.View;
Yorke Lee6b049122013-07-16 10:38:02 -070050import android.view.View.OnFocusChangeListener;
51import android.view.ViewConfiguration;
Yorke Lee6b049122013-07-16 10:38:02 -070052import android.view.inputmethod.InputMethodManager;
Yorke Leedfb2eee2013-06-26 18:24:32 -070053import android.widget.AbsListView.OnScrollListener;
Yorke Leed9999322013-07-19 09:22:03 -070054import android.widget.EditText;
55import android.widget.ImageView;
Yorke Lee6b049122013-07-16 10:38:02 -070056import android.widget.PopupMenu;
57import android.widget.SearchView;
58import android.widget.SearchView.OnCloseListener;
59import android.widget.SearchView.OnQueryTextListener;
60import android.widget.Toast;
61
62import com.android.contacts.common.CallUtil;
63import com.android.contacts.common.activity.TransactionSafeActivity;
Yorke Leedfb2eee2013-06-26 18:24:32 -070064import com.android.contacts.common.dialog.ClearFrequentsDialog;
65import com.android.contacts.common.interactions.ImportExportDialogFragment;
Yorke Lee6b049122013-07-16 10:38:02 -070066import com.android.contacts.common.list.ContactListItemView;
67import com.android.contacts.common.list.OnPhoneNumberPickerActionListener;
68import com.android.contacts.common.list.PhoneNumberPickerFragment;
Yorke Leedfb2eee2013-06-26 18:24:32 -070069import com.android.dialer.calllog.NewCallLogActivity;
70import com.android.dialer.dialpad.NewDialpadFragment;
71import com.android.dialer.dialpad.SmartDialNameMatcher;
Yorke Lee6b049122013-07-16 10:38:02 -070072import com.android.dialer.interactions.PhoneNumberInteraction;
Yorke Leedfb2eee2013-06-26 18:24:32 -070073import com.android.dialer.list.NewPhoneFavoriteFragment;
74import com.android.dialer.list.OnListFragmentScrolledListener;
75import com.android.dialer.list.SmartDialSearchFragment;
Yorke Lee6b049122013-07-16 10:38:02 -070076import com.android.internal.telephony.ITelephony;
77
Yorke Lee191df332013-07-30 17:21:28 -070078import java.util.ArrayList;
79
Yorke Lee6b049122013-07-16 10:38:02 -070080/**
Yorke Lee6b049122013-07-16 10:38:02 -070081 * The dialer tab's title is 'phone', a more common name (see strings.xml).
Yorke Leedfb2eee2013-06-26 18:24:32 -070082 *
83 * TODO krelease: All classes currently prefixed with New will replace the original classes or
84 * be renamed more appropriately before shipping.
Yorke Lee6b049122013-07-16 10:38:02 -070085 */
Yorke Leedfb2eee2013-06-26 18:24:32 -070086public class NewDialtactsActivity extends TransactionSafeActivity implements View.OnClickListener,
87 NewDialpadFragment.OnDialpadQueryChangedListener, PopupMenu.OnMenuItemClickListener,
88 OnListFragmentScrolledListener,
Yorke Lee714d6c72013-07-26 16:11:39 -070089 NewPhoneFavoriteFragment.OnPhoneFavoriteFragmentStartedListener,
90 NewDialpadFragment.OnDialpadFragmentStartedListener {
Yorke Lee6b049122013-07-16 10:38:02 -070091 private static final String TAG = "DialtactsActivity";
92
93 public static final boolean DEBUG = false;
94
95 /** Used to open Call Setting */
96 private static final String PHONE_PACKAGE = "com.android.phone";
97 private static final String CALL_SETTINGS_CLASS_NAME =
98 "com.android.phone.CallFeaturesSetting";
99
100 /** @see #getCallOrigin() */
101 private static final String CALL_ORIGIN_DIALTACTS =
102 "com.android.dialer.DialtactsActivity";
103
Yorke Leedfb2eee2013-06-26 18:24:32 -0700104 private static final String TAG_DIALPAD_FRAGMENT = "dialpad";
105 private static final String TAG_REGULAR_SEARCH_FRAGMENT = "search";
106 private static final String TAG_SMARTDIAL_SEARCH_FRAGMENT = "smartdial";
107 private static final String TAG_FAVORITES_FRAGMENT = "favorites";
108
Yorke Lee6b049122013-07-16 10:38:02 -0700109 /**
110 * Just for backward compatibility. Should behave as same as {@link Intent#ACTION_DIAL}.
111 */
112 private static final String ACTION_TOUCH_DIALER = "com.android.phone.action.TOUCH_DIALER";
113
Yorke Lee6b049122013-07-16 10:38:02 -0700114 private static final int SUBACTIVITY_ACCOUNT_FILTER = 1;
115
Yorke Lee191df332013-07-30 17:21:28 -0700116 private static final int ACTIVITY_REQUEST_CODE_VOICE_SEARCH = 1;
117
Yorke Lee6b049122013-07-16 10:38:02 -0700118 private String mFilterText;
119
Yorke Leedfb2eee2013-06-26 18:24:32 -0700120 /**
121 * The main fragment displaying the user's favorites and frequent contacts
122 */
123 private NewPhoneFavoriteFragment mPhoneFavoriteFragment;
Yorke Lee6b049122013-07-16 10:38:02 -0700124
125 /**
Yorke Leedfb2eee2013-06-26 18:24:32 -0700126 * Fragment containing the dialpad that slides into view
Yorke Lee6b049122013-07-16 10:38:02 -0700127 */
Yorke Leedfb2eee2013-06-26 18:24:32 -0700128 private NewDialpadFragment mDialpadFragment;
129
130 /**
131 * Fragment for searching phone numbers using the alphanumeric keyboard.
132 */
133 private NewSearchFragment mRegularSearchFragment;
134
135 /**
136 * Fragment for searching phone numbers using the dialpad.
137 */
138 private SmartDialSearchFragment mSmartDialSearchFragment;
139
140 private View mMenuButton;
141 private View mCallHistoryButton;
142 private View mDialpadButton;
143
144 // Padding view used to shift the fragments up when the dialpad is shown.
145 private View mBottomPaddingView;
146
Yorke Lee6b049122013-07-16 10:38:02 -0700147 /**
148 * True when this Activity is in its search UI (with a {@link SearchView} and
149 * {@link PhoneNumberPickerFragment}).
150 */
151 private boolean mInSearchUi;
Yorke Leed9999322013-07-19 09:22:03 -0700152 private View mSearchViewContainer;
153 private View mSearchViewCloseButton;
Yorke Lee191df332013-07-30 17:21:28 -0700154 private View mVoiceSearchButton;
Yorke Leed9999322013-07-19 09:22:03 -0700155 private EditText mSearchView;
Yorke Lee6b049122013-07-16 10:38:02 -0700156
Yorke Lee6b049122013-07-16 10:38:02 -0700157 /**
Yorke Lee6b049122013-07-16 10:38:02 -0700158 * Listener used when one of phone numbers in search UI is selected. This will initiate a
159 * phone call using the phone number.
160 */
161 private final OnPhoneNumberPickerActionListener mPhoneNumberPickerActionListener =
162 new OnPhoneNumberPickerActionListener() {
163 @Override
164 public void onPickPhoneNumberAction(Uri dataUri) {
165 // Specify call-origin so that users will see the previous tab instead of
166 // CallLog screen (search UI will be automatically exited).
167 PhoneNumberInteraction.startInteractionForPhoneCall(
Yorke Leedfb2eee2013-06-26 18:24:32 -0700168 NewDialtactsActivity.this, dataUri, getCallOrigin());
Yorke Lee6b049122013-07-16 10:38:02 -0700169 }
170
171 @Override
172 public void onShortcutIntentCreated(Intent intent) {
173 Log.w(TAG, "Unsupported intent has come (" + intent + "). Ignoring.");
174 }
175
176 @Override
177 public void onHomeInActionBarSelected() {
178 exitSearchUi();
179 }
180 };
181
182 /**
183 * Listener used to send search queries to the phone search fragment.
184 */
Yorke Leed9999322013-07-19 09:22:03 -0700185 private final TextWatcher mPhoneSearchQueryTextListener = new TextWatcher() {
186 @Override
187 public void beforeTextChanged(CharSequence s, int start, int count, int after) {
188 }
189
190 @Override
191 public void onTextChanged(CharSequence s, int start, int before, int count) {
Yorke Lee936e4432013-07-29 09:54:42 -0700192 // TODO krelease: populate the search fragments with the correct
193 // search query at the correct point in time of the fragment lifecycle.
194 // The current behavior is to simply return to the favorites screen
195 // (when docked), or returning to the Dialer after it has been
196 // swapped out of memory.
197 if (mDialpadFragment == null) return;
198 final boolean smartDialSearch = isDialpadShowing();
Yorke Leed9999322013-07-19 09:22:03 -0700199 final String newText = s.toString();
200 // Show search result with non-empty text. Show a bare list otherwise.
201 if (TextUtils.isEmpty(newText) && mInSearchUi) {
202 exitSearchUi();
203 mSearchViewCloseButton.setVisibility(View.GONE);
204 return;
205 } else if (!TextUtils.isEmpty(newText) && !mInSearchUi) {
206 enterSearchUi(smartDialSearch);
Yorke Lee6b049122013-07-16 10:38:02 -0700207 }
208
Yorke Lee936e4432013-07-29 09:54:42 -0700209 if (smartDialSearch) {
Yorke Leed9999322013-07-19 09:22:03 -0700210 mSmartDialSearchFragment.setQueryString(newText, false);
211 } else {
212 mRegularSearchFragment.setQueryString(newText, false);
Yorke Lee6b049122013-07-16 10:38:02 -0700213 }
Yorke Leed9999322013-07-19 09:22:03 -0700214 mSearchViewCloseButton.setVisibility(View.VISIBLE);
215 return;
216 }
217
218 @Override
219 public void afterTextChanged(Editable s) {
220 }
Yorke Lee6b049122013-07-16 10:38:02 -0700221 };
222
Yorke Leedfb2eee2013-06-26 18:24:32 -0700223 private boolean isDialpadShowing() {
Yorke Lee936e4432013-07-29 09:54:42 -0700224 return mDialpadFragment != null && mDialpadFragment.isVisible();
Yorke Leedfb2eee2013-06-26 18:24:32 -0700225 }
226
Yorke Lee6b049122013-07-16 10:38:02 -0700227 @Override
Yorke Leedfb2eee2013-06-26 18:24:32 -0700228 protected void onCreate(Bundle savedInstanceState) {
229 super.onCreate(savedInstanceState);
Yorke Lee6b049122013-07-16 10:38:02 -0700230
231 final Intent intent = getIntent();
232 fixIntent(intent);
233
Yorke Leedfb2eee2013-06-26 18:24:32 -0700234 setContentView(R.layout.new_dialtacts_activity);
Yorke Lee6b049122013-07-16 10:38:02 -0700235
Yorke Leedfb2eee2013-06-26 18:24:32 -0700236 getActionBar().hide();
Yorke Lee6b049122013-07-16 10:38:02 -0700237
Yorke Lee936e4432013-07-29 09:54:42 -0700238 if (savedInstanceState == null) {
239 mPhoneFavoriteFragment = new NewPhoneFavoriteFragment();
240 mPhoneFavoriteFragment.setRetainInstance(true);
241 mPhoneFavoriteFragment.setListener(mPhoneFavoriteListener);
Yorke Lee6b049122013-07-16 10:38:02 -0700242
Yorke Lee936e4432013-07-29 09:54:42 -0700243 mRegularSearchFragment = new NewSearchFragment();
244 mSmartDialSearchFragment = new SmartDialSearchFragment();
245 mDialpadFragment = new NewDialpadFragment();
Yorke Lee6b049122013-07-16 10:38:02 -0700246
Yorke Lee936e4432013-07-29 09:54:42 -0700247 // TODO krelease: load fragments on demand instead of creating all of them at run time
248 final FragmentTransaction ft = getFragmentManager().beginTransaction();
249 ft.add(R.id.dialtacts_frame, mPhoneFavoriteFragment, TAG_FAVORITES_FRAGMENT);
250 ft.add(R.id.dialtacts_frame, mRegularSearchFragment, TAG_REGULAR_SEARCH_FRAGMENT);
251 ft.add(R.id.dialtacts_frame, mSmartDialSearchFragment, TAG_SMARTDIAL_SEARCH_FRAGMENT);
252 ft.add(R.id.dialtacts_container, mDialpadFragment, TAG_DIALPAD_FRAGMENT);
253 // Fragments will be hidden as necessary in onAttachFragment
254 ft.commit();
255 }
Yorke Lee6b049122013-07-16 10:38:02 -0700256
Yorke Leedfb2eee2013-06-26 18:24:32 -0700257 mBottomPaddingView = findViewById(R.id.dialtacts_bottom_padding);
258 prepareSearchView();
Yorke Lee6b049122013-07-16 10:38:02 -0700259
Yorke Lee80a05ed2013-07-26 17:05:04 -0700260 displayFragment(intent);
Yorke Lee6b049122013-07-16 10:38:02 -0700261
262 if (UI.FILTER_CONTACTS_ACTION.equals(intent.getAction())
Yorke Leedfb2eee2013-06-26 18:24:32 -0700263 && savedInstanceState == null) {
Yorke Lee6b049122013-07-16 10:38:02 -0700264 setupFilterText(intent);
265 }
266 }
267
268 @Override
Yorke Leedfb2eee2013-06-26 18:24:32 -0700269 protected void onResume() {
270 super.onResume();
271 final FragmentManager fm = getFragmentManager();
272 mPhoneFavoriteFragment = (NewPhoneFavoriteFragment) fm.findFragmentByTag(
273 TAG_FAVORITES_FRAGMENT);
274 mDialpadFragment = (NewDialpadFragment) fm.findFragmentByTag(TAG_DIALPAD_FRAGMENT);
275
276 mRegularSearchFragment = (NewSearchFragment) fm.findFragmentByTag(
277 TAG_REGULAR_SEARCH_FRAGMENT);
278 mRegularSearchFragment.setOnPhoneNumberPickerActionListener(
279 mPhoneNumberPickerActionListener);
Yorke Lee6b049122013-07-16 10:38:02 -0700280
Yorke Leedfb2eee2013-06-26 18:24:32 -0700281 mSmartDialSearchFragment = (SmartDialSearchFragment) fm.findFragmentByTag(
282 TAG_SMARTDIAL_SEARCH_FRAGMENT);
283 mSmartDialSearchFragment.setOnPhoneNumberPickerActionListener(
284 mPhoneNumberPickerActionListener);
Yorke Lee936e4432013-07-29 09:54:42 -0700285 }
286
287 @Override
288 public void onAttachFragment(Fragment fragment) {
289 if (fragment instanceof NewDialpadFragment || fragment instanceof NewSearchFragment
290 || fragment instanceof SmartDialSearchFragment) {
Yorke Leedfb2eee2013-06-26 18:24:32 -0700291 final FragmentTransaction transaction = getFragmentManager().beginTransaction();
Yorke Lee936e4432013-07-29 09:54:42 -0700292 transaction.hide(fragment);
Yorke Leedfb2eee2013-06-26 18:24:32 -0700293 transaction.commit();
Yorke Lee6b049122013-07-16 10:38:02 -0700294 }
Yorke Lee936e4432013-07-29 09:54:42 -0700295 // TODO krelease: Save some kind of state here to show the appropriate fragment
296 // based on the state of the dialer when it was last paused
Yorke Lee6b049122013-07-16 10:38:02 -0700297 }
298
299 @Override
Yorke Leedfb2eee2013-06-26 18:24:32 -0700300 public boolean onMenuItemClick(MenuItem item) {
301 switch (item.getItemId()) {
302 case R.id.menu_import_export:
303 // We hard-code the "contactsAreAvailable" argument because doing it properly would
304 // involve querying a {@link ProviderStatusLoader}, which we don't want to do right
305 // now in Dialtacts for (potential) performance reasons. Compare with how it is
306 // done in {@link PeopleActivity}.
307 ImportExportDialogFragment.show(getFragmentManager(), true,
308 DialtactsActivity.class);
309 return true;
310 case R.id.menu_clear_frequents:
311 ClearFrequentsDialog.show(getFragmentManager());
312 return true;
313 case R.id.add_contact:
314 try {
315 startActivity(new Intent(Intent.ACTION_INSERT, Contacts.CONTENT_URI));
316 } catch (ActivityNotFoundException e) {
317 Toast toast = Toast.makeText(this, R.string.add_contact_not_available,
318 Toast.LENGTH_SHORT);
319 toast.show();
320 }
321 return true;
322 case R.id.menu_call_settings:
323 final Intent settingsIntent = DialtactsActivity.getCallSettingsIntent();
324 startActivity(settingsIntent);
325 }
326 return false;
Yorke Lee6b049122013-07-16 10:38:02 -0700327 }
328
329 @Override
330 public void onClick(View view) {
331 switch (view.getId()) {
Yorke Lee714d6c72013-07-26 16:11:39 -0700332 case R.id.overflow_menu_on_dialpad:
Yorke Lee6b049122013-07-16 10:38:02 -0700333 case R.id.overflow_menu: {
Yorke Leedfb2eee2013-06-26 18:24:32 -0700334 final PopupMenu popupMenu = new PopupMenu(NewDialtactsActivity.this, view);
335 final Menu menu = popupMenu.getMenu();
336 popupMenu.inflate(R.menu.dialtacts_options_new);
337 popupMenu.setOnMenuItemClickListener(this);
338 popupMenu.show();
Yorke Lee6b049122013-07-16 10:38:02 -0700339 break;
340 }
Yorke Leedfb2eee2013-06-26 18:24:32 -0700341 case R.id.dialpad_button:
Yorke Lee80a05ed2013-07-26 17:05:04 -0700342 showDialpadFragment(true);
Yorke Leedfb2eee2013-06-26 18:24:32 -0700343 break;
Yorke Lee714d6c72013-07-26 16:11:39 -0700344 case R.id.call_history_on_dialpad_button:
Yorke Leedfb2eee2013-06-26 18:24:32 -0700345 case R.id.call_history_button:
Yorke Lee80a05ed2013-07-26 17:05:04 -0700346 // TODO krelease: This should start an intent with content type
347 // CallLog.Calls.CONTENT_TYPE, once the intent filters for the call log activity
348 // is enabled
Yorke Leedfb2eee2013-06-26 18:24:32 -0700349 final Intent intent = new Intent(this, NewCallLogActivity.class);
350 startActivity(intent);
351 break;
Yorke Leed9999322013-07-19 09:22:03 -0700352 case R.id.search_close_button:
353 // Clear the search field
354 if (!TextUtils.isEmpty(mSearchView.getText())) {
355 mSearchView.setText("");
356 }
357 break;
Yorke Lee191df332013-07-30 17:21:28 -0700358 case R.id.voice_search_button:
359 final Intent voiceIntent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
360 startActivityForResult(voiceIntent, ACTIVITY_REQUEST_CODE_VOICE_SEARCH);
361 break;
Yorke Lee6b049122013-07-16 10:38:02 -0700362 default: {
363 Log.wtf(TAG, "Unexpected onClick event from " + view);
364 break;
365 }
366 }
367 }
368
Yorke Lee191df332013-07-30 17:21:28 -0700369 @Override
370 protected void onActivityResult(int requestCode, int resultCode, Intent data) {
371 if (requestCode == ACTIVITY_REQUEST_CODE_VOICE_SEARCH) {
372 if (resultCode == RESULT_OK) {
373 final ArrayList<String> matches = data.getStringArrayListExtra(
374 RecognizerIntent.EXTRA_RESULTS);
375 if (matches.size() > 0) {
376 final String match = matches.get(0);
377 mSearchView.setText(match);
378 } else {
379 Log.e(TAG, "Voice search - nothing heard");
380 }
381 } else {
382 Log.e(TAG, "Voice search failed");
383 }
384 }
385 super.onActivityResult(requestCode, resultCode, data);
386 }
387
Yorke Lee80a05ed2013-07-26 17:05:04 -0700388 private void showDialpadFragment(boolean animate) {
Yorke Lee6b049122013-07-16 10:38:02 -0700389 final FragmentTransaction ft = getFragmentManager().beginTransaction();
Yorke Lee80a05ed2013-07-26 17:05:04 -0700390 if (animate) {
391 ft.setCustomAnimations(R.anim.slide_in, 0);
392 }
Yorke Leedfb2eee2013-06-26 18:24:32 -0700393 ft.show(mDialpadFragment);
394 ft.commit();
395 }
Yorke Lee6b049122013-07-16 10:38:02 -0700396
Yorke Lee80a05ed2013-07-26 17:05:04 -0700397 private void hideDialpadFragment(boolean animate) {
Yorke Leedfb2eee2013-06-26 18:24:32 -0700398 final FragmentTransaction ft = getFragmentManager().beginTransaction();
Yorke Lee80a05ed2013-07-26 17:05:04 -0700399 if (animate) {
400 ft.setCustomAnimations(0, R.anim.slide_out);
401 }
Yorke Leedfb2eee2013-06-26 18:24:32 -0700402 ft.hide(mDialpadFragment);
403 ft.commit();
Yorke Lee6b049122013-07-16 10:38:02 -0700404 }
405
406 private void prepareSearchView() {
Yorke Leed9999322013-07-19 09:22:03 -0700407 mSearchViewContainer = findViewById(R.id.search_view_container);
408 mSearchViewCloseButton = findViewById(R.id.search_close_button);
Yorke Leed9999322013-07-19 09:22:03 -0700409 mSearchViewCloseButton.setOnClickListener(this);
Yorke Lee191df332013-07-30 17:21:28 -0700410 mVoiceSearchButton = findViewById(R.id.voice_search_button);
411 mVoiceSearchButton.setOnClickListener(this);
Yorke Leed9999322013-07-19 09:22:03 -0700412 mSearchView = (EditText) findViewById(R.id.search_view);
413 mSearchView.addTextChangedListener(mPhoneSearchQueryTextListener);
414 mSearchView.setHint(getString(R.string.dialer_hint_find_contact));
415 mSearchView.setOnFocusChangeListener(new OnFocusChangeListener() {
Yorke Lee6b049122013-07-16 10:38:02 -0700416 @Override
417 public void onFocusChange(View view, boolean hasFocus) {
418 if (hasFocus) {
419 showInputMethod(view.findFocus());
420 }
421 }
422 });
Yorke Lee6b049122013-07-16 10:38:02 -0700423 }
424
Yorke Leedfb2eee2013-06-26 18:24:32 -0700425 private void hideDialpadFragmentIfNecessary() {
426 if (mDialpadFragment.isVisible()) {
Yorke Lee80a05ed2013-07-26 17:05:04 -0700427 hideDialpadFragment(true);
Yorke Leedfb2eee2013-06-26 18:24:32 -0700428 }
429 }
Yorke Lee6b049122013-07-16 10:38:02 -0700430
Yorke Leedfb2eee2013-06-26 18:24:32 -0700431 final AnimatorListener mHideListener = new AnimatorListenerAdapter() {
432 @Override
433 public void onAnimationEnd(Animator animation) {
Yorke Leed9999322013-07-19 09:22:03 -0700434 mSearchViewContainer.setVisibility(View.GONE);
Yorke Leedfb2eee2013-06-26 18:24:32 -0700435 }
436 };
437
438 public void hideSearchBar() {
Yorke Lee936e4432013-07-29 09:54:42 -0700439 // If the favorites fragment hasn't been fully created before the dialpad fragment
440 // is hidden (i.e. onResume), don't bother animating
441 if (mPhoneFavoriteFragment == null || mPhoneFavoriteFragment.getView() == null) {
442 return;
443 }
Yorke Leed9999322013-07-19 09:22:03 -0700444 mSearchViewContainer.animate().cancel();
445 mSearchViewContainer.setAlpha(1);
446 mSearchViewContainer.setTranslationY(0);
447 mSearchViewContainer.animate().withLayer().alpha(0).translationY(-mSearchView.getHeight())
448 .setDuration(200).setListener(mHideListener);
Yorke Leedfb2eee2013-06-26 18:24:32 -0700449
450 mPhoneFavoriteFragment.getView().animate().withLayer()
Yorke Leed9999322013-07-19 09:22:03 -0700451 .translationY(-mSearchViewContainer.getHeight()).setDuration(200).setListener(
Yorke Leedfb2eee2013-06-26 18:24:32 -0700452 new AnimatorListenerAdapter() {
453 @Override
454 public void onAnimationEnd(Animator animation) {
455 mBottomPaddingView.setVisibility(View.VISIBLE);
456 mPhoneFavoriteFragment.getView().setTranslationY(0);
457 }
458 });
459 }
460
461 public void showSearchBar() {
Yorke Lee936e4432013-07-29 09:54:42 -0700462 // If the favorites fragment hasn't been fully created before the dialpad fragment
463 // is hidden (i.e. onResume), don't bother animating
464 if (mPhoneFavoriteFragment == null || mPhoneFavoriteFragment.getView() == null) {
465 return;
466 }
Yorke Leed9999322013-07-19 09:22:03 -0700467 mSearchViewContainer.animate().cancel();
468 mSearchViewContainer.setAlpha(0);
469 mSearchViewContainer.setTranslationY(-mSearchViewContainer.getHeight());
470 mSearchViewContainer.animate().withLayer().alpha(1).translationY(0).setDuration(200)
Yorke Leedfb2eee2013-06-26 18:24:32 -0700471 .setListener(new AnimatorListenerAdapter() {
472 @Override
473 public void onAnimationStart(Animator animation) {
Yorke Leed9999322013-07-19 09:22:03 -0700474 mSearchViewContainer.setVisibility(View.VISIBLE);
Yorke Leedfb2eee2013-06-26 18:24:32 -0700475 }
476 });
477
Yorke Leed9999322013-07-19 09:22:03 -0700478 mPhoneFavoriteFragment.getView().setTranslationY(-mSearchViewContainer.getHeight());
Yorke Leedfb2eee2013-06-26 18:24:32 -0700479 mPhoneFavoriteFragment.getView().animate().withLayer().translationY(0).setDuration(200)
480 .setListener(
481 new AnimatorListenerAdapter() {
482 @Override
483 public void onAnimationStart(Animator animation) {
484 mBottomPaddingView.setVisibility(View.GONE);
485 }
486 });
487 }
488
489
Yorke Lee714d6c72013-07-26 16:11:39 -0700490 public void setupFakeActionBarItemsForFavoritesFragment() {
Yorke Leedfb2eee2013-06-26 18:24:32 -0700491 mMenuButton = findViewById(R.id.overflow_menu);
492 if (mMenuButton != null) {
493 // mMenuButton.setMinimumWidth(fakeMenuItemWidth);
494 if (ViewConfiguration.get(this).hasPermanentMenuKey()) {
495 // This is required for dialpad button's layout, so must not use GONE here.
496 mMenuButton.setVisibility(View.INVISIBLE);
497 } else {
498 mMenuButton.setOnClickListener(this);
Yorke Lee6b049122013-07-16 10:38:02 -0700499 }
500 }
Yorke Leedfb2eee2013-06-26 18:24:32 -0700501
502 mCallHistoryButton = findViewById(R.id.call_history_button);
503 // mCallHistoryButton.setMinimumWidth(fakeMenuItemWidth);
504 mCallHistoryButton.setOnClickListener(this);
505
506 mDialpadButton = findViewById(R.id.dialpad_button);
507 // DialpadButton.setMinimumWidth(fakeMenuItemWidth);
508 mDialpadButton.setOnClickListener(this);
Yorke Lee6b049122013-07-16 10:38:02 -0700509 }
510
Yorke Lee714d6c72013-07-26 16:11:39 -0700511 public void setupFakeActionBarItemsForDialpadFragment() {
512 final View overflowButton = findViewById(R.id.overflow_menu_on_dialpad);
513 overflowButton.setOnClickListener(this);
514 final View callhistoryButton = findViewById(R.id.call_history_on_dialpad_button);
515 callhistoryButton.setOnClickListener(this);
516 }
517
Yorke Lee6b049122013-07-16 10:38:02 -0700518 private void fixIntent(Intent intent) {
519 // This should be cleaned up: the call key used to send an Intent
520 // that just said to go to the recent calls list. It now sends this
521 // abstract action, but this class hasn't been rewritten to deal with it.
522 if (Intent.ACTION_CALL_BUTTON.equals(intent.getAction())) {
523 intent.setDataAndType(Calls.CONTENT_URI, Calls.CONTENT_TYPE);
524 intent.putExtra("call_key", true);
525 setIntent(intent);
526 }
527 }
528
Yorke Lee6b049122013-07-16 10:38:02 -0700529 /**
530 * Returns true if the intent is due to hitting the green send key (hardware call button:
531 * KEYCODE_CALL) while in a call.
532 *
533 * @param intent the intent that launched this activity
534 * @param recentCallsRequest true if the intent is requesting to view recent calls
535 * @return true if the intent is due to hitting the green send key while in a call
536 */
Yorke Leedfb2eee2013-06-26 18:24:32 -0700537 private boolean isSendKeyWhileInCall(Intent intent, boolean recentCallsRequest) {
Yorke Lee6b049122013-07-16 10:38:02 -0700538 // If there is a call in progress go to the call screen
539 if (recentCallsRequest) {
540 final boolean callKey = intent.getBooleanExtra("call_key", false);
541
542 try {
543 ITelephony phone = ITelephony.Stub.asInterface(ServiceManager.checkService("phone"));
544 if (callKey && phone != null && phone.showCallScreen()) {
545 return true;
546 }
547 } catch (RemoteException e) {
548 Log.e(TAG, "Failed to handle send while in call", e);
549 }
550 }
551
552 return false;
553 }
554
555 /**
556 * Sets the current tab based on the intent's request type
557 *
558 * @param intent Intent that contains information about which tab should be selected
559 */
Yorke Leedfb2eee2013-06-26 18:24:32 -0700560 private void displayFragment(Intent intent) {
561 // TODO krelease: Make navigation via intent work by displaying the correct fragment
562 // as appropriate.
563
Yorke Lee6b049122013-07-16 10:38:02 -0700564 // If we got here by hitting send and we're in call forward along to the in-call activity
565 boolean recentCallsRequest = Calls.CONTENT_TYPE.equals(intent.resolveType(
566 getContentResolver()));
567 if (isSendKeyWhileInCall(intent, recentCallsRequest)) {
568 finish();
569 return;
570 }
Yorke Lee80a05ed2013-07-26 17:05:04 -0700571
572 if ((mDialpadFragment != null && phoneIsInUse())
573 || isDialIntent(intent)) {
574 mDialpadFragment.setStartedFromNewIntent(true);
575 // TODO krelease: This should use showDialpadFragment(false) to avoid animating
576 // the dialpad in. Need to fix the onPreDrawListener in NewDialpadFragment first.
577 showDialpadFragment(true);
578 }
Yorke Lee6b049122013-07-16 10:38:02 -0700579 }
580
581 @Override
582 public void onNewIntent(Intent newIntent) {
583 setIntent(newIntent);
584 fixIntent(newIntent);
Yorke Leedfb2eee2013-06-26 18:24:32 -0700585 displayFragment(newIntent);
Yorke Lee6b049122013-07-16 10:38:02 -0700586 final String action = newIntent.getAction();
Yorke Lee80a05ed2013-07-26 17:05:04 -0700587
Yorke Leedfb2eee2013-06-26 18:24:32 -0700588 if (mInSearchUi || (mRegularSearchFragment != null && mRegularSearchFragment.isVisible())) {
Yorke Lee6b049122013-07-16 10:38:02 -0700589 exitSearchUi();
590 }
591
Yorke Leedfb2eee2013-06-26 18:24:32 -0700592 // TODO krelease: Handle onNewIntent for all other fragments
593 /*
594 *if (mViewPager.getCurrentItem() == TAB_INDEX_DIALER) { if (mDialpadFragment != null) {
595 * mDialpadFragment.setStartedFromNewIntent(true); } else { Log.e(TAG,
596 * "DialpadFragment isn't ready yet when the tab is already selected."); } } else if
597 * (mViewPager.getCurrentItem() == TAB_INDEX_CALL_LOG) { if (mCallLogFragment != null) {
598 * mCallLogFragment.configureScreenFromIntent(newIntent); } else { Log.e(TAG,
599 * "CallLogFragment isn't ready yet when the tab is already selected."); } }
600 */
Yorke Lee6b049122013-07-16 10:38:02 -0700601 invalidateOptionsMenu();
602 }
603
604 /** Returns true if the given intent contains a phone number to populate the dialer with */
605 private boolean isDialIntent(Intent intent) {
606 final String action = intent.getAction();
607 if (Intent.ACTION_DIAL.equals(action) || ACTION_TOUCH_DIALER.equals(action)) {
608 return true;
609 }
610 if (Intent.ACTION_VIEW.equals(action)) {
611 final Uri data = intent.getData();
612 if (data != null && CallUtil.SCHEME_TEL.equals(data.getScheme())) {
613 return true;
614 }
615 }
616 return false;
617 }
618
619 /**
620 * Returns an appropriate call origin for this Activity. May return null when no call origin
621 * should be used (e.g. when some 3rd party application launched the screen. Call origin is
622 * for remembering the tab in which the user made a phone call, so the external app's DIAL
623 * request should not be counted.)
624 */
625 public String getCallOrigin() {
626 return !isDialIntent(getIntent()) ? CALL_ORIGIN_DIALTACTS : null;
627 }
628
629 /**
630 * Retrieves the filter text stored in {@link #setupFilterText(Intent)}.
631 * This text originally came from a FILTER_CONTACTS_ACTION intent received
632 * by this activity. The stored text will then be cleared after after this
633 * method returns.
634 *
635 * @return The stored filter text
636 */
637 public String getAndClearFilterText() {
638 String filterText = mFilterText;
639 mFilterText = null;
640 return filterText;
641 }
642
643 /**
644 * Stores the filter text associated with a FILTER_CONTACTS_ACTION intent.
645 * This is so child activities can check if they are supposed to display a filter.
646 *
647 * @param intent The intent received in {@link #onNewIntent(Intent)}
648 */
649 private void setupFilterText(Intent intent) {
650 // If the intent was relaunched from history, don't apply the filter text.
651 if ((intent.getFlags() & Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY) != 0) {
652 return;
653 }
654 String filter = intent.getStringExtra(UI.FILTER_TEXT_EXTRA_KEY);
655 if (filter != null && filter.length() > 0) {
656 mFilterText = filter;
657 }
658 }
659
Yorke Leedfb2eee2013-06-26 18:24:32 -0700660 private final NewPhoneFavoriteFragment.Listener mPhoneFavoriteListener =
661 new NewPhoneFavoriteFragment.Listener() {
Yorke Lee6b049122013-07-16 10:38:02 -0700662 @Override
663 public void onContactSelected(Uri contactUri) {
664 PhoneNumberInteraction.startInteractionForPhoneCall(
Yorke Leedfb2eee2013-06-26 18:24:32 -0700665 NewDialtactsActivity.this, contactUri, getCallOrigin());
Yorke Lee6b049122013-07-16 10:38:02 -0700666 }
667
668 @Override
669 public void onCallNumberDirectly(String phoneNumber) {
670 Intent intent = CallUtil.getCallIntent(phoneNumber, getCallOrigin());
671 startActivity(intent);
672 }
673 };
674
Yorke Leedfb2eee2013-06-26 18:24:32 -0700675 /* TODO krelease: This is only relevant for phones that have a hard button search key (i.e.
676 * Nexus S). Supporting it is a little more tricky because of the dialpad fragment might
677 * be showing when the search key is pressed so there is more state management involved.
Yorke Lee6b049122013-07-16 10:38:02 -0700678
679 @Override
680 public void startSearch(String initialQuery, boolean selectInitialQuery,
681 Bundle appSearchData, boolean globalSearch) {
Yorke Leedfb2eee2013-06-26 18:24:32 -0700682 if (mRegularSearchFragment != null && mRegularSearchFragment.isAdded() && !globalSearch) {
Yorke Lee6b049122013-07-16 10:38:02 -0700683 if (mInSearchUi) {
684 if (mSearchView.hasFocus()) {
685 showInputMethod(mSearchView.findFocus());
686 } else {
687 mSearchView.requestFocus();
688 }
689 } else {
690 enterSearchUi();
691 }
692 } else {
693 super.startSearch(initialQuery, selectInitialQuery, appSearchData, globalSearch);
694 }
Yorke Leedfb2eee2013-06-26 18:24:32 -0700695 }*/
Yorke Lee6b049122013-07-16 10:38:02 -0700696
697 private void showInputMethod(View view) {
Yorke Leedfb2eee2013-06-26 18:24:32 -0700698 final InputMethodManager imm = (InputMethodManager) getSystemService(
699 Context.INPUT_METHOD_SERVICE);
Yorke Lee6b049122013-07-16 10:38:02 -0700700 if (imm != null) {
Yorke Leedfb2eee2013-06-26 18:24:32 -0700701 imm.showSoftInput(view, 0);
Yorke Lee6b049122013-07-16 10:38:02 -0700702 }
703 }
704
705 private void hideInputMethod(View view) {
Yorke Leedfb2eee2013-06-26 18:24:32 -0700706 final InputMethodManager imm = (InputMethodManager) getSystemService(
707 Context.INPUT_METHOD_SERVICE);
Yorke Lee6b049122013-07-16 10:38:02 -0700708 if (imm != null && view != null) {
709 imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
710 }
711 }
712
713 /**
Yorke Leedfb2eee2013-06-26 18:24:32 -0700714 * Shows the search fragment
Yorke Lee6b049122013-07-16 10:38:02 -0700715 */
Yorke Leedfb2eee2013-06-26 18:24:32 -0700716 private void enterSearchUi(boolean smartDialSearch) {
717 final FragmentTransaction transaction = getFragmentManager().beginTransaction();
718 transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
719 transaction.hide(mPhoneFavoriteFragment);
720 if (smartDialSearch) {
721 transaction.show(mSmartDialSearchFragment);
722 } else {
723 transaction.show(mRegularSearchFragment);
Yorke Lee6b049122013-07-16 10:38:02 -0700724 }
Yorke Leedfb2eee2013-06-26 18:24:32 -0700725 transaction.commit();
Yorke Lee6b049122013-07-16 10:38:02 -0700726
Yorke Leedfb2eee2013-06-26 18:24:32 -0700727 mInSearchUi = true;
Yorke Lee6b049122013-07-16 10:38:02 -0700728 }
729
730 /**
Yorke Leedfb2eee2013-06-26 18:24:32 -0700731 * Hides the search fragment
Yorke Lee6b049122013-07-16 10:38:02 -0700732 */
Yorke Leedfb2eee2013-06-26 18:24:32 -0700733 private void exitSearchUi() {
734 final FragmentTransaction transaction = getFragmentManager().beginTransaction();
735 transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
736 transaction.hide(mRegularSearchFragment);
737 transaction.hide(mSmartDialSearchFragment);
738 transaction.show(mPhoneFavoriteFragment);
739 transaction.commit();
740 mInSearchUi = false;
Yorke Lee6b049122013-07-16 10:38:02 -0700741 }
742
743 /** Returns an Intent to launch Call Settings screen */
744 public static Intent getCallSettingsIntent() {
745 final Intent intent = new Intent(Intent.ACTION_MAIN);
746 intent.setClassName(PHONE_PACKAGE, CALL_SETTINGS_CLASS_NAME);
747 intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
748 return intent;
749 }
750
751 @Override
Yorke Leedfb2eee2013-06-26 18:24:32 -0700752 public void onBackPressed() {
753 if (mDialpadFragment.isVisible()) {
Yorke Lee80a05ed2013-07-26 17:05:04 -0700754 hideDialpadFragment(true);
Yorke Leedfb2eee2013-06-26 18:24:32 -0700755 } else if (mInSearchUi) {
Yorke Leed9999322013-07-19 09:22:03 -0700756 mSearchView.setText(null);
Yorke Leedfb2eee2013-06-26 18:24:32 -0700757 } else if (isTaskRoot()) {
758 // Instead of stopping, simply push this to the back of the stack.
759 // This is only done when running at the top of the stack;
760 // otherwise, we have been launched by someone else so need to
761 // allow the user to go back to the caller.
762 moveTaskToBack(false);
763 } else {
764 super.onBackPressed();
Yorke Lee6b049122013-07-16 10:38:02 -0700765 }
Yorke Leedfb2eee2013-06-26 18:24:32 -0700766 }
767
768 @Override
769 public void onDialpadQueryChanged(String query) {
770 final String normalizedQuery = SmartDialNameMatcher.normalizeNumber(query,
771 SmartDialNameMatcher.LATIN_SMART_DIAL_MAP);
Yorke Leed9999322013-07-19 09:22:03 -0700772 if (!TextUtils.equals(mSearchView.getText(), normalizedQuery)) {
773 mSearchView.setText(normalizedQuery);
Yorke Lee6b049122013-07-16 10:38:02 -0700774 }
775 }
Yorke Leedfb2eee2013-06-26 18:24:32 -0700776
777 @Override
778 public void onListFragmentScrollStateChange(int scrollState) {
779 if (scrollState == OnScrollListener.SCROLL_STATE_TOUCH_SCROLL) {
780 hideDialpadFragmentIfNecessary();
781 hideInputMethod(getCurrentFocus());
782 }
783 }
784
785 @Override
786 public void onPhoneFavoriteFragmentStarted() {
Yorke Lee714d6c72013-07-26 16:11:39 -0700787 setupFakeActionBarItemsForFavoritesFragment();
Yorke Leedfb2eee2013-06-26 18:24:32 -0700788 }
Yorke Lee714d6c72013-07-26 16:11:39 -0700789
790 @Override
791 public void onDialpadFragmentStarted() {
792 setupFakeActionBarItemsForDialpadFragment();
793 }
794
Yorke Lee80a05ed2013-07-26 17:05:04 -0700795 private boolean phoneIsInUse() {
796 final TelephonyManager tm = (TelephonyManager) getSystemService(
797 Context.TELEPHONY_SERVICE);
798 return tm.getCallState() != TelephonyManager.CALL_STATE_IDLE;
799 }
Yorke Lee6b049122013-07-16 10:38:02 -0700800}