blob: 7f0a6bcd5bf863a9b8cd1e8cb5c4ce8870f84ed7 [file] [log] [blame]
/*
* Copyright (C) 2011 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.app.list;
import android.content.ClipData;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Point;
import android.net.Uri;
import android.provider.ContactsContract.PinnedPositions;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.View;
import android.widget.ImageView;
import com.android.contacts.common.MoreContactUtils;
import com.android.contacts.common.list.ContactEntry;
import com.android.contacts.common.list.ContactTileView;
import com.android.contacts.common.model.ContactLoader;
import com.android.dialer.app.R;
import com.android.dialer.callintent.CallInitiationType;
import com.android.dialer.callintent.CallSpecificAppData;
import com.android.dialer.callintent.SpeedDialContactType;
import com.android.dialer.contactphoto.ContactPhotoManager.DefaultImageRequest;
import com.android.dialer.lettertile.LetterTileDrawable;
import com.android.dialer.logging.InteractionEvent;
import com.android.dialer.logging.Logger;
/**
* A light version of the {@link com.android.contacts.common.list.ContactTileView} that is used in
* Dialtacts for frequently called contacts. Slightly different behavior from superclass when you
* tap it, you want to call the frequently-called number for the contact, even if that is not the
* default number for that contact. This abstract class is the super class to both the row and tile
* view.
*/
public abstract class PhoneFavoriteTileView extends ContactTileView {
// Constant to pass to the drag event so that the drag action only happens when a phone favorite
// tile is long pressed.
static final String DRAG_PHONE_FAVORITE_TILE = "PHONE_FAVORITE_TILE";
private static final String TAG = PhoneFavoriteTileView.class.getSimpleName();
// These parameters instruct the photo manager to display the default image/letter at 70% of
// its normal size, and vertically offset upwards 12% towards the top of the letter tile, to
// make room for the contact name and number label at the bottom of the image.
private static final float DEFAULT_IMAGE_LETTER_OFFSET = -0.12f;
private static final float DEFAULT_IMAGE_LETTER_SCALE = 0.70f;
// Placeholder clip data object that is attached to drag shadows so that text views
// don't crash with an NPE if the drag shadow is released in their bounds
private static final ClipData EMPTY_CLIP_DATA = ClipData.newPlainText("", "");
/** View that contains the transparent shadow that is overlaid on top of the contact image. */
private View shadowOverlay;
/** Users' most frequent phone number. */
private String phoneNumberString;
private boolean isPinned;
private boolean isStarred;
private int position = -1;
public PhoneFavoriteTileView(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
shadowOverlay = findViewById(R.id.shadow_overlay);
setOnLongClickListener(
(v) -> {
final PhoneFavoriteTileView view = (PhoneFavoriteTileView) v;
// NOTE The drag shadow is handled in the ListView.
view.startDragAndDrop(
EMPTY_CLIP_DATA, new EmptyDragShadowBuilder(), DRAG_PHONE_FAVORITE_TILE, 0);
return true;
});
}
@Override
public void loadFromContact(ContactEntry entry) {
super.loadFromContact(entry);
// Set phone number to null in case we're reusing the view.
phoneNumberString = null;
isPinned = (entry.pinned != PinnedPositions.UNPINNED);
isStarred = entry.isFavorite;
if (entry != null) {
sendViewNotification(getContext(), entry.lookupUri);
// Grab the phone-number to call directly. See {@link onClick()}.
phoneNumberString = entry.phoneNumber;
// If this is a blank entry, don't show anything. For this to truly look like an empty row
// the entire ContactTileRow needs to be hidden.
if (entry == ContactEntry.BLANK_ENTRY) {
setVisibility(View.INVISIBLE);
} else {
final ImageView starIcon = (ImageView) findViewById(R.id.contact_star_icon);
starIcon.setVisibility(entry.isFavorite ? View.VISIBLE : View.GONE);
setVisibility(View.VISIBLE);
}
}
}
@Override
protected boolean isDarkTheme() {
return false;
}
@Override
protected OnClickListener createClickListener() {
return new OnClickListener() {
@Override
public void onClick(View v) {
if (mListener == null) {
return;
}
CallSpecificAppData.Builder callSpecificAppData =
CallSpecificAppData.newBuilder()
.setAllowAssistedDialing(true)
.setCallInitiationType(CallInitiationType.Type.SPEED_DIAL)
.setSpeedDialContactPosition(position);
if (isStarred) {
callSpecificAppData.addSpeedDialContactType(SpeedDialContactType.Type.STARRED_CONTACT);
} else {
callSpecificAppData.addSpeedDialContactType(SpeedDialContactType.Type.FREQUENT_CONTACT);
}
if (isPinned) {
callSpecificAppData.addSpeedDialContactType(SpeedDialContactType.Type.PINNED_CONTACT);
}
if (TextUtils.isEmpty(phoneNumberString)) {
// Don't set performance report now, since user may spend some time on picking a number
// Copy "superclass" implementation
Logger.get(getContext())
.logInteraction(InteractionEvent.Type.SPEED_DIAL_CLICK_CONTACT_WITH_AMBIGUOUS_NUMBER);
mListener.onContactSelected(
getLookupUri(),
MoreContactUtils.getTargetRectFromView(PhoneFavoriteTileView.this),
callSpecificAppData.build());
} else {
// When you tap a frequently-called contact, you want to
// call them at the number that you usually talk to them
// at (i.e. the one displayed in the UI), regardless of
// whether that's their default number.
mListener.onCallNumberDirectly(phoneNumberString, callSpecificAppData.build());
}
}
};
}
@Override
protected DefaultImageRequest getDefaultImageRequest(String displayName, String lookupKey) {
return new DefaultImageRequest(
displayName,
lookupKey,
LetterTileDrawable.TYPE_DEFAULT,
DEFAULT_IMAGE_LETTER_SCALE,
DEFAULT_IMAGE_LETTER_OFFSET,
false);
}
@Override
protected void configureViewForImage(boolean isDefaultImage) {
// Hide the shadow overlay if the image is a default image (i.e. colored letter tile)
if (shadowOverlay != null) {
shadowOverlay.setVisibility(isDefaultImage ? View.GONE : View.VISIBLE);
}
}
@Override
protected boolean isContactPhotoCircular() {
// Unlike Contacts' tiles, the Dialer's favorites tiles are square.
return false;
}
public void setPosition(int position) {
this.position = position;
}
private ContactLoader loader;
/**
* Send a notification using a {@link ContactLoader} to inform the sync adapter that we are
* viewing a particular contact, so that it can download the high-res photo.
*/
private void sendViewNotification(Context context, Uri contactUri) {
if (loader != null) {
// Cancels the current load if it's running and clears up any memory if it's using any.
loader.reset();
}
loader = new ContactLoader(context, contactUri, true /* postViewNotification */);
// Immediately release anything we're holding in memory
loader.registerListener(0, (loader1, contact) -> loader.reset());
loader.startLoading();
}
/**
* A {@link View.DragShadowBuilder} that doesn't draw anything. An object of this class should be
* passed to {@link View#startDragAndDrop} to prevent the framework from drawing a drag shadow.
*/
public static class EmptyDragShadowBuilder extends View.DragShadowBuilder {
@Override
public void onProvideShadowMetrics(Point size, Point touch) {
// A workaround for P+ not accepting non-positive drag shadow sizes.
size.set(1, 1);
touch.set(0, 0);
}
@Override
public void onDrawShadow(Canvas canvas) {
// Don't draw anything
}
}
}