diff options
Diffstat (limited to 'java/com/android')
-rw-r--r-- | java/com/android/dialer/calllog/database/contract/coalesced_ids.proto (renamed from java/com/android/dialer/calllog/model/coalesced_ids.proto) | 0 | ||||
-rw-r--r-- | java/com/android/dialer/calllog/database/contract/number_attributes.proto (renamed from java/com/android/dialer/calllog/model/number_attributes.proto) | 0 | ||||
-rw-r--r-- | java/com/android/dialer/calllog/ui/RealtimeRowProcessor.java | 103 | ||||
-rw-r--r-- | java/com/android/dialer/contactsfragment/ContactsFragment.java | 15 | ||||
-rw-r--r-- | java/com/android/dialer/database/DialerDatabaseHelper.java | 37 | ||||
-rw-r--r-- | java/com/android/newbubble/BottomActionViewController.java | 4 | ||||
-rw-r--r-- | java/com/android/newbubble/NewMoveHandler.java | 8 |
7 files changed, 150 insertions, 17 deletions
diff --git a/java/com/android/dialer/calllog/model/coalesced_ids.proto b/java/com/android/dialer/calllog/database/contract/coalesced_ids.proto index 059f95782..059f95782 100644 --- a/java/com/android/dialer/calllog/model/coalesced_ids.proto +++ b/java/com/android/dialer/calllog/database/contract/coalesced_ids.proto diff --git a/java/com/android/dialer/calllog/model/number_attributes.proto b/java/com/android/dialer/calllog/database/contract/number_attributes.proto index 64f8f180e..64f8f180e 100644 --- a/java/com/android/dialer/calllog/model/number_attributes.proto +++ b/java/com/android/dialer/calllog/database/contract/number_attributes.proto diff --git a/java/com/android/dialer/calllog/ui/RealtimeRowProcessor.java b/java/com/android/dialer/calllog/ui/RealtimeRowProcessor.java index 9e58e53ad..86cc24c04 100644 --- a/java/com/android/dialer/calllog/ui/RealtimeRowProcessor.java +++ b/java/com/android/dialer/calllog/ui/RealtimeRowProcessor.java @@ -16,22 +16,37 @@ package com.android.dialer.calllog.ui; +import android.content.ContentProviderOperation; +import android.content.ContentValues; import android.content.Context; import android.support.annotation.MainThread; +import android.support.annotation.VisibleForTesting; import android.util.ArrayMap; import com.android.dialer.DialerPhoneNumber; import com.android.dialer.NumberAttributes; import com.android.dialer.calllog.model.CoalescedRow; import com.android.dialer.common.Assert; +import com.android.dialer.common.LogUtil; +import com.android.dialer.common.concurrent.Annotations.BackgroundExecutor; import com.android.dialer.common.concurrent.Annotations.Ui; +import com.android.dialer.common.concurrent.ThreadUtil; import com.android.dialer.inject.ApplicationContext; import com.android.dialer.phonelookup.PhoneLookup; import com.android.dialer.phonelookup.PhoneLookupInfo; import com.android.dialer.phonelookup.consolidator.PhoneLookupInfoConsolidator; +import com.android.dialer.phonelookup.database.contract.PhoneLookupHistoryContract; +import com.android.dialer.phonelookup.database.contract.PhoneLookupHistoryContract.PhoneLookupHistory; +import com.android.dialer.phonenumberproto.DialerPhoneNumberUtil; +import com.google.common.collect.ImmutableMap; +import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListeningExecutorService; +import com.google.i18n.phonenumbers.PhoneNumberUtil; +import java.util.ArrayList; import java.util.Map; +import java.util.Map.Entry; +import java.util.concurrent.TimeUnit; import javax.inject.Inject; /** @@ -44,22 +59,36 @@ import javax.inject.Inject; * <p>For example, when there are many invalid numbers in the call log, we cannot efficiently update * the CP2 information for all of them at once, and so information for those rows must be retrieved * at display time. + * + * <p>This class also updates {@link PhoneLookupHistory} with the results that it fetches. */ public final class RealtimeRowProcessor { + /* + * The time to wait between writing batches of records to PhoneLookupHistory. + */ + @VisibleForTesting static final long BATCH_WAIT_MILLIS = TimeUnit.SECONDS.toMillis(3); + private final Context appContext; private final PhoneLookup<PhoneLookupInfo> phoneLookup; private final ListeningExecutorService uiExecutor; + private final ListeningExecutorService backgroundExecutor; private final Map<DialerPhoneNumber, PhoneLookupInfo> cache = new ArrayMap<>(); + private final Map<DialerPhoneNumber, PhoneLookupInfo> queuedPhoneLookupHistoryWrites = + new ArrayMap<>(); + private final Runnable writePhoneLookupHistoryRunnable = this::writePhoneLookupHistory; + @Inject RealtimeRowProcessor( @ApplicationContext Context appContext, @Ui ListeningExecutorService uiExecutor, + @BackgroundExecutor ListeningExecutorService backgroundExecutor, PhoneLookup<PhoneLookupInfo> phoneLookup) { this.appContext = appContext; this.uiExecutor = uiExecutor; + this.backgroundExecutor = backgroundExecutor; this.phoneLookup = phoneLookup; } @@ -83,6 +112,7 @@ public final class RealtimeRowProcessor { return Futures.transform( phoneLookupInfoFuture, phoneLookupInfo -> { + queuePhoneLookupHistoryWrite(row.number(), phoneLookupInfo); cache.put(row.number(), phoneLookupInfo); return applyPhoneLookupInfoToRow(phoneLookupInfo, row); }, @@ -96,6 +126,79 @@ public final class RealtimeRowProcessor { cache.clear(); } + @MainThread + private void queuePhoneLookupHistoryWrite( + DialerPhoneNumber dialerPhoneNumber, PhoneLookupInfo phoneLookupInfo) { + Assert.isMainThread(); + queuedPhoneLookupHistoryWrites.put(dialerPhoneNumber, phoneLookupInfo); + ThreadUtil.getUiThreadHandler().removeCallbacks(writePhoneLookupHistoryRunnable); + ThreadUtil.getUiThreadHandler().postDelayed(writePhoneLookupHistoryRunnable, BATCH_WAIT_MILLIS); + } + + @MainThread + private void writePhoneLookupHistory() { + Assert.isMainThread(); + + // Copy the batch to a new collection that be safely processed on a background thread. + ImmutableMap<DialerPhoneNumber, PhoneLookupInfo> currentBatch = + ImmutableMap.copyOf(queuedPhoneLookupHistoryWrites); + + // Clear the queue, handing responsibility for its items to the background task. + queuedPhoneLookupHistoryWrites.clear(); + + // Returns the number of rows updated. + ListenableFuture<Integer> applyBatchFuture = + backgroundExecutor.submit( + () -> { + DialerPhoneNumberUtil dialerPhoneNumberUtil = + new DialerPhoneNumberUtil(PhoneNumberUtil.getInstance()); + + ArrayList<ContentProviderOperation> operations = new ArrayList<>(); + long currentTimestamp = System.currentTimeMillis(); + for (Entry<DialerPhoneNumber, PhoneLookupInfo> entry : currentBatch.entrySet()) { + DialerPhoneNumber dialerPhoneNumber = entry.getKey(); + PhoneLookupInfo phoneLookupInfo = entry.getValue(); + + // Note: Multiple DialerPhoneNumbers can map to the same normalized number but we + // just write them all and the value for the last one will arbitrarily win. + String normalizedNumber = dialerPhoneNumberUtil.normalizeNumber(dialerPhoneNumber); + + ContentValues contentValues = new ContentValues(); + contentValues.put( + PhoneLookupHistory.PHONE_LOOKUP_INFO, phoneLookupInfo.toByteArray()); + contentValues.put(PhoneLookupHistory.LAST_MODIFIED, currentTimestamp); + operations.add( + ContentProviderOperation.newUpdate( + PhoneLookupHistory.contentUriForNumber(normalizedNumber)) + .withValues(contentValues) + .build()); + } + return Assert.isNotNull( + appContext + .getContentResolver() + .applyBatch(PhoneLookupHistoryContract.AUTHORITY, operations)) + .length; + }); + + Futures.addCallback( + applyBatchFuture, + new FutureCallback<Integer>() { + @Override + public void onSuccess(Integer rowsAffected) { + LogUtil.i( + "RealtimeRowProcessor.onSuccess", + "wrote %d rows to PhoneLookupHistory", + rowsAffected); + } + + @Override + public void onFailure(Throwable throwable) { + throw new RuntimeException(throwable); + } + }, + uiExecutor); + } + private CoalescedRow applyPhoneLookupInfoToRow( PhoneLookupInfo phoneLookupInfo, CoalescedRow row) { PhoneLookupInfoConsolidator phoneLookupInfoConsolidator = diff --git a/java/com/android/dialer/contactsfragment/ContactsFragment.java b/java/com/android/dialer/contactsfragment/ContactsFragment.java index 714739300..ae2bd746a 100644 --- a/java/com/android/dialer/contactsfragment/ContactsFragment.java +++ b/java/com/android/dialer/contactsfragment/ContactsFragment.java @@ -46,7 +46,6 @@ import com.android.contacts.common.preference.ContactsPreferences.ChangeListener import com.android.dialer.common.Assert; import com.android.dialer.common.FragmentUtils; import com.android.dialer.common.LogUtil; -import com.android.dialer.contactsfragment.ContactsFragment.OnContactSelectedListener; import com.android.dialer.performancereport.PerformanceReport; import com.android.dialer.util.DialerUtils; import com.android.dialer.util.IntentUtil; @@ -86,7 +85,7 @@ public class ContactsFragment extends Fragment new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { - getLoaderManager().initLoader(0, null, ContactsFragment.this); + loadContacts(); } }; @@ -208,11 +207,12 @@ public class ContactsFragment extends Fragment emptyContentView.setActionClickedListener(this); if (PermissionsUtil.hasContactsReadPermissions(getContext())) { - getLoaderManager().initLoader(0, null, this); + loadContacts(); } else { emptyContentView.setDescription(R.string.permission_no_contacts); emptyContentView.setActionLabel(R.string.permission_single_turn_on); emptyContentView.setVisibility(View.VISIBLE); + recyclerView.setVisibility(View.GONE); } return view; @@ -349,12 +349,17 @@ public class ContactsFragment extends Fragment if (requestCode == READ_CONTACTS_PERMISSION_REQUEST_CODE) { if (grantResults.length >= 1 && PackageManager.PERMISSION_GRANTED == grantResults[0]) { // Force a refresh of the data since we were missing the permission before this. - emptyContentView.setVisibility(View.GONE); - getLoaderManager().initLoader(0, null, this); + PermissionsUtil.notifyPermissionGranted(getContext(), permissions[0]); } } } + private void loadContacts() { + getLoaderManager().initLoader(0, null, this); + recyclerView.setVisibility(View.VISIBLE); + emptyContentView.setVisibility(View.GONE); + } + /** Listener for contacts list scroll state. */ public interface OnContactsListScrolledListener { void onContactsListScrolled(boolean isDragging); diff --git a/java/com/android/dialer/database/DialerDatabaseHelper.java b/java/com/android/dialer/database/DialerDatabaseHelper.java index bc70fa46f..18c61342d 100644 --- a/java/com/android/dialer/database/DialerDatabaseHelper.java +++ b/java/com/android/dialer/database/DialerDatabaseHelper.java @@ -41,6 +41,7 @@ import com.android.contacts.common.util.StopWatch; import com.android.dialer.common.LogUtil; import com.android.dialer.common.concurrent.DialerExecutor.Worker; import com.android.dialer.common.concurrent.DialerExecutorComponent; +import com.android.dialer.common.database.Selection; import com.android.dialer.database.FilteredNumberContract.FilteredNumberColumns; import com.android.dialer.smartdial.util.SmartDialNameMatcher; import com.android.dialer.smartdial.util.SmartDialPrefix; @@ -351,24 +352,40 @@ public class DialerDatabaseHelper extends SQLiteOpenHelper { * other apps since last update. * * @param db Database to operate on. - * @param deletedContactCursor Cursor containing rows of deleted contacts + * @param lastUpdatedTimeMillis the last time at which an update to the smart dial database was + * run. */ - @VisibleForTesting - void removeDeletedContacts(SQLiteDatabase db, Cursor deletedContactCursor) { + private void removeDeletedContacts(SQLiteDatabase db, String lastUpdatedTimeMillis) { + Cursor deletedContactCursor = getDeletedContactCursor(lastUpdatedTimeMillis); + if (deletedContactCursor == null) { return; } db.beginTransaction(); try { - while (deletedContactCursor.moveToNext()) { - final Long deleteContactId = - deletedContactCursor.getLong(DeleteContactQuery.DELETED_CONTACT_ID); - db.delete( - Tables.SMARTDIAL_TABLE, SmartDialDbColumns.CONTACT_ID + "=" + deleteContactId, null); - db.delete(Tables.PREFIX_TABLE, PrefixColumns.CONTACT_ID + "=" + deleteContactId, null); + if (!deletedContactCursor.moveToFirst()) { + return; } + do { + Long deleteContactId = deletedContactCursor.getLong(DeleteContactQuery.DELETED_CONTACT_ID); + + Selection smartDialSelection = + Selection.column(SmartDialDbColumns.CONTACT_ID).is("=", deleteContactId); + db.delete( + Tables.SMARTDIAL_TABLE, + smartDialSelection.getSelection(), + smartDialSelection.getSelectionArgs()); + + Selection prefixSelection = + Selection.column(PrefixColumns.CONTACT_ID).is("=", deleteContactId); + db.delete( + Tables.PREFIX_TABLE, + prefixSelection.getSelection(), + prefixSelection.getSelectionArgs()); + } while (deletedContactCursor.moveToNext()); + db.setTransactionSuccessful(); } finally { deletedContactCursor.close(); @@ -633,7 +650,7 @@ public class DialerDatabaseHelper extends SQLiteOpenHelper { } /** Removes contacts that have been deleted. */ - removeDeletedContacts(db, getDeletedContactCursor(lastUpdateMillis)); + removeDeletedContacts(db, lastUpdateMillis); removePotentiallyCorruptedContacts(db, lastUpdateMillis); if (DEBUG) { diff --git a/java/com/android/newbubble/BottomActionViewController.java b/java/com/android/newbubble/BottomActionViewController.java index 485d04169..04e0e5fe7 100644 --- a/java/com/android/newbubble/BottomActionViewController.java +++ b/java/com/android/newbubble/BottomActionViewController.java @@ -40,8 +40,8 @@ final class BottomActionViewController { private final Context context; private final WindowManager windowManager; private final int gradientHeight; - private final int bottomActionViewTop; private final int textOffsetSize; + private int bottomActionViewTop; private View bottomActionView; private View dismissView; @@ -55,7 +55,6 @@ final class BottomActionViewController { windowManager = context.getSystemService(WindowManager.class); gradientHeight = context.getResources().getDimensionPixelSize(R.dimen.bubble_bottom_action_view_height); - bottomActionViewTop = context.getResources().getDisplayMetrics().heightPixels - gradientHeight; textOffsetSize = context.getResources().getDimensionPixelSize(R.dimen.bubble_bottom_action_text_offset); } @@ -76,6 +75,7 @@ final class BottomActionViewController { // Add the target to the window // TODO(yueg): use TYPE_NAVIGATION_BAR_PANEL to draw over navigation bar + bottomActionViewTop = context.getResources().getDisplayMetrics().heightPixels - gradientHeight; LayoutParams layoutParams = new LayoutParams( LayoutParams.MATCH_PARENT, diff --git a/java/com/android/newbubble/NewMoveHandler.java b/java/com/android/newbubble/NewMoveHandler.java index c00c10729..12e099c3a 100644 --- a/java/com/android/newbubble/NewMoveHandler.java +++ b/java/com/android/newbubble/NewMoveHandler.java @@ -251,6 +251,14 @@ class NewMoveHandler implements OnTouchListener { } } break; + case MotionEvent.ACTION_CANCEL: + if (isMoving) { + snapX(); + isMoving = false; + bubble.onMoveFinish(); + bottomActionViewController.destroyBottomActionView(); + } + break; default: // fall out } return true; |