From eb9646cc7e01366527b5ecfe2700300ce276bb36 Mon Sep 17 00:00:00 2001 From: Milos Kozak Date: Thu, 27 Oct 2022 10:16:49 +0200 Subject: [PATCH] replace graphview module by aar --- app/build.gradle | 5 +- automation/build.gradle | 4 +- core/build.gradle | 3 +- graphview/.gitignore | 1 - graphview/build.gradle | 20 - graphview/consumer-rules.pro | 0 graphview/proguard-rules.pro | 21 - graphview/src/main/AndroidManifest.xml | 4 - .../graphview/DefaultLabelFormatter.java | 105 -- .../java/com/jjoe64/graphview/GraphView.java | 548 ------ .../jjoe64/graphview/GridLabelRenderer.java | 1467 ----------------- .../com/jjoe64/graphview/LabelFormatter.java | 54 - .../com/jjoe64/graphview/LegendRenderer.java | 394 ----- .../com/jjoe64/graphview/SecondScale.java | 165 -- .../jjoe64/graphview/ValueDependentColor.java | 41 - .../java/com/jjoe64/graphview/Viewport.java | 996 ----------- .../graphview/compat/OverScrollerCompat.java | 46 - .../helper/DateAsXAxisLabelFormatter.java | 94 -- .../jjoe64/graphview/helper/GraphViewXML.java | 137 -- .../helper/StaticLabelsFormatter.java | 209 --- .../graphview/series/BarGraphSeries.java | 379 ----- .../jjoe64/graphview/series/BaseSeries.java | 448 ----- .../jjoe64/graphview/series/DataPoint.java | 63 - .../graphview/series/DataPointInterface.java | 41 - .../graphview/series/LineGraphSeries.java | 409 ----- .../series/OnDataPointTapListener.java | 38 - .../graphview/series/PointsGraphSeries.java | 312 ---- .../com/jjoe64/graphview/series/Series.java | 125 -- graphview/src/main/res/values/attr.xml | 10 - .../com/joanzapata/iconify/IconDrawable.java | 229 --- libs/graphview.aar | Bin 0 -> 62697 bytes {app/libs => libs}/ustwo-clockwise-debug.aar | Bin .../wearpreferenceactivity-0.5.0.aar | Bin settings.gradle | 3 +- wear/build.gradle | 4 +- wear/libs/ustwo-clockwise-debug.aar | Bin 90266 -> 0 bytes wear/libs/wearpreferenceactivity-0.5.0.aar | Bin 30815 -> 0 bytes 37 files changed, 11 insertions(+), 6364 deletions(-) delete mode 100644 graphview/.gitignore delete mode 100644 graphview/build.gradle delete mode 100644 graphview/consumer-rules.pro delete mode 100644 graphview/proguard-rules.pro delete mode 100644 graphview/src/main/AndroidManifest.xml delete mode 100644 graphview/src/main/java/com/jjoe64/graphview/DefaultLabelFormatter.java delete mode 100644 graphview/src/main/java/com/jjoe64/graphview/GraphView.java delete mode 100644 graphview/src/main/java/com/jjoe64/graphview/GridLabelRenderer.java delete mode 100644 graphview/src/main/java/com/jjoe64/graphview/LabelFormatter.java delete mode 100644 graphview/src/main/java/com/jjoe64/graphview/LegendRenderer.java delete mode 100644 graphview/src/main/java/com/jjoe64/graphview/SecondScale.java delete mode 100644 graphview/src/main/java/com/jjoe64/graphview/ValueDependentColor.java delete mode 100644 graphview/src/main/java/com/jjoe64/graphview/Viewport.java delete mode 100644 graphview/src/main/java/com/jjoe64/graphview/compat/OverScrollerCompat.java delete mode 100644 graphview/src/main/java/com/jjoe64/graphview/helper/DateAsXAxisLabelFormatter.java delete mode 100644 graphview/src/main/java/com/jjoe64/graphview/helper/GraphViewXML.java delete mode 100644 graphview/src/main/java/com/jjoe64/graphview/helper/StaticLabelsFormatter.java delete mode 100644 graphview/src/main/java/com/jjoe64/graphview/series/BarGraphSeries.java delete mode 100644 graphview/src/main/java/com/jjoe64/graphview/series/BaseSeries.java delete mode 100644 graphview/src/main/java/com/jjoe64/graphview/series/DataPoint.java delete mode 100644 graphview/src/main/java/com/jjoe64/graphview/series/DataPointInterface.java delete mode 100644 graphview/src/main/java/com/jjoe64/graphview/series/LineGraphSeries.java delete mode 100644 graphview/src/main/java/com/jjoe64/graphview/series/OnDataPointTapListener.java delete mode 100644 graphview/src/main/java/com/jjoe64/graphview/series/PointsGraphSeries.java delete mode 100644 graphview/src/main/java/com/jjoe64/graphview/series/Series.java delete mode 100644 graphview/src/main/res/values/attr.xml delete mode 100644 iconify/src/main/java/com/joanzapata/iconify/IconDrawable.java create mode 100644 libs/graphview.aar rename {app/libs => libs}/ustwo-clockwise-debug.aar (100%) rename {app/libs => libs}/wearpreferenceactivity-0.5.0.aar (100%) delete mode 100644 wear/libs/ustwo-clockwise-debug.aar delete mode 100644 wear/libs/wearpreferenceactivity-0.5.0.aar diff --git a/app/build.gradle b/app/build.gradle index 51246830f1..82ecb4b85d 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -169,8 +169,11 @@ allprojects { dependencies { wearApp project(':wear') + // https://github.com/nightscout/graphview.git + // in order to use internet's version you'd need to enable Jetifier again + implementation(files("${rootProject.rootDir}/libs/graphview.aar")) + implementation project(':iconify') - implementation project(':graphview') implementation project(':shared') implementation project(':core') implementation project(':automation') diff --git a/automation/build.gradle b/automation/build.gradle index f778782309..9d2099d443 100644 --- a/automation/build.gradle +++ b/automation/build.gradle @@ -12,10 +12,10 @@ android { namespace 'info.nightscout.androidaps.automation' } - dependencies { + implementation(files("${rootProject.rootDir}/libs/graphview.aar")) + implementation project(':core') implementation project(':database') implementation project(':shared') - implementation project(':graphview') } \ No newline at end of file diff --git a/core/build.gradle b/core/build.gradle index 567918dc1b..784e264e5d 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -12,9 +12,10 @@ apply from: "${project.rootDir}/core/test_dependencies.gradle" apply from: "${project.rootDir}/core/jacoco_global.gradle" dependencies { + implementation(files("${rootProject.rootDir}/libs/graphview.aar")) + implementation project(':shared') implementation project(':database') - implementation project(':graphview') } android { diff --git a/graphview/.gitignore b/graphview/.gitignore deleted file mode 100644 index 42afabfd2a..0000000000 --- a/graphview/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/build \ No newline at end of file diff --git a/graphview/build.gradle b/graphview/build.gradle deleted file mode 100644 index e1138b7d40..0000000000 --- a/graphview/build.gradle +++ /dev/null @@ -1,20 +0,0 @@ -apply plugin: 'com.android.library' -apply plugin: 'kotlin-android' -apply plugin: 'kotlin-kapt' -apply plugin: 'kotlin-allopen' -apply plugin: 'com.hiya.jacoco-android' -apply plugin: 'kotlinx-serialization' - -apply from: "${project.rootDir}/core/android_dependencies.gradle" -apply from: "${project.rootDir}/core/android_module_dependencies.gradle" -apply from: "${project.rootDir}/core/test_dependencies.gradle" -apply from: "${project.rootDir}/core/jacoco_global.gradle" - -android { - - namespace 'com.jjoe64.graphview' -} - -dependencies { - api "androidx.core:core-ktx:$core_version" -} \ No newline at end of file diff --git a/graphview/consumer-rules.pro b/graphview/consumer-rules.pro deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/graphview/proguard-rules.pro b/graphview/proguard-rules.pro deleted file mode 100644 index 481bb43481..0000000000 --- a/graphview/proguard-rules.pro +++ /dev/null @@ -1,21 +0,0 @@ -# Add project specific ProGuard rules here. -# You can control the set of applied configuration files using the -# proguardFiles setting in build.gradle. -# -# For more details, see -# http://developer.android.com/guide/developing/tools/proguard.html - -# If your project uses WebView with JS, uncomment the following -# and specify the fully qualified class name to the JavaScript interface -# class: -#-keepclassmembers class fqcn.of.javascript.interface.for.webview { -# public *; -#} - -# Uncomment this to preserve the line number information for -# debugging stack traces. -#-keepattributes SourceFile,LineNumberTable - -# If you keep the line number information, uncomment this to -# hide the original source file name. -#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/graphview/src/main/AndroidManifest.xml b/graphview/src/main/AndroidManifest.xml deleted file mode 100644 index a5918e68ab..0000000000 --- a/graphview/src/main/AndroidManifest.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/graphview/src/main/java/com/jjoe64/graphview/DefaultLabelFormatter.java b/graphview/src/main/java/com/jjoe64/graphview/DefaultLabelFormatter.java deleted file mode 100644 index 0a761e2d73..0000000000 --- a/graphview/src/main/java/com/jjoe64/graphview/DefaultLabelFormatter.java +++ /dev/null @@ -1,105 +0,0 @@ -/** - * GraphView - * Copyright (C) 2014 Jonas Gehring - * - * 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, - * with the "Linking Exception", which can be found at the license.txt - * file in this program. - * - * 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 - * with the "Linking Exception" along with this program; if not, - * write to the author Jonas Gehring . - */ -package com.jjoe64.graphview; - -import java.text.NumberFormat; - -/** - * The label formatter that will be used - * by default. - * It will use the NumberFormat from Android - * and sets the maximal fraction digits - * depending on the range between min and max - * value of the current viewport. - * - * It is recommended to use this label formatter - * as base class to implement a custom formatter. - * - * @author jjoe64 - */ -public class DefaultLabelFormatter implements LabelFormatter { - /** - * number formatter for x and y values - */ - protected NumberFormat[] mNumberFormatter = new NumberFormat[2]; - - /** - * reference to the viewport of the - * graph. - * Will be used to calculate the current - * range of values. - */ - protected Viewport mViewport; - - /** - * uses the default number format for the labels - */ - public DefaultLabelFormatter() { - } - - /** - * use custom number format - * - * @param xFormat the number format for the x labels - * @param yFormat the number format for the y labels - */ - public DefaultLabelFormatter(NumberFormat xFormat, NumberFormat yFormat) { - mNumberFormatter[0] = yFormat; - mNumberFormatter[1] = xFormat; - } - - /** - * @param viewport the viewport of the graph - */ - @Override - public void setViewport(Viewport viewport) { - mViewport = viewport; - } - - /** - * Formats the raw value to a nice - * looking label, depending on the - * current range of the viewport. - * - * @param value raw value - * @param isValueX true if it's a x value, otherwise false - * @return the formatted value as string - */ - public String formatLabel(double value, boolean isValueX) { - int i = isValueX ? 1 : 0; - if (mNumberFormatter[i] == null) { - mNumberFormatter[i] = NumberFormat.getNumberInstance(); - double highestvalue = isValueX ? mViewport.getMaxX(false) : mViewport.getMaxY(false); - double lowestvalue = isValueX ? mViewport.getMinX(false) : mViewport.getMinY(false); - if (highestvalue - lowestvalue < 0.1) { - mNumberFormatter[i].setMaximumFractionDigits(6); - } else if (highestvalue - lowestvalue < 1) { - mNumberFormatter[i].setMaximumFractionDigits(4); - } else if (highestvalue - lowestvalue < 20) { - mNumberFormatter[i].setMaximumFractionDigits(3); - } else if (highestvalue - lowestvalue < 100) { - mNumberFormatter[i].setMaximumFractionDigits(1); - } else { - mNumberFormatter[i].setMaximumFractionDigits(0); - } - } - return mNumberFormatter[i].format(value); - } -} diff --git a/graphview/src/main/java/com/jjoe64/graphview/GraphView.java b/graphview/src/main/java/com/jjoe64/graphview/GraphView.java deleted file mode 100644 index 51debe1eef..0000000000 --- a/graphview/src/main/java/com/jjoe64/graphview/GraphView.java +++ /dev/null @@ -1,548 +0,0 @@ -/** - * GraphView - * Copyright (C) 2014 Jonas Gehring - * - * 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, - * with the "Linking Exception", which can be found at the license.txt - * file in this program. - * - * 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 - * with the "Linking Exception" along with this program; if not, - * write to the author Jonas Gehring . - */ -package com.jjoe64.graphview; - -import android.content.Context; -import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.Paint; -import android.graphics.Point; -import android.graphics.PointF; -import android.util.AttributeSet; -import android.util.Log; -import android.view.MotionEvent; -import android.view.View; - -import com.jjoe64.graphview.series.Series; - -import java.util.ArrayList; -import java.util.List; - -/** - * @author jjoe64 - * @version 4.0.0 - */ -public class GraphView extends View { - /** - * Class to wrap style options that are general - * to graphs. - * - * @author jjoe64 - */ - private static final class Styles { - /** - * The font size of the title that can be displayed - * above the graph. - * - * @see GraphView#setTitle(String) - */ - float titleTextSize; - - /** - * The font color of the title that can be displayed - * above the graph. - * - * @see GraphView#setTitle(String) - */ - int titleColor; - } - - /** - * Helper class to detect tap events on the - * graph. - * - * @author jjoe64 - */ - private class TapDetector { - /** - * save the time of the last down event - */ - private long lastDown; - - /** - * point of the tap down event - */ - private PointF lastPoint; - - /** - * to be called to process the events - * - * @param event - * @return true if there was a tap event. otherwise returns false. - */ - public boolean onTouchEvent(MotionEvent event) { - if (event.getAction() == MotionEvent.ACTION_DOWN) { - lastDown = System.currentTimeMillis(); - lastPoint = new PointF(event.getX(), event.getY()); - } else if (lastDown > 0 && event.getAction() == MotionEvent.ACTION_MOVE) { - if (Math.abs(event.getX() - lastPoint.x) > 60 - || Math.abs(event.getY() - lastPoint.y) > 60) { - lastDown = 0; - } - } else if (event.getAction() == MotionEvent.ACTION_UP) { - if (System.currentTimeMillis() - lastDown < 400) { - return true; - } - } - return false; - } - } - - /** - * our series (this does not contain the series - * that can be displayed on the right side. The - * right side series is a special feature of - * the {@link SecondScale} feature. - */ - private List mSeries; - - /** - * the renderer for the grid and labels - */ - private GridLabelRenderer mGridLabelRenderer; - - /** - * viewport that holds the current bounds of - * view. - */ - private Viewport mViewport; - - /** - * title of the graph that will be shown above - */ - private String mTitle; - - /** - * wraps the general styles - */ - private Styles mStyles; - - /** - * feature to have a second scale e.g. on the - * right side - */ - protected SecondScale mSecondScale; - - /** - * tap detector - */ - private TapDetector mTapDetector; - - /** - * renderer for the legend - */ - private LegendRenderer mLegendRenderer; - - /** - * paint for the graph title - */ - private Paint mPaintTitle; - - /** - * paint for the preview (in the SDK) - */ - private Paint mPreviewPaint; - - /** - * Initialize the GraphView view - * @param context - */ - public GraphView(Context context) { - super(context); - init(); - } - - /** - * Initialize the GraphView view. - * - * @param context - * @param attrs - */ - public GraphView(Context context, AttributeSet attrs) { - super(context, attrs); - init(); - } - - /** - * Initialize the GraphView view - * - * @param context - * @param attrs - * @param defStyle - */ - public GraphView(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - init(); - } - - /** - * initialize the internal objects. - * This method has to be called directly - * in the constructors. - */ - protected void init() { - mPreviewPaint = new Paint(); - mPreviewPaint.setTextAlign(Paint.Align.CENTER); - mPreviewPaint.setColor(Color.BLACK); - mPreviewPaint.setTextSize(50); - - mStyles = new Styles(); - mViewport = new Viewport(this); - mGridLabelRenderer = new GridLabelRenderer(this); - mLegendRenderer = new LegendRenderer(this); - - mSeries = new ArrayList(); - mPaintTitle = new Paint(); - - mTapDetector = new TapDetector(); - - loadStyles(); - } - - /** - * loads the font - */ - protected void loadStyles() { - mStyles.titleColor = mGridLabelRenderer.getHorizontalLabelsColor(); - mStyles.titleTextSize = mGridLabelRenderer.getTextSize(); - } - - /** - * @return the renderer for the grid and labels - */ - public GridLabelRenderer getGridLabelRenderer() { - return mGridLabelRenderer; - } - - /** - * Add a new series to the graph. This will - * automatically redraw the graph. - * @param s the series to be added - */ - public void addSeries(Series s) { - s.onGraphViewAttached(this); - mSeries.add(s); - onDataChanged(false, false); - } - - /** - * important: do not do modifications on the list - * object that will be returned. - * Use {@link #removeSeries(com.jjoe64.graphview.series.Series)} and {@link #addSeries(com.jjoe64.graphview.series.Series)} - * - * @return all series - */ - public List getSeries() { - // TODO immutable array - return mSeries; - } - - /** - * call this to let the graph redraw and - * recalculate the viewport. - * This will be called when a new series - * was added or removed and when data - * was appended via {@link com.jjoe64.graphview.series.BaseSeries#appendData(com.jjoe64.graphview.series.DataPointInterface, boolean, int)} - * or {@link com.jjoe64.graphview.series.BaseSeries#resetData(com.jjoe64.graphview.series.DataPointInterface[])}. - * - * @param keepLabelsSize true if you don't want - * to recalculate the size of - * the labels. It is recommended - * to use "true" because this will - * improve performance and prevent - * a flickering. - * @param keepViewport true if you don't want that - * the viewport will be recalculated. - * It is recommended to use "true" for - * performance. - */ - public void onDataChanged(boolean keepLabelsSize, boolean keepViewport) { - // adjust grid system - mViewport.calcCompleteRange(); - mGridLabelRenderer.invalidate(keepLabelsSize, keepViewport); - invalidate(); - } - - /** - * will be called from Android system. - * - * @param canvas Canvas - */ - @Override - protected void onDraw(Canvas canvas) { - if (isInEditMode()) { - canvas.drawColor(Color.rgb(200, 200, 200)); - canvas.drawText("GraphView: No Preview available", canvas.getWidth()/2, canvas.getHeight()/2, mPreviewPaint); - } else { - drawTitle(canvas); - mViewport.drawFirst(canvas); - mGridLabelRenderer.draw(canvas); - for (Series s : mSeries) { - s.draw(this, canvas, false); - } - if (mSecondScale != null) { - for (Series s : mSecondScale.getSeries()) { - s.draw(this, canvas, true); - } - } - mViewport.draw(canvas); - mLegendRenderer.draw(canvas); - } - } - - /** - * Draws the Graphs title that will be - * shown above the viewport. - * Will be called by GraphView. - * - * @param canvas Canvas - */ - protected void drawTitle(Canvas canvas) { - if (mTitle != null && mTitle.length()>0) { - mPaintTitle.setColor(mStyles.titleColor); - mPaintTitle.setTextSize(mStyles.titleTextSize); - mPaintTitle.setTextAlign(Paint.Align.CENTER); - float x = canvas.getWidth()/2; - float y = mPaintTitle.getTextSize(); - canvas.drawText(mTitle, x, y, mPaintTitle); - } - } - - /** - * Calculates the height of the title. - * - * @return the actual size of the title. - * if there is no title, 0 will be - * returned. - */ - protected int getTitleHeight() { - if (mTitle != null && mTitle.length()>0) { - return (int) mPaintTitle.getTextSize(); - } else { - return 0; - } - } - - /** - * @return the viewport of the Graph. - * @see com.jjoe64.graphview.Viewport - */ - public Viewport getViewport() { - return mViewport; - } - - /** - * Called by Android system if the size - * of the view was changed. Will recalculate - * the viewport and labels. - * - * @param w - * @param h - * @param oldw - * @param oldh - */ - @Override - protected void onSizeChanged(int w, int h, int oldw, int oldh) { - super.onSizeChanged(w, h, oldw, oldh); - onDataChanged(false, false); - } - - /** - * @return the space on the left side of the - * view from the left border to the - * beginning of the graph viewport. - */ - public int getGraphContentLeft() { - int border = getGridLabelRenderer().getStyles().padding; - return border + getGridLabelRenderer().getLabelVerticalWidth() + getGridLabelRenderer().getVerticalAxisTitleWidth(); - } - - /** - * @return the space on the top of the - * view from the top border to the - * beginning of the graph viewport. - */ - public int getGraphContentTop() { - int border = getGridLabelRenderer().getStyles().padding + getTitleHeight(); - return border; - } - - /** - * @return the height of the graph viewport. - */ - public int getGraphContentHeight() { - int border = getGridLabelRenderer().getStyles().padding; - int graphheight = getHeight() - (2 * border) - getGridLabelRenderer().getLabelHorizontalHeight() - getTitleHeight(); - graphheight -= getGridLabelRenderer().getHorizontalAxisTitleHeight(); - return graphheight; - } - - /** - * @return the width of the graph viewport. - */ - public int getGraphContentWidth() { - int border = getGridLabelRenderer().getStyles().padding; - int graphwidth = getWidth() - (2 * border) - getGridLabelRenderer().getLabelVerticalWidth(); - if (mSecondScale != null) { - graphwidth -= getGridLabelRenderer().getLabelVerticalSecondScaleWidth(); - } - return graphwidth; - } - - /** - * will be called from Android system. - * - * @param event - * @return - */ - @Override - public boolean onTouchEvent(MotionEvent event) { - boolean b = mViewport.onTouchEvent(event); - boolean a = super.onTouchEvent(event); - - // is it a click? - if (mTapDetector.onTouchEvent(event)) { - for (Series s : mSeries) { - s.onTap(event.getX(), event.getY()); - } - if (mSecondScale != null) { - for (Series s : mSecondScale.getSeries()) { - s.onTap(event.getX(), event.getY()); - } - } - } - - return b || a; - } - - /** - * - */ - @Override - public void computeScroll() { - super.computeScroll(); - mViewport.computeScroll(); - } - - /** - * @return the legend renderer. - * @see com.jjoe64.graphview.LegendRenderer - */ - public LegendRenderer getLegendRenderer() { - return mLegendRenderer; - } - - /** - * use a specific legend renderer - * - * @param mLegendRenderer the new legend renderer - */ - public void setLegendRenderer(LegendRenderer mLegendRenderer) { - this.mLegendRenderer = mLegendRenderer; - } - - /** - * @return the title that will be shown - * above the graph. - */ - public String getTitle() { - return mTitle; - } - - /** - * Set the title of the graph that will - * be shown above the graph's viewport. - * - * @param mTitle the title - * @see #setTitleColor(int) to set the font color - * @see #setTitleTextSize(float) to set the font size - */ - public void setTitle(String mTitle) { - this.mTitle = mTitle; - } - - /** - * @return the title font size - */ - public float getTitleTextSize() { - return mStyles.titleTextSize; - } - - /** - * Set the title's font size - * - * @param titleTextSize font size - * @see #setTitle(String) - */ - public void setTitleTextSize(float titleTextSize) { - mStyles.titleTextSize = titleTextSize; - } - - /** - * @return font color of the title - */ - public int getTitleColor() { - return mStyles.titleColor; - } - - /** - * Set the title's font color - * - * @param titleColor font color of the title - * @see #setTitle(String) - */ - public void setTitleColor(int titleColor) { - mStyles.titleColor = titleColor; - } - - /** - * - * @return - */ - public SecondScale getSecondScale() { - if (mSecondScale == null) { - mSecondScale = new SecondScale(mViewport); - } - return mSecondScale; - } - - /** - * Removes all series of the graph. - */ - public void removeAllSeries() { - mSeries.clear(); - onDataChanged(false, false); - } - - /** - * Remove a specific series of the graph. - * This will also re-render the graph, but - * without recalculating the viewport and - * label sizes. - * If you want this, you have to call {@link #onDataChanged(boolean, boolean)} - * manually. - * - * @param series - */ - public void removeSeries(Series series) { - mSeries.remove(series); - onDataChanged(false, false); - } -} diff --git a/graphview/src/main/java/com/jjoe64/graphview/GridLabelRenderer.java b/graphview/src/main/java/com/jjoe64/graphview/GridLabelRenderer.java deleted file mode 100644 index e6060c9b50..0000000000 --- a/graphview/src/main/java/com/jjoe64/graphview/GridLabelRenderer.java +++ /dev/null @@ -1,1467 +0,0 @@ -/** - * GraphView - * Copyright (C) 2014 Jonas Gehring - * - * 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, - * with the "Linking Exception", which can be found at the license.txt - * file in this program. - * - * 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 - * with the "Linking Exception" along with this program; if not, - * write to the author Jonas Gehring . - */ -package com.jjoe64.graphview; - -import android.content.res.TypedArray; -import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.Paint; -import android.graphics.Rect; -import android.util.TypedValue; - -import androidx.core.view.ViewCompat; - -import java.util.LinkedHashMap; -import java.util.Map; - -/** - * The default renderer for the grid - * and the labels. - * - * @author jjoe64 - */ -public class GridLabelRenderer { - /** - * wrapper for the styles regarding - * to the grid and the labels - */ - public final class Styles { - /** - * the general text size of the axis titles. - * can be overwritten with #verticalAxisTitleTextSize - * and #horizontalAxisTitleTextSize - */ - public float textSize; - - /** - * the alignment of the vertical labels - */ - public Paint.Align verticalLabelsAlign; - - /** - * the alignment of the labels on the right side - */ - public Paint.Align verticalLabelsSecondScaleAlign; - - /** - * the color of the vertical labels - */ - public int verticalLabelsColor; - - /** - * the color of the labels on the right side - */ - public int verticalLabelsSecondScaleColor; - - /** - * the color of the horizontal labels - */ - public int horizontalLabelsColor; - - /** - * the color of the grid lines - */ - public int gridColor; - - /** - * flag whether the zero-lines (vertical+ - * horizontal) shall be highlighted - */ - public boolean highlightZeroLines; - - /** - * the padding around the graph and labels - */ - public int padding; - - /** - * font size of the vertical axis title - */ - public float verticalAxisTitleTextSize; - - /** - * font color of the vertical axis title - */ - public int verticalAxisTitleColor; - - /** - * font size of the horizontal axis title - */ - public float horizontalAxisTitleTextSize; - - /** - * font color of the horizontal axis title - */ - public int horizontalAxisTitleColor; - - /** - * flag whether the horizontal labels are - * visible - */ - boolean horizontalLabelsVisible; - - /** - * flag whether the vertical labels are - * visible - */ - boolean verticalLabelsVisible; - - /** - * defines which lines will be drawn in the background - */ - GridStyle gridStyle; - - /** - * the space between the labels text and the graph content - */ - int labelsSpace; - } - - /** - * Definition which lines will be drawn in the background - */ - public enum GridStyle { - BOTH, VERTICAL, HORIZONTAL, NONE; - - public boolean drawVertical() { return this == BOTH || this == VERTICAL && this != NONE; } - public boolean drawHorizontal() { return this == BOTH || this == HORIZONTAL && this != NONE; } - } - - /** - * wraps the styles regarding the - * grid and labels - */ - protected Styles mStyles; - - /** - * reference to graphview - */ - private final GraphView mGraphView; - - /** - * cache of the vertical steps - * (horizontal lines and vertical labels) - * Key = Pixel (y) - * Value = y-value - */ - private Map mStepsVertical; - - /** - * cache of the vertical steps for the - * second scale, which is on the right side - * (horizontal lines and vertical labels) - * Key = Pixel (y) - * Value = y-value - */ - private Map mStepsVerticalSecondScale; - - /** - * cache of the horizontal steps - * (vertical lines and horizontal labels) - * Key = Pixel (x) - * Value = x-value - */ - private Map mStepsHorizontal; - - /** - * the paint to draw the grid lines - */ - private Paint mPaintLine; - - /** - * the paint to draw the labels - */ - private Paint mPaintLabel; - - /** - * the paint to draw axis titles - */ - private Paint mPaintAxisTitle; - - /** - * flag whether is bounds are automatically - * adjusted for nice human-readable numbers - */ - private boolean mIsAdjusted; - - /** - * the width of the vertical labels - */ - private Integer mLabelVerticalWidth; - - /** - * indicates if the width was set manually - */ - private boolean mLabelVerticalWidthFixed; - - /** - * the height of the vertical labels - */ - private Integer mLabelVerticalHeight; - - /** - * indicates if the height was set manually - */ - private boolean mLabelHorizontalHeightFixed; - - /** - * the width of the vertical labels - * of the second scale - */ - private Integer mLabelVerticalSecondScaleWidth; - - /** - * the height of the vertical labels - * of the second scale - */ - private Integer mLabelVerticalSecondScaleHeight; - - /** - * the width of the horizontal labels - */ - private Integer mLabelHorizontalWidth; - - /** - * the height of the horizontal labels - */ - private Integer mLabelHorizontalHeight; - - /** - * the label formatter, that converts - * the raw numbers to strings - */ - private LabelFormatter mLabelFormatter; - - /** - * the title of the horizontal axis - */ - private String mHorizontalAxisTitle; - - /** - * the title of the vertical axis - */ - private String mVerticalAxisTitle; - - /** - * count of the vertical labels, that - * will be shown at one time. - */ - private int mNumVerticalLabels; - - /** - * count of the horizontal labels, that - * will be shown at one time. - */ - private int mNumHorizontalLabels; - - /** - * create the default grid label renderer. - * - * @param graphView the corresponding graphview object - */ - public GridLabelRenderer(GraphView graphView) { - mGraphView = graphView; - setLabelFormatter(new DefaultLabelFormatter()); - mStyles = new Styles(); - resetStyles(); - mNumVerticalLabels = 5; - mNumHorizontalLabels = 5; - } - - /** - * resets the styles. This loads the style - * from reading the values of the current - * theme. - */ - public void resetStyles() { - // get matching styles from theme - TypedValue typedValue = new TypedValue(); - mGraphView.getContext().getTheme().resolveAttribute(android.R.attr.textAppearanceSmall, typedValue, true); - - int color1; - int color2; - int size; - int size2; - - TypedArray array = null; - try { - array = mGraphView.getContext().obtainStyledAttributes(typedValue.data, new int[]{ - android.R.attr.textColorPrimary - , android.R.attr.textColorSecondary - , android.R.attr.textSize - , android.R.attr.horizontalGap}); - color1 = array.getColor(0, Color.BLACK); - color2 = array.getColor(1, Color.GRAY); - size = array.getDimensionPixelSize(2, 20); - size2 = array.getDimensionPixelSize(3, 20); - array.recycle(); - } catch (Exception e) { - color1 = Color.BLACK; - color2 = Color.GRAY; - size = 20; - size2 = 20; - } - - mStyles.verticalLabelsColor = color1; - mStyles.verticalLabelsSecondScaleColor = color1; - mStyles.horizontalLabelsColor = color1; - mStyles.gridColor = color2; - mStyles.textSize = size; - mStyles.padding = size2; - mStyles.labelsSpace = (int) mStyles.textSize/5; - - mStyles.verticalLabelsAlign = Paint.Align.RIGHT; - mStyles.verticalLabelsSecondScaleAlign = Paint.Align.LEFT; - mStyles.highlightZeroLines = true; - - mStyles.verticalAxisTitleColor = mStyles.verticalLabelsColor; - mStyles.horizontalAxisTitleColor = mStyles.horizontalLabelsColor; - mStyles.verticalAxisTitleTextSize = mStyles.textSize; - mStyles.horizontalAxisTitleTextSize = mStyles.textSize; - - mStyles.horizontalLabelsVisible = true; - mStyles.verticalLabelsVisible = true; - - mStyles.gridStyle = GridStyle.BOTH; - - reloadStyles(); - } - - /** - * will load the styles to the internal - * paint objects (color, text size, text align) - */ - public void reloadStyles() { - mPaintLine = new Paint(); - mPaintLine.setColor(mStyles.gridColor); - mPaintLine.setStrokeWidth(0); - - mPaintLabel = new Paint(); - mPaintLabel.setTextSize(getTextSize()); - - mPaintAxisTitle = new Paint(); - mPaintAxisTitle.setTextSize(getTextSize()); - mPaintAxisTitle.setTextAlign(Paint.Align.CENTER); - } - - /** - * @return the general text size for the axis titles - */ - public float getTextSize() { - return mStyles.textSize; - } - - /** - * @return the font color of the vertical labels - */ - public int getVerticalLabelsColor() { - return mStyles.verticalLabelsColor; - } - - /** - * @return the alignment of the text of the - * vertical labels - */ - public Paint.Align getVerticalLabelsAlign() { - return mStyles.verticalLabelsAlign; - } - - /** - * @return the font color of the horizontal labels - */ - public int getHorizontalLabelsColor() { - return mStyles.horizontalLabelsColor; - } - - /** - * clears the internal cache and forces - * to redraw the grid and labels. - * Normally you should always call {@link GraphView#onDataChanged(boolean, boolean)} - * which will call this method. - * - * @param keepLabelsSize true if you don't want - * to recalculate the size of - * the labels. It is recommended - * to use "true" because this will - * improve performance and prevent - * a flickering. - * @param keepViewport true if you don't want that - * the viewport will be recalculated. - * It is recommended to use "true" for - * performance. - */ - public void invalidate(boolean keepLabelsSize, boolean keepViewport) { - if (!keepViewport) { - mIsAdjusted = false; - } - if (!keepLabelsSize) { - if (!mLabelVerticalWidthFixed) { - mLabelVerticalWidth = null; - } - mLabelVerticalHeight = null; - mLabelVerticalSecondScaleWidth = null; - mLabelVerticalSecondScaleHeight = null; - } - //reloadStyles(); - } - - /** - * calculates the vertical steps of - * the second scale. - * This will not do any automatically update - * of the bounds. - * Use always manual bounds for the second scale. - * - * @return true if it is ready - */ - protected boolean adjustVerticalSecondScale() { - if (mLabelHorizontalHeight == null) { - return false; - } - if (mGraphView.mSecondScale == null) { - return true; - } - - double minY = mGraphView.mSecondScale.getMinY(); - double maxY = mGraphView.mSecondScale.getMaxY(); - - // TODO find the number of labels - int numVerticalLabels = mNumVerticalLabels; - - double newMinY; - double exactSteps; - - if (mGraphView.mSecondScale.isYAxisBoundsManual()) { - newMinY = minY; - double rangeY = maxY - newMinY; - exactSteps = rangeY / (numVerticalLabels - 1); - } else { - // TODO auto adjusting - throw new IllegalStateException("Not yet implemented"); - } - - double newMaxY = newMinY + (numVerticalLabels - 1) * exactSteps; - - // TODO auto adjusting - //mGraphView.getViewport().setMinY(newMinY); - //mGraphView.getViewport().setMaxY(newMaxY); - - //if (!mGraphView.getViewport().isYAxisBoundsManual()) { - // mGraphView.getViewport().setYAxisBoundsStatus(Viewport.AxisBoundsStatus.AUTO_ADJUSTED); - //} - - if (mStepsVerticalSecondScale != null) { - mStepsVerticalSecondScale.clear(); - } else { - mStepsVerticalSecondScale = new LinkedHashMap(numVerticalLabels); - } - int height = mGraphView.getGraphContentHeight(); - double v = newMaxY; - int p = mGraphView.getGraphContentTop(); // start - int pixelStep = height / (numVerticalLabels - 1); - for (int i = 0; i < numVerticalLabels; i++) { - mStepsVerticalSecondScale.put(p, v); - p += pixelStep; - v -= exactSteps; - } - - return true; - } - - /** - * calculates the vertical steps. This will - * automatically change the bounds to nice - * human-readable min/max. - * - * @return true if it is ready - */ - protected boolean adjustVertical() { - if (mLabelHorizontalHeight == null) { - return false; - } - - double minY = mGraphView.getViewport().getMinY(false); - double maxY = mGraphView.getViewport().getMaxY(false); - - if (minY == maxY) { - return false; - } - - // TODO find the number of labels - int numVerticalLabels = mNumVerticalLabels; - - double newMinY; - double exactSteps; - - if (mGraphView.getViewport().isYAxisBoundsManual()) { - newMinY = minY; - double rangeY = maxY - newMinY; - exactSteps = rangeY / (numVerticalLabels - 1); - } else { - // find good steps - boolean adjusting = true; - newMinY = minY; - exactSteps = 0d; - while (adjusting) { - double rangeY = maxY - newMinY; - exactSteps = rangeY / (numVerticalLabels - 1); - exactSteps = humanRound(exactSteps, true); - - // adjust viewport - // wie oft passt STEP in minY rein? - int count = 0; - if (newMinY >= 0d) { - // positive number - while (newMinY - exactSteps >= 0) { - newMinY -= exactSteps; - count++; - } - newMinY = exactSteps * count; - } else { - // negative number - count++; - while (newMinY + exactSteps < 0) { - newMinY += exactSteps; - count++; - } - newMinY = exactSteps * count * -1; - } - - // wenn minY sich geändert hat, steps nochmal berechnen - // wenn nicht, fertig - if (newMinY == minY) { - adjusting = false; - } else { - minY = newMinY; - } - } - } - - double newMaxY = newMinY + (numVerticalLabels - 1) * exactSteps; - mGraphView.getViewport().setMinY(newMinY); - mGraphView.getViewport().setMaxY(newMaxY); - - if (!mGraphView.getViewport().isYAxisBoundsManual()) { - mGraphView.getViewport().setYAxisBoundsStatus(Viewport.AxisBoundsStatus.AUTO_ADJUSTED); - } - - if (mStepsVertical != null) { - mStepsVertical.clear(); - } else { - mStepsVertical = new LinkedHashMap(numVerticalLabels); - } - int height = mGraphView.getGraphContentHeight(); - double v = newMaxY; - int p = mGraphView.getGraphContentTop(); // start - int pixelStep = height / (numVerticalLabels - 1); - for (int i = 0; i < numVerticalLabels; i++) { - mStepsVertical.put(p, v); - p += pixelStep; - v -= exactSteps; - } - - return true; - } - - /** - * calculates the horizontal steps. This will - * automatically change the bounds to nice - * human-readable min/max. - * - * @return true if it is ready - */ - protected boolean adjustHorizontal() { - if (mLabelVerticalWidth == null) { - return false; - } - - double minX = mGraphView.getViewport().getMinX(false); - double maxX = mGraphView.getViewport().getMaxX(false); - if (minX == maxX) return false; - - // TODO find the number of labels - int numHorizontalLabels = mNumHorizontalLabels; - - double newMinX; - double exactSteps; - - float scalingOffset = 0f; - if (mGraphView.getViewport().isXAxisBoundsManual() && mGraphView.getViewport().getXAxisBoundsStatus() != Viewport.AxisBoundsStatus.READJUST_AFTER_SCALE) { - // scaling - if (mGraphView.getViewport().mScalingActive) { - minX = mGraphView.getViewport().mScalingBeginLeft; - maxX = minX + mGraphView.getViewport().mScalingBeginWidth; - - //numHorizontalLabels *= (mGraphView.getViewport().mCurrentViewport.width()+oldStep)/(mGraphView.getViewport().mScalingBeginWidth+oldStep); - //numHorizontalLabels = (float) Math.ceil(numHorizontalLabels); - } - - newMinX = minX; - double rangeX = maxX - newMinX; - exactSteps = rangeX / (numHorizontalLabels - 1); - } else { - // find good steps - boolean adjusting = true; - newMinX = minX; - exactSteps = 0d; - while (adjusting) { - double rangeX = maxX - newMinX; - exactSteps = rangeX / (numHorizontalLabels - 1); - - boolean roundAlwaysUp = true; - if (mGraphView.getViewport().getXAxisBoundsStatus() == Viewport.AxisBoundsStatus.READJUST_AFTER_SCALE) { - // if viewports gets smaller, round down - if (mGraphView.getViewport().mCurrentViewport.width() < mGraphView.getViewport().mScalingBeginWidth) { - roundAlwaysUp = false; - } - } - exactSteps = humanRound(exactSteps, roundAlwaysUp); - - // adjust viewport - // wie oft passt STEP in minX rein? - int count = 0; - if (newMinX >= 0d) { - // positive number - while (newMinX - exactSteps >= 0) { - newMinX -= exactSteps; - count++; - } - newMinX = exactSteps * count; - } else { - // negative number - count++; - while (newMinX + exactSteps < 0) { - newMinX += exactSteps; - count++; - } - newMinX = exactSteps * count * -1; - } - - // wenn minX sich geändert hat, steps nochmal berechnen - // wenn nicht, fertig - if (newMinX == minX) { - adjusting = false; - } else { - minX = newMinX; - } - } - - double newMaxX = newMinX + (numHorizontalLabels - 1) * exactSteps; - mGraphView.getViewport().setMinX(newMinX); - mGraphView.getViewport().setMaxX(newMaxX); - if (mGraphView.getViewport().getXAxisBoundsStatus() == Viewport.AxisBoundsStatus.READJUST_AFTER_SCALE) { - mGraphView.getViewport().setXAxisBoundsStatus(Viewport.AxisBoundsStatus.FIX); - } else { - mGraphView.getViewport().setXAxisBoundsStatus(Viewport.AxisBoundsStatus.AUTO_ADJUSTED); - } - } - - if (mStepsHorizontal != null) { - mStepsHorizontal.clear(); - } else { - mStepsHorizontal = new LinkedHashMap((int) numHorizontalLabels); - } - int width = mGraphView.getGraphContentWidth(); - - float scrolled = 0; - float scrolledPixels = 0; - - double v = newMinX; - int p = mGraphView.getGraphContentLeft(); // start - float pixelStep = width / (numHorizontalLabels - 1); - - if (mGraphView.getViewport().mScalingActive) { - float oldStep = mGraphView.getViewport().mScalingBeginWidth / (numHorizontalLabels - 1); - float factor = (mGraphView.getViewport().mCurrentViewport.width() + oldStep) / (mGraphView.getViewport().mScalingBeginWidth + oldStep); - pixelStep *= 1f / factor; - - //numHorizontalLabels *= (mGraphView.getViewport().mCurrentViewport.width()+oldStep)/(mGraphView.getViewport().mScalingBeginWidth+oldStep); - //numHorizontalLabels = (float) Math.ceil(numHorizontalLabels); - - //scrolled = ((float) mGraphView.getViewport().getMinX(false) - mGraphView.getViewport().mScalingBeginLeft)*2; - float newWidth = width * 1f / factor; - scrolledPixels = (newWidth - width) * -0.5f; - - } - - // scrolling - if (!Float.isNaN(mGraphView.getViewport().mScrollingReferenceX)) { - scrolled = mGraphView.getViewport().mScrollingReferenceX - (float) newMinX; - scrolledPixels += scrolled * (pixelStep / (float) exactSteps); - - if (scrolled < 0 - exactSteps) { - mGraphView.getViewport().mScrollingReferenceX += exactSteps; - } else if (scrolled > exactSteps) { - mGraphView.getViewport().mScrollingReferenceX -= exactSteps; - } - } - p += scrolledPixels; - v += scrolled; - - for (int i = 0; i < numHorizontalLabels; i++) { - // don't draw steps before 0 (scrolling) - if (p >= mGraphView.getGraphContentLeft()) { - mStepsHorizontal.put(p, v); - } - p += pixelStep; - v += exactSteps; - } - - return true; - } - - /** - * adjusts the grid and labels to match to the data - * this will automatically change the bounds to - * nice human-readable values, except the bounds - * are manual. - */ - protected void adjust() { - mIsAdjusted = adjustVertical(); - mIsAdjusted &= adjustVerticalSecondScale(); - mIsAdjusted &= adjustHorizontal(); - } - - /** - * calculates the vertical label size - * @param canvas canvas - */ - protected void calcLabelVerticalSize(Canvas canvas) { - // test label with first and last label - String testLabel = mLabelFormatter.formatLabel(mGraphView.getViewport().getMaxY(false), false); - if (testLabel == null) testLabel = ""; - - Rect textBounds = new Rect(); - mPaintLabel.getTextBounds(testLabel, 0, testLabel.length(), textBounds); - mLabelVerticalWidth = textBounds.width(); - mLabelVerticalHeight = textBounds.height(); - - testLabel = mLabelFormatter.formatLabel(mGraphView.getViewport().getMinY(false), false); - if (testLabel == null) testLabel = ""; - - mPaintLabel.getTextBounds(testLabel, 0, testLabel.length(), textBounds); - mLabelVerticalWidth = Math.max(mLabelVerticalWidth, textBounds.width()); - - // add some pixel to get a margin - mLabelVerticalWidth += 6; - - // space between text and graph content - mLabelVerticalWidth += mStyles.labelsSpace; - - // multiline - int lines = 1; - for (byte c : testLabel.getBytes()) { - if (c == '\n') lines++; - } - mLabelVerticalHeight *= lines; - } - - /** - * calculates the vertical second scale - * label size - * @param canvas canvas - */ - protected void calcLabelVerticalSecondScaleSize(Canvas canvas) { - if (mGraphView.mSecondScale == null) { - mLabelVerticalSecondScaleWidth = 0; - mLabelVerticalSecondScaleHeight = 0; - return; - } - - // test label - double testY = ((mGraphView.mSecondScale.getMaxY() - mGraphView.mSecondScale.getMinY()) * 0.783) + mGraphView.mSecondScale.getMinY(); - String testLabel = mGraphView.mSecondScale.getLabelFormatter().formatLabel(testY, false); - Rect textBounds = new Rect(); - mPaintLabel.getTextBounds(testLabel, 0, testLabel.length(), textBounds); - mLabelVerticalSecondScaleWidth = textBounds.width(); - mLabelVerticalSecondScaleHeight = textBounds.height(); - - // multiline - int lines = 1; - for (byte c : testLabel.getBytes()) { - if (c == '\n') lines++; - } - mLabelVerticalSecondScaleHeight *= lines; - } - - /** - * calculates the horizontal label size - * @param canvas canvas - */ - protected void calcLabelHorizontalSize(Canvas canvas) { - // test label - double testX = ((mGraphView.getViewport().getMaxX(false) - mGraphView.getViewport().getMinX(false)) * 0.783) + mGraphView.getViewport().getMinX(false); - String testLabel = mLabelFormatter.formatLabel(testX, true); - if (testLabel == null) { - testLabel = ""; - } - Rect textBounds = new Rect(); - mPaintLabel.getTextBounds(testLabel, 0, testLabel.length(), textBounds); - mLabelHorizontalWidth = textBounds.width(); - - if (!mLabelHorizontalHeightFixed) { - mLabelHorizontalHeight = textBounds.height(); - - // multiline - int lines = 1; - for (byte c : testLabel.getBytes()) { - if (c == '\n') lines++; - } - mLabelHorizontalHeight *= lines; - - mLabelHorizontalHeight = (int) Math.max(mLabelHorizontalHeight, mStyles.textSize); - } - - // space between text and graph content - mLabelHorizontalHeight += mStyles.labelsSpace; - } - - /** - * do the drawing of the grid - * and labels - * @param canvas canvas - */ - public void draw(Canvas canvas) { - boolean labelSizeChanged = false; - if (mLabelHorizontalWidth == null) { - calcLabelHorizontalSize(canvas); - labelSizeChanged = true; - } - if (mLabelVerticalWidth == null) { - calcLabelVerticalSize(canvas); - labelSizeChanged = true; - } - if (mLabelVerticalSecondScaleWidth == null) { - calcLabelVerticalSecondScaleSize(canvas); - labelSizeChanged = true; - } - if (labelSizeChanged) { - // redraw - ViewCompat.postInvalidateOnAnimation(mGraphView); - return; - } - - if (!mIsAdjusted) { - adjust(); - } - - if (mIsAdjusted) { - drawVerticalSteps(canvas); - drawVerticalStepsSecondScale(canvas); - drawHorizontalSteps(canvas); - } else { - // we can not draw anything - return; - } - - drawHorizontalAxisTitle(canvas); - drawVerticalAxisTitle(canvas); - } - - /** - * draws the horizontal axis title if - * it is set - * @param canvas canvas - */ - protected void drawHorizontalAxisTitle(Canvas canvas) { - if (mHorizontalAxisTitle != null && mHorizontalAxisTitle.length() > 0) { - mPaintAxisTitle.setColor(getHorizontalAxisTitleColor()); - mPaintAxisTitle.setTextSize(getHorizontalAxisTitleTextSize()); - float x = canvas.getWidth() / 2; - float y = canvas.getHeight() - mStyles.padding; - canvas.drawText(mHorizontalAxisTitle, x, y, mPaintAxisTitle); - } - } - - /** - * draws the vertical axis title if - * it is set - * @param canvas canvas - */ - protected void drawVerticalAxisTitle(Canvas canvas) { - if (mVerticalAxisTitle != null && mVerticalAxisTitle.length() > 0) { - mPaintAxisTitle.setColor(getVerticalAxisTitleColor()); - mPaintAxisTitle.setTextSize(getVerticalAxisTitleTextSize()); - float x = getVerticalAxisTitleWidth(); - float y = canvas.getHeight() / 2; - canvas.save(); - canvas.rotate(-90, x, y); - canvas.drawText(mVerticalAxisTitle, x, y, mPaintAxisTitle); - canvas.restore(); - } - } - - /** - * @return the horizontal axis title height - * or 0 if there is no title - */ - public int getHorizontalAxisTitleHeight() { - if (mHorizontalAxisTitle != null && mHorizontalAxisTitle.length() > 0) { - return (int) getHorizontalAxisTitleTextSize(); - } else { - return 0; - } - } - - /** - * @return the vertical axis title width - * or 0 if there is no title - */ - public int getVerticalAxisTitleWidth() { - if (mVerticalAxisTitle != null && mVerticalAxisTitle.length() > 0) { - return (int) getVerticalAxisTitleTextSize(); - } else { - return 0; - } - } - - /** - * draws the horizontal steps - * vertical lines and horizontal labels - * - * @param canvas canvas - */ - protected void drawHorizontalSteps(Canvas canvas) { - // draw horizontal steps (vertical lines and horizontal labels) - mPaintLabel.setColor(getHorizontalLabelsColor()); - int i = 0; - for (Map.Entry e : mStepsHorizontal.entrySet()) { - // draw line - if (mStyles.highlightZeroLines) { - if (e.getValue() == 0d) { - mPaintLine.setStrokeWidth(5); - } else { - mPaintLine.setStrokeWidth(0); - } - } - if (mStyles.gridStyle.drawVertical()) { - canvas.drawLine(e.getKey(), mGraphView.getGraphContentTop(), e.getKey(), mGraphView.getGraphContentTop() + mGraphView.getGraphContentHeight(), mPaintLine); - } - - // draw label - if (isHorizontalLabelsVisible()) { - mPaintLabel.setTextAlign(Paint.Align.CENTER); - if (i == mStepsHorizontal.size() - 1) - mPaintLabel.setTextAlign(Paint.Align.RIGHT); - if (i == 0) - mPaintLabel.setTextAlign(Paint.Align.LEFT); - - // multiline labels - String label = mLabelFormatter.formatLabel(e.getValue(), true); - if (label == null) { - label = ""; - } - String[] lines = label.split("\n"); - for (int li = 0; li < lines.length; li++) { - // for the last line y = height - float y = (canvas.getHeight() - mStyles.padding - getHorizontalAxisTitleHeight()) - (lines.length - li - 1) * getTextSize() * 1.1f + mStyles.labelsSpace; - canvas.drawText(lines[li], e.getKey(), y, mPaintLabel); - } - } - i++; - } - } - - /** - * draws the vertical steps for the - * second scale on the right side - * - * @param canvas canvas - */ - protected void drawVerticalStepsSecondScale(Canvas canvas) { - if (mGraphView.mSecondScale == null) { - return; - } - - // draw only the vertical labels on the right - float startLeft = mGraphView.getGraphContentLeft() + mGraphView.getGraphContentWidth(); - mPaintLabel.setColor(getVerticalLabelsSecondScaleColor()); - mPaintLabel.setTextAlign(getVerticalLabelsSecondScaleAlign()); - for (Map.Entry e : mStepsVerticalSecondScale.entrySet()) { - // draw label - int labelsWidth = mLabelVerticalSecondScaleWidth; - int labelsOffset = (int) startLeft; - if (getVerticalLabelsSecondScaleAlign() == Paint.Align.RIGHT) { - labelsOffset += labelsWidth; - } else if (getVerticalLabelsSecondScaleAlign() == Paint.Align.CENTER) { - labelsOffset += labelsWidth / 2; - } - - float y = e.getKey(); - - String[] lines = mGraphView.mSecondScale.mLabelFormatter.formatLabel(e.getValue(), false).split("\n"); - y += (lines.length * getTextSize() * 1.1f) / 2; // center text vertically - for (int li = 0; li < lines.length; li++) { - // for the last line y = height - float y2 = y - (lines.length - li - 1) * getTextSize() * 1.1f; - canvas.drawText(lines[li], labelsOffset, y2, mPaintLabel); - } - } - } - - /** - * draws the vertical steps - * horizontal lines and vertical labels - * - * @param canvas canvas - */ - protected void drawVerticalSteps(Canvas canvas) { - // draw vertical steps (horizontal lines and vertical labels) - float startLeft = mGraphView.getGraphContentLeft(); - mPaintLabel.setColor(getVerticalLabelsColor()); - mPaintLabel.setTextAlign(getVerticalLabelsAlign()); - for (Map.Entry e : mStepsVertical.entrySet()) { - // draw line - if (mStyles.highlightZeroLines) { - if (e.getValue() == 0d) { - mPaintLine.setStrokeWidth(5); - } else { - mPaintLine.setStrokeWidth(0); - } - } - if (mStyles.gridStyle.drawHorizontal()) { - canvas.drawLine(startLeft, e.getKey(), startLeft + mGraphView.getGraphContentWidth(), e.getKey(), mPaintLine); - } - - // draw label - if (isVerticalLabelsVisible()) { - int labelsWidth = mLabelVerticalWidth; - int labelsOffset = 0; - if (getVerticalLabelsAlign() == Paint.Align.RIGHT) { - labelsOffset = labelsWidth; - labelsOffset -= mStyles.labelsSpace; - } else if (getVerticalLabelsAlign() == Paint.Align.CENTER) { - labelsOffset = labelsWidth / 2; - } - labelsOffset += mStyles.padding + getVerticalAxisTitleWidth(); - - float y = e.getKey(); - - String label = mLabelFormatter.formatLabel(e.getValue(), false); - if (label == null) { - label = ""; - } - String[] lines = label.split("\n"); - y += (lines.length * getTextSize() * 1.1f) / 2; // center text vertically - for (int li = 0; li < lines.length; li++) { - // for the last line y = height - float y2 = y - (lines.length - li - 1) * getTextSize() * 1.1f; - canvas.drawText(lines[li], labelsOffset, y2, mPaintLabel); - } - } - } - } - - /** - * this will do rounding to generate - * nice human-readable bounds. - * - * @param in the raw value that is to be rounded - * @param roundAlwaysUp true if it shall always round up (ceil) - * @return the rounded number - */ - protected double humanRound(double in, boolean roundAlwaysUp) { - // round-up to 1-steps, 2-steps or 5-steps - int ten = 0; - while (in >= 10d) { - in /= 10d; - ten++; - } - while (in < 1d) { - in *= 10d; - ten--; - } - if (roundAlwaysUp) { - if (in == 1d) { - } else if (in <= 2d) { - in = 2d; - } else if (in <= 5d) { - in = 5d; - } else if (in < 10d) { - in = 10d; - } - } else { // always round down - if (in == 1d) { - } else if (in <= 4.9d) { - in = 2d; - } else if (in <= 9.9d) { - in = 5d; - } else if (in < 15d) { - in = 10d; - } - } - return in * Math.pow(10d, ten); - } - - /** - * @return the wrapped styles - */ - public Styles getStyles() { - return mStyles; - } - - /** - * @return the vertical label width - * 0 if there are no vertical labels - */ - public int getLabelVerticalWidth() { - return mLabelVerticalWidth == null || !isVerticalLabelsVisible() ? 0 : mLabelVerticalWidth; - } - - /** - * sets a manual and fixed with of the space for - * the vertical labels. This will prevent GraphView to - * calculate the width automatically. - * - * @param width the width of the space for the vertical labels. - * Use null to let GraphView automatically calculate the width. - */ - public void setLabelVerticalWidth(Integer width) { - mLabelVerticalWidth = width; - mLabelVerticalWidthFixed = mLabelVerticalWidth != null; - } - - /** - * @return the horizontal label height - * 0 if there are no horizontal labels - */ - public int getLabelHorizontalHeight() { - return mLabelHorizontalHeight == null || !isHorizontalLabelsVisible() ? 0 : mLabelHorizontalHeight; - } - - /** - * sets a manual and fixed height of the space for - * the horizontal labels. This will prevent GraphView to - * calculate the height automatically. - * - * @param height the height of the space for the horizontal labels. - * Use null to let GraphView automatically calculate the height. - */ - public void setLabelHorizontalHeight(Integer height) { - mLabelHorizontalHeight = height; - mLabelHorizontalHeightFixed = mLabelHorizontalHeight != null; - } - - /** - * @return the grid line color - */ - public int getGridColor() { - return mStyles.gridColor; - } - - /** - * @return whether the line at 0 are highlighted - */ - public boolean isHighlightZeroLines() { - return mStyles.highlightZeroLines; - } - - /** - * @return the padding around the grid and labels - */ - public int getPadding() { - return mStyles.padding; - } - - /** - * @param textSize the general text size of the axis titles. - * can be overwritten with {@link #setVerticalAxisTitleTextSize(float)} - * and {@link #setHorizontalAxisTitleTextSize(float)} - */ - public void setTextSize(float textSize) { - mStyles.textSize = textSize; - } - - /** - * @param verticalLabelsAlign the alignment of the vertical labels - */ - public void setVerticalLabelsAlign(Paint.Align verticalLabelsAlign) { - mStyles.verticalLabelsAlign = verticalLabelsAlign; - } - - /** - * @param verticalLabelsColor the color of the vertical labels - */ - public void setVerticalLabelsColor(int verticalLabelsColor) { - mStyles.verticalLabelsColor = verticalLabelsColor; - } - - /** - * @param horizontalLabelsColor the color of the horizontal labels - */ - public void setHorizontalLabelsColor(int horizontalLabelsColor) { - mStyles.horizontalLabelsColor = horizontalLabelsColor; - } - - /** - * @param gridColor the color of the grid lines - */ - public void setGridColor(int gridColor) { - mStyles.gridColor = gridColor; - } - - /** - * @param highlightZeroLines flag whether the zero-lines (vertical+ - * horizontal) shall be highlighted - */ - public void setHighlightZeroLines(boolean highlightZeroLines) { - mStyles.highlightZeroLines = highlightZeroLines; - } - - /** - * @param padding the padding around the graph and labels - */ - public void setPadding(int padding) { - mStyles.padding = padding; - } - - /** - * @return the label formatter, that converts - * the raw numbers to strings - */ - public LabelFormatter getLabelFormatter() { - return mLabelFormatter; - } - - /** - * @param mLabelFormatter the label formatter, that converts - * the raw numbers to strings - */ - public void setLabelFormatter(LabelFormatter mLabelFormatter) { - this.mLabelFormatter = mLabelFormatter; - mLabelFormatter.setViewport(mGraphView.getViewport()); - } - - /** - * @return the title of the horizontal axis - */ - public String getHorizontalAxisTitle() { - return mHorizontalAxisTitle; - } - - /** - * @param mHorizontalAxisTitle the title of the horizontal axis - */ - public void setHorizontalAxisTitle(String mHorizontalAxisTitle) { - this.mHorizontalAxisTitle = mHorizontalAxisTitle; - } - - /** - * @return the title of the vertical axis - */ - public String getVerticalAxisTitle() { - return mVerticalAxisTitle; - } - - /** - * @param mVerticalAxisTitle the title of the vertical axis - */ - public void setVerticalAxisTitle(String mVerticalAxisTitle) { - this.mVerticalAxisTitle = mVerticalAxisTitle; - } - - /** - * @return font size of the vertical axis title - */ - public float getVerticalAxisTitleTextSize() { - return mStyles.verticalAxisTitleTextSize; - } - - /** - * @param verticalAxisTitleTextSize font size of the vertical axis title - */ - public void setVerticalAxisTitleTextSize(float verticalAxisTitleTextSize) { - mStyles.verticalAxisTitleTextSize = verticalAxisTitleTextSize; - } - - /** - * @return font color of the vertical axis title - */ - public int getVerticalAxisTitleColor() { - return mStyles.verticalAxisTitleColor; - } - - /** - * @param verticalAxisTitleColor font color of the vertical axis title - */ - public void setVerticalAxisTitleColor(int verticalAxisTitleColor) { - mStyles.verticalAxisTitleColor = verticalAxisTitleColor; - } - - /** - * @return font size of the horizontal axis title - */ - public float getHorizontalAxisTitleTextSize() { - return mStyles.horizontalAxisTitleTextSize; - } - - /** - * @param horizontalAxisTitleTextSize font size of the horizontal axis title - */ - public void setHorizontalAxisTitleTextSize(float horizontalAxisTitleTextSize) { - mStyles.horizontalAxisTitleTextSize = horizontalAxisTitleTextSize; - } - - /** - * @return font color of the horizontal axis title - */ - public int getHorizontalAxisTitleColor() { - return mStyles.horizontalAxisTitleColor; - } - - /** - * @param horizontalAxisTitleColor font color of the horizontal axis title - */ - public void setHorizontalAxisTitleColor(int horizontalAxisTitleColor) { - mStyles.horizontalAxisTitleColor = horizontalAxisTitleColor; - } - - /** - * @return the alignment of the labels on the right side - */ - public Paint.Align getVerticalLabelsSecondScaleAlign() { - return mStyles.verticalLabelsSecondScaleAlign; - } - - /** - * @param verticalLabelsSecondScaleAlign the alignment of the labels on the right side - */ - public void setVerticalLabelsSecondScaleAlign(Paint.Align verticalLabelsSecondScaleAlign) { - mStyles.verticalLabelsSecondScaleAlign = verticalLabelsSecondScaleAlign; - } - - /** - * @return the color of the labels on the right side - */ - public int getVerticalLabelsSecondScaleColor() { - return mStyles.verticalLabelsSecondScaleColor; - } - - /** - * @param verticalLabelsSecondScaleColor the color of the labels on the right side - */ - public void setVerticalLabelsSecondScaleColor(int verticalLabelsSecondScaleColor) { - mStyles.verticalLabelsSecondScaleColor = verticalLabelsSecondScaleColor; - } - - /** - * @return the width of the vertical labels - * of the second scale - */ - public int getLabelVerticalSecondScaleWidth() { - return mLabelVerticalSecondScaleWidth==null?0:mLabelVerticalSecondScaleWidth; - } - - /** - * @return flag whether the horizontal labels are - * visible - */ - public boolean isHorizontalLabelsVisible() { - return mStyles.horizontalLabelsVisible; - } - - /** - * @param horizontalTitleVisible flag whether the horizontal labels are - * visible - */ - public void setHorizontalLabelsVisible(boolean horizontalTitleVisible) { - mStyles.horizontalLabelsVisible = horizontalTitleVisible; - } - - /** - * @return flag whether the vertical labels are - * visible - */ - public boolean isVerticalLabelsVisible() { - return mStyles.verticalLabelsVisible; - } - - /** - * @param verticalTitleVisible flag whether the vertical labels are - * visible - */ - public void setVerticalLabelsVisible(boolean verticalTitleVisible) { - mStyles.verticalLabelsVisible = verticalTitleVisible; - } - - /** - * @return count of the vertical labels, that - * will be shown at one time. - */ - public int getNumVerticalLabels() { - return mNumVerticalLabels; - } - - /** - * @param mNumVerticalLabels count of the vertical labels, that - * will be shown at one time. - */ - public void setNumVerticalLabels(int mNumVerticalLabels) { - this.mNumVerticalLabels = mNumVerticalLabels; - } - - /** - * @return count of the horizontal labels, that - * will be shown at one time. - */ - public int getNumHorizontalLabels() { - return mNumHorizontalLabels; - } - - /** - * @param mNumHorizontalLabels count of the horizontal labels, that - * will be shown at one time. - */ - public void setNumHorizontalLabels(int mNumHorizontalLabels) { - this.mNumHorizontalLabels = mNumHorizontalLabels; - } - - /** - * @return the grid style - */ - public GridStyle getGridStyle() { - return mStyles.gridStyle; - } - - /** - * Define which grid lines shall be drawn - * - * @param gridStyle the grid style - */ - public void setGridStyle(GridStyle gridStyle) { - mStyles.gridStyle = gridStyle; - } - - /** - * @return the space between the labels text and the graph content - */ - public int getLabelsSpace() { - return mStyles.labelsSpace; - } - - /** - * the space between the labels text and the graph content - * - * @param labelsSpace the space between the labels text and the graph content - */ - public void setLabelsSpace(int labelsSpace) { - mStyles.labelsSpace = labelsSpace; - } -} diff --git a/graphview/src/main/java/com/jjoe64/graphview/LabelFormatter.java b/graphview/src/main/java/com/jjoe64/graphview/LabelFormatter.java deleted file mode 100644 index 80aad2990a..0000000000 --- a/graphview/src/main/java/com/jjoe64/graphview/LabelFormatter.java +++ /dev/null @@ -1,54 +0,0 @@ -/** - * GraphView - * Copyright (C) 2014 Jonas Gehring - * - * 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, - * with the "Linking Exception", which can be found at the license.txt - * file in this program. - * - * 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 - * with the "Linking Exception" along with this program; if not, - * write to the author Jonas Gehring . - */ -package com.jjoe64.graphview; - -/** - * Interface to use as label formatter. - * Implement this in order to generate - * your own labels format. - * It is recommended to override {@link com.jjoe64.graphview.DefaultLabelFormatter}. - * - * @author jjoe64 - */ -public interface LabelFormatter { - /** - * converts a raw number as input to - * a formatted string for the label. - * - * @param value raw input number - * @param isValueX true if it is a value for the x axis - * false if it is a value for the y axis - * @return the formatted number as string - */ - public String formatLabel(double value, boolean isValueX); - - /** - * will be called in order to have a - * reference to the current viewport. - * This is useful if you need the bounds - * to generate your labels. - * You store this viewport in as member variable - * and access it e.g. in the {@link #formatLabel(double, boolean)} - * method. - * - * @param viewport the used viewport - */ - public void setViewport(Viewport viewport); -} diff --git a/graphview/src/main/java/com/jjoe64/graphview/LegendRenderer.java b/graphview/src/main/java/com/jjoe64/graphview/LegendRenderer.java deleted file mode 100644 index db901577f7..0000000000 --- a/graphview/src/main/java/com/jjoe64/graphview/LegendRenderer.java +++ /dev/null @@ -1,394 +0,0 @@ -/** - * GraphView - * Copyright (C) 2014 Jonas Gehring - * - * 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, - * with the "Linking Exception", which can be found at the license.txt - * file in this program. - * - * 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 - * with the "Linking Exception" along with this program; if not, - * write to the author Jonas Gehring . - */ -package com.jjoe64.graphview; - -import android.content.res.TypedArray; -import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.Paint; -import android.graphics.Point; -import android.graphics.Rect; -import android.graphics.RectF; -import android.util.TypedValue; - -import com.jjoe64.graphview.series.Series; - -import java.util.ArrayList; -import java.util.List; - -/** - * The default renderer for the legend box - * - * @author jjoe64 - */ -public class LegendRenderer { - /** - * wrapped styles regarding to the - * legend - */ - private final class Styles { - float textSize; - int spacing; - int padding; - int width; - int backgroundColor; - int textColor; - int margin; - LegendAlign align; - Point fixedPosition; - } - - /** - * alignment of the legend - */ - public enum LegendAlign { - /** - * top right corner - */ - TOP, - - /** - * middle right - */ - MIDDLE, - - /** - * bottom right corner - */ - BOTTOM - } - - /** - * wrapped styles - */ - private Styles mStyles; - - /** - * reference to the graphview - */ - private final GraphView mGraphView; - - /** - * flag whether legend will be - * drawn - */ - private boolean mIsVisible; - - /** - * paint for the drawing - */ - private Paint mPaint; - - /** - * cached legend width - * this will be filled in the drawing. - * Can be cleared via {@link #resetStyles()} - */ - private int cachedLegendWidth; - - /** - * creates legend renderer - * - * @param graphView regarding graphview - */ - public LegendRenderer(GraphView graphView) { - mGraphView = graphView; - mIsVisible = false; - mPaint = new Paint(); - mPaint.setTextAlign(Paint.Align.LEFT); - mStyles = new Styles(); - cachedLegendWidth = 0; - resetStyles(); - } - - /** - * resets the styles to the defaults - * and clears the legend width cache - */ - public void resetStyles() { - mStyles.align = LegendAlign.MIDDLE; - mStyles.textSize = mGraphView.getGridLabelRenderer().getTextSize(); - mStyles.spacing = (int) (mStyles.textSize / 5); - mStyles.padding = (int) (mStyles.textSize / 2); - mStyles.width = 0; - mStyles.backgroundColor = Color.argb(180, 100, 100, 100); - mStyles.margin = (int) (mStyles.textSize / 5); - - // get matching styles from theme - TypedValue typedValue = new TypedValue(); - mGraphView.getContext().getTheme().resolveAttribute(android.R.attr.textAppearanceSmall, typedValue, true); - - int color1; - - try { - TypedArray array = mGraphView.getContext().obtainStyledAttributes(typedValue.data, new int[]{ - android.R.attr.textColorPrimary}); - color1 = array.getColor(0, Color.BLACK); - array.recycle(); - } catch (Exception e) { - color1 = Color.BLACK; - } - - mStyles.textColor = color1; - - cachedLegendWidth = 0; - } - - /** - * draws the legend if it is visible - * - * @param canvas canvas - * @see #setVisible(boolean) - */ - public void draw(Canvas canvas) { - if (!mIsVisible) return; - - mPaint.setTextSize(mStyles.textSize); - - int shapeSize = (int) (mStyles.textSize*0.8d); - - List allSeries = new ArrayList(); - allSeries.addAll(mGraphView.getSeries()); - if (mGraphView.mSecondScale != null) { - allSeries.addAll(mGraphView.getSecondScale().getSeries()); - } - - // width - int legendWidth = mStyles.width; - if (legendWidth == 0) { - // auto - legendWidth = cachedLegendWidth; - - if (legendWidth == 0) { - Rect textBounds = new Rect(); - for (Series s : allSeries) { - if (s.getTitle() != null) { - mPaint.getTextBounds(s.getTitle(), 0, s.getTitle().length(), textBounds); - legendWidth = Math.max(legendWidth, textBounds.width()); - } - } - if (legendWidth == 0) legendWidth = 1; - - // add shape size - legendWidth += shapeSize+mStyles.padding*2 + mStyles.spacing; - cachedLegendWidth = legendWidth; - } - } - - // rect - float legendHeight = (mStyles.textSize+mStyles.spacing)*allSeries.size() -mStyles.spacing; - float lLeft; - float lTop; - if (mStyles.fixedPosition != null) { - // use fied position - lLeft = mGraphView.getGraphContentLeft() + mStyles.margin + mStyles.fixedPosition.x; - lTop = mGraphView.getGraphContentTop() + mStyles.margin + mStyles.fixedPosition.y; - } else { - lLeft = mGraphView.getGraphContentLeft() + mGraphView.getGraphContentWidth() - legendWidth - mStyles.margin; - switch (mStyles.align) { - case TOP: - lTop = mGraphView.getGraphContentTop() + mStyles.margin; - break; - case MIDDLE: - lTop = mGraphView.getHeight() / 2 - legendHeight / 2; - break; - default: - lTop = mGraphView.getGraphContentTop() + mGraphView.getGraphContentHeight() - mStyles.margin - legendHeight - 2*mStyles.padding; - } - } - float lRight = lLeft+legendWidth; - float lBottom = lTop+legendHeight+2*mStyles.padding; - mPaint.setColor(mStyles.backgroundColor); - canvas.drawRoundRect(new RectF(lLeft, lTop, lRight, lBottom), 8, 8, mPaint); - - int i=0; - for (Series series : allSeries) { - mPaint.setColor(series.getColor()); - canvas.drawRect(new RectF(lLeft+mStyles.padding, lTop+mStyles.padding+(i*(mStyles.textSize+mStyles.spacing)), lLeft+mStyles.padding+shapeSize, lTop+mStyles.padding+(i*(mStyles.textSize+mStyles.spacing))+shapeSize), mPaint); - if (series.getTitle() != null) { - mPaint.setColor(mStyles.textColor); - canvas.drawText(series.getTitle(), lLeft+mStyles.padding+shapeSize+mStyles.spacing, lTop+mStyles.padding+mStyles.textSize+(i*(mStyles.textSize+mStyles.spacing)), mPaint); - } - i++; - } - } - - /** - * @return the flag whether the legend will be drawn - */ - public boolean isVisible() { - return mIsVisible; - } - - /** - * set the flag whether the legend will be drawn - * - * @param mIsVisible visible flag - */ - public void setVisible(boolean mIsVisible) { - this.mIsVisible = mIsVisible; - } - - /** - * @return font size - */ - public float getTextSize() { - return mStyles.textSize; - } - - /** - * sets the font size. this will clear - * the internal legend width cache - * - * @param textSize font size - */ - public void setTextSize(float textSize) { - mStyles.textSize = textSize; - cachedLegendWidth = 0; - } - - /** - * @return the spacing between the text lines - */ - public int getSpacing() { - return mStyles.spacing; - } - - /** - * set the spacing between the text lines - * - * @param spacing the spacing between the text lines - */ - public void setSpacing(int spacing) { - mStyles.spacing = spacing; - } - - /** - * padding is the space between the edge of the box - * and the beginning of the text - * - * @return padding from edge to text - */ - public int getPadding() { - return mStyles.padding; - } - - /** - * padding is the space between the edge of the box - * and the beginning of the text - * - * @param padding padding from edge to text - */ - public void setPadding(int padding) { - mStyles.padding = padding; - } - - /** - * the width of the box exclusive padding - * - * @return the width of the box - * 0 => auto - */ - public int getWidth() { - return mStyles.width; - } - - /** - * the width of the box exclusive padding - * @param width the width of the box exclusive padding - * 0 => auto - */ - public void setWidth(int width) { - mStyles.width = width; - } - - /** - * @return background color of the box - * it is recommended to use semi-transparent - * color. - */ - public int getBackgroundColor() { - return mStyles.backgroundColor; - } - - /** - * @param backgroundColor background color of the box - * it is recommended to use semi-transparent - * color. - */ - public void setBackgroundColor(int backgroundColor) { - mStyles.backgroundColor = backgroundColor; - } - - /** - * @return margin from the edge of the box - * to the corner of the graphview - */ - public int getMargin() { - return mStyles.margin; - } - - /** - * @param margin margin from the edge of the box - * to the corner of the graphview - */ - public void setMargin(int margin) { - mStyles.margin = margin; - } - - /** - * @return the vertical alignment of the box - */ - public LegendAlign getAlign() { - return mStyles.align; - } - - /** - * @param align the vertical alignment of the box - */ - public void setAlign(LegendAlign align) { - mStyles.align = align; - } - - /** - * @return font color - */ - public int getTextColor() { - return mStyles.textColor; - } - - /** - * @param textColor font color - */ - public void setTextColor(int textColor) { - mStyles.textColor = textColor; - } - - /** - * Use fixed coordinates to position the legend. - * This will override the align setting. - * - * @param x x coordinates in pixel - * @param y y coordinates in pixel - */ - public void setFixedPosition(int x, int y) { - mStyles.fixedPosition = new Point(x, y); - } -} diff --git a/graphview/src/main/java/com/jjoe64/graphview/SecondScale.java b/graphview/src/main/java/com/jjoe64/graphview/SecondScale.java deleted file mode 100644 index 1fff8ee8a9..0000000000 --- a/graphview/src/main/java/com/jjoe64/graphview/SecondScale.java +++ /dev/null @@ -1,165 +0,0 @@ -/** - * GraphView - * Copyright (C) 2014 Jonas Gehring - * - * 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, - * with the "Linking Exception", which can be found at the license.txt - * file in this program. - * - * 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 - * with the "Linking Exception" along with this program; if not, - * write to the author Jonas Gehring . - */ -package com.jjoe64.graphview; - -import com.jjoe64.graphview.series.Series; - -import java.util.ArrayList; -import java.util.List; - -/** - * To be used to plot a second scale - * on the graph. - * The second scale has always to have - * manual bounds. - * Use {@link #setMinY(double)} and {@link #setMaxY(double)} - * to set them. - * The second scale has it's own array of series. - * - * @author jjoe64 - */ -public class SecondScale { - /** - * reference to the viewport of the graph - */ - protected final Viewport mViewport; - - /** - * array of series for the second - * scale - */ - protected List mSeries; - - /** - * flag whether the y axis bounds - * are manual. - * For the current version this is always - * true. - */ - private boolean mYAxisBoundsManual = true; - - /** - * min y value for the y axis bounds - */ - private double mMinY; - - /** - * max y value for the y axis bounds - */ - private double mMaxY; - - /** - * label formatter for the y labels - * on the right side - */ - protected LabelFormatter mLabelFormatter; - - /** - * creates the second scale. - * normally you do not call this contructor. - * Use {@link com.jjoe64.graphview.GraphView#getSecondScale()} - * in order to get the instance. - */ - SecondScale(Viewport viewport) { - mViewport = viewport; - mSeries = new ArrayList(); - mLabelFormatter = new DefaultLabelFormatter(); - mLabelFormatter.setViewport(mViewport); - } - - /** - * add a series to the second scale. - * Don't add this series also to the GraphView - * object. - * - * @param s the series - */ - public void addSeries(Series s) { - mSeries.add(s); - } - - //public void setYAxisBoundsManual(boolean mYAxisBoundsManual) { - // this.mYAxisBoundsManual = mYAxisBoundsManual; - //} - - /** - * set the min y bounds - * - * @param d min y value - */ - public void setMinY(double d) { - mMinY = d; - } - - /** - * set the max y bounds - * - * @param d max y value - */ - public void setMaxY(double d) { - mMaxY = d; - } - - /** - * @return the series of the second scale - */ - public List getSeries() { - return mSeries; - } - - /** - * @return min y bound - */ - public double getMinY() { - return mMinY; - } - - /** - * @return max y bound - */ - public double getMaxY() { - return mMaxY; - } - - /** - * @return always true for the current implementation - */ - public boolean isYAxisBoundsManual() { - return mYAxisBoundsManual; - } - - /** - * @return label formatter for the y labels on the right side - */ - public LabelFormatter getLabelFormatter() { - return mLabelFormatter; - } - - /** - * Set a custom label formatter that is used - * for the y labels on the right side. - * - * @param formatter label formatter for the y labels - */ - public void setLabelFormatter(LabelFormatter formatter) { - mLabelFormatter = formatter; - mLabelFormatter.setViewport(mViewport); - } -} diff --git a/graphview/src/main/java/com/jjoe64/graphview/ValueDependentColor.java b/graphview/src/main/java/com/jjoe64/graphview/ValueDependentColor.java deleted file mode 100644 index a02f22a21e..0000000000 --- a/graphview/src/main/java/com/jjoe64/graphview/ValueDependentColor.java +++ /dev/null @@ -1,41 +0,0 @@ -/** - * GraphView - * Copyright (C) 2014 Jonas Gehring - * - * 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, - * with the "Linking Exception", which can be found at the license.txt - * file in this program. - * - * 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 - * with the "Linking Exception" along with this program; if not, - * write to the author Jonas Gehring . - */ - -package com.jjoe64.graphview; - -import com.jjoe64.graphview.series.DataPointInterface; - -/** - * you can change the color depending on the value. - * takes only effect for BarGraphSeries. - * - * @see com.jjoe64.graphview.series.BarGraphSeries#setValueDependentColor(ValueDependentColor) - */ -public interface ValueDependentColor { - /** - * this is called when a bar is about to draw - * and the color is be loaded. - * - * @param data the current input value - * @return the color that the bar should be drawn with - * Generate the int via the android.graphics.Color class. - */ - public int get(T data); -} diff --git a/graphview/src/main/java/com/jjoe64/graphview/Viewport.java b/graphview/src/main/java/com/jjoe64/graphview/Viewport.java deleted file mode 100644 index de90c05ea2..0000000000 --- a/graphview/src/main/java/com/jjoe64/graphview/Viewport.java +++ /dev/null @@ -1,996 +0,0 @@ -/** - * GraphView - * Copyright (C) 2014 Jonas Gehring - * - * 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, - * with the "Linking Exception", which can be found at the license.txt - * file in this program. - * - * 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 - * with the "Linking Exception" along with this program; if not, - * write to the author Jonas Gehring . - */ -package com.jjoe64.graphview; - -import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.Paint; -import android.graphics.RectF; -import android.util.Log; -import android.view.GestureDetector; -import android.view.MotionEvent; -import android.view.ScaleGestureDetector; -import android.widget.OverScroller; - -import androidx.core.view.ViewCompat; -import androidx.core.widget.EdgeEffectCompat; - -import com.jjoe64.graphview.compat.OverScrollerCompat; -import com.jjoe64.graphview.series.DataPointInterface; -import com.jjoe64.graphview.series.Series; - -import java.util.Iterator; -import java.util.List; - -/** - * This is the default implementation for the viewport. - * This implementation so for a normal viewport - * where there is a horizontal x-axis and a - * vertical y-axis. - * This viewport is compatible with - * - {@link com.jjoe64.graphview.series.BarGraphSeries} - * - {@link com.jjoe64.graphview.series.LineGraphSeries} - * - {@link com.jjoe64.graphview.series.PointsGraphSeries} - * - * @author jjoe64 - */ -public class Viewport { - /** - * listener for the scale gesture - */ - private final ScaleGestureDetector.OnScaleGestureListener mScaleGestureListener - = new ScaleGestureDetector.OnScaleGestureListener() { - /** - * called by android - * @param detector detector - * @return always true - */ - @Override - public boolean onScale(ScaleGestureDetector detector) { - float viewportWidth = mCurrentViewport.width(); - float center = mCurrentViewport.left + viewportWidth / 2; - viewportWidth /= detector.getScaleFactor(); - mCurrentViewport.left = center - viewportWidth / 2; - mCurrentViewport.right = mCurrentViewport.left+viewportWidth; - - // viewportStart must not be < minX - float minX = (float) getMinX(true); - if (mCurrentViewport.left < minX) { - mCurrentViewport.left = minX; - mCurrentViewport.right = mCurrentViewport.left+viewportWidth; - } - - // viewportStart + viewportSize must not be > maxX - float maxX = (float) getMaxX(true); - if (viewportWidth == 0) { - mCurrentViewport.right = maxX; - } - double overlap = mCurrentViewport.left + viewportWidth - maxX; - if (overlap > 0) { - // scroll left - if (mCurrentViewport.left-overlap > minX) { - mCurrentViewport.left -= overlap; - mCurrentViewport.right = mCurrentViewport.left+viewportWidth; - } else { - // maximal scale - mCurrentViewport.left = minX; - mCurrentViewport.right = maxX; - } - } - - // adjust viewport, labels, etc. - mGraphView.onDataChanged(true, false); - - ViewCompat.postInvalidateOnAnimation(mGraphView); - - return true; - } - - /** - * called when scaling begins - * - * @param detector detector - * @return true if it is scalable - */ - @Override - public boolean onScaleBegin(ScaleGestureDetector detector) { - if (mIsScalable) { - mScalingBeginWidth = mCurrentViewport.width(); - mScalingBeginLeft = mCurrentViewport.left; - mScalingActive = true; - return true; - } else { - return false; - } - } - - /** - * called when sacling ends - * This will re-adjust the viewport. - * - * @param detector detector - */ - @Override - public void onScaleEnd(ScaleGestureDetector detector) { - mScalingActive = false; - - // re-adjust - mXAxisBoundsStatus = AxisBoundsStatus.READJUST_AFTER_SCALE; - - mScrollingReferenceX = Float.NaN; - - // adjust viewport, labels, etc. - mGraphView.onDataChanged(true, false); - - ViewCompat.postInvalidateOnAnimation(mGraphView); - } - }; - - /** - * simple gesture listener to track scroll events - */ - private final GestureDetector.SimpleOnGestureListener mGestureListener - = new GestureDetector.SimpleOnGestureListener() { - @Override - public boolean onDown(MotionEvent e) { - if (!mIsScrollable || mScalingActive) return false; - - // Initiates the decay phase of any active edge effects. - releaseEdgeEffects(); - mScrollerStartViewport.set(mCurrentViewport); - // Aborts any active scroll animations and invalidates. - mScroller.forceFinished(true); - ViewCompat.postInvalidateOnAnimation(mGraphView); - return true; - } - - @Override - public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { - if (!mIsScrollable || mScalingActive) return false; - - if (Float.isNaN(mScrollingReferenceX)) { - mScrollingReferenceX = mCurrentViewport.left; - } - - // Scrolling uses math based on the viewport (as opposed to math using pixels). - /** - * Pixel offset is the offset in screen pixels, while viewport offset is the - * offset within the current viewport. For additional information on surface sizes - * and pixel offsets, see the docs for {@link computeScrollSurfaceSize()}. For - * additional information about the viewport, see the comments for - * {@link mCurrentViewport}. - */ - float viewportOffsetX = distanceX * mCurrentViewport.width() / mGraphView.getGraphContentWidth(); - float viewportOffsetY = -distanceY * mCurrentViewport.height() / mGraphView.getGraphContentHeight(); - - int completeWidth = (int)((mCompleteRange.width()/mCurrentViewport.width()) * (float) mGraphView.getGraphContentWidth()); - int completeHeight = (int)((mCompleteRange.height()/mCurrentViewport.height()) * (float) mGraphView.getGraphContentHeight()); - - int scrolledX = (int) (completeWidth - * (mCurrentViewport.left + viewportOffsetX - mCompleteRange.left) - / mCompleteRange.width()); - int scrolledY = (int) (completeHeight - * (mCompleteRange.bottom - mCurrentViewport.bottom - viewportOffsetY) - / mCompleteRange.height()); - boolean canScrollX = mCurrentViewport.left > mCompleteRange.left - || mCurrentViewport.right < mCompleteRange.right; - boolean canScrollY = mCurrentViewport.bottom > mCompleteRange.bottom - || mCurrentViewport.top < mCompleteRange.top; - - if (canScrollX) { - if (viewportOffsetX < 0) { - float tooMuch = mCurrentViewport.left+viewportOffsetX - mCompleteRange.left; - if (tooMuch < 0) { - viewportOffsetX -= tooMuch; - } - } else { - float tooMuch = mCurrentViewport.right+viewportOffsetX - mCompleteRange.right; - if (tooMuch > 0) { - viewportOffsetX -= tooMuch; - } - } - mCurrentViewport.left += viewportOffsetX; - mCurrentViewport.right += viewportOffsetX; - } - if (canScrollY) { - //mCurrentViewport.top += viewportOffsetX; - //mCurrentViewport.bottom -= viewportOffsetX; - } - - if (canScrollX && scrolledX < 0) { - mEdgeEffectLeft.onPull(scrolledX / (float) mGraphView.getGraphContentWidth()); - mEdgeEffectLeftActive = true; - } - if (canScrollY && scrolledY < 0) { - mEdgeEffectBottom.onPull(scrolledY / (float) mGraphView.getGraphContentHeight()); - mEdgeEffectBottomActive = true; - } - if (canScrollX && scrolledX > completeWidth - mGraphView.getGraphContentWidth()) { - mEdgeEffectRight.onPull((scrolledX - completeWidth + mGraphView.getGraphContentWidth()) - / (float) mGraphView.getGraphContentWidth()); - mEdgeEffectRightActive = true; - } - //if (canScrollY && scrolledY > mSurfaceSizeBuffer.y - mContentRect.height()) { - // mEdgeEffectTop.onPull((scrolledY - mSurfaceSizeBuffer.y + mContentRect.height()) - // / (float) mContentRect.height()); - // mEdgeEffectTopActive = true; - //} - - // adjust viewport, labels, etc. - mGraphView.onDataChanged(true, false); - - ViewCompat.postInvalidateOnAnimation(mGraphView); - return true; - } - - @Override - public boolean onFling(MotionEvent e1, MotionEvent e2, - float velocityX, float velocityY) { - //fling((int) -velocityX, (int) -velocityY); - return true; - } - }; - - /** - * the state of the axis bounds - */ - public enum AxisBoundsStatus { - /** - * initial means that the bounds gets - * auto adjusted if they are not manual. - * After adjusting the status comes to - * #AUTO_ADJUSTED. - */ - INITIAL, - - /** - * after the bounds got auto-adjusted, - * this status will set. - */ - AUTO_ADJUSTED, - - /** - * this flags the status that a scale was - * done and the bounds has to be auto-adjusted - * afterwards. - */ - READJUST_AFTER_SCALE, - - /** - * means that the bounds are fix (manually) and - * are not to be auto-adjusted. - */ - FIX - } - - /** - * paint to draw background - */ - private Paint mPaint; - - /** - * reference to the graphview - */ - private final GraphView mGraphView; - - /** - * this holds the current visible viewport - * left = minX, right = maxX - * bottom = minY, top = maxY - */ - protected RectF mCurrentViewport = new RectF(); - - /** - * this holds the whole range of the data - * left = minX, right = maxX - * bottom = minY, top = maxY - */ - protected RectF mCompleteRange = new RectF(); - - /** - * flag whether scaling is currently active - */ - protected boolean mScalingActive; - - /** - * stores the width of the viewport at the time - * of beginning of the scaling. - */ - protected float mScalingBeginWidth; - - /** - * stores the viewport left at the time of - * beginning of the scaling. - */ - protected float mScalingBeginLeft; - - /** - * flag whether the viewport is scrollable - */ - private boolean mIsScrollable; - - /** - * flag whether the viewport is scalable - */ - private boolean mIsScalable; - - /** - * gesture detector to detect scrolling - */ - protected GestureDetector mGestureDetector; - - /** - * detect scaling - */ - protected ScaleGestureDetector mScaleGestureDetector; - - /** - * not used - for fling - */ - protected OverScroller mScroller; - - /** - * not used - */ - private EdgeEffectCompat mEdgeEffectTop; - - /** - * not used - */ - private EdgeEffectCompat mEdgeEffectBottom; - - /** - * glow effect when scrolling left - */ - private EdgeEffectCompat mEdgeEffectLeft; - - /** - * glow effect when scrolling right - */ - private EdgeEffectCompat mEdgeEffectRight; - - /** - * not used - */ - private boolean mEdgeEffectTopActive; - - /** - * not used - */ - private boolean mEdgeEffectBottomActive; - - /** - * glow effect when scrolling left - */ - private boolean mEdgeEffectLeftActive; - - /** - * glow effect when scrolling right - */ - private boolean mEdgeEffectRightActive; - - /** - * stores the viewport at the time of - * the beginning of scaling - */ - private RectF mScrollerStartViewport = new RectF(); - - /** - * stores the viewport left value at the - * time of beginning of the scrolling - */ - protected float mScrollingReferenceX = Float.NaN; - - /** - * state of the x axis - */ - private AxisBoundsStatus mXAxisBoundsStatus; - - /** - * state of the y axis - */ - private AxisBoundsStatus mYAxisBoundsStatus; - - /** - * flag whether the x axis bounds are manual - */ - private boolean mXAxisBoundsManual; - - /** - * flag whether the y axis bounds are manual - */ - private boolean mYAxisBoundsManual; - - /** - * background color of the viewport area - * it is recommended to use a semi-transparent color - */ - private int mBackgroundColor; - - /** - * creates the viewport - * - * @param graphView graphview - */ - Viewport(GraphView graphView) { - mScroller = new OverScroller(graphView.getContext()); - mEdgeEffectTop = new EdgeEffectCompat(graphView.getContext()); - mEdgeEffectBottom = new EdgeEffectCompat(graphView.getContext()); - mEdgeEffectLeft = new EdgeEffectCompat(graphView.getContext()); - mEdgeEffectRight = new EdgeEffectCompat(graphView.getContext()); - mGestureDetector = new GestureDetector(graphView.getContext(), mGestureListener); - mScaleGestureDetector = new ScaleGestureDetector(graphView.getContext(), mScaleGestureListener); - - mGraphView = graphView; - mXAxisBoundsStatus = AxisBoundsStatus.INITIAL; - mYAxisBoundsStatus = AxisBoundsStatus.INITIAL; - mBackgroundColor = Color.TRANSPARENT; - mPaint = new Paint(); - } - - /** - * will be called on a touch event. - * needed to use scaling and scrolling - * - * @param event - * @return true if it was consumed - */ - public boolean onTouchEvent(MotionEvent event) { - boolean b = mScaleGestureDetector.onTouchEvent(event); - b |= mGestureDetector.onTouchEvent(event); - return b; - } - - /** - * change the state of the x axis. - * normally you do not call this method. - * If you want to set manual axis use - * {@link #setXAxisBoundsManual(boolean)} and {@link #setYAxisBoundsManual(boolean)} - * - * @param s state - */ - public void setXAxisBoundsStatus(AxisBoundsStatus s) { - mXAxisBoundsStatus = s; - } - - /** - * change the state of the y axis. - * normally you do not call this method. - * If you want to set manual axis use - * {@link #setXAxisBoundsManual(boolean)} and {@link #setYAxisBoundsManual(boolean)} - * - * @param s state - */ - public void setYAxisBoundsStatus(AxisBoundsStatus s) { - mYAxisBoundsStatus = s; - } - - /** - * @return whether the viewport is scrollable - */ - public boolean isScrollable() { - return mIsScrollable; - } - - /** - * @param mIsScrollable whether is viewport is scrollable - */ - public void setScrollable(boolean mIsScrollable) { - this.mIsScrollable = mIsScrollable; - } - - /** - * @return the x axis state - */ - public AxisBoundsStatus getXAxisBoundsStatus() { - return mXAxisBoundsStatus; - } - - /** - * @return the y axis state - */ - public AxisBoundsStatus getYAxisBoundsStatus() { - return mYAxisBoundsStatus; - } - - /** - * caches the complete range (minX, maxX, minY, maxY) - * by iterating all series and all datapoints and - * stores it into #mCompleteRange - */ - public void calcCompleteRange() { - List series = mGraphView.getSeries(); - mCompleteRange.set(0, 0, 0, 0); - if (!series.isEmpty() && !series.get(0).isEmpty()) { - double d = series.get(0).getLowestValueX(); - for (Series s : series) { - if (!s.isEmpty() && d > s.getLowestValueX()) { - d = s.getLowestValueX(); - } - } - mCompleteRange.left = (float) d; - - d = series.get(0).getHighestValueX(); - for (Series s : series) { - if (!s.isEmpty() && d < s.getHighestValueX()) { - d = s.getHighestValueX(); - } - } - mCompleteRange.right = (float) d; - - d = series.get(0).getLowestValueY(); - for (Series s : series) { - if (!s.isEmpty() && d > s.getLowestValueY()) { - d = s.getLowestValueY(); - } - } - mCompleteRange.bottom = (float) d; - - d = series.get(0).getHighestValueY(); - for (Series s : series) { - if (!s.isEmpty() && d < s.getHighestValueY()) { - d = s.getHighestValueY(); - } - } - mCompleteRange.top = (float) d; - } - - // calc current viewport bounds - if (mYAxisBoundsStatus == AxisBoundsStatus.AUTO_ADJUSTED) { - mYAxisBoundsStatus = AxisBoundsStatus.INITIAL; - } - if (mYAxisBoundsStatus == AxisBoundsStatus.INITIAL) { - mCurrentViewport.top = mCompleteRange.top; - mCurrentViewport.bottom = mCompleteRange.bottom; - } - - if (mXAxisBoundsStatus == AxisBoundsStatus.AUTO_ADJUSTED) { - mXAxisBoundsStatus = AxisBoundsStatus.INITIAL; - } - if (mXAxisBoundsStatus == AxisBoundsStatus.INITIAL) { - mCurrentViewport.left = mCompleteRange.left; - mCurrentViewport.right = mCompleteRange.right; - } else if (mXAxisBoundsManual && !mYAxisBoundsManual && mCompleteRange.width() != 0) { - // get highest/lowest of current viewport - // lowest - double d = Double.MAX_VALUE; - for (Series s : series) { - Iterator values = s.getValues(mCurrentViewport.left, mCurrentViewport.right); - while (values.hasNext()) { - double v = values.next().getY(); - if (d > v) { - d = v; - } - } - } - - mCurrentViewport.bottom = (float) d; - - // highest - d = Double.MIN_VALUE; - for (Series s : series) { - Iterator values = s.getValues(mCurrentViewport.left, mCurrentViewport.right); - while (values.hasNext()) { - double v = values.next().getY(); - if (d < v) { - d = v; - } - } - } - mCurrentViewport.top = (float) d; - } - - // fixes blank screen when range is zero - if (mCurrentViewport.left == mCurrentViewport.right) mCurrentViewport.right++; - if (mCurrentViewport.top == mCurrentViewport.bottom) mCurrentViewport.top++; - } - - /** - * @param completeRange if true => minX of the complete range of all series - * if false => minX of the current visible viewport - * @return the min x value - */ - public double getMinX(boolean completeRange) { - if (completeRange) { - return (double) mCompleteRange.left; - } else { - return (double) mCurrentViewport.left; - } - } - - /** - * @param completeRange if true => maxX of the complete range of all series - * if false => maxX of the current visible viewport - * @return the max x value - */ - public double getMaxX(boolean completeRange) { - if (completeRange) { - return (double) mCompleteRange.right; - } else { - return mCurrentViewport.right; - } - } - - /** - * @param completeRange if true => minY of the complete range of all series - * if false => minY of the current visible viewport - * @return the min y value - */ - public double getMinY(boolean completeRange) { - if (completeRange) { - return (double) mCompleteRange.bottom; - } else { - return mCurrentViewport.bottom; - } - } - - /** - * @param completeRange if true => maxY of the complete range of all series - * if false => maxY of the current visible viewport - * @return the max y value - */ - public double getMaxY(boolean completeRange) { - if (completeRange) { - return (double) mCompleteRange.top; - } else { - return mCurrentViewport.top; - } - } - - /** - * set the maximal y value for the current viewport. - * Make sure to set the y bounds to manual via - * {@link #setYAxisBoundsManual(boolean)} - * @param y max / highest value - */ - public void setMaxY(double y) { - mCurrentViewport.top = (float) y; - } - - /** - * set the minimal y value for the current viewport. - * Make sure to set the y bounds to manual via - * {@link #setYAxisBoundsManual(boolean)} - * @param y min / lowest value - */ - public void setMinY(double y) { - mCurrentViewport.bottom = (float) y; - } - - /** - * set the maximal x value for the current viewport. - * Make sure to set the x bounds to manual via - * {@link #setXAxisBoundsManual(boolean)} - * @param x max / highest value - */ - public void setMaxX(double x) { - mCurrentViewport.right = (float) x; - } - - /** - * set the minimal x value for the current viewport. - * Make sure to set the x bounds to manual via - * {@link #setXAxisBoundsManual(boolean)} - * @param x min / lowest value - */ - public void setMinX(double x) { - mCurrentViewport.left = (float) x; - } - - /** - * release the glowing effects - */ - private void releaseEdgeEffects() { - mEdgeEffectLeftActive - = mEdgeEffectRightActive - = false; - mEdgeEffectLeft.onRelease(); - mEdgeEffectRight.onRelease(); - } - - /** - * not used currently - * - * @param velocityX - * @param velocityY - */ - private void fling(int velocityX, int velocityY) { - velocityY = 0; - releaseEdgeEffects(); - // Flings use math in pixels (as opposed to math based on the viewport). - mScrollerStartViewport.set(mCurrentViewport); - int maxX = (int)((mCurrentViewport.width()/mCompleteRange.width())*(float)mGraphView.getGraphContentWidth()) - mGraphView.getGraphContentWidth(); - int maxY = (int)((mCurrentViewport.height()/mCompleteRange.height())*(float)mGraphView.getGraphContentHeight()) - mGraphView.getGraphContentHeight(); - int startX = (int)((mCurrentViewport.left - mCompleteRange.left)/mCompleteRange.width())*maxX; - int startY = (int)((mCurrentViewport.top - mCompleteRange.top)/mCompleteRange.height())*maxY; - mScroller.forceFinished(true); - mScroller.fling( - startX, - startY, - velocityX, - velocityY, - 0, maxX, - 0, maxY, - mGraphView.getGraphContentWidth() / 2, - mGraphView.getGraphContentHeight() / 2); - ViewCompat.postInvalidateOnAnimation(mGraphView); - } - - /** - * not used currently - */ - public void computeScroll() { - if (true) return; - - boolean needsInvalidate = false; - - if (mScroller.computeScrollOffset()) { - // The scroller isn't finished, meaning a fling or programmatic pan operation is - // currently active. - - int completeWidth = (int)((mCompleteRange.width()/mCurrentViewport.width()) * (float) mGraphView.getGraphContentWidth()); - int completeHeight = (int)((mCompleteRange.height()/mCurrentViewport.height()) * (float) mGraphView.getGraphContentHeight()); - - int currX = mScroller.getCurrX(); - int currY = mScroller.getCurrY(); - - boolean canScrollX = mCurrentViewport.left > mCompleteRange.left - || mCurrentViewport.right < mCompleteRange.right; - boolean canScrollY = mCurrentViewport.bottom > mCompleteRange.bottom - || mCurrentViewport.top < mCompleteRange.top; - - if (canScrollX - && currX < 0 - && mEdgeEffectLeft.isFinished() - && !mEdgeEffectLeftActive) { - mEdgeEffectLeft.onAbsorb((int) OverScrollerCompat.getCurrVelocity(mScroller)); - mEdgeEffectLeftActive = true; - needsInvalidate = true; - } else if (canScrollX - && currX > (completeWidth - mGraphView.getGraphContentWidth()) - && mEdgeEffectRight.isFinished() - && !mEdgeEffectRightActive) { - mEdgeEffectRight.onAbsorb((int) OverScrollerCompat.getCurrVelocity(mScroller)); - mEdgeEffectRightActive = true; - needsInvalidate = true; - } - - if (canScrollY - && currY < 0 - && mEdgeEffectTop.isFinished() - && !mEdgeEffectTopActive) { - mEdgeEffectTop.onAbsorb((int) OverScrollerCompat.getCurrVelocity(mScroller)); - mEdgeEffectTopActive = true; - needsInvalidate = true; - } else if (canScrollY - && currY > (completeHeight - mGraphView.getGraphContentHeight()) - && mEdgeEffectBottom.isFinished() - && !mEdgeEffectBottomActive) { - mEdgeEffectBottom.onAbsorb((int) OverScrollerCompat.getCurrVelocity(mScroller)); - mEdgeEffectBottomActive = true; - needsInvalidate = true; - } - - float currXRange = mCompleteRange.left + mCompleteRange.width() - * currX / completeWidth; - float currYRange = mCompleteRange.top - mCompleteRange.height() - * currY / completeHeight; - - float currWidth = mCurrentViewport.width(); - float currHeight = mCurrentViewport.height(); - mCurrentViewport.left = currXRange; - mCurrentViewport.right = currXRange + currWidth; - //mCurrentViewport.bottom = currYRange; - //mCurrentViewport.top = currYRange + currHeight; - } - - if (needsInvalidate) { - ViewCompat.postInvalidateOnAnimation(mGraphView); - } - } - - /** - * Draws the overscroll "glow" at the four edges of the chart region, if necessary. - * - * @see EdgeEffectCompat - */ - private void drawEdgeEffectsUnclipped(Canvas canvas) { - // The methods below rotate and translate the canvas as needed before drawing the glow, - // since EdgeEffectCompat always draws a top-glow at 0,0. - - boolean needsInvalidate = false; - - if (!mEdgeEffectTop.isFinished()) { - final int restoreCount = canvas.save(); - canvas.translate(mGraphView.getGraphContentLeft(), mGraphView.getGraphContentTop()); - mEdgeEffectTop.setSize(mGraphView.getGraphContentWidth(), mGraphView.getGraphContentHeight()); - if (mEdgeEffectTop.draw(canvas)) { - needsInvalidate = true; - } - canvas.restoreToCount(restoreCount); - } - - //if (!mEdgeEffectBottom.isFinished()) { - // final int restoreCount = canvas.save(); - // canvas.translate(2 * mContentRect.left - mContentRect.right, mContentRect.bottom); - // canvas.rotate(180, mContentRect.width(), 0); - // mEdgeEffectBottom.setSize(mContentRect.width(), mContentRect.height()); - // if (mEdgeEffectBottom.draw(canvas)) { - // needsInvalidate = true; - // } - // canvas.restoreToCount(restoreCount); - //} - - if (!mEdgeEffectLeft.isFinished()) { - final int restoreCount = canvas.save(); - canvas.translate(mGraphView.getGraphContentLeft(), mGraphView.getGraphContentTop()+ mGraphView.getGraphContentHeight()); - canvas.rotate(-90, 0, 0); - mEdgeEffectLeft.setSize(mGraphView.getGraphContentHeight(), mGraphView.getGraphContentWidth()); - if (mEdgeEffectLeft.draw(canvas)) { - needsInvalidate = true; - } - canvas.restoreToCount(restoreCount); - } - - if (!mEdgeEffectRight.isFinished()) { - final int restoreCount = canvas.save(); - canvas.translate(mGraphView.getGraphContentLeft()+ mGraphView.getGraphContentWidth(), mGraphView.getGraphContentTop()); - canvas.rotate(90, 0, 0); - mEdgeEffectRight.setSize(mGraphView.getGraphContentHeight(), mGraphView.getGraphContentWidth()); - if (mEdgeEffectRight.draw(canvas)) { - needsInvalidate = true; - } - canvas.restoreToCount(restoreCount); - } - - if (needsInvalidate) { - ViewCompat.postInvalidateOnAnimation(mGraphView); - } - } - - /** - * will be first called in order to draw - * the canvas - * Used to draw the background - * - * @param c canvas. - */ - public void drawFirst(Canvas c) { - // draw background - if (mBackgroundColor != Color.TRANSPARENT) { - mPaint.setColor(mBackgroundColor); - c.drawRect( - mGraphView.getGraphContentLeft(), - mGraphView.getGraphContentTop(), - mGraphView.getGraphContentLeft()+mGraphView.getGraphContentWidth(), - mGraphView.getGraphContentTop()+mGraphView.getGraphContentHeight(), - mPaint - ); - } - } - - /** - * draws the glowing edge effect - * - * @param c canvas - */ - public void draw(Canvas c) { - drawEdgeEffectsUnclipped(c); - } - - /** - * @return background of the viewport area - */ - public int getBackgroundColor() { - return mBackgroundColor; - } - - /** - * @param mBackgroundColor background of the viewport area - * use transparent to have no background - */ - public void setBackgroundColor(int mBackgroundColor) { - this.mBackgroundColor = mBackgroundColor; - } - - /** - * @return whether the viewport is scalable - */ - public boolean isScalable() { - return mIsScalable; - } - - /** - * active the scaling/zooming feature - * notice: sets the x axis bounds to manual - * - * @param mIsScalable whether the viewport is scalable - */ - public void setScalable(boolean mIsScalable) { - this.mIsScalable = mIsScalable; - if (mIsScalable) { - mIsScrollable = true; - - // set viewport to manual - setXAxisBoundsManual(true); - } - - } - - /** - * @return whether the x axis bounds are manual. - * @see #setMinX(double) - * @see #setMaxX(double) - */ - public boolean isXAxisBoundsManual() { - return mXAxisBoundsManual; - } - - /** - * @param mXAxisBoundsManual whether the x axis bounds are manual. - * @see #setMinX(double) - * @see #setMaxX(double) - */ - public void setXAxisBoundsManual(boolean mXAxisBoundsManual) { - this.mXAxisBoundsManual = mXAxisBoundsManual; - if (mXAxisBoundsManual) { - mXAxisBoundsStatus = AxisBoundsStatus.FIX; - } - } - - /** - * @return whether the y axis bound are manual - */ - public boolean isYAxisBoundsManual() { - return mYAxisBoundsManual; - } - - /** - * @param mYAxisBoundsManual whether the y axis bounds are manual - * @see #setMaxY(double) - * @see #setMinY(double) - */ - public void setYAxisBoundsManual(boolean mYAxisBoundsManual) { - this.mYAxisBoundsManual = mYAxisBoundsManual; - if (mYAxisBoundsManual) { - mYAxisBoundsStatus = AxisBoundsStatus.FIX; - } - } - - /** - * forces the viewport to scroll to the end - * of the range by keeping the current viewport size. - * - * Important: Only takes effect if x axis bounds are manual. - * - * @see #setXAxisBoundsManual(boolean) - */ - public void scrollToEnd() { - if (mXAxisBoundsManual) { - float size = mCurrentViewport.width(); - mCurrentViewport.right = mCompleteRange.right; - mCurrentViewport.left = mCompleteRange.right - size; - mScrollingReferenceX = Float.NaN; - mGraphView.onDataChanged(true, false); - } else { - Log.w("GraphView", "scrollToEnd works only with manual x axis bounds"); - } - } -} diff --git a/graphview/src/main/java/com/jjoe64/graphview/compat/OverScrollerCompat.java b/graphview/src/main/java/com/jjoe64/graphview/compat/OverScrollerCompat.java deleted file mode 100644 index 20b497c91f..0000000000 --- a/graphview/src/main/java/com/jjoe64/graphview/compat/OverScrollerCompat.java +++ /dev/null @@ -1,46 +0,0 @@ -/** - * GraphView - * Copyright (C) 2014 Jonas Gehring - * - * 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, - * with the "Linking Exception", which can be found at the license.txt - * file in this program. - * - * 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 - * with the "Linking Exception" along with this program; if not, - * write to the author Jonas Gehring . - */ -package com.jjoe64.graphview.compat; - -import android.annotation.TargetApi; -import android.os.Build; -import android.widget.OverScroller; - -/** - * A utility class for using {@link android.widget.OverScroller} in a backward-compatible fashion. - */ -public class OverScrollerCompat { - /** - * Disallow instantiation. - */ - private OverScrollerCompat() { - } - /** - * @see android.view.ScaleGestureDetector#getCurrentSpanY() - */ - @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH) - public static float getCurrVelocity(OverScroller overScroller) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) { - return overScroller.getCurrVelocity(); - } else { - return 0; - } - } -} \ No newline at end of file diff --git a/graphview/src/main/java/com/jjoe64/graphview/helper/DateAsXAxisLabelFormatter.java b/graphview/src/main/java/com/jjoe64/graphview/helper/DateAsXAxisLabelFormatter.java deleted file mode 100644 index 1e9fa3cff4..0000000000 --- a/graphview/src/main/java/com/jjoe64/graphview/helper/DateAsXAxisLabelFormatter.java +++ /dev/null @@ -1,94 +0,0 @@ -/** - * GraphView - * Copyright (C) 2014 Jonas Gehring - * - * 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, - * with the "Linking Exception", which can be found at the license.txt - * file in this program. - * - * 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 - * with the "Linking Exception" along with this program; if not, - * write to the author Jonas Gehring . - */ -package com.jjoe64.graphview.helper; - -import android.content.Context; - -import com.jjoe64.graphview.DefaultLabelFormatter; - -import java.text.DateFormat; -import java.util.Calendar; -import java.util.Date; - -/** - * Helper class to use date objects as x-values. - * This will use your own Date Format or by default - * the Android default date format to convert - * the x-values (that has to be millis from - * 01-01-1970) into a formatted date string. - * - * See the DateAsXAxis example in the GraphView-Demos project - * to see a working example. - * - * @author jjoe64 - */ -public class DateAsXAxisLabelFormatter extends DefaultLabelFormatter { - /** - * the date format that will convert - * the unix timestamp to string - */ - protected final DateFormat mDateFormat; - - /** - * calendar to avoid creating new date objects - */ - protected final Calendar mCalendar; - - /** - * create the formatter with the Android default date format to convert - * the x-values. - * - * @param context the application context - */ - public DateAsXAxisLabelFormatter(Context context) { - mDateFormat = android.text.format.DateFormat.getDateFormat(context); - mCalendar = Calendar.getInstance(); - } - - /** - * create the formatter with your own custom - * date format to convert the x-values. - * - * @param context the application context - * @param dateFormat custom date format - */ - public DateAsXAxisLabelFormatter(Context context, DateFormat dateFormat) { - mDateFormat = dateFormat; - mCalendar = Calendar.getInstance(); - } - - /** - * formats the x-values as date string. - * - * @param value raw value - * @param isValueX true if it's a x value, otherwise false - * @return value converted to string - */ - @Override - public String formatLabel(double value, boolean isValueX) { - if (isValueX) { - // format as date - mCalendar.setTimeInMillis((long) value); - return mDateFormat.format(mCalendar.getTimeInMillis()); - } else { - return super.formatLabel(value, isValueX); - } - } -} diff --git a/graphview/src/main/java/com/jjoe64/graphview/helper/GraphViewXML.java b/graphview/src/main/java/com/jjoe64/graphview/helper/GraphViewXML.java deleted file mode 100644 index d036805bc8..0000000000 --- a/graphview/src/main/java/com/jjoe64/graphview/helper/GraphViewXML.java +++ /dev/null @@ -1,137 +0,0 @@ -/** - * GraphView - * Copyright (C) 2014 Jonas Gehring - * - * 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, - * with the "Linking Exception", which can be found at the license.txt - * file in this program. - * - * 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 - * with the "Linking Exception" along with this program; if not, - * write to the author Jonas Gehring . - */ -package com.jjoe64.graphview.helper; - -import android.content.Context; -import android.content.res.TypedArray; -import android.graphics.Color; -import android.util.AttributeSet; -import android.util.Log; - -import com.jjoe64.graphview.GraphView; -import com.jjoe64.graphview.R; -import com.jjoe64.graphview.series.BarGraphSeries; -import com.jjoe64.graphview.series.BaseSeries; -import com.jjoe64.graphview.series.DataPoint; -import com.jjoe64.graphview.series.DataPointInterface; -import com.jjoe64.graphview.series.LineGraphSeries; -import com.jjoe64.graphview.series.PointsGraphSeries; -import com.jjoe64.graphview.series.Series; - -/** - * helper class to use GraphView directly - * in a XML layout file. - * - * You can set the data via attribute app:seriesData - * in the format: "X=Y;X=Y;..." e.g. "0=5.0;1=5;2=4;3=9" - * - * Other styling options: - *
  • app:seriesType="line|bar|points"
  • - *
  • app:seriesColor="#ff0000"
  • - *
  • app:seriesTitle="foobar" - if this is set, the legend will be drawn
  • - *
  • android:title="foobar"
  • - * - * Example: - *
    - * {@code
    - *  
    - * }
    - * 
    - * - * @author jjoe64 - */ -public class GraphViewXML extends GraphView { - /** - * creates the graphview object with data and - * other options from xml attributes. - * - * @param context - * @param attrs - */ - public GraphViewXML(Context context, AttributeSet attrs) { - super(context, attrs); - - // get attributes - TypedArray a=context.obtainStyledAttributes( - attrs, - R.styleable.GraphViewXML); - - String dataStr = a.getString(R.styleable.GraphViewXML_seriesData); - int color = a.getColor(R.styleable.GraphViewXML_seriesColor, Color.TRANSPARENT); - String type = a.getString(R.styleable.GraphViewXML_seriesType); - String seriesTitle = a.getString(R.styleable.GraphViewXML_seriesTitle); - String title = a.getString(R.styleable.GraphViewXML_android_title); - - a.recycle(); - - // decode data - DataPoint[] data; - if (dataStr == null || dataStr.isEmpty()) { - throw new IllegalArgumentException("Attribute seriesData is required in the format: 0=5.0;1=5;2=4;3=9"); - } else { - String[] d = dataStr.split(";"); - try { - data = new DataPoint[d.length]; - int i = 0; - for (String dd : d) { - String[] xy = dd.split("="); - data[i] = new DataPoint(Double.parseDouble(xy[0]), Double.parseDouble(xy[1])); - i++; - } - } catch (Exception e) { - Log.e("GraphViewXML", e.toString()); - throw new IllegalArgumentException("Attribute seriesData is broken. Use this format: 0=5.0;1=5;2=4;3=9"); - } - } - - // create series - BaseSeries series; - if (type == null || type.isEmpty()) { - type = "line"; - } - if (type.equals("line")) { - series = new LineGraphSeries(data); - } else if (type.equals("bar")) { - series = new BarGraphSeries(data); - } else if (type.equals("points")) { - series = new PointsGraphSeries(data); - } else { - throw new IllegalArgumentException("unknown graph type: "+type+". Possible is line|bar|points"); - } - if (color != Color.TRANSPARENT) { - series.setColor(color); - } - addSeries(series); - - if (seriesTitle != null && !seriesTitle.isEmpty()) { - series.setTitle(seriesTitle); - getLegendRenderer().setVisible(true); - } - - if (title != null && !title.isEmpty()) { - setTitle(title); - } - } -} diff --git a/graphview/src/main/java/com/jjoe64/graphview/helper/StaticLabelsFormatter.java b/graphview/src/main/java/com/jjoe64/graphview/helper/StaticLabelsFormatter.java deleted file mode 100644 index 4b38dbf4c2..0000000000 --- a/graphview/src/main/java/com/jjoe64/graphview/helper/StaticLabelsFormatter.java +++ /dev/null @@ -1,209 +0,0 @@ -package com.jjoe64.graphview.helper; - -import com.jjoe64.graphview.DefaultLabelFormatter; -import com.jjoe64.graphview.GraphView; -import com.jjoe64.graphview.LabelFormatter; -import com.jjoe64.graphview.Viewport; - -/** - * Use this label formatter to show static labels. - * Static labels are not bound to the data. It is typical used - * for show text like "low", "middle", "high". - * - * You can set the static labels for vertical or horizontal - * individually and you can define a label formatter that - * is to be used if you don't define static labels. - * - * For example if you only use static labels for horizontal labels, - * graphview will use the dynamicLabelFormatter for the vertical labels. - */ -public class StaticLabelsFormatter implements LabelFormatter { - /** - * reference to the viewport - */ - protected Viewport mViewport; - - /** - * the vertical labels, ordered from bottom to the top - * if it is null, the labels will be generated via the #dynamicLabelFormatter - */ - protected String[] mVerticalLabels; - - /** - * the horizontal labels, ordered form the left to the right - * if it is null, the labels will be generated via the #dynamicLabelFormatter - */ - protected String[] mHorizontalLabels; - - /** - * the label formatter that will format the labels - * for that there are no static labels defined. - */ - protected LabelFormatter mDynamicLabelFormatter; - - /** - * reference to the graphview - */ - protected final GraphView mGraphView; - - /** - * creates the formatter without any static labels - * define your static labels via {@link #setHorizontalLabels(String[])} and {@link #setVerticalLabels(String[])} - * - * @param graphView reference to the graphview - */ - public StaticLabelsFormatter(GraphView graphView) { - mGraphView = graphView; - init(null, null, null); - } - - /** - * creates the formatter without any static labels. - * define your static labels via {@link #setHorizontalLabels(String[])} and {@link #setVerticalLabels(String[])} - * - * @param graphView reference to the graphview - * @param dynamicLabelFormatter the label formatter that will format the labels - * for that there are no static labels defined. - */ - public StaticLabelsFormatter(GraphView graphView, LabelFormatter dynamicLabelFormatter) { - mGraphView = graphView; - init(null, null, dynamicLabelFormatter); - } - - /** - * creates the formatter with static labels defined. - * - * @param graphView reference to the graphview - * @param horizontalLabels the horizontal labels, ordered form the left to the right - * if it is null, the labels will be generated via the #dynamicLabelFormatter - * @param verticalLabels the vertical labels, ordered from bottom to the top - * if it is null, the labels will be generated via the #dynamicLabelFormatter - */ - public StaticLabelsFormatter(GraphView graphView, String[] horizontalLabels, String[] verticalLabels) { - mGraphView = graphView; - init(horizontalLabels, verticalLabels, null); - } - - /** - * creates the formatter with static labels defined. - * - * @param graphView reference to the graphview - * @param horizontalLabels the horizontal labels, ordered form the left to the right - * if it is null, the labels will be generated via the #dynamicLabelFormatter - * @param verticalLabels the vertical labels, ordered from bottom to the top - * if it is null, the labels will be generated via the #dynamicLabelFormatter - * @param dynamicLabelFormatter the label formatter that will format the labels - * for that there are no static labels defined. - */ - public StaticLabelsFormatter(GraphView graphView, String[] horizontalLabels, String[] verticalLabels, LabelFormatter dynamicLabelFormatter) { - mGraphView = graphView; - init(horizontalLabels, verticalLabels, dynamicLabelFormatter); - } - - /** - * @param horizontalLabels the horizontal labels, ordered form the left to the right - * if it is null, the labels will be generated via the #dynamicLabelFormatter - * @param verticalLabels the vertical labels, ordered from bottom to the top - * if it is null, the labels will be generated via the #dynamicLabelFormatter - * @param dynamicLabelFormatter the label formatter that will format the labels - * for that there are no static labels defined. - */ - protected void init(String[] horizontalLabels, String[] verticalLabels, LabelFormatter dynamicLabelFormatter) { - mDynamicLabelFormatter = dynamicLabelFormatter; - if (mDynamicLabelFormatter == null) { - mDynamicLabelFormatter = new DefaultLabelFormatter(); - } - - mHorizontalLabels = horizontalLabels; - mVerticalLabels = verticalLabels; - } - - /** - * Set a label formatter that will be used for the labels - * that don't have static labels. - * - * For example if you only use static labels for horizontal labels, - * graphview will use the dynamicLabelFormatter for the vertical labels. - * - * @param dynamicLabelFormatter the label formatter that will format the labels - * for that there are no static labels defined. - */ - public void setDynamicLabelFormatter(LabelFormatter dynamicLabelFormatter) { - this.mDynamicLabelFormatter = dynamicLabelFormatter; - adjust(); - } - - /** - * @param horizontalLabels the horizontal labels, ordered form the left to the right - * if it is null, the labels will be generated via the #dynamicLabelFormatter - */ - public void setHorizontalLabels(String[] horizontalLabels) { - this.mHorizontalLabels = horizontalLabels; - adjust(); - } - - /** - * @param verticalLabels the vertical labels, ordered from bottom to the top - * if it is null, the labels will be generated via the #dynamicLabelFormatter - */ - public void setVerticalLabels(String[] verticalLabels) { - this.mVerticalLabels = verticalLabels; - adjust(); - } - - /** - * - * @param value raw input number - * @param isValueX true if it is a value for the x axis - * false if it is a value for the y axis - * @return - */ - @Override - public String formatLabel(double value, boolean isValueX) { - if (isValueX && mHorizontalLabels != null) { - double minX = mViewport.getMinX(false); - double maxX = mViewport.getMaxX(false); - double range = maxX - minX; - value = value-minX; - int idx = (int)((value/range) * (mHorizontalLabels.length-1)); - return mHorizontalLabels[idx]; - } else if (!isValueX && mVerticalLabels != null) { - double minY = mViewport.getMinY(false); - double maxY = mViewport.getMaxY(false); - double range = maxY - minY; - value = value-minY; - int idx = (int)((value/range) * (mVerticalLabels.length-1)); - return mVerticalLabels[idx]; - } else { - return mDynamicLabelFormatter.formatLabel(value, isValueX); - } - } - - /** - * @param viewport the used viewport - */ - @Override - public void setViewport(Viewport viewport) { - mViewport = viewport; - adjust(); - } - - /** - * adjusts the number of vertical/horizontal labels - */ - protected void adjust() { - mDynamicLabelFormatter.setViewport(mViewport); - if (mVerticalLabels != null) { - if (mVerticalLabels.length < 2) { - throw new IllegalStateException("You need at least 2 vertical labels if you use static label formatter."); - } - mGraphView.getGridLabelRenderer().setNumVerticalLabels(mVerticalLabels.length); - } - if (mHorizontalLabels != null) { - if (mHorizontalLabels.length < 2) { - throw new IllegalStateException("You need at least 2 horizontal labels if you use static label formatter."); - } - mGraphView.getGridLabelRenderer().setNumHorizontalLabels(mHorizontalLabels.length); - } - } -} diff --git a/graphview/src/main/java/com/jjoe64/graphview/series/BarGraphSeries.java b/graphview/src/main/java/com/jjoe64/graphview/series/BarGraphSeries.java deleted file mode 100644 index 8db7bac94b..0000000000 --- a/graphview/src/main/java/com/jjoe64/graphview/series/BarGraphSeries.java +++ /dev/null @@ -1,379 +0,0 @@ -/** - * GraphView - * Copyright (C) 2014 Jonas Gehring - * - * 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, - * with the "Linking Exception", which can be found at the license.txt - * file in this program. - * - * 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 - * with the "Linking Exception" along with this program; if not, - * write to the author Jonas Gehring . - */ -package com.jjoe64.graphview.series; - -import android.graphics.Canvas; -import android.graphics.Paint; -import android.graphics.RectF; -import android.util.Log; - -import com.jjoe64.graphview.GraphView; -import com.jjoe64.graphview.ValueDependentColor; - -import java.util.HashMap; -import java.util.Iterator; -import java.util.Map; -import java.util.SortedSet; -import java.util.TreeSet; - -/** - * Series with Bars to visualize the data. - * The Bars are always vertical. - * - * @author jjoe64 - */ -public class BarGraphSeries extends BaseSeries { - /** - * paint to do drawing on canvas - */ - private Paint mPaint; - - /** - * spacing between the bars in percentage. - * 0 => no spacing - * 100 => the space bewetten the bars is as big as the bars itself - */ - private int mSpacing; - - /** - * callback to generate value-dependent colors - * of the bars - */ - private ValueDependentColor mValueDependentColor; - - /** - * flag whether the values should drawn - * above the bars as text - */ - private boolean mDrawValuesOnTop; - - /** - * color of the text above the bars. - * - * @see #mDrawValuesOnTop - */ - private int mValuesOnTopColor; - - /** - * font size of the text above the bars. - * - * @see #mDrawValuesOnTop - */ - private float mValuesOnTopSize; - - /** - * stores the coordinates of the bars to - * trigger tap on series events. - */ - private Map mDataPoints = new HashMap(); - - /** - * creates bar series without any data - */ - public BarGraphSeries() { - mPaint = new Paint(); - } - - /** - * creates bar series with data - * - * @param data values - */ - public BarGraphSeries(E[] data) { - super(data); - mPaint = new Paint(); - } - - /** - * draws the bars on the canvas - * - * @param graphView corresponding graphview - * @param canvas canvas - * @param isSecondScale whether we are plotting the second scale or not - */ - @Override - public void draw(GraphView graphView, Canvas canvas, boolean isSecondScale) { - mPaint.setTextAlign(Paint.Align.CENTER); - if (mValuesOnTopSize == 0) { - mValuesOnTopSize = graphView.getGridLabelRenderer().getTextSize(); - } - mPaint.setTextSize(mValuesOnTopSize); - - // get data - double maxX = graphView.getViewport().getMaxX(false); - double minX = graphView.getViewport().getMinX(false); - - double maxY; - double minY; - if (isSecondScale) { - maxY = graphView.getSecondScale().getMaxY(); - minY = graphView.getSecondScale().getMinY(); - } else { - maxY = graphView.getViewport().getMaxY(false); - minY = graphView.getViewport().getMinY(false); - } - - // Iterate through all bar graph series - // so we know how wide to make our bar, - // and in what position to put it in - int numBarSeries = 0; - int currentSeriesOrder = 0; - int numValues = 0; - boolean isCurrentSeries; - SortedSet xVals = new TreeSet(); - for(Series inspectedSeries: graphView.getSeries()) { - if(inspectedSeries instanceof BarGraphSeries) { - isCurrentSeries = (inspectedSeries == this); - if(isCurrentSeries) { - currentSeriesOrder = numBarSeries; - } - numBarSeries++; - - // calculate the number of slots for bars based on the minimum distance between - // x coordinates in the series. This is divided into the range to find - // the placement and width of bar slots - // (sections of the x axis for each bar or set of bars) - // TODO: Move this somewhere more general and cache it, so we don't recalculate it for each series - Iterator curValues = inspectedSeries.getValues(minX, maxX); - if (curValues.hasNext()) { - xVals.add(curValues.next().getX()); - if(isCurrentSeries) { numValues++; } - while (curValues.hasNext()) { - xVals.add(curValues.next().getX()); - if(isCurrentSeries) { numValues++; } - } - } - } - } - if (numValues == 0) { - return; - } - - Double lastVal = null; - double minGap = 0; - for(Double curVal: xVals) { - if(lastVal != null) { - double curGap = Math.abs(curVal - lastVal); - if (minGap == 0 || (curGap > 0 && curGap < minGap)) { - minGap = curGap; - } - } - lastVal = curVal; - } - - int numBarSlots = (minGap == 0) ? 1 : (int)Math.round((maxX - minX)/minGap) + 1; - - Iterator values = getValues(minX, maxX); - - // Calculate the overall bar slot width - this includes all bars across - // all series, and any spacing between sets of bars - float barSlotWidth = numBarSlots == 1 - ? graphView.getGraphContentWidth() - : graphView.getGraphContentWidth() / (numBarSlots-1); - Log.d("BarGraphSeries", "numBars=" + numBarSlots); - - // Total spacing (both sides) between sets of bars - float spacing = Math.min((float) barSlotWidth*mSpacing/100, barSlotWidth*0.98f); - // Width of an individual bar - float barWidth = (barSlotWidth - spacing) / numBarSeries; - // Offset from the center of a given bar to start drawing - float offset = barSlotWidth/2; - - double diffY = maxY - minY; - double diffX = maxX - minX; - float contentHeight = graphView.getGraphContentHeight(); - float contentWidth = graphView.getGraphContentWidth(); - float contentLeft = graphView.getGraphContentLeft(); - float contentTop = graphView.getGraphContentTop(); - - // draw data - int i=0; - while (values.hasNext()) { - E value = values.next(); - - double valY = value.getY() - minY; - double ratY = valY / diffY; - double y = contentHeight * ratY; - - double valY0 = 0 - minY; - double ratY0 = valY0 / diffY; - double y0 = contentHeight * ratY0; - - double valX = value.getX() - minX; - double ratX = valX / diffX; - double x = contentWidth * ratX; - - // hook for value dependent color - if (getValueDependentColor() != null) { - mPaint.setColor(getValueDependentColor().get(value)); - } else { - mPaint.setColor(getColor()); - } - - float left = (float)x + contentLeft - offset + spacing/2 + currentSeriesOrder*barWidth; - float top = (contentTop - (float)y) + contentHeight; - float right = left + barWidth; - float bottom = (contentTop - (float)y0) + contentHeight - (graphView.getGridLabelRenderer().isHighlightZeroLines()?4:1); - - boolean reverse = top > bottom; - if (reverse) { - float tmp = top; - top = bottom + (graphView.getGridLabelRenderer().isHighlightZeroLines()?4:1); - bottom = tmp; - } - - // overdraw - left = Math.max(left, contentLeft); - right = Math.min(right, contentLeft+contentWidth); - bottom = Math.min(bottom, contentTop+contentHeight); - top = Math.max(top, contentTop); - - mDataPoints.put(new RectF(left, top, right, bottom), value); - - canvas.drawRect(left, top, right, bottom, mPaint); - - // set values on top of graph - if (mDrawValuesOnTop) { - if (reverse) { - top = bottom + mValuesOnTopSize + 4; - if (top > contentTop+contentHeight) top = contentTop + contentHeight; - } else { - top -= 4; - if (top<=contentTop) top+=contentTop+4; - } - - mPaint.setColor(mValuesOnTopColor); - canvas.drawText( - graphView.getGridLabelRenderer().getLabelFormatter().formatLabel(value.getY(), false) - , (left+right)/2, top, mPaint); - } - - i++; - } - } - - /** - * @return the hook to generate value-dependent color. default null - */ - public ValueDependentColor getValueDependentColor() { - return mValueDependentColor; - } - - /** - * set a hook to make the color of the bars depending - * on the actually value/data. - * - * @param mValueDependentColor hook - * null to disable - */ - public void setValueDependentColor(ValueDependentColor mValueDependentColor) { - this.mValueDependentColor = mValueDependentColor; - } - - /** - * @return the spacing between the bars in percentage - */ - public int getSpacing() { - return mSpacing; - } - - /** - * @param mSpacing spacing between the bars in percentage. - * 0 => no spacing - * 100 => the space between the bars is as big as the bars itself - */ - public void setSpacing(int mSpacing) { - this.mSpacing = mSpacing; - } - - /** - * @return whether the values should be drawn above the bars - */ - public boolean isDrawValuesOnTop() { - return mDrawValuesOnTop; - } - - /** - * @param mDrawValuesOnTop flag whether the values should drawn - * above the bars as text - */ - public void setDrawValuesOnTop(boolean mDrawValuesOnTop) { - this.mDrawValuesOnTop = mDrawValuesOnTop; - } - - /** - * @return font color of the values on top of the bars - * @see #setDrawValuesOnTop(boolean) - */ - public int getValuesOnTopColor() { - return mValuesOnTopColor; - } - - /** - * @param mValuesOnTopColor the font color of the values on top of the bars - * @see #setDrawValuesOnTop(boolean) - */ - public void setValuesOnTopColor(int mValuesOnTopColor) { - this.mValuesOnTopColor = mValuesOnTopColor; - } - - /** - * @return font size of the values above the bars - * @see #setDrawValuesOnTop(boolean) - */ - public float getValuesOnTopSize() { - return mValuesOnTopSize; - } - - /** - * @param mValuesOnTopSize font size of the values above the bars - * @see #setDrawValuesOnTop(boolean) - */ - public void setValuesOnTopSize(float mValuesOnTopSize) { - this.mValuesOnTopSize = mValuesOnTopSize; - } - - /** - * resets the cached coordinates of the bars - */ - @Override - protected void resetDataPoints() { - mDataPoints.clear(); - } - - /** - * find the corresponding data point by - * coordinates. - * - * @param x pixels - * @param y pixels - * @return datapoint or null - */ - @Override - protected E findDataPoint(float x, float y) { - for (Map.Entry entry : mDataPoints.entrySet()) { - if (x >= entry.getKey().left && x <= entry.getKey().right - && y >= entry.getKey().top && y <= entry.getKey().bottom) { - return entry.getValue(); - } - } - return null; - } -} diff --git a/graphview/src/main/java/com/jjoe64/graphview/series/BaseSeries.java b/graphview/src/main/java/com/jjoe64/graphview/series/BaseSeries.java deleted file mode 100644 index 8ecdf23a73..0000000000 --- a/graphview/src/main/java/com/jjoe64/graphview/series/BaseSeries.java +++ /dev/null @@ -1,448 +0,0 @@ -/** - * GraphView - * Copyright (C) 2014 Jonas Gehring - * - * 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, - * with the "Linking Exception", which can be found at the license.txt - * file in this program. - * - * 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 - * with the "Linking Exception" along with this program; if not, - * write to the author Jonas Gehring . - */ -package com.jjoe64.graphview.series; - -import android.graphics.PointF; -import android.util.Log; - -import com.jjoe64.graphview.GraphView; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.NoSuchElementException; -import java.util.Set; - -/** - * Basis implementation for series. - * Used for series that are plotted on - * a default x/y 2d viewport. - * - * Extend this class to implement your own custom - * graph type. - * - * This implementation uses a internal Array to store - * the data. If you want to implement a custom data provider - * you may want to implement {@link com.jjoe64.graphview.series.Series}. - * - * @author jjoe64 - */ -public abstract class BaseSeries implements Series { - /** - * holds the data - */ - final private List mData = new ArrayList(); - - /** - * stores the used coordinates to find the - * corresponding data point on a tap - * - * Key => x/y pixel - * Value => Plotted Datapoint - * - * will be filled while drawing via {@link #registerDataPoint(float, float, DataPointInterface)} - */ - private Map mDataPoints = new HashMap(); - - /** - * title for this series that can be displayed - * in the legend. - */ - private String mTitle; - - /** - * base color for this series. will be used also in - * the legend - */ - private int mColor = 0xff0077cc; - - /** - * listener to handle tap events on a data point - */ - protected OnDataPointTapListener mOnDataPointTapListener; - - /** - * stores the graphviews where this series is used. - * Can be more than one. - */ - private List mGraphViews; - - /** - * creates series without data - */ - public BaseSeries() { - mGraphViews = new ArrayList(); - } - - /** - * creates series with data - * - * @param data data points - * important: array has to be sorted from lowest x-value to the highest - */ - public BaseSeries(E[] data) { - mGraphViews = new ArrayList(); - for (E d : data) { - mData.add(d); - } - } - - /** - * @return the lowest x value, or 0 if there is no data - */ - public double getLowestValueX() { - if (mData.isEmpty()) return 0d; - return mData.get(0).getX(); - } - - /** - * @return the highest x value, or 0 if there is no data - */ - public double getHighestValueX() { - if (mData.isEmpty()) return 0d; - return mData.get(mData.size()-1).getX(); - } - - /** - * @return the lowest y value, or 0 if there is no data - */ - public double getLowestValueY() { - if (mData.isEmpty()) return 0d; - double l = mData.get(0).getY(); - for (int i = 1; i < mData.size(); i++) { - double c = mData.get(i).getY(); - if (l > c) { - l = c; - } - } - return l; - } - - /** - * @return the highest y value, or 0 if there is no data - */ - public double getHighestValueY() { - if (mData.isEmpty()) return 0d; - double h = mData.get(0).getY(); - for (int i = 1; i < mData.size(); i++) { - double c = mData.get(i).getY(); - if (h < c) { - h = c; - } - } - return h; - } - - /** - * get the values for a given x range. if from and until are bigger or equal than - * all the data, the original data is returned. - * If it is only a part of the data, the range is returned plus one datapoint - * before and after to get a nice scrolling. - * - * @param from minimal x-value - * @param until maximal x-value - * @return data for the range +/- 1 datapoint - */ - @Override - public Iterator getValues(final double from, final double until) { - if (from <= getLowestValueX() && until >= getHighestValueX()) { - return mData.iterator(); - } else { - return new Iterator() { - Iterator org = mData.iterator(); - E nextValue = null; - E nextNextValue = null; - boolean plusOne = true; - - { - // go to first - boolean found = false; - E prevValue = null; - if (org.hasNext()) { - prevValue = org.next(); - } - if (prevValue.getX() >= from) { - nextValue = prevValue; - found = true; - } else { - while (org.hasNext()) { - nextValue = org.next(); - if (nextValue.getX() >= from) { - found = true; - nextNextValue = nextValue; - nextValue = prevValue; - break; - } - prevValue = nextValue; - } - } - if (!found) { - nextValue = null; - } - } - - @Override - public void remove() { - throw new UnsupportedOperationException(); - } - - @Override - public E next() { - if (hasNext()) { - E r = nextValue; - if (r.getX() > until) { - plusOne = false; - } - if (nextNextValue != null) { - nextValue = nextNextValue; - nextNextValue = null; - } else if (org.hasNext()) nextValue = org.next(); - else nextValue = null; - return r; - } else { - throw new NoSuchElementException(); - } - } - - @Override - public boolean hasNext() { - return nextValue != null && (nextValue.getX() <= until || plusOne); - } - }; - } - } - - /** - * @return the title of the series - */ - public String getTitle() { - return mTitle; - } - - /** - * set the title of the series. This will be used in - * the legend. - * - * @param mTitle title of the series - */ - public void setTitle(String mTitle) { - this.mTitle = mTitle; - } - - /** - * @return color of the series - */ - public int getColor() { - return mColor; - } - - /** - * set the color of the series. This will be used in - * plotting (depends on the series implementation) and - * is used in the legend. - * - * @param mColor - */ - public void setColor(int mColor) { - this.mColor = mColor; - } - - /** - * set a listener for tap on a data point. - * - * @param l listener - */ - public void setOnDataPointTapListener(OnDataPointTapListener l) { - this.mOnDataPointTapListener = l; - } - - /** - * called by the tap detector in order to trigger - * the on tap on datapoint event. - * - * @param x pixel - * @param y pixel - */ - @Override - public void onTap(float x, float y) { - if (mOnDataPointTapListener != null) { - E p = findDataPoint(x, y); - if (p != null) { - mOnDataPointTapListener.onTap(this, p); - } - } - } - - /** - * find the data point which is next to the - * coordinates - * - * @param x pixel - * @param y pixel - * @return the data point or null if nothing was found - */ - protected E findDataPoint(float x, float y) { - float shortestDistance = Float.NaN; - E shortest = null; - for (Map.Entry entry : mDataPoints.entrySet()) { - float x1 = entry.getKey().x; - float y1 = entry.getKey().y; - float x2 = x; - float y2 = y; - - float distance = (float) Math.sqrt((x1-x2)*(x1-x2) + (y1-y2)*(y1-y2)); - if (shortest == null || distance < shortestDistance) { - shortestDistance = distance; - shortest = entry.getValue(); - } - } - if (shortest != null) { - if (shortestDistance < 120) { - return shortest; - } - } - return null; - } - - /** - * register the datapoint to find it at a tap - * - * @param x pixel - * @param y pixel - * @param dp the data point to save - */ - protected void registerDataPoint(float x, float y, E dp) { - mDataPoints.put(new PointF(x, y), dp); - } - - /** - * clears the cached data point coordinates - */ - protected void resetDataPoints() { - mDataPoints.clear(); - } - - /** - * clears the data of this series and sets new. - * will redraw the graph - * - * @param data the values must be in the correct order! - * x-value has to be ASC. First the lowest x value and at least the highest x value. - */ - public void resetData(E[] data) { - mData.clear(); - for (E d : data) { - mData.add(d); - } - checkValueOrder(null); - - // update graphview - for (GraphView gv : mGraphViews) { - gv.onDataChanged(true, false); - } - } - - /** - * called when the series was added to a graph - * - * @param graphView graphview - */ - @Override - public void onGraphViewAttached(GraphView graphView) { - mGraphViews.add(graphView); - } - - /** - * - * @param dataPoint values the values must be in the correct order! - * x-value has to be ASC. First the lowest x value and at least the highest x value. - * @param scrollToEnd true => graphview will scroll to the end (maxX) - * @param maxDataPoints if max data count is reached, the oldest data - * value will be lost to avoid memory leaks - */ - public void appendData(E dataPoint, boolean scrollToEnd, int maxDataPoints) { - checkValueOrder(dataPoint); - - if (!mData.isEmpty() && dataPoint.getX() < mData.get(mData.size()-1).getX()) { - throw new IllegalArgumentException("new x-value must be greater then the last value. x-values has to be ordered in ASC."); - } - synchronized (mData) { - int curDataCount = mData.size(); - if (curDataCount < maxDataPoints) { - // enough space - mData.add(dataPoint); - } else { - // we have to trim one data - mData.remove(0); - mData.add(dataPoint); - } - } - - // recalc the labels when it was the first data - boolean keepLabels = mData.size() != 1; - - // update linked graph views - // update graphview - for (GraphView gv : mGraphViews) { - gv.onDataChanged(keepLabels, scrollToEnd); - if (scrollToEnd) { - gv.getViewport().scrollToEnd(); - } - } - } - - /** - * @return whether there are data points - */ - @Override - public boolean isEmpty() { - return mData.isEmpty(); - } - - /** - * checks that the data is in the correct order - * - * @param onlyLast if not null, it will only check that this - * datapoint is after the last point. - */ - protected void checkValueOrder(DataPointInterface onlyLast) { - if (mData.size()>1) { - if (onlyLast != null) { - // only check last - if (onlyLast.getX() < mData.get(mData.size()-1).getX()) { - throw new IllegalArgumentException("new x-value must be greater then the last value. x-values has to be ordered in ASC."); - } - } else { - double lx = mData.get(0).getX(); - - for (int i = 1; i < mData.size(); i++) { - if (mData.get(i).getX() != Double.NaN) { - if (lx > mData.get(i).getX()) { - throw new IllegalArgumentException("The order of the values is not correct. X-Values have to be ordered ASC. First the lowest x value and at least the highest x value."); - } - lx = mData.get(i).getX(); - } - } - } - } - } -} diff --git a/graphview/src/main/java/com/jjoe64/graphview/series/DataPoint.java b/graphview/src/main/java/com/jjoe64/graphview/series/DataPoint.java deleted file mode 100644 index b5f5eb3ef3..0000000000 --- a/graphview/src/main/java/com/jjoe64/graphview/series/DataPoint.java +++ /dev/null @@ -1,63 +0,0 @@ -/** - * GraphView - * Copyright (C) 2014 Jonas Gehring - * - * 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, - * with the "Linking Exception", which can be found at the license.txt - * file in this program. - * - * 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 - * with the "Linking Exception" along with this program; if not, - * write to the author Jonas Gehring . - */ -package com.jjoe64.graphview.series; - -import android.provider.ContactsContract; - -import java.io.Serializable; -import java.util.Date; - -/** - * default data point implementation. - * This stores the x and y values. - * - * @author jjoe64 - */ -public class DataPoint implements DataPointInterface, Serializable { - private static final long serialVersionUID=1428263322645L; - - private double x; - private double y; - - public DataPoint(double x, double y) { - this.x=x; - this.y=y; - } - - public DataPoint(Date x, double y) { - this.x = x.getTime(); - this.y = y; - } - - @Override - public double getX() { - return x; - } - - @Override - public double getY() { - return y; - } - - @Override - public String toString() { - return "["+x+"/"+y+"]"; - } -} diff --git a/graphview/src/main/java/com/jjoe64/graphview/series/DataPointInterface.java b/graphview/src/main/java/com/jjoe64/graphview/series/DataPointInterface.java deleted file mode 100644 index 9be683bef8..0000000000 --- a/graphview/src/main/java/com/jjoe64/graphview/series/DataPointInterface.java +++ /dev/null @@ -1,41 +0,0 @@ -/** - * GraphView - * Copyright (C) 2014 Jonas Gehring - * - * 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, - * with the "Linking Exception", which can be found at the license.txt - * file in this program. - * - * 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 - * with the "Linking Exception" along with this program; if not, - * write to the author Jonas Gehring . - */ -package com.jjoe64.graphview.series; - -/** - * interface of data points. Implement this in order - * to use your class in {@link com.jjoe64.graphview.series.Series}. - * - * You can also use the default implementation {@link com.jjoe64.graphview.series.DataPoint} so - * you do not have to implement it for yourself. - * - * @author jjoe64 - */ -public interface DataPointInterface { - /** - * @return the x value - */ - public double getX(); - - /** - * @return the y value - */ - public double getY(); -} diff --git a/graphview/src/main/java/com/jjoe64/graphview/series/LineGraphSeries.java b/graphview/src/main/java/com/jjoe64/graphview/series/LineGraphSeries.java deleted file mode 100644 index 4d721c2e6c..0000000000 --- a/graphview/src/main/java/com/jjoe64/graphview/series/LineGraphSeries.java +++ /dev/null @@ -1,409 +0,0 @@ -/** - * GraphView - * Copyright (C) 2014 Jonas Gehring - * - * 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, - * with the "Linking Exception", which can be found at the license.txt - * file in this program. - * - * 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 - * with the "Linking Exception" along with this program; if not, - * write to the author Jonas Gehring . - */ -package com.jjoe64.graphview.series; - -import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.Paint; -import android.graphics.Path; - -import com.jjoe64.graphview.GraphView; - -import java.util.Iterator; - -/** - * Series to plot the data as line. - * The line can be styled with many options. - * - * @author jjoe64 - */ -public class LineGraphSeries extends BaseSeries { - /** - * wrapped styles regarding the line - */ - private final class Styles { - /** - * the thickness of the line. - * This option will be ignored if you are - * using a custom paint via {@link #setCustomPaint(android.graphics.Paint)} - */ - private int thickness = 5; - - /** - * flag whether the area under the line to the bottom - * of the viewport will be filled with a - * specific background color. - * - * @see #backgroundColor - */ - private boolean drawBackground = false; - - /** - * flag whether the data points are highlighted as - * a visible point. - * - * @see #dataPointsRadius - */ - private boolean drawDataPoints = false; - - /** - * the radius for the data points. - * - * @see #drawDataPoints - */ - private float dataPointsRadius = 10f; - - /** - * the background color for the filling under - * the line. - * - * @see #drawBackground - */ - private int backgroundColor = Color.argb(100, 172, 218, 255); - } - - /** - * wrapped styles - */ - private Styles mStyles; - - /** - * internal paint object - */ - private Paint mPaint; - - /** - * paint for the background - */ - private Paint mPaintBackground; - - /** - * path for the background filling - */ - private Path mPathBackground; - - /** - * path to the line - */ - private Path mPath; - - /** - * custom paint that can be used. - * this will ignore the thickness and color styles. - */ - private Paint mCustomPaint; - - /** - * creates a series without data - */ - public LineGraphSeries() { - init(); - } - - /** - * creates a series with data - * - * @param data data points - */ - public LineGraphSeries(E[] data) { - super(data); - init(); - } - - /** - * do the initialization - * creates internal objects - */ - protected void init() { - mStyles = new Styles(); - mPaint = new Paint(); - mPaint.setStrokeCap(Paint.Cap.ROUND); - mPaint.setStyle(Paint.Style.STROKE); - mPaintBackground = new Paint(); - - mPathBackground = new Path(); - mPath = new Path(); - } - - /** - * plots the series - * draws the line and the background - * - * @param graphView graphview - * @param canvas canvas - * @param isSecondScale flag if it is the second scale - */ - @Override - public void draw(GraphView graphView, Canvas canvas, boolean isSecondScale) { - resetDataPoints(); - - // get data - double maxX = graphView.getViewport().getMaxX(false); - double minX = graphView.getViewport().getMinX(false); - - double maxY; - double minY; - if (isSecondScale) { - maxY = graphView.getSecondScale().getMaxY(); - minY = graphView.getSecondScale().getMinY(); - } else { - maxY = graphView.getViewport().getMaxY(false); - minY = graphView.getViewport().getMinY(false); - } - - Iterator values = getValues(minX, maxX); - - // draw background - double lastEndY = 0; - double lastEndX = 0; - - // draw data - mPaint.setStrokeWidth(mStyles.thickness); - mPaint.setColor(getColor()); - mPaintBackground.setColor(mStyles.backgroundColor); - - Paint paint; - if (mCustomPaint != null) { - paint = mCustomPaint; - } else { - paint = mPaint; - } - - if (mStyles.drawBackground) { - mPathBackground.reset(); - } - - double diffY = maxY - minY; - double diffX = maxX - minX; - - float graphHeight = graphView.getGraphContentHeight(); - float graphWidth = graphView.getGraphContentWidth(); - float graphLeft = graphView.getGraphContentLeft(); - float graphTop = graphView.getGraphContentTop(); - - lastEndY = 0; - lastEndX = 0; - double lastUsedEndX = 0; - float firstX = 0; - int i=0; - while (values.hasNext()) { - E value = values.next(); - - double valY = value.getY() - minY; - double ratY = valY / diffY; - double y = graphHeight * ratY; - - double valX = value.getX() - minX; - double ratX = valX / diffX; - double x = graphWidth * ratX; - - double orgX = x; - double orgY = y; - - if (i > 0) { - // overdraw - if (x > graphWidth) { // end right - double b = ((graphWidth - lastEndX) * (y - lastEndY)/(x - lastEndX)); - y = lastEndY+b; - x = graphWidth; - } - if (y < 0) { // end bottom - double b = ((0 - lastEndY) * (x - lastEndX)/(y - lastEndY)); - x = lastEndX+b; - y = 0; - } - if (y > graphHeight) { // end top - double b = ((graphHeight - lastEndY) * (x - lastEndX)/(y - lastEndY)); - x = lastEndX+b; - y = graphHeight; - } - if (lastEndY < 0) { // start bottom - double b = ((0 - y) * (x - lastEndX)/(lastEndY - y)); - lastEndX = x-b; - lastEndY = 0; - } - if (lastEndX < 0) { // start left - double b = ((0 - x) * (y - lastEndY)/(lastEndX - x)); - lastEndY = y-b; - lastEndX = 0; - } - if (lastEndY > graphHeight) { // start top - double b = ((graphHeight - y) * (x - lastEndX)/(lastEndY - y)); - lastEndX = x-b; - lastEndY = graphHeight; - } - - float startX = (float) lastEndX + (graphLeft + 1); - float startY = (float) (graphTop - lastEndY) + graphHeight; - float endX = (float) x + (graphLeft + 1); - float endY = (float) (graphTop - y) + graphHeight; - - // draw data point - if (mStyles.drawDataPoints) { - //fix: last value was not drawn. Draw here now the end values - canvas.drawCircle(endX, endY, mStyles.dataPointsRadius, mPaint); - } - registerDataPoint(endX, endY, value); - - mPath.reset(); - mPath.moveTo(startX, startY); - mPath.lineTo(endX, endY); - canvas.drawPath(mPath, paint); - if (mStyles.drawBackground) { - if (i==1) { - firstX = startX; - mPathBackground.moveTo(startX, startY); - } - mPathBackground.lineTo(endX, endY); - } - lastUsedEndX = endX; - } else if (mStyles.drawDataPoints) { - //fix: last value not drawn as datapoint. Draw first point here, and then on every step the end values (above) - float first_X = (float) x + (graphLeft + 1); - float first_Y = (float) (graphTop - y) + graphHeight; - //TODO canvas.drawCircle(first_X, first_Y, dataPointsRadius, mPaint); - } - lastEndY = orgY; - lastEndX = orgX; - i++; - } - - if (mStyles.drawBackground) { - // end / close path - mPathBackground.lineTo((float) lastUsedEndX, graphHeight + graphTop); - mPathBackground.lineTo(firstX, graphHeight + graphTop); - mPathBackground.close(); - canvas.drawPath(mPathBackground, mPaintBackground); - } - - } - - /** - * the thickness of the line. - * This option will be ignored if you are - * using a custom paint via {@link #setCustomPaint(android.graphics.Paint)} - * - * @return the thickness of the line - */ - public int getThickness() { - return mStyles.thickness; - } - - /** - * the thickness of the line. - * This option will be ignored if you are - * using a custom paint via {@link #setCustomPaint(android.graphics.Paint)} - * - * @param thickness thickness of the line - */ - public void setThickness(int thickness) { - mStyles.thickness = thickness; - } - - /** - * flag whether the area under the line to the bottom - * of the viewport will be filled with a - * specific background color. - * - * @return whether the background will be drawn - * @see #getBackgroundColor() - */ - public boolean isDrawBackground() { - return mStyles.drawBackground; - } - - /** - * flag whether the area under the line to the bottom - * of the viewport will be filled with a - * specific background color. - * - * @param drawBackground whether the background will be drawn - * @see #setBackgroundColor(int) - */ - public void setDrawBackground(boolean drawBackground) { - mStyles.drawBackground = drawBackground; - } - - /** - * flag whether the data points are highlighted as - * a visible point. - * - * @return flag whether the data points are highlighted - * @see #setDataPointsRadius(float) - */ - public boolean isDrawDataPoints() { - return mStyles.drawDataPoints; - } - - /** - * flag whether the data points are highlighted as - * a visible point. - * - * @param drawDataPoints flag whether the data points are highlighted - * @see #setDataPointsRadius(float) - */ - public void setDrawDataPoints(boolean drawDataPoints) { - mStyles.drawDataPoints = drawDataPoints; - } - - /** - * @return the radius for the data points. - * @see #setDrawDataPoints(boolean) - */ - public float getDataPointsRadius() { - return mStyles.dataPointsRadius; - } - - /** - * @param dataPointsRadius the radius for the data points. - * @see #setDrawDataPoints(boolean) - */ - public void setDataPointsRadius(float dataPointsRadius) { - mStyles.dataPointsRadius = dataPointsRadius; - } - - /** - * @return the background color for the filling under - * the line. - * @see #setDrawBackground(boolean) - */ - public int getBackgroundColor() { - return mStyles.backgroundColor; - } - - /** - * @param backgroundColor the background color for the filling under - * the line. - * @see #setDrawBackground(boolean) - */ - public void setBackgroundColor(int backgroundColor) { - mStyles.backgroundColor = backgroundColor; - } - - /** - * custom paint that can be used. - * this will ignore the thickness and color styles. - * - * @param customPaint the custom paint to be used for rendering the line - */ - public void setCustomPaint(Paint customPaint) { - this.mCustomPaint = customPaint; - } -} diff --git a/graphview/src/main/java/com/jjoe64/graphview/series/OnDataPointTapListener.java b/graphview/src/main/java/com/jjoe64/graphview/series/OnDataPointTapListener.java deleted file mode 100644 index 748a1122ee..0000000000 --- a/graphview/src/main/java/com/jjoe64/graphview/series/OnDataPointTapListener.java +++ /dev/null @@ -1,38 +0,0 @@ -/** - * GraphView - * Copyright (C) 2014 Jonas Gehring - * - * 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, - * with the "Linking Exception", which can be found at the license.txt - * file in this program. - * - * 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 - * with the "Linking Exception" along with this program; if not, - * write to the author Jonas Gehring . - */ -package com.jjoe64.graphview.series; - -/** - * Listener for the tap event which will be - * triggered when the user touches on a datapoint. - * - * Use this in {@link com.jjoe64.graphview.series.BaseSeries#setOnDataPointTapListener(OnDataPointTapListener)} - * - * @author jjoe64 - */ -public interface OnDataPointTapListener { - /** - * gets called when the user touches on a datapoint. - * - * @param series the corresponding series - * @param dataPoint the data point that was tapped on - */ - void onTap(Series series, DataPointInterface dataPoint); -} diff --git a/graphview/src/main/java/com/jjoe64/graphview/series/PointsGraphSeries.java b/graphview/src/main/java/com/jjoe64/graphview/series/PointsGraphSeries.java deleted file mode 100644 index c57d476d7b..0000000000 --- a/graphview/src/main/java/com/jjoe64/graphview/series/PointsGraphSeries.java +++ /dev/null @@ -1,312 +0,0 @@ -/** - * GraphView - * Copyright (C) 2014 Jonas Gehring - * - * 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, - * with the "Linking Exception", which can be found at the license.txt - * file in this program. - * - * 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 - * with the "Linking Exception" along with this program; if not, - * write to the author Jonas Gehring . - */ -package com.jjoe64.graphview.series; - -import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.Paint; -import android.graphics.Path; -import android.graphics.Point; - -import com.jjoe64.graphview.GraphView; - -import java.util.Iterator; - -/** - * Series that plots the data as points. - * The points can be different shapes or a - * complete custom drawing. - * - * @author jjoe64 - */ -public class PointsGraphSeries extends BaseSeries { - /** - * interface to implement a custom - * drawing for the data points. - */ - public static interface CustomShape { - /** - * called when drawing a single data point. - * use the x and y coordinates to render your - * drawing at this point. - * - * @param canvas canvas to draw on - * @param paint internal paint object. this has the correct color. - * But you can use your own paint. - * @param x x-coordinate the point has to be drawn to - * @param y y-coordinate the point has to be drawn to - * @param dataPoint the related data point - */ - void draw(Canvas canvas, Paint paint, float x, float y, DataPointInterface dataPoint); - } - - /** - * choose a predefined shape to render for - * each data point. - * You can also render a custom drawing via {@link com.jjoe64.graphview.series.PointsGraphSeries.CustomShape} - */ - public enum Shape { - /** - * draws a point / circle - */ - POINT, - - /** - * draws a triangle - */ - TRIANGLE, - - /** - * draws a rectangle - */ - RECTANGLE - } - - /** - * wrapped styles for this series - */ - private final class Styles { - /** - * this is used for the size of the shape that - * will be drawn. - * This is useless if you are using a custom shape. - */ - float size; - - /** - * the shape that will be drawn for each point. - */ - Shape shape; - } - - /** - * wrapped styles - */ - private Styles mStyles; - - /** - * internal paint object - */ - private Paint mPaint; - - /** - * handler to use a custom drawing - */ - private CustomShape mCustomShape; - - /** - * creates the series without data - */ - public PointsGraphSeries() { - init(); - } - - /** - * creates the series with data - * - * @param data datapoints - */ - public PointsGraphSeries(E[] data) { - super(data); - init(); - } - - /** - * inits the internal objects - * set the defaults - */ - protected void init() { - mStyles = new Styles(); - mStyles.size = 20f; - mPaint = new Paint(); - mPaint.setStrokeCap(Paint.Cap.ROUND); - setShape(Shape.POINT); - } - - /** - * plot the data to the viewport - * - * @param graphView graphview - * @param canvas canvas to draw on - * @param isSecondScale whether it is the second scale - */ - @Override - public void draw(GraphView graphView, Canvas canvas, boolean isSecondScale) { - resetDataPoints(); - - // get data - double maxX = graphView.getViewport().getMaxX(false); - double minX = graphView.getViewport().getMinX(false); - - double maxY; - double minY; - if (isSecondScale) { - maxY = graphView.getSecondScale().getMaxY(); - minY = graphView.getSecondScale().getMinY(); - } else { - maxY = graphView.getViewport().getMaxY(false); - minY = graphView.getViewport().getMinY(false); - } - - Iterator values = getValues(minX, maxX); - - // draw background - double lastEndY = 0; - double lastEndX = 0; - - // draw data - mPaint.setColor(getColor()); - - double diffY = maxY - minY; - double diffX = maxX - minX; - - float graphHeight = graphView.getGraphContentHeight(); - float graphWidth = graphView.getGraphContentWidth(); - float graphLeft = graphView.getGraphContentLeft(); - float graphTop = graphView.getGraphContentTop(); - - lastEndY = 0; - lastEndX = 0; - float firstX = 0; - int i=0; - while (values.hasNext()) { - E value = values.next(); - - double valY = value.getY() - minY; - double ratY = valY / diffY; - double y = graphHeight * ratY; - - double valX = value.getX() - minX; - double ratX = valX / diffX; - double x = graphWidth * ratX; - - double orgX = x; - double orgY = y; - - // overdraw - boolean overdraw = false; - if (x > graphWidth) { // end right - overdraw = true; - } - if (y < 0) { // end bottom - overdraw = true; - } - if (y > graphHeight) { // end top - overdraw = true; - } - - float endX = (float) x + (graphLeft + 1); - float endY = (float) (graphTop - y) + graphHeight; - registerDataPoint(endX, endY, value); - - // draw data point - if (!overdraw) { - if (mCustomShape != null) { - mCustomShape.draw(canvas, mPaint, endX, endY, value); - } else if (mStyles.shape == Shape.POINT) { - canvas.drawCircle(endX, endY, mStyles.size, mPaint); - } else if (mStyles.shape == Shape.RECTANGLE) { - canvas.drawRect(endX-mStyles.size, endY-mStyles.size, endX+mStyles.size, endY+mStyles.size, mPaint); - } else if (mStyles.shape == Shape.TRIANGLE) { - Point[] points = new Point[3]; - points[0] = new Point((int)endX, (int)(endY-getSize())); - points[1] = new Point((int)(endX+getSize()), (int)(endY+getSize()*0.67)); - points[2] = new Point((int)(endX-getSize()), (int)(endY+getSize()*0.67)); - drawArrows(points, canvas, mPaint); - } - } - - i++; - } - - } - - /** - * helper to render triangle - * - * @param point array with 3 coordinates - * @param canvas canvas to draw on - * @param paint paint object - */ - private void drawArrows(Point[] point, Canvas canvas, Paint paint) { - float [] points = new float[8]; - points[0] = point[0].x; - points[1] = point[0].y; - points[2] = point[1].x; - points[3] = point[1].y; - points[4] = point[2].x; - points[5] = point[2].y; - points[6] = point[0].x; - points[7] = point[0].y; - - canvas.drawVertices(Canvas.VertexMode.TRIANGLES, 8, points, 0, null, 0, null, 0, null, 0, 0, paint); - Path path = new Path(); - path.moveTo(point[0].x , point[0].y); - path.lineTo(point[1].x,point[1].y); - path.lineTo(point[2].x,point[2].y); - canvas.drawPath(path,paint); - } - - /** - * This is used for the size of the shape that - * will be drawn. - * This is useless if you are using a custom shape. - * - * @return the size of the shape - */ - public float getSize() { - return mStyles.size; - } - - /** - * This is used for the size of the shape that - * will be drawn. - * This is useless if you are using a custom shape. - * - * @param radius the size of the shape - */ - public void setSize(float radius) { - mStyles.size = radius; - } - - /** - * @return the shape that will be drawn for each point - */ - public Shape getShape() { - return mStyles.shape; - } - - /** - * @param s the shape that will be drawn for each point - */ - public void setShape(Shape s) { - mStyles.shape = s; - } - - /** - * Use a custom handler to render your own - * drawing for each data point. - * - * @param shape handler to use a custom drawing - */ - public void setCustomShape(CustomShape shape) { - mCustomShape = shape; - } -} diff --git a/graphview/src/main/java/com/jjoe64/graphview/series/Series.java b/graphview/src/main/java/com/jjoe64/graphview/series/Series.java deleted file mode 100644 index dce32eb78e..0000000000 --- a/graphview/src/main/java/com/jjoe64/graphview/series/Series.java +++ /dev/null @@ -1,125 +0,0 @@ -/** - * GraphView - * Copyright (C) 2014 Jonas Gehring - * - * 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, - * with the "Linking Exception", which can be found at the license.txt - * file in this program. - * - * 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 - * with the "Linking Exception" along with this program; if not, - * write to the author Jonas Gehring . - */ -package com.jjoe64.graphview.series; - -import android.graphics.Canvas; - -import com.jjoe64.graphview.GraphView; - -import java.util.Iterator; - -/** - * Basis interface for series that can be plotted - * on the graph. - * You can implement this in order to create a completely - * custom series type. - * But it is recommended to extend {@link com.jjoe64.graphview.series.BaseSeries} or another - * implemented Series class to save time. - * Anyway this interface can make sense if you want to implement - * a custom data provider, because BaseSeries uses a internal Array to store - * the data. - * - * @author jjoe64 - */ -public interface Series { - /** - * @return the lowest x-value of the data - */ - public double getLowestValueX(); - - /** - * @return the highest x-value of the data - */ - public double getHighestValueX(); - - /** - * @return the lowest y-value of the data - */ - public double getLowestValueY(); - - /** - * @return the highest y-value of the data - */ - public double getHighestValueY(); - - /** - * get the values for a specific range. It is - * important that the data comes in the sorted order - * (from lowest to highest x-value). - * - * @param from the minimal x-value - * @param until the maximal x-value - * @return all datapoints between the from and until x-value - * including the from and until data points. - */ - public Iterator getValues(double from, double until); - - /** - * Plots the series to the viewport. - * You have to care about overdrawing. - * This method may be called 2 times: one for - * the default scale and one time for the - * second scale. - * - * @param graphView corresponding graphview - * @param canvas canvas to draw on - * @param isSecondScale true if the drawing is for the second scale - */ - public void draw(GraphView graphView, Canvas canvas, boolean isSecondScale); - - /** - * @return the title of the series. Used in the legend - */ - public String getTitle(); - - /** - * @return the color of the series. Used in the legend and should - * be used for the plotted points or lines. - */ - public int getColor(); - - /** - * set a listener for tap on a data point. - * - * @param l listener - */ - public void setOnDataPointTapListener(OnDataPointTapListener l); - - /** - * called by the tap detector in order to trigger - * the on tap on datapoint event. - * - * @param x pixel - * @param y pixel - */ - void onTap(float x, float y); - - /** - * called when the series was added to a graph - * - * @param graphView graphview - */ - void onGraphViewAttached(GraphView graphView); - - /** - * @return whether there are data points - */ - boolean isEmpty(); -} diff --git a/graphview/src/main/res/values/attr.xml b/graphview/src/main/res/values/attr.xml deleted file mode 100644 index 8b73838605..0000000000 --- a/graphview/src/main/res/values/attr.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/iconify/src/main/java/com/joanzapata/iconify/IconDrawable.java b/iconify/src/main/java/com/joanzapata/iconify/IconDrawable.java deleted file mode 100644 index ea8783672e..0000000000 --- a/iconify/src/main/java/com/joanzapata/iconify/IconDrawable.java +++ /dev/null @@ -1,229 +0,0 @@ -package com.joanzapata.iconify; - -import android.annotation.SuppressLint; -import android.content.Context; -import android.graphics.*; -import android.graphics.drawable.Drawable; -import android.text.TextPaint; -import android.util.TypedValue; -import com.joanzapata.iconify.internal.IconFontDescriptorWrapper; - -import static android.util.TypedValue.COMPLEX_UNIT_DIP; - -/** - * Embed an icon into a Drawable that can be used as TextView icons, or ActionBar icons. - *
    - *     new IconDrawable(context, IconValue.icon_star)
    - *           .colorRes(R.color.white)
    - *           .actionBarSize();
    - * 
    - * If you don't set the size of the drawable, it will use the size - * that is given to him. Note that in an ActionBar, if you don't - * set the size explicitly it uses 0, so please use actionBarSize(). - */ -public class IconDrawable extends Drawable { - - public static final int ANDROID_ACTIONBAR_ICON_SIZE_DP = 24; - - private Context context; - - private Icon icon; - - private TextPaint paint; - - private int size = -1; - - private int alpha = 255; - - /** - * Create an IconDrawable. - * @param context Your activity or application context. - * @param iconKey The icon key you want this drawable to display. - * @throws IllegalArgumentException if the key doesn't match any icon. - */ - public IconDrawable(Context context, String iconKey) { - Icon icon = Iconify.findIconForKey(iconKey); - if (icon == null) { - throw new IllegalArgumentException("No icon with that key \"" + iconKey + "\"."); - } - init(context, icon); - } - - /** - * Create an IconDrawable. - * @param context Your activity or application context. - * @param icon The icon you want this drawable to display. - */ - public IconDrawable(Context context, Icon icon) { - init(context, icon); - } - - private void init(Context context, Icon icon) { - this.context = context; - this.icon = icon; - paint = new TextPaint(); - IconFontDescriptorWrapper descriptor = Iconify.findTypefaceOf(icon); - if (descriptor == null) { - throw new IllegalStateException("Unable to find the module associated " + - "with icon " + icon.key() + ", have you registered the module " + - "you are trying to use with Iconify.with(...) in your Application?"); - } - paint.setTypeface(descriptor.getTypeface(context)); - paint.setStyle(Paint.Style.FILL); - paint.setTextAlign(Paint.Align.CENTER); - paint.setUnderlineText(false); - paint.setColor(Color.BLACK); - paint.setAntiAlias(true); - } - - /** - * Set the size of this icon to the standard Android ActionBar. - * @return The current IconDrawable for chaining. - */ - public IconDrawable actionBarSize() { - return sizeDp(ANDROID_ACTIONBAR_ICON_SIZE_DP); - } - - /** - * Set the size of the drawable. - * @param dimenRes The dimension resource. - * @return The current IconDrawable for chaining. - */ - public IconDrawable sizeRes(int dimenRes) { - return sizePx(context.getResources().getDimensionPixelSize(dimenRes)); - } - - /** - * Set the size of the drawable. - * @param size The size in density-independent pixels (dp). - * @return The current IconDrawable for chaining. - */ - public IconDrawable sizeDp(int size) { - return sizePx(convertDpToPx(context, size)); - } - - /** - * Set the size of the drawable. - * @param size The size in pixels (px). - * @return The current IconDrawable for chaining. - */ - public IconDrawable sizePx(int size) { - this.size = size; - setBounds(0, 0, size, size); - invalidateSelf(); - return this; - } - - /** - * Set the color of the drawable. - * @param color The color, usually from android.graphics.Color or 0xFF012345. - * @return The current IconDrawable for chaining. - */ - public IconDrawable color(int color) { - paint.setColor(color); - invalidateSelf(); - return this; - } - - /** - * Set the color of the drawable. - * @param colorRes The color resource, from your R file. - * @return The current IconDrawable for chaining. - */ - public IconDrawable colorRes(int colorRes) { - paint.setColor(context.getColor(colorRes)); - invalidateSelf(); - return this; - } - - /** - * Set the alpha of this drawable. - * @param alpha The alpha, between 0 (transparent) and 255 (opaque). - * @return The current IconDrawable for chaining. - */ - public IconDrawable alpha(int alpha) { - setAlpha(alpha); - invalidateSelf(); - return this; - } - - @Override - public int getIntrinsicHeight() { - return size; - } - - @Override - public int getIntrinsicWidth() { - return size; - } - - @Override - public void draw(Canvas canvas) { - Rect bounds = getBounds(); - int height = bounds.height(); - paint.setTextSize(height); - Rect textBounds = new Rect(); - String textValue = String.valueOf(icon.character()); - paint.getTextBounds(textValue, 0, 1, textBounds); - int textHeight = textBounds.height(); - float textBottom = bounds.top + (height - textHeight) / 2f + textHeight - textBounds.bottom; - canvas.drawText(textValue, bounds.exactCenterX(), textBottom, paint); - } - - @Override - public boolean isStateful() { - return true; - } - - @Override - public boolean setState(int[] stateSet) { - int oldValue = paint.getAlpha(); - int newValue = isEnabled(stateSet) ? alpha : alpha / 2; - paint.setAlpha(newValue); - return oldValue != newValue; - } - - @Override - public void setAlpha(int alpha) { - this.alpha = alpha; - paint.setAlpha(alpha); - } - - @Override - public void setColorFilter(ColorFilter cf) { - paint.setColorFilter(cf); - } - - @Override - public void clearColorFilter() { - paint.setColorFilter(null); - } - - @SuppressLint("WrongConstant") @Override - public int getOpacity() { - return this.alpha; - } - - /** - * Sets paint style. - * @param style to be applied - */ - public void setStyle(Paint.Style style) { - paint.setStyle(style); - } - - // Util - private boolean isEnabled(int[] stateSet) { - for (int state : stateSet) - if (state == android.R.attr.state_enabled) - return true; - return false; - } - - // Util - private int convertDpToPx(Context context, float dp) { - return (int) TypedValue.applyDimension( - COMPLEX_UNIT_DIP, dp, - context.getResources().getDisplayMetrics()); - } -} \ No newline at end of file diff --git a/libs/graphview.aar b/libs/graphview.aar new file mode 100644 index 0000000000000000000000000000000000000000..f3d0206ceebf549858dade47e8069709509a96d5 GIT binary patch literal 62697 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 literal 0 HcmV?d00001 diff --git a/app/libs/ustwo-clockwise-debug.aar b/libs/ustwo-clockwise-debug.aar similarity index 100% rename from app/libs/ustwo-clockwise-debug.aar rename to libs/ustwo-clockwise-debug.aar diff --git a/app/libs/wearpreferenceactivity-0.5.0.aar b/libs/wearpreferenceactivity-0.5.0.aar similarity index 100% rename from app/libs/wearpreferenceactivity-0.5.0.aar rename to libs/wearpreferenceactivity-0.5.0.aar diff --git a/settings.gradle b/settings.gradle index b11f11adfc..d34737dfb6 100644 --- a/settings.gradle +++ b/settings.gradle @@ -18,5 +18,4 @@ include ':diaconn' include ':openhumans' include ':shared' -include ':iconify' -include ':graphview' +include ':iconify' \ No newline at end of file diff --git a/wear/build.gradle b/wear/build.gradle index f530c47ebe..e02bd28f66 100644 --- a/wear/build.gradle +++ b/wear/build.gradle @@ -107,8 +107,8 @@ dependencies { compileOnly "com.google.android.wearable:wearable:$wearable_version" implementation "com.google.android.support:wearable:$wearable_version" implementation "com.google.android.gms:play-services-wearable:$play_services_wearable_version" - implementation(files('libs/ustwo-clockwise-debug.aar')) - implementation(files('libs/wearpreferenceactivity-0.5.0.aar')) + implementation(files("${rootProject.rootDir}/libs/ustwo-clockwise-debug.aar")) + implementation(files("${rootProject.rootDir}/libs/wearpreferenceactivity-0.5.0.aar")) implementation('com.github.lecho:hellocharts-library:1.5.8@aar') implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutines_version" diff --git a/wear/libs/ustwo-clockwise-debug.aar b/wear/libs/ustwo-clockwise-debug.aar deleted file mode 100644 index 8257a991be6e1ff5490cafa8f66372d72b49e003..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 90266 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 z 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 U0-rk*OpE&|;B3@zHvoYD10>zvIRF3v diff --git a/wear/libs/wearpreferenceactivity-0.5.0.aar b/wear/libs/wearpreferenceactivity-0.5.0.aar deleted file mode 100644 index 66b3cad816ca298c74a9f5c4d8c11fb1bfde068b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 30815 zcmV((K;XYnO9KQ7000OG07zgyM`mz~vIYSF09yh8022TJ06}hKa&Kv5O<`_nW@U49 zE_iKhtx~~GgfI}jlkg9u_ib0b7*be`d-Pzk#`~llpn(?B4k&+bHw)rMPsTZQUf+9t zGpRg$*e;vU2h+QP-S9oj(5YSSi(p|{GY2O=Nu!d;Jg$Sxa z3xO{KKJnGDS=E7Rnnkz>HH z3%b3u=UPm2YurhxcGY+w%q*L z1bS-vKA?YGXZT%8aamFOu=auG4#n#kxDxSBA%w1Na9IEd0efKC2j*LZ&cIDZZO ze*@)(6{ID^Rn-{eC4S3KP0GpAGt48(($maL%{HqrEwSz$9~^`Io3sD@Rq(%N3;d7o z{ci}|e?v@xb~ZrI|4$^zKalP~fRlq0&F+AHYxu&buUz~Kybp%jf`2u-irY$&pqsUDu=t*FfR|(XVedh*+(A0 z8%iKjq(^O#hjJFxpoHr&DC8MfyM;1Zf*Ud{(ruSnDzaFkUO&wOSiY#_1-4CG;>Px^ z8gS;1aEg?9Sn3>Kp459$#OjOOFVU>EMzcM^Xm>iz!ywAEI*Bu|n)n}0oh=WvpN$i3 zEcPpkT0SLni zO>f{k#@JtW_tci;DjrfS!bSm)ES8)TRcj(_6;|F#$jC+CYt{-CG-a*Yc!U3crv?09 z`=5FI4+3D^20S(Y#fCzHfRO(aDr)IuVgppQ^a2XoS=s_zEbZ<7HRWo*6ws8B`8P4G zCRGH)Y3@l{8uU|&4ycBP64*#8hd|E1HlKp9Hn+_>L(kq{K*)n41${t>COodMRp6o~ z@-une&1Z7B-8YB?dwrpAA@D2_jF>~&(pnl@s7nAQ+S$+>d`o8H*~X~V(tIpTN~#96 zB$pTz@rX5Pp{mb%>Wn{lYtt@YhfS#Ylx@cjP|%{X0rwiJ_)_|fc1Bj^hc8vsKXb^I zHKu$Hx#}2wj(Kds+jl|4pPd~yU4QV#LJ7+mdO$DiqPQ*tQk6*?Ic_>Q|KLq1e_DfE zugFuU!C`<$%Kv0QL`Y2~`^h_9_3-2p893jhixwMon-W(%8S-V&c&^ueJnnlgT~0~1 zrT-WSUS-Av5_wvu@D-<*wUE9;fiogjz5LR43R~WN;{|991XeE*Q0_6zBu1yJu0FyH zPLSA4+QJyFJg+75yh5dYq6Oi4C1#8>MS?iWk|ABc6+P|OT8JN(yKl#7CR3CKj|(F7 zIDL-`iDaJ2W@5H5#wMebU``q`Nv>`)JD|?X^r%BK*ei|HBo55%os{mc$0?-(9-GzA zQrXK2cJc3)hG5i$jE7!=&S-~Trd;{nA_Ru4s&cwGVB{A$`Lp(aK9Qf)`X6G^WCVW_ zfID7D+X|CQ{CN<4DAuuh%h2E*J8%Nz+}TQ~zj6I(`-2hI?Gu%e%PX#zJtVEfF3cM! zu)$2Om28~SPau)pV)R^)r;G`sKmzT%kZaO5=}dz;4=P;dz-U#kl~XXwux5xX$cFWF zjuyU*s!!)1)&u`7vtSmTxqRK4^2|1TczZ|N4-eMMJd=5H0&t&usQ-}zNlFNHZIZ`f ziUJ>n>AK4Lu$j+HK$z@yX0QCsw`n$k2Ue^rfujCPjQSciTQ}w^APa|7VhK}Z|996E zB-*h*=s!t6`k1H8_m}W$e-{VwKS?hI1ejXdnaf!^yJ%Pf-T#~Lxf<4*XqxET$q*6) z#0+97;qf{(BwB~>p*m!6Wst2xjT<_=w_}n%8+l@%8zUF{1ndAu%(}Kq;H&>7CJ|PBSdT+amtp+BV zKffJJEz*{X4a9NIa^~tfbPaP~XO2c^HdKKdD=hi2*ZUl&sqZT| zhvOx&Ov(qvX75bd?j5W|QiOR`?x%>un@t=@@tt*)sQ%GOoHQZM&e9U7 zpSRh9b7p{tN~tEl8x;ti$2+m`{=<0}zGaD$8Cc%kVW?rEv(~{Gd4HtpJ^tgC0gApW zxR`~9L|!SV7k#${gy+-cfh>Gy%5EqSjLm(dxduq*11>`a^m28yNRR zvb|eAJ^ly^e6)kS2d33a6t8FH*kMn1$sh*2Kolle2tG1o#NDKxHHP=De``y6I}LF*l9kh4QyI#XR3OkIDi zQPeq_&N+GflH-A>wezB%cW0F-TX~+;wPIGMX4Qk%DP9U09>gHG0Z846-Z+je4;BMr zKPoXyWpEK~x2L6e5q+`Vb%%9fp)(xClSX?PK>X0&@MK&gJhZ{5_6vCWxTT5ld4p8x zb;=s>wsDtadY=!n3nS=robk%K9s_&_vy~(qgg~1^V?IMaBwv_7%GVNqYpwWWL1D$K zim^l{{{^uer3`!!P+(EHg=hXWF?|8$iST#8$XrFm50eIqo)by1ha!g{fEAXMBQ93u zn${}h8N?`D*z$pYS18;_*A}0&9*25mEyzAJL0NA%lM-3UOmL+%&1KGvnQ=-1q^K?S zdHOM?I3&vYlpWB|ihF{#f6(ase%&?VXt*S5=@#P1ziBFx9)-V}`I@l@&%K0w5*t5? z;0R%$bjqm2KUF4(kHQ0tMB?7p4Egx8$RuUs+ZoNqw~1`olm#+8RpvV=lp$VqQ~nW} z6mm5x2(;TLltL=OTT7p9kP=h{X>os)!ar{P!U8KRF1cSq^T?yDCVs!!zeaLA9c=5H z6)$syUCo@~s-t}45)(EAmX>%WOWRU8#^(79aLo|A`eKc8D#;z?+G*&bo+B;#@AV-e zKGbPa;bq$2o<-pub>E%YC8-?oX9eWN7irhKW~@{s6yp)cKnzF`+x_c3jR}h{fo3;O8+YE;a{Wtr}F-n;)&}W!Br)je4xvSShlzq$L_V}ol_8a;^I*p! z>=TWowj0JdK}6blH@F!^>%;#8qZ~p=B9M`t+ND8(CZT)$!NNJxs4rDdb9}QXq87xY zXQ_=_{s0^m;4Ft>Ch67@Xa>@AzbofX!34*y*rb;_#1W8GT53~2P;3&xr{ytyB1x+t zjw$_0x`zH|peQdK1XvItAgF&I@&Eq^s`P)q7PoV8vIIH{ zn*tnMfKLA%f2oG8GpZ)K9|JPx*@ltLhL%YdO)Eqeh?Tr$O%yo|{lZeoT1HQsb{&BB zdI2as#dwkK*T8DIP|1oT_$cbH_KQ26z?4p^XmD!%-D$=-|F;|C*T?%RXaGEuNrVZB z%z+ca=v`4!O3V-<;kIe0E}e|c2Ss!}TMGLYCXBahb?O+L zTzYn$53#;wHjC;H9fQVeF8Xs!x!H}Tu3X54ZkzQqckw+Nr;K|!bz(IaD;1*_oAssz zzZQ~hb(3OYS`*+X8?O3btH(8CSVlzOkQ0{2_>-RO4>#`PaA(TK&%NhO&? z10>8ak$gDLS4FgliW>CozLSV5Lt=aOU zk1U%D4(riz7yAAqIcs!xVClhYY!S(ldEa=1hzDq^dFA1vzt1n-ACc`~aXmIIQf$!R z@hWVRb+(xp{=CwuC(=x|@B1-iiLmNh5z2YbyWpaeX3qViuMuOGiD-u-i{`Nk6<%#L zWnaD$pN7XaPKk+Ib)kWd>C2z$#|i7EM(hKtdr7+qN6BWo&+&Il{J6?}yui}JWx*O2 z=PE(4_+~mMF!L}xCC@r8+tn0Ky6dz^0Uc*SJl(K3ccwZM1S$^~$-Se#AV4SvV)c?t zeXiEXi-shaI-Ac&X$vO{vShczb5tFXepms1M((%9vS#Y4AYz_mp}Oo_TY9ql;xeNC~X~eZ?2I~NQ_cuNOF{Z@Hf@Q$XTVE!n7Ne zNwP>Bae75>Ir^X+MSA&aONcyGE)iUTc_v!6slZ`pIHMb#0Elkj81d!16R1ePgl`&j zZ#VjTIDccj+shCM)d!v9273q(*it@RV)|%AC zF%S^^YY*-KSsFWpB!nDFr8t7Pp+7aYKX66AWO%1xM_q$9*t_&O^Fb>6hh=u&c_N3c zXp}gNZIXd>_Rh2KuI*>oC(my;AS~Tv!hoW^`OeJL`-#WzowxNbflr8lS`P*oB8AWf zf^y*0goidvtcNEEwOiD!8Yz?c=kMag?Z@K0ri`XD~HFRv|($fPR z$v)-oM4}Ru#B7J=(3a);TOrzW$Xwvh-$%S_L%f;3SV+$-?{-kX3H`i@ns0zlzI+hLwLNG)_;ii1Zec_@Jhh(5WYyt71w~jtVI7kP{By#(z(=TiSi!tH1+Jpk5m_%k3 z&*Lv$F^pp#VnIn9^3w{qR)qx^7^&>xT?-$DkYSY=dP?`%Os&yQAHTi6$tr+ ziWri4n6jkHCB_UwiSn`Wu=$3PC#$Kn<4-i&oX15o5vDtfP;nKlno)hcR#+&cEMc2Y zyod`AEijgjQmo=F+%eZB!LITF1>{RZSr0Z`G98};9hJPDV^(Y&5*o=GaXH!?71kbE)yc}1}au^1< z7_if`Z=KsycXAos?T}t>&i`EZx@cmZMEzw9Hf>q(!_b_%uiw zJ1tHSYV#KtHI&R-^mNb-H1Hfl9-?puQt*ONNdUh;CPkR@f7(zv%Bb2Z7#d5gDo}j$ z#xJ*xp_F2^(XyHeP38HuM$_IJmjM=UQ`-dG~g;@l$_?RZPP-vl=&UtB~yzKXk zjYRemKN7Vr?J*Bgg@8GEs;Z7TM!@vud5T-?SeH;v8W->B8r z5q1aS7+3waaA88WW!-L;(r0zOl+}V&d$L@UgZM}kgfq%v-A;MP-J<=nOeBY0LGWE? z>30~A-1tfb`vEaj6>pO;Ec{+^A9K!dc|)qG9QxV=*@w{j0a~kcx*m zkh=pvG598>dV_ChUI=^Yi9>VY?%P!Rk#0pHy%RMW;=_sdN8z4KltvWMKhOn}8|sqQ zTL2cxRm?_;nB~JOV^*G$r-VL(2?pPl45QX|pdu~0L`kvo$r3Gw%VAn?u}%K8htRvW zc8ijsV(3#ES1sz0P?EHlsJxoCTWGIFcq0#)eu(rl-qGUpYTV*|DD>alLF4$V-BNvM zj~JqVVEC)vy3gDSw)L&3QHU@3#i41=M1~M(w$Tr37%Q-P>i*OlkW8xx6oBRAzmXsY z90GHN4+e-ioQMrw-U}_v(;a1&x$QYtn3QgOWR?>QA1bO%%vI@mqZ#QA^HGf#bx6xI z!#HS*bfu#%D1zKN3uko1FW5d=8f!>7_6fymmza|4Jl&(;i!8rcJM@?Q$7eP7vp|?72`?A*axJpkc*{Kreo0_#n zo5iI)X$A}ucG9x#%v%%)v-E}Qb2h9szzHDsMOJ5=oeje|`AgaT{ zK_1hEP3UY2xAaAnYt(t(`?gTS&D!L8ingNsA5~BVPov$h-k_`s7%Kte+~dyLr2LKTr?bAORh%CI`OxUX3G<_|lW0au@- z-!6GFUxP(kLyG>3%^=jSX2caeK)|JlNdM?%IRqFh&N@(-xrFQSU zUOgU=Bj_|nmXraKen40Y@Zg<|YL!L-dNZ)=oy44CL!n=?M^v}yCIU*@MeN(uI$LEn zZ!DpWiuWw3PIL#grI1#Y?Sc-0E#F6Qs#KLuz||TB04>RRe;&-DIBFHtcj7qr>BV-X zdKuqd6(i&K9gBM7{P*#ChzSRRUup$k{;l|&nFNxrA%cKl6N7*d{bz&nLGX-_(*JtZD)&z5_-^X%)a z(=0zH-=BTac$9fmlzS!KZEX?fbB55K{#|>g-}H7p)6?68Y|l)~-CI}3Cjv;@itirm zgVmy2e0|fwgkBMZ426%mylh2-IGKJuL9!by`Tiz3*f(7OL z4+wu6Lh?$9s2>go_WVc<-lYMzxXsUcu>6XGzy++f=b~^w2}Z}8)2)x7>~zm-jBA3U zlXO0xWzppaT+ZHu6IVOG+Bti^mJ~l-u@l7*NqeRxPT6yMuk37hj~8B~ZrR?`6Qr$= z#M93qy3eaZoCmDX&xze$>IZkE)&)Vn2gNrm#@?$C9kyEdDmz%$mX_L!YnVC&+p`v_ zPR%;3qiOwdmZWOcpkIZp{dLG@ns#Ri1Tb9EO6Ni%H_K>VbTw3v)AozTiDFw35CeBw zGV!Hgx!J8DgxdKOR!}R1WSEW$a9P`F$7pLM){$4n-%FoOl7EsB$m0C*`u<~D3YTA* zA3J(jRc9g~CcDET}v(1R3X?&?^`n+JEf0bCCb5d_WU1?K)&!Z=%?ZCDTgoH6-; zGC%6Y>f1wXM&C?PN@5h4Wo)-=C(8MvY9NEJyIW^mi6~%zRWLL1o-01V^gV=gW~VPexN`={#^*hb;w>y^CC$KdG~9 zUvsn3By6fps^7WD%u3SWv(EAMdoe$+rPx>PL+*4AP_zoVrX$XOoqrPY&ks`u|FFGl zz}&8&Kp@mj4y~Mq;yZLbuwqT3elpMIz*a0@GX^C0F4nykK`tM^`|;;2*AL=y3oCKj zArG10L+`mQt?6vQpQoT8La0`T%qJ_H=XO{IkRr0GlsBG35d3L;3D*k1o%iNd4TiC! zgXK9nbWEy|3DS8-7Y82L|rk9`6^M0FV^QJz{z<4WZta8>Qc%R_X=WgL7ap_T^$|=i~N-p*fLm-aLYDwVnTA8CZdN3Ex5->~?WllG)-} z`+J=zc@p=rbeBK^Oy3k}fw$eQR5n3zN}pYfqRt?A~m{IXdAJ zhf!}63zR8eu%z+LdNmkQOJ7tFN3SZ9YQs>jfkWboR4Jq}Mpbxin2X~9UvYIT4l`!X ziLOYKyO35-iF6W<7TlmHU07d=)gnCfQ#nT~saz7xBBfDd)|OKqQd5^hOO2{fIad)G zS7J4*)1a0kpb@VSMOm(@Kw(we$6!@gmhFzpD2-Y-j-xqg%KF`wIV+#5RS*@rr8^WG z?^acmSEmu0?^an9=pEYvzi#xi7JcHD2E{# zS*-3D!=-^SZDGN=T@ZDsHFO82xp@SrmRgkdjzBxR&`WhzY^G5_7Z$Hk5|7rZj8gBD z``G|j8{;XFm#OY#4aj}9Z=}g))cqXZGguy?;U165fih)Kb z=Ro+!$UJi@Go!>#D{fFv`=V6`K^TUGQLa(tW_e+L35GZ*End|SU*-60`mV_Gmr@O9 zRpxjXC&~@c^XIe-PN;A*=?YBM%_?|XTG3m)D!g$}i(U=MlW!(=KZ5J&q@7@MdhmeSN{1vF{Nu+l+C(W_ zZ@~6UIM_!7^jY=0ycDw`Q&{xIOEM|Aw~@-+8JJL!V!ZF=71}wpP1*ZyzaKYk)7G|O zwf$Y5M+VQycMFKXqLtYY@Kh&G_|m39dunE=WB)0 zk5V@}ID@=0QDZ&BR-?6k`vMJc!tUP$+i3Uj&+GU3 zhU*<+8rX2_?eZWxVn6EGitgzTpmvknMRHH7iCQJ5^9M0+7mFNROH~&-^60tvrynac zqe;lH3D#=%>&DJaDnZzA0;)j9;Dwwk5D!*lg#1(k5G!_| z)a*eIcG?}x14G>Mj=S(cW54t^#)@d+!z*0$9ORh6wyD*?T!2EH7-wbR`t)K2{2=d!U&x(~(O3Qv#JCcjc6^X$(?*+mB6I1)bwgOkp5xXhuT-)%D zdFgu(W=P`6h@F-#qWcH#4Y1N%@MaVlyl1`nWkdz(xGZe6qRltu+>%(Y)zM{Bn@#a2 z&gI{7A!DH}8VVg5=|i|#Th6q8_KtL?`y?@lKFUp?QFiBO^&N((Jj81p>6i$IeLG}2 z=r?v&8u|nBoQ|>D#_GZqTuC=Ghq6_v!Qw*hehXhITXX$^!2U{9R$7i2A8vsIU20+# zn4_}h0=eKqFh(BYQbwFAA3ADAno2KpUnL2r3l+7@v{lKZjV*x}Fh3LC%@EvMKUBDl zXulhh*?9)G5#Hbp2Y7~~8#fa~cDP7zp5>hB`(Ka$U89aa@iK% zO`srK@8^`SzX*e|c3r0~v*tZ9j(=}}8*s+;SI-`}(kEnR8U@?VLd{f5sf-R}C4V5P zG)Fhp$b0+Y)bxTebN1(P#sg+&$EyWzuHU-|&}omhMb!Y(+%w`V#iyf#^vKS%bz7VG zcS?Xr$2%eqx{vJ@eVt%*w-248RRsFe69_wnDJ7=-JAyR_D!af=d@NvWZ12c`Aq3*9 zSld1Ox;&>=6479K?7{_%h<%vG-X(jufsjT2&nJ*+!7FKUUl-a0IgM=v)pq}k))=2= z8oUla3k4U>Z@>8G{2AqZh$avrh)LY%k?BjJ9YguWs?U^1mp81c=VS~M3{G|k#e!s? zdLj%05u7sLXGFCDG6MOpFMJM(o|TX;mRpM+%d zgr-dQvo&_&v(Fr}6cxE@^vp$TMA368NmfYG?85LbKc+dIqb50iuKT6-n1#-YYP7j! zNFd!xPAiQirgnQHYmIT?@SMACwYkL!tnXvnSh5lf4(bq^v1`TX=AMDzgXELw&J>jI z2be2O@x}>I;Ex^Jmb)fm!#+C$nYBT zHk;U}Ar;paPr(<*T#Kss*ym59c0+Op;b@kjJy&woEX(W1!j zDV`;76H&qgV(}rzTVeLlov_>g)9(w`h>#YShq-X9B#f&JwJmkX77?lm_9!W1C7~}S z>4u!Cx)8jqsk3@a59}HHVc@cMI`x-|U$fGhkrdYMZ*V)Ft=f#zxpX^VBuVP?7V(jt z)`@~TQwzPUW|$(5vBZxlyJ5!?rXg!#sT1G{%rl9}t3(miL_DDRR@Wih-dZ=s{;is_FSKWq^z!JW_ z1U9Ua#-NkN)Oa&6QJx8>1OTpFJ~e~h&6z7Ys8b#Rxi_2z))e&@y2k1>fESjz=Bi*G zueoXyWKo{jbAiDj|CGtO?Us!?GWos0I7m8{7!IoD6Rx6x+=_*PLW(ERb7gt7OBNUl zQyfE|8X3bi97pUoK4-5XxHVCN_}dIpgLyClS*?{j2t`-EL1(Lt(r?qK8?>Z)&Z7Ha z2*DChrf_Lk)YEUA7j|F6h89PieBZzCA^48G`cCz9iz#N$$&SkgJxwR(FHhkDoUk%i z)fZS5c`IUzv2|J0So1h%`e$kVun9XQQGR!BF)B~0lc@#lk@qDyW@d`;&e(RY4;U>! zKh4Ai02bPuRGWb@^GMs$nqY$<9dq)0@+$B;=Rhm0$fv{+@|lRACvkl};*@{HHZNrK zsH!+;z}tlR>fGA)KOmXC0*1F9P;{KwhZbttTYSYYpi%7?#>rZ~cvh@>=Nt2~NtEkJ zQnM@fvt+QM7h>mXAuLkFsA{NX=EODCI8R_6!3x!xSRU07u`gWE1|fzDcqEx*`dF3u z%I0FlQ)9ls|4IAku8aW9zqBWU00ANUC+*cNU2K4+nn1w6HH$)3^cB&B(fJpVe$Yo- z$i99TK@r|8s@WAVk}HXnPPU3kcq_x{5asQdTG_Uw6_hPbcro(1?P`{{vqB%~hOJMW_`}&~pp;sbnj89!Tp`^{ zFPP}q+MU1+J7$zR2nuuc?}UGrVB0av@g>Pzj3SK7v<@d!z1JCK*vU}jJtIAdHB3J9 zO&n$WWqZ>TN;$_TQ*Wn|h_tk+Yt!Flx=gC4N0P;x%lm`CZj#bOerMCVyB)~aFKy$5yYtK(7Vo(v|q z>#@&@E#0#3U~B%YD{^(NEKE^ak4YPAzM4E1z+LO^$=XXhQ;}8Pjf~!EtiL zcB?Rm*$!_iMSii%{7^uK15?RZ>#f`!fwkPGYt>m{5tERqh6rX9V*)LFCP8M#!z0GY zpiVlS1i;Z*m;!S_h$JdZW`Zd5OZGTxCDo@mOK##~sf=q^bXq1% zcPilVE4Ng`=u;zLp48Anae-8p+Bs3-oA_^feO{<{sc;B}c>xrP<}pbi3JBzsl3b?p z(6D_3op7VxCYMB!#(T&Sipp-{XHevZxsGH*TmpCTv}(JuqN-;g%VY{OYBLpEV+i*& zK;R-K3@f^uGDa99R%>PMtFHtWuTktiM=X*2q~fts!8MK@`n8g0Df` z8(lIL@#>i2Rv#s%-@ow^IF#!)3IhTXg!&)6{4YlS*FN3<$nw90Sku~DO%ua!R?o?d zk_wiboFqXgK!`Fqu(1GLG`h^P5FD1c&=MZqi-ns!e2RkK84AY03ZqN2`Y}vL_bw>C zB7hT?cl?;(CF@f42_Mm5$_ zKfPk9oFsSvHkwKdof7X4rV4Iv(_)mpOZhrMks%DWY%_*UXD$ z-J<>5`*!^NM!=~+>|qF*ZH{-TXYp4fc7!JUnA)VB=!bgL21d zbGY1FbBFg3OB`Mr7lHTMT&*3e^$nw(V{z6P8I|sD9`Q*Y{=%MA_?L1a=S;zb@XnG5 zWA}-AcmYU^?jL#B*YlG2in(L_t7MYm7Sxq7!I}LMM>=tYK z+|dGSabA#kr%2Me%BT-2s$NP1g46J=b=eeKlQzsyWtkHyWyeAcw%OEVc>%vF{X69Wmst@-mK@{?WRc^R%gMWY zx{7n=1KYa3G%Isg15C1!rSbP{+AmmyZpY4^Cw+I_H|X}Z;0 z0w2xcXEwc|XLg$*N9~HOhSo2LaTaZ%WHbiLx4K{gEyHdxI2B> zxKDuIu<*I^eP#Rw2f3_K;ejvWB7bzjuaf=Qm*}wG1N-LneAx8z<*s>v%c(S;%(S-; zwN~qG;Y3vK$>Ux^DtD@2NqPzfcjPIi3Kw5zlG*G~DJ5G~X3|M}6Xh1i zX}jUt0J*)6LvWA`wZdq~rK&#Hj+;l_#y5vkVM-2?_icmd^2M;_)TL(a`DHIEKKi&V zHw=GGWOp!T zSiYMr|CAQ2)I>E)l7kKb14WQKl?-Cxsmxptk0b1=W!Kj z5e1+lr+VhXC0e}Bd~(j#a-jWTibCUg z++)>y$}{1!Hh<(vsGj)bSRsE1qbII}Df&8G+oMC2p3XJdDq!B7V?yr*iD7WVMZ^1% z2=#@b=4cCxG3jy6RW)l8&w7-WRSS%BS?5Y~tw@A&#c$;l(NsR&#;ri)%ExNpyC=Ok zs^%ajeV>6!iT?RT5&ekh$AP6hkK!VM*grl7k!BbMjr3cXNqUy@E^5)+k#n=S!n_j! zs1@b3bXQFns};wjk(4Z9BZ)>R##*{+3_?F<#Q(*(8Q^R&%)0f6zri%}3HO>u+{-z3 zPyW6-c86!`f!9hTj67@1z7zYc$rtKb#TuGE@9>TPLnYMys?Qt5{K3G51(&4*NngxB zar#+xKvwMZ4n$JmfIBod<;kYezKqL z+vD?I0&xEw0Cj8CMyCD9fSNUIg(7)$ExvKxl&2Ludi6F)3jD(j|64aGF`!d)d(2PmK zvZ}feHw$BI%qQK_`V{XBrHT?PitGhmieub=$P1S;_DtoryWen|&*c7jy`1C(nbaC5 zj^?FCFg4n@%S!X6M#$7+%X~$U(ULM|4t>WdX-o;p9Rd$K3*-Wfsqg$Iv+LAjzp8pF zwW?v!a%~(1*IaGGg9#gAtt|1IX;7(gP=Dsv{z;%{S=iETcF<_>3j>T^zq1C`igT`r z=HbF8mgL9DGOT3-Y3dqWR_B2Kw7Wjwg+IKL{aV)fn$pC}@CVL)Y8vBH2xAJ_6=%?U zy_Y0vrVGy!0-CQn^ANW*Y~vfeDYpAlsB;7TH}LaluX-up`J%-9TB~b(n|lpSqau_-*d@4aR*`-ymGUIZefFFP|nzdXHn0vf$#)e zZ)W%~?wm}*re^MFo+1$$0Qd9VFsFd=1*#!GV(S4M6{3VcQ4gJZC-|#9`C&zfH$oBo zxGcx8ySLi8pA5S$9`j=kSxrBsWXVk%&rN%x@LuA(t;q+DmMAhju*}MIb4$mh+RiiH z5&ubM`)4=flD{Or0{M?UmVc7@-w%HCKH9GeKtVy7K-sxLxw%0pNI=!?eJm6msr{VF zQImi=zg-$C3Mg7?St?rUI#L@wN}6h#N|NcW8EO)ffbv`II?DMtnCf!bT}q1iKzD;; zkbpV{Yjg6w>QG0(!SUexeXkOo|ts0Gk10e);aY}wtdihQTETv3mV)>5BCMgsc zm>3ud3YHO=06Jy@FoIm5L$DD_0*`19u)-GR1O4`I_vxbVI&We~P2>lLdHLPq#u&mbV|Cin%c zndTb0kht^!fGjWt$rLa7OX{#ODikEwbm*}NyXoL1Qi~ooq1@PmG7N}8OOwa?k|STt z!ZPDPulnN^-p;wX53O47XZpqFdXR|?-pn;yi&ZhrZT*o7E)m<3Kw2=(mB`#dhdVBC1V6{7Cfzcw1~yy7@Lu$11n}u=IB?63 ziL*|PWs>d$;11`MaWl~~>8VjR=FQ2bOJUjU@y}ixb%fCdIXIU==rGz*!GhaZwpK)j zZ8>dq6i$0`8RqjAInb{dEH`)x1Jexd_~QATC?ae-->63Z)HUX)A>#e4Cc$N3#v8sk z&i0k|{gsOxhBX3wZHg(pYjR4Dr$KBq$+$x|1%cNtu-XdiC5HsgbI+o`k=|E#^_GIc z>P$RetCsbFD0^FEPZC3rdF%7a91~lx`bB)O{zV=#bGEq8;dKaN30XoJd=wrLZ*9ql zxDo(=iQ6+0687B~H_RcG#PdW!498u<{35EVQ{|3VTfn(UDcgRZ9@wZ1hrp54VwCx< zwO&kSsvbX+#Khb=sPHDDBwcE-JjvL?eoW~L2`ENNQ5C10gd=*s`uc_D6a>~khgTSt zq)D;8CB-*xe;0&&!yN=4cvP9l!2VcSO@*FnqZZ^J4>UVp6ahVWQt95MPIP?*t-db`c53YCxveRpM z+E0P&OAcPbBveefDvWRtC4@G9NqTm>Km<1T{%u0X1}P7`1-RS)lfw(-(UpMi`!pYU z0XHmmHFbM{u`43kf)r!jA(XVW7<&v%KdUlRUAy3US?oyzQ!Dh>o-PC*mlzY?uIM{S z3a?D>-)yYb@b0>}cwatW%{zvHZcadykS-~+hMZM9xF#8vEG|1q4!bx>4(mLgMA(#} zP;^1%%nx5WeFc45q-U`75i=LT-xqzNT)yzCB0PyF2);?KS#ztsj&5gbG&Br4_x_&y zV+>`i3IeZ+dMVSrZR*caqR~0*bW)(b9yr>yHMH@a=*trd&v_#_yAoK$fd;>R*MgA} z7{YaNb*KwT2nh~xV1f(El2*=Ar(u3UUB^X%Pxr4XL1N3o>*!&mzBmqp=*W6PG@|Rd zOXl{h5q+HCo01cRo}|h(33ZnENtXjM_`#z$@?m!^*gahy$uULt2M}(G+kd+oNl6F{ z6odi+S^fqBLiK+lVE+;<&Da0BBKb)DTAEvN_WEe|S5v&nj=@1qNlA0|_?_12_uS)c`1$r8F9?c1 zq=@ohtT8kU1D1hU3>|-$J4)sOmi}@-&ctw72{Vn3vQ9}70At96^83R>c*q#SHsd%O zH(!6hxf@JgJAci=~2ZFwB1e!Jo;VRZa30XPDh$^jZ;*wN`G!OGGCgK!A%5u<1 zLq^jkdmU_}EaT8(Bq&!cwQGBQ-WD-a%o9S*+e!nMui1lVS|?t19^#k z6Q!Y7#tiG;bI}P^R<1FHJ6HXM!J`^Md3FxU(QZoDCQo~6%!zD+RWqe1={r|e-kz8k za^3yTd1O1x@GvWL_C_mg&Zr}X$}58HTS!BU{fCNAOIMgxRm2&OFnV`KT;aQu*n!#3pPKQexbCQou%JGNkD8 z`ghG;OIq==GQ>^g2+s+}O3v*-t-f#K>c0F>mXlNRESq#%+ICue)5GFz`W&|9>YySz zLUefsmX|gP&*F05)8yw5dJ{02EHwdbmsu0Js3LS4L{~mP}h_EU)WO!%1E!ti~sn#{|OD1N;J0SP*tAwj@C@bWW z*5kZmJa8Mt=@d9^S$N2t9O>!Q4rf^(fp`)WVTXWpmLFpvUmtZC-yU@%-wm}3+>N%G z*gG}n5Oq7*AWt?NH2|wOV1HMh_E#ub2Ic#o3kM3(K?xtkkRh@fkNSm3w5e%R$6_;? z`HrBEs+J41JWfDzqr9sa=b_T`bHudMT;mAmmxBnb@Zh2vggrGFX&ihpmJ8&rW*7IJ zbtZ7#hnIF=q;aq`-nE$RbRK;nOq?xFcNCpx_oXRTX%K5APKY;<9;}4gRjER%t-R;b z;^wmL<<#!c#ZX-ih3}8uvjwlrJ7Sz3$R3wuV8_ISq%lk0`>oKC`HA@|eV>~-g5&k- z#30=unHmf^Hp3#wNtgH&#=rvETr_dVl;4FcLRk!~zchT7H`GIipRLdFAY%J#euaSL z$9&}TZ}+fW9@>Ps09nTGHPga0#qZ?+fr8(lcD03wpXhY5&^g+6Iog`&D1MbxU4E(b znhL2eU-Dk6AL}XpjzfP4dD(;kF)B<>e!{dG)|Qx+wdT%(-V(#=3%<6#o6R=>;4NmW zdM0~g{9wmtOoww>%h#QLWkax7_ePOqD8JZ_S}|A)o!Nv`^z=OjKa3{{jWW}>7N1Bq z47sJnD#*L&EifS85RO@@<90nOE5BHoQksd=2BR3!!st@-0E(-@lesUUQeI#>_9CR7 z+4^JJWa@aFV4}Of%i)oQrjhmAWgNOMWUzkC<;~NLUn(xjV@ua!)OO!tLWfvKyJ`9E?ix<-v$~od>uN0{pd&BrSD9wiP z+xyTM>TX1^uW-|5#4s45At_>dSWD>XC-jDqpT4cu@Vk$MP+h@ev^y>JA3tH<+J%fX z$&z#kL-B#FT&Ha5`eh%?^jI#X^z56%K^WJ`x_q7-E5fWaIIKW_f&EV?~rL zyNc!~d$&Lb6YyhKS|kEP>j|Kkjr&dVzrFXWZ(g@K!#!=%trR1%&o{2w&7z(p8FS$XnBV|W zAEL2nZCmlS=c@H&&2nY+d!_bsGqAU}jOaF*$2>VVCZ+uB+-pzhoM(RV>L7iJKFxpQ zR>y-$)QX>Q3oUZr4|O9R>d1D5tRs>)5(8OJC$OFl&n;XnJ;Kb@hC%XIxC>hvyL5_M zv=&i_x6A^qqbiopGea*3;!1cntRlTmZ!4~AA&hi#DL^i1vtmrsX5YCs*r$7!!#s4f znWfi@=}q77S&rO$T@9&Oa;4m@vAp%aqkxkUC1jbLaanVHikl#Z%vN6CkcLfV-$yoL z8cL_>6UhHMD049P{FMv{0hIg)ZR-e5BjEyRaUUCW86A}K#HJ^tjH{ypoA8*ef=gKw z2H!pmbXPba6zwUgaE9$#_1q-p1Sq}yxZ1PFmrOpQ*zX{?J|f%}$loY}-iu}Sm&-xF z=A0OE&78TzAIj?=m8sJ(tHIX#*^7#D4uvrx<;4idy=71qq9iT;Iq=Q2J|8 zhy{yW(w7zaWQ@H~JlO9Y;n8G3-HyW@#-dtnEe6-=*oxp4%seYij4I=BwialbeiiD> zt@P#eIk@DK(?TwhV$(H`qeA8=r4B!`hIP~k;p#(fYoa5wB233c*ZO919!qFh`5HBA zF1BYgU))Tl1p7#}$kYLcI!774)xudI#1$q@Ynm1|-I@y%hDKEmc(N^5nVB@6IRY}D1p+Fc zadkCc01BUlQ~5!o_d-meEb=jfZuzztO&ny zj?n7L6KI#CceFF^rIY|mLm|Du8-wdimJ>m<9NYZUjy?EO! z5ey+^^H!NKL*AxhG}=Ca+MtTS-vTz;!&dhI=b#H$-6P?c#*v*{%!;H8JyCWx1}LU_ z?rMZW`B1sm$E~DkJUE&&QNE}Jq_v6h8XwE7|4up$JZvi{n(S?#Hx?%THZ*WkIo!$}{u;Ff-+mjN&%**! zgevNb%G7z7WT`tB50O=ksd_E19Y+!P;DQ3Wo@O86S{ILV4;S z_O!b0L+p5u*tS}7Dhq2@GuvBOroXSjDgI6rlLfVWF)Sju878|$Mf)+JOUK8EZqIIuAa|_oSeQ!Rr72a zr`5{N(?t?|s+N*$L)x7kKAaWRZW+r`1FnF}8~8P_DT7Ae^4D!i^ZYI~8Ka>Q6{o|N zt`A$uNs@BW3=D0uGod8yqQ=q+<7UM`^^O|mm6tZ#SNqLokx6~=yB)hf?hr(#P&h6p zp~1>5{9NKA0IO=;kE?#P&&`yP+K44oCkvyfdl8?|sz(gYqIcmS8n!CyJbx$j4p(vF zf0c<#?#=`qx@Teb1~87pO>M@#7xdXQA-5@%2jXoFOB{Mzt9ZT3rK*mRuziG;u|sOM zSK;)F^rZ_3_U*Z<-n#gd+k7it8xp^bVv>jF9a6A&y-ko?t2SX|FKDM8oI@!Zep^iG@$&9Gz@n-FWov?ea-_*?xy{NIqFph;wP2S(tBIs zlh{nw9T!s=weJmcpNFO5o!e35{1~`?#p9fA!q6+vrFI$DwYGUF7__sUvnX#2$lS^O zNxWloS(*rxGt23*Mf$Rry&3N)hx27c1NPw)_Hz&x=cK)RyEz=?jkLB0NpkThw?j{x zEiKp*oJoujfJyPdy|yv=8I$pLoRKN%wK>K0UMWF9?&4ITuHIMWg){o(pSy1iK*4!K z5)WdHN#>1|!ik+~4lX9E85_R#Y0#yz7d$d{E|`~SUg z*~wwY1I^I4jJBNcuhIb}1cr zJ?kWFTk@XXi8Z0jz6XMEh-peN4kMnuUY!EOxo<0-EwLEsVwgEQVm~BlM1| zcM($RG9(+~{;-&Y5ujxqwe3X5d2W4*J#!Tuhwb9W;u^zeNmFXgEe3CWz#;aFJeH?; zLEA3QMpHvO&Ttu}?gnt8B#0TXaCETvf2Uw~-ug=aPNtsFN81j>}3U6F#N|lAc z4KaAn{%*G2J!+gsBKFR&69H}uRnZoW^hf~f7B-RQ7h)s-8kiSEukU*- z4&Z4x9lh9(cJ|*0oxr$|7PpAzUJ zhtacGP#}zgyC$vT-_@R}ZB$itnDlF!`$>LCKG>Gt67Qb!(K3(VD&Z*bO!w1Uch0AR zK|E)Ova+mg&23{7!?x4@{p;h0-Wwab8O>~g3rz=QFY37e1%&{7#&(ApQ8442Fj}Y0 z#p(`&P!rH6{m)@cc+p++J+=ctP=y7x{rd`QlZ9dGr@@U z#DZowF11ONEY1+cNf2$DQQG~aGUO6iY^v@l-1e*Y#ssO8U*P<1D8yX=)sBBC^BgisHP(_YMh2U9jdlxGrhq8kcLqo zaeBc;_+o%XI@Fhgj%LxkUZg?~TgnPZ2D+s_EVocZ1N%}ceGQBLZGi%-cr|%6_$K(P zvm>(}Y9HvfOqf#KNh*qXyws{s#8Gufaa-{kDLvh)tByvCFqa+GfO`P-ufLmZc1%TO zo=M=EbQaNSG>7>^Yc=PU0YB|5$b*Y8aIRGD^8H*|$(6BCQecx?;_=B@j9EwYnTYt=MLjL_f6$E2XLMsy1`md?wOod#`RqI z9wT02q*Z9_rB^_3$R8%LPFdRpQul4UZANSwRm+ainH%aO&?s5Yr5{6YW-sOI;l^`% z0%FDclDtRs+n|mPi!#Dlnf-iTOklfj@uQOguCaKu6XP3l7j52`;v9I))C0f-4X3QD z?JhP~-s4oLOkf*^U37%5k(n!om8RH5O2;$I=WqFz->8OoQ|#P~i#xHD0?8`?x2}ZN zkzrq~7s5V3zQxAyZh4~16lHE%^zW!n6z@)nXB9+vKpy&5NP-B>!bu>Om)Qu^twuFV zyRkbypCFxUMo>4R*noXq38vQI(-*$A+DQenzy(?i#=jr&Mlv zkl4g+*NRZHk>q&{J{3s#d4Y$LG88OP){@A<>p|Xte1JXIPoA)!1HTLbWHh&Zl^#l@ zTVGs9Q*|t>3+iyfq3N(LUcsm3>1NT>RezsbzSPS4(_K7B?g38EWCknSB()cRM-9Nt zx8nv zgfKo!BRkpn;SCi|xFuJCN6@Wv>B5Dm%G(zS}HD$;sa=hN$F3CQZ?ep{;H?85<88W3U1O!r)v`C*KK| zhsWF+fmi*sqoZE=d zF(h&7qJK+X=b3+z#r8?ay#<~J#EJMpFruVx6&`%z>Ik>GjafyMapmR2~snGS26{etJS17tiOpJz3N-2mr&6?_wuG}i{}RPj$Gh~s%O?g z?HY1)C)97>y;isCi^}k|w)>w$Xuj<3-g?h+;XYmxmM?1$Te%TiQEvVyA+WL8`X0b5 zx}bP9gYKO^B)KsXHvDd%fIWTpFa(;VS%HwWr<6tMOU*$(79`{{M=tgilF9dMR( zJ5Zn?v#i7*wWXp%xCAQn81mW6*Zfwa{9J+pH&6w6~u8BoZYu~!@)-@|RCxHkEv-xiR}tjF z;m4>qi|zJkaAIL++u4cZ7ua?*CKpZ9e>@3ATSA$JD6v?%KF=DwK4x}+biJ^b@CR+| zmecEuff%K3_I%EbD#!C0Vx@KIbLDbf*$?~LEoSh-)WC6kGo!;uET}zrG*;W2w;-!* zFH?)JL$B%621@YkBZ}w?Fmo^}Nwra12VS`zY%-{mW)58RBX(9FKMC+RcyiN*XX#l( zp`VsH_^9H(gT*)2)InM>(rTWEh&H3Wh;m0m--g8sCi_$^$MdxO zcw79t)IH`d%DJU^jx$0UOFIH<$!+(AMAm$KZ|>0_la$Xv6&2O7mY5!t5@l^jXS3^m zp3t4wxf|_-0;V_F48JK(NV^x$jmbVSuNIXqzt7`h0kWVTmAePOH`lFSu91Jq$r_QM zV~n^088l_I!At;s?6P^pt>Xq}GSxyePi}e&?=xrj#`jp0GLqRWVr85}EWAm&70Hf& z%PUL^I*ykuz&bUx9%v53=p0$9+C)MmAa?kH3-6SRCo>;*+DZD%fT&ZnaI7|-*LE*G z9@Vapw(8WG!9t&Ot9qU{C$d6uQwb&NhtGy(z+Oc%bN?-X`+%R>SA*Nnh^aZI?bO*? z*@?dLy(<)_%lV*fZkSbSw@Jf6R)$qRuDkG;__=dw)?3JfIbpOPtUj1 zwr14M+k-L4!QoD|UN|)nn#zEtw#LZ@W82G;H1*kxpQlS|9SnCh69nZy02I#K4DklS zIL#cexB1!~&&`-0kBm-jIhZ+MeC_?CYQbJsiAjU$8ERm;=Z^d<2-i3nEQf5OJG&*W z8&lEWzZC7Ka8^FNG(fXrQz~#y5M{AhmWi%ZmS$GH`)-)pu(5$-N6Kub1KlG;F_NH# za*7xDMWiP??u%kUGsNd7Pj;Nuwc#;$XYsXIK30SKlb1BxHOe~4AKX@var(qt*0u6j z;U=A<^LdDL7Mcw6D54wDi{8l3k(QHh2+$$S@4meqys2}uU70l}cz~u=3eKtJw1{#0 z)6(`Pzc$3ZCzMGnHOAc$JBya4(}Z_cM>c-HbSgi`(2h4_#&X(@5+)9YHs{ehutLCv z2p3Kn&-M>K%XEHKL-mZ0rh(cIxaqj6(H=O?-ad>YB+&*2AmVk0B`8-c27vI#Gd`a- zJ^%@r4)7Tc#Df7}1R*?%UkFHVrz$DVndn^F%SaV{P9_uds6xwxu=OOa8`pfgCpC|v zBA94f71${yzc@n;Xarpk_5>2ITl$d_Ysf3unnHw&j7fm`MCM4;FfFAf9Nb*xH=JG)0?v)F8Joz4c7q*(!)D}l?8-*mDiNWLLnBqz0D;70Cdvyd4CAA^jpK;%x`57RO{f6SE|jA<)M+84)Bs^D{MN4i z_7wVOJwkpHkgw00TY)WI=U2!UyZ*t2J!Ph9zh3!iAl@#^nkZ$5%Jq3xe2wu*4wVz5 z!|f0C@D`-*{DViW+_WzB{eAwWkc@ZeJGy=ksAyfSg6{em|L52$(qrldI-{RHFfo|j z;45k?C%kz>KKXS)+e-|uiU%HEP%)ULW`6veZiDnq3xEnwZBmLi#oO564+^6hRAF$I zIH4F&2i9-z%!$TD`%wjMRs<>jc+=V(ksvATH+-fPqaDF_(EPvdQp~Bo-VWvy3#}&A z5q7BNnm<#Y;heVnK3*XDn)dBgQ8rWl=k5+$5fN>(6Q$}MT;n%PnrU3rd0hjS> zxUmVqa{M`Wq~mk(Z%RC%`jEm|Teu?Z@V&QPqSspo`>8JjCWJWK%_-X;IAbB^_l%94 zz)>%Xh294H5wNQ5DD(mNmXtIva*S1%(x4&Rm1%@P*}~#xrY9neC#LnPIE>&5u2Aw! zmwAyyG;0^S6CXlVl-E{8R(Frj50=@YOyNX9+cR;HmhA?yMaKtP*ee{M=c$4eH0(L~ zp%}~_M;E60#>YmbOOC4U;2-ZgDgDrS3RI?gQnt!f$F{s;Ur&{NKX*2 zi{7YfFrFhjCo$}UEnQEQZj7yeLA&lI{#C!SbS3+<9B54plxq%h%-vZ8h6 zQ=0OGaKdS4fN5vA6}9CtDk?Bl+hkGM-GnZ=NzN$ivS#8+ve8FPsJQi6ZvWbGDdQBj z?XDEp@X%F*b!tw~AU?w;X#b%pjerma{_KMbc@+Z%w|mUo=i~Gd0vCXXaFw$hBA5KB z_pPT}BJwR|KmzJ3RkcQfoU08o+nBR~Fv2`jzM&^;kD6LP&A1<}S}1SIx%)I$AcwCCBRs zQ{Ttu-S;3_P#f0gRCDah=?T)^u&W2Dm;uEbAB!arxB#0oufSY}Pug_P-P$XwgqW2O zG@k__2)MvY7Y~r4`cL^sEG~E*wh!=$AD_((_O9q1wvPzLw0GUH*B|qWfk>k~`i()K z)A8z0L{|b$!$d@TK2#O~A$^ocxVh|eJ_U+T#iD>N=nvp`@^Ah5^sYpn)b9uw)i1+I zPOfC0_xlKt3(xf$q3MK>r`umgJ|1d6*EbQJ?}lT^$QEG-KA=5PY|x?s%WTGQkvY?&(*p)<9XrVznLpP(k@68>F$-70B7!J^Vi& z6_zpV#neYfkqo^1$8D|STPUj02vt)C#g7!q14)Iwm0k$Vl@B&v4FZ}g2XCxAw5pPi zwsk-ANWc`rbYls6^Ag!v)!b<8M}w9I%Vmim!xQs(9F7vgij4?!CLj%kw2y`J9PK9q zkB&Y`N8y&+E*=*~E8=h)P%k4C&`7Dv!&M8%qGVi&KRuG%2syoDp4yfotb1(As2G10 z>M`by1VKFJI;i-E63O;_2oUVEb2eEJR>Tvg#B=bd8WmaA`+)pvUwK)$O2VhfEA(Yu{^F48HiRi)vNj6FtA08} zxXZe&b7d%dijZ0dM;HRn;qwdM9_Y-FBHm8b(+*#uSdBbG@QbFZmxK@rFB&V479rlTe9jw+0qgN12f5R zg`4%fsp2W4f5=-xG53BLWb9`*j+9rx7=HqF?tz-suM9uH>#=o6Zeo`~?EsdsIJT(6 ziEdk@g=^R^K3%xP`RQNjbxI^CM=G8@!Y$6<>s!L*DL35s!-+78Iar{XGl6Yl={~0H zBZLN|@CkyWm23oE00lD3V*1Z0g^e$iiL{cO?hBh_6Pi(Wcgb*s9y}&|(M8@7$MGJ4 z2K0)&yS^_A{0M!xWGUfodaXumQ)!_@;5aPh3b#=4Kx=I}&Ye4UW>ye9x>4X#PGU1e zglS9byX{k&#OPTkUn$E2Sun)Of_&#}cIhU4FB0<#@#cSF#{Kx_L>Y%dTD^t{x6D>} zH&-g{a(X(}S|N_dClS&?rudCiT|So@Hvn25P4i`CuYG|e zVH|e7bLGW2nOP_BrVX;bqK5AJ7Ub0^nK?;=*?OJ4nw@v2iGNCkq23Z4kGH;}IGzN# zFqc0tN=Iok@Lj$k-CA&&?pv*1bpUPqx7kLP)-|)5-sC6>>nEdqkhngP2S6@d6 z6Tm2($+n>gzN<2tKHf_Oo_oeJ5)NA=Ukjz0yh67~`<8>Ott|OfxCr_FETY>c^cYt@ z>ErT0eZd$j1ep_A&xqoMD_(lrh$fpyqn5aJbzA1LFY^qgfG{NPccy{N|DOHe*%hwU zMW+-P`!8e99wp?1(IJ=P_#PUc#)4_8! zmJj1wTN}|?CR=J!uxqk!cY1UgDy9?ma5PDGznHCMqt%SW8g?Ipa_+1O>I22;!Gs+Q zgkkq~*-{i1EL`9_qwQ^SwP15i6nr``#%cj;AO;Nf7uHRGn(~)T)2FG5d$+sJtY$1N z+Yq$nyKNUn?-P;+uyCrHlJaVjRIY#QA$l9IXB&+%6@&=u5vHa|ydaTV=wK%5TlJz-cfj0x?A+q<)Hjz{ zM82aOiLSO^r_}@>;Jsd8>MQ)4Nf)GKE<(FTSytG6v79>kxh8JcsmKxU@PWBF1$uY` zylAC-X9?(D@+aa;Z-TFVr{zYvlyGQFyZ6XkWPn$b*qP~X&dI3;w^*z1--YiF*OqLvYB_*fAK&4X86wyi=|O;31hsqF#@ zwF=r~w}$#akEC|;tFQX3A$UC4Lk>F=$jk4K1>K#C8dNO2TkRvwKSc@H#(%OzQPO+r5nMjkSxW6UpgVnENw2zOD?ps-7VzUDyUBo1z0kw*~I zpj@rR!rzp92(ew2tS*;QwTAQu_kRIR7 zk`WRjD)4>-tE{OfX~X@=#=gAE`(s?!i__1{f;rptw=hRj+0z1{YCBg`v52kt2QiS& zzTtSP-9{K9@&2mil-NKdw@#VBE8CKd83!xc+|fJO&T35H)uwJtemD~R!O?rj>A(v~ zFfJ5|47I>DGH(WCUX1V@=t0@wJyOA@5CaCm>xA5=(V2WmNp4M-<>}$^fP;hnbJyi) z>>|~nRy8qyF*(x?V===0f)P3da?SvSeH0=637xnPcf9Le2v;QxJA-QYt_m^#!@z;} zjAGC1y=`u%T@c>$Oc3<$3ESy74h1AfTyr;qK^Q^#B%EBFT(CrNH;q6p>hRLRb)@T) zbsHfR#=`aiL9XLuM<3FQlz|<0Z1zjd8?R@+Oqc8pS2OFMyo=TQ5mt_62;m;HYd#YA zBb_cQ#^a95tLWOk1chkvi}1~p?Cj>~4(v8h8M@ViMKA3xqjkQbH5 z;nba7RMgF-!Y~_Tw_J#`h4vu$J%zIqg^}#<#<-}Ft;73q+!Y2k6NOz)&7N&F+fay5 zHl9Y|I{Pv_mTbA0S5W?-rmUJe;nU` z*ZJK?>$i@jo|}!+|8#|x(=^U+dmLW?0C4}QYHM#~Y-Dd_ZD6Ep=4fQ~yJ^{=^0@Un z9efAa2yY9BdtxexFc!GVMvs{<5IeHi8ZJz_hNRFlP;1@3rw%Llj}zXn{?^ z6O|joT*wN0V~{cPJz@MK?{A=G6?7&M_-{fAgW84|16sQbCh<%7QX@P{_MMEYjqVsc ztcVtu+C7f^sNto-j#b~{qLpJY!!Jb=uObjB=Z-|8s&#dNs>I;dgYFZ3TFKt@luU&1 z4!lS-xfkl4^$fOIj?e?6G`sFi711DX@(g0KSApoSSlrHl5_=b$*fhS+yL1u9bi1~3 zhJu;oxzJpuhkyO(^7l64ctB<$OdRvBcU()+w5o2m=JfFu-N;TbsP~6tu6bW!b^7eG z$)Dy!^r+7EmAO&8~qjvQi3M$yeK4;aIg6S<5 z-)m({f;EvC92vv-!8n&SY13n_c7sJ~A?TkmfZcE_-E+x5&7+eUl|@g%^2mw&#;W{| z@a@Y%XrM({S(Kxu(u4)hXNVU|D2;@eDkXc&&HPf6nzwJ13{uUtR8o zfd99#V`=tx!#hPu$pJd}E~t+X%qybF3PQp{Z4_ZHS9nhxNtUe5FSmr^F4{hwCrxcA zPNYNxNK|WnyilG_<1O45N=;=`j}9{qFR*|5@;`+P zXFW@&{}5fun5Vtkf4QCFSI9v5J7oN=`q#^UMG7Tpi;Z9H4S%S%P;hgs1>=tGIe|m? z@!I0)oUr|0hJN80e zF_7(Fs#g4RoY9Ya@gR7?5JY`1g@W=Ix#U6SFU?d9wKWGRk{@45P`p961g5L~&#_&{ zps;Fo>ASC()!t7m?JzPK=~Y1nef)cDz%l6-gP_EX4N872HIUW(Kp=MMbIr!SimK6e zANdA6G=JVfB`Sztk|@raf{~K_EeH)(g-VjCG8XO;ougXq{d$~mdl8?70a=U^eoU+r zbXEg>wV7lN#;+a4>o{~V3BOMO%+X)vT-+~eW*E7Up5aLZGZ8g?my$PgUZ@5iVj;3Z zDm<;(rkD>)4)CI+lhw-t`JKZ*S|E)ESK89iJe%#{sQ&Bfeo2wD`>&swz#-nJdsyyu z5;(rRMzmaHO2V)@aGOnvv5vZPjU8HtRHuw*Fgc(?%zvZM5_y%+2kYIE*;$*=^7rrIr0thMq1nYcIoVg#Y_b?)}exBofv^ z@6IFaxkHMXy+-Aawp_!~$D&nlbH7iENyhqepq6BsTInn$Tv+0@Tk57!vCh{)9iMGmhMot`{5nK9(7`%)E7;v@e^SPrMH? zIDcr29iD=ow>0jiYb+*FKd+9}qJX6dc+9DktMkflzEa)K&OO%APON^mXC8OGy%3L^ zGY3?uS~$q0q0mfn7qc-xR*#sB%a|3ih7@5vTkS_E*KB0Q2Qt3x5~dL z)$d*ZOW*;1^1qn>&0zn8{K;Vd1uF2vv-lhG|FDz4QUBWTZ!Y(j{NAvp