Merge "Import translations. DO NOT MERGE"
diff --git a/res/drawable-hdpi/ic_arrow_back_24dp.png b/res/drawable-hdpi/ic_arrow_back_24dp.png
new file mode 100644
index 0000000..86cb894
--- /dev/null
+++ b/res/drawable-hdpi/ic_arrow_back_24dp.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_back_arrow.png b/res/drawable-hdpi/ic_back_arrow.png
deleted file mode 100644
index aad4f36..0000000
--- a/res/drawable-hdpi/ic_back_arrow.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/ic_arrow_back_24dp.png b/res/drawable-mdpi/ic_arrow_back_24dp.png
new file mode 100644
index 0000000..dc81cd1
--- /dev/null
+++ b/res/drawable-mdpi/ic_arrow_back_24dp.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_back_arrow.png b/res/drawable-mdpi/ic_back_arrow.png
deleted file mode 100644
index 56eb887..0000000
--- a/res/drawable-mdpi/ic_back_arrow.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/ic_arrow_back_24dp.png b/res/drawable-xhdpi/ic_arrow_back_24dp.png
new file mode 100644
index 0000000..4f4fbaa
--- /dev/null
+++ b/res/drawable-xhdpi/ic_arrow_back_24dp.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_back_arrow.png b/res/drawable-xhdpi/ic_back_arrow.png
deleted file mode 100644
index 9d46e3d..0000000
--- a/res/drawable-xhdpi/ic_back_arrow.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_arrow_back_24dp.png b/res/drawable-xxhdpi/ic_arrow_back_24dp.png
new file mode 100644
index 0000000..46e90f7
--- /dev/null
+++ b/res/drawable-xxhdpi/ic_arrow_back_24dp.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_back_arrow.png b/res/drawable-xxhdpi/ic_back_arrow.png
deleted file mode 100644
index 66b6e35..0000000
--- a/res/drawable-xxhdpi/ic_back_arrow.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable/ic_back_arrow.xml b/res/drawable/ic_back_arrow.xml
new file mode 100644
index 0000000..68a875d
--- /dev/null
+++ b/res/drawable/ic_back_arrow.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2014 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
+ android:src="@drawable/ic_arrow_back_24dp"
+ android:autoMirrored="true"
+ android:tint="@color/actionbar_icon_color" />
\ No newline at end of file
diff --git a/res/layout/lists_fragment.xml b/res/layout/lists_fragment.xml
index d75095a..7e15f84 100644
--- a/res/layout/lists_fragment.xml
+++ b/res/layout/lists_fragment.xml
@@ -24,7 +24,7 @@
android:id="@+id/shortcut_card_list"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:paddingTop="?android:attr/actionBarSize"
+ android:paddingTop="@dimen/action_bar_height"
android:background="@color/actionbar_background_color"
android:clipToPadding="false"
android:fadingEdge="none"
diff --git a/res/values-land/styles.xml b/res/values-land/styles.xml
deleted file mode 100644
index 1388a17..0000000
--- a/res/values-land/styles.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<!--
- Copyright (C) 2012 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- -->
-
-<resources>
-
- <style name="FragmentActionBarPadding">
- <item name="android:paddingBottom">0dp</item>
- </style>
-</resources>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 78d91c2..e884566 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -88,12 +88,16 @@
<!-- Height of the all contacts Button in the favorites menu. -->
<dimen name="favorites_menu_all_contacts_height">24dp</dimen>
+ <!-- Height of the actionBar - this is 8dps bigger than the platform standard to give more
+ room to the search box-->
+ <dimen name="action_bar_height">64dp</dimen>
+
<!-- Margin to the left and right of the search box. -->
<dimen name="search_margin_horizontal">7dp</dimen>
<!-- Margin above the search box. -->
- <dimen name="search_top_margin">4dp</dimen>
+ <dimen name="search_top_margin">8dp</dimen>
<!-- Margin below the search box. -->
- <dimen name="search_bottom_margin">4dp</dimen>
+ <dimen name="search_bottom_margin">8dp</dimen>
<!-- Search box text size -->
<dimen name="search_text_size">13.24sp</dimen>
<!-- Search box interior padding - left -->
@@ -108,7 +112,7 @@
<!-- Size of the icon (voice search, close search) in the search box. -->
<dimen name="search_box_icon_size">28dp</dimen>
<!-- Elevation of the search box -->
- <dimen name="search_box_elevation">10dp</dimen>
+ <dimen name="search_box_elevation">5dp</dimen>
<!-- Size of text in tabs. -->
<dimen name="tab_height">43dp</dimen>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 42351b8..e84f4b5 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -22,14 +22,10 @@
<item name="android:windowActionBarOverlay">true</item>
<item name="android:windowActionModeOverlay">true</item>
<item name="android:actionBarStyle">@style/DialtactsActionBarStyle</item>
- <!-- Style for the tabs (for the tabs) -->
- <item name="android:actionBarTabStyle">@style/DialtactsActionBarTabStyle</item>
- <!-- Style for the tab bar (for the divider between tabs) -->
- <item name="android:actionBarTabBarStyle">@style/DialtactsActionBarTabBarStyle</item>
- <!-- Style for the tab bar text (for text on tabs) -->
- <item name="android:actionBarTabTextStyle">@style/DialtactsActionBarTabTextStyle</item>
<!-- Style for the overflow button in the actionbar. -->
<item name="android:actionOverflowButtonStyle">@style/DialtactsActionBarOverflow</item>
+ <!-- Drawable for the back button -->
+ <item name="android:homeAsUpIndicator">@drawable/ic_back_arrow</item>
<item name="android:windowContentOverlay">@null</item>
<!-- Searchbox Style -->
<item name="android:editTextStyle">@style/DialtactsSearchboxStyle</item>
@@ -73,7 +69,7 @@
<item name="call_log_voicemail_status_action_text_color">#33b5e5</item>
<!-- Favorites -->
<item name="favorites_padding_bottom">?android:attr/actionBarSize</item>
- <item name="android:colorPrimaryDark">@color/actionbar_background_color_dark</item>
+ <item name="android:colorPrimaryDark">@color/actionbar_background_color_dark</item>
</style>
<!-- Action bar overflow menu icon. -->
@@ -118,6 +114,7 @@
<item name="android:background">@color/actionbar_background_color</item>
<item name="android:backgroundStacked">#ffffff</item>
<item name="android:titleTextStyle">@style/DialtactsActionBarTitleText</item>
+ <item name="android:height">@dimen/action_bar_height</item>
<!-- Empty icon -->
<item name="android:icon">@android:color/transparent</item>
</style>
@@ -157,10 +154,6 @@
<item name="android:overScrollMode">always</item>
</style>
- <style name="FragmentActionBarPadding">
- <item name="android:paddingBottom">?android:attr/actionBarSize</item>
- </style>
-
<style name="DialtactsSearchboxStyle" parent="@android:style/Widget.EditText">
<item name="android:background">@null</item>
<item name="android:textColorHint">@color/searchbox_text_color</item>
diff --git a/src/com/android/dialer/DialtactsActivity.java b/src/com/android/dialer/DialtactsActivity.java
index 8445ff7..a593c14 100644
--- a/src/com/android/dialer/DialtactsActivity.java
+++ b/src/com/android/dialer/DialtactsActivity.java
@@ -20,7 +20,6 @@
import android.app.ActionBar;
import android.app.Activity;
import android.app.Fragment;
-import android.app.FragmentManager;
import android.app.FragmentTransaction;
import android.content.ActivityNotFoundException;
import android.content.Context;
@@ -85,6 +84,7 @@
import com.android.dialer.list.RemoveView;
import com.android.dialer.list.SearchFragment;
import com.android.dialer.list.SmartDialSearchFragment;
+import com.android.dialer.widget.ActionBarController;
import com.android.dialer.widget.SearchEditTextLayout;
import com.android.dialer.widget.SearchEditTextLayout.OnBackButtonClickedListener;
import com.android.dialerbind.DatabaseHelperManager;
@@ -102,10 +102,12 @@
DialpadFragment.HostInterface,
ListsFragment.HostInterface,
SpeedDialFragment.HostInterface,
+ SearchFragment.HostInterface,
OnDragDropListener,
OnPhoneNumberPickerActionListener,
PopupMenu.OnMenuItemClickListener,
- ViewPager.OnPageChangeListener {
+ ViewPager.OnPageChangeListener,
+ ActionBarController.ActivityUi {
private static final String TAG = "DialtactsActivity";
public static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
@@ -208,6 +210,7 @@
private DialerDatabaseHelper mDialerDatabaseHelper;
private DragDropController mDragDropController;
+ private ActionBarController mActionBarController;
private class OptionsPopupMenu extends PopupMenu {
public OptionsPopupMenu(Context context, View anchor) {
@@ -293,6 +296,7 @@
@Override
public void onClick(View v) {
if (!isInSearchUi()) {
+ mActionBarController.onSearchBoxTapped();
enterSearchUi(false /* smartDialSearch */, mSearchView.getText().toString());
}
}
@@ -325,6 +329,9 @@
actionBar.setDisplayShowCustomEnabled(true);
actionBar.setBackgroundDrawable(null);
+ mActionBarController = new ActionBarController(this,
+ (SearchEditTextLayout) actionBar.getCustomView());
+
mSearchEditTextLayout = (SearchEditTextLayout) actionBar.getCustomView();
mSearchEditTextLayout.setPreImeKeyListener(mSearchEditTextLayoutListener);
@@ -346,10 +353,7 @@
final OptionsPopupMenu optionsMenu = buildOptionsMenu(optionsMenuButton);
optionsMenuButton.setOnTouchListener(optionsMenu.getDragToOpenListener());
- final TypedArray styledAttributes = getTheme().obtainStyledAttributes(
- new int[] { android.R.attr.actionBarSize });
- mActionBarHeight = (int) styledAttributes.getDimension(0, 0);
- styledAttributes.recycle();
+ mActionBarHeight = getResources().getDimensionPixelSize(R.dimen.action_bar_height);
// Add the favorites fragment, and the dialpad fragment, but only if savedInstanceState
// is null. Otherwise the fragment manager takes care of recreating these fragments.
@@ -363,6 +367,7 @@
mInRegularSearch = savedInstanceState.getBoolean(KEY_IN_REGULAR_SEARCH_UI);
mInDialpadSearch = savedInstanceState.getBoolean(KEY_IN_DIALPAD_SEARCH_UI);
mFirstLaunch = savedInstanceState.getBoolean(KEY_FIRST_LAUNCH);
+ mActionBarController.restoreInstanceState(savedInstanceState);
}
parentLayout = (RelativeLayout) findViewById(R.id.dialtacts_mainlayout);
@@ -426,6 +431,7 @@
outState.putBoolean(KEY_IN_REGULAR_SEARCH_UI, mInRegularSearch);
outState.putBoolean(KEY_IN_DIALPAD_SEARCH_UI, mInDialpadSearch);
outState.putBoolean(KEY_FIRST_LAUNCH, mFirstLaunch);
+ mActionBarController.saveInstanceState(outState);
}
@Override
@@ -563,6 +569,8 @@
ft.show(mDialpadFragment);
ft.commit();
+ mActionBarController.onDialpadUp();
+
if (!isInSearchUi()) {
enterSearchUi(true /* isSmartDial */, mSearchQuery);
}
@@ -581,7 +589,6 @@
}
updateSearchFragmentPosition();
- getActionBar().hide();
}
/**
@@ -617,7 +624,7 @@
commitDialpadFragmentHide();
}
- mListsFragment.maybeShowActionBar();
+ mActionBarController.onDialpadDown();
if (isInSearchUi()) {
if (TextUtils.isEmpty(mSearchQuery)) {
@@ -649,10 +656,21 @@
}
}
- private boolean isInSearchUi() {
+ @Override
+ public boolean isInSearchUi() {
return mInDialpadSearch || mInRegularSearch;
}
+ @Override
+ public boolean hasSearchQuery() {
+ return !TextUtils.isEmpty(mSearchQuery);
+ }
+
+ @Override
+ public boolean shouldShowActionBar() {
+ return mListsFragment.shouldShowActionBar();
+ }
+
private void setNotInSearchUi() {
mInDialpadSearch = false;
mInRegularSearch = false;
@@ -829,7 +847,6 @@
transaction.commit();
mListsFragment.getView().animate().alpha(0).withLayer();
- mSearchEditTextLayout.animateExpandOrCollapse(true);
}
/**
@@ -856,7 +873,7 @@
transaction.commit();
mListsFragment.getView().animate().alpha(1).withLayer();
- mSearchEditTextLayout.animateExpandOrCollapse(false);
+ mActionBarController.onSearchUiExited();
}
/** Returns an Intent to launch Call Settings screen */
@@ -974,7 +991,7 @@
*/
@Override
public void onDragStarted(int x, int y, PhoneFavoriteSquareTileView view) {
- getActionBar().hide();
+ mActionBarController.slideActionBarUp(true);
mRemoveViewContainer.setVisibility(View.VISIBLE);
}
@@ -987,7 +1004,7 @@
*/
@Override
public void onDragFinished(int x, int y) {
- getActionBar().show();
+ mActionBarController.slideActionBarDown(true);
mRemoveViewContainer.setVisibility(View.GONE);
}
@@ -1031,10 +1048,6 @@
exitSearchUi();
}
- public int getActionBarHeight() {
- return mActionBarHeight;
- }
-
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
@@ -1107,4 +1120,24 @@
public void onAnimationRepeat(Animation animation) {
}
}
+
+ @Override
+ public boolean isActionBarShowing() {
+ return mActionBarController.isActionBarShowing();
+ }
+
+ @Override
+ public int getActionBarHideOffset() {
+ return getActionBar().getHideOffset();
+ }
+
+ @Override
+ public int getActionBarHeight() {
+ return mActionBarHeight;
+ }
+
+ @Override
+ public void setActionBarHideOffset(int hideOffset) {
+ getActionBar().setHideOffset(hideOffset);
+ }
}
diff --git a/src/com/android/dialer/list/ListsFragment.java b/src/com/android/dialer/list/ListsFragment.java
index 78570e1..2ff5a2a 100644
--- a/src/com/android/dialer/list/ListsFragment.java
+++ b/src/com/android/dialer/list/ListsFragment.java
@@ -326,13 +326,8 @@
}
}
- public void maybeShowActionBar() {
- // TODO: Try to show the action bar regardless of whether the panel is open, and then update
- // the offset to show/hide the action bar, instead of updating the whether the action bar is
- // shown in onPanelSlide.
- if (mIsPanelOpen && mActionBar != null) {
- mActionBar.show();
- }
+ public boolean shouldShowActionBar() {
+ return mIsPanelOpen && mActionBar != null;
}
private void setupPaneLayout(OverlappingPaneLayout paneLayout) {
diff --git a/src/com/android/dialer/list/PhoneFavoriteSquareTileView.java b/src/com/android/dialer/list/PhoneFavoriteSquareTileView.java
index 0520ab4..776a7a2 100644
--- a/src/com/android/dialer/list/PhoneFavoriteSquareTileView.java
+++ b/src/com/android/dialer/list/PhoneFavoriteSquareTileView.java
@@ -21,6 +21,7 @@
import android.util.AttributeSet;
import android.view.View;
import android.widget.ImageButton;
+import android.widget.TextView;
import com.android.contacts.common.R;
import com.android.contacts.common.list.ContactEntry;
@@ -47,7 +48,10 @@
@Override
protected void onFinishInflate() {
super.onFinishInflate();
-
+ final TextView nameView = (TextView) findViewById(R.id.contact_tile_name);
+ nameView.setElegantTextHeight(false);
+ final TextView phoneTypeView = (TextView) findViewById(R.id.contact_tile_phone_type);
+ phoneTypeView.setElegantTextHeight(false);
mSecondaryButton = (ImageButton) findViewById(R.id.contact_tile_secondary_button);
}
diff --git a/src/com/android/dialer/list/SearchFragment.java b/src/com/android/dialer/list/SearchFragment.java
index f863d90..9a30c4d 100644
--- a/src/com/android/dialer/list/SearchFragment.java
+++ b/src/com/android/dialer/list/SearchFragment.java
@@ -45,6 +45,12 @@
private String mAddToContactNumber;
private int mActionBarHeight;
+ public interface HostInterface {
+ public boolean isActionBarShowing();
+ public int getActionBarHideOffset();
+ public int getActionBarHeight();
+ }
+
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
@@ -69,7 +75,9 @@
getAdapter().setHasHeader(0, false);
}
- mActionBarHeight = ((DialtactsActivity) getActivity()).getActionBarHeight();
+ HostInterface activity = (HostInterface) getActivity();
+
+ mActionBarHeight = activity.getActionBarHeight();
final View parentView = getView();
parentView.setPaddingRelative(
@@ -92,7 +100,8 @@
}
});
- if (!getActivity().getActionBar().isShowing()) {
+
+ if (!activity.isActionBarShowing()) {
parentView.setTranslationY(-mActionBarHeight);
}
}
diff --git a/src/com/android/dialer/widget/ActionBarController.java b/src/com/android/dialer/widget/ActionBarController.java
new file mode 100644
index 0000000..49506f4
--- /dev/null
+++ b/src/com/android/dialer/widget/ActionBarController.java
@@ -0,0 +1,219 @@
+package com.android.dialer.widget;
+
+import com.google.common.annotations.VisibleForTesting;
+
+import android.animation.ValueAnimator;
+import android.animation.ValueAnimator.AnimatorUpdateListener;
+import android.os.Bundle;
+import android.util.Log;
+
+import com.android.dialer.DialtactsActivity;
+
+/**
+ * Controls the various animated properties of the actionBar: showing/hiding, fading/revealing,
+ * and collapsing/expanding, and assigns suitable properties to the actionBar based on the
+ * current state of the UI.
+ */
+public class ActionBarController {
+ public static final boolean DEBUG = DialtactsActivity.DEBUG;
+ public static final String TAG = "ActionBarController";
+ private static final String KEY_IS_SLID_UP = "key_actionbar_is_slid_up";
+ private static final String KEY_IS_FADED_OUT = "key_actionbar_is_faded_out";
+ private static final String KEY_IS_EXPANDED = "key_actionbar_is_expanded";
+
+ private ActivityUi mActivityUi;
+ private SearchEditTextLayout mSearchBox;
+
+ private boolean mIsActionBarSlidUp;
+
+ public interface ActivityUi {
+ public boolean isInSearchUi();
+ public boolean hasSearchQuery();
+ public boolean shouldShowActionBar();
+ public int getActionBarHeight();
+ public int getActionBarHideOffset();
+ public void setActionBarHideOffset(int hideOffset);
+ }
+
+ public ActionBarController(ActivityUi activityUi, SearchEditTextLayout searchBox) {
+ mActivityUi = activityUi;
+ mSearchBox = searchBox;
+ }
+
+ /**
+ * @return The offset the action bar is being translated upwards by
+ */
+ public int getHideOffset() {
+ return mActivityUi.getActionBarHideOffset();
+ }
+
+ /**
+ * @return Whether or not the action bar is currently showing (both slid down and visible)
+ */
+ public boolean isActionBarShowing() {
+ return !mIsActionBarSlidUp && !mSearchBox.isFadedOut();
+ }
+
+ /**
+ * Called when the user has tapped on the collapsed search box, to start a new search query.
+ */
+ public void onSearchBoxTapped() {
+ if (DEBUG) {
+ Log.d(TAG, "OnSearchBoxTapped: isInSearchUi " + mActivityUi.isInSearchUi());
+ }
+ if (!mActivityUi.isInSearchUi()) {
+ mSearchBox.expand(true /* animate */, true /* requestFocus */);
+ }
+ }
+
+ /**
+ * Called when search UI has been exited for some reason.
+ */
+ public void onSearchUiExited() {
+ if (DEBUG) {
+ Log.d(TAG, "OnSearchUIExited: isExpanded " + mSearchBox.isExpanded()
+ + " isFadedOut: " + mSearchBox.isFadedOut()
+ + " shouldShowActionBar: " + mActivityUi.shouldShowActionBar());
+ }
+ if (mSearchBox.isExpanded()) {
+ mSearchBox.collapse(true /* animate */);
+ }
+ if (mSearchBox.isFadedOut()) {
+ mSearchBox.fadeIn();
+ }
+
+ if (mActivityUi.shouldShowActionBar()) {
+ slideActionBarDown(false /* animate */);
+ } else {
+ slideActionBarUp(false /* animate */);
+ }
+ }
+
+ /**
+ * Called to indicate that the user is trying to hide the dialpad. Should be called before
+ * any state changes have actually occurred.
+ */
+ public void onDialpadDown() {
+ if (DEBUG) {
+ Log.d(TAG, "OnDialpadDown: isInSearchUi " + mActivityUi.isInSearchUi()
+ + " hasSearchQuery: " + mActivityUi.hasSearchQuery()
+ + " isFadedOut: " + mSearchBox.isFadedOut()
+ + " isExpanded: " + mSearchBox.isExpanded());
+ }
+ if (mActivityUi.isInSearchUi()) {
+ if (mActivityUi.hasSearchQuery()) {
+ if (mSearchBox.isFadedOut()) {
+ mSearchBox.setVisible(true);
+ }
+ if (!mSearchBox.isExpanded()) {
+ mSearchBox.expand(false /* animate */, false /* requestFocus */);
+ }
+ slideActionBarDown(true /* animate */);
+ } else {
+ mSearchBox.fadeIn();
+ }
+ }
+ }
+
+ /**
+ * Called to indicate that the user is trying to show the dialpad. Should be called before
+ * any state changes have actually occurred.
+ */
+ public void onDialpadUp() {
+ if (DEBUG) {
+ Log.d(TAG, "OnDialpadUp: isInSearchUi " + mActivityUi.isInSearchUi());
+ }
+ if (mActivityUi.isInSearchUi()) {
+ slideActionBarUp(true);
+ } else {
+ // From the lists fragment
+ mSearchBox.fadeOut();
+ }
+ }
+
+ public void slideActionBarUp(boolean animate) {
+ if (DEBUG) {
+ Log.d(TAG, "Sliding actionBar up - animate: " + animate);
+ }
+ if (animate) {
+ ValueAnimator animator = ValueAnimator.ofFloat(0, 1);
+ animator.addUpdateListener(new AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ final float value = (float) animation.getAnimatedValue();
+ mActivityUi.setActionBarHideOffset(
+ (int) (mActivityUi.getActionBarHeight() * value));
+ }
+ });
+ animator.start();
+ } else {
+ mActivityUi.setActionBarHideOffset(mActivityUi.getActionBarHeight());
+ }
+ mIsActionBarSlidUp = true;
+ }
+
+ public void slideActionBarDown(boolean animate) {
+ if (DEBUG) {
+ Log.d(TAG, "Sliding actionBar down - animate: " + animate);
+ }
+ if (animate) {
+ ValueAnimator animator = ValueAnimator.ofFloat(1, 0);
+ animator.addUpdateListener(new AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ final float value = (float) animation.getAnimatedValue();
+ mActivityUi.setActionBarHideOffset(
+ (int) (mActivityUi.getActionBarHeight() * value));
+ }
+ });
+ animator.start();
+ } else {
+ mActivityUi.setActionBarHideOffset(0);
+ }
+ mIsActionBarSlidUp = false;
+ }
+
+ /**
+ * Saves the current state of the action bar into a provided {@link Bundle}
+ */
+ public void saveInstanceState(Bundle outState) {
+ outState.putBoolean(KEY_IS_SLID_UP, mIsActionBarSlidUp);
+ outState.putBoolean(KEY_IS_FADED_OUT, mSearchBox.isFadedOut());
+ outState.putBoolean(KEY_IS_EXPANDED, mSearchBox.isExpanded());
+ }
+
+ /**
+ * Restores the action bar state from a provided {@link Bundle}
+ */
+ public void restoreInstanceState(Bundle inState) {
+ mIsActionBarSlidUp = inState.getBoolean(KEY_IS_SLID_UP);
+ if (mIsActionBarSlidUp) {
+ slideActionBarUp(false);
+ } else {
+ slideActionBarDown(false);
+ }
+
+ final boolean isSearchBoxFadedOut = inState.getBoolean(KEY_IS_FADED_OUT);
+ if (isSearchBoxFadedOut) {
+ if (!mSearchBox.isFadedOut()) {
+ mSearchBox.setVisible(false);
+ }
+ } else if (mSearchBox.isFadedOut()) {
+ mSearchBox.setVisible(true);
+ }
+
+ final boolean isSearchBoxExpanded = inState.getBoolean(KEY_IS_EXPANDED);
+ if (isSearchBoxExpanded) {
+ if (!mSearchBox.isExpanded()) {
+ mSearchBox.expand(false, false);
+ }
+ } else if (mSearchBox.isExpanded()) {
+ mSearchBox.collapse(false);
+ }
+ }
+
+ @VisibleForTesting
+ public boolean getIsActionBarSlidUp() {
+ return mIsActionBarSlidUp;
+ }
+}
\ No newline at end of file
diff --git a/src/com/android/dialer/widget/SearchEditTextLayout.java b/src/com/android/dialer/widget/SearchEditTextLayout.java
index 6830842..ef3ddcc 100644
--- a/src/com/android/dialer/widget/SearchEditTextLayout.java
+++ b/src/com/android/dialer/widget/SearchEditTextLayout.java
@@ -16,6 +16,8 @@
package com.android.dialer.widget;
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.animation.ValueAnimator.AnimatorUpdateListener;
import android.content.Context;
@@ -27,7 +29,7 @@
import android.widget.EditText;
import android.widget.FrameLayout;
-import com.android.contacts.common.animation.AnimationUtils;
+import com.android.contacts.common.animation.AnimUtils;
import com.android.dialer.R;
public class SearchEditTextLayout extends FrameLayout {
@@ -39,12 +41,16 @@
private int mLeftMargin;
private int mRightMargin;
- private int mBackgroundColor;
+ /* Subclass-visible for testing */
+ protected boolean mIsExpanded = false;
+ protected boolean mIsFadedOut = false;
private View mCollapsed;
private View mExpanded;
private EditText mSearchView;
+ private ValueAnimator mAnimator;
+
private OnBackButtonClickedListener mOnBackButtonClickedListener;
/**
@@ -56,7 +62,6 @@
public SearchEditTextLayout(Context context, AttributeSet attrs) {
super(context, attrs);
- mBackgroundColor = getResources().getColor(R.color.searchbox_background_color);
}
public void setPreImeKeyListener(OnKeyListener listener) {
@@ -117,32 +122,85 @@
return super.dispatchKeyEventPreIme(event);
}
- public void animateExpandOrCollapse(boolean expand) {
- final ValueAnimator animator;
- if (expand) {
- AnimationUtils.crossFadeViews(mExpanded, mCollapsed, ANIMATION_DURATION);
- animator = ValueAnimator.ofFloat(1f, 0f);
- setBackgroundResource(R.drawable.search_shadow);
- mSearchView.requestFocus();
+ public void fadeOut() {
+ AnimUtils.fadeOut(this, ANIMATION_DURATION);
+ mIsFadedOut = true;
+ }
+
+ public void fadeIn() {
+ AnimUtils.fadeIn(this, ANIMATION_DURATION);
+ mIsFadedOut = false;
+ }
+
+ public void setVisible(boolean visible) {
+ if (visible) {
+ setAlpha(1);
+ setVisibility(View.VISIBLE);
+ mIsFadedOut = false;
} else {
- AnimationUtils.crossFadeViews(mCollapsed, mExpanded, ANIMATION_DURATION);
- animator = ValueAnimator.ofFloat(0f, 1f);
- setBackgroundResource(R.drawable.rounded_corner);
+ setAlpha(0);
+ setVisibility(View.GONE);
+ mIsFadedOut = true;
}
- animator.addUpdateListener(new AnimatorUpdateListener() {
+ }
+ public void expand(boolean animate, boolean requestFocus) {
+ if (animate) {
+ AnimUtils.crossFadeViews(mExpanded, mCollapsed, ANIMATION_DURATION);
+ mAnimator = ValueAnimator.ofFloat(1f, 0f);
+ prepareAnimator(true);
+ } else {
+ mExpanded.setVisibility(View.VISIBLE);
+ mExpanded.setAlpha(1);
+ setMargins(0f);
+ mCollapsed.setVisibility(View.GONE);
+ }
+
+ setBackgroundResource(R.drawable.search_shadow);
+ if (requestFocus) {
+ mSearchView.requestFocus();
+ }
+ mIsExpanded = true;
+ }
+
+ public void collapse(boolean animate) {
+ if (animate) {
+ AnimUtils.crossFadeViews(mCollapsed, mExpanded, ANIMATION_DURATION);
+ mAnimator = ValueAnimator.ofFloat(0f, 1f);
+ prepareAnimator(false);
+ } else {
+ mCollapsed.setVisibility(View.VISIBLE);
+ mCollapsed.setAlpha(1);
+ setMargins(1f);
+ mExpanded.setVisibility(View.GONE);
+ }
+
+ mIsExpanded = false;
+ setBackgroundResource(R.drawable.rounded_corner);
+ }
+
+ private void prepareAnimator(final boolean expand) {
+ if (mAnimator != null) {
+ mAnimator.cancel();
+ }
+
+ mAnimator.addUpdateListener(new AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
final Float fraction = (Float) animation.getAnimatedValue();
- MarginLayoutParams params = (MarginLayoutParams) getLayoutParams();
- params.topMargin = (int) (mTopMargin * fraction);
- params.bottomMargin = (int) (mBottomMargin * fraction);
- params.leftMargin = (int) (mLeftMargin * fraction);
- params.rightMargin = (int) (mRightMargin * fraction);
- requestLayout();
+ setMargins(fraction);
}
});
- animator.setDuration(ANIMATION_DURATION);
- animator.start();
+
+ mAnimator.setDuration(ANIMATION_DURATION);
+ mAnimator.start();
+ }
+
+ public boolean isExpanded() {
+ return mIsExpanded;
+ }
+
+ public boolean isFadedOut() {
+ return mIsFadedOut;
}
private void showInputMethod(View view) {
@@ -152,4 +210,18 @@
imm.showSoftInput(view, 0);
}
}
+
+ /**
+ * Assigns margins to the search box as a fraction of its maximum margin size
+ *
+ * @param fraction How large the margins should be as a fraction of their full size
+ */
+ private void setMargins(float fraction) {
+ MarginLayoutParams params = (MarginLayoutParams) getLayoutParams();
+ params.topMargin = (int) (mTopMargin * fraction);
+ params.bottomMargin = (int) (mBottomMargin * fraction);
+ params.leftMargin = (int) (mLeftMargin * fraction);
+ params.rightMargin = (int) (mRightMargin * fraction);
+ requestLayout();
+ }
}
\ No newline at end of file
diff --git a/tests/src/com/android/dialer/widget/ActionBarControllerTest.java b/tests/src/com/android/dialer/widget/ActionBarControllerTest.java
new file mode 100644
index 0000000..c67ea35
--- /dev/null
+++ b/tests/src/com/android/dialer/widget/ActionBarControllerTest.java
@@ -0,0 +1,181 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.dialer.widget;
+
+import android.content.Context;
+import android.test.InstrumentationTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.dialer.widget.ActionBarController.ActivityUi;
+
+@SmallTest
+public class ActionBarControllerTest extends InstrumentationTestCase {
+
+ private static final int ACTION_BAR_HEIGHT = 100;
+ private ActionBarController mActionBarController;
+ private SearchEditTextLayout mSearchBox;
+ private MockActivityUi mActivityUi;
+
+ private class MockActivityUi implements ActivityUi {
+ boolean isInSearchUi;
+ boolean hasSearchQuery;
+ boolean shouldShowActionBar;
+ int actionBarHideOffset;
+
+ @Override
+ public boolean isInSearchUi() {
+ return isInSearchUi;
+ }
+
+ @Override
+ public boolean hasSearchQuery() {
+ return hasSearchQuery;
+ }
+
+ @Override
+ public boolean shouldShowActionBar() {
+ return shouldShowActionBar;
+ }
+
+ @Override
+ public int getActionBarHeight() {
+ return ACTION_BAR_HEIGHT;
+ }
+
+ @Override
+ public int getActionBarHideOffset() {
+ return actionBarHideOffset;
+ }
+
+ @Override
+ public void setActionBarHideOffset(int hideOffset) {
+ actionBarHideOffset = hideOffset;
+ }
+ }
+
+ /**
+ * Mock version of the searchbox, that updates its state immediately instead of animating
+ */
+ private class MockSearchBox extends SearchEditTextLayout {
+
+ public MockSearchBox(Context context) {
+ super(context, null);
+ }
+
+ @Override
+ public void expand(boolean animate, boolean requestFocus) {
+ mIsExpanded = true;
+ }
+
+ @Override
+ public void collapse(boolean animate) {
+ mIsExpanded = false;
+ }
+ }
+
+ @Override
+ protected void setUp() {
+ mActivityUi = new MockActivityUi();
+ mSearchBox = new MockSearchBox(this.getInstrumentation().getContext());
+ mActionBarController = new ActionBarController(mActivityUi, mSearchBox);
+ }
+
+ // Tapping the search box should only do something when the activity is not in the search UI
+ public void testSearchBoxTapped() {
+ mSearchBox.collapse(false);
+ mActivityUi.isInSearchUi = false;
+ mActionBarController.onSearchBoxTapped();
+ assertActionBarState(true, false, false);
+
+ // Collapse the search box manually again. This time tapping on the search box should not
+ // expand the search box because isInSearchUi is not true.
+ mSearchBox.collapse(false);
+ mActivityUi.isInSearchUi = true;
+ mActionBarController.onSearchBoxTapped();
+ assertActionBarState(false, false, false);
+ }
+
+ // The search box should always end up being faded in and collapsed. If necessary, it should
+ // be slid down or up depending on what the state of the action bar was before that.
+ public void testOnSearchUiExited() {
+ // ActionBar shown previously before entering searchUI
+ mSearchBox.expand(true, false);
+ mSearchBox.setVisible(false);
+ mActivityUi.shouldShowActionBar = true;
+ mActionBarController.onSearchUiExited();
+ assertActionBarState(false, false, false);
+
+ // ActionBar slid up previously before entering searchUI
+ mSearchBox.collapse(false);
+ mSearchBox.setVisible(false);
+ mActivityUi.shouldShowActionBar = false;
+ mActionBarController.onSearchUiExited();
+ assertActionBarState(false, false, true);
+ }
+
+ // Depending on what state the UI was in previously, sliding the dialpad down can mean either
+ // displaying the expanded search box by sliding it down, displaying the unexpanded search box,
+ // or nothing at all.
+ public void testOnDialpadDown() {
+ // No search query typed in the dialpad and action bar was showing before
+ mActivityUi.shouldShowActionBar = true;
+ mActivityUi.isInSearchUi = true;
+ mSearchBox.setVisible(false);
+ mActionBarController.onDialpadDown();
+ assertActionBarState(false, false, false);
+
+ // No search query typed in the dialpad, but action bar was not showing before
+ mActionBarController.slideActionBarUp(false);
+ mActivityUi.shouldShowActionBar = false;
+ mSearchBox.setVisible(false);
+ mActionBarController.onDialpadDown();
+ assertActionBarState(false, false, true);
+
+ // Something typed in the dialpad - so remain in search UI and slide the expanded search
+ // box down
+ mActionBarController.slideActionBarUp(false);
+ mActivityUi.shouldShowActionBar = true;
+ mActivityUi.hasSearchQuery= true;
+ mSearchBox.setVisible(false);
+ mSearchBox.expand(false, false);
+ mActionBarController.onDialpadDown();
+ assertActionBarState(true, false, false);
+ }
+
+ // Sliding the dialpad up should fade out the search box if we weren't already in search, or
+ // slide up the search box otherwise
+ public void testOnDialpadUp() {
+ mActivityUi.isInSearchUi = false;
+ mActionBarController.onDialpadUp();
+ assertActionBarState(false, true, false);
+
+ // In Search UI, with expanded search box and something currently typed in the search box
+ mActivityUi.isInSearchUi = true;
+ mActivityUi.hasSearchQuery = true;
+ mSearchBox.expand(true, false);
+ mSearchBox.setVisible(true);
+ mActionBarController.slideActionBarUp(false);
+ mActionBarController.onDialpadUp();
+ assertActionBarState(true, false, true);
+ }
+
+ private void assertActionBarState(boolean isExpanded, boolean isFadedOut, boolean isSlidUp) {
+ assertEquals(isExpanded, mSearchBox.isExpanded());
+ assertEquals(isFadedOut, mSearchBox.isFadedOut());
+ assertEquals(isSlidUp, mActionBarController.getIsActionBarSlidUp());
+ }
+}