Speed up app bar and FAB animation (1/2)
am: bd114976b3

Change-Id: I4c8f129d4140fc3f871b2a9e3f665e4c8a1746aa
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 51c10a4..2058e70 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -16,8 +16,8 @@
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
     package="com.android.contacts"
-    android:versionCode="10507"
-    android:versionName="1.5.7">
+    android:versionCode="10508"
+    android:versionName="1.5.8">
 
     <uses-sdk android:minSdkVersion="21" android:targetSdkVersion="25" />
 
@@ -348,8 +348,6 @@
             android:name=".activities.CompactContactEditorActivity"
             android:label="@string/editContactActivityLabel"
             android:theme="@style/EditorActivityTheme"
-            android:excludeFromRecents="true"
-            android:taskAffinity=""
             android:windowSoftInputMode="stateHidden|adjustResize">
 
             <intent-filter android:label="@string/editContactDescription">
diff --git a/res/drawable/ic_add_circle_24dp.xml b/res/drawable/ic_add_circle_24dp.xml
index 85a31c0..ae37470 100644
--- a/res/drawable/ic_add_circle_24dp.xml
+++ b/res/drawable/ic_add_circle_24dp.xml
@@ -14,13 +14,14 @@
      limitations under the License.
 -->
 
-<!-- A circle with a plus sign in the middle (ic_add_circle) -->
+<!-- Customized ic_add_circle material asset. See b/30018040#comment12 -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="24dp"
         android:height="24dp"
-        android:viewportWidth="24.0"
-        android:viewportHeight="24.0">
-    <path
-        android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM17,13h-4v4h-2v-4L7,13v-2h4L11,7h2v4h4v2z"
-        android:fillColor="#0288d1"/>
+        android:width="24dp"
+        android:viewportHeight="192.0"
+        android:viewportWidth="192.0">
+  <path android:fillColor="#F5F5F5"
+        android:pathData="M96,8C47.38,8 8,47.38 8,96s39.38,88 88,88s88,-39.38 88,-88S144.62,8 96,8z"/>
+  <path android:fillColor="#039BE5"
+        android:pathData="M124,100h-24v24h-8v-24H68v-8h24V68h8v24h24V100z"/>
 </vector>
diff --git a/res/layout/selection_bar.xml b/res/layout/selection_bar.xml
index c2521d1..72a5a10 100644
--- a/res/layout/selection_bar.xml
+++ b/res/layout/selection_bar.xml
@@ -22,8 +22,8 @@
 
     <ImageButton
         android:id="@+id/selection_close"
-        android:layout_width="48dp"
-        android:layout_height="48dp"
+        android:layout_width="@dimen/selection_bar_close_icon_size"
+        android:layout_height="@dimen/selection_bar_close_icon_size"
         android:layout_gravity="center_vertical|start"
         android:alpha="@dimen/close_icon_alpha"
         android:background="?attr/selectableItemBackgroundBorderless"
diff --git a/res/values/colors.xml b/res/values/colors.xml
index 8089a2e..3656089 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -84,4 +84,9 @@
     <!-- Color of background of all empty states. -->
     <color name="empty_state_background">#efefef</color>
 
+    <!-- Colors of swipeRefreshLayout's spinning circle. -->
+    <color name="swipe_refresh_color1">#0f9d58</color>
+    <color name="swipe_refresh_color2">#dd4b37</color>
+    <color name="swipe_refresh_color3">#4285f4</color>
+    <color name="swipe_refresh_color4">#f4b400</color>
 </resources>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index a3b097f..5db217a 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -292,4 +292,7 @@
 
     <!-- Minimum height for group name EditText -->
     <dimen name="group_name_edit_text_min_height">48dp</dimen>
+
+    <!-- Distance to pull down before causing a refresh. -->
+    <dimen name="pull_to_refresh_distance">40dp</dimen>
 </resources>
diff --git a/src/com/android/contacts/activities/PeopleActivity.java b/src/com/android/contacts/activities/PeopleActivity.java
index 8429d2a..d22ef05 100644
--- a/src/com/android/contacts/activities/PeopleActivity.java
+++ b/src/com/android/contacts/activities/PeopleActivity.java
@@ -37,6 +37,7 @@
 import android.support.v4.view.GravityCompat;
 import android.support.v4.view.PagerAdapter;
 import android.support.v4.view.ViewPager;
+import android.support.v4.widget.SwipeRefreshLayout;
 import android.text.TextUtils;
 import android.util.Log;
 import android.view.KeyCharacterMap;
@@ -55,6 +56,7 @@
 import com.android.contacts.ContactsDrawerActivity;
 import com.android.contacts.R;
 import com.android.contacts.activities.ActionBarAdapter.TabState;
+import com.android.contacts.common.Experiments;
 import com.android.contacts.common.activity.RequestPermissionsActivity;
 import com.android.contacts.common.compat.CompatUtils;
 import com.android.contacts.common.interactions.ImportExportDialogFragment;
@@ -74,6 +76,7 @@
 import com.android.contacts.common.util.Constants;
 import com.android.contacts.common.util.ImplicitIntentsUtil;
 import com.android.contacts.common.widget.FloatingActionButtonController;
+import com.android.contacts.commonbind.experiments.Flags;
 import com.android.contacts.editor.EditorIntents;
 import com.android.contacts.interactions.ContactDeletionInteraction;
 import com.android.contacts.interactions.ContactMultiDeletionInteraction;
@@ -1414,6 +1417,47 @@
                         .sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
             }
         }
+
+        // Determine whether the account has pullToRefresh feature
+        if (Flags.getInstance(this).getBoolean(Experiments.PULL_TO_REFRESH)) {
+            setSwipeRefreshLayoutEnabledOrNot(filter);
+        }
+    }
+
+    private void setSwipeRefreshLayoutEnabledOrNot(ContactListFilter filter) {
+        final SwipeRefreshLayout swipeRefreshLayout = mAllFragment.getSwipeRefreshLayout();
+        if (swipeRefreshLayout == null) {
+            if (Log.isLoggable(TAG, Log.DEBUG)) {
+                Log.d(TAG, "Can not load swipeRefreshLayout, swipeRefreshLayout is null");
+            }
+            return;
+        }
+        swipeRefreshLayout.setRefreshing(false);
+        swipeRefreshLayout.setEnabled(false);
+
+        if (mActionBarAdapter.isSearchMode()) {
+            return;
+        }
+
+        if (GoogleAccountType.ACCOUNT_TYPE.equals(filter.accountType) && filter.dataSet == null
+                && filter.filterType == ContactListFilter.FILTER_TYPE_ACCOUNT) {
+            swipeRefreshLayout.setEnabled(true);
+            return;
+        }
+        // TODO(samchen): check against both FILTER_TYPE_ALL_ACCOUNTS and FILTER_TYPE_DEFAULT
+        if (filter.filterType == ContactListFilter.FILTER_TYPE_ALL_ACCOUNTS) {
+            final List<AccountWithDataSet> accounts = AccountTypeManager.getInstance(this)
+                    .getAccounts(/* contactsWritableOnly */ true);
+            if (accounts != null && accounts.size() > 0) {
+                for (AccountWithDataSet account : accounts) {
+                    if (GoogleAccountType.ACCOUNT_TYPE.equals(account.type)
+                            && filter.dataSet == null) {
+                        swipeRefreshLayout.setEnabled(true);
+                        return;
+                    }
+                }
+            }
+        }
     }
 
     private String getActionBarTitleForAccount(ContactListFilter filter) {
diff --git a/src/com/android/contacts/editor/AggregationSuggestionView.java b/src/com/android/contacts/editor/AggregationSuggestionView.java
index e3bab7e..5a3da00 100644
--- a/src/com/android/contacts/editor/AggregationSuggestionView.java
+++ b/src/com/android/contacts/editor/AggregationSuggestionView.java
@@ -22,6 +22,7 @@
 import android.provider.ContactsContract.Contacts;
 import android.text.TextUtils;
 import android.util.AttributeSet;
+import android.view.View;
 import android.widget.ImageView;
 import android.widget.LinearLayout;
 import android.widget.TextView;
@@ -104,6 +105,8 @@
             dataText = suggestion.emailAddress;
         } else if (suggestion.phoneNumber != null) {
             dataText = suggestion.phoneNumber;
+            // Phone numbers should always be in LTR mode.
+            data.setTextDirection(View.TEXT_DIRECTION_LTR);
         }
         data.setText(dataText);
     }
diff --git a/src/com/android/contacts/editor/TextFieldsEditorView.java b/src/com/android/contacts/editor/TextFieldsEditorView.java
index f705819..df201e1 100644
--- a/src/com/android/contacts/editor/TextFieldsEditorView.java
+++ b/src/com/android/contacts/editor/TextFieldsEditorView.java
@@ -244,7 +244,7 @@
             // Show the "next" button in IME to navigate between text fields
             // TODO: Still need to properly navigate to/from sections without text fields,
             // See Bug: 5713510
-            fieldView.setImeOptions(EditorInfo.IME_ACTION_NEXT);
+            fieldView.setImeOptions(EditorInfo.IME_ACTION_NEXT | EditorInfo.IME_FLAG_NO_FULLSCREEN);
 
             // Read current value from state
             final String column = field.column;
diff --git a/src/com/android/contacts/list/ContactBrowseListFragment.java b/src/com/android/contacts/list/ContactBrowseListFragment.java
index 2060068..da33b55 100644
--- a/src/com/android/contacts/list/ContactBrowseListFragment.java
+++ b/src/com/android/contacts/list/ContactBrowseListFragment.java
@@ -92,7 +92,7 @@
     private boolean mSelectionVerified;
     private int mLastSelectedPosition = -1;
     private boolean mRefreshingContactUri;
-    private ContactListFilter mFilter;
+    protected ContactListFilter mFilter;
     private String mPersistentSelectionPrefix = PERSISTENT_SELECTION_PREFIX;
 
     protected OnContactBrowserActionListener mListener;
diff --git a/src/com/android/contacts/list/DefaultContactBrowseListFragment.java b/src/com/android/contacts/list/DefaultContactBrowseListFragment.java
index 8fdce75..cbbccb0 100644
--- a/src/com/android/contacts/list/DefaultContactBrowseListFragment.java
+++ b/src/com/android/contacts/list/DefaultContactBrowseListFragment.java
@@ -15,11 +15,17 @@
  */
 package com.android.contacts.list;
 
+import android.accounts.Account;
+import android.content.ContentResolver;
 import android.content.Context;
 import android.content.CursorLoader;
 import android.content.Loader;
 import android.database.Cursor;
 import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.provider.ContactsContract;
+import android.support.v4.widget.SwipeRefreshLayout;
 import android.text.TextUtils;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -29,13 +35,19 @@
 import android.widget.TextView;
 
 import com.android.contacts.R;
+import com.android.contacts.common.Experiments;
 import com.android.contacts.common.list.ContactListAdapter;
 import com.android.contacts.common.list.ContactListFilter;
 import com.android.contacts.common.list.ContactListFilterController;
 import com.android.contacts.common.list.ContactListItemView;
 import com.android.contacts.common.list.DefaultContactListAdapter;
 import com.android.contacts.common.list.FavoritesAndContactsLoader;
+import com.android.contacts.common.model.AccountTypeManager;
 import com.android.contacts.common.model.account.AccountWithDataSet;
+import com.android.contacts.common.model.account.GoogleAccountType;
+import com.android.contacts.commonbind.experiments.Flags;
+
+import java.util.List;
 
 /**
  * Fragment containing a contact list used for browsing (as compared to
@@ -45,6 +57,7 @@
     private View mSearchHeaderView;
     private View mSearchProgress;
     private TextView mSearchProgressText;
+    private SwipeRefreshLayout mSwipeRefreshLayout;
 
     public DefaultContactBrowseListFragment() {
         setPhotoLoaderEnabled(true);
@@ -119,6 +132,10 @@
     protected void onCreateView(LayoutInflater inflater, ViewGroup container) {
         super.onCreateView(inflater, container);
 
+        if (Flags.getInstance(getActivity()).getBoolean(Experiments.PULL_TO_REFRESH)) {
+            initSwipeRefreshLayout();
+
+        }
         // Putting the header view inside a container will allow us to make
         // it invisible later. See checkHeaderViewVisibility()
         FrameLayout headerContainer = new FrameLayout(inflater.getContext());
@@ -131,6 +148,62 @@
         mSearchProgressText = (TextView) mSearchHeaderView.findViewById(R.id.totalContactsText);
     }
 
+    private void initSwipeRefreshLayout() {
+        mSwipeRefreshLayout = (SwipeRefreshLayout) mView.findViewById(R.id.swipe_refresh);
+        if (mSwipeRefreshLayout == null) {
+            return;
+        }
+
+        mSwipeRefreshLayout.setEnabled(true);
+        // Request sync contacts
+        mSwipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
+            @Override
+            public void onRefresh() {
+                syncContacts(mFilter);
+                new Handler().postDelayed(new Runnable() {
+                    @Override
+                    public void run() {
+                        mSwipeRefreshLayout.setRefreshing(false);
+                    }
+                }, 3000 /* spinning time */);
+            }
+        });
+        mSwipeRefreshLayout.setColorSchemeResources(
+                R.color.swipe_refresh_color1,
+                R.color.swipe_refresh_color2,
+                R.color.swipe_refresh_color3,
+                R.color.swipe_refresh_color4);
+        mSwipeRefreshLayout.setDistanceToTriggerSync(
+                (int) getResources().getDimension(R.dimen.pull_to_refresh_distance));
+    }
+
+    /** Request sync for Google accounts(not include G+ accounts) in filter. */
+    private void syncContacts(ContactListFilter filter) {
+        if (filter == null) {
+            return;
+        }
+        final Bundle bundle = new Bundle();
+        bundle.putBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, true);
+        bundle.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true);
+
+        if (GoogleAccountType.ACCOUNT_TYPE.equals(filter.accountType) && filter.dataSet == null) {
+            final Account account = new Account(filter.accountName, filter.accountType);
+            ContentResolver.requestSync(account, ContactsContract.AUTHORITY, bundle);
+        } else {
+            final List<AccountWithDataSet> accounts = AccountTypeManager.getInstance(
+                    getActivity()).getAccounts(/* contactsWritableOnly= */ true);
+            if (accounts != null && accounts.size() > 0) {
+                for (AccountWithDataSet account : accounts) {
+                    if (GoogleAccountType.ACCOUNT_TYPE.equals(account.type)
+                            && filter.dataSet == null) {
+                        ContentResolver.requestSync(new Account(account.name, account.type),
+                                ContactsContract.AUTHORITY, bundle);
+                    }
+                }
+            }
+        }
+    }
+
     @Override
     protected void setSearchMode(boolean flag) {
         super.setSearchMode(flag);
@@ -179,4 +252,8 @@
             }
         }
     }
+
+    public SwipeRefreshLayout getSwipeRefreshLayout() {
+        return mSwipeRefreshLayout;
+    }
 }
\ No newline at end of file