public void testTrimInsertInsert() { 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); // Try creating a contact with single empty entry final EntityDelta state = getEntity(null); final ValuesDelta values = EntityModifier.insertChild(state, kindPhone, typeHome); final EntitySet set = EntitySet.fromSingle(state); // Build diff, expecting two insert operations final ArrayList<ContentProviderOperation> diff = Lists.newArrayList(); state.buildDiff(diff); assertEquals("Unexpected operations", 3, diff.size()); { final ContentProviderOperation oper = diff.get(0); assertEquals("Incorrect type", TYPE_INSERT, oper.getType()); assertEquals("Incorrect target", RawContacts.CONTENT_URI, oper.getUri()); } { final ContentProviderOperation oper = diff.get(1); assertEquals("Incorrect type", TYPE_INSERT, oper.getType()); assertEquals("Incorrect target", Data.CONTENT_URI, oper.getUri()); } // Trim empty rows and try again, expecting silence EntityModifier.trimEmpty(set, sources); diff.clear(); state.buildDiff(diff); assertEquals("Unexpected operations", 0, diff.size()); }
public void testParseExtrasIgnoreUnhandled() { final ContactsSource source = getSource(); final EntityDelta state = getEntity(TEST_ID); // We should silently ignore types unsupported by source final Bundle extras = new Bundle(); extras.putString(Insert.POSTAL, TEST_POSTAL); EntityModifier.parseExtras(mContext, source, state, extras); assertNull("Broke source rules", state.getMimeEntries(StructuredPostal.CONTENT_ITEM_TYPE)); }
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 testParseExtrasJobTitle() { final ContactsSource source = getSource(); final EntityDelta state = getEntity(TEST_ID); // Make sure that we create partial Organizations final Bundle extras = new Bundle(); extras.putString(Insert.JOB_TITLE, TEST_NAME); EntityModifier.parseExtras(mContext, source, state, extras); final int count = state.getMimeEntries(Organization.CONTENT_ITEM_TYPE).size(); assertEquals("Expected to create organization", 1, count); }
/** 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 testParseExtrasExistingName() { final ContactsSource source = getSource(); final DataKind kindName = source.getKindForMimetype(StructuredName.CONTENT_ITEM_TYPE); // Build "before" name final ContentValues first = new ContentValues(); first.put(Data._ID, TEST_ID); first.put(Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE); first.put(StructuredName.GIVEN_NAME, TEST_NAME); // Parse extras, making sure we keep single name final EntityDelta state = getEntity(TEST_ID, first); final Bundle extras = new Bundle(); extras.putString(Insert.NAME, TEST_NAME2); EntityModifier.parseExtras(mContext, source, state, extras); final int nameCount = state.getMimeEntriesCount(StructuredName.CONTENT_ITEM_TYPE, true); assertEquals("Unexpected names", 1, nameCount); }
public void testTrimEmptySingle() { 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); // Build diff, expecting insert for data row and update enforcement final ArrayList<ContentProviderOperation> diff = Lists.newArrayList(); 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_INSERT, 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()); } // Trim empty rows and try again, expecting delete of overall contact EntityModifier.trimEmpty(state, source); 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 testParseExtrasIgnoreLimit() { final ContactsSource source = getSource(); final DataKind kindIm = source.getKindForMimetype(Im.CONTENT_ITEM_TYPE); // Build "before" IM final ContentValues first = new ContentValues(); first.put(Data._ID, TEST_ID); first.put(Data.MIMETYPE, Im.CONTENT_ITEM_TYPE); first.put(Im.DATA, TEST_IM); final EntityDelta state = getEntity(TEST_ID, first); final int beforeCount = state.getMimeEntries(Im.CONTENT_ITEM_TYPE).size(); // We should ignore data that doesn't fit source rules, since source // only allows single Im final Bundle extras = new Bundle(); extras.putInt(Insert.IM_PROTOCOL, Im.PROTOCOL_GOOGLE_TALK); extras.putString(Insert.IM_HANDLE, TEST_IM); EntityModifier.parseExtras(mContext, source, state, extras); final int afterCount = state.getMimeEntries(Im.CONTENT_ITEM_TYPE).size(); assertEquals("Broke source rules", beforeCount, afterCount); }
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()); }
/** Build an {@link Entity} with the requested set of phone numbers. */ protected EntityDelta getEntity(Long existingId, ContentValues... entries) { final ContentValues contact = new ContentValues(); if (existingId != null) { contact.put(RawContacts._ID, existingId); } contact.put(RawContacts.ACCOUNT_NAME, TEST_ACCOUNT_NAME); contact.put(RawContacts.ACCOUNT_TYPE, TEST_ACCOUNT_TYPE); final Entity before = new Entity(contact); for (ContentValues values : entries) { before.addSubValue(Data.CONTENT_URI, values); } return EntityDelta.fromBefore(before); }
public boolean isAnyEditorFilledOut() { if (mState == null) { return false; } if (!mState.hasMimeEntries(mKind.mimeType)) { return false; } int editorCount = mEditors.getChildCount(); for (int i = 0; i < editorCount; i++) { GenericEditorView editorView = (GenericEditorView) mEditors.getChildAt(i); if (editorView.isAnyFieldFilledOut()) { return true; } } return false; }
/** * 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); } }