package org.oobd.ui.android;
import android.content.Context;
import android.content.DialogInterface;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.os.Bundle;
import android.os.Environment;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.Log;
import android.view.View;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.CompoundButton.OnCheckedChangeListener;
import android.widget.EditText;
import android.widget.Spinner;
import android.widget.ArrayAdapter;
import android.widget.AdapterView;
import android.widget.TextView;
import org.oobd.base.Core;
import org.oobd.base.OOBDConstants;
import org.oobd.base.archive.Archive;
import org.oobd.base.archive.Factory;
import org.oobd.base.port.OOBDPort;
import org.oobd.base.port.PortInfo;
import org.oobd.ui.android.application.OOBDApp;
import android.app.Activity;
import android.app.AlertDialog;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.List;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* @author Andreas Budde, Peter Mayer Settings activity that allows users to
* configure the app (Bluetooth OBD connection, Lua script file
* selection and simulation mode.
*/
public class Settings extends Activity implements org.oobd.base.OOBDConstants {
public static final String KEY_LIST_SELECT_PAIRED_OBD2_DEVICE = "PREF_OOBD_BT_DEVICE";
private Spinner connectTypeSpinner;
private Spinner mDeviceSpinner;
private String connectDeviceName;
private String connectTypeName;
private PortInfo[] portList;
SharedPreferences preferences;
private CheckBox pgpEnabled;
private CheckBox httpEnabled;
private TextView pgpStatus;
private Button pgpImportKeys;
private TextView urlEditText;
private TextView wsProxyHostEditText;
private TextView wsProxyPortEditText;
private TextView deviceEditText;
private Hashtable<String, Class> supplyHardwareConnects;
private String oldConnectTypeName = null;
public static Settings mySettingsActivity;
// protected void onCreate(Bundle savedInstanceState,OOBDPort comPort) {
protected void onCreate(Bundle savedInstanceState) {
mySettingsActivity = this;
super.onCreate(savedInstanceState);
setContentView(R.layout.settings);
preferences = this.getSharedPreferences("OOBD_SETTINGS", MODE_PRIVATE);
connectTypeSpinner = (Spinner) findViewById(R.id.connectionTypeSpinner);
connectTypeSpinner
.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
public void onItemSelected(AdapterView<?> parent,
View view, int pos, long id) {
connectTypeName = (String) parent
.getItemAtPosition(pos);
if (connectTypeName != null
&& !connectTypeName.equalsIgnoreCase("")) {
Editor prefs = preferences.edit();
prefs.putString(PropName_ConnectType,
connectTypeName);
Core.getSingleInstance().writeDataPool(
DP_ACTUAL_CONNECTION_TYPE,
connectTypeName);
if (oldConnectTypeName != null) {
// !! The value of the connection device is not
// stored here, as this already controlled in
// the comportComboBox change() event
prefs.putString(
oldConnectTypeName
+ "_"
+ PropName_ConnectServerURL,
((EditText) findViewById(R.id.wsURLeditText))
.getText().toString());
prefs.putString(
oldConnectTypeName
+ "_"
+ PropName_ProxyHost,
((EditText) findViewById(R.id.wsProxyHostEditText))
.getText().toString());
String portString = ((EditText) findViewById(R.id.wsProxyPortEditText))
.getText().toString();
int portInt = 0;
if (portString.length() > 0) {
portInt = Integer.parseInt(portString);
}
prefs.putInt(oldConnectTypeName + "_"
+ PropName_ProxyPort,
portInt);
}
oldConnectTypeName=connectTypeName;
prefs.commit();
updateUI();
}
}
public void onNothingSelected(AdapterView<?> parent) {
preferences
.edit()
.putString(PropName_ConnectType,
"").commit();
}
});
List<String> list = new ArrayList<String>();
supplyHardwareConnects = OOBDApp.getInstance().getCore()
.getConnectorList();
Enumeration<String> e = supplyHardwareConnects.keys();
// iterate through HashTable keys Enumeration
while (e.hasMoreElements()) {
list.add(e.nextElement());
}
ArrayAdapter<String> dataAdapter = new ArrayAdapter<String>(this,
android.R.layout.simple_spinner_item, list);
dataAdapter
.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
connectTypeSpinner.setAdapter(dataAdapter);
pgpEnabled = (CheckBox) findViewById(R.id.PGPCheckBox);
pgpEnabled.setOnCheckedChangeListener(new OnCheckedChangeListener() {
public void onCheckedChanged(CompoundButton parent,
boolean isChecked) {
preferences.edit().putBoolean("PGPENABLED", isChecked).commit();
}
});
httpEnabled = (CheckBox) findViewById(R.id.HTTPCheckBox);
httpEnabled.setOnCheckedChangeListener(new OnCheckedChangeListener() {
public void onCheckedChanged(CompoundButton parent,
boolean isChecked) {
if (isChecked) {
preferences
.edit()
.putString(PropName_UIHander,
UIHANDLER_WS_NAME).commit();
} else {
preferences
.edit()
.putString(PropName_UIHander,
UIHANDLER_LOCAL_NAME)
.commit();
}
}
});
pgpStatus = (TextView) findViewById(R.id.pgpStatustextView);
pgpImportKeys = (Button) findViewById(R.id.pgpImportKeysbutton);
pgpImportKeys.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
if (checkKeyFiles() != 0) {
importKeyFiles();
updateUI();
} else {
new AlertDialog.Builder(mySettingsActivity)
.setTitle("Delete PGP Key Files")
.setMessage(
"Do you REALLY want to delete your PGP keys??")
.setPositiveButton("Delete them!",
new DialogInterface.OnClickListener() {
public void onClick(
DialogInterface dialog,
int whichButton) {
try {
deleteKeyFiles();
updateUI();
} catch (Exception e) {
}
}
})
.setNegativeButton("Cancel",
new DialogInterface.OnClickListener() {
public void onClick(
DialogInterface dialog,
int whichButton) {
// Do nothing.
}
}).show();
}
}
});
urlEditText = (EditText) findViewById(R.id.wsURLeditText);
urlEditText.addTextChangedListener(new TextWatcher() {
public void afterTextChanged(Editable s) {
preferences
.edit()
.putString(
connectTypeName
+ "_"
+ PropName_ConnectServerURL,
urlEditText.getText().toString()).commit();
Core.getSingleInstance().writeDataPool(
DP_ACTUAL_REMOTECONNECT_SERVER,
urlEditText.getText().toString());
}
public void beforeTextChanged(CharSequence s, int start, int count,
int after) {
}
public void onTextChanged(CharSequence s, int start, int before,
int count) {
}
});
wsProxyHostEditText = (EditText) findViewById(R.id.wsProxyHostEditText);
wsProxyHostEditText.addTextChangedListener(new TextWatcher() {
public void afterTextChanged(Editable s) {
preferences
.edit()
.putString(
connectTypeName + "_"
+ PropName_ProxyHost,
wsProxyHostEditText.getText().toString())
.commit();
}
public void beforeTextChanged(CharSequence s, int start, int count,
int after) {
}
public void onTextChanged(CharSequence s, int start, int before,
int count) {
}
});
wsProxyPortEditText = (EditText) findViewById(R.id.wsProxyPortEditText);
wsProxyPortEditText.addTextChangedListener(new TextWatcher() {
public void afterTextChanged(Editable s) {
String content = wsProxyPortEditText.getText().toString();
if (content.length() > 0) {
preferences
.edit()
.putInt(connectTypeName + "_"
+ PropName_ProxyPort,
Integer.parseInt(wsProxyPortEditText
.getText().toString())).commit();
}
}
public void beforeTextChanged(CharSequence s, int start, int count,
int after) {
}
public void onTextChanged(CharSequence s, int start, int before,
int count) {
}
});
deviceEditText = (EditText) findViewById(R.id.deviceEditText);
deviceEditText.addTextChangedListener(new TextWatcher() {
public void afterTextChanged(Editable s) {
connectDeviceName = deviceEditText.getText().toString();
connectDeviceName = connectDeviceName
.replaceAll("\\(.*\\)", "").trim();
/*
* Do we really have to program our own combobox to input own
* devices? http://stackoverflow.com/questions
* /3024656/how-can-i-show-a-combobox-in-android
* cb.getEditor().setItem(connectDeviceName);
*/
if (connectDeviceName != null
&& !connectDeviceName.equalsIgnoreCase("")) {
preferences
.edit()
.putString(connectTypeName + "_" + PropName_SerialPort,
connectDeviceName).commit();
Core.getSingleInstance().writeDataPool(
DP_ACTUAL_CONNECT_ID,
connectDeviceName);
}
}
public void beforeTextChanged(CharSequence s, int start, int count,
int after) {
}
public void onTextChanged(CharSequence s, int start, int before,
int count) {
}
});
mDeviceSpinner = (Spinner) findViewById(R.id.BTDeviceSpinner);
mDeviceSpinner
.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
public void onItemSelected(AdapterView<?> parent,
View view, int pos, long id) {
Object item = parent.getItemAtPosition(pos);
if (item instanceof PortInfo) {
connectDeviceName = ((PortInfo) item).getDevice();
} else {
connectDeviceName = item.toString();
connectDeviceName = connectDeviceName.replaceAll(
"\\(.*\\)", "").trim();
/*
* Do we really have to program our own combobox to
* input own devices?
* http://stackoverflow.com/questions
* /3024656/how-can-i-show-a-combobox-in-android
* cb.getEditor().setItem(connectDeviceName);
*/
}
if (connectDeviceName != null
&& !connectDeviceName.equalsIgnoreCase("")) {
deviceEditText.setText(connectDeviceName);
}
}
public void onNothingSelected(AdapterView<?> parent) {
deviceEditText.setText("");
}
});
String actualScriptDir = preferences.getString(
PropName_ScriptDir, Environment
.getExternalStorageDirectory().getPath() + "/OOBD/");
Core.getSingleInstance().writeDataPool(DP_SCRIPTDIR, actualScriptDir);
Core.getSingleInstance().writeDataPool(
DP_WWW_LIB_DIR,
preferences.getString(PropName_LibraryDir,
actualScriptDir + PropName_LibraryDirDefault));
connectTypeName = preferences.getString(
PropName_ConnectType, connectTypeName);
Core.getSingleInstance().writeDataPool(
DP_ACTUAL_CONNECTION_TYPE, connectTypeName);
connectDeviceName = preferences.getString(connectTypeName + "_"+PropName_SerialPort,
connectDeviceName);
Core.getSingleInstance().writeDataPool(DP_ACTUAL_CONNECT_ID,
connectDeviceName);
ArrayList<Archive> files = Factory.getDirContent(actualScriptDir);
Core.getSingleInstance().writeDataPool(DP_LIST_OF_SCRIPTS, files);
updateUI();
}
protected void onRestart() {
super.onRestart();
updateUI();
}
private void updateUI() {
if (preferences != null) {
connectTypeName = preferences.getString(
PropName_ConnectType,
PropName_ConnectTypeBT);
connectDeviceName = preferences.getString(connectTypeName
+ "_"+PropName_SerialPort, "");
pgpEnabled.setChecked(preferences.getBoolean("PGPENABLED", false));
httpEnabled.setChecked(preferences.getString(
PropName_UIHander, "UIHandler")
.equalsIgnoreCase(UIHANDLER_WS_NAME));
}
urlEditText.setText(preferences.getString(connectTypeName + "_"
+ PropName_ConnectServerURL,
PropName_KadaverServerDefault));
wsProxyHostEditText.setText(preferences.getString(connectTypeName + "_"
+ PropName_ProxyHost, ""));
wsProxyPortEditText.setText(Integer.toString(preferences.getInt(connectTypeName + "_"
+ PropName_ProxyPort, 0)));
deviceEditText.setText(connectDeviceName);
Class<OOBDPort> value = supplyHardwareConnects.get(connectTypeName);
try { // tricky: try to call a static method of an interface, where a
// interface don't have static values by definition..
// Class[] parameterTypes = new Class[]{};
java.lang.reflect.Method method = value.getMethod("getPorts",
new Class[] {}); // no parameters
Object instance = null;
portList = (PortInfo[]) method.invoke(instance, new Object[] {}); // no
// parameters
} catch (Exception ex) {
Logger.getLogger(Core.class.getName())
.log(Level.WARNING,
"can't call static method 'getPorts' of "
+ value.getName());
ex.printStackTrace();
}
for (int i = 0; i < connectTypeSpinner.getAdapter().getCount(); i++) {
if (connectTypeName.equals(connectTypeSpinner.getAdapter().getItem(
i))) {
connectTypeSpinner.setSelection(i);
break;
}
}
int portListIndex = -1;
PortInfo[] portCopyPlusOne = new PortInfo[portList.length + 1]; // needed
// maybe
// later,
// in
// case
// the
// port
// is
// not
// part
// of
// the
// port
// list,
// which
// was
// delivered
// by
// the
// port-getPorts()
// function
for (int i = 0; i < portList.length; i++) {
portCopyPlusOne[i + 1] = portList[i];
if (portList[i].getDevice().equals(connectDeviceName)) {
portListIndex = i;
}
}
if (portListIndex == -1) { // now we use the List, which has space on
// item[0] to add the port which was not
// found in the device list
portCopyPlusOne[0] = new PortInfo(connectDeviceName,
connectDeviceName);
ArrayAdapter<PortInfo> adapter = new ArrayAdapter<PortInfo>(this,
android.R.layout.simple_spinner_item, portCopyPlusOne);
mDeviceSpinner.setAdapter(adapter);
mDeviceSpinner.setSelection(0);
} else {
ArrayAdapter<PortInfo> adapter = new ArrayAdapter<PortInfo>(this,
android.R.layout.simple_spinner_item, portList);
mDeviceSpinner.setAdapter(adapter);
mDeviceSpinner.setSelection(portListIndex);
}
int pgp = checkKeyFiles();
String pgpStatusText = "No PGP keys available";
if (pgp == 0) { // all ok
pgpStatusText = "All Keys in place";
}
if ((pgp & 0x04) > 0) { // no group key
pgpStatusText = "Missing Group Key File !!";
}
if ((pgp & 0x01) > 0) {// new group key
pgpStatusText = "New Group Key File is waiting for import";
}
if ((pgp & 0x08) > 0) { // no user key
pgpStatusText = "Missing User Key File !!";
}
if ((pgp & 0x02) > 0) {// new user key
pgpStatusText = "New User Key File is waiting for import";
}
if ((pgp == (0x04 + 0x04))) {// no keys
pgpStatusText = "No PGP keys available";
}
pgpStatus.setText("PGP Key Status: " + pgpStatusText);
if (pgp != 0) {
preferences.edit()
.putBoolean(PropName_PGPEnabled, false)
.commit();
pgpEnabled.setSelected(false);
pgpEnabled.setEnabled(false);
if ((pgp & (0x02 + 0x01)) > 0) { // any new keys there
pgpImportKeys.setText("Import PGP keys now");
} else {
pgpImportKeys.setText("No PGP Keys available");
}
} else {
pgpEnabled.setEnabled(true);
pgpImportKeys.setText("DELETE PGP keys now");
}
// taken from http://stackoverflow.com/a/4401945
getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN);
}
private int checkKeyFiles() {
Boolean userKeyExist;
Boolean groupKeyExist;
Boolean newUserKeyExist;
Boolean newGroupKeyExist;
try {
FileInputStream keyfile = openFileInput(PGP_USER_KEYFILE_NAME);
userKeyExist = keyfile != null;
keyfile.close();
} catch (Exception e) {
userKeyExist = false;
}
try {
FileInputStream keyfile = openFileInput(PGP_GROUP_KEYFILE_NAME);
groupKeyExist = keyfile != null;
keyfile.close();
} catch (Exception e) {
groupKeyExist = false;
}
try {
InputStream keyfile = OOBDApp.getInstance().generateResourceStream(
FT_KEY_IMPORT,
PGP_USER_KEYFILE_NAME);
newUserKeyExist = keyfile != null;
keyfile.close();
} catch (Exception e) {
newUserKeyExist = false;
}
try {
InputStream keyfile = OOBDApp.getInstance().generateResourceStream(
FT_KEY_IMPORT,
PGP_GROUP_KEYFILE_NAME);
newGroupKeyExist = keyfile != null;
keyfile.close();
} catch (Exception e) {
newGroupKeyExist = false;
}
return (userKeyExist ? 0 : 8) + (groupKeyExist ? 0 : 4)
+ (newUserKeyExist ? 2 : 0) + (newGroupKeyExist ? 1 : 0);
}
private void deleteKeyFiles() {
deleteFile(PGP_USER_KEYFILE_NAME);
deleteFile(PGP_GROUP_KEYFILE_NAME);
}
private void importKeyFiles() {
if (importsingleKeyFile(PGP_USER_KEYFILE_NAME,
PGP_USER_KEYFILE_NAME)) {
File f = new File(OOBDApp.getInstance().generateUIFilePath(
FT_KEY_IMPORT,
PGP_USER_KEYFILE_NAME));
f.delete();
}
if (importsingleKeyFile(PGP_GROUP_KEYFILE_NAME,
PGP_GROUP_KEYFILE_NAME)) {
File f = new File(OOBDApp.getInstance().generateUIFilePath(
FT_KEY_IMPORT,
PGP_GROUP_KEYFILE_NAME));
f.delete();
}
}
private boolean importsingleKeyFile(String from, String to) {
FileOutputStream fos;
InputStream inFile = OOBDApp.getInstance().generateResourceStream(
FT_KEY_IMPORT, from);
if (inFile != null) {
try {
fos = openFileOutput(to, Context.MODE_PRIVATE);
org.apache.commons.io.IOUtils.copy(inFile, fos);
inFile.close();
fos.close();
return true;
} catch (IOException e) {
// e.printStackTrace(); no stacktrace needed
}
}
return false;
}
}