/**
* Odoo, Open Source Management Solution
* Copyright (C) 2012-today Odoo SA (<http:www.odoo.com>)
* <p/>
* 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
* <p/>
* 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
* <p/>
* 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/>
* <p/>
* Created on 7/1/15 5:11 PM
*/
package odoo.controls;
import android.annotation.TargetApi;
import android.app.AlertDialog;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.graphics.Color;
import android.os.Build;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.RadioButton;
import android.widget.RadioGroup;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.Toast;
import com.odoo.core.orm.ODataRow;
import com.odoo.core.orm.OM2ORecord;
import com.odoo.core.orm.OModel;
import com.odoo.core.orm.fields.OColumn;
import com.odoo.core.orm.fields.types.OSelection;
import com.odoo.core.utils.OControls;
import java.util.ArrayList;
import java.util.List;
public class OSelectionField extends LinearLayout implements IOControlData,
AdapterView.OnItemSelectedListener, AdapterView.OnItemClickListener, RadioGroup.OnCheckedChangeListener {
public static final String TAG = OSelectionField.class.getSimpleName();
private Context mContext;
private Object mValue = null;
private Boolean mEditable = false;
private OField.WidgetType mWidget = null;
private Integer mResourceArray = null;
private OColumn mCol;
private String mLabel;
private OModel mModel;
private List<ODataRow> items = new ArrayList<>();
private ValueUpdateListener mValueUpdateListener = null;
// Controls
private Spinner mSpinner = null;
private SpinnerAdapter mAdapter;
private RadioGroup mRadioGroup = null;
private TextView txvView = null;
private Boolean mReady = false;
private float textSize = -1;
private int appearance = -1;
private int textColor = Color.BLACK;
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public OSelectionField(Context context, AttributeSet attrs,
int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init(context, attrs, defStyleAttr, defStyleRes);
}
public OSelectionField(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context, attrs, defStyleAttr, 0);
}
public OSelectionField(Context context, AttributeSet attrs) {
super(context, attrs);
init(context, attrs, 0, 0);
}
public OSelectionField(Context context) {
super(context);
init(context, null, 0, 0);
}
private void init(Context context, AttributeSet attrs, int defStyleAttr,
int defStyleRes) {
mContext = context;
if (attrs != null) {
}
if (mContext.getClass().getSimpleName().contains("BridgeContext"))
initControl();
}
private void createRadioGroup() {
final LayoutParams params = new LayoutParams(LayoutParams.MATCH_PARENT,
LayoutParams.WRAP_CONTENT);
if (mRadioGroup == null) {
mRadioGroup = new RadioGroup(mContext);
mRadioGroup.setLayoutParams(params);
} else {
removeView(mRadioGroup);
}
mRadioGroup.removeAllViews();
mRadioGroup.setOnCheckedChangeListener(this);
for (ODataRow label : items) {
RadioButton rdoBtn = new RadioButton(mContext);
rdoBtn.setLayoutParams(params);
rdoBtn.setText(label.getString(mModel.getDefaultNameColumn()));
if (textSize > -1) {
rdoBtn.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize);
}
if (appearance > -1) {
rdoBtn.setTextAppearance(mContext, appearance);
}
rdoBtn.setTextColor(textColor);
mRadioGroup.addView(rdoBtn);
}
addView(mRadioGroup);
}
@Override
public void initControl() {
final LayoutParams params = new LayoutParams(LayoutParams.MATCH_PARENT,
LayoutParams.WRAP_CONTENT);
removeAllViews();
setOrientation(VERTICAL);
createItems();
if (isEditable()) {
if (mWidget != null) {
switch (mWidget) {
case RadioGroup:
createRadioGroup();
return;
case SelectionDialog:
txvView = new TextView(mContext);
txvView.setLayoutParams(params);
mAdapter = new SpinnerAdapter(mContext,
android.R.layout.simple_list_item_1, items);
setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
AlertDialog dialog = createSelectionDialog(
getPos(), items, params);
txvView.setTag(dialog);
dialog.show();
}
});
if (textSize > -1) {
txvView.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize);
}
if (appearance > -1) {
txvView.setTextAppearance(mContext, appearance);
}
txvView.setTextColor(textColor);
addView(txvView);
return;
case Searchable:
case SearchableLive:
txvView = new TextView(mContext);
txvView.setLayoutParams(params);
setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(mContext,
SearchableItemActivity.class);
intent.putExtra("resource_id", mResourceArray);
intent.putExtra("selected_position", getPos());
intent.putExtra(OColumn.ROW_ID, getPos());
intent.putExtra("search_hint", getLabel());
if (mCol != null) {
intent.putExtra("column_name", mCol.getName());
}
/*
* FIXME: What about filter domain. Pass detail for
* filter domain
*/
intent.putExtra("model", mModel.getModelName());
intent.putExtra("live_search",
(mWidget == OField.WidgetType.SearchableLive));
try {
mContext.unregisterReceiver(valueReceiver);
} catch (Exception e) {
}
mContext.registerReceiver(valueReceiver,
new IntentFilter("searchable_value_select"));
mContext.startActivity(intent);
}
});
if (textSize > -1) {
txvView.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize);
}
if (appearance > -1) {
txvView.setTextAppearance(mContext, appearance);
}
txvView.setTextColor(textColor);
addView(txvView);
return;
default:
break;
}
}
// Default View
mSpinner = new Spinner(mContext);
mSpinner.setLayoutParams(params);
mAdapter = new SpinnerAdapter(mContext,
android.R.layout.simple_list_item_1, items);
mSpinner.setAdapter(mAdapter);
mSpinner.setOnItemSelectedListener(this);
addView(mSpinner);
} else {
setOnClickListener(null);
txvView = new TextView(mContext);
if (textSize > -1) {
txvView.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize);
}
if (appearance > -1) {
txvView.setTextAppearance(mContext, appearance);
}
txvView.setTextColor(textColor);
addView(txvView);
}
}
private void createItems() {
items.clear();
if (!mContext.getClass().getSimpleName().contains("BridgeContext")) {
if (mResourceArray != null && mResourceArray != -1) {
String[] items_list = mContext.getResources().getStringArray(
mResourceArray);
ODataRow row = new ODataRow();
row.put(OColumn.ROW_ID, -1);
row.put(mModel.getDefaultNameColumn(), "Nothing Selected");
items.add(row);
for (int i = 0; i < items_list.length; i++) {
row = new ODataRow();
row.put(OColumn.ROW_ID, i);
row.put(mModel.getDefaultNameColumn(), items_list[i]);
items.add(row);
}
} else if (mCol.getType().isAssignableFrom(OSelection.class)) {
List<ODataRow> rows = new ArrayList<>();
Object defaultVal = mCol.getDefaultValue();
for (String key : mCol.getSelectionMap().keySet()) {
String val = mCol.getSelectionMap().get(key);
ODataRow row = new ODataRow();
row.put("key", key);
row.put("name", val);
if (defaultVal != null && defaultVal.toString().equals(val)) {
rows.add(0, row);
} else {
rows.add(row);
}
}
items.addAll(rows);
} else {
items.addAll(getRecordItems(mModel, mCol));
}
}
}
private int getPos() {
if (mResourceArray != -1 && mValue != null) {
return Integer.parseInt(mValue.toString());
} else if (mCol.getType().isAssignableFrom(OSelection.class)) {
if (items.size() <= 0) {
createItems();
}
for (ODataRow item : items) {
int index = items.indexOf(item);
if (item.getString("key").equals(mValue.toString())) {
return index;
}
}
} else {
ODataRow rec = getValueForM2O();
if (rec != null) {
return rec.getInt(OColumn.ROW_ID);
}
}
return -1;
}
@Override
public void setValue(Object value) {
mValue = value;
if (mValue == null || mValue.toString().equals("false")) {
mValue = -1;
}
ODataRow row = new ODataRow();
if (isEditable()) {
if (mWidget != null) {
switch (mWidget) {
case RadioGroup:
if (mResourceArray != -1) {
((RadioButton) mRadioGroup.getChildAt(getPos()))
.setChecked(true);
row = items.get(getPos());
} else {
Integer row_id = null;
if (mValue instanceof OM2ORecord) {
row = ((OM2ORecord) mValue).browse();
row_id = row.getInt(OColumn.ROW_ID);
} else
row_id = (Integer) mValue;
int index = 0;
for (int i = 0; i < items.size(); i++) {
if (items.get(i).getInt(OColumn.ROW_ID) == row_id) {
index = i;
break;
}
}
row = items.get(index);
((RadioButton) mRadioGroup.getChildAt(index))
.setChecked(true);
}
break;
case Searchable:
case SearchableLive:
case SelectionDialog:
if (mResourceArray != -1) {
row = items.get(getPos());
} else {
if (mValue instanceof OM2ORecord)
row = ((OM2ORecord) mValue).browse();
else if (mValue instanceof Integer)
row = getRecordData((Integer) mValue);
}
txvView.setText(row.getString(mModel.getDefaultNameColumn()));
if (txvView.getTag() != null) {
AlertDialog dialog = (AlertDialog) txvView.getTag();
dialog.dismiss();
}
break;
default:
break;
}
} else {
if (mResourceArray != -1) {
mSpinner.setSelection(getPos());
row = items.get(getPos());
} else if (mCol.getType().isAssignableFrom(OSelection.class)) {
int pos = getPos();
mSpinner.setSelection(pos);
} else {
Integer row_id = null;
if (mValue instanceof OM2ORecord) {
row = ((OM2ORecord) mValue).browse();
row_id = row.getInt(OColumn.ROW_ID);
} else if (mValue instanceof Integer)
row_id = (Integer) mValue;
int index = 0;
for (int i = 0; i < items.size(); i++) {
if (items.get(i).getInt(OColumn.ROW_ID) == row_id) {
index = i;
break;
}
}
row = items.get(index);
mSpinner.setSelection(index);
}
}
} else {
if (mResourceArray != -1 || mCol.getType().isAssignableFrom(OSelection.class)) {
row = items.get(getPos());
} else {
if (mValue instanceof OM2ORecord) {
row = ((OM2ORecord) mValue).browse();
if (row == null) {
row = new ODataRow();
}
} else {
if (!(mValue instanceof Boolean) && mValue != null && !mValue.toString().equals("false")) {
int row_id = (Integer) mValue;
row = getRecordData(row_id);
} else {
row = new ODataRow();
row.put(mModel.getDefaultNameColumn(), "No " + mCol.getLabel() + " selected");
}
}
}
if (!row.getString(mModel.getDefaultNameColumn()).equals("false"))
txvView.setText(row.getString(mModel.getDefaultNameColumn()));
}
if (isEditable() && mValueUpdateListener != null && (mValue instanceof Integer &&
(int) mValue != -1)) {
mValueUpdateListener.onValueUpdate(row);
}
}
@Override
public View getFieldView() {
return null;
}
@Override
public void setError(String error) {
if (error != null)
Toast.makeText(mContext, error, Toast.LENGTH_LONG).show();
}
private ODataRow getValueForM2O() {
if (getValue() != null) {
if (getValue() instanceof OM2ORecord)
return ((OM2ORecord) getValue()).browse();
else if (getValue() instanceof Integer)
return getRecordData((Integer) getValue());
}
return null;
}
@Override
public Object getValue() {
if (mValue instanceof OM2ORecord) {
return ((OM2ORecord) mValue).getId();
}
return mValue;
}
@Override
public void setEditable(Boolean editable) {
mEditable = editable;
initControl();
}
@Override
public Boolean isEditable() {
return mEditable;
}
public void setWidgetType(OField.WidgetType type) {
mWidget = type;
initControl();
}
public void setArrayResourceId(int res_id) {
mResourceArray = res_id;
}
public void setColumn(OColumn col) {
mCol = col;
if (mCol != null && mLabel == null) {
mLabel = mCol.getLabel();
}
}
private ODataRow getRecordData(int row_id) {
ODataRow row;
if (row_id > 0) {
OModel rel_model = mModel.createInstance(mCol.getType());
row = rel_model.browse(row_id);
} else {
row = items.get(0);
}
return row;
}
private class SpinnerAdapter extends ArrayAdapter<ODataRow> {
public SpinnerAdapter(Context context, int resource,
List<ODataRow> objects) {
super(context, resource, objects);
}
public View getView(int position, View convertView, ViewGroup parent) {
return generateView(position, convertView, parent);
}
@Override
public View getDropDownView(int position, View convertView,
ViewGroup parent) {
return generateView(position, convertView, parent);
}
private View generateView(int position, View convertView,
ViewGroup parent) {
View v = convertView;
if (v == null)
v = LayoutInflater.from(mContext).inflate(
android.R.layout.simple_list_item_1, parent, false);
ODataRow row = getItem(position);
OControls.setText(v, android.R.id.text1, row.getString(mModel.getDefaultNameColumn()));
return v;
}
}
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position,
long id) {
if (mResourceArray != -1) {
mValue = position;
} else if (mCol.getType().isAssignableFrom(OSelection.class)) {
ODataRow row = mAdapter.getItem(position);
mValue = row.getString("key");
} else {
mValue = items.get(position).get(OColumn.ROW_ID);
}
setValue(mValue);
}
@Override
public void onNothingSelected(AdapterView<?> parent) {
mValue = null;
}
@Override
public void setLabelText(String label) {
mLabel = label;
}
@Override
public String getLabel() {
if (mLabel != null)
return mLabel;
if (mCol != null)
return mCol.getLabel();
return "unknown";
}
private AlertDialog createSelectionDialog(final int selected_position,
final List<ODataRow> items, LayoutParams params) {
final AlertDialog.Builder builder = new AlertDialog.Builder(mContext);
ListView dialogView = new ListView(mContext);
dialogView.setAdapter(mAdapter);
dialogView.setOnItemClickListener(this);
dialogView.setLayoutParams(params);
builder.setView(dialogView);
return builder.create();
}
@Override
public void onItemClick(AdapterView<?> parent, View view, int position,
long id) {
setValue(position);
}
BroadcastReceiver valueReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
setValue(intent.getIntExtra("selected_position", -1));
mContext.unregisterReceiver(valueReceiver);
}
};
public void setModel(OModel model) {
mModel = model;
}
public static List<ODataRow> getRecordItems(OModel model, OColumn column) {
List<ODataRow> items = new ArrayList<ODataRow>();
OModel rel_model = model.createInstance(column.getType());
StringBuffer whr = new StringBuffer();
List<Object> args_list = new ArrayList<Object>();
// Skipping onchange domain filter
if (!column.hasDomainFilterColumn()) {
for (String key : column.getDomains().keySet()) {
OColumn.ColumnDomain domain = column.getDomains().get(key);
if (domain.getConditionalOperator() != null) {
whr.append(domain.getConditionalOperator());
} else {
whr.append(" ");
whr.append(domain.getColumn());
whr.append(" ");
whr.append(domain.getOperator());
whr.append(" ? ");
args_list.add(domain.getValue().toString());
}
}
}
String where = null;
String[] args = null;
if (args_list.size() > 0) {
where = whr.toString();
args = args_list.toArray(new String[args_list.size()]);
}
List<ODataRow> rows = new ArrayList<>();
rows = rel_model.select(new String[]{rel_model.getDefaultNameColumn()}, where,
args, rel_model.getDefaultNameColumn());
ODataRow row = new ODataRow();
row.put(OColumn.ROW_ID, -1);
row.put(rel_model.getDefaultNameColumn(), "No " + column.getLabel() + " selected");
items.add(row);
items.addAll(rows);
return items;
}
@Override
public void setValueUpdateListener(ValueUpdateListener listener) {
mValueUpdateListener = listener;
}
@Override
public void onCheckedChanged(RadioGroup group, int checkedId) {
int index = mRadioGroup.indexOfChild(group.findViewById(checkedId));
ODataRow row = items.get(index);
setValue(row.getInt(OColumn.ROW_ID));
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
mReady = true;
}
@Override
public Boolean isControlReady() {
return mReady;
}
@Override
public void resetData() {
if (isEditable()) {
if (mWidget == null) {
if (mAdapter != null) {
createItems();
mAdapter.notifyDataSetChanged();
}
} else {
switch (mWidget) {
case SelectionDialog:
createItems();
break;
case RadioGroup:
createItems();
createRadioGroup();
break;
case Searchable:
case SearchableLive:
break;
default:
break;
}
}
}
}
public void setResource(float textSize, int appearance, int color) {
this.textSize = textSize;
this.appearance = appearance;
this.textColor = color;
}
}