Programming     Travel Logs     Life Is Good     Surfing Online     About Me
You’re not going to get rich renting out your time. You must own equity — a piece of a business — to gain your financial freedom.
-Naval Ravikant
2018-07-17 17:52:22

Copy this link when reproducing:
http://www.casperlee.com/en/y/blog/200

Since I've finished the module which allows user to initialize the categories of spending, it is time to implement the key module of this application: Add a Spending Item. Although a quit lot of code is needed, it is still very straightforward, so there is no extra explanations needed, I'll just list all the changes of the code in this article, in stead of explaining too much. Just like before, let's take it easy and enjoy a few beautiful photos first.

/Images/20171003/01.jpg

/Images/20171003/02.jpg

/Images/20171003/03.jpg

/Images/20171003/04.jpg

/Images/20171003/05.jpg

/Images/20171003/06.jpg

1. Open the file "app -> res -> values -> strings.xml", add the following strings into it:

/Images/20171003/101.jpg

2. Open the file "app -> res -> values -> string.xml (zh)", add the following strings into it:

/Images/20171003/102.jpg

    Note:
    I. The captions for the button "Prev" and "Next" are "<" and ">" respectively, but we need to use their escaped strings here: "&lt;" and "&gt;".
    II. For the format of the summary information, since I need 2 placeholders, I need to number them sequentially.

3. Open the file "app -> java -> com.casperlee.personalexpense -> dal -> DBHelper", add the following code to create the Expenditure table:

public class DBHelper extends SQLiteOpenHelper {
    ...
    public static final int DATABASE_VERSION = 5;
    ...
    private void createTablesIfNotExists(SQLiteDatabase db) {

        // create expenditure table
        createTable ="create table if not exists "
                + "Expenditure ("
                + "ID INTEGER PRIMARY KEY AUTOINCREMENT,"
                + "Day TEXT,"
                + "Category INTEGER,"
                + "Amount REAL,"
                + "Remark TEXT,"
                + "T0 TEXT)";
        db.execSQL(createTable);
    }

Note: we need to change the database version to a bigger number so that OnUpgrade will be executed when updating the application.

4. Add a new entity class named "ExpenditureEntity" in the folder "app -> java -> com.casperlee.personalexpense -> entities", and put the following code in:

package com.casperlee.personalexpense.entities;

public class ExpenditureEntity {

    private int id;
    private String day;
    private int categoryID;
    private double amount;
    private String remark;

    @Override
    public String toString() {

        return this.remark;
    }

    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getDay() {
        return day;
    }
    public void setDay(String day) {
        this.day = day;
    }
    public int getCategoryID() {
        return categoryID;
    }
    public void setCategoryID(int categoryID) {
        this.categoryID = categoryID;
    }
    public double getAmount() {
        return amount;
    }
    public void setAmount(double amount) {
        this.amount = amount;
    }
    public String getRemark() {
        return remark;
    }
    public void setRemark(String remark) {
        this.remark = remark;
    }
}

5. Add a new DAL class named "ExpenditureDal" in the folder "app -> java -> com.casperlee.personalexpense -> dal", and put the following code in:

package com.casperlee.personalexpense.dal;

import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;

import com.casperlee.personalexpense.entities.ExpenditureEntity;

public class ExpenditureDal {

    private SQLiteDatabase db;

    public ExpenditureDal() {

        DBHelper helper = DBHelper.getInstance();
        this.db = helper.getWritableDatabase();
    }

    private static ExpenditureDal instance;
    public static ExpenditureDal getInstance() {

        if (ExpenditureDal.instance == null) {

            ExpenditureDal.instance = new ExpenditureDal();
        }

        return ExpenditureDal.instance;
    }

    public void add(ExpenditureEntity entity) {

        String sql = "insert into Expenditure (Day, Category, Amount, Remark) "
                + " values ('" + entity.getDay() + "',"
                + entity.getCategoryID() + "," + entity.getAmount() + ", '"
                + entity.getRemark() + "')";
        db.execSQL(sql);
    }

    public void del(ExpenditureEntity entity) {

        String sql = "delete from Expenditure where ID = " + entity.getId();
        db.execSQL(sql);
    }

    public void update(ExpenditureEntity entity) {

        String sql = "update Expenditure"
                + " set Category = " + entity.getCategoryID() + ","
                + " Day = '" + entity.getDay() + "',"
                + " Amount = " + entity.getAmount() + ","
                + " Remark = '" + entity.getRemark() + "'"
                + " where ID = " + entity.getId();
        db.execSQL(sql);
    }

    public double getSummaryMoneyByDate(String dayStr) {

        String sql = "select sum(Amount) from Expenditure"
                + " where Day like '" + dayStr + "%'";
        Cursor cursor = db.rawQuery(sql, null);
        try {

            if (cursor.getCount() <= 0) {

                return 0;

            } else {

                cursor.moveToFirst();
                return cursor.getDouble(0);
            }
        } finally {

            cursor.close();
        }
    }

    public ExpenditureEntity[] getExpenditureDetailByCondition(String searchCondition) {

        String sql = "select b.* from Category a, Expenditure b" +
                " where a.ID = b.Category" +
                " and " + searchCondition +
                " order by b.Day";

        Cursor cursor = db.rawQuery(sql, null);
        try {

            if (cursor.getCount() <= 0) {

                return null;

            } else {

                ExpenditureEntity[] es = new ExpenditureEntity[cursor.getCount()];

                cursor.moveToFirst();
                int i = 0;
                while (!cursor.isAfterLast()) {

                    ExpenditureEntity e = new ExpenditureEntity();
                    e.setId(cursor.getInt(0));
                    e.setDay(cursor.getString(1));
                    e.setCategoryID(cursor.getInt(2));
                    e.setAmount(cursor.getDouble(3));
                    e.setRemark(cursor.getString(4));
                    es[i++] = e;
                    cursor.moveToNext();
                }

                return es;
            }

        } finally {

            cursor.close();
        }
    }

    public ExpenditureEntity[] getItemsByDate(String dayStr) {

        String sql = "select * from Expenditure where Day like '" + dayStr + "%'";
        Cursor cursor = db.rawQuery(sql, null);
        try {

            if (cursor.getCount() <= 0) {

                return null;

            } else {

                ExpenditureEntity[] es = new ExpenditureEntity[cursor.getCount()];

                cursor.moveToFirst();
                int i = 0;
                while (!cursor.isAfterLast()) {

                    ExpenditureEntity e = new ExpenditureEntity();
                    e.setId(cursor.getInt(0));
                    e.setDay(cursor.getString(1));
                    e.setCategoryID(cursor.getInt(2));
                    e.setAmount(cursor.getDouble(3));
                    e.setRemark(cursor.getString(4));
                    es[i++] = e;
                    cursor.moveToNext();
                }

                return es;
            }
        } finally {

            cursor.close();
        }
    }

    public ExpenditureEntity get(int id) {

        String sql = "select * from Expenditure where ID = " + id;
        Cursor cursor = db.rawQuery(sql, null);
        try {

            if (cursor.getCount() <= 0) {

                return null;

            } else {

                // can only find one record.
                cursor.moveToFirst();

                ExpenditureEntity e = new ExpenditureEntity();
                e.setId(cursor.getInt(0));
                e.setDay(cursor.getString(1));
                e.setCategoryID(cursor.getInt(2));
                e.setAmount(cursor.getDouble(3));
                e.setRemark(cursor.getString(4));
                cursor.moveToNext();

                return e;
            }
        } finally {

            cursor.close();
        }
    }

    @Override
    protected void finalize() throws Throwable {

        this.db.close();
        super.finalize();
    }
}

6. Add a new BLL class named "ExpenditureBll" in the folder "app -> java -> com.casperlee.personalexpense -> bll", and put the following code in:

package com.casperlee.personalexpense.bll;

import android.content.Context;

import com.casperlee.personalexpense.R;
import com.casperlee.personalexpense.dal.ExpenditureDal;
import com.casperlee.personalexpense.entities.ExpenditureEntity;

import java.util.List;

public class ExpenditureBll {

    private ExpenditureDal dal;
    private static ExpenditureBll instance = null;
    public static ExpenditureBll getInstance() {

        if (instance == null) {

            instance = new ExpenditureBll();
        }

        return instance;
    }

    public ExpenditureBll() {

        this.dal = ExpenditureDal.getInstance();
    }

    public String GetMonthString(int year, int month) {

        return String.format("%04d%02d", year, month);
    }
    public String GetDayString(int year, int month, int day){

        return String.format("%04d%02d%02d", year, month, day);
    }

    public void GetDayExpendItems(Context context, List<ExpenditureEntity> dayExpends,
                                  int year, int monthOfYear, int dayOfMonth) {

        String dayStr = this.GetDayString(year, monthOfYear, dayOfMonth);
        dayExpends.clear();
        ExpenditureEntity[] items = ExpenditureDal.getInstance().getItemsByDate(dayStr);
        if (items != null) {

            for (int i = 0; i< items.length; i++) {

                dayExpends.add(items[i]);
            }
        }
    }

    public String getSummaryInfo(Context context,
                                 int year, int monthOfYear, int dayOfMonth) {

        double daySum = ExpenditureDal.getInstance().getSummaryMoneyByDate(
                this.GetDayString(year, monthOfYear, dayOfMonth));
        double monthSum = ExpenditureDal.getInstance().getSummaryMoneyByDate(
                this.GetMonthString(year, monthOfYear));
        String fmt = context.getResources().getString(R.string.fmt_summary_info);
        return String.format(fmt, daySum, monthSum);
    }

    public boolean SaveExpenditure(Context context, ExpenditureEntity e) {

        if (e.getId() == -1) {

            ExpenditureDal.getInstance().add(e);
        } else {

            ExpenditureDal.getInstance().update(e);
        }

        return true;
    }

    public boolean DeleteExpenditure(Context context, ExpenditureEntity e) {

        if (e.getId() == -1) {

            return false;
        }

        ExpenditureDal.getInstance().del(e);
        return true;
    }
}

7. Add a new Activity class named "AddExpenseActivity" in the folder "app -> java -> com.casperlee.personalexpense", and put the following code in:

package com.casperlee.personalexpense;

import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.text.SpannableString;
import android.text.style.UnderlineSpan;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.DatePicker;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.Toast;

import com.casperlee.personalexpense.bll.ExpenditureBll;
import com.casperlee.personalexpense.entities.CategoryEntity;
import com.casperlee.personalexpense.entities.ExpenditureEntity;
import com.casperlee.personalexpense.global.GlobalVars;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;

public class AddExpenseActivity extends AppCompatActivity {

    // Fields
    private Layout ui = null;
    private CategorySpinnerAdapter categoriesAdapter = null;
    private List<ExpenditureEntity> dayExpends = new ArrayList<ExpenditureEntity>();
    private int currentExpendIndex = -1;
    private ExpenditureEntity currentExpend;
    private int currentYear;
    private int currentMonth;
    private int currentDay;

    // Override functions
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_add_expense);
        this.ui = new Layout();
        this.setListeners();
        this.InitDayPicker();
        this.initKeyboard();
    }

    @Override
    protected void onResume() {
        if (getRequestedOrientation() != ActivityInfo.SCREEN_ORIENTATION_PORTRAIT) {
            setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
        }
        super.onResume();

        this.InitCategories();
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {

        getMenuInflater().inflate(R.menu.main, menu);
        return super.onCreateOptionsMenu(menu);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {

        switch (item.getItemId()) {

            case R.id.miSettings:
                Intent intent = new Intent();
                intent.setClass(AddExpenseActivity.this, SettingsActivity.class);
                this.startActivity(intent);
                break;

            case R.id.miAbout:
                Toast.makeText(this, "About", Toast.LENGTH_SHORT).show();
                break;
        }

        return super.onOptionsItemSelected(item);
    }

    // Properties
    private boolean needShowLastCategory;
    public boolean isNeedShowLastCategory() {
        return needShowLastCategory;
    }
    public void setNeedShowLastCategory(boolean needShowLastCategory) {
        this.needShowLastCategory = needShowLastCategory;
    }

    // Private functions
    private void setListeners() {

        this.setViewClickListeners();
        this.setSpinnerItemSelectedListeners();
        this.setViewKeyListeners();
    }
    private void InitDayPicker() {

        Calendar c = Calendar.getInstance();
        int year = c.get(Calendar.YEAR);
        int month = c.get(Calendar.MONTH);
        int day = c.get(Calendar.DAY_OF_MONTH);
        this.ui.DayPicker.init(year, month, day,
                new DayPickerDateChangedListener());
        this.currentYear = year;
        this.currentMonth = month + 1;
        this.currentDay = day;
        this.getExpendItems();
        this.getSummaryInfo();
        this.newExpenditure(true);
    }
    private void initKeyboard() {

        this.ui.MoneyEditText.setInputType(EditorInfo.TYPE_CLASS_PHONE);
    }
    private void InitCategories() {

        if (GlobalVars.Categories == null || GlobalVars.Categories.length <= 0) {

            Toast.makeText(this, getString(R.string.toast_categories_empty),
                    Toast.LENGTH_LONG).show();
            return;
        }

        this.categoriesAdapter = new CategorySpinnerAdapter(this,
                GlobalVars.Categories);
        this.ui.CategorySpinner.setAdapter(this.categoriesAdapter);
        if (this.isNeedShowLastCategory()
                && this.ui.CategorySpinner.getCount() - 2 >= 0) {

            this.ui.CategorySpinner.setSelection(this.ui.CategorySpinner
                    .getCount() - 2);
            this.setNeedShowLastCategory(false);

        } else {

            this.ui.CategorySpinner.setSelection(0);
        }

        this.categoriesAdapter.notifyDataSetChanged();
    }

    private void setViewClickListeners() {

        OnMyViewClickListener l = new OnMyViewClickListener();
        this.ui.PrevButton.setOnClickListener(l);
        this.ui.NextButton.setOnClickListener(l);
        this.ui.AddButton.setOnClickListener(l);
        this.ui.DeleteButton.setOnClickListener(l);
        this.ui.SaveButton.setOnClickListener(l);
        this.ui.TotalMoneyTextView.setOnClickListener(l);
    }
    private void setSpinnerItemSelectedListeners() {

        OnMyItemSelectedListener l = new OnMyItemSelectedListener();
        this.ui.CategorySpinner.setOnItemSelectedListener(l);
    }
    private void setViewKeyListeners() {

        OnMyKeyListener l = new OnMyKeyListener();
        this.ui.MoneyEditText.setOnKeyListener(l);
        this.ui.DescEditText.setOnKeyListener(l);
    }

    private void getExpendItems() {

        ExpenditureBll.getInstance().GetDayExpendItems(this, this.dayExpends,
                this.currentYear, this.currentMonth, this.currentDay);
    }
    private void getSummaryInfo() {

        String summaryString = ExpenditureBll.getInstance().getSummaryInfo(this,
                this.currentYear, this.currentMonth, this.currentDay);
        SpannableString content = new SpannableString(summaryString);
        content.setSpan(new UnderlineSpan(), 0, content.length(), 0);
        this.ui.TotalMoneyTextView.setText(content);
    }
    private void newExpenditure(boolean showSoftInput) {

        this.currentExpend = new ExpenditureEntity();
        this.currentExpend.setId(-1);
        this.currentExpendIndex = -1;
        this.UpdateUI();
        if (showSoftInput) {

            this.ui.MoneyEditText.post(new Runnable() {

                @Override
                public void run() {

                    AddExpenseActivity.this.ui.MoneyEditText.requestFocus();
                    InputMethodManager keyboard = (InputMethodManager)
                            getSystemService(Context.INPUT_METHOD_SERVICE);
                    keyboard.showSoftInput(AddExpenseActivity.this.ui.MoneyEditText, 0);
                }
            });

        } else {

            this.ui.MoneyEditText.post(new Runnable() {

                @Override
                public void run() {

                    AddExpenseActivity.this.ui.MoneyEditText.requestFocus();
                    InputMethodManager keyboard = (InputMethodManager)
                            getSystemService(Context.INPUT_METHOD_SERVICE);
                    keyboard.hideSoftInputFromWindow(AddExpenseActivity.this.ui.MoneyEditText.getWindowToken(), 0);
                }

            });
        }
    }

    private void UpdateUI() {

        if (this.dayExpends.size() > 0) {

            if (this.currentExpendIndex != 0) {

                this.ui.PrevButton.setEnabled(true);

            } else {

                this.ui.PrevButton.setEnabled(false);
            }

            if (this.currentExpendIndex != this.dayExpends.size() - 1) {

                this.ui.NextButton.setEnabled(true);

            } else {

                this.ui.NextButton.setEnabled(false);
            }
        } else {

            this.ui.PrevButton.setEnabled(false);
            this.ui.NextButton.setEnabled(false);
        }

        if (this.currentExpend == null) {

            return;
        }

        if (this.currentExpend.getId() == -1) {

            this.ui.MoneyEditText.setText("");
            this.ui.MoneyEditText.setSelected(true);
            this.ui.DescEditText.setText("");
            this.ui.AddButton.setEnabled(false);
            this.ui.DeleteButton.setEnabled(false);
            this.ui.SaveButton.setEnabled(true);

        } else {

            for (int i = 0; i < this.ui.CategorySpinner.getCount(); i++) {

                if (((CategoryEntity) this.ui.CategorySpinner
                        .getItemAtPosition(i)).getId() == this.currentExpend.getCategoryID()) {

                    this.ui.CategorySpinner.setSelection(i);
                }
            }

            this.ui.MoneyEditText.setText(String
                    .valueOf(this.currentExpend.getAmount()));
            this.ui.DescEditText.setText(this.currentExpend.getRemark());
            this.ui.AddButton.setEnabled(true);
            this.ui.DeleteButton.setEnabled(true);
            this.ui.SaveButton.setEnabled(true);
        }
    }

    private boolean ReadExpenditure() {

        try {

            this.currentExpend.setAmount(Double
                    .parseDouble(this.ui.MoneyEditText.getText().toString()));

        } catch (NumberFormatException ex) {

            Toast.makeText(this, R.string.input_money_invalid,
                    Toast.LENGTH_LONG).show();
            this.ui.MoneyEditText.setSelected(true);
            return false;
        }

        this.currentExpend.setDay(ExpenditureBll.getInstance().GetDayString(
                this.currentYear, this.currentMonth, this.currentDay));
        CategoryEntity c = (CategoryEntity) this.ui.CategorySpinner
                .getSelectedItem();
        this.currentExpend.setCategoryID(c.getId());
        this.currentExpend.setRemark(this.ui.DescEditText.getText().toString());
        return true;
    }
    private boolean SaveExpenditure() {

        if (!this.ReadExpenditure()) {

            return false;
        }

        if (!ExpenditureBll.getInstance().SaveExpenditure(this, this.currentExpend)) {

            return false;
        }

        return true;
    }

    private void ModifyCategories() {

        /*
        Intent intent = new Intent();
        intent.setClass(AddExpenseActivity.this, ModifyCategoryActivity.class);
        intent.putExtra(ModifyCategoryActivity.KEY_INVOKE_TYPE, ModifyCategoryActivity.INVOKE_TYPE_ADD);
        this.startActivity(intent);
        */
    }

    // Event handlers
    private void PerformPrevButtonClick() {

        if (this.dayExpends.size() == 0) {

            return;
        }

        if (this.currentExpend.getId() == -1) {

            this.currentExpendIndex = this.dayExpends.size() - 1;

        } else if (this.currentExpendIndex > 0) {

            this.currentExpendIndex -= 1;
        }

        this.currentExpend = this.dayExpends.get(this.currentExpendIndex);
        this.UpdateUI();
    }
    private void PerformNextButtonClick() {

        if (this.dayExpends.size() == 0) {

            return;
        }

        if (this.currentExpend.getId() == -1) {

            this.currentExpendIndex = this.dayExpends.size() - 1;

        } else if (this.currentExpendIndex < this.dayExpends.size() - 1) {

            this.currentExpendIndex += 1;
        }

        this.currentExpend = this.dayExpends.get(this.currentExpendIndex);
        this.UpdateUI();
    }
    private void PerformDeleteButtonClick() {

        if (this.dayExpends.size() == 0 || this.currentExpend.getId() == -1) {

            return;
        }

        if (!ExpenditureBll.getInstance().DeleteExpenditure(this, this.currentExpend)) {

            return;
        }

        this.getExpendItems();
        this.getSummaryInfo();

        if (this.dayExpends.size() == 0) {

            this.newExpenditure(true);

        } else {

            this.currentExpendIndex = 0;
            this.currentExpend = this.dayExpends.get(0);
        }

        this.UpdateUI();
    }
    private void PerformTotalMoneyTextClick() {

        //TODO
    }
    private void PerformCategorySpinnerItemSelected(int postion) {

        if (postion == this.ui.CategorySpinner.getCount() - 1) {

            // The last item is to add a category.
            this.ModifyCategories();
            return;
        }

        this.ui.MoneyEditText.requestFocus();
    }
    private boolean PerformOnKey(View v, int keyCode, KeyEvent event) {

        if (keyCode == KeyEvent.KEYCODE_ENTER
                && event.getAction() == KeyEvent.ACTION_DOWN) {

            return true;
        }

        if (keyCode == KeyEvent.KEYCODE_ENTER
                && event.getAction() == KeyEvent.ACTION_UP) {

            switch (v.getId()) {
                case R.id.MoneyEditText:
                    break;

                case R.id.DescEditText:
                    return this.ui.SaveButton.performClick();
            }
        }

        return false;
    }


    // Private classes
    private class Layout {

        private DatePicker DayPicker = null;
        private Spinner CategorySpinner = null;
        private EditText MoneyEditText = null;
        private EditText DescEditText = null;
        private Button PrevButton = null;
        private Button NextButton = null;
        private Button AddButton = null;
        private Button DeleteButton = null;
        private Button SaveButton = null;
        private TextView TotalMoneyTextView = null;

        public Layout() {

            this.Initialize();
        }

        private void Initialize() {

            this.DayPicker = (DatePicker) findViewById(R.id.dpDate);
            this.CategorySpinner = (Spinner) findViewById(R.id.CategorySpinner);
            this.MoneyEditText = (EditText) findViewById(R.id.MoneyEditText);
            this.DescEditText = (EditText) findViewById(R.id.DescEditText);
            this.PrevButton = (Button) findViewById(R.id.PrevButton);
            this.NextButton = (Button) findViewById(R.id.NextButton);
            this.AddButton = (Button) findViewById(R.id.AddButton);
            this.DeleteButton = (Button) findViewById(R.id.DeleteButton);
            this.SaveButton = (Button) findViewById(R.id.SaveButton);
            this.TotalMoneyTextView = (TextView) findViewById(R.id.TotalMoneyTextView);
        }
    }
    private class CategorySpinnerAdapter extends BaseAdapter {

        private Context mContext;
        private List<CategoryEntity> mItems = new ArrayList<CategoryEntity>();

        public CategorySpinnerAdapter(Context context, CategoryEntity[] items) {

            this.mContext = context;
            this.mItems.clear();
            for (int i = 0; i < items.length; i++) {

                this.mItems.add(items[i]);
            }
        }

        @Override
        public int getCount() {

            return this.mItems.size();
        }

        @Override
        public Object getItem(int position) {

            return this.mItems.get(position);
        }

        @Override
        public long getItemId(int position) {

            return position;
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {

            TextView btv;
            if (convertView == null) {

                btv = new TextView(this.mContext);

            } else {

                btv = (TextView) convertView;
            }

            String n = this.mItems.get(position).getName();
            btv.setTextSize(20.0f);
            btv.setPadding(4, 8, 4, 8);
            btv.setText(n);

            return btv;
        }
    }
    private class OnMyViewClickListener implements View.OnClickListener {

        @Override
        public void onClick(View v) {

            if (v == AddExpenseActivity.this.ui.SaveButton) {

                if (AddExpenseActivity.this.SaveExpenditure()) {

                    AddExpenseActivity.this.getExpendItems();
                    AddExpenseActivity.this.getSummaryInfo();
                    AddExpenseActivity.this.newExpenditure(true);
                }

            } else if (v == AddExpenseActivity.this.ui.PrevButton) {

                AddExpenseActivity.this.PerformPrevButtonClick();

            } else if (v == AddExpenseActivity.this.ui.NextButton) {

                AddExpenseActivity.this.PerformNextButtonClick();

            } else if (v == AddExpenseActivity.this.ui.AddButton) {

                AddExpenseActivity.this.newExpenditure(true);

            } else if (v == AddExpenseActivity.this.ui.DeleteButton) {

                AddExpenseActivity.this.PerformDeleteButtonClick();

            } else if (v == AddExpenseActivity.this.ui.TotalMoneyTextView) {

                AddExpenseActivity.this.PerformTotalMoneyTextClick();
            }
        }

    }
    private class OnMyItemSelectedListener implements AdapterView.OnItemSelectedListener {

        @Override
        public void onItemSelected(AdapterView<?> parent, View view,
                                   int position, long id) {

            if (parent == AddExpenseActivity.this.ui.CategorySpinner) {

                AddExpenseActivity.this.PerformCategorySpinnerItemSelected(position);
            }
        }

        @Override
        public void onNothingSelected(AdapterView<?> parent) {

        }
    }
    private class OnMyKeyListener implements View.OnKeyListener {

        @Override
        public boolean onKey(View v, int keyCode, KeyEvent event) {

            return AddExpenseActivity.this.PerformOnKey(v, keyCode, event);
        }
    }
    private class DayPickerDateChangedListener implements DatePicker.OnDateChangedListener {

        @Override
        public void onDateChanged(DatePicker view, int year, int monthOfYear,
                                  int dayOfMonth) {

            AddExpenseActivity.this.currentYear = year;
            AddExpenseActivity.this.currentMonth = monthOfYear + 1;
            AddExpenseActivity.this.currentDay = dayOfMonth;
            AddExpenseActivity.this.getExpendItems();
            AddExpenseActivity.this.getSummaryInfo();
            AddExpenseActivity.this.newExpenditure(false);
        }

    }
}

Here is the code of its layout ("app -> res -> layout -> activity_add_expense.xml"):

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_add_expense"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#FF000000"
    android:gravity="center_horizontal"
    tools:context="com.casperlee.personalexpense.AddExpenseActivity">

    <LinearLayout
        android:id="@+id/layout0"
        android:paddingTop="8dp"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center">
        <DatePicker
            android:id="@+id/dpDate"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:theme="@android:style/Theme.Light.NoTitleBar"/>
    </LinearLayout>

    <LinearLayout
        android:id="@+id/layout1"
        android:paddingTop="8dp"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@+id/layout0"
        android:gravity="center">

        <TextView
            android:id="@+id/textView1"
            android:layout_width="100dp"
            android:layout_height="wrap_content"
            android:text="@string/caption_category"
            android:textColor="@color/colorWhite"
            android:gravity="right"
            android:textAppearance="?android:attr/textAppearanceLarge" />

        <Spinner
            android:id="@+id/CategorySpinner"
            android:layout_width="200dp"
            android:layout_height="wrap_content"
            android:theme="@android:style/Theme.Light"
            android:textAppearance="?android:attr/textAppearanceLarge"/>

    </LinearLayout>
    <LinearLayout
        android:id="@+id/layout2"
        android:paddingTop="8dp"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@+id/layout1"
        android:gravity="center">
        <TextView
            android:id="@+id/textView2"
            android:layout_width="100dp"
            android:layout_height="wrap_content"
            android:text="@string/caption_money"
            android:textColor="@color/colorWhite"
            android:gravity="right"
            android:textAppearance="?android:attr/textAppearanceLarge" />

        <EditText
            android:id="@+id/MoneyEditText"
            android:focusable="true"
            android:focusableInTouchMode="true"
            android:layout_width="200dp"
            android:layout_height="wrap_content"
            android:ems="10"
            android:theme="@android:style/Theme.Light"
            android:inputType="numberDecimal">
            <requestFocus />
        </EditText>

    </LinearLayout>
    <LinearLayout
        android:id="@+id/layout3"
        android:paddingTop="8dp"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@+id/layout2"
        android:gravity="center">

        <TextView
            android:id="@+id/textView3"
            android:layout_width="100dp"
            android:layout_height="wrap_content"
            android:text="@string/caption_desc"
            android:textColor="@color/colorWhite"
            android:gravity="right"
            android:textAppearance="?android:attr/textAppearanceLarge" />

        <EditText
            android:id="@+id/DescEditText"
            android:layout_width="200dp"
            android:layout_height="wrap_content"
            android:hint=""
            android:ems="10"
            android:theme="@android:style/Theme.Light"/>

    </LinearLayout>

    <LinearLayout
        android:id="@+id/Layout4"
        android:paddingTop="8dp"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@+id/layout3"
        android:gravity="center">

        <Button
            android:id="@+id/PrevButton"
            android:layout_width="60dp"
            android:layout_height="wrap_content"
            android:theme="@android:style/Theme.Light"
            android:text="@string/btn_prev_caption" />

        <Button
            android:id="@+id/NextButton"
            android:layout_width="60dp"
            android:layout_height="wrap_content"
            android:theme="@android:style/Theme.Light"
            android:text="@string/btn_next_caption" />

        <Button
            android:id="@+id/AddButton"
            android:layout_width="60dp"
            android:layout_height="wrap_content"
            android:theme="@android:style/Theme.Light"
            android:text="@string/btn_add_caption" />

        <Button
            android:id="@+id/DeleteButton"
            android:layout_width="60dp"
            android:layout_height="wrap_content"
            android:theme="@android:style/Theme.Light"
            android:text="@string/btn_delete_caption" />

        <Button
            android:id="@+id/SaveButton"
            android:layout_width="60dp"
            android:layout_height="wrap_content"
            android:text="@string/btn_save_caption"
            android:theme="@android:style/Theme.Light"
            android:textAppearance="?android:attr/textAppearanceSmall" />
    </LinearLayout>

    <LinearLayout
        android:id="@+id/Layout5"
        android:paddingTop="8dp"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@+id/Layout4"
        android:gravity="center"
        android:orientation="vertical" >

        <TextView
            android:id="@+id/TotalMoneyTextView"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/fmt_summary_info"
            android:textColor="@color/colorWhite"
            android:textStyle="bold|italic"
            android:textAppearance="?android:attr/textAppearanceLarge" />
    </LinearLayout>
</RelativeLayout>

8. Open the file "app -> java -> com.casperlee.personalexpense -> MainActivity", uncomment the following code:

@Override
    protected void onCreate(Bundle savedInstanceState) {
        ...
        if (GlobalVars.Categories == null) {

            ...

        } else {

            Intent intent = new Intent();
            intent.setClass(MainActivity.this, AddExpenseActivity.class);
            this.startActivity(intent);
            this.finish();
        }
    }

9. Done!