com.github.chilinh.android.form.Form.java Source code

Java tutorial

Introduction

Here is the source code for com.github.chilinh.android.form.Form.java

Source

/*
 * MIT License
 *
 * Copyright (c) [2016] [linh]
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

package com.github.chilinh.android.form;

import android.content.Context;
import android.content.DialogInterface;
import android.content.res.Resources;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v7.app.AlertDialog;
import android.text.TextUtils;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.TextView;

import com.github.chilinh.android.form.element.BaseFormElement;
import com.github.chilinh.android.form.element.SectionElement;
import com.github.chilinh.android.form.validator.ElementErrorDisplay;
import com.github.chilinh.android.form.validator.ErrorDisplay;
import com.github.chilinh.android.form.validator.InputError;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Created by Linh on 11/3/16.
 */
public class Form {

    private static final String TAG = "Form";
    private static final boolean DEBUG = true;

    //region Builder
    public static class Builder {
        private ErrorDisplay errorDisplay;

        private CharSequence title;
        private int titleResId = View.NO_ID;

        private CharSequence submitBtn;
        private int submitResId = View.NO_ID;

        private CharSequence cancelBtn;
        private int cancelResId = View.NO_ID;

        public Builder errorDisplay(ErrorDisplay errorDisplay) {
            this.errorDisplay = errorDisplay;
            return this;
        }

        public Builder title(CharSequence title) {
            this.title = title;
            this.titleResId = View.NO_ID;
            return this;
        }

        public Builder title(int titleResId) {
            this.titleResId = titleResId;
            this.title = null;
            return this;
        }

        public Builder submitButton(CharSequence submitBtn) {
            this.submitBtn = submitBtn;
            this.submitResId = View.NO_ID;
            return this;
        }

        public Builder submitButton(int submitResId) {
            this.submitResId = submitResId;
            this.submitBtn = null;
            return this;
        }

        public Builder cancelButton(CharSequence cancelBtn) {
            this.cancelBtn = cancelBtn;
            this.cancelResId = View.NO_ID;
            return this;
        }

        public Builder cancelButton(int cancelResId) {
            this.cancelResId = cancelResId;
            this.cancelBtn = null;
            return this;
        }

        private final Map<String, SectionElement> mSectionMap = new HashMap<>();
        private final List<SectionElement> mSections = new ArrayList<>();

        public Builder addElement(SectionElement section, int position) {
            if (mSectionMap.containsKey(section.getName())) {
                throw new IllegalArgumentException("Section with that name already exists");
            }

            mSectionMap.put(section.getName(), section);
            mSections.add(position, section);
            return this;
        }

        public Builder addSection(SectionElement section) {
            addElement(section, mSections.size());
            return this;
        }

        public Builder addElement(BaseFormElement element) {
            if (mSections.isEmpty()) {
                addSection(new SectionElement());
            }
            SectionElement section = mSections.get(mSections.size() - 1);
            section.addElement(element);
            return this;
        }

        public static CharSequence getCharsequence(Resources res, int resId, CharSequence value) {
            return resId != View.NO_ID ? res.getText(resId) : value;
        }

        public Form build(Context context) {
            Form form = new Form(context.getApplicationContext(), mSectionMap, mSections, errorDisplay);
            Resources res = context.getResources();
            form.mTitle = getCharsequence(res, titleResId, title);
            form.mSubmitBtn = getCharsequence(res, submitResId, submitBtn);
            form.mCancelBtn = getCharsequence(res, cancelResId, cancelBtn);
            return form;
        }
    }

    private Form(Context context, Map<String, SectionElement> sectionMap, List<SectionElement> sections,
            ErrorDisplay errorDisplay) {
        mContext = context;
        mErrorDisplay = errorDisplay == null ? new ElementErrorDisplay(this) : null;
        mSectionMap = sectionMap;
        mSections = sections;
    }

    private final Context mContext;
    private final Map<String, SectionElement> mSectionMap;
    private final List<SectionElement> mSections;
    private final ErrorDisplay mErrorDisplay;

    private CharSequence mTitle;
    private CharSequence mSubmitBtn;
    private CharSequence mCancelBtn;

    public List<SectionElement> getSections() {
        return Collections.unmodifiableList(mSections);
    }

    public SectionElement getSection(String name) {
        return mSectionMap.get(name);
    }

    public SectionElement getLastSection() {
        return mSections.isEmpty() ? null : mSections.get(mSections.size() - 1);
    }

    public BaseFormElement getElement(String name) {
        for (SectionElement section : mSections) {
            BaseFormElement element = section.getElement(name);
            if (element != null) {
                return element;
            }
        }
        return null;
    }
    //endregion

    //region Validation
    private List<InputError> getErrors() {
        List<InputError> errors = new ArrayList<>();
        for (SectionElement section : mSections) {
            section.validate(errors);
        }
        return errors;
    }

    public void clearValidateError(Context context) {
        mLastestErrors = null;
        mErrorDisplay.clear(context);
    }

    private List<InputError> mLastestErrors;

    public boolean validate(Context context) {
        clearValidateError(context);
        mLastestErrors = getErrors();
        if (mLastestErrors != null && !mLastestErrors.isEmpty()) {
            mErrorDisplay.show(context, mLastestErrors);
            return false;
        }
        return true;
    }

    public List<InputError> getLastErrorList() {
        return mLastestErrors;
    }
    //endregion

    private void makeFormView(ViewGroup containerView) {
        containerView.removeAllViews();

        for (SectionElement section : getSections()) {
            section.setModel(mModel);
            section.makeView(containerView);
        }
    }

    private FormModel mModel;

    public FormModel getModel() {
        return mModel;
    }

    private void applyWindow(Window window) {
        window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE
                | WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN);
    }

    private static final String FORM_FRAGMENT_MODEL = "#gh_form_model";

    public void buildToFragment(Fragment fragment, final Callback.Submit<Fragment> submitCb) {
        View view = fragment.getView();
        FragmentManager fm = fragment.getActivity().getSupportFragmentManager();
        FormModelFragment model = (FormModelFragment) fm.findFragmentByTag(FORM_FRAGMENT_MODEL);
        if (model == null) {
            model = new FormModelFragment();
            fm.beginTransaction().add(model, FORM_FRAGMENT_MODEL).commit();
        }
        mModel = model;

        if (TextUtils.isEmpty(mSubmitBtn)) {
            mSubmitBtn = "Summit";
        }

        ViewGroup group = (ViewGroup) view.findViewById(R.id.form_elements_container);
        makeFormView(group);

        View submitBtn = view.findViewById(R.id.button_form_submit);
        if (submitBtn != null) {
            submitBtn.setClickable(true);
            submitBtn.setFocusable(true);

            submitBtn.setOnClickListener((v) -> {
                if (validate(mContext)) {
                    if (submitCb != null) {
                        if (submitCb.validate(fragment, Form.this)) {
                            submitCb.onSubmit(fragment, Form.this);
                        }
                        return;
                    }
                }
            });
        }
    }

    public AlertDialog buildDialog(Context context, final Callback.Submit<DialogInterface> submitCb,
            final Callback.Dismiss<DialogInterface> dismissCb) {
        mModel = new FormModelDialog();

        if (TextUtils.isEmpty(mSubmitBtn)) {
            mSubmitBtn = "Summit";
        }

        TextView title = new TextView(context);
        title.setText(mTitle);
        title.setPadding(10, 10, 10, 15);
        title.setGravity(Gravity.CENTER);
        title.setTextSize(22);

        View view = LayoutInflater.from(context).inflate(R.layout.base_form, null, false);
        ViewGroup group = (ViewGroup) view.findViewById(R.id.form_elements_container);
        makeFormView(group);
        AlertDialog.Builder builder = new AlertDialog.Builder(context).setCustomTitle(title).setView(view)
                .setPositiveButton(mSubmitBtn, null).setNegativeButton(mCancelBtn, null);

        final AlertDialog alertDialog = builder.create();
        applyWindow(alertDialog.getWindow());

        alertDialog.setOnShowListener(new DialogInterface.OnShowListener() {
            @Override
            public void onShow(final DialogInterface dialog) {
                Button positive = alertDialog.getButton(AlertDialog.BUTTON_POSITIVE);
                if (positive != null) {
                    positive.setOnClickListener(new View.OnClickListener() {
                        @Override
                        public void onClick(View v) {
                            if (validate(mContext)) {
                                if (submitCb != null) {
                                    if (submitCb.validate(dialog, Form.this)) {
                                        submitCb.onSubmit(dialog, Form.this);
                                    }
                                    return;
                                }

                                dialog.dismiss();
                            }
                        }
                    });
                }
            }
        });

        alertDialog.setOnDismissListener(new DialogInterface.OnDismissListener() {
            @Override
            public void onDismiss(DialogInterface dialog) {
                if (dismissCb != null) {
                    dismissCb.onDismiss(dialog, Form.this);
                }
            }
        });

        alertDialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
            @Override
            public void onCancel(DialogInterface dialog) {
                if (dismissCb != null) {
                    dismissCb.onCancel(dialog, Form.this);
                }
            }
        });
        return alertDialog;
    }
}