blob: 1bfd832fdab909ce11f100d81ec3091e4dbc83f2 [file] [log] [blame]
Dmitri Plotnikov08e1c4e2010-07-02 17:57:51 -07001/*
Dmitri Plotnikovfcfdffb2010-07-15 18:00:55 -07002 * Copyright (C) 2010 The Android Open Source Project
Dmitri Plotnikov08e1c4e2010-07-02 17:57:51 -07003 *
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
17package com.android.contacts.activities;
18
Wenyi Wang11ff39a2016-08-07 15:46:03 -070019import android.animation.ArgbEvaluator;
Brian Attwell3c763b32014-07-21 14:25:28 -070020import android.animation.ValueAnimator;
Brian Attwell20510ec2015-02-27 16:10:45 -080021import android.app.Activity;
Dmitri Plotnikov08e1c4e2010-07-02 17:57:51 -070022import android.content.Context;
Wenyi Wangdb662692016-07-14 16:07:44 -070023import android.content.res.ColorStateList;
Dmitri Plotnikov08e1c4e2010-07-02 17:57:51 -070024import android.os.Bundle;
Wenyi Wang23d9b6e2015-11-19 15:09:06 -080025import android.support.v4.content.ContextCompat;
Wenyi Wang3c3b6f12016-04-02 13:58:43 -070026import android.support.v7.app.ActionBar;
27import android.support.v7.widget.Toolbar;
Brian Attwell6adbf1e2014-05-30 16:44:51 -070028import android.text.Editable;
Dmitri Plotnikov15ccbb42010-10-05 17:29:07 -070029import android.text.TextUtils;
Brian Attwell6adbf1e2014-05-30 16:44:51 -070030import android.text.TextWatcher;
Brian Attwell20510ec2015-02-27 16:10:45 -080031import android.view.Gravity;
Brian Attwell3c763b32014-07-21 14:25:28 -070032import android.view.LayoutInflater;
Katherine Kuan1ee9df62011-06-11 16:54:01 -070033import android.view.View;
Gary Mai0a49afa2016-12-05 15:53:58 -080034import android.view.View.OnClickListener;
Brian Attwell3c763b32014-07-21 14:25:28 -070035import android.view.ViewGroup;
Wenyi Wangdb662692016-07-14 16:07:44 -070036import android.view.ViewTreeObserver;
Wenyi Wang11ff39a2016-08-07 15:46:03 -070037import android.view.Window;
Katherine Kuand8a61b72011-07-27 13:57:13 -070038import android.view.inputmethod.InputMethodManager;
Gary Mai0a49afa2016-12-05 15:53:58 -080039import android.widget.EditText;
Brian Attwell20510ec2015-02-27 16:10:45 -080040import android.widget.FrameLayout;
John Shaobd9ef3c2016-12-15 12:42:03 -080041import android.widget.ImageButton;
Wenyi Wangdb662692016-07-14 16:07:44 -070042import android.widget.ImageView;
Brian Attwell20510ec2015-02-27 16:10:45 -080043import android.widget.LinearLayout.LayoutParams;
Dmitri Plotnikov1a59cff2010-09-18 12:50:45 -070044import android.widget.SearchView.OnCloseListener;
Brian Attwell20510ec2015-02-27 16:10:45 -080045import android.widget.TextView;
Dmitri Plotnikov92a6fdf2010-07-09 13:19:33 -070046
Wenyi Wang92d7e512016-10-03 14:57:19 -070047import com.android.contacts.ContactsDrawerActivity;
Chiao Chenge0b2f1e2012-06-12 13:07:56 -070048import com.android.contacts.R;
49import com.android.contacts.activities.ActionBarAdapter.Listener.Action;
Gary Mai69c182a2016-12-05 13:07:03 -080050import com.android.contacts.compat.CompatUtils;
Chiao Chenge0b2f1e2012-06-12 13:07:56 -070051import com.android.contacts.list.ContactsRequest;
Gary Mai0a49afa2016-12-05 15:53:58 -080052import com.android.contacts.util.MaterialColorMapUtils;
Chiao Chenge0b2f1e2012-06-12 13:07:56 -070053
Wenyi Wangdb662692016-07-14 16:07:44 -070054import java.util.ArrayList;
55
Dmitri Plotnikov08e1c4e2010-07-02 17:57:51 -070056/**
Dmitri Plotnikovfcfdffb2010-07-15 18:00:55 -070057 * Adapter for the action bar at the top of the Contacts activity.
Dmitri Plotnikov08e1c4e2010-07-02 17:57:51 -070058 */
Brian Attwell6adbf1e2014-05-30 16:44:51 -070059public class ActionBarAdapter implements OnCloseListener {
Dmitri Plotnikov08e1c4e2010-07-02 17:57:51 -070060
61 public interface Listener {
Daniel Lehmann311882a2012-04-27 22:31:38 -070062 public abstract class Action {
63 public static final int CHANGE_SEARCH_QUERY = 0;
64 public static final int START_SEARCH_MODE = 1;
Brian Attwell20510ec2015-02-27 16:10:45 -080065 public static final int START_SELECTION_MODE = 2;
66 public static final int STOP_SEARCH_AND_SELECTION_MODE = 3;
Brian Attwell865847b2015-04-20 14:40:17 -070067 public static final int BEGIN_STOPPING_SEARCH_AND_SELECTION_MODE = 4;
Katherine Kuanb5760b92011-06-01 16:19:40 -070068 }
69
Daniel Lehmann311882a2012-04-27 22:31:38 -070070 void onAction(int action);
Makoto Onuki4d788fc2011-07-12 10:00:10 -070071
Brian Attwell6adbf1e2014-05-30 16:44:51 -070072 void onUpButtonPressed();
Dmitri Plotnikov08e1c4e2010-07-02 17:57:51 -070073 }
74
Dmitri Plotnikov15ccbb42010-10-05 17:29:07 -070075 private static final String EXTRA_KEY_SEARCH_MODE = "navBar.searchMode";
Dmitri Plotnikov08e1c4e2010-07-02 17:57:51 -070076 private static final String EXTRA_KEY_QUERY = "navBar.query";
Brian Attwell20510ec2015-02-27 16:10:45 -080077 private static final String EXTRA_KEY_SELECTED_MODE = "navBar.selectionMode";
Dmitri Plotnikov08e1c4e2010-07-02 17:57:51 -070078
Brian Attwell20510ec2015-02-27 16:10:45 -080079 private boolean mSelectionMode;
Dmitri Plotnikov15ccbb42010-10-05 17:29:07 -070080 private boolean mSearchMode;
Dmitri Plotnikov08e1c4e2010-07-02 17:57:51 -070081 private String mQueryString;
Dmitri Plotnikov15ccbb42010-10-05 17:29:07 -070082
Brian Attwell6adbf1e2014-05-30 16:44:51 -070083 private EditText mSearchView;
Andrew Leead6e0ee2015-05-05 17:47:42 -070084 private View mClearSearchView;
Brian Attwell6adbf1e2014-05-30 16:44:51 -070085 private View mSearchContainer;
Brian Attwell20510ec2015-02-27 16:10:45 -080086 private View mSelectionContainer;
Dmitri Plotnikov92a6fdf2010-07-09 13:19:33 -070087
Brian Attwell3c763b32014-07-21 14:25:28 -070088 private int mMaxToolbarContentInsetStart;
Wenyi Wangbd114972016-07-22 10:42:40 -070089 private int mActionBarAnimationDuration;
Brian Attwell3c763b32014-07-21 14:25:28 -070090
Brian Attwell20510ec2015-02-27 16:10:45 -080091 private final Activity mActivity;
Dmitri Plotnikov08e1c4e2010-07-02 17:57:51 -070092
93 private Listener mListener;
Dmitri Plotnikov08e1c4e2010-07-02 17:57:51 -070094
Makoto Onuki4d788fc2011-07-12 10:00:10 -070095 private final ActionBar mActionBar;
Brian Attwell3c763b32014-07-21 14:25:28 -070096 private final Toolbar mToolbar;
Brian Attwell20510ec2015-02-27 16:10:45 -080097 /**
98 * Frame that contains the toolbar and draws the toolbar's background color. This is useful
99 * for placing things behind the toolbar.
100 */
101 private final FrameLayout mToolBarFrame;
Dmitri Plotnikov8b826462010-11-17 14:52:24 -0800102
Makoto Onukie0c66af2011-07-13 14:24:03 -0700103 private boolean mShowHomeIcon;
Walter Jang59087d32016-05-17 13:52:15 -0700104 private boolean mShowHomeAsUp;
Makoto Onukie0c66af2011-07-13 14:24:03 -0700105
Walter Jang68a0be72016-05-20 10:36:28 -0700106 private int mSearchHintResId;
107
Wenyi Wang11ff39a2016-08-07 15:46:03 -0700108 private ValueAnimator mStatusBarAnimator;
109
Brian Attwell20510ec2015-02-27 16:10:45 -0800110 public ActionBarAdapter(Activity activity, Listener listener, ActionBar actionBar,
Wenyi Wangca6018d2016-08-18 17:23:13 -0700111 Toolbar toolbar) {
112 this(activity, listener, actionBar, toolbar, R.string.hint_findContacts);
Walter Jang68a0be72016-05-20 10:36:28 -0700113 }
114
115 public ActionBarAdapter(Activity activity, Listener listener, ActionBar actionBar,
Wenyi Wangca6018d2016-08-18 17:23:13 -0700116 Toolbar toolbar, int searchHintResId) {
Brian Attwell20510ec2015-02-27 16:10:45 -0800117 mActivity = activity;
Makoto Onuki4d788fc2011-07-12 10:00:10 -0700118 mListener = listener;
119 mActionBar = actionBar;
Brian Attwell3c763b32014-07-21 14:25:28 -0700120 mToolbar = toolbar;
Brian Attwell20510ec2015-02-27 16:10:45 -0800121 mToolBarFrame = (FrameLayout) mToolbar.getParent();
Brian Attwell3c763b32014-07-21 14:25:28 -0700122 mMaxToolbarContentInsetStart = mToolbar.getContentInsetStart();
Walter Jang68a0be72016-05-20 10:36:28 -0700123 mSearchHintResId = searchHintResId;
Wenyi Wangbd114972016-07-22 10:42:40 -0700124 mActionBarAnimationDuration =
125 mActivity.getResources().getInteger(R.integer.action_bar_animation_duration);
Makoto Onukie0c66af2011-07-13 14:24:03 -0700126
Brian Attwell20510ec2015-02-27 16:10:45 -0800127 setupSearchAndSelectionViews();
Maurice Chu7f5220e2012-05-14 10:00:28 -0700128 }
129
Walter Jang59087d32016-05-17 13:52:15 -0700130 public void setShowHomeIcon(boolean showHomeIcon) {
131 mShowHomeIcon = showHomeIcon;
132 }
133
134 public void setShowHomeAsUp(boolean showHomeAsUp) {
135 mShowHomeAsUp = showHomeAsUp;
136 }
137
Wenyi Wangf5493a62016-07-11 14:28:52 -0700138 public View getSelectionContainer() {
139 return mSelectionContainer;
140 }
141
Brian Attwell20510ec2015-02-27 16:10:45 -0800142 private void setupSearchAndSelectionViews() {
Brian Attwell3c763b32014-07-21 14:25:28 -0700143 final LayoutInflater inflater = (LayoutInflater) mToolbar.getContext().getSystemService(
144 Context.LAYOUT_INFLATER_SERVICE);
Brian Attwell20510ec2015-02-27 16:10:45 -0800145
146 // Setup search bar
Brian Attwell3c763b32014-07-21 14:25:28 -0700147 mSearchContainer = inflater.inflate(R.layout.search_bar_expanded, mToolbar,
148 /* attachToRoot = */ false);
149 mSearchContainer.setVisibility(View.VISIBLE);
150 mToolbar.addView(mSearchContainer);
Brian Attwell20510ec2015-02-27 16:10:45 -0800151 mSearchContainer.setBackgroundColor(mActivity.getResources().getColor(
Brian Attwell6adbf1e2014-05-30 16:44:51 -0700152 R.color.searchbox_background_color));
153 mSearchView = (EditText) mSearchContainer.findViewById(R.id.search_view);
Walter Jang68a0be72016-05-20 10:36:28 -0700154 mSearchView.setHint(mActivity.getString(mSearchHintResId));
Brian Attwell6adbf1e2014-05-30 16:44:51 -0700155 mSearchView.addTextChangedListener(new SearchTextWatcher());
John Shaobd9ef3c2016-12-15 12:42:03 -0800156 final ImageButton searchBackButton = (ImageButton) mSearchContainer
157 .findViewById(R.id.search_back_button);
158 searchBackButton.setOnClickListener(
Brian Attwell6adbf1e2014-05-30 16:44:51 -0700159 new OnClickListener() {
160 @Override
161 public void onClick(View v) {
162 if (mListener != null) {
163 mListener.onUpButtonPressed();
164 }
165 }
166 });
John Shaobd9ef3c2016-12-15 12:42:03 -0800167 searchBackButton.getDrawable().setAutoMirrored(true);
Brian Attwell20510ec2015-02-27 16:10:45 -0800168
Andrew Leead6e0ee2015-05-05 17:47:42 -0700169 mClearSearchView = mSearchContainer.findViewById(R.id.search_close_button);
170 mClearSearchView.setOnClickListener(
171 new OnClickListener() {
172 @Override
173 public void onClick(View v) {
174 setQueryString(null);
175 }
176 });
177
Brian Attwell20510ec2015-02-27 16:10:45 -0800178 // Setup selection bar
179 mSelectionContainer = inflater.inflate(R.layout.selection_bar, mToolbar,
180 /* attachToRoot = */ false);
181 // Insert the selection container into mToolBarFrame behind the Toolbar, so that
182 // the Toolbar's MenuItems can appear on top of the selection container.
183 mToolBarFrame.addView(mSelectionContainer, 0);
184 mSelectionContainer.findViewById(R.id.selection_close).setOnClickListener(
185 new OnClickListener() {
186 @Override
187 public void onClick(View v) {
188 if (mListener != null) {
189 mListener.onUpButtonPressed();
190 }
191 }
192 });
Brian Attwell6adbf1e2014-05-30 16:44:51 -0700193 }
194
Makoto Onuki4d788fc2011-07-12 10:00:10 -0700195 public void initialize(Bundle savedState, ContactsRequest request) {
196 if (savedState == null) {
197 mSearchMode = request.isSearchMode();
198 mQueryString = request.getQueryString();
Brian Attwelld2962a32015-03-02 14:48:50 -0800199 mSelectionMode = false;
Makoto Onuki4d788fc2011-07-12 10:00:10 -0700200 } else {
201 mSearchMode = savedState.getBoolean(EXTRA_KEY_SEARCH_MODE);
Brian Attwell20510ec2015-02-27 16:10:45 -0800202 mSelectionMode = savedState.getBoolean(EXTRA_KEY_SELECTED_MODE);
Makoto Onuki4d788fc2011-07-12 10:00:10 -0700203 mQueryString = savedState.getString(EXTRA_KEY_QUERY);
Brian Attwell26dc5592014-02-07 17:10:03 -0800204 }
Katherine Kuan3c877e32012-01-03 17:27:36 -0800205 // Show tabs or the expanded {@link SearchView}, depending on whether or not we are in
206 // search mode.
Brian Attwell6adbf1e2014-05-30 16:44:51 -0700207 update(true /* skipAnimation */);
Katherine Kuan3c877e32012-01-03 17:27:36 -0800208 // Expanding the {@link SearchView} clears the query, so set the query from the
209 // {@link ContactsRequest} after it has been expanded, if applicable.
210 if (mSearchMode && !TextUtils.isEmpty(mQueryString)) {
211 setQueryString(mQueryString);
212 }
Dmitri Plotnikov8b826462010-11-17 14:52:24 -0800213 }
214
Dmitri Plotnikov08e1c4e2010-07-02 17:57:51 -0700215 public void setListener(Listener listener) {
216 mListener = listener;
217 }
218
Brian Attwell6adbf1e2014-05-30 16:44:51 -0700219 private class SearchTextWatcher implements TextWatcher {
220
221 @Override
222 public void onTextChanged(CharSequence queryString, int start, int before, int count) {
223 if (queryString.equals(mQueryString)) {
224 return;
225 }
226 mQueryString = queryString.toString();
227 if (!mSearchMode) {
228 if (!TextUtils.isEmpty(queryString)) {
229 setSearchMode(true);
230 }
231 } else if (mListener != null) {
232 mListener.onAction(Action.CHANGE_SEARCH_QUERY);
233 }
Andrew Leead6e0ee2015-05-05 17:47:42 -0700234 mClearSearchView.setVisibility(
235 TextUtils.isEmpty(queryString) ? View.GONE : View.VISIBLE);
Brian Attwell6adbf1e2014-05-30 16:44:51 -0700236 }
237
238 @Override
239 public void afterTextChanged(Editable s) {}
240
241 @Override
242 public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
243 }
244
Makoto Onuki4d788fc2011-07-12 10:00:10 -0700245 /**
Makoto Onuki1db00f62011-10-19 12:40:12 -0700246 * @return Whether in search mode, i.e. if the search view is visible/expanded.
247 *
248 * Note even if the action bar is in search mode, if the query is empty, the search fragment
249 * will not be in search mode.
250 */
Dmitri Plotnikov15ccbb42010-10-05 17:29:07 -0700251 public boolean isSearchMode() {
252 return mSearchMode;
253 }
254
Brian Attwell20510ec2015-02-27 16:10:45 -0800255 /**
256 * @return Whether in selection mode, i.e. if the selection view is visible/expanded.
257 */
258 public boolean isSelectionMode() {
259 return mSelectionMode;
260 }
261
Dmitri Plotnikov15ccbb42010-10-05 17:29:07 -0700262 public void setSearchMode(boolean flag) {
263 if (mSearchMode != flag) {
264 mSearchMode = flag;
Brian Attwell6adbf1e2014-05-30 16:44:51 -0700265 update(false /* skipAnimation */);
Katherine Kuanf3f933a2011-06-10 11:34:54 -0700266 if (mSearchView == null) {
267 return;
268 }
Dmitri Plotnikov0b51a8d2010-10-21 21:22:52 -0700269 if (mSearchMode) {
Brian Attwell87901e02014-12-01 19:11:31 -0800270 mSearchView.setEnabled(true);
Makoto Onuki4ba903c2011-07-07 16:55:17 -0700271 setFocusOnSearchView();
Brian Attwell87901e02014-12-01 19:11:31 -0800272 } else {
273 // Disable search view, so that it doesn't keep the IME visible.
274 mSearchView.setEnabled(false);
Dmitri Plotnikov0b51a8d2010-10-21 21:22:52 -0700275 }
Brian Attwellb3944dc2014-11-18 20:53:59 -0800276 setQueryString(null);
Daniel Lehmann311882a2012-04-27 22:31:38 -0700277 } else if (flag) {
278 // Everything is already set up. Still make sure the keyboard is up
279 if (mSearchView != null) setFocusOnSearchView();
Dmitri Plotnikov08e1c4e2010-07-02 17:57:51 -0700280 }
281 }
282
Brian Attwell20510ec2015-02-27 16:10:45 -0800283 public void setSelectionMode(boolean flag) {
284 if (mSelectionMode != flag) {
285 mSelectionMode = flag;
286 update(false /* skipAnimation */);
287 }
288 }
289
Dmitri Plotnikov08e1c4e2010-07-02 17:57:51 -0700290 public String getQueryString() {
Makoto Onuki1db00f62011-10-19 12:40:12 -0700291 return mSearchMode ? mQueryString : null;
Dmitri Plotnikov08e1c4e2010-07-02 17:57:51 -0700292 }
293
294 public void setQueryString(String query) {
295 mQueryString = query;
Katherine Kuanf3f933a2011-06-10 11:34:54 -0700296 if (mSearchView != null) {
Brian Attwell6adbf1e2014-05-30 16:44:51 -0700297 mSearchView.setText(query);
Brian Attwellb3944dc2014-11-18 20:53:59 -0800298 // When programmatically entering text into the search view, the most reasonable
299 // place for the cursor is after all the text.
300 mSearchView.setSelection(mSearchView.getText() == null ?
301 0 : mSearchView.getText().length());
Katherine Kuanf3f933a2011-06-10 11:34:54 -0700302 }
Dmitri Plotnikov08e1c4e2010-07-02 17:57:51 -0700303 }
304
Makoto Onukie0c66af2011-07-13 14:24:03 -0700305 /** @return true if the "UP" icon is showing. */
306 public boolean isUpShowing() {
307 return mSearchMode; // Only shown on the search mode.
308 }
309
Brian Attwell3c763b32014-07-21 14:25:28 -0700310 private void updateDisplayOptionsInner() {
Makoto Onukie0c66af2011-07-13 14:24:03 -0700311 // All the flags we may change in this method.
312 final int MASK = ActionBar.DISPLAY_SHOW_TITLE | ActionBar.DISPLAY_SHOW_HOME
Brian Attwell20510ec2015-02-27 16:10:45 -0800313 | ActionBar.DISPLAY_HOME_AS_UP;
Makoto Onukie0c66af2011-07-13 14:24:03 -0700314
315 // The current flags set to the action bar. (only the ones that we may change here)
316 final int current = mActionBar.getDisplayOptions() & MASK;
317
Brian Attwell20510ec2015-02-27 16:10:45 -0800318 final boolean isSearchOrSelectionMode = mSearchMode || mSelectionMode;
319
Makoto Onukie0c66af2011-07-13 14:24:03 -0700320 // Build the new flags...
321 int newFlags = 0;
Brian Attwell20510ec2015-02-27 16:10:45 -0800322 if (mShowHomeIcon && !isSearchOrSelectionMode) {
Makoto Onukie0c66af2011-07-13 14:24:03 -0700323 newFlags |= ActionBar.DISPLAY_SHOW_HOME;
Walter Jang59087d32016-05-17 13:52:15 -0700324 if (mShowHomeAsUp) {
325 newFlags |= ActionBar.DISPLAY_HOME_AS_UP;
326 }
Makoto Onukie0c66af2011-07-13 14:24:03 -0700327 }
Brian Attwell865847b2015-04-20 14:40:17 -0700328 if (mSearchMode && !mSelectionMode) {
Brian Attwell20510ec2015-02-27 16:10:45 -0800329 // The search container is placed inside the toolbar. So we need to disable the
330 // Toolbar's content inset in order to allow the search container to be the width of
331 // the window.
Brian Attwell3c763b32014-07-21 14:25:28 -0700332 mToolbar.setContentInsetsRelative(0, mToolbar.getContentInsetEnd());
Brian Attwell20510ec2015-02-27 16:10:45 -0800333 }
334 if (!isSearchOrSelectionMode) {
Brian Attwell6adbf1e2014-05-30 16:44:51 -0700335 newFlags |= ActionBar.DISPLAY_SHOW_TITLE;
Brian Attwell3c763b32014-07-21 14:25:28 -0700336 mToolbar.setContentInsetsRelative(mMaxToolbarContentInsetStart,
337 mToolbar.getContentInsetEnd());
John Shaobd9ef3c2016-12-15 12:42:03 -0800338 mToolbar.setNavigationIcon(R.drawable.quantum_ic_menu_vd_theme_24);
Wenyi Wangf568b302016-04-02 13:58:43 -0700339 } else {
340 mToolbar.setNavigationIcon(null);
Makoto Onukie0c66af2011-07-13 14:24:03 -0700341 }
Brian Attwellb0d3c762014-05-22 03:44:22 +0000342
Brian Attwell20510ec2015-02-27 16:10:45 -0800343 if (mSelectionMode) {
344 // Minimize the horizontal width of the Toolbar since the selection container is placed
345 // behind the toolbar and its left hand side needs to be clickable.
346 FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) mToolbar.getLayoutParams();
347 params.width = LayoutParams.WRAP_CONTENT;
348 params.gravity = Gravity.END;
349 mToolbar.setLayoutParams(params);
350 } else {
351 FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) mToolbar.getLayoutParams();
352 params.width = LayoutParams.MATCH_PARENT;
353 params.gravity = Gravity.END;
354 mToolbar.setLayoutParams(params);
355 }
Brian Attwell3c763b32014-07-21 14:25:28 -0700356
Makoto Onukie0c66af2011-07-13 14:24:03 -0700357 if (current != newFlags) {
358 // Pass the mask here to preserve other flags that we're not interested here.
359 mActionBar.setDisplayOptions(newFlags, MASK);
360 }
361 }
362
Brian Attwell6adbf1e2014-05-30 16:44:51 -0700363 private void update(boolean skipAnimation) {
Wenyi Wangdb662692016-07-14 16:07:44 -0700364 updateOverflowButtonColor();
365
Brian Attwell20510ec2015-02-27 16:10:45 -0800366 final boolean isSelectionModeChanging
367 = (mSelectionContainer.getParent() == null) == mSelectionMode;
Brian Attwell865847b2015-04-20 14:40:17 -0700368 final boolean isSwitchingFromSearchToSelection =
369 mSearchMode && isSelectionModeChanging || mSearchMode && mSelectionMode;
Brian Attwell20510ec2015-02-27 16:10:45 -0800370 final boolean isSearchModeChanging
Brian Attwell3c763b32014-07-21 14:25:28 -0700371 = (mSearchContainer.getParent() == null) == mSearchMode;
Brian Attwell20510ec2015-02-27 16:10:45 -0800372 final boolean isTabHeightChanging = isSearchModeChanging || isSelectionModeChanging;
373
Wenyi Wang92d7e512016-10-03 14:57:19 -0700374 // Update toolbar and status bar color.
375 mToolBarFrame.setBackgroundColor(MaterialColorMapUtils.getToolBarColor(mActivity));
Wenyi Wang11ff39a2016-08-07 15:46:03 -0700376 updateStatusBarColor(isSelectionModeChanging && !isSearchModeChanging);
377
Brian Attwell20510ec2015-02-27 16:10:45 -0800378 // When skipAnimation=true, it is possible that we will switch from search mode
379 // to selection mode directly. So we need to remove the undesired container in addition
380 // to adding the desired container.
Brian Attwell865847b2015-04-20 14:40:17 -0700381 if (skipAnimation || isSwitchingFromSearchToSelection) {
382 if (isTabHeightChanging || isSwitchingFromSearchToSelection) {
Brian Attwell865847b2015-04-20 14:40:17 -0700383 mToolbar.removeView(mSearchContainer);
384 mToolBarFrame.removeView(mSelectionContainer);
385 if (mSelectionMode) {
Brian Attwell20510ec2015-02-27 16:10:45 -0800386 addSelectionContainer();
Brian Attwell865847b2015-04-20 14:40:17 -0700387 } else if (mSearchMode) {
Brian Attwell865847b2015-04-20 14:40:17 -0700388 addSearchContainer();
Brian Attwell20510ec2015-02-27 16:10:45 -0800389 }
390 updateDisplayOptions(isSearchModeChanging);
391 }
392 return;
393 }
394
395 // Handle a switch to/from selection mode, due to UI interaction.
396 if (isSelectionModeChanging) {
Brian Attwell20510ec2015-02-27 16:10:45 -0800397 if (mSelectionMode) {
398 addSelectionContainer();
399 mSelectionContainer.setAlpha(0);
Wenyi Wangbd114972016-07-22 10:42:40 -0700400 mSelectionContainer.animate().alpha(1).setDuration(mActionBarAnimationDuration);
Brian Attwell20510ec2015-02-27 16:10:45 -0800401 updateDisplayOptions(isSearchModeChanging);
402 } else {
Brian Attwell865847b2015-04-20 14:40:17 -0700403 if (mListener != null) {
404 mListener.onAction(Action.BEGIN_STOPPING_SEARCH_AND_SELECTION_MODE);
405 }
Brian Attwell20510ec2015-02-27 16:10:45 -0800406 mSelectionContainer.setAlpha(1);
Wenyi Wangbd114972016-07-22 10:42:40 -0700407 mSelectionContainer.animate().alpha(0).setDuration(mActionBarAnimationDuration)
408 .withEndAction(new Runnable() {
Brian Attwell20510ec2015-02-27 16:10:45 -0800409 @Override
410 public void run() {
411 updateDisplayOptions(isSearchModeChanging);
Brian Attwell20510ec2015-02-27 16:10:45 -0800412 mToolBarFrame.removeView(mSelectionContainer);
413 }
414 });
415 }
416 }
417
418 // Handle a switch to/from search mode, due to UI interaction.
419 if (isSearchModeChanging) {
Brian Attwell6adbf1e2014-05-30 16:44:51 -0700420 if (mSearchMode) {
Brian Attwell3c763b32014-07-21 14:25:28 -0700421 addSearchContainer();
Brian Attwell6adbf1e2014-05-30 16:44:51 -0700422 mSearchContainer.setAlpha(0);
Wenyi Wangbd114972016-07-22 10:42:40 -0700423 mSearchContainer.animate().alpha(1).setDuration(mActionBarAnimationDuration);
Brian Attwell20510ec2015-02-27 16:10:45 -0800424 updateDisplayOptions(isSearchModeChanging);
Brian Attwell6adbf1e2014-05-30 16:44:51 -0700425 } else {
426 mSearchContainer.setAlpha(1);
Wenyi Wangbd114972016-07-22 10:42:40 -0700427 mSearchContainer.animate().alpha(0).setDuration(mActionBarAnimationDuration)
428 .withEndAction(new Runnable() {
Brian Attwell6adbf1e2014-05-30 16:44:51 -0700429 @Override
430 public void run() {
Brian Attwell20510ec2015-02-27 16:10:45 -0800431 updateDisplayOptions(isSearchModeChanging);
Brian Attwell3c763b32014-07-21 14:25:28 -0700432 mToolbar.removeView(mSearchContainer);
Brian Attwell6adbf1e2014-05-30 16:44:51 -0700433 }
434 });
435 }
Brian Attwell6adbf1e2014-05-30 16:44:51 -0700436 }
Brian Attwell20510ec2015-02-27 16:10:45 -0800437 }
438
Wenyi Wangdb662692016-07-14 16:07:44 -0700439 /**
440 * Find overflow menu ImageView by its content description and update its color.
441 */
442 private void updateOverflowButtonColor() {
443 final String overflowDescription = mActivity.getResources().getString(
444 R.string.abc_action_menu_overflow_description);
445 final ViewGroup decorView = (ViewGroup) mActivity.getWindow().getDecorView();
446 final ViewTreeObserver viewTreeObserver = decorView.getViewTreeObserver();
447 viewTreeObserver.addOnGlobalLayoutListener(
448 new ViewTreeObserver.OnGlobalLayoutListener() {
449 @Override
450 public void onGlobalLayout() {
451 // Find the overflow ImageView.
452 final ArrayList<View> outViews = new ArrayList<>();
453 decorView.findViewsWithText(outViews, overflowDescription,
454 View.FIND_VIEWS_WITH_CONTENT_DESCRIPTION);
Wenyi Wangdb662692016-07-14 16:07:44 -0700455
Wenyi Wang44702ac2016-12-01 13:31:22 -0800456 for (View view : outViews) {
457 if (!(view instanceof ImageView)) {
458 continue;
459 }
460 final ImageView overflow = (ImageView) view;
461
462 // Update the overflow image color.
463 final int iconColor;
464 if (mSelectionMode) {
465 iconColor = mActivity.getResources().getColor(
466 R.color.actionbar_color_grey_solid);
467 } else {
468 iconColor = mActivity.getResources().getColor(
469 R.color.actionbar_text_color);
470 }
471 overflow.setImageTintList(ColorStateList.valueOf(iconColor));
Wenyi Wangdb662692016-07-14 16:07:44 -0700472 }
Wenyi Wangdb662692016-07-14 16:07:44 -0700473
474 // We're done, remove the listener.
475 decorView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
476 }
477 });
478 }
479
Brian Attwell20510ec2015-02-27 16:10:45 -0800480 public void setSelectionCount(int selectionCount) {
481 TextView textView = (TextView) mSelectionContainer.findViewById(R.id.selection_count_text);
482 if (selectionCount == 0) {
483 textView.setVisibility(View.GONE);
484 } else {
485 textView.setVisibility(View.VISIBLE);
Brian Attwell6adbf1e2014-05-30 16:44:51 -0700486 }
Brian Attwell20510ec2015-02-27 16:10:45 -0800487 textView.setText(String.valueOf(selectionCount));
488 }
489
Wenyi Wang64a6bc42016-07-07 18:34:48 -0700490 public void setActionBarTitle(String title) {
491 final TextView textView =
492 (TextView) mSelectionContainer.findViewById(R.id.selection_count_text);
493 textView.setVisibility(View.VISIBLE);
494 textView.setText(title);
495 }
496
Wenyi Wang11ff39a2016-08-07 15:46:03 -0700497 private void updateStatusBarColor(boolean shouldAnimate) {
Wenyi Wang4c3d3e22015-12-17 14:30:02 -0800498 if (!CompatUtils.isLollipopCompatible()) {
499 return; // we can't change the status bar color prior to Lollipop
500 }
Wenyi Wang11ff39a2016-08-07 15:46:03 -0700501
Brian Attwell20510ec2015-02-27 16:10:45 -0800502 if (mSelectionMode) {
Wenyi Wangd348a902016-08-14 12:56:06 -0700503 final int cabStatusBarColor = ContextCompat.getColor(
Wenyi Wang11ff39a2016-08-07 15:46:03 -0700504 mActivity, R.color.contextual_selection_bar_status_bar_color);
505 runStatusBarAnimation(/* colorTo */ cabStatusBarColor);
Wenyi Wang8e73c362016-06-23 18:03:53 -0700506 } else {
Wenyi Wang11ff39a2016-08-07 15:46:03 -0700507 if (shouldAnimate) {
Wenyi Wang92d7e512016-10-03 14:57:19 -0700508 runStatusBarAnimation(/* colorTo */
509 MaterialColorMapUtils.getStatusBarColor(mActivity));
510 } else if (mActivity instanceof ContactsDrawerActivity) {
511 ((ContactsDrawerActivity) mActivity).updateStatusBarBackground();
Wenyi Wang11ff39a2016-08-07 15:46:03 -0700512 }
513 }
514 }
515
516 private void runStatusBarAnimation(int colorTo) {
517 final Window window = mActivity.getWindow();
518 if (window.getStatusBarColor() != colorTo) {
519 // Cancel running animation.
520 if (mStatusBarAnimator != null && mStatusBarAnimator.isRunning()) {
521 mStatusBarAnimator.cancel();
522 }
523 final int from = window.getStatusBarColor();
524 // Set up mStatusBarAnimator and run animation.
525 mStatusBarAnimator = ValueAnimator.ofObject(new ArgbEvaluator(), from, colorTo);
526 mStatusBarAnimator.addUpdateListener(
527 new ValueAnimator.AnimatorUpdateListener() {
528 @Override
529 public void onAnimationUpdate(ValueAnimator animator) {
530 window.setStatusBarColor((Integer) animator.getAnimatedValue());
531 }
532 });
533 mStatusBarAnimator.setDuration(mActionBarAnimationDuration);
534 mStatusBarAnimator.setStartDelay(0);
535 mStatusBarAnimator.start();
Brian Attwell20510ec2015-02-27 16:10:45 -0800536 }
Brian Attwell6adbf1e2014-05-30 16:44:51 -0700537 }
538
Brian Attwell3c763b32014-07-21 14:25:28 -0700539 private void addSearchContainer() {
540 mToolbar.removeView(mSearchContainer);
541 mToolbar.addView(mSearchContainer);
Brian Attwell865847b2015-04-20 14:40:17 -0700542 mSearchContainer.setAlpha(1);
Brian Attwell3c763b32014-07-21 14:25:28 -0700543 }
544
Brian Attwell20510ec2015-02-27 16:10:45 -0800545 private void addSelectionContainer() {
546 mToolBarFrame.removeView(mSelectionContainer);
547 mToolBarFrame.addView(mSelectionContainer, 0);
Brian Attwell865847b2015-04-20 14:40:17 -0700548 mSelectionContainer.setAlpha(1);
Brian Attwell20510ec2015-02-27 16:10:45 -0800549 }
550
551 private void updateDisplayOptions(boolean isSearchModeChanging) {
Brian Attwell865847b2015-04-20 14:40:17 -0700552 if (mSearchMode && !mSelectionMode) {
Makoto Onuki52673672011-07-25 12:39:59 -0700553 setFocusOnSearchView();
Katherine Kuand8a61b72011-07-27 13:57:13 -0700554 // Since we have the {@link SearchView} in a custom action bar, we must manually handle
Katherine Kuan3c877e32012-01-03 17:27:36 -0800555 // expanding the {@link SearchView} when a search is initiated. Note that a side effect
556 // of this method is that the {@link SearchView} query text is set to empty string.
Brian Attwell20510ec2015-02-27 16:10:45 -0800557 if (isSearchModeChanging) {
Brian Attwell6adbf1e2014-05-30 16:44:51 -0700558 final CharSequence queryText = mSearchView.getText();
Yorke Lee8d5eec32013-01-18 11:07:48 -0800559 if (!TextUtils.isEmpty(queryText)) {
Brian Attwell6adbf1e2014-05-30 16:44:51 -0700560 mSearchView.setText(queryText);
Yorke Lee8d5eec32013-01-18 11:07:48 -0800561 }
Daniel Lehmannb0d82d22012-05-07 19:00:06 -0700562 }
Brian Attwell20510ec2015-02-27 16:10:45 -0800563 }
564 if (mListener != null) {
565 if (mSearchMode) {
Katherine Kuanb5760b92011-06-01 16:19:40 -0700566 mListener.onAction(Action.START_SEARCH_MODE);
Dmitri Plotnikovc9916d32010-10-13 11:42:09 -0700567 }
Brian Attwell20510ec2015-02-27 16:10:45 -0800568 if (mSelectionMode) {
569 mListener.onAction(Action.START_SELECTION_MODE);
570 }
571 if (!mSearchMode && !mSelectionMode) {
572 mListener.onAction(Action.STOP_SEARCH_AND_SELECTION_MODE);
Dmitri Plotnikovbee8d3b2010-10-12 20:57:46 -0700573 }
Dmitri Plotnikov88a405f2010-09-27 18:50:57 -0700574 }
Brian Attwell3c763b32014-07-21 14:25:28 -0700575 updateDisplayOptionsInner();
Dmitri Plotnikov1a59cff2010-09-18 12:50:45 -0700576 }
577
578 @Override
Dmitri Plotnikov1a59cff2010-09-18 12:50:45 -0700579 public boolean onClose() {
Dmitri Plotnikov15ccbb42010-10-05 17:29:07 -0700580 setSearchMode(false);
581 return false;
Dmitri Plotnikov08e1c4e2010-07-02 17:57:51 -0700582 }
583
584 public void onSaveInstanceState(Bundle outState) {
Dmitri Plotnikov15ccbb42010-10-05 17:29:07 -0700585 outState.putBoolean(EXTRA_KEY_SEARCH_MODE, mSearchMode);
Brian Attwell20510ec2015-02-27 16:10:45 -0800586 outState.putBoolean(EXTRA_KEY_SELECTED_MODE, mSelectionMode);
Dmitri Plotnikov08e1c4e2010-07-02 17:57:51 -0700587 outState.putString(EXTRA_KEY_QUERY, mQueryString);
Dmitri Plotnikovfa49a662011-01-13 11:04:56 -0800588 }
589
Daniel Lehmann09d66a32012-04-13 19:14:39 -0700590 public void setFocusOnSearchView() {
Makoto Onuki4ba903c2011-07-07 16:55:17 -0700591 mSearchView.requestFocus();
Brian Attwell6adbf1e2014-05-30 16:44:51 -0700592 showInputMethod(mSearchView); // Workaround for the "IME not popping up" issue.
593 }
594
595 private void showInputMethod(View view) {
Brian Attwell20510ec2015-02-27 16:10:45 -0800596 final InputMethodManager imm = (InputMethodManager) mActivity.getSystemService(
Brian Attwell6adbf1e2014-05-30 16:44:51 -0700597 Context.INPUT_METHOD_SERVICE);
598 if (imm != null) {
599 imm.showSoftInput(view, 0);
600 }
Makoto Onuki4ba903c2011-07-07 16:55:17 -0700601 }
Dmitri Plotnikov08e1c4e2010-07-02 17:57:51 -0700602}