/* * Copyright (C) 2015 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.messaging.ui; import android.content.Context; import android.content.Intent; import androidx.core.text.BidiFormatter; import androidx.core.text.TextDirectionHeuristicsCompat; import android.text.TextUtils; import android.util.AttributeSet; import android.view.LayoutInflater; import android.view.View; import android.view.View.OnLayoutChangeListener; import android.widget.LinearLayout; import android.widget.TextView; import com.android.messaging.R; import com.android.messaging.datamodel.binding.BindingBase; import com.android.messaging.datamodel.binding.DetachableBinding; import com.android.messaging.datamodel.data.PersonItemData; import com.android.messaging.datamodel.data.PersonItemData.PersonItemDataListener; import com.android.messaging.util.AccessibilityUtil; import com.android.messaging.util.UiUtils; /** * Shows a view for a "person" - could be a contact or a participant. This always shows a * contact icon on the left, and the person's display name on the right. * * This view is always bound to an abstract PersonItemData class, so to use it for a specific * scenario, all you need to do is to create a concrete PersonItemData subclass that bridges * between the underlying data (e.g. ParticipantData) and what the UI wants (e.g. display name). */ public class PersonItemView extends LinearLayout implements PersonItemDataListener, OnLayoutChangeListener { public interface PersonItemViewListener { void onPersonClicked(PersonItemData data); boolean onPersonLongClicked(PersonItemData data); } protected final DetachableBinding mBinding; private TextView mNameTextView; private TextView mDetailsTextView; private ContactIconView mContactIconView; private View mDetailsContainer; private PersonItemViewListener mListener; private boolean mAvatarOnly; public PersonItemView(final Context context, final AttributeSet attrs) { super(context, attrs); mBinding = BindingBase.createDetachableBinding(this); LayoutInflater.from(getContext()).inflate(R.layout.person_item_view, this, true); } @Override protected void onFinishInflate() { mNameTextView = (TextView) findViewById(R.id.name); mDetailsTextView = (TextView) findViewById(R.id.details); mContactIconView = (ContactIconView) findViewById(R.id.contact_icon); mDetailsContainer = findViewById(R.id.details_container); mNameTextView.addOnLayoutChangeListener(this); } @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); if (mBinding.isBound()) { mBinding.detach(); } } @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); mBinding.reAttachIfPossible(); } /** * Binds to a person item data which will provide us info to be displayed. * @param personData the PersonItemData to be bound to. */ public void bind(final PersonItemData personData) { if (mBinding.isBound()) { if (mBinding.getData().equals(personData)) { // Don't rebind if we are requesting the same data. return; } mBinding.unbind(); } if (personData != null) { mBinding.bind(personData); mBinding.getData().setListener(this); // Accessibility reason : in case phone numbers are mixed in the display name, // we need to vocalize it for talkback. final String vocalizedDisplayName = AccessibilityUtil.getVocalizedPhoneNumber( getResources(), getDisplayName()); mNameTextView.setContentDescription(vocalizedDisplayName); } updateViewAppearance(); } /** * @return Display name, possibly comma-ellipsized. */ private String getDisplayName() { final int width = mNameTextView.getMeasuredWidth(); final String displayName = mBinding.getData().getDisplayName(); if (width == 0 || TextUtils.isEmpty(displayName) || !displayName.contains(",")) { return displayName; } final String plusOneString = getContext().getString(R.string.plus_one); final String plusNString = getContext().getString(R.string.plus_n); return BidiFormatter.getInstance().unicodeWrap( UiUtils.commaEllipsize( displayName, mNameTextView.getPaint(), width, plusOneString, plusNString).toString(), TextDirectionHeuristicsCompat.LTR); } @Override public void onLayoutChange(final View v, final int left, final int top, final int right, final int bottom, final int oldLeft, final int oldTop, final int oldRight, final int oldBottom) { if (mBinding.isBound() && v == mNameTextView) { setNameTextView(); } } /** * When set to true, we display only the avatar of the person and hide everything else. */ public void setAvatarOnly(final boolean avatarOnly) { mAvatarOnly = avatarOnly; mDetailsContainer.setVisibility(avatarOnly ? GONE : VISIBLE); } public boolean isAvatarOnly() { return mAvatarOnly; } public void setNameTextColor(final int color) { mNameTextView.setTextColor(color); } public void setDetailsTextColor(final int color) { mDetailsTextView.setTextColor(color); } public void setListener(final PersonItemViewListener listener) { mListener = listener; if (mListener == null) { return; } setOnClickListener(new OnClickListener() { @Override public void onClick(final View v) { if (mListener != null && mBinding.isBound()) { mListener.onPersonClicked(mBinding.getData()); } } }); final OnLongClickListener onLongClickListener = new OnLongClickListener() { @Override public boolean onLongClick(View v) { if (mListener != null && mBinding.isBound()) { return mListener.onPersonLongClicked(mBinding.getData()); } return false; } }; setOnLongClickListener(onLongClickListener); mContactIconView.setOnLongClickListener(onLongClickListener); } public void performClickOnAvatar() { mContactIconView.performClick(); } protected void updateViewAppearance() { if (mBinding.isBound()) { setNameTextView(); final String details = mBinding.getData().getDetails(); if (TextUtils.isEmpty(details)) { mDetailsTextView.setVisibility(GONE); } else { mDetailsTextView.setVisibility(VISIBLE); mDetailsTextView.setText(details); } mContactIconView.setImageResourceUri(mBinding.getData().getAvatarUri(), mBinding.getData().getContactId(), mBinding.getData().getLookupKey(), mBinding.getData().getNormalizedDestination()); } else { mNameTextView.setText(""); mContactIconView.setImageResourceUri(null); } } private void setNameTextView() { final String displayName = getDisplayName(); if (TextUtils.isEmpty(displayName)) { mNameTextView.setVisibility(GONE); } else { mNameTextView.setVisibility(VISIBLE); mNameTextView.setText(displayName); } } @Override public void onPersonDataUpdated(final PersonItemData data) { mBinding.ensureBound(data); updateViewAppearance(); } @Override public void onPersonDataFailed(final PersonItemData data, final Exception exception) { mBinding.ensureBound(data); updateViewAppearance(); } public Intent getClickIntent() { return mBinding.getData().getClickIntent(); } }