Merge "Add call log list item actions for adding contacts." into mnc-dev
diff --git a/src/com/android/dialer/CallDetailActivity.java b/src/com/android/dialer/CallDetailActivity.java
index dbcfa64..7a23944 100644
--- a/src/com/android/dialer/CallDetailActivity.java
+++ b/src/com/android/dialer/CallDetailActivity.java
@@ -55,6 +55,7 @@
 import com.android.contacts.common.GeoUtil;
 import com.android.contacts.common.CallUtil;
 import com.android.dialer.calllog.CallDetailHistoryAdapter;
+import com.android.dialer.calllog.CallLogNotificationsService;
 import com.android.dialer.calllog.CallTypeHelper;
 import com.android.dialer.calllog.ContactInfo;
 import com.android.dialer.calllog.ContactInfoHelper;
@@ -280,6 +281,9 @@
                 values.put(Voicemails.IS_READ, true);
                 getContentResolver().update(voicemailUri, values,
                         Voicemails.IS_READ + " = 0", null);
+                Intent intent = new Intent(getBaseContext(), CallLogNotificationsService.class);
+                intent.setAction(CallLogNotificationsService.ACTION_MARK_NEW_VOICEMAILS_AS_OLD);
+                getBaseContext().startService(intent);
                 return null;
             }
         });
diff --git a/src/com/android/dialer/DialtactsActivity.java b/src/com/android/dialer/DialtactsActivity.java
index 5c7dd6f..cccc06e 100644
--- a/src/com/android/dialer/DialtactsActivity.java
+++ b/src/com/android/dialer/DialtactsActivity.java
@@ -165,6 +165,15 @@
      */
     private Animation mSlideOut;
 
+    AnimationListenerAdapter mSlideInListener = new AnimationListenerAdapter() {
+        @Override
+        public void onAnimationEnd(Animation animation) {
+            if (!isInSearchUi()) {
+                enterSearchUi(true /* isSmartDial */, mSearchQuery, false);
+            }
+        }
+    };
+
     /**
      * Listener for after slide out animation completes on dialer fragment.
      */
@@ -293,7 +302,7 @@
                 final boolean sameSearchMode = (mIsDialpadShown && mInDialpadSearch) ||
                         (!mIsDialpadShown && mInRegularSearch);
                 if (!sameSearchMode) {
-                    enterSearchUi(mIsDialpadShown, mSearchQuery);
+                    enterSearchUi(mIsDialpadShown, mSearchQuery, true /* animate */);
                 }
             }
 
@@ -318,7 +327,8 @@
         public void onClick(View v) {
             if (!isInSearchUi()) {
                 mActionBarController.onSearchBoxTapped();
-                enterSearchUi(false /* smartDialSearch */, mSearchView.getText().toString());
+                enterSearchUi(false /* smartDialSearch */, mSearchView.getText().toString(),
+                        true /* animate */);
             }
         }
     };
@@ -431,6 +441,7 @@
         mSlideIn.setInterpolator(AnimUtils.EASE_IN);
         mSlideOut.setInterpolator(AnimUtils.EASE_OUT);
 
+        mSlideIn.setAnimationListener(mSlideInListener);
         mSlideOut.setAnimationListener(mSlideOutListener);
 
         mParentLayout = (FrameLayout) findViewById(R.id.dialtacts_mainlayout);
@@ -682,9 +693,7 @@
         }
         mActionBarController.onDialpadUp();
 
-        if (!isInSearchUi()) {
-            enterSearchUi(true /* isSmartDial */, mSearchQuery);
-        }
+        mListsFragment.getView().animate().alpha(0).withLayer();
     }
 
     /**
@@ -898,7 +907,7 @@
     /**
      * Shows the search fragment
      */
-    private void enterSearchUi(boolean smartDialSearch, String query) {
+    private void enterSearchUi(boolean smartDialSearch, String query, boolean animate) {
         if (mStateSaved || getFragmentManager().isDestroyed()) {
             // Weird race condition where fragment is doing work after the activity is destroyed
             // due to talkback being on (b/10209937). Just return since we can't do any
@@ -927,7 +936,11 @@
         mInRegularSearch = !smartDialSearch;
 
         SearchFragment fragment = (SearchFragment) getFragmentManager().findFragmentByTag(tag);
-        transaction.setCustomAnimations(android.R.animator.fade_in, 0);
+        if (animate) {
+            transaction.setCustomAnimations(android.R.animator.fade_in, 0);
+        } else {
+            transaction.setTransition(FragmentTransaction.TRANSIT_NONE);
+        }
         if (fragment == null) {
             if (smartDialSearch) {
                 fragment = new SmartDialSearchFragment();
@@ -941,10 +954,14 @@
         // DialtactsActivity will provide the options menu
         fragment.setHasOptionsMenu(false);
         fragment.setShowEmptyListForNullQuery(true);
-        fragment.setQueryString(query, false /* delaySelection */);
+        if (!smartDialSearch) {
+            fragment.setQueryString(query, false /* delaySelection */);
+        }
         transaction.commit();
 
-        mListsFragment.getView().animate().alpha(0).withLayer();
+        if (animate) {
+            mListsFragment.getView().animate().alpha(0).withLayer();
+        }
         mListsFragment.setUserVisibleHint(false);
     }
 
diff --git a/src/com/android/dialer/calllog/CallLogAdapter.java b/src/com/android/dialer/calllog/CallLogAdapter.java
index 77b2a43..ee83c2e 100644
--- a/src/com/android/dialer/calllog/CallLogAdapter.java
+++ b/src/com/android/dialer/calllog/CallLogAdapter.java
@@ -629,7 +629,7 @@
     private void handleRowExpanded(View view, boolean forceExpand) {
         final CallLogListItemViewHolder views = (CallLogListItemViewHolder) view.getTag();
 
-        if (forceExpand && isExpanded(views.rowId)) {
+        if (views == null || (forceExpand && isExpanded(views.rowId))) {
             return;
         }
 
diff --git a/src/com/android/dialer/calllog/CallLogNotificationsService.java b/src/com/android/dialer/calllog/CallLogNotificationsService.java
index ccd9335..2e0e502 100644
--- a/src/com/android/dialer/calllog/CallLogNotificationsService.java
+++ b/src/com/android/dialer/calllog/CallLogNotificationsService.java
@@ -56,7 +56,7 @@
      */
     public static final String EXTRA_NEW_VOICEMAIL_URI = "NEW_VOICEMAIL_URI";
 
-    private CallLogQueryHandler mCallLogQueryHandler;
+    private VoicemailQueryHandler mVoicemailQueryHandler;
 
     public CallLogNotificationsService() {
         super("CallLogNotificationsService");
@@ -65,7 +65,7 @@
     @Override
     public void onCreate() {
         super.onCreate();
-        mCallLogQueryHandler = new CallLogQueryHandler(getContentResolver(), null /*listener*/);
+        mVoicemailQueryHandler = new VoicemailQueryHandler(this, getContentResolver());
     }
 
     @Override
@@ -74,8 +74,9 @@
             Log.d(TAG, "onHandleIntent: could not handle null intent");
             return;
         }
+
         if (ACTION_MARK_NEW_VOICEMAILS_AS_OLD.equals(intent.getAction())) {
-            mCallLogQueryHandler.markNewVoicemailsAsOld();
+            mVoicemailQueryHandler.markNewVoicemailsAsOld();
         } else if (ACTION_UPDATE_NOTIFICATIONS.equals(intent.getAction())) {
             Uri voicemailUri = (Uri) intent.getParcelableExtra(EXTRA_NEW_VOICEMAIL_URI);
             DefaultVoicemailNotifier.getInstance(this).updateNotification(voicemailUri);
diff --git a/src/com/android/dialer/calllog/CallLogQueryHandler.java b/src/com/android/dialer/calllog/CallLogQueryHandler.java
index 761c8e0..7eb5f8a 100644
--- a/src/com/android/dialer/calllog/CallLogQueryHandler.java
+++ b/src/com/android/dialer/calllog/CallLogQueryHandler.java
@@ -20,8 +20,6 @@
 import android.content.ContentResolver;
 import android.content.ContentValues;
 import android.database.Cursor;
-import android.database.MatrixCursor;
-import android.database.MergeCursor;
 import android.database.sqlite.SQLiteDatabaseCorruptException;
 import android.database.sqlite.SQLiteDiskIOException;
 import android.database.sqlite.SQLiteException;
@@ -35,7 +33,6 @@
 import android.provider.VoicemailContract.Voicemails;
 import android.util.Log;
 
-import com.android.common.io.MoreCloseables;
 import com.android.contacts.common.database.NoNullCursorAsyncQueryHandler;
 import com.android.dialer.voicemail.VoicemailStatusHelperImpl;
 
@@ -55,12 +52,10 @@
     private static final int QUERY_CALLLOG_TOKEN = 54;
     /** The token for the query to mark all missed calls as old after seeing the call log. */
     private static final int UPDATE_MARK_AS_OLD_TOKEN = 55;
-    /** The token for the query to mark all new voicemails as old. */
-    private static final int UPDATE_MARK_VOICEMAILS_AS_OLD_TOKEN = 56;
     /** The token for the query to mark all missed calls as read after seeing the call log. */
-    private static final int UPDATE_MARK_MISSED_CALL_AS_READ_TOKEN = 57;
+    private static final int UPDATE_MARK_MISSED_CALL_AS_READ_TOKEN = 56;
     /** The token for the query to fetch voicemail status messages. */
-    private static final int QUERY_VOICEMAIL_STATUS_TOKEN = 58;
+    private static final int QUERY_VOICEMAIL_STATUS_TOKEN = 57;
 
     private final int mLogLimit;
 
@@ -195,22 +190,6 @@
                 values, where.toString(), null);
     }
 
-    /** Updates all new voicemails to mark them as old. */
-    public void markNewVoicemailsAsOld() {
-        // Mark all "new" voicemails as not new anymore.
-        StringBuilder where = new StringBuilder();
-        where.append(Calls.NEW);
-        where.append(" = 1 AND ");
-        where.append(Calls.TYPE);
-        where.append(" = ?");
-
-        ContentValues values = new ContentValues(1);
-        values.put(Calls.NEW, "0");
-
-        startUpdate(UPDATE_MARK_VOICEMAILS_AS_OLD_TOKEN, null, Calls.CONTENT_URI_WITH_VOICEMAIL,
-                values, where.toString(), new String[]{ Integer.toString(Calls.VOICEMAIL_TYPE) });
-    }
-
     /** Updates all missed calls to mark them as read. */
     public void markMissedCallsAsRead() {
         // Mark all "new" calls as not new anymore.
@@ -227,7 +206,8 @@
     }
 
     @Override
-    protected synchronized void onNotNullableQueryComplete(int token, Object cookie, Cursor cursor) {
+    protected synchronized void onNotNullableQueryComplete(int token, Object cookie,
+            Cursor cursor) {
         if (cursor == null) {
             return;
         }
@@ -274,7 +254,7 @@
         void onVoicemailStatusFetched(Cursor statusCursor);
 
         /**
-         * Called when {@link CallLogQueryHandler#fetchCalls(int)}complete.
+         * Called when {@link CallLogQueryHandler#fetchCalls(int)} complete.
          * Returns true if takes ownership of cursor.
          */
         boolean onCallsFetched(Cursor combinedCursor);
diff --git a/src/com/android/dialer/calllog/VoicemailQueryHandler.java b/src/com/android/dialer/calllog/VoicemailQueryHandler.java
new file mode 100644
index 0000000..26f9bd1
--- /dev/null
+++ b/src/com/android/dialer/calllog/VoicemailQueryHandler.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2011 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.calllog;
+
+import android.content.AsyncQueryHandler;
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.Intent;
+import android.provider.CallLog.Calls;
+import android.util.Log;
+
+/**
+ * Handles asynchronous queries to the call log for voicemail.
+ */
+public class VoicemailQueryHandler extends AsyncQueryHandler {
+    private static final String TAG = "VoicemailQueryHandler";
+
+    /** The token for the query to mark all new voicemails as old. */
+    private static final int UPDATE_MARK_VOICEMAILS_AS_OLD_TOKEN = 50;
+    private Context mContext;
+
+    public VoicemailQueryHandler(Context context, ContentResolver contentResolver) {
+        super(contentResolver);
+        mContext = context;
+    }
+
+    /** Updates all new voicemails to mark them as old. */
+    public void markNewVoicemailsAsOld() {
+        // Mark all "new" voicemails as not new anymore.
+        StringBuilder where = new StringBuilder();
+        where.append(Calls.NEW);
+        where.append(" = 1 AND ");
+        where.append(Calls.TYPE);
+        where.append(" = ?");
+
+        ContentValues values = new ContentValues(1);
+        values.put(Calls.NEW, "0");
+
+        startUpdate(UPDATE_MARK_VOICEMAILS_AS_OLD_TOKEN, null, Calls.CONTENT_URI_WITH_VOICEMAIL,
+                values, where.toString(), new String[]{ Integer.toString(Calls.VOICEMAIL_TYPE) });
+    }
+
+    @Override
+    protected void onUpdateComplete(int token, Object cookie, int result) {
+        if (token == UPDATE_MARK_VOICEMAILS_AS_OLD_TOKEN) {
+            if (mContext != null) {
+                Intent serviceIntent = new Intent(mContext, CallLogNotificationsService.class);
+                serviceIntent.setAction(CallLogNotificationsService.ACTION_UPDATE_NOTIFICATIONS);
+                mContext.startService(serviceIntent);
+            } else {
+                Log.w(TAG, "Unknown update completed: ignoring: " + token);
+            }
+        }
+    }
+}