From 0388bfc9717dbf1d67ed4c14f34aa5414a08b39c Mon Sep 17 00:00:00 2001 From: calderwoodra Date: Wed, 18 Apr 2018 16:48:57 -0700 Subject: 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 --- .../dialer/constants/ActivityRequestCodes.java | 5 + .../dialer/speeddial/AddFavoriteActivity.java | 109 --------------------- .../android/dialer/speeddial/AndroidManifest.xml | 13 +-- .../dialer/speeddial/SpeedDialFragment.java | 38 ++++++- .../speeddial/loader/SpeedDialUiItemLoader.java | 60 +++++++++++- 5 files changed, 99 insertions(+), 126 deletions(-) delete mode 100644 java/com/android/dialer/speeddial/AddFavoriteActivity.java diff --git a/java/com/android/dialer/constants/ActivityRequestCodes.java b/java/com/android/dialer/constants/ActivityRequestCodes.java index 7fd619ba9..c96fe9ed1 100644 --- a/java/com/android/dialer/constants/ActivityRequestCodes.java +++ b/java/com/android/dialer/constants/ActivityRequestCodes.java @@ -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; } diff --git a/java/com/android/dialer/speeddial/AddFavoriteActivity.java b/java/com/android/dialer/speeddial/AddFavoriteActivity.java deleted file mode 100644 index eea6e840e..000000000 --- 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. - * - *

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 99ab12c4b..913e5007a 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 --> - - - - - - \ No newline at end of file + diff --git a/java/com/android/dialer/speeddial/SpeedDialFragment.java b/java/com/android/dialer/speeddial/SpeedDialFragment.java index 004588ef4..52d7b7fc9 100644 --- a/java/com/android/dialer/speeddial/SpeedDialFragment.java +++ b/java/com/android/dialer/speeddial/SpeedDialFragment.java @@ -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> 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); } } diff --git a/java/com/android/dialer/speeddial/loader/SpeedDialUiItemLoader.java b/java/com/android/dialer/speeddial/loader/SpeedDialUiItemLoader.java index 9a027de71..9ea84eca2 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 @@ public final class SpeedDialUiItemLoader { * from {@link Contacts#STREQUENT_PHONE_ONLY}. */ public ListenableFuture> 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> starContact(Uri contactUri) { + return dialerFutureSerializer.submit( + () -> insertNewContactEntry(contactUri), backgroundExecutor); + } + + @WorkerThread + private ImmutableList insertNewContactEntry(Uri contactUri) { + Assert.isWorkerThread(); + 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 doInBackground() { + private ImmutableList loadSpeedDialUiItemsInternal() { Assert.isWorkerThread(); - SpeedDialEntryDao db = new SpeedDialEntryDatabaseHelper(appContext); + SpeedDialEntryDao db = getSpeedDialEntryDao(); // This is the list of contacts that we will display to the user List 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); + } } -- cgit v1.2.3