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