diff options
author | Yorke Lee <yorkelee@google.com> | 2013-08-09 17:39:25 -0700 |
---|---|---|
committer | Yorke Lee <yorkelee@google.com> | 2013-08-11 15:22:31 -0700 |
commit | ef2b7388569a94432278a617b528f628385fbb8c (patch) | |
tree | 2a17f308421912390b12fd29b3320d9af01e32e4 | |
parent | c87896cd74290eba75d3ba3ee8a3f5bfe36b63fb (diff) |
Improve fragment handling
* SmartDialSearchFragment now extends SearchFragment to avoid code
duplication
* Load search fragments dynamically instead of all at once on startup
* Don't always return the user to the favorites screen on resume
* Activity and fragment state persist across activity recreation
Bug: 10248520
Change-Id: I6bce83e22a8eced9783805ea5a924f34bae78a14
-rw-r--r-- | src/com/android/dialer/DialtactsActivity.java | 182 | ||||
-rw-r--r-- | src/com/android/dialer/SearchFragment.java | 9 | ||||
-rw-r--r-- | src/com/android/dialer/list/SmartDialSearchFragment.java | 51 |
3 files changed, 115 insertions, 127 deletions
diff --git a/src/com/android/dialer/DialtactsActivity.java b/src/com/android/dialer/DialtactsActivity.java index 8381c8dd4..316465e74 100644 --- a/src/com/android/dialer/DialtactsActivity.java +++ b/src/com/android/dialer/DialtactsActivity.java @@ -102,11 +102,14 @@ public class DialtactsActivity extends TransactionSafeActivity implements View.O private static final String CALL_ORIGIN_DIALTACTS = "com.android.dialer.DialtactsActivity"; + private static final String KEY_IN_SEARCH_UI = "in_search_ui"; + private static final String KEY_SEARCH_QUERY = "search_query"; + private static final String KEY_FIRST_LAUNCH = "first_launch"; + private static final String TAG_DIALPAD_FRAGMENT = "dialpad"; private static final String TAG_REGULAR_SEARCH_FRAGMENT = "search"; private static final String TAG_SMARTDIAL_SEARCH_FRAGMENT = "smartdial"; private static final String TAG_FAVORITES_FRAGMENT = "favorites"; - private static final String TAG_SHOW_ALL_CONTACTS_FRAGMENT = "show_all_contacts"; /** * Just for backward compatibility. Should behave as same as {@link Intent#ACTION_DIAL}. @@ -151,12 +154,17 @@ public class DialtactsActivity extends TransactionSafeActivity implements View.O * {@link PhoneNumberPickerFragment}). */ private boolean mInSearchUi; + /** + * True when this activity has been launched for the first time. + */ private boolean mFirstLaunch; private View mSearchViewContainer; private View mSearchViewCloseButton; private View mVoiceSearchButton; private EditText mSearchView; + private String mSearchQuery; + /** * Listener used when one of phone numbers in search UI is selected. This will initiate a * phone call using the phone number. @@ -198,26 +206,31 @@ public class DialtactsActivity extends TransactionSafeActivity implements View.O @Override public void onTextChanged(CharSequence s, int start, int before, int count) { - // TODO krelease: populate the search fragments with the correct - // search query at the correct point in time of the fragment lifecycle. - // The current behavior is to simply return to the favorites screen - // (when docked), or returning to the Dialer after it has been - // swapped out of memory. - if (mDialpadFragment == null) return; - final boolean smartDialSearch = isDialpadShowing(); final String newText = s.toString(); + if (newText.equals(mSearchQuery)) { + // If the query hasn't changed (perhaps due to activity being destroyed + // and restored, or user launching the same DIAL intent twice), then there is + // no need to do anything here. + return; + } + mSearchQuery = newText; + if (DEBUG) { + Log.d(TAG, "onTextChange for mSearchView called with new query: " + s); + } + final boolean smartDialSearch = isDialpadShowing(); + // Show search result with non-empty text. Show a bare list otherwise. if (TextUtils.isEmpty(newText) && mInSearchUi) { exitSearchUi(); mSearchViewCloseButton.setVisibility(View.GONE); return; } else if (!TextUtils.isEmpty(newText) && !mInSearchUi) { - enterSearchUi(smartDialSearch); + enterSearchUi(smartDialSearch, newText); } - if (smartDialSearch) { - mSmartDialSearchFragment.setQueryString(newText, false); - } else { + if (smartDialSearch && mSmartDialSearchFragment != null) { + mSmartDialSearchFragment.setQueryString(newText, false); + } else if (mRegularSearchFragment != null) { mRegularSearchFragment.setQueryString(newText, false); } mSearchViewCloseButton.setVisibility(View.VISIBLE); @@ -236,7 +249,6 @@ public class DialtactsActivity extends TransactionSafeActivity implements View.O @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - mFirstLaunch = true; final Intent intent = getIntent(); @@ -246,21 +258,20 @@ public class DialtactsActivity extends TransactionSafeActivity implements View.O getActionBar().hide(); + // Add the favorites fragment, and the dialpad fragment, but only if savedInstanceState + // is null. Otherwise the fragment manager takes care of recreating these fragments. if (savedInstanceState == null) { - mPhoneFavoriteFragment = new PhoneFavoriteFragment(); - mPhoneFavoriteFragment.setListener(mPhoneFavoriteListener); - - mRegularSearchFragment = new SearchFragment(); - mSmartDialSearchFragment = new SmartDialSearchFragment(); - mDialpadFragment = new DialpadFragment(); + final PhoneFavoriteFragment phoneFavoriteFragment = new PhoneFavoriteFragment(); + phoneFavoriteFragment.setListener(mPhoneFavoriteListener); - // TODO krelease: load fragments on demand instead of creating all of them at run time final FragmentTransaction ft = getFragmentManager().beginTransaction(); - ft.add(R.id.dialtacts_frame, mPhoneFavoriteFragment, TAG_FAVORITES_FRAGMENT); - ft.add(R.id.dialtacts_frame, mRegularSearchFragment, TAG_REGULAR_SEARCH_FRAGMENT); - ft.add(R.id.dialtacts_frame, mSmartDialSearchFragment, TAG_SMARTDIAL_SEARCH_FRAGMENT); - ft.add(R.id.dialtacts_container, mDialpadFragment, TAG_DIALPAD_FRAGMENT); + ft.add(R.id.dialtacts_frame, phoneFavoriteFragment, TAG_FAVORITES_FRAGMENT); + ft.add(R.id.dialtacts_container, new DialpadFragment(), TAG_DIALPAD_FRAGMENT); ft.commit(); + } else { + mSearchQuery = savedInstanceState.getString(KEY_SEARCH_QUERY); + mInSearchUi = savedInstanceState.getBoolean(KEY_IN_SEARCH_UI); + mFirstLaunch = savedInstanceState.getBoolean(KEY_FIRST_LAUNCH); } mBottomPaddingView = findViewById(R.id.dialtacts_bottom_padding); @@ -275,21 +286,6 @@ public class DialtactsActivity extends TransactionSafeActivity implements View.O @Override protected void onResume() { super.onResume(); - final FragmentManager fm = getFragmentManager(); - mPhoneFavoriteFragment = (PhoneFavoriteFragment) fm.findFragmentByTag( - TAG_FAVORITES_FRAGMENT); - mDialpadFragment = (DialpadFragment) fm.findFragmentByTag(TAG_DIALPAD_FRAGMENT); - - mRegularSearchFragment = (SearchFragment) fm.findFragmentByTag( - TAG_REGULAR_SEARCH_FRAGMENT); - mRegularSearchFragment.setOnPhoneNumberPickerActionListener( - mPhoneNumberPickerActionListener); - - mSmartDialSearchFragment = (SmartDialSearchFragment) fm.findFragmentByTag( - TAG_SMARTDIAL_SEARCH_FRAGMENT); - mSmartDialSearchFragment.setOnPhoneNumberPickerActionListener( - mPhoneNumberPickerActionListener); - if (mFirstLaunch) { displayFragment(getIntent()); } @@ -297,15 +293,27 @@ public class DialtactsActivity extends TransactionSafeActivity implements View.O } @Override + protected void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + outState.putString(KEY_SEARCH_QUERY, mSearchQuery); + outState.putBoolean(KEY_IN_SEARCH_UI, mInSearchUi); + outState.putBoolean(KEY_FIRST_LAUNCH, mFirstLaunch); + } + + @Override public void onAttachFragment(Fragment fragment) { - if (fragment instanceof DialpadFragment || fragment instanceof SearchFragment - || fragment instanceof SmartDialSearchFragment) { + if (fragment instanceof DialpadFragment) { + mDialpadFragment = (DialpadFragment) fragment; final FragmentTransaction transaction = getFragmentManager().beginTransaction(); - transaction.hide(fragment); + transaction.hide(mDialpadFragment); transaction.commit(); + } else if (fragment instanceof SmartDialSearchFragment) { + mSmartDialSearchFragment = (SmartDialSearchFragment) fragment; + } else if (fragment instanceof SearchFragment) { + mRegularSearchFragment = (SearchFragment) fragment; + } else if (fragment instanceof PhoneFavoriteFragment) { + mPhoneFavoriteFragment = (PhoneFavoriteFragment) fragment; } - // TODO krelease: Save some kind of state here to show the appropriate fragment - // based on the state of the dialer when it was last paused } @Override @@ -440,7 +448,7 @@ public class DialtactsActivity extends TransactionSafeActivity implements View.O } private void hideDialpadFragmentIfNecessary() { - if (mDialpadFragment.isVisible()) { + if (mDialpadFragment != null && mDialpadFragment.isVisible()) { hideDialpadFragment(true); } } @@ -464,13 +472,20 @@ public class DialtactsActivity extends TransactionSafeActivity implements View.O mSearchViewContainer.animate().withLayer().alpha(0).translationY(-mSearchView.getHeight()) .setDuration(200).setListener(mHideListener); + if (mPhoneFavoriteFragment == null || mPhoneFavoriteFragment.getView() == null) { + mBottomPaddingView.setVisibility(View.VISIBLE); + return; + } + mPhoneFavoriteFragment.getView().animate().withLayer() .translationY(-mSearchViewContainer.getHeight()).setDuration(200).setListener( new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { mBottomPaddingView.setVisibility(View.VISIBLE); - mPhoneFavoriteFragment.getView().setTranslationY(0); + if (mPhoneFavoriteFragment.getView() != null) { + mPhoneFavoriteFragment.getView().setTranslationY(0); + } } }); } else { @@ -479,11 +494,8 @@ public class DialtactsActivity extends TransactionSafeActivity implements View.O } public void showSearchBar() { - // If the favorites fragment hasn't been fully created before the dialpad fragment - // is hidden (i.e. onResume), don't bother animating - if (mPhoneFavoriteFragment == null || mPhoneFavoriteFragment.getView() == null) { - return; - } + + mSearchViewContainer.animate().cancel(); mSearchViewContainer.setAlpha(0); mSearchViewContainer.setTranslationY(-mSearchViewContainer.getHeight()); @@ -495,6 +507,12 @@ public class DialtactsActivity extends TransactionSafeActivity implements View.O } }); + // If the favorites fragment hasn't been fully created before the dialpad fragment + // is hidden (i.e. onResume), don't bother animating + if (mPhoneFavoriteFragment == null || mPhoneFavoriteFragment.getView() == null) { + mBottomPaddingView.setVisibility(View.GONE); + return; + } mPhoneFavoriteFragment.getView().setTranslationY(-mSearchViewContainer.getHeight()); mPhoneFavoriteFragment.getView().animate().withLayer().translationY(0).setDuration(200) .setListener( @@ -591,19 +609,6 @@ public class DialtactsActivity extends TransactionSafeActivity implements View.O displayFragment(newIntent); final String action = newIntent.getAction(); - if (mInSearchUi || (mRegularSearchFragment != null && mRegularSearchFragment.isVisible())) { - exitSearchUi(); - } - - // TODO krelease: Handle onNewIntent for all other fragments - /* - *if (mViewPager.getCurrentItem() == TAB_INDEX_DIALER) { if (mDialpadFragment != null) { - * mDialpadFragment.setStartedFromNewIntent(true); } else { Log.e(TAG, - * "DialpadFragment isn't ready yet when the tab is already selected."); } } else if - * (mViewPager.getCurrentItem() == TAB_INDEX_CALL_LOG) { if (mCallLogFragment != null) { - * mCallLogFragment.configureScreenFromIntent(newIntent); } else { Log.e(TAG, - * "CallLogFragment isn't ready yet when the tab is already selected."); } } - */ invalidateOptionsMenu(); } @@ -719,17 +724,34 @@ public class DialtactsActivity extends TransactionSafeActivity implements View.O /** * Shows the search fragment */ - private void enterSearchUi(boolean smartDialSearch) { + private void enterSearchUi(boolean smartDialSearch, String query) { + if (DEBUG) { + Log.d(TAG, "Entering search UI - smart dial " + smartDialSearch); + } + final String tag = smartDialSearch ? TAG_SMARTDIAL_SEARCH_FRAGMENT : + TAG_REGULAR_SEARCH_FRAGMENT; + final FragmentTransaction transaction = getFragmentManager().beginTransaction(); transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE); - transaction.hide(mPhoneFavoriteFragment); - if (smartDialSearch) { - transaction.show(mSmartDialSearchFragment); + + SearchFragment fragment; + + transaction.remove(mPhoneFavoriteFragment); + fragment = (SearchFragment) getFragmentManager().findFragmentByTag(tag); + if (fragment == null) { + if (smartDialSearch) { + fragment = new SmartDialSearchFragment(); + } else { + fragment = new SearchFragment(); + } + transaction.replace(R.id.dialtacts_frame, fragment, tag); } else { - transaction.show(mRegularSearchFragment); + transaction.attach(fragment); } - transaction.commit(); + transaction.addToBackStack(null); + fragment.setQueryString(query, false); + transaction.commit(); mInSearchUi = true; } @@ -737,12 +759,7 @@ public class DialtactsActivity extends TransactionSafeActivity implements View.O * Hides the search fragment */ private void exitSearchUi() { - final FragmentTransaction transaction = getFragmentManager().beginTransaction(); - transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE); - transaction.hide(mRegularSearchFragment); - transaction.hide(mSmartDialSearchFragment); - transaction.show(mPhoneFavoriteFragment); - transaction.commit(); + getFragmentManager().popBackStack(); mInSearchUi = false; } @@ -756,7 +773,7 @@ public class DialtactsActivity extends TransactionSafeActivity implements View.O @Override public void onBackPressed() { - if (mDialpadFragment.isVisible()) { + if (mDialpadFragment != null && mDialpadFragment.isVisible()) { hideDialpadFragment(true); } else if (mInSearchUi) { mSearchView.setText(null); @@ -776,6 +793,17 @@ public class DialtactsActivity extends TransactionSafeActivity implements View.O final String normalizedQuery = SmartDialNameMatcher.normalizeNumber(query, SmartDialNameMatcher.LATIN_SMART_DIAL_MAP); if (!TextUtils.equals(mSearchView.getText(), normalizedQuery)) { + if (DEBUG) { + Log.d(TAG, "onDialpadQueryChanged - new query: " + query); + } + if (mDialpadFragment == null || !mDialpadFragment.isVisible()) { + // This callback can happen if the dialpad fragment is recreated because of + // activity destruction. In that case, don't update the search view because + // that would bring the user back to the search fragment regardless of the + // previous state of the application. Instead, just return here and let the + // fragment manager correctly figure out whatever fragment was last displayed. + return; + } mSearchView.setText(normalizedQuery); } } diff --git a/src/com/android/dialer/SearchFragment.java b/src/com/android/dialer/SearchFragment.java index 54d29d43f..86cd87323 100644 --- a/src/com/android/dialer/SearchFragment.java +++ b/src/com/android/dialer/SearchFragment.java @@ -22,6 +22,7 @@ import android.widget.AbsListView; import android.widget.AbsListView.OnScrollListener; import com.android.contacts.common.list.ContactListItemView; +import com.android.contacts.common.list.ContactEntryListAdapter; import com.android.contacts.common.list.PhoneNumberPickerFragment; import com.android.dialer.list.OnListFragmentScrolledListener; @@ -53,6 +54,9 @@ public class SearchFragment extends PhoneNumberPickerFragment { @Override public void onStart() { super.onStart(); + if (isSearchMode()) { + getAdapter().setHasHeader(0, false); + } getListView().setOnScrollListener(new OnScrollListener() { @Override public void onScrollStateChanged(AbsListView view, int scrollState) { @@ -70,6 +74,9 @@ public class SearchFragment extends PhoneNumberPickerFragment { protected void setSearchMode(boolean flag) { super.setSearchMode(flag); // This hides the "All contacts with phone numbers" header in the search fragment - getAdapter().setHasHeader(0, false); + final ContactEntryListAdapter adapter = getAdapter(); + if (adapter != null) { + adapter.setHasHeader(0, false); + } } } diff --git a/src/com/android/dialer/list/SmartDialSearchFragment.java b/src/com/android/dialer/list/SmartDialSearchFragment.java index 3c1e51343..492073fb5 100644 --- a/src/com/android/dialer/list/SmartDialSearchFragment.java +++ b/src/com/android/dialer/list/SmartDialSearchFragment.java @@ -15,62 +15,22 @@ */ package com.android.dialer.list; -import android.app.Activity; import android.content.Loader; import android.database.Cursor; import android.net.Uri; import android.os.Bundle; -import android.provider.ContactsContract.Directory; import android.util.Log; -import android.widget.AbsListView; -import android.widget.AbsListView.OnScrollListener; import com.android.contacts.common.list.ContactEntryListAdapter; -import com.android.contacts.common.list.ContactListItemView; -import com.android.contacts.common.list.PhoneNumberPickerFragment; +import com.android.dialer.SearchFragment; import com.android.dialer.dialpad.SmartDialCursorLoader; /** * Implements a fragment to load and display SmartDial search results. */ -public class SmartDialSearchFragment extends PhoneNumberPickerFragment { +public class SmartDialSearchFragment extends SearchFragment { private static final String TAG = SmartDialSearchFragment.class.getSimpleName(); - private OnListFragmentScrolledListener mActivityScrollListener; - - @Override - public void onAttach(Activity activity) { - super.onAttach(activity); - - setQuickContactEnabled(true); - setDarkTheme(false); - setPhotoPosition(ContactListItemView.getDefaultPhotoPosition(true /* opposite */)); - setUseCallableUri(true); - - try { - mActivityScrollListener = (OnListFragmentScrolledListener) activity; - } catch (ClassCastException e) { - throw new ClassCastException(activity.toString() - + " must implement OnListFragmentScrolledListener"); - } - } - - @Override - public void onStart() { - super.onStart(); - getListView().setOnScrollListener(new OnScrollListener() { - @Override - public void onScrollStateChanged(AbsListView view, int scrollState) { - mActivityScrollListener.onListFragmentScrollStateChange(scrollState); - } - - @Override - public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, - int totalItemCount) { - } - }); - } - /** * Creates a SmartDialListAdapter to display and operate on search results. */ @@ -110,11 +70,4 @@ public class SmartDialSearchFragment extends PhoneNumberPickerFragment { final SmartDialNumberListAdapter adapter = (SmartDialNumberListAdapter) getAdapter(); return adapter.getDataUri(position); } - - @Override - protected void setSearchMode(boolean flag) { - super.setSearchMode(flag); - // This hides the "All contacts with phone numbers" header in the search fragment - getAdapter().setHasHeader(0, false); - } } |