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));
  }
예제 #5
0
  /** 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);
    }
  }