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 
18 package com.android.mail.ui;
19 
20 import android.content.Context;
21 import android.content.res.Resources;
22 import android.graphics.drawable.ColorDrawable;
23 import android.graphics.drawable.Drawable;
24 import android.support.annotation.DrawableRes;
25 import android.text.Editable;
26 import android.text.TextWatcher;
27 import android.util.AttributeSet;
28 import android.view.ActionMode;
29 import android.view.KeyEvent;
30 import android.view.Menu;
31 import android.view.MenuItem;
32 import android.view.View;
33 import android.view.ViewGroup;
34 import android.view.inputmethod.EditorInfo;
35 import android.view.inputmethod.InputMethodManager;
36 import android.widget.EditText;
37 import android.widget.ImageView;
38 import android.widget.LinearLayout;
39 import android.widget.TextView;
40 
41 import com.android.mail.R;
42 import com.android.mail.utils.ViewUtils;
43 
44 /**
45  * Custom view for the action bar when search is displayed.
46  */
47 public 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 
MaterialSearchActionView(Context context)72     public MaterialSearchActionView(Context context) {
73         this(context, null);
74     }
75 
MaterialSearchActionView(Context context, AttributeSet attrs)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_wht_24dp;
88         mLightBgTextColor = res.getColor(R.color.search_query_text);
89         mDarkBgTextColor = res.getColor(android.R.color.white);
90     }
91 
92     // PUBLIC API
setController(MaterialSearchViewController controller, String initialQuery, boolean supportVoice)93     public void setController(MaterialSearchViewController controller, String initialQuery,
94             boolean supportVoice) {
95         mController = controller;
96         mQueryText.setText(initialQuery);
97         mSupportVoice = supportVoice;
98     }
99 
clearSearchQuery()100     public void clearSearchQuery() {
101         mQueryText.setText("");
102     }
103 
focusSearchBar(boolean hasFocus)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 
adjustViewForTwoPaneLandscape(boolean alignWithTl, int xEnd)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
onFinishInflate()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 
setupEndingButton(CharSequence currentText)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
beforeTextChanged(CharSequence charSequence, int i, int i2, int i3)198     public void beforeTextChanged(CharSequence charSequence, int i, int i2, int i3) {
199         // Only care about onTextChanged
200     }
201 
202     @Override
onTextChanged(CharSequence charSequence, int i, int i2, int i3)203     public void onTextChanged(CharSequence charSequence, int i, int i2, int i3) {
204         mController.onQueryTextChanged(charSequence.toString());
205         setupEndingButton(charSequence);
206     }
207 
208     @Override
afterTextChanged(Editable editable)209     public void afterTextChanged(Editable editable) {
210         // Only care about onTextChanged
211     }
212 
213     @Override
onClick(View view)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
onEditorAction(TextView textView, int actionId, KeyEvent keyEvent)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
onKey(View v, int keyCode, KeyEvent event)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