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 
17 package com.android.timezonepicker;
18 
19 import android.content.Context;
20 import android.graphics.drawable.Drawable;
21 import android.text.Editable;
22 import android.text.Spannable;
23 import android.text.SpannableStringBuilder;
24 import android.text.TextWatcher;
25 import android.text.style.ImageSpan;
26 import android.util.AttributeSet;
27 import android.view.LayoutInflater;
28 import android.view.View;
29 import android.view.View.OnClickListener;
30 import android.view.inputmethod.InputMethodManager;
31 import android.widget.AdapterView;
32 import android.widget.AdapterView.OnItemClickListener;
33 import android.widget.AutoCompleteTextView;
34 import android.widget.ImageButton;
35 import android.widget.LinearLayout;
36 import android.widget.ListView;
37 
38 public class TimeZonePickerView extends LinearLayout implements TextWatcher, OnItemClickListener,
39     OnClickListener {
40     private static final String TAG = "TimeZonePickerView";
41 
42     private Context mContext;
43     private AutoCompleteTextView mAutoCompleteTextView;
44     private TimeZoneFilterTypeAdapter mFilterAdapter;
45     private boolean mHideFilterSearchOnStart = false;
46     private boolean mFirstTime = true;
47     TimeZoneResultAdapter mResultAdapter;
48 
49     private ImageButton mClearButton;
50 
51     public interface OnTimeZoneSetListener {
onTimeZoneSet(TimeZoneInfo tzi)52         void onTimeZoneSet(TimeZoneInfo tzi);
53     }
54 
TimeZonePickerView(Context context, AttributeSet attrs, String timeZone, long timeMillis, OnTimeZoneSetListener l, boolean hideFilterSearch)55     public TimeZonePickerView(Context context, AttributeSet attrs,
56             String timeZone, long timeMillis, OnTimeZoneSetListener l,
57             boolean hideFilterSearch) {
58         super(context, attrs);
59         mContext = context;
60         LayoutInflater inflater = (LayoutInflater) context.getSystemService(
61                 Context.LAYOUT_INFLATER_SERVICE);
62         inflater.inflate(R.layout.timezonepickerview, this, true);
63 
64         mHideFilterSearchOnStart = hideFilterSearch;
65 
66         TimeZoneData tzd = new TimeZoneData(mContext, timeZone, timeMillis);
67 
68         mResultAdapter = new TimeZoneResultAdapter(mContext, tzd, l);
69         ListView timeZoneList = (ListView) findViewById(R.id.timezonelist);
70         timeZoneList.setAdapter(mResultAdapter);
71         timeZoneList.setOnItemClickListener(mResultAdapter);
72 
73         mFilterAdapter = new TimeZoneFilterTypeAdapter(mContext, tzd, mResultAdapter);
74 
75         mAutoCompleteTextView = (AutoCompleteTextView) findViewById(R.id.searchBox);
76         mAutoCompleteTextView.addTextChangedListener(this);
77         mAutoCompleteTextView.setOnItemClickListener(this);
78         mAutoCompleteTextView.setOnClickListener(this);
79 
80         updateHint(R.string.hint_time_zone_search, R.drawable.ic_search_holo_light);
81         mClearButton = (ImageButton) findViewById(R.id.clear_search);
82         mClearButton.setOnClickListener(new OnClickListener() {
83             @Override
84             public void onClick(View v) {
85                 mAutoCompleteTextView.getEditableText().clear();
86             }
87         });
88     }
89 
showFilterResults(int type, String string, int time)90     public void showFilterResults(int type, String string, int time) {
91         if (mResultAdapter != null) {
92             mResultAdapter.onSetFilter(type, string, time);
93         }
94     }
95 
hasResults()96     public boolean hasResults() {
97         return mResultAdapter != null && mResultAdapter.hasResults();
98     }
99 
getLastFilterType()100     public int getLastFilterType() {
101         return mResultAdapter != null ? mResultAdapter.getLastFilterType() : -1;
102     }
103 
getLastFilterString()104     public String getLastFilterString() {
105         return mResultAdapter != null ? mResultAdapter.getLastFilterString() : null;
106     }
107 
getLastFilterTime()108     public int getLastFilterTime() {
109         return mResultAdapter != null ? mResultAdapter.getLastFilterType() : -1;
110     }
111 
getHideFilterSearchOnStart()112     public boolean getHideFilterSearchOnStart() {
113         return mHideFilterSearchOnStart;
114     }
115 
updateHint(int hintTextId, int imageDrawableId)116     private void updateHint(int hintTextId, int imageDrawableId) {
117         String hintText = getResources().getString(hintTextId);
118         Drawable searchIcon = getResources().getDrawable(imageDrawableId);
119 
120         SpannableStringBuilder ssb = new SpannableStringBuilder("   "); // for the icon
121         ssb.append(hintText);
122         int textSize = (int) (mAutoCompleteTextView.getTextSize() * 1.25);
123         searchIcon.setBounds(0, 0, textSize, textSize);
124         ssb.setSpan(new ImageSpan(searchIcon), 1, 2, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
125         mAutoCompleteTextView.setHint(ssb);
126     }
127 
128     // Implementation of TextWatcher
129     @Override
beforeTextChanged(CharSequence s, int start, int count, int after)130     public void beforeTextChanged(CharSequence s, int start, int count, int after) {
131     }
132 
133     // Implementation of TextWatcher
134     @Override
onTextChanged(CharSequence s, int start, int before, int count)135     public void onTextChanged(CharSequence s, int start, int before, int count) {
136         if (mFirstTime && mHideFilterSearchOnStart) {
137             mFirstTime = false;
138             return;
139         }
140         filterOnString(s.toString());
141     }
142 
143     // Implementation of TextWatcher
144     @Override
afterTextChanged(Editable s)145     public void afterTextChanged(Editable s) {
146         if (mClearButton != null) {
147             mClearButton.setVisibility(s.length() > 0 ? View.VISIBLE : View.GONE);
148         }
149     }
150 
151     @Override
onItemClick(AdapterView<?> parent, View view, int position, long id)152     public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
153         // Hide the keyboard since the user explicitly selected an item.
154         InputMethodManager manager =
155                 (InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
156         manager.hideSoftInputFromWindow(mAutoCompleteTextView.getWindowToken(), 0);
157         // An onClickListener for the view item because I haven't figured out a
158         // way to update the AutoCompleteTextView without causing an infinite loop.
159         mHideFilterSearchOnStart = true;
160         mFilterAdapter.onClick(view);
161     }
162 
163     @Override
onClick(View v)164     public void onClick(View v) {
165         if (mAutoCompleteTextView != null && !mAutoCompleteTextView.isPopupShowing()) {
166             filterOnString(mAutoCompleteTextView.getText().toString());
167         }
168     }
169 
170     // This method will set the adapter if no adapter has been set.  The adapter is initialized
171     // here to prevent the drop-down from appearing uninvited on orientation change, as the
172     // AutoCompleteTextView.setText() will trigger the drop-down if the adapter has been set.
filterOnString(String string)173     private void filterOnString(String string) {
174         if (mAutoCompleteTextView.getAdapter() == null) {
175             mAutoCompleteTextView.setAdapter(mFilterAdapter);
176         }
177         mHideFilterSearchOnStart = false;
178         mFilterAdapter.getFilter().filter(string);
179     }
180 }
181