blob: 8cac17eb33f3e451cd4ed7d2c57e135da956e301 [file] [log] [blame]
Chiao Cheng94b10b52012-08-17 16:59:12 -07001/*
Yorke Leec3766332013-07-31 11:13:16 -07002 * Copyright (C) 2013 The Android Open Source Project
Chiao Cheng94b10b52012-08-17 16:59:12 -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 Leec3766332013-07-31 11:13:16 -070019import android.animation.Animator;
20import android.animation.Animator.AnimatorListener;
21import android.animation.AnimatorListenerAdapter;
Chiao Cheng94b10b52012-08-17 16:59:12 -070022import android.app.Activity;
Christine Chen9c1e0652013-05-23 15:40:19 -070023import android.app.backup.BackupManager;
Chiao Cheng94b10b52012-08-17 16:59:12 -070024import android.app.Fragment;
25import android.app.FragmentManager;
26import android.app.FragmentTransaction;
Chiao Cheng073b5cf2012-12-07 11:36:00 -080027import android.content.ActivityNotFoundException;
Chiao Cheng94b10b52012-08-17 16:59:12 -070028import android.content.Context;
29import android.content.Intent;
30import android.content.SharedPreferences;
Yorke Leec3766332013-07-31 11:13:16 -070031import android.content.res.Resources;
Chiao Cheng94b10b52012-08-17 16:59:12 -070032import android.net.Uri;
33import android.os.Bundle;
34import android.os.RemoteException;
35import android.os.ServiceManager;
Chiao Cheng94b10b52012-08-17 16:59:12 -070036import android.provider.CallLog.Calls;
Yorke Leec3766332013-07-31 11:13:16 -070037import android.provider.ContactsContract;
Chiao Cheng94b10b52012-08-17 16:59:12 -070038import android.provider.ContactsContract.Contacts;
39import android.provider.ContactsContract.Intents.UI;
Yorke Leec3766332013-07-31 11:13:16 -070040import android.provider.Settings;
41import android.speech.RecognizerIntent;
42import android.support.v4.app.NavUtils;
43import android.telephony.TelephonyManager;
44import android.text.Editable;
Chiao Cheng94b10b52012-08-17 16:59:12 -070045import android.text.TextUtils;
Yorke Leec3766332013-07-31 11:13:16 -070046import android.text.TextWatcher;
Chiao Cheng94b10b52012-08-17 16:59:12 -070047import android.util.Log;
48import android.view.Menu;
Chiao Cheng94b10b52012-08-17 16:59:12 -070049import android.view.MenuItem;
Chiao Cheng94b10b52012-08-17 16:59:12 -070050import android.view.View;
Chiao Cheng94b10b52012-08-17 16:59:12 -070051import android.view.View.OnFocusChangeListener;
52import android.view.ViewConfiguration;
Chiao Cheng94b10b52012-08-17 16:59:12 -070053import android.view.inputmethod.InputMethodManager;
Yorke Leec3766332013-07-31 11:13:16 -070054import android.widget.AbsListView.OnScrollListener;
55import android.widget.EditText;
56import android.widget.ImageView;
Chiao Cheng94b10b52012-08-17 16:59:12 -070057import android.widget.PopupMenu;
58import android.widget.SearchView;
59import android.widget.SearchView.OnCloseListener;
60import android.widget.SearchView.OnQueryTextListener;
Chiao Cheng073b5cf2012-12-07 11:36:00 -080061import android.widget.Toast;
Chiao Cheng94b10b52012-08-17 16:59:12 -070062
Chiao Cheng9d4f3b22012-09-05 16:00:16 -070063import com.android.contacts.common.CallUtil;
Chiao Cheng603ff682012-10-24 15:18:40 -070064import com.android.contacts.common.activity.TransactionSafeActivity;
Yorke Leec3766332013-07-31 11:13:16 -070065import com.android.contacts.common.dialog.ClearFrequentsDialog;
66import com.android.contacts.common.interactions.ImportExportDialogFragment;
Chiao Cheng1429f1a2012-11-01 16:35:28 -070067import com.android.contacts.common.list.ContactListItemView;
Chiao Cheng8efbcf92012-12-04 17:43:02 -080068import com.android.contacts.common.list.OnPhoneNumberPickerActionListener;
69import com.android.contacts.common.list.PhoneNumberPickerFragment;
Yorke Leec3766332013-07-31 11:13:16 -070070import com.android.dialer.calllog.CallLogActivity;
Yorke Leef74011e2013-08-02 11:30:30 -070071import com.android.dialer.dialpad.DialpadFragment;
Yorke Leec3766332013-07-31 11:13:16 -070072import com.android.dialer.dialpad.SmartDialNameMatcher;
Chiao Cheng1429f1a2012-11-01 16:35:28 -070073import com.android.dialer.interactions.PhoneNumberInteraction;
Yorke Leec3766332013-07-31 11:13:16 -070074import com.android.dialer.list.NewPhoneFavoriteFragment;
75import com.android.dialer.list.OnListFragmentScrolledListener;
76import com.android.dialer.list.ShowAllContactsFragment;
77import com.android.dialer.list.SmartDialSearchFragment;
Chiao Cheng94b10b52012-08-17 16:59:12 -070078import com.android.internal.telephony.ITelephony;
79
Yorke Leec3766332013-07-31 11:13:16 -070080import java.util.ArrayList;
81
Chiao Cheng94b10b52012-08-17 16:59:12 -070082/**
Chiao Cheng94b10b52012-08-17 16:59:12 -070083 * The dialer tab's title is 'phone', a more common name (see strings.xml).
Yorke Leec3766332013-07-31 11:13:16 -070084 *
85 * TODO krelease: All classes currently prefixed with New will replace the original classes or
86 * be renamed more appropriately before shipping.
Chiao Cheng94b10b52012-08-17 16:59:12 -070087 */
Yorke Leec3766332013-07-31 11:13:16 -070088public class DialtactsActivity extends TransactionSafeActivity implements View.OnClickListener,
Yorke Leef74011e2013-08-02 11:30:30 -070089 DialpadFragment.OnDialpadQueryChangedListener, PopupMenu.OnMenuItemClickListener,
Yorke Leec3766332013-07-31 11:13:16 -070090 OnListFragmentScrolledListener,
91 NewPhoneFavoriteFragment.OnPhoneFavoriteFragmentStartedListener,
Yorke Leef74011e2013-08-02 11:30:30 -070092 DialpadFragment.OnDialpadFragmentStartedListener {
Chiao Cheng94b10b52012-08-17 16:59:12 -070093 private static final String TAG = "DialtactsActivity";
94
95 public static final boolean DEBUG = false;
96
Yorke Leef74011e2013-08-02 11:30:30 -070097 public static final String SHARED_PREFS_NAME = "com.android.dialer_preferences";
98
Chiao Cheng94b10b52012-08-17 16:59:12 -070099 /** Used to open Call Setting */
100 private static final String PHONE_PACKAGE = "com.android.phone";
101 private static final String CALL_SETTINGS_CLASS_NAME =
102 "com.android.phone.CallFeaturesSetting";
Chiao Cheng94b10b52012-08-17 16:59:12 -0700103 /** @see #getCallOrigin() */
104 private static final String CALL_ORIGIN_DIALTACTS =
105 "com.android.dialer.DialtactsActivity";
106
Yorke Leec3766332013-07-31 11:13:16 -0700107 private static final String TAG_DIALPAD_FRAGMENT = "dialpad";
108 private static final String TAG_REGULAR_SEARCH_FRAGMENT = "search";
109 private static final String TAG_SMARTDIAL_SEARCH_FRAGMENT = "smartdial";
110 private static final String TAG_FAVORITES_FRAGMENT = "favorites";
111 private static final String TAG_SHOW_ALL_CONTACTS_FRAGMENT = "show_all_contacts";
112
Chiao Cheng94b10b52012-08-17 16:59:12 -0700113 /**
114 * Just for backward compatibility. Should behave as same as {@link Intent#ACTION_DIAL}.
115 */
116 private static final String ACTION_TOUCH_DIALER = "com.android.phone.action.TOUCH_DIALER";
117
Chiao Cheng94b10b52012-08-17 16:59:12 -0700118 private static final int SUBACTIVITY_ACCOUNT_FILTER = 1;
119
Yorke Leec3766332013-07-31 11:13:16 -0700120 private static final int ACTIVITY_REQUEST_CODE_VOICE_SEARCH = 1;
Chiao Cheng94b10b52012-08-17 16:59:12 -0700121
122 private String mFilterText;
123
Yorke Leec3766332013-07-31 11:13:16 -0700124 /**
125 * The main fragment displaying the user's favorites and frequent contacts
126 */
127 private NewPhoneFavoriteFragment mPhoneFavoriteFragment;
Chiao Cheng94b10b52012-08-17 16:59:12 -0700128
129 /**
Yorke Leec3766332013-07-31 11:13:16 -0700130 * Fragment containing the dialpad that slides into view
Chiao Cheng94b10b52012-08-17 16:59:12 -0700131 */
Yorke Leef74011e2013-08-02 11:30:30 -0700132 private DialpadFragment mDialpadFragment;
Yorke Leec3766332013-07-31 11:13:16 -0700133
134 /**
135 * Fragment for searching phone numbers using the alphanumeric keyboard.
136 */
137 private NewSearchFragment mRegularSearchFragment;
138
139 /**
140 * Fragment for searching phone numbers using the dialpad.
141 */
142 private SmartDialSearchFragment mSmartDialSearchFragment;
143
144 private ShowAllContactsFragment mShowAllContactsFragment;
145
146 private View mMenuButton;
147 private View mCallHistoryButton;
148 private View mDialpadButton;
149
150 // Padding view used to shift the fragments up when the dialpad is shown.
151 private View mBottomPaddingView;
152
Chiao Cheng94b10b52012-08-17 16:59:12 -0700153 /**
154 * True when this Activity is in its search UI (with a {@link SearchView} and
155 * {@link PhoneNumberPickerFragment}).
156 */
157 private boolean mInSearchUi;
Yorke Lee98702de2013-08-06 12:03:32 -0700158 private boolean mFirstLaunch;
Yorke Leec3766332013-07-31 11:13:16 -0700159 private View mSearchViewContainer;
160 private View mSearchViewCloseButton;
161 private View mVoiceSearchButton;
162 private EditText mSearchView;
Chiao Cheng94b10b52012-08-17 16:59:12 -0700163
164 /**
165 * Listener used when one of phone numbers in search UI is selected. This will initiate a
166 * phone call using the phone number.
167 */
168 private final OnPhoneNumberPickerActionListener mPhoneNumberPickerActionListener =
169 new OnPhoneNumberPickerActionListener() {
170 @Override
171 public void onPickPhoneNumberAction(Uri dataUri) {
172 // Specify call-origin so that users will see the previous tab instead of
173 // CallLog screen (search UI will be automatically exited).
174 PhoneNumberInteraction.startInteractionForPhoneCall(
Yorke Leec3766332013-07-31 11:13:16 -0700175 DialtactsActivity.this, dataUri, getCallOrigin());
Chiao Cheng94b10b52012-08-17 16:59:12 -0700176 }
177
178 @Override
179 public void onShortcutIntentCreated(Intent intent) {
180 Log.w(TAG, "Unsupported intent has come (" + intent + "). Ignoring.");
181 }
182
183 @Override
184 public void onHomeInActionBarSelected() {
185 exitSearchUi();
186 }
187 };
188
189 /**
190 * Listener used to send search queries to the phone search fragment.
191 */
Yorke Leec3766332013-07-31 11:13:16 -0700192 private final TextWatcher mPhoneSearchQueryTextListener = new TextWatcher() {
193 @Override
194 public void beforeTextChanged(CharSequence s, int start, int count, int after) {
195 }
196
197 @Override
198 public void onTextChanged(CharSequence s, int start, int before, int count) {
199 // TODO krelease: populate the search fragments with the correct
200 // search query at the correct point in time of the fragment lifecycle.
201 // The current behavior is to simply return to the favorites screen
202 // (when docked), or returning to the Dialer after it has been
203 // swapped out of memory.
204 if (mDialpadFragment == null) return;
205 final boolean smartDialSearch = isDialpadShowing();
206 final String newText = s.toString();
207 // Show search result with non-empty text. Show a bare list otherwise.
208 if (TextUtils.isEmpty(newText) && mInSearchUi) {
209 exitSearchUi();
210 mSearchViewCloseButton.setVisibility(View.GONE);
211 return;
212 } else if (!TextUtils.isEmpty(newText) && !mInSearchUi) {
213 enterSearchUi(smartDialSearch);
Chiao Cheng94b10b52012-08-17 16:59:12 -0700214 }
215
Yorke Leec3766332013-07-31 11:13:16 -0700216 if (smartDialSearch) {
217 mSmartDialSearchFragment.setQueryString(newText, false);
218 } else {
219 mRegularSearchFragment.setQueryString(newText, false);
Chiao Cheng94b10b52012-08-17 16:59:12 -0700220 }
Yorke Leec3766332013-07-31 11:13:16 -0700221 mSearchViewCloseButton.setVisibility(View.VISIBLE);
222 return;
223 }
224
225 @Override
226 public void afterTextChanged(Editable s) {
227 }
Chiao Cheng94b10b52012-08-17 16:59:12 -0700228 };
229
Yorke Leec3766332013-07-31 11:13:16 -0700230 private boolean isDialpadShowing() {
231 return mDialpadFragment != null && mDialpadFragment.isVisible();
232 }
Chiao Cheng94b10b52012-08-17 16:59:12 -0700233
234 @Override
Yorke Leec3766332013-07-31 11:13:16 -0700235 protected void onCreate(Bundle savedInstanceState) {
236 super.onCreate(savedInstanceState);
Chiao Cheng94b10b52012-08-17 16:59:12 -0700237
Yorke Lee98702de2013-08-06 12:03:32 -0700238 mFirstLaunch = true;
239
Chiao Cheng94b10b52012-08-17 16:59:12 -0700240 final Intent intent = getIntent();
241 fixIntent(intent);
242
Yorke Leec3766332013-07-31 11:13:16 -0700243 setContentView(R.layout.new_dialtacts_activity);
Chiao Cheng94b10b52012-08-17 16:59:12 -0700244
Yorke Leec3766332013-07-31 11:13:16 -0700245 getActionBar().hide();
Chiao Cheng94b10b52012-08-17 16:59:12 -0700246
Yorke Leec3766332013-07-31 11:13:16 -0700247 if (savedInstanceState == null) {
248 mPhoneFavoriteFragment = new NewPhoneFavoriteFragment();
249 mPhoneFavoriteFragment.setListener(mPhoneFavoriteListener);
Chiao Cheng94b10b52012-08-17 16:59:12 -0700250
Yorke Leec3766332013-07-31 11:13:16 -0700251 mRegularSearchFragment = new NewSearchFragment();
252 mSmartDialSearchFragment = new SmartDialSearchFragment();
Yorke Leef74011e2013-08-02 11:30:30 -0700253 mDialpadFragment = new DialpadFragment();
Yorke Leec3766332013-07-31 11:13:16 -0700254 mShowAllContactsFragment = new ShowAllContactsFragment();
255 mShowAllContactsFragment.setOnPhoneNumberPickerActionListener(
256 mPhoneNumberPickerActionListener);
Chiao Cheng94b10b52012-08-17 16:59:12 -0700257
Yorke Leec3766332013-07-31 11:13:16 -0700258 // TODO krelease: load fragments on demand instead of creating all of them at run time
259 final FragmentTransaction ft = getFragmentManager().beginTransaction();
260 ft.add(R.id.dialtacts_frame, mPhoneFavoriteFragment, TAG_FAVORITES_FRAGMENT);
261 ft.add(R.id.dialtacts_frame, mRegularSearchFragment, TAG_REGULAR_SEARCH_FRAGMENT);
262 ft.add(R.id.dialtacts_frame, mSmartDialSearchFragment, TAG_SMARTDIAL_SEARCH_FRAGMENT);
263 ft.add(R.id.dialtacts_frame, mShowAllContactsFragment, TAG_SHOW_ALL_CONTACTS_FRAGMENT);
264 ft.add(R.id.dialtacts_container, mDialpadFragment, TAG_DIALPAD_FRAGMENT);
265 ft.commit();
Chiao Cheng94b10b52012-08-17 16:59:12 -0700266 }
267
Yorke Leec3766332013-07-31 11:13:16 -0700268 mBottomPaddingView = findViewById(R.id.dialtacts_bottom_padding);
269 prepareSearchView();
Chiao Cheng94b10b52012-08-17 16:59:12 -0700270
Chiao Cheng94b10b52012-08-17 16:59:12 -0700271 if (UI.FILTER_CONTACTS_ACTION.equals(intent.getAction())
Yorke Leec3766332013-07-31 11:13:16 -0700272 && savedInstanceState == null) {
Chiao Cheng94b10b52012-08-17 16:59:12 -0700273 setupFilterText(intent);
274 }
275 }
276
277 @Override
Yorke Leec3766332013-07-31 11:13:16 -0700278 protected void onResume() {
279 super.onResume();
280 final FragmentManager fm = getFragmentManager();
281 mPhoneFavoriteFragment = (NewPhoneFavoriteFragment) fm.findFragmentByTag(
282 TAG_FAVORITES_FRAGMENT);
Yorke Leef74011e2013-08-02 11:30:30 -0700283 mDialpadFragment = (DialpadFragment) fm.findFragmentByTag(TAG_DIALPAD_FRAGMENT);
Chiao Cheng94b10b52012-08-17 16:59:12 -0700284
Yorke Leec3766332013-07-31 11:13:16 -0700285 mRegularSearchFragment = (NewSearchFragment) fm.findFragmentByTag(
286 TAG_REGULAR_SEARCH_FRAGMENT);
287 mRegularSearchFragment.setOnPhoneNumberPickerActionListener(
288 mPhoneNumberPickerActionListener);
Chiao Cheng94b10b52012-08-17 16:59:12 -0700289
Yorke Leec3766332013-07-31 11:13:16 -0700290 mSmartDialSearchFragment = (SmartDialSearchFragment) fm.findFragmentByTag(
291 TAG_SMARTDIAL_SEARCH_FRAGMENT);
292 mSmartDialSearchFragment.setOnPhoneNumberPickerActionListener(
293 mPhoneNumberPickerActionListener);
294
295 mShowAllContactsFragment = (ShowAllContactsFragment) fm.findFragmentByTag(
296 TAG_SHOW_ALL_CONTACTS_FRAGMENT);
297 mShowAllContactsFragment.setOnPhoneNumberPickerActionListener(
298 mPhoneNumberPickerActionListener);
Yorke Lee98702de2013-08-06 12:03:32 -0700299
300 if (mFirstLaunch) {
301 displayFragment(getIntent());
302 }
303 mFirstLaunch = false;
Chiao Cheng94b10b52012-08-17 16:59:12 -0700304 }
305
306 @Override
Yorke Leec3766332013-07-31 11:13:16 -0700307 public void onAttachFragment(Fragment fragment) {
Yorke Leef74011e2013-08-02 11:30:30 -0700308 if (fragment instanceof DialpadFragment || fragment instanceof NewSearchFragment
Yorke Leec3766332013-07-31 11:13:16 -0700309 || fragment instanceof SmartDialSearchFragment
310 || fragment instanceof ShowAllContactsFragment) {
311 final FragmentTransaction transaction = getFragmentManager().beginTransaction();
312 transaction.hide(fragment);
313 transaction.commit();
314 }
315 // TODO krelease: Save some kind of state here to show the appropriate fragment
316 // based on the state of the dialer when it was last paused
317 }
318
319 @Override
320 public boolean onOptionsItemSelected(MenuItem item) {
321 switch (item.getItemId()) {
322 // Respond to the action bar's Up/Home button
323 case android.R.id.home:
324 hideAllContactsFragment();
325 }
326 return super.onOptionsItemSelected(item);
327 }
328
329 @Override
330 public boolean onMenuItemClick(MenuItem item) {
331 switch (item.getItemId()) {
332 case R.id.menu_import_export:
333 // We hard-code the "contactsAreAvailable" argument because doing it properly would
334 // involve querying a {@link ProviderStatusLoader}, which we don't want to do right
335 // now in Dialtacts for (potential) performance reasons. Compare with how it is
336 // done in {@link PeopleActivity}.
337 ImportExportDialogFragment.show(getFragmentManager(), true,
Yorke Leef74011e2013-08-02 11:30:30 -0700338 DialtactsActivity.class);
Yorke Leec3766332013-07-31 11:13:16 -0700339 return true;
340 case R.id.menu_clear_frequents:
341 ClearFrequentsDialog.show(getFragmentManager());
342 return true;
343 case R.id.add_contact:
344 try {
345 startActivity(new Intent(Intent.ACTION_INSERT, Contacts.CONTENT_URI));
346 } catch (ActivityNotFoundException e) {
347 Toast toast = Toast.makeText(this,
348 R.string.add_contact_not_available,
349 Toast.LENGTH_SHORT);
350 toast.show();
351 }
352 return true;
353 case R.id.menu_call_settings:
Yorke Leef74011e2013-08-02 11:30:30 -0700354 final Intent settingsIntent = DialtactsActivity.getCallSettingsIntent();
Yorke Leec3766332013-07-31 11:13:16 -0700355 startActivity(settingsIntent);
356 }
357 return false;
Chiao Cheng94b10b52012-08-17 16:59:12 -0700358 }
359
360 @Override
361 public void onClick(View view) {
362 switch (view.getId()) {
Yorke Leec3766332013-07-31 11:13:16 -0700363 case R.id.overflow_menu: {
364 final PopupMenu popupMenu = new PopupMenu(DialtactsActivity.this, view);
365 final Menu menu = popupMenu.getMenu();
366 popupMenu.inflate(R.menu.dialtacts_options_new);
367 popupMenu.setOnMenuItemClickListener(this);
368 popupMenu.show();
Chiao Cheng94b10b52012-08-17 16:59:12 -0700369 break;
370 }
Yorke Leec3766332013-07-31 11:13:16 -0700371 case R.id.dialpad_button:
372 showDialpadFragment(true);
373 break;
374 case R.id.call_history_on_dialpad_button:
375 case R.id.call_history_button:
376 // TODO krelease: This should start an intent with content type
377 // CallLog.Calls.CONTENT_TYPE, once the intent filters for the call log activity
378 // is enabled
379 final Intent intent = new Intent(this, CallLogActivity.class);
380 startActivity(intent);
381 break;
382 case R.id.search_close_button:
383 // Clear the search field
384 if (!TextUtils.isEmpty(mSearchView.getText())) {
385 mSearchView.setText("");
Chiao Cheng94b10b52012-08-17 16:59:12 -0700386 }
387 break;
Yorke Leec3766332013-07-31 11:13:16 -0700388 case R.id.voice_search_button:
389 final Intent voiceIntent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
390 startActivityForResult(voiceIntent, ACTIVITY_REQUEST_CODE_VOICE_SEARCH);
391 break;
Chiao Cheng94b10b52012-08-17 16:59:12 -0700392 default: {
393 Log.wtf(TAG, "Unexpected onClick event from " + view);
394 break;
395 }
396 }
397 }
398
Yorke Leec3766332013-07-31 11:13:16 -0700399 @Override
400 protected void onActivityResult(int requestCode, int resultCode, Intent data) {
401 if (requestCode == ACTIVITY_REQUEST_CODE_VOICE_SEARCH) {
402 if (resultCode == RESULT_OK) {
403 final ArrayList<String> matches = data.getStringArrayListExtra(
404 RecognizerIntent.EXTRA_RESULTS);
405 if (matches.size() > 0) {
406 final String match = matches.get(0);
407 mSearchView.setText(match);
408 } else {
409 Log.e(TAG, "Voice search - nothing heard");
410 }
411 } else {
412 Log.e(TAG, "Voice search failed");
413 }
414 }
415 super.onActivityResult(requestCode, resultCode, data);
416 }
Chiao Cheng94b10b52012-08-17 16:59:12 -0700417
Yorke Leec3766332013-07-31 11:13:16 -0700418 private void showDialpadFragment(boolean animate) {
Chiao Cheng94b10b52012-08-17 16:59:12 -0700419 final FragmentTransaction ft = getFragmentManager().beginTransaction();
Yorke Leec3766332013-07-31 11:13:16 -0700420 if (animate) {
421 ft.setCustomAnimations(R.anim.slide_in, 0);
422 }
423 ft.show(mDialpadFragment);
424 ft.commit();
425 }
Chiao Cheng94b10b52012-08-17 16:59:12 -0700426
Yorke Leec3766332013-07-31 11:13:16 -0700427 private void hideDialpadFragment(boolean animate) {
428 final FragmentTransaction ft = getFragmentManager().beginTransaction();
429 if (animate) {
430 ft.setCustomAnimations(0, R.anim.slide_out);
431 }
432 ft.hide(mDialpadFragment);
433 ft.commit();
434 }
435
436 public void showAllContactsFragment() {
437 final FragmentTransaction ft = getFragmentManager().beginTransaction();
438 ft.hide(mPhoneFavoriteFragment);
439 ft.show(mShowAllContactsFragment);
440 // TODO{klp} Add animation
441 ft.commit();
442 hideSearchBar(false);
443 }
444
445 private void hideAllContactsFragment() {
446 final FragmentTransaction ft = getFragmentManager().beginTransaction();
447 ft.hide(mShowAllContactsFragment);
448 ft.show(mPhoneFavoriteFragment);
449 ft.commit();
450 showSearchBar();
Chiao Cheng94b10b52012-08-17 16:59:12 -0700451 }
452
453 private void prepareSearchView() {
Yorke Leec3766332013-07-31 11:13:16 -0700454 mSearchViewContainer = findViewById(R.id.search_view_container);
455 mSearchViewCloseButton = findViewById(R.id.search_close_button);
456 mSearchViewCloseButton.setOnClickListener(this);
457 mVoiceSearchButton = findViewById(R.id.voice_search_button);
458 mVoiceSearchButton.setOnClickListener(this);
459 mSearchView = (EditText) findViewById(R.id.search_view);
460 mSearchView.addTextChangedListener(mPhoneSearchQueryTextListener);
461 mSearchView.setHint(getString(R.string.dialer_hint_find_contact));
462 mSearchView.setOnFocusChangeListener(new OnFocusChangeListener() {
Chiao Cheng94b10b52012-08-17 16:59:12 -0700463 @Override
464 public void onFocusChange(View view, boolean hasFocus) {
465 if (hasFocus) {
466 showInputMethod(view.findFocus());
467 }
468 }
469 });
Chiao Cheng94b10b52012-08-17 16:59:12 -0700470 }
471
Yorke Leec3766332013-07-31 11:13:16 -0700472 private void hideDialpadFragmentIfNecessary() {
473 if (mDialpadFragment.isVisible()) {
474 hideDialpadFragment(true);
Chiao Cheng94b10b52012-08-17 16:59:12 -0700475 }
476 }
477
Yorke Leec3766332013-07-31 11:13:16 -0700478 final AnimatorListener mHideListener = new AnimatorListenerAdapter() {
479 @Override
480 public void onAnimationEnd(Animator animation) {
481 mSearchViewContainer.setVisibility(View.GONE);
482 }
483 };
Chiao Cheng94b10b52012-08-17 16:59:12 -0700484
Yorke Leec3766332013-07-31 11:13:16 -0700485 public void hideSearchBar() {
486 hideSearchBar(true);
Christine Chen9c1e0652013-05-23 15:40:19 -0700487 }
488
Yorke Leec3766332013-07-31 11:13:16 -0700489 public void hideSearchBar(boolean shiftView) {
490 if (shiftView) {
491 mSearchViewContainer.animate().cancel();
492 mSearchViewContainer.setAlpha(1);
493 mSearchViewContainer.setTranslationY(0);
494 mSearchViewContainer.animate().withLayer().alpha(0).translationY(-mSearchView.getHeight())
495 .setDuration(200).setListener(mHideListener);
496
497 mPhoneFavoriteFragment.getView().animate().withLayer()
498 .translationY(-mSearchViewContainer.getHeight()).setDuration(200).setListener(
499 new AnimatorListenerAdapter() {
500 @Override
501 public void onAnimationEnd(Animator animation) {
502 mBottomPaddingView.setVisibility(View.VISIBLE);
503 mPhoneFavoriteFragment.getView().setTranslationY(0);
504 }
505 });
506 } else {
507 mSearchViewContainer.setTranslationY(-mSearchView.getHeight());
508 }
509 }
510
511 public void showSearchBar() {
512 // If the favorites fragment hasn't been fully created before the dialpad fragment
513 // is hidden (i.e. onResume), don't bother animating
514 if (mPhoneFavoriteFragment == null || mPhoneFavoriteFragment.getView() == null) {
515 return;
516 }
517 mSearchViewContainer.animate().cancel();
518 mSearchViewContainer.setAlpha(0);
519 mSearchViewContainer.setTranslationY(-mSearchViewContainer.getHeight());
520 mSearchViewContainer.animate().withLayer().alpha(1).translationY(0).setDuration(200)
521 .setListener(new AnimatorListenerAdapter() {
522 @Override
523 public void onAnimationStart(Animator animation) {
524 mSearchViewContainer.setVisibility(View.VISIBLE);
525 }
526 });
527
528 mPhoneFavoriteFragment.getView().setTranslationY(-mSearchViewContainer.getHeight());
529 mPhoneFavoriteFragment.getView().animate().withLayer().translationY(0).setDuration(200)
530 .setListener(
531 new AnimatorListenerAdapter() {
532 @Override
533 public void onAnimationStart(Animator animation) {
534 mBottomPaddingView.setVisibility(View.GONE);
535 }
536 });
537 }
538
539
540 public void setupFakeActionBarItemsForFavoritesFragment() {
541 mMenuButton = findViewById(R.id.overflow_menu);
542 if (mMenuButton != null) {
543 mMenuButton.setOnClickListener(this);
544 }
545
546 mCallHistoryButton = findViewById(R.id.call_history_button);
547 // mCallHistoryButton.setMinimumWidth(fakeMenuItemWidth);
548 mCallHistoryButton.setOnClickListener(this);
549
550 mDialpadButton = findViewById(R.id.dialpad_button);
551 // DialpadButton.setMinimumWidth(fakeMenuItemWidth);
552 mDialpadButton.setOnClickListener(this);
553 }
554
555 public void setupFakeActionBarItemsForDialpadFragment() {
556 final View callhistoryButton = findViewById(R.id.call_history_on_dialpad_button);
557 callhistoryButton.setOnClickListener(this);
Chiao Cheng94b10b52012-08-17 16:59:12 -0700558 }
559
560 private void fixIntent(Intent intent) {
561 // This should be cleaned up: the call key used to send an Intent
562 // that just said to go to the recent calls list. It now sends this
563 // abstract action, but this class hasn't been rewritten to deal with it.
564 if (Intent.ACTION_CALL_BUTTON.equals(intent.getAction())) {
565 intent.setDataAndType(Calls.CONTENT_URI, Calls.CONTENT_TYPE);
566 intent.putExtra("call_key", true);
567 setIntent(intent);
568 }
569 }
570
Chiao Cheng94b10b52012-08-17 16:59:12 -0700571 /**
572 * Returns true if the intent is due to hitting the green send key (hardware call button:
573 * KEYCODE_CALL) while in a call.
574 *
575 * @param intent the intent that launched this activity
576 * @param recentCallsRequest true if the intent is requesting to view recent calls
577 * @return true if the intent is due to hitting the green send key while in a call
578 */
Yorke Leec3766332013-07-31 11:13:16 -0700579 private boolean isSendKeyWhileInCall(Intent intent, boolean recentCallsRequest) {
Chiao Cheng94b10b52012-08-17 16:59:12 -0700580 // If there is a call in progress go to the call screen
581 if (recentCallsRequest) {
582 final boolean callKey = intent.getBooleanExtra("call_key", false);
583
584 try {
585 ITelephony phone = ITelephony.Stub.asInterface(ServiceManager.checkService("phone"));
586 if (callKey && phone != null && phone.showCallScreen()) {
587 return true;
588 }
589 } catch (RemoteException e) {
590 Log.e(TAG, "Failed to handle send while in call", e);
591 }
592 }
593
594 return false;
595 }
596
597 /**
598 * Sets the current tab based on the intent's request type
599 *
600 * @param intent Intent that contains information about which tab should be selected
601 */
Yorke Leec3766332013-07-31 11:13:16 -0700602 private void displayFragment(Intent intent) {
603 // TODO krelease: Make navigation via intent work by displaying the correct fragment
604 // as appropriate.
605
Chiao Cheng94b10b52012-08-17 16:59:12 -0700606 // If we got here by hitting send and we're in call forward along to the in-call activity
607 boolean recentCallsRequest = Calls.CONTENT_TYPE.equals(intent.resolveType(
608 getContentResolver()));
609 if (isSendKeyWhileInCall(intent, recentCallsRequest)) {
610 finish();
611 return;
612 }
613
Yorke Lee98702de2013-08-06 12:03:32 -0700614 if (mDialpadFragment != null && (phoneIsInUse() || isDialIntent(intent))) {
Yorke Leec3766332013-07-31 11:13:16 -0700615 mDialpadFragment.setStartedFromNewIntent(true);
616 // TODO krelease: This should use showDialpadFragment(false) to avoid animating
617 // the dialpad in. Need to fix the onPreDrawListener in NewDialpadFragment first.
618 showDialpadFragment(true);
Chiao Cheng94b10b52012-08-17 16:59:12 -0700619 }
Chiao Cheng94b10b52012-08-17 16:59:12 -0700620 }
621
622 @Override
623 public void onNewIntent(Intent newIntent) {
624 setIntent(newIntent);
625 fixIntent(newIntent);
Yorke Leec3766332013-07-31 11:13:16 -0700626 displayFragment(newIntent);
Chiao Cheng94b10b52012-08-17 16:59:12 -0700627 final String action = newIntent.getAction();
Yorke Leec3766332013-07-31 11:13:16 -0700628
629 if (mInSearchUi || (mRegularSearchFragment != null && mRegularSearchFragment.isVisible())) {
Chiao Cheng94b10b52012-08-17 16:59:12 -0700630 exitSearchUi();
631 }
632
Yorke Leec3766332013-07-31 11:13:16 -0700633 // TODO krelease: Handle onNewIntent for all other fragments
634 /*
635 *if (mViewPager.getCurrentItem() == TAB_INDEX_DIALER) { if (mDialpadFragment != null) {
636 * mDialpadFragment.setStartedFromNewIntent(true); } else { Log.e(TAG,
637 * "DialpadFragment isn't ready yet when the tab is already selected."); } } else if
638 * (mViewPager.getCurrentItem() == TAB_INDEX_CALL_LOG) { if (mCallLogFragment != null) {
639 * mCallLogFragment.configureScreenFromIntent(newIntent); } else { Log.e(TAG,
640 * "CallLogFragment isn't ready yet when the tab is already selected."); } }
641 */
Chiao Cheng94b10b52012-08-17 16:59:12 -0700642 invalidateOptionsMenu();
643 }
644
645 /** Returns true if the given intent contains a phone number to populate the dialer with */
646 private boolean isDialIntent(Intent intent) {
647 final String action = intent.getAction();
648 if (Intent.ACTION_DIAL.equals(action) || ACTION_TOUCH_DIALER.equals(action)) {
649 return true;
650 }
651 if (Intent.ACTION_VIEW.equals(action)) {
652 final Uri data = intent.getData();
Chiao Cheng9d4f3b22012-09-05 16:00:16 -0700653 if (data != null && CallUtil.SCHEME_TEL.equals(data.getScheme())) {
Chiao Cheng94b10b52012-08-17 16:59:12 -0700654 return true;
655 }
656 }
657 return false;
658 }
659
660 /**
661 * Returns an appropriate call origin for this Activity. May return null when no call origin
662 * should be used (e.g. when some 3rd party application launched the screen. Call origin is
663 * for remembering the tab in which the user made a phone call, so the external app's DIAL
664 * request should not be counted.)
665 */
666 public String getCallOrigin() {
667 return !isDialIntent(getIntent()) ? CALL_ORIGIN_DIALTACTS : null;
668 }
669
670 /**
671 * Retrieves the filter text stored in {@link #setupFilterText(Intent)}.
672 * This text originally came from a FILTER_CONTACTS_ACTION intent received
673 * by this activity. The stored text will then be cleared after after this
674 * method returns.
675 *
676 * @return The stored filter text
677 */
678 public String getAndClearFilterText() {
679 String filterText = mFilterText;
680 mFilterText = null;
681 return filterText;
682 }
683
684 /**
685 * Stores the filter text associated with a FILTER_CONTACTS_ACTION intent.
686 * This is so child activities can check if they are supposed to display a filter.
687 *
688 * @param intent The intent received in {@link #onNewIntent(Intent)}
689 */
690 private void setupFilterText(Intent intent) {
691 // If the intent was relaunched from history, don't apply the filter text.
692 if ((intent.getFlags() & Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY) != 0) {
693 return;
694 }
695 String filter = intent.getStringExtra(UI.FILTER_TEXT_EXTRA_KEY);
696 if (filter != null && filter.length() > 0) {
697 mFilterText = filter;
698 }
699 }
700
Yorke Leec3766332013-07-31 11:13:16 -0700701 private final NewPhoneFavoriteFragment.Listener mPhoneFavoriteListener =
702 new NewPhoneFavoriteFragment.Listener() {
Chiao Cheng94b10b52012-08-17 16:59:12 -0700703 @Override
704 public void onContactSelected(Uri contactUri) {
705 PhoneNumberInteraction.startInteractionForPhoneCall(
Yorke Leec3766332013-07-31 11:13:16 -0700706 DialtactsActivity.this, contactUri, getCallOrigin());
Chiao Cheng94b10b52012-08-17 16:59:12 -0700707 }
708
709 @Override
710 public void onCallNumberDirectly(String phoneNumber) {
Chiao Cheng9d4f3b22012-09-05 16:00:16 -0700711 Intent intent = CallUtil.getCallIntent(phoneNumber, getCallOrigin());
Chiao Cheng94b10b52012-08-17 16:59:12 -0700712 startActivity(intent);
713 }
714 };
715
Yorke Leec3766332013-07-31 11:13:16 -0700716 /* TODO krelease: This is only relevant for phones that have a hard button search key (i.e.
717 * Nexus S). Supporting it is a little more tricky because of the dialpad fragment might
718 * be showing when the search key is pressed so there is more state management involved.
Chiao Cheng94b10b52012-08-17 16:59:12 -0700719
720 @Override
721 public void startSearch(String initialQuery, boolean selectInitialQuery,
722 Bundle appSearchData, boolean globalSearch) {
Yorke Leec3766332013-07-31 11:13:16 -0700723 if (mRegularSearchFragment != null && mRegularSearchFragment.isAdded() && !globalSearch) {
Chiao Cheng94b10b52012-08-17 16:59:12 -0700724 if (mInSearchUi) {
725 if (mSearchView.hasFocus()) {
726 showInputMethod(mSearchView.findFocus());
727 } else {
728 mSearchView.requestFocus();
729 }
730 } else {
731 enterSearchUi();
732 }
733 } else {
734 super.startSearch(initialQuery, selectInitialQuery, appSearchData, globalSearch);
735 }
Yorke Leec3766332013-07-31 11:13:16 -0700736 }*/
Chiao Cheng94b10b52012-08-17 16:59:12 -0700737
738 private void showInputMethod(View view) {
Yorke Leec3766332013-07-31 11:13:16 -0700739 final InputMethodManager imm = (InputMethodManager) getSystemService(
740 Context.INPUT_METHOD_SERVICE);
Chiao Cheng94b10b52012-08-17 16:59:12 -0700741 if (imm != null) {
Yorke Leec3766332013-07-31 11:13:16 -0700742 imm.showSoftInput(view, 0);
Chiao Cheng94b10b52012-08-17 16:59:12 -0700743 }
744 }
745
746 private void hideInputMethod(View view) {
Yorke Leec3766332013-07-31 11:13:16 -0700747 final InputMethodManager imm = (InputMethodManager) getSystemService(
748 Context.INPUT_METHOD_SERVICE);
Chiao Cheng94b10b52012-08-17 16:59:12 -0700749 if (imm != null && view != null) {
750 imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
751 }
752 }
753
754 /**
Yorke Leec3766332013-07-31 11:13:16 -0700755 * Shows the search fragment
Chiao Cheng94b10b52012-08-17 16:59:12 -0700756 */
Yorke Leec3766332013-07-31 11:13:16 -0700757 private void enterSearchUi(boolean smartDialSearch) {
758 final FragmentTransaction transaction = getFragmentManager().beginTransaction();
759 transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
760 transaction.hide(mPhoneFavoriteFragment);
761 if (smartDialSearch) {
762 transaction.show(mSmartDialSearchFragment);
763 } else {
764 transaction.show(mRegularSearchFragment);
Chiao Cheng94b10b52012-08-17 16:59:12 -0700765 }
Yorke Leec3766332013-07-31 11:13:16 -0700766 transaction.commit();
Chiao Cheng94b10b52012-08-17 16:59:12 -0700767
Yorke Leec3766332013-07-31 11:13:16 -0700768 mInSearchUi = true;
Chiao Cheng94b10b52012-08-17 16:59:12 -0700769 }
770
771 /**
Yorke Leec3766332013-07-31 11:13:16 -0700772 * Hides the search fragment
Chiao Cheng94b10b52012-08-17 16:59:12 -0700773 */
Yorke Leec3766332013-07-31 11:13:16 -0700774 private void exitSearchUi() {
775 final FragmentTransaction transaction = getFragmentManager().beginTransaction();
776 transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
777 transaction.hide(mRegularSearchFragment);
778 transaction.hide(mSmartDialSearchFragment);
779 transaction.show(mPhoneFavoriteFragment);
780 transaction.commit();
781 mInSearchUi = false;
Chiao Cheng94b10b52012-08-17 16:59:12 -0700782 }
783
784 /** Returns an Intent to launch Call Settings screen */
785 public static Intent getCallSettingsIntent() {
786 final Intent intent = new Intent(Intent.ACTION_MAIN);
787 intent.setClassName(PHONE_PACKAGE, CALL_SETTINGS_CLASS_NAME);
788 intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
789 return intent;
790 }
791
792 @Override
Yorke Leec3766332013-07-31 11:13:16 -0700793 public void onBackPressed() {
794 if (mDialpadFragment.isVisible()) {
795 hideDialpadFragment(true);
796 } else if (mInSearchUi) {
797 mSearchView.setText(null);
798 } else if (mShowAllContactsFragment.isVisible()) {
799 hideAllContactsFragment();
800 } else if (isTaskRoot()) {
801 // Instead of stopping, simply push this to the back of the stack.
802 // This is only done when running at the top of the stack;
803 // otherwise, we have been launched by someone else so need to
804 // allow the user to go back to the caller.
805 moveTaskToBack(false);
806 } else {
807 super.onBackPressed();
Chiao Cheng94b10b52012-08-17 16:59:12 -0700808 }
Yorke Leec3766332013-07-31 11:13:16 -0700809 }
810
811 @Override
812 public void onDialpadQueryChanged(String query) {
813 final String normalizedQuery = SmartDialNameMatcher.normalizeNumber(query,
814 SmartDialNameMatcher.LATIN_SMART_DIAL_MAP);
815 if (!TextUtils.equals(mSearchView.getText(), normalizedQuery)) {
816 mSearchView.setText(normalizedQuery);
Chiao Cheng94b10b52012-08-17 16:59:12 -0700817 }
818 }
Yorke Leec3766332013-07-31 11:13:16 -0700819
820 @Override
821 public void onListFragmentScrollStateChange(int scrollState) {
822 if (scrollState == OnScrollListener.SCROLL_STATE_TOUCH_SCROLL) {
823 hideDialpadFragmentIfNecessary();
824 hideInputMethod(getCurrentFocus());
825 }
826 }
827
828 @Override
829 public void onPhoneFavoriteFragmentStarted() {
830 setupFakeActionBarItemsForFavoritesFragment();
831 }
832
833 @Override
834 public void onDialpadFragmentStarted() {
835 setupFakeActionBarItemsForDialpadFragment();
836 }
837
838 private boolean phoneIsInUse() {
839 final TelephonyManager tm = (TelephonyManager) getSystemService(
840 Context.TELEPHONY_SERVICE);
841 return tm.getCallState() != TelephonyManager.CALL_STATE_IDLE;
842 }
Chiao Cheng94b10b52012-08-17 16:59:12 -0700843}