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