de.anderdonau.hackersdiet.MonthDetailFragment.java Source code

Java tutorial

Introduction

Here is the source code for de.anderdonau.hackersdiet.MonthDetailFragment.java

Source

package de.anderdonau.hackersdiet;
/*
The Hackers Diet Offline for Android
Copyright (C) 2014 Benjamin Schieder <hackersdiet@wegwerf.anderdonau.de>
    
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License.
    
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.
    
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/

import android.graphics.Color;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.TextView;

import com.jjoe64.graphview.CustomLabelFormatter;
import com.jjoe64.graphview.GraphView;
import com.jjoe64.graphview.GraphViewSeries;
import com.jjoe64.graphview.GraphViewStyle;
import com.jjoe64.graphview.LineGraphView;

import java.lang.reflect.Field;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.GregorianCalendar;

/**
 * A fragment representing a single Month detail screen.
 * This fragment is either contained in a {@link MonthListActivity}
 * in two-pane mode (on tablets) or a {@link MonthDetailActivity}
 * on handsets.
 */
public class MonthDetailFragment extends Fragment {
    Calendar mToday;
    View rootView = null;
    boolean mCanSave = false;
    boolean viewCachePopulated = false;
    public static final String ARG_ITEM_ID = "item_id";
    private MonthListContent.MonthItem mItem;
    viewCache[] mViewCache = new viewCache[32]; // 0 is left empty, saves subtracting 1 every time
    weightData mWeight = null;
    weightDataDay mPtr = null;
    LineGraphView graphView;

    /**
     * Search a widget by its name and return its ID.
     */
    public int getIdByName(String name) {
        Class res = R.id.class;
        int id;
        try {
            Field field = res.getField(name);
            id = field.getInt(null);
        } catch (Exception e) {
            Log.d("getIdByName", "Cant get " + name);
            return -1;
        }
        return id;
    }

    /**
     * Main drawing-handling function. Updates all widgets and text
     */
    public void updateEverything() {
        mCanSave = false; // while this is running, we prevent saving. Works as a mutex.

        for (int d = 1; d <= 31; d++) {
            if (d > 28 && d > mWeight.daysinmonth(mToday.get(Calendar.MONTH) + 1, mToday.get(Calendar.YEAR))) {
                /**
                 * Hide all widgets that are not used in this month and set them to empty strings or false.
                 */
                mViewCache[d].row.setVisibility(View.GONE);
                mViewCache[d].weight.setText("");
                mViewCache[d].trend.setText(" ");
                mViewCache[d].var.setText(" ");
                mViewCache[d].rung.setText("");
                mViewCache[d].flag.setChecked(false);
                mViewCache[d].comment.setText("");
            } else if (d > 28) {
                /**
                 * Obviously only necessary for days 29 and 30 for feb, and 31 for apr, jun, sep, nov.
                 */
                mViewCache[d].row.setVisibility(View.VISIBLE);
            }
            if (d <= mWeight.daysinmonth(mToday.get(Calendar.MONTH) + 1, mToday.get(Calendar.YEAR))) {
                mToday.set(Calendar.DAY_OF_MONTH, d);
            }
        }

        int year = mItem.year;
        int month = mItem.month;
        int wholedate = year * 10000 + month * 100 + 1;
        int wholedatelast = year * 10000 + month * 100 + mWeight.daysinmonth(month, year);
        Calendar tmpDate = new GregorianCalendar(year, month - 1, 1);
        double max = -1;
        double min = 99999;

        SimpleDateFormat sdfDay = new SimpleDateFormat("MMMM yyyy");
        ((TextView) rootView.findViewById(R.id.textMonth)).setText(sdfDay.format(tmpDate.getTime()));

        mPtr = mWeight.allData;

        if (mPtr.wholedate < wholedate) { // mPtr is before today
            while (mPtr.wholedate < wholedate && mPtr.next != null) {
                mPtr = mPtr.next;
            }
            if (mPtr.wholedate < wholedate) { // No entries for this month
                mCanSave = true;
                return;
            }
        } else if (mPtr.wholedate > wholedate) { // mPtr is beyond today
            while (mPtr.wholedate > wholedate && mPtr.prev != null) {
                mPtr = mPtr.prev;
            }
            if (mPtr.wholedate > wholedatelast) { // No entries for this month
                mCanSave = true;
                return;
            }
        }

        int numWeight = 0;
        int numTrend = 0;
        int numRung = 0;
        GraphView.GraphViewData[] tmpWeightValues = new GraphView.GraphViewData[31];
        GraphView.GraphViewData[] tmpTrendValues = new GraphView.GraphViewData[31];
        GraphView.GraphViewData[] tmpRungValues = new GraphView.GraphViewData[31];

        for (; mPtr != null && mPtr.wholedate <= wholedatelast; mPtr = mPtr.next) { // fill all values
            int d = mPtr.wholedate % (year * 10000 + month * 100);

            tmpTrendValues[numTrend] = new GraphView.GraphViewData(d, mPtr.getTrend());
            numTrend++;
            max = Math.max(max, mPtr.getTrend());
            min = Math.min(min, mPtr.getTrend());
            if (mPtr.getWeight() > 0.0f) { // only care if we have an actual value
                max = Math.max(max, mPtr.getWeight());
                min = Math.min(min, mPtr.getWeight());
                tmpWeightValues[numWeight] = new GraphView.GraphViewData(d, mPtr.getWeight());
            } else {
                tmpWeightValues[numWeight] = new GraphView.GraphViewData(d, Double.NaN);
            }
            numWeight++;

            if (mPtr.rung > 0) {
                tmpRungValues[numRung] = new GraphView.GraphViewData(d, mPtr.rung);
                numRung++;
            }

            if (mPtr.getVar() > 0) {
                mViewCache[d].var.setTextColor(getResources().getColor(R.color.weightGain));
            } else if (mPtr.getVar() < 0) {
                mViewCache[d].var.setTextColor(getResources().getColor(R.color.weightLoss));
            } else {
                mViewCache[d].var.setTextColor(getResources().getColor(R.color.weightConstant));
            }

            mViewCache[d].weight.setText(String.valueOf(mPtr.getWeight()));
            mViewCache[d].trend.setText(String.format("%.1f", mPtr.getTrend()));
            mViewCache[d].var.setText(String.format("%+.1f", mPtr.getVar()));
            mViewCache[d].rung.setText(String.valueOf(mPtr.rung));
            mViewCache[d].flag.setChecked(mPtr.flag);
            mViewCache[d].comment.setText(mPtr.comment);

            tmpDate.set(Calendar.DAY_OF_MONTH, d);
        }

        graphView.removeAllSeries();
        if (numWeight > 0) {
            max += 0.2;
            min -= 0.2;

            GraphView.GraphViewData[] weightValues = new GraphView.GraphViewData[numWeight];
            GraphView.GraphViewData[] trendValues = new GraphView.GraphViewData[numTrend];
            System.arraycopy(tmpWeightValues, 0, weightValues, 0, numWeight);
            System.arraycopy(tmpTrendValues, 0, trendValues, 0, numTrend);

            GraphViewSeries gvsWeight = new GraphViewSeries("Weight", null, weightValues);
            gvsWeight.getStyle().color = Color.GREEN;
            GraphViewSeries gvsTrend = new GraphViewSeries("Trend", null, trendValues);
            gvsTrend.getStyle().color = Color.RED;

            graphView.addSeries(gvsWeight);
            graphView.addSeries(gvsTrend);
            graphView.setManualYAxisBounds(max, min);
        }
        /**
         * TODO: Need a second Y-Axis
         * if (numRung > 0){
         *   GraphView.GraphViewData[] rungValues = new GraphView.GraphViewData[numRung];
         *   System.arraycopy(tmpRungValues, 0, rungValues, 0, numRung);
         *   graphView.addSeries(new GraphViewSeries("Rung", null, rungValues));
         * }
         */
        mCanSave = true;
    }

    public MonthDetailFragment() {
    }

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

        if (getArguments().containsKey(ARG_ITEM_ID)) {
            mItem = MonthListContent.ITEM_MAP.get(getArguments().getString(ARG_ITEM_ID));
        } else {
            return;
        }

        graphView = new LineGraphView(MonthListActivity.getAppContext(), "");
        GraphViewStyle gStyle = graphView.getGraphViewStyle();
        mToday = new GregorianCalendar();
        mWeight = MonthListActivity.getmWeightData();

        gStyle.setGridColor(Color.LTGRAY);
        gStyle.setHorizontalLabelsColor(Color.BLACK);
        gStyle.setVerticalLabelsColor(Color.BLACK);
        gStyle.setNumVerticalLabels(5);
        gStyle.setNumHorizontalLabels(9);
        gStyle.setLegendWidth(140);
        //graphView.setViewPort(1, 10);
        //graphView.setScalable(true);
        //graphView.setScrollable(true);
        graphView.setShowLegend(true);
        graphView.setLegendAlign(GraphView.LegendAlign.BOTTOM);
        graphView.setBackgroundColor(Color.DKGRAY);
        graphView.setCustomLabelFormatter(new CustomLabelFormatter() {
            @Override
            public String formatLabel(double value, boolean isValueX) {
                if (isValueX) {
                    return String.format("%.0f", value);
                }
                return String.format("%.1f", value);
            }
        });
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        rootView = inflater.inflate(R.layout.fragment_month_detail, container, false);

        mPtr = mWeight.allData;

        View.OnFocusChangeListener onBlur = new View.OnFocusChangeListener() {
            public void onFocusChange(View arg0, boolean hasFocus) {
                if (hasFocus) { // only update on loss of focus
                    return;
                }
                if (MonthListActivity.mChanged) {
                    updateEverything();
                }
            }
        };

        if (!viewCachePopulated) {
            for (int d = 1; d <= 31; d++) {
                String day = "";
                if (d < 10) {
                    day = "0";
                }
                day += String.valueOf(d);
                mViewCache[d] = new viewCache();
                mViewCache[d].weight = (EditText) rootView.findViewById(getIdByName("weight" + day));
                mViewCache[d].trend = (TextView) rootView.findViewById(getIdByName("trend" + day));
                mViewCache[d].var = (TextView) rootView.findViewById(getIdByName("var" + day));
                mViewCache[d].rung = (EditText) rootView.findViewById(getIdByName("rung" + day));
                mViewCache[d].flag = (CheckBox) rootView.findViewById(getIdByName("flag" + day));
                mViewCache[d].comment = (EditText) rootView.findViewById(getIdByName("comment" + day));
                mViewCache[d].row = (LinearLayout) rootView.findViewById(getIdByName("rowDay" + day));
                final int dayOfMonth = d;
                TextWatcher onChange = new TextWatcher() {
                    @Override
                    public void beforeTextChanged(CharSequence charSequence, int i, int i2, int i3) {

                    }

                    @Override
                    public void onTextChanged(CharSequence charSequence, int i, int i2, int i3) {

                    }

                    @Override
                    public void afterTextChanged(Editable editable) {
                        if (!mCanSave) // do not save when updateEverything is running
                            return;
                        double weight;
                        int rung;
                        try {
                            weight = Double.parseDouble(mViewCache[dayOfMonth].weight.getText().toString());
                        } catch (Exception e) {
                            weight = 0.0f;
                        }
                        try {
                            rung = Integer.parseInt(mViewCache[dayOfMonth].rung.getText().toString());
                        } catch (Exception e) {
                            rung = 0;
                        }
                        mWeight.add(mItem.year, mItem.month, dayOfMonth, weight, rung,
                                mViewCache[dayOfMonth].flag.isChecked(),
                                mViewCache[dayOfMonth].comment.getText().toString());
                        MonthListActivity.mChanged = true;
                    }
                };
                CompoundButton.OnCheckedChangeListener onCheck = new CompoundButton.OnCheckedChangeListener() {
                    @Override
                    public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
                        if (!mCanSave) // do not save when updateEverything is running
                            return;
                        double weight;
                        int rung;
                        try {
                            weight = Double.parseDouble(mViewCache[dayOfMonth].weight.getText().toString());
                        } catch (Exception e) {
                            weight = 0.0f;
                        }
                        try {
                            rung = Integer.parseInt(mViewCache[dayOfMonth].rung.getText().toString());
                        } catch (Exception e) {
                            rung = 0;
                        }
                        mWeight.add(mItem.year, mItem.month, dayOfMonth, weight, rung,
                                mViewCache[dayOfMonth].flag.isChecked(),
                                mViewCache[dayOfMonth].comment.getText().toString());
                        MonthListActivity.mChanged = true;
                    }
                };

                mViewCache[d].weight.setOnFocusChangeListener(onBlur);
                mViewCache[d].rung.setOnFocusChangeListener(onBlur);
                mViewCache[d].flag.setOnFocusChangeListener(onBlur);
                mViewCache[d].comment.setOnFocusChangeListener(onBlur);

                mViewCache[d].weight.addTextChangedListener(onChange);
                mViewCache[d].rung.addTextChangedListener(onChange);
                mViewCache[d].comment.addTextChangedListener(onChange);

                mViewCache[d].flag.setOnCheckedChangeListener(onCheck);

                if (d == mToday.get(Calendar.DAY_OF_MONTH) && mToday.get(Calendar.YEAR) == mItem.year
                        && mToday.get(Calendar.MONTH) + 1 == mItem.month) {
                    mViewCache[d].weight.requestFocus();
                }
            }
            viewCachePopulated = true;
        }

        LinearLayout layout = (LinearLayout) rootView.findViewById(R.id.weightGraph);
        layout.addView(graphView);

        updateEverything();

        return rootView;
    }
}