KindSectionView.java revision 1b22073590c1801c3e6c7bde2f4636632a049f6c
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 com.android.contacts.R;
2018ffaa2561cc7dd2e3ef81737e6537931c0a9a11Dmitri Plotnikovimport com.android.contacts.editor.Editor.EditorListener;
21405671a7b42d97ebf7ae7c0eeb4721f881139673Daisuke Miyakawaimport com.android.contacts.model.DataKind;
22d25f958595b190586bd838d50718b1a2a3f0d417Dmitri Plotnikovimport com.android.contacts.model.EntityDelta;
23aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkeyimport com.android.contacts.model.EntityDelta.ValuesDelta;
24d25f958595b190586bd838d50718b1a2a3f0d417Dmitri Plotnikovimport com.android.contacts.model.EntityModifier;
25aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey
26aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkeyimport android.content.Context;
271b22073590c1801c3e6c7bde2f4636632a049f6cKatherine Kuanimport android.provider.ContactsContract.CommonDataKinds.Email;
281b22073590c1801c3e6c7bde2f4636632a049f6cKatherine Kuanimport android.provider.ContactsContract.CommonDataKinds.Event;
291b22073590c1801c3e6c7bde2f4636632a049f6cKatherine Kuanimport android.provider.ContactsContract.CommonDataKinds.Im;
301b22073590c1801c3e6c7bde2f4636632a049f6cKatherine Kuanimport android.provider.ContactsContract.CommonDataKinds.Note;
311b22073590c1801c3e6c7bde2f4636632a049f6cKatherine Kuanimport android.provider.ContactsContract.CommonDataKinds.Phone;
321b22073590c1801c3e6c7bde2f4636632a049f6cKatherine Kuanimport android.provider.ContactsContract.CommonDataKinds.Relation;
331b22073590c1801c3e6c7bde2f4636632a049f6cKatherine Kuanimport android.provider.ContactsContract.CommonDataKinds.SipAddress;
341b22073590c1801c3e6c7bde2f4636632a049f6cKatherine Kuanimport android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
351b22073590c1801c3e6c7bde2f4636632a049f6cKatherine Kuanimport android.provider.ContactsContract.CommonDataKinds.Website;
36aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkeyimport android.provider.ContactsContract.Data;
373e7436450f0fdd5ad939c3b46e7176e082c9ee11Daniel Lehmannimport android.text.TextUtils;
38aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkeyimport android.util.AttributeSet;
3963ffb90935d8aec295e4a8f62af6c77dafe1899fKatherine Kuanimport android.view.LayoutInflater;
40aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkeyimport android.view.View;
41aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkeyimport android.view.ViewGroup;
42aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkeyimport android.widget.LinearLayout;
431b22073590c1801c3e6c7bde2f4636632a049f6cKatherine Kuanimport android.widget.TextView;
44aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey
4562bab31374dd66462643cbc04ebb2acece4b9b39Dmitri Plotnikovimport java.util.ArrayList;
461b22073590c1801c3e6c7bde2f4636632a049f6cKatherine Kuanimport java.util.HashMap;
471b22073590c1801c3e6c7bde2f4636632a049f6cKatherine Kuanimport java.util.List;
4862bab31374dd66462643cbc04ebb2acece4b9b39Dmitri Plotnikov
49aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey/**
50aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey * Custom view for an entire section of data as segmented by
51aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey * {@link DataKind} around a {@link Data#MIMETYPE}. This view shows a
52aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey * section header and a trigger for adding new {@link Data} rows.
53aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey */
54bb556954e29ae0700b729f128d8cd09ecdd91ee3Daniel Lehmannpublic class KindSectionView extends LinearLayout implements EditorListener {
55aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey    private static final String TAG = "KindSectionView";
56aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey
57aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey    private ViewGroup mEditors;
581b22073590c1801c3e6c7bde2f4636632a049f6cKatherine Kuan    private View mAddFieldFooter;
591b22073590c1801c3e6c7bde2f4636632a049f6cKatherine Kuan    private TextView mAddFieldText;
60f7ce5978a6a6314028fe9bbb1da99338b111750fDaniel Lehmann    private String mTitleString;
61aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey
62aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey    private DataKind mKind;
63aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey    private EntityDelta mState;
6492525ff51e2cb06531447a7dcde5e22334669c8eMegha Joshi    private boolean mReadOnly;
656f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey
664b722a93f1ecdeb2890fb7e5db4c22794bccfdcdMakoto Onuki    private ViewIdGenerator mViewIdGenerator;
674b722a93f1ecdeb2890fb7e5db4c22794bccfdcdMakoto Onuki
6863ffb90935d8aec295e4a8f62af6c77dafe1899fKatherine Kuan    private LayoutInflater mInflater;
6991d8e892d549bbeba721cb434163a83bc99330a9Dmitri Plotnikov
701b22073590c1801c3e6c7bde2f4636632a049f6cKatherine Kuan    /**
711b22073590c1801c3e6c7bde2f4636632a049f6cKatherine Kuan     * Map of data MIME types to the "add field" footer text resource ID
721b22073590c1801c3e6c7bde2f4636632a049f6cKatherine Kuan     * (that for example, maps to "Add new phone number").
731b22073590c1801c3e6c7bde2f4636632a049f6cKatherine Kuan     */
741b22073590c1801c3e6c7bde2f4636632a049f6cKatherine Kuan    private static final HashMap<String, Integer> sAddFieldFooterTextResourceIds =
751b22073590c1801c3e6c7bde2f4636632a049f6cKatherine Kuan            new HashMap<String, Integer>();
761b22073590c1801c3e6c7bde2f4636632a049f6cKatherine Kuan
771b22073590c1801c3e6c7bde2f4636632a049f6cKatherine Kuan    static {
781b22073590c1801c3e6c7bde2f4636632a049f6cKatherine Kuan        final HashMap<String, Integer> hashMap = sAddFieldFooterTextResourceIds;
791b22073590c1801c3e6c7bde2f4636632a049f6cKatherine Kuan        hashMap.put(Phone.CONTENT_ITEM_TYPE, R.string.add_phone);
801b22073590c1801c3e6c7bde2f4636632a049f6cKatherine Kuan        hashMap.put(Email.CONTENT_ITEM_TYPE, R.string.add_email);
811b22073590c1801c3e6c7bde2f4636632a049f6cKatherine Kuan        hashMap.put(Im.CONTENT_ITEM_TYPE, R.string.add_im);
821b22073590c1801c3e6c7bde2f4636632a049f6cKatherine Kuan        hashMap.put(StructuredPostal.CONTENT_ITEM_TYPE, R.string.add_address);
831b22073590c1801c3e6c7bde2f4636632a049f6cKatherine Kuan        hashMap.put(Note.CONTENT_ITEM_TYPE, R.string.add_note);
841b22073590c1801c3e6c7bde2f4636632a049f6cKatherine Kuan        hashMap.put(Website.CONTENT_ITEM_TYPE, R.string.add_website);
851b22073590c1801c3e6c7bde2f4636632a049f6cKatherine Kuan        hashMap.put(SipAddress.CONTENT_ITEM_TYPE, R.string.add_internet_call);
861b22073590c1801c3e6c7bde2f4636632a049f6cKatherine Kuan        hashMap.put(Event.CONTENT_ITEM_TYPE, R.string.add_event);
871b22073590c1801c3e6c7bde2f4636632a049f6cKatherine Kuan        hashMap.put(Relation.CONTENT_ITEM_TYPE, R.string.add_relationship);
881b22073590c1801c3e6c7bde2f4636632a049f6cKatherine Kuan    }
891b22073590c1801c3e6c7bde2f4636632a049f6cKatherine Kuan
901b22073590c1801c3e6c7bde2f4636632a049f6cKatherine Kuan    /**
911b22073590c1801c3e6c7bde2f4636632a049f6cKatherine Kuan     * List of the empty editor views.
921b22073590c1801c3e6c7bde2f4636632a049f6cKatherine Kuan     */
931b22073590c1801c3e6c7bde2f4636632a049f6cKatherine Kuan    private List<View> mEmptyEditorViews = new ArrayList<View>();
941b22073590c1801c3e6c7bde2f4636632a049f6cKatherine Kuan
95aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey    public KindSectionView(Context context) {
9691d8e892d549bbeba721cb434163a83bc99330a9Dmitri Plotnikov        this(context, null);
97aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey    }
98aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey
99aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey    public KindSectionView(Context context, AttributeSet attrs) {
100aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey        super(context, attrs);
101aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey    }
102aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey
10302bb1252aa990813fa9e86ad75c458acc15e6801Dmitri Plotnikov    @Override
10402bb1252aa990813fa9e86ad75c458acc15e6801Dmitri Plotnikov    public void setEnabled(boolean enabled) {
10502bb1252aa990813fa9e86ad75c458acc15e6801Dmitri Plotnikov        super.setEnabled(enabled);
10602bb1252aa990813fa9e86ad75c458acc15e6801Dmitri Plotnikov        if (mEditors != null) {
10702bb1252aa990813fa9e86ad75c458acc15e6801Dmitri Plotnikov            int childCount = mEditors.getChildCount();
10802bb1252aa990813fa9e86ad75c458acc15e6801Dmitri Plotnikov            for (int i = 0; i < childCount; i++) {
10902bb1252aa990813fa9e86ad75c458acc15e6801Dmitri Plotnikov                mEditors.getChildAt(i).setEnabled(enabled);
11002bb1252aa990813fa9e86ad75c458acc15e6801Dmitri Plotnikov            }
11102bb1252aa990813fa9e86ad75c458acc15e6801Dmitri Plotnikov        }
11202bb1252aa990813fa9e86ad75c458acc15e6801Dmitri Plotnikov
1131b22073590c1801c3e6c7bde2f4636632a049f6cKatherine Kuan        if (enabled && !mReadOnly) {
1141b22073590c1801c3e6c7bde2f4636632a049f6cKatherine Kuan            mAddFieldFooter.setVisibility(View.VISIBLE);
1151b22073590c1801c3e6c7bde2f4636632a049f6cKatherine Kuan        } else {
1161b22073590c1801c3e6c7bde2f4636632a049f6cKatherine Kuan            mAddFieldFooter.setVisibility(View.GONE);
11791d8e892d549bbeba721cb434163a83bc99330a9Dmitri Plotnikov        }
11891d8e892d549bbeba721cb434163a83bc99330a9Dmitri Plotnikov    }
11991d8e892d549bbeba721cb434163a83bc99330a9Dmitri Plotnikov
12002bb1252aa990813fa9e86ad75c458acc15e6801Dmitri Plotnikov    public boolean isReadOnly() {
12102bb1252aa990813fa9e86ad75c458acc15e6801Dmitri Plotnikov        return mReadOnly;
12202bb1252aa990813fa9e86ad75c458acc15e6801Dmitri Plotnikov    }
12302bb1252aa990813fa9e86ad75c458acc15e6801Dmitri Plotnikov
124aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey    /** {@inheritDoc} */
125aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey    @Override
126aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey    protected void onFinishInflate() {
127aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey        setDrawingCacheEnabled(true);
128aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey        setAlwaysDrawnWithCacheEnabled(true);
129aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey
13063ffb90935d8aec295e4a8f62af6c77dafe1899fKatherine Kuan        mInflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
13163ffb90935d8aec295e4a8f62af6c77dafe1899fKatherine Kuan
132aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey        mEditors = (ViewGroup)findViewById(R.id.kind_editors);
1331b22073590c1801c3e6c7bde2f4636632a049f6cKatherine Kuan        mAddFieldText = (TextView) findViewById(R.id.add_text);
1341b22073590c1801c3e6c7bde2f4636632a049f6cKatherine Kuan        mAddFieldFooter = findViewById(R.id.add_field_footer);
1351b22073590c1801c3e6c7bde2f4636632a049f6cKatherine Kuan        mAddFieldFooter.setOnClickListener(new OnClickListener() {
13679c9c5a8f849908d2b141c30af15923ea2b60f5bDaniel Lehmann            @Override
137bb556954e29ae0700b729f128d8cd09ecdd91ee3Daniel Lehmann            public void onClick(View v) {
1381b22073590c1801c3e6c7bde2f4636632a049f6cKatherine Kuan                // Setup click listener to add an empty field when the footer is clicked.
1391b22073590c1801c3e6c7bde2f4636632a049f6cKatherine Kuan                mAddFieldFooter.setVisibility(View.GONE);
1401b22073590c1801c3e6c7bde2f4636632a049f6cKatherine Kuan                addItem();
141bb556954e29ae0700b729f128d8cd09ecdd91ee3Daniel Lehmann            }
142bb556954e29ae0700b729f128d8cd09ecdd91ee3Daniel Lehmann        });
143aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey    }
144aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey
1456f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey    /** {@inheritDoc} */
14679c9c5a8f849908d2b141c30af15923ea2b60f5bDaniel Lehmann    @Override
147aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey    public void onDeleted(Editor editor) {
1481b22073590c1801c3e6c7bde2f4636632a049f6cKatherine Kuan        updateAddFooterVisible();
1491b22073590c1801c3e6c7bde2f4636632a049f6cKatherine Kuan        updateSectionVisible();
150aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey    }
151aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey
1526f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey    /** {@inheritDoc} */
15379c9c5a8f849908d2b141c30af15923ea2b60f5bDaniel Lehmann    @Override
1546f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey    public void onRequest(int request) {
1551b22073590c1801c3e6c7bde2f4636632a049f6cKatherine Kuan        // If a field has changed, then check if another row can be added dynamically.
1561b22073590c1801c3e6c7bde2f4636632a049f6cKatherine Kuan        if (request == FIELD_CHANGED) {
1571b22073590c1801c3e6c7bde2f4636632a049f6cKatherine Kuan            updateAddFooterVisible();
1581b22073590c1801c3e6c7bde2f4636632a049f6cKatherine Kuan        }
1596f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey    }
1606f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey
1614b722a93f1ecdeb2890fb7e5db4c22794bccfdcdMakoto Onuki    public void setState(DataKind kind, EntityDelta state, boolean readOnly, ViewIdGenerator vig) {
162aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey        mKind = kind;
163aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey        mState = state;
16492525ff51e2cb06531447a7dcde5e22334669c8eMegha Joshi        mReadOnly = readOnly;
1654b722a93f1ecdeb2890fb7e5db4c22794bccfdcdMakoto Onuki        mViewIdGenerator = vig;
1664b722a93f1ecdeb2890fb7e5db4c22794bccfdcdMakoto Onuki
1674b722a93f1ecdeb2890fb7e5db4c22794bccfdcdMakoto Onuki        setId(mViewIdGenerator.getId(state, kind, null, ViewIdGenerator.NO_VIEW_INDEX));
168aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey
169aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey        // TODO: handle resources from remote packages
170f7ce5978a6a6314028fe9bbb1da99338b111750fDaniel Lehmann        mTitleString = (kind.titleRes == -1 || kind.titleRes == 0)
171f7ce5978a6a6314028fe9bbb1da99338b111750fDaniel Lehmann                ? ""
172cfb525b3f18d71d6cc5d4ef36d704b3ba9e9a6f3Daniel Lehmann                : getResources().getString(kind.titleRes);
173aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey
1741b22073590c1801c3e6c7bde2f4636632a049f6cKatherine Kuan        // Set "add field" footer message according to MIME type. Some MIME types
1751b22073590c1801c3e6c7bde2f4636632a049f6cKatherine Kuan        // can only have max 1 field, so the map will return null if these sections
1761b22073590c1801c3e6c7bde2f4636632a049f6cKatherine Kuan        // should not have an "Add field" option.
1771b22073590c1801c3e6c7bde2f4636632a049f6cKatherine Kuan        Integer textResourceId = sAddFieldFooterTextResourceIds.get(kind.mimeType);
1781b22073590c1801c3e6c7bde2f4636632a049f6cKatherine Kuan        if (textResourceId != null) {
1791b22073590c1801c3e6c7bde2f4636632a049f6cKatherine Kuan            mAddFieldText.setText(textResourceId);
1801b22073590c1801c3e6c7bde2f4636632a049f6cKatherine Kuan        }
1811b22073590c1801c3e6c7bde2f4636632a049f6cKatherine Kuan
182bb556954e29ae0700b729f128d8cd09ecdd91ee3Daniel Lehmann        rebuildFromState();
1831b22073590c1801c3e6c7bde2f4636632a049f6cKatherine Kuan        updateAddFooterVisible();
1841b22073590c1801c3e6c7bde2f4636632a049f6cKatherine Kuan        updateSectionVisible();
185bb556954e29ae0700b729f128d8cd09ecdd91ee3Daniel Lehmann    }
186bb556954e29ae0700b729f128d8cd09ecdd91ee3Daniel Lehmann
187f7ce5978a6a6314028fe9bbb1da99338b111750fDaniel Lehmann    public String getTitle() {
188f7ce5978a6a6314028fe9bbb1da99338b111750fDaniel Lehmann        return mTitleString;
189aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey    }
190aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey
191aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey    /**
192aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey     * Build editors for all current {@link #mState} rows.
193aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey     */
194aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey    public void rebuildFromState() {
195aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey        // Remove any existing editors
196aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey        mEditors.removeAllViews();
197aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey
198da5fd9039bd303f5e4634a31ed9a00d58d679b39Daniel Lehmann        // Check if we are displaying anything here
199da5fd9039bd303f5e4634a31ed9a00d58d679b39Daniel Lehmann        boolean hasEntries = mState.hasMimeEntries(mKind.mimeType);
200da5fd9039bd303f5e4634a31ed9a00d58d679b39Daniel Lehmann
201da5fd9039bd303f5e4634a31ed9a00d58d679b39Daniel Lehmann        if (hasEntries) {
202da5fd9039bd303f5e4634a31ed9a00d58d679b39Daniel Lehmann            for (ValuesDelta entry : mState.getMimeEntries(mKind.mimeType)) {
203da5fd9039bd303f5e4634a31ed9a00d58d679b39Daniel Lehmann                // Skip entries that aren't visible
204da5fd9039bd303f5e4634a31ed9a00d58d679b39Daniel Lehmann                if (!entry.isVisible()) continue;
2053e7436450f0fdd5ad939c3b46e7176e082c9ee11Daniel Lehmann                if (isEmptyNoop(entry)) continue;
206da5fd9039bd303f5e4634a31ed9a00d58d679b39Daniel Lehmann
207634ab3baaf2bc69bd7977153fcdebf0e737e207dDaniel Lehmann                createEditorView(entry);
208da5fd9039bd303f5e4634a31ed9a00d58d679b39Daniel Lehmann            }
209aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey        }
210aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey    }
211aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey
212634ab3baaf2bc69bd7977153fcdebf0e737e207dDaniel Lehmann
213634ab3baaf2bc69bd7977153fcdebf0e737e207dDaniel Lehmann    /**
214634ab3baaf2bc69bd7977153fcdebf0e737e207dDaniel Lehmann     * Creates an EditorView for the given entry. This function must be used while constructing
215634ab3baaf2bc69bd7977153fcdebf0e737e207dDaniel Lehmann     * the views corresponding to the the object-model. The resulting EditorView is also added
216634ab3baaf2bc69bd7977153fcdebf0e737e207dDaniel Lehmann     * to the end of mEditors
217634ab3baaf2bc69bd7977153fcdebf0e737e207dDaniel Lehmann     */
218634ab3baaf2bc69bd7977153fcdebf0e737e207dDaniel Lehmann    private View createEditorView(ValuesDelta entry) {
219634ab3baaf2bc69bd7977153fcdebf0e737e207dDaniel Lehmann        final View view;
22063ffb90935d8aec295e4a8f62af6c77dafe1899fKatherine Kuan        try {
22163ffb90935d8aec295e4a8f62af6c77dafe1899fKatherine Kuan            view = mInflater.inflate(mKind.editorLayoutResourceId, mEditors, false);
22263ffb90935d8aec295e4a8f62af6c77dafe1899fKatherine Kuan        } catch (Exception e) {
22363ffb90935d8aec295e4a8f62af6c77dafe1899fKatherine Kuan            throw new RuntimeException(
22463ffb90935d8aec295e4a8f62af6c77dafe1899fKatherine Kuan                    "Cannot allocate editor with layout resource ID " +
22563ffb90935d8aec295e4a8f62af6c77dafe1899fKatherine Kuan                    mKind.editorLayoutResourceId);
226634ab3baaf2bc69bd7977153fcdebf0e737e207dDaniel Lehmann        }
227634ab3baaf2bc69bd7977153fcdebf0e737e207dDaniel Lehmann
22802bb1252aa990813fa9e86ad75c458acc15e6801Dmitri Plotnikov        view.setEnabled(isEnabled());
22902bb1252aa990813fa9e86ad75c458acc15e6801Dmitri Plotnikov
230634ab3baaf2bc69bd7977153fcdebf0e737e207dDaniel Lehmann        if (view instanceof Editor) {
231634ab3baaf2bc69bd7977153fcdebf0e737e207dDaniel Lehmann            Editor editor = (Editor) view;
232634ab3baaf2bc69bd7977153fcdebf0e737e207dDaniel Lehmann            editor.setValues(mKind, entry, mState, mReadOnly, mViewIdGenerator);
233634ab3baaf2bc69bd7977153fcdebf0e737e207dDaniel Lehmann            editor.setEditorListener(this);
234634ab3baaf2bc69bd7977153fcdebf0e737e207dDaniel Lehmann            editor.setDeletable(true);
235634ab3baaf2bc69bd7977153fcdebf0e737e207dDaniel Lehmann        }
236634ab3baaf2bc69bd7977153fcdebf0e737e207dDaniel Lehmann        mEditors.addView(view);
237634ab3baaf2bc69bd7977153fcdebf0e737e207dDaniel Lehmann        return view;
238634ab3baaf2bc69bd7977153fcdebf0e737e207dDaniel Lehmann    }
239634ab3baaf2bc69bd7977153fcdebf0e737e207dDaniel Lehmann
2409e83c84a7603eb6fca1b42631d894731d61f7a34Daniel Lehmann    /**
2413e7436450f0fdd5ad939c3b46e7176e082c9ee11Daniel Lehmann     * Tests whether the given item has no changes (so it exists in the database) but is empty
2423e7436450f0fdd5ad939c3b46e7176e082c9ee11Daniel Lehmann     */
2433e7436450f0fdd5ad939c3b46e7176e082c9ee11Daniel Lehmann    private boolean isEmptyNoop(ValuesDelta item) {
2443e7436450f0fdd5ad939c3b46e7176e082c9ee11Daniel Lehmann        if (!item.isNoop()) return false;
2453e7436450f0fdd5ad939c3b46e7176e082c9ee11Daniel Lehmann        final int fieldCount = mKind.fieldList.size();
2463e7436450f0fdd5ad939c3b46e7176e082c9ee11Daniel Lehmann        for (int i = 0; i < fieldCount; i++) {
2473e7436450f0fdd5ad939c3b46e7176e082c9ee11Daniel Lehmann            final String column = mKind.fieldList.get(i).column;
2483e7436450f0fdd5ad939c3b46e7176e082c9ee11Daniel Lehmann            final String value = item.getAsString(column);
2493e7436450f0fdd5ad939c3b46e7176e082c9ee11Daniel Lehmann            if (!TextUtils.isEmpty(value)) return false;
2503e7436450f0fdd5ad939c3b46e7176e082c9ee11Daniel Lehmann        }
2513e7436450f0fdd5ad939c3b46e7176e082c9ee11Daniel Lehmann        return true;
2523e7436450f0fdd5ad939c3b46e7176e082c9ee11Daniel Lehmann    }
2533e7436450f0fdd5ad939c3b46e7176e082c9ee11Daniel Lehmann
2541b22073590c1801c3e6c7bde2f4636632a049f6cKatherine Kuan    private void updateSectionVisible() {
255bb556954e29ae0700b729f128d8cd09ecdd91ee3Daniel Lehmann        setVisibility(getEditorCount() != 0 ? VISIBLE : GONE);
256bb556954e29ae0700b729f128d8cd09ecdd91ee3Daniel Lehmann    }
257bb556954e29ae0700b729f128d8cd09ecdd91ee3Daniel Lehmann
2581b22073590c1801c3e6c7bde2f4636632a049f6cKatherine Kuan    protected void updateAddFooterVisible() {
2591b22073590c1801c3e6c7bde2f4636632a049f6cKatherine Kuan        if (!mReadOnly && mKind.isList) {
2601b22073590c1801c3e6c7bde2f4636632a049f6cKatherine Kuan            // First determine whether there are any existing empty editors.
2611b22073590c1801c3e6c7bde2f4636632a049f6cKatherine Kuan            updateEmptyEditors();
2621b22073590c1801c3e6c7bde2f4636632a049f6cKatherine Kuan            // If there are no existing empty editors and it's possible to add
2631b22073590c1801c3e6c7bde2f4636632a049f6cKatherine Kuan            // another field, then make the "add footer" field visible.
2641b22073590c1801c3e6c7bde2f4636632a049f6cKatherine Kuan            if (!hasEmptyEditor() && EntityModifier.canInsert(mState, mKind)) {
2651b22073590c1801c3e6c7bde2f4636632a049f6cKatherine Kuan                mAddFieldFooter.setVisibility(View.VISIBLE);
2661b22073590c1801c3e6c7bde2f4636632a049f6cKatherine Kuan                return;
2671b22073590c1801c3e6c7bde2f4636632a049f6cKatherine Kuan            }
2681b22073590c1801c3e6c7bde2f4636632a049f6cKatherine Kuan        }
2691b22073590c1801c3e6c7bde2f4636632a049f6cKatherine Kuan        mAddFieldFooter.setVisibility(View.GONE);
2701b22073590c1801c3e6c7bde2f4636632a049f6cKatherine Kuan    }
271bb556954e29ae0700b729f128d8cd09ecdd91ee3Daniel Lehmann
2721b22073590c1801c3e6c7bde2f4636632a049f6cKatherine Kuan    /**
2731b22073590c1801c3e6c7bde2f4636632a049f6cKatherine Kuan     * Determines a list of {@link Editor} {@link View}s that have an empty
2741b22073590c1801c3e6c7bde2f4636632a049f6cKatherine Kuan     * field in them and removes extra ones, so there is max 1 empty
2751b22073590c1801c3e6c7bde2f4636632a049f6cKatherine Kuan     * {@link Editor} {@link View} at a time.
2761b22073590c1801c3e6c7bde2f4636632a049f6cKatherine Kuan     */
2771b22073590c1801c3e6c7bde2f4636632a049f6cKatherine Kuan    private void updateEmptyEditors() {
2781b22073590c1801c3e6c7bde2f4636632a049f6cKatherine Kuan        mEmptyEditorViews.clear();
2791b22073590c1801c3e6c7bde2f4636632a049f6cKatherine Kuan
2801b22073590c1801c3e6c7bde2f4636632a049f6cKatherine Kuan        // Construct a list of editors that have an empty field in them.
2811b22073590c1801c3e6c7bde2f4636632a049f6cKatherine Kuan        for (int i = 0; i < mEditors.getChildCount(); i++) {
2821b22073590c1801c3e6c7bde2f4636632a049f6cKatherine Kuan            View v = mEditors.getChildAt(i);
2831b22073590c1801c3e6c7bde2f4636632a049f6cKatherine Kuan            if (((Editor) v).hasEmptyField()) {
2841b22073590c1801c3e6c7bde2f4636632a049f6cKatherine Kuan                mEmptyEditorViews.add(v);
2851b22073590c1801c3e6c7bde2f4636632a049f6cKatherine Kuan            }
2861b22073590c1801c3e6c7bde2f4636632a049f6cKatherine Kuan        }
2871b22073590c1801c3e6c7bde2f4636632a049f6cKatherine Kuan
2881b22073590c1801c3e6c7bde2f4636632a049f6cKatherine Kuan        // If there is more than 1 empty editor, then remove it from the list of editors.
2891b22073590c1801c3e6c7bde2f4636632a049f6cKatherine Kuan        if (mEmptyEditorViews.size() > 1) {
2901b22073590c1801c3e6c7bde2f4636632a049f6cKatherine Kuan            for (View emptyEditorView : mEmptyEditorViews) {
2911b22073590c1801c3e6c7bde2f4636632a049f6cKatherine Kuan                // If no child {@link View}s are being focused on within
2921b22073590c1801c3e6c7bde2f4636632a049f6cKatherine Kuan                // this {@link View}, then remove this empty editor.
2931b22073590c1801c3e6c7bde2f4636632a049f6cKatherine Kuan                if (emptyEditorView.findFocus() == null) {
2941b22073590c1801c3e6c7bde2f4636632a049f6cKatherine Kuan                    mEditors.removeView(emptyEditorView);
2951b22073590c1801c3e6c7bde2f4636632a049f6cKatherine Kuan                }
2961b22073590c1801c3e6c7bde2f4636632a049f6cKatherine Kuan            }
2973f26e24c0cbc988b32d274b7a4e88c994355ef4fDaniel Lehmann        }
2981b22073590c1801c3e6c7bde2f4636632a049f6cKatherine Kuan    }
2991b22073590c1801c3e6c7bde2f4636632a049f6cKatherine Kuan
3001b22073590c1801c3e6c7bde2f4636632a049f6cKatherine Kuan    /**
3011b22073590c1801c3e6c7bde2f4636632a049f6cKatherine Kuan     * Returns true if one of the editors has an empty field, or false
3021b22073590c1801c3e6c7bde2f4636632a049f6cKatherine Kuan     * otherwise.
3031b22073590c1801c3e6c7bde2f4636632a049f6cKatherine Kuan     */
3041b22073590c1801c3e6c7bde2f4636632a049f6cKatherine Kuan    private boolean hasEmptyEditor() {
3051b22073590c1801c3e6c7bde2f4636632a049f6cKatherine Kuan        return mEmptyEditorViews.size() > 0;
306aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey    }
3076f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey
308bb556954e29ae0700b729f128d8cd09ecdd91ee3Daniel Lehmann    public void addItem() {
30962bab31374dd66462643cbc04ebb2acece4b9b39Dmitri Plotnikov        ValuesDelta values = null;
310bb556954e29ae0700b729f128d8cd09ecdd91ee3Daniel Lehmann        // if this is a list, we can freely add. if not, only allow adding the first
31162bab31374dd66462643cbc04ebb2acece4b9b39Dmitri Plotnikov        if (!mKind.isList) {
31262bab31374dd66462643cbc04ebb2acece4b9b39Dmitri Plotnikov            if (getEditorCount() == 1) {
31362bab31374dd66462643cbc04ebb2acece4b9b39Dmitri Plotnikov                return;
31462bab31374dd66462643cbc04ebb2acece4b9b39Dmitri Plotnikov            }
31562bab31374dd66462643cbc04ebb2acece4b9b39Dmitri Plotnikov
31662bab31374dd66462643cbc04ebb2acece4b9b39Dmitri Plotnikov            // If we already have an item, just make it visible
31762bab31374dd66462643cbc04ebb2acece4b9b39Dmitri Plotnikov            ArrayList<ValuesDelta> entries = mState.getMimeEntries(mKind.mimeType);
31862bab31374dd66462643cbc04ebb2acece4b9b39Dmitri Plotnikov            if (entries != null && entries.size() > 0) {
31962bab31374dd66462643cbc04ebb2acece4b9b39Dmitri Plotnikov                values = entries.get(0);
32062bab31374dd66462643cbc04ebb2acece4b9b39Dmitri Plotnikov            }
32162bab31374dd66462643cbc04ebb2acece4b9b39Dmitri Plotnikov        }
322da5fd9039bd303f5e4634a31ed9a00d58d679b39Daniel Lehmann
323634ab3baaf2bc69bd7977153fcdebf0e737e207dDaniel Lehmann        // Insert a new child, create its view and set its focus
32462bab31374dd66462643cbc04ebb2acece4b9b39Dmitri Plotnikov        if (values == null) {
32562bab31374dd66462643cbc04ebb2acece4b9b39Dmitri Plotnikov            values = EntityModifier.insertChild(mState, mKind);
32662bab31374dd66462643cbc04ebb2acece4b9b39Dmitri Plotnikov        }
32762bab31374dd66462643cbc04ebb2acece4b9b39Dmitri Plotnikov
32862bab31374dd66462643cbc04ebb2acece4b9b39Dmitri Plotnikov        final View newField = createEditorView(values);
329eeb68a341734ded343004d4eecb585420e70605bDmitri Plotnikov        post(new Runnable() {
330eeb68a341734ded343004d4eecb585420e70605bDmitri Plotnikov
331eeb68a341734ded343004d4eecb585420e70605bDmitri Plotnikov            @Override
332eeb68a341734ded343004d4eecb585420e70605bDmitri Plotnikov            public void run() {
333eeb68a341734ded343004d4eecb585420e70605bDmitri Plotnikov                newField.requestFocus();
334eeb68a341734ded343004d4eecb585420e70605bDmitri Plotnikov            }
335eeb68a341734ded343004d4eecb585420e70605bDmitri Plotnikov        });
3364b722a93f1ecdeb2890fb7e5db4c22794bccfdcdMakoto Onuki
3371b22073590c1801c3e6c7bde2f4636632a049f6cKatherine Kuan        // Hide the "add field" footer because there is now a blank field.
3381b22073590c1801c3e6c7bde2f4636632a049f6cKatherine Kuan        mAddFieldFooter.setVisibility(View.GONE);
339eeac4dc8dccb20466669525f16dc58fa593a3483Daniel Lehmann
340634ab3baaf2bc69bd7977153fcdebf0e737e207dDaniel Lehmann        // Ensure we are visible
3411b22073590c1801c3e6c7bde2f4636632a049f6cKatherine Kuan        updateSectionVisible();
342bb556954e29ae0700b729f128d8cd09ecdd91ee3Daniel Lehmann    }
343bb556954e29ae0700b729f128d8cd09ecdd91ee3Daniel Lehmann
344bb556954e29ae0700b729f128d8cd09ecdd91ee3Daniel Lehmann    public int getEditorCount() {
345bb556954e29ae0700b729f128d8cd09ecdd91ee3Daniel Lehmann        return mEditors.getChildCount();
346bb556954e29ae0700b729f128d8cd09ecdd91ee3Daniel Lehmann    }
347bb556954e29ae0700b729f128d8cd09ecdd91ee3Daniel Lehmann
348bb556954e29ae0700b729f128d8cd09ecdd91ee3Daniel Lehmann    public DataKind getKind() {
349bb556954e29ae0700b729f128d8cd09ecdd91ee3Daniel Lehmann        return mKind;
350aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey    }
351aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey}
352