Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 3ac8116e authored by James Kung's avatar James Kung Committed by Android (Google) Code Review
Browse files

Merge "Cities sort options" into ics-ub-clock-amazon

parents af16c7cf 62b9f3d8
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -22,6 +22,9 @@
        android:actionViewClass="android.widget.SearchView"
        android:imeOptions="actionSearch"
        android:orderInCategory="1" />
    <item android:id="@+id/menu_item_sort"
        android:title="@string/menu_item_sort_by_gmt_offset"
        android:showAsAction="never" />
    <item android:id="@+id/menu_item_settings"
        android:title="@string/menu_item_settings"
        android:icon="@android:drawable/ic_menu_preferences"
+1 −1
Original line number Diff line number Diff line
@@ -77,7 +77,7 @@
    <dimen name="dialpad_font_size">48sp</dimen>
    <dimen name="dialpad_font_size_ampm">24sp</dimen>
    <dimen name="city_name_font_size">24sp</dimen>
    <dimen name="city_time_font_size">14sp</dimen>
    <dimen name="city_time_font_size">18sp</dimen>

    <dimen name="tablet_dialpad_font_size">60sp</dimen>
    <dimen name="tablet_dialpad_font_size_ampm">30sp</dimen>
+4 −0
Original line number Diff line number Diff line
@@ -329,6 +329,10 @@
    <string name="menu_item_help">Help</string>
    <!-- Menu item on clock screen to enter night mode. -->
    <string name="menu_item_night_mode">Night mode</string>
    <!-- Menu item on Cities screen to sort by GMT offset -->
    <string name="menu_item_sort_by_gmt_offset">Sort by time</string>
    <!-- Menu item on Cities screen to sort by alphabetical order -->
    <string name="menu_item_sort_by_name">Sort by name</string>

    <!-- Stop Watch strings -->
    <!-- Describes the purpose of the button to resume running a stopwatch -->
+31 −13
Original line number Diff line number Diff line
@@ -38,8 +38,11 @@ import android.os.Handler;
import android.os.SystemClock;
import android.preference.PreferenceManager;
import android.provider.Settings;
import android.text.Spannable;
import android.text.TextUtils;
import android.text.format.DateFormat;
import android.text.format.DateUtils;
import android.text.style.ForegroundColorSpan;
import android.view.MenuItem;
import android.view.View;
import android.view.animation.AccelerateInterpolator;
@@ -50,13 +53,11 @@ import com.android.deskclock.stopwatch.Stopwatches;
import com.android.deskclock.timer.Timers;
import com.android.deskclock.worldclock.CityObj;

import java.text.Collator;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Comparator;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;


public class Utils {
@@ -437,8 +438,7 @@ public class Utils {
        }
    }

    public static CityObj[] loadCitiesDataBase(Context c) {
        final Collator collator = Collator.getInstance();
    public static CityObj[] loadCitiesFromXml(Context c) {
        Resources r = c.getResources();
        // Read strings array of name,timezone, id
        // make sure the list are the same length
@@ -453,17 +453,35 @@ public class Utils {
        for (int i = 0; i < cities.length; i++) {
            tempList[i] = new CityObj(cities[i], timezones[i], ids[i]);
        }
        // Sort alphabetically
        Arrays.sort(tempList, new Comparator<CityObj> () {
            @Override
            public int compare(CityObj c1, CityObj c2) {
                Comparator<CityObj> mCollator;
                return collator.compare(c1.mCityName, c2.mCityName);
            }
        });
        return tempList;
    }

    /**
     * Returns string denoting the timezone hour offset (e.g. GMT-8:00)
     */
    public static String getGMTHourOffset(TimeZone timezone, boolean showMinutes) {
        StringBuilder sb = new StringBuilder();
        sb.append("GMT");
        int gmtOffset = timezone.getRawOffset();
        if (gmtOffset < 0) {
            sb.append('-');
        } else {
            sb.append('+');
        }
        sb.append(Math.abs(gmtOffset) / DateUtils.HOUR_IN_MILLIS); // Hour

        if (showMinutes) {
            final int min = (Math.abs(gmtOffset) / (int) DateUtils.MINUTE_IN_MILLIS) % 60;
            sb.append(':');
            if (min < 10) {
                sb.append('0');
            }
            sb.append(min);
        }

        return sb.toString();
    }

    public static String getCityName(CityObj city, CityObj dbCity) {
        return (city.mCityId == null || dbCity == null) ? city.mCityName : dbCity.mCityName;
    }
+187 −70
Original line number Diff line number Diff line
@@ -18,16 +18,14 @@ package com.android.deskclock.worldclock;

import android.app.ActionBar;
import android.app.Activity;
import android.app.SearchableInfo;
import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.text.Spannable;
import android.text.TextUtils;
import android.text.format.DateFormat;
import android.text.style.ForegroundColorSpan;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
@@ -54,8 +52,10 @@ import com.android.deskclock.SettingsActivity;
import com.android.deskclock.Utils;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.HashMap;
import java.util.List;
import java.util.TimeZone;

/**
@@ -66,6 +66,12 @@ public class CitiesActivity extends Activity implements OnCheckedChangeListener,

    private static final String KEY_SEARCH_QUERY = "search_query";
    private static final String KEY_SEARCH_MODE = "search_mode";
    private static final String KEY_LIST_POSITION = "list_position";

    private static final String PREF_SORT = "sort_preference";

    private static final int SORT_BY_NAME = 0;
    private static final int SORT_BY_GMT_OFFSET = 1;

    /**
     * This must be false for production. If true, turns on logging, test code,
@@ -80,28 +86,35 @@ public class CitiesActivity extends Activity implements OnCheckedChangeListener,
    private HashMap<String, CityObj> mUserSelectedCities;
    private Calendar mCalendar;

    private MenuItem mSearchMenu;
    private SearchView mSearchView;
    private StringBuffer mQueryTextBuffer = new StringBuffer();
    private boolean mSearchMode;
    private int mPosition = -1;

    private SharedPreferences mPrefs;
    private int mSortType;

    /***
     * Adapter for a list of cities with the respected time zone. The Adapter
     * sorts the list alphabetically and create an indexer.
     ***/

    private class CityAdapter extends BaseAdapter implements Filterable, SectionIndexer {
        private static final String DELETED_ENTRY = "C0";
        private ArrayList<CityObj> mAllTheCitiesList; // full list of the cities
        private final HashMap<String, CityObj> mSelectedCitiesList;  // selected cities
        private List<CityObj> mDisplayedCitiesList;
        private CityObj[] mCities;

        private String[] mSortByNameSectionHeaders;
        private Integer[] mSortByNameSectionPositions;

        private String[] mSortByTimeSectionHeaders;
        private Integer[] mSortByTimeSectionPositions;

        private ArrayList<CityObj> mDisplayedCitiesList;
        private final int mSpanColor;
        private CityNameComparator mSortByNameComparator = new CityNameComparator();
        private CityGmtOffsetComparator mSortByGmtOffsetComparator = new CityGmtOffsetComparator();

        private final LayoutInflater mInflater;
        private boolean mIs24HoursMode; // AM/PM or 24 hours mode
        private Object[] mSectionHeaders;
        private Object[] mSectionPositions;

        private Filter mFilter = new Filter() {

@@ -110,18 +123,51 @@ public class CitiesActivity extends Activity implements OnCheckedChangeListener,
                FilterResults results = new FilterResults();
                String modifiedQuery = constraint.toString().trim().toUpperCase();

                ArrayList<CityObj> filteredList = new ArrayList<CityObj>();
                int positionIndex = 0;
                int i = 0;
                while (i < mCities.length) {
                    CityObj city = mCities[i];

                    // If the city is a deleted entry, ignore it.
                    if (city.mCityId.equals(DELETED_ENTRY)) {
                        i++;
                        continue;
                    }

                    // If the search query is empty, add section headers
                    if (TextUtils.isEmpty(modifiedQuery)) {
                    results.values = mAllTheCitiesList;
                    results.count = mAllTheCitiesList.size();
                    return results;

                        // If the list is sorted by name, and the position index is correct,
                        // insert a section header into the list
                        if (mSortType == SORT_BY_NAME &&
                                mSortByNameSectionPositions.length > positionIndex &&
                                mSortByNameSectionPositions[positionIndex] == filteredList.size()) {
                            String name = mSortByNameSectionHeaders[positionIndex];
                            filteredList.add(new CityObj(name, null, null));
                            positionIndex++;
                            continue;
                        }

                ArrayList<CityObj> filteredList = new ArrayList<CityObj>();
                for (CityObj city : mAllTheCitiesList) {
                        // If the list is sorted by time, and the position index is correct,
                        // insert a section header into the list
                        if (mSortType == SORT_BY_GMT_OFFSET &&
                                mSortByTimeSectionPositions.length > positionIndex &&
                                mSortByTimeSectionPositions[positionIndex] == filteredList.size()) {
                            String timezone = mSortByTimeSectionHeaders[positionIndex];
                            filteredList.add(new CityObj(null, timezone, null));
                            positionIndex++;
                            continue;
                        }
                    }

                    // If the city name begins with the query, add the city into the list.
                    // If the query is empty, the city will automatically be added to the list.
                    String cityName = city.mCityName.trim().toUpperCase();
                    if (city.mCityId != null && cityName.startsWith(modifiedQuery)) {
                        filteredList.add(city);
                    }
                    i++;
                }
                results.values = filteredList;
                results.count = filteredList.size();
@@ -131,6 +177,10 @@ public class CitiesActivity extends Activity implements OnCheckedChangeListener,
            @Override
            protected void publishResults(CharSequence constraint, FilterResults results) {
                mDisplayedCitiesList = (ArrayList<CityObj>) results.values;
                if (mPosition >= 0) {
                    mCitiesList.setSelectionFromTop(mPosition, 0);
                    mPosition = -1;
                }
                notifyDataSetChanged();
            }
        };
@@ -138,15 +188,88 @@ public class CitiesActivity extends Activity implements OnCheckedChangeListener,
        public CityAdapter(
                Context context, HashMap<String, CityObj> selectedList, LayoutInflater factory) {
            super();
            loadCitiesDataBase(context);
            loadCities(context);
            mSelectedCitiesList = selectedList;
            mInflater = factory;
            mCalendar = Calendar.getInstance();
            mCalendar.setTimeInMillis(System.currentTimeMillis());
            mSpanColor = context.getResources().getColor(R.color.clock_blue);
            set24HoursMode(context);
        }


        private void loadCities(Context c) {
            mCities = Utils.loadCitiesFromXml(c);
            if (mCities == null) {
                return;
            }

            // Sort alphabetically and populate section headers for sort-by-name
            Arrays.sort(mCities, mSortByNameComparator);
            String val = null;
            ArrayList<String> sections = new ArrayList<String>();
            ArrayList<Integer> positions = new ArrayList<Integer>();
            int count = 0;
            for (CityObj city : mCities) {
                if (city.mCityId.equals(DELETED_ENTRY)) {
                    continue;
                }

                if (!city.mCityName.substring(0, 1).equals(val)) {
                    val = city.mCityName.substring(0, 1).toUpperCase();
                    sections.add(val);
                    positions.add(count);
                    count++;
                }
                count++;
            }
            mSortByNameSectionHeaders = sections.toArray(new String[sections.size()]);
            mSortByNameSectionPositions = positions.toArray(new Integer[positions.size()]);

            // Sort by GMT offset and populate section headers for sort-by-time
            Arrays.sort(mCities, mSortByGmtOffsetComparator);
            int offset = -100000; // some number that cannot be a real offset
            val = null;
            sections.clear();
            positions.clear();
            ArrayList<String> scrollLabels = new ArrayList<String>();
            count = 0;
            for (CityObj city : mCities) {
                if (city.mCityId.equals(DELETED_ENTRY)) {
                    continue;
                }

                TimeZone timezone = TimeZone.getTimeZone(city.mTimeZone);
                int newOffset = timezone.getRawOffset();
                if (newOffset != offset) {
                    offset = newOffset;
                    sections.add(Utils.getGMTHourOffset(timezone, true));
                    positions.add(count);
                    count++;
                }
                count++;
            }
            mSortByTimeSectionHeaders = sections.toArray(new String[sections.size()]);
            mSortByTimeSectionPositions = positions.toArray(new Integer[positions.size()]);

            sortCities(mSortType);
        }

        public void toggleSort() {
            if (mSortType == SORT_BY_NAME) {
                sortCities(SORT_BY_GMT_OFFSET);
            } else {
                sortCities(SORT_BY_NAME);
            }
        }

        private void sortCities(final int sortType) {
            mSortType = sortType;
            Arrays.sort(mCities, sortType == SORT_BY_NAME ? mSortByNameComparator
                    : mSortByGmtOffsetComparator);
            mPrefs.edit().putInt(PREF_SORT, sortType).commit();
            mFilter.filter(mQueryTextBuffer.toString());
        }

        @Override
        public int getCount() {
            return (mDisplayedCitiesList != null) ? mDisplayedCitiesList.size() : 0;
@@ -183,7 +306,7 @@ public class CitiesActivity extends Activity implements OnCheckedChangeListener,
                    view = mInflater.inflate(R.layout.city_list_header, parent, false);
                }
                TextView header = (TextView) view.findViewById(R.id.header);
                header.setText(c.mCityName);
                header.setText(mSortType == SORT_BY_NAME ? c.mCityName : c.mTimeZone);
            } else { // City view
                // Make sure to recycle a City view only
                if (view == null || view.findViewById(R.id.city_name) == null) {
@@ -208,55 +331,27 @@ public class CitiesActivity extends Activity implements OnCheckedChangeListener,
            notifyDataSetChanged();
        }

        private void loadCitiesDataBase(Context c) {
            CityObj[] tempList = Utils.loadCitiesDataBase(c);
            if (tempList == null) {
                return;
            }
            // Create section indexer and add headers to the cities list
            String val = null;
            ArrayList<String> sections = new ArrayList<String>();
            ArrayList<Integer> positions = new ArrayList<Integer>();
            ArrayList<CityObj> items = new ArrayList<CityObj>();
            int count = 0;
            for (int i = 0; i < tempList.length; i++) {
                CityObj city = tempList[i];
                if (city.mCityId.equals(DELETED_ENTRY)) {
                    continue;
                }
                if (!city.mCityName.substring(0, 1).equals(val)) {
                    val = city.mCityName.substring(0, 1).toUpperCase();
                    sections.add(val);
                    positions.add(count);
                    // Add a header
                    items.add(new CityObj(val, null, null));
                    count++;
                }
                items.add(city);
                count++;
            }
            mSectionHeaders = sections.toArray();
            mSectionPositions = positions.toArray();
            mAllTheCitiesList = items;
            mDisplayedCitiesList = items;
        }

        @Override
        public int getPositionForSection(int section) {
            return (mSectionPositions != null) ? (Integer) mSectionPositions[section] : 0;
            Integer[] positions = mSortType == SORT_BY_NAME ? mSortByNameSectionPositions :
                mSortByTimeSectionPositions;
            return (positions != null) ? (Integer) positions[section] : 0;
        }


        @Override
        public int getSectionForPosition(int p) {
            if (mSectionPositions != null) {
                for (int i = 0; i < mSectionPositions.length - 1; i++) {
                    if (p >= (Integer) mSectionPositions[i]
                            && p < (Integer) mSectionPositions[i + 1]) {
            Integer[] positions = mSortType == SORT_BY_NAME ? mSortByNameSectionPositions :
                mSortByTimeSectionPositions;
            if (positions != null) {
                for (int i = 0; i < positions.length - 1; i++) {
                    if (p >= positions[i]
                            && p < positions[i + 1]) {
                        return i;
                    }
                }
                if (p >= (Integer) mSectionPositions[mSectionPositions.length - 1]) {
                    return mSectionPositions.length - 1;
                if (p >= positions[positions.length - 1]) {
                    return positions.length - 1;
                }
            }
            return 0;
@@ -264,7 +359,8 @@ public class CitiesActivity extends Activity implements OnCheckedChangeListener,

        @Override
        public Object[] getSections() {
            return mSectionHeaders;
            return mSortType == SORT_BY_NAME ? mSortByNameSectionHeaders
                    : mSortByTimeSectionHeaders;
        }

        @Override
@@ -277,9 +373,12 @@ public class CitiesActivity extends Activity implements OnCheckedChangeListener,
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mFactory = LayoutInflater.from(this);
        mPrefs = PreferenceManager.getDefaultSharedPreferences(this);
        mSortType = mPrefs.getInt(PREF_SORT, SORT_BY_NAME);
        if (savedInstanceState != null) {
            mQueryTextBuffer.append(savedInstanceState.getString(KEY_SEARCH_QUERY));
            mSearchMode = savedInstanceState.getBoolean(KEY_SEARCH_MODE);
            mPosition = savedInstanceState.getInt(KEY_LIST_POSITION);
        }
        updateLayout();
    }
@@ -289,13 +388,13 @@ public class CitiesActivity extends Activity implements OnCheckedChangeListener,
        super.onSaveInstanceState(bundle);
        bundle.putString(KEY_SEARCH_QUERY, mQueryTextBuffer.toString());
        bundle.putBoolean(KEY_SEARCH_MODE, mSearchMode);
        bundle.putInt(KEY_LIST_POSITION, mCitiesList.getFirstVisiblePosition());
    }

    private void updateLayout() {
        setContentView(R.layout.cities_activity);
        mCitiesList = (ListView) findViewById(R.id.cities_list);
        mCitiesList.setFastScrollAlwaysVisible(true);
        mCitiesList.setFastScrollEnabled(TextUtils.isEmpty(mQueryTextBuffer.toString()));
        mCitiesList.setFastScrollEnabled(TextUtils.isEmpty(mQueryTextBuffer.toString().trim()));
        mCitiesList.setScrollBarStyle(View.SCROLLBARS_INSIDE_INSET);
        mUserSelectedCities = Cities.readCitiesFromSharedPrefs(
                PreferenceManager.getDefaultSharedPreferences(this));
@@ -340,6 +439,13 @@ public class CitiesActivity extends Activity implements OnCheckedChangeListener,
                    }
                }
                return true;
            case R.id.menu_item_sort:
                if (mAdapter != null) {
                    mAdapter.toggleSort();
                    mCitiesList.setFastScrollEnabled(
                            TextUtils.isEmpty(mQueryTextBuffer.toString().trim()));
                }
                return true;
            case android.R.id.home:
                Intent intent = new Intent(this, DeskClock.class);
                intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
@@ -359,8 +465,8 @@ public class CitiesActivity extends Activity implements OnCheckedChangeListener,
            Utils.prepareHelpMenuItem(this, help);
        }

        mSearchMenu = menu.findItem(R.id.menu_item_search);
        mSearchView = (SearchView) mSearchMenu.getActionView();
        MenuItem searchMenu = menu.findItem(R.id.menu_item_search);
        mSearchView = (SearchView) searchMenu.getActionView();
        mSearchView.setImeOptions(EditorInfo.IME_FLAG_NO_EXTRACT_UI);
        mSearchView.setOnSearchClickListener(new OnClickListener() {

@@ -388,6 +494,17 @@ public class CitiesActivity extends Activity implements OnCheckedChangeListener,
        return super.onCreateOptionsMenu(menu);
    }

    @Override
    public boolean onPrepareOptionsMenu(Menu menu) {
        MenuItem sortMenuItem = menu.findItem(R.id.menu_item_sort);
        if (mSortType == SORT_BY_NAME) {
            sortMenuItem.setTitle(getString(R.string.menu_item_sort_by_gmt_offset));
        } else {
            sortMenuItem.setTitle(getString(R.string.menu_item_sort_by_name));
        }
        return super.onPrepareOptionsMenu(menu);
    }

    @Override
    public void onCheckedChanged(CompoundButton b, boolean checked) {
        CityObj c = (CityObj) b.getTag();
@@ -410,7 +527,7 @@ public class CitiesActivity extends Activity implements OnCheckedChangeListener,
    public boolean onQueryTextChange(String queryText) {
        mQueryTextBuffer.setLength(0);
        mQueryTextBuffer.append(queryText);
        mCitiesList.setFastScrollEnabled(TextUtils.isEmpty(queryText.trim()));
        mCitiesList.setFastScrollEnabled(TextUtils.isEmpty(mQueryTextBuffer.toString().trim()));
        mAdapter.getFilter().filter(queryText);
        return true;
    }
Loading