Home | History | Annotate | Download | only in settings
      1 /*
      2  * Copyright (C) 2006 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 
     17 package com.android.settings;
     18 
     19 import android.app.AlarmManager;
     20 import android.app.ListActivity;
     21 import android.content.Context;
     22 import android.content.res.XmlResourceParser;
     23 import android.os.Bundle;
     24 import android.text.TextUtils;
     25 import android.util.Log;
     26 import android.view.Menu;
     27 import android.view.MenuItem;
     28 import android.view.View;
     29 import android.widget.ListAdapter;
     30 import android.widget.ListView;
     31 import android.widget.SimpleAdapter;
     32 
     33 import org.xmlpull.v1.XmlPullParserException;
     34 
     35 import java.util.ArrayList;
     36 import java.util.Arrays;
     37 import java.util.Calendar;
     38 import java.util.Collections;
     39 import java.util.Comparator;
     40 import java.util.HashMap;
     41 import java.util.List;
     42 import java.util.Map;
     43 import java.util.TimeZone;
     44 
     45 /**
     46  * This activity displays a list of time zones that match a filter string
     47  * such as "Africa", "Europe", etc. Choosing an item from the list will set
     48  * the time zone. Pressing Back without choosing from the list will not
     49  * result in a change in the time zone setting.
     50  */
     51 public class ZoneList extends ListActivity {
     52 
     53     private static final String TAG = "ZoneList";
     54     private static final String KEY_ID = "id";
     55     private static final String KEY_DISPLAYNAME = "name";
     56     private static final String KEY_GMT = "gmt";
     57     private static final String KEY_OFFSET = "offset";
     58     private static final String XMLTAG_TIMEZONE = "timezone";
     59 
     60     private static final int HOURS_1 = 60 * 60000;
     61     private static final int HOURS_24 = 24 * HOURS_1;
     62     private static final int HOURS_HALF = HOURS_1 / 2;
     63 
     64     private static final int MENU_TIMEZONE = Menu.FIRST+1;
     65     private static final int MENU_ALPHABETICAL = Menu.FIRST;
     66 
     67     // Initial focus position
     68     private int mDefault;
     69 
     70     private boolean mSortedByTimezone;
     71 
     72     private SimpleAdapter mTimezoneSortedAdapter;
     73     private SimpleAdapter mAlphabeticalAdapter;
     74 
     75     @Override
     76     public void onCreate(Bundle icicle) {
     77         super.onCreate(icicle);
     78 
     79         String[] from = new String[] {KEY_DISPLAYNAME, KEY_GMT};
     80         int[] to = new int[] {android.R.id.text1, android.R.id.text2};
     81 
     82         MyComparator comparator = new MyComparator(KEY_OFFSET);
     83 
     84         List<HashMap> timezoneSortedList = getZones();
     85         Collections.sort(timezoneSortedList, comparator);
     86         mTimezoneSortedAdapter = new SimpleAdapter(this,
     87                 (List) timezoneSortedList,
     88                 android.R.layout.simple_list_item_2,
     89                 from,
     90                 to);
     91 
     92         List<HashMap> alphabeticalList = new ArrayList<HashMap>(timezoneSortedList);
     93         comparator.setSortingKey(KEY_DISPLAYNAME);
     94         Collections.sort(alphabeticalList, comparator);
     95         mAlphabeticalAdapter = new SimpleAdapter(this,
     96                 (List) alphabeticalList,
     97                 android.R.layout.simple_list_item_2,
     98                 from,
     99                 to);
    100 
    101         // Sets the adapter
    102         setSorting(true);
    103 
    104         // If current timezone is in this list, move focus to it
    105         setSelection(mDefault);
    106 
    107         // Assume user may press Back
    108         setResult(RESULT_CANCELED);
    109     }
    110 
    111     @Override
    112     public boolean onCreateOptionsMenu(Menu menu) {
    113         menu.add(0, MENU_ALPHABETICAL, 0, R.string.zone_list_menu_sort_alphabetically)
    114             .setIcon(android.R.drawable.ic_menu_sort_alphabetically);
    115         menu.add(0, MENU_TIMEZONE, 0, R.string.zone_list_menu_sort_by_timezone)
    116             .setIcon(R.drawable.ic_menu_3d_globe);
    117 
    118         return true;
    119     }
    120 
    121     @Override
    122     public boolean onPrepareOptionsMenu(Menu menu) {
    123 
    124         if (mSortedByTimezone) {
    125             menu.findItem(MENU_TIMEZONE).setVisible(false);
    126             menu.findItem(MENU_ALPHABETICAL).setVisible(true);
    127         } else {
    128             menu.findItem(MENU_TIMEZONE).setVisible(true);
    129             menu.findItem(MENU_ALPHABETICAL).setVisible(false);
    130         }
    131 
    132         return true;
    133     }
    134 
    135     @Override
    136     public boolean onOptionsItemSelected(MenuItem item) {
    137         switch (item.getItemId()) {
    138 
    139             case MENU_TIMEZONE:
    140                 setSorting(true);
    141                 return true;
    142 
    143             case MENU_ALPHABETICAL:
    144                 setSorting(false);
    145                 return true;
    146 
    147             default:
    148                 return false;
    149         }
    150     }
    151 
    152     private void setSorting(boolean timezone) {
    153         setListAdapter(timezone ? mTimezoneSortedAdapter : mAlphabeticalAdapter);
    154         mSortedByTimezone = timezone;
    155     }
    156 
    157     private List<HashMap> getZones() {
    158         List<HashMap> myData = new ArrayList<HashMap>();
    159         long date = Calendar.getInstance().getTimeInMillis();
    160         try {
    161             XmlResourceParser xrp = getResources().getXml(R.xml.timezones);
    162             while (xrp.next() != XmlResourceParser.START_TAG)
    163                 continue;
    164             xrp.next();
    165             while (xrp.getEventType() != XmlResourceParser.END_TAG) {
    166                 while (xrp.getEventType() != XmlResourceParser.START_TAG) {
    167                     if (xrp.getEventType() == XmlResourceParser.END_DOCUMENT) {
    168                         return myData;
    169                     }
    170                     xrp.next();
    171                 }
    172                 if (xrp.getName().equals(XMLTAG_TIMEZONE)) {
    173                     String id = xrp.getAttributeValue(0);
    174                     String displayName = xrp.nextText();
    175                     addItem(myData, id, displayName, date);
    176                 }
    177                 while (xrp.getEventType() != XmlResourceParser.END_TAG) {
    178                     xrp.next();
    179                 }
    180                 xrp.next();
    181             }
    182             xrp.close();
    183         } catch (XmlPullParserException xppe) {
    184             Log.e(TAG, "Ill-formatted timezones.xml file");
    185         } catch (java.io.IOException ioe) {
    186             Log.e(TAG, "Unable to read timezones.xml file");
    187         }
    188 
    189         return myData;
    190     }
    191 
    192     protected void addItem(List<HashMap> myData, String id, String displayName,
    193             long date) {
    194         HashMap map = new HashMap();
    195         map.put(KEY_ID, id);
    196         map.put(KEY_DISPLAYNAME, displayName);
    197         TimeZone tz = TimeZone.getTimeZone(id);
    198         int offset = tz.getOffset(date);
    199         int p = Math.abs(offset);
    200         StringBuilder name = new StringBuilder();
    201         name.append("GMT");
    202 
    203         if (offset < 0) {
    204             name.append('-');
    205         } else {
    206             name.append('+');
    207         }
    208 
    209         name.append(p / (HOURS_1));
    210         name.append(':');
    211 
    212         int min = p / 60000;
    213         min %= 60;
    214 
    215         if (min < 10) {
    216             name.append('0');
    217         }
    218         name.append(min);
    219 
    220         map.put(KEY_GMT, name.toString());
    221         map.put(KEY_OFFSET, offset);
    222 
    223         if (id.equals(TimeZone.getDefault().getID())) {
    224             mDefault = myData.size();
    225         }
    226 
    227         myData.add(map);
    228     }
    229 
    230     @Override
    231     protected void onListItemClick(ListView l, View v, int position, long id) {
    232         Map map = (Map) l.getItemAtPosition(position);
    233         // Update the system timezone value
    234         AlarmManager alarm = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
    235         alarm.setTimeZone((String) map.get(KEY_ID));
    236         setResult(RESULT_OK);
    237         finish();
    238     }
    239 
    240     private static class MyComparator implements Comparator<HashMap> {
    241         private String mSortingKey;
    242 
    243         public MyComparator(String sortingKey) {
    244             mSortingKey = sortingKey;
    245         }
    246 
    247         public void setSortingKey(String sortingKey) {
    248             mSortingKey = sortingKey;
    249         }
    250 
    251         public int compare(HashMap map1, HashMap map2) {
    252             Object value1 = map1.get(mSortingKey);
    253             Object value2 = map2.get(mSortingKey);
    254 
    255             /*
    256              * This should never happen, but just in-case, put non-comparable
    257              * items at the end.
    258              */
    259             if (!isComparable(value1)) {
    260                 return isComparable(value2) ? 1 : 0;
    261             } else if (!isComparable(value2)) {
    262                 return -1;
    263             }
    264 
    265             return ((Comparable) value1).compareTo(value2);
    266         }
    267 
    268         private boolean isComparable(Object value) {
    269             return (value != null) && (value instanceof Comparable);
    270         }
    271     }
    272 
    273 }
    274