1b1b7080deea42aa533c3757b585cf765c6b76732Michael Chan/*
2b1b7080deea42aa533c3757b585cf765c6b76732Michael Chan * Copyright (C) 2013 The Android Open Source Project
3b1b7080deea42aa533c3757b585cf765c6b76732Michael Chan *
4b1b7080deea42aa533c3757b585cf765c6b76732Michael Chan * Licensed under the Apache License, Version 2.0 (the "License");
5b1b7080deea42aa533c3757b585cf765c6b76732Michael Chan * you may not use this file except in compliance with the License.
6b1b7080deea42aa533c3757b585cf765c6b76732Michael Chan * You may obtain a copy of the License at
7b1b7080deea42aa533c3757b585cf765c6b76732Michael Chan *
8b1b7080deea42aa533c3757b585cf765c6b76732Michael Chan *      http://www.apache.org/licenses/LICENSE-2.0
9b1b7080deea42aa533c3757b585cf765c6b76732Michael Chan *
10b1b7080deea42aa533c3757b585cf765c6b76732Michael Chan * Unless required by applicable law or agreed to in writing, software
11b1b7080deea42aa533c3757b585cf765c6b76732Michael Chan * distributed under the License is distributed on an "AS IS" BASIS,
12b1b7080deea42aa533c3757b585cf765c6b76732Michael Chan * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13b1b7080deea42aa533c3757b585cf765c6b76732Michael Chan * See the License for the specific language governing permissions and
14b1b7080deea42aa533c3757b585cf765c6b76732Michael Chan * limitations under the License.
15b1b7080deea42aa533c3757b585cf765c6b76732Michael Chan */
16b1b7080deea42aa533c3757b585cf765c6b76732Michael Chan
17b1b7080deea42aa533c3757b585cf765c6b76732Michael Chanpackage com.android.timezonepicker;
18b1b7080deea42aa533c3757b585cf765c6b76732Michael Chan
19b1b7080deea42aa533c3757b585cf765c6b76732Michael Chanimport android.content.Context;
20dba26e2a0025a404fba38a44500c0f072735a610Sam Blitzsteinimport android.graphics.drawable.Drawable;
21b1b7080deea42aa533c3757b585cf765c6b76732Michael Chanimport android.text.Editable;
22dba26e2a0025a404fba38a44500c0f072735a610Sam Blitzsteinimport android.text.Spannable;
23dba26e2a0025a404fba38a44500c0f072735a610Sam Blitzsteinimport android.text.SpannableStringBuilder;
24b1b7080deea42aa533c3757b585cf765c6b76732Michael Chanimport android.text.TextWatcher;
25dba26e2a0025a404fba38a44500c0f072735a610Sam Blitzsteinimport android.text.style.ImageSpan;
26b1b7080deea42aa533c3757b585cf765c6b76732Michael Chanimport android.util.AttributeSet;
27b1b7080deea42aa533c3757b585cf765c6b76732Michael Chanimport android.view.LayoutInflater;
28b1b7080deea42aa533c3757b585cf765c6b76732Michael Chanimport android.view.View;
29a8dd2dfa92587e5fc11e6c24da91be9e7bf3afb7James Kungimport android.view.View.OnClickListener;
3009a2165919cb9b67c95b7885357c78e20bf5d9fbSam Blitzsteinimport android.view.inputmethod.InputMethodManager;
31b1b7080deea42aa533c3757b585cf765c6b76732Michael Chanimport android.widget.AdapterView;
32b1b7080deea42aa533c3757b585cf765c6b76732Michael Chanimport android.widget.AdapterView.OnItemClickListener;
33b1b7080deea42aa533c3757b585cf765c6b76732Michael Chanimport android.widget.AutoCompleteTextView;
34dba26e2a0025a404fba38a44500c0f072735a610Sam Blitzsteinimport android.widget.ImageButton;
35b1b7080deea42aa533c3757b585cf765c6b76732Michael Chanimport android.widget.LinearLayout;
36b1b7080deea42aa533c3757b585cf765c6b76732Michael Chanimport android.widget.ListView;
37b1b7080deea42aa533c3757b585cf765c6b76732Michael Chan
38a8dd2dfa92587e5fc11e6c24da91be9e7bf3afb7James Kungpublic class TimeZonePickerView extends LinearLayout implements TextWatcher, OnItemClickListener,
39a8dd2dfa92587e5fc11e6c24da91be9e7bf3afb7James Kung    OnClickListener {
40b1b7080deea42aa533c3757b585cf765c6b76732Michael Chan    private static final String TAG = "TimeZonePickerView";
41b1b7080deea42aa533c3757b585cf765c6b76732Michael Chan
42b1b7080deea42aa533c3757b585cf765c6b76732Michael Chan    private Context mContext;
43b1b7080deea42aa533c3757b585cf765c6b76732Michael Chan    private AutoCompleteTextView mAutoCompleteTextView;
44b1b7080deea42aa533c3757b585cf765c6b76732Michael Chan    private TimeZoneFilterTypeAdapter mFilterAdapter;
45a8dd2dfa92587e5fc11e6c24da91be9e7bf3afb7James Kung    private boolean mHideFilterSearchOnStart = false;
46a8dd2dfa92587e5fc11e6c24da91be9e7bf3afb7James Kung    private boolean mFirstTime = true;
47b1b7080deea42aa533c3757b585cf765c6b76732Michael Chan    TimeZoneResultAdapter mResultAdapter;
48b1b7080deea42aa533c3757b585cf765c6b76732Michael Chan
49dba26e2a0025a404fba38a44500c0f072735a610Sam Blitzstein    private ImageButton mClearButton;
50dba26e2a0025a404fba38a44500c0f072735a610Sam Blitzstein
51b1b7080deea42aa533c3757b585cf765c6b76732Michael Chan    public interface OnTimeZoneSetListener {
52b1b7080deea42aa533c3757b585cf765c6b76732Michael Chan        void onTimeZoneSet(TimeZoneInfo tzi);
53b1b7080deea42aa533c3757b585cf765c6b76732Michael Chan    }
54b1b7080deea42aa533c3757b585cf765c6b76732Michael Chan
55b1b7080deea42aa533c3757b585cf765c6b76732Michael Chan    public TimeZonePickerView(Context context, AttributeSet attrs,
56a8dd2dfa92587e5fc11e6c24da91be9e7bf3afb7James Kung            String timeZone, long timeMillis, OnTimeZoneSetListener l,
57a8dd2dfa92587e5fc11e6c24da91be9e7bf3afb7James Kung            boolean hideFilterSearch) {
58b1b7080deea42aa533c3757b585cf765c6b76732Michael Chan        super(context, attrs);
59b1b7080deea42aa533c3757b585cf765c6b76732Michael Chan        mContext = context;
60b1b7080deea42aa533c3757b585cf765c6b76732Michael Chan        LayoutInflater inflater = (LayoutInflater) context.getSystemService(
61b1b7080deea42aa533c3757b585cf765c6b76732Michael Chan                Context.LAYOUT_INFLATER_SERVICE);
62b1b7080deea42aa533c3757b585cf765c6b76732Michael Chan        inflater.inflate(R.layout.timezonepickerview, this, true);
63b1b7080deea42aa533c3757b585cf765c6b76732Michael Chan
64a8dd2dfa92587e5fc11e6c24da91be9e7bf3afb7James Kung        mHideFilterSearchOnStart = hideFilterSearch;
65a8dd2dfa92587e5fc11e6c24da91be9e7bf3afb7James Kung
66b1b7080deea42aa533c3757b585cf765c6b76732Michael Chan        TimeZoneData tzd = new TimeZoneData(mContext, timeZone, timeMillis);
67b1b7080deea42aa533c3757b585cf765c6b76732Michael Chan
68b1b7080deea42aa533c3757b585cf765c6b76732Michael Chan        mResultAdapter = new TimeZoneResultAdapter(mContext, tzd, l);
69b1b7080deea42aa533c3757b585cf765c6b76732Michael Chan        ListView timeZoneList = (ListView) findViewById(R.id.timezonelist);
70b1b7080deea42aa533c3757b585cf765c6b76732Michael Chan        timeZoneList.setAdapter(mResultAdapter);
71b064db0d1a7ffcedddd691b35d57e2cdb0268883Michael Chan        timeZoneList.setOnItemClickListener(mResultAdapter);
72b1b7080deea42aa533c3757b585cf765c6b76732Michael Chan
73b1b7080deea42aa533c3757b585cf765c6b76732Michael Chan        mFilterAdapter = new TimeZoneFilterTypeAdapter(mContext, tzd, mResultAdapter);
74a8dd2dfa92587e5fc11e6c24da91be9e7bf3afb7James Kung
75a8dd2dfa92587e5fc11e6c24da91be9e7bf3afb7James Kung        mAutoCompleteTextView = (AutoCompleteTextView) findViewById(R.id.searchBox);
76b1b7080deea42aa533c3757b585cf765c6b76732Michael Chan        mAutoCompleteTextView.addTextChangedListener(this);
77b1b7080deea42aa533c3757b585cf765c6b76732Michael Chan        mAutoCompleteTextView.setOnItemClickListener(this);
78a8dd2dfa92587e5fc11e6c24da91be9e7bf3afb7James Kung        mAutoCompleteTextView.setOnClickListener(this);
79dba26e2a0025a404fba38a44500c0f072735a610Sam Blitzstein
80dba26e2a0025a404fba38a44500c0f072735a610Sam Blitzstein        updateHint(R.string.hint_time_zone_search, R.drawable.ic_search_holo_light);
81dba26e2a0025a404fba38a44500c0f072735a610Sam Blitzstein        mClearButton = (ImageButton) findViewById(R.id.clear_search);
82dba26e2a0025a404fba38a44500c0f072735a610Sam Blitzstein        mClearButton.setOnClickListener(new OnClickListener() {
83dba26e2a0025a404fba38a44500c0f072735a610Sam Blitzstein            @Override
84dba26e2a0025a404fba38a44500c0f072735a610Sam Blitzstein            public void onClick(View v) {
85dba26e2a0025a404fba38a44500c0f072735a610Sam Blitzstein                mAutoCompleteTextView.getEditableText().clear();
86dba26e2a0025a404fba38a44500c0f072735a610Sam Blitzstein            }
87dba26e2a0025a404fba38a44500c0f072735a610Sam Blitzstein        });
88dba26e2a0025a404fba38a44500c0f072735a610Sam Blitzstein    }
89dba26e2a0025a404fba38a44500c0f072735a610Sam Blitzstein
90a8dd2dfa92587e5fc11e6c24da91be9e7bf3afb7James Kung    public void showFilterResults(int type, String string, int time) {
91a8dd2dfa92587e5fc11e6c24da91be9e7bf3afb7James Kung        if (mResultAdapter != null) {
92a8dd2dfa92587e5fc11e6c24da91be9e7bf3afb7James Kung            mResultAdapter.onSetFilter(type, string, time);
93a8dd2dfa92587e5fc11e6c24da91be9e7bf3afb7James Kung        }
94a8dd2dfa92587e5fc11e6c24da91be9e7bf3afb7James Kung    }
95a8dd2dfa92587e5fc11e6c24da91be9e7bf3afb7James Kung
96a8dd2dfa92587e5fc11e6c24da91be9e7bf3afb7James Kung    public boolean hasResults() {
97a8dd2dfa92587e5fc11e6c24da91be9e7bf3afb7James Kung        return mResultAdapter != null && mResultAdapter.hasResults();
98a8dd2dfa92587e5fc11e6c24da91be9e7bf3afb7James Kung    }
99a8dd2dfa92587e5fc11e6c24da91be9e7bf3afb7James Kung
100a8dd2dfa92587e5fc11e6c24da91be9e7bf3afb7James Kung    public int getLastFilterType() {
101a8dd2dfa92587e5fc11e6c24da91be9e7bf3afb7James Kung        return mResultAdapter != null ? mResultAdapter.getLastFilterType() : -1;
102a8dd2dfa92587e5fc11e6c24da91be9e7bf3afb7James Kung    }
103a8dd2dfa92587e5fc11e6c24da91be9e7bf3afb7James Kung
104a8dd2dfa92587e5fc11e6c24da91be9e7bf3afb7James Kung    public String getLastFilterString() {
105a8dd2dfa92587e5fc11e6c24da91be9e7bf3afb7James Kung        return mResultAdapter != null ? mResultAdapter.getLastFilterString() : null;
106a8dd2dfa92587e5fc11e6c24da91be9e7bf3afb7James Kung    }
107a8dd2dfa92587e5fc11e6c24da91be9e7bf3afb7James Kung
108a8dd2dfa92587e5fc11e6c24da91be9e7bf3afb7James Kung    public int getLastFilterTime() {
109a8dd2dfa92587e5fc11e6c24da91be9e7bf3afb7James Kung        return mResultAdapter != null ? mResultAdapter.getLastFilterType() : -1;
110a8dd2dfa92587e5fc11e6c24da91be9e7bf3afb7James Kung    }
111a8dd2dfa92587e5fc11e6c24da91be9e7bf3afb7James Kung
112a8dd2dfa92587e5fc11e6c24da91be9e7bf3afb7James Kung    public boolean getHideFilterSearchOnStart() {
113a8dd2dfa92587e5fc11e6c24da91be9e7bf3afb7James Kung        return mHideFilterSearchOnStart;
114a8dd2dfa92587e5fc11e6c24da91be9e7bf3afb7James Kung    }
115a8dd2dfa92587e5fc11e6c24da91be9e7bf3afb7James Kung
116dba26e2a0025a404fba38a44500c0f072735a610Sam Blitzstein    private void updateHint(int hintTextId, int imageDrawableId) {
117dba26e2a0025a404fba38a44500c0f072735a610Sam Blitzstein        String hintText = getResources().getString(hintTextId);
118dba26e2a0025a404fba38a44500c0f072735a610Sam Blitzstein        Drawable searchIcon = getResources().getDrawable(imageDrawableId);
119dba26e2a0025a404fba38a44500c0f072735a610Sam Blitzstein
120dba26e2a0025a404fba38a44500c0f072735a610Sam Blitzstein        SpannableStringBuilder ssb = new SpannableStringBuilder("   "); // for the icon
121dba26e2a0025a404fba38a44500c0f072735a610Sam Blitzstein        ssb.append(hintText);
122dba26e2a0025a404fba38a44500c0f072735a610Sam Blitzstein        int textSize = (int) (mAutoCompleteTextView.getTextSize() * 1.25);
123dba26e2a0025a404fba38a44500c0f072735a610Sam Blitzstein        searchIcon.setBounds(0, 0, textSize, textSize);
124dba26e2a0025a404fba38a44500c0f072735a610Sam Blitzstein        ssb.setSpan(new ImageSpan(searchIcon), 1, 2, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
125dba26e2a0025a404fba38a44500c0f072735a610Sam Blitzstein        mAutoCompleteTextView.setHint(ssb);
126b1b7080deea42aa533c3757b585cf765c6b76732Michael Chan    }
127b1b7080deea42aa533c3757b585cf765c6b76732Michael Chan
128b1b7080deea42aa533c3757b585cf765c6b76732Michael Chan    // Implementation of TextWatcher
129b1b7080deea42aa533c3757b585cf765c6b76732Michael Chan    @Override
130b1b7080deea42aa533c3757b585cf765c6b76732Michael Chan    public void beforeTextChanged(CharSequence s, int start, int count, int after) {
131b1b7080deea42aa533c3757b585cf765c6b76732Michael Chan    }
132b1b7080deea42aa533c3757b585cf765c6b76732Michael Chan
133b1b7080deea42aa533c3757b585cf765c6b76732Michael Chan    // Implementation of TextWatcher
134b1b7080deea42aa533c3757b585cf765c6b76732Michael Chan    @Override
135b1b7080deea42aa533c3757b585cf765c6b76732Michael Chan    public void onTextChanged(CharSequence s, int start, int before, int count) {
136a8dd2dfa92587e5fc11e6c24da91be9e7bf3afb7James Kung        if (mFirstTime && mHideFilterSearchOnStart) {
137a8dd2dfa92587e5fc11e6c24da91be9e7bf3afb7James Kung            mFirstTime = false;
138a8dd2dfa92587e5fc11e6c24da91be9e7bf3afb7James Kung            return;
139a8dd2dfa92587e5fc11e6c24da91be9e7bf3afb7James Kung        }
140a8dd2dfa92587e5fc11e6c24da91be9e7bf3afb7James Kung        filterOnString(s.toString());
141b1b7080deea42aa533c3757b585cf765c6b76732Michael Chan    }
142b1b7080deea42aa533c3757b585cf765c6b76732Michael Chan
143b1b7080deea42aa533c3757b585cf765c6b76732Michael Chan    // Implementation of TextWatcher
144b1b7080deea42aa533c3757b585cf765c6b76732Michael Chan    @Override
145b1b7080deea42aa533c3757b585cf765c6b76732Michael Chan    public void afterTextChanged(Editable s) {
146dba26e2a0025a404fba38a44500c0f072735a610Sam Blitzstein        if (mClearButton != null) {
147dba26e2a0025a404fba38a44500c0f072735a610Sam Blitzstein            mClearButton.setVisibility(s.length() > 0 ? View.VISIBLE : View.GONE);
148dba26e2a0025a404fba38a44500c0f072735a610Sam Blitzstein        }
149b1b7080deea42aa533c3757b585cf765c6b76732Michael Chan    }
150b1b7080deea42aa533c3757b585cf765c6b76732Michael Chan
151b1b7080deea42aa533c3757b585cf765c6b76732Michael Chan    @Override
152b1b7080deea42aa533c3757b585cf765c6b76732Michael Chan    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
15309a2165919cb9b67c95b7885357c78e20bf5d9fbSam Blitzstein        // Hide the keyboard since the user explicitly selected an item.
15409a2165919cb9b67c95b7885357c78e20bf5d9fbSam Blitzstein        InputMethodManager manager =
15509a2165919cb9b67c95b7885357c78e20bf5d9fbSam Blitzstein                (InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
15609a2165919cb9b67c95b7885357c78e20bf5d9fbSam Blitzstein        manager.hideSoftInputFromWindow(mAutoCompleteTextView.getWindowToken(), 0);
157b1b7080deea42aa533c3757b585cf765c6b76732Michael Chan        // An onClickListener for the view item because I haven't figured out a
158b1b7080deea42aa533c3757b585cf765c6b76732Michael Chan        // way to update the AutoCompleteTextView without causing an infinite loop.
159a8dd2dfa92587e5fc11e6c24da91be9e7bf3afb7James Kung        mHideFilterSearchOnStart = true;
160b1b7080deea42aa533c3757b585cf765c6b76732Michael Chan        mFilterAdapter.onClick(view);
161b1b7080deea42aa533c3757b585cf765c6b76732Michael Chan    }
162a8dd2dfa92587e5fc11e6c24da91be9e7bf3afb7James Kung
163a8dd2dfa92587e5fc11e6c24da91be9e7bf3afb7James Kung    @Override
164a8dd2dfa92587e5fc11e6c24da91be9e7bf3afb7James Kung    public void onClick(View v) {
165a8dd2dfa92587e5fc11e6c24da91be9e7bf3afb7James Kung        if (mAutoCompleteTextView != null && !mAutoCompleteTextView.isPopupShowing()) {
166a8dd2dfa92587e5fc11e6c24da91be9e7bf3afb7James Kung            filterOnString(mAutoCompleteTextView.getText().toString());
167a8dd2dfa92587e5fc11e6c24da91be9e7bf3afb7James Kung        }
168a8dd2dfa92587e5fc11e6c24da91be9e7bf3afb7James Kung    }
169a8dd2dfa92587e5fc11e6c24da91be9e7bf3afb7James Kung
170a8dd2dfa92587e5fc11e6c24da91be9e7bf3afb7James Kung    // This method will set the adapter if no adapter has been set.  The adapter is initialized
171a8dd2dfa92587e5fc11e6c24da91be9e7bf3afb7James Kung    // here to prevent the drop-down from appearing uninvited on orientation change, as the
172a8dd2dfa92587e5fc11e6c24da91be9e7bf3afb7James Kung    // AutoCompleteTextView.setText() will trigger the drop-down if the adapter has been set.
173a8dd2dfa92587e5fc11e6c24da91be9e7bf3afb7James Kung    private void filterOnString(String string) {
174a8dd2dfa92587e5fc11e6c24da91be9e7bf3afb7James Kung        if (mAutoCompleteTextView.getAdapter() == null) {
175a8dd2dfa92587e5fc11e6c24da91be9e7bf3afb7James Kung            mAutoCompleteTextView.setAdapter(mFilterAdapter);
176a8dd2dfa92587e5fc11e6c24da91be9e7bf3afb7James Kung        }
177a8dd2dfa92587e5fc11e6c24da91be9e7bf3afb7James Kung        mHideFilterSearchOnStart = false;
178a8dd2dfa92587e5fc11e6c24da91be9e7bf3afb7James Kung        mFilterAdapter.getFilter().filter(string);
179a8dd2dfa92587e5fc11e6c24da91be9e7bf3afb7James Kung    }
180b1b7080deea42aa533c3757b585cf765c6b76732Michael Chan}
181