com.activiti.android.ui.form.FormManager.java Source code

Java tutorial

Introduction

Here is the source code for com.activiti.android.ui.form.FormManager.java

Source

/*
 *  Copyright (C) 2005-2015 Alfresco Software Limited.
 *
 * This file is part of Alfresco Activiti Mobile for Android.
 *
 * Alfresco Activiti Mobile for Android is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Alfresco Activiti Mobile for Android is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
 *
 */

package com.activiti.android.ui.form;

import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import android.content.DialogInterface;
import android.support.design.widget.TabLayout;
import android.support.v4.app.FragmentActivity;
import android.text.Html;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;

import com.activiti.android.app.R;
import com.activiti.android.sdk.model.runtime.AppVersion;
import com.activiti.android.ui.form.fields.BaseField;
import com.activiti.android.ui.form.fields.TabField;
import com.activiti.android.ui.fragments.AlfrescoFragment;
import com.activiti.client.api.model.editor.form.ContainerRepresentation;
import com.activiti.client.api.model.editor.form.DynamicTableRepresentation;
import com.activiti.client.api.model.editor.form.FormDefinitionRepresentation;
import com.activiti.client.api.model.editor.form.FormFieldRepresentation;
import com.activiti.client.api.model.editor.form.FormFieldTypes;
import com.activiti.client.api.model.editor.form.FormOutcomeRepresentation;
import com.activiti.client.api.model.editor.form.FormTabRepresentation;
import com.activiti.client.api.model.editor.form.RestFieldRepresentation;
import com.afollestad.materialdialogs.MaterialDialog;

/**
 * Created by jpascal on 28/03/2015.
 */
public class FormManager {
    protected ViewGroup vRoot;

    protected AlfrescoFragment fr;

    protected WeakReference<FragmentActivity> activity;

    private Map<String, BaseField> fieldsIndex;

    private Map<String, ViewGroup> tabHookViewIndex;

    private LinkedHashMap<String, TabField> formTabFieldIndex;

    private Map<String, FormFieldRepresentation> formFieldIndex = new HashMap<>();

    private ArrayList<BaseField> fieldsOrderIndex = new ArrayList<>();

    private ArrayList<BaseField> mandatoryFields = new ArrayList<>(0);

    private Map<String, View> outcomeIndex;

    private BaseField currentPickerField;

    private FormDefinitionRepresentation data;

    private AppVersion version;

    private TabLayout tabLayout = null;

    // ///////////////////////////////////////////////////////////////////////////
    // CONSTRUCTORS & HELPERS
    // ///////////////////////////////////////////////////////////////////////////
    public FormManager(AlfrescoFragment fr, ViewGroup vRoot, FormDefinitionRepresentation data,
            AppVersion version) {
        this.fr = fr;
        this.vRoot = vRoot;
        this.activity = new WeakReference<>(fr.getActivity());
        this.data = data;
        this.version = version;
    }

    // ///////////////////////////////////////////////////////////////////////////
    // PUBLIC METHODS
    // ///////////////////////////////////////////////////////////////////////////
    public void prepare(FormDefinitionRepresentation data) {
        this.data = data;
        vRoot.removeAllViews();
        generateForm(LayoutInflater.from(getActivity()), false);
    }

    public void displayReadForm() {
        vRoot.removeAllViews();
        generateForm(LayoutInflater.from(getActivity()), false);
    }

    public void displayEditForm() {
        vRoot.removeAllViews();
        generateForm(LayoutInflater.from(getActivity()), true);
    }

    public Map<String, View> getOutComesView() {
        return (outcomeIndex == null) ? new HashMap<String, View>(0) : outcomeIndex;
    }

    public HashMap<String, Object> getValues() {
        HashMap<String, Object> props = new HashMap<>(fieldsIndex.size());
        for (Map.Entry<String, BaseField> entry : fieldsIndex.entrySet()) {
            if (entry.getValue().hasEditionValueChanged()) {
                props.put(entry.getKey(), entry.getValue().getOutputValue());
            }
        }
        return props;
    }

    // ///////////////////////////////////////////////////////////////////////////
    // UTILS
    // ///////////////////////////////////////////////////////////////////////////
    public boolean hasOutcome() {
        return data.getOutcomes() != null && data.getOutcomes().size() > 0;
    }

    public boolean checkValidation() {
        if (mandatoryFields.isEmpty()) {
            return true;
        }
        boolean isValid = true;
        boolean requiredFocus = false;
        for (BaseField field : mandatoryFields) {
            if (!field.isValid()) {
                field.showError();
                isValid = false;

                // Focus the first view that is invalid
                if (!requiredFocus) {
                    field.getEditionView().requestFocus();
                    requiredFocus = true;
                }
            }
        }
        return isValid;
    }

    public void setPropertyValue(String propertyId, Object object) {
        fieldsIndex.get(propertyId).setEditionValue(object);
    }

    public BaseField getField(String propertyId) {
        return fieldsIndex.get(propertyId);
    }

    // ///////////////////////////////////////////////////////////////////////////
    // GENERATOR
    // ///////////////////////////////////////////////////////////////////////////
    protected void generateForm(LayoutInflater li, boolean isEdition) {
        boolean hasTab = false;

        if (!version.is120OrAbove()) {
            generateForm11(li, isEdition);
            return;
        }

        ViewGroup rootView;

        // Generate Tabs
        if (data.getTabs() != null && !data.getTabs().isEmpty()) {
            rootView = (ViewGroup) li.inflate(R.layout.form_root_tabs, null);

            hasTab = true;
            tabLayout = (TabLayout) rootView.findViewById(R.id.task_form_tabs_container);
            tabLayout.setVisibility(View.VISIBLE);

            tabHookViewIndex = new HashMap<>(data.getTabs().size());
            formTabFieldIndex = new LinkedHashMap<>(data.getTabs().size());

            int i = 0;
            for (FormTabRepresentation tabRepresentation : data.getTabs()) {
                TabLayout.Tab tab = tabLayout.newTab().setText(tabRepresentation.getTitle())
                        .setTag(tabRepresentation.getId());
                tabLayout.addTab(tab);
                ViewGroup tabHookView = (ViewGroup) li.inflate(R.layout.form_tab_children, null);
                tabHookViewIndex.put(tabRepresentation.getId(), tabHookView);
                tabHookView.setVisibility(View.GONE);
                rootView.addView(tabHookView);

                formTabFieldIndex.put(tabRepresentation.getId(),
                        new TabField(tab, tabRepresentation, i, tabHookView));

                i++;
            }

            tabLayout.setOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
                @Override
                public void onTabSelected(TabLayout.Tab tab) {
                    formTabFieldIndex.get(tab.getTag()).getHookView().setVisibility(View.VISIBLE);
                }

                @Override
                public void onTabUnselected(TabLayout.Tab tab) {
                    formTabFieldIndex.get(tab.getTag()).getHookView().setVisibility(View.GONE);
                }

                @Override
                public void onTabReselected(TabLayout.Tab tab) {
                    formTabFieldIndex.get(tab.getTag()).getHookView().setVisibility(View.VISIBLE);
                }
            });

            tabLayout.getTabAt(0).select();

        } else {
            rootView = (ViewGroup) li.inflate(R.layout.form_root, null);
        }

        ViewGroup hookView = rootView;
        fieldsIndex = new HashMap<>(data.getFields().size());

        // Generate Fields
        ViewGroup groupRoot = null;
        BaseField field;
        for (FormFieldRepresentation fieldData : data.getFields()) {
            if (FormFieldTypes.GROUP.equals(fieldData.getType())) {
                if (groupRoot != null) {
                    rootView.addView(groupRoot);
                }
                // Header
                field = generateField(fieldData, rootView, isEdition);
                groupRoot = null;

                if (fieldData instanceof ContainerRepresentation) {
                    // Create groupView
                    if (groupRoot == null) {
                        groupRoot = (ViewGroup) li.inflate(R.layout.form_fields_group, null);
                    }

                    if (hasTab && fieldData.getTab() != null) {
                        hookView = (ViewGroup) tabHookViewIndex.get(fieldData.getTab())
                                .findViewById(R.id.tab_group_container);
                    } else {
                        hookView = (ViewGroup) groupRoot.findViewById(R.id.form_group_container);
                    }

                    Map<String, List<FormFieldRepresentation>> fields = ((ContainerRepresentation) fieldData)
                            .getFields();
                    for (Map.Entry<String, List<FormFieldRepresentation>> entry : fields.entrySet()) {
                        for (FormFieldRepresentation representation : entry.getValue()) {
                            formFieldIndex.put(representation.getId(), representation);
                            field = generateField(representation, hookView, isEdition);
                            fieldsOrderIndex.add(field);
                            fieldsIndex.put(representation.getId(), field);

                            // Mark All fields in edition mode
                            if (isEdition) {
                                // Mark required Field
                                if (representation.isRequired()) {
                                    mandatoryFields.add(field);
                                }

                                if (representation instanceof RestFieldRepresentation) {
                                    field.setFragment(fr);
                                }
                            }

                            // If requires fragment for pickers.
                            if (field.isPickerRequired()) {
                                field.setFragment(fr);
                            }
                        }
                    }
                }
            } else if (FormFieldTypes.CONTAINER.equals(fieldData.getType())) {
                // Create groupView
                if (groupRoot == null) {
                    groupRoot = (ViewGroup) li.inflate(R.layout.form_fields_group, null);
                }

                if (hasTab && fieldData.getTab() != null) {
                    hookView = (ViewGroup) tabHookViewIndex.get(fieldData.getTab())
                            .findViewById(R.id.tab_group_container);
                } else {
                    hookView = (ViewGroup) groupRoot.findViewById(R.id.form_group_container);
                }

                Map<String, List<FormFieldRepresentation>> fields = ((ContainerRepresentation) fieldData)
                        .getFields();
                for (Map.Entry<String, List<FormFieldRepresentation>> entry : fields.entrySet()) {
                    for (FormFieldRepresentation representation : entry.getValue()) {
                        field = generateField(representation, hookView, isEdition);
                        fieldsOrderIndex.add(field);
                        fieldsIndex.put(representation.getId(), field);

                        // Mark All fields in edition mode
                        if (isEdition) {
                            // Mark required Field
                            if (representation.isRequired()) {
                                mandatoryFields.add(field);
                            }

                            if (representation instanceof RestFieldRepresentation) {
                                field.setFragment(fr);
                            }
                        }

                        // If requires fragment for pickers.
                        if (field.isPickerRequired()) {
                            field.setFragment(fr);
                        }
                    }
                }
            } else if (FormFieldTypes.DYNAMIC_TABLE.equals(fieldData.getType())
                    || (FormFieldTypes.READONLY.equals(fieldData.getType())
                            && fieldData.getParams().get("field") != null
                            && (FormFieldTypes.DYNAMIC_TABLE.toString()
                                    .equals(((HashMap) fieldData.getParams().get("field")).get("type"))))) {
                // Create groupView
                if (groupRoot == null) {
                    groupRoot = (ViewGroup) li.inflate(R.layout.form_fields_group, null);
                }

                if (hasTab && fieldData.getTab() != null) {
                    hookView = (ViewGroup) tabHookViewIndex.get(fieldData.getTab())
                            .findViewById(R.id.tab_group_container);
                } else {
                    hookView = (ViewGroup) groupRoot.findViewById(R.id.form_group_container);
                }

                if (fieldData instanceof DynamicTableRepresentation) {
                    field = generateField(fieldData, hookView, isEdition);
                    fieldsOrderIndex.add(field);
                    continue;
                }
            }
        }

        // Now time to evaluate everyone
        evaluateViews();

        // Add Container to root ?
        if (groupRoot != null) {
            rootView.addView(groupRoot);
        }

        vRoot.addView(rootView);

        // OUTCOME
        if (!isEdition) {
            return;
        }
        View vr;
        if (data.getOutcomes() == null || data.getOutcomes().size() == 0) {
            outcomeIndex = new HashMap<>(1);
            vr = generateOutcome(rootView, getActivity().getString(R.string.form_default_outcome_complete), li);
            outcomeIndex.put(getActivity().getString(R.string.form_default_outcome_complete), vr);
        } else {
            outcomeIndex = new HashMap<>(data.getOutcomes().size());
            for (FormOutcomeRepresentation outcomeData : data.getOutcomes()) {
                vr = generateOutcome(rootView, outcomeData.getName(), li);
                outcomeIndex.put(outcomeData.getName(), vr);
            }
        }
    }

    /**
     * Works for 1.1 Form Definition
     *
     * @param li
     * @param isEdition
     */
    protected void generateForm11(LayoutInflater li, boolean isEdition) {
        ViewGroup rootView = (ViewGroup) li.inflate(R.layout.form_root, null);
        ViewGroup hookView = rootView;
        fieldsIndex = new HashMap<>(data.getFields().size());

        boolean createGroup = true;

        ViewGroup groupRoot = null;
        BaseField field;
        for (FormFieldRepresentation fieldData : data.getFields()) {
            if (FormFieldTypes.GROUP.equals(fieldData.getType())) {
                if (groupRoot != null) {
                    rootView.addView(groupRoot);
                }
                // Header
                field = generateField(fieldData, rootView, isEdition);
                createGroup = true;
                groupRoot = null;
            } else {
                if (createGroup) {
                    // Create groupView
                    groupRoot = (ViewGroup) li.inflate(R.layout.form_fields_group, null);
                    hookView = (ViewGroup) groupRoot.findViewById(R.id.form_group_container);
                    createGroup = false;
                }

                // Normal Field
                field = generateField(fieldData, hookView, isEdition);
            }

            fieldsOrderIndex.add(field);
            fieldsIndex.put(fieldData.getId(), field);

            // Mark All fields in edition mode
            if (isEdition) {
                // Mark required Field
                if (fieldData.isRequired()) {
                    mandatoryFields.add(field);
                }

                if (fieldData instanceof RestFieldRepresentation) {
                    field.setFragment(fr);
                }
            }

            // If requires fragment for pickers.
            if (field.isPickerRequired()) {
                field.setFragment(fr);
            }
        }

        // Now time to evaluate everyone
        evaluateViews();

        // Add Container to root ?
        if (groupRoot != null) {
            rootView.addView(groupRoot);
        }

        vRoot.addView(rootView);

        // OUTCOME
        if (!isEdition) {
            return;
        }
        View vr;
        if (data.getOutcomes() == null || data.getOutcomes().size() == 0) {
            outcomeIndex = new HashMap<>(1);
            vr = generateOutcome(rootView, getActivity().getString(R.string.form_default_outcome_complete), li);
            outcomeIndex.put(getActivity().getString(R.string.form_default_outcome_complete), vr);
        } else {
            outcomeIndex = new HashMap<>(data.getOutcomes().size());
            for (FormOutcomeRepresentation outcomeData : data.getOutcomes()) {
                vr = generateOutcome(rootView, outcomeData.getName(), li);
                outcomeIndex.put(outcomeData.getName(), vr);
            }
        }
    }

    private BaseField generateField(FormFieldRepresentation data, ViewGroup hookView, boolean isEdition) {
        boolean tmpIsEdition = isEdition;
        // First we prepare the field
        String dataType = (tmpIsEdition) ? data.getType() : data.getReadOnlyFieldType();

        // ReadOnly is a specific case
        // We don't use type but the type defined by params.
        // Works like a pointer to a specific field
        if (FormFieldTypes.READONLY.equals(dataType)) {
            dataType = data.getReadOnlyFieldType();
            tmpIsEdition = false;
        }

        BaseField field = FieldTypeFactory.createField(getActivity(), this, dataType, data, !tmpIsEdition);

        // Then we create the view
        View fieldView = (tmpIsEdition) ? field.setupEditionView(data.getValue()) : field.setupdReadView();

        // If a view has been generated we kept it.
        if (fieldView != null) {
            hookView.addView(fieldView);
        }

        return field;
    }

    private View generateOutcome(ViewGroup hookView, String name, LayoutInflater li) {
        View vr = li.inflate(R.layout.form_outcome, null);
        ((Button) vr.findViewById(R.id.outcome_button)).setText(name);
        hookView.addView(vr);
        return vr;
    }

    public void evaluateViews() {
        FormEvaluateExpression formEvaluateExpression = new FormEvaluateExpression(getValues(), formFieldIndex);

        for (BaseField field : fieldsOrderIndex) {
            if (field.getData().getVisibilityCondition() != null) {
                field.evaluateVisibility(
                        formEvaluateExpression.evaluateExpression(field.getData().getVisibilityCondition()));
            } else {
                field.evaluateVisibility();
            }
        }

        if (tabLayout == null) {
            return;
        }

        Map<String, TabLayout.Tab> tabLaIndex = new HashMap<>(tabLayout.getTabCount());

        for (int i = 0; i < tabLayout.getTabCount(); i++) {
            tabLaIndex.put((String) tabLayout.getTabAt(i).getTag(), tabLayout.getTabAt(i));
        }

        for (Map.Entry<String, TabField> tabField : formTabFieldIndex.entrySet()) {
            if (tabField.getValue().getData().getVisibilityCondition() != null) {
                TabField tabInfo = tabField.getValue();
                boolean isVisible = formEvaluateExpression
                        .evaluateExpression(tabInfo.getData().getVisibilityCondition());
                if (isVisible && !tabLaIndex.containsKey(tabInfo.getId())) {
                    tabLayout.addTab(tabInfo.getTab());
                } else if (!isVisible && tabLaIndex.containsKey(tabInfo.getId())) {
                    tabLayout.removeTabAt(tabLaIndex.get(tabInfo.getId()).getPosition());
                }
            }
        }
    }

    public void refreshViews() {
        for (BaseField field : fieldsOrderIndex) {
            field.refreshEditionView();
        }
    }

    /**
     * Unsupported + Required field
     */
    public void abort() {
        getActivity().getSupportFragmentManager().popBackStack();

        MaterialDialog.Builder builder = new MaterialDialog.Builder(getActivity())
                .title(R.string.form_message_unsupported_title)
                .cancelListener(new DialogInterface.OnCancelListener() {
                    @Override
                    public void onCancel(DialogInterface dialog) {
                        dialog.dismiss();
                    }
                }).content(Html.fromHtml(getActivity().getString(R.string.form_message_unsupported_description)))
                .positiveText(R.string.ok).callback(new MaterialDialog.ButtonCallback() {
                    @Override
                    public void onPositive(MaterialDialog dialog) {
                    }
                });
        builder.show();
    }

    // ///////////////////////////////////////////////////////////////////////////
    // UTILS
    // ///////////////////////////////////////////////////////////////////////////
    public int getVersionNumber() {
        return fr.getVersionNumber();
    }

    public FragmentActivity getActivity() {
        return activity.get();
    }

    public void setCurrentPickerField(BaseField field) {
        currentPickerField = field;
    }

    public BaseField getCurrentPickerField() {
        return currentPickerField;
    }

    public String getTaskId() {
        return data.getTaskId();
    }

}