content: /*
   * Copyright (C) 2016 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.internal.app;
  
  import android.app.FragmentManager;
  import android.app.FragmentTransaction;
  import android.app.ListFragment;
  import android.content.Context;
  import android.os.Bundle;
  import android.os.LocaleList;
  import android.text.TextUtils;
  import android.view.Menu;
  import android.view.MenuInflater;
  import android.view.MenuItem;
  import android.view.View;
  import android.widget.ListView;
  import android.widget.SearchView;
  
  import com.android.internal.R;
  
  import java.util.Collections;
  import java.util.HashSet;
  import java.util.Locale;
  import java.util.Set;
  
  /**
   * A two-step locale picker. It shows a language, then a country.
   *
   * <p>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.</p>
   */
  public class LocalePickerWithRegion extends ListFragment implements SearchView.OnQueryTextListener {
      private static final String PARENT_FRAGMENT_NAME = "localeListEditor";
  
      private SuggestedLocaleAdapter mAdapter;
      private LocaleSelectedListener mListener;
      private Set<LocaleStore.LocaleInfo> 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.
       *
       * <p>This is the mechanism to "return" the result of the selection.</p>
       */
      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.
       *
       * <p>Returns true if we need to show the list, false if not.</p>
       *
       * <p>Can return false because of an error, trying to show a list of countries,
       * but no parent locale was provided.</p>
       *
       * <p>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.</p>
       */
      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<String> 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;
      }
  }
  