diff options
Diffstat (limited to 'java/com')
7 files changed, 208 insertions, 14 deletions
diff --git a/java/com/android/incallui/InCallServiceImpl.java b/java/com/android/incallui/InCallServiceImpl.java index 402e0021f..539dba8dd 100644 --- a/java/com/android/incallui/InCallServiceImpl.java +++ b/java/com/android/incallui/InCallServiceImpl.java @@ -99,7 +99,8 @@ public class InCallServiceImpl extends InCallService { returnToCallController = new ReturnToCallController(this); } if (NewReturnToCallController.isEnabled(this)) { - newReturnToCallController = new NewReturnToCallController(this); + newReturnToCallController = + new NewReturnToCallController(this, ContactInfoCache.getInstance(context)); } IBinder iBinder = super.onBind(intent); diff --git a/java/com/android/incallui/NewReturnToCallController.java b/java/com/android/incallui/NewReturnToCallController.java index cd69ea1be..fa7a45de0 100644 --- a/java/com/android/incallui/NewReturnToCallController.java +++ b/java/com/android/incallui/NewReturnToCallController.java @@ -19,15 +19,21 @@ package com.android.incallui; import android.app.PendingIntent; import android.content.Context; import android.content.Intent; +import android.graphics.drawable.Drawable; import android.graphics.drawable.Icon; import android.support.annotation.NonNull; import android.support.annotation.VisibleForTesting; import android.telecom.CallAudioState; +import android.text.TextUtils; +import com.android.contacts.common.util.ContactDisplayUtils; import com.android.dialer.common.LogUtil; import com.android.dialer.configprovider.ConfigProviderBindings; +import com.android.dialer.lettertile.LetterTileDrawable; import com.android.dialer.logging.DialerImpression; import com.android.dialer.logging.Logger; import com.android.dialer.telecom.TelecomUtil; +import com.android.incallui.ContactInfoCache.ContactCacheEntry; +import com.android.incallui.ContactInfoCache.ContactInfoCacheCallback; import com.android.incallui.InCallPresenter.InCallUiListener; import com.android.incallui.audiomode.AudioModeProvider; import com.android.incallui.audiomode.AudioModeProvider.AudioModeListener; @@ -41,6 +47,7 @@ import com.android.newbubble.NewBubble.BubbleExpansionStateListener; import com.android.newbubble.NewBubble.ExpansionState; import com.android.newbubble.NewBubbleInfo; import com.android.newbubble.NewBubbleInfo.Action; +import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.List; @@ -65,12 +72,15 @@ public class NewReturnToCallController implements InCallUiListener, Listener, Au private final PendingIntent endCall; private final PendingIntent fullScreen; + private final ContactInfoCache contactInfoCache; + public static boolean isEnabled(Context context) { return ConfigProviderBindings.get(context).getBoolean("enable_return_to_call_bubble_v2", false); } - public NewReturnToCallController(Context context) { + public NewReturnToCallController(Context context, ContactInfoCache contactInfoCache) { this.context = context; + this.contactInfoCache = contactInfoCache; toggleSpeaker = createActionIntent(ReturnToCallActionReceiver.ACTION_TOGGLE_SPEAKER); showSpeakerSelect = @@ -130,6 +140,7 @@ public class NewReturnToCallController implements InCallUiListener, Listener, Au } else { bubble.show(); } + startContactInfoSearch(); } @VisibleForTesting @@ -213,6 +224,8 @@ public class NewReturnToCallController implements InCallUiListener, Listener, Au // parent call is still there. if (!CallList.getInstance().hasNonParentActiveOrBackgroundCall()) { hideAndReset(); + } else { + startContactInfoSearch(); } } @@ -233,6 +246,22 @@ public class NewReturnToCallController implements InCallUiListener, Listener, Au } } + private void startContactInfoSearch() { + DialerCall dialerCall = CallList.getInstance().getActiveOrBackgroundCall(); + if (dialerCall != null) { + contactInfoCache.findInfo( + dialerCall, false /* isIncoming */, new ReturnToCallContactInfoCacheCallback(this)); + } + } + + private void onPhotoAvatarReceived(@NonNull Drawable photo) { + bubble.updatePhotoAvatar(photo); + } + + private void onLetterTileAvatarReceived(@NonNull Drawable photo) { + bubble.updateAvatar(photo); + } + private NewBubbleInfo generateBubbleInfo() { return NewBubbleInfo.builder() .setPrimaryColor(context.getResources().getColor(R.color.dialer_theme_color, null)) @@ -280,4 +309,72 @@ public class NewReturnToCallController implements InCallUiListener, Listener, Au toggleSpeaker.setAction(action); return PendingIntent.getBroadcast(context, 0, toggleSpeaker, 0); } + + @NonNull + private LetterTileDrawable createLettleTileDrawable( + DialerCall dialerCall, ContactCacheEntry entry) { + String preferredName = + ContactDisplayUtils.getPreferredDisplayName( + entry.namePrimary, + entry.nameAlternative, + ContactsPreferencesFactory.newContactsPreferences(context)); + if (TextUtils.isEmpty(preferredName)) { + preferredName = entry.number; + } + + LetterTileDrawable letterTile = new LetterTileDrawable(context.getResources()); + letterTile.setCanonicalDialerLetterTileDetails( + dialerCall.updateNameIfRestricted(preferredName), + entry.lookupKey, + LetterTileDrawable.SHAPE_CIRCLE, + LetterTileDrawable.getContactTypeFromPrimitives( + dialerCall.isVoiceMailNumber(), + dialerCall.isSpam(), + entry.isBusiness, + dialerCall.getNumberPresentation(), + dialerCall.isConferenceCall())); + return letterTile; + } + + private static class ReturnToCallContactInfoCacheCallback implements ContactInfoCacheCallback { + + private final WeakReference<NewReturnToCallController> newReturnToCallControllerWeakReference; + + private ReturnToCallContactInfoCacheCallback( + NewReturnToCallController newReturnToCallController) { + newReturnToCallControllerWeakReference = new WeakReference<>(newReturnToCallController); + } + + @Override + public void onContactInfoComplete(String callId, ContactCacheEntry entry) { + NewReturnToCallController newReturnToCallController = + newReturnToCallControllerWeakReference.get(); + if (newReturnToCallController == null) { + return; + } + if (entry.photo != null) { + newReturnToCallController.onPhotoAvatarReceived(entry.photo); + } else { + DialerCall dialerCall = CallList.getInstance().getCallById(callId); + newReturnToCallController.onLetterTileAvatarReceived( + newReturnToCallController.createLettleTileDrawable(dialerCall, entry)); + } + } + + @Override + public void onImageLoadComplete(String callId, ContactCacheEntry entry) { + NewReturnToCallController newReturnToCallController = + newReturnToCallControllerWeakReference.get(); + if (newReturnToCallController == null) { + return; + } + if (entry.photo != null) { + newReturnToCallController.onPhotoAvatarReceived(entry.photo); + } else { + DialerCall dialerCall = CallList.getInstance().getCallById(callId); + newReturnToCallController.onLetterTileAvatarReceived( + newReturnToCallController.createLettleTileDrawable(dialerCall, entry)); + } + } + } } diff --git a/java/com/android/newbubble/NewBubble.java b/java/com/android/newbubble/NewBubble.java index d9b9ae2ad..226326f3c 100644 --- a/java/com/android/newbubble/NewBubble.java +++ b/java/com/android/newbubble/NewBubble.java @@ -57,6 +57,7 @@ import android.view.animation.OvershootInterpolator; import android.widget.ImageView; import android.widget.TextView; import android.widget.ViewAnimator; +import com.android.dialer.util.DrawableConverter; import com.android.newbubble.NewBubbleInfo.Action; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -113,7 +114,10 @@ public class NewBubble { hideAndReset(); } else { doResize( - () -> viewHolder.getPrimaryButton().setDisplayedChild(ViewHolder.CHILD_INDEX_ICON)); + () -> + viewHolder + .getPrimaryButton() + .setDisplayedChild(ViewHolder.CHILD_INDEX_AVATAR_AND_ICON)); } } }; @@ -351,6 +355,32 @@ public class NewBubble { updateButtonStates(); } + /** + * Update the avatar from photo. + * + * @param avatar the new photo avatar in the bubble's primary button + */ + public void updatePhotoAvatar(@NonNull Drawable avatar) { + // Make it round + int bubbleSize = context.getResources().getDimensionPixelSize(R.dimen.bubble_size); + Drawable roundAvatar = + DrawableConverter.getRoundedDrawable(context, avatar, bubbleSize, bubbleSize); + + updateAvatar(roundAvatar); + } + + /** + * Update the avatar. + * + * @param avatar the new avatar in the bubble's primary button + */ + public void updateAvatar(@NonNull Drawable avatar) { + if (!avatar.equals(currentInfo.getAvatar())) { + currentInfo = NewBubbleInfo.from(currentInfo).setAvatar(avatar).build(); + viewHolder.getPrimaryAvatar().setImageDrawable(currentInfo.getAvatar()); + } + } + /** Returns the currently displayed NewBubbleInfo */ public NewBubbleInfo getBubbleInfo() { return currentInfo; @@ -525,6 +555,7 @@ public class NewBubble { } private void update() { + // Whole primary button background RippleDrawable backgroundRipple = (RippleDrawable) context.getResources().getDrawable(R.drawable.bubble_ripple_circle, context.getTheme()); @@ -532,12 +563,23 @@ public class NewBubble { ColorUtils.compositeColors( context.getColor(R.color.bubble_primary_background_darken), currentInfo.getPrimaryColor()); - backgroundRipple.getDrawable(0).setTint(primaryTint); + backgroundRipple.getDrawable(0).mutate().setTint(primaryTint); viewHolder.getPrimaryButton().setBackground(backgroundRipple); + // Small icon + RippleDrawable smallIconBackgroundRipple = + (RippleDrawable) + context + .getResources() + .getDrawable(R.drawable.bubble_ripple_circle_small, context.getTheme()); + smallIconBackgroundRipple + .getDrawable(0) + .setTint(context.getColor(R.color.bubble_button_text_color_blue)); + viewHolder.getPrimaryIcon().setBackground(smallIconBackgroundRipple); viewHolder.getPrimaryIcon().setImageIcon(currentInfo.getPrimaryIcon()); - updatePrimaryIconAnimation(); + viewHolder.getPrimaryAvatar().setImageDrawable(currentInfo.getAvatar()); + updatePrimaryIconAnimation(); updateButtonStates(); } @@ -715,13 +757,14 @@ public class NewBubble { @VisibleForTesting class ViewHolder { - public static final int CHILD_INDEX_ICON = 0; + public static final int CHILD_INDEX_AVATAR_AND_ICON = 0; public static final int CHILD_INDEX_TEXT = 1; private final NewMoveHandler moveHandler; private final NewWindowRoot root; private final ViewAnimator primaryButton; private final ImageView primaryIcon; + private final ImageView primaryAvatar; private final TextView primaryText; private final NewCheckableButton fullScreenButton; @@ -737,6 +780,7 @@ public class NewBubble { View contentView = inflater.inflate(R.layout.new_bubble_base, root, true); expandedView = contentView.findViewById(R.id.bubble_expanded_layout); 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); @@ -793,6 +837,10 @@ public class NewBubble { return primaryIcon; } + public ImageView getPrimaryAvatar() { + return primaryAvatar; + } + public TextView getPrimaryText() { return primaryText; } diff --git a/java/com/android/newbubble/NewBubbleInfo.java b/java/com/android/newbubble/NewBubbleInfo.java index f615929e3..44232f39b 100644 --- a/java/com/android/newbubble/NewBubbleInfo.java +++ b/java/com/android/newbubble/NewBubbleInfo.java @@ -35,6 +35,9 @@ public abstract class NewBubbleInfo { public abstract Icon getPrimaryIcon(); + @Nullable + public abstract Drawable getAvatar(); + @Px public abstract int getStartingYPosition(); @@ -61,6 +64,8 @@ public abstract class NewBubbleInfo { public abstract Builder setPrimaryIcon(@NonNull Icon primaryIcon); + public abstract Builder setAvatar(@Nullable Drawable avatar); + public abstract Builder setStartingYPosition(@Px int startingYPosition); public abstract Builder setActions(List<Action> actions); diff --git a/java/com/android/newbubble/res/drawable/bubble_ripple_circle_small.xml b/java/com/android/newbubble/res/drawable/bubble_ripple_circle_small.xml new file mode 100644 index 000000000..109d1cec1 --- /dev/null +++ b/java/com/android/newbubble/res/drawable/bubble_ripple_circle_small.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ 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 + --> + +<ripple xmlns:android="http://schemas.android.com/apk/res/android" + android:color="?android:colorControlHighlight"> + <item> + <shape> + <corners android:radius="@dimen/bubble_small_icon_size"/> + <solid android:color="@android:color/white"/> + </shape> + </item> +</ripple> 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 ef35d7426..9174f3fdb 100644 --- a/java/com/android/newbubble/res/layout/new_bubble_base.xml +++ b/java/com/android/newbubble/res/layout/new_bubble_base.xml @@ -36,14 +36,28 @@ android:background="@drawable/bubble_ripple_circle" android:measureAllChildren="false" tools:backgroundTint="#FF0000AA"> - <ImageView - android:id="@+id/bubble_icon_primary" - android:layout_width="@dimen/bubble_size" - android:layout_height="@dimen/bubble_size" - android:padding="@dimen/bubble_icon_padding" - android:tint="@android:color/white" - android:tintMode="src_in" - tools:src="@android:drawable/ic_btn_speak_now"/> + <RelativeLayout + android:layout_width="wrap_content" + android:layout_height="wrap_content"> + <ImageView + android:id="@+id/bubble_icon_avatar" + android:layout_width="@dimen/bubble_size" + android:layout_height="@dimen/bubble_size" + tools:src="@android:drawable/ic_btn_speak_now"/> + <ImageView + android:id="@+id/bubble_icon_primary" + android:layout_width="@dimen/bubble_small_icon_size" + android:layout_height="@dimen/bubble_small_icon_size" + android:layout_alignBottom="@id/bubble_icon_avatar" + android:layout_alignEnd="@id/bubble_icon_avatar" + android:padding="@dimen/bubble_small_icon_padding" + android:tint="@android:color/white" + android:tintMode="src_in" + android:background="@drawable/bubble_ripple_circle_small" + android:measureAllChildren="false" + tools:backgroundTint="#FF0000AA" + tools:src="@android:drawable/ic_btn_speak_now"/> + </RelativeLayout> <TextView android:id="@+id/bubble_text" android:layout_width="wrap_content" diff --git a/java/com/android/newbubble/res/values/values.xml b/java/com/android/newbubble/res/values/values.xml index 4bb90aff0..381b0079e 100644 --- a/java/com/android/newbubble/res/values/values.xml +++ b/java/com/android/newbubble/res/values/values.xml @@ -20,6 +20,7 @@ <dimen name="bubble_icon_padding">16dp</dimen> <dimen name="bubble_move_elevation_change">4dp</dimen> + <dimen name="bubble_button_icon_padding">16dp</dimen> <dimen name="bubble_safe_margin_horizontal">-4dp</dimen> <dimen name="bubble_safe_margin_vertical">64dp</dimen> <dimen name="bubble_shadow_padding_size_vertical">16dp</dimen> @@ -29,4 +30,6 @@ <dimen name="bubble_expanded_width">160dp</dimen> <dimen name="bubble_radius">20dp</dimen> <dimen name="bubble_expanded_separator_height">4dp</dimen> + <dimen name="bubble_small_icon_size">24dp</dimen> + <dimen name="bubble_small_icon_padding">4dp</dimen> </resources> |