1948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelson/*
2948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelson * Copyright (C) 2012 The Android Open Source Project
3948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelson *
4948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelson * Licensed under the Apache License, Version 2.0 (the "License");
5948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelson * you may not use this file except in compliance with the License.
6948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelson * You may obtain a copy of the License at
7948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelson *
8948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelson *      http://www.apache.org/licenses/LICENSE-2.0
9948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelson *
10948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelson * Unless required by applicable law or agreed to in writing, software
11948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelson * distributed under the License is distributed on an "AS IS" BASIS,
12948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelson * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelson * See the License for the specific language governing permissions and
14948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelson * limitations under the License.
15948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelson */
16948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelson
17948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelsonpackage com.android.deskclock.worldclock;
18948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelson
19948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelsonimport android.content.ActivityNotFoundException;
20948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelsonimport android.content.Context;
21948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelsonimport android.content.Intent;
2262b9f3d89a2be17f70d2fe45c1c94f732e0224a4James Kungimport android.content.SharedPreferences;
23049ea81888a1305b7b1122a0076e1621581f13deBudi Kusmiantoroimport android.media.AudioManager;
24b61894e8535c1a2d9ce3819ddab93675ea62c286Annie Chinimport android.os.Build;
25948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelsonimport android.os.Bundle;
26948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelsonimport android.preference.PreferenceManager;
276c6337ce2b39ffa1395e8039bd739893b41a1b3aBudi Kusmiantoroimport android.support.v4.view.MenuItemCompat;
286c6337ce2b39ffa1395e8039bd739893b41a1b3aBudi Kusmiantoroimport android.support.v7.widget.SearchView;
2925ee07c27a7bab22f3b87e2bd634dcc35029f0c7James Kungimport android.text.TextUtils;
30948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelsonimport android.text.format.DateFormat;
3157cb1dbcead54ff08e8f07e727d0b1c842301440rachelzhangimport android.util.TypedValue;
32948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelsonimport android.view.LayoutInflater;
33948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelsonimport android.view.Menu;
34948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelsonimport android.view.MenuItem;
35948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelsonimport android.view.View;
3625ee07c27a7bab22f3b87e2bd634dcc35029f0c7James Kungimport android.view.View.OnClickListener;
37948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelsonimport android.view.ViewGroup;
3825ee07c27a7bab22f3b87e2bd634dcc35029f0c7James Kungimport android.view.inputmethod.EditorInfo;
39948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelsonimport android.widget.BaseAdapter;
40948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelsonimport android.widget.CheckBox;
41948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelsonimport android.widget.CompoundButton;
42948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelsonimport android.widget.CompoundButton.OnCheckedChangeListener;
4325ee07c27a7bab22f3b87e2bd634dcc35029f0c7James Kungimport android.widget.Filter;
4425ee07c27a7bab22f3b87e2bd634dcc35029f0c7James Kungimport android.widget.Filterable;
45948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelsonimport android.widget.ListView;
46ef08487b5fec499811774e78ac0e556634ebd29aIsaac Katzenelsonimport android.widget.SectionIndexer;
47948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelsonimport android.widget.TextView;
48948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelson
491799dca790bfb9995f779da181c84cac47ee1468Justin Klaassenimport com.android.deskclock.BaseActivity;
50948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelsonimport com.android.deskclock.R;
51948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelsonimport com.android.deskclock.SettingsActivity;
52948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelsonimport com.android.deskclock.Utils;
53948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelson
54948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelsonimport java.util.ArrayList;
5562b9f3d89a2be17f70d2fe45c1c94f732e0224a4James Kungimport java.util.Arrays;
56948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelsonimport java.util.Calendar;
5796ae9779cf470b6502d46b75f606d6445d854bbbJames Kungimport java.util.Collection;
58948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelsonimport java.util.HashMap;
5957cb1dbcead54ff08e8f07e727d0b1c842301440rachelzhangimport java.util.HashSet;
6062b9f3d89a2be17f70d2fe45c1c94f732e0224a4James Kungimport java.util.List;
61db9bd0d88b8e320a331bfca5973e8afe9984b1a7James Kungimport java.util.Locale;
62948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelsonimport java.util.TimeZone;
63948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelson
64948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelson/**
65948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelson * Cities chooser for the world clock
66948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelson */
671799dca790bfb9995f779da181c84cac47ee1468Justin Klaassenpublic class CitiesActivity extends BaseActivity implements OnCheckedChangeListener,
686c6337ce2b39ffa1395e8039bd739893b41a1b3aBudi Kusmiantoro        View.OnClickListener, SearchView.OnQueryTextListener {
69948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelson
7025ee07c27a7bab22f3b87e2bd634dcc35029f0c7James Kung    private static final String KEY_SEARCH_QUERY = "search_query";
7125ee07c27a7bab22f3b87e2bd634dcc35029f0c7James Kung    private static final String KEY_SEARCH_MODE = "search_mode";
7262b9f3d89a2be17f70d2fe45c1c94f732e0224a4James Kung    private static final String KEY_LIST_POSITION = "list_position";
7362b9f3d89a2be17f70d2fe45c1c94f732e0224a4James Kung
7462b9f3d89a2be17f70d2fe45c1c94f732e0224a4James Kung    private static final String PREF_SORT = "sort_preference";
7562b9f3d89a2be17f70d2fe45c1c94f732e0224a4James Kung
7662b9f3d89a2be17f70d2fe45c1c94f732e0224a4James Kung    private static final int SORT_BY_NAME = 0;
7762b9f3d89a2be17f70d2fe45c1c94f732e0224a4James Kung    private static final int SORT_BY_GMT_OFFSET = 1;
7825ee07c27a7bab22f3b87e2bd634dcc35029f0c7James Kung
7925ee07c27a7bab22f3b87e2bd634dcc35029f0c7James Kung    /**
8025ee07c27a7bab22f3b87e2bd634dcc35029f0c7James Kung     * This must be false for production. If true, turns on logging, test code,
8125ee07c27a7bab22f3b87e2bd634dcc35029f0c7James Kung     * etc.
8225ee07c27a7bab22f3b87e2bd634dcc35029f0c7James Kung     */
83948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelson    static final boolean DEBUG = false;
84ef08487b5fec499811774e78ac0e556634ebd29aIsaac Katzenelson    static final String TAG = "CitiesActivity";
85948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelson
86948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelson    private LayoutInflater mFactory;
87948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelson    private ListView mCitiesList;
88948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelson    private CityAdapter mAdapter;
89948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelson    private HashMap<String, CityObj> mUserSelectedCities;
90948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelson    private Calendar mCalendar;
91948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelson
9225ee07c27a7bab22f3b87e2bd634dcc35029f0c7James Kung    private SearchView mSearchView;
9325ee07c27a7bab22f3b87e2bd634dcc35029f0c7James Kung    private StringBuffer mQueryTextBuffer = new StringBuffer();
9425ee07c27a7bab22f3b87e2bd634dcc35029f0c7James Kung    private boolean mSearchMode;
9562b9f3d89a2be17f70d2fe45c1c94f732e0224a4James Kung    private int mPosition = -1;
9662b9f3d89a2be17f70d2fe45c1c94f732e0224a4James Kung
9762b9f3d89a2be17f70d2fe45c1c94f732e0224a4James Kung    private SharedPreferences mPrefs;
9862b9f3d89a2be17f70d2fe45c1c94f732e0224a4James Kung    private int mSortType;
9925ee07c27a7bab22f3b87e2bd634dcc35029f0c7James Kung
10096ae9779cf470b6502d46b75f606d6445d854bbbJames Kung    private String mSelectedCitiesHeaderString;
10196ae9779cf470b6502d46b75f606d6445d854bbbJames Kung
10225ee07c27a7bab22f3b87e2bd634dcc35029f0c7James Kung    /***
10325ee07c27a7bab22f3b87e2bd634dcc35029f0c7James Kung     * Adapter for a list of cities with the respected time zone. The Adapter
10425ee07c27a7bab22f3b87e2bd634dcc35029f0c7James Kung     * sorts the list alphabetically and create an indexer.
10525ee07c27a7bab22f3b87e2bd634dcc35029f0c7James Kung     ***/
10625ee07c27a7bab22f3b87e2bd634dcc35029f0c7James Kung    private class CityAdapter extends BaseAdapter implements Filterable, SectionIndexer {
10796ae9779cf470b6502d46b75f606d6445d854bbbJames Kung        private static final int VIEW_TYPE_CITY = 0;
10896ae9779cf470b6502d46b75f606d6445d854bbbJames Kung        private static final int VIEW_TYPE_HEADER = 1;
10996ae9779cf470b6502d46b75f606d6445d854bbbJames Kung
1108782f6f859307b50754c03619f3ee44992f8cb11Isaac Katzenelson        private static final String DELETED_ENTRY = "C0";
11196ae9779cf470b6502d46b75f606d6445d854bbbJames Kung
11262b9f3d89a2be17f70d2fe45c1c94f732e0224a4James Kung        private List<CityObj> mDisplayedCitiesList;
11362b9f3d89a2be17f70d2fe45c1c94f732e0224a4James Kung
11496ae9779cf470b6502d46b75f606d6445d854bbbJames Kung        private CityObj[] mCities;
11596ae9779cf470b6502d46b75f606d6445d854bbbJames Kung        private CityObj[] mSelectedCities;
11625ee07c27a7bab22f3b87e2bd634dcc35029f0c7James Kung
117cad0b1d4fdb4d0572464986e3f5ab6661445a1aaElliott Hughes        private final int mLayoutDirection;
118db9bd0d88b8e320a331bfca5973e8afe9984b1a7James Kung
119c026745245d7d61f04c30d1b095afb69cf535a02James Kung        // A map that caches names of cities in local memory.  The names in this map are
120c026745245d7d61f04c30d1b095afb69cf535a02James Kung        // preferred over the names of the selected cities stored in SharedPreferences, which could
121c026745245d7d61f04c30d1b095afb69cf535a02James Kung        // be in a different language.  This map gets reloaded on a locale change, when the new
122c026745245d7d61f04c30d1b095afb69cf535a02James Kung        // language's city strings are read from the xml file.
123c026745245d7d61f04c30d1b095afb69cf535a02James Kung        private HashMap<String, String> mCityNameMap = new HashMap<String, String>();
124c026745245d7d61f04c30d1b095afb69cf535a02James Kung
12596ae9779cf470b6502d46b75f606d6445d854bbbJames Kung        private String[] mSectionHeaders;
12696ae9779cf470b6502d46b75f606d6445d854bbbJames Kung        private Integer[] mSectionPositions;
12762b9f3d89a2be17f70d2fe45c1c94f732e0224a4James Kung
12862b9f3d89a2be17f70d2fe45c1c94f732e0224a4James Kung        private CityNameComparator mSortByNameComparator = new CityNameComparator();
12996ae9779cf470b6502d46b75f606d6445d854bbbJames Kung        private CityGmtOffsetComparator mSortByTimeComparator = new CityGmtOffsetComparator();
13025ee07c27a7bab22f3b87e2bd634dcc35029f0c7James Kung
131ef08487b5fec499811774e78ac0e556634ebd29aIsaac Katzenelson        private final LayoutInflater mInflater;
13225ee07c27a7bab22f3b87e2bd634dcc35029f0c7James Kung        private boolean mIs24HoursMode; // AM/PM or 24 hours mode
13325ee07c27a7bab22f3b87e2bd634dcc35029f0c7James Kung
134cad0b1d4fdb4d0572464986e3f5ab6661445a1aaElliott Hughes        private final String mPattern12;
135cad0b1d4fdb4d0572464986e3f5ab6661445a1aaElliott Hughes        private final String mPattern24;
136cad0b1d4fdb4d0572464986e3f5ab6661445a1aaElliott Hughes
137c026745245d7d61f04c30d1b095afb69cf535a02James Kung        private int mSelectedEndPosition = 0;
138b2be9640d79d92d8b9e714bfae3cd444b76104e0James Kung
13925ee07c27a7bab22f3b87e2bd634dcc35029f0c7James Kung        private Filter mFilter = new Filter() {
14025ee07c27a7bab22f3b87e2bd634dcc35029f0c7James Kung
14125ee07c27a7bab22f3b87e2bd634dcc35029f0c7James Kung            @Override
142263c74772f786b43a346d5649df70e15fc12067dJames Kung            protected synchronized FilterResults performFiltering(CharSequence constraint) {
14325ee07c27a7bab22f3b87e2bd634dcc35029f0c7James Kung                FilterResults results = new FilterResults();
14425ee07c27a7bab22f3b87e2bd634dcc35029f0c7James Kung                String modifiedQuery = constraint.toString().trim().toUpperCase();
14525ee07c27a7bab22f3b87e2bd634dcc35029f0c7James Kung
146454a73df9a0ca65e50fee7ded80abda0de8a386bAnnie Chin                ArrayList<CityObj> filteredList = new ArrayList<>();
147454a73df9a0ca65e50fee7ded80abda0de8a386bAnnie Chin                ArrayList<String> sectionHeaders = new ArrayList<>();
148454a73df9a0ca65e50fee7ded80abda0de8a386bAnnie Chin                ArrayList<Integer> sectionPositions = new ArrayList<>();
14996ae9779cf470b6502d46b75f606d6445d854bbbJames Kung
15057cb1dbcead54ff08e8f07e727d0b1c842301440rachelzhang                // Update the list first when user using search filter
15157cb1dbcead54ff08e8f07e727d0b1c842301440rachelzhang                final Collection<CityObj> selectedCities = mUserSelectedCities.values();
15257cb1dbcead54ff08e8f07e727d0b1c842301440rachelzhang                mSelectedCities = selectedCities.toArray(new CityObj[selectedCities.size()]);
15396ae9779cf470b6502d46b75f606d6445d854bbbJames Kung                // If the search query is empty, add in the selected cities
154b10333515a9b3992238c66b1495abd6749f309b2James Kung                if (TextUtils.isEmpty(modifiedQuery) && mSelectedCities != null) {
15596ae9779cf470b6502d46b75f606d6445d854bbbJames Kung                    if (mSelectedCities.length > 0) {
156263c74772f786b43a346d5649df70e15fc12067dJames Kung                        sectionHeaders.add("+");
157263c74772f786b43a346d5649df70e15fc12067dJames Kung                        sectionPositions.add(0);
15896ae9779cf470b6502d46b75f606d6445d854bbbJames Kung                        filteredList.add(new CityObj(mSelectedCitiesHeaderString,
159454a73df9a0ca65e50fee7ded80abda0de8a386bAnnie Chin                                mSelectedCitiesHeaderString, null, null));
16096ae9779cf470b6502d46b75f606d6445d854bbbJames Kung                    }
16196ae9779cf470b6502d46b75f606d6445d854bbbJames Kung                    for (CityObj city : mSelectedCities) {
16257cb1dbcead54ff08e8f07e727d0b1c842301440rachelzhang                        city.isHeader = false;
16396ae9779cf470b6502d46b75f606d6445d854bbbJames Kung                        filteredList.add(city);
16496ae9779cf470b6502d46b75f606d6445d854bbbJames Kung                    }
16596ae9779cf470b6502d46b75f606d6445d854bbbJames Kung                }
16696ae9779cf470b6502d46b75f606d6445d854bbbJames Kung
16757cb1dbcead54ff08e8f07e727d0b1c842301440rachelzhang                final HashSet<String> selectedCityIds = new HashSet<>();
16857cb1dbcead54ff08e8f07e727d0b1c842301440rachelzhang                for (CityObj c : mSelectedCities) {
16957cb1dbcead54ff08e8f07e727d0b1c842301440rachelzhang                    selectedCityIds.add(c.mCityId);
17057cb1dbcead54ff08e8f07e727d0b1c842301440rachelzhang                }
171b2be9640d79d92d8b9e714bfae3cd444b76104e0James Kung                mSelectedEndPosition = filteredList.size();
172b2be9640d79d92d8b9e714bfae3cd444b76104e0James Kung
17375508868c1c02ae1b23969f12106f409a7ea5504James Kung                long currentTime = System.currentTimeMillis();
17496ae9779cf470b6502d46b75f606d6445d854bbbJames Kung                String val = null;
17596ae9779cf470b6502d46b75f606d6445d854bbbJames Kung                int offset = -100000; //some value that cannot be a real offset
17696ae9779cf470b6502d46b75f606d6445d854bbbJames Kung                for (CityObj city : mCities) {
17762b9f3d89a2be17f70d2fe45c1c94f732e0224a4James Kung
17862b9f3d89a2be17f70d2fe45c1c94f732e0224a4James Kung                    // If the city is a deleted entry, ignore it.
17962b9f3d89a2be17f70d2fe45c1c94f732e0224a4James Kung                    if (city.mCityId.equals(DELETED_ENTRY)) {
18062b9f3d89a2be17f70d2fe45c1c94f732e0224a4James Kung                        continue;
18162b9f3d89a2be17f70d2fe45c1c94f732e0224a4James Kung                    }
18262b9f3d89a2be17f70d2fe45c1c94f732e0224a4James Kung
18396ae9779cf470b6502d46b75f606d6445d854bbbJames Kung                    // If the search query is empty, add section headers.
18462b9f3d89a2be17f70d2fe45c1c94f732e0224a4James Kung                    if (TextUtils.isEmpty(modifiedQuery)) {
18557cb1dbcead54ff08e8f07e727d0b1c842301440rachelzhang                        if (!selectedCityIds.contains(city.mCityId)) {
186454a73df9a0ca65e50fee7ded80abda0de8a386bAnnie Chin                            // If the list is sorted by name, and the city has an index
187454a73df9a0ca65e50fee7ded80abda0de8a386bAnnie Chin                            // different than the previous city's index, update the section header.
18857cb1dbcead54ff08e8f07e727d0b1c842301440rachelzhang                            if (mSortType == SORT_BY_NAME
189454a73df9a0ca65e50fee7ded80abda0de8a386bAnnie Chin                                    && !city.mCityIndex.equals(val)) {
190454a73df9a0ca65e50fee7ded80abda0de8a386bAnnie Chin                                val = city.mCityIndex.toUpperCase();
19196ae9779cf470b6502d46b75f606d6445d854bbbJames Kung                                sectionHeaders.add(val);
19296ae9779cf470b6502d46b75f606d6445d854bbbJames Kung                                sectionPositions.add(filteredList.size());
19357cb1dbcead54ff08e8f07e727d0b1c842301440rachelzhang                                city.isHeader = true;
19457cb1dbcead54ff08e8f07e727d0b1c842301440rachelzhang                            } else {
19557cb1dbcead54ff08e8f07e727d0b1c842301440rachelzhang                                city.isHeader = false;
19657cb1dbcead54ff08e8f07e727d0b1c842301440rachelzhang                            }
19762b9f3d89a2be17f70d2fe45c1c94f732e0224a4James Kung
19857cb1dbcead54ff08e8f07e727d0b1c842301440rachelzhang                            // If the list is sorted by time, and the gmt offset is different than
19957cb1dbcead54ff08e8f07e727d0b1c842301440rachelzhang                            // the previous city's gmt offset, insert a section header.
20057cb1dbcead54ff08e8f07e727d0b1c842301440rachelzhang                            if (mSortType == SORT_BY_GMT_OFFSET) {
20157cb1dbcead54ff08e8f07e727d0b1c842301440rachelzhang                                TimeZone timezone = TimeZone.getTimeZone(city.mTimeZone);
20257cb1dbcead54ff08e8f07e727d0b1c842301440rachelzhang                                int newOffset = timezone.getOffset(currentTime);
20357cb1dbcead54ff08e8f07e727d0b1c842301440rachelzhang                                if (offset != newOffset) {
20457cb1dbcead54ff08e8f07e727d0b1c842301440rachelzhang                                    offset = newOffset;
205b61894e8535c1a2d9ce3819ddab93675ea62c286Annie Chin                                    // Because JB fastscroll only supports ~1 char strings
206b61894e8535c1a2d9ce3819ddab93675ea62c286Annie Chin                                    // and KK ellipsizes strings, trim section headers to the
207b61894e8535c1a2d9ce3819ddab93675ea62c286Annie Chin                                    // nearest hour.
208b61894e8535c1a2d9ce3819ddab93675ea62c286Annie Chin                                    final String offsetString = Utils.getGMTHourOffset(timezone,
209b61894e8535c1a2d9ce3819ddab93675ea62c286Annie Chin                                            Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT
210b61894e8535c1a2d9ce3819ddab93675ea62c286Annie Chin                                            /* useShortForm */ );
21157cb1dbcead54ff08e8f07e727d0b1c842301440rachelzhang                                    sectionHeaders.add(offsetString);
21257cb1dbcead54ff08e8f07e727d0b1c842301440rachelzhang                                    sectionPositions.add(filteredList.size());
21357cb1dbcead54ff08e8f07e727d0b1c842301440rachelzhang                                    city.isHeader = true;
21457cb1dbcead54ff08e8f07e727d0b1c842301440rachelzhang                                } else {
21557cb1dbcead54ff08e8f07e727d0b1c842301440rachelzhang                                    city.isHeader = false;
21657cb1dbcead54ff08e8f07e727d0b1c842301440rachelzhang                                }
21796ae9779cf470b6502d46b75f606d6445d854bbbJames Kung                            }
21862b9f3d89a2be17f70d2fe45c1c94f732e0224a4James Kung
21957cb1dbcead54ff08e8f07e727d0b1c842301440rachelzhang                            filteredList.add(city);
22057cb1dbcead54ff08e8f07e727d0b1c842301440rachelzhang                        }
22157cb1dbcead54ff08e8f07e727d0b1c842301440rachelzhang                    } else {
22257cb1dbcead54ff08e8f07e727d0b1c842301440rachelzhang                        // If the city name begins with the non-empty query, add it into the list.
22357cb1dbcead54ff08e8f07e727d0b1c842301440rachelzhang                        String cityName = city.mCityName.trim().toUpperCase();
22457cb1dbcead54ff08e8f07e727d0b1c842301440rachelzhang                        if (city.mCityId != null && cityName.startsWith(modifiedQuery)) {
22557cb1dbcead54ff08e8f07e727d0b1c842301440rachelzhang                            city.isHeader = false;
22657cb1dbcead54ff08e8f07e727d0b1c842301440rachelzhang                            filteredList.add(city);
22757cb1dbcead54ff08e8f07e727d0b1c842301440rachelzhang                        }
22862b9f3d89a2be17f70d2fe45c1c94f732e0224a4James Kung                    }
22925ee07c27a7bab22f3b87e2bd634dcc35029f0c7James Kung                }
23096ae9779cf470b6502d46b75f606d6445d854bbbJames Kung
23196ae9779cf470b6502d46b75f606d6445d854bbbJames Kung                mSectionHeaders = sectionHeaders.toArray(new String[sectionHeaders.size()]);
23296ae9779cf470b6502d46b75f606d6445d854bbbJames Kung                mSectionPositions = sectionPositions.toArray(new Integer[sectionPositions.size()]);
23396ae9779cf470b6502d46b75f606d6445d854bbbJames Kung
23425ee07c27a7bab22f3b87e2bd634dcc35029f0c7James Kung                results.values = filteredList;
23525ee07c27a7bab22f3b87e2bd634dcc35029f0c7James Kung                results.count = filteredList.size();
23625ee07c27a7bab22f3b87e2bd634dcc35029f0c7James Kung                return results;
23725ee07c27a7bab22f3b87e2bd634dcc35029f0c7James Kung            }
23825ee07c27a7bab22f3b87e2bd634dcc35029f0c7James Kung
23925ee07c27a7bab22f3b87e2bd634dcc35029f0c7James Kung            @Override
24025ee07c27a7bab22f3b87e2bd634dcc35029f0c7James Kung            protected void publishResults(CharSequence constraint, FilterResults results) {
24125ee07c27a7bab22f3b87e2bd634dcc35029f0c7James Kung                mDisplayedCitiesList = (ArrayList<CityObj>) results.values;
24262b9f3d89a2be17f70d2fe45c1c94f732e0224a4James Kung                if (mPosition >= 0) {
24362b9f3d89a2be17f70d2fe45c1c94f732e0224a4James Kung                    mCitiesList.setSelectionFromTop(mPosition, 0);
24462b9f3d89a2be17f70d2fe45c1c94f732e0224a4James Kung                    mPosition = -1;
24562b9f3d89a2be17f70d2fe45c1c94f732e0224a4James Kung                }
24625ee07c27a7bab22f3b87e2bd634dcc35029f0c7James Kung                notifyDataSetChanged();
24725ee07c27a7bab22f3b87e2bd634dcc35029f0c7James Kung            }
24825ee07c27a7bab22f3b87e2bd634dcc35029f0c7James Kung        };
249948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelson
250948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelson        public CityAdapter(
25196ae9779cf470b6502d46b75f606d6445d854bbbJames Kung                Context context, LayoutInflater factory) {
252948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelson            super();
253948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelson            mCalendar = Calendar.getInstance();
254948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelson            mCalendar.setTimeInMillis(System.currentTimeMillis());
255c026745245d7d61f04c30d1b095afb69cf535a02James Kung            mLayoutDirection = TextUtils.getLayoutDirectionFromLocale(Locale.getDefault());
256c026745245d7d61f04c30d1b095afb69cf535a02James Kung            mInflater = factory;
257c026745245d7d61f04c30d1b095afb69cf535a02James Kung
258c026745245d7d61f04c30d1b095afb69cf535a02James Kung            // Load the cities from xml.
259c026745245d7d61f04c30d1b095afb69cf535a02James Kung            mCities = Utils.loadCitiesFromXml(context);
260c026745245d7d61f04c30d1b095afb69cf535a02James Kung
261c026745245d7d61f04c30d1b095afb69cf535a02James Kung            // Reload the city name map with the recently parsed city names of the currently
262c026745245d7d61f04c30d1b095afb69cf535a02James Kung            // selected language for use with selected cities.
263c026745245d7d61f04c30d1b095afb69cf535a02James Kung            mCityNameMap.clear();
264c026745245d7d61f04c30d1b095afb69cf535a02James Kung            for (CityObj city : mCities) {
265c026745245d7d61f04c30d1b095afb69cf535a02James Kung                mCityNameMap.put(city.mCityId, city.mCityName);
266c026745245d7d61f04c30d1b095afb69cf535a02James Kung            }
267c026745245d7d61f04c30d1b095afb69cf535a02James Kung
268c026745245d7d61f04c30d1b095afb69cf535a02James Kung            // Re-organize the selected cities into an array.
26996ae9779cf470b6502d46b75f606d6445d854bbbJames Kung            Collection<CityObj> selectedCities = mUserSelectedCities.values();
27096ae9779cf470b6502d46b75f606d6445d854bbbJames Kung            mSelectedCities = selectedCities.toArray(new CityObj[selectedCities.size()]);
271c026745245d7d61f04c30d1b095afb69cf535a02James Kung
272c026745245d7d61f04c30d1b095afb69cf535a02James Kung            // Override the selected city names in the shared preferences with the
273c026745245d7d61f04c30d1b095afb69cf535a02James Kung            // city names in the updated city name map, which will always reflect the
274c026745245d7d61f04c30d1b095afb69cf535a02James Kung            // current language.
275c026745245d7d61f04c30d1b095afb69cf535a02James Kung            for (CityObj city : mSelectedCities) {
276c026745245d7d61f04c30d1b095afb69cf535a02James Kung                String newCityName = mCityNameMap.get(city.mCityId);
277c026745245d7d61f04c30d1b095afb69cf535a02James Kung                if (newCityName != null) {
278c026745245d7d61f04c30d1b095afb69cf535a02James Kung                    city.mCityName = newCityName;
279c026745245d7d61f04c30d1b095afb69cf535a02James Kung                }
280c026745245d7d61f04c30d1b095afb69cf535a02James Kung            }
281c026745245d7d61f04c30d1b095afb69cf535a02James Kung
2827eddc8e6dec60494c00702ea1403682a9c662a45Annie Chin            mPattern24 = Utils.isJBMR2OrLater()
2837eddc8e6dec60494c00702ea1403682a9c662a45Annie Chin                    ? DateFormat.getBestDateTimePattern(Locale.getDefault(), "Hm")
2847eddc8e6dec60494c00702ea1403682a9c662a45Annie Chin                    : getString(R.string.time_format_24_mode);
285cad0b1d4fdb4d0572464986e3f5ab6661445a1aaElliott Hughes
286cad0b1d4fdb4d0572464986e3f5ab6661445a1aaElliott Hughes            // There's an RTL layout bug that causes jank when fast-scrolling through
287cad0b1d4fdb4d0572464986e3f5ab6661445a1aaElliott Hughes            // the list in 12-hour mode in an RTL locale. We can work around this by
288cad0b1d4fdb4d0572464986e3f5ab6661445a1aaElliott Hughes            // ensuring the strings are the same length by using "hh" instead of "h".
2897eddc8e6dec60494c00702ea1403682a9c662a45Annie Chin            String pattern12 = Utils.isJBMR2OrLater()
2907eddc8e6dec60494c00702ea1403682a9c662a45Annie Chin                    ? DateFormat.getBestDateTimePattern(Locale.getDefault(), "hma")
2917eddc8e6dec60494c00702ea1403682a9c662a45Annie Chin                    : getString(R.string.time_format_12_mode);
2927eddc8e6dec60494c00702ea1403682a9c662a45Annie Chin
293cad0b1d4fdb4d0572464986e3f5ab6661445a1aaElliott Hughes            if (mLayoutDirection == View.LAYOUT_DIRECTION_RTL) {
294cad0b1d4fdb4d0572464986e3f5ab6661445a1aaElliott Hughes                pattern12 = pattern12.replaceAll("h", "hh");
295cad0b1d4fdb4d0572464986e3f5ab6661445a1aaElliott Hughes            }
296cad0b1d4fdb4d0572464986e3f5ab6661445a1aaElliott Hughes            mPattern12 = pattern12;
297cad0b1d4fdb4d0572464986e3f5ab6661445a1aaElliott Hughes
298b2be9640d79d92d8b9e714bfae3cd444b76104e0James Kung            sortCities(mSortType);
299948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelson            set24HoursMode(context);
300948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelson        }
301948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelson
30262b9f3d89a2be17f70d2fe45c1c94f732e0224a4James Kung        public void toggleSort() {
30362b9f3d89a2be17f70d2fe45c1c94f732e0224a4James Kung            if (mSortType == SORT_BY_NAME) {
30462b9f3d89a2be17f70d2fe45c1c94f732e0224a4James Kung                sortCities(SORT_BY_GMT_OFFSET);
30562b9f3d89a2be17f70d2fe45c1c94f732e0224a4James Kung            } else {
30662b9f3d89a2be17f70d2fe45c1c94f732e0224a4James Kung                sortCities(SORT_BY_NAME);
30762b9f3d89a2be17f70d2fe45c1c94f732e0224a4James Kung            }
30862b9f3d89a2be17f70d2fe45c1c94f732e0224a4James Kung        }
30962b9f3d89a2be17f70d2fe45c1c94f732e0224a4James Kung
31062b9f3d89a2be17f70d2fe45c1c94f732e0224a4James Kung        private void sortCities(final int sortType) {
31162b9f3d89a2be17f70d2fe45c1c94f732e0224a4James Kung            mSortType = sortType;
31262b9f3d89a2be17f70d2fe45c1c94f732e0224a4James Kung            Arrays.sort(mCities, sortType == SORT_BY_NAME ? mSortByNameComparator
31396ae9779cf470b6502d46b75f606d6445d854bbbJames Kung                    : mSortByTimeComparator);
31496ae9779cf470b6502d46b75f606d6445d854bbbJames Kung            if (mSelectedCities != null) {
31596ae9779cf470b6502d46b75f606d6445d854bbbJames Kung                Arrays.sort(mSelectedCities, sortType == SORT_BY_NAME ? mSortByNameComparator
31696ae9779cf470b6502d46b75f606d6445d854bbbJames Kung                        : mSortByTimeComparator);
31796ae9779cf470b6502d46b75f606d6445d854bbbJames Kung            }
31862b9f3d89a2be17f70d2fe45c1c94f732e0224a4James Kung            mPrefs.edit().putInt(PREF_SORT, sortType).commit();
31962b9f3d89a2be17f70d2fe45c1c94f732e0224a4James Kung            mFilter.filter(mQueryTextBuffer.toString());
32062b9f3d89a2be17f70d2fe45c1c94f732e0224a4James Kung        }
32162b9f3d89a2be17f70d2fe45c1c94f732e0224a4James Kung
322948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelson        @Override
323948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelson        public int getCount() {
32431d063400a4975f2a6eedc70cf679782af762f80James Kung            return mDisplayedCitiesList != null ? mDisplayedCitiesList.size() : 0;
325948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelson        }
326948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelson
327948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelson        @Override
328948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelson        public Object getItem(int p) {
32925ee07c27a7bab22f3b87e2bd634dcc35029f0c7James Kung            if (mDisplayedCitiesList != null && p >= 0 && p < mDisplayedCitiesList.size()) {
33025ee07c27a7bab22f3b87e2bd634dcc35029f0c7James Kung                return mDisplayedCitiesList.get(p);
331948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelson            }
332948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelson            return null;
333948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelson        }
334948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelson
335948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelson        @Override
336948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelson        public long getItemId(int p) {
337948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelson            return p;
338948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelson        }
339948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelson
340948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelson        @Override
341041e2988c31e2387012ba9baf53db011e4057e79Itzhak Katzenelson        public boolean isEnabled(int p) {
34225ee07c27a7bab22f3b87e2bd634dcc35029f0c7James Kung            return mDisplayedCitiesList != null && mDisplayedCitiesList.get(p).mCityId != null;
343041e2988c31e2387012ba9baf53db011e4057e79Itzhak Katzenelson        }
344041e2988c31e2387012ba9baf53db011e4057e79Itzhak Katzenelson
345041e2988c31e2387012ba9baf53db011e4057e79Itzhak Katzenelson        @Override
346b2be9640d79d92d8b9e714bfae3cd444b76104e0James Kung        public synchronized View getView(int position, View view, ViewGroup parent) {
34725ee07c27a7bab22f3b87e2bd634dcc35029f0c7James Kung            if (mDisplayedCitiesList == null || position < 0
34825ee07c27a7bab22f3b87e2bd634dcc35029f0c7James Kung                    || position >= mDisplayedCitiesList.size()) {
349948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelson                return null;
350948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelson            }
35125ee07c27a7bab22f3b87e2bd634dcc35029f0c7James Kung            CityObj c = mDisplayedCitiesList.get(position);
35257cb1dbcead54ff08e8f07e727d0b1c842301440rachelzhang            // Header view: A CityObj with nothing but the "selected cities" label
353e52ef62f28567d7e3ffa0508668d735589c19e52Isaac Katzenelson            if (c.mCityId == null) {
35496ae9779cf470b6502d46b75f606d6445d854bbbJames Kung                if (view == null) {
35525ee07c27a7bab22f3b87e2bd634dcc35029f0c7James Kung                    view = mInflater.inflate(R.layout.city_list_header, parent, false);
356185d5b2028f2043405ed2431a36b6ec6697efa67Isaac Katzenelson                }
357185d5b2028f2043405ed2431a36b6ec6697efa67Isaac Katzenelson            } else { // City view
358185d5b2028f2043405ed2431a36b6ec6697efa67Isaac Katzenelson                // Make sure to recycle a City view only
35996ae9779cf470b6502d46b75f606d6445d854bbbJames Kung                if (view == null) {
360185d5b2028f2043405ed2431a36b6ec6697efa67Isaac Katzenelson                    view = mInflater.inflate(R.layout.city_list_item, parent, false);
361263c74772f786b43a346d5649df70e15fc12067dJames Kung                    final CityViewHolder holder = new CityViewHolder();
36257cb1dbcead54ff08e8f07e727d0b1c842301440rachelzhang                    holder.index = (TextView) view.findViewById(R.id.index);
36396ae9779cf470b6502d46b75f606d6445d854bbbJames Kung                    holder.name = (TextView) view.findViewById(R.id.city_name);
36496ae9779cf470b6502d46b75f606d6445d854bbbJames Kung                    holder.time = (TextView) view.findViewById(R.id.city_time);
36596ae9779cf470b6502d46b75f606d6445d854bbbJames Kung                    holder.selected = (CheckBox) view.findViewById(R.id.city_onoff);
36696ae9779cf470b6502d46b75f606d6445d854bbbJames Kung                    view.setTag(holder);
367185d5b2028f2043405ed2431a36b6ec6697efa67Isaac Katzenelson                }
368041e2988c31e2387012ba9baf53db011e4057e79Itzhak Katzenelson                view.setOnClickListener(CitiesActivity.this);
36996ae9779cf470b6502d46b75f606d6445d854bbbJames Kung                CityViewHolder holder = (CityViewHolder) view.getTag();
370c026745245d7d61f04c30d1b095afb69cf535a02James Kung
37196ae9779cf470b6502d46b75f606d6445d854bbbJames Kung                holder.selected.setTag(c);
37296ae9779cf470b6502d46b75f606d6445d854bbbJames Kung                holder.selected.setChecked(mUserSelectedCities.containsKey(c.mCityId));
37396ae9779cf470b6502d46b75f606d6445d854bbbJames Kung                holder.selected.setOnCheckedChangeListener(CitiesActivity.this);
37496ae9779cf470b6502d46b75f606d6445d854bbbJames Kung                holder.name.setText(c.mCityName, TextView.BufferType.SPANNABLE);
375db9bd0d88b8e320a331bfca5973e8afe9984b1a7James Kung                holder.time.setText(getTimeCharSequence(c.mTimeZone));
37657cb1dbcead54ff08e8f07e727d0b1c842301440rachelzhang                if (c.isHeader) {
37757cb1dbcead54ff08e8f07e727d0b1c842301440rachelzhang                    holder.index.setVisibility(View.VISIBLE);
37857cb1dbcead54ff08e8f07e727d0b1c842301440rachelzhang                    if (mSortType == SORT_BY_NAME) {
379454a73df9a0ca65e50fee7ded80abda0de8a386bAnnie Chin                        holder.index.setText(c.mCityIndex);
38057cb1dbcead54ff08e8f07e727d0b1c842301440rachelzhang                        holder.index.setTextSize(TypedValue.COMPLEX_UNIT_SP, 24);
38157cb1dbcead54ff08e8f07e727d0b1c842301440rachelzhang                    } else { // SORT_BY_GMT_OFFSET
38257cb1dbcead54ff08e8f07e727d0b1c842301440rachelzhang                        holder.index.setTextSize(TypedValue.COMPLEX_UNIT_SP, 14);
38357cb1dbcead54ff08e8f07e727d0b1c842301440rachelzhang                        holder.index.setText(Utils.getGMTHourOffset(
384b61894e8535c1a2d9ce3819ddab93675ea62c286Annie Chin                                TimeZone.getTimeZone(c.mTimeZone), false));
38557cb1dbcead54ff08e8f07e727d0b1c842301440rachelzhang                    }
38657cb1dbcead54ff08e8f07e727d0b1c842301440rachelzhang                } else {
38757cb1dbcead54ff08e8f07e727d0b1c842301440rachelzhang                    // If not a header, use the invisible index for left padding
38857cb1dbcead54ff08e8f07e727d0b1c842301440rachelzhang                    holder.index.setVisibility(View.INVISIBLE);
38957cb1dbcead54ff08e8f07e727d0b1c842301440rachelzhang                }
390cc9031fdd26a7c32b6d5368af69e4e441ac7fa50Budi Kusmiantoro                // skip checkbox and other animations
391cc9031fdd26a7c32b6d5368af69e4e441ac7fa50Budi Kusmiantoro                view.jumpDrawablesToCurrentState();
392e52ef62f28567d7e3ffa0508668d735589c19e52Isaac Katzenelson            }
393948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelson            return view;
394948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelson        }
395948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelson
396db9bd0d88b8e320a331bfca5973e8afe9984b1a7James Kung        private CharSequence getTimeCharSequence(String timeZone) {
397db9bd0d88b8e320a331bfca5973e8afe9984b1a7James Kung            mCalendar.setTimeZone(TimeZone.getTimeZone(timeZone));
398cad0b1d4fdb4d0572464986e3f5ab6661445a1aaElliott Hughes            return DateFormat.format(mIs24HoursMode ? mPattern24 : mPattern12, mCalendar);
399db9bd0d88b8e320a331bfca5973e8afe9984b1a7James Kung        }
400db9bd0d88b8e320a331bfca5973e8afe9984b1a7James Kung
40196ae9779cf470b6502d46b75f606d6445d854bbbJames Kung        @Override
40296ae9779cf470b6502d46b75f606d6445d854bbbJames Kung        public int getViewTypeCount() {
40396ae9779cf470b6502d46b75f606d6445d854bbbJames Kung            return 2;
40496ae9779cf470b6502d46b75f606d6445d854bbbJames Kung        }
40596ae9779cf470b6502d46b75f606d6445d854bbbJames Kung
40696ae9779cf470b6502d46b75f606d6445d854bbbJames Kung        @Override
40796ae9779cf470b6502d46b75f606d6445d854bbbJames Kung        public int getItemViewType(int position) {
40896ae9779cf470b6502d46b75f606d6445d854bbbJames Kung            return (mDisplayedCitiesList.get(position).mCityId != null)
40996ae9779cf470b6502d46b75f606d6445d854bbbJames Kung                    ? VIEW_TYPE_CITY : VIEW_TYPE_HEADER;
41096ae9779cf470b6502d46b75f606d6445d854bbbJames Kung        }
41196ae9779cf470b6502d46b75f606d6445d854bbbJames Kung
41296ae9779cf470b6502d46b75f606d6445d854bbbJames Kung        private class CityViewHolder {
41357cb1dbcead54ff08e8f07e727d0b1c842301440rachelzhang            TextView index;
41496ae9779cf470b6502d46b75f606d6445d854bbbJames Kung            TextView name;
41596ae9779cf470b6502d46b75f606d6445d854bbbJames Kung            TextView time;
41696ae9779cf470b6502d46b75f606d6445d854bbbJames Kung            CheckBox selected;
41796ae9779cf470b6502d46b75f606d6445d854bbbJames Kung        }
41896ae9779cf470b6502d46b75f606d6445d854bbbJames Kung
419948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelson        public void set24HoursMode(Context c) {
420ab0d28c68a77bea2a4a2ee50378da3670a1dd939Paul Sliwowski            mIs24HoursMode = DateFormat.is24HourFormat(c);
421948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelson            notifyDataSetChanged();
422948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelson        }
423ef08487b5fec499811774e78ac0e556634ebd29aIsaac Katzenelson
424ef08487b5fec499811774e78ac0e556634ebd29aIsaac Katzenelson        @Override
425ef08487b5fec499811774e78ac0e556634ebd29aIsaac Katzenelson        public int getPositionForSection(int section) {
42631d063400a4975f2a6eedc70cf679782af762f80James Kung            return !isEmpty(mSectionPositions) ? mSectionPositions[section] : 0;
427ef08487b5fec499811774e78ac0e556634ebd29aIsaac Katzenelson        }
428ef08487b5fec499811774e78ac0e556634ebd29aIsaac Katzenelson
42962b9f3d89a2be17f70d2fe45c1c94f732e0224a4James Kung
430ef08487b5fec499811774e78ac0e556634ebd29aIsaac Katzenelson        @Override
431ef08487b5fec499811774e78ac0e556634ebd29aIsaac Katzenelson        public int getSectionForPosition(int p) {
43296ae9779cf470b6502d46b75f606d6445d854bbbJames Kung            final Integer[] positions = mSectionPositions;
43331d063400a4975f2a6eedc70cf679782af762f80James Kung            if (!isEmpty(positions)) {
43462b9f3d89a2be17f70d2fe45c1c94f732e0224a4James Kung                for (int i = 0; i < positions.length - 1; i++) {
43562b9f3d89a2be17f70d2fe45c1c94f732e0224a4James Kung                    if (p >= positions[i]
43662b9f3d89a2be17f70d2fe45c1c94f732e0224a4James Kung                            && p < positions[i + 1]) {
437d4bbc122b90c63c5906ad32c4652b72c9409fd44Isaac Katzenelson                        return i;
438d4bbc122b90c63c5906ad32c4652b72c9409fd44Isaac Katzenelson                    }
439d4bbc122b90c63c5906ad32c4652b72c9409fd44Isaac Katzenelson                }
44062b9f3d89a2be17f70d2fe45c1c94f732e0224a4James Kung                if (p >= positions[positions.length - 1]) {
44162b9f3d89a2be17f70d2fe45c1c94f732e0224a4James Kung                    return positions.length - 1;
442ef08487b5fec499811774e78ac0e556634ebd29aIsaac Katzenelson                }
443ef08487b5fec499811774e78ac0e556634ebd29aIsaac Katzenelson            }
444ef08487b5fec499811774e78ac0e556634ebd29aIsaac Katzenelson            return 0;
445ef08487b5fec499811774e78ac0e556634ebd29aIsaac Katzenelson        }
446ef08487b5fec499811774e78ac0e556634ebd29aIsaac Katzenelson
447ef08487b5fec499811774e78ac0e556634ebd29aIsaac Katzenelson        @Override
448ef08487b5fec499811774e78ac0e556634ebd29aIsaac Katzenelson        public Object[] getSections() {
44996ae9779cf470b6502d46b75f606d6445d854bbbJames Kung            return mSectionHeaders;
450ef08487b5fec499811774e78ac0e556634ebd29aIsaac Katzenelson        }
451948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelson
45225ee07c27a7bab22f3b87e2bd634dcc35029f0c7James Kung        @Override
45325ee07c27a7bab22f3b87e2bd634dcc35029f0c7James Kung        public Filter getFilter() {
45425ee07c27a7bab22f3b87e2bd634dcc35029f0c7James Kung            return mFilter;
45525ee07c27a7bab22f3b87e2bd634dcc35029f0c7James Kung        }
45631d063400a4975f2a6eedc70cf679782af762f80James Kung
45731d063400a4975f2a6eedc70cf679782af762f80James Kung        private boolean isEmpty(Object[] array) {
45831d063400a4975f2a6eedc70cf679782af762f80James Kung            return array == null || array.length == 0;
45931d063400a4975f2a6eedc70cf679782af762f80James Kung        }
46025ee07c27a7bab22f3b87e2bd634dcc35029f0c7James Kung    }
461948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelson
462948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelson    @Override
46325ee07c27a7bab22f3b87e2bd634dcc35029f0c7James Kung    protected void onCreate(Bundle savedInstanceState) {
46425ee07c27a7bab22f3b87e2bd634dcc35029f0c7James Kung        super.onCreate(savedInstanceState);
465049ea81888a1305b7b1122a0076e1621581f13deBudi Kusmiantoro        setVolumeControlStream(AudioManager.STREAM_ALARM);
466049ea81888a1305b7b1122a0076e1621581f13deBudi Kusmiantoro
467948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelson        mFactory = LayoutInflater.from(this);
46862b9f3d89a2be17f70d2fe45c1c94f732e0224a4James Kung        mPrefs = PreferenceManager.getDefaultSharedPreferences(this);
46962b9f3d89a2be17f70d2fe45c1c94f732e0224a4James Kung        mSortType = mPrefs.getInt(PREF_SORT, SORT_BY_NAME);
47096ae9779cf470b6502d46b75f606d6445d854bbbJames Kung        mSelectedCitiesHeaderString = getString(R.string.selected_cities_label);
47125ee07c27a7bab22f3b87e2bd634dcc35029f0c7James Kung        if (savedInstanceState != null) {
47225ee07c27a7bab22f3b87e2bd634dcc35029f0c7James Kung            mQueryTextBuffer.append(savedInstanceState.getString(KEY_SEARCH_QUERY));
47325ee07c27a7bab22f3b87e2bd634dcc35029f0c7James Kung            mSearchMode = savedInstanceState.getBoolean(KEY_SEARCH_MODE);
47462b9f3d89a2be17f70d2fe45c1c94f732e0224a4James Kung            mPosition = savedInstanceState.getInt(KEY_LIST_POSITION);
47525ee07c27a7bab22f3b87e2bd634dcc35029f0c7James Kung        }
476948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelson        updateLayout();
477948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelson    }
478948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelson
47925ee07c27a7bab22f3b87e2bd634dcc35029f0c7James Kung    @Override
48025ee07c27a7bab22f3b87e2bd634dcc35029f0c7James Kung    public void onSaveInstanceState(Bundle bundle) {
48125ee07c27a7bab22f3b87e2bd634dcc35029f0c7James Kung        super.onSaveInstanceState(bundle);
48225ee07c27a7bab22f3b87e2bd634dcc35029f0c7James Kung        bundle.putString(KEY_SEARCH_QUERY, mQueryTextBuffer.toString());
48325ee07c27a7bab22f3b87e2bd634dcc35029f0c7James Kung        bundle.putBoolean(KEY_SEARCH_MODE, mSearchMode);
48462b9f3d89a2be17f70d2fe45c1c94f732e0224a4James Kung        bundle.putInt(KEY_LIST_POSITION, mCitiesList.getFirstVisiblePosition());
48525ee07c27a7bab22f3b87e2bd634dcc35029f0c7James Kung    }
48625ee07c27a7bab22f3b87e2bd634dcc35029f0c7James Kung
487948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelson    private void updateLayout() {
488948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelson        setContentView(R.layout.cities_activity);
489948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelson        mCitiesList = (ListView) findViewById(R.id.cities_list);
490b2be9640d79d92d8b9e714bfae3cd444b76104e0James Kung        setFastScroll(TextUtils.isEmpty(mQueryTextBuffer.toString().trim()));
4918626fb870b24a4cf4dc4bf2d76ade8a8d80b602aIsaac Katzenelson        mCitiesList.setScrollBarStyle(View.SCROLLBARS_INSIDE_INSET);
492948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelson        mUserSelectedCities = Cities.readCitiesFromSharedPrefs(
493948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelson                PreferenceManager.getDefaultSharedPreferences(this));
49496ae9779cf470b6502d46b75f606d6445d854bbbJames Kung        mAdapter = new CityAdapter(this, mFactory);
495948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelson        mCitiesList.setAdapter(mAdapter);
496948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelson    }
497948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelson
498b2be9640d79d92d8b9e714bfae3cd444b76104e0James Kung    private void setFastScroll(boolean enabled) {
499b2be9640d79d92d8b9e714bfae3cd444b76104e0James Kung        if (mCitiesList != null) {
500b2be9640d79d92d8b9e714bfae3cd444b76104e0James Kung            mCitiesList.setFastScrollAlwaysVisible(enabled);
501b2be9640d79d92d8b9e714bfae3cd444b76104e0James Kung            mCitiesList.setFastScrollEnabled(enabled);
502b2be9640d79d92d8b9e714bfae3cd444b76104e0James Kung        }
503b2be9640d79d92d8b9e714bfae3cd444b76104e0James Kung    }
504b2be9640d79d92d8b9e714bfae3cd444b76104e0James Kung
505948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelson    @Override
506948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelson    public void onResume() {
507948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelson        super.onResume();
508948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelson        if (mAdapter != null) {
509948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelson            mAdapter.set24HoursMode(this);
510948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelson        }
511948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelson    }
512948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelson
513948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelson    @Override
514948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelson    public void onPause() {
515948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelson        super.onPause();
516948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelson        Cities.saveCitiesToSharedPrefs(PreferenceManager.getDefaultSharedPreferences(this),
517948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelson                mUserSelectedCities);
5187048625adb7c349deab4c00b2d85bcf65c164ae5Isaac Katzenelson        Intent i = new Intent(Cities.WORLDCLOCK_UPDATE_INTENT);
5197048625adb7c349deab4c00b2d85bcf65c164ae5Isaac Katzenelson        sendBroadcast(i);
520948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelson    }
521948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelson
522948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelson    @Override
523948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelson    public boolean onOptionsItemSelected(MenuItem item) {
524948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelson        switch (item.getItemId()) {
525657450878af559e8c0d39fae8f4606c1968fcae8Budi Kusmiantoro            case android.R.id.home:
526657450878af559e8c0d39fae8f4606c1968fcae8Budi Kusmiantoro                finish();
527657450878af559e8c0d39fae8f4606c1968fcae8Budi Kusmiantoro                return true;
528948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelson            case R.id.menu_item_settings:
529948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelson                startActivity(new Intent(this, SettingsActivity.class));
530948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelson                return true;
531948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelson            case R.id.menu_item_help:
532948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelson                Intent i = item.getIntent();
533948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelson                if (i != null) {
534948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelson                    try {
535948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelson                        startActivity(i);
536948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelson                    } catch (ActivityNotFoundException e) {
537948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelson                        // No activity found to match the intent - ignore
538948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelson                    }
539948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelson                }
540948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelson                return true;
54162b9f3d89a2be17f70d2fe45c1c94f732e0224a4James Kung            case R.id.menu_item_sort:
54262b9f3d89a2be17f70d2fe45c1c94f732e0224a4James Kung                if (mAdapter != null) {
54362b9f3d89a2be17f70d2fe45c1c94f732e0224a4James Kung                    mAdapter.toggleSort();
544b2be9640d79d92d8b9e714bfae3cd444b76104e0James Kung                    setFastScroll(TextUtils.isEmpty(mQueryTextBuffer.toString().trim()));
54562b9f3d89a2be17f70d2fe45c1c94f732e0224a4James Kung                }
54662b9f3d89a2be17f70d2fe45c1c94f732e0224a4James Kung                return true;
547948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelson            default:
548948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelson                break;
549948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelson        }
550948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelson        return super.onOptionsItemSelected(item);
551948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelson    }
552948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelson
553948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelson    @Override
554948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelson    public boolean onCreateOptionsMenu(Menu menu) {
555948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelson        getMenuInflater().inflate(R.menu.cities_menu, menu);
556948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelson        MenuItem help = menu.findItem(R.id.menu_item_help);
557948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelson        if (help != null) {
558948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelson            Utils.prepareHelpMenuItem(this, help);
559948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelson        }
56025ee07c27a7bab22f3b87e2bd634dcc35029f0c7James Kung
56162b9f3d89a2be17f70d2fe45c1c94f732e0224a4James Kung        MenuItem searchMenu = menu.findItem(R.id.menu_item_search);
5626c6337ce2b39ffa1395e8039bd739893b41a1b3aBudi Kusmiantoro        mSearchView = (SearchView) MenuItemCompat.getActionView(searchMenu);
56325ee07c27a7bab22f3b87e2bd634dcc35029f0c7James Kung        mSearchView.setImeOptions(EditorInfo.IME_FLAG_NO_EXTRACT_UI);
56425ee07c27a7bab22f3b87e2bd634dcc35029f0c7James Kung        mSearchView.setOnSearchClickListener(new OnClickListener() {
56525ee07c27a7bab22f3b87e2bd634dcc35029f0c7James Kung
56625ee07c27a7bab22f3b87e2bd634dcc35029f0c7James Kung            @Override
56725ee07c27a7bab22f3b87e2bd634dcc35029f0c7James Kung            public void onClick(View arg0) {
56825ee07c27a7bab22f3b87e2bd634dcc35029f0c7James Kung                mSearchMode = true;
56925ee07c27a7bab22f3b87e2bd634dcc35029f0c7James Kung            }
57025ee07c27a7bab22f3b87e2bd634dcc35029f0c7James Kung        });
57125ee07c27a7bab22f3b87e2bd634dcc35029f0c7James Kung        mSearchView.setOnCloseListener(new SearchView.OnCloseListener() {
57225ee07c27a7bab22f3b87e2bd634dcc35029f0c7James Kung
57325ee07c27a7bab22f3b87e2bd634dcc35029f0c7James Kung            @Override
57425ee07c27a7bab22f3b87e2bd634dcc35029f0c7James Kung            public boolean onClose() {
57525ee07c27a7bab22f3b87e2bd634dcc35029f0c7James Kung                mSearchMode = false;
57625ee07c27a7bab22f3b87e2bd634dcc35029f0c7James Kung                return false;
57725ee07c27a7bab22f3b87e2bd634dcc35029f0c7James Kung            }
57825ee07c27a7bab22f3b87e2bd634dcc35029f0c7James Kung        });
57925ee07c27a7bab22f3b87e2bd634dcc35029f0c7James Kung        if (mSearchView != null) {
58025ee07c27a7bab22f3b87e2bd634dcc35029f0c7James Kung            mSearchView.setOnQueryTextListener(this);
58125ee07c27a7bab22f3b87e2bd634dcc35029f0c7James Kung            mSearchView.setQuery(mQueryTextBuffer.toString(), false);
58225ee07c27a7bab22f3b87e2bd634dcc35029f0c7James Kung            if (mSearchMode) {
58325ee07c27a7bab22f3b87e2bd634dcc35029f0c7James Kung                mSearchView.requestFocus();
58425ee07c27a7bab22f3b87e2bd634dcc35029f0c7James Kung                mSearchView.setIconified(false);
58525ee07c27a7bab22f3b87e2bd634dcc35029f0c7James Kung            }
58625ee07c27a7bab22f3b87e2bd634dcc35029f0c7James Kung        }
587948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelson        return super.onCreateOptionsMenu(menu);
588948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelson    }
589948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelson
590948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelson    @Override
59162b9f3d89a2be17f70d2fe45c1c94f732e0224a4James Kung    public boolean onPrepareOptionsMenu(Menu menu) {
59262b9f3d89a2be17f70d2fe45c1c94f732e0224a4James Kung        MenuItem sortMenuItem = menu.findItem(R.id.menu_item_sort);
59362b9f3d89a2be17f70d2fe45c1c94f732e0224a4James Kung        if (mSortType == SORT_BY_NAME) {
59462b9f3d89a2be17f70d2fe45c1c94f732e0224a4James Kung            sortMenuItem.setTitle(getString(R.string.menu_item_sort_by_gmt_offset));
59562b9f3d89a2be17f70d2fe45c1c94f732e0224a4James Kung        } else {
59662b9f3d89a2be17f70d2fe45c1c94f732e0224a4James Kung            sortMenuItem.setTitle(getString(R.string.menu_item_sort_by_name));
59762b9f3d89a2be17f70d2fe45c1c94f732e0224a4James Kung        }
59862b9f3d89a2be17f70d2fe45c1c94f732e0224a4James Kung        return super.onPrepareOptionsMenu(menu);
59962b9f3d89a2be17f70d2fe45c1c94f732e0224a4James Kung    }
60062b9f3d89a2be17f70d2fe45c1c94f732e0224a4James Kung
60162b9f3d89a2be17f70d2fe45c1c94f732e0224a4James Kung    @Override
602948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelson    public void onCheckedChanged(CompoundButton b, boolean checked) {
60325ee07c27a7bab22f3b87e2bd634dcc35029f0c7James Kung        CityObj c = (CityObj) b.getTag();
604948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelson        if (checked) {
605ef08487b5fec499811774e78ac0e556634ebd29aIsaac Katzenelson            mUserSelectedCities.put(c.mCityId, c);
606948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelson        } else {
607ef08487b5fec499811774e78ac0e556634ebd29aIsaac Katzenelson            mUserSelectedCities.remove(c.mCityId);
608948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelson        }
609948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelson    }
610041e2988c31e2387012ba9baf53db011e4057e79Itzhak Katzenelson
611041e2988c31e2387012ba9baf53db011e4057e79Itzhak Katzenelson    @Override
612041e2988c31e2387012ba9baf53db011e4057e79Itzhak Katzenelson    public void onClick(View v) {
61325ee07c27a7bab22f3b87e2bd634dcc35029f0c7James Kung        CompoundButton b = (CompoundButton) v.findViewById(R.id.city_onoff);
614041e2988c31e2387012ba9baf53db011e4057e79Itzhak Katzenelson        boolean checked = b.isChecked();
615041e2988c31e2387012ba9baf53db011e4057e79Itzhak Katzenelson        onCheckedChanged(b, checked);
616041e2988c31e2387012ba9baf53db011e4057e79Itzhak Katzenelson        b.setChecked(!checked);
617041e2988c31e2387012ba9baf53db011e4057e79Itzhak Katzenelson    }
61825ee07c27a7bab22f3b87e2bd634dcc35029f0c7James Kung
61925ee07c27a7bab22f3b87e2bd634dcc35029f0c7James Kung    @Override
62025ee07c27a7bab22f3b87e2bd634dcc35029f0c7James Kung    public boolean onQueryTextChange(String queryText) {
62125ee07c27a7bab22f3b87e2bd634dcc35029f0c7James Kung        mQueryTextBuffer.setLength(0);
62225ee07c27a7bab22f3b87e2bd634dcc35029f0c7James Kung        mQueryTextBuffer.append(queryText);
62362b9f3d89a2be17f70d2fe45c1c94f732e0224a4James Kung        mCitiesList.setFastScrollEnabled(TextUtils.isEmpty(mQueryTextBuffer.toString().trim()));
62425ee07c27a7bab22f3b87e2bd634dcc35029f0c7James Kung        mAdapter.getFilter().filter(queryText);
62525ee07c27a7bab22f3b87e2bd634dcc35029f0c7James Kung        return true;
62625ee07c27a7bab22f3b87e2bd634dcc35029f0c7James Kung    }
62725ee07c27a7bab22f3b87e2bd634dcc35029f0c7James Kung
62825ee07c27a7bab22f3b87e2bd634dcc35029f0c7James Kung    @Override
62925ee07c27a7bab22f3b87e2bd634dcc35029f0c7James Kung    public boolean onQueryTextSubmit(String arg0) {
63025ee07c27a7bab22f3b87e2bd634dcc35029f0c7James Kung        return false;
63125ee07c27a7bab22f3b87e2bd634dcc35029f0c7James Kung    }
632948edb548f26c6aef3d918d01d80ea37570da41dIsaac Katzenelson}
633