it.imwatch.nfclottery.dialogs.InsertContactDialog.java Source code

Java tutorial

Introduction

Here is the source code for it.imwatch.nfclottery.dialogs.InsertContactDialog.java

Source

/*
 * Copyright 2013 i'm SpA
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package it.imwatch.nfclottery.dialogs;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.os.Bundle;
import android.support.v4.app.DialogFragment;
import android.text.Editable;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.util.Log;
import android.util.Patterns;
import android.view.LayoutInflater;
import android.view.View;
import android.view.WindowManager;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.view.animation.AnimationSet;
import android.view.animation.TranslateAnimation;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
import de.keyboardsurfer.android.widget.crouton.Style;
import it.imwatch.nfclottery.DataHelper;
import it.imwatch.nfclottery.MainActivity;
import it.imwatch.nfclottery.R;

import java.util.ArrayList;
import java.util.regex.Pattern;

import static it.imwatch.nfclottery.Const.DEBUG;

/**
 * A dialog that allows the user to manually add a contact to the database.
 */
public class InsertContactDialog extends DialogFragment {

    private static final String TAG = InsertContactDialog.class.getSimpleName();
    private static final Pattern NAME_PATTERN = Pattern.compile("^\\p{L}+\\s+(?:\\p{L}+\\s*)+",
            Pattern.CASE_INSENSITIVE);

    private static final String EXTRA_NAME_ERROR = "name_error", EXTRA_EMAIL_ERROR = "email_error";
    private float mErrorAnimTranslateY;

    private TextView mEmailErrorTextView, mNameErrorTextView;
    private int mNameErrorState = 1, mEmailErrorState = 1;

    private View.OnFocusChangeListener mFocusWatcher = new View.OnFocusChangeListener() {
        @Override
        public void onFocusChange(View v, boolean hasFocus) {
            if (!hasFocus) {
                final CharSequence text = ((TextView) v).getText();
                final Context context = v.getContext();

                if (v.getId() == R.id.txt_edit_email) {
                    validateEmailInput(text, context);
                } else if (v.getId() == R.id.txt_edit_name) {
                    validateNameInput(text);
                }
            }
        }
    };

    private TextWatcher mEmailTypingWatcher = new TextWatcher() {
        @Override
        public void beforeTextChanged(CharSequence s, int start, int count, int after) {
        }

        @Override
        public void onTextChanged(CharSequence s, int start, int before, int count) {
        }

        @Override
        public void afterTextChanged(Editable s) {
            final CharSequence text = s.toString();
            final Context context = InsertContactDialog.this.getActivity();
            if (context != null) {
                validateEmailInput(text, context);
            }
        }
    };

    private EditText mEmailEditText, mNameEditText, mOrganizationEditText, mTitleEditText;;

    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        // Use the Builder class for convenient dialog construction
        final Activity activity = getActivity();
        if (activity == null) {
            Log.e(TAG, "Not attached to Activity: cannot build dialog");
            return null;
        }

        AlertDialog.Builder builder = new AlertDialog.Builder(activity);

        // Get the layout inflater
        LayoutInflater inflater = LayoutInflater.from(activity);
        final View rootView = inflater.inflate(R.layout.dialog_insert, null);
        if (rootView == null) {
            Log.e(TAG, "Cannot inflate the dialog layout!");
            return null;
        }

        mErrorAnimTranslateY = getResources().getDimensionPixelSize(R.dimen.error_anim_translate_y);

        mEmailErrorTextView = (TextView) rootView.findViewById(R.id.lbl_email_error);
        mNameErrorTextView = (TextView) rootView.findViewById(R.id.lbl_name_error);

        // Restore instance state (if any)
        if (savedInstanceState != null) {
            mNameErrorState = savedInstanceState.getInt(EXTRA_NAME_ERROR, 0);
            mEmailErrorState = savedInstanceState.getInt(EXTRA_EMAIL_ERROR, 0);

            if (mNameErrorState == 1)
                showNameError();
            if (mEmailErrorState == 1) {
                showEmailError(activity.getString(R.string.error_emailinput_invalid), 1);
            } else if (mEmailErrorState == 2) {
                showEmailError(activity.getString(R.string.error_emailinput_duplicate), 2);
            }
        }

        mEmailEditText = (EditText) rootView.findViewById(R.id.txt_edit_email);
        mNameEditText = (EditText) rootView.findViewById(R.id.txt_edit_name);
        mOrganizationEditText = (EditText) rootView.findViewById(R.id.txt_edit_organization);
        mTitleEditText = (EditText) rootView.findViewById(R.id.txt_edit_title);

        // Add the check for a valid email address and names
        mEmailEditText.setOnFocusChangeListener(mFocusWatcher);
        mNameEditText.setOnFocusChangeListener(mFocusWatcher);

        // Add the check for a valid email during typing
        mEmailEditText.addTextChangedListener(mEmailTypingWatcher);

        // Inflate and set the layout for the dialog
        // Pass null as the parent view because its going in the dialog layout
        builder.setView(rootView).setPositiveButton(android.R.string.ok, null)
                .setNegativeButton(android.R.string.no, new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int id) {
                        final Dialog thisDialog = InsertContactDialog.this.getDialog();
                        if (thisDialog != null) {
                            thisDialog.cancel();
                        } else {
                            Log.w(TAG, "Can't get the Dialog instance.");
                        }
                    }
                });

        // Create the AlertDialog object and return it
        AlertDialog alertDialog = builder.create();

        alertDialog.setOnShowListener(new DialogInterface.OnShowListener() {
            @Override
            public void onShow(DialogInterface dialog) {
                final AlertDialog alertDialog = (AlertDialog) dialog;

                // Disable the positive button. It will be enabled only when there is a valid email
                final Button button = alertDialog.getButton(AlertDialog.BUTTON_POSITIVE);
                if (button != null) {
                    button.setEnabled(false);
                    button.setOnClickListener(new DontAutoCloseDialogListener(alertDialog));
                } else {
                    Log.w(TAG, "Can't get the dialog positive button.");
                }

                alertDialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
            }
        });

        return alertDialog;
    }

    @Override
    public void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);

        outState.putInt(EXTRA_NAME_ERROR, mNameErrorState);
        outState.putInt(EXTRA_EMAIL_ERROR, mEmailErrorState);
    }

    /**
     * Check the data inserted into the form and it all the required fields are present,
     * then it adds the contact into DB
     */
    private boolean checkAndInsert() {
        final Activity activity = getActivity();
        if (activity == null) {
            Log.e(TAG, "Not attached to Activity: cannot insert contact in DB");
            return true; // We can close, there's nothing to do anyway
        }
        String email = null;
        ArrayList<String> name = new ArrayList<String>(1), organization = new ArrayList<String>(1),
                title = new ArrayList<String>(1);

        if (mNameEditText != null && mNameEditText.getText() != null) {
            name.add(mNameEditText.getText().toString());
        }

        if (mEmailEditText != null && mEmailEditText.getText() != null) {
            email = mEmailEditText.getText().toString();
        }

        if (mOrganizationEditText != null && mOrganizationEditText.getText() != null
                && TextUtils.isGraphic(mOrganizationEditText.getText())) {

            organization.add(mOrganizationEditText.getText().toString());
        }

        if (mTitleEditText != null && mTitleEditText.getText() != null
                && TextUtils.isGraphic(mTitleEditText.getText())) {
            title.add(mTitleEditText.getText().toString());
        }

        if (activity instanceof MainActivity) {
            final MainActivity mainActivity = (MainActivity) activity;

            if (!isValidEmailAddress(email)) {
                // Empty or invalid email field!
                showEmailError(activity.getString(R.string.error_emailinput_invalid), 1);
                return false;
            }

            if (DataHelper.isEmailAlreadyPresent(mainActivity, email)) {
                // We add each email only once
                showEmailError(activity.getString(R.string.error_emailinput_duplicate), 2);
                return false;
            }

            if (DataHelper.insertContact(mainActivity, name, email, organization, title)) {
                mainActivity.showCroutonNao(activity.getString(R.string.new_contact_added, email), Style.CONFIRM);
                mainActivity.updateParticipantsCount();

                return true;
            }

            return false;
        } else {
            Log.e(TAG, "The parent Activity is not MainActivity! Wat is this I don't even");
            if (DEBUG)
                Log.d(TAG, "Activity class: " + activity.getLocalClassName());
            Toast.makeText(activity, activity.getString(R.string.insert_failed_wrong_parent), Toast.LENGTH_SHORT)
                    .show();
            return true; // We can close, there's nothing to do anyway
        }
    }

    /**
     * Validates a string as an email address.
     *
     * @param email The email address to validate
     *
     * @return Returns true if the string is a valid email address,
     * false otherwise
     */
    private static boolean isValidEmailAddress(CharSequence email) {
        return email != null && Patterns.EMAIL_ADDRESS.matcher(email.toString()).matches();
    }

    /**
     * Validates a string as a name.
     *
     * @param name The name to validate
     *
     * @return Returns true if the string is a valid name,
     * false otherwise
     */
    private static boolean isValidName(CharSequence name) {
        return name != null && NAME_PATTERN.matcher(name.toString()).matches();
    }

    /**
     * Sets the validation state of the form.
     *
     * @param validated When true, the confirmation button will be enabled;
     *                  when false, it will be disabled.
     */
    private void setFormIsValidated(boolean validated) {
        final AlertDialog dialog = (AlertDialog) getDialog();
        if (dialog != null) {
            final Button button = dialog.getButton(DialogInterface.BUTTON_POSITIVE);

            if (button != null) {
                button.setEnabled(validated);
            }
        }
    }

    /**
     * Validates the provided text as an email input from the user.
     *
     * @param text    The text to validate as email
     * @param context The current context
     */
    private void validateEmailInput(CharSequence text, Context context) {
        if (context != null) {
            if (!isValidEmailAddress(text)) {
                showEmailError(context.getString(R.string.error_emailinput_invalid), 1);
            } else if (DataHelper.isEmailAlreadyPresent(context, text.toString())) {
                showEmailError(context.getString(R.string.error_emailinput_duplicate), 2);
            } else {
                hideEmailError();
            }
        } else {
            hideEmailError();
        }
    }

    /**
     * Validates the provided text as a name input from the user.
     *
     * @param text The text to validate as name
     */
    private void validateNameInput(CharSequence text) {
        if (!isValidName(text)) {
            showNameError();
        } else {
            hideNameError();
        }
    }

    /**
     * Hides the email error textview.
     */
    private void hideEmailError() {
        mEmailErrorState = 0;

        // Re-enable the positive button of the dialog iif the name is also valid
        setFormIsValidated(mNameErrorState == 0);

        if (mEmailErrorTextView.getVisibility() == View.GONE) {
            return; // No need to animate out the textview, it's already gone
        }

        AnimationSet fadeOutSet = new AnimationSet(true);
        fadeOutSet.addAnimation(new AlphaAnimation(1f, 0f));
        fadeOutSet.addAnimation(new TranslateAnimation(0f, 0f, 0f, -mErrorAnimTranslateY));
        fadeOutSet.setDuration(300);
        fadeOutSet.setAnimationListener(new Animation.AnimationListener() {
            @Override
            public void onAnimationStart(Animation animation) {
                // Don't care
            }

            @Override
            public void onAnimationEnd(Animation animation) {
                mEmailErrorTextView.setVisibility(View.GONE);
            }

            @Override
            public void onAnimationRepeat(Animation animation) {
                // Don't care
            }
        });

        mEmailErrorTextView.startAnimation(fadeOutSet);
    }

    /**
     * Hides the name error textview.
     */
    private void hideNameError() {
        mNameErrorState = 0;

        // Re-enable the positive button of the dialog iif the name is also valid
        setFormIsValidated(mEmailErrorState == 0);

        if (mNameErrorTextView.getVisibility() == View.GONE) {
            return; // No need to animate out the textview, it's already gone
        }

        AnimationSet fadeOutSet = new AnimationSet(true);
        fadeOutSet.addAnimation(new AlphaAnimation(1f, 0f));
        fadeOutSet.addAnimation(new TranslateAnimation(0f, 0f, 0f, -mErrorAnimTranslateY));
        fadeOutSet.setDuration(300);
        fadeOutSet.setAnimationListener(new Animation.AnimationListener() {
            @Override
            public void onAnimationStart(Animation animation) {
                // Don't care
            }

            @Override
            public void onAnimationEnd(Animation animation) {
                mNameErrorTextView.setVisibility(View.GONE);
            }

            @Override
            public void onAnimationRepeat(Animation animation) {
                // Don't care
            }
        });

        mNameErrorTextView.startAnimation(fadeOutSet);
    }

    /**
     * Shows the email error textfield with the appropriate
     * error message.
     *
     * @param message The error message to show
     */
    private void showEmailError(CharSequence message, int errState) {
        mEmailErrorTextView.setText(message);
        mEmailErrorState = errState;

        // Disable the positive button of the dialog
        setFormIsValidated(false);

        if (mEmailErrorTextView.getVisibility() == View.VISIBLE) {
            return; // No need to animate in the textview, it's already visible
        }

        mEmailErrorTextView.setVisibility(View.VISIBLE);

        AnimationSet fadeInSet = new AnimationSet(true);
        fadeInSet.addAnimation(new AlphaAnimation(0f, 1f));
        fadeInSet.addAnimation(new TranslateAnimation(0f, 0f, -mErrorAnimTranslateY, 0f));
        fadeInSet.setDuration(300);
        mEmailErrorTextView.startAnimation(fadeInSet);
    }

    /**
     * Shows the name error textfield with the appropriate
     * error message.
     */
    private void showNameError() {
        mNameErrorState = 1;

        // Disable the positive button of the dialog
        setFormIsValidated(false);

        if (mNameErrorTextView.getVisibility() == View.VISIBLE) {
            return; // No need to animate in the textview, it's already visible
        }

        mNameErrorTextView.setVisibility(View.VISIBLE);

        AnimationSet fadeInSet = new AnimationSet(true);
        fadeInSet.addAnimation(new AlphaAnimation(0f, 1f));
        fadeInSet.addAnimation(new TranslateAnimation(0f, 0f, -mErrorAnimTranslateY, 0f));
        fadeInSet.setDuration(300);
        mNameErrorTextView.startAnimation(fadeInSet);
    }

    private class DontAutoCloseDialogListener implements View.OnClickListener {

        private final AlertDialog mDialog;

        private DontAutoCloseDialogListener(AlertDialog dialog) {
            mDialog = dialog;
        }

        @Override
        public void onClick(View v) {
            validateEmailInput(mEmailEditText.getText(), mDialog.getContext());
            validateNameInput(mNameEditText.getText());

            final Button button = mDialog.getButton(AlertDialog.BUTTON_POSITIVE);
            if (button != null && button.isEnabled()) {
                // Only proceed if the form is validated
                if (checkAndInsert()) {
                    mDialog.dismiss();
                }
            }
        }
    }
}