Search code examples
androidsqlitevalidationandroid-contentprovider

Android - adding validation to input fields


I am making an Android app that uses a sqlite database to store the app data, an activity class to edit / insert data, a DbHelper to manage the database creation and version, and a content provider which manages access to the database.

I have managed to get data to be queried, inserted, deleted and edited from the database. However, I'm not too sure on how to add visual feedback to the user. I've tried adding a Toast in place where I have put an IllegalArgumentException but the app will simply add the content to the database.

If I omit the Name, the app will trigger the defined IllegalArgumentException and then crash the application.

This is a snippet for the insert method in the content provider

@Override
public Uri insert(Uri uri, ContentValues contentValues) {
    final int match = sUriMatcher.match(uri);
    switch (match) {
        case PATIENT:
            return insertPatient(uri, contentValues);
        default:
            throw new IllegalArgumentException("Insertion is not supported for " + uri);
    }
}

/**
 * Insert a patient into the database with the given content values.
 */
private Uri insertPatient(Uri uri, ContentValues values) {
    String name = values.getAsString(PatientEntry.COLUMN_PATIENT_NAME);
    if (name == null || name.length()==0) {
        //Toast.makeText(getContext(), "Patient requires a name", Toast.LENGTH_SHORT).show();
        throw new IllegalArgumentException("Patient requires a name");
    }

    Integer weight = values.getAsInteger(PatientEntry.COLUMN_PATIENT_WEIGHT);
    if (weight != null && weight < 0) {
        throw new IllegalArgumentException("Patient requires valid weight");
    }

    SQLiteDatabase database = mDbHelper.getWritableDatabase();

    long id = database.insert(PatientEntry.TABLE_NAME, null, values);
    if (id == -1) {
        Log.e(LOG_TAG, "Failed to insert row for " + uri);
        return null;
    }

    getContext().getContentResolver().notifyChange(uri, null);
    return ContentUris.withAppendedId(uri, id);
}

This is a snippet from the activity file

private void savePatient() {
    String nameString = mNameEditText.getText().toString().trim();
    String weightString = mWeightEditText.getText().toString().trim();

    if (mCurrentPatientUri == null &&
            TextUtils.isEmpty(nameString) && TextUtils.isEmpty(weightString) {
            Toast.makeText(this, "Data was not saved", Toast.LENGTH_SHORT).show();
        return;
    }

    ContentValues values = new ContentValues();
    values.put(PatientEntry.COLUMN_PATIENT_NAME, nameString);
    int weight = 0;
    if (!TextUtils.isEmpty(weightString)) {
        weight = Integer.parseInt(weightString);
    }
    values.put(PatientEntry.COLUMN_PATIENT_WEIGHT, weight);

    // Determine if this is a new or existing Patient by checking if mCurrentPatientUri is null or not
    if (mCurrentPatientUri == null) {
        // This is a NEW patient
        Uri newUri = getContentResolver().insert(PatientEntry.CONTENT_URI, values);

        if (newUri == null) {
            Toast.makeText(this, getString(R.string.editor_insert_patient_failed),
                    Toast.LENGTH_SHORT).show();
        } else {
            Toast.makeText(this, getString(R.string.editor_insert_patient_successful),
                    Toast.LENGTH_SHORT).show();
        }
    } else {
        // Otherwise this is an EXISTING patient
        int rowsAffected = getContentResolver().update(mCurrentPatientUri, values, null, null);

        if (rowsAffected == 0) {
            Toast.makeText(this, getString(R.string.editor_update_patient_failed),
                    Toast.LENGTH_SHORT).show();
        } else {
            Toast.makeText(this, getString(R.string.editor_update_patient_successful),
                    Toast.LENGTH_SHORT).show();
        }
    }
}

Could someone kindly lend a hand?

.................................................................

EDIT 1:

This is the menu in the activity which calls savePatient:

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    // User clicked on a menu option in the app bar overflow menu
    switch (item.getItemId()) {
        case R.id.action_save:
            savePatient();
            finish();
            return true;
        case R.id.action_delete:
            showDeleteConfirmationDialog();
            return true;
        case android.R.id.home:
            if (!mPatientHasChanged) {
                NavUtils.navigateUpFromSameTask(EditorActivity.this);
                return true;
            }

            // Otherwise if there are unsaved changes, setup a dialog to warn the user.
            DialogInterface.OnClickListener discardButtonClickListener =
                    new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialogInterface, int i) {
                            NavUtils.navigateUpFromSameTask(EditorActivity.this);
                        }
                    };

            showUnsavedChangesDialog(discardButtonClickListener);
            return true;
    }
    return super.onOptionsItemSelected(item);
}

Solution

  • For visual feedback validations are to be done as early as possible. For example before you call save patient on some user action just call validatePatient() which if fails save will not be called.

    For Visual feedback you can have error texts below your fields that will become visible only if validation related to that field fails.