1aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey/*
2aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey * Copyright (C) 2009 The Android Open Source Project
3aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey *
4aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey * Licensed under the Apache License, Version 2.0 (the "License");
5aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey * you may not use this file except in compliance with the License.
6aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey * You may obtain a copy of the License at
7aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey *
8aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey *      http://www.apache.org/licenses/LICENSE-2.0
9aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey *
10aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey * Unless required by applicable law or agreed to in writing, software
11aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey * distributed under the License is distributed on an "AS IS" BASIS,
12aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey * See the License for the specific language governing permissions and
14aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey * limitations under the License.
15aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey */
16aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey
1718ffaa2561cc7dd2e3ef81737e6537931c0a9a11Dmitri Plotnikovpackage com.android.contacts.editor;
18aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey
19aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkeyimport android.content.Context;
20043fba637b421dcd554c634f769f0e883fb5ff8bBrian Attwellimport android.graphics.drawable.Drawable;
21043fba637b421dcd554c634f769f0e883fb5ff8bBrian Attwellimport android.provider.Contacts.GroupMembership;
22043fba637b421dcd554c634f769f0e883fb5ff8bBrian Attwellimport android.provider.ContactsContract.CommonDataKinds.Email;
23043fba637b421dcd554c634f769f0e883fb5ff8bBrian Attwellimport android.provider.ContactsContract.CommonDataKinds.Event;
24043fba637b421dcd554c634f769f0e883fb5ff8bBrian Attwellimport android.provider.ContactsContract.CommonDataKinds.Im;
25043fba637b421dcd554c634f769f0e883fb5ff8bBrian Attwellimport android.provider.ContactsContract.CommonDataKinds.Note;
26043fba637b421dcd554c634f769f0e883fb5ff8bBrian Attwellimport android.provider.ContactsContract.CommonDataKinds.Organization;
27043fba637b421dcd554c634f769f0e883fb5ff8bBrian Attwellimport android.provider.ContactsContract.CommonDataKinds.Phone;
28043fba637b421dcd554c634f769f0e883fb5ff8bBrian Attwellimport android.provider.ContactsContract.CommonDataKinds.Photo;
29043fba637b421dcd554c634f769f0e883fb5ff8bBrian Attwellimport android.provider.ContactsContract.CommonDataKinds.Relation;
30043fba637b421dcd554c634f769f0e883fb5ff8bBrian Attwellimport android.provider.ContactsContract.CommonDataKinds.SipAddress;
31043fba637b421dcd554c634f769f0e883fb5ff8bBrian Attwellimport android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
32043fba637b421dcd554c634f769f0e883fb5ff8bBrian Attwellimport android.provider.ContactsContract.CommonDataKinds.Website;
33ca87e9c598929b5b6a62da9b80d2114168e24274Daniel Lehmannimport android.provider.ContactsContract.Data;
343e7436450f0fdd5ad939c3b46e7176e082c9ee11Daniel Lehmannimport android.text.TextUtils;
35aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkeyimport android.util.AttributeSet;
3663ffb90935d8aec295e4a8f62af6c77dafe1899fKatherine Kuanimport android.view.LayoutInflater;
37aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkeyimport android.view.View;
38aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkeyimport android.view.ViewGroup;
39043fba637b421dcd554c634f769f0e883fb5ff8bBrian Attwellimport android.widget.ImageView;
40aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkeyimport android.widget.LinearLayout;
41aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey
42e0b2f1e2d01d1ac52ba207dc7ce76971d853298eChiao Chengimport com.android.contacts.R;
43cd321f65f1e50409812976380ad1f0fdb3fa35cbYorke Leeimport com.android.contacts.common.model.RawContactDelta;
444a9351db3c8385469bad037544d4b60d76504f5eWalter Jangimport com.android.contacts.common.model.RawContactModifier;
45738ff8623dc77dd91a1b9023861e924ba5e4c27eChiao Chengimport com.android.contacts.common.model.ValuesDelta;
46428f008513d1591cc08fcfe2cf0c9237fb313241Chiao Chengimport com.android.contacts.common.model.dataitem.DataKind;
474a9351db3c8385469bad037544d4b60d76504f5eWalter Jangimport com.android.contacts.editor.Editor.EditorListener;
48e0b2f1e2d01d1ac52ba207dc7ce76971d853298eChiao Cheng
4962bab31374dd66462643cbc04ebb2acece4b9b39Dmitri Plotnikovimport java.util.ArrayList;
501b22073590c1801c3e6c7bde2f4636632a049f6cKatherine Kuanimport java.util.List;
5162bab31374dd66462643cbc04ebb2acece4b9b39Dmitri Plotnikov
52aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey/**
53aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey * Custom view for an entire section of data as segmented by
54aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey * {@link DataKind} around a {@link Data#MIMETYPE}. This view shows a
55aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey * section header and a trigger for adding new {@link Data} rows.
56aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey */
57bb556954e29ae0700b729f128d8cd09ecdd91ee3Daniel Lehmannpublic class KindSectionView extends LinearLayout implements EditorListener {
58aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey
594a9351db3c8385469bad037544d4b60d76504f5eWalter Jang    public interface Listener {
604a9351db3c8385469bad037544d4b60d76504f5eWalter Jang
614a9351db3c8385469bad037544d4b60d76504f5eWalter Jang        /**
624a9351db3c8385469bad037544d4b60d76504f5eWalter Jang         * Invoked when any editor that is displayed in this section view is deleted by the user.
634a9351db3c8385469bad037544d4b60d76504f5eWalter Jang         */
64995bb4af702a619bda3819daeeaa68c55d9e96bdWalter Jang        public void onDeleteRequested(Editor editor);
654a9351db3c8385469bad037544d4b60d76504f5eWalter Jang    }
664a9351db3c8385469bad037544d4b60d76504f5eWalter Jang
67aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey    private ViewGroup mEditors;
68043fba637b421dcd554c634f769f0e883fb5ff8bBrian Attwell    private ImageView mIcon;
69aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey
70aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey    private DataKind mKind;
71851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu    private RawContactDelta mState;
7292525ff51e2cb06531447a7dcde5e22334669c8eMegha Joshi    private boolean mReadOnly;
734a9351db3c8385469bad037544d4b60d76504f5eWalter Jang    private boolean mShowOneEmptyEditor;
746f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey
75995bb4af702a619bda3819daeeaa68c55d9e96bdWalter Jang    /**
76995bb4af702a619bda3819daeeaa68c55d9e96bdWalter Jang     * Whether this KindSectionView will be removed from the layout.
77995bb4af702a619bda3819daeeaa68c55d9e96bdWalter Jang     * We need this because we want to animate KindSectionViews away (which takes time),
78995bb4af702a619bda3819daeeaa68c55d9e96bdWalter Jang     * but calculate which KindSectionViews will be visible immediately after starting removal
79995bb4af702a619bda3819daeeaa68c55d9e96bdWalter Jang     * animations.
80995bb4af702a619bda3819daeeaa68c55d9e96bdWalter Jang     */
81995bb4af702a619bda3819daeeaa68c55d9e96bdWalter Jang    private boolean mMarkedForRemoval;
82995bb4af702a619bda3819daeeaa68c55d9e96bdWalter Jang
834b722a93f1ecdeb2890fb7e5db4c22794bccfdcdMakoto Onuki    private ViewIdGenerator mViewIdGenerator;
844b722a93f1ecdeb2890fb7e5db4c22794bccfdcdMakoto Onuki
8563ffb90935d8aec295e4a8f62af6c77dafe1899fKatherine Kuan    private LayoutInflater mInflater;
8691d8e892d549bbeba721cb434163a83bc99330a9Dmitri Plotnikov
874a9351db3c8385469bad037544d4b60d76504f5eWalter Jang    private Listener mListener;
884a9351db3c8385469bad037544d4b60d76504f5eWalter Jang
89aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey    public KindSectionView(Context context) {
9091d8e892d549bbeba721cb434163a83bc99330a9Dmitri Plotnikov        this(context, null);
91aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey    }
92aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey
93aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey    public KindSectionView(Context context, AttributeSet attrs) {
94aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey        super(context, attrs);
95aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey    }
96aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey
9702bb1252aa990813fa9e86ad75c458acc15e6801Dmitri Plotnikov    @Override
9802bb1252aa990813fa9e86ad75c458acc15e6801Dmitri Plotnikov    public void setEnabled(boolean enabled) {
9902bb1252aa990813fa9e86ad75c458acc15e6801Dmitri Plotnikov        super.setEnabled(enabled);
10002bb1252aa990813fa9e86ad75c458acc15e6801Dmitri Plotnikov        if (mEditors != null) {
10102bb1252aa990813fa9e86ad75c458acc15e6801Dmitri Plotnikov            int childCount = mEditors.getChildCount();
10202bb1252aa990813fa9e86ad75c458acc15e6801Dmitri Plotnikov            for (int i = 0; i < childCount; i++) {
10302bb1252aa990813fa9e86ad75c458acc15e6801Dmitri Plotnikov                mEditors.getChildAt(i).setEnabled(enabled);
10402bb1252aa990813fa9e86ad75c458acc15e6801Dmitri Plotnikov            }
10502bb1252aa990813fa9e86ad75c458acc15e6801Dmitri Plotnikov        }
10602bb1252aa990813fa9e86ad75c458acc15e6801Dmitri Plotnikov
107043fba637b421dcd554c634f769f0e883fb5ff8bBrian Attwell        updateEmptyEditors(/* shouldAnimate = */ true);
10891d8e892d549bbeba721cb434163a83bc99330a9Dmitri Plotnikov    }
10991d8e892d549bbeba721cb434163a83bc99330a9Dmitri Plotnikov
11002bb1252aa990813fa9e86ad75c458acc15e6801Dmitri Plotnikov    public boolean isReadOnly() {
11102bb1252aa990813fa9e86ad75c458acc15e6801Dmitri Plotnikov        return mReadOnly;
11202bb1252aa990813fa9e86ad75c458acc15e6801Dmitri Plotnikov    }
11302bb1252aa990813fa9e86ad75c458acc15e6801Dmitri Plotnikov
114aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey    /** {@inheritDoc} */
115aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey    @Override
116aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey    protected void onFinishInflate() {
117aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey        setDrawingCacheEnabled(true);
118aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey        setAlwaysDrawnWithCacheEnabled(true);
119aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey
120f140227beb6d9e78955a7676dc0dd3f793f72017Brian Attwell        mInflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
12163ffb90935d8aec295e4a8f62af6c77dafe1899fKatherine Kuan
122a94859f61c216d4e137a631995026d3ddefbbf70Katherine Kuan        mEditors = (ViewGroup) findViewById(R.id.kind_editors);
123043fba637b421dcd554c634f769f0e883fb5ff8bBrian Attwell        mIcon = (ImageView) findViewById(R.id.kind_icon);
124aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey    }
125aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey
12679c9c5a8f849908d2b141c30af15923ea2b60f5bDaniel Lehmann    @Override
1272293e55d550fbc9974d1185960715e59acb14a85Katherine Kuan    public void onDeleteRequested(Editor editor) {
128995bb4af702a619bda3819daeeaa68c55d9e96bdWalter Jang        if (mShowOneEmptyEditor && getEditorCount() == 1) {
129995bb4af702a619bda3819daeeaa68c55d9e96bdWalter Jang            // If there is only 1 editor in the section, then don't allow the user to delete it.
130995bb4af702a619bda3819daeeaa68c55d9e96bdWalter Jang            // Just clear the fields in the editor.
1312293e55d550fbc9974d1185960715e59acb14a85Katherine Kuan            editor.clearAllFields();
1322293e55d550fbc9974d1185960715e59acb14a85Katherine Kuan        } else {
133995bb4af702a619bda3819daeeaa68c55d9e96bdWalter Jang            // If there is a listener, let it decide whether to delete the Editor or the entire
134995bb4af702a619bda3819daeeaa68c55d9e96bdWalter Jang            // KindSectionView so that there is no jank from both animations happening in succession.
135995bb4af702a619bda3819daeeaa68c55d9e96bdWalter Jang            if (mListener != null) {
13623a34d10b03e8a713a2b93980ff7f4c696aff93dWalter Jang                editor.markDeleted();
137995bb4af702a619bda3819daeeaa68c55d9e96bdWalter Jang                mListener.onDeleteRequested(editor);
138995bb4af702a619bda3819daeeaa68c55d9e96bdWalter Jang            } else {
139995bb4af702a619bda3819daeeaa68c55d9e96bdWalter Jang                editor.deleteEditor();
140995bb4af702a619bda3819daeeaa68c55d9e96bdWalter Jang            }
1414a9351db3c8385469bad037544d4b60d76504f5eWalter Jang        }
142aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey    }
143aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey
14423a34d10b03e8a713a2b93980ff7f4c696aff93dWalter Jang    /**
14523a34d10b03e8a713a2b93980ff7f4c696aff93dWalter Jang     * Calling this signifies that this entire section view is intended to be removed from the
14623a34d10b03e8a713a2b93980ff7f4c696aff93dWalter Jang     * layout. Note, calling this does not change the deleted state of any underlying
14723a34d10b03e8a713a2b93980ff7f4c696aff93dWalter Jang     * {@link Editor}, i.e. {@link com.android.contacts.common.model.ValuesDelta#markDeleted()}
14823a34d10b03e8a713a2b93980ff7f4c696aff93dWalter Jang     * is not invoked on any editor in this section.  It is purely marked for higher level UI
14923a34d10b03e8a713a2b93980ff7f4c696aff93dWalter Jang     * layers to manipulate the layout w/o introducing jank.
15023a34d10b03e8a713a2b93980ff7f4c696aff93dWalter Jang     * See b/22228718 for context.
15123a34d10b03e8a713a2b93980ff7f4c696aff93dWalter Jang     */
152995bb4af702a619bda3819daeeaa68c55d9e96bdWalter Jang    public void markForRemoval() {
153995bb4af702a619bda3819daeeaa68c55d9e96bdWalter Jang        mMarkedForRemoval = true;
154995bb4af702a619bda3819daeeaa68c55d9e96bdWalter Jang    }
155995bb4af702a619bda3819daeeaa68c55d9e96bdWalter Jang
15623a34d10b03e8a713a2b93980ff7f4c696aff93dWalter Jang    /**
15723a34d10b03e8a713a2b93980ff7f4c696aff93dWalter Jang     * Whether the entire section view is intended to be removed from the layout.
15823a34d10b03e8a713a2b93980ff7f4c696aff93dWalter Jang     */
159995bb4af702a619bda3819daeeaa68c55d9e96bdWalter Jang    public boolean isMarkedForRemoval() {
160995bb4af702a619bda3819daeeaa68c55d9e96bdWalter Jang        return mMarkedForRemoval;
161995bb4af702a619bda3819daeeaa68c55d9e96bdWalter Jang    }
162995bb4af702a619bda3819daeeaa68c55d9e96bdWalter Jang
16379c9c5a8f849908d2b141c30af15923ea2b60f5bDaniel Lehmann    @Override
1646f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey    public void onRequest(int request) {
1652591436971bad3050c85929c8e0c5a8d48111f88Katherine Kuan        // If a field has become empty or non-empty, then check if another row
1662591436971bad3050c85929c8e0c5a8d48111f88Katherine Kuan        // can be added dynamically.
1672591436971bad3050c85929c8e0c5a8d48111f88Katherine Kuan        if (request == FIELD_TURNED_EMPTY || request == FIELD_TURNED_NON_EMPTY) {
168043fba637b421dcd554c634f769f0e883fb5ff8bBrian Attwell            updateEmptyEditors(/* shouldAnimate = */ true);
1691b22073590c1801c3e6c7bde2f4636632a049f6cKatherine Kuan        }
1706f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey    }
1716f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey
1724a9351db3c8385469bad037544d4b60d76504f5eWalter Jang    /**
1734a9351db3c8385469bad037544d4b60d76504f5eWalter Jang     * @param showOneEmptyEditor If true, one empty input will always be displayed,
1744a9351db3c8385469bad037544d4b60d76504f5eWalter Jang     *         otherwise an empty input will only be displayed if there is no non-empty value.
1754a9351db3c8385469bad037544d4b60d76504f5eWalter Jang     */
1764a9351db3c8385469bad037544d4b60d76504f5eWalter Jang    public void setShowOneEmptyEditor(boolean showOneEmptyEditor) {
1774a9351db3c8385469bad037544d4b60d76504f5eWalter Jang        mShowOneEmptyEditor = showOneEmptyEditor;
1784a9351db3c8385469bad037544d4b60d76504f5eWalter Jang    }
1794a9351db3c8385469bad037544d4b60d76504f5eWalter Jang
1804a9351db3c8385469bad037544d4b60d76504f5eWalter Jang    public void setListener(Listener listener) {
1814a9351db3c8385469bad037544d4b60d76504f5eWalter Jang        mListener = listener;
1824a9351db3c8385469bad037544d4b60d76504f5eWalter Jang    }
1834a9351db3c8385469bad037544d4b60d76504f5eWalter Jang
1844a9351db3c8385469bad037544d4b60d76504f5eWalter Jang    public void setIconVisibility(boolean visible) {
1854a9351db3c8385469bad037544d4b60d76504f5eWalter Jang        mIcon.setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
1864a9351db3c8385469bad037544d4b60d76504f5eWalter Jang    }
1874a9351db3c8385469bad037544d4b60d76504f5eWalter Jang
1881044645b9c2050436f4f0e0c7e5b8da2931879baWalter Jang    public void setState(DataKind kind, RawContactDelta state, boolean readOnly,
18988b0a8cc7c512c5f28dad958da276974c845355cWalter Jang            ViewIdGenerator vig) {
190aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey        mKind = kind;
191aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey        mState = state;
19292525ff51e2cb06531447a7dcde5e22334669c8eMegha Joshi        mReadOnly = readOnly;
1934b722a93f1ecdeb2890fb7e5db4c22794bccfdcdMakoto Onuki        mViewIdGenerator = vig;
1944b722a93f1ecdeb2890fb7e5db4c22794bccfdcdMakoto Onuki
1954b722a93f1ecdeb2890fb7e5db4c22794bccfdcdMakoto Onuki        setId(mViewIdGenerator.getId(state, kind, null, ViewIdGenerator.NO_VIEW_INDEX));
196aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey
197aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey        // TODO: handle resources from remote packages
198043fba637b421dcd554c634f769f0e883fb5ff8bBrian Attwell        final String titleString = (kind.titleRes == -1 || kind.titleRes == 0)
199f7ce5978a6a6314028fe9bbb1da99338b111750fDaniel Lehmann                ? ""
200cfb525b3f18d71d6cc5d4ef36d704b3ba9e9a6f3Daniel Lehmann                : getResources().getString(kind.titleRes);
201043fba637b421dcd554c634f769f0e883fb5ff8bBrian Attwell        mIcon.setContentDescription(titleString);
202aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey
203043fba637b421dcd554c634f769f0e883fb5ff8bBrian Attwell        mIcon.setImageDrawable(getMimeTypeDrawable(kind.mimeType));
2048c8cafb21cebbea33468c068efacea6c77635811Brian Attwell        if (mIcon.getDrawable() == null) {
2058c8cafb21cebbea33468c068efacea6c77635811Brian Attwell            mIcon.setContentDescription(null);
2068c8cafb21cebbea33468c068efacea6c77635811Brian Attwell        }
207aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey
208043fba637b421dcd554c634f769f0e883fb5ff8bBrian Attwell        rebuildFromState();
209043fba637b421dcd554c634f769f0e883fb5ff8bBrian Attwell        updateEmptyEditors(/* shouldAnimate = */ false);
2103fa75acf99f5245eae00c77086ff130b3552ceecKatherine Kuan    }
2113fa75acf99f5245eae00c77086ff130b3552ceecKatherine Kuan
212aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey    /**
213aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey     * Build editors for all current {@link #mState} rows.
214aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey     */
215043fba637b421dcd554c634f769f0e883fb5ff8bBrian Attwell    private void rebuildFromState() {
216aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey        // Remove any existing editors
217aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey        mEditors.removeAllViews();
218aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey
219da5fd9039bd303f5e4634a31ed9a00d58d679b39Daniel Lehmann        // Check if we are displaying anything here
220da5fd9039bd303f5e4634a31ed9a00d58d679b39Daniel Lehmann        boolean hasEntries = mState.hasMimeEntries(mKind.mimeType);
221da5fd9039bd303f5e4634a31ed9a00d58d679b39Daniel Lehmann
222da5fd9039bd303f5e4634a31ed9a00d58d679b39Daniel Lehmann        if (hasEntries) {
223da5fd9039bd303f5e4634a31ed9a00d58d679b39Daniel Lehmann            for (ValuesDelta entry : mState.getMimeEntries(mKind.mimeType)) {
224da5fd9039bd303f5e4634a31ed9a00d58d679b39Daniel Lehmann                // Skip entries that aren't visible
225da5fd9039bd303f5e4634a31ed9a00d58d679b39Daniel Lehmann                if (!entry.isVisible()) continue;
2263e7436450f0fdd5ad939c3b46e7176e082c9ee11Daniel Lehmann                if (isEmptyNoop(entry)) continue;
227da5fd9039bd303f5e4634a31ed9a00d58d679b39Daniel Lehmann
228634ab3baaf2bc69bd7977153fcdebf0e737e207dDaniel Lehmann                createEditorView(entry);
229da5fd9039bd303f5e4634a31ed9a00d58d679b39Daniel Lehmann            }
230aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey        }
231aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey    }
232aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey
233634ab3baaf2bc69bd7977153fcdebf0e737e207dDaniel Lehmann
234634ab3baaf2bc69bd7977153fcdebf0e737e207dDaniel Lehmann    /**
235634ab3baaf2bc69bd7977153fcdebf0e737e207dDaniel Lehmann     * Creates an EditorView for the given entry. This function must be used while constructing
236634ab3baaf2bc69bd7977153fcdebf0e737e207dDaniel Lehmann     * the views corresponding to the the object-model. The resulting EditorView is also added
237634ab3baaf2bc69bd7977153fcdebf0e737e207dDaniel Lehmann     * to the end of mEditors
238634ab3baaf2bc69bd7977153fcdebf0e737e207dDaniel Lehmann     */
239634ab3baaf2bc69bd7977153fcdebf0e737e207dDaniel Lehmann    private View createEditorView(ValuesDelta entry) {
240634ab3baaf2bc69bd7977153fcdebf0e737e207dDaniel Lehmann        final View view;
241ddd4e821226395fd977d87b6692f761c90d59139Chiao Cheng        final int layoutResId = EditorUiUtils.getLayoutResourceId(mKind.mimeType);
24263ffb90935d8aec295e4a8f62af6c77dafe1899fKatherine Kuan        try {
243ddd4e821226395fd977d87b6692f761c90d59139Chiao Cheng            view = mInflater.inflate(layoutResId, mEditors, false);
24463ffb90935d8aec295e4a8f62af6c77dafe1899fKatherine Kuan        } catch (Exception e) {
24563ffb90935d8aec295e4a8f62af6c77dafe1899fKatherine Kuan            throw new RuntimeException(
24663ffb90935d8aec295e4a8f62af6c77dafe1899fKatherine Kuan                    "Cannot allocate editor with layout resource ID " +
247ddd4e821226395fd977d87b6692f761c90d59139Chiao Cheng                    layoutResId + " for MIME type " + mKind.mimeType +
2482591436971bad3050c85929c8e0c5a8d48111f88Katherine Kuan                    " with error " + e.toString());
249634ab3baaf2bc69bd7977153fcdebf0e737e207dDaniel Lehmann        }
2509f9d78e41f336b77012437c8622eafeaad4b2adcWalter Jang        // Hide the types drop downs until the associated edit field is focused
2519f9d78e41f336b77012437c8622eafeaad4b2adcWalter Jang        if (view instanceof LabeledEditorView) {
2529f9d78e41f336b77012437c8622eafeaad4b2adcWalter Jang            ((LabeledEditorView) view).setHideTypeInitially(true);
2539f9d78e41f336b77012437c8622eafeaad4b2adcWalter Jang        }
254634ab3baaf2bc69bd7977153fcdebf0e737e207dDaniel Lehmann
25502bb1252aa990813fa9e86ad75c458acc15e6801Dmitri Plotnikov        view.setEnabled(isEnabled());
25602bb1252aa990813fa9e86ad75c458acc15e6801Dmitri Plotnikov
257634ab3baaf2bc69bd7977153fcdebf0e737e207dDaniel Lehmann        if (view instanceof Editor) {
258634ab3baaf2bc69bd7977153fcdebf0e737e207dDaniel Lehmann            Editor editor = (Editor) view;
2595e330ed98f1c2711b4cd3b375a1c0f76bedaf3e1Katherine Kuan            editor.setDeletable(true);
260634ab3baaf2bc69bd7977153fcdebf0e737e207dDaniel Lehmann            editor.setValues(mKind, entry, mState, mReadOnly, mViewIdGenerator);
261634ab3baaf2bc69bd7977153fcdebf0e737e207dDaniel Lehmann            editor.setEditorListener(this);
262634ab3baaf2bc69bd7977153fcdebf0e737e207dDaniel Lehmann        }
263634ab3baaf2bc69bd7977153fcdebf0e737e207dDaniel Lehmann        mEditors.addView(view);
264634ab3baaf2bc69bd7977153fcdebf0e737e207dDaniel Lehmann        return view;
265634ab3baaf2bc69bd7977153fcdebf0e737e207dDaniel Lehmann    }
266634ab3baaf2bc69bd7977153fcdebf0e737e207dDaniel Lehmann
2679e83c84a7603eb6fca1b42631d894731d61f7a34Daniel Lehmann    /**
2683e7436450f0fdd5ad939c3b46e7176e082c9ee11Daniel Lehmann     * Tests whether the given item has no changes (so it exists in the database) but is empty
2693e7436450f0fdd5ad939c3b46e7176e082c9ee11Daniel Lehmann     */
2703e7436450f0fdd5ad939c3b46e7176e082c9ee11Daniel Lehmann    private boolean isEmptyNoop(ValuesDelta item) {
2713e7436450f0fdd5ad939c3b46e7176e082c9ee11Daniel Lehmann        if (!item.isNoop()) return false;
2723e7436450f0fdd5ad939c3b46e7176e082c9ee11Daniel Lehmann        final int fieldCount = mKind.fieldList.size();
2733e7436450f0fdd5ad939c3b46e7176e082c9ee11Daniel Lehmann        for (int i = 0; i < fieldCount; i++) {
2743e7436450f0fdd5ad939c3b46e7176e082c9ee11Daniel Lehmann            final String column = mKind.fieldList.get(i).column;
2753e7436450f0fdd5ad939c3b46e7176e082c9ee11Daniel Lehmann            final String value = item.getAsString(column);
2763e7436450f0fdd5ad939c3b46e7176e082c9ee11Daniel Lehmann            if (!TextUtils.isEmpty(value)) return false;
2773e7436450f0fdd5ad939c3b46e7176e082c9ee11Daniel Lehmann        }
2783e7436450f0fdd5ad939c3b46e7176e082c9ee11Daniel Lehmann        return true;
2793e7436450f0fdd5ad939c3b46e7176e082c9ee11Daniel Lehmann    }
2803e7436450f0fdd5ad939c3b46e7176e082c9ee11Daniel Lehmann
2811b22073590c1801c3e6c7bde2f4636632a049f6cKatherine Kuan    /**
2822591436971bad3050c85929c8e0c5a8d48111f88Katherine Kuan     * Updates the editors being displayed to the user removing extra empty
2832591436971bad3050c85929c8e0c5a8d48111f88Katherine Kuan     * {@link Editor}s, so there is only max 1 empty {@link Editor} view at a time.
2841b22073590c1801c3e6c7bde2f4636632a049f6cKatherine Kuan     */
2854a9351db3c8385469bad037544d4b60d76504f5eWalter Jang    public void updateEmptyEditors(boolean shouldAnimate) {
286043fba637b421dcd554c634f769f0e883fb5ff8bBrian Attwell
287043fba637b421dcd554c634f769f0e883fb5ff8bBrian Attwell        final List<View> emptyEditors = getEmptyEditors();
2881b22073590c1801c3e6c7bde2f4636632a049f6cKatherine Kuan
2891b22073590c1801c3e6c7bde2f4636632a049f6cKatherine Kuan        // If there is more than 1 empty editor, then remove it from the list of editors.
2902591436971bad3050c85929c8e0c5a8d48111f88Katherine Kuan        if (emptyEditors.size() > 1) {
291043fba637b421dcd554c634f769f0e883fb5ff8bBrian Attwell            for (final View emptyEditorView : emptyEditors) {
292043fba637b421dcd554c634f769f0e883fb5ff8bBrian Attwell                // If no child {@link View}s are being focused on within this {@link View}, then
293043fba637b421dcd554c634f769f0e883fb5ff8bBrian Attwell                // remove this empty editor. We can assume that at least one empty editor has focus.
294043fba637b421dcd554c634f769f0e883fb5ff8bBrian Attwell                // The only way to get two empty editors is by deleting characters from a non-empty
295043fba637b421dcd554c634f769f0e883fb5ff8bBrian Attwell                // editor, in which case this editor has focus.
2961b22073590c1801c3e6c7bde2f4636632a049f6cKatherine Kuan                if (emptyEditorView.findFocus() == null) {
297043fba637b421dcd554c634f769f0e883fb5ff8bBrian Attwell                    final Editor editor = (Editor) emptyEditorView;
298043fba637b421dcd554c634f769f0e883fb5ff8bBrian Attwell                    if (shouldAnimate) {
299043fba637b421dcd554c634f769f0e883fb5ff8bBrian Attwell                        editor.deleteEditor();
300043fba637b421dcd554c634f769f0e883fb5ff8bBrian Attwell                    } else {
301043fba637b421dcd554c634f769f0e883fb5ff8bBrian Attwell                        mEditors.removeView(emptyEditorView);
302043fba637b421dcd554c634f769f0e883fb5ff8bBrian Attwell                    }
3031b22073590c1801c3e6c7bde2f4636632a049f6cKatherine Kuan                }
3041b22073590c1801c3e6c7bde2f4636632a049f6cKatherine Kuan            }
305043fba637b421dcd554c634f769f0e883fb5ff8bBrian Attwell        } else if (mKind == null) {
306043fba637b421dcd554c634f769f0e883fb5ff8bBrian Attwell            // There is nothing we can do.
307043fba637b421dcd554c634f769f0e883fb5ff8bBrian Attwell            return;
308043fba637b421dcd554c634f769f0e883fb5ff8bBrian Attwell        } else if (isReadOnly()) {
309043fba637b421dcd554c634f769f0e883fb5ff8bBrian Attwell            // We don't show empty editors for read only data kinds.
310043fba637b421dcd554c634f769f0e883fb5ff8bBrian Attwell            return;
311043fba637b421dcd554c634f769f0e883fb5ff8bBrian Attwell        } else if (mKind.typeOverallMax == getEditorCount() && mKind.typeOverallMax != 0) {
312043fba637b421dcd554c634f769f0e883fb5ff8bBrian Attwell            // We have already reached the maximum number of editors. Lets not add any more.
313043fba637b421dcd554c634f769f0e883fb5ff8bBrian Attwell            return;
314043fba637b421dcd554c634f769f0e883fb5ff8bBrian Attwell        } else if (emptyEditors.size() == 1) {
315043fba637b421dcd554c634f769f0e883fb5ff8bBrian Attwell            // We have already reached the maximum number of empty editors. Lets not add any more.
316043fba637b421dcd554c634f769f0e883fb5ff8bBrian Attwell            return;
3174a9351db3c8385469bad037544d4b60d76504f5eWalter Jang        } else if (mShowOneEmptyEditor) {
318043fba637b421dcd554c634f769f0e883fb5ff8bBrian Attwell            final ValuesDelta values = RawContactModifier.insertChild(mState, mKind);
319043fba637b421dcd554c634f769f0e883fb5ff8bBrian Attwell            final View newField = createEditorView(values);
320043fba637b421dcd554c634f769f0e883fb5ff8bBrian Attwell            if (shouldAnimate) {
321043fba637b421dcd554c634f769f0e883fb5ff8bBrian Attwell                newField.setVisibility(View.GONE);
322043fba637b421dcd554c634f769f0e883fb5ff8bBrian Attwell                EditorAnimator.getInstance().showFieldFooter(newField);
323043fba637b421dcd554c634f769f0e883fb5ff8bBrian Attwell            }
3243f26e24c0cbc988b32d274b7a4e88c994355ef4fDaniel Lehmann        }
3251b22073590c1801c3e6c7bde2f4636632a049f6cKatherine Kuan    }
3261b22073590c1801c3e6c7bde2f4636632a049f6cKatherine Kuan
3271b22073590c1801c3e6c7bde2f4636632a049f6cKatherine Kuan    /**
3282591436971bad3050c85929c8e0c5a8d48111f88Katherine Kuan     * Returns a list of empty editor views in this section.
3292591436971bad3050c85929c8e0c5a8d48111f88Katherine Kuan     */
3302591436971bad3050c85929c8e0c5a8d48111f88Katherine Kuan    private List<View> getEmptyEditors() {
3312591436971bad3050c85929c8e0c5a8d48111f88Katherine Kuan        List<View> emptyEditorViews = new ArrayList<View>();
3322591436971bad3050c85929c8e0c5a8d48111f88Katherine Kuan        for (int i = 0; i < mEditors.getChildCount(); i++) {
3332591436971bad3050c85929c8e0c5a8d48111f88Katherine Kuan            View view = mEditors.getChildAt(i);
3342591436971bad3050c85929c8e0c5a8d48111f88Katherine Kuan            if (((Editor) view).isEmpty()) {
3352591436971bad3050c85929c8e0c5a8d48111f88Katherine Kuan                emptyEditorViews.add(view);
3362591436971bad3050c85929c8e0c5a8d48111f88Katherine Kuan            }
3372591436971bad3050c85929c8e0c5a8d48111f88Katherine Kuan        }
3382591436971bad3050c85929c8e0c5a8d48111f88Katherine Kuan        return emptyEditorViews;
3392591436971bad3050c85929c8e0c5a8d48111f88Katherine Kuan    }
3402591436971bad3050c85929c8e0c5a8d48111f88Katherine Kuan
341ae9c9ec49c460d34f7c649c3a3bf684fc5ef262dWalter Jang    public boolean areAllEditorsEmpty() {
342ae9c9ec49c460d34f7c649c3a3bf684fc5ef262dWalter Jang        for (int i = 0; i < mEditors.getChildCount(); i++) {
343ae9c9ec49c460d34f7c649c3a3bf684fc5ef262dWalter Jang            final View view = mEditors.getChildAt(i);
344ae9c9ec49c460d34f7c649c3a3bf684fc5ef262dWalter Jang            if (!((Editor) view).isEmpty()) {
345ae9c9ec49c460d34f7c649c3a3bf684fc5ef262dWalter Jang                return false;
346ae9c9ec49c460d34f7c649c3a3bf684fc5ef262dWalter Jang            }
347ae9c9ec49c460d34f7c649c3a3bf684fc5ef262dWalter Jang        }
348ae9c9ec49c460d34f7c649c3a3bf684fc5ef262dWalter Jang        return true;
349ae9c9ec49c460d34f7c649c3a3bf684fc5ef262dWalter Jang    }
350ae9c9ec49c460d34f7c649c3a3bf684fc5ef262dWalter Jang
351043fba637b421dcd554c634f769f0e883fb5ff8bBrian Attwell    public int getEditorCount() {
352043fba637b421dcd554c634f769f0e883fb5ff8bBrian Attwell        return mEditors.getChildCount();
35326918da1650c1ff7b6f92deb978ab976525c811aJosh Gargus    }
35426918da1650c1ff7b6f92deb978ab976525c811aJosh Gargus
355043fba637b421dcd554c634f769f0e883fb5ff8bBrian Attwell    public DataKind getKind() {
356043fba637b421dcd554c634f769f0e883fb5ff8bBrian Attwell        return mKind;
35726918da1650c1ff7b6f92deb978ab976525c811aJosh Gargus    }
35826918da1650c1ff7b6f92deb978ab976525c811aJosh Gargus
35926918da1650c1ff7b6f92deb978ab976525c811aJosh Gargus    /**
360043fba637b421dcd554c634f769f0e883fb5ff8bBrian Attwell     * Return an icon that represents {@param mimeType}.
36126918da1650c1ff7b6f92deb978ab976525c811aJosh Gargus     */
362043fba637b421dcd554c634f769f0e883fb5ff8bBrian Attwell    private Drawable getMimeTypeDrawable(String mimeType) {
363043fba637b421dcd554c634f769f0e883fb5ff8bBrian Attwell        switch (mimeType) {
364043fba637b421dcd554c634f769f0e883fb5ff8bBrian Attwell            case StructuredPostal.CONTENT_ITEM_TYPE:
365043fba637b421dcd554c634f769f0e883fb5ff8bBrian Attwell                return getResources().getDrawable(R.drawable.ic_place_24dp);
366043fba637b421dcd554c634f769f0e883fb5ff8bBrian Attwell            case SipAddress.CONTENT_ITEM_TYPE:
367043fba637b421dcd554c634f769f0e883fb5ff8bBrian Attwell                return getResources().getDrawable(R.drawable.ic_dialer_sip_black_24dp);
368043fba637b421dcd554c634f769f0e883fb5ff8bBrian Attwell            case Phone.CONTENT_ITEM_TYPE:
369043fba637b421dcd554c634f769f0e883fb5ff8bBrian Attwell                return getResources().getDrawable(R.drawable.ic_phone_24dp);
370043fba637b421dcd554c634f769f0e883fb5ff8bBrian Attwell            case Im.CONTENT_ITEM_TYPE:
371043fba637b421dcd554c634f769f0e883fb5ff8bBrian Attwell                return getResources().getDrawable(R.drawable.ic_message_24dp);
372043fba637b421dcd554c634f769f0e883fb5ff8bBrian Attwell            case Event.CONTENT_ITEM_TYPE:
373043fba637b421dcd554c634f769f0e883fb5ff8bBrian Attwell                return getResources().getDrawable(R.drawable.ic_event_24dp);
374043fba637b421dcd554c634f769f0e883fb5ff8bBrian Attwell            case Email.CONTENT_ITEM_TYPE:
375043fba637b421dcd554c634f769f0e883fb5ff8bBrian Attwell                return getResources().getDrawable(R.drawable.ic_email_24dp);
376043fba637b421dcd554c634f769f0e883fb5ff8bBrian Attwell            case Website.CONTENT_ITEM_TYPE:
377043fba637b421dcd554c634f769f0e883fb5ff8bBrian Attwell                return getResources().getDrawable(R.drawable.ic_public_black_24dp);
378043fba637b421dcd554c634f769f0e883fb5ff8bBrian Attwell            case Photo.CONTENT_ITEM_TYPE:
379043fba637b421dcd554c634f769f0e883fb5ff8bBrian Attwell                return getResources().getDrawable(R.drawable.ic_camera_alt_black_24dp);
380043fba637b421dcd554c634f769f0e883fb5ff8bBrian Attwell            case GroupMembership.CONTENT_ITEM_TYPE:
381043fba637b421dcd554c634f769f0e883fb5ff8bBrian Attwell                return getResources().getDrawable(R.drawable.ic_people_black_24dp);
382043fba637b421dcd554c634f769f0e883fb5ff8bBrian Attwell            case Organization.CONTENT_ITEM_TYPE:
383043fba637b421dcd554c634f769f0e883fb5ff8bBrian Attwell                return getResources().getDrawable(R.drawable.ic_business_black_24dp);
384043fba637b421dcd554c634f769f0e883fb5ff8bBrian Attwell            case Note.CONTENT_ITEM_TYPE:
385043fba637b421dcd554c634f769f0e883fb5ff8bBrian Attwell                return getResources().getDrawable(R.drawable.ic_insert_comment_black_24dp);
386043fba637b421dcd554c634f769f0e883fb5ff8bBrian Attwell            case Relation.CONTENT_ITEM_TYPE:
387043fba637b421dcd554c634f769f0e883fb5ff8bBrian Attwell                return getResources().getDrawable(R.drawable.ic_circles_extended_black_24dp);
388043fba637b421dcd554c634f769f0e883fb5ff8bBrian Attwell            default:
389043fba637b421dcd554c634f769f0e883fb5ff8bBrian Attwell                return null;
39062bab31374dd66462643cbc04ebb2acece4b9b39Dmitri Plotnikov        }
391aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey    }
392aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey}
393