Home | History | Annotate | Download | only in ui
      1 /*
      2  * Copyright (C) 2015 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 package com.android.messaging.ui;
     17 
     18 import android.content.Context;
     19 import android.content.Intent;
     20 import android.support.v4.text.BidiFormatter;
     21 import android.support.v4.text.TextDirectionHeuristicsCompat;
     22 import android.text.TextUtils;
     23 import android.util.AttributeSet;
     24 import android.view.LayoutInflater;
     25 import android.view.View;
     26 import android.view.View.OnLayoutChangeListener;
     27 import android.widget.LinearLayout;
     28 import android.widget.TextView;
     29 
     30 import com.android.messaging.R;
     31 import com.android.messaging.datamodel.binding.BindingBase;
     32 import com.android.messaging.datamodel.binding.DetachableBinding;
     33 import com.android.messaging.datamodel.data.PersonItemData;
     34 import com.android.messaging.datamodel.data.PersonItemData.PersonItemDataListener;
     35 import com.android.messaging.util.AccessibilityUtil;
     36 import com.android.messaging.util.UiUtils;
     37 
     38 /**
     39  * Shows a view for a "person" - could be a contact or a participant. This always shows a
     40  * contact icon on the left, and the person's display name on the right.
     41  *
     42  * This view is always bound to an abstract PersonItemData class, so to use it for a specific
     43  * scenario, all you need to do is to create a concrete PersonItemData subclass that bridges
     44  * between the underlying data (e.g. ParticipantData) and what the UI wants (e.g. display name).
     45  */
     46 public class PersonItemView extends LinearLayout implements PersonItemDataListener,
     47         OnLayoutChangeListener {
     48     public interface PersonItemViewListener {
     49         void onPersonClicked(PersonItemData data);
     50         boolean onPersonLongClicked(PersonItemData data);
     51     }
     52 
     53     protected final DetachableBinding<PersonItemData> mBinding;
     54     private TextView mNameTextView;
     55     private TextView mDetailsTextView;
     56     private ContactIconView mContactIconView;
     57     private View mDetailsContainer;
     58     private PersonItemViewListener mListener;
     59     private boolean mAvatarOnly;
     60 
     61     public PersonItemView(final Context context, final AttributeSet attrs) {
     62         super(context, attrs);
     63         mBinding = BindingBase.createDetachableBinding(this);
     64         LayoutInflater.from(getContext()).inflate(R.layout.person_item_view, this, true);
     65     }
     66 
     67     @Override
     68     protected void onFinishInflate() {
     69         mNameTextView = (TextView) findViewById(R.id.name);
     70         mDetailsTextView = (TextView) findViewById(R.id.details);
     71         mContactIconView = (ContactIconView) findViewById(R.id.contact_icon);
     72         mDetailsContainer = findViewById(R.id.details_container);
     73         mNameTextView.addOnLayoutChangeListener(this);
     74     }
     75 
     76     @Override
     77     protected void onDetachedFromWindow() {
     78         super.onDetachedFromWindow();
     79         if (mBinding.isBound()) {
     80             mBinding.detach();
     81         }
     82     }
     83 
     84     @Override
     85     protected void onAttachedToWindow() {
     86         super.onAttachedToWindow();
     87         mBinding.reAttachIfPossible();
     88     }
     89 
     90     /**
     91      * Binds to a person item data which will provide us info to be displayed.
     92      * @param personData the PersonItemData to be bound to.
     93      */
     94     public void bind(final PersonItemData personData) {
     95         if (mBinding.isBound()) {
     96             if (mBinding.getData().equals(personData)) {
     97                 // Don't rebind if we are requesting the same data.
     98                 return;
     99             }
    100             mBinding.unbind();
    101         }
    102 
    103         if (personData != null) {
    104             mBinding.bind(personData);
    105             mBinding.getData().setListener(this);
    106 
    107             // Accessibility reason : in case phone numbers are mixed in the display name,
    108             // we need to vocalize it for talkback.
    109             final String vocalizedDisplayName = AccessibilityUtil.getVocalizedPhoneNumber(
    110                     getResources(), getDisplayName());
    111             mNameTextView.setContentDescription(vocalizedDisplayName);
    112         }
    113         updateViewAppearance();
    114     }
    115 
    116     /**
    117      * @return Display name, possibly comma-ellipsized.
    118      */
    119     private String getDisplayName() {
    120         final int width = mNameTextView.getMeasuredWidth();
    121         final String displayName = mBinding.getData().getDisplayName();
    122         if (width == 0 || TextUtils.isEmpty(displayName) || !displayName.contains(",")) {
    123             return displayName;
    124         }
    125         final String plusOneString = getContext().getString(R.string.plus_one);
    126         final String plusNString = getContext().getString(R.string.plus_n);
    127         return BidiFormatter.getInstance().unicodeWrap(
    128                 UiUtils.commaEllipsize(
    129                         displayName,
    130                         mNameTextView.getPaint(),
    131                         width,
    132                         plusOneString,
    133                         plusNString).toString(),
    134                 TextDirectionHeuristicsCompat.LTR);
    135     }
    136 
    137     @Override
    138     public void onLayoutChange(final View v, final int left, final int top, final int right,
    139             final int bottom, final int oldLeft, final int oldTop, final int oldRight,
    140             final int oldBottom) {
    141         if (mBinding.isBound() && v == mNameTextView) {
    142             setNameTextView();
    143         }
    144     }
    145 
    146     /**
    147      * When set to true, we display only the avatar of the person and hide everything else.
    148      */
    149     public void setAvatarOnly(final boolean avatarOnly) {
    150         mAvatarOnly = avatarOnly;
    151         mDetailsContainer.setVisibility(avatarOnly ? GONE : VISIBLE);
    152     }
    153 
    154     public boolean isAvatarOnly() {
    155         return mAvatarOnly;
    156     }
    157 
    158     public void setNameTextColor(final int color) {
    159         mNameTextView.setTextColor(color);
    160     }
    161 
    162     public void setDetailsTextColor(final int color) {
    163         mDetailsTextView.setTextColor(color);
    164     }
    165 
    166     public void setListener(final PersonItemViewListener listener) {
    167         mListener = listener;
    168         if (mListener == null) {
    169             return;
    170         }
    171         setOnClickListener(new OnClickListener() {
    172             @Override
    173             public void onClick(final View v) {
    174                 if (mListener != null && mBinding.isBound()) {
    175                     mListener.onPersonClicked(mBinding.getData());
    176                 }
    177             }
    178         });
    179         final OnLongClickListener onLongClickListener = new OnLongClickListener() {
    180             @Override
    181             public boolean onLongClick(View v) {
    182                 if (mListener != null && mBinding.isBound()) {
    183                     return mListener.onPersonLongClicked(mBinding.getData());
    184                 }
    185                 return false;
    186             }
    187         };
    188         setOnLongClickListener(onLongClickListener);
    189         mContactIconView.setOnLongClickListener(onLongClickListener);
    190     }
    191 
    192     public void performClickOnAvatar() {
    193         mContactIconView.performClick();
    194     }
    195 
    196     protected void updateViewAppearance() {
    197         if (mBinding.isBound()) {
    198             setNameTextView();
    199 
    200             final String details = mBinding.getData().getDetails();
    201             if (TextUtils.isEmpty(details)) {
    202                 mDetailsTextView.setVisibility(GONE);
    203             } else {
    204                 mDetailsTextView.setVisibility(VISIBLE);
    205                 mDetailsTextView.setText(details);
    206             }
    207 
    208             mContactIconView.setImageResourceUri(mBinding.getData().getAvatarUri(),
    209                     mBinding.getData().getContactId(), mBinding.getData().getLookupKey(),
    210                     mBinding.getData().getNormalizedDestination());
    211         } else {
    212             mNameTextView.setText("");
    213             mContactIconView.setImageResourceUri(null);
    214         }
    215     }
    216 
    217     private void setNameTextView() {
    218         final String displayName = getDisplayName();
    219         if (TextUtils.isEmpty(displayName)) {
    220             mNameTextView.setVisibility(GONE);
    221         } else {
    222             mNameTextView.setVisibility(VISIBLE);
    223             mNameTextView.setText(displayName);
    224         }
    225     }
    226 
    227     @Override
    228     public void onPersonDataUpdated(final PersonItemData data) {
    229         mBinding.ensureBound(data);
    230         updateViewAppearance();
    231     }
    232 
    233     @Override
    234     public void onPersonDataFailed(final PersonItemData data, final Exception exception) {
    235         mBinding.ensureBound(data);
    236         updateViewAppearance();
    237     }
    238 
    239     public Intent getClickIntent() {
    240         return mBinding.getData().getClickIntent();
    241     }
    242 }
    243