blob: 66cbe8530d62725dff9f7235803d9e7e5fadcde4 [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;
31import android.net.Uri;
32import android.os.Bundle;
33import android.os.RemoteException;
34import android.os.ServiceManager;
Yorke Lee6b049122013-07-16 10:38:02 -070035import android.provider.CallLog.Calls;
Yorke Leedfb2eee2013-06-26 18:24:32 -070036import android.provider.ContactsContract;
Yorke Lee6b049122013-07-16 10:38:02 -070037import android.provider.ContactsContract.Contacts;
38import android.provider.ContactsContract.Intents.UI;
Yorke Leedfb2eee2013-06-26 18:24:32 -070039import android.provider.Settings;
Yorke Lee6b049122013-07-16 10:38:02 -070040import android.text.TextUtils;
Yorke Lee6b049122013-07-16 10:38:02 -070041import android.util.Log;
42import android.view.Menu;
Yorke Lee6b049122013-07-16 10:38:02 -070043import android.view.MenuItem;
Yorke Lee6b049122013-07-16 10:38:02 -070044import android.view.View;
Yorke Lee6b049122013-07-16 10:38:02 -070045import android.view.View.OnFocusChangeListener;
46import android.view.ViewConfiguration;
Yorke Lee6b049122013-07-16 10:38:02 -070047import android.view.inputmethod.InputMethodManager;
Yorke Leedfb2eee2013-06-26 18:24:32 -070048import android.widget.AbsListView.OnScrollListener;
Yorke Lee6b049122013-07-16 10:38:02 -070049import android.widget.PopupMenu;
50import android.widget.SearchView;
51import android.widget.SearchView.OnCloseListener;
52import android.widget.SearchView.OnQueryTextListener;
53import android.widget.Toast;
54
55import com.android.contacts.common.CallUtil;
56import com.android.contacts.common.activity.TransactionSafeActivity;
Yorke Leedfb2eee2013-06-26 18:24:32 -070057import com.android.contacts.common.dialog.ClearFrequentsDialog;
58import com.android.contacts.common.interactions.ImportExportDialogFragment;
Yorke Lee6b049122013-07-16 10:38:02 -070059import com.android.contacts.common.list.ContactListItemView;
60import com.android.contacts.common.list.OnPhoneNumberPickerActionListener;
61import com.android.contacts.common.list.PhoneNumberPickerFragment;
Yorke Leedfb2eee2013-06-26 18:24:32 -070062import com.android.dialer.calllog.NewCallLogActivity;
63import com.android.dialer.dialpad.NewDialpadFragment;
64import com.android.dialer.dialpad.SmartDialNameMatcher;
Yorke Lee6b049122013-07-16 10:38:02 -070065import com.android.dialer.interactions.PhoneNumberInteraction;
Yorke Leedfb2eee2013-06-26 18:24:32 -070066import com.android.dialer.list.NewPhoneFavoriteFragment;
67import com.android.dialer.list.OnListFragmentScrolledListener;
68import com.android.dialer.list.SmartDialSearchFragment;
Yorke Lee6b049122013-07-16 10:38:02 -070069import com.android.internal.telephony.ITelephony;
70
71/**
Yorke Lee6b049122013-07-16 10:38:02 -070072 * The dialer tab's title is 'phone', a more common name (see strings.xml).
Yorke Leedfb2eee2013-06-26 18:24:32 -070073 *
74 * TODO krelease: All classes currently prefixed with New will replace the original classes or
75 * be renamed more appropriately before shipping.
Yorke Lee6b049122013-07-16 10:38:02 -070076 */
Yorke Leedfb2eee2013-06-26 18:24:32 -070077public class NewDialtactsActivity extends TransactionSafeActivity implements View.OnClickListener,
78 NewDialpadFragment.OnDialpadQueryChangedListener, PopupMenu.OnMenuItemClickListener,
79 OnListFragmentScrolledListener,
80 NewPhoneFavoriteFragment.OnPhoneFavoriteFragmentStartedListener {
Yorke Lee6b049122013-07-16 10:38:02 -070081 private static final String TAG = "DialtactsActivity";
82
83 public static final boolean DEBUG = false;
84
85 /** Used to open Call Setting */
86 private static final String PHONE_PACKAGE = "com.android.phone";
87 private static final String CALL_SETTINGS_CLASS_NAME =
88 "com.android.phone.CallFeaturesSetting";
89
90 /** @see #getCallOrigin() */
91 private static final String CALL_ORIGIN_DIALTACTS =
92 "com.android.dialer.DialtactsActivity";
93
Yorke Leedfb2eee2013-06-26 18:24:32 -070094 private static final String TAG_DIALPAD_FRAGMENT = "dialpad";
95 private static final String TAG_REGULAR_SEARCH_FRAGMENT = "search";
96 private static final String TAG_SMARTDIAL_SEARCH_FRAGMENT = "smartdial";
97 private static final String TAG_FAVORITES_FRAGMENT = "favorites";
98
Yorke Lee6b049122013-07-16 10:38:02 -070099 /**
100 * Just for backward compatibility. Should behave as same as {@link Intent#ACTION_DIAL}.
101 */
102 private static final String ACTION_TOUCH_DIALER = "com.android.phone.action.TOUCH_DIALER";
103
Yorke Lee6b049122013-07-16 10:38:02 -0700104 private SharedPreferences mPrefs;
105
106 public static final String SHARED_PREFS_NAME = "com.android.dialer_preferences";
107
108 /** Last manually selected tab index */
109 private static final String PREF_LAST_MANUALLY_SELECTED_TAB =
110 "DialtactsActivity_last_manually_selected_tab";
Yorke Lee6b049122013-07-16 10:38:02 -0700111
112 private static final int SUBACTIVITY_ACCOUNT_FILTER = 1;
113
Yorke Lee6b049122013-07-16 10:38:02 -0700114 private String mFilterText;
115
Yorke Leedfb2eee2013-06-26 18:24:32 -0700116 /**
117 * The main fragment displaying the user's favorites and frequent contacts
118 */
119 private NewPhoneFavoriteFragment mPhoneFavoriteFragment;
Yorke Lee6b049122013-07-16 10:38:02 -0700120
121 /**
Yorke Leedfb2eee2013-06-26 18:24:32 -0700122 * Fragment containing the dialpad that slides into view
Yorke Lee6b049122013-07-16 10:38:02 -0700123 */
Yorke Leedfb2eee2013-06-26 18:24:32 -0700124 private NewDialpadFragment mDialpadFragment;
125
126 /**
127 * Fragment for searching phone numbers using the alphanumeric keyboard.
128 */
129 private NewSearchFragment mRegularSearchFragment;
130
131 /**
132 * Fragment for searching phone numbers using the dialpad.
133 */
134 private SmartDialSearchFragment mSmartDialSearchFragment;
135
136 private View mMenuButton;
137 private View mCallHistoryButton;
138 private View mDialpadButton;
139
140 // Padding view used to shift the fragments up when the dialpad is shown.
141 private View mBottomPaddingView;
142
Yorke Lee6b049122013-07-16 10:38:02 -0700143 /**
144 * True when this Activity is in its search UI (with a {@link SearchView} and
145 * {@link PhoneNumberPickerFragment}).
146 */
147 private boolean mInSearchUi;
148 private SearchView mSearchView;
149
Yorke Lee6b049122013-07-16 10:38:02 -0700150 /**
151 * The index of the Fragment (or, the tab) that has last been manually selected.
152 * This value does not keep track of programmatically set Tabs (e.g. Call Log after a Call)
153 */
154 private int mLastManuallySelectedFragment;
155
Yorke Lee6b049122013-07-16 10:38:02 -0700156 /**
157 * Listener used when one of phone numbers in search UI is selected. This will initiate a
158 * phone call using the phone number.
159 */
160 private final OnPhoneNumberPickerActionListener mPhoneNumberPickerActionListener =
161 new OnPhoneNumberPickerActionListener() {
162 @Override
163 public void onPickPhoneNumberAction(Uri dataUri) {
164 // Specify call-origin so that users will see the previous tab instead of
165 // CallLog screen (search UI will be automatically exited).
166 PhoneNumberInteraction.startInteractionForPhoneCall(
Yorke Leedfb2eee2013-06-26 18:24:32 -0700167 NewDialtactsActivity.this, dataUri, getCallOrigin());
Yorke Lee6b049122013-07-16 10:38:02 -0700168 }
169
170 @Override
171 public void onShortcutIntentCreated(Intent intent) {
172 Log.w(TAG, "Unsupported intent has come (" + intent + "). Ignoring.");
173 }
174
175 @Override
176 public void onHomeInActionBarSelected() {
177 exitSearchUi();
178 }
179 };
180
181 /**
182 * Listener used to send search queries to the phone search fragment.
183 */
184 private final OnQueryTextListener mPhoneSearchQueryTextListener =
185 new OnQueryTextListener() {
186 @Override
187 public boolean onQueryTextSubmit(String query) {
188 View view = getCurrentFocus();
189 if (view != null) {
190 hideInputMethod(view);
191 view.clearFocus();
192 }
193 return true;
194 }
195
196 @Override
197 public boolean onQueryTextChange(String newText) {
Yorke Leedfb2eee2013-06-26 18:24:32 -0700198 final boolean smartDialSearch = isDialpadShowing();
199
Yorke Lee6b049122013-07-16 10:38:02 -0700200 // Show search result with non-empty text. Show a bare list otherwise.
Yorke Leedfb2eee2013-06-26 18:24:32 -0700201 if (TextUtils.isEmpty(newText) && mInSearchUi) {
202 exitSearchUi();
203 return true;
204 } else if (!TextUtils.isEmpty(newText) && !mInSearchUi) {
205 enterSearchUi(smartDialSearch);
206 }
207
208 if (isDialpadShowing()) {
209 mSmartDialSearchFragment.setQueryString(newText, false);
210 } else {
211 mRegularSearchFragment.setQueryString(newText, false);
Yorke Lee6b049122013-07-16 10:38:02 -0700212 }
213 return true;
214 }
215 };
216
Yorke Leedfb2eee2013-06-26 18:24:32 -0700217 private boolean isDialpadShowing() {
218 return mDialpadFragment.isVisible();
219 }
220
Yorke Lee6b049122013-07-16 10:38:02 -0700221 /**
222 * Listener used to handle the "close" button on the right side of {@link SearchView}.
223 * If some text is in the search view, this will clean it up. Otherwise this will exit
224 * the search UI and let users go back to usual Phone UI.
225 *
226 * This does _not_ handle back button.
227 */
228 private final OnCloseListener mPhoneSearchCloseListener =
229 new OnCloseListener() {
230 @Override
231 public boolean onClose() {
232 if (!TextUtils.isEmpty(mSearchView.getQuery())) {
233 mSearchView.setQuery(null, true);
234 }
235 return true;
236 }
237 };
238
Yorke Lee6b049122013-07-16 10:38:02 -0700239 @Override
Yorke Leedfb2eee2013-06-26 18:24:32 -0700240 protected void onCreate(Bundle savedInstanceState) {
241 super.onCreate(savedInstanceState);
Yorke Lee6b049122013-07-16 10:38:02 -0700242
243 final Intent intent = getIntent();
244 fixIntent(intent);
245
Yorke Leedfb2eee2013-06-26 18:24:32 -0700246 setContentView(R.layout.new_dialtacts_activity);
Yorke Lee6b049122013-07-16 10:38:02 -0700247
Yorke Leedfb2eee2013-06-26 18:24:32 -0700248 getActionBar().hide();
Yorke Lee6b049122013-07-16 10:38:02 -0700249
Yorke Leedfb2eee2013-06-26 18:24:32 -0700250 mPhoneFavoriteFragment = new NewPhoneFavoriteFragment();
251 mPhoneFavoriteFragment.setListener(mPhoneFavoriteListener);
Yorke Lee6b049122013-07-16 10:38:02 -0700252
Yorke Leedfb2eee2013-06-26 18:24:32 -0700253 mRegularSearchFragment = new NewSearchFragment();
254 mSmartDialSearchFragment = new SmartDialSearchFragment();
255 mDialpadFragment = new NewDialpadFragment();
Yorke Lee6b049122013-07-16 10:38:02 -0700256
Yorke Leedfb2eee2013-06-26 18:24:32 -0700257 // TODO krelease: load fragments on demand instead of creating all of them at run time
258 final FragmentTransaction ft = getFragmentManager().beginTransaction();
259 ft.add(R.id.dialtacts_frame, mPhoneFavoriteFragment, TAG_FAVORITES_FRAGMENT);
260 ft.add(R.id.dialtacts_frame, mRegularSearchFragment, TAG_REGULAR_SEARCH_FRAGMENT);
261 ft.add(R.id.dialtacts_frame, mSmartDialSearchFragment, TAG_SMARTDIAL_SEARCH_FRAGMENT);
262 ft.add(R.id.dialtacts_container, mDialpadFragment, TAG_DIALPAD_FRAGMENT);
263 ft.hide(mRegularSearchFragment);
264 ft.hide(mDialpadFragment);
265 ft.hide(mSmartDialSearchFragment);
266 ft.commit();
Yorke Lee6b049122013-07-16 10:38:02 -0700267
Yorke Leedfb2eee2013-06-26 18:24:32 -0700268 mBottomPaddingView = findViewById(R.id.dialtacts_bottom_padding);
269 prepareSearchView();
Yorke Lee6b049122013-07-16 10:38:02 -0700270
271 // Load the last manually loaded tab
272 mPrefs = this.getSharedPreferences(SHARED_PREFS_NAME, Context.MODE_PRIVATE);
Yorke Lee6b049122013-07-16 10:38:02 -0700273
Yorke Leedfb2eee2013-06-26 18:24:32 -0700274 /*
275 * TODO krelease : Remember which fragment was last displayed, and then redisplay it as
276 * necessary. mLastManuallySelectedFragment = mPrefs.getInt(PREF_LAST_MANUALLY_SELECTED_TAB,
277 * PREF_LAST_MANUALLY_SELECTED_TAB_DEFAULT); if (mLastManuallySelectedFragment >=
278 * TAB_INDEX_COUNT) { // Stored value may have exceeded the number of current tabs. Reset
279 * it. mLastManuallySelectedFragment = PREF_LAST_MANUALLY_SELECTED_TAB_DEFAULT; }
280 */
Yorke Lee6b049122013-07-16 10:38:02 -0700281
282 if (UI.FILTER_CONTACTS_ACTION.equals(intent.getAction())
Yorke Leedfb2eee2013-06-26 18:24:32 -0700283 && savedInstanceState == null) {
Yorke Lee6b049122013-07-16 10:38:02 -0700284 setupFilterText(intent);
285 }
286 }
287
288 @Override
Yorke Leedfb2eee2013-06-26 18:24:32 -0700289 protected void onResume() {
290 super.onResume();
291 final FragmentManager fm = getFragmentManager();
292 mPhoneFavoriteFragment = (NewPhoneFavoriteFragment) fm.findFragmentByTag(
293 TAG_FAVORITES_FRAGMENT);
294 mDialpadFragment = (NewDialpadFragment) fm.findFragmentByTag(TAG_DIALPAD_FRAGMENT);
295
296 mRegularSearchFragment = (NewSearchFragment) fm.findFragmentByTag(
297 TAG_REGULAR_SEARCH_FRAGMENT);
298 mRegularSearchFragment.setOnPhoneNumberPickerActionListener(
299 mPhoneNumberPickerActionListener);
300 if (!mRegularSearchFragment.isHidden()) {
301 final FragmentTransaction transaction = getFragmentManager().beginTransaction();
302 transaction.hide(mRegularSearchFragment);
303 transaction.commit();
Yorke Lee6b049122013-07-16 10:38:02 -0700304 }
305
Yorke Leedfb2eee2013-06-26 18:24:32 -0700306 mSmartDialSearchFragment = (SmartDialSearchFragment) fm.findFragmentByTag(
307 TAG_SMARTDIAL_SEARCH_FRAGMENT);
308 mSmartDialSearchFragment.setOnPhoneNumberPickerActionListener(
309 mPhoneNumberPickerActionListener);
310 if (!mSmartDialSearchFragment.isHidden()) {
311 final FragmentTransaction transaction = getFragmentManager().beginTransaction();
312 transaction.hide(mSmartDialSearchFragment);
313 transaction.commit();
Yorke Lee6b049122013-07-16 10:38:02 -0700314 }
315 }
316
317 @Override
Yorke Leedfb2eee2013-06-26 18:24:32 -0700318 public boolean onMenuItemClick(MenuItem item) {
319 switch (item.getItemId()) {
320 case R.id.menu_import_export:
321 // We hard-code the "contactsAreAvailable" argument because doing it properly would
322 // involve querying a {@link ProviderStatusLoader}, which we don't want to do right
323 // now in Dialtacts for (potential) performance reasons. Compare with how it is
324 // done in {@link PeopleActivity}.
325 ImportExportDialogFragment.show(getFragmentManager(), true,
326 DialtactsActivity.class);
327 return true;
328 case R.id.menu_clear_frequents:
329 ClearFrequentsDialog.show(getFragmentManager());
330 return true;
331 case R.id.add_contact:
332 try {
333 startActivity(new Intent(Intent.ACTION_INSERT, Contacts.CONTENT_URI));
334 } catch (ActivityNotFoundException e) {
335 Toast toast = Toast.makeText(this, R.string.add_contact_not_available,
336 Toast.LENGTH_SHORT);
337 toast.show();
338 }
339 return true;
340 case R.id.menu_call_settings:
341 final Intent settingsIntent = DialtactsActivity.getCallSettingsIntent();
342 startActivity(settingsIntent);
343 }
344 return false;
Yorke Lee6b049122013-07-16 10:38:02 -0700345 }
346
347 @Override
348 public void onClick(View view) {
349 switch (view.getId()) {
Yorke Lee6b049122013-07-16 10:38:02 -0700350 case R.id.overflow_menu: {
Yorke Leedfb2eee2013-06-26 18:24:32 -0700351 final PopupMenu popupMenu = new PopupMenu(NewDialtactsActivity.this, view);
352 final Menu menu = popupMenu.getMenu();
353 popupMenu.inflate(R.menu.dialtacts_options_new);
354 popupMenu.setOnMenuItemClickListener(this);
355 popupMenu.show();
Yorke Lee6b049122013-07-16 10:38:02 -0700356 break;
357 }
Yorke Leedfb2eee2013-06-26 18:24:32 -0700358 case R.id.dialpad_button:
359 showDialpadFragment();
360 break;
361 case R.id.call_history_button:
362 final Intent intent = new Intent(this, NewCallLogActivity.class);
363 startActivity(intent);
364 break;
Yorke Lee6b049122013-07-16 10:38:02 -0700365 default: {
366 Log.wtf(TAG, "Unexpected onClick event from " + view);
367 break;
368 }
369 }
370 }
371
Yorke Leedfb2eee2013-06-26 18:24:32 -0700372 private void showDialpadFragment() {
Yorke Lee6b049122013-07-16 10:38:02 -0700373 final FragmentTransaction ft = getFragmentManager().beginTransaction();
Yorke Leedfb2eee2013-06-26 18:24:32 -0700374 ft.setCustomAnimations(R.anim.slide_in, 0);
375 ft.show(mDialpadFragment);
376 ft.commit();
377 }
Yorke Lee6b049122013-07-16 10:38:02 -0700378
Yorke Leedfb2eee2013-06-26 18:24:32 -0700379 private void hideDialpadFragment() {
380 final FragmentTransaction ft = getFragmentManager().beginTransaction();
381 ft.setCustomAnimations(0, R.anim.slide_out);
382 ft.hide(mDialpadFragment);
383 ft.commit();
Yorke Lee6b049122013-07-16 10:38:02 -0700384 }
385
386 private void prepareSearchView() {
Yorke Leedfb2eee2013-06-26 18:24:32 -0700387 mSearchView = (SearchView) findViewById(R.id.search_view);
Yorke Lee6b049122013-07-16 10:38:02 -0700388 mSearchView.setOnQueryTextListener(mPhoneSearchQueryTextListener);
389 mSearchView.setOnCloseListener(mPhoneSearchCloseListener);
390 // Since we're using a custom layout for showing SearchView instead of letting the
391 // search menu icon do that job, we need to manually configure the View so it looks
392 // "shown via search menu".
393 // - it should be iconified by default
394 // - it should not be iconified at this time
395 // See also comments for onActionViewExpanded()/onActionViewCollapsed()
396 mSearchView.setIconifiedByDefault(true);
Yorke Leedfb2eee2013-06-26 18:24:32 -0700397 mSearchView.setQueryHint(getString(R.string.dialer_hint_find_contact));
Yorke Lee6b049122013-07-16 10:38:02 -0700398 mSearchView.setIconified(false);
Yorke Leedfb2eee2013-06-26 18:24:32 -0700399 mSearchView.clearFocus();
Yorke Lee6b049122013-07-16 10:38:02 -0700400 mSearchView.setOnQueryTextFocusChangeListener(new OnFocusChangeListener() {
401 @Override
402 public void onFocusChange(View view, boolean hasFocus) {
403 if (hasFocus) {
404 showInputMethod(view.findFocus());
405 }
406 }
407 });
Yorke Lee6b049122013-07-16 10:38:02 -0700408 }
409
Yorke Leedfb2eee2013-06-26 18:24:32 -0700410 private void hideDialpadFragmentIfNecessary() {
411 if (mDialpadFragment.isVisible()) {
412 hideDialpadFragment();
413 }
414 }
Yorke Lee6b049122013-07-16 10:38:02 -0700415
Yorke Leedfb2eee2013-06-26 18:24:32 -0700416 final AnimatorListener mHideListener = new AnimatorListenerAdapter() {
417 @Override
418 public void onAnimationEnd(Animator animation) {
419 mSearchView.setVisibility(View.GONE);
420 }
421 };
422
423 public void hideSearchBar() {
424 mSearchView.animate().cancel();
425 mSearchView.setAlpha(1);
426 mSearchView.setTranslationY(0);
427 mSearchView.animate().withLayer().alpha(0).translationY(-mSearchView.getHeight()).
428 setDuration(200).setListener(mHideListener);
429
430 mPhoneFavoriteFragment.getView().animate().withLayer()
431 .translationY(-mSearchView.getHeight()).setDuration(200).setListener(
432 new AnimatorListenerAdapter() {
433 @Override
434 public void onAnimationEnd(Animator animation) {
435 mBottomPaddingView.setVisibility(View.VISIBLE);
436 mPhoneFavoriteFragment.getView().setTranslationY(0);
437 }
438 });
439 }
440
441 public void showSearchBar() {
442 mSearchView.animate().cancel();
443 mSearchView.setAlpha(0);
444 mSearchView.setTranslationY(-mSearchView.getHeight());
445 mSearchView.animate().withLayer().alpha(1).translationY(0).setDuration(200)
446 .setListener(new AnimatorListenerAdapter() {
447 @Override
448 public void onAnimationStart(Animator animation) {
449 mSearchView.setVisibility(View.VISIBLE);
450 }
451 });
452
453 mPhoneFavoriteFragment.getView().setTranslationY(-mSearchView.getHeight());
454 mPhoneFavoriteFragment.getView().animate().withLayer().translationY(0).setDuration(200)
455 .setListener(
456 new AnimatorListenerAdapter() {
457 @Override
458 public void onAnimationStart(Animator animation) {
459 mBottomPaddingView.setVisibility(View.GONE);
460 }
461 });
462 }
463
464
465 public void setupFakeActionBarItems() {
466 mMenuButton = findViewById(R.id.overflow_menu);
467 if (mMenuButton != null) {
468 // mMenuButton.setMinimumWidth(fakeMenuItemWidth);
469 if (ViewConfiguration.get(this).hasPermanentMenuKey()) {
470 // This is required for dialpad button's layout, so must not use GONE here.
471 mMenuButton.setVisibility(View.INVISIBLE);
472 } else {
473 mMenuButton.setOnClickListener(this);
Yorke Lee6b049122013-07-16 10:38:02 -0700474 }
475 }
Yorke Leedfb2eee2013-06-26 18:24:32 -0700476
477 mCallHistoryButton = findViewById(R.id.call_history_button);
478 // mCallHistoryButton.setMinimumWidth(fakeMenuItemWidth);
479 mCallHistoryButton.setOnClickListener(this);
480
481 mDialpadButton = findViewById(R.id.dialpad_button);
482 // DialpadButton.setMinimumWidth(fakeMenuItemWidth);
483 mDialpadButton.setOnClickListener(this);
Yorke Lee6b049122013-07-16 10:38:02 -0700484 }
485
486 @Override
487 protected void onPause() {
488 super.onPause();
Yorke Lee6b049122013-07-16 10:38:02 -0700489 mPrefs.edit().putInt(PREF_LAST_MANUALLY_SELECTED_TAB, mLastManuallySelectedFragment)
490 .apply();
491 requestBackup();
492 }
493
494 private void requestBackup() {
495 final BackupManager bm = new BackupManager(this);
496 bm.dataChanged();
497 }
498
499 private void fixIntent(Intent intent) {
500 // This should be cleaned up: the call key used to send an Intent
501 // that just said to go to the recent calls list. It now sends this
502 // abstract action, but this class hasn't been rewritten to deal with it.
503 if (Intent.ACTION_CALL_BUTTON.equals(intent.getAction())) {
504 intent.setDataAndType(Calls.CONTENT_URI, Calls.CONTENT_TYPE);
505 intent.putExtra("call_key", true);
506 setIntent(intent);
507 }
508 }
509
Yorke Lee6b049122013-07-16 10:38:02 -0700510 /**
511 * Returns true if the intent is due to hitting the green send key (hardware call button:
512 * KEYCODE_CALL) while in a call.
513 *
514 * @param intent the intent that launched this activity
515 * @param recentCallsRequest true if the intent is requesting to view recent calls
516 * @return true if the intent is due to hitting the green send key while in a call
517 */
Yorke Leedfb2eee2013-06-26 18:24:32 -0700518 private boolean isSendKeyWhileInCall(Intent intent, boolean recentCallsRequest) {
Yorke Lee6b049122013-07-16 10:38:02 -0700519 // If there is a call in progress go to the call screen
520 if (recentCallsRequest) {
521 final boolean callKey = intent.getBooleanExtra("call_key", false);
522
523 try {
524 ITelephony phone = ITelephony.Stub.asInterface(ServiceManager.checkService("phone"));
525 if (callKey && phone != null && phone.showCallScreen()) {
526 return true;
527 }
528 } catch (RemoteException e) {
529 Log.e(TAG, "Failed to handle send while in call", e);
530 }
531 }
532
533 return false;
534 }
535
536 /**
537 * Sets the current tab based on the intent's request type
538 *
539 * @param intent Intent that contains information about which tab should be selected
540 */
Yorke Leedfb2eee2013-06-26 18:24:32 -0700541 private void displayFragment(Intent intent) {
542 // TODO krelease: Make navigation via intent work by displaying the correct fragment
543 // as appropriate.
544
Yorke Lee6b049122013-07-16 10:38:02 -0700545 // If we got here by hitting send and we're in call forward along to the in-call activity
546 boolean recentCallsRequest = Calls.CONTENT_TYPE.equals(intent.resolveType(
547 getContentResolver()));
548 if (isSendKeyWhileInCall(intent, recentCallsRequest)) {
549 finish();
550 return;
551 }
Yorke Lee6b049122013-07-16 10:38:02 -0700552 }
553
554 @Override
555 public void onNewIntent(Intent newIntent) {
556 setIntent(newIntent);
557 fixIntent(newIntent);
Yorke Leedfb2eee2013-06-26 18:24:32 -0700558 displayFragment(newIntent);
Yorke Lee6b049122013-07-16 10:38:02 -0700559 final String action = newIntent.getAction();
560 if (UI.FILTER_CONTACTS_ACTION.equals(action)) {
561 setupFilterText(newIntent);
562 }
Yorke Leedfb2eee2013-06-26 18:24:32 -0700563 if (mInSearchUi || (mRegularSearchFragment != null && mRegularSearchFragment.isVisible())) {
Yorke Lee6b049122013-07-16 10:38:02 -0700564 exitSearchUi();
565 }
566
Yorke Leedfb2eee2013-06-26 18:24:32 -0700567 // TODO krelease: Handle onNewIntent for all other fragments
568 /*
569 *if (mViewPager.getCurrentItem() == TAB_INDEX_DIALER) { if (mDialpadFragment != null) {
570 * mDialpadFragment.setStartedFromNewIntent(true); } else { Log.e(TAG,
571 * "DialpadFragment isn't ready yet when the tab is already selected."); } } else if
572 * (mViewPager.getCurrentItem() == TAB_INDEX_CALL_LOG) { if (mCallLogFragment != null) {
573 * mCallLogFragment.configureScreenFromIntent(newIntent); } else { Log.e(TAG,
574 * "CallLogFragment isn't ready yet when the tab is already selected."); } }
575 */
Yorke Lee6b049122013-07-16 10:38:02 -0700576 invalidateOptionsMenu();
577 }
578
579 /** Returns true if the given intent contains a phone number to populate the dialer with */
580 private boolean isDialIntent(Intent intent) {
581 final String action = intent.getAction();
582 if (Intent.ACTION_DIAL.equals(action) || ACTION_TOUCH_DIALER.equals(action)) {
583 return true;
584 }
585 if (Intent.ACTION_VIEW.equals(action)) {
586 final Uri data = intent.getData();
587 if (data != null && CallUtil.SCHEME_TEL.equals(data.getScheme())) {
588 return true;
589 }
590 }
591 return false;
592 }
593
594 /**
595 * Returns an appropriate call origin for this Activity. May return null when no call origin
596 * should be used (e.g. when some 3rd party application launched the screen. Call origin is
597 * for remembering the tab in which the user made a phone call, so the external app's DIAL
598 * request should not be counted.)
599 */
600 public String getCallOrigin() {
601 return !isDialIntent(getIntent()) ? CALL_ORIGIN_DIALTACTS : null;
602 }
603
604 /**
605 * Retrieves the filter text stored in {@link #setupFilterText(Intent)}.
606 * This text originally came from a FILTER_CONTACTS_ACTION intent received
607 * by this activity. The stored text will then be cleared after after this
608 * method returns.
609 *
610 * @return The stored filter text
611 */
612 public String getAndClearFilterText() {
613 String filterText = mFilterText;
614 mFilterText = null;
615 return filterText;
616 }
617
618 /**
619 * Stores the filter text associated with a FILTER_CONTACTS_ACTION intent.
620 * This is so child activities can check if they are supposed to display a filter.
621 *
622 * @param intent The intent received in {@link #onNewIntent(Intent)}
623 */
624 private void setupFilterText(Intent intent) {
625 // If the intent was relaunched from history, don't apply the filter text.
626 if ((intent.getFlags() & Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY) != 0) {
627 return;
628 }
629 String filter = intent.getStringExtra(UI.FILTER_TEXT_EXTRA_KEY);
630 if (filter != null && filter.length() > 0) {
631 mFilterText = filter;
632 }
633 }
634
Yorke Leedfb2eee2013-06-26 18:24:32 -0700635 private final NewPhoneFavoriteFragment.Listener mPhoneFavoriteListener =
636 new NewPhoneFavoriteFragment.Listener() {
Yorke Lee6b049122013-07-16 10:38:02 -0700637 @Override
638 public void onContactSelected(Uri contactUri) {
639 PhoneNumberInteraction.startInteractionForPhoneCall(
Yorke Leedfb2eee2013-06-26 18:24:32 -0700640 NewDialtactsActivity.this, contactUri, getCallOrigin());
Yorke Lee6b049122013-07-16 10:38:02 -0700641 }
642
643 @Override
644 public void onCallNumberDirectly(String phoneNumber) {
645 Intent intent = CallUtil.getCallIntent(phoneNumber, getCallOrigin());
646 startActivity(intent);
647 }
648 };
649
Yorke Leedfb2eee2013-06-26 18:24:32 -0700650 /* TODO krelease: This is only relevant for phones that have a hard button search key (i.e.
651 * Nexus S). Supporting it is a little more tricky because of the dialpad fragment might
652 * be showing when the search key is pressed so there is more state management involved.
Yorke Lee6b049122013-07-16 10:38:02 -0700653
654 @Override
655 public void startSearch(String initialQuery, boolean selectInitialQuery,
656 Bundle appSearchData, boolean globalSearch) {
Yorke Leedfb2eee2013-06-26 18:24:32 -0700657 if (mRegularSearchFragment != null && mRegularSearchFragment.isAdded() && !globalSearch) {
Yorke Lee6b049122013-07-16 10:38:02 -0700658 if (mInSearchUi) {
659 if (mSearchView.hasFocus()) {
660 showInputMethod(mSearchView.findFocus());
661 } else {
662 mSearchView.requestFocus();
663 }
664 } else {
665 enterSearchUi();
666 }
667 } else {
668 super.startSearch(initialQuery, selectInitialQuery, appSearchData, globalSearch);
669 }
Yorke Leedfb2eee2013-06-26 18:24:32 -0700670 }*/
Yorke Lee6b049122013-07-16 10:38:02 -0700671
672 private void showInputMethod(View view) {
Yorke Leedfb2eee2013-06-26 18:24:32 -0700673 final InputMethodManager imm = (InputMethodManager) getSystemService(
674 Context.INPUT_METHOD_SERVICE);
Yorke Lee6b049122013-07-16 10:38:02 -0700675 if (imm != null) {
Yorke Leedfb2eee2013-06-26 18:24:32 -0700676 imm.showSoftInput(view, 0);
Yorke Lee6b049122013-07-16 10:38:02 -0700677 }
678 }
679
680 private void hideInputMethod(View view) {
Yorke Leedfb2eee2013-06-26 18:24:32 -0700681 final InputMethodManager imm = (InputMethodManager) getSystemService(
682 Context.INPUT_METHOD_SERVICE);
Yorke Lee6b049122013-07-16 10:38:02 -0700683 if (imm != null && view != null) {
684 imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
685 }
686 }
687
688 /**
Yorke Leedfb2eee2013-06-26 18:24:32 -0700689 * Shows the search fragment
Yorke Lee6b049122013-07-16 10:38:02 -0700690 */
Yorke Leedfb2eee2013-06-26 18:24:32 -0700691 private void enterSearchUi(boolean smartDialSearch) {
692 final FragmentTransaction transaction = getFragmentManager().beginTransaction();
693 transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
694 transaction.hide(mPhoneFavoriteFragment);
695 if (smartDialSearch) {
696 transaction.show(mSmartDialSearchFragment);
697 } else {
698 transaction.show(mRegularSearchFragment);
Yorke Lee6b049122013-07-16 10:38:02 -0700699 }
Yorke Leedfb2eee2013-06-26 18:24:32 -0700700 transaction.commit();
Yorke Lee6b049122013-07-16 10:38:02 -0700701
Yorke Leedfb2eee2013-06-26 18:24:32 -0700702 mInSearchUi = true;
Yorke Lee6b049122013-07-16 10:38:02 -0700703 }
704
705 /**
Yorke Leedfb2eee2013-06-26 18:24:32 -0700706 * Hides the search fragment
Yorke Lee6b049122013-07-16 10:38:02 -0700707 */
Yorke Leedfb2eee2013-06-26 18:24:32 -0700708 private void exitSearchUi() {
709 final FragmentTransaction transaction = getFragmentManager().beginTransaction();
710 transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
711 transaction.hide(mRegularSearchFragment);
712 transaction.hide(mSmartDialSearchFragment);
713 transaction.show(mPhoneFavoriteFragment);
714 transaction.commit();
715 mInSearchUi = false;
Yorke Lee6b049122013-07-16 10:38:02 -0700716 }
717
718 /** Returns an Intent to launch Call Settings screen */
719 public static Intent getCallSettingsIntent() {
720 final Intent intent = new Intent(Intent.ACTION_MAIN);
721 intent.setClassName(PHONE_PACKAGE, CALL_SETTINGS_CLASS_NAME);
722 intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
723 return intent;
724 }
725
726 @Override
Yorke Leedfb2eee2013-06-26 18:24:32 -0700727 public void onBackPressed() {
728 if (mDialpadFragment.isVisible()) {
729 hideDialpadFragment();
730 } else if (mInSearchUi) {
731 mSearchView.setQuery(null, false);
732 } else if (isTaskRoot()) {
733 // Instead of stopping, simply push this to the back of the stack.
734 // This is only done when running at the top of the stack;
735 // otherwise, we have been launched by someone else so need to
736 // allow the user to go back to the caller.
737 moveTaskToBack(false);
738 } else {
739 super.onBackPressed();
Yorke Lee6b049122013-07-16 10:38:02 -0700740 }
Yorke Leedfb2eee2013-06-26 18:24:32 -0700741 }
742
743 @Override
744 public void onDialpadQueryChanged(String query) {
745 final String normalizedQuery = SmartDialNameMatcher.normalizeNumber(query,
746 SmartDialNameMatcher.LATIN_SMART_DIAL_MAP);
747 if (!TextUtils.equals(mSearchView.getQuery(), normalizedQuery)) {
748 mSearchView.setQuery(normalizedQuery, false);
Yorke Lee6b049122013-07-16 10:38:02 -0700749 }
750 }
Yorke Leedfb2eee2013-06-26 18:24:32 -0700751
752 @Override
753 public void onListFragmentScrollStateChange(int scrollState) {
754 if (scrollState == OnScrollListener.SCROLL_STATE_TOUCH_SCROLL) {
755 hideDialpadFragmentIfNecessary();
756 hideInputMethod(getCurrentFocus());
757 }
758 }
759
760 @Override
761 public void onPhoneFavoriteFragmentStarted() {
762 setupFakeActionBarItems();
763 }
Yorke Lee6b049122013-07-16 10:38:02 -0700764}