/* * Copyright (c) 2016 Ngewi Fet <ngewif@gmail.com> * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.gnucash.android.ui.settings; import android.content.Context; import android.content.DialogInterface; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.os.Bundle; import android.support.annotation.Nullable; import android.support.v4.app.ListFragment; import android.support.v4.app.LoaderManager; import android.support.v4.content.ContextCompat; import android.support.v4.content.Loader; import android.support.v4.widget.SimpleCursorAdapter; import android.support.v7.app.ActionBar; import android.support.v7.app.AlertDialog; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.PopupMenu; import android.util.Log; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.widget.EditText; import android.widget.ImageView; import android.widget.ListView; import android.widget.TextView; import org.gnucash.android.R; import org.gnucash.android.app.GnuCashApplication; import org.gnucash.android.db.DatabaseCursorLoader; import org.gnucash.android.db.DatabaseHelper; import org.gnucash.android.db.DatabaseSchema.BookEntry; import org.gnucash.android.db.adapter.AccountsDbAdapter; import org.gnucash.android.db.adapter.BooksDbAdapter; import org.gnucash.android.db.adapter.SplitsDbAdapter; import org.gnucash.android.db.adapter.TransactionsDbAdapter; import org.gnucash.android.ui.account.AccountsActivity; import org.gnucash.android.ui.common.Refreshable; import org.gnucash.android.util.BookUtils; import org.gnucash.android.util.PreferencesHelper; import java.sql.Timestamp; /** * Fragment for managing the books in the database */ public class BookManagerFragment extends ListFragment implements LoaderManager.LoaderCallbacks<Cursor>, Refreshable{ private static final String LOG_TAG = "BookManagerFragment"; private SimpleCursorAdapter mCursorAdapter; @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { return inflater.inflate(R.layout.fragment_book_list, container, false); } @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); mCursorAdapter = new BooksCursorAdapter(getActivity(), R.layout.cardview_book, null, new String[]{BookEntry.COLUMN_DISPLAY_NAME, BookEntry.COLUMN_SOURCE_URI}, new int[]{R.id.primary_text, R.id.secondary_text}); setListAdapter(mCursorAdapter); } @Override public void onActivityCreated(@Nullable Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); ActionBar actionBar = ((AppCompatActivity) getActivity()).getSupportActionBar(); assert actionBar != null; actionBar.setHomeButtonEnabled(true); actionBar.setDisplayHomeAsUpEnabled(true); actionBar.setTitle(R.string.title_manage_books); setHasOptionsMenu(true); getListView().setChoiceMode(ListView.CHOICE_MODE_NONE); } @Override public void onResume() { super.onResume(); refresh(); } @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { inflater.inflate(R.menu.book_list_actions, menu); } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()){ case R.id.menu_create_book: AccountsActivity.createDefaultAccounts(GnuCashApplication.getDefaultCurrencyCode(), getActivity()); return true; default: return false; } } @Override public void refresh() { getLoaderManager().restartLoader(0, null, this); } @Override public void refresh(String uid) { refresh(); } @Override public Loader<Cursor> onCreateLoader(int id, Bundle args) { Log.d(LOG_TAG, "Creating loader for books"); return new BooksCursorLoader(getActivity()); } @Override public void onLoadFinished(Loader<Cursor> loader, Cursor data) { Log.d(LOG_TAG, "Finished loading books from database"); mCursorAdapter.swapCursor(data); mCursorAdapter.notifyDataSetChanged(); } @Override public void onLoaderReset(Loader<Cursor> loader) { Log.d(LOG_TAG, "Resetting books list loader"); mCursorAdapter.swapCursor(null); } private class BooksCursorAdapter extends SimpleCursorAdapter { BooksCursorAdapter(Context context, int layout, Cursor c, String[] from, int[] to) { super(context, layout, c, from, to, 0); } @Override public void bindView(View view, final Context context, Cursor cursor) { super.bindView(view, context, cursor); final String bookUID = cursor.getString(cursor.getColumnIndexOrThrow(BookEntry.COLUMN_UID)); setLastExportedText(view, bookUID); setStatisticsText(view, bookUID); setUpMenu(view, context, cursor, bookUID); view.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { //do nothing if the active book is tapped if (!BooksDbAdapter.getInstance().getActiveBookUID().equals(bookUID)) { BookUtils.loadBook(bookUID); } } }); } private void setUpMenu(View view, final Context context, Cursor cursor, final String bookUID) { final String bookName = cursor.getString( cursor.getColumnIndexOrThrow(BookEntry.COLUMN_DISPLAY_NAME)); ImageView optionsMenu = (ImageView) view.findViewById(R.id.options_menu); optionsMenu.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { PopupMenu popupMenu = new PopupMenu(context, v); MenuInflater menuInflater = popupMenu.getMenuInflater(); menuInflater.inflate(R.menu.book_context_menu, popupMenu.getMenu()); popupMenu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() { @Override public boolean onMenuItemClick(MenuItem item) { switch (item.getItemId()) { case R.id.ctx_menu_rename_book: return handleMenuRenameBook(bookName, bookUID); case R.id.ctx_menu_sync_book: //TODO implement sync return false; case R.id.ctx_menu_delete_book: { AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(getActivity()); dialogBuilder.setTitle(getString(R.string.title_confirm_delete_book)) .setIcon(R.drawable.ic_close_black_24dp) .setMessage(getString(R.string.msg_all_book_data_will_be_deleted)); dialogBuilder.setPositiveButton(getString(R.string.btn_delete_book), new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { BooksDbAdapter.getInstance().deleteBook(bookUID); refresh(); } }); dialogBuilder.setNegativeButton(R.string.btn_cancel, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { dialog.dismiss(); } }); AlertDialog dialog = dialogBuilder.create(); dialog.show(); //must be called before you can access buttons dialog.getButton(AlertDialog.BUTTON_POSITIVE) .setTextColor(ContextCompat.getColor(context, R.color.account_red)); } return true; default: return true; } } }); String activeBookUID = BooksDbAdapter.getInstance().getActiveBookUID(); if (activeBookUID.equals(bookUID)) {//we cannot delete the active book popupMenu.getMenu().findItem(R.id.ctx_menu_delete_book).setEnabled(false); } popupMenu.show(); } }); } /** * Opens a dialog for renaming a book * @param bookName Current name of the book * @param bookUID GUID of the book * @return {@code true} */ private boolean handleMenuRenameBook(String bookName, final String bookUID) { AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(getActivity()); dialogBuilder.setTitle(R.string.title_rename_book) .setView(R.layout.dialog_rename_book) .setPositiveButton(R.string.btn_rename, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { EditText bookTitle = (EditText) ((AlertDialog)dialog).findViewById(R.id.input_book_title); BooksDbAdapter.getInstance() .updateRecord(bookUID, BookEntry.COLUMN_DISPLAY_NAME, bookTitle.getText().toString().trim()); refresh(); } }) .setNegativeButton(R.string.btn_cancel, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { dialog.dismiss(); } }); AlertDialog dialog = dialogBuilder.create(); dialog.show(); ((TextView)dialog.findViewById(R.id.input_book_title)).setText(bookName); return true; } private void setLastExportedText(View view, String bookUID) { TextView labelLastSync = (TextView) view.findViewById(R.id.label_last_sync); labelLastSync.setText(R.string.label_last_export_time); Timestamp lastSyncTime = PreferencesHelper.getLastExportTime(bookUID); TextView lastSyncText = (TextView) view.findViewById(R.id.last_sync_time); if (lastSyncTime.equals(new Timestamp(0))) lastSyncText.setText(R.string.last_export_time_never); else lastSyncText.setText(lastSyncTime.toString()); } private void setStatisticsText(View view, String bookUID) { DatabaseHelper dbHelper = new DatabaseHelper(GnuCashApplication.getAppContext(), bookUID); SQLiteDatabase db = dbHelper.getReadableDatabase(); TransactionsDbAdapter trnAdapter = new TransactionsDbAdapter(db, new SplitsDbAdapter(db)); int transactionCount = (int) trnAdapter.getRecordsCount(); String transactionStats = getResources().getQuantityString(R.plurals.book_transaction_stats, transactionCount, transactionCount); AccountsDbAdapter accountsDbAdapter = new AccountsDbAdapter(db, trnAdapter); int accountsCount = (int) accountsDbAdapter.getRecordsCount(); String accountStats = getResources().getQuantityString(R.plurals.book_account_stats, accountsCount, accountsCount); String stats = accountStats + ", " + transactionStats; TextView statsText = (TextView) view.findViewById(R.id.secondary_text); statsText.setText(stats); if (bookUID.equals(BooksDbAdapter.getInstance().getActiveBookUID())){ ((TextView)view.findViewById(R.id.primary_text)).setTextColor(getResources().getColor(R.color.theme_primary)); } } } /** * {@link DatabaseCursorLoader} for loading the book list from the database * @author Ngewi Fet <ngewif@gmail.com> */ private static class BooksCursorLoader extends DatabaseCursorLoader { BooksCursorLoader(Context context){ super(context); } @Override public Cursor loadInBackground() { BooksDbAdapter booksDbAdapter = BooksDbAdapter.getInstance(); Cursor cursor = booksDbAdapter.fetchAllRecords(); registerContentObserver(cursor); return cursor; } } }