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

Commit af2757a2 authored by Treehugger Robot's avatar Treehugger Robot Committed by Gerrit Code Review
Browse files

Merge "Implement adding favorites."

parents 1d1913a9 0388bfc9
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);
  }
}