2017-02-21 16:20:42 +01:00
package nodomain.freeyourgadget.gadgetbridge.activities.charts ;
2017-02-26 00:40:50 +01:00
import android.app.Activity ;
2017-02-21 16:20:42 +01:00
import android.graphics.Color ;
import android.os.Bundle ;
import android.view.LayoutInflater ;
import android.view.View ;
import android.view.ViewGroup ;
import com.github.mikephil.charting.charts.BarChart ;
import com.github.mikephil.charting.charts.Chart ;
import com.github.mikephil.charting.charts.PieChart ;
import com.github.mikephil.charting.components.LimitLine ;
import com.github.mikephil.charting.components.XAxis ;
import com.github.mikephil.charting.components.YAxis ;
import com.github.mikephil.charting.data.BarData ;
import com.github.mikephil.charting.data.BarDataSet ;
import com.github.mikephil.charting.data.BarEntry ;
import com.github.mikephil.charting.data.PieData ;
import com.github.mikephil.charting.data.PieDataSet ;
import com.github.mikephil.charting.data.PieEntry ;
2017-03-03 14:21:59 +01:00
import com.github.mikephil.charting.formatter.IAxisValueFormatter ;
2017-02-23 08:49:55 +01:00
import com.github.mikephil.charting.formatter.IValueFormatter ;
2017-02-21 16:20:42 +01:00
import org.slf4j.Logger ;
import org.slf4j.LoggerFactory ;
import java.util.ArrayList ;
import java.util.Calendar ;
import java.util.List ;
import java.util.Locale ;
import nodomain.freeyourgadget.gadgetbridge.R ;
import nodomain.freeyourgadget.gadgetbridge.database.DBHandler ;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice ;
2017-02-26 00:40:50 +01:00
import nodomain.freeyourgadget.gadgetbridge.model.ActivityAmounts ;
2017-02-21 16:20:42 +01:00
import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample ;
2017-02-26 00:40:50 +01:00
import nodomain.freeyourgadget.gadgetbridge.util.LimitedQueue ;
2017-02-21 16:20:42 +01:00
public abstract class AbstractWeekChartFragment extends AbstractChartFragment {
protected static final Logger LOG = LoggerFactory . getLogger ( AbstractWeekChartFragment . class ) ;
private Locale mLocale ;
private int mTargetValue = 0 ;
private PieChart mTodayPieChart ;
private BarChart mWeekChart ;
2017-03-03 17:33:00 +01:00
private int mOffsetHours = getOffsetHours ( ) ;
2017-02-21 16:20:42 +01:00
@Override
protected ChartsData refreshInBackground ( ChartsHost chartsHost , DBHandler db , GBDevice device ) {
Calendar day = Calendar . getInstance ( ) ;
day . setTime ( chartsHost . getEndDate ( ) ) ;
//NB: we could have omitted the day, but this way we can move things to the past easily
DayData dayData = refreshDayPie ( db , day , device ) ;
DefaultChartsData weekBeforeData = refreshWeekBeforeData ( db , mWeekChart , day , device ) ;
return new MyChartsData ( dayData , weekBeforeData ) ;
}
@Override
protected void updateChartsnUIThread ( ChartsData chartsData ) {
MyChartsData mcd = ( MyChartsData ) chartsData ;
// setupLegend(mWeekChart);
2017-02-23 21:15:57 +01:00
mTodayPieChart . setCenterText ( mcd . getDayData ( ) . centerText ) ;
2017-02-21 16:20:42 +01:00
mTodayPieChart . setData ( mcd . getDayData ( ) . data ) ;
mWeekChart . setData ( null ) ; // workaround for https://github.com/PhilJay/MPAndroidChart/issues/2317
mWeekChart . setData ( mcd . getWeekBeforeData ( ) . getData ( ) ) ;
mWeekChart . getLegend ( ) . setEnabled ( false ) ;
mWeekChart . getXAxis ( ) . setValueFormatter ( mcd . getWeekBeforeData ( ) . getXValueFormatter ( ) ) ;
}
@Override
protected void renderCharts ( ) {
mWeekChart . invalidate ( ) ;
mTodayPieChart . invalidate ( ) ;
}
private DefaultChartsData < BarData > refreshWeekBeforeData ( DBHandler db , BarChart barChart , Calendar day , GBDevice device ) {
day = ( Calendar ) day . clone ( ) ; // do not modify the caller's argument
day . add ( Calendar . DATE , - 7 ) ;
List < BarEntry > entries = new ArrayList < > ( ) ;
ArrayList < String > labels = new ArrayList < String > ( ) ;
for ( int counter = 0 ; counter < 7 ; counter + + ) {
2017-02-26 00:40:50 +01:00
ActivityAmounts amounts = getActivityAmountsForDay ( db , day , device ) ;
2017-02-26 21:41:27 +01:00
entries . add ( new BarEntry ( counter , getTotalsForActivityAmounts ( amounts ) ) ) ;
2017-02-21 16:20:42 +01:00
labels . add ( day . getDisplayName ( Calendar . DAY_OF_WEEK , Calendar . SHORT , mLocale ) ) ;
day . add ( Calendar . DATE , 1 ) ;
}
BarDataSet set = new BarDataSet ( entries , "" ) ;
2017-02-26 21:41:27 +01:00
set . setColors ( getColors ( ) ) ;
2017-03-03 14:21:59 +01:00
set . setValueFormatter ( getBarValueFormatter ( ) ) ;
2017-02-21 16:20:42 +01:00
BarData barData = new BarData ( set ) ;
barData . setValueTextColor ( Color . GRAY ) ; //prevent tearing other graph elements with the black text. Another approach would be to hide the values cmpletely with data.setDrawValues(false);
LimitLine target = new LimitLine ( mTargetValue ) ;
barChart . getAxisLeft ( ) . removeAllLimitLines ( ) ;
barChart . getAxisLeft ( ) . addLimitLine ( target ) ;
return new DefaultChartsData ( barData , new PreformattedXIndexLabelFormatter ( labels ) ) ;
}
private DayData refreshDayPie ( DBHandler db , Calendar day , GBDevice device ) {
PieData data = new PieData ( ) ;
List < PieEntry > entries = new ArrayList < > ( ) ;
2017-02-26 21:41:27 +01:00
PieDataSet set = new PieDataSet ( entries , "" ) ;
ActivityAmounts amounts = getActivityAmountsForDay ( db , day , device ) ;
float totalValues [ ] = getTotalsForActivityAmounts ( amounts ) ;
float totalValue = 0 ;
for ( float value : totalValues ) {
totalValue + = value ;
entries . add ( new PieEntry ( value ) ) ;
}
2017-02-21 16:20:42 +01:00
2017-03-03 14:21:59 +01:00
set . setValueFormatter ( getPieValueFormatter ( ) ) ;
2017-02-26 21:41:27 +01:00
set . setColors ( getColors ( ) ) ;
2017-02-21 16:20:42 +01:00
if ( totalValue < mTargetValue ) {
2017-02-26 21:41:27 +01:00
entries . add ( new PieEntry ( ( mTargetValue - totalValue ) ) ) ;
set . addColor ( Color . GRAY ) ;
2017-02-21 16:20:42 +01:00
}
data . setDataSet ( set ) ;
//this hides the values (numeric) added to the set. These would be shown aside the strings set with addXValue above
data . setDrawValues ( false ) ;
2017-02-26 21:41:27 +01:00
return new DayData ( data , formatPieValue ( ( int ) totalValue ) ) ;
2017-02-21 16:20:42 +01:00
}
@Override
public View onCreateView ( LayoutInflater inflater , ViewGroup container ,
Bundle savedInstanceState ) {
mLocale = getResources ( ) . getConfiguration ( ) . locale ;
View rootView = inflater . inflate ( R . layout . fragment_weeksteps_chart , container , false ) ;
int goal = getGoal ( ) ;
if ( goal > = 0 ) {
mTargetValue = goal ;
}
mTodayPieChart = ( PieChart ) rootView . findViewById ( R . id . todaystepschart ) ;
mWeekChart = ( BarChart ) rootView . findViewById ( R . id . weekstepschart ) ;
setupWeekChart ( ) ;
setupTodayPieChart ( ) ;
// refresh immediately instead of use refreshIfVisible(), for perceived performance
refresh ( ) ;
return rootView ;
}
private void setupTodayPieChart ( ) {
mTodayPieChart . setBackgroundColor ( BACKGROUND_COLOR ) ;
mTodayPieChart . getDescription ( ) . setTextColor ( DESCRIPTION_COLOR ) ;
2017-03-03 14:21:59 +01:00
mTodayPieChart . getDescription ( ) . setText ( getPieDescription ( mTargetValue ) ) ;
2017-02-21 16:20:42 +01:00
// mTodayPieChart.setNoDataTextDescription("");
mTodayPieChart . setNoDataText ( "" ) ;
mTodayPieChart . getLegend ( ) . setEnabled ( false ) ;
// setupLegend(mTodayPieChart);
}
private void setupWeekChart ( ) {
mWeekChart . setBackgroundColor ( BACKGROUND_COLOR ) ;
mWeekChart . getDescription ( ) . setTextColor ( DESCRIPTION_COLOR ) ;
mWeekChart . getDescription ( ) . setText ( "" ) ;
mWeekChart . setFitBars ( true ) ;
configureBarLineChartDefaults ( mWeekChart ) ;
XAxis x = mWeekChart . getXAxis ( ) ;
x . setDrawLabels ( true ) ;
x . setDrawGridLines ( false ) ;
x . setEnabled ( true ) ;
x . setTextColor ( CHART_TEXT_COLOR ) ;
x . setDrawLimitLinesBehindData ( true ) ;
x . setPosition ( XAxis . XAxisPosition . BOTTOM ) ;
YAxis y = mWeekChart . getAxisLeft ( ) ;
y . setDrawGridLines ( false ) ;
y . setDrawTopYLabelEntry ( false ) ;
y . setTextColor ( CHART_TEXT_COLOR ) ;
y . setDrawZeroLine ( true ) ;
y . setSpaceBottom ( 0 ) ;
y . setAxisMinimum ( 0 ) ;
2017-03-03 14:21:59 +01:00
y . setValueFormatter ( getYAxisFormatter ( ) ) ;
2017-02-21 16:20:42 +01:00
y . setEnabled ( true ) ;
YAxis yAxisRight = mWeekChart . getAxisRight ( ) ;
yAxisRight . setDrawGridLines ( false ) ;
yAxisRight . setEnabled ( false ) ;
yAxisRight . setDrawLabels ( false ) ;
yAxisRight . setDrawTopYLabelEntry ( false ) ;
yAxisRight . setTextColor ( CHART_TEXT_COLOR ) ;
}
@Override
protected void setupLegend ( Chart chart ) {
// List<Integer> legendColors = new ArrayList<>(1);
// List<String> legendLabels = new ArrayList<>(1);
// legendColors.add(akActivity.color);
// legendLabels.add(getContext().getString(R.string.chart_steps));
// chart.getLegend().setCustom(legendColors, legendLabels);
// chart.getLegend().setTextColor(LEGEND_TEXT_COLOR);
}
2017-03-03 17:33:00 +01:00
private List < ? extends ActivitySample > getSamplesOfDay ( DBHandler db , Calendar day , int offsetHours , GBDevice device ) {
2017-02-21 16:20:42 +01:00
int startTs ;
int endTs ;
day = ( Calendar ) day . clone ( ) ; // do not modify the caller's argument
day . set ( Calendar . HOUR_OF_DAY , 0 ) ;
day . set ( Calendar . MINUTE , 0 ) ;
day . set ( Calendar . SECOND , 0 ) ;
2017-03-03 17:33:00 +01:00
day . add ( Calendar . HOUR , offsetHours ) ;
2017-02-21 16:20:42 +01:00
2017-03-03 17:33:00 +01:00
startTs = ( int ) ( day . getTimeInMillis ( ) / 1000 ) ;
endTs = startTs + 24 * 60 * 60 - 1 ;
2017-02-21 16:20:42 +01:00
return getSamples ( db , device , startTs , endTs ) ;
}
@Override
protected List < ? extends ActivitySample > getSamples ( DBHandler db , GBDevice device , int tsFrom , int tsTo ) {
return super . getAllSamples ( db , device , tsFrom , tsTo ) ;
}
private static class DayData {
private final PieData data ;
2017-02-23 21:15:57 +01:00
private final CharSequence centerText ;
2017-02-21 16:20:42 +01:00
2017-02-23 21:15:57 +01:00
DayData ( PieData data , String centerText ) {
2017-02-21 16:20:42 +01:00
this . data = data ;
2017-02-23 21:15:57 +01:00
this . centerText = centerText ;
2017-02-21 16:20:42 +01:00
}
}
private static class MyChartsData extends ChartsData {
private final DefaultChartsData < BarData > weekBeforeData ;
private final DayData dayData ;
MyChartsData ( DayData dayData , DefaultChartsData < BarData > weekBeforeData ) {
this . dayData = dayData ;
this . weekBeforeData = weekBeforeData ;
}
DayData getDayData ( ) {
return dayData ;
}
DefaultChartsData < BarData > getWeekBeforeData ( ) {
return weekBeforeData ;
}
}
2017-02-26 00:40:50 +01:00
private ActivityAmounts getActivityAmountsForDay ( DBHandler db , Calendar day , GBDevice device ) {
LimitedQueue activityAmountCache = null ;
ActivityAmounts amounts = null ;
Activity activity = getActivity ( ) ;
if ( activity ! = null ) {
activityAmountCache = ( ( ChartsActivity ) activity ) . mActivityAmountCache ;
2017-03-03 17:33:00 +01:00
amounts = ( ActivityAmounts ) ( activityAmountCache . lookup ( day . hashCode ( ) ^ mOffsetHours ) ) ;
2017-02-26 00:40:50 +01:00
}
if ( amounts = = null ) {
ActivityAnalysis analysis = new ActivityAnalysis ( ) ;
2017-03-03 17:33:00 +01:00
amounts = analysis . calculateActivityAmounts ( getSamplesOfDay ( db , day , mOffsetHours , device ) ) ;
2017-02-26 00:40:50 +01:00
if ( activityAmountCache ! = null ) {
2017-03-03 17:33:00 +01:00
activityAmountCache . add ( day . hashCode ( ) ^ mOffsetHours , amounts ) ;
2017-02-26 00:40:50 +01:00
}
}
return amounts ;
}
2017-02-21 16:20:42 +01:00
abstract int getGoal ( ) ;
2017-03-03 17:33:00 +01:00
abstract int getOffsetHours ( ) ;
2017-02-26 21:41:27 +01:00
abstract float [ ] getTotalsForActivityAmounts ( ActivityAmounts activityAmounts ) ;
2017-02-23 08:49:55 +01:00
2017-03-03 14:21:59 +01:00
abstract String formatPieValue ( int value ) ;
abstract IValueFormatter getPieValueFormatter ( ) ;
abstract IValueFormatter getBarValueFormatter ( ) ;
abstract IAxisValueFormatter getYAxisFormatter ( ) ;
2017-02-23 08:49:55 +01:00
2017-02-26 21:41:27 +01:00
abstract int [ ] getColors ( ) ;
2017-03-03 14:21:59 +01:00
abstract String getPieDescription ( int targetValue ) ;
2017-02-21 16:20:42 +01:00
}