summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--java/com/android/dialer/binary/basecomponent/BaseDialerRootComponent.java2
-rw-r--r--java/com/android/dialer/calllog/database/AnnotatedCallLogDatabaseHelper.java3
-rw-r--r--java/com/android/dialer/calllog/database/contract/AnnotatedCallLogContract.java10
-rw-r--r--java/com/android/dialer/calllog/datasources/phonelookup/PhoneLookupDataSource.java3
-rw-r--r--java/com/android/dialer/calllog/model/CoalescedRow.java7
-rw-r--r--java/com/android/dialer/calllog/ui/CallLogUiComponent.java37
-rw-r--r--java/com/android/dialer/calllog/ui/CoalescedAnnotatedCallLogCursorLoader.java4
-rw-r--r--java/com/android/dialer/calllog/ui/NewCallLogAdapter.java8
-rw-r--r--java/com/android/dialer/calllog/ui/NewCallLogFragment.java3
-rw-r--r--java/com/android/dialer/calllog/ui/NewCallLogViewHolder.java55
-rw-r--r--java/com/android/dialer/calllog/ui/RealtimeRowProcessor.java107
-rw-r--r--java/com/android/dialer/phonelookup/cp2/Cp2PhoneLookup.java30
-rw-r--r--java/com/android/newbubble/BottomActionViewController.java44
-rw-r--r--java/com/android/newbubble/NewBubble.java169
-rw-r--r--java/com/android/newbubble/res/drawable/bottom_action_scrim.xml2
-rw-r--r--java/com/android/newbubble/res/layout/bottom_action_base.xml37
-rw-r--r--java/com/android/newbubble/res/layout/new_bubble_base.xml10
-rw-r--r--java/com/android/newbubble/res/values/strings.xml5
-rw-r--r--java/com/android/newbubble/res/values/values.xml7
19 files changed, 355 insertions, 188 deletions
diff --git a/java/com/android/dialer/binary/basecomponent/BaseDialerRootComponent.java b/java/com/android/dialer/binary/basecomponent/BaseDialerRootComponent.java
index 8973a329f..20152af03 100644
--- a/java/com/android/dialer/binary/basecomponent/BaseDialerRootComponent.java
+++ b/java/com/android/dialer/binary/basecomponent/BaseDialerRootComponent.java
@@ -18,6 +18,7 @@ package com.android.dialer.binary.basecomponent;
import com.android.dialer.calllog.CallLogComponent;
import com.android.dialer.calllog.database.CallLogDatabaseComponent;
+import com.android.dialer.calllog.ui.CallLogUiComponent;
import com.android.dialer.common.concurrent.DialerExecutorComponent;
import com.android.dialer.configprovider.ConfigProviderComponent;
import com.android.dialer.duo.DuoComponent;
@@ -44,6 +45,7 @@ public interface BaseDialerRootComponent
extends CallLocationComponent.HasComponent,
CallLogComponent.HasComponent,
CallLogDatabaseComponent.HasComponent,
+ CallLogUiComponent.HasComponent,
ConfigProviderComponent.HasComponent,
DialerExecutorComponent.HasComponent,
DuoComponent.HasComponent,
diff --git a/java/com/android/dialer/calllog/database/AnnotatedCallLogDatabaseHelper.java b/java/com/android/dialer/calllog/database/AnnotatedCallLogDatabaseHelper.java
index 68d4b95df..f90d657b8 100644
--- a/java/com/android/dialer/calllog/database/AnnotatedCallLogDatabaseHelper.java
+++ b/java/com/android/dialer/calllog/database/AnnotatedCallLogDatabaseHelper.java
@@ -60,7 +60,8 @@ class AnnotatedCallLogDatabaseHelper extends SQLiteOpenHelper {
+ (AnnotatedCallLog.TRANSCRIPTION + " integer, ")
+ (AnnotatedCallLog.VOICEMAIL_URI + " text, ")
+ (AnnotatedCallLog.CALL_TYPE + " integer, ")
- + (AnnotatedCallLog.CAN_REPORT_AS_INVALID_NUMBER + " integer")
+ + (AnnotatedCallLog.CAN_REPORT_AS_INVALID_NUMBER + " integer, ")
+ + (AnnotatedCallLog.CP2_INFO_INCOMPLETE + " integer")
+ ");";
/** Deletes all but the first maxRows rows (by timestamp) to keep the table a manageable size. */
diff --git a/java/com/android/dialer/calllog/database/contract/AnnotatedCallLogContract.java b/java/com/android/dialer/calllog/database/contract/AnnotatedCallLogContract.java
index 1b3e09095..9161d6087 100644
--- a/java/com/android/dialer/calllog/database/contract/AnnotatedCallLogContract.java
+++ b/java/com/android/dialer/calllog/database/contract/AnnotatedCallLogContract.java
@@ -185,6 +185,13 @@ public class AnnotatedCallLogContract {
*/
String CAN_REPORT_AS_INVALID_NUMBER = "can_report_as_invalid_number";
+ /**
+ * True if the CP2 information is incomplete and needs to be queried at display time.
+ *
+ * <p>TYPE: INTEGER (boolean)
+ */
+ String CP2_INFO_INCOMPLETE = "cp2_info_incomplete";
+
String[] ALL_COMMON_COLUMNS =
new String[] {
_ID,
@@ -207,7 +214,8 @@ public class AnnotatedCallLogContract {
IS_BUSINESS,
IS_VOICEMAIL,
CALL_TYPE,
- CAN_REPORT_AS_INVALID_NUMBER
+ CAN_REPORT_AS_INVALID_NUMBER,
+ CP2_INFO_INCOMPLETE
};
}
diff --git a/java/com/android/dialer/calllog/datasources/phonelookup/PhoneLookupDataSource.java b/java/com/android/dialer/calllog/datasources/phonelookup/PhoneLookupDataSource.java
index 6ec11ad13..935ea7406 100644
--- a/java/com/android/dialer/calllog/datasources/phonelookup/PhoneLookupDataSource.java
+++ b/java/com/android/dialer/calllog/datasources/phonelookup/PhoneLookupDataSource.java
@@ -276,6 +276,7 @@ public final class PhoneLookupDataSource
.useMostRecentLong(AnnotatedCallLog.PHOTO_ID)
.useMostRecentString(AnnotatedCallLog.LOOKUP_URI)
.useMostRecentInt(AnnotatedCallLog.CAN_REPORT_AS_INVALID_NUMBER)
+ .useMostRecentInt(AnnotatedCallLog.CP2_INFO_INCOMPLETE)
.combine();
}
@@ -582,6 +583,8 @@ public final class PhoneLookupDataSource
contentValues.put(
AnnotatedCallLog.CAN_REPORT_AS_INVALID_NUMBER,
PhoneLookupSelector.canReportAsInvalidNumber(phoneLookupInfo));
+ contentValues.put(
+ AnnotatedCallLog.CP2_INFO_INCOMPLETE, phoneLookupInfo.getCp2Info().getIsIncomplete());
}
private static Uri numberUri(String number) {
diff --git a/java/com/android/dialer/calllog/model/CoalescedRow.java b/java/com/android/dialer/calllog/model/CoalescedRow.java
index 1824ba146..2520d996a 100644
--- a/java/com/android/dialer/calllog/model/CoalescedRow.java
+++ b/java/com/android/dialer/calllog/model/CoalescedRow.java
@@ -40,9 +40,12 @@ public abstract class CoalescedRow {
.setIsVoicemail(false)
.setCallType(0)
.setCanReportAsInvalidNumber(false)
+ .setCp2InfoIncomplete(false)
.setCoalescedIds(CoalescedIds.getDefaultInstance());
}
+ public abstract Builder toBuilder();
+
public abstract int id();
public abstract long timestamp();
@@ -95,6 +98,8 @@ public abstract class CoalescedRow {
public abstract boolean canReportAsInvalidNumber();
+ public abstract boolean cp2InfoIncomplete();
+
public abstract CoalescedIds coalescedIds();
/** Builder for {@link CoalescedRow}. */
@@ -144,6 +149,8 @@ public abstract class CoalescedRow {
public abstract Builder setCanReportAsInvalidNumber(boolean canReportAsInvalidNumber);
+ public abstract Builder setCp2InfoIncomplete(boolean cp2InfoIncomplete);
+
public abstract Builder setCoalescedIds(CoalescedIds coalescedIds);
public abstract CoalescedRow build();
diff --git a/java/com/android/dialer/calllog/ui/CallLogUiComponent.java b/java/com/android/dialer/calllog/ui/CallLogUiComponent.java
new file mode 100644
index 000000000..a8e3b225b
--- /dev/null
+++ b/java/com/android/dialer/calllog/ui/CallLogUiComponent.java
@@ -0,0 +1,37 @@
+/*
+ * 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
+ */
+package com.android.dialer.calllog.ui;
+
+import android.content.Context;
+import com.android.dialer.inject.HasRootComponent;
+import dagger.Subcomponent;
+
+/** Dagger component for the call log UI package. */
+@Subcomponent
+public abstract class CallLogUiComponent {
+
+ public abstract RealtimeRowProcessor realtimeRowProcessor();
+
+ public static CallLogUiComponent get(Context context) {
+ return ((HasComponent) ((HasRootComponent) context.getApplicationContext()).component())
+ .callLogUiComponent();
+ }
+
+ /** Used to refer to the root application component. */
+ public interface HasComponent {
+ CallLogUiComponent callLogUiComponent();
+ }
+}
diff --git a/java/com/android/dialer/calllog/ui/CoalescedAnnotatedCallLogCursorLoader.java b/java/com/android/dialer/calllog/ui/CoalescedAnnotatedCallLogCursorLoader.java
index 6d60bdda4..5c0ce2816 100644
--- a/java/com/android/dialer/calllog/ui/CoalescedAnnotatedCallLogCursorLoader.java
+++ b/java/com/android/dialer/calllog/ui/CoalescedAnnotatedCallLogCursorLoader.java
@@ -50,7 +50,8 @@ final class CoalescedAnnotatedCallLogCursorLoader extends CursorLoader {
private static final int IS_VOICEMAIL = 18;
private static final int CALL_TYPE = 19;
private static final int CAN_REPORT_AS_INVALID_NUMBER = 20;
- private static final int COALESCED_IDS = 21;
+ private static final int CP2_INFO_INCOMPLETE = 21;
+ private static final int COALESCED_IDS = 22;
CoalescedAnnotatedCallLogCursorLoader(Context context) {
// CoalescedAnnotatedCallLog requires that PROJECTION be ALL_COLUMNS and the following params be
@@ -102,6 +103,7 @@ final class CoalescedAnnotatedCallLogCursorLoader extends CursorLoader {
.setIsVoicemail(cursor.getInt(IS_VOICEMAIL) == 1)
.setCallType(cursor.getInt(CALL_TYPE))
.setCanReportAsInvalidNumber(cursor.getInt(CAN_REPORT_AS_INVALID_NUMBER) == 1)
+ .setCp2InfoIncomplete(cursor.getInt(CP2_INFO_INCOMPLETE) == 1)
.setCoalescedIds(coalescedIds)
.build();
}
diff --git a/java/com/android/dialer/calllog/ui/NewCallLogAdapter.java b/java/com/android/dialer/calllog/ui/NewCallLogAdapter.java
index d5cfb7e24..6dd742be5 100644
--- a/java/com/android/dialer/calllog/ui/NewCallLogAdapter.java
+++ b/java/com/android/dialer/calllog/ui/NewCallLogAdapter.java
@@ -15,6 +15,7 @@
*/
package com.android.dialer.calllog.ui;
+import android.content.Context;
import android.database.Cursor;
import android.support.annotation.IntDef;
import android.support.annotation.Nullable;
@@ -45,15 +46,17 @@ final class NewCallLogAdapter extends RecyclerView.Adapter<ViewHolder> {
private final Cursor cursor;
private final Clock clock;
+ private final RealtimeRowProcessor realtimeRowProcessor;
/** Null when the "Today" header should not be displayed. */
@Nullable private final Integer todayHeaderPosition;
/** Null when the "Older" header should not be displayed. */
@Nullable private final Integer olderHeaderPosition;
- NewCallLogAdapter(Cursor cursor, Clock clock) {
+ NewCallLogAdapter(Context context, Cursor cursor, Clock clock) {
this.cursor = cursor;
this.clock = clock;
+ this.realtimeRowProcessor = CallLogUiComponent.get(context).realtimeRowProcessor();
// Calculate header adapter positions by reading cursor.
long currentTimeMillis = clock.currentTimeMillis();
@@ -95,7 +98,8 @@ final class NewCallLogAdapter extends RecyclerView.Adapter<ViewHolder> {
return new NewCallLogViewHolder(
LayoutInflater.from(viewGroup.getContext())
.inflate(R.layout.new_call_log_entry, viewGroup, false),
- clock);
+ clock,
+ realtimeRowProcessor);
default:
throw Assert.createUnsupportedOperationFailException("Unsupported view type: " + viewType);
}
diff --git a/java/com/android/dialer/calllog/ui/NewCallLogFragment.java b/java/com/android/dialer/calllog/ui/NewCallLogFragment.java
index 719878cec..e422b5f83 100644
--- a/java/com/android/dialer/calllog/ui/NewCallLogFragment.java
+++ b/java/com/android/dialer/calllog/ui/NewCallLogFragment.java
@@ -171,7 +171,8 @@ public final class NewCallLogFragment extends Fragment
}
// TODO(zachh): Handle empty cursor by showing empty view.
recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
- recyclerView.setAdapter(new NewCallLogAdapter(newCursor, System::currentTimeMillis));
+ recyclerView.setAdapter(
+ new NewCallLogAdapter(getContext(), newCursor, System::currentTimeMillis));
}
@Override
diff --git a/java/com/android/dialer/calllog/ui/NewCallLogViewHolder.java b/java/com/android/dialer/calllog/ui/NewCallLogViewHolder.java
index e45257f7b..5cceac989 100644
--- a/java/com/android/dialer/calllog/ui/NewCallLogViewHolder.java
+++ b/java/com/android/dialer/calllog/ui/NewCallLogViewHolder.java
@@ -30,12 +30,18 @@ import com.android.dialer.calllog.ui.menu.NewCallLogMenu;
import com.android.dialer.calllogutils.CallLogEntryText;
import com.android.dialer.calllogutils.CallLogIntents;
import com.android.dialer.calllogutils.CallTypeIconsView;
+import com.android.dialer.common.LogUtil;
+import com.android.dialer.common.concurrent.DialerExecutorComponent;
import com.android.dialer.compat.telephony.TelephonyManagerCompat;
import com.android.dialer.contactphoto.ContactPhotoManager;
import com.android.dialer.lettertile.LetterTileDrawable;
import com.android.dialer.oem.MotorolaUtils;
import com.android.dialer.time.Clock;
+import com.google.common.base.Optional;
+import com.google.common.util.concurrent.FutureCallback;
+import com.google.common.util.concurrent.Futures;
import java.util.Locale;
+import java.util.concurrent.ExecutorService;
/** {@link RecyclerView.ViewHolder} for the new call log. */
final class NewCallLogViewHolder extends RecyclerView.ViewHolder {
@@ -50,8 +56,12 @@ final class NewCallLogViewHolder extends RecyclerView.ViewHolder {
private final ImageView menuButton;
private final Clock clock;
+ private final RealtimeRowProcessor realtimeRowProcessor;
+ private final ExecutorService uiExecutorService;
- NewCallLogViewHolder(View view, Clock clock) {
+ private int currentRowId;
+
+ NewCallLogViewHolder(View view, Clock clock, RealtimeRowProcessor realtimeRowProcessor) {
super(view);
this.context = view.getContext();
primaryTextView = view.findViewById(R.id.primary_text);
@@ -63,12 +73,29 @@ final class NewCallLogViewHolder extends RecyclerView.ViewHolder {
menuButton = view.findViewById(R.id.menu_button);
this.clock = clock;
+ this.realtimeRowProcessor = realtimeRowProcessor;
+ uiExecutorService = DialerExecutorComponent.get(context).uiExecutorService();
}
/** @param cursor a cursor from {@link CoalescedAnnotatedCallLogCursorLoader}. */
void bind(Cursor cursor) {
CoalescedRow row = CoalescedAnnotatedCallLogCursorLoader.toRow(cursor);
+ currentRowId = row.id(); // Used to make sure async updates are applied to the correct views
+
+ // Even if there is additional real time processing necessary, we still want to immediately show
+ // what information we have, rather than an empty card. For example, if CP2 information needs to
+ // be queried on the fly, we can still show the phone number until the contact name loads.
+ handleRow(row);
+
+ // Note: This leaks the view holder via the callback (which is an inner class), but this is OK
+ // because we only create ~10 of them (and they'll be collected assuming all jobs finish).
+ Futures.addCallback(
+ realtimeRowProcessor.applyRealtimeProcessing(row),
+ new RealtimeRowFutureCallback(row.id()),
+ uiExecutorService);
+ }
+ private void handleRow(CoalescedRow row) {
// TODO(zachh): Handle RTL properly.
primaryTextView.setText(CallLogEntryText.buildPrimaryText(context, row));
secondaryTextView.setText(CallLogEntryText.buildSecondaryTextForEntries(context, clock, row));
@@ -152,4 +179,30 @@ final class NewCallLogViewHolder extends RecyclerView.ViewHolder {
private void setOnClickListenerForMenuButon(CoalescedRow row) {
menuButton.setOnClickListener(NewCallLogMenu.createOnClickListener(context, row));
}
+
+ private class RealtimeRowFutureCallback implements FutureCallback<Optional<CoalescedRow>> {
+ private final int id;
+
+ RealtimeRowFutureCallback(int id) {
+ this.id = id;
+ }
+
+ /**
+ * @param updatedRow the updated row if an update is required, or absent if no updates are
+ * required
+ */
+ @Override
+ public void onSuccess(Optional<CoalescedRow> updatedRow) {
+ // If the user scrolled then this ViewHolder may not correspond to the completed task and
+ // there's nothing to do.
+ if (updatedRow.isPresent() && id == currentRowId) {
+ handleRow(updatedRow.get());
+ }
+ }
+
+ @Override
+ public void onFailure(Throwable throwable) {
+ LogUtil.e("RealtimeRowFutureCallback.onFailure", "realtime processing failed", throwable);
+ }
+ }
}
diff --git a/java/com/android/dialer/calllog/ui/RealtimeRowProcessor.java b/java/com/android/dialer/calllog/ui/RealtimeRowProcessor.java
new file mode 100644
index 000000000..814efc779
--- /dev/null
+++ b/java/com/android/dialer/calllog/ui/RealtimeRowProcessor.java
@@ -0,0 +1,107 @@
+/*
+ * 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.
+ */
+
+package com.android.dialer.calllog.ui;
+
+import android.support.annotation.MainThread;
+import android.util.ArrayMap;
+import com.android.dialer.DialerPhoneNumber;
+import com.android.dialer.calllog.model.CoalescedRow;
+import com.android.dialer.common.concurrent.Annotations.Ui;
+import com.android.dialer.phonelookup.PhoneLookupInfo;
+import com.android.dialer.phonelookup.PhoneLookupInfo.Cp2Info;
+import com.android.dialer.phonelookup.cp2.Cp2PhoneLookup;
+import com.android.dialer.phonelookup.selector.PhoneLookupSelector;
+import com.google.common.base.Optional;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.ListeningExecutorService;
+import java.util.Map;
+import javax.inject.Inject;
+
+/**
+ * Does work necessary to update a {@link CoalescedRow} when it is requested to be displayed.
+ *
+ * <p>In most cases this is a no-op as most AnnotatedCallLog rows can be displayed immediately
+ * as-is. However, there are certain times that a row from the AnnotatedCallLog cannot be displayed
+ * without further work being performed.
+ *
+ * <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.
+ */
+public final class RealtimeRowProcessor {
+
+ private final ListeningExecutorService uiExecutor;
+ private final Cp2PhoneLookup cp2PhoneLookup;
+ private final PhoneLookupSelector phoneLookupSelector;
+
+ private final Map<DialerPhoneNumber, Cp2Info> cache = new ArrayMap<>();
+
+ @Inject
+ RealtimeRowProcessor(
+ @Ui ListeningExecutorService uiExecutor,
+ Cp2PhoneLookup cp2PhoneLookup,
+ PhoneLookupSelector phoneLookupSelector) {
+ this.uiExecutor = uiExecutor;
+ this.cp2PhoneLookup = cp2PhoneLookup;
+ this.phoneLookupSelector = phoneLookupSelector;
+ }
+
+ /**
+ * Converts a {@link CoalescedRow} to a future which is the result of performing additional work
+ * on the row. Returns {@link Optional#absent()} if no modifications were necessary.
+ */
+ @MainThread
+ ListenableFuture<Optional<CoalescedRow>> applyRealtimeProcessing(final CoalescedRow row) {
+ // Cp2PhoneLookup can not always efficiently process all rows.
+ if (!row.cp2InfoIncomplete()) {
+ return Futures.immediateFuture(Optional.absent());
+ }
+
+ Cp2Info cachedCp2Info = cache.get(row.number());
+ if (cachedCp2Info != null) {
+ if (cachedCp2Info.equals(Cp2Info.getDefaultInstance())) {
+ return Futures.immediateFuture(Optional.absent());
+ }
+ return Futures.immediateFuture(Optional.of(applyCp2InfoToRow(cachedCp2Info, row)));
+ }
+
+ ListenableFuture<Cp2Info> cp2InfoFuture = cp2PhoneLookup.lookupByNumber(row.number());
+ return Futures.transform(
+ cp2InfoFuture,
+ cp2Info -> {
+ cache.put(row.number(), cp2Info);
+ if (!cp2Info.equals(Cp2Info.getDefaultInstance())) {
+ return Optional.of(applyCp2InfoToRow(cp2Info, row));
+ }
+ return Optional.absent();
+ },
+ uiExecutor /* ensures the cache is updated on a single thread */);
+ }
+
+ private CoalescedRow applyCp2InfoToRow(Cp2Info cp2Info, CoalescedRow row) {
+ PhoneLookupInfo phoneLookupInfo = PhoneLookupInfo.newBuilder().setCp2Info(cp2Info).build();
+ // It is safe to overwrite any existing data because CP2 always has highest priority.
+ return row.toBuilder()
+ .setName(phoneLookupSelector.selectName(phoneLookupInfo))
+ .setPhotoUri(phoneLookupSelector.selectPhotoUri(phoneLookupInfo))
+ .setPhotoId(phoneLookupSelector.selectPhotoId(phoneLookupInfo))
+ .setLookupUri(phoneLookupSelector.selectLookupUri(phoneLookupInfo))
+ .setNumberTypeLabel(phoneLookupSelector.selectNumberLabel(phoneLookupInfo))
+ .build();
+ }
+}
diff --git a/java/com/android/dialer/phonelookup/cp2/Cp2PhoneLookup.java b/java/com/android/dialer/phonelookup/cp2/Cp2PhoneLookup.java
index 0d312cbbe..5ae0fb68a 100644
--- a/java/com/android/dialer/phonelookup/cp2/Cp2PhoneLookup.java
+++ b/java/com/android/dialer/phonelookup/cp2/Cp2PhoneLookup.java
@@ -40,6 +40,7 @@ import com.android.dialer.phonelookup.PhoneLookupInfo;
import com.android.dialer.phonelookup.PhoneLookupInfo.Cp2Info;
import com.android.dialer.phonelookup.PhoneLookupInfo.Cp2Info.Cp2ContactInfo;
import com.android.dialer.phonelookup.database.contract.PhoneLookupHistoryContract.PhoneLookupHistory;
+import com.android.dialer.phonenumberproto.DialerPhoneNumberUtil;
import com.android.dialer.phonenumberproto.PartitionedNumbers;
import com.android.dialer.storage.Unencrypted;
import com.android.dialer.telecom.TelecomCallUtil;
@@ -51,6 +52,7 @@ import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.common.util.concurrent.MoreExecutors;
+import com.google.i18n.phonenumbers.PhoneNumberUtil;
import com.google.protobuf.InvalidProtocolBufferException;
import java.util.ArrayList;
import java.util.List;
@@ -157,6 +159,34 @@ public final class Cp2PhoneLookup implements PhoneLookup<Cp2Info> {
return Cp2Info.newBuilder().addAllCp2ContactInfo(cp2ContactInfos).build();
}
+ /**
+ * Queries ContactsContract.PhoneLookup for the {@link Cp2Info} associated with the provided
+ * {@link DialerPhoneNumber}. Returns {@link Cp2Info#getDefaultInstance()} if there is no
+ * information.
+ */
+ public ListenableFuture<Cp2Info> lookupByNumber(DialerPhoneNumber dialerPhoneNumber) {
+ return backgroundExecutorService.submit(
+ () -> {
+ DialerPhoneNumberUtil dialerPhoneNumberUtil =
+ new DialerPhoneNumberUtil(PhoneNumberUtil.getInstance());
+ String rawNumber = dialerPhoneNumberUtil.normalizeNumber(dialerPhoneNumber);
+ if (rawNumber.isEmpty()) {
+ return Cp2Info.getDefaultInstance();
+ }
+ Set<Cp2ContactInfo> cp2ContactInfos = new ArraySet<>();
+ try (Cursor cursor = queryPhoneLookup(PHONE_LOOKUP_PROJECTION, rawNumber)) {
+ if (cursor == null) {
+ LogUtil.w("Cp2PhoneLookup.lookup", "null cursor");
+ return Cp2Info.getDefaultInstance();
+ }
+ while (cursor.moveToNext()) {
+ cp2ContactInfos.add(buildCp2ContactInfoFromPhoneCursor(appContext, cursor));
+ }
+ }
+ return Cp2Info.newBuilder().addAllCp2ContactInfo(cp2ContactInfos).build();
+ });
+ }
+
@Override
public ListenableFuture<Boolean> isDirty(ImmutableSet<DialerPhoneNumber> phoneNumbers) {
PartitionedNumbers partitionedNumbers = new PartitionedNumbers(phoneNumbers);
diff --git a/java/com/android/newbubble/BottomActionViewController.java b/java/com/android/newbubble/BottomActionViewController.java
index 7c7105194..a34d3a2b1 100644
--- a/java/com/android/newbubble/BottomActionViewController.java
+++ b/java/com/android/newbubble/BottomActionViewController.java
@@ -32,14 +32,16 @@ final class BottomActionViewController {
// the bubble, to prevent the bottom action view from animating if the user just wants to fling
// the bubble.
private static final int SHOW_TARGET_DELAY = 100;
- private static final int SHOW_TARGET_DURATION = 350;
- private static final int HIDE_TARGET_DURATION = 225;
+ private static final int SHOW_HIDE_TARGET_DURATION = 175;
+ private static final int HIGHLIGHT_TARGET_DURATION = 150;
private static final float HIGHLIGHT_TARGET_SCALE = 1.5f;
+ private static final float UNHIGHLIGHT_TARGET_ALPHA = 0.38f;
private final Context context;
private final WindowManager windowManager;
private final int gradientHeight;
private final int bottomActionViewTop;
+ private final int textOffsetSize;
private View bottomActionView;
private View dismissView;
@@ -54,6 +56,8 @@ final class BottomActionViewController {
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);
}
/** Creates and show the bottom action view. */
@@ -104,7 +108,7 @@ final class BottomActionViewController {
.alpha(1f)
.setInterpolator(new LinearInterpolator())
.setStartDelay(SHOW_TARGET_DELAY)
- .setDuration(SHOW_TARGET_DURATION)
+ .setDuration(SHOW_HIDE_TARGET_DURATION)
.start();
}
@@ -117,7 +121,7 @@ final class BottomActionViewController {
.animate()
.alpha(0f)
.setInterpolator(new LinearInterpolator())
- .setDuration(HIDE_TARGET_DURATION)
+ .setDuration(SHOW_HIDE_TARGET_DURATION)
.withEndAction(
() -> {
// Use removeViewImmediate instead of removeView to avoid view flashing before removed
@@ -143,32 +147,56 @@ final class BottomActionViewController {
boolean shouldHighlightDismiss = y > bottomActionViewTop && x < middle;
boolean shouldHighlightEndCall = y > bottomActionViewTop && x >= middle;
+ // Set target alpha back to 1
+ if (!dismissHighlighted && endCallHighlighted && !shouldHighlightEndCall) {
+ dismissView.animate().alpha(1f).setDuration(HIGHLIGHT_TARGET_DURATION).start();
+ }
+ if (!endCallHighlighted && dismissHighlighted && !shouldHighlightDismiss) {
+ endCallView.animate().alpha(1f).setDuration(HIGHLIGHT_TARGET_DURATION).start();
+ }
+
+ // Scale unhighlight target back to 1x
if (!shouldHighlightDismiss && dismissHighlighted) {
// Unhighlight dismiss
- dismissView.animate().scaleX(1f).scaleY(1f).setDuration(HIDE_TARGET_DURATION).start();
+ dismissView.animate().scaleX(1f).scaleY(1f).setDuration(HIGHLIGHT_TARGET_DURATION).start();
dismissHighlighted = false;
} else if (!shouldHighlightEndCall && endCallHighlighted) {
// Unhighlight end call
- endCallView.animate().scaleX(1f).scaleY(1f).setDuration(HIDE_TARGET_DURATION).start();
+ endCallView.animate().scaleX(1f).scaleY(1f).setDuration(HIGHLIGHT_TARGET_DURATION).start();
endCallHighlighted = false;
}
+ // Scale highlight target larger
if (shouldHighlightDismiss && !dismissHighlighted) {
// Highlight dismiss
+ dismissView.setPivotY(dismissView.getHeight() / 2 + textOffsetSize);
dismissView
.animate()
.scaleX(HIGHLIGHT_TARGET_SCALE)
.scaleY(HIGHLIGHT_TARGET_SCALE)
- .setDuration(SHOW_TARGET_DURATION)
+ .setDuration(HIGHLIGHT_TARGET_DURATION)
+ .start();
+ // Fade the other target
+ endCallView
+ .animate()
+ .alpha(UNHIGHLIGHT_TARGET_ALPHA)
+ .setDuration(HIGHLIGHT_TARGET_DURATION)
.start();
dismissHighlighted = true;
} else if (shouldHighlightEndCall && !endCallHighlighted) {
// Highlight end call
+ endCallView.setPivotY(dismissView.getHeight() / 2 + textOffsetSize);
endCallView
.animate()
.scaleX(HIGHLIGHT_TARGET_SCALE)
.scaleY(HIGHLIGHT_TARGET_SCALE)
- .setDuration(SHOW_TARGET_DURATION)
+ .setDuration(HIGHLIGHT_TARGET_DURATION)
+ .start();
+ // Fade the other target
+ dismissView
+ .animate()
+ .alpha(UNHIGHLIGHT_TARGET_ALPHA)
+ .setDuration(HIGHLIGHT_TARGET_DURATION)
.start();
endCallHighlighted = true;
}
diff --git a/java/com/android/newbubble/NewBubble.java b/java/com/android/newbubble/NewBubble.java
index f5a036f93..202cfcd33 100644
--- a/java/com/android/newbubble/NewBubble.java
+++ b/java/com/android/newbubble/NewBubble.java
@@ -40,8 +40,6 @@ import android.support.v4.graphics.ColorUtils;
import android.support.v4.os.BuildCompat;
import android.support.v4.view.animation.LinearOutSlowInInterpolator;
import android.text.TextUtils;
-import android.transition.TransitionManager;
-import android.transition.TransitionValues;
import android.view.ContextThemeWrapper;
import android.view.Gravity;
import android.view.LayoutInflater;
@@ -49,7 +47,6 @@ import android.view.MotionEvent;
import android.view.View;
import android.view.View.AccessibilityDelegate;
import android.view.ViewGroup;
-import android.view.ViewPropertyAnimator;
import android.view.ViewTreeObserver.OnPreDrawListener;
import android.view.WindowManager;
import android.view.WindowManager.LayoutParams;
@@ -59,7 +56,6 @@ import android.view.animation.AccelerateDecelerateInterpolator;
import android.view.animation.AnticipateInterpolator;
import android.view.animation.OvershootInterpolator;
import android.widget.ImageView;
-import android.widget.TextView;
import android.widget.Toast;
import android.widget.ViewAnimator;
import com.android.dialer.common.LogUtil;
@@ -84,13 +80,12 @@ public class NewBubble {
// This class has some odd behavior that is not immediately obvious in order to avoid jank when
// resizing. See http://go/bubble-resize for details.
- // How long text should show after showText(CharSequence) is called
- private static final int SHOW_TEXT_DURATION_MILLIS = 3000;
// How long the new window should show before destroying the old one during resize operations.
// This ensures the new window has had time to draw first.
private static final int WINDOW_REDRAW_DELAY_MILLIS = 50;
private static final int EXPAND_AND_COLLAPSE_ANIMATION_DURATION = 200;
+ private static final int HIDE_BUBBLE_ANIMATION_DURATION = 250;
private static Boolean canShowBubblesForTesting = null;
@@ -110,13 +105,11 @@ public class NewBubble {
@Visibility private int visibility;
private boolean expanded;
- private boolean textShowing;
- private boolean hideAfterText;
private CharSequence textAfterShow;
private int collapseEndAction;
ViewHolder viewHolder;
- private ViewPropertyAnimator collapseAnimation;
+ private AnimatorSet collapseAnimatorSet;
private Integer overrideGravity;
@VisibleForTesting AnimatorSet exitAnimatorSet;
@@ -124,20 +117,6 @@ public class NewBubble {
private final int leftBoundary;
private int savedYPosition = -1;
- private final Runnable collapseRunnable =
- new Runnable() {
- @Override
- public void run() {
- textShowing = false;
- if (hideAfterText) {
- // Always reset here since text shouldn't keep showing.
- hideAndReset();
- } else {
- viewHolder.getPrimaryButton().setDisplayedChild(ViewHolder.CHILD_INDEX_AVATAR_AND_ICON);
- }
- }
- };
-
/** Type of action after bubble collapse */
@Retention(RetentionPolicy.SOURCE)
@IntDef({CollapseEnd.NOTHING, CollapseEnd.HIDE})
@@ -309,7 +288,7 @@ public class NewBubble {
@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
public void startCollapse(@CollapseEnd int endAction, boolean shouldRecoverYPosition) {
View expandedView = viewHolder.getExpandedView();
- if (expandedView.getVisibility() != View.VISIBLE || collapseAnimation != null) {
+ if (expandedView.getVisibility() != View.VISIBLE || collapseAnimatorSet != null) {
// Drawer is already collapsed or animation is running.
return;
}
@@ -353,31 +332,26 @@ public class NewBubble {
fadeOut.setInterpolator(accelerateDecelerateInterpolator);
// Play all animation together
- AnimatorSet collapseAnimatorSet = new AnimatorSet();
+ collapseAnimatorSet = new AnimatorSet();
collapseAnimatorSet.setDuration(EXPAND_AND_COLLAPSE_ANIMATION_DURATION);
collapseAnimatorSet.playTogether(revealAnim, fadeOut, xValueAnimator);
collapseAnimatorSet.addListener(
new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
- collapseAnimation = null;
+ collapseAnimatorSet = null;
expanded = false;
- if (textShowing) {
- // Will do resize once the text is done.
- return;
- }
+ // If collapse on the right side, the primary button move left a bit after drawer
+ // visibility becoming GONE. To avoid it, we create a new ViewHolder.
+ // It also set primary button clickable back to true, so no need to reset manually.
+ replaceViewHolder();
// If this collapse was to come before a hide, do it now.
if (collapseEndAction == CollapseEnd.HIDE) {
hide();
+ collapseEndAction = CollapseEnd.NOTHING;
}
- collapseEndAction = CollapseEnd.NOTHING;
-
- // If collapse on the right side, the primary button move left a bit after drawer
- // visibility becoming GONE. To avoid it, we create a new ViewHolder.
- // It also set primary button clickable back to true, so no need to reset manually.
- replaceViewHolder();
// Resume normal gravity after any resizing is done.
handler.postDelayed(
@@ -407,8 +381,6 @@ public class NewBubble {
return;
}
- hideAfterText = false;
-
boolean isRtl =
TextUtils.getLayoutDirectionFromLocale(Locale.getDefault()) == View.LAYOUT_DIRECTION_RTL;
if (windowParams == null) {
@@ -489,10 +461,6 @@ public class NewBubble {
/** Hide the bubble. */
public void hide() {
- if (hideAfterText) {
- // hideAndReset() will be called after showing text, do nothing here.
- return;
- }
hideHelper(this::defaultAfterHidingAnimation);
}
@@ -564,76 +532,16 @@ public class NewBubble {
}
/**
- * Display text in the main bubble. The bubble's drawer is not expandable while text is showing,
- * and the drawer will be closed if already open.
+ * Display text. The bubble's drawer is not expandable while text is showing, and the drawer will
+ * be closed if already open.
*
* @param text the text to display to the user
*/
public void showText(@NonNull CharSequence text) {
- textShowing = true;
if (expanded) {
startCollapse(CollapseEnd.NOTHING, false /* shouldRecoverYPosition */);
- doShowText(text);
- } else {
- // Need to transition from old bounds to new bounds manually
- NewChangeOnScreenBounds transition = new NewChangeOnScreenBounds();
- // Prepare and capture start values
- TransitionValues startValues = new TransitionValues();
- startValues.view = viewHolder.getPrimaryButton();
- transition.addTarget(startValues.view);
- transition.captureStartValues(startValues);
-
- // If our view is not laid out yet, postpone showing the text.
- if (startValues.values.isEmpty()) {
- textAfterShow = text;
- return;
- }
-
- doShowText(text);
- // Hide the text so we can animate it in
- viewHolder.getPrimaryText().setAlpha(0);
-
- ViewAnimator primaryButton = viewHolder.getPrimaryButton();
- // Cancel the automatic transition scheduled in doShowText
- TransitionManager.endTransitions((ViewGroup) primaryButton.getParent());
- primaryButton
- .getViewTreeObserver()
- .addOnPreDrawListener(
- new OnPreDrawListener() {
- @Override
- public boolean onPreDraw() {
- primaryButton.getViewTreeObserver().removeOnPreDrawListener(this);
-
- // Prepare and capture end values, always use the size of primaryText since
- // its invisibility makes primaryButton smaller than expected
- TransitionValues endValues = new TransitionValues();
- endValues.values.put(
- NewChangeOnScreenBounds.PROPNAME_WIDTH,
- viewHolder.getPrimaryText().getWidth());
- endValues.values.put(
- NewChangeOnScreenBounds.PROPNAME_HEIGHT,
- viewHolder.getPrimaryText().getHeight());
- endValues.view = primaryButton;
- transition.addTarget(endValues.view);
- transition.captureEndValues(endValues);
-
- // animate the primary button bounds change
- Animator bounds =
- transition.createAnimator(primaryButton, startValues, endValues);
-
- // Animate the text in
- Animator alpha =
- ObjectAnimator.ofFloat(viewHolder.getPrimaryText(), View.ALPHA, 1f);
-
- AnimatorSet set = new AnimatorSet();
- set.play(bounds).before(alpha);
- set.start();
- return false;
- }
- });
}
- handler.removeCallbacks(collapseRunnable);
- handler.postDelayed(collapseRunnable, SHOW_TEXT_DURATION_MILLIS);
+ Toast.makeText(context, text, Toast.LENGTH_SHORT).show();
}
@Nullable
@@ -662,9 +570,6 @@ public class NewBubble {
}
void primaryButtonClick() {
- if (textShowing || currentInfo.getActions().isEmpty()) {
- return;
- }
if (expanded) {
logBasicOrCallImpression(DialerImpression.Type.BUBBLE_V2_CLICK_TO_COLLAPSE);
startCollapse(CollapseEnd.NOTHING, true /* shouldRecoverYPosition */);
@@ -702,12 +607,7 @@ public class NewBubble {
// Make bubble non clickable to prevent further buggy actions
viewHolder.setChildClickable(false);
- if (textShowing) {
- hideAfterText = true;
- return;
- }
-
- if (collapseAnimation != null) {
+ if (collapseAnimatorSet != null) {
collapseEndAction = CollapseEnd.HIDE;
return;
}
@@ -732,6 +632,7 @@ public class NewBubble {
exitAnimatorSet.playTogether(
scaleXAnimator, scaleYAnimator, avatarAlphaAnimator, iconAlphaAnimator);
exitAnimatorSet.setInterpolator(new AnticipateInterpolator());
+ exitAnimatorSet.setDuration(HIDE_BUBBLE_ANIMATION_DURATION);
exitAnimatorSet.addListener(
new AnimatorListenerAdapter() {
@Override
@@ -795,13 +696,6 @@ public class NewBubble {
configureButton(currentInfo.getActions().get(3), viewHolder.getEndCallButton());
}
- @VisibleForTesting
- void doShowText(@NonNull CharSequence text) {
- TransitionManager.beginDelayedTransition((ViewGroup) viewHolder.getPrimaryButton().getParent());
- viewHolder.getPrimaryText().setText(text);
- viewHolder.getPrimaryButton().setDisplayedChild(ViewHolder.CHILD_INDEX_TEXT);
- }
-
private void configureButton(Action action, NewCheckableButton button) {
boolean isRtl =
TextUtils.getLayoutDirectionFromLocale(Locale.getDefault()) == View.LAYOUT_DIRECTION_RTL;
@@ -844,7 +738,6 @@ public class NewBubble {
viewHolder
.getPrimaryButton()
.setDisplayedChild(oldViewHolder.getPrimaryButton().getDisplayedChild());
- viewHolder.getPrimaryText().setText(oldViewHolder.getPrimaryText().getText());
viewHolder.getPrimaryIcon().setX(isDrawingFromRight() ? 0 : primaryIconMoveDistance);
viewHolder
.getPrimaryIcon()
@@ -889,16 +782,10 @@ public class NewBubble {
void bottomActionEndCall() {
logBasicOrCallImpression(DialerImpression.Type.BUBBLE_V2_BOTTOM_ACTION_END_CALL);
- // Hide without animation
- hideHelper(
- () -> {
- defaultAfterHidingAnimation();
- DialerCall call = getCall();
- if (call != null) {
- call.disconnect();
- Toast.makeText(context, R.string.incall_call_ended, Toast.LENGTH_SHORT).show();
- }
- });
+ DialerCall call = getCall();
+ if (call != null) {
+ call.disconnect();
+ }
}
private boolean isDrawingFromRight() {
@@ -987,15 +874,11 @@ public class NewBubble {
@VisibleForTesting
class ViewHolder {
- public static final int CHILD_INDEX_AVATAR_AND_ICON = 0;
- public static final int CHILD_INDEX_TEXT = 1;
-
private NewMoveHandler moveHandler;
private final NewWindowRoot root;
private final ViewAnimator primaryButton;
private final ImageView primaryIcon;
private final ImageView primaryAvatar;
- private final TextView primaryText;
private final View arrow;
private final NewCheckableButton fullScreenButton;
@@ -1013,7 +896,6 @@ public class NewBubble {
primaryButton = contentView.findViewById(R.id.bubble_button_primary);
primaryAvatar = contentView.findViewById(R.id.bubble_icon_avatar);
primaryIcon = contentView.findViewById(R.id.bubble_icon_primary);
- primaryText = contentView.findViewById(R.id.bubble_text);
arrow = contentView.findViewById(R.id.bubble_triangle);
fullScreenButton = contentView.findViewById(R.id.bubble_button_full_screen);
@@ -1032,6 +914,15 @@ public class NewBubble {
});
root.setOnConfigurationChangedListener(
(configuration) -> {
+ if (expanded) {
+ // Collapse immediately without animation
+ if (collapseAnimatorSet != null) {
+ collapseAnimatorSet.removeAllListeners();
+ collapseAnimatorSet.cancel();
+ }
+ setDrawerVisibility(View.GONE);
+ expanded = false;
+ }
// The values in the current MoveHandler may be stale, so replace it. Then ensure the
// Window is in bounds
moveHandler = new NewMoveHandler(primaryButton, NewBubble.this);
@@ -1087,10 +978,6 @@ public class NewBubble {
return primaryAvatar;
}
- public TextView getPrimaryText() {
- return primaryText;
- }
-
public View getExpandedView() {
return expandedView;
}
diff --git a/java/com/android/newbubble/res/drawable/bottom_action_scrim.xml b/java/com/android/newbubble/res/drawable/bottom_action_scrim.xml
index bd13382ec..1109aa6d2 100644
--- a/java/com/android/newbubble/res/drawable/bottom_action_scrim.xml
+++ b/java/com/android/newbubble/res/drawable/bottom_action_scrim.xml
@@ -20,5 +20,5 @@
<gradient
android:angle="90"
android:endColor="@android:color/transparent"
- android:startColor="#AA000000"/>
+ android:startColor="#FF000000"/>
</shape>
diff --git a/java/com/android/newbubble/res/layout/bottom_action_base.xml b/java/com/android/newbubble/res/layout/bottom_action_base.xml
index bf08e1be5..af7f7987f 100644
--- a/java/com/android/newbubble/res/layout/bottom_action_base.xml
+++ b/java/com/android/newbubble/res/layout/bottom_action_base.xml
@@ -18,6 +18,7 @@
android:layout_width="match_parent"
android:layout_height="@dimen/bubble_bottom_action_view_height"
android:orientation="horizontal"
+ android:gravity="center"
android:background="@drawable/bottom_action_scrim">
<LinearLayout
@@ -25,19 +26,19 @@
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
- android:gravity="center_horizontal|center_vertical">
- <ImageView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginEnd="@dimen/bubble_button_icon_padding"
- android:src="@drawable/quantum_ic_clear_vd_theme_24"
- android:tint="@color/bubble_button_color_white"/>
+ android:gravity="center">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:textAllCaps="true"
+ android:layout_marginTop="@dimen/bubble_bottom_action_text_offset"
android:textColor="@color/bubble_button_color_white"
- android:text="Hide"/>
+ android:textSize="16sp"
+ android:fontFamily="roboto-medium"
+ android:text="@string/bubble_bottom_action_hide"
+ android:drawableStart="@drawable/quantum_ic_clear_vd_theme_24"
+ android:drawableTint="@color/bubble_button_color_white"
+ android:drawablePadding="10dp"
+ android:elevation="2dp"/>
</LinearLayout>
<LinearLayout
@@ -45,19 +46,19 @@
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
- android:gravity="center_horizontal|center_vertical">
- <ImageView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginEnd="@dimen/bubble_button_icon_padding"
- android:src="@drawable/quantum_ic_call_end_vd_theme_24"
- android:tint="@color/bubble_button_color_white"/>
+ android:gravity="center">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:textAllCaps="true"
+ android:layout_marginTop="@dimen/bubble_bottom_action_text_offset"
android:textColor="@color/bubble_button_color_white"
- android:text="End call"/>
+ android:textSize="16sp"
+ android:fontFamily="roboto-medium"
+ android:text="@string/bubble_bottom_action_end_call"
+ android:drawableStart="@drawable/quantum_ic_call_end_vd_theme_24"
+ android:drawableTint="@color/bubble_button_color_white"
+ android:drawablePadding="10dp"
+ android:elevation="2dp"/>
</LinearLayout>
</LinearLayout> \ No newline at end of file
diff --git a/java/com/android/newbubble/res/layout/new_bubble_base.xml b/java/com/android/newbubble/res/layout/new_bubble_base.xml
index f6ce26dd1..2b53b360e 100644
--- a/java/com/android/newbubble/res/layout/new_bubble_base.xml
+++ b/java/com/android/newbubble/res/layout/new_bubble_base.xml
@@ -66,16 +66,6 @@
tools:backgroundTint="#FF0000AA"
tools:src="@android:drawable/ic_btn_speak_now"/>
</FrameLayout>
- <TextView
- android:id="@+id/bubble_text"
- android:layout_width="wrap_content"
- android:layout_height="@dimen/bubble_size"
- android:paddingStart="@dimen/bubble_icon_padding"
- android:paddingEnd="@dimen/bubble_icon_padding"
- android:gravity="center"
- android:minWidth="@dimen/bubble_size"
- android:textAppearance="@style/TextAppearance.AppCompat"
- tools:text="Call ended"/>
</ViewAnimator>
</RelativeLayout>
<!-- The RelativeLayout below serves as boundary for @id/bubble_expanded_layout during animation -->
diff --git a/java/com/android/newbubble/res/values/strings.xml b/java/com/android/newbubble/res/values/strings.xml
index 5b82b181f..ce7d45606 100644
--- a/java/com/android/newbubble/res/values/strings.xml
+++ b/java/com/android/newbubble/res/values/strings.xml
@@ -24,4 +24,9 @@
<!-- A string to describe available action for accessibility user. It will be read as "Actions:
double tap to collapse call action menu". -->
<string name="a11y_bubble_primary_button_collapse_action">Collapse call action menu</string>
+
+ <!-- The label of drag-and-drop target for dismissing bubble. [CHAR LIMIT=10]-->
+ <string name="bubble_bottom_action_hide">Hide</string>
+ <!-- The label of drag-and-drop target for ending call. [CHAR LIMIT=10]-->
+ <string name="bubble_bottom_action_end_call">End call</string>
</resources> \ No newline at end of file
diff --git a/java/com/android/newbubble/res/values/values.xml b/java/com/android/newbubble/res/values/values.xml
index 040a5be1c..540f6653e 100644
--- a/java/com/android/newbubble/res/values/values.xml
+++ b/java/com/android/newbubble/res/values/values.xml
@@ -26,8 +26,8 @@
<dimen name="bubble_button_padding_horizontal">16dp</dimen>
<dimen name="bubble_off_screen_size_horizontal">-4dp</dimen>
- <!-- 64dp - 16dp(bubble_shadow_padding_size_vertical) -->
- <dimen name="bubble_safe_margin_vertical">48dp</dimen>
+ <!-- 36dp - 16dp(bubble_shadow_padding_size_vertical) -->
+ <dimen name="bubble_safe_margin_vertical">20dp</dimen>
<dimen name="bubble_shadow_padding_size_vertical">16dp</dimen>
<dimen name="bubble_shadow_padding_size_vertical_minus">-16dp</dimen>
@@ -42,5 +42,6 @@
<dimen name="bubble_small_icon_size">24dp</dimen>
<dimen name="bubble_small_icon_padding">4dp</dimen>
- <dimen name="bubble_bottom_action_view_height">180dp</dimen>
+ <dimen name="bubble_bottom_action_view_height">124dp</dimen>
+ <dimen name="bubble_bottom_action_text_offset">28dp</dimen>
</resources>