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