MaterialSearchActionView.java revision 54d5b6103887f92c922a4cb31915142e77519abd
1/*
2 * Copyright (C) 2014 Google Inc.
3 * Licensed to The Android Open Source Project.
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 *      http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18package com.android.mail.ui;
19
20import android.content.Context;
21import android.content.res.Resources;
22import android.graphics.drawable.ColorDrawable;
23import android.graphics.drawable.Drawable;
24import android.support.annotation.DrawableRes;
25import android.text.Editable;
26import android.text.TextWatcher;
27import android.util.AttributeSet;
28import android.view.ActionMode;
29import android.view.KeyEvent;
30import android.view.Menu;
31import android.view.MenuItem;
32import android.view.View;
33import android.view.ViewGroup;
34import android.view.inputmethod.EditorInfo;
35import android.view.inputmethod.InputMethodManager;
36import android.widget.EditText;
37import android.widget.ImageView;
38import android.widget.LinearLayout;
39import android.widget.TextView;
40
41import com.android.mail.R;
42import com.android.mail.utils.ViewUtils;
43
44/**
45 * Custom view for the action bar when search is displayed.
46 */
47public class MaterialSearchActionView extends LinearLayout implements TextWatcher,
48        View.OnClickListener, TextView.OnEditorActionListener, View.OnKeyListener {
49    // Dark drawables are used for when the search bar is visible (thus dark icon on light bg).
50    // Light drawables are used when we are showing the default action bar.
51    private Drawable mLightBgDrawable;
52    private Drawable mDarkBgDrawable;
53    private @DrawableRes int mLightBgBackDrawable;
54    private @DrawableRes int mDarkBgBackDrawable;
55    private @DrawableRes int mLightBgClearTextDrawable;
56    private @DrawableRes int mDarkBgClearTextDrawable;
57    private @DrawableRes int mLightBgVoiceDrawable;
58    private @DrawableRes int mDarkBgVoiceDrawable;
59    private int mLightBgTextColor;
60    private int mDarkBgTextColor;
61
62    private MaterialSearchViewController mController;
63    private InputMethodManager mImm;
64    private boolean mSupportVoice;
65    private boolean mShowingClearButton;
66    private boolean mAlignWithTl;
67
68    private ImageView mBackButton;
69    private EditText mQueryText;
70    private ImageView mEndingButton;
71
72    public MaterialSearchActionView(Context context) {
73        this(context, null);
74    }
75
76    public MaterialSearchActionView(Context context, AttributeSet attrs) {
77        super(context, attrs);
78
79        final Resources res = getResources();
80        mLightBgDrawable = new ColorDrawable(res.getColor(android.R.color.white));
81        mDarkBgDrawable = new ColorDrawable(res.getColor(R.color.primary_color));
82        mLightBgBackDrawable = R.drawable.ic_arrow_back_24dp_with_rtl;
83        mDarkBgBackDrawable = R.drawable.ic_arrow_back_wht_24dp_with_rtl;
84        mLightBgClearTextDrawable = R.drawable.ic_close_24dp;
85        mDarkBgClearTextDrawable = R.drawable.ic_close_wht_24dp;
86        mLightBgVoiceDrawable = R.drawable.ic_mic_24dp;
87        mDarkBgVoiceDrawable = R.drawable.ic_mic_white_24dp;
88        mLightBgTextColor = res.getColor(R.color.search_query_text);
89        mDarkBgTextColor = res.getColor(android.R.color.white);
90    }
91
92    // PUBLIC API
93    public void setController(MaterialSearchViewController controller, String initialQuery,
94            boolean supportVoice) {
95        mController = controller;
96        mQueryText.setText(initialQuery);
97        mSupportVoice = supportVoice;
98    }
99
100    public void clearSearchQuery() {
101        mQueryText.setText("");
102    }
103
104    public void focusSearchBar(boolean hasFocus) {
105        if (hasFocus) {
106            mQueryText.requestFocus();
107            mImm.showSoftInput(mQueryText, 0);
108        } else {
109            mImm.hideSoftInputFromWindow(mQueryText.getWindowToken(), 0);
110        }
111    }
112
113    public void adjustViewForTwoPaneLandscape(boolean alignWithTl, int xEnd) {
114        mAlignWithTl = alignWithTl;
115        final ViewGroup.LayoutParams params = getLayoutParams();
116        if (alignWithTl) {
117            setBackgroundDrawable(mDarkBgDrawable);
118            mBackButton.setImageResource(mDarkBgBackDrawable);
119            mQueryText.setTextColor(mDarkBgTextColor);
120
121            if (ViewUtils.isViewRtl(this)) {
122                int[] coords = new int[2];
123                getLocationInWindow(coords);
124                params.width = coords[0] + getWidth() - xEnd;
125            } else {
126                params.width = xEnd;
127            }
128        } else {
129            setBackgroundDrawable(mLightBgDrawable);
130            mBackButton.setImageResource(mLightBgBackDrawable);
131            mQueryText.setTextColor(mLightBgTextColor);
132            params.width = ViewGroup.LayoutParams.MATCH_PARENT;
133        }
134        setupEndingButton(mQueryText.getText());
135        setLayoutParams(params);
136    }
137
138    @Override
139    protected void onFinishInflate() {
140        super.onFinishInflate();
141
142        mImm = (InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
143        mBackButton = (ImageView) findViewById(R.id.search_actionbar_back_button);
144        mBackButton.setOnClickListener(this);
145        mQueryText = (EditText) findViewById(R.id.search_actionbar_query_text);
146        mQueryText.addTextChangedListener(this);
147        mQueryText.setOnClickListener(this);
148        mQueryText.setOnEditorActionListener(this);
149        mQueryText.setOnKeyListener(this);
150        // Disable CAB for search edittext
151        mQueryText.setCustomSelectionActionModeCallback(new ActionMode.Callback() {
152            @Override
153            public boolean onCreateActionMode(ActionMode mode, Menu menu) {
154                return false;
155            }
156
157            @Override
158            public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
159                return false;
160            }
161
162            @Override
163            public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
164                return false;
165            }
166
167            @Override
168            public void onDestroyActionMode(ActionMode mode) {
169            }
170        });
171        mEndingButton = (ImageView) findViewById(R.id.search_actionbar_ending_button);
172        mEndingButton.setOnClickListener(this);
173        setupEndingButton(mQueryText.getText());
174    }
175
176    private void setupEndingButton(CharSequence currentText) {
177        final Resources res = getResources();
178        if (!mSupportVoice || currentText.length() > 0) {
179            if (mAlignWithTl) {
180                mEndingButton.setImageResource(mDarkBgClearTextDrawable);
181            } else {
182                mEndingButton.setImageResource(mLightBgClearTextDrawable);
183            }
184            mEndingButton.setContentDescription(res.getString(R.string.search_clear_desc));
185            mShowingClearButton = true;
186        } else {
187            if (mAlignWithTl) {
188                mEndingButton.setImageResource(mDarkBgVoiceDrawable);
189            } else {
190                mEndingButton.setImageResource(mLightBgVoiceDrawable);
191            }
192            mEndingButton.setContentDescription(res.getString(R.string.search_voice_desc));
193            mShowingClearButton = false;
194        }
195    }
196
197    @Override
198    public void beforeTextChanged(CharSequence charSequence, int i, int i2, int i3) {
199        // Only care about onTextChanged
200    }
201
202    @Override
203    public void onTextChanged(CharSequence charSequence, int i, int i2, int i3) {
204        mController.onQueryTextChanged(charSequence.toString());
205        setupEndingButton(charSequence);
206    }
207
208    @Override
209    public void afterTextChanged(Editable editable) {
210        // Only care about onTextChanged
211    }
212
213    @Override
214    public void onClick(View view) {
215        if (view == mBackButton) {
216            mController.onSearchCanceled();
217        } else if (view == mEndingButton) {
218            if (mShowingClearButton) {
219                mQueryText.setText("");
220                mController.showSearchActionBar(
221                        MaterialSearchViewController.SEARCH_VIEW_STATE_VISIBLE);
222            } else {
223                mController.onVoiceSearch();
224            }
225        } else if (view == mQueryText) {
226            mController.showSearchActionBar(MaterialSearchViewController.SEARCH_VIEW_STATE_VISIBLE);
227        }
228    }
229
230    @Override
231    public boolean onEditorAction(TextView textView, int actionId, KeyEvent keyEvent) {
232        if (actionId == EditorInfo.IME_ACTION_SEARCH) {
233            mController.onSearchPerformed(mQueryText.getText().toString());
234        }
235        return false;
236    }
237
238    @Override
239    public boolean onKey(View v, int keyCode, KeyEvent event) {
240        // Hardware keyboard doesn't represent Enter as Search through imeOptions, so we need to
241        // capture them manually here.
242        if (event.getAction() == KeyEvent.ACTION_UP && keyCode == KeyEvent.KEYCODE_ENTER) {
243            mController.onSearchPerformed(mQueryText.getText().toString());
244        }
245        return false;
246    }
247}
248