Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 0388bfc9 authored by calderwoodra's avatar calderwoodra Committed by Copybara-Service
Browse files

Implement adding favorites.

This change deletes the AddFavoritesActivity in favor of the activity provided
to us by the Intent.ACTION_PICK intent.

Bug: 36841782
Test: SpeedDialUiItemLoaderTest
PiperOrigin-RevId: 193439522
Change-Id: Ie297abd2307c727d746f26ba99961de116636dc4
parent 560eb125
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -37,4 +37,9 @@ public final class ActivityRequestCodes {

  /** 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;
}
+0 −109
Original line number Diff line number Diff line
/*
 * 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());
  }
}
+1 −12
Original line number Diff line number Diff line
@@ -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"/>
+37 −1
Original line number Diff line number Diff line
@@ -18,8 +18,10 @@ package com.android.dialer.speeddial;

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 @@ public class SpeedDialFragment extends Fragment {
  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 @@ public class SpeedDialFragment extends Fragment {
  @Override
  public void onResume() {
    super.onResume();
    if (!updateSpeedDialItemsOnResume) {
      updateSpeedDialItemsOnResume = true;
      return;
    }

    speedDialLoaderListener.listen(
        getContext(),
        UiItemLoaderComponent.get(getContext()).speedDialUiItemLoader().loadSpeedDialUiItems(),
@@ -138,11 +151,34 @@ public class SpeedDialFragment extends Fragment {
        });
  }

  @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);
    }
  }

+56 −4
Original line number Diff line number Diff line
@@ -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 @@ public final class SpeedDialUiItemLoader {
   * 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 @@ public final class SpeedDialUiItemLoader {
    }
    return item.toBuilder().setChannels(newChannelsList.build()).build();
  }

  private SpeedDialEntryDao getSpeedDialEntryDao() {
    return new SpeedDialEntryDatabaseHelper(appContext);
  }
}