1/* 2 * Copyright (C) 2012 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.deskclock.worldclock; 18 19import android.app.ActionBar; 20import android.app.Activity; 21import android.content.ActivityNotFoundException; 22import android.content.Context; 23import android.content.Intent; 24import android.content.res.Resources; 25import android.os.Bundle; 26import android.preference.PreferenceManager; 27import android.text.format.DateFormat; 28import android.view.LayoutInflater; 29import android.view.Menu; 30import android.view.MenuItem; 31import android.view.View; 32import android.view.ViewGroup; 33import android.widget.BaseAdapter; 34import android.widget.CheckBox; 35import android.widget.CompoundButton; 36import android.widget.CompoundButton.OnCheckedChangeListener; 37import android.widget.ListView; 38import android.widget.SectionIndexer; 39import android.widget.TextView; 40 41import com.android.deskclock.Alarms; 42import com.android.deskclock.DeskClock; 43import com.android.deskclock.Log; 44import com.android.deskclock.R; 45import com.android.deskclock.SettingsActivity; 46import com.android.deskclock.Utils; 47 48import java.text.Collator; 49import java.util.ArrayList; 50import java.util.Arrays; 51import java.util.Calendar; 52import java.util.Comparator; 53import java.util.HashMap; 54import java.util.TimeZone; 55 56/** 57 * Cities chooser for the world clock 58 */ 59public class CitiesActivity extends Activity implements OnCheckedChangeListener, View.OnClickListener { 60 61 /** This must be false for production. If true, turns on logging, 62 test code, etc. */ 63 static final boolean DEBUG = false; 64 static final String TAG = "CitiesActivity"; 65 66 private LayoutInflater mFactory; 67 private ListView mCitiesList; 68 private CityAdapter mAdapter; 69 private HashMap<String, CityObj> mUserSelectedCities; 70 private Calendar mCalendar; 71 private final Collator mCollator = Collator.getInstance(); 72 73 74/*** 75* Adapter for a list of cities with the respected time zone. 76* The Adapter sorts the list alphabetically and create an indexer. 77***/ 78 79 private class CityAdapter extends BaseAdapter implements SectionIndexer { 80 private static final String DELETED_ENTRY = "C0"; 81 private Object [] mAllTheCitiesList; // full list of the cities 82 private final HashMap<String, CityObj> mSelectedCitiesList; // Selected cities by the use 83 private final LayoutInflater mInflater; 84 private boolean mIs24HoursMode; // AM/PM or 24 hours mode 85 private Object [] mSectionHeaders; 86 private Object [] mSectionPositions; 87 88 public CityAdapter( 89 Context context, HashMap<String, CityObj> selectedList, LayoutInflater factory) { 90 super(); 91 loadCitiesDataBase(context); 92 mSelectedCitiesList = selectedList; 93 mInflater = factory; 94 mCalendar = Calendar.getInstance(); 95 mCalendar.setTimeInMillis(System.currentTimeMillis()); 96 set24HoursMode(context); 97 } 98 99 @Override 100 public int getCount() { 101 return mAllTheCitiesList.length; 102 } 103 104 @Override 105 public Object getItem(int p) { 106 if (p >=0 && p < mAllTheCitiesList.length) { 107 return mAllTheCitiesList [p]; 108 } 109 return null; 110 } 111 112 @Override 113 public long getItemId(int p) { 114 return p; 115 } 116 117 @Override 118 public boolean isEnabled(int p) { 119 return ((CityObj)mAllTheCitiesList[p]).mCityId != null; 120 } 121 122 @Override 123 public View getView(int position, View view, ViewGroup parent) { 124 if (position < 0 || position >= mAllTheCitiesList.length) { 125 return null; 126 } 127 CityObj c = (CityObj)mAllTheCitiesList [position]; 128 // Header view (A CityObj with nothing but the first letter as the name 129 if (c.mCityId == null) { 130 if (view == null || view.findViewById(R.id.header) == null) { 131 view = mInflater.inflate(R.layout.city_list_header, parent, false); 132 } 133 TextView header = (TextView)view.findViewById(R.id.header); 134 header.setText(c.mCityName); 135 } else { // City view 136 // Make sure to recycle a City view only 137 if (view == null || view.findViewById(R.id.city_name) == null) { 138 view = mInflater.inflate(R.layout.city_list_item, parent, false); 139 } 140 view.setOnClickListener(CitiesActivity.this); 141 TextView name = (TextView)view.findViewById(R.id.city_name); 142 TextView tz = (TextView)view.findViewById(R.id.city_time); 143 CheckBox cb = (CheckBox)view.findViewById(R.id.city_onoff); 144 cb.setTag(c); 145 cb.setChecked(mSelectedCitiesList.containsKey(c.mCityId)); 146 cb.setOnCheckedChangeListener(CitiesActivity.this); 147 mCalendar.setTimeZone(TimeZone.getTimeZone(c.mTimeZone)); 148 tz.setText(DateFormat.format(mIs24HoursMode ? "k:mm" : "h:mmaa", mCalendar)); 149 name.setText(c.mCityName); 150 } 151 return view; 152 } 153 154 public void set24HoursMode(Context c) { 155 mIs24HoursMode = Alarms.get24HourMode(c); 156 notifyDataSetChanged(); 157 } 158 159 private void loadCitiesDataBase(Context c) { 160 Resources r = c.getResources(); 161 // Read strings array of name,timezone, id 162 // make sure the list are the same length 163 String [] cities = r.getStringArray(R.array.cities_names); 164 String [] timezones = r.getStringArray(R.array.cities_tz); 165 String [] ids = r.getStringArray(R.array.cities_id); 166 if (cities.length != timezones.length || ids.length != cities.length) { 167 Log.wtf("City lists sizes are not the same, cannot use the data"); 168 return; 169 } 170 CityObj[] tempList = new CityObj [cities.length]; 171 for (int i = 0; i < cities.length; i++) { 172 tempList[i] = new CityObj(cities[i], timezones[i], ids[i]); 173 } 174 // Sort alphabetically 175 Arrays.sort(tempList, new Comparator<CityObj> () { 176 @Override 177 public int compare(CityObj c1, CityObj c2) { 178 return mCollator.compare(c1.mCityName, c2.mCityName); 179 } 180 }); 181 //Create section indexer and add headers to the cities list 182 String val = null; 183 ArrayList<String> sections = new ArrayList<String> (); 184 ArrayList<Integer> positions = new ArrayList<Integer> (); 185 ArrayList<CityObj> items = new ArrayList<CityObj>(); 186 int count = 0; 187 for (int i = 0; i < tempList.length; i++) { 188 CityObj city = tempList[i]; 189 if (city.mCityId.equals(DELETED_ENTRY)) { 190 continue; 191 } 192 if (!city.mCityName.substring(0, 1).equals(val)) { 193 val = city.mCityName.substring(0, 1); 194 sections.add((new String(val)).toUpperCase()); 195 positions.add(count); 196 // Add a header 197 items.add(new CityObj(val, null, null)); 198 count++; 199 } 200 items.add(city); 201 count++; 202 } 203 mSectionHeaders = sections.toArray(); 204 mSectionPositions = positions.toArray(); 205 mAllTheCitiesList = items.toArray(); 206 } 207 208 @Override 209 public int getPositionForSection(int section) { 210 return (Integer) mSectionPositions[section]; 211 } 212 213 @Override 214 public int getSectionForPosition(int p) { 215 for (int i = 0; i < mSectionPositions.length - 1; i++) { 216 if (p >= (Integer)mSectionPositions[i] && p < (Integer)mSectionPositions[i + 1]) { 217 return i; 218 } 219 } 220 if (p >= (Integer)mSectionPositions[mSectionPositions.length - 1]) { 221 return mSectionPositions.length - 1; 222 } 223 return 0; 224 } 225 226 @Override 227 public Object[] getSections() { 228 return mSectionHeaders; 229 } 230 } 231 232 233 @Override 234 protected void onCreate(Bundle icicle) { 235 super.onCreate(icicle); 236 mFactory = LayoutInflater.from(this); 237 updateLayout(); 238 } 239 240 private void updateLayout() { 241 setContentView(R.layout.cities_activity); 242 mCitiesList = (ListView) findViewById(R.id.cities_list); 243 mCitiesList.setFastScrollAlwaysVisible(true); 244 mCitiesList.setScrollBarStyle(View.SCROLLBARS_INSIDE_INSET); 245 mCitiesList.setFastScrollEnabled(true); 246 mUserSelectedCities = Cities.readCitiesFromSharedPrefs( 247 PreferenceManager.getDefaultSharedPreferences(this)); 248 mAdapter = new CityAdapter(this, mUserSelectedCities, mFactory); 249 mCitiesList.setAdapter(mAdapter); 250 ActionBar actionBar = getActionBar(); 251 if (actionBar != null) { 252 actionBar.setDisplayOptions(ActionBar.DISPLAY_HOME_AS_UP, ActionBar.DISPLAY_HOME_AS_UP); 253 } 254 } 255 256 @Override 257 public void onResume() { 258 super.onResume(); 259 if (mAdapter != null) { 260 mAdapter.set24HoursMode(this); 261 } 262 } 263 264 265 @Override 266 public void onPause() { 267 super.onPause(); 268 Cities.saveCitiesToSharedPrefs(PreferenceManager.getDefaultSharedPreferences(this), 269 mUserSelectedCities); 270 Intent i = new Intent(Cities.WORLDCLOCK_UPDATE_INTENT); 271 sendBroadcast(i); 272 } 273 274 @Override 275 public boolean onOptionsItemSelected(MenuItem item) { 276 switch (item.getItemId()) { 277 case R.id.menu_item_settings: 278 startActivity(new Intent(this, SettingsActivity.class)); 279 return true; 280 case R.id.menu_item_help: 281 Intent i = item.getIntent(); 282 if (i != null) { 283 try { 284 startActivity(i); 285 } catch (ActivityNotFoundException e) { 286 // No activity found to match the intent - ignore 287 } 288 } 289 return true; 290 case android.R.id.home: 291 Intent intent = new Intent(this, DeskClock.class); 292 intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP); 293 startActivity(intent); 294 return true; 295 default: 296 break; 297 } 298 return super.onOptionsItemSelected(item); 299 } 300 301 @Override 302 public boolean onCreateOptionsMenu(Menu menu) { 303 getMenuInflater().inflate(R.menu.cities_menu, menu); 304 MenuItem help = menu.findItem(R.id.menu_item_help); 305 if (help != null) { 306 Utils.prepareHelpMenuItem(this, help); 307 } 308 return super.onCreateOptionsMenu(menu); 309 } 310 311 @Override 312 public void onCheckedChanged(CompoundButton b, boolean checked) { 313 CityObj c = (CityObj)b.getTag(); 314 if (checked) { 315 mUserSelectedCities.put(c.mCityId, c); 316 } else { 317 mUserSelectedCities.remove(c.mCityId); 318 } 319 } 320 321 @Override 322 public void onClick(View v) { 323 CompoundButton b = (CompoundButton)v.findViewById(R.id.city_onoff); 324 boolean checked = b.isChecked(); 325 onCheckedChanged(b, checked); 326 b.setChecked(!checked); 327 } 328} 329