/* * Copyright (C) 2009 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.contacts.ui.widget; import com.android.contacts.R; import com.android.contacts.model.ContactsSource; import com.android.contacts.model.EntityDelta; import com.android.contacts.model.EntityModifier; import com.android.contacts.model.ContactsSource.DataKind; import com.android.contacts.model.ContactsSource.EditType; import com.android.contacts.model.Editor.EditorListener; import com.android.contacts.model.EntityDelta.ValuesDelta; import com.android.contacts.ui.ViewIdGenerator; import android.content.Context; import android.content.Entity; import android.content.res.Resources; import android.graphics.drawable.Drawable; import android.os.Parcel; import android.os.Parcelable; import android.provider.ContactsContract.Contacts; import android.provider.ContactsContract.Data; import android.provider.ContactsContract.RawContacts; import android.provider.ContactsContract.CommonDataKinds.Photo; import android.provider.ContactsContract.CommonDataKinds.StructuredName; import android.text.TextUtils; import android.util.AttributeSet; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.view.View.OnClickListener; import android.widget.ImageView; import android.widget.TextView; /** * Custom view that provides all the editor interaction for a specific * {@link Contacts} represented through an {@link EntityDelta}. Callers can * reuse this view and quickly rebuild its contents through * {@link #setState(EntityDelta, ContactsSource)}. * <p> * Internal updates are performed against {@link ValuesDelta} so that the * source {@link Entity} can be swapped out. Any state-based changes, such as * adding {@link Data} rows or changing {@link EditType}, are performed through * {@link EntityModifier} to ensure that {@link ContactsSource} are enforced. */ public class ContactEditorView extends BaseContactEditorView implements OnClickListener { private TextView mReadOnly; private TextView mReadOnlyName; private View mPhotoStub; private GenericEditorView mName; private boolean mIsSourceReadOnly; private ViewGroup mGeneral; private ViewGroup mSecondary; private boolean mSecondaryVisible; private TextView mSecondaryHeader; private Drawable mSecondaryOpen; private Drawable mSecondaryClosed; private View mHeaderColorBar; private View mSideBar; private ImageView mHeaderIcon; private TextView mHeaderAccountType; private TextView mHeaderAccountName; private long mRawContactId = -1; public ContactEditorView(Context context) { super(context); } public ContactEditorView(Context context, AttributeSet attrs) { super(context, attrs); } /** {@inheritDoc} */ @Override protected void onFinishInflate() { super.onFinishInflate(); mInflater = (LayoutInflater)getContext().getSystemService( Context.LAYOUT_INFLATER_SERVICE); mPhoto = (PhotoEditorView)findViewById(R.id.edit_photo); mPhotoStub = findViewById(R.id.stub_photo); final int photoSize = getResources().getDimensionPixelSize(R.dimen.edit_photo_size); mReadOnly = (TextView)findViewById(R.id.edit_read_only); mName = (GenericEditorView)findViewById(R.id.edit_name); mName.setMinimumHeight(photoSize); mName.setDeletable(false); mReadOnlyName = (TextView) findViewById(R.id.read_only_name); mGeneral = (ViewGroup)findViewById(R.id.sect_general); mSecondary = (ViewGroup)findViewById(R.id.sect_secondary); mHeaderColorBar = findViewById(R.id.header_color_bar); mSideBar = findViewById(R.id.color_bar); mHeaderIcon = (ImageView) findViewById(R.id.header_icon); mHeaderAccountType = (TextView) findViewById(R.id.header_account_type); mHeaderAccountName = (TextView) findViewById(R.id.header_account_name); mSecondaryHeader = (TextView)findViewById(R.id.head_secondary); mSecondaryHeader.setOnClickListener(this); final Resources res = getResources(); mSecondaryOpen = res.getDrawable(com.android.internal.R.drawable.expander_ic_maximized); mSecondaryClosed = res.getDrawable(com.android.internal.R.drawable.expander_ic_minimized); this.setSecondaryVisible(false); } /** {@inheritDoc} */ public void onClick(View v) { // Toggle visibility of secondary kinds final boolean makeVisible = mSecondary.getVisibility() != View.VISIBLE; this.setSecondaryVisible(makeVisible); } /** * Set the visibility of secondary sections, along with header icon. * * <p>If the source is read-only and there's no secondary fields, the entire secondary section * will be hidden. */ private void setSecondaryVisible(boolean makeVisible) { mSecondaryVisible = makeVisible; if (!mIsSourceReadOnly && mSecondary.getChildCount() > 0) { mSecondaryHeader.setVisibility(View.VISIBLE); mSecondaryHeader.setCompoundDrawablesWithIntrinsicBounds( makeVisible ? mSecondaryOpen : mSecondaryClosed, null, null, null); mSecondary.setVisibility(makeVisible ? View.VISIBLE : View.GONE); } else { mSecondaryHeader.setVisibility(View.GONE); mSecondary.setVisibility(View.GONE); } } /** * Set the internal state for this view, given a current * {@link EntityDelta} state and the {@link ContactsSource} that * apply to that state. */ @Override public void setState(EntityDelta state, ContactsSource source, ViewIdGenerator vig) { // Remove any existing sections mGeneral.removeAllViews(); mSecondary.removeAllViews(); // Bail if invalid state or source if (state == null || source == null) return; setId(vig.getId(state, null, null, ViewIdGenerator.NO_VIEW_INDEX)); mIsSourceReadOnly = source.readOnly; // Make sure we have StructuredName EntityModifier.ensureKindExists(state, source, StructuredName.CONTENT_ITEM_TYPE); // Fill in the header info ValuesDelta values = state.getValues(); String accountName = values.getAsString(RawContacts.ACCOUNT_NAME); CharSequence accountType = source.getDisplayLabel(mContext); if (TextUtils.isEmpty(accountType)) { accountType = mContext.getString(R.string.account_phone); } if (!TextUtils.isEmpty(accountName)) { mHeaderAccountName.setText( mContext.getString(R.string.from_account_format, accountName)); } mHeaderAccountType.setText(mContext.getString(R.string.account_type_format, accountType)); mHeaderIcon.setImageDrawable(source.getDisplayIcon(mContext)); mRawContactId = values.getAsLong(RawContacts._ID); // Show photo editor when supported EntityModifier.ensureKindExists(state, source, Photo.CONTENT_ITEM_TYPE); mHasPhotoEditor = (source.getKindForMimetype(Photo.CONTENT_ITEM_TYPE) != null); mPhoto.setVisibility(mHasPhotoEditor ? View.VISIBLE : View.GONE); mPhoto.setEnabled(!mIsSourceReadOnly); mName.setEnabled(!mIsSourceReadOnly); // Show and hide the appropriate views if (mIsSourceReadOnly) { mGeneral.setVisibility(View.GONE); mName.setVisibility(View.GONE); mReadOnly.setVisibility(View.VISIBLE); mReadOnly.setText(mContext.getString(R.string.contact_read_only, accountType)); mReadOnlyName.setVisibility(View.VISIBLE); } else { mGeneral.setVisibility(View.VISIBLE); mName.setVisibility(View.VISIBLE); mReadOnly.setVisibility(View.GONE); mReadOnlyName.setVisibility(View.GONE); } boolean anySecondaryFieldFilled = false; // Create editor sections for each possible data kind for (DataKind kind : source.getSortedDataKinds()) { // Skip kind of not editable if (!kind.editable) continue; final String mimeType = kind.mimeType; if (StructuredName.CONTENT_ITEM_TYPE.equals(mimeType)) { // Handle special case editor for structured name final ValuesDelta primary = state.getPrimaryEntry(mimeType); if (!mIsSourceReadOnly) { mName.setValues(kind, primary, state, mIsSourceReadOnly, vig); } else { String displayName = primary.getAsString(StructuredName.DISPLAY_NAME); mReadOnlyName.setText(displayName); } } else if (Photo.CONTENT_ITEM_TYPE.equals(mimeType)) { // Handle special case editor for photos final ValuesDelta primary = state.getPrimaryEntry(mimeType); mPhoto.setValues(kind, primary, state, mIsSourceReadOnly, vig); if (mIsSourceReadOnly && !mPhoto.hasSetPhoto()) { mPhotoStub.setVisibility(View.GONE); } else { mPhotoStub.setVisibility(View.VISIBLE); } } else if (!mIsSourceReadOnly) { // Otherwise use generic section-based editors if (kind.fieldList == null) continue; final ViewGroup parent = kind.secondary ? mSecondary : mGeneral; final KindSectionView section = (KindSectionView)mInflater.inflate( R.layout.item_kind_section, parent, false); section.setState(kind, state, mIsSourceReadOnly, vig); if (kind.secondary && section.isAnyEditorFilledOut()) { anySecondaryFieldFilled = true; } parent.addView(section); } } setSecondaryVisible(anySecondaryFieldFilled); } /** * Sets the {@link EditorListener} on the name field */ @Override public void setNameEditorListener(EditorListener listener) { mName.setEditorListener(listener); } @Override public long getRawContactId() { return mRawContactId; } private static class SavedState extends BaseSavedState { public boolean mSecondaryVisible; SavedState(Parcelable superState) { super(superState); } private SavedState(Parcel in) { super(in); mSecondaryVisible = (in.readInt() == 0 ? false : true); } @Override public void writeToParcel(Parcel out, int flags) { super.writeToParcel(out, flags); out.writeInt(mSecondaryVisible ? 1 : 0); } public static final Parcelable.Creator<SavedState> CREATOR = new Parcelable.Creator<SavedState>() { public SavedState createFromParcel(Parcel in) { return new SavedState(in); } public SavedState[] newArray(int size) { return new SavedState[size]; } }; } /** * Saves the visibility of the secondary field. */ @Override protected Parcelable onSaveInstanceState() { Parcelable superState = super.onSaveInstanceState(); SavedState ss = new SavedState(superState); ss.mSecondaryVisible = mSecondaryVisible; return ss; } /** * Restores the visibility of the secondary field. */ @Override protected void onRestoreInstanceState(Parcelable state) { SavedState ss = (SavedState) state; super.onRestoreInstanceState(ss.getSuperState()); setSecondaryVisible(ss.mSecondaryVisible); } }