TimeZoneResultAdapter.java revision 215ec1009ae3b52ed491fde7b2aa0a13dd4387be
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.View.OnClickListener;
26import android.view.ViewGroup;
27import android.widget.BaseAdapter;
28import android.widget.TextView;
29
30import com.android.timezonepicker.TimeZoneFilterTypeAdapter.OnSetFilterListener;
31import com.android.timezonepicker.TimeZonePickerView.OnTimeZoneSetListener;
32
33import java.util.ArrayList;
34import java.util.Arrays;
35import java.util.Iterator;
36import java.util.List;
37
38public class TimeZoneResultAdapter extends BaseAdapter implements OnClickListener,
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    private static final int RESULT_LABEL_RECENT = -100;
57    private static final int RESULT_LABEL_CURRENT = -200;
58
59    static class ViewHolder {
60        TextView timeZone;
61        TextView timeOffset;
62        TextView location;
63        TextView label;
64
65        static void setupViewHolder(View v) {
66            ViewHolder vh = new ViewHolder();
67            vh.timeZone = (TextView) v.findViewById(R.id.time_zone);
68            vh.timeOffset = (TextView) v.findViewById(R.id.time_offset);
69            vh.location = (TextView) v.findViewById(R.id.location);
70            vh.label = (TextView) v.findViewById(R.id.label);
71            v.setTag(vh);
72        }
73    }
74
75    private Context mContext;
76    private LayoutInflater mInflater;
77
78    private OnTimeZoneSetListener mTimeZoneSetListener;
79    private TimeZoneData mTimeZoneData;
80
81    private int[] mFilteredTimeZoneIndices;
82    private int mFilteredTimeZoneLength = 0;
83    private int mFilterType;
84
85    public TimeZoneResultAdapter(Context context, TimeZoneData tzd,
86            com.android.timezonepicker.TimeZonePickerView.OnTimeZoneSetListener l) {
87        super();
88
89        mContext = context;
90        mTimeZoneData = tzd;
91        mTimeZoneSetListener = l;
92
93        mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
94
95        mFilteredTimeZoneIndices = new int[mTimeZoneData.size()];
96
97        onSetFilter(TimeZoneFilterTypeAdapter.FILTER_TYPE_NONE, null, 0);
98    }
99
100    // Implements OnSetFilterListener
101    @Override
102    public void onSetFilter(int filterType, String str, int time) {
103        Log.d(TAG, "onSetFilter: " + filterType + " [" + str + "] " + time);
104
105        mFilterType = filterType;
106        mFilteredTimeZoneLength = 0;
107        int idx = 0;
108
109        switch (filterType) {
110            case TimeZoneFilterTypeAdapter.FILTER_TYPE_EMPTY:
111                break;
112            case TimeZoneFilterTypeAdapter.FILTER_TYPE_NONE:
113                // Show the default/current value first
114                int defaultTzIndex = mTimeZoneData.getDefaultTimeZoneIndex();
115                if (defaultTzIndex != -1) {
116                    mFilteredTimeZoneIndices[mFilteredTimeZoneLength++] = RESULT_LABEL_CURRENT;
117                    mFilteredTimeZoneIndices[mFilteredTimeZoneLength++] = defaultTzIndex;
118                }
119
120                // Show the recent selections
121                SharedPreferences prefs = mContext.getSharedPreferences(SHARED_PREFS_NAME,
122                        Context.MODE_PRIVATE);
123                String recentsString = prefs.getString(KEY_RECENT_TIMEZONES, null);
124                if (!TextUtils.isEmpty(recentsString)) {
125                    String[] recents = recentsString.split(RECENT_TIMEZONES_DELIMITER);
126                    boolean first = true;
127                    for (int i = recents.length - 1; i >= 0; i--) {
128                        if (!TextUtils.isEmpty(recents[i])
129                                && !recents[i].equals(mTimeZoneData.mDefaultTimeZoneId)) {
130                            int index = mTimeZoneData.findIndexByTimeZoneIdSlow(recents[i]);
131                            if (index != -1) {
132                                if (first) {
133                                    mFilteredTimeZoneIndices[mFilteredTimeZoneLength++] =
134                                            RESULT_LABEL_RECENT;
135                                    first = false;
136                                }
137                                mFilteredTimeZoneIndices[mFilteredTimeZoneLength++] = index;
138                            }
139                        }
140                    }
141                }
142
143                break;
144            case TimeZoneFilterTypeAdapter.FILTER_TYPE_GMT:
145                ArrayList<Integer> indices = mTimeZoneData.getTimeZonesByOffset(time);
146                if (indices != null) {
147                    for (Integer i : indices) {
148                        mFilteredTimeZoneIndices[mFilteredTimeZoneLength++] = i;
149                    }
150                }
151                break;
152            case TimeZoneFilterTypeAdapter.FILTER_TYPE_TIME:
153                // TODO make this faster
154                long now = System.currentTimeMillis();
155                for (TimeZoneInfo tzi : mTimeZoneData.mTimeZones) {
156                    int localHr = tzi.getLocalHr(now);
157                    boolean match = localHr == time;
158                    if (!match && !TimeZoneData.is24HourFormat) {
159                        // PM + noon cases
160                        if ((time + 12 == localHr) || (time == 12 && localHr == 0)) {
161                            match = true;
162                        }
163                    }
164                    if (match) {
165                        mFilteredTimeZoneIndices[mFilteredTimeZoneLength++] = idx;
166                    }
167                    idx++;
168                }
169                break;
170            case TimeZoneFilterTypeAdapter.FILTER_TYPE_TIME_ZONE:
171                if (str != null) {
172                    for (TimeZoneInfo tzi : mTimeZoneData.mTimeZones) {
173                        if (str.equalsIgnoreCase(tzi.mDisplayName)) {
174                            mFilteredTimeZoneIndices[mFilteredTimeZoneLength++] = idx;
175                        }
176                        idx++;
177                    }
178                }
179                break;
180            case TimeZoneFilterTypeAdapter.FILTER_TYPE_COUNTRY:
181                ArrayList<Integer> tzIds = mTimeZoneData.mTimeZonesByCountry.get(str);
182                if (tzIds != null) {
183                    for (Integer tzi : tzIds) {
184                        mFilteredTimeZoneIndices[mFilteredTimeZoneLength++] = tzi;
185                    }
186                }
187                break;
188            case TimeZoneFilterTypeAdapter.FILTER_TYPE_STATE:
189                // TODO Filter by state
190                break;
191            default:
192                throw new IllegalArgumentException();
193        }
194        notifyDataSetChanged();
195    }
196
197    /**
198     * Saves the given timezone ID as a recent timezone under shared
199     * preferences. If there are already the maximum number of recent timezones
200     * saved, it will remove the oldest and append this one.
201     *
202     * @param id the ID of the timezone to save
203     * @see {@link #MAX_RECENT_TIMEZONES}
204     */
205    public void saveRecentTimezone(String id) {
206        SharedPreferences prefs = mContext.getSharedPreferences(SHARED_PREFS_NAME,
207                Context.MODE_PRIVATE);
208        String recentsString = prefs.getString(KEY_RECENT_TIMEZONES, null);
209        if (recentsString == null) {
210            recentsString = id;
211        } else {
212            List<String> recents = new ArrayList<String>(
213                    Arrays.asList(recentsString.split(RECENT_TIMEZONES_DELIMITER)));
214            Iterator<String> it = recents.iterator();
215            while (it.hasNext()) {
216                String tz = it.next();
217                if (id.equals(tz)) {
218                    it.remove();
219                }
220            }
221
222            while (recents.size() >= MAX_RECENT_TIMEZONES) {
223                recents.remove(0);
224            }
225            recents.add(id);
226
227            StringBuilder builder = new StringBuilder();
228            boolean first = true;
229            for (String recent : recents) {
230                if (first) {
231                    first = false;
232                } else {
233                    builder.append(RECENT_TIMEZONES_DELIMITER);
234                }
235                builder.append(recent);
236            }
237            recentsString = builder.toString();
238        }
239
240        prefs.edit().putString(KEY_RECENT_TIMEZONES, recentsString).apply();
241    }
242
243    @Override
244    public int getCount() {
245        return mFilteredTimeZoneLength;
246    }
247
248    @Override
249    public Object getItem(int position) {
250        if (position < 0 || position >= mFilteredTimeZoneLength) {
251            return null;
252        }
253
254        switch (mFilteredTimeZoneIndices[position]) {
255            case RESULT_LABEL_CURRENT:
256                return "CURRENT TIME ZONE";
257            case RESULT_LABEL_RECENT:
258                return "RECENT TIME ZONE";
259        }
260
261        return mTimeZoneData.get(mFilteredTimeZoneIndices[position]);
262    }
263
264    @Override
265    public boolean isEnabled(int position) {
266        return mFilteredTimeZoneIndices[position] >= 0;
267    }
268
269    @Override
270    public long getItemId(int position) {
271        return mFilteredTimeZoneIndices[position];
272    }
273
274    @Override
275    public View getView(int position, View convertView, ViewGroup parent) {
276        View v = convertView;
277
278        if (v == null) {
279            v = mInflater.inflate(R.layout.time_zone_item, null);
280            v.setOnClickListener(this);
281            ViewHolder.setupViewHolder(v);
282        }
283
284        ViewHolder vh = (ViewHolder) v.getTag();
285
286        if (mFilteredTimeZoneIndices[position] >= 0) {
287            TimeZoneInfo tzi = mTimeZoneData.get(mFilteredTimeZoneIndices[position]);
288            v.setTag(VIEW_TAG_TIME_ZONE, tzi);
289
290            vh.label.setVisibility(View.GONE);
291            vh.timeZone.setText(tzi.mDisplayName);
292            vh.timeZone.setVisibility(View.VISIBLE);
293
294            vh.timeOffset.setText(tzi.getGmtDisplayName(mContext));
295            vh.timeOffset.setVisibility(View.VISIBLE);
296
297            String location = tzi.mCountry;
298            if (location == null) {
299                vh.location.setVisibility(View.INVISIBLE);
300            } else {
301                vh.location.setText(location);
302                vh.location.setVisibility(View.VISIBLE);
303            }
304        } else {
305            if (mFilteredTimeZoneIndices[position] == RESULT_LABEL_CURRENT) {
306                vh.label.setText(v.getResources().getText(R.string.current_time_zone));
307            } else if (mFilteredTimeZoneIndices[position] == RESULT_LABEL_RECENT) {
308                vh.label.setText(v.getResources().getQuantityText(R.plurals.recent_time_zone,
309                        /* num of recent tzs */ mFilteredTimeZoneLength - position - 1));
310            }
311            vh.label.setVisibility(View.VISIBLE);
312            vh.timeZone.setVisibility(View.GONE);
313            vh.timeOffset.setVisibility(View.GONE);
314            vh.location.setVisibility(View.GONE);
315            v.setTag(VIEW_TAG_TIME_ZONE, null);
316        }
317
318        return v;
319    }
320
321    @Override
322    public boolean hasStableIds() {
323        return true;
324    }
325
326    // Implements OnClickListener
327    @Override
328    public void onClick(View v) {
329        if (mTimeZoneSetListener != null) {
330            TimeZoneInfo tzi = (TimeZoneInfo) v.getTag(VIEW_TAG_TIME_ZONE);
331            mTimeZoneSetListener.onTimeZoneSet(tzi);
332            saveRecentTimezone(tzi.mTzId);
333        }
334    }
335
336}
337