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