From 3e35e4c85224bffdeb9e5649b439cf16d4c66bc2 Mon Sep 17 00:00:00 2001 From: maxwelb Date: Tue, 23 Jan 2018 11:23:01 -0800 Subject: Attempt to work around sqlite database cursor window issue Bug: 72320869 Test: DialerDatabaseHelperTest PiperOrigin-RevId: 182964251 Change-Id: Ie6539ce27bcbd97b093a47e1f5882770fc345e59 --- .../dialer/database/DialerDatabaseHelper.java | 37 ++++++++++++++++------ 1 file changed, 27 insertions(+), 10 deletions(-) (limited to 'java/com') 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) { -- cgit v1.2.3 From 8d7b8c7f6d24a90af38e01c47b9dc386b27859b4 Mon Sep 17 00:00:00 2001 From: zachh Date: Tue, 23 Jan 2018 12:27:03 -0800 Subject: Write PhoneLookup results to PhoneLookupHistory in RealtimeRowProcessor. This is an optimization to reduce "popping" in the new call log. Since we have to do the PhoneLookup anyway to update the UI, we may as well write the result to PhoneLookupHistory so that the next time the AnnotatedCallLog is refreshed, the updated results can be retrieved from PhoneLookupHistory. I also updated RealtimeRowProcessorTest to use FakePhoneLookup rather than the real Cp2PhoneLookup since RealtimeRowProcessor no longer uses Cp2PhoneLookup directly (it was updated to use the general-purpose PhoneLookup in a previous CL but I didn't update the test at that time). Test: unit PiperOrigin-RevId: 182974567 Change-Id: I813e9d69f802ca08757238290fdfcf58e78b3592 --- .../dialer/calllog/ui/RealtimeRowProcessor.java | 103 +++++++++++++++++++++ 1 file changed, 103 insertions(+) (limited to 'java/com') 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; *

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. + * + *

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 phoneLookup; private final ListeningExecutorService uiExecutor; + private final ListeningExecutorService backgroundExecutor; private final Map cache = new ArrayMap<>(); + private final Map queuedPhoneLookupHistoryWrites = + new ArrayMap<>(); + private final Runnable writePhoneLookupHistoryRunnable = this::writePhoneLookupHistory; + @Inject RealtimeRowProcessor( @ApplicationContext Context appContext, @Ui ListeningExecutorService uiExecutor, + @BackgroundExecutor ListeningExecutorService backgroundExecutor, PhoneLookup 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 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 applyBatchFuture = + backgroundExecutor.submit( + () -> { + DialerPhoneNumberUtil dialerPhoneNumberUtil = + new DialerPhoneNumberUtil(PhoneNumberUtil.getInstance()); + + ArrayList operations = new ArrayList<>(); + long currentTimestamp = System.currentTimeMillis(); + for (Entry 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() { + @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 = -- cgit v1.2.3 From 8d06ecde0ec7040d00efec6bff367bedee426fc6 Mon Sep 17 00:00:00 2001 From: zachh Date: Tue, 23 Jan 2018 12:43:06 -0800 Subject: Moved coalesced_ids.proto and number_attributes.proto to calllog/database/contract. The "model" package should be reserved for call history proper (and not voicemail) so it shouldn't contain things needed by voicemail. Test: existing PiperOrigin-RevId: 182976719 Change-Id: I463c8ed4600950a8d18db49d991609bfaa49c709 --- .../calllog/database/contract/coalesced_ids.proto | 30 +++++++++++ .../database/contract/number_attributes.proto | 61 ++++++++++++++++++++++ .../dialer/calllog/model/coalesced_ids.proto | 30 ----------- .../dialer/calllog/model/number_attributes.proto | 61 ---------------------- 4 files changed, 91 insertions(+), 91 deletions(-) create mode 100644 java/com/android/dialer/calllog/database/contract/coalesced_ids.proto create mode 100644 java/com/android/dialer/calllog/database/contract/number_attributes.proto delete mode 100644 java/com/android/dialer/calllog/model/coalesced_ids.proto delete mode 100644 java/com/android/dialer/calllog/model/number_attributes.proto (limited to 'java/com') diff --git a/java/com/android/dialer/calllog/database/contract/coalesced_ids.proto b/java/com/android/dialer/calllog/database/contract/coalesced_ids.proto new file mode 100644 index 000000000..059f95782 --- /dev/null +++ b/java/com/android/dialer/calllog/database/contract/coalesced_ids.proto @@ -0,0 +1,30 @@ +// 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 + +syntax = "proto2"; + +option java_package = "com.android.dialer"; +option java_multiple_files = true; +option optimize_for = LITE_RUNTIME; + + +package com.android.dialer; + +// A proto containing a list of IDs of the rows in AnnotatedCallLog that are +// coalesced into a row in CoalescedAnnotatedCallLog. +// For example, if rows in the AnnotatedCallLog with IDs 123, 124, 125 are +// coalesced into one row, the list in the proto will be [123, 124, 125]. +message CoalescedIds { + repeated int64 coalesced_id = 1; +} \ No newline at end of file diff --git a/java/com/android/dialer/calllog/database/contract/number_attributes.proto b/java/com/android/dialer/calllog/database/contract/number_attributes.proto new file mode 100644 index 000000000..64f8f180e --- /dev/null +++ b/java/com/android/dialer/calllog/database/contract/number_attributes.proto @@ -0,0 +1,61 @@ +// Copyright (C) 2018 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 + +syntax = "proto2"; + +option java_package = "com.android.dialer"; +option java_multiple_files = true; +option optimize_for = LITE_RUNTIME; + + +package com.android.dialer; + +// Information related to the phone number of the call. +message NumberAttributes { + // The name (which may be a person's name or business name, but not a number) + // formatted exactly as it should appear to the user. If the user's locale or + // name display preferences change, this field should be rewritten. + optional string name = 1; + + // A photo URI for the contact to display in the call log list view. + optional string photo_uri = 2; + + // A photo ID (from the contacts provider) for the contact to display in the + // call log list view. + optional int64 photo_id = 3; + + // TODO(zachh): If we need to support photos other than local contacts', add a + // (blob?) column. + + // The contacts provider lookup URI for the contact associated with the call. + optional string lookup_uri = 4; + + // The number type as a string to be displayed to the user, for example "Home" + // or "Mobile". This column should be updated for the appropriate language + // when the locale changes. + optional string number_type_label = 5; + + // The number is a call to a business from nearby places lookup. + optional bool is_business = 6; + + // The number is a call to the voicemail inbox. + optional bool is_voicemail = 7; + + // Can the number be reported as invalid through People API + optional bool can_report_as_invalid_number = 8; + + // True if the CP2 information is incomplete and needs to be queried at + // display time. + optional bool is_cp2_info_incomplete = 9; +} \ No newline at end of file diff --git a/java/com/android/dialer/calllog/model/coalesced_ids.proto b/java/com/android/dialer/calllog/model/coalesced_ids.proto deleted file mode 100644 index 059f95782..000000000 --- a/java/com/android/dialer/calllog/model/coalesced_ids.proto +++ /dev/null @@ -1,30 +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 - -syntax = "proto2"; - -option java_package = "com.android.dialer"; -option java_multiple_files = true; -option optimize_for = LITE_RUNTIME; - - -package com.android.dialer; - -// A proto containing a list of IDs of the rows in AnnotatedCallLog that are -// coalesced into a row in CoalescedAnnotatedCallLog. -// For example, if rows in the AnnotatedCallLog with IDs 123, 124, 125 are -// coalesced into one row, the list in the proto will be [123, 124, 125]. -message CoalescedIds { - repeated int64 coalesced_id = 1; -} \ No newline at end of file diff --git a/java/com/android/dialer/calllog/model/number_attributes.proto b/java/com/android/dialer/calllog/model/number_attributes.proto deleted file mode 100644 index 64f8f180e..000000000 --- a/java/com/android/dialer/calllog/model/number_attributes.proto +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright (C) 2018 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 - -syntax = "proto2"; - -option java_package = "com.android.dialer"; -option java_multiple_files = true; -option optimize_for = LITE_RUNTIME; - - -package com.android.dialer; - -// Information related to the phone number of the call. -message NumberAttributes { - // The name (which may be a person's name or business name, but not a number) - // formatted exactly as it should appear to the user. If the user's locale or - // name display preferences change, this field should be rewritten. - optional string name = 1; - - // A photo URI for the contact to display in the call log list view. - optional string photo_uri = 2; - - // A photo ID (from the contacts provider) for the contact to display in the - // call log list view. - optional int64 photo_id = 3; - - // TODO(zachh): If we need to support photos other than local contacts', add a - // (blob?) column. - - // The contacts provider lookup URI for the contact associated with the call. - optional string lookup_uri = 4; - - // The number type as a string to be displayed to the user, for example "Home" - // or "Mobile". This column should be updated for the appropriate language - // when the locale changes. - optional string number_type_label = 5; - - // The number is a call to a business from nearby places lookup. - optional bool is_business = 6; - - // The number is a call to the voicemail inbox. - optional bool is_voicemail = 7; - - // Can the number be reported as invalid through People API - optional bool can_report_as_invalid_number = 8; - - // True if the CP2 information is incomplete and needs to be queried at - // display time. - optional bool is_cp2_info_incomplete = 9; -} \ No newline at end of file -- cgit v1.2.3 From c0326e9b6b013321c44f9b1ab1a640d361379774 Mon Sep 17 00:00:00 2001 From: calderwoodra Date: Tue, 23 Jan 2018 12:50:03 -0800 Subject: Contacts recyclerview is now hidden when we don't have contacts permission. Bug: 71867982 Test: ContactsFragmentTest PiperOrigin-RevId: 182977635 Change-Id: Ibdefc49c76dd607c5f1316ae46da5e38f74c0e85 --- .../android/dialer/contactsfragment/ContactsFragment.java | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) (limited to 'java/com') 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); -- cgit v1.2.3 From b52ea741beaf9d852c3dc6797fac52a3351e0dd9 Mon Sep 17 00:00:00 2001 From: yueg Date: Tue, 23 Jan 2018 13:04:18 -0800 Subject: Bubble bottom action changes. - Hide bottom action view when bubble move canceled - Get display height every time when show bottom action view (onConfigurationChanged() won't be called when bubble is not visible) Test: manual PiperOrigin-RevId: 182979768 Change-Id: I989422c4ab5866b22c78bffbc82f229842a6dd9e --- java/com/android/newbubble/BottomActionViewController.java | 4 ++-- java/com/android/newbubble/NewMoveHandler.java | 8 ++++++++ 2 files changed, 10 insertions(+), 2 deletions(-) (limited to 'java/com') 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; -- cgit v1.2.3