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.annotation.NonNull;
     20 import android.app.Activity;
     21 import android.app.AlarmManager;
     22 import android.app.ListFragment;
     23 import android.content.Context;
     24 import android.os.Bundle;
     25 import android.view.LayoutInflater;
     26 import android.view.Menu;
     27 import android.view.MenuInflater;
     28 import android.view.MenuItem;
     29 import android.view.View;
     30 import android.view.ViewGroup;
     31 import android.widget.ListView;
     32 import android.widget.SimpleAdapter;
     33 
     34 import com.android.settingslib.datetime.ZoneGetter;
     35 
     36 import java.util.Collections;
     37 import java.util.Comparator;
     38 import java.util.HashMap;
     39 import java.util.List;
     40 import java.util.Map;
     41 import java.util.TimeZone;
     42 
     43 /**
     44  * The class displaying a list of time zones that match a filter string
     45  * such as "Africa", "Europe", etc. Choosing an item from the list will set
     46  * the time zone. Pressing Back without choosing from the list will not
     47  * result in a change in the time zone setting.
     48  */
     49 public class ZonePicker extends ListFragment {
     50     private static final String TAG = "ZonePicker";
     51 
     52     public interface ZoneSelectionListener {
     53         // You can add any argument if you really need it...
     54         void onZoneSelected(TimeZone tz);
     55     }
     56 
     57     private static final int MENU_TIMEZONE = Menu.FIRST+1;
     58     private static final int MENU_ALPHABETICAL = Menu.FIRST;
     59 
     60     private boolean mSortedByTimezone;
     61 
     62     private SimpleAdapter mTimezoneSortedAdapter;
     63     private SimpleAdapter mAlphabeticalAdapter;
     64 
     65     private ZoneSelectionListener mListener;
     66 
     67     /**
     68      * Constructs an adapter with TimeZone list. Sorted by TimeZone in default.
     69      *
     70      * @param sortedByName use Name for sorting the list.
     71      */
     72     public static SimpleAdapter constructTimezoneAdapter(Context context,
     73             boolean sortedByName) {
     74         return constructTimezoneAdapter(context, sortedByName,
     75                 R.layout.date_time_setup_custom_list_item_2);
     76     }
     77 
     78     /**
     79      * Constructs an adapter with TimeZone list. Sorted by TimeZone in default.
     80      *
     81      * @param sortedByName use Name for sorting the list.
     82      */
     83     public static SimpleAdapter constructTimezoneAdapter(Context context,
     84             boolean sortedByName, int layoutId) {
     85         final String[] from = new String[] {ZoneGetter.KEY_DISPLAYNAME, ZoneGetter.KEY_GMT};
     86         final int[] to = new int[] {android.R.id.text1, android.R.id.text2};
     87 
     88         final String sortKey = (sortedByName ? ZoneGetter.KEY_DISPLAYNAME : ZoneGetter.KEY_OFFSET);
     89         final MyComparator comparator = new MyComparator(sortKey);
     90         final List<Map<String, Object>> sortedList = ZoneGetter.getZonesList(context);
     91         Collections.sort(sortedList, comparator);
     92         final SimpleAdapter adapter = new SimpleAdapter(context,
     93                 sortedList,
     94                 layoutId,
     95                 from,
     96                 to);
     97 
     98         return adapter;
     99     }
    100 
    101     /**
    102      * Searches {@link TimeZone} from the given {@link SimpleAdapter} object, and returns
    103      * the index for the TimeZone.
    104      *
    105      * @param adapter SimpleAdapter constructed by
    106      * {@link #constructTimezoneAdapter(Context, boolean)}.
    107      * @param tz TimeZone to be searched.
    108      * @return Index for the given TimeZone. -1 when there's no corresponding list item.
    109      * returned.
    110      */
    111     public static int getTimeZoneIndex(SimpleAdapter adapter, TimeZone tz) {
    112         final String defaultId = tz.getID();
    113         final int listSize = adapter.getCount();
    114         for (int i = 0; i < listSize; i++) {
    115             // Using HashMap<String, Object> induces unnecessary warning.
    116             final HashMap<?,?> map = (HashMap<?,?>)adapter.getItem(i);
    117             final String id = (String)map.get(ZoneGetter.KEY_ID);
    118             if (defaultId.equals(id)) {
    119                 // If current timezone is in this list, move focus to it
    120                 return i;
    121             }
    122         }
    123         return -1;
    124     }
    125 
    126     /**
    127      * @param item one of items in adapters. The adapter should be constructed by
    128      * {@link #constructTimezoneAdapter(Context, boolean)}.
    129      * @return TimeZone object corresponding to the item.
    130      */
    131     public static TimeZone obtainTimeZoneFromItem(Object item) {
    132         return TimeZone.getTimeZone((String)((Map<?, ?>)item).get(ZoneGetter.KEY_ID));
    133     }
    134 
    135     @Override
    136     public void onActivityCreated(Bundle savedInstanceState) {
    137         super.onActivityCreated(savedInstanceState);
    138 
    139         final Activity activity = getActivity();
    140         mTimezoneSortedAdapter = constructTimezoneAdapter(activity, false);
    141         mAlphabeticalAdapter = constructTimezoneAdapter(activity, true);
    142 
    143         // Sets the adapter
    144         setSorting(true);
    145         setHasOptionsMenu(true);
    146     }
    147 
    148     @Override
    149     public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
    150             Bundle savedInstanceState) {
    151         final View view = super.onCreateView(inflater, container, savedInstanceState);
    152         final ListView list = (ListView) view.findViewById(android.R.id.list);
    153         Utils.forcePrepareCustomPreferencesList(container, view, list, false);
    154         return view;
    155     }
    156 
    157     @Override
    158     public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
    159         menu.add(0, MENU_ALPHABETICAL, 0, R.string.zone_list_menu_sort_alphabetically)
    160             .setIcon(android.R.drawable.ic_menu_sort_alphabetically);
    161         menu.add(0, MENU_TIMEZONE, 0, R.string.zone_list_menu_sort_by_timezone)
    162             .setIcon(R.drawable.ic_menu_3d_globe);
    163         super.onCreateOptionsMenu(menu, inflater);
    164     }
    165 
    166     @Override
    167     public void onPrepareOptionsMenu(Menu menu) {
    168         if (mSortedByTimezone) {
    169             menu.findItem(MENU_TIMEZONE).setVisible(false);
    170             menu.findItem(MENU_ALPHABETICAL).setVisible(true);
    171         } else {
    172             menu.findItem(MENU_TIMEZONE).setVisible(true);
    173             menu.findItem(MENU_ALPHABETICAL).setVisible(false);
    174         }
    175     }
    176 
    177     @Override
    178     public boolean onOptionsItemSelected(MenuItem item) {
    179         switch (item.getItemId()) {
    180 
    181             case MENU_TIMEZONE:
    182                 setSorting(true);
    183                 return true;
    184 
    185             case MENU_ALPHABETICAL:
    186                 setSorting(false);
    187                 return true;
    188 
    189             default:
    190                 return false;
    191         }
    192     }
    193 
    194     public void setZoneSelectionListener(ZoneSelectionListener listener) {
    195         mListener = listener;
    196     }
    197 
    198     private void setSorting(boolean sortByTimezone) {
    199         final SimpleAdapter adapter =
    200                 sortByTimezone ? mTimezoneSortedAdapter : mAlphabeticalAdapter;
    201         setListAdapter(adapter);
    202         mSortedByTimezone = sortByTimezone;
    203         final int defaultIndex = getTimeZoneIndex(adapter, TimeZone.getDefault());
    204         if (defaultIndex >= 0) {
    205             setSelection(defaultIndex);
    206         }
    207     }
    208 
    209     @Override
    210     public void onListItemClick(ListView listView, View v, int position, long id) {
    211         // Ignore extra clicks
    212         if (!isResumed()) return;
    213         final Map<?, ?> map = (Map<?, ?>)listView.getItemAtPosition(position);
    214         final String tzId = (String) map.get(ZoneGetter.KEY_ID);
    215 
    216         // Update the system timezone value
    217         final Activity activity = getActivity();
    218         final AlarmManager alarm = (AlarmManager) activity.getSystemService(Context.ALARM_SERVICE);
    219         alarm.setTimeZone(tzId);
    220         final TimeZone tz = TimeZone.getTimeZone(tzId);
    221         if (mListener != null) {
    222             mListener.onZoneSelected(tz);
    223         } else {
    224             getActivity().onBackPressed();
    225         }
    226     }
    227 
    228     private static class MyComparator implements Comparator<Map<?, ?>> {
    229         private String mSortingKey;
    230 
    231         public MyComparator(String sortingKey) {
    232             mSortingKey = sortingKey;
    233         }
    234 
    235         public void setSortingKey(String sortingKey) {
    236             mSortingKey = sortingKey;
    237         }
    238 
    239         public int compare(Map<?, ?> map1, Map<?, ?> map2) {
    240             Object value1 = map1.get(mSortingKey);
    241             Object value2 = map2.get(mSortingKey);
    242 
    243             /*
    244              * This should never happen, but just in-case, put non-comparable
    245              * items at the end.
    246              */
    247             if (!isComparable(value1)) {
    248                 return isComparable(value2) ? 1 : 0;
    249             } else if (!isComparable(value2)) {
    250                 return -1;
    251             }
    252 
    253             return ((Comparable) value1).compareTo(value2);
    254         }
    255 
    256         private boolean isComparable(Object value) {
    257             return (value != null) && (value instanceof Comparable);
    258         }
    259     }
    260 }
    261