DialtactsActivity.java revision 7b3b10b6cb5596b61ae781ef7932c12d6761fc23
1/*
2 * Copyright (C) 2013 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.dialer;
18
19import android.animation.Animator;
20import android.animation.Animator.AnimatorListener;
21import android.animation.AnimatorListenerAdapter;
22import 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.content.res.Resources;
32import android.net.Uri;
33import android.os.Bundle;
34import android.os.RemoteException;
35import android.os.ServiceManager;
36import android.provider.CallLog.Calls;
37import android.provider.ContactsContract;
38import android.provider.ContactsContract.Contacts;
39import android.provider.ContactsContract.Intents.UI;
40import android.provider.Settings;
41import android.speech.RecognizerIntent;
42import android.support.v4.app.NavUtils;
43import android.telephony.TelephonyManager;
44import android.text.Editable;
45import android.text.TextUtils;
46import android.text.TextWatcher;
47import android.util.Log;
48import android.view.Menu;
49import android.view.MenuItem;
50import android.view.View;
51import android.view.View.OnFocusChangeListener;
52import android.view.ViewConfiguration;
53import android.view.inputmethod.InputMethodManager;
54import android.widget.AbsListView.OnScrollListener;
55import android.widget.EditText;
56import android.widget.ImageView;
57import android.widget.PopupMenu;
58import android.widget.SearchView;
59import android.widget.SearchView.OnCloseListener;
60import android.widget.SearchView.OnQueryTextListener;
61import android.widget.Toast;
62
63import com.android.contacts.common.CallUtil;
64import com.android.contacts.common.activity.TransactionSafeActivity;
65import com.android.contacts.common.dialog.ClearFrequentsDialog;
66import com.android.contacts.common.interactions.ImportExportDialogFragment;
67import com.android.contacts.common.list.ContactListItemView;
68import com.android.contacts.common.list.OnPhoneNumberPickerActionListener;
69import com.android.contacts.common.list.PhoneNumberPickerFragment;
70import com.android.dialer.calllog.CallLogActivity;
71import com.android.dialer.dialpad.DialpadFragment;
72import com.android.dialer.dialpad.SmartDialNameMatcher;
73import com.android.dialer.interactions.PhoneNumberInteraction;
74import com.android.dialer.list.PhoneFavoriteFragment;
75import com.android.dialer.list.OnListFragmentScrolledListener;
76import com.android.dialer.list.ShowAllContactsFragment;
77import com.android.dialer.list.SmartDialSearchFragment;
78import com.android.internal.telephony.ITelephony;
79
80import java.util.ArrayList;
81
82/**
83 * The dialer tab's title is 'phone', a more common name (see strings.xml).
84 */
85public class DialtactsActivity extends TransactionSafeActivity implements View.OnClickListener,
86        DialpadFragment.OnDialpadQueryChangedListener, PopupMenu.OnMenuItemClickListener,
87        OnListFragmentScrolledListener,
88        PhoneFavoriteFragment.OnPhoneFavoriteFragmentStartedListener,
89        DialpadFragment.OnDialpadFragmentStartedListener {
90    private static final String TAG = "DialtactsActivity";
91
92    public static final boolean DEBUG = false;
93
94    public static final String SHARED_PREFS_NAME = "com.android.dialer_preferences";
95
96    /** Used to open Call Setting */
97    private static final String PHONE_PACKAGE = "com.android.phone";
98    private static final String CALL_SETTINGS_CLASS_NAME =
99            "com.android.phone.CallFeaturesSetting";
100    /** @see #getCallOrigin() */
101    private static final String CALL_ORIGIN_DIALTACTS =
102            "com.android.dialer.DialtactsActivity";
103
104    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    private static final String TAG_SHOW_ALL_CONTACTS_FRAGMENT = "show_all_contacts";
109
110    /**
111     * Just for backward compatibility. Should behave as same as {@link Intent#ACTION_DIAL}.
112     */
113    private static final String ACTION_TOUCH_DIALER = "com.android.phone.action.TOUCH_DIALER";
114
115    private static final int SUBACTIVITY_ACCOUNT_FILTER = 1;
116
117    private static final int ACTIVITY_REQUEST_CODE_VOICE_SEARCH = 1;
118
119    private String mFilterText;
120
121    /**
122     * The main fragment displaying the user's favorites and frequent contacts
123     */
124    private PhoneFavoriteFragment mPhoneFavoriteFragment;
125
126    /**
127     * Fragment containing the dialpad that slides into view
128     */
129    private DialpadFragment mDialpadFragment;
130
131    /**
132     * Fragment for searching phone numbers using the alphanumeric keyboard.
133     */
134    private SearchFragment mRegularSearchFragment;
135
136    /**
137     * Fragment for searching phone numbers using the dialpad.
138     */
139    private SmartDialSearchFragment mSmartDialSearchFragment;
140
141    private ShowAllContactsFragment mShowAllContactsFragment;
142
143    private View mMenuButton;
144    private View mCallHistoryButton;
145    private View mDialpadButton;
146
147    // Padding view used to shift the fragments up when the dialpad is shown.
148    private View mBottomPaddingView;
149
150    /**
151     * True when this Activity is in its search UI (with a {@link SearchView} and
152     * {@link PhoneNumberPickerFragment}).
153     */
154    private boolean mInSearchUi;
155    private boolean mFirstLaunch;
156    private View mSearchViewContainer;
157    private View mSearchViewCloseButton;
158    private View mVoiceSearchButton;
159    private EditText mSearchView;
160
161    /**
162     * Listener used when one of phone numbers in search UI is selected. This will initiate a
163     * phone call using the phone number.
164     */
165    private final OnPhoneNumberPickerActionListener mPhoneNumberPickerActionListener =
166            new OnPhoneNumberPickerActionListener() {
167                @Override
168                public void onPickPhoneNumberAction(Uri dataUri) {
169                    // Specify call-origin so that users will see the previous tab instead of
170                    // CallLog screen (search UI will be automatically exited).
171                    PhoneNumberInteraction.startInteractionForPhoneCall(
172                        DialtactsActivity.this, dataUri, getCallOrigin());
173                }
174
175                @Override
176                public void onCallNumberDirectly(String phoneNumber) {
177                    Intent intent = CallUtil.getCallIntent(phoneNumber, getCallOrigin());
178                    startActivity(intent);
179                }
180
181                @Override
182                public void onShortcutIntentCreated(Intent intent) {
183                    Log.w(TAG, "Unsupported intent has come (" + intent + "). Ignoring.");
184                }
185
186                @Override
187                public void onHomeInActionBarSelected() {
188                    exitSearchUi();
189                }
190    };
191
192    /**
193     * Listener used to send search queries to the phone search fragment.
194     */
195    private final TextWatcher mPhoneSearchQueryTextListener = new TextWatcher() {
196            @Override
197            public void beforeTextChanged(CharSequence s, int start, int count, int after) {
198            }
199
200            @Override
201            public void onTextChanged(CharSequence s, int start, int before, int count) {
202                // TODO krelease: populate the search fragments with the correct
203                // search query at the correct point in time of the fragment lifecycle.
204                // The current behavior is to simply return to the favorites screen
205                // (when docked), or returning to the Dialer after it has been
206                // swapped out of memory.
207                if (mDialpadFragment == null) return;
208                final boolean smartDialSearch = isDialpadShowing();
209                final String newText = s.toString();
210                // Show search result with non-empty text. Show a bare list otherwise.
211                if (TextUtils.isEmpty(newText) && mInSearchUi) {
212                    exitSearchUi();
213                    mSearchViewCloseButton.setVisibility(View.GONE);
214                    return;
215                } else if (!TextUtils.isEmpty(newText) && !mInSearchUi) {
216                    enterSearchUi(smartDialSearch);
217                }
218
219                if (smartDialSearch) {
220                    mSmartDialSearchFragment.setQueryString(newText, false);
221                } else {
222                    mRegularSearchFragment.setQueryString(newText, false);
223                }
224                mSearchViewCloseButton.setVisibility(View.VISIBLE);
225                return;
226            }
227
228            @Override
229            public void afterTextChanged(Editable s) {
230            }
231    };
232
233    private boolean isDialpadShowing() {
234        return mDialpadFragment != null && mDialpadFragment.isVisible();
235    }
236
237    @Override
238    protected void onCreate(Bundle savedInstanceState) {
239        super.onCreate(savedInstanceState);
240
241        mFirstLaunch = true;
242
243        final Intent intent = getIntent();
244        fixIntent(intent);
245
246        setContentView(R.layout.dialtacts_activity);
247
248        getActionBar().hide();
249
250        if (savedInstanceState == null) {
251            mPhoneFavoriteFragment = new PhoneFavoriteFragment();
252            mPhoneFavoriteFragment.setListener(mPhoneFavoriteListener);
253
254            mRegularSearchFragment = new SearchFragment();
255            mSmartDialSearchFragment = new SmartDialSearchFragment();
256            mDialpadFragment = new DialpadFragment();
257            mShowAllContactsFragment = new ShowAllContactsFragment();
258            mShowAllContactsFragment.setOnPhoneNumberPickerActionListener(
259                    mPhoneNumberPickerActionListener);
260
261            // TODO krelease: load fragments on demand instead of creating all of them at run time
262            final FragmentTransaction ft = getFragmentManager().beginTransaction();
263            ft.add(R.id.dialtacts_frame, mPhoneFavoriteFragment, TAG_FAVORITES_FRAGMENT);
264            ft.add(R.id.dialtacts_frame, mRegularSearchFragment, TAG_REGULAR_SEARCH_FRAGMENT);
265            ft.add(R.id.dialtacts_frame, mSmartDialSearchFragment, TAG_SMARTDIAL_SEARCH_FRAGMENT);
266            ft.add(R.id.dialtacts_frame, mShowAllContactsFragment, TAG_SHOW_ALL_CONTACTS_FRAGMENT);
267            ft.add(R.id.dialtacts_container, mDialpadFragment, TAG_DIALPAD_FRAGMENT);
268            ft.commit();
269        }
270
271        mBottomPaddingView = findViewById(R.id.dialtacts_bottom_padding);
272        prepareSearchView();
273
274        if (UI.FILTER_CONTACTS_ACTION.equals(intent.getAction())
275                && savedInstanceState == null) {
276            setupFilterText(intent);
277        }
278    }
279
280    @Override
281    protected void onResume() {
282        super.onResume();
283        final FragmentManager fm = getFragmentManager();
284        mPhoneFavoriteFragment = (PhoneFavoriteFragment) fm.findFragmentByTag(
285                TAG_FAVORITES_FRAGMENT);
286        mDialpadFragment = (DialpadFragment) fm.findFragmentByTag(TAG_DIALPAD_FRAGMENT);
287
288        mRegularSearchFragment = (SearchFragment) fm.findFragmentByTag(
289                TAG_REGULAR_SEARCH_FRAGMENT);
290        mRegularSearchFragment.setOnPhoneNumberPickerActionListener(
291                mPhoneNumberPickerActionListener);
292
293        mSmartDialSearchFragment = (SmartDialSearchFragment) fm.findFragmentByTag(
294                TAG_SMARTDIAL_SEARCH_FRAGMENT);
295        mSmartDialSearchFragment.setOnPhoneNumberPickerActionListener(
296                mPhoneNumberPickerActionListener);
297
298        mShowAllContactsFragment = (ShowAllContactsFragment) fm.findFragmentByTag(
299                TAG_SHOW_ALL_CONTACTS_FRAGMENT);
300        mShowAllContactsFragment.setOnPhoneNumberPickerActionListener(
301                mPhoneNumberPickerActionListener);
302
303        if (mFirstLaunch) {
304            displayFragment(getIntent());
305        }
306        mFirstLaunch = false;
307    }
308
309    @Override
310    public void onAttachFragment(Fragment fragment) {
311        if (fragment instanceof DialpadFragment || fragment instanceof SearchFragment
312                || fragment instanceof SmartDialSearchFragment
313                || fragment instanceof ShowAllContactsFragment) {
314            final FragmentTransaction transaction = getFragmentManager().beginTransaction();
315            transaction.hide(fragment);
316            transaction.commit();
317        }
318        // TODO krelease: Save some kind of state here to show the appropriate fragment
319        // based on the state of the dialer when it was last paused
320    }
321
322    @Override
323    public boolean onOptionsItemSelected(MenuItem item) {
324        switch (item.getItemId()) {
325            // Respond to the action bar's Up/Home button
326            case android.R.id.home:
327                hideAllContactsFragment();
328        }
329        return super.onOptionsItemSelected(item);
330    }
331
332    @Override
333    public boolean onMenuItemClick(MenuItem item) {
334        switch (item.getItemId()) {
335            case R.id.menu_import_export:
336                // We hard-code the "contactsAreAvailable" argument because doing it properly would
337                // involve querying a {@link ProviderStatusLoader}, which we don't want to do right
338                // now in Dialtacts for (potential) performance reasons. Compare with how it is
339                // done in {@link PeopleActivity}.
340                ImportExportDialogFragment.show(getFragmentManager(), true,
341                        DialtactsActivity.class);
342                return true;
343            case R.id.menu_clear_frequents:
344                ClearFrequentsDialog.show(getFragmentManager());
345                return true;
346            case R.id.add_contact:
347                try {
348                    startActivity(new Intent(Intent.ACTION_INSERT, Contacts.CONTENT_URI));
349                } catch (ActivityNotFoundException e) {
350                    Toast toast = Toast.makeText(this,
351                            R.string.add_contact_not_available,
352                            Toast.LENGTH_SHORT);
353                    toast.show();
354                }
355                return true;
356            case R.id.menu_call_settings:
357                final Intent settingsIntent = DialtactsActivity.getCallSettingsIntent();
358                startActivity(settingsIntent);
359        }
360        return false;
361    }
362
363    @Override
364    public void onClick(View view) {
365        switch (view.getId()) {
366            case R.id.overflow_menu: {
367                final PopupMenu popupMenu = new PopupMenu(DialtactsActivity.this, view);
368                final Menu menu = popupMenu.getMenu();
369                popupMenu.inflate(R.menu.dialtacts_options);
370                final MenuItem clearFrequents = menu.findItem(R.id.menu_clear_frequents);
371                clearFrequents.setEnabled(mPhoneFavoriteFragment.hasFrequents());
372                popupMenu.setOnMenuItemClickListener(this);
373                popupMenu.show();
374                break;
375            }
376            case R.id.dialpad_button:
377                showDialpadFragment(true);
378                break;
379            case R.id.call_history_on_dialpad_button:
380            case R.id.call_history_button:
381                // Use explicit CallLogActivity intent instead of ACTION_VIEW +
382                // CONTENT_TYPE, so that we always open our call log from our dialer
383                final Intent intent = new Intent(this, CallLogActivity.class);
384                startActivity(intent);
385                break;
386            case R.id.search_close_button:
387                // Clear the search field
388                if (!TextUtils.isEmpty(mSearchView.getText())) {
389                    mSearchView.setText("");
390                }
391                break;
392            case R.id.voice_search_button:
393                final Intent voiceIntent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
394                startActivityForResult(voiceIntent, ACTIVITY_REQUEST_CODE_VOICE_SEARCH);
395                break;
396            default: {
397                Log.wtf(TAG, "Unexpected onClick event from " + view);
398                break;
399            }
400        }
401    }
402
403    @Override
404    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
405        if (requestCode == ACTIVITY_REQUEST_CODE_VOICE_SEARCH) {
406            if (resultCode == RESULT_OK) {
407                final ArrayList<String> matches = data.getStringArrayListExtra(
408                        RecognizerIntent.EXTRA_RESULTS);
409                if (matches.size() > 0) {
410                    final String match = matches.get(0);
411                    mSearchView.setText(match);
412                } else {
413                    Log.e(TAG, "Voice search - nothing heard");
414                }
415            } else {
416                Log.e(TAG, "Voice search failed");
417            }
418        }
419        super.onActivityResult(requestCode, resultCode, data);
420    }
421
422    private void showDialpadFragment(boolean animate) {
423        mDialpadFragment.setAdjustTranslationForAnimation(animate);
424        final FragmentTransaction ft = getFragmentManager().beginTransaction();
425        if (animate) {
426            ft.setCustomAnimations(R.anim.slide_in, 0);
427        } else {
428            mDialpadFragment.setYFraction(0);
429        }
430        ft.show(mDialpadFragment);
431        ft.commit();
432    }
433
434    private void hideDialpadFragment(boolean animate) {
435        mDialpadFragment.setAdjustTranslationForAnimation(animate);
436        final FragmentTransaction ft = getFragmentManager().beginTransaction();
437        if (animate) {
438            ft.setCustomAnimations(0, R.anim.slide_out);
439        }
440        ft.hide(mDialpadFragment);
441        ft.commit();
442    }
443
444    public void showAllContactsFragment() {
445        final FragmentTransaction ft = getFragmentManager().beginTransaction();
446        ft.hide(mPhoneFavoriteFragment);
447        ft.show(mShowAllContactsFragment);
448        // TODO{klp} Add animation
449        ft.commit();
450        hideSearchBar(false);
451    }
452
453    private void hideAllContactsFragment() {
454        final FragmentTransaction ft = getFragmentManager().beginTransaction();
455        ft.hide(mShowAllContactsFragment);
456        ft.show(mPhoneFavoriteFragment);
457        ft.commit();
458        showSearchBar();
459    }
460
461    private void prepareSearchView() {
462        mSearchViewContainer = findViewById(R.id.search_view_container);
463        mSearchViewCloseButton = findViewById(R.id.search_close_button);
464        mSearchViewCloseButton.setOnClickListener(this);
465        mVoiceSearchButton = findViewById(R.id.voice_search_button);
466        mVoiceSearchButton.setOnClickListener(this);
467        mSearchView = (EditText) findViewById(R.id.search_view);
468        mSearchView.addTextChangedListener(mPhoneSearchQueryTextListener);
469        mSearchView.setHint(getString(R.string.dialer_hint_find_contact));
470        mSearchView.setOnFocusChangeListener(new OnFocusChangeListener() {
471            @Override
472            public void onFocusChange(View view, boolean hasFocus) {
473                if (hasFocus) {
474                    showInputMethod(view.findFocus());
475                }
476            }
477        });
478    }
479
480    private void hideDialpadFragmentIfNecessary() {
481        if (mDialpadFragment.isVisible()) {
482            hideDialpadFragment(true);
483        }
484    }
485
486    final AnimatorListener mHideListener = new AnimatorListenerAdapter() {
487        @Override
488        public void onAnimationEnd(Animator animation) {
489            mSearchViewContainer.setVisibility(View.GONE);
490        }
491    };
492
493    public void hideSearchBar() {
494       hideSearchBar(true);
495    }
496
497    public void hideSearchBar(boolean shiftView) {
498        if (shiftView) {
499            mSearchViewContainer.animate().cancel();
500            mSearchViewContainer.setAlpha(1);
501            mSearchViewContainer.setTranslationY(0);
502            mSearchViewContainer.animate().withLayer().alpha(0).translationY(-mSearchView.getHeight())
503                    .setDuration(200).setListener(mHideListener);
504
505            mPhoneFavoriteFragment.getView().animate().withLayer()
506                    .translationY(-mSearchViewContainer.getHeight()).setDuration(200).setListener(
507                    new AnimatorListenerAdapter() {
508                        @Override
509                        public void onAnimationEnd(Animator animation) {
510                            mBottomPaddingView.setVisibility(View.VISIBLE);
511                            mPhoneFavoriteFragment.getView().setTranslationY(0);
512                        }
513                    });
514        } else {
515            mSearchViewContainer.setTranslationY(-mSearchView.getHeight());
516        }
517    }
518
519    public void showSearchBar() {
520        // If the favorites fragment hasn't been fully created before the dialpad fragment
521        // is hidden (i.e. onResume), don't bother animating
522        if (mPhoneFavoriteFragment == null || mPhoneFavoriteFragment.getView() == null) {
523            return;
524        }
525        mSearchViewContainer.animate().cancel();
526        mSearchViewContainer.setAlpha(0);
527        mSearchViewContainer.setTranslationY(-mSearchViewContainer.getHeight());
528        mSearchViewContainer.animate().withLayer().alpha(1).translationY(0).setDuration(200)
529                .setListener(new AnimatorListenerAdapter() {
530                    @Override
531                    public void onAnimationStart(Animator animation) {
532                        mSearchViewContainer.setVisibility(View.VISIBLE);
533                    }
534                });
535
536        mPhoneFavoriteFragment.getView().setTranslationY(-mSearchViewContainer.getHeight());
537        mPhoneFavoriteFragment.getView().animate().withLayer().translationY(0).setDuration(200)
538                .setListener(
539                        new AnimatorListenerAdapter() {
540                            @Override
541                            public void onAnimationStart(Animator animation) {
542                                mBottomPaddingView.setVisibility(View.GONE);
543                            }
544                        });
545    }
546
547
548    public void setupFakeActionBarItemsForFavoritesFragment() {
549        mMenuButton = findViewById(R.id.overflow_menu);
550        if (mMenuButton != null) {
551            mMenuButton.setOnClickListener(this);
552        }
553
554        mCallHistoryButton = findViewById(R.id.call_history_button);
555        // mCallHistoryButton.setMinimumWidth(fakeMenuItemWidth);
556        mCallHistoryButton.setOnClickListener(this);
557
558        mDialpadButton = findViewById(R.id.dialpad_button);
559        // DialpadButton.setMinimumWidth(fakeMenuItemWidth);
560        mDialpadButton.setOnClickListener(this);
561    }
562
563    public void setupFakeActionBarItemsForDialpadFragment() {
564        final View callhistoryButton = findViewById(R.id.call_history_on_dialpad_button);
565        callhistoryButton.setOnClickListener(this);
566    }
567
568    private void fixIntent(Intent intent) {
569        // This should be cleaned up: the call key used to send an Intent
570        // that just said to go to the recent calls list.  It now sends this
571        // abstract action, but this class hasn't been rewritten to deal with it.
572        if (Intent.ACTION_CALL_BUTTON.equals(intent.getAction())) {
573            intent.setDataAndType(Calls.CONTENT_URI, Calls.CONTENT_TYPE);
574            intent.putExtra("call_key", true);
575            setIntent(intent);
576        }
577    }
578
579    /**
580     * Returns true if the intent is due to hitting the green send key (hardware call button:
581     * KEYCODE_CALL) while in a call.
582     *
583     * @param intent the intent that launched this activity
584     * @param recentCallsRequest true if the intent is requesting to view recent calls
585     * @return true if the intent is due to hitting the green send key while in a call
586     */
587    private boolean isSendKeyWhileInCall(Intent intent, boolean recentCallsRequest) {
588        // If there is a call in progress go to the call screen
589        if (recentCallsRequest) {
590            final boolean callKey = intent.getBooleanExtra("call_key", false);
591
592            try {
593                ITelephony phone = ITelephony.Stub.asInterface(ServiceManager.checkService("phone"));
594                if (callKey && phone != null && phone.showCallScreen()) {
595                    return true;
596                }
597            } catch (RemoteException e) {
598                Log.e(TAG, "Failed to handle send while in call", e);
599            }
600        }
601
602        return false;
603    }
604
605    /**
606     * Sets the current tab based on the intent's request type
607     *
608     * @param intent Intent that contains information about which tab should be selected
609     */
610    private void displayFragment(Intent intent) {
611        // If we got here by hitting send and we're in call forward along to the in-call activity
612        boolean recentCallsRequest = Calls.CONTENT_TYPE.equals(intent.resolveType(
613            getContentResolver()));
614        if (isSendKeyWhileInCall(intent, recentCallsRequest)) {
615            finish();
616            return;
617        }
618
619        if (mDialpadFragment != null && (phoneIsInUse() || isDialIntent(intent))) {
620            mDialpadFragment.setStartedFromNewIntent(true);
621            showDialpadFragment(false);
622        }
623    }
624
625    @Override
626    public void onNewIntent(Intent newIntent) {
627        setIntent(newIntent);
628        fixIntent(newIntent);
629        displayFragment(newIntent);
630        final String action = newIntent.getAction();
631
632        if (mInSearchUi || (mRegularSearchFragment != null && mRegularSearchFragment.isVisible())) {
633            exitSearchUi();
634        }
635
636        // TODO krelease: Handle onNewIntent for all other fragments
637        /*
638         *if (mViewPager.getCurrentItem() == TAB_INDEX_DIALER) { if (mDialpadFragment != null) {
639         * mDialpadFragment.setStartedFromNewIntent(true); } else { Log.e(TAG,
640         * "DialpadFragment isn't ready yet when the tab is already selected."); } } else if
641         * (mViewPager.getCurrentItem() == TAB_INDEX_CALL_LOG) { if (mCallLogFragment != null) {
642         * mCallLogFragment.configureScreenFromIntent(newIntent); } else { Log.e(TAG,
643         * "CallLogFragment isn't ready yet when the tab is already selected."); } }
644         */
645        invalidateOptionsMenu();
646    }
647
648    /** Returns true if the given intent contains a phone number to populate the dialer with */
649    private boolean isDialIntent(Intent intent) {
650        final String action = intent.getAction();
651        if (Intent.ACTION_DIAL.equals(action) || ACTION_TOUCH_DIALER.equals(action)) {
652            return true;
653        }
654        if (Intent.ACTION_VIEW.equals(action)) {
655            final Uri data = intent.getData();
656            if (data != null && CallUtil.SCHEME_TEL.equals(data.getScheme())) {
657                return true;
658            }
659        }
660        return false;
661    }
662
663    /**
664     * Returns an appropriate call origin for this Activity. May return null when no call origin
665     * should be used (e.g. when some 3rd party application launched the screen. Call origin is
666     * for remembering the tab in which the user made a phone call, so the external app's DIAL
667     * request should not be counted.)
668     */
669    public String getCallOrigin() {
670        return !isDialIntent(getIntent()) ? CALL_ORIGIN_DIALTACTS : null;
671    }
672
673    /**
674     * Retrieves the filter text stored in {@link #setupFilterText(Intent)}.
675     * This text originally came from a FILTER_CONTACTS_ACTION intent received
676     * by this activity. The stored text will then be cleared after after this
677     * method returns.
678     *
679     * @return The stored filter text
680     */
681    public String getAndClearFilterText() {
682        String filterText = mFilterText;
683        mFilterText = null;
684        return filterText;
685    }
686
687    /**
688     * Stores the filter text associated with a FILTER_CONTACTS_ACTION intent.
689     * This is so child activities can check if they are supposed to display a filter.
690     *
691     * @param intent The intent received in {@link #onNewIntent(Intent)}
692     */
693    private void setupFilterText(Intent intent) {
694        // If the intent was relaunched from history, don't apply the filter text.
695        if ((intent.getFlags() & Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY) != 0) {
696            return;
697        }
698        String filter = intent.getStringExtra(UI.FILTER_TEXT_EXTRA_KEY);
699        if (filter != null && filter.length() > 0) {
700            mFilterText = filter;
701        }
702    }
703
704    private final PhoneFavoriteFragment.Listener mPhoneFavoriteListener =
705            new PhoneFavoriteFragment.Listener() {
706        @Override
707        public void onContactSelected(Uri contactUri) {
708            PhoneNumberInteraction.startInteractionForPhoneCall(
709                        DialtactsActivity.this, contactUri, getCallOrigin());
710        }
711
712        @Override
713        public void onCallNumberDirectly(String phoneNumber) {
714            Intent intent = CallUtil.getCallIntent(phoneNumber, getCallOrigin());
715            startActivity(intent);
716        }
717    };
718
719    /* TODO krelease: This is only relevant for phones that have a hard button search key (i.e.
720     * Nexus S). Supporting it is a little more tricky because of the dialpad fragment might
721     * be showing when the search key is pressed so there is more state management involved.
722
723    @Override
724    public void startSearch(String initialQuery, boolean selectInitialQuery,
725            Bundle appSearchData, boolean globalSearch) {
726        if (mRegularSearchFragment != null && mRegularSearchFragment.isAdded() && !globalSearch) {
727            if (mInSearchUi) {
728                if (mSearchView.hasFocus()) {
729                    showInputMethod(mSearchView.findFocus());
730                } else {
731                    mSearchView.requestFocus();
732                }
733            } else {
734                enterSearchUi();
735            }
736        } else {
737            super.startSearch(initialQuery, selectInitialQuery, appSearchData, globalSearch);
738        }
739    }*/
740
741    private void showInputMethod(View view) {
742        final InputMethodManager imm = (InputMethodManager) getSystemService(
743                Context.INPUT_METHOD_SERVICE);
744        if (imm != null) {
745            imm.showSoftInput(view, 0);
746        }
747    }
748
749    private void hideInputMethod(View view) {
750        final InputMethodManager imm = (InputMethodManager) getSystemService(
751                Context.INPUT_METHOD_SERVICE);
752        if (imm != null && view != null) {
753            imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
754        }
755    }
756
757    /**
758     * Shows the search fragment
759     */
760    private void enterSearchUi(boolean smartDialSearch) {
761        final FragmentTransaction transaction = getFragmentManager().beginTransaction();
762        transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
763        transaction.hide(mPhoneFavoriteFragment);
764        if (smartDialSearch) {
765            transaction.show(mSmartDialSearchFragment);
766        } else {
767            transaction.show(mRegularSearchFragment);
768        }
769        transaction.commit();
770
771        mInSearchUi = true;
772    }
773
774    /**
775     * Hides the search fragment
776     */
777    private void exitSearchUi() {
778        final FragmentTransaction transaction = getFragmentManager().beginTransaction();
779        transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
780        transaction.hide(mRegularSearchFragment);
781        transaction.hide(mSmartDialSearchFragment);
782        transaction.show(mPhoneFavoriteFragment);
783        transaction.commit();
784        mInSearchUi = false;
785    }
786
787    /** Returns an Intent to launch Call Settings screen */
788    public static Intent getCallSettingsIntent() {
789        final Intent intent = new Intent(Intent.ACTION_MAIN);
790        intent.setClassName(PHONE_PACKAGE, CALL_SETTINGS_CLASS_NAME);
791        intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
792        return intent;
793    }
794
795    @Override
796    public void onBackPressed() {
797        if (mDialpadFragment.isVisible()) {
798            hideDialpadFragment(true);
799        } else if (mInSearchUi) {
800            mSearchView.setText(null);
801        } else if (mShowAllContactsFragment.isVisible()) {
802            hideAllContactsFragment();
803        } else if (isTaskRoot()) {
804            // Instead of stopping, simply push this to the back of the stack.
805            // This is only done when running at the top of the stack;
806            // otherwise, we have been launched by someone else so need to
807            // allow the user to go back to the caller.
808            moveTaskToBack(false);
809        } else {
810            super.onBackPressed();
811        }
812    }
813
814    @Override
815    public void onDialpadQueryChanged(String query) {
816        final String normalizedQuery = SmartDialNameMatcher.normalizeNumber(query,
817                SmartDialNameMatcher.LATIN_SMART_DIAL_MAP);
818        if (!TextUtils.equals(mSearchView.getText(), normalizedQuery)) {
819            mSearchView.setText(normalizedQuery);
820        }
821    }
822
823    @Override
824    public void onListFragmentScrollStateChange(int scrollState) {
825        if (scrollState == OnScrollListener.SCROLL_STATE_TOUCH_SCROLL) {
826            hideDialpadFragmentIfNecessary();
827            hideInputMethod(getCurrentFocus());
828        }
829    }
830
831    @Override
832    public void onPhoneFavoriteFragmentStarted() {
833        setupFakeActionBarItemsForFavoritesFragment();
834    }
835
836    @Override
837    public void onDialpadFragmentStarted() {
838        setupFakeActionBarItemsForDialpadFragment();
839    }
840
841    private boolean phoneIsInUse() {
842        final TelephonyManager tm = (TelephonyManager) getSystemService(
843                Context.TELEPHONY_SERVICE);
844        return tm.getCallState() != TelephonyManager.CALL_STATE_IDLE;
845    }
846}
847