1 /* 2 * Copyright (C) 2009 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.contacts.ui.widget; 18 19 import com.android.contacts.R; 20 import com.android.contacts.model.Editor; 21 import com.android.contacts.model.EntityDelta; 22 import com.android.contacts.model.EntityModifier; 23 import com.android.contacts.model.ContactsSource.DataKind; 24 import com.android.contacts.model.Editor.EditorListener; 25 import com.android.contacts.model.EntityDelta.ValuesDelta; 26 import com.android.contacts.ui.ViewIdGenerator; 27 28 import android.content.Context; 29 import android.provider.ContactsContract.Data; 30 import android.util.AttributeSet; 31 import android.view.LayoutInflater; 32 import android.view.View; 33 import android.view.ViewGroup; 34 import android.view.View.OnClickListener; 35 import android.widget.ImageView; 36 import android.widget.LinearLayout; 37 import android.widget.TextView; 38 39 /** 40 * Custom view for an entire section of data as segmented by 41 * {@link DataKind} around a {@link Data#MIMETYPE}. This view shows a 42 * section header and a trigger for adding new {@link Data} rows. 43 */ 44 public class KindSectionView extends LinearLayout implements OnClickListener, EditorListener { 45 private static final String TAG = "KindSectionView"; 46 47 private LayoutInflater mInflater; 48 49 private ViewGroup mEditors; 50 private View mAdd; 51 private ImageView mAddPlusButton; 52 private TextView mTitle; 53 54 private DataKind mKind; 55 private EntityDelta mState; 56 private boolean mReadOnly; 57 58 private ViewIdGenerator mViewIdGenerator; 59 60 public KindSectionView(Context context) { 61 super(context); 62 } 63 64 public KindSectionView(Context context, AttributeSet attrs) { 65 super(context, attrs); 66 } 67 68 /** {@inheritDoc} */ 69 @Override 70 protected void onFinishInflate() { 71 mInflater = (LayoutInflater)getContext().getSystemService( 72 Context.LAYOUT_INFLATER_SERVICE); 73 74 setDrawingCacheEnabled(true); 75 setAlwaysDrawnWithCacheEnabled(true); 76 77 mEditors = (ViewGroup)findViewById(R.id.kind_editors); 78 79 mAdd = findViewById(R.id.kind_header); 80 mAdd.setOnClickListener(this); 81 82 mAddPlusButton = (ImageView) findViewById(R.id.kind_plus); 83 84 mTitle = (TextView)findViewById(R.id.kind_title); 85 } 86 87 /** {@inheritDoc} */ 88 public void onDeleted(Editor editor) { 89 this.updateAddEnabled(); 90 this.updateEditorsVisible(); 91 } 92 93 /** {@inheritDoc} */ 94 public void onRequest(int request) { 95 // Ignore requests 96 } 97 98 public void setState(DataKind kind, EntityDelta state, boolean readOnly, ViewIdGenerator vig) { 99 mKind = kind; 100 mState = state; 101 mReadOnly = readOnly; 102 mViewIdGenerator = vig; 103 104 setId(mViewIdGenerator.getId(state, kind, null, ViewIdGenerator.NO_VIEW_INDEX)); 105 106 // TODO: handle resources from remote packages 107 mTitle.setText(kind.titleRes); 108 109 // Only show the add button if this is a list 110 mAddPlusButton.setVisibility(mKind.isList ? View.VISIBLE : View.GONE); 111 112 this.rebuildFromState(); 113 this.updateAddEnabled(); 114 this.updateEditorsVisible(); 115 } 116 117 public boolean isAnyEditorFilledOut() { 118 if (mState == null) { 119 return false; 120 } 121 122 if (!mState.hasMimeEntries(mKind.mimeType)) { 123 return false; 124 } 125 126 int editorCount = mEditors.getChildCount(); 127 for (int i = 0; i < editorCount; i++) { 128 GenericEditorView editorView = (GenericEditorView) mEditors.getChildAt(i); 129 if (editorView.isAnyFieldFilledOut()) { 130 return true; 131 } 132 } 133 134 return false; 135 } 136 137 /** 138 * Build editors for all current {@link #mState} rows. 139 */ 140 public void rebuildFromState() { 141 // Remove any existing editors 142 mEditors.removeAllViews(); 143 144 // Check if we are displaying anything here 145 boolean hasEntries = mState.hasMimeEntries(mKind.mimeType); 146 147 if (!mKind.isList) { 148 if (hasEntries) { 149 // we might have no visible entries. check that, too 150 for (ValuesDelta entry : mState.getMimeEntries(mKind.mimeType)) { 151 if (!entry.isVisible()) { 152 hasEntries = false; 153 break; 154 } 155 } 156 } 157 158 if (!hasEntries) { 159 EntityModifier.insertChild(mState, mKind); 160 hasEntries = true; 161 } 162 } 163 164 if (hasEntries) { 165 int entryIndex = 0; 166 for (ValuesDelta entry : mState.getMimeEntries(mKind.mimeType)) { 167 // Skip entries that aren't visible 168 if (!entry.isVisible()) continue; 169 170 final GenericEditorView editor = (GenericEditorView)mInflater.inflate( 171 R.layout.item_generic_editor, mEditors, false); 172 editor.setValues(mKind, entry, mState, mReadOnly, mViewIdGenerator); 173 // older versions of android had lists where we now have a single value 174 // in these cases we should show the remove button for all but the first value 175 // to ensure that nothing is removed 176 editor.mDelete.setVisibility((mKind.isList || (entryIndex != 0)) 177 ? View.VISIBLE : View.GONE); 178 editor.setEditorListener(this); 179 mEditors.addView(editor); 180 entryIndex++; 181 } 182 } 183 } 184 185 protected void updateEditorsVisible() { 186 final boolean hasChildren = mEditors.getChildCount() > 0; 187 mEditors.setVisibility(hasChildren ? View.VISIBLE : View.GONE); 188 } 189 190 protected void updateAddEnabled() { 191 // Set enabled state on the "add" view 192 final boolean canInsert = EntityModifier.canInsert(mState, mKind); 193 final boolean isEnabled = !mReadOnly && canInsert; 194 mAdd.setEnabled(isEnabled); 195 } 196 197 /** {@inheritDoc} */ 198 public void onClick(View v) { 199 // if this is not a list the plus button is not visible but the user might have clicked 200 // the text. 201 if (!mKind.isList) 202 return; 203 204 // Insert a new child and rebuild 205 final ValuesDelta newValues = EntityModifier.insertChild(mState, mKind); 206 this.rebuildFromState(); 207 this.updateAddEnabled(); 208 this.updateEditorsVisible(); 209 210 // Find the newly added EditView and set focus. 211 final int newFieldId = mViewIdGenerator.getId(mState, mKind, newValues, 0); 212 final View newField = findViewById(newFieldId); 213 if (newField != null) { 214 newField.requestFocus(); 215 } 216 } 217 } 218