Skip to content

Commit

Permalink
Fixed: crashes when adding transactions to an account with different …
Browse files Browse the repository at this point in the history
…currency from device locale

Fixed: transaction time was always updated (inadvertently) when the edit view is open and closed
Fixed: crash when changing currency of accounts which have transactions
Fixed: crash when adding widget to homescreen with an account which has different currency from device locale
should solve problems described in codinguser#8
Added code documentation and tests
  • Loading branch information
codinguser committed Aug 10, 2012
1 parent 47f0ab7 commit 529479f
Show file tree
Hide file tree
Showing 18 changed files with 326 additions and 79 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -150,8 +150,10 @@ public Account buildAccountInstance(Cursor c){
String uid = c.getString(DatabaseAdapter.COLUMN_UID);
account.setUID(uid);
account.setAccountType(AccountType.valueOf(c.getString(DatabaseAdapter.COLUMN_TYPE)));
//make sure the account currency is set before setting the transactions
//else the transactions end up with a different currency from the account
account.setCurrency(Currency.getInstance(c.getString(DatabaseAdapter.COLUMN_CURRENCY_CODE)));
account.setTransactions(mTransactionsAdapter.getAllTransactionsForAccount(uid));
account.setCurrency(Currency.getInstance(c.getString(DatabaseAdapter.COLUMN_ACCOUNT_CURRENCY_CODE)));
return account;
}

Expand Down
5 changes: 3 additions & 2 deletions GnucashMobile/src/org/gnucash/android/db/DatabaseAdapter.java
Original file line number Diff line number Diff line change
Expand Up @@ -42,14 +42,15 @@ public class DatabaseAdapter {
public static final int COLUMN_NAME = 2;
public static final int COLUMN_TYPE = 3;

//columns specific to transactions
public static final int COLUMN_AMOUNT = 4;
public static final int COLUMN_DESCRIPTION = 5;
public static final int COLUMN_TIMESTAMP = 6;
public static final int COLUMN_ACCOUNT_UID = 7;
public static final int COLUMN_EXPORTED = 8;

public static final int COLUMN_CURRENCY_CODE = 1;
public static final int COLUMN_ACCOUNT_CURRENCY_CODE = 4;
//columns specific to accounts
public static final int COLUMN_CURRENCY_CODE = 4;

/**
* {@link DatabaseHelper} for creating and opening the database
Expand Down
12 changes: 6 additions & 6 deletions GnucashMobile/src/org/gnucash/android/db/DatabaseHelper.java
Original file line number Diff line number Diff line change
Expand Up @@ -124,19 +124,19 @@ public class DatabaseHelper extends SQLiteOpenHelper {
+ KEY_UID + " varchar(255) not null, "
+ KEY_NAME + " varchar(255) not null, "
+ KEY_TYPE + " varchar(255), "
+ KEY_CURRENCY_CODE + " varchar(255), "
+ KEY_CURRENCY_CODE + " varchar(255) not null, "
+ "UNIQUE (" + KEY_UID + ")"
+ ");";

/**
* SQL statement to create the transactions table in the database
*/
private static final String TRANSACTIONS_TABLE_CREATE = "create table " + TRANSACTIONS_TABLE_NAME + " ("
+ KEY_ROW_ID + " integer primary key autoincrement, "
+ KEY_UID + " varchar(255) not null, "
+ KEY_NAME + " varchar(255), "
+ KEY_TYPE + " varchar(255) not null, "
+ KEY_AMOUNT + " varchar(255) not null, "
+ KEY_ROW_ID + " integer primary key autoincrement, "
+ KEY_UID + " varchar(255) not null, "
+ KEY_NAME + " varchar(255), "
+ KEY_TYPE + " varchar(255) not null, "
+ KEY_AMOUNT + " varchar(255) not null, "
+ KEY_DESCRIPTION + " text, "
+ KEY_TIMESTAMP + " integer not null, "
+ KEY_ACCOUNT_UID + " varchar(255) not null, "
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -169,8 +169,10 @@ public Transaction buildTransactionInstance(Cursor c){
String accountUID = c.getString(DatabaseAdapter.COLUMN_ACCOUNT_UID);
Currency currency = Currency.getInstance(getCurrencyCode(accountUID));
String amount = c.getString(DatabaseAdapter.COLUMN_AMOUNT);
Transaction transaction = new Transaction(new Money(new BigDecimal(amount), currency),
c.getString(DatabaseAdapter.COLUMN_NAME));
Money moneyAmount = new Money(new BigDecimal(amount), currency);
String name = c.getString(DatabaseAdapter.COLUMN_NAME);

Transaction transaction = new Transaction(moneyAmount, name);
transaction.setUID(c.getString(DatabaseAdapter.COLUMN_UID));
transaction.setAccountUID(accountUID);
transaction.setTime(c.getLong(DatabaseAdapter.COLUMN_TIMESTAMP));
Expand All @@ -196,7 +198,7 @@ public String getCurrencyCode(String accountUID) {
if (cursor == null || cursor.getCount() <= 0)
return null;

cursor.moveToNext();
cursor.moveToFirst();
String currencyCode = cursor.getString(0);
cursor.close();
return currencyCode;
Expand Down Expand Up @@ -304,12 +306,10 @@ public Money getTransactionsSum(long accountId){
new String[]{DatabaseHelper.KEY_AMOUNT},
DatabaseHelper.KEY_ACCOUNT_UID + "= '" + getAccountUID(accountId) + "'",
null, null, null, null);


//transactions will have the currency of the account
String currencyCode = getCurrencyCode(accountId);

if (currencyCode == null)
currencyCode = Money.DEFAULT_CURRENCY_CODE;

if (c == null || c.getCount() <= 0)
return new Money("0", currencyCode);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,16 @@
import android.os.Bundle;
import android.util.Log;

/**
* Broadcast receiver responsible for creating {@link Account}s received through intents.
* In order to create an <code>Account</code>, you need to broadcast an {@link Intent} with arguments
* for the name, currency and optionally, a unique identifier for the account (which should be unique to Gnucash)
* of the Account to be created. Also remember to set the right mime type so that Android can properly route the Intent
* <b>Note</b> This Broadcast receiver requires the permission "org.gnucash.android.permission.CREATE_ACCOUNT"
* in order to be able to use Intents to create accounts. So remember to declare it in your manifest
* @author Ngewi Fet <ngewif@gmail.com>
* @see {@link Account#EXTRA_CURRENCY_CODE}, {@link Account#MIME_TYPE} {@link Intent#EXTRA_TITLE}, {@link Intent#EXTRA_UID}
*/
public class AccountCreator extends BroadcastReceiver {

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,13 @@
import android.content.SharedPreferences.Editor;
import android.preference.PreferenceManager;

/**
* {@link AppWidgetProvider} which is responsible for managing widgets on the homescreen
* It receives broadcasts related to updating and deleting widgets
* Widgets can also be updated manually by calling {@link WidgetConfigurationActivity#updateAllWidgets(Context)}
* @author Ngewi Fet <ngewif@gmail.com>
*
*/
public class TransactionAppWidgetProvider extends AppWidgetProvider {

@Override
Expand Down Expand Up @@ -55,6 +62,6 @@ public void onDeleted(Context context, int[] appWidgetIds) {
for (int appWidgetId : appWidgetIds) {
editor.remove(TransactionsListFragment.SELECTED_ACCOUNT_ID + appWidgetId);
}
editor.commit();
editor.commit();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,17 @@
import android.os.Bundle;
import android.util.Log;

/**
* Broadcast receiver responsible for creating transactions received through {@link Intent}s
* In order to create a transaction through Intents, broadcast an intent with the arguments needed to
* create the transaction. Transactions are strongly bound to {@link Account}s and it is recommended to
* create an Account for your transactions. The transactions will be associated to the account using a unique
* Identifier passed as {@link Transaction#EXTRA_ACCOUNT_UID}
* <p>Remember to declare the appropriate permissions in order to create transactions with Intents.
* The required permission is "org.gnucash.android.permission.RECORD_TRANSACTION"</p>
* @author Ngewi Fet <ngewif@gmail.com>
* @see AccountCreator
*/
public class TransactionRecorder extends BroadcastReceiver {

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,37 @@
import android.os.Bundle;
import android.support.v4.app.DialogFragment;

/**
* Fragment for displaying a date picker dialog
* @author Ngewi Fet <ngewif@gmail.com>
*
*/
public class DatePickerDialogFragment extends DialogFragment {

/**
* Listener to notify of events in the dialog
*/
private OnDateSetListener mDateSetListener;

/**
* Date selected in the dialog or to which the dialog is initialized
*/
private Calendar mDate;

/**
* Default Constructor
* Is required for when the device is rotated while the dialog is open.
* If this constructor is not present, the app will crash
*/
public DatePickerDialogFragment() {
// nothing to see here, move along
//nothing to see here, move along
}

/**
* Overloaded constructor
* @param callback Listener to notify when the date is set and the dialog is closed
* @param dateMillis Time in milliseconds to which to initialize the dialog
*/
public DatePickerDialogFragment(OnDateSetListener callback, long dateMillis) {
mDateSetListener = (OnDateSetListener) callback;
if (dateMillis > 0){
Expand All @@ -42,6 +64,9 @@ public DatePickerDialogFragment(OnDateSetListener callback, long dateMillis) {
}
}

/**
* Creates and returns an Android {@link DatePickerDialog}
*/
public Dialog onCreateDialog(Bundle savedInstanceState) {
Calendar cal = mDate == null ? Calendar.getInstance() : mDate;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,36 @@
import android.os.Bundle;
import android.support.v4.app.DialogFragment;

/**
* Fragment for displaying a time choose dialog
* @author Ngewi Fet <ngewif@gmail.com>
*
*/
public class TimePickerDialogFragment extends DialogFragment {
/**
* Listener to notify when the time is set
*/
private OnTimeSetListener mListener = null;

/**
* Current time to initialize the dialog to, or to notify the listener of.
*/
Calendar mCurrentTime = null;

/**
* Default constructor
* Is required for when the device is rotated while the dialog is open.
* If this constructor is not present, the app will crash
*/
public TimePickerDialogFragment() {
// nothing to see here, move along
}
Calendar mCurrentTime = null;

/**
* Overloaded constructor
* @param listener {@link OnTimeSetListener} to notify when the time has been set
* @param timeMillis Time in milliseconds to initialize the dialog to
*/
public TimePickerDialogFragment(OnTimeSetListener listener, long timeMillis){
mListener = listener;
if (timeMillis > 0){
Expand All @@ -40,6 +62,9 @@ public TimePickerDialogFragment(OnTimeSetListener listener, long timeMillis){
}
}

/**
* Creates and returns an Android {@link TimePickerDialog}
*/
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
Calendar cal = mCurrentTime == null ? Calendar.getInstance() : mCurrentTime;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,18 +47,43 @@
import com.actionbarsherlock.view.MenuItem;

/**
* Displays the list of accounts and summary of transactions
*
* Manages actions related to accounts, displaying, exporting and creating new accounts
* The various actions are implemented as Fragments which are then added to this activity
* @author Ngewi Fet <ngewif@gmail.com>
*
*/
public class AccountsActivity extends SherlockFragmentActivity implements OnAccountClickedListener {

/**
* Tag used for identifying the account list fragment when it is added to this activity
*/
public static final String FRAGMENT_ACCOUNTS_LIST = "accounts_list";

/**
* Tag used for identifying the account export fragment
*/
protected static final String FRAGMENT_EXPORT_OFX = "export_ofx";

/**
* Tag for identifying the "New account" fragment
*/
protected static final String FRAGMENT_NEW_ACCOUNT = "new_account_dialog";

/**
* Logging tag
*/
protected static final String TAG = "AccountsActivity";

/**
* Stores the indices of accounts which have been selected by the user for creation from the dialog.
* The account names are stored as string resources and the selected indices are then used to choose which accounts to create
* The dialog for creating default accounts is only shown when the app is started for the first time.
*/
private ArrayList<Integer> mSelectedDefaultAccounts = new ArrayList<Integer>();

/**
* Dialog which is shown to the user on first start prompting the user to create some accounts
*/
private AlertDialog mDefaultAccountsDialog;

@Override
Expand Down Expand Up @@ -113,12 +138,6 @@ public boolean onOptionsItemSelected(MenuItem item) {
}
}

@Override
protected void onActivityResult(int arg0, int arg1, Intent arg2) {
// TODO Auto-generated method stub
super.onActivityResult(arg0, arg1, arg2);
}

/**
* Opens a dialog fragment to create a new account
* @param v View which triggered this callback
Expand All @@ -130,6 +149,10 @@ public void onNewAccountClick(View v) {
accountFragment.showAddAccountDialog(0);
}

/**
* Creates the default accounts which have the selected by the user.
* The indices of the default accounts is stored in {@link #mSelectedDefaultAccounts}
*/
private void createDefaultAccounts(){
AlertDialog.Builder builder = new AlertDialog.Builder(this);
boolean[] checkedDefaults = new boolean[]{true, true, false, false, false};
Expand Down Expand Up @@ -193,6 +216,10 @@ public void accountSelected(long accountRowId) {
startActivity(intent);
}

/**
* Removes the flag indicating that the app is being run for the first time.
* This is called every time the app is started because the next time won't be the first time
*/
private void removeFirstRunFlag(){
Editor editor = PreferenceManager.getDefaultSharedPreferences(this).edit();
editor.putBoolean(getString(R.string.key_first_run), false);
Expand Down
Loading

0 comments on commit 529479f

Please sign in to comment.