package glaukon.tutorials.tipcalc;
// Import statements let us use code that's not in our project's package.
import java.util.ArrayList;
import android.app.Activity;
import android.os.Bundle;
import android.view.Menu;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnFocusChangeListener;
import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.SeekBar;
import android.widget.SeekBar.OnSeekBarChangeListener;
import android.widget.TableLayout;
import android.widget.TableRow;
import android.widget.TextView;
// MainActivity inherits from the Activity class, but also from the OnClickListener, OnFocusChangeListener, and OnSeekBarChangeListener interfaces.
public class MainActivity extends Activity implements OnClickListener, OnFocusChangeListener, OnSeekBarChangeListener {
// Variable declarations go here.
// totalTip is calculated as tipPercentValue * grandTotal.
private double totalTip;
private double tipPercentValue;
// tax is an absolute number, and taxPercentValue is calculated as tax / grandTotal.
private double tax;
private double taxPercentValue;
// grandTotal does not include tip or tax, but is simply the sum of all the Diners' totals.
private double grandTotal;
// Next, we have the UI elements we made in the layout XML file.
private TableLayout mainTable;
private ImageButton addDinerButton;
// firstCustomer, amount1of1, addButton1, textSplit1, and textSplit1Dollar are all attributes of the first Diner.
private EditText firstCustomer;
private EditText amount1of1;
private ImageButton addButton1;
private TextView textSplit1;
private TextView textSplit1Dollar;
private EditText tipPercent;
private TextView tipDollar;
private SeekBar tipSlider;
private EditText taxDollar;
private TextView textGTotal;
// dinerList will be used to organize all Diners in one outer-object.
private ArrayList<Diner> dinerList;
// Replace the onCreate method we inherited from Activity with our own code using @Override.
@Override
protected void onCreate(Bundle savedInstanceState) {
// First, run Activity's version of onCreate.
super.onCreate(savedInstanceState);
// Inflate the UI using activity_mail.xml in the layout folder.
setContentView(R.layout.activity_main);
// Set all doubles to 0, except tipPercent, which defaults to 15%.
// Note that we use 0.0 instead of 0. Doesn't really matter, just reminds us they're doubles, not ints.
totalTip = 0.0;
tipPercentValue = 0.15;
tax = 0.0;
taxPercentValue = 0.0;
grandTotal = 0.0;
// Retrieve all the Views we need from our UI using findViewById.
// Then assign them to variables we declared earlier.
mainTable = (TableLayout) findViewById(R.id.mainTable);
addDinerButton = (ImageButton) findViewById(R.id.addDinerButton);
firstCustomer = (EditText) findViewById(R.id.firstCustomer);
amount1of1 = (EditText) findViewById(R.id.amount1of1);
addButton1 = (ImageButton) findViewById(R.id.addButton1);
textSplit1 = (TextView) findViewById(R.id.textSplit1);
textSplit1Dollar = (TextView) findViewById(R.id.textSplit1Dollar);
tipDollar = (TextView) findViewById(R.id.tipDollar);
tipPercent = (EditText) findViewById(R.id.tipPercent);
tipSlider = (SeekBar) findViewById(R.id.tipSlider);
taxDollar = (EditText) findViewById(R.id.taxDollar);
textGTotal = (TextView) findViewById(R.id.textGTotal);
// Make a new ArrayList called dinerList, then make the first Diner using the Views from our UI.
// Finally, add that Diner to dinerList.
dinerList = new ArrayList<Diner>();
Diner diner = new Diner(firstCustomer, amount1of1, addButton1, textSplit1, textSplit1Dollar);
dinerList.add(diner);
// Set up listeners.
firstCustomer.setOnFocusChangeListener(this);
amount1of1.setOnFocusChangeListener(this);
tipPercent.setOnFocusChangeListener(this);
taxDollar.setOnFocusChangeListener(this);
addDinerButton.setOnClickListener(this);
addButton1.setOnClickListener(this);
tipSlider.setOnSeekBarChangeListener(this);
}
// Replace the onCreateOptiosnMenu method we inherited from Activity with our own code using @Override.
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the options menu using activity_main.xml in the menu folder.
getMenuInflater().inflate(R.menu.activity_main, menu);
return true;
}
// Implement the onClick method of OnClickListener.
@Override
public void onClick(View v) {
// If it's addDinerButton that was clicked, do this.
if (v == addDinerButton) {
// Create a 2 TableRows, 2 EditTexts, 1 ImageButton, and 2 TextViews.
// Set their attributes based on inflated Views from the UI.
TableRow row1 = new TableRow(this);
EditText et1 = new EditText(this);
et1.setText("Customer");
et1.setSelectAllOnFocus(true);
et1.setInputType(firstCustomer.getInputType());
et1.setGravity(firstCustomer.getGravity());
et1.setLayoutParams(firstCustomer.getLayoutParams());
et1.setWidth(firstCustomer.getWidth());
et1.setOnFocusChangeListener(this);
EditText et2 = new EditText(this);
et2.setText("$0.00");
et2.setSelectAllOnFocus(true);
et2.setInputType(amount1of1.getInputType());
et2.setGravity(amount1of1.getGravity());
et2.setLayoutParams(amount1of1.getLayoutParams());
et2.setWidth(amount1of1.getWidth());
et2.setOnFocusChangeListener(this);
// Make sure the order EditText gets selected automatically.
et2.requestFocus();
ImageButton ib = new ImageButton(this);
ib.setImageResource(R.drawable.additem);
// Remember to set the listener.
ib.setOnClickListener(this);
// Add Views to rows, then add rows to mainTable.
// Use a for loop to get the right row numbers to insert rows at.
// Make sure to account for every order of every Diner.
row1.addView(et1);
row1.addView(et2);
row1.addView(ib);
int rowIndex = 1;
for (int i = 0; i < dinerList.size(); i++) {
rowIndex += dinerList.get(i).orderList.size();
}
mainTable.addView(row1, rowIndex);
TableRow row2 = new TableRow(this);
TextView tv1 = new TextView(this);
tv1.setText(et1.getText().toString());
tv1.setGravity(textSplit1.getGravity());
TextView tv2 = new TextView(this);
tv2.setText(et2.getText().toString());
tv2.setGravity(textSplit1Dollar.getGravity());
row2.addView(tv1);
row2.addView(tv2);
// Add 11 static rows to rowIndex, as well as one row for each Diner in the bill splitting portion of app.
mainTable.addView(row2, rowIndex + 11 + dinerList.size());
// Make a new Diner and add it to dinerList.
Diner diner = new Diner(et1, et2, ib, tv1, tv2);
dinerList.add(diner);
// If it's not addDinerButton, do this.
} else {
// Use a for loop to run though all of dinerList.
// If any one of their ibAddOrder ImageButtons were clicked, do this.
for (int i = 0; i < dinerList.size(); i++) {
if (v == dinerList.get(i).ibAddOrder) {
// Create a row, then 2 EditTexts.
// The first is a dummy one used to push the second one into the second column.
// Again, grab attributes from Views inflated by XML.
TableRow row3 = new TableRow(this);
EditText emptyEditText = new EditText(this);
emptyEditText.setVisibility(4);
EditText newOrder = new EditText(this);
newOrder.setText("$0.00");
newOrder.setSelectAllOnFocus(true);
newOrder.setInputType(amount1of1.getInputType());
newOrder.setGravity(amount1of1.getGravity());
newOrder.setLayoutParams(amount1of1.getLayoutParams());
newOrder.setWidth(amount1of1.getWidth());
newOrder.setOnFocusChangeListener(this);
newOrder.requestFocus();
row3.addView(emptyEditText);
row3.addView(newOrder);
// For loop again to get the right row number to insert the row at.
int rowIndex2 = 1;
for (int j = 0; j <= i; j++) {
rowIndex2 += dinerList.get(j).orderList.size();
}
mainTable.addView(row3, rowIndex2);
// Add the order to the correct Diner's orderList.
dinerList.get(i).newOrder(newOrder);
}
}
}
}
// Implement the onFocusChange method of OnFocusChangeListener.
@Override
public void onFocusChange(View v, boolean hasFocus) {
// If tipPercent lost focus, do this.
if (v == tipPercent && hasFocus == false) {
// Get the tip from tipPercent, making sure to adjust it so that Double.parseDouble can handle it.
tipPercentValue = Double.parseDouble(((EditText) v).getText().toString().replace("%", ""));
// If the user set the tip higher than 50, bring it back down to 50.
if (tipPercentValue > 50) {
tipPercentValue = 50;
}
// Use setProgress to trigger the code in onProgressChanged.
// Note we have to cast to int because tipPercentValue is a double.
tipSlider.setProgress((int) tipPercentValue);
// If the user didn't change the tip, then setProgress won't actually move tipSlider.
// Thus, onProgressChange won't get triggered, and tipPercent won't get divided by 100.
// To make sure tipPercent is a percentage, and not a whole number, we check if it's greater than 0.5.
// If it is, we need to change it back to a percentage here, since onProgressChanged was never triggered.
if (tipPercentValue > .5) {
tipPercentValue = tipPercentValue / 100;
}
// We don't need to do anything else in this block, since onProgressChanged should get triggered.
// The rest of the stuff that needs to happen will happen in onProgressChanged.
// And if it was not triggered, then that means nothing changed and nothing needs to happen anyway.
// If tipPercent didn't trigger onFocusChange, check if taxDollar lost focus. If so, do this.
} else if (v == taxDollar && hasFocus == false) {
// Set tax to what's inside taxDollar. Then set taxDollar to tax.
// We need to set taxDollar to back to tax for the sake of formatting.
tax = Double.parseDouble(((EditText) v).getText().toString().replace("$", "").replace(",", ""));
((EditText) v).setText("$" + String.format("%,.2f", tax));
// Then we run calcGrandTotal to update everything.
calcGrandTotal();
// Finally, if it wasn't tipPercent or taxDollar, do this stuff.
} else {
// Use a for loop to run though all of dinerList.
for (int i = 0; i < dinerList.size(); i++) {
// If any one of their etName EditTexts lost focus, do this.
if (v == dinerList.get(i).etName && hasFocus == false) {
// Set the tvName TextView using the Diner's setName method.
dinerList.get(i).setName();
} else {
// Otherwise, run through the orderList for each Diner.
for (int j = 0; j < dinerList.get(i).orderList.size(); j++) {
// If any EditTexts in the orderList lost focus, do this.
if (v == dinerList.get(i).orderList.get(j) && hasFocus == false) {
// Run the updateTotal method to calculate the Diner's new total.
// Then use calcGrandTotal to update everthing that needs updating.
dinerList.get(i).updateTotal((EditText) v);
calcGrandTotal();
}
}
}
}
}
}
// Implement the onProgressChanged method of OnSeekBarChangeListener.
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
// Check to make sure the correct seekBar was changed (technically not necessary since we only have one).
if (seekBar == tipSlider) {
// Set tipPercentValue to the progress of tipSlider, then set the tipPercent EditText accordingly.
// Make sure tipPercentValue goes back to being a percent value, not a whole number, at the end.
tipPercentValue = progress;
tipPercent.setText(String.format("%.0f", tipPercentValue) + "%");
tipPercentValue = tipPercentValue / 100;
// Finally, update everything else with calcGrandTotal.
calcGrandTotal();
}
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
// Not needed.
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
// Not needed.
}
// This is the final method that gets run every time something changes.
// It does all the major updating that the user sees.
public void calcGrandTotal() {
// Before calculating grandTotal, remember to reset it back to 0 first.
grandTotal = 0.0;
// Go through dinerList and add each Diner's total to grandTotal.
for (int i = 0; i < dinerList.size(); i++) {
grandTotal += dinerList.get(i).total;
}
// Calculate totalTip, then set tipDollar accordingly.
totalTip = grandTotal * tipPercentValue;
tipDollar.setText("$" + String.format("%,.2f", totalTip));
// Then calculate taxPercentValue.
taxPercentValue = tax / grandTotal;
// Use tipPercentValue and taxPercentValue to call every Diner's setTotalText method.
for (int i = 0; i < dinerList.size(); i++) {
dinerList.get(i).setTotalText(tipPercentValue, taxPercentValue);
}
// To finish, set textGTotal to grandTotal + totalTip + tax.
textGTotal.setText("$" + String.format("%,.2f", grandTotal + totalTip + tax));
}
}
package glaukon.tutorials.tipcalc;
// Import statements let us use code that's not in our project's package.
import java.util.ArrayList;
import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.TextView;
public class Diner {
// Declare the variables we'll need.
// total does not include tip or tax.
public double total;
public ArrayList<EditText> orderList;
public EditText etName;
public EditText etFirstOrder;
public ImageButton ibAddOrder;
public TextView tvName;
public TextView tvSplitBill;
// This is the constructor. It runs when you instantiate a Diner.
// It brings together all the Views that are associated with a new Diner object.
public Diner(EditText et1, EditText et2, ImageButton ib, TextView tv1, TextView tv2) {
// total starts off as 0, and is used to keep track of the sum of a Diner's orders.
total = 0.0;
// We also need an ArrayList to keep track of each Diner's orders.
orderList = new ArrayList<EditText>();
etName = et1;
etFirstOrder = et2;
ibAddOrder = ib;
tvName = tv1;
tvSplitBill = tv2;
// Make sure to add the first order to orderList.
orderList.add(etFirstOrder);
}
// This method sets the Text property of tvName to whatever the user changed the Text property of etName to.
public void setName() {
// We need to use toString because getText does not return a string.
tvName.setText(etName.getText().toString());
}
// This method adds a new order to a Diner's orderList.
public void newOrder(EditText newOrder) {
orderList.add(newOrder);
}
// This method updates the Diner's total based its orderList.
// It is run when the user changes focus from an EditText in orderList.
public void updateTotal(EditText toBeUpdated) {
// First, reset total
total = 0.0;
// Then, format the EditText that was just changed
toBeUpdated.setText("$" + String.format("%,.2f", editTextToDouble(toBeUpdated)));
// Finally, go through each order and add it to total. Use editTextToDouble to help.
for (int i = 0; i < orderList.size(); i++) {
total += editTextToDouble(orderList.get(i));
}
}
// We used to have an overloaded updateTotal method here, but we no longer need it.
// public void updateTotal() {
// updateTotal(etFirstOrder);
// }
// This method converts what's inside an EditText to a double, and returns it.
public double editTextToDouble(EditText et) {
double db = 0.0;
db = Double.parseDouble(et.getText().toString().replace("$", "").replace(",", ""));
return db;
}
// This method uses the tip and tax MainActivity gets it to set the proper value for tvSplitBill.
public void setTotalText(double tip, double tax) {
tvSplitBill.setText("$" + String.format("%,.2f", total * (1 + tip) + total * tax));
}
}