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