Home | History | Annotate | Download | only in timezonepicker
      1 /*
      2  * Copyright (C) 2013 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.timezonepicker;
     18 
     19 import android.content.Context;
     20 import android.content.SharedPreferences;
     21 import android.text.TextUtils;
     22 import android.util.Log;
     23 import android.view.LayoutInflater;
     24 import android.view.View;
     25 import android.view.ViewGroup;
     26 import android.widget.AdapterView;
     27 import android.widget.AdapterView.OnItemClickListener;
     28 import android.widget.BaseAdapter;
     29 import android.widget.TextView;
     30 
     31 import com.android.timezonepicker.TimeZoneFilterTypeAdapter.OnSetFilterListener;
     32 import com.android.timezonepicker.TimeZonePickerView.OnTimeZoneSetListener;
     33 
     34 import java.util.ArrayList;
     35 import java.util.Iterator;
     36 import java.util.LinkedHashSet;
     37 
     38 public class TimeZoneResultAdapter extends BaseAdapter implements OnItemClickListener,
     39         OnSetFilterListener {
     40     private static final String TAG = "TimeZoneResultAdapter";
     41     private static final boolean DEBUG = false;
     42     private static final int VIEW_TAG_TIME_ZONE = R.id.time_zone;
     43     private static final int EMPTY_INDEX = -100;
     44 
     45     /** SharedPref name and key for recent time zones */
     46     private static final String SHARED_PREFS_NAME = "com.android.calendar_preferences";
     47     private static final String KEY_RECENT_TIMEZONES = "preferences_recent_timezones";
     48 
     49     private int mLastFilterType;
     50     private String mLastFilterString;
     51     private int mLastFilterTime;
     52 
     53     private boolean mHasResults = false;
     54 
     55     /**
     56      * The delimiter we use when serializing recent timezones to shared
     57      * preferences
     58      */
     59     private static final String RECENT_TIMEZONES_DELIMITER = ",";
     60 
     61     /** The maximum number of recent timezones to save */
     62     private static final int MAX_RECENT_TIMEZONES = 3;
     63 
     64     static class ViewHolder {
     65         TextView timeZone;
     66         TextView timeOffset;
     67         TextView location;
     68 
     69         static void setupViewHolder(View v) {
     70             ViewHolder vh = new ViewHolder();
     71             vh.timeZone = (TextView) v.findViewById(R.id.time_zone);
     72             vh.timeOffset = (TextView) v.findViewById(R.id.time_offset);
     73             vh.location = (TextView) v.findViewById(R.id.location);
     74             v.setTag(vh);
     75         }
     76     }
     77 
     78     private Context mContext;
     79     private LayoutInflater mInflater;
     80 
     81     private OnTimeZoneSetListener mTimeZoneSetListener;
     82     private TimeZoneData mTimeZoneData;
     83 
     84     private int[] mFilteredTimeZoneIndices;
     85     private int mFilteredTimeZoneLength = 0;
     86 
     87     public TimeZoneResultAdapter(Context context, TimeZoneData tzd,
     88             com.android.timezonepicker.TimeZonePickerView.OnTimeZoneSetListener l) {
     89         super();
     90 
     91         mContext = context;
     92         mTimeZoneData = tzd;
     93         mTimeZoneSetListener = l;
     94 
     95         mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
     96 
     97         mFilteredTimeZoneIndices = new int[mTimeZoneData.size()];
     98 
     99         onSetFilter(TimeZoneFilterTypeAdapter.FILTER_TYPE_NONE, null, 0);
    100     }
    101 
    102     public boolean hasResults() {
    103         return mHasResults;
    104     }
    105 
    106     public int getLastFilterType() {
    107         return mLastFilterType;
    108     }
    109 
    110     public String getLastFilterString() {
    111         return mLastFilterString;
    112     }
    113 
    114     public int getLastFilterTime() {
    115         return mLastFilterTime;
    116     }
    117 
    118     // Implements OnSetFilterListener
    119     @Override
    120     public void onSetFilter(int filterType, String str, int time) {
    121         if (DEBUG) {
    122             Log.d(TAG, "onSetFilter: " + filterType + " [" + str + "] " + time);
    123         }
    124 
    125         mLastFilterType = filterType;
    126         mLastFilterString = str;
    127         mLastFilterTime = time;
    128 
    129         mFilteredTimeZoneLength = 0;
    130         int idx = 0;
    131 
    132         switch (filterType) {
    133             case TimeZoneFilterTypeAdapter.FILTER_TYPE_EMPTY:
    134                 mFilteredTimeZoneIndices[mFilteredTimeZoneLength++] = EMPTY_INDEX;
    135                 break;
    136             case TimeZoneFilterTypeAdapter.FILTER_TYPE_NONE:
    137                 // Show the default/current value first
    138                 int defaultTzIndex = mTimeZoneData.getDefaultTimeZoneIndex();
    139                 if (defaultTzIndex != -1) {
    140                     mFilteredTimeZoneIndices[mFilteredTimeZoneLength++] = defaultTzIndex;
    141                 }
    142 
    143                 // Show the recent selections
    144                 SharedPreferences prefs = mContext.getSharedPreferences(SHARED_PREFS_NAME,
    145                         Context.MODE_PRIVATE);
    146                 String recentsString = prefs.getString(KEY_RECENT_TIMEZONES, null);
    147                 if (!TextUtils.isEmpty(recentsString)) {
    148                     String[] recents = recentsString.split(RECENT_TIMEZONES_DELIMITER);
    149                     for (int i = recents.length - 1; i >= 0; i--) {
    150                         if (!TextUtils.isEmpty(recents[i])
    151                                 && !recents[i].equals(mTimeZoneData.mDefaultTimeZoneId)) {
    152                             int index = mTimeZoneData.findIndexByTimeZoneIdSlow(recents[i]);
    153                             if (index != -1) {
    154                                 mFilteredTimeZoneIndices[mFilteredTimeZoneLength++] = index;
    155                             }
    156                         }
    157                     }
    158                 }
    159 
    160                 break;
    161             case TimeZoneFilterTypeAdapter.FILTER_TYPE_GMT:
    162                 ArrayList<Integer> indices = mTimeZoneData.getTimeZonesByOffset(time);
    163                 if (indices != null) {
    164                     for (Integer i : indices) {
    165                         mFilteredTimeZoneIndices[mFilteredTimeZoneLength++] = i;
    166                     }
    167                 }
    168                 break;
    169             case TimeZoneFilterTypeAdapter.FILTER_TYPE_COUNTRY:
    170                 ArrayList<Integer> tzIds = mTimeZoneData.mTimeZonesByCountry.get(str);
    171                 if (tzIds != null) {
    172                     for (Integer tzi : tzIds) {
    173                         mFilteredTimeZoneIndices[mFilteredTimeZoneLength++] = tzi;
    174                     }
    175                 }
    176                 break;
    177             case TimeZoneFilterTypeAdapter.FILTER_TYPE_STATE:
    178                 // TODO Filter by state
    179                 break;
    180             default:
    181                 throw new IllegalArgumentException();
    182         }
    183         mHasResults = mFilteredTimeZoneLength > 0;
    184 
    185         notifyDataSetChanged();
    186     }
    187 
    188     /**
    189      * Saves the given timezone ID as a recent timezone under shared
    190      * preferences. If there are already the maximum number of recent timezones
    191      * saved, it will remove the oldest and append this one.
    192      *
    193      * @param id the ID of the timezone to save
    194      * @see {@link #MAX_RECENT_TIMEZONES}
    195      */
    196     public void saveRecentTimezone(String id) {
    197         SharedPreferences prefs = mContext.getSharedPreferences(SHARED_PREFS_NAME,
    198                 Context.MODE_PRIVATE);
    199         String recentsString = prefs.getString(KEY_RECENT_TIMEZONES, null);
    200         if (recentsString == null) {
    201             recentsString = id;
    202         } else {
    203             // De-dup
    204             LinkedHashSet<String> recents = new LinkedHashSet<String>();
    205             for(String tzId : recentsString.split(RECENT_TIMEZONES_DELIMITER)) {
    206                 if (!recents.contains(tzId) && !id.equals(tzId)) {
    207                     recents.add(tzId);
    208                 }
    209             }
    210 
    211             Iterator<String> it = recents.iterator();
    212             while (recents.size() >= MAX_RECENT_TIMEZONES) {
    213                 if (!it.hasNext()) {
    214                     break;
    215                 }
    216                 it.next();
    217                 it.remove();
    218             }
    219             recents.add(id);
    220 
    221             StringBuilder builder = new StringBuilder();
    222             boolean first = true;
    223             for (String recent : recents) {
    224                 if (first) {
    225                     first = false;
    226                 } else {
    227                     builder.append(RECENT_TIMEZONES_DELIMITER);
    228                 }
    229                 builder.append(recent);
    230             }
    231             recentsString = builder.toString();
    232         }
    233 
    234         prefs.edit().putString(KEY_RECENT_TIMEZONES, recentsString).apply();
    235     }
    236 
    237     @Override
    238     public int getCount() {
    239         return mFilteredTimeZoneLength;
    240     }
    241 
    242     @Override
    243     public Object getItem(int position) {
    244         if (position < 0 || position >= mFilteredTimeZoneLength) {
    245             return null;
    246         }
    247 
    248         return mTimeZoneData.get(mFilteredTimeZoneIndices[position]);
    249     }
    250 
    251     @Override
    252     public boolean areAllItemsEnabled() {
    253         return false;
    254     }
    255 
    256     @Override
    257     public boolean isEnabled(int position) {
    258         return mFilteredTimeZoneIndices[position] >= 0;
    259     }
    260 
    261     @Override
    262     public long getItemId(int position) {
    263         return mFilteredTimeZoneIndices[position];
    264     }
    265 
    266     @Override
    267     public View getView(int position, View convertView, ViewGroup parent) {
    268         View v = convertView;
    269 
    270         if (mFilteredTimeZoneIndices[position] == EMPTY_INDEX) {
    271             v = mInflater.inflate(R.layout.empty_time_zone_item, null);
    272             return v;
    273         }
    274 
    275         // We'll need to re-inflate the view if it was null, or if it was used as an empty item.
    276         if (v == null || v.findViewById(R.id.empty_item) != null) {
    277             v = mInflater.inflate(R.layout.time_zone_item, null);
    278             ViewHolder.setupViewHolder(v);
    279         }
    280 
    281         ViewHolder vh = (ViewHolder) v.getTag();
    282 
    283         TimeZoneInfo tzi = mTimeZoneData.get(mFilteredTimeZoneIndices[position]);
    284         v.setTag(VIEW_TAG_TIME_ZONE, tzi);
    285 
    286         vh.timeZone.setText(tzi.mDisplayName);
    287 
    288         vh.timeOffset.setText(tzi.getGmtDisplayName(mContext));
    289 
    290         String location = tzi.mCountry;
    291         if (location == null) {
    292             vh.location.setVisibility(View.INVISIBLE);
    293         } else {
    294             vh.location.setText(location);
    295             vh.location.setVisibility(View.VISIBLE);
    296         }
    297 
    298         return v;
    299     }
    300 
    301     @Override
    302     public boolean hasStableIds() {
    303         return true;
    304     }
    305 
    306     // Implements OnItemClickListener
    307     @Override
    308     public void onItemClick(AdapterView<?> parent, View v, int position, long id) {
    309         if (mTimeZoneSetListener != null) {
    310             TimeZoneInfo tzi = (TimeZoneInfo) v.getTag(VIEW_TAG_TIME_ZONE);
    311             if (tzi != null) {
    312               mTimeZoneSetListener.onTimeZoneSet(tzi);
    313               saveRecentTimezone(tzi.mTzId);
    314             }
    315         }
    316     }
    317 }
    318