org.gnucash.android.ui.budget.BudgetDetailFragment.java Source code

Java tutorial

Introduction

Here is the source code for org.gnucash.android.ui.budget.BudgetDetailFragment.java

Source

/*
 * Copyright (c) 2015 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.budget;

import android.app.Activity;
import android.content.Intent;
import android.content.res.Configuration;
import android.graphics.Color;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
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.ProgressBar;
import android.widget.TextView;

import com.github.mikephil.charting.charts.BarChart;
import com.github.mikephil.charting.components.LimitLine;
import com.github.mikephil.charting.data.BarData;
import com.github.mikephil.charting.data.BarDataSet;
import com.github.mikephil.charting.data.BarEntry;

import org.gnucash.android.R;
import org.gnucash.android.db.DatabaseSchema;
import org.gnucash.android.db.adapter.AccountsDbAdapter;
import org.gnucash.android.db.adapter.BudgetsDbAdapter;
import org.gnucash.android.model.Budget;
import org.gnucash.android.model.BudgetAmount;
import org.gnucash.android.model.Money;
import org.gnucash.android.ui.common.FormActivity;
import org.gnucash.android.ui.common.Refreshable;
import org.gnucash.android.ui.common.UxArgument;
import org.gnucash.android.ui.transaction.TransactionsActivity;
import org.gnucash.android.ui.util.widget.EmptyRecyclerView;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.List;

import butterknife.BindView;
import butterknife.ButterKnife;

/**
 * Fragment for displaying budget details
 */
public class BudgetDetailFragment extends Fragment implements Refreshable {
    @BindView(R.id.primary_text)
    TextView mBudgetNameTextView;
    @BindView(R.id.secondary_text)
    TextView mBudgetDescriptionTextView;
    @BindView(R.id.budget_recurrence)
    TextView mBudgetRecurrence;
    @BindView(R.id.budget_amount_recycler)
    EmptyRecyclerView mRecyclerView;

    private String mBudgetUID;
    private BudgetsDbAdapter mBudgetsDbAdapter;

    public static BudgetDetailFragment newInstance(String budgetUID) {
        BudgetDetailFragment fragment = new BudgetDetailFragment();
        Bundle args = new Bundle();
        args.putString(UxArgument.BUDGET_UID, budgetUID);
        fragment.setArguments(args);
        return fragment;
    }

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_budget_detail, container, false);
        ButterKnife.bind(this, view);
        mBudgetDescriptionTextView.setMaxLines(3);

        mRecyclerView.setHasFixedSize(true);

        if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {
            GridLayoutManager gridLayoutManager = new GridLayoutManager(getActivity(), 2);
            mRecyclerView.setLayoutManager(gridLayoutManager);
        } else {
            LinearLayoutManager mLayoutManager = new LinearLayoutManager(getActivity());
            mRecyclerView.setLayoutManager(mLayoutManager);
        }
        return view;
    }

    @Override
    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);

        mBudgetsDbAdapter = BudgetsDbAdapter.getInstance();
        mBudgetUID = getArguments().getString(UxArgument.BUDGET_UID);
        bindViews();

        setHasOptionsMenu(true);
    }

    private void bindViews() {
        Budget budget = mBudgetsDbAdapter.getRecord(mBudgetUID);
        mBudgetNameTextView.setText(budget.getName());

        String description = budget.getDescription();
        if (description != null && !description.isEmpty())
            mBudgetDescriptionTextView.setText(description);
        else {
            mBudgetDescriptionTextView.setVisibility(View.GONE);
        }
        mBudgetRecurrence.setText(budget.getRecurrence().getRepeatString());

        mRecyclerView.setAdapter(new BudgetAmountAdapter());
    }

    @Override
    public void onResume() {
        super.onResume();
        refresh();

        View view = getActivity().findViewById(R.id.fab_create_budget);
        if (view != null) {
            view.setVisibility(View.GONE);
        }
    }

    @Override
    public void refresh() {
        bindViews();
        String budgetName = mBudgetsDbAdapter.getAttribute(mBudgetUID, DatabaseSchema.BudgetEntry.COLUMN_NAME);
        ActionBar actionBar = ((AppCompatActivity) getActivity()).getSupportActionBar();
        assert actionBar != null;
        actionBar.setTitle("Budget: " + budgetName);
    }

    @Override
    public void refresh(String budgetUID) {
        mBudgetUID = budgetUID;
        refresh();
    }

    @Override
    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
        inflater.inflate(R.menu.budget_actions, menu);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
        case R.id.menu_edit_budget:
            Intent addAccountIntent = new Intent(getActivity(), FormActivity.class);
            addAccountIntent.setAction(Intent.ACTION_INSERT_OR_EDIT);
            addAccountIntent.putExtra(UxArgument.FORM_TYPE, FormActivity.FormType.BUDGET.name());
            addAccountIntent.putExtra(UxArgument.BUDGET_UID, mBudgetUID);
            startActivityForResult(addAccountIntent, 0x11);
            return true;

        default:
            return false;
        }
    }

    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (resultCode == Activity.RESULT_OK) {
            refresh();
        }
    }

    public class BudgetAmountAdapter extends RecyclerView.Adapter<BudgetAmountAdapter.BudgetAmountViewHolder> {
        private List<BudgetAmount> mBudgetAmounts;
        private Budget mBudget;

        public BudgetAmountAdapter() {
            mBudget = mBudgetsDbAdapter.getRecord(mBudgetUID);
            mBudgetAmounts = mBudget.getCompactedBudgetAmounts();
        }

        @Override
        public BudgetAmountViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
            View view = LayoutInflater.from(getActivity()).inflate(R.layout.cardview_budget_amount, parent, false);
            return new BudgetAmountViewHolder(view);
        }

        @Override
        public void onBindViewHolder(BudgetAmountViewHolder holder, final int position) {
            BudgetAmount budgetAmount = mBudgetAmounts.get(position);
            Money projectedAmount = budgetAmount.getAmount();
            AccountsDbAdapter accountsDbAdapter = AccountsDbAdapter.getInstance();

            holder.budgetAccount.setText(accountsDbAdapter.getAccountFullName(budgetAmount.getAccountUID()));
            holder.budgetAmount.setText(projectedAmount.formattedString());

            Money spentAmount = accountsDbAdapter.getAccountBalance(budgetAmount.getAccountUID(),
                    mBudget.getStartofCurrentPeriod(), mBudget.getEndOfCurrentPeriod());

            holder.budgetSpent.setText(spentAmount.abs().formattedString());
            holder.budgetLeft.setText(projectedAmount.subtract(spentAmount.abs()).formattedString());

            double budgetProgress = 0;
            if (projectedAmount.asDouble() != 0) {
                budgetProgress = spentAmount.asBigDecimal()
                        .divide(projectedAmount.asBigDecimal(),
                                spentAmount.getCommodity().getSmallestFractionDigits(), RoundingMode.HALF_EVEN)
                        .doubleValue();
            }

            holder.budgetIndicator.setProgress((int) (budgetProgress * 100));
            holder.budgetSpent.setTextColor(BudgetsActivity.getBudgetProgressColor(1 - budgetProgress));
            holder.budgetLeft.setTextColor(BudgetsActivity.getBudgetProgressColor(1 - budgetProgress));

            generateChartData(holder.budgetChart, budgetAmount);

            holder.itemView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    Intent intent = new Intent(getActivity(), TransactionsActivity.class);
                    intent.putExtra(UxArgument.SELECTED_ACCOUNT_UID, mBudgetAmounts.get(position).getAccountUID());
                    startActivityForResult(intent, 0x10);
                }
            });
        }

        /**
         * Generate the chart data for the chart
         * @param barChart View where to display the chart
         * @param budgetAmount BudgetAmount to visualize
         */
        public void generateChartData(BarChart barChart, BudgetAmount budgetAmount) {
            // FIXME: 25.10.15 chart is broken

            AccountsDbAdapter accountsDbAdapter = AccountsDbAdapter.getInstance();

            List<BarEntry> barEntries = new ArrayList<>();
            List<String> xVals = new ArrayList<>();

            //todo: refactor getNumberOfPeriods into budget
            int budgetPeriods = (int) mBudget.getNumberOfPeriods();
            budgetPeriods = budgetPeriods == 0 ? 12 : budgetPeriods;
            int periods = mBudget.getRecurrence().getNumberOfPeriods(budgetPeriods); //// FIXME: 15.08.2016 why do we need number of periods

            for (int periodNum = 1; periodNum <= periods; periodNum++) {
                BigDecimal amount = accountsDbAdapter.getAccountBalance(budgetAmount.getAccountUID(),
                        mBudget.getStartOfPeriod(periodNum), mBudget.getEndOfPeriod(periodNum)).asBigDecimal();

                if (amount.equals(BigDecimal.ZERO))
                    continue;

                barEntries.add(new BarEntry(amount.floatValue(), periodNum));
                xVals.add(mBudget.getRecurrence().getTextOfCurrentPeriod(periodNum));
            }

            String label = accountsDbAdapter.getAccountName(budgetAmount.getAccountUID());
            BarDataSet barDataSet = new BarDataSet(barEntries, label);

            BarData barData = new BarData(xVals, barDataSet);
            LimitLine limitLine = new LimitLine(budgetAmount.getAmount().asBigDecimal().floatValue());
            limitLine.setLineWidth(2f);
            limitLine.setLineColor(Color.RED);

            barChart.setData(barData);
            barChart.getAxisLeft().addLimitLine(limitLine);
            BigDecimal maxValue = budgetAmount.getAmount()
                    .add(budgetAmount.getAmount().multiply(new BigDecimal("0.2"))).asBigDecimal();
            barChart.getAxisLeft().setAxisMaxValue(maxValue.floatValue());
            barChart.animateX(1000);
            barChart.setAutoScaleMinMaxEnabled(true);
            barChart.setDrawValueAboveBar(true);
            barChart.invalidate();
        }

        @Override
        public int getItemCount() {
            return mBudgetAmounts.size();
        }

        class BudgetAmountViewHolder extends RecyclerView.ViewHolder {
            @BindView(R.id.budget_account)
            TextView budgetAccount;
            @BindView(R.id.budget_amount)
            TextView budgetAmount;
            @BindView(R.id.budget_spent)
            TextView budgetSpent;
            @BindView(R.id.budget_left)
            TextView budgetLeft;
            @BindView(R.id.budget_indicator)
            ProgressBar budgetIndicator;
            @BindView(R.id.budget_chart)
            BarChart budgetChart;

            public BudgetAmountViewHolder(View itemView) {
                super(itemView);
                ButterKnife.bind(this, itemView);
            }

        }
    }
}