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 androidx.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