Home | History | Annotate | Download | only in worldclock
      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 
     17 package com.android.deskclock.worldclock;
     18 
     19 import android.content.Context;
     20 import android.content.SharedPreferences;
     21 import android.preference.PreferenceManager;
     22 import android.view.LayoutInflater;
     23 import android.view.View;
     24 import android.view.ViewGroup;
     25 import android.widget.BaseAdapter;
     26 import android.widget.TextClock;
     27 import android.widget.TextView;
     28 
     29 import com.android.deskclock.AnalogClock;
     30 import com.android.deskclock.R;
     31 import com.android.deskclock.SettingsActivity;
     32 import com.android.deskclock.Utils;
     33 
     34 import java.text.Collator;
     35 import java.util.Arrays;
     36 import java.util.Calendar;
     37 import java.util.Comparator;
     38 import java.util.Date;
     39 import java.util.HashMap;
     40 import java.util.Locale;
     41 import java.util.TimeZone;
     42 
     43 public class WorldClockAdapter extends BaseAdapter {
     44     protected Object [] mCitiesList;
     45     private final LayoutInflater mInflater;
     46     private final Context mContext;
     47     private String mClockStyle;
     48     private final Collator mCollator = Collator.getInstance();
     49     protected HashMap<String, CityObj> mCitiesDb = new HashMap<String, CityObj>();
     50     private int mClocksPerRow;
     51 
     52     public WorldClockAdapter(Context context) {
     53         super();
     54         mContext = context;
     55         loadData(context);
     56         loadCitiesDb(context);
     57         mInflater = LayoutInflater.from(context);
     58         mClocksPerRow = context.getResources().getInteger(R.integer.world_clocks_per_row);
     59     }
     60 
     61     public void reloadData(Context context) {
     62         loadData(context);
     63         notifyDataSetChanged();
     64     }
     65 
     66     public void loadData(Context context) {
     67         SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(mContext);
     68         mClockStyle = prefs.getString(SettingsActivity.KEY_CLOCK_STYLE,
     69                 mContext.getResources().getString(R.string.default_clock_style));
     70         mCitiesList = Cities.readCitiesFromSharedPrefs(prefs).values().toArray();
     71         sortList();
     72         mCitiesList = addHomeCity();
     73     }
     74 
     75     public void loadCitiesDb(Context context) {
     76         mCitiesDb.clear();
     77         // Read the cities DB so that the names and timezones will be taken from the DB
     78         // and not from the selected list so that change of locale or changes in the DB will
     79         // be reflected.
     80         CityObj[] cities = Utils.loadCitiesFromXml(context);
     81         if (cities != null) {
     82             for (int i = 0; i < cities.length; i ++) {
     83                 mCitiesDb.put(cities[i].mCityId, cities [i]);
     84             }
     85         }
     86     }
     87 
     88     /***
     89      * Adds the home city as the first item of the adapter if the feature is on and the device time
     90      * zone is different from the home time zone that was set by the user.
     91      * return the list of cities.
     92      */
     93     private Object[] addHomeCity() {
     94         if (needHomeCity()) {
     95             SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(mContext);
     96             String homeTZ = sharedPref.getString(SettingsActivity.KEY_HOME_TZ, "");
     97             CityObj c = new CityObj(
     98                     mContext.getResources().getString(R.string.home_label), homeTZ, null);
     99             Object[] temp = new Object[mCitiesList.length + 1];
    100             temp[0] = c;
    101             for (int i = 0; i < mCitiesList.length; i++) {
    102                 temp[i + 1] = mCitiesList[i];
    103             }
    104             return temp;
    105         } else {
    106             return mCitiesList;
    107         }
    108     }
    109 
    110     public void updateHomeLabel(Context context) {
    111         // Update the "home" label if the home time zone clock is shown
    112         if (needHomeCity() && mCitiesList.length > 0) {
    113             ((CityObj) mCitiesList[0]).mCityName =
    114                     context.getResources().getString(R.string.home_label);
    115         }
    116     }
    117 
    118     public boolean needHomeCity() {
    119         SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(mContext);
    120         if (sharedPref.getBoolean(SettingsActivity.KEY_AUTO_HOME_CLOCK, false)) {
    121             String homeTZ = sharedPref.getString(
    122                     SettingsActivity.KEY_HOME_TZ, TimeZone.getDefault().getID());
    123             final Date now = new Date();
    124             return TimeZone.getTimeZone(homeTZ).getOffset(now.getTime())
    125                     != TimeZone.getDefault().getOffset(now.getTime());
    126         } else {
    127             return false;
    128         }
    129     }
    130 
    131     public boolean hasHomeCity() {
    132         return (mCitiesList != null) && mCitiesList.length > 0
    133                 && ((CityObj) mCitiesList[0]).mCityId == null;
    134     }
    135 
    136     private void sortList() {
    137         final Date now = new Date();
    138 
    139         // Sort by the Offset from GMT taking DST into account
    140         // and if the same sort by City Name
    141         Arrays.sort(mCitiesList, new Comparator<Object>() {
    142             private int safeCityNameCompare(CityObj city1, CityObj city2) {
    143                 if (city1.mCityName == null && city2.mCityName == null) {
    144                     return 0;
    145                 } else if (city1.mCityName == null) {
    146                     return -1;
    147                 } else if (city2.mCityName == null) {
    148                     return 1;
    149                 } else {
    150                     return mCollator.compare(city1.mCityName, city2.mCityName);
    151                 }
    152             }
    153 
    154             @Override
    155             public int compare(Object object1, Object object2) {
    156                 CityObj city1 = (CityObj) object1;
    157                 CityObj city2 = (CityObj) object2;
    158                 if (city1.mTimeZone == null && city2.mTimeZone == null) {
    159                     return safeCityNameCompare(city1, city2);
    160                 } else if (city1.mTimeZone == null) {
    161                     return -1;
    162                 } else if (city2.mTimeZone == null) {
    163                     return 1;
    164                 }
    165 
    166                 int gmOffset1 = TimeZone.getTimeZone(city1.mTimeZone).getOffset(now.getTime());
    167                 int gmOffset2 = TimeZone.getTimeZone(city2.mTimeZone).getOffset(now.getTime());
    168                 if (gmOffset1 == gmOffset2) {
    169                     return safeCityNameCompare(city1, city2);
    170                 } else {
    171                     return gmOffset1 - gmOffset2;
    172                 }
    173             }
    174         });
    175     }
    176 
    177     @Override
    178     public int getCount() {
    179         if (mClocksPerRow == 1) {
    180             // In the special case where we have only 1 clock per view.
    181             return mCitiesList.length;
    182         }
    183 
    184         // Otherwise, each item in the list holds 1 or 2 clocks
    185         return (mCitiesList.length  + 1)/2;
    186     }
    187 
    188     @Override
    189     public Object getItem(int p) {
    190         return null;
    191     }
    192 
    193     @Override
    194     public long getItemId(int p) {
    195         return p;
    196     }
    197 
    198     @Override
    199     public boolean isEnabled(int p) {
    200         return false;
    201     }
    202 
    203     @Override
    204     public View getView(int position, View view, ViewGroup parent) {
    205         // Index in cities list
    206         int index = position * mClocksPerRow;
    207         if (index < 0 || index >= mCitiesList.length) {
    208             return null;
    209         }
    210 
    211         if (view == null) {
    212             view = mInflater.inflate(R.layout.world_clock_list_item, parent, false);
    213         }
    214         // The world clock list item can hold two world clocks
    215         View rightClock = view.findViewById(R.id.city_right);
    216         updateView(view.findViewById(R.id.city_left), (CityObj)mCitiesList[index]);
    217         if (rightClock != null) {
    218             // rightClock may be null (landscape phone layout has only one clock per row) so only
    219             // process it if it exists.
    220             if (index + 1 < mCitiesList.length) {
    221                 rightClock.setVisibility(View.VISIBLE);
    222                 updateView(rightClock, (CityObj)mCitiesList[index + 1]);
    223             } else {
    224                 // To make sure the spacing is right , make sure that the right clock style is
    225                 // selected even if the clock is invisible.
    226                 View dclock = rightClock.findViewById(R.id.digital_clock);
    227                 View aclock = rightClock.findViewById(R.id.analog_clock);
    228                 if (mClockStyle.equals("analog")) {
    229                     dclock.setVisibility(View.GONE);
    230                     aclock.setVisibility(View.INVISIBLE);
    231                 } else {
    232                     dclock.setVisibility(View.INVISIBLE);
    233                     aclock.setVisibility(View.GONE);
    234                 }
    235                 // If there's only the one item, center it. If there are other items in the list,
    236                 // keep it side-aligned.
    237                 if (getCount() == 1) {
    238                     rightClock.setVisibility(View.GONE);
    239                 } else {
    240                     rightClock.setVisibility(View.INVISIBLE);
    241                 }
    242             }
    243         }
    244 
    245         return view;
    246     }
    247 
    248     private void updateView(View clock, CityObj cityObj) {
    249         View nameLayout= clock.findViewById(R.id.city_name_layout);
    250         TextView name = (TextView)(nameLayout.findViewById(R.id.city_name));
    251         TextView dayOfWeek = (TextView)(nameLayout.findViewById(R.id.city_day));
    252         TextClock dclock = (TextClock)(clock.findViewById(R.id.digital_clock));
    253         AnalogClock aclock = (AnalogClock)(clock.findViewById(R.id.analog_clock));
    254         View separator = clock.findViewById(R.id.separator);
    255 
    256         if (mClockStyle.equals("analog")) {
    257             dclock.setVisibility(View.GONE);
    258             separator.setVisibility(View.GONE);
    259             aclock.setVisibility(View.VISIBLE);
    260             aclock.setTimeZone(cityObj.mTimeZone);
    261             aclock.enableSeconds(false);
    262         } else {
    263             dclock.setVisibility(View.VISIBLE);
    264             separator.setVisibility(View.VISIBLE);
    265             aclock.setVisibility(View.GONE);
    266             dclock.setTimeZone(cityObj.mTimeZone);
    267             Utils.setTimeFormat(dclock,
    268                     (int)mContext.getResources().getDimension(R.dimen.label_font_size));
    269         }
    270         CityObj cityInDb = mCitiesDb.get(cityObj.mCityId);
    271         // Home city or city not in DB , use data from the save selected cities list
    272         name.setText(Utils.getCityName(cityObj, cityInDb));
    273 
    274         final Calendar now = Calendar.getInstance();
    275         now.setTimeZone(TimeZone.getDefault());
    276         int myDayOfWeek = now.get(Calendar.DAY_OF_WEEK);
    277         // Get timezone from cities DB if available
    278         String cityTZ = (cityInDb != null) ? cityInDb.mTimeZone : cityObj.mTimeZone;
    279         now.setTimeZone(TimeZone.getTimeZone(cityTZ));
    280         int cityDayOfWeek = now.get(Calendar.DAY_OF_WEEK);
    281         if (myDayOfWeek != cityDayOfWeek) {
    282             dayOfWeek.setText(mContext.getString(R.string.world_day_of_week_label,
    283                     now.getDisplayName(Calendar.DAY_OF_WEEK, Calendar.SHORT, Locale.getDefault())));
    284             dayOfWeek.setVisibility(View.VISIBLE);
    285         } else {
    286             dayOfWeek.setVisibility(View.GONE);
    287         }
    288     }
    289 }
    290