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