Merge "Implement adding favorites."
diff --git a/java/com/android/dialer/constants/ActivityRequestCodes.java b/java/com/android/dialer/constants/ActivityRequestCodes.java
index 7fd619b..c96fe9e 100644
--- a/java/com/android/dialer/constants/ActivityRequestCodes.java
+++ b/java/com/android/dialer/constants/ActivityRequestCodes.java
@@ -37,4 +37,9 @@
 
   /** Request code for {@link com.android.dialer.calldetails.OldCallDetailsActivity} intent. */
   public static final int DIALTACTS_CALL_DETAILS = 4;
+
+  /**
+   * Request code for {@link com.android.dialer.speeddial.SpeedDialFragment} contact picker intent.
+   */
+  public static final int SPEED_DIAL_ADD_FAVORITE = 5;
 }
diff --git a/java/com/android/dialer/speeddial/AddFavoriteActivity.java b/java/com/android/dialer/speeddial/AddFavoriteActivity.java
deleted file mode 100644
index eea6e84..0000000
--- a/java/com/android/dialer/speeddial/AddFavoriteActivity.java
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * Copyright (C) 2017 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.speeddial;
-
-import android.content.ContentValues;
-import android.net.Uri;
-import android.os.Bundle;
-import android.provider.ContactsContract.Contacts;
-import android.support.annotation.Nullable;
-import android.support.annotation.WorkerThread;
-import android.support.v7.app.AppCompatActivity;
-import android.support.v7.widget.SearchView;
-import android.view.Menu;
-import android.view.MenuItem;
-import android.widget.ImageView;
-import com.android.dialer.common.Assert;
-import com.android.dialer.common.concurrent.DialerExecutorComponent;
-import com.android.dialer.contactsfragment.ContactsFragment;
-import com.android.dialer.contactsfragment.ContactsFragment.OnContactSelectedListener;
-
-/**
- * Activity for selecting a single contact and adding it to favorites.
- *
- * <p>Contacts are displayed using {@link ContactsFragment}. Contacts are searchable via search bar
- * in the toolbar. When a contact is selected, it's uri is passed back in the result data.
- */
-public class AddFavoriteActivity extends AppCompatActivity implements OnContactSelectedListener {
-
-  private ContactsFragment contactsFragment;
-
-  @Override
-  protected void onCreate(@Nullable Bundle bundle) {
-    super.onCreate(bundle);
-    setContentView(R.layout.add_favorite_activity);
-    contactsFragment = ContactsFragment.newAddFavoritesInstance();
-    getFragmentManager()
-        .beginTransaction()
-        .add(R.id.add_favorite_container, contactsFragment, null)
-        .commit();
-  }
-
-  @Override
-  public boolean onCreateOptionsMenu(Menu menu) {
-    getMenuInflater().inflate(R.menu.add_favorite_menu, menu);
-
-    MenuItem searchItem = menu.findItem(R.id.action_search);
-    SearchView searchView = (SearchView) searchItem.getActionView();
-    searchView.setOnQueryTextListener(
-        new SearchView.OnQueryTextListener() {
-          @Override
-          public boolean onQueryTextSubmit(String query) {
-            if (!searchView.isIconified()) {
-              searchView.setIconified(true);
-            }
-            searchItem.collapseActionView();
-            return false;
-          }
-
-          @Override
-          public boolean onQueryTextChange(String s) {
-            contactsFragment.updateQuery(s);
-            return false;
-          }
-        });
-    return true;
-  }
-
-  @Override
-  public void onContactSelected(ImageView photo, Uri contactUri, long contactId) {
-    DialerExecutorComponent.get(this)
-        .dialerExecutorFactory()
-        .createUiTaskBuilder(
-            getFragmentManager(), "mark_contact_favorite", this::markContactStarred)
-        .onSuccess(output -> finish())
-        .onFailure(this::onContactStarredFailed)
-        .build()
-        .executeParallel(contactId);
-  }
-
-  @WorkerThread
-  private int markContactStarred(long contactId) {
-    // TODO(calderwoodra): For now, we will just mark contacts as starred. This means that contacts
-    // will only be able to exist once in favorites until we implement multiple contact avenues.
-    ContentValues contentValues = new ContentValues();
-    contentValues.put(Contacts.STARRED, 1);
-
-    String where = Contacts._ID + " = ?";
-    String[] selectionArgs = new String[] {Long.toString(contactId)};
-    return getContentResolver().update(Contacts.CONTENT_URI, contentValues, where, selectionArgs);
-  }
-
-  private void onContactStarredFailed(Throwable throwable) {
-    throw Assert.createAssertionFailException(throwable.getMessage());
-  }
-}
diff --git a/java/com/android/dialer/speeddial/AndroidManifest.xml b/java/com/android/dialer/speeddial/AndroidManifest.xml
index 99ab12c..913e500 100644
--- a/java/com/android/dialer/speeddial/AndroidManifest.xml
+++ b/java/com/android/dialer/speeddial/AndroidManifest.xml
@@ -13,15 +13,4 @@
  ~ See the License for the specific language governing permissions and
  ~ limitations under the License
  -->
-<manifest
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.android.dialer.speeddial">
-
-  <application android:theme="@style/Theme.AppCompat">
-    <activity
-        android:name="com.android.dialer.speeddial.AddFavoriteActivity"
-        android:label="@string/add_favorite_activity_title"
-        android:exported="false"
-        android:theme="@style/DialtactsTheme"/>
-  </application>
-</manifest>
\ No newline at end of file
+<manifest package="com.android.dialer.speeddial"/>
diff --git a/java/com/android/dialer/speeddial/SpeedDialFragment.java b/java/com/android/dialer/speeddial/SpeedDialFragment.java
index 004588e..52d7b7f 100644
--- a/java/com/android/dialer/speeddial/SpeedDialFragment.java
+++ b/java/com/android/dialer/speeddial/SpeedDialFragment.java
@@ -18,8 +18,10 @@
 
 import android.content.Intent;
 import android.os.Bundle;
+import android.provider.ContactsContract.CommonDataKinds.Phone;
 import android.support.annotation.Nullable;
 import android.support.v4.app.Fragment;
+import android.support.v7.app.AppCompatActivity;
 import android.support.v7.widget.RecyclerView;
 import android.support.v7.widget.helper.ItemTouchHelper;
 import android.view.LayoutInflater;
@@ -73,6 +75,12 @@
   private SpeedDialLayoutManager layoutManager;
   private SupportUiListener<ImmutableList<SpeedDialUiItem>> speedDialLoaderListener;
 
+  /**
+   * We update the UI every time the fragment is resumed. This boolean suppresses that functionality
+   * once per onResume call.
+   */
+  private boolean updateSpeedDialItemsOnResume = true;
+
   public static SpeedDialFragment newInstance() {
     return new SpeedDialFragment();
   }
@@ -123,6 +131,11 @@
   @Override
   public void onResume() {
     super.onResume();
+    if (!updateSpeedDialItemsOnResume) {
+      updateSpeedDialItemsOnResume = true;
+      return;
+    }
+
     speedDialLoaderListener.listen(
         getContext(),
         UiItemLoaderComponent.get(getContext()).speedDialUiItemLoader().loadSpeedDialUiItems(),
@@ -138,11 +151,34 @@
         });
   }
 
+  @Override
+  public void onActivityResult(int requestCode, int resultCode, Intent data) {
+    if (requestCode == ActivityRequestCodes.SPEED_DIAL_ADD_FAVORITE) {
+      if (resultCode == AppCompatActivity.RESULT_OK && data.getData() != null) {
+        updateSpeedDialItemsOnResume = false;
+        speedDialLoaderListener.listen(
+            getContext(),
+            UiItemLoaderComponent.get(getContext())
+                .speedDialUiItemLoader()
+                .starContact(data.getData()),
+            speedDialUiItems -> {
+              adapter.setSpeedDialUiItems(speedDialUiItems);
+              // TODO(calderwoodra): Use DiffUtil to properly update and animate the change
+              adapter.notifyDataSetChanged();
+            },
+            throwable -> {
+              throw new RuntimeException(throwable);
+            });
+      }
+    }
+  }
+
   private class SpeedDialFragmentHeaderListener implements SpeedDialHeaderListener {
 
     @Override
     public void onAddFavoriteClicked() {
-      startActivity(new Intent(getContext(), AddFavoriteActivity.class));
+      Intent intent = new Intent(Intent.ACTION_PICK, Phone.CONTENT_URI);
+      startActivityForResult(intent, ActivityRequestCodes.SPEED_DIAL_ADD_FAVORITE);
     }
   }
 
diff --git a/java/com/android/dialer/speeddial/loader/SpeedDialUiItemLoader.java b/java/com/android/dialer/speeddial/loader/SpeedDialUiItemLoader.java
index 9a027de..9ea84ec 100644
--- a/java/com/android/dialer/speeddial/loader/SpeedDialUiItemLoader.java
+++ b/java/com/android/dialer/speeddial/loader/SpeedDialUiItemLoader.java
@@ -17,6 +17,7 @@
 package com.android.dialer.speeddial.loader;
 
 import android.annotation.TargetApi;
+import android.content.ContentValues;
 import android.content.Context;
 import android.database.Cursor;
 import android.net.Uri;
@@ -97,14 +98,61 @@
    * from {@link Contacts#STREQUENT_PHONE_ONLY}.
    */
   public ListenableFuture<ImmutableList<SpeedDialUiItem>> loadSpeedDialUiItems() {
-    return dialerFutureSerializer.submitAsync(
-        () -> backgroundExecutor.submit(this::doInBackground), backgroundExecutor);
+    return dialerFutureSerializer.submit(this::loadSpeedDialUiItemsInternal, backgroundExecutor);
+  }
+
+  /**
+   * Takes a contact uri from {@link Phone#CONTENT_URI} and updates {@link Phone#STARRED} to be
+   * true, if it isn't already or Inserts the contact into the {@link SpeedDialEntryDatabaseHelper}
+   */
+  public ListenableFuture<ImmutableList<SpeedDialUiItem>> starContact(Uri contactUri) {
+    return dialerFutureSerializer.submit(
+        () -> insertNewContactEntry(contactUri), backgroundExecutor);
   }
 
   @WorkerThread
-  private ImmutableList<SpeedDialUiItem> doInBackground() {
+  private ImmutableList<SpeedDialUiItem> insertNewContactEntry(Uri contactUri) {
     Assert.isWorkerThread();
-    SpeedDialEntryDao db = new SpeedDialEntryDatabaseHelper(appContext);
+    try (Cursor cursor =
+        appContext
+            .getContentResolver()
+            .query(contactUri, SpeedDialUiItem.PHONE_PROJECTION, null, null, null)) {
+      if (cursor == null) {
+        LogUtil.e("SpeedDialUiItemLoader.insertNewContactEntry", "Cursor was null");
+        return loadSpeedDialUiItemsInternal();
+      }
+      Assert.checkArgument(cursor.moveToFirst(), "Cursor should never be empty");
+      SpeedDialUiItem item = SpeedDialUiItem.fromCursor(cursor);
+
+      // Star the contact if it isn't starred already, then return.
+      if (!item.isStarred()) {
+        ContentValues values = new ContentValues();
+        values.put(Phone.STARRED, "1");
+        appContext
+            .getContentResolver()
+            .update(
+                Contacts.CONTENT_URI,
+                values,
+                Contacts._ID + " = ?",
+                new String[] {Long.toString(item.contactId())});
+      }
+
+      // Insert a new entry into the SpeedDialEntry database
+      getSpeedDialEntryDao()
+          .insert(
+              SpeedDialEntry.builder()
+                  .setLookupKey(item.lookupKey())
+                  .setContactId(item.contactId())
+                  .setDefaultChannel(item.defaultChannel())
+                  .build());
+    }
+    return loadSpeedDialUiItemsInternal();
+  }
+
+  @WorkerThread
+  private ImmutableList<SpeedDialUiItem> loadSpeedDialUiItemsInternal() {
+    Assert.isWorkerThread();
+    SpeedDialEntryDao db = getSpeedDialEntryDao();
 
     // This is the list of contacts that we will display to the user
     List<SpeedDialUiItem> speedDialUiItems = new ArrayList<>();
@@ -392,4 +440,8 @@
     }
     return item.toBuilder().setChannels(newChannelsList.build()).build();
   }
+
+  private SpeedDialEntryDao getSpeedDialEntryDao() {
+    return new SpeedDialEntryDatabaseHelper(appContext);
+  }
 }