/* * Odoo, Open Source Management Solution * Copyright (C) 2012-today Odoo SA (<http:www.odoo.com>) * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version * * This program 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 Affero General Public License for more details * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http:www.gnu.org/licenses/> * */ package odoo.controls; import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import odoo.controls.OField.OFieldType; import android.app.ProgressDialog; import android.content.Context; import android.content.res.TypedArray; import android.os.AsyncTask; import android.util.AttributeSet; import android.view.View; import android.view.ViewGroup; import android.widget.LinearLayout; import android.widget.RelativeLayout; import com.odoo.R; import com.odoo.orm.OColumn; import com.odoo.orm.OColumn.ColumnDomain; import com.odoo.orm.OColumn.RelationType; import com.odoo.orm.ODataRow; import com.odoo.orm.OModel; import com.odoo.orm.OValues; import com.odoo.orm.types.OBlob; import com.odoo.orm.types.OBoolean; import com.odoo.orm.types.OHtml; /** * The Class OForm. */ public class OForm extends LinearLayout implements View.OnClickListener { /** The Constant KEY_BACKGROUND_SELECTOR. */ public static final String KEY_BACKGROUND_SELECTOR = "background_selector"; /** The Constant KEY_BACKGROUND_SELECTOR_BOOLEAN_FIELD. */ public static final String KEY_BACKGROUND_SELECTOR_BOOLEAN_FIELD = "background_selector_boolean_field"; /** The Constant KEY_TRUE_BACKGROUND_SELECTOR. */ public static final String KEY_TRUE_BACKGROUND_SELECTOR = "true_background_selector"; /** The Constant KEY_FALSE_BACKGROUND_SELECTOR. */ public static final String KEY_FALSE_BACKGROUND_SELECTOR = "false_background_selector"; public static final String KEY_AUTO_UI_GENERATE = "autoUIGenerate"; /** The Constant KEY_MODEL. */ public static final String KEY_MODEL = "model"; /** The context. */ Context mContext = null; /** The typed array. */ TypedArray mTypedArray = null; /** The current record. */ ODataRow mRecord = null; /** The control attributes. */ OControlAttributes mAttrs = new OControlAttributes(); /** The current data model. */ OModel mModel = null; /** The fields. */ List<String> mFields = new ArrayList<String>(); /** The field columns. */ HashMap<String, OColumn> mFieldColumns = new HashMap<String, OColumn>(); /** The m form field controls. */ HashMap<String, OField> mFormFieldControls = new HashMap<String, OField>(); /** The m on view click listener. */ OnViewClickListener mOnViewClickListener = null; /** * Instantiates a new form. * * @param context * the context */ public OForm(Context context) { super(context); init(context, null, 0); } /** * Instantiates a new form. * * @param context * the context * @param attrs * the attrs */ public OForm(Context context, AttributeSet attrs) { super(context, attrs); init(context, attrs, 0); } /** * Instantiates a new form. * * @param context * the context * @param attrs * the attrs * @param defStyle * the def style */ public OForm(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); init(context, attrs, defStyle); } /** * Inits the form control. * * @param context * the context * @param attrs * the attrs * @param defStyle * the def style */ private void init(Context context, AttributeSet attrs, int defStyle) { mContext = context; if (attrs != null) { mTypedArray = mContext.obtainStyledAttributes(attrs, R.styleable.OForm); mAttrs.put(KEY_BACKGROUND_SELECTOR, mTypedArray.getResourceId( R.styleable.OForm_background_selector, 0)); mAttrs.put(KEY_MODEL, mTypedArray.getString(R.styleable.OForm_model)); mAttrs.put( KEY_BACKGROUND_SELECTOR_BOOLEAN_FIELD, mTypedArray .getString(R.styleable.OForm_background_selector_boolean_field)); mAttrs.put(KEY_TRUE_BACKGROUND_SELECTOR, mTypedArray.getResourceId( R.styleable.OForm_true_background_selector, -1)); mAttrs.put(KEY_FALSE_BACKGROUND_SELECTOR, mTypedArray .getResourceId(R.styleable.OForm_false_background_selector, -1)); mAttrs.put(KEY_AUTO_UI_GENERATE, mTypedArray.getBoolean( R.styleable.OForm_autoUIGenerate, false)); mModel = OModel.get(mContext, mAttrs.getString(KEY_MODEL, null)); mTypedArray.recycle(); } } /** * _init form. * * @param editable * the editable */ private void _initForm(boolean editable) { mFieldColumns.clear(); mFormFieldControls.clear(); findAllFields(this); if (mAttrs.get(KEY_BACKGROUND_SELECTOR_BOOLEAN_FIELD) != null) { Boolean val = mRecord.getBoolean(mAttrs.getString( KEY_BACKGROUND_SELECTOR_BOOLEAN_FIELD, "")); if (val) { setBackgroundResource(mAttrs.getResource( KEY_TRUE_BACKGROUND_SELECTOR, -1)); } else { setBackgroundResource(mAttrs.getResource( KEY_FALSE_BACKGROUND_SELECTOR, -1)); } setClickable(true); } else { if (mAttrs.getResource(KEY_BACKGROUND_SELECTOR, 0) != 0) { setBackgroundResource(mAttrs.getResource( KEY_BACKGROUND_SELECTOR, 0)); setClickable(true); } } for (String key : mFormFieldControls.keySet()) { View v = mFormFieldControls.get(key); if (v instanceof OField) { OField field = (OField) v; field.setAutoUpdateUI(autoGenerateUI()); field.reInit(); OColumn column = mModel.getColumn(field.getFieldName()); OModel ref_model = null; if (column != null) { if (column.getRelationType() != null) { ref_model = mModel.createInstance(column.getType()); } OColumn ref_column = null; if (field.getRefColumn() != null) { ref_column = ref_model.getColumn(field.getRefColumn()); } field.setColumn(column); /** * Check for onChange of columns in EditMode */ if (column.hasOnChange() && editable) { setOnChangeForColumn(field, column); } /** * Check for domain filter column. */ if (column.hasDomainFilterColumn() && editable) { setOnDomainFilterCallBack(field, column); } OFieldType widget = null; String label = field.getFieldName(); mFieldColumns.put(field.getFieldName(), column); mFields.add(field.getTag().toString()); label = column.getLabel(); if (column.getRelationType() != null && column.getRelationType() == RelationType.ManyToOne) { widget = OFieldType.MANY_TO_ONE; if (field.isSearchableWidget()) { widget = OFieldType.MANY_TO_ONE_SEARCHABLE; } } if (column.getRelationType() != null && (column.getRelationType() == RelationType.ManyToMany || column .getRelationType() == RelationType.OneToMany)) { widget = OFieldType.MANY_TO_MANY_TAGS; } if (column.isFunctionalColumn()) { Object value = mRecord.get(column.getName()); if (column.getType() == null) column.setType(value.getClass()); field.setColumn(column); } if (column.getType().isAssignableFrom(OBlob.class) || (ref_column != null && ref_column.getType() .isAssignableFrom(OBlob.class))) { widget = OFieldType.BINARY; } if (column.getType().isAssignableFrom(OBoolean.class)) { widget = OFieldType.BOOLEAN_WIDGET; } if (column.getType().isAssignableFrom(OHtml.class)) { widget = OFieldType.WEB_VIEW; } if (widget != null) { if (mRecord != null && mRecord.contains(column.getName())) { field.createControl(widget, column, mRecord); field.setEditable(editable); } else { if (mRecord != null && !mRecord.contains(column.getName())) field.setVisibility(View.GONE); else { field.createControl(widget, column, mRecord); field.setEditable(editable); } } } else { field.setEditable(editable); if (mRecord != null) field.setText(mRecord.getString(field .getFieldName())); else field.setText(""); } field.setLabel(label); } else { if (mRecord != null) { if (field.getWidget() != null) { OColumn col = new OColumn(""); col.setName(field.getFieldName()); field.createControl(field.getWidget(), col, mRecord); } else field.setText(mRecord.getString(field .getFieldName())); } } } } } public boolean autoGenerateUI() { return mAttrs.getBoolean(KEY_AUTO_UI_GENERATE, false); } private void setOnDomainFilterCallBack(final OField field, final OColumn col) { LinkedHashMap<String, ColumnDomain> filter_domain = col .getFilterDomains(); for (String k : filter_domain.keySet()) { ColumnDomain dmn = filter_domain.get(k); if (dmn.getColumn() != null) { OField fld = mFormFieldControls.get(dmn.getColumn()); if (fld != null) { fld.setOnFilterDomainCallBack(dmn, new OnDomainFilterCallbacks() { @Override public void onFieldValueChanged( ColumnDomain domain) { col.addDomain(domain.getColumn(), domain.getOperator(), domain.getValue()); if (field.getWidget() != null && field.getWidget() == OFieldType.MANY_TO_ONE) { col.setHasDomainFilterColumn(false); field.setColumn(col); field.reInit(); } } }); } } } } private void setOnChangeForColumn(OField field, final OColumn col) { field.setOnChangeCallBack(new OnChangeCallback() { @Override public void onValueChange(ODataRow row) { if (!col.isOnChangeBGProcess()) { ODataRow vals = mModel.getOnChangeValue(col, row); if (vals != null) { for (String key : vals.keys()) { if (mFormFieldControls.containsKey(key)) { OField field = mFormFieldControls.get(key); if (field.getWidget() != null) { // Relation field field.selectManyToOneRecord(vals .getInt(key)); } else { field.setText(vals.getString(key)); } } } } } else { OnChangeBackgroundProcess bgProcess = new OnChangeBackgroundProcess( mContext, mModel, col, row); bgProcess.execute(); } } }); } private class OnChangeBackgroundProcess extends AsyncTask<Void, Void, ODataRow> { private OModel mModel; private OColumn mCol; private ODataRow mRow; private ProgressDialog mDialog; public OnChangeBackgroundProcess(Context context, OModel model, OColumn col, ODataRow row) { mModel = model; mCol = col; mRow = row; mDialog = new ProgressDialog(context); mDialog.setTitle(context.getString(R.string.title_working)); mDialog.setMessage(context.getString(R.string.title_please_wait)); mDialog.setCancelable(false); } @Override protected void onPreExecute() { super.onPreExecute(); mDialog.show(); } @Override protected ODataRow doInBackground(Void... params) { try { Thread.sleep(500); } catch (Exception e) { e.printStackTrace(); } return mModel.getOnChangeValue(mCol, mRow); } @Override protected void onPostExecute(ODataRow vals) { super.onPostExecute(vals); for (String key : vals.keys()) { if (mFormFieldControls.containsKey(key)) { OField field = mFormFieldControls.get(key); if (field.getWidget() != null) { // Relation field field.selectManyToOneRecord(vals.getInt(key)); } else { field.setText(vals.getString(key)); } } } mDialog.dismiss(); } } /** * Find all fields. * * @param view * the view */ private void findAllFields(ViewGroup view) { int childs = view.getChildCount(); for (int i = 0; i < childs; i++) { View v = view.getChildAt(i); if (v instanceof LinearLayout || v instanceof RelativeLayout) { findAllFields((ViewGroup) v); } if (v instanceof OField) { OField field = (OField) v; mFormFieldControls.put(field.getFieldName(), field); } } } /** * Sets the model. * * @param model * the new model */ public void setModel(OModel model) { mModel = model; } /** * Inits the form. * * @param record * the record */ public void initForm(ODataRow record) { initForm(record, false); } /** * Gets the form values. * * @return the form values, null if validation failed */ public OValues getFormValues() { OValues values = null; if (validateForm()) { values = new OValues(); for (String key : mFields) { OField field = (OField) findViewWithTag(key); if (field != null) { if (field.getValue() != null) { values.put(field.getFieldName(), field.getValue()); } } } if (mRecord != null) { values.put("id", mRecord.getInt("id")); values.put("local_id", mRecord.getInt(OColumn.ROW_ID)); values.put("is_dirty", true); } } return values; } /** * Validate form. * * @return true, if successful */ private boolean validateForm() { for (String key : mFields) { OField field = (OField) findViewWithTag(key); if (field != null) { OColumn col = mFieldColumns.get(field.getFieldName()); field.setError(null); if (col.isRequired() && field.isEmpty()) { field.setError(col.getLabel() + " is required"); return false; } } } return true; } /** * Inits the form width data and editable mode. * * @param record * the record * @param editable * the editable */ public void initForm(ODataRow record, boolean editable) { mRecord = record; _initForm(editable); } /** * Sets the editable. * * @param mEditMode * the new editable */ public void setEditable(Boolean mEditMode) { _initForm(mEditMode); } /** * Sets the on view click listener. * * @param view_id * the view_id * @param listener * the listener */ public void setOnViewClickListener(int view_id, OnViewClickListener listener) { findViewById(view_id).setOnClickListener(this); mOnViewClickListener = listener; } /** * The listener interface for receiving onViewClick events. The class that * is interested in processing a onViewClick event implements this * interface, and the object created with that class is registered with a * component using the component's * <code>addOnViewClickListener<code> method. When * the onViewClick event occurs, that object's appropriate * method is invoked. * * @see OnViewClickEvent */ public interface OnViewClickListener { /** * On form view click. * * @param view * the view * @param row * the row */ public void onFormViewClick(View view, ODataRow row); } /* * (non-Javadoc) * * @see android.view.View.OnClickListener#onClick(android.view.View) */ @Override public void onClick(View v) { if (mOnViewClickListener != null) { mOnViewClickListener.onFormViewClick(v, mRecord); } } }