| /* |
| * Copyright (C) 2013 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.timezonepicker; |
| |
| import android.content.Context; |
| import android.content.SharedPreferences; |
| import android.text.TextUtils; |
| import android.util.Log; |
| import android.view.LayoutInflater; |
| import android.view.View; |
| import android.view.ViewGroup; |
| import android.widget.AdapterView; |
| import android.widget.AdapterView.OnItemClickListener; |
| import android.widget.BaseAdapter; |
| import android.widget.TextView; |
| |
| import com.android.timezonepicker.TimeZoneFilterTypeAdapter.OnSetFilterListener; |
| import com.android.timezonepicker.TimeZonePickerView.OnTimeZoneSetListener; |
| |
| import java.util.ArrayList; |
| import java.util.Iterator; |
| import java.util.LinkedHashSet; |
| |
| public class TimeZoneResultAdapter extends BaseAdapter implements OnItemClickListener, |
| OnSetFilterListener { |
| private static final String TAG = "TimeZoneResultAdapter"; |
| private static final boolean DEBUG = false; |
| private static final int VIEW_TAG_TIME_ZONE = R.id.time_zone; |
| private static final int EMPTY_INDEX = -100; |
| |
| /** SharedPref name and key for recent time zones */ |
| private static final String SHARED_PREFS_NAME = "com.android.calendar_preferences"; |
| private static final String KEY_RECENT_TIMEZONES = "preferences_recent_timezones"; |
| |
| private int mLastFilterType; |
| private String mLastFilterString; |
| private int mLastFilterTime; |
| |
| private boolean mHasResults = false; |
| |
| /** |
| * The delimiter we use when serializing recent timezones to shared |
| * preferences |
| */ |
| private static final String RECENT_TIMEZONES_DELIMITER = ","; |
| |
| /** The maximum number of recent timezones to save */ |
| private static final int MAX_RECENT_TIMEZONES = 3; |
| |
| static class ViewHolder { |
| TextView timeZone; |
| TextView timeOffset; |
| TextView location; |
| |
| static void setupViewHolder(View v) { |
| ViewHolder vh = new ViewHolder(); |
| vh.timeZone = (TextView) v.findViewById(R.id.time_zone); |
| vh.timeOffset = (TextView) v.findViewById(R.id.time_offset); |
| vh.location = (TextView) v.findViewById(R.id.location); |
| v.setTag(vh); |
| } |
| } |
| |
| private Context mContext; |
| private LayoutInflater mInflater; |
| |
| private OnTimeZoneSetListener mTimeZoneSetListener; |
| private TimeZoneData mTimeZoneData; |
| |
| private int[] mFilteredTimeZoneIndices; |
| private int mFilteredTimeZoneLength = 0; |
| |
| public TimeZoneResultAdapter(Context context, TimeZoneData tzd, |
| com.android.timezonepicker.TimeZonePickerView.OnTimeZoneSetListener l) { |
| super(); |
| |
| mContext = context; |
| mTimeZoneData = tzd; |
| mTimeZoneSetListener = l; |
| |
| mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); |
| |
| mFilteredTimeZoneIndices = new int[mTimeZoneData.size()]; |
| |
| onSetFilter(TimeZoneFilterTypeAdapter.FILTER_TYPE_NONE, null, 0); |
| } |
| |
| public boolean hasResults() { |
| return mHasResults; |
| } |
| |
| public int getLastFilterType() { |
| return mLastFilterType; |
| } |
| |
| public String getLastFilterString() { |
| return mLastFilterString; |
| } |
| |
| public int getLastFilterTime() { |
| return mLastFilterTime; |
| } |
| |
| // Implements OnSetFilterListener |
| @Override |
| public void onSetFilter(int filterType, String str, int time) { |
| if (DEBUG) { |
| Log.d(TAG, "onSetFilter: " + filterType + " [" + str + "] " + time); |
| } |
| |
| mLastFilterType = filterType; |
| mLastFilterString = str; |
| mLastFilterTime = time; |
| |
| mFilteredTimeZoneLength = 0; |
| int idx = 0; |
| |
| switch (filterType) { |
| case TimeZoneFilterTypeAdapter.FILTER_TYPE_EMPTY: |
| mFilteredTimeZoneIndices[mFilteredTimeZoneLength++] = EMPTY_INDEX; |
| break; |
| case TimeZoneFilterTypeAdapter.FILTER_TYPE_NONE: |
| // Show the default/current value first |
| int defaultTzIndex = mTimeZoneData.getDefaultTimeZoneIndex(); |
| if (defaultTzIndex != -1) { |
| mFilteredTimeZoneIndices[mFilteredTimeZoneLength++] = defaultTzIndex; |
| } |
| |
| // Show the recent selections |
| SharedPreferences prefs = mContext.getSharedPreferences(SHARED_PREFS_NAME, |
| Context.MODE_PRIVATE); |
| String recentsString = prefs.getString(KEY_RECENT_TIMEZONES, null); |
| if (!TextUtils.isEmpty(recentsString)) { |
| String[] recents = recentsString.split(RECENT_TIMEZONES_DELIMITER); |
| for (int i = recents.length - 1; i >= 0; i--) { |
| if (!TextUtils.isEmpty(recents[i]) |
| && !recents[i].equals(mTimeZoneData.mDefaultTimeZoneId)) { |
| int index = mTimeZoneData.findIndexByTimeZoneIdSlow(recents[i]); |
| if (index != -1) { |
| mFilteredTimeZoneIndices[mFilteredTimeZoneLength++] = index; |
| } |
| } |
| } |
| } |
| |
| break; |
| case TimeZoneFilterTypeAdapter.FILTER_TYPE_GMT: |
| ArrayList<Integer> indices = mTimeZoneData.getTimeZonesByOffset(time); |
| if (indices != null) { |
| for (Integer i : indices) { |
| mFilteredTimeZoneIndices[mFilteredTimeZoneLength++] = i; |
| } |
| } |
| break; |
| case TimeZoneFilterTypeAdapter.FILTER_TYPE_COUNTRY: |
| ArrayList<Integer> tzIds = mTimeZoneData.mTimeZonesByCountry.get(str); |
| if (tzIds != null) { |
| for (Integer tzi : tzIds) { |
| mFilteredTimeZoneIndices[mFilteredTimeZoneLength++] = tzi; |
| } |
| } |
| break; |
| case TimeZoneFilterTypeAdapter.FILTER_TYPE_STATE: |
| // TODO Filter by state |
| break; |
| default: |
| throw new IllegalArgumentException(); |
| } |
| mHasResults = mFilteredTimeZoneLength > 0; |
| |
| notifyDataSetChanged(); |
| } |
| |
| /** |
| * Saves the given timezone ID as a recent timezone under shared |
| * preferences. If there are already the maximum number of recent timezones |
| * saved, it will remove the oldest and append this one. |
| * |
| * @param id the ID of the timezone to save |
| * @see {@link #MAX_RECENT_TIMEZONES} |
| */ |
| public void saveRecentTimezone(String id) { |
| SharedPreferences prefs = mContext.getSharedPreferences(SHARED_PREFS_NAME, |
| Context.MODE_PRIVATE); |
| String recentsString = prefs.getString(KEY_RECENT_TIMEZONES, null); |
| if (recentsString == null) { |
| recentsString = id; |
| } else { |
| // De-dup |
| LinkedHashSet<String> recents = new LinkedHashSet<String>(); |
| for(String tzId : recentsString.split(RECENT_TIMEZONES_DELIMITER)) { |
| if (!recents.contains(tzId) && !id.equals(tzId)) { |
| recents.add(tzId); |
| } |
| } |
| |
| Iterator<String> it = recents.iterator(); |
| while (recents.size() >= MAX_RECENT_TIMEZONES) { |
| if (!it.hasNext()) { |
| break; |
| } |
| it.next(); |
| it.remove(); |
| } |
| recents.add(id); |
| |
| StringBuilder builder = new StringBuilder(); |
| boolean first = true; |
| for (String recent : recents) { |
| if (first) { |
| first = false; |
| } else { |
| builder.append(RECENT_TIMEZONES_DELIMITER); |
| } |
| builder.append(recent); |
| } |
| recentsString = builder.toString(); |
| } |
| |
| prefs.edit().putString(KEY_RECENT_TIMEZONES, recentsString).apply(); |
| } |
| |
| @Override |
| public int getCount() { |
| return mFilteredTimeZoneLength; |
| } |
| |
| @Override |
| public Object getItem(int position) { |
| if (position < 0 || position >= mFilteredTimeZoneLength) { |
| return null; |
| } |
| |
| return mTimeZoneData.get(mFilteredTimeZoneIndices[position]); |
| } |
| |
| @Override |
| public boolean areAllItemsEnabled() { |
| return false; |
| } |
| |
| @Override |
| public boolean isEnabled(int position) { |
| return mFilteredTimeZoneIndices[position] >= 0; |
| } |
| |
| @Override |
| public long getItemId(int position) { |
| return mFilteredTimeZoneIndices[position]; |
| } |
| |
| @Override |
| public View getView(int position, View convertView, ViewGroup parent) { |
| View v = convertView; |
| |
| if (mFilteredTimeZoneIndices[position] == EMPTY_INDEX) { |
| v = mInflater.inflate(R.layout.empty_time_zone_item, null); |
| return v; |
| } |
| |
| // We'll need to re-inflate the view if it was null, or if it was used as an empty item. |
| if (v == null || v.findViewById(R.id.empty_item) != null) { |
| v = mInflater.inflate(R.layout.time_zone_item, null); |
| ViewHolder.setupViewHolder(v); |
| } |
| |
| ViewHolder vh = (ViewHolder) v.getTag(); |
| |
| TimeZoneInfo tzi = mTimeZoneData.get(mFilteredTimeZoneIndices[position]); |
| v.setTag(VIEW_TAG_TIME_ZONE, tzi); |
| |
| vh.timeZone.setText(tzi.mDisplayName); |
| |
| vh.timeOffset.setText(tzi.getGmtDisplayName(mContext)); |
| |
| String location = tzi.mCountry; |
| if (location == null) { |
| vh.location.setVisibility(View.INVISIBLE); |
| } else { |
| vh.location.setText(location); |
| vh.location.setVisibility(View.VISIBLE); |
| } |
| |
| return v; |
| } |
| |
| @Override |
| public boolean hasStableIds() { |
| return true; |
| } |
| |
| // Implements OnItemClickListener |
| @Override |
| public void onItemClick(AdapterView<?> parent, View v, int position, long id) { |
| if (mTimeZoneSetListener != null) { |
| TimeZoneInfo tzi = (TimeZoneInfo) v.getTag(VIEW_TAG_TIME_ZONE); |
| if (tzi != null) { |
| mTimeZoneSetListener.onTimeZoneSet(tzi); |
| saveRecentTimezone(tzi.mTzId); |
| } |
| } |
| } |
| } |