/**
* MenuViewActivity.java
* @author Marco Secchiero
*
* This activity is the core of the application. It realizes all client-side
* logic implemented by ConnectionManager and ManagerStaticBuilder classes.
* The menu is an ExpandableListView where for each category shows the
* items available.
* Each item can be ordered by selecting and long-pressing it to increase
* (or decrease) the quantity.
* Finally the total order is sent to the server by a menu-button.
*/
package wfta.client.gui;
import java.io.Serializable;
import java.util.Vector;
import wfta.client.ConnectionManager;
import wfta.client.ConnectionStatus;
import wfta.client.ManagerStaticBuilder;
import wfta.common.Item;
import wfta.client.MessageType;
import android.app.Dialog;
import android.app.ExpandableListActivity;
import android.app.ProgressDialog;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.ContextMenu;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ExpandableListView;
import android.widget.TextView;
import android.widget.ExpandableListView.ExpandableListContextMenuInfo;
public class MenuViewActivity extends ExpandableListActivity {
// menu item constants
private static final int MENU_SEND = 0;
private static final int MENU_EXIT = 1;
private static final int INC_QTY = 0;
private static final int DEC_QTY = 1;
private static final int GET_DESC = 2;
private static final int ESC = 3;
private static final int DIALOG_SEND_ORDER = 0;
private static final int DIALOG_EXIT = 1;
private static final int PROGRESS_DIALOG = 2;
private static final int ERROR_DIALOG = 3;
private static MenuItem MENU_ITEM;
/**
* the progress thread
*/
private ProgressThread progrThread = null;
/**
* the connection
*/
private ConnectionManager _manager;
/**
* the expandable list
*/
private MyExpList _expList;
/**
* the TextView of the status of the sending
*/
private TextView messagesLst;
private boolean resetted = false;
/**
* Handler that reads and shows on a Dialog all informative and error
* messages received from the server
*/
final Handler handlerSend = new Handler() {
public void handleMessage(Message msg) {
if (_manager.IsServerConnected() == ConnectionStatus.ERROR_DISCONECTED) {
// something gone wrong
messagesLst.setText("ERROR! Server unreachable!");
resetted = true;
} else {
Vector<String> info_messages = (Vector<String>) msg.getData().getSerializable("info");
Vector<String> err_messages = (Vector<String>) msg.getData().getSerializable("err");
if (err_messages != null) {
for (String m: err_messages){
messagesLst.append(m+'\n');
}
_manager.ResetConfiguration();
}
else for (String m: info_messages){
messagesLst.append(m+'\n');
}
}
}
};
/**
* Nested class that performs progress calculations (counting)
* @author Marco Secchiero
*/
private class ProgressThread extends AsyncTask {
private int idDialog;
private int total;
/**
* Constructor
* @param h - the Handler that manages all progress' comunication
*/
public ProgressThread(int i) {
idDialog = i;
}
@Override
protected Object doInBackground(Object... params) {
// TODO Auto-generated method stub
try {
total = 0;
while (total < 100) {
Thread.sleep(10);
total++;
}
} catch (Exception e) {
showDialog(ERROR_DIALOG);
}
return null;
}
/**
* Called when the method doInBackground is terminated
*/
@Override
protected void onPostExecute(Object result) {
this.cancel(true);
removeDialog(PROGRESS_DIALOG);
showDialog(idDialog);
}
}
/**
* Nested class that listens for server messages and shows them in a Dialog
* @author Marco Secchiero
*/
private class SendThread extends AsyncTask {
private TextView messagesLst;
private Handler mHandler;
public SendThread(Handler h) {
mHandler = h;
}
@Override
protected Object doInBackground(Object... arg0) {
// TODO Auto-generated method stub
Vector<String> err_messages = null;
Vector<String> info_messages = null;
MenuViewActivity.this.messagesLst.setText("Order total price: "+_manager.GetTotalPrice()+"\n");
while (_manager.IsServerConnected()==ConnectionStatus.CONNECTED
&& err_messages==null) {
// distinguish informative messages from error messages
info_messages = _manager.GetUnreadedMessages(MessageType.INFORMATIVE);
if (_manager.ThereAreErrors())
err_messages = _manager.GetUnreadedMessages(MessageType.ERROR);
/*
* set the handler to the object (Message) containing all information
* to print on the send dialog
*/
Message msg = mHandler.obtainMessage();
Bundle b = new Bundle();
b.putSerializable("info", info_messages);
b.putSerializable("err", err_messages);
b.putSerializable("textview", (Serializable) messagesLst);
msg.setData(b);
mHandler.sendMessage(msg);
}
// check if there are unread messages
if (_manager.IsServerConnected() == ConnectionStatus.CLOSED_BY_SERVER ||
_manager.IsServerConnected() == ConnectionStatus.ERROR_DISCONECTED){
info_messages = _manager.GetUnreadedMessages(MessageType.INFORMATIVE);
Message msg = mHandler.obtainMessage();
Bundle b = new Bundle();
b.putSerializable("info", info_messages);
b.putSerializable("err", err_messages);
b.putSerializable("textview", (Serializable) messagesLst);
msg.setData(b);
mHandler.sendMessage(msg);
// disable menu item
if (_manager.IsServerConnected() == ConnectionStatus.CLOSED_BY_SERVER)
MENU_ITEM.setEnabled(false);
}
return messagesLst;
}
}
/**
* Dialog's button OnClickListener
* @author Marco Secchiero
*
*/
protected class OKListener implements OnClickListener {
private int id;
public OKListener(int id) {
this.id = id;
}
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
removeDialog(id);
if (id == DIALOG_EXIT){
_manager.OnClose();
finish();
}
}
}
/**
* Dialog's button OnClickListener
* @author Marco Secchiero
*/
protected class OnGeneralListener implements OnClickListener {
private Dialog dialog;
public OnGeneralListener(Dialog d) {
dialog = d;
}
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
dialog.dismiss();
}
}
/**
* Dialog's button OnClickListener to exit
* @author Marco Secchiero
*
*/
protected class OKExitListener implements OnClickListener {
private Dialog dialog;
public OKExitListener(Dialog dialog) {
this.dialog = dialog;
}
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
removeDialog(DIALOG_EXIT);
_manager.OnClose();
finish();
}
}
/**
* This method is called when a dialog is created
* @param id: identifies a choice
* @return the dialog
*/
protected Dialog onCreateDialog(int id) {
//if (progrDialog != null) progrDialog.dismiss();
Dialog dialog = new Dialog(this);
dialog.setContentView(wfta.client.R.layout.dialog);
// creates the button and assigns it to the dialog
Button okBtn = (Button) dialog.findViewById(wfta.client.R.id.button);
okBtn.setText("Ok");
switch(id) {
case DIALOG_SEND_ORDER:
// called when "Send Order" is selected from the menu button
okBtn.setOnClickListener(new OKListener(DIALOG_SEND_ORDER));
dialog.setTitle("Sending details...");
messagesLst = (TextView) dialog.findViewById(wfta.client.R.id.text);
new SendThread(handlerSend).execute();
return dialog;
case DIALOG_EXIT:
// called when "Exit" is selected from the menu button
okBtn.setOnClickListener(new OKListener(DIALOG_EXIT));
dialog.setTitle("Exit Application");
TextView message = (TextView) dialog.findViewById(wfta.client.R.id.text);
message.setText("Thanks and have a nice meal! Goodbye!");
return dialog;
case PROGRESS_DIALOG:
// launch a progress dialog
ProgressDialog progrDialog = new ProgressDialog(this);
progrDialog.setMessage("Sending order...");
progrDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
progrThread = new ProgressThread(DIALOG_SEND_ORDER);
progrThread.execute();
return progrDialog;
case ERROR_DIALOG:
// called to notify a connection error to the user
dialog.setTitle("Error!");
TextView messages = (TextView) dialog.findViewById(wfta.client.R.id.text);
messages.setText("Connection Error!");
return dialog;
}
return null;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(wfta.client.R.layout.main);
try {
_manager = ManagerStaticBuilder.GetLastConnection();
_expList = new MyExpList(this);
} catch (Exception e) {
// TODO Auto-generated catch block
showDialog(ERROR_DIALOG);
}
setListAdapter( _expList.getExpandableListAdapter());
registerForContextMenu(getExpandableListView());
}
/**
* Creates the menu by adding menu items
* @param menu: the menu
*/
public boolean onCreateOptionsMenu(Menu menu) {
menu.add(0,MENU_SEND,0,"Send order");
menu.add(0,MENU_EXIT,0,"Exit");
return true;
}
/**
* Defines action to perform on option item selection
* @param item: a MenuItem
*/
public boolean onOptionsItemSelected(MenuItem item) {
switch(item.getItemId()) {
case MENU_SEND:
try {
/*
* send orders and show the progress dialog, when it's completed
* a dialog contained sending details is shown to the user
*/
// useful for disable the menu item at the end of the sending
MENU_ITEM = item;
if (resetted) _manager.ResetConnection();
if (_expList.getQtyOrdered()>0) {
// be sure that the user has selected one or more items
_manager.SendOrders();
showDialog(PROGRESS_DIALOG);
}
return true;
} catch (Exception e) {
// TODO Auto-generated catch block
showDialog(ERROR_DIALOG);
}
case MENU_EXIT:
/*
* launch a progress dialog, when it's completed disconnect the client
* and close the application
*/
ProgressDialog progrDialog = new ProgressDialog(this);
progrDialog.setMessage("Disconnecting...");
progrDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
progrDialog.show();
progrThread = new ProgressThread(DIALOG_EXIT);
progrThread.execute();
return true;
}
return false;
}
/**
* Creates a context menu by long pressing an expandable list item
* @param menu: the menu
* @param v: the view
* @param menuInfo: the context menu information
*/
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
ExpandableListContextMenuInfo info =
(ExpandableListContextMenuInfo)menuInfo;
// show the context menu only if a child (menu item) is selected, not a category
if (ExpandableListView.getPackedPositionChild(info.packedPosition)>=0) {
super.onCreateContextMenu(menu, v, menuInfo);
menu.add(0, INC_QTY, 0, "Increase quantity to order");
menu.add(0, DEC_QTY, 0, "Decrease quantity to order");
menu.add(0, GET_DESC,0, "Item Description");
menu.add(0, ESC, 0, "Back");
}
}
/**
* Actions perform when menu item of context menu are selected
* @param item: the menu item
*/
public boolean onContextItemSelected(MenuItem item) {
ExpandableListContextMenuInfo info =
(ExpandableListContextMenuInfo)item.getMenuInfo();
switch(item.getItemId()) {
case INC_QTY:
// Increments quantity to order
_expList.incrementQuantity(ExpandableListView.getPackedPositionGroup(info.packedPosition),
ExpandableListView.getPackedPositionChild(info.packedPosition));
_manager.IncrementOrder(
(int) _expList.getExpandableListAdapter().getChildId(
ExpandableListView.getPackedPositionGroup(info.packedPosition),
ExpandableListView.getPackedPositionChild(info.packedPosition)));
return true;
case DEC_QTY:
// Decrements quantity to order
_expList.decrementQuantity(ExpandableListView.getPackedPositionGroup(info.packedPosition),
ExpandableListView.getPackedPositionChild(info.packedPosition));
_manager.DecrementOrder(
(int) _expList.getExpandableListAdapter().getChildId(
ExpandableListView.getPackedPositionGroup(info.packedPosition),
ExpandableListView.getPackedPositionChild(info.packedPosition)));
return true;
case GET_DESC:
// Shows item description
Dialog dialog = new Dialog(this);
dialog.setContentView(wfta.client.R.layout.dialog);
// creates the button and assigns it to the dialog
Button okBtn = (Button) dialog.findViewById(wfta.client.R.id.button);
okBtn.setText("Ok");
okBtn.setOnClickListener(new OnGeneralListener(dialog));
dialog.setTitle("Item Description");
TextView text = (TextView) dialog.findViewById(wfta.client.R.id.text);
Item child = (Item) _expList.getExpandableListAdapter().getChild(
ExpandableListView.getPackedPositionGroup(info.packedPosition),
ExpandableListView.getPackedPositionChild(info.packedPosition));
text.setText(child.getDescription());
dialog.show();
return true;
case ESC:
return true;
}
return false;
}
}
|