From c0ed614252a3254eabcba78b7c0bd9cd0d15141f Mon Sep 17 00:00:00 2001 From: tmfang Date: Fri, 13 Jul 2018 11:11:26 +0800 Subject: [PATCH] Remove LocalePickerWithRegion class in Settings. This class is too complex, we can't afford maintaining this long term. We need a new Activity that wraps the framework version of LocalePickerWithRegion. Bug: 111373939 Test: manual test & make RunSettingsRoboTests -j56 Change-Id: I93c719246b84350d2eee4e8ce1ffd97cd168925b --- AndroidManifest.xml | 6 + .../localepicker/LocaleListEditor.java | 38 +-- .../localepicker/LocalePickerWithRegion.java | 278 ------------------ .../LocalePickerWithRegionActivity.java | 79 +++++ ...randfather_not_implementing_instrumentable | 3 +- .../LocalePickerWithRegionActivityTest.java | 51 ++++ 6 files changed, 158 insertions(+), 297 deletions(-) delete mode 100644 src/com/android/settings/localepicker/LocalePickerWithRegion.java create mode 100644 src/com/android/settings/localepicker/LocalePickerWithRegionActivity.java create mode 100644 tests/robotests/src/com/android/settings/localepicker/LocalePickerWithRegionActivityTest.java diff --git a/AndroidManifest.xml b/AndroidManifest.xml index d3acd131657..303dea1761a 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -574,6 +574,12 @@ android:value="true" /> + + + It shows suggestions at the top, then the rest of the locales. - * Allows the user to search for locales using both their native name and their name in the - * default locale.

- */ -public class LocalePickerWithRegion extends ListFragment implements SearchView.OnQueryTextListener { - private static final String PARENT_FRAGMENT_NAME = "localeListEditor"; - - private SuggestedLocaleAdapter mAdapter; - private LocaleSelectedListener mListener; - private Set mLocaleList; - private LocaleStore.LocaleInfo mParentLocale; - private boolean mTranslatedOnly = false; - private SearchView mSearchView = null; - private CharSequence mPreviousSearch = null; - private boolean mPreviousSearchHadFocus = false; - private int mFirstVisiblePosition = 0; - private int mTopDistance = 0; - - /** - * Other classes can register to be notified when a locale was selected. - * - *

This is the mechanism to "return" the result of the selection.

- */ - public interface LocaleSelectedListener { - /** - * The classes that want to retrieve the locale picked should implement this method. - * @param locale the locale picked. - */ - void onLocaleSelected(LocaleStore.LocaleInfo locale); - } - - private static LocalePickerWithRegion createCountryPicker(Context context, - LocaleSelectedListener listener, LocaleStore.LocaleInfo parent, - boolean translatedOnly) { - LocalePickerWithRegion - localePicker = new LocalePickerWithRegion(); - boolean shouldShowTheList = localePicker.setListener(context, listener, parent, - translatedOnly); - return shouldShowTheList ? localePicker : null; - } - - public static LocalePickerWithRegion createLanguagePicker(Context context, - LocaleSelectedListener listener, boolean translatedOnly) { - LocalePickerWithRegion - localePicker = new LocalePickerWithRegion(); - localePicker.setListener(context, listener, /* parent */ null, translatedOnly); - return localePicker; - } - - /** - * Sets the listener and initializes the locale list. - * - *

Returns true if we need to show the list, false if not.

- * - *

Can return false because of an error, trying to show a list of countries, - * but no parent locale was provided.

- * - *

It can also return false if the caller tries to show the list in country mode and - * there is only one country available (i.e. Japanese => Japan). - * In this case we don't even show the list, we call the listener with that locale, - * "pretending" it was selected, and return false.

- */ - private boolean setListener(Context context, LocaleSelectedListener listener, - LocaleStore.LocaleInfo parent, boolean translatedOnly) { - this.mParentLocale = parent; - this.mListener = listener; - this.mTranslatedOnly = translatedOnly; - setRetainInstance(true); - - final HashSet langTagsToIgnore = new HashSet<>(); - if (!translatedOnly) { - final LocaleList userLocales = LocalePicker.getLocales(); - final String[] langTags = userLocales.toLanguageTags().split(","); - Collections.addAll(langTagsToIgnore, langTags); - } - - if (parent != null) { - mLocaleList = LocaleStore.getLevelLocales(context, - langTagsToIgnore, parent, translatedOnly); - if (mLocaleList.size() <= 1) { - if (listener != null && (mLocaleList.size() == 1)) { - listener.onLocaleSelected(mLocaleList.iterator().next()); - } - return false; - } - } else { - mLocaleList = LocaleStore.getLevelLocales(context, langTagsToIgnore, - null /* no parent */, translatedOnly); - } - - return true; - } - - private void returnToParentFrame() { - getFragmentManager().popBackStack(PARENT_FRAGMENT_NAME, - FragmentManager.POP_BACK_STACK_INCLUSIVE); - } - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setHasOptionsMenu(true); - - if (mLocaleList == null) { - // The fragment was killed and restored by the FragmentManager. - // At this point we have no data, no listener. Just return, to prevend a NPE. - // Fixes b/28748150. Created b/29400003 for a cleaner solution. - returnToParentFrame(); - return; - } - - final boolean countryMode = mParentLocale != null; - final Locale sortingLocale = countryMode ? mParentLocale.getLocale() : Locale.getDefault(); - mAdapter = new SuggestedLocaleAdapter(mLocaleList, countryMode); - final LocaleHelper.LocaleInfoComparator comp = - new LocaleHelper.LocaleInfoComparator(sortingLocale, countryMode); - mAdapter.sort(comp); - setListAdapter(mAdapter); - } - - @Override - public boolean onOptionsItemSelected(MenuItem menuItem) { - int id = menuItem.getItemId(); - switch (id) { - case android.R.id.home: - getFragmentManager().popBackStack(); - return true; - } - return super.onOptionsItemSelected(menuItem); - } - - @Override - public void onResume() { - super.onResume(); - - if (mParentLocale != null) { - getActivity().setTitle(mParentLocale.getFullNameNative()); - } else { - getActivity().setTitle(R.string.language_selection_title); - } - - getListView().requestFocus(); - } - - @Override - public void onPause() { - super.onPause(); - - // Save search status - if (mSearchView != null) { - mPreviousSearchHadFocus = mSearchView.hasFocus(); - mPreviousSearch = mSearchView.getQuery(); - } else { - mPreviousSearchHadFocus = false; - mPreviousSearch = null; - } - - // Save scroll position - final ListView list = getListView(); - final View firstChild = list.getChildAt(0); - mFirstVisiblePosition = list.getFirstVisiblePosition(); - mTopDistance = (firstChild == null) ? 0 : (firstChild.getTop() - list.getPaddingTop()); - } - - @Override - public void onListItemClick(ListView l, View v, int position, long id) { - final LocaleStore.LocaleInfo locale = - (LocaleStore.LocaleInfo) getListAdapter().getItem(position); - - if (locale.getParent() != null) { - if (mListener != null) { - mListener.onLocaleSelected(locale); - } - returnToParentFrame(); - } else { - LocalePickerWithRegion - selector = LocalePickerWithRegion.createCountryPicker( - getContext(), mListener, locale, mTranslatedOnly /* translate only */); - if (selector != null) { - getFragmentManager().beginTransaction() - .setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN) - .replace(getId(), selector).addToBackStack(null) - .commit(); - } else { - returnToParentFrame(); - } - } - } - - @Override - public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { - if (mParentLocale == null) { - inflater.inflate(R.menu.language_selection_list, menu); - - final MenuItem searchMenuItem = menu.findItem(R.id.locale_search_menu); - mSearchView = (SearchView) searchMenuItem.getActionView(); - - mSearchView.setQueryHint(getText(R.string.search_language_hint)); - mSearchView.setOnQueryTextListener(this); - - // Restore previous search status - if (!TextUtils.isEmpty(mPreviousSearch)) { - searchMenuItem.expandActionView(); - mSearchView.setIconified(false); - mSearchView.setActivated(true); - if (mPreviousSearchHadFocus) { - mSearchView.requestFocus(); - } - mSearchView.setQuery(mPreviousSearch, true /* submit */); - } else { - mSearchView.setQuery(null, false /* submit */); - } - - // Restore previous scroll position - getListView().setSelectionFromTop(mFirstVisiblePosition, mTopDistance); - } - } - - @Override - public boolean onQueryTextSubmit(String query) { - return false; - } - - @Override - public boolean onQueryTextChange(String newText) { - if (mAdapter != null) { - mAdapter.getFilter().filter(newText); - } - return false; - } -} diff --git a/src/com/android/settings/localepicker/LocalePickerWithRegionActivity.java b/src/com/android/settings/localepicker/LocalePickerWithRegionActivity.java new file mode 100644 index 00000000000..6ddcf2396c9 --- /dev/null +++ b/src/com/android/settings/localepicker/LocalePickerWithRegionActivity.java @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settings.localepicker; + +import android.app.Activity; +import android.app.FragmentTransaction; +import android.content.Intent; +import android.os.Bundle; +import android.view.MenuItem; + +import com.android.internal.app.LocalePickerWithRegion; +import com.android.internal.app.LocaleStore; + +public class LocalePickerWithRegionActivity extends Activity + implements LocalePickerWithRegion.LocaleSelectedListener { + + private static final String PARENT_FRAGMENT_NAME = "localeListEditor"; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + getActionBar().setDisplayHomeAsUpEnabled(true); + + final LocalePickerWithRegion selector = LocalePickerWithRegion.createLanguagePicker( + this, LocalePickerWithRegionActivity.this, false /* translate only */); + getFragmentManager() + .beginTransaction() + .setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN) + .replace(android.R.id.content, selector) + .addToBackStack(PARENT_FRAGMENT_NAME) + .commit(); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + if (item.getItemId() == android.R.id.home) { + handleBackPressed(); + return true; + } + return super.onOptionsItemSelected(item); + } + + @Override + public void onLocaleSelected(LocaleStore.LocaleInfo locale) { + final Intent intent = new Intent(); + intent.putExtra(LocaleListEditor.INTENT_LOCALE_KEY, locale); + setResult(RESULT_OK, intent); + finish(); + } + + @Override + public void onBackPressed() { + handleBackPressed(); + } + + private void handleBackPressed() { + if (getFragmentManager().getBackStackEntryCount() > 1) { + super.onBackPressed(); + } else { + setResult(RESULT_CANCELED); + finish(); + } + } +} + diff --git a/tests/robotests/assets/grandfather_not_implementing_instrumentable b/tests/robotests/assets/grandfather_not_implementing_instrumentable index 27ab65cf063..2c8ae5d9267 100644 --- a/tests/robotests/assets/grandfather_not_implementing_instrumentable +++ b/tests/robotests/assets/grandfather_not_implementing_instrumentable @@ -5,5 +5,4 @@ com.android.settings.password.ChooseLockPassword$SaveAndFinishWorker com.android.settings.password.ChooseLockPattern$SaveAndFinishWorker com.android.settings.RestrictedListPreference$RestrictedListPreferenceDialogFragment com.android.settings.password.ConfirmDeviceCredentialBaseFragment$LastTryDialog -com.android.settings.password.CredentialCheckResultTracker -com.android.settings.localepicker.LocalePickerWithRegion +com.android.settings.password.CredentialCheckResultTracker \ No newline at end of file diff --git a/tests/robotests/src/com/android/settings/localepicker/LocalePickerWithRegionActivityTest.java b/tests/robotests/src/com/android/settings/localepicker/LocalePickerWithRegionActivityTest.java new file mode 100644 index 00000000000..bad3dbda032 --- /dev/null +++ b/tests/robotests/src/com/android/settings/localepicker/LocalePickerWithRegionActivityTest.java @@ -0,0 +1,51 @@ +package com.android.settings.localepicker; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; + +import android.app.Activity; + +import com.android.internal.app.LocaleStore; +import com.android.settings.testutils.SettingsRobolectricTestRunner; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.MockitoAnnotations; +import org.robolectric.Robolectric; +import org.robolectric.Shadows; +import org.robolectric.android.controller.ActivityController; +import org.robolectric.shadows.ShadowActivity; + +@RunWith(SettingsRobolectricTestRunner.class) +public class LocalePickerWithRegionActivityTest { + + private LocalePickerWithRegionActivity mActivity; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + final ActivityController mActivityController = + Robolectric.buildActivity(LocalePickerWithRegionActivity.class); + mActivity = spy(mActivityController.get()); + } + + @Test + public void onLocaleSelected_resultShouldBeOK() { + final ShadowActivity shadowActivity = Shadows.shadowOf(mActivity); + mActivity.onLocaleSelected(mock(LocaleStore.LocaleInfo.class)); + + assertEquals(Activity.RESULT_OK, shadowActivity.getResultCode()); + } + + @Test + public void onLocaleSelected_localeInfoShouldBeSentBack() { + final ShadowActivity shadowActivity = Shadows.shadowOf(mActivity); + mActivity.onLocaleSelected(mock(LocaleStore.LocaleInfo.class)); + + assertNotNull(shadowActivity.getResultIntent().getSerializableExtra( + LocaleListEditor.INTENT_LOCALE_KEY)); + } +}