public void testTrimLeaveValid() { final ContactsSource source = getSource(); final DataKind kindPhone = source.getKindForMimetype(Phone.CONTENT_ITEM_TYPE); final EditType typeHome = EntityModifier.getType(kindPhone, Phone.TYPE_HOME); // Test row that has type values with valid number final EntityDelta state = EntitySetTests.buildBeforeEntity(TEST_ID, VER_FIRST); final ValuesDelta values = EntityModifier.insertChild(state, kindPhone, typeHome); values.put(Phone.NUMBER, TEST_PHONE); // Build diff, expecting insert for data row and update enforcement EntitySetTests.assertDiffPattern( state, EntitySetTests.buildAssertVersion(VER_FIRST), EntitySetTests.buildUpdateAggregationSuspended(), EntitySetTests.buildOper( Data.CONTENT_URI, TYPE_INSERT, EntitySetTests.buildDataInsert(values, TEST_ID)), EntitySetTests.buildUpdateAggregationDefault()); // Trim empty rows and try again, expecting no differences EntityModifier.trimEmpty(state, source); EntitySetTests.assertDiffPattern( state, EntitySetTests.buildAssertVersion(VER_FIRST), EntitySetTests.buildUpdateAggregationSuspended(), EntitySetTests.buildOper( Data.CONTENT_URI, TYPE_INSERT, EntitySetTests.buildDataInsert(values, TEST_ID)), EntitySetTests.buildUpdateAggregationDefault()); }
/** Find {@link RawContacts#_ID} of the requested {@link EntityDelta}. */ public Long getRawContactId(int index) { if (index >= 0 && index < this.size()) { final EntityDelta delta = this.get(index); final ValuesDelta values = delta.getValues(); if (values.isVisible()) { return values.getAsLong(RawContacts._ID); } } return null; }
public void testTrimUpdateUpdate() { final ContactsSource source = getSource(); final Sources sources = getSources(source); final DataKind kindPhone = source.getKindForMimetype(Phone.CONTENT_ITEM_TYPE); final EditType typeHome = EntityModifier.getType(kindPhone, Phone.TYPE_HOME); // Build "before" with two phone numbers final ContentValues first = new ContentValues(); first.put(Data._ID, TEST_ID); first.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE); first.put(kindPhone.typeColumn, typeHome.rawValue); first.put(Phone.NUMBER, TEST_PHONE); final EntityDelta state = getEntity(TEST_ID, first); final EntitySet set = EntitySet.fromSingle(state); // Build diff, expecting no changes final ArrayList<ContentProviderOperation> diff = Lists.newArrayList(); state.buildDiff(diff); assertEquals("Unexpected operations", 0, diff.size()); // Now update row by changing number to empty string, expecting single update final ValuesDelta child = state.getEntry(TEST_ID); child.put(Phone.NUMBER, ""); diff.clear(); state.buildDiff(diff); assertEquals("Unexpected operations", 3, diff.size()); { final ContentProviderOperation oper = diff.get(0); assertEquals("Expected aggregation mode change", TYPE_UPDATE, oper.getType()); assertEquals("Incorrect target", RawContacts.CONTENT_URI, oper.getUri()); } { final ContentProviderOperation oper = diff.get(1); assertEquals("Incorrect type", TYPE_UPDATE, oper.getType()); assertEquals("Incorrect target", Data.CONTENT_URI, oper.getUri()); } { final ContentProviderOperation oper = diff.get(2); assertEquals("Expected aggregation mode change", TYPE_UPDATE, oper.getType()); assertEquals("Incorrect target", RawContacts.CONTENT_URI, oper.getUri()); } // Now run trim, which should turn into deleting the whole contact EntityModifier.trimEmpty(set, sources); diff.clear(); state.buildDiff(diff); assertEquals("Unexpected operations", 1, diff.size()); { final ContentProviderOperation oper = diff.get(0); assertEquals("Incorrect type", TYPE_DELETE, oper.getType()); assertEquals("Incorrect target", RawContacts.CONTENT_URI, oper.getUri()); } }
public void testIsEmptyDirectFields() { final ContactsSource source = getSource(); final DataKind kindPhone = source.getKindForMimetype(Phone.CONTENT_ITEM_TYPE); final EditType typeHome = EntityModifier.getType(kindPhone, Phone.TYPE_HOME); // Test row that has type values, but core fields are empty final EntityDelta state = getEntity(TEST_ID); final ValuesDelta values = EntityModifier.insertChild(state, kindPhone, typeHome); assertTrue("Expected empty", EntityModifier.isEmpty(values, kindPhone)); // Insert some data to trigger non-empty state values.put(Phone.NUMBER, TEST_PHONE); assertFalse("Expected non-empty", EntityModifier.isEmpty(values, kindPhone)); }
/** Build editors for all current {@link #mState} rows. */ public void rebuildFromState() { // Remove any existing editors mEditors.removeAllViews(); // Check if we are displaying anything here boolean hasEntries = mState.hasMimeEntries(mKind.mimeType); if (!mKind.isList) { if (hasEntries) { // we might have no visible entries. check that, too for (ValuesDelta entry : mState.getMimeEntries(mKind.mimeType)) { if (!entry.isVisible()) { hasEntries = false; break; } } } if (!hasEntries) { EntityModifier.insertChild(mState, mKind); hasEntries = true; } } if (hasEntries) { int entryIndex = 0; for (ValuesDelta entry : mState.getMimeEntries(mKind.mimeType)) { // Skip entries that aren't visible if (!entry.isVisible()) continue; final GenericEditorView editor = (GenericEditorView) mInflater.inflate(R.layout.item_generic_editor, mEditors, false); editor.setValues(mKind, entry, mState, mReadOnly, mViewIdGenerator); // older versions of android had lists where we now have a single value // in these cases we should show the remove button for all but the first value // to ensure that nothing is removed editor.mDelete.setVisibility( (mKind.isList || (entryIndex != 0)) ? View.VISIBLE : View.GONE); editor.setEditorListener(this); mEditors.addView(editor); entryIndex++; } } }
public void testIsEmptyEmpty() { final ContactsSource source = getSource(); final DataKind kindPhone = source.getKindForMimetype(Phone.CONTENT_ITEM_TYPE); // Test entirely empty row final ContentValues after = new ContentValues(); final ValuesDelta values = ValuesDelta.fromAfter(after); assertTrue("Expected empty", EntityModifier.isEmpty(values, kindPhone)); }
public ValuesDelta getSuperPrimaryEntry(final String mimeType) { ValuesDelta primary = null; ValuesDelta randomEntry = null; for (EntityDelta delta : this) { final ArrayList<ValuesDelta> mimeEntries = delta.getMimeEntries(mimeType); if (mimeEntries == null) return null; for (ValuesDelta entry : mimeEntries) { if (entry.isSuperPrimary()) { return entry; } else if (primary == null && entry.isPrimary()) { primary = entry; } else if (randomEntry == null) { randomEntry = entry; } } } // When no direct super primary, return something if (primary != null) { return primary; } return randomEntry; }
public void testTrimEmptyUntouched() { final ContactsSource source = getSource(); final DataKind kindPhone = source.getKindForMimetype(Phone.CONTENT_ITEM_TYPE); final EditType typeHome = EntityModifier.getType(kindPhone, Phone.TYPE_HOME); // Build "before" that has empty row final EntityDelta state = getEntity(TEST_ID); final ContentValues before = new ContentValues(); before.put(Data._ID, TEST_ID); before.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE); state.addEntry(ValuesDelta.fromBefore(before)); // Build diff, expecting no changes final ArrayList<ContentProviderOperation> diff = Lists.newArrayList(); state.buildDiff(diff); assertEquals("Unexpected operations", 0, diff.size()); // Try trimming existing empty, which we shouldn't touch EntityModifier.trimEmpty(state, source); diff.clear(); state.buildDiff(diff); assertEquals("Unexpected operations", 0, diff.size()); }
@Override public void setValues( DataKind kind, ValuesDelta entry, EntityDelta state, boolean readOnly, ViewIdGenerator vig) { super.setValues(kind, entry, state, readOnly, vig); // Remove edit texts that we currently have if (mFieldEditTexts != null) { for (EditText fieldEditText : mFieldEditTexts) { mFields.removeView(fieldEditText); } mFieldEditTexts = null; } if (localGroupsSelectors != null) { for (View view : localGroupsSelectors) { mFields.removeView(view); } localGroupsSelectors = null; } boolean hidePossible = false; int fieldCount = kind.fieldList.size(); if (LocalGroup.CONTENT_ITEM_TYPE.equals(kind.mimeType)) localGroupsSelectors = new LocalGroupsSelector[fieldCount]; else mFieldEditTexts = new EditText[fieldCount]; for (int index = 0; index < fieldCount; index++) { final EditField field = kind.fieldList.get(index); if (LocalGroup.CONTENT_ITEM_TYPE.equals(kind.mimeType)) { final LocalGroupsSelector localGroupsSelector = new LocalGroupsSelector(mContext, entry, field.column); localGroupsSelector.setOnGroupSelectListener( new OnGroupSelectListener() { @Override public void onGroupChanged() { setDeleteButtonVisible(localGroupsSelector.getGroupId() > -1); } }); // Show the delete button if we have a non-null value setDeleteButtonVisible(localGroupsSelector.getGroupId() > -1); localGroupsSelectors[index] = localGroupsSelector; mFields.addView(localGroupsSelector); } else { final EditText fieldView = new EditText(mContext); /*Begin: Modified by bxinchun for change the layout 2012/07/24*/ /*Begin: Modified by xiepengfei for change the layout 2012/05/02*/ LinearLayout.LayoutParams params = new LinearLayout.LayoutParams( LayoutParams.MATCH_PARENT, field.isMultiLine() ? LayoutParams.WRAP_CONTENT : mMinFieldHeight); params.bottomMargin = 10; fieldView.setLayoutParams(params); /*End: Modified by xiepengfei for change the layout 2012/05/02*/ /*End: Modified by bxinchun for change the layout 2012/07/24*/ // Set either a minimum line requirement or a minimum height (because {@link TextView} // only takes one or the other at a single time). if (field.minLines != 0) { fieldView.setMinLines(field.minLines); } else { fieldView.setMinHeight(mMinFieldHeight); } fieldView.setTextAppearance(getContext(), android.R.style.TextAppearance_Medium); fieldView.setGravity(Gravity.TOP); /*Begin: Modified by xiepengfei for change the layout 2012/05/02*/ fieldView.setBackgroundResource(R.drawable.edit_editor_background); fieldView.setTextSize(20); fieldView.setTextColor(Color.BLACK); /*End: Modified by xiepengfei for change the layout 2012/05/02*/ mFieldEditTexts[index] = fieldView; fieldView.setId(vig.getId(state, kind, entry, index)); if (field.titleRes > 0) { fieldView.setHint(field.titleRes); } int inputType = field.inputType; fieldView.setInputType(inputType); if (inputType == InputType.TYPE_CLASS_PHONE) { PhoneNumberFormatter.setPhoneNumberFormattingTextWatcher(mContext, fieldView); } // Show the "next" button in IME to navigate between text fields // TODO: Still need to properly navigate to/from sections without text fields, // See Bug: 5713510 fieldView.setImeOptions(EditorInfo.IME_ACTION_NEXT); // Read current value from state final String column = field.column; final String value = entry.getAsString(column); fieldView.setText(value); // Show the delete button if we have a non-null value setDeleteButtonVisible(value != null); // Prepare listener for writing changes fieldView.addTextChangedListener( new TextWatcher() { @Override public void afterTextChanged(Editable s) { // Trigger event for newly changed value onFieldChanged(column, s.toString()); } @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) {} @Override public void onTextChanged(CharSequence s, int start, int before, int count) {} }); fieldView.setEnabled(isEnabled() && !readOnly); if (field.shortForm) { hidePossible = true; mHasShortAndLongForms = true; fieldView.setVisibility(mHideOptional ? View.VISIBLE : View.GONE); } else if (field.longForm) { hidePossible = true; mHasShortAndLongForms = true; fieldView.setVisibility(mHideOptional ? View.GONE : View.VISIBLE); } else { // Hide field when empty and optional value final boolean couldHide = (!ContactsUtils.isGraphic(value) && field.optional); final boolean willHide = (mHideOptional && couldHide); fieldView.setVisibility(willHide ? View.GONE : View.VISIBLE); hidePossible = hidePossible || couldHide; } mFields.addView(fieldView); } } // When hiding fields, place expandable setupExpansionView(hidePossible, mHideOptional); mExpansionView.setEnabled(!readOnly && isEnabled()); }
/** * Set the internal state for this view, given a current {@link EntityDelta} state and the {@link * ContactsSource} that apply to that state. * * <p>TODO: make this more generic using data from the source */ @Override public void setState(EntityDelta state, ContactsSource source, ViewIdGenerator vig) { // Remove any existing sections mGeneral.removeAllViews(); // Bail if invalid state or source if (state == null || source == null) return; // Make sure we have StructuredName EntityModifier.ensureKindExists(state, source, StructuredName.CONTENT_ITEM_TYPE); // Fill in the header info ValuesDelta values = state.getValues(); String accountName = values.getAsString(RawContacts.ACCOUNT_NAME); CharSequence accountType = source.getDisplayLabel(mContext); if (TextUtils.isEmpty(accountType)) { accountType = mContext.getString(R.string.account_phone); } if (!TextUtils.isEmpty(accountName)) { mHeaderAccountName.setText(mContext.getString(R.string.from_account_format, accountName)); } mHeaderAccountType.setText(mContext.getString(R.string.account_type_format, accountType)); mHeaderIcon.setImageDrawable(source.getDisplayIcon(mContext)); mRawContactId = values.getAsLong(RawContacts._ID); ValuesDelta primary; // Photo DataKind kind = source.getKindForMimetype(Photo.CONTENT_ITEM_TYPE); if (kind != null) { EntityModifier.ensureKindExists(state, source, Photo.CONTENT_ITEM_TYPE); mHasPhotoEditor = (source.getKindForMimetype(Photo.CONTENT_ITEM_TYPE) != null); primary = state.getPrimaryEntry(Photo.CONTENT_ITEM_TYPE); mPhoto.setValues(kind, primary, state, source.readOnly, vig); if (!mHasPhotoEditor || !mPhoto.hasSetPhoto()) { mPhotoStub.setVisibility(View.GONE); } else { mPhotoStub.setVisibility(View.VISIBLE); } } else { mPhotoStub.setVisibility(View.VISIBLE); } // Name primary = state.getPrimaryEntry(StructuredName.CONTENT_ITEM_TYPE); mName.setText(primary.getAsString(StructuredName.DISPLAY_NAME)); // Read only warning mReadOnlyWarning.setText(mContext.getString(R.string.contact_read_only, accountType)); // Phones ArrayList<ValuesDelta> phones = state.getMimeEntries(Phone.CONTENT_ITEM_TYPE); if (phones != null) { for (ValuesDelta phone : phones) { View field = mInflater.inflate(R.layout.item_read_only_field, mGeneral, false); TextView v; v = (TextView) field.findViewById(R.id.label); v.setText(mContext.getText(R.string.phoneLabelsGroup)); v = (TextView) field.findViewById(R.id.data); v.setText(PhoneNumberFormatUtilEx.formatNumber(phone.getAsString(Phone.NUMBER))); mGeneral.addView(field); } } // Emails ArrayList<ValuesDelta> emails = state.getMimeEntries(Email.CONTENT_ITEM_TYPE); if (emails != null) { for (ValuesDelta email : emails) { View field = mInflater.inflate(R.layout.item_read_only_field, mGeneral, false); TextView v; v = (TextView) field.findViewById(R.id.label); v.setText(mContext.getText(R.string.emailLabelsGroup)); v = (TextView) field.findViewById(R.id.data); v.setText(email.getAsString(Email.DATA)); mGeneral.addView(field); } } // Hide mGeneral if it's empty if (mGeneral.getChildCount() > 0) { mGeneral.setVisibility(View.VISIBLE); } else { mGeneral.setVisibility(View.GONE); } }