/**
* Global Sensor Networks (GSN) Source Code
* Copyright (c) 2006-2014, Ecole Polytechnique Federale de Lausanne (EPFL)
* <p/>
* This file is part of GSN.
* <p/>
* GSN is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* <p/>
* GSN 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 General Public License for more details.
* <p/>
* You should have received a copy of the GNU General Public License
* along with GSN. If not, see <http://www.gnu.org/licenses/>.
* <p/>
* File: gsn-tiny/src/tinygsn/gui/android/ActivityVSConfig.java
*
* @author Do Ngoc Hoan
*/
package tinygsn.gui.android;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.graphics.Color;
import android.os.AsyncTask;
import android.os.Bundle;
import android.text.Editable;
import android.text.InputType;
import android.text.TextWatcher;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.Spinner;
import android.widget.TableLayout;
import android.widget.TableRow;
import android.widget.TextView;
import android.widget.Toast;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Properties;
import tinygsn.beans.DataField;
import tinygsn.beans.StaticData;
import tinygsn.beans.StreamSource;
import tinygsn.beans.Subscription;
import tinygsn.controller.AndroidControllerVS;
import tinygsn.model.vsensor.AbstractVirtualSensor;
import tinygsn.model.utils.ParameterType;
import tinygsn.model.utils.Parameter;
import tinygsn.model.wrappers.AbstractWrapper;
import tinygsn.storage.db.SqliteStorageManager;
public class ActivityVSConfig extends AbstractActivity {
static int TEXT_SIZE = 10;
private Context context = this;
private Spinner spinnerVSType;
private EditText editText_vsName;
private TableLayout table_notify_config, table_layout;
private TableRow table_vsensor_config;
private SettingPanel vssetting;
private SqliteStorageManager storage = null;
private Properties wrapperList;
AndroidControllerVS controller = new AndroidControllerVS();
private ArrayList<StreamSourcePanel> pannels = new ArrayList<>();
private ArrayList<String> selectedVS = new ArrayList<>();
private boolean isEnableSave = true;
private String editingVS = null;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_vs_config);
Bundle extras = getIntent().getExtras();
if (extras != null) {
editingVS = extras.getString("vsname");
loadEditingValues();
}
getActionBar().setDisplayHomeAsUpEnabled(true);
editText_vsName = (EditText) findViewById(R.id.editText_vsName);
table_vsensor_config = (TableRow) findViewById(R.id.table_vsensor_config);
table_layout = (TableLayout) findViewById(R.id.tableLayout_vs);
Button button_add = (Button) findViewById(R.id.button_add);
button_add.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
table_layout.addView(addSource());
}
});
wrapperList = AbstractWrapper.getWrapperList(this);
storage = new SqliteStorageManager();
loadVSType();
}
public void loadEditingValues() {
if (editingVS != null) {
new AsyncTask<Activity, Void, AbstractVirtualSensor>() {
@Override
protected AbstractVirtualSensor doInBackground(Activity... params) {
return storage.getVSByName(editingVS);
}
@Override
protected void onPostExecute(AbstractVirtualSensor result) {
if (result != null) {
result.getVirtualSensorConfiguration().getName();
//TODO load values inside fields and create the input sources
//some of them must be set readonly !!!
}
}
}.execute((Activity) null);
}
}
public void loadVSType() {
spinnerVSType = (Spinner) findViewById(R.id.spinner_vsType);
List<String> list = new ArrayList<String>();
for (String s : AbstractVirtualSensor.VIRTUAL_SENSOR_LIST) {
list.add(s);
}
ArrayAdapter<String> dataAdapter = new ArrayAdapter<String>(this, R.layout.spinner_item, list);
dataAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spinnerVSType.setAdapter(dataAdapter);
spinnerVSType.setOnItemSelectedListener(new OnItemSelectedListener() {
public void onItemSelected(AdapterView<?> parent, View view, final int pos, long id) {
table_vsensor_config.removeAllViews();
new AsyncTask<Activity, Void, SettingPanel>() {
@Override
protected SettingPanel doInBackground(Activity... params) {
vssetting = null;
try {
ArrayList<String> vp = new ArrayList<>();
for (String wrapperName : selectedVS) {
String[] fields = ((AbstractWrapper) Class.forName(wrapperName).newInstance()).getFieldList();
for (int i = 0; i < fields.length; i++) {
vp.add(fields[i]);
}
}
ArrayList<Parameter> param = ((AbstractVirtualSensor) Class.forName(AbstractVirtualSensor.VIRTUAL_SENSOR_CLASSES[pos]).newInstance()).getParameters(vp);
vssetting = new SettingPanel("vsensor", param);
} catch (Exception e) {
e.printStackTrace();
}
return vssetting;
}
@Override
protected void onPostExecute(SettingPanel result) {
if (result != null) {
table_vsensor_config.addView(vssetting.getPanel());
}
}
}.execute((Activity) null);
}
@Override
public void onNothingSelected(AdapterView<?> arg0) {
Toast.makeText(context, "Please select a virtual sensor",
Toast.LENGTH_SHORT).show();
}
});
}
private TableRow addSource() {
final StreamSourcePanel panel = new StreamSourcePanel();
final TableRow row = new TableRow(this);
final TableLayout settingLayout = new TableLayout(this);
TableLayout layout = new TableLayout(this);
TableRow.LayoutParams p = new TableRow.LayoutParams();
p.span = 2;
p.width = TableRow.LayoutParams.MATCH_PARENT;
layout.setColumnStretchable(1, true);
layout.setLayoutParams(p);
settingLayout.setLayoutParams(p);
settingLayout.setColumnStretchable(1, true);
row.addView(layout);
TableRow inrow = new TableRow(this);
layout.addView(inrow);
LinearLayout separator = new LinearLayout(this);
separator.setBackgroundColor(Color.rgb(150, 150, 150));
p = new TableRow.LayoutParams();
p.span = 2;
separator.setLayoutParams(p);
inrow.addView(separator);
Button b = new Button(this);
b.setText("-");
separator.addView(b);
b.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
// Get wrapper name of deleted stream source
String wrapperName = panel.wrapper.getSelectedItem().toString();
wrapperName = wrapperList.getProperty(wrapperName);
int index = selectedVS.indexOf(wrapperName);
//Remove name of the selected list
selectedVS.remove(index);
//Update the "field" list in the VS
updateVSSpinnerParameter("field");
pannels.remove(panel);
table_layout.removeView(row);
}
});
//window size
inrow = new TableRow(this);
TextView label = new TextView(this);
label.setText("Window size: ");
label.setTextColor(Color.rgb(0, 0, 0));
inrow.addView(label);
panel.windowsize = new EditText(this);
panel.windowsize.setText("5");
panel.windowsize.setTextSize(TEXT_SIZE + 5);
panel.windowsize.setInputType(InputType.TYPE_CLASS_NUMBER);
panel.windowsize.setTextColor(Color.rgb(0, 0, 0));
inrow.addView(panel.windowsize);
layout.addView(inrow);
//step size
inrow = new TableRow(this);
label = new TextView(this);
label.setText("Step size: ");
label.setTextColor(Color.rgb(0, 0, 0));
inrow.addView(label);
panel.stepsize = new EditText(this);
panel.stepsize.setText("1");
panel.stepsize.setTextSize(TEXT_SIZE + 5);
panel.stepsize.setInputType(InputType.TYPE_CLASS_NUMBER);
panel.stepsize.setTextColor(Color.rgb(0, 0, 0));
inrow.addView(panel.stepsize);
layout.addView(inrow);
//time based
inrow = new TableRow(this);
label = new TextView(this);
label.setText("Time based? ");
label.setTextColor(Color.rgb(0, 0, 0));
inrow.addView(label);
panel.timebased = new CheckBox(this);
panel.timebased.setTextColor(Color.rgb(0, 0, 0));
inrow.addView(panel.timebased);
layout.addView(inrow);
//aggregator
inrow = new TableRow(this);
label = new TextView(this);
label.setText("Aggregation: ");
label.setTextColor(Color.rgb(0, 0, 0));
inrow.addView(label);
panel.aggregator = new Spinner(this);
List<String> list = new ArrayList<>();
for (String s : StreamSource.AGGREGATOR) {
list.add(s);
}
ArrayAdapter<String> dataAdapter = new ArrayAdapter<>(this, R.layout.spinner_item, list);
dataAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
panel.aggregator.setAdapter(dataAdapter);
inrow.addView(panel.aggregator);
layout.addView(inrow);
//wrapper
inrow = new TableRow(this);
label = new TextView(this);
label.setText("Wrapper: ");
label.setTextColor(Color.rgb(0, 0, 0));
inrow.addView(label);
panel.wrapper = new Spinner(this);
list = new ArrayList<>();
for (String s : wrapperList.stringPropertyNames()) {
list.add(s);
}
Collections.sort(list);
for (String s : storage.getListofVSName()) {
list.add("local: " + s);
}
SqliteStorageManager storage = new SqliteStorageManager();
for (Subscription s : storage.getSubscribeList()){
try {
list.add("remote: Subscription "+s.getId());
} catch (Exception e) {
e.printStackTrace();
}
}
dataAdapter = new ArrayAdapter<>(this, R.layout.spinner_item, list);
dataAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
panel.wrapper.setAdapter(dataAdapter);
inrow.addView(panel.wrapper);
layout.addView(inrow);
final LastVSSelected lastVSSelected = new LastVSSelected();
// used only to keep somewhere the previous value of a spinner
// to remove the corresponding fields
panel.wrapper.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
if (panel.wrapper != null) {
lastVSSelected.setLastVSSelected(wrapperList.getProperty(panel.wrapper.getSelectedItem().toString()));
}
return false;
}
});
panel.wrapper.setOnItemSelectedListener(new OnItemSelectedListener() {
public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) {
settingLayout.removeAllViews();
panel.settings = null;
new AsyncTask<Activity, Void, SettingPanel>() {
@Override
protected SettingPanel doInBackground(Activity... params) {
try {
String wrapperName = panel.wrapper.getSelectedItem().toString();
if (wrapperName.startsWith("local: ")) {
wrapperName = "tinygsn.model.wrappers.LocalWrapper";
} else if (wrapperName.startsWith("remote: ")) {
wrapperName = "tinygsn.model.wrappers.RemoteWrapper";
} else {
wrapperName = wrapperList.getProperty(wrapperName);
}
ArrayList<Parameter> param = ((AbstractWrapper) Class.forName(wrapperName).newInstance()).getParameters();
panel.settings = new SettingPanel("wrapper", param);
selectedVS.remove(lastVSSelected.getLastVSSelected());
selectedVS.add(wrapperName);
} catch (Exception e) {
e.printStackTrace();
}
return panel.settings;
}
@Override
protected void onPostExecute(SettingPanel result) {
if (result != null) {
settingLayout.addView(panel.settings.getPanel());
updateVSSpinnerParameter("field");
}
}
}.execute((Activity) null);
}
@Override
public void onNothingSelected(AdapterView<?> arg0) {
Toast.makeText(context, "Please select a virtual sensor",
Toast.LENGTH_SHORT).show();
}
});
//setting container
inrow = new TableRow(this);
inrow.addView(settingLayout);
layout.addView(inrow);
pannels.add(panel);
return row;
}
/**
* Update the spinner list of one field of a Virtual Sensor
*
* @param paramName
*/
private void updateVSSpinnerParameter(String paramName) {
try {
for (Parameter param : vssetting.params) {
if (param.getmName().equals(paramName)) {
ArrayList<String> vp = new ArrayList<>();
for (String wrapperName : selectedVS) {
String[] fields = ((AbstractWrapper) Class.forName(wrapperName).newInstance()).getFieldList();
for (int i = 0; i < fields.length; i++) {
if (!vp.contains(fields[i])) {
vp.add(fields[i]);
}
}
}
param.setmParameters(vp);
table_vsensor_config.removeAllViews();
table_vsensor_config.addView(vssetting.getPanel());
return;
}
}
} catch (Exception e) {
}
}
@Override
protected void onPause() {
super.onPause();
}
public void saveVS() {
String vsName = editText_vsName.getText().toString();
int vsType = spinnerVSType.getSelectedItemPosition();
try {
storage.executeInsert(
"vsList",
new ArrayList<String>(Arrays.asList("running", "vsname",
"vstype")),
new ArrayList<String>(Arrays.asList("1", vsName, "" + vsType)));
vssetting.saveTo(vsName, storage);
String wrapperName = "";
for (StreamSourcePanel p : pannels) {
wrapperName = p.saveTo(vsName, storage); // TODO compute actual output structure !!!
}
//take the structure from the last wrapper !!
AbstractWrapper w;
try {
w = StaticData.getWrapperByName(wrapperName);
DataField[] outputStructure = w.getOutputStructure();
AbstractVirtualSensor vs = (AbstractVirtualSensor) Class.forName(AbstractVirtualSensor.VIRTUAL_SENSOR_CLASSES[vsType]).newInstance();
outputStructure = vs.getOutputStructure(outputStructure);
storage.executeCreateTable("vs_" + vsName, outputStructure, true);
} catch (Exception e) {
e.printStackTrace();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
menu.add("Save")
// .setIcon(R.drawable.ic_menu_save)
.setShowAsAction(
MenuItem.SHOW_AS_ACTION_IF_ROOM | MenuItem.SHOW_AS_ACTION_WITH_TEXT);
return super.onCreateOptionsMenu(menu);
}
public boolean onMenuItemSelected(int featureId, MenuItem item) {
int itemId = item.getItemId();
switch (itemId) {
case android.R.id.home:
finish();
break;
case 0:
if (isEnableSave) {
String vsName = editText_vsName.getText().toString();
if (vsName.equals("")) {
Toast.makeText(this, "Please input VS Name", Toast.LENGTH_SHORT).show();
} else if (vsName.matches("(.*)[[\\s*{}\\[\\]#~@'`!\"£$€%^&*()_\\-+=\\/\\?.,<>|\\\\¬|:;]](.*)")) {
Toast.makeText(this, "VS Name cannot have special characters!",
Toast.LENGTH_SHORT).show();
} else if (storage.vsExists("vs_" + vsName) == true) {
Toast.makeText(this, "VS Name already exists, please choose a new one!",
Toast.LENGTH_SHORT).show();
} else if (selectedVS.isEmpty()) {
Toast.makeText(this, "You must choose at least on wrapper for your VS",
Toast.LENGTH_SHORT).show();
} else {
new AsyncTask<Activity, Void, Void>() {
@Override
protected Void doInBackground(Activity... params) {
saveVS();
isEnableSave = false;
String vsName = editText_vsName.getText().toString();
controller.startStopVS(vsName, true);
return null;
}
@Override
protected void onPostExecute(Void result) {
finish();
}
}.execute(this);
}
} else
Toast.makeText(this, "There is nothing changed to save!",
Toast.LENGTH_SHORT).show();
break;
}
return true;
}
void enableSave(boolean isEnabled) {
Button saveButton = (Button) findViewById(R.id.btnSaveVS);
saveButton.setEnabled(isEnabled);
}
private class StreamSourcePanel {
public EditText windowsize, stepsize;
public Spinner aggregator, wrapper;
public CheckBox timebased;
public SettingPanel settings;
public boolean validate() {
if (windowsize.getText().toString().equals("")) {
Toast.makeText(ActivityVSConfig.this, "Please input Window Size", Toast.LENGTH_SHORT)
.show();
return false;
}
if (stepsize.getText().toString().equals("")) {
Toast.makeText(ActivityVSConfig.this, "Please input Step", Toast.LENGTH_SHORT).show();
return false;
}
return true;
}
public String saveTo(String vsname, SqliteStorageManager storage) throws SQLException {
String wrapperName = wrapper.getSelectedItem().toString();
if (wrapperName.startsWith("local: ")) {
wrapperName = "tinygsn.model.wrappers.LocalWrapper?" + wrapperName.substring(7);
} else if (wrapperName.startsWith("remote: ")) {
wrapperName = "tinygsn.model.wrappers.RemoteWrapper?" + wrapperName.substring(21);
} else {
wrapperName = wrapperList.getProperty(wrapperName);
}
if (validate()) {
storage.executeInsert(
"sourcesList",
new ArrayList<>(Arrays.asList("vsname",
"sswindowsize", "ssstep", "sstimebased", "ssaggregator",
"wrappername")),
new ArrayList<>(Arrays.asList(vsname, windowsize.getText().toString(), stepsize
.getText().toString(), timebased.isChecked() + "", aggregator.getSelectedItemPosition()
+ "", wrapperName)));
// As Wrappers are singleton, remove first old settings for this wrapper
for (Parameter param : settings.params) {
storage.deleteSetting("wrapper" + ":" + wrapperName + ":" + param.getmName());
}
settings.saveTo(wrapperName, storage);
}
return wrapperName;
}
}
private class LastVSSelected { // use to keep the value of wrapper in VS to remove corresponding fields
String lastVSSelected = "";
public void setLastVSSelected(String s) {
this.lastVSSelected = s;
}
public String getLastVSSelected() {
return this.lastVSSelected;
}
}
private class SettingPanel { //key-value parameters (for VS and wrappers)
private String prefix;
private ArrayList<Parameter> params;
private TextView[] values;
private Spinner[] spinners;
SettingPanel(String prefix, ArrayList<Parameter> params) {
this.prefix = prefix;
this.params = params;
}
public TableLayout getPanel() {
TextWatcher textWatcher = new TextWatcher() {
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
@Override
public void beforeTextChanged(CharSequence s, int start, int count,
int after) {
}
@Override
public void afterTextChanged(Editable s) {
try {
// numLatest =
// Integer.parseInt(editText_numLatest.getText().toString());
// loadLatestData();
} catch (NumberFormatException e) {
AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(ActivityVSConfig.this);
alertDialogBuilder.setTitle("Please Fill in all fields!");
}
}
};
TableLayout layout = new TableLayout(ActivityVSConfig.this);
layout.setColumnStretchable(1, true);
TableRow.LayoutParams p = new TableRow.LayoutParams();
p.span = 2;
p.width = TableRow.LayoutParams.MATCH_PARENT;
layout.setLayoutParams(p);
values = new TextView[params.size()];
spinners = new Spinner[params.size()];
int indexSpinner = 0;
int indexTextView = 0;
for (int i = 0; i < params.size(); i++) {
TableRow inrow = new TableRow(ActivityVSConfig.this);
TextView label = new TextView(ActivityVSConfig.this);
label.setText(params.get(i).getmName() + ": ");
label.setTextColor(Color.rgb(0, 0, 0));
inrow.addView(label);
switch (params.get(i).getmType()) {
case EDITBOX:
values[indexTextView] = new EditText(ActivityVSConfig.this);
break;
case EDITBOX_NUMBER:
values[indexTextView] = new EditText(ActivityVSConfig.this);
values[indexTextView].setInputType(InputType.TYPE_CLASS_NUMBER);
break;
case EDITBOX_PHONE:
values[indexTextView] = new EditText(ActivityVSConfig.this);
values[indexTextView].setInputType(InputType.TYPE_CLASS_PHONE);
break;
case CHECKBOX:
values[indexTextView] = new CheckBox(ActivityVSConfig.this);
break;
case SPINNER:
spinners[indexSpinner] = new Spinner(ActivityVSConfig.this);
List<String> list = params.get(i).getmParameters();
ArrayAdapter<String> dataAdapter = new ArrayAdapter<String>(context,
tinygsn.gui.android.R.layout.spinner_item, list);
dataAdapter
.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spinners[indexSpinner].setAdapter(dataAdapter);
inrow.addView(spinners[indexSpinner]);
layout.addView(inrow);
indexSpinner++;
break;
default:
values[indexTextView] = new EditText(ActivityVSConfig.this);
break;
}
if (params.get(i).getmType() != ParameterType.SPINNER) {
values[indexTextView].setTextSize(TEXT_SIZE + 5);
values[indexTextView].setText(params.get(i).getmDefaultParameter());
values[indexTextView].setTextColor(Color.rgb(0, 0, 0));
inrow.addView(values[indexTextView]);
values[indexTextView].addTextChangedListener(textWatcher);
indexTextView++;
layout.addView(inrow);
}
}
return layout;
}
public void saveTo(String module, SqliteStorageManager storage) {
int indexSpinner = 0;
int indexTextView = 0;
for (int i = 0; i < params.size(); i++) {
String value;
if (params.get(i).getmType() == ParameterType.SPINNER) {
value = spinners[indexSpinner].getSelectedItem().toString();
indexSpinner++;
} else if (params.get(i).getmType() == ParameterType.CHECKBOX) {
boolean boolValue = ((CheckBox) values[indexTextView]).isChecked();
if (boolValue) {
value = "true";
} else {
value = "false";
}
indexTextView++;
} else {
value = values[indexTextView].getText().toString();
indexTextView++;
}
storage.setSetting(prefix + ":" + module + ":" + params.get(i).getmName(), value);
}
}
}
}